104 lines
3.9 KiB
JavaScript
104 lines
3.9 KiB
JavaScript
/**
|
|
* Network resources routes — DC-144.
|
|
*
|
|
* CRUD for Private Network resources (spec §2): apps or devices reachable
|
|
* by name (`plex.internal`, `nas.internal`) — the dst side of the access
|
|
* map (DC-145) and the compiled ACLs (DC-148).
|
|
*
|
|
* GET /network/resources → list
|
|
* GET /network/resources/:name → one resource
|
|
* POST /network/resources → create (name slug, port
|
|
* 1-65535, host non-empty)
|
|
* PATCH /network/resources/:name → update (name immutable)
|
|
* DELETE /network/resources/:name → delete
|
|
*
|
|
* `sensitive` resources additionally require DashCaddy login + TOTP at
|
|
* the forward_auth layer (spec §4 step-up); here it just round-trips.
|
|
*
|
|
* Mounted on the authed apiRouter — auth middleware is the caller's job
|
|
* (same as every other route module in this repo). Factory pattern.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const { ValidationError, NotFoundError, ConflictError } = require('../src/utilities/errors');
|
|
const { ok, created } = require('../src/utils/responses');
|
|
const { createResourcesStore, ResourceError } = require('../src/security/network-resources');
|
|
|
|
module.exports = function networkResourcesRoutesFactory({
|
|
resourcesStore,
|
|
asyncHandler,
|
|
log = { info() {}, warn() {}, error() {} },
|
|
} = {}) {
|
|
const router = require('express').Router(); // eslint-disable-line global-require
|
|
|
|
const store = resourcesStore || createResourcesStore({ log });
|
|
if (!store || typeof store.listResources !== 'function') {
|
|
if (process.env.NODE_ENV === 'test') {
|
|
log.warn && log.warn('network-resources', 'resourcesStore missing — routes returning 404 in this environment');
|
|
router.all('*', (_req, res) => res.status(404).json({ success: false, error: '[DC-144] resources unavailable' }));
|
|
return router;
|
|
}
|
|
throw new Error('networkResourcesRoutes requires resourcesStore');
|
|
}
|
|
if (!asyncHandler) {
|
|
// Must always be wired in prod (see share.js); test fallback only.
|
|
if (process.env.NODE_ENV !== 'test') {
|
|
throw new Error('networkResourcesRoutes requires asyncHandler');
|
|
}
|
|
asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next); // eslint-disable-line no-param-reassign
|
|
}
|
|
|
|
// Map ResourceError (statusCode-carrying, operational) onto the repo's
|
|
// error classes so the shared error handler renders the right status.
|
|
function _map(err) {
|
|
if (err instanceof ResourceError) {
|
|
if (err.statusCode === 404) return new NotFoundError('resource');
|
|
if (err.statusCode === 409) return new ConflictError(err.message);
|
|
return new ValidationError(err.message, 'resource');
|
|
}
|
|
return err;
|
|
}
|
|
|
|
router.get('/', asyncHandler(async (req, res) => {
|
|
const resources = await store.listResources();
|
|
ok(res, { data: resources, count: resources.length });
|
|
}, 'network-resources-list'));
|
|
|
|
router.get('/:name', asyncHandler(async (req, res) => {
|
|
const resource = await store.getResource(req.params.name);
|
|
if (!resource) throw new NotFoundError('resource');
|
|
ok(res, { resource });
|
|
}, 'network-resources-get'));
|
|
|
|
router.post('/', asyncHandler(async (req, res) => {
|
|
try {
|
|
const resource = await store.createResource(req.body || {});
|
|
log.info && log.info('network-resources', 'resource created via API', { name: resource.name });
|
|
created(res, { resource });
|
|
} catch (err) {
|
|
throw _map(err);
|
|
}
|
|
}, 'network-resources-create'));
|
|
|
|
router.patch('/:name', asyncHandler(async (req, res) => {
|
|
try {
|
|
const resource = await store.updateResource(req.params.name, req.body || {});
|
|
ok(res, { resource });
|
|
} catch (err) {
|
|
throw _map(err);
|
|
}
|
|
}, 'network-resources-update'));
|
|
|
|
router.delete('/:name', asyncHandler(async (req, res) => {
|
|
try {
|
|
const result = await store.deleteResource(req.params.name);
|
|
ok(res, result);
|
|
} catch (err) {
|
|
throw _map(err);
|
|
}
|
|
}, 'network-resources-delete'));
|
|
|
|
return router;
|
|
};
|