diff --git a/server/api/externalapi.ts b/server/api/externalapi.ts index 723a0e2c..93e7d159 100644 --- a/server/api/externalapi.ts +++ b/server/api/externalapi.ts @@ -1,4 +1,3 @@ -// import rateLimit from 'axios-rate-limit'; import rateLimit from '@server/utils/rateLimit'; import type NodeCache from 'node-cache'; diff --git a/server/lib/imageproxy.ts b/server/lib/imageproxy.ts index 38203b7b..7ef18570 100644 --- a/server/lib/imageproxy.ts +++ b/server/lib/imageproxy.ts @@ -1,6 +1,5 @@ import logger from '@server/logger'; -import axios from 'axios'; -import rateLimit, { type rateLimitOptions } from 'axios-rate-limit'; +import rateLimit from '@server/utils/rateLimit'; import { createHash } from 'crypto'; import { promises } from 'fs'; import path, { join } from 'path'; @@ -98,26 +97,30 @@ class ImageProxy { return files.length; } - private axios; + private fetch: typeof fetch; private cacheVersion; private key; + private baseUrl; constructor( key: string, baseUrl: string, options: { cacheVersion?: number; - rateLimitOptions?: rateLimitOptions; + rateLimitOptions?: { + maxRPS: number; + maxRequests: number; + }; } = {} ) { this.cacheVersion = options.cacheVersion ?? 1; + this.baseUrl = baseUrl; this.key = key; - this.axios = axios.create({ - baseURL: baseUrl, - }); if (options.rateLimitOptions) { - this.axios = rateLimit(this.axios, options.rateLimitOptions); + this.fetch = rateLimit(this.fetch, options.rateLimitOptions); + } else { + this.fetch = fetch; } } @@ -182,17 +185,20 @@ class ImageProxy { ): Promise { try { const directory = join(this.getCacheDirectory(), cacheKey); - const response = await this.axios.get(path, { - responseType: 'arraybuffer', - }); + const href = + this.baseUrl + + (this.baseUrl.endsWith('/') ? '' : '/') + + (path.startsWith('/') ? path.slice(1) : path); + const response = await this.fetch(href); + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); - const buffer = Buffer.from(response.data, 'binary'); const extension = path.split('.').pop() ?? ''; const maxAge = Number( - (response.headers['cache-control'] ?? '0').split('=')[1] + (response.headers.get('cache-control') ?? '0').split('=')[1] ); const expireAt = Date.now() + maxAge * 1000; - const etag = (response.headers.etag ?? '').replace(/"/g, ''); + const etag = (response.headers.get('etag') ?? '').replace(/"/g, ''); await this.writeToCacheDir( directory, diff --git a/server/lib/notifications/agents/discord.ts b/server/lib/notifications/agents/discord.ts index 67a278bf..1585af5a 100644 --- a/server/lib/notifications/agents/discord.ts +++ b/server/lib/notifications/agents/discord.ts @@ -4,7 +4,6 @@ import { User } from '@server/entity/User'; import type { NotificationAgentDiscord } from '@server/lib/settings'; import { getSettings, NotificationAgentKey } from '@server/lib/settings'; import logger from '@server/logger'; -import axios from 'axios'; import { hasNotificationType, Notification, @@ -292,14 +291,20 @@ class DiscordAgent } } - await axios.post(settings.options.webhookUrl, { - username: settings.options.botUsername - ? settings.options.botUsername - : getSettings().main.applicationTitle, - avatar_url: settings.options.botAvatarUrl, - embeds: [this.buildEmbed(type, payload)], - content: userMentions.join(' '), - } as DiscordWebhookPayload); + await fetch(settings.options.webhookUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + username: settings.options.botUsername + ? settings.options.botUsername + : getSettings().main.applicationTitle, + avatar_url: settings.options.botAvatarUrl, + embeds: [this.buildEmbed(type, payload)], + content: userMentions.join(' '), + } as DiscordWebhookPayload), + }); return true; } catch (e) { diff --git a/server/lib/notifications/agents/gotify.ts b/server/lib/notifications/agents/gotify.ts index d07caac4..c390fbf8 100644 --- a/server/lib/notifications/agents/gotify.ts +++ b/server/lib/notifications/agents/gotify.ts @@ -2,7 +2,6 @@ import { IssueStatus, IssueTypeName } from '@server/constants/issue'; import type { NotificationAgentGotify } from '@server/lib/settings'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; -import axios from 'axios'; import { hasNotificationType, Notification } from '..'; import type { NotificationAgent, NotificationPayload } from './agent'; import { BaseAgent } from './agent'; @@ -133,7 +132,13 @@ class GotifyAgent const endpoint = `${settings.options.url}/message?token=${settings.options.token}`; const notificationPayload = this.getNotificationPayload(type, payload); - await axios.post(endpoint, notificationPayload); + await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(notificationPayload), + }); return true; } catch (e) { diff --git a/server/lib/notifications/agents/lunasea.ts b/server/lib/notifications/agents/lunasea.ts index 885b038c..1e8e3efa 100644 --- a/server/lib/notifications/agents/lunasea.ts +++ b/server/lib/notifications/agents/lunasea.ts @@ -3,7 +3,6 @@ import { MediaStatus } from '@server/constants/media'; import type { NotificationAgentLunaSea } from '@server/lib/settings'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; -import axios from 'axios'; import { hasNotificationType, Notification } from '..'; import type { NotificationAgent, NotificationPayload } from './agent'; import { BaseAgent } from './agent'; @@ -101,19 +100,20 @@ class LunaSeaAgent }); try { - await axios.post( - settings.options.webhookUrl, - this.buildPayload(type, payload), - settings.options.profileName + await fetch(settings.options.webhookUrl, { + method: 'POST', + headers: settings.options.profileName ? { - headers: { - Authorization: `Basic ${Buffer.from( - `${settings.options.profileName}:` - ).toString('base64')}`, - }, + 'Content-Type': 'application/json', } - : undefined - ); + : { + 'Content-Type': 'application/json', + Authorization: `Basic ${Buffer.from( + `${settings.options.profileName}:` + ).toString('base64')}`, + }, + body: JSON.stringify(this.buildPayload(type, payload)), + }); return true; } catch (e) { diff --git a/server/lib/notifications/agents/pushbullet.ts b/server/lib/notifications/agents/pushbullet.ts index eed4fda9..68aa911f 100644 --- a/server/lib/notifications/agents/pushbullet.ts +++ b/server/lib/notifications/agents/pushbullet.ts @@ -5,7 +5,6 @@ import { User } from '@server/entity/User'; import type { NotificationAgentPushbullet } from '@server/lib/settings'; import { getSettings, NotificationAgentKey } from '@server/lib/settings'; import logger from '@server/logger'; -import axios from 'axios'; import { hasNotificationType, Notification, @@ -123,15 +122,17 @@ class PushbulletAgent }); try { - await axios.post( - endpoint, - { ...notificationPayload, channel_tag: settings.options.channelTag }, - { - headers: { - 'Access-Token': settings.options.accessToken, - }, - } - ); + await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Access-Token': settings.options.accessToken, + }, + body: JSON.stringify({ + ...notificationPayload, + channel_tag: settings.options.channelTag, + }), + }); } catch (e) { logger.error('Error sending Pushbullet notification', { label: 'Notifications', @@ -163,10 +164,13 @@ class PushbulletAgent }); try { - await axios.post(endpoint, notificationPayload, { + await fetch(endpoint, { + method: 'POST', headers: { + 'Content-Type': 'application/json', 'Access-Token': payload.notifyUser.settings.pushbulletAccessToken, }, + body: JSON.stringify(notificationPayload), }); } catch (e) { logger.error('Error sending Pushbullet notification', { @@ -211,10 +215,13 @@ class PushbulletAgent }); try { - await axios.post(endpoint, notificationPayload, { + await fetch(endpoint, { + method: 'POST', headers: { + 'Content-Type': 'application/json', 'Access-Token': user.settings.pushbulletAccessToken, }, + body: JSON.stringify(notificationPayload), }); } catch (e) { logger.error('Error sending Pushbullet notification', { diff --git a/server/lib/notifications/agents/pushover.ts b/server/lib/notifications/agents/pushover.ts index db5176a7..e2a0650e 100644 --- a/server/lib/notifications/agents/pushover.ts +++ b/server/lib/notifications/agents/pushover.ts @@ -5,7 +5,6 @@ import { User } from '@server/entity/User'; import type { NotificationAgentPushover } from '@server/lib/settings'; import { getSettings, NotificationAgentKey } from '@server/lib/settings'; import logger from '@server/logger'; -import axios from 'axios'; import { hasNotificationType, Notification, @@ -52,12 +51,12 @@ class PushoverAgent imageUrl: string ): Promise> { try { - const response = await axios.get(imageUrl, { - responseType: 'arraybuffer', - }); - const base64 = Buffer.from(response.data, 'binary').toString('base64'); + const response = await fetch(imageUrl); + const arrayBuffer = await response.arrayBuffer(); + const base64 = Buffer.from(arrayBuffer).toString('base64'); const contentType = ( - response.headers['Content-Type'] || response.headers['content-type'] + response.headers.get('Content-Type') || + response.headers.get('content-type') )?.toString(); return { @@ -201,12 +200,18 @@ class PushoverAgent }); try { - await axios.post(endpoint, { - ...notificationPayload, - token: settings.options.accessToken, - user: settings.options.userToken, - sound: settings.options.sound, - } as PushoverPayload); + await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...notificationPayload, + token: settings.options.accessToken, + user: settings.options.userToken, + sound: settings.options.sound, + } as PushoverPayload), + }); } catch (e) { logger.error('Error sending Pushover notification', { label: 'Notifications', @@ -241,12 +246,18 @@ class PushoverAgent }); try { - await axios.post(endpoint, { - ...notificationPayload, - token: payload.notifyUser.settings.pushoverApplicationToken, - user: payload.notifyUser.settings.pushoverUserKey, - sound: payload.notifyUser.settings.pushoverSound, - } as PushoverPayload); + await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...notificationPayload, + token: payload.notifyUser.settings.pushoverApplicationToken, + user: payload.notifyUser.settings.pushoverUserKey, + sound: payload.notifyUser.settings.pushoverSound, + } as PushoverPayload), + }); } catch (e) { logger.error('Error sending Pushover notification', { label: 'Notifications', @@ -291,11 +302,17 @@ class PushoverAgent }); try { - await axios.post(endpoint, { - ...notificationPayload, - token: user.settings.pushoverApplicationToken, - user: user.settings.pushoverUserKey, - } as PushoverPayload); + await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...notificationPayload, + token: user.settings.pushoverApplicationToken, + user: user.settings.pushoverUserKey, + } as PushoverPayload), + }); } catch (e) { logger.error('Error sending Pushover notification', { label: 'Notifications', diff --git a/server/lib/notifications/agents/slack.ts b/server/lib/notifications/agents/slack.ts index 9447cda3..175f38a5 100644 --- a/server/lib/notifications/agents/slack.ts +++ b/server/lib/notifications/agents/slack.ts @@ -2,7 +2,6 @@ import { IssueStatus, IssueTypeName } from '@server/constants/issue'; import type { NotificationAgentSlack } from '@server/lib/settings'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; -import axios from 'axios'; import { hasNotificationType, Notification } from '..'; import type { NotificationAgent, NotificationPayload } from './agent'; import { BaseAgent } from './agent'; @@ -238,10 +237,13 @@ class SlackAgent subject: payload.subject, }); try { - await axios.post( - settings.options.webhookUrl, - this.buildEmbed(type, payload) - ); + await fetch(settings.options.webhookUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(this.buildEmbed(type, payload)), + }); return true; } catch (e) { diff --git a/server/lib/notifications/agents/telegram.ts b/server/lib/notifications/agents/telegram.ts index 7d706212..50cea9c0 100644 --- a/server/lib/notifications/agents/telegram.ts +++ b/server/lib/notifications/agents/telegram.ts @@ -5,7 +5,6 @@ import { User } from '@server/entity/User'; import type { NotificationAgentTelegram } from '@server/lib/settings'; import { getSettings, NotificationAgentKey } from '@server/lib/settings'; import logger from '@server/logger'; -import axios from 'axios'; import { hasNotificationType, Notification, @@ -175,11 +174,17 @@ class TelegramAgent }); try { - await axios.post(endpoint, { - ...notificationPayload, - chat_id: settings.options.chatId, - disable_notification: !!settings.options.sendSilently, - } as TelegramMessagePayload | TelegramPhotoPayload); + await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...notificationPayload, + chat_id: settings.options.chatId, + disable_notification: !!settings.options.sendSilently, + } as TelegramMessagePayload | TelegramPhotoPayload), + }); } catch (e) { logger.error('Error sending Telegram notification', { label: 'Notifications', @@ -210,12 +215,18 @@ class TelegramAgent }); try { - await axios.post(endpoint, { - ...notificationPayload, - chat_id: payload.notifyUser.settings.telegramChatId, - disable_notification: - !!payload.notifyUser.settings.telegramSendSilently, - } as TelegramMessagePayload | TelegramPhotoPayload); + await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...notificationPayload, + chat_id: payload.notifyUser.settings.telegramChatId, + disable_notification: + !!payload.notifyUser.settings.telegramSendSilently, + } as TelegramMessagePayload | TelegramPhotoPayload), + }); } catch (e) { logger.error('Error sending Telegram notification', { label: 'Notifications', @@ -257,11 +268,17 @@ class TelegramAgent }); try { - await axios.post(endpoint, { - ...notificationPayload, - chat_id: user.settings.telegramChatId, - disable_notification: !!user.settings?.telegramSendSilently, - } as TelegramMessagePayload | TelegramPhotoPayload); + await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...notificationPayload, + chat_id: user.settings.telegramChatId, + disable_notification: !!user.settings?.telegramSendSilently, + } as TelegramMessagePayload | TelegramPhotoPayload), + }); } catch (e) { logger.error('Error sending Telegram notification', { label: 'Notifications', diff --git a/server/lib/notifications/agents/webhook.ts b/server/lib/notifications/agents/webhook.ts index b8e8969d..7382f1d0 100644 --- a/server/lib/notifications/agents/webhook.ts +++ b/server/lib/notifications/agents/webhook.ts @@ -3,7 +3,6 @@ import { MediaStatus } from '@server/constants/media'; import type { NotificationAgentWebhook } from '@server/lib/settings'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; -import axios from 'axios'; import { get } from 'lodash'; import { hasNotificationType, Notification } from '..'; import type { NotificationAgent, NotificationPayload } from './agent'; @@ -178,17 +177,16 @@ class WebhookAgent }); try { - await axios.post( - settings.options.webhookUrl, - this.buildPayload(type, payload), - settings.options.authHeader - ? { - headers: { - Authorization: settings.options.authHeader, - }, - } - : undefined - ); + await fetch(settings.options.webhookUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + ...(settings.options.authHeader + ? { Authorization: settings.options.authHeader } + : {}), + }, + body: JSON.stringify(this.buildPayload(type, payload)), + }); return true; } catch (e) {