fix: resolve multi-instance capability in proxy routes (fixes #33)

This commit is contained in:
2026-05-22 22:31:47 +01:00
parent 12c44a611e
commit 2e9fe8e049
3 changed files with 130 additions and 40 deletions
+54 -18
View File
@@ -43,9 +43,13 @@ router.use(requireAuth);
// Get queue
router.get('/queue', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.get(`${process.env.RADARR_URL}/api/v3/queue`, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/queue`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -79,9 +83,13 @@ router.get('/queue', async (req, res) => {
*/
// Get history
router.get('/history', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.get(`${process.env.RADARR_URL}/api/v3/history`, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY },
const response = await axios.get(`${instance.url}/api/v3/history`, {
headers: { 'X-Api-Key': instance.apiKey },
params: { pageSize: req.query.pageSize || 50 }
});
res.json(response.data);
@@ -92,9 +100,13 @@ router.get('/history', async (req, res) => {
// Get movie details
router.get('/movies/:id', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.get(`${process.env.RADARR_URL}/api/v3/movie/${req.params.id}`, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/movie/${req.params.id}`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -104,9 +116,13 @@ router.get('/movies/:id', async (req, res) => {
// Get all movies with tags
router.get('/movies', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.get(`${process.env.RADARR_URL}/api/v3/movie`, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/movie`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -164,9 +180,13 @@ router.get('/notifications', async (req, res) => {
// GET /api/radarr/notifications/:id - get specific notification
router.get('/notifications/:id', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.get(`${process.env.RADARR_URL}/api/v3/notification/${req.params.id}`, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/notification/${req.params.id}`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -176,9 +196,13 @@ router.get('/notifications/:id', async (req, res) => {
// POST /api/radarr/notifications - create notification
router.post('/notifications', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.post(`${process.env.RADARR_URL}/api/v3/notification`, req.body, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY }
const response = await axios.post(`${instance.url}/api/v3/notification`, req.body, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -188,9 +212,13 @@ router.post('/notifications', async (req, res) => {
// PUT /api/radarr/notifications/:id - update notification
router.put('/notifications/:id', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.put(`${process.env.RADARR_URL}/api/v3/notification/${req.params.id}`, req.body, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY }
const response = await axios.put(`${instance.url}/api/v3/notification/${req.params.id}`, req.body, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -200,9 +228,13 @@ router.put('/notifications/:id', async (req, res) => {
// DELETE /api/radarr/notifications/:id - delete notification
router.delete('/notifications/:id', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.delete(`${process.env.RADARR_URL}/api/v3/notification/${req.params.id}`, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY }
const response = await axios.delete(`${instance.url}/api/v3/notification/${req.params.id}`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -233,9 +265,13 @@ router.post('/notifications/test', async (req, res) => {
// GET /api/radarr/notifications/schema - get notification schema
router.get('/notifications/schema', async (req, res) => {
const instance = getFirstRadarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Radarr not configured' });
}
try {
const response = await axios.get(`${process.env.RADARR_URL}/api/v3/notification/schema`, {
headers: { 'X-Api-Key': process.env.RADARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/notification/schema`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
+22 -4
View File
@@ -4,6 +4,16 @@ const axios = require('axios');
const router = express.Router();
const requireAuth = require('../middleware/requireAuth');
const sanitizeError = require('../utils/sanitizeError');
const { getSABnzbdInstances } = require('../utils/config');
// Helper to get first SABnzbd instance
function getFirstSABnzbdInstance() {
const instances = getSABnzbdInstances();
if (!instances || instances.length === 0) {
return null;
}
return instances[0];
}
/**
* @openapi
@@ -32,11 +42,15 @@ router.use(requireAuth);
// GET /api/sabnzbd/queue
router.get('/queue', async (req, res) => {
const instance = getFirstSABnzbdInstance();
if (!instance) {
return res.status(503).json({ error: 'SABnzbd not configured' });
}
try {
const response = await axios.get(`${process.env.SABNZBD_URL}/api`, {
const response = await axios.get(`${instance.url}/api`, {
params: {
mode: 'queue',
apikey: process.env.SABNZBD_API_KEY,
apikey: instance.apiKey,
output: 'json'
}
});
@@ -72,11 +86,15 @@ router.get('/queue', async (req, res) => {
*/
// GET /api/sabnzbd/history
router.get('/history', async (req, res) => {
const instance = getFirstSABnzbdInstance();
if (!instance) {
return res.status(503).json({ error: 'SABnzbd not configured' });
}
try {
const response = await axios.get(`${process.env.SABNZBD_URL}/api`, {
const response = await axios.get(`${instance.url}/api`, {
params: {
mode: 'history',
apikey: process.env.SABNZBD_API_KEY,
apikey: instance.apiKey,
output: 'json',
limit: req.query.limit || 50
}
+54 -18
View File
@@ -43,9 +43,13 @@ router.use(requireAuth);
// Get queue
router.get('/queue', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.get(`${process.env.SONARR_URL}/api/v3/queue`, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/queue`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -79,9 +83,13 @@ router.get('/queue', async (req, res) => {
*/
// Get history
router.get('/history', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.get(`${process.env.SONARR_URL}/api/v3/history`, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY },
const response = await axios.get(`${instance.url}/api/v3/history`, {
headers: { 'X-Api-Key': instance.apiKey },
params: { pageSize: req.query.pageSize || 50 }
});
res.json(response.data);
@@ -92,9 +100,13 @@ router.get('/history', async (req, res) => {
// Get series details
router.get('/series/:id', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.get(`${process.env.SONARR_URL}/api/v3/series/${req.params.id}`, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/series/${req.params.id}`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -104,9 +116,13 @@ router.get('/series/:id', async (req, res) => {
// Get all series with tags
router.get('/series', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.get(`${process.env.SONARR_URL}/api/v3/series`, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/series`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -164,9 +180,13 @@ router.get('/notifications', async (req, res) => {
// GET /api/sonarr/notifications/:id - get specific notification
router.get('/notifications/:id', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.get(`${process.env.SONARR_URL}/api/v3/notification/${req.params.id}`, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/notification/${req.params.id}`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -176,9 +196,13 @@ router.get('/notifications/:id', async (req, res) => {
// POST /api/sonarr/notifications - create notification
router.post('/notifications', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.post(`${process.env.SONARR_URL}/api/v3/notification`, req.body, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY }
const response = await axios.post(`${instance.url}/api/v3/notification`, req.body, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -188,9 +212,13 @@ router.post('/notifications', async (req, res) => {
// PUT /api/sonarr/notifications/:id - update notification
router.put('/notifications/:id', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.put(`${process.env.SONARR_URL}/api/v3/notification/${req.params.id}`, req.body, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY }
const response = await axios.put(`${instance.url}/api/v3/notification/${req.params.id}`, req.body, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -200,9 +228,13 @@ router.put('/notifications/:id', async (req, res) => {
// DELETE /api/sonarr/notifications/:id - delete notification
router.delete('/notifications/:id', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.delete(`${process.env.SONARR_URL}/api/v3/notification/${req.params.id}`, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY }
const response = await axios.delete(`${instance.url}/api/v3/notification/${req.params.id}`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {
@@ -233,9 +265,13 @@ router.post('/notifications/test', async (req, res) => {
// GET /api/sonarr/notifications/schema - get notification schema
router.get('/notifications/schema', async (req, res) => {
const instance = getFirstSonarrInstance();
if (!instance) {
return res.status(503).json({ error: 'Sonarr not configured' });
}
try {
const response = await axios.get(`${process.env.SONARR_URL}/api/v3/notification/schema`, {
headers: { 'X-Api-Key': process.env.SONARR_API_KEY }
const response = await axios.get(`${instance.url}/api/v3/notification/schema`, {
headers: { 'X-Api-Key': instance.apiKey }
});
res.json(response.data);
} catch (error) {