Merge branch 'develop' of https://github.com/sct/overseerr into jellyfin-support
This commit is contained in:
@@ -1,24 +1,34 @@
|
||||
import { Router } from 'express';
|
||||
import { getSettings, Library, MainSettings } from '../../lib/settings';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import fs from 'fs';
|
||||
import { merge, omit } from 'lodash';
|
||||
import path from 'path';
|
||||
import { getRepository } from 'typeorm';
|
||||
import { User } from '../../entity/User';
|
||||
import { URL } from 'url';
|
||||
import JellyfinAPI from '../../api/jellyfin';
|
||||
import PlexAPI from '../../api/plexapi';
|
||||
import PlexTvAPI from '../../api/plextv';
|
||||
import JellyfinAPI from '../../api/jellyfin';
|
||||
import { jobPlexFullSync } from '../../job/plexsync';
|
||||
import { jobJellyfinFullSync } from '../../job/jellyfinsync';
|
||||
import { scheduledJobs } from '../../job/schedule';
|
||||
import { Permission } from '../../lib/permissions';
|
||||
import { isAuthenticated } from '../../middleware/auth';
|
||||
import { merge, omit } from 'lodash';
|
||||
import Media from '../../entity/Media';
|
||||
import { MediaRequest } from '../../entity/MediaRequest';
|
||||
import { getAppVersion } from '../../utils/appVersion';
|
||||
import { SettingsAboutResponse } from '../../interfaces/api/settingsInterfaces';
|
||||
import notificationRoutes from './notifications';
|
||||
import sonarrRoutes from './sonarr';
|
||||
import radarrRoutes from './radarr';
|
||||
import { User } from '../../entity/User';
|
||||
import { PlexConnection } from '../../interfaces/api/plexInterfaces';
|
||||
import {
|
||||
LogMessage,
|
||||
LogsResultsResponse,
|
||||
SettingsAboutResponse,
|
||||
} from '../../interfaces/api/settingsInterfaces';
|
||||
import { jobJellyfinFullSync } from '../../job/jellyfinsync';
|
||||
import { scheduledJobs } from '../../job/schedule';
|
||||
import cacheManager, { AvailableCacheIds } from '../../lib/cache';
|
||||
import { Permission } from '../../lib/permissions';
|
||||
import { plexFullScanner } from '../../lib/scanners/plex';
|
||||
import { getSettings, MainSettings } from '../../lib/settings';
|
||||
import logger from '../../logger';
|
||||
import { isAuthenticated } from '../../middleware/auth';
|
||||
import { getAppVersion } from '../../utils/appVersion';
|
||||
import notificationRoutes from './notifications';
|
||||
import radarrRoutes from './radarr';
|
||||
import sonarrRoutes from './sonarr';
|
||||
|
||||
const settingsRoutes = Router();
|
||||
|
||||
@@ -107,7 +117,6 @@ settingsRoutes.post('/plex', async (req, res, next) => {
|
||||
|
||||
settingsRoutes.get('/plex/devices/servers', async (req, res, next) => {
|
||||
const userRepository = getRepository(User);
|
||||
const regexp = /(http(s?):\/\/)(.*)(:[0-9]*)/;
|
||||
try {
|
||||
const admin = await userRepository.findOneOrFail({
|
||||
select: ['id', 'plexToken'],
|
||||
@@ -120,40 +129,51 @@ settingsRoutes.get('/plex/devices/servers', async (req, res, next) => {
|
||||
return device.provides.includes('server') && device.owned;
|
||||
});
|
||||
const settings = getSettings();
|
||||
|
||||
if (devices) {
|
||||
await Promise.all(
|
||||
devices.map(async (device) => {
|
||||
const plexDirectConnections: PlexConnection[] = [];
|
||||
|
||||
device.connection.forEach((connection) => {
|
||||
const url = new URL(connection.uri);
|
||||
|
||||
if (url.hostname !== connection.address) {
|
||||
const plexDirectConnection = { ...connection };
|
||||
plexDirectConnection.address = url.hostname;
|
||||
plexDirectConnections.push(plexDirectConnection);
|
||||
|
||||
// Connect to IP addresses over HTTP
|
||||
connection.protocol = 'http';
|
||||
}
|
||||
});
|
||||
|
||||
plexDirectConnections.forEach((plexDirectConnection) => {
|
||||
device.connection.push(plexDirectConnection);
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
device.connection.map(async (connection) => {
|
||||
connection.host = connection.uri.replace(regexp, '$3');
|
||||
let msg:
|
||||
| { status: number; message: string }
|
||||
| undefined = undefined;
|
||||
const plexDeviceSettings = {
|
||||
...settings.plex,
|
||||
ip: connection.host,
|
||||
ip: connection.address,
|
||||
port: connection.port,
|
||||
useSsl: connection.protocol === 'https' ? true : false,
|
||||
useSsl: connection.protocol === 'https',
|
||||
};
|
||||
const plexClient = new PlexAPI({
|
||||
plexToken: admin.plexToken,
|
||||
plexSettings: plexDeviceSettings,
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
try {
|
||||
await plexClient.getStatus();
|
||||
msg = {
|
||||
status: 200,
|
||||
message: 'OK',
|
||||
};
|
||||
connection.status = 200;
|
||||
connection.message = 'OK';
|
||||
} catch (e) {
|
||||
msg = {
|
||||
status: 500,
|
||||
message: e.message,
|
||||
};
|
||||
connection.status = 500;
|
||||
connection.message = e.message.split(':')[0];
|
||||
}
|
||||
connection.status = msg?.status;
|
||||
connection.message = msg?.message;
|
||||
})
|
||||
);
|
||||
})
|
||||
@@ -179,26 +199,7 @@ settingsRoutes.get('/plex/library', async (req, res) => {
|
||||
});
|
||||
const plexapi = new PlexAPI({ plexToken: admin.plexToken });
|
||||
|
||||
const libraries = await plexapi.getLibraries();
|
||||
|
||||
const newLibraries: Library[] = libraries
|
||||
// Remove libraries that are not movie or show
|
||||
.filter((library) => library.type === 'movie' || library.type === 'show')
|
||||
// Remove libraries that do not have a metadata agent set (usually personal video libraries)
|
||||
.filter((library) => library.agent !== 'com.plexapp.agents.none')
|
||||
.map((library) => {
|
||||
const existing = settings.plex.libraries.find(
|
||||
(l) => l.id === library.key && l.name === library.title
|
||||
);
|
||||
|
||||
return {
|
||||
id: library.key,
|
||||
name: library.title,
|
||||
enabled: existing?.enabled ?? false,
|
||||
};
|
||||
});
|
||||
|
||||
settings.plex.libraries = newLibraries;
|
||||
await plexapi.syncLibraries();
|
||||
}
|
||||
|
||||
const enabledLibraries = req.query.enable
|
||||
@@ -213,16 +214,16 @@ settingsRoutes.get('/plex/library', async (req, res) => {
|
||||
});
|
||||
|
||||
settingsRoutes.get('/plex/sync', (_req, res) => {
|
||||
return res.status(200).json(jobPlexFullSync.status());
|
||||
return res.status(200).json(plexFullScanner.status());
|
||||
});
|
||||
|
||||
settingsRoutes.post('/plex/sync', (req, res) => {
|
||||
if (req.body.cancel) {
|
||||
jobPlexFullSync.cancel();
|
||||
plexFullScanner.cancel();
|
||||
} else if (req.body.start) {
|
||||
jobPlexFullSync.run();
|
||||
plexFullScanner.run();
|
||||
}
|
||||
return res.status(200).json(jobPlexFullSync.status());
|
||||
return res.status(200).json(plexFullScanner.status());
|
||||
});
|
||||
|
||||
settingsRoutes.get('/jellyfin', (_req, res) => {
|
||||
@@ -297,6 +298,85 @@ settingsRoutes.post('/jellyfin/sync', (req, res) => {
|
||||
}
|
||||
return res.status(200).json(jobJellyfinFullSync.status());
|
||||
});
|
||||
settingsRoutes.get(
|
||||
'/logs',
|
||||
rateLimit({ windowMs: 60 * 1000, max: 50 }),
|
||||
(req, res, next) => {
|
||||
const pageSize = req.query.take ? Number(req.query.take) : 25;
|
||||
const skip = req.query.skip ? Number(req.query.skip) : 0;
|
||||
|
||||
let filter: string[] = [];
|
||||
switch (req.query.filter) {
|
||||
case 'debug':
|
||||
filter.push('debug');
|
||||
// falls through
|
||||
case 'info':
|
||||
filter.push('info');
|
||||
// falls through
|
||||
case 'warn':
|
||||
filter.push('warn');
|
||||
// falls through
|
||||
case 'error':
|
||||
filter.push('error');
|
||||
break;
|
||||
default:
|
||||
filter = ['debug', 'info', 'warn', 'error'];
|
||||
}
|
||||
|
||||
const logFile = process.env.CONFIG_DIRECTORY
|
||||
? `${process.env.CONFIG_DIRECTORY}/logs/overseerr.log`
|
||||
: path.join(__dirname, '../../../config/logs/overseerr.log');
|
||||
const logs: LogMessage[] = [];
|
||||
|
||||
try {
|
||||
fs.readFileSync(logFile)
|
||||
.toString()
|
||||
.split('\n')
|
||||
.forEach((line) => {
|
||||
if (!line.length) return;
|
||||
|
||||
const timestamp = line.match(new RegExp(/^.{24}/)) || [];
|
||||
const level = line.match(new RegExp(/\s\[\w+\]/)) || [];
|
||||
const label = line.match(new RegExp(/\]\[.+?\]/)) || [];
|
||||
const message = line.match(new RegExp(/:\s([^{}]+)({.*})?/)) || [];
|
||||
|
||||
if (level.length && filter.includes(level[0].slice(2, -1))) {
|
||||
logs.push({
|
||||
timestamp: timestamp[0],
|
||||
level: level.length ? level[0].slice(2, -1) : '',
|
||||
label: label.length ? label[0].slice(2, -1) : '',
|
||||
message: message.length && message[1] ? message[1] : '',
|
||||
data:
|
||||
message.length && message[2]
|
||||
? JSON.parse(message[2])
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const displayedLogs = logs.reverse().slice(skip, skip + pageSize);
|
||||
|
||||
return res.status(200).json({
|
||||
pageInfo: {
|
||||
pages: Math.ceil(logs.length / pageSize),
|
||||
pageSize,
|
||||
results: logs.length,
|
||||
page: Math.ceil(skip / pageSize) + 1,
|
||||
},
|
||||
results: displayedLogs,
|
||||
} as LogsResultsResponse);
|
||||
} catch (error) {
|
||||
logger.error('Something went wrong while fetching the logs', {
|
||||
label: 'Logs',
|
||||
errorMessage: error.message,
|
||||
});
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Something went wrong while fetching the logs',
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
settingsRoutes.get('/jobs', (_req, res) => {
|
||||
return res.status(200).json(
|
||||
|
||||
@@ -1,39 +1,18 @@
|
||||
import { Router } from 'express';
|
||||
import { getSettings } from '../../lib/settings';
|
||||
import { Notification } from '../../lib/notifications';
|
||||
import DiscordAgent from '../../lib/notifications/agents/discord';
|
||||
import EmailAgent from '../../lib/notifications/agents/email';
|
||||
import LunaSeaAgent from '../../lib/notifications/agents/lunasea';
|
||||
import PushbulletAgent from '../../lib/notifications/agents/pushbullet';
|
||||
import PushoverAgent from '../../lib/notifications/agents/pushover';
|
||||
import SlackAgent from '../../lib/notifications/agents/slack';
|
||||
import TelegramAgent from '../../lib/notifications/agents/telegram';
|
||||
import PushoverAgent from '../../lib/notifications/agents/pushover';
|
||||
import WebhookAgent from '../../lib/notifications/agents/webhook';
|
||||
import PushbulletAgent from '../../lib/notifications/agents/pushbullet';
|
||||
import WebPushAgent from '../../lib/notifications/agents/webpush';
|
||||
import { getSettings } from '../../lib/settings';
|
||||
|
||||
const notificationRoutes = Router();
|
||||
|
||||
notificationRoutes.get('/', (_req, res) => {
|
||||
const settings = getSettings().notifications;
|
||||
return res.status(200).json({
|
||||
enabled: settings.enabled,
|
||||
autoapprovalEnabled: settings.autoapprovalEnabled,
|
||||
});
|
||||
});
|
||||
|
||||
notificationRoutes.post('/', (req, res) => {
|
||||
const settings = getSettings();
|
||||
|
||||
Object.assign(settings.notifications, {
|
||||
enabled: req.body.enabled,
|
||||
autoapprovalEnabled: req.body.autoapprovalEnabled,
|
||||
});
|
||||
settings.save();
|
||||
|
||||
return res.status(200).json({
|
||||
enabled: settings.notifications.enabled,
|
||||
autoapprovalEnabled: settings.notifications.autoapprovalEnabled,
|
||||
});
|
||||
});
|
||||
|
||||
notificationRoutes.get('/discord', (_req, res) => {
|
||||
const settings = getSettings();
|
||||
|
||||
@@ -49,23 +28,30 @@ notificationRoutes.post('/discord', (req, res) => {
|
||||
res.status(200).json(settings.notifications.agents.discord);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/discord/test', (req, res, next) => {
|
||||
notificationRoutes.post('/discord/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User information missing from request',
|
||||
message: 'User information is missing from the request.',
|
||||
});
|
||||
}
|
||||
|
||||
const discordAgent = new DiscordAgent(req.body);
|
||||
discordAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
});
|
||||
|
||||
return res.status(204).send();
|
||||
if (
|
||||
await discordAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send Discord notification.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.get('/slack', (_req, res) => {
|
||||
@@ -83,23 +69,30 @@ notificationRoutes.post('/slack', (req, res) => {
|
||||
res.status(200).json(settings.notifications.agents.slack);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/slack/test', (req, res, next) => {
|
||||
notificationRoutes.post('/slack/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User information missing from request',
|
||||
message: 'User information is missing from the request.',
|
||||
});
|
||||
}
|
||||
|
||||
const slackAgent = new SlackAgent(req.body);
|
||||
slackAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
});
|
||||
|
||||
return res.status(204).send();
|
||||
if (
|
||||
await slackAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send Slack notification.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.get('/telegram', (_req, res) => {
|
||||
@@ -117,23 +110,30 @@ notificationRoutes.post('/telegram', (req, res) => {
|
||||
res.status(200).json(settings.notifications.agents.telegram);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/telegram/test', (req, res, next) => {
|
||||
notificationRoutes.post('/telegram/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User information missing from request',
|
||||
message: 'User information is missing from the request.',
|
||||
});
|
||||
}
|
||||
|
||||
const telegramAgent = new TelegramAgent(req.body);
|
||||
telegramAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
});
|
||||
|
||||
return res.status(204).send();
|
||||
if (
|
||||
await telegramAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send Telegram notification.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.get('/pushbullet', (_req, res) => {
|
||||
@@ -151,23 +151,30 @@ notificationRoutes.post('/pushbullet', (req, res) => {
|
||||
res.status(200).json(settings.notifications.agents.pushbullet);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/pushbullet/test', (req, res, next) => {
|
||||
notificationRoutes.post('/pushbullet/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User information missing from request',
|
||||
message: 'User information is missing from the request.',
|
||||
});
|
||||
}
|
||||
|
||||
const pushbulletAgent = new PushbulletAgent(req.body);
|
||||
pushbulletAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
});
|
||||
|
||||
return res.status(204).send();
|
||||
if (
|
||||
await pushbulletAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send Pushbullet notification.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.get('/pushover', (_req, res) => {
|
||||
@@ -185,23 +192,30 @@ notificationRoutes.post('/pushover', (req, res) => {
|
||||
res.status(200).json(settings.notifications.agents.pushover);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/pushover/test', (req, res, next) => {
|
||||
notificationRoutes.post('/pushover/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User information missing from request',
|
||||
message: 'User information is missing from the request.',
|
||||
});
|
||||
}
|
||||
|
||||
const pushoverAgent = new PushoverAgent(req.body);
|
||||
pushoverAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
});
|
||||
|
||||
return res.status(204).send();
|
||||
if (
|
||||
await pushoverAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send Pushover notification.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.get('/email', (_req, res) => {
|
||||
@@ -219,7 +233,48 @@ notificationRoutes.post('/email', (req, res) => {
|
||||
res.status(200).json(settings.notifications.agents.email);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/email/test', (req, res, next) => {
|
||||
notificationRoutes.post('/email/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User information is missing from the request.',
|
||||
});
|
||||
}
|
||||
|
||||
const emailAgent = new EmailAgent(req.body);
|
||||
if (
|
||||
await emailAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send email notification.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.get('/webpush', (_req, res) => {
|
||||
const settings = getSettings();
|
||||
|
||||
res.status(200).json(settings.notifications.agents.webpush);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/webpush', (req, res) => {
|
||||
const settings = getSettings();
|
||||
|
||||
settings.notifications.agents.webpush = req.body;
|
||||
settings.save();
|
||||
|
||||
res.status(200).json(settings.notifications.agents.webpush);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/webpush/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
@@ -227,15 +282,22 @@ notificationRoutes.post('/email/test', (req, res, next) => {
|
||||
});
|
||||
}
|
||||
|
||||
const emailAgent = new EmailAgent(req.body);
|
||||
emailAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
});
|
||||
|
||||
return res.status(204).send();
|
||||
const webpushAgent = new WebPushAgent(req.body);
|
||||
if (
|
||||
await webpushAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send web push notification.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.get('/webhook', (_req, res) => {
|
||||
@@ -283,11 +345,11 @@ notificationRoutes.post('/webhook', (req, res, next) => {
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.post('/webhook/test', (req, res, next) => {
|
||||
notificationRoutes.post('/webhook/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User information missing from request',
|
||||
message: 'User information is missing from the request.',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -307,17 +369,65 @@ notificationRoutes.post('/webhook/test', (req, res, next) => {
|
||||
};
|
||||
|
||||
const webhookAgent = new WebhookAgent(testBody);
|
||||
webhookAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
});
|
||||
|
||||
return res.status(204).send();
|
||||
if (
|
||||
await webhookAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send webhook notification.',
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
next({ status: 500, message: e.message });
|
||||
}
|
||||
});
|
||||
|
||||
notificationRoutes.get('/lunasea', (_req, res) => {
|
||||
const settings = getSettings();
|
||||
|
||||
res.status(200).json(settings.notifications.agents.lunasea);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/lunasea', (req, res) => {
|
||||
const settings = getSettings();
|
||||
|
||||
settings.notifications.agents.lunasea = req.body;
|
||||
settings.save();
|
||||
|
||||
res.status(200).json(settings.notifications.agents.lunasea);
|
||||
});
|
||||
|
||||
notificationRoutes.post('/lunasea/test', async (req, res, next) => {
|
||||
if (!req.user) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User information missing from request',
|
||||
});
|
||||
}
|
||||
|
||||
const lunaseaAgent = new LunaSeaAgent(req.body);
|
||||
if (
|
||||
await lunaseaAgent.send(Notification.TEST_NOTIFICATION, {
|
||||
notifyUser: req.user,
|
||||
subject: 'Test Notification',
|
||||
message:
|
||||
'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?',
|
||||
})
|
||||
) {
|
||||
return res.status(204).send();
|
||||
} else {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'Failed to send web push notification.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default notificationRoutes;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Router } from 'express';
|
||||
import RadarrAPI from '../../api/radarr';
|
||||
import RadarrAPI from '../../api/servarr/radarr';
|
||||
import { getSettings, RadarrSettings } from '../../lib/settings';
|
||||
import logger from '../../logger';
|
||||
|
||||
@@ -35,15 +35,20 @@ radarrRoutes.post('/', (req, res) => {
|
||||
return res.status(201).json(newRadarr);
|
||||
});
|
||||
|
||||
radarrRoutes.post('/test', async (req, res, next) => {
|
||||
radarrRoutes.post<
|
||||
undefined,
|
||||
Record<string, unknown>,
|
||||
RadarrSettings & { tagLabel?: string }
|
||||
>('/test', async (req, res, next) => {
|
||||
try {
|
||||
const radarr = new RadarrAPI({
|
||||
apiKey: req.body.apiKey,
|
||||
url: RadarrAPI.buildRadarrUrl(req.body, '/api/v3'),
|
||||
url: RadarrAPI.buildUrl(req.body, '/api/v3'),
|
||||
});
|
||||
|
||||
const profiles = await radarr.getProfiles();
|
||||
const folders = await radarr.getRootFolders();
|
||||
const tags = await radarr.getTags();
|
||||
|
||||
return res.status(200).json({
|
||||
profiles,
|
||||
@@ -51,6 +56,7 @@ radarrRoutes.post('/test', async (req, res, next) => {
|
||||
id: folder.id,
|
||||
path: folder.path,
|
||||
})),
|
||||
tags,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error('Failed to test Radarr', {
|
||||
@@ -62,40 +68,41 @@ radarrRoutes.post('/test', async (req, res, next) => {
|
||||
}
|
||||
});
|
||||
|
||||
radarrRoutes.put<{ id: string }>('/:id', (req, res) => {
|
||||
const settings = getSettings();
|
||||
radarrRoutes.put<{ id: string }, RadarrSettings, RadarrSettings>(
|
||||
'/:id',
|
||||
(req, res, next) => {
|
||||
const settings = getSettings();
|
||||
|
||||
const radarrIndex = settings.radarr.findIndex(
|
||||
(r) => r.id === Number(req.params.id)
|
||||
);
|
||||
const radarrIndex = settings.radarr.findIndex(
|
||||
(r) => r.id === Number(req.params.id)
|
||||
);
|
||||
|
||||
if (radarrIndex === -1) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ status: '404', message: 'Settings instance not found' });
|
||||
if (radarrIndex === -1) {
|
||||
return next({ status: '404', message: 'Settings instance not found' });
|
||||
}
|
||||
|
||||
// If we are setting this as the default, clear any previous defaults for the same type first
|
||||
// ex: if is4k is true, it will only remove defaults for other servers that have is4k set to true
|
||||
// and are the default
|
||||
if (req.body.isDefault) {
|
||||
settings.radarr
|
||||
.filter((radarrInstance) => radarrInstance.is4k === req.body.is4k)
|
||||
.forEach((radarrInstance) => {
|
||||
radarrInstance.isDefault = false;
|
||||
});
|
||||
}
|
||||
|
||||
settings.radarr[radarrIndex] = {
|
||||
...req.body,
|
||||
id: Number(req.params.id),
|
||||
} as RadarrSettings;
|
||||
settings.save();
|
||||
|
||||
return res.status(200).json(settings.radarr[radarrIndex]);
|
||||
}
|
||||
);
|
||||
|
||||
// If we are setting this as the default, clear any previous defaults for the same type first
|
||||
// ex: if is4k is true, it will only remove defaults for other servers that have is4k set to true
|
||||
// and are the default
|
||||
if (req.body.isDefault) {
|
||||
settings.radarr
|
||||
.filter((radarrInstance) => radarrInstance.is4k === req.body.is4k)
|
||||
.forEach((radarrInstance) => {
|
||||
radarrInstance.isDefault = false;
|
||||
});
|
||||
}
|
||||
|
||||
settings.radarr[radarrIndex] = {
|
||||
...req.body,
|
||||
id: Number(req.params.id),
|
||||
} as RadarrSettings;
|
||||
settings.save();
|
||||
|
||||
return res.status(200).json(settings.radarr[radarrIndex]);
|
||||
});
|
||||
|
||||
radarrRoutes.get<{ id: string }>('/:id/profiles', async (req, res) => {
|
||||
radarrRoutes.get<{ id: string }>('/:id/profiles', async (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
|
||||
const radarrSettings = settings.radarr.find(
|
||||
@@ -103,14 +110,12 @@ radarrRoutes.get<{ id: string }>('/:id/profiles', async (req, res) => {
|
||||
);
|
||||
|
||||
if (!radarrSettings) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ status: '404', message: 'Settings instance not found' });
|
||||
return next({ status: '404', message: 'Settings instance not found' });
|
||||
}
|
||||
|
||||
const radarr = new RadarrAPI({
|
||||
apiKey: radarrSettings.apiKey,
|
||||
url: RadarrAPI.buildRadarrUrl(radarrSettings, '/api/v3'),
|
||||
url: RadarrAPI.buildUrl(radarrSettings, '/api/v3'),
|
||||
});
|
||||
|
||||
const profiles = await radarr.getProfiles();
|
||||
@@ -123,7 +128,7 @@ radarrRoutes.get<{ id: string }>('/:id/profiles', async (req, res) => {
|
||||
);
|
||||
});
|
||||
|
||||
radarrRoutes.delete<{ id: string }>('/:id', (req, res) => {
|
||||
radarrRoutes.delete<{ id: string }>('/:id', (req, res, next) => {
|
||||
const settings = getSettings();
|
||||
|
||||
const radarrIndex = settings.radarr.findIndex(
|
||||
@@ -131,9 +136,7 @@ radarrRoutes.delete<{ id: string }>('/:id', (req, res) => {
|
||||
);
|
||||
|
||||
if (radarrIndex === -1) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ status: '404', message: 'Settings instance not found' });
|
||||
return next({ status: '404', message: 'Settings instance not found' });
|
||||
}
|
||||
|
||||
const removed = settings.radarr.splice(radarrIndex, 1);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Router } from 'express';
|
||||
import SonarrAPI from '../../api/sonarr';
|
||||
import SonarrAPI from '../../api/servarr/sonarr';
|
||||
import { getSettings, SonarrSettings } from '../../lib/settings';
|
||||
import logger from '../../logger';
|
||||
|
||||
@@ -39,12 +39,13 @@ sonarrRoutes.post('/test', async (req, res, next) => {
|
||||
try {
|
||||
const sonarr = new SonarrAPI({
|
||||
apiKey: req.body.apiKey,
|
||||
url: SonarrAPI.buildSonarrUrl(req.body, '/api/v3'),
|
||||
url: SonarrAPI.buildUrl(req.body, '/api/v3'),
|
||||
});
|
||||
|
||||
const profiles = await sonarr.getProfiles();
|
||||
const folders = await sonarr.getRootFolders();
|
||||
const languageProfiles = await sonarr.getLanguageProfiles();
|
||||
const tags = await sonarr.getTags();
|
||||
|
||||
return res.status(200).json({
|
||||
profiles,
|
||||
@@ -53,6 +54,7 @@ sonarrRoutes.post('/test', async (req, res, next) => {
|
||||
path: folder.path,
|
||||
})),
|
||||
languageProfiles,
|
||||
tags,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error('Failed to test Sonarr', {
|
||||
|
||||
Reference in New Issue
Block a user