Refactor arr routes: explicit dependency injection

- Updated all arr route modules to use destructured dependencies
- Added JSDoc comments for factory functions
- Replaced ctx. references with direct parameter access
- Updated arr/index.js to extract and pass explicit dependencies
- Maintained backward compatibility with context pattern
- All files pass syntax validation

Files refactored:
- routes/arr/detect.js
- routes/arr/credentials.js
- routes/arr/config.js (579 lines)
- routes/arr/smart-connect.js
- routes/arr/plex.js
- routes/arr/helpers.js
- routes/arr/index.js (orchestrator)
This commit is contained in:
Krystie
2026-03-29 21:30:20 -07:00
parent ac23b2e093
commit 6bde2eb62e
7 changed files with 217 additions and 132 deletions

View File

@@ -1,12 +1,23 @@
const express = require('express');
const { APP_PORTS, ARR_SERVICES } = require('../../constants');
module.exports = function(ctx, helpers) {
/**
* Arr service detection routes factory
* @param {Object} deps - Explicit dependencies
* @param {Object} deps.docker - Docker client wrapper
* @param {Object} deps.servicesStateManager - Services state manager
* @param {Object} deps.credentialManager - Credential manager
* @param {Function} deps.fetchT - Timeout-wrapped fetch
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Object} deps.helpers - Arr helpers module
* @returns {express.Router}
*/
module.exports = function({ docker, servicesStateManager, credentialManager, fetchT, asyncHandler, helpers }) {
const router = express.Router();
// Detect running arr services and their configurations
router.get('/arr/detect', ctx.asyncHandler(async (req, res) => {
const containers = await ctx.docker.client.listContainers({ all: false });
router.get('/arr/detect', asyncHandler(async (req, res) => {
const containers = await docker.client.listContainers({ all: false });
const detected = {
plex: null,
radarr: null,
@@ -64,14 +75,14 @@ module.exports = function(ctx, helpers) {
}, 'arr-detect'));
// Smart Detect: Unified discovery of all arr services
router.get('/arr/smart-detect', ctx.asyncHandler(async (req, res) => {
router.get('/arr/smart-detect', asyncHandler(async (req, res) => {
const serviceList = ['plex', 'radarr', 'sonarr', 'prowlarr', 'seerr'];
const defaultPorts = APP_PORTS;
const result = {};
// 1. Scan Docker containers
let containers = [];
try { containers = await ctx.docker.client.listContainers({ all: false }); } catch (e) { /* Docker not available */ }
try { containers = await docker.client.listContainers({ all: false }); } catch (e) { /* Docker not available */ }
const servicePatterns = ARR_SERVICES;
@@ -95,18 +106,18 @@ module.exports = function(ctx, helpers) {
// 2. Load services.json for external entries
let storedServices = [];
try {
const data = await ctx.servicesStateManager.read();
const data = await servicesStateManager.read();
storedServices = Array.isArray(data) ? data : data.services || [];
} catch (e) { /* ignore */ }
// 3. Load stored credentials
const storedCreds = {};
const seedboxBaseUrl = await ctx.credentialManager.retrieve('arr.seedbox.baseurl');
const seedboxBaseUrl = await credentialManager.retrieve('arr.seedbox.baseurl');
for (const svc of serviceList) {
const credKey = svc === 'plex' ? 'arr.plex.token' : `arr.${svc}.apikey`;
const apiKey = await ctx.credentialManager.retrieve(credKey);
const metadata = await ctx.credentialManager.getMetadata(credKey);
const apiKey = await credentialManager.retrieve(credKey);
const metadata = await credentialManager.getMetadata(credKey);
if (apiKey) {
storedCreds[svc] = { apiKey, metadata };
}
@@ -141,7 +152,7 @@ module.exports = function(ctx, helpers) {
entry.hasToken = true;
entry.status = 'connected';
// Store for later use
await ctx.credentialManager.store('arr.plex.token', token, {
await credentialManager.store('arr.plex.token', token, {
service: 'plex', source: 'local', url: entry.url,
lastVerified: new Date().toISOString()
});
@@ -158,7 +169,7 @@ module.exports = function(ctx, helpers) {
entry.hasApiKey = true;
const configuredServices = { radarr: false, sonarr: false, plex: false };
try {
const radarrCheck = await ctx.fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/radarr`, {
const radarrCheck = await fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/radarr`, {
headers: { 'Cookie': session.cookie },
signal: AbortSignal.timeout(5000)
});
@@ -168,7 +179,7 @@ module.exports = function(ctx, helpers) {
}
} catch (e) { /* ignore */ }
try {
const sonarrCheck = await ctx.fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/sonarr`, {
const sonarrCheck = await fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/sonarr`, {
headers: { 'Cookie': session.cookie },
signal: AbortSignal.timeout(5000)
});
@@ -178,7 +189,7 @@ module.exports = function(ctx, helpers) {
}
} catch (e) { /* ignore */ }
try {
const plexCheck = await ctx.fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/plex`, {
const plexCheck = await fetchT(`http://host.docker.internal:${dc.port}/api/v1/settings/plex`, {
headers: { 'Cookie': session.cookie },
signal: AbortSignal.timeout(5000)
});