Files
dashcaddy/dashcaddy-api/routes/themes.js
Hermes 7bc2a207f3 DC-005: Fix all 138 broken test paths after src/ refactor
After the DC-005 module reorganization (41 files moved into src/ subdirs),
138 test suites failed because the refactor script's path-rewrite logic
missed three categories:

1. Files inside src/ doing 'require("./src/...")' — should be 'require("../...")'
2. Files in src/X/Y/ doing 'require("../../../src/...")' — should be 'require("../../...")'
3. Test files in __tests__/ with leftover 'require("../../../src/...")' paths

Root cause: the original refactor script ran before all files were moved,
so it computed relative paths against stale filesystem state.

Result:
- 30/30 test suites pass
- 879/879 tests pass (was: 18/30 suites, 614/687 tests)

Also fixed:
- routes/apps/restore.js: wrong responses import path
- routes/*/*.js: '../../src/utilities/X' → '../src/utilities/X' (depth 2 routes)
2026-06-13 12:16:56 -07:00

82 lines
2.6 KiB
JavaScript

const express = require('express');
const fs = require('fs');
const path = require('path');
const { success } = require('../src/utils/responses');
const { ValidationError, NotFoundError } = require('../src/utilities/errors');
const platformPaths = require('../platform-paths');
/**
* Themes routes factory
* @param {Object} deps - Explicit dependencies
* @param {Function} deps.asyncHandler - Async route handler wrapper
* @param {Object} deps.log - Logger instance
* @returns {express.Router}
*/
module.exports = function({ asyncHandler, log }) {
const router = express.Router();
const THEMES_DIR = process.env.THEMES_DIR || path.join(path.dirname(platformPaths.servicesFile), 'themes');
// Ensure themes directory exists
if (!fs.existsSync(THEMES_DIR)) {
fs.mkdirSync(THEMES_DIR, { recursive: true });
}
function readAllThemes() {
const themes = {};
try {
const files = fs.readdirSync(THEMES_DIR).filter(f => f.endsWith('.json'));
for (const file of files) {
const slug = path.basename(file, '.json');
const data = JSON.parse(fs.readFileSync(path.join(THEMES_DIR, file), 'utf8'));
themes[slug] = data;
}
} catch (e) {
log.error('themes', 'Failed to read themes', { error: e.message });
}
return themes;
}
// Get all user themes
router.get('/themes', (req, res) => {
success(res, { themes: readAllThemes() });
});
// Save a theme (create or update)
router.post('/themes/:slug', asyncHandler(async (req, res) => {
const { slug } = req.params;
const { name, colors, lightBg } = req.body;
if (!slug || !name || !colors) {
throw new ValidationError('Missing slug, name, or colors');
}
if (!/^[a-z0-9-]+$/.test(slug)) {
throw new ValidationError('Invalid slug format (use lowercase letters, numbers, and hyphens only)', 'slug');
}
const themeData = { name, ...colors };
if (lightBg) themeData.lightBg = true;
fs.writeFileSync(path.join(THEMES_DIR, slug + '.json'), JSON.stringify(themeData, null, 2), 'utf8');
success(res, { message: name + ' theme saved' });
}));
// Delete a theme
router.delete('/themes/:slug', asyncHandler(async (req, res) => {
const { slug } = req.params;
const filePath = path.join(THEMES_DIR, slug + '.json');
if (!fs.existsSync(filePath)) {
throw new NotFoundError(`Theme ${slug}`);
}
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
const name = data.name || slug;
fs.unlinkSync(filePath);
success(res, { message: name + ' theme deleted' });
}));
return router;
};