Files
dashcaddy/dashcaddy-api/routes/arr/plex.js
T
Hermes c39c80b3ad
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled
Fix DC-005 depth-2 route path bugs: 67 broken requires across 21 files
The DC-005 src/ refactor left depth-2 route files (routes/auth/*,
routes/recipes/*, routes/apps/*, routes/arr/*, routes/config/*) with
broken require() paths. A filesystem-resolving scanner found 67 broken
requires across 21 files — three distinct bug classes:

  A) '../../../src/...' (3 levels up, above package root) — Bug 7, ~49 occurrences
  B) '../src/utils/...' (1 level up, resolves to nonexistent routes/src/) — ~15 occurrences
  C) routes/apps/restore.js:5 used utilities/responses (wrong dir) — should be utils/responses

All fixed to '../../src/...' (or '../../src/utils/responses' for class C).
routes/auth/totp.js was already fixed in the DC-006 commit.

Post-fix: 922/922 tests pass, zero new ESLint warnings. No logic changes —
purely mechanical require() path corrections.
2026-06-25 16:55:06 -07:00

95 lines
3.2 KiB
JavaScript

const express = require('express');
const { APP_PORTS } = require('../../src/utilities/constants');
const { ok } = require('../../src/utils/responses');
/**
* Plex routes factory
* @param {Object} deps - Explicit dependencies
* @param {Function} deps.fetchT - Timeout-wrapped fetch
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Function} deps.errorResponse - Error response helper
* @param {Object} deps.log - Logger instance
* @param {Object} deps.helpers - Arr helpers module
* @param {Object} deps.credentialManager - Credential manager
* @param {Object} deps.servicesStateManager - Services state manager
* @returns {express.Router}
*/
module.exports = function({ fetchT, asyncHandler, errorResponse, log: _log, helpers, credentialManager, servicesStateManager }) {
const router = express.Router();
const ctx = {
credentialManager,
servicesStateManager
};
// Plex Libraries endpoint
router.get('/plex/libraries', asyncHandler(async (req, res) => {
// Get Plex token
let plexToken = await helpers.getPlexToken('plex');
if (!plexToken) {
plexToken = await ctx.credentialManager.retrieve('arr.plex.token');
}
if (!plexToken) {
return errorResponse(res, 400, 'No Plex token available. Claim your Plex server first.', {
hint: 'Deploy Plex with a claim token or manually configure it.'
});
}
// Get Plex URL
let plexUrl = `http://localhost:${APP_PORTS.plex}`;
try {
const services = await ctx.servicesStateManager.read();
const svcList = Array.isArray(services) ? services : services.services || [];
const plexService = svcList.find(s => s.id === 'plex' || s.appTemplate === 'plex');
if (plexService?.url) {
plexUrl = plexService.url.replace('host.docker.internal', 'localhost');
}
} catch (e) { /* use default */ }
// Fetch libraries
const libRes = await fetchT(`${plexUrl}/library/sections`, {
headers: { 'X-Plex-Token': plexToken, 'Accept': 'application/json' },
signal: AbortSignal.timeout(10000)
});
if (!libRes.ok) {
return errorResponse(res, 502, `Plex returned ${libRes.status}`);
}
const data = await libRes.json();
const libraries = (data.MediaContainer?.Directory || []).map(dir => ({
key: dir.key,
title: dir.title,
type: dir.type,
count: parseInt(dir.count) || 0,
scannedAt: dir.scannedAt
}));
// Get server name
let serverName = 'Plex';
let version = null;
try {
const identityRes = await fetchT(`${plexUrl}/identity`, {
headers: { 'X-Plex-Token': plexToken, 'Accept': 'application/json' },
signal: AbortSignal.timeout(5000)
});
if (identityRes.ok) {
const identity = await identityRes.json();
serverName = identity.MediaContainer?.friendlyName || 'Plex';
version = identity.MediaContainer?.version;
}
} catch (e) { /* use default */ }
// Store token for future use
await ctx.credentialManager.store('arr.plex.token', plexToken, {
service: 'plex', source: 'local', url: plexUrl,
lastVerified: new Date().toISOString()
});
ok(res, { serverName, version, libraries });
}, 'plex-libraries'));
return router;
};