stage 1
This commit is contained in:
@@ -2,37 +2,100 @@ import { Router } from 'express';
|
||||
|
||||
import {
|
||||
getActiveTemplate,
|
||||
getAllActiveTemplates,
|
||||
getTemplateVersion,
|
||||
listTemplates
|
||||
listTemplates,
|
||||
listTemplateVersions,
|
||||
publishTemplateVersion
|
||||
} from '../services/templateService.js';
|
||||
import { logAuditEvent } from '../services/auditService.js';
|
||||
import { asyncHandler } from '../utils/asyncHandler.js';
|
||||
import { validateParam, validateNumericParam } from '../middleware/validateParams.js';
|
||||
import { templateCache } from '../services/cacheService.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
asyncHandler(async (_req, res) => {
|
||||
asyncHandler(async (req, res) => {
|
||||
/*
|
||||
* When ?include=definitions is set the response embeds the full JSON
|
||||
* definition for every active template. This eliminates the N+1 round-trip
|
||||
* the old client performed (list → fetch each) and makes initial sync a
|
||||
* single request. Without the flag the response stays lightweight.
|
||||
*/
|
||||
const includeDefinitions = req.query.include === 'definitions';
|
||||
const cacheKey = `templates-list-${includeDefinitions}`;
|
||||
const cached = templateCache.get(cacheKey);
|
||||
|
||||
if (cached) {
|
||||
return res.json(cached);
|
||||
}
|
||||
|
||||
if (includeDefinitions) {
|
||||
const templates = await getAllActiveTemplates();
|
||||
const payload = { items: templates };
|
||||
templateCache.set(cacheKey, payload);
|
||||
return res.json(payload);
|
||||
}
|
||||
|
||||
const templates = await listTemplates();
|
||||
res.json({ items: templates });
|
||||
const payload = { items: templates };
|
||||
templateCache.set(cacheKey, payload);
|
||||
return res.json(payload);
|
||||
})
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:templateCode',
|
||||
validateParam('templateCode'),
|
||||
asyncHandler(async (req, res) => {
|
||||
/*
|
||||
* New reports always use the latest active template, so the primary route is
|
||||
* optimized for that case. Older versions remain accessible through the
|
||||
* versioned route so existing drafts can stay bound to the original schema.
|
||||
*/
|
||||
const cacheKey = `template-active-${req.params.templateCode}`;
|
||||
const cached = templateCache.get(cacheKey);
|
||||
|
||||
if (cached) {
|
||||
return res.json(cached);
|
||||
}
|
||||
|
||||
const template = await getActiveTemplate(req.params.templateCode);
|
||||
|
||||
if (!template) {
|
||||
return res.status(404).json({ message: 'Template not found.' });
|
||||
}
|
||||
|
||||
templateCache.set(cacheKey, template);
|
||||
return res.json(template);
|
||||
})
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:templateCode/versions/:versionNumber',
|
||||
'/:templateCode/versions',
|
||||
validateParam('templateCode'),
|
||||
asyncHandler(async (req, res) => {
|
||||
/*
|
||||
* Version listing lets the admin workspace display a template's publication
|
||||
* history and choose which version to activate or review.
|
||||
*/
|
||||
const versions = await listTemplateVersions(req.params.templateCode);
|
||||
return res.json({ items: versions });
|
||||
})
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:templateCode/versions/:versionNumber',
|
||||
validateParam('templateCode'),
|
||||
validateNumericParam('versionNumber'),
|
||||
asyncHandler(async (req, res) => {
|
||||
/*
|
||||
* Version-specific access is what allows the frontend to reopen old drafts
|
||||
* safely even after templates evolve. Without this route, cached reports
|
||||
* would eventually drift away from the structure they were created against.
|
||||
*/
|
||||
const template = await getTemplateVersion(
|
||||
req.params.templateCode,
|
||||
req.params.versionNumber
|
||||
@@ -46,4 +109,36 @@ router.get(
|
||||
})
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/:templateCode/versions/:versionNumber/publish',
|
||||
validateParam('templateCode'),
|
||||
validateNumericParam('versionNumber'),
|
||||
asyncHandler(async (req, res) => {
|
||||
/*
|
||||
* Publishing a version marks it active and retires the previously active
|
||||
* version for the same template. This lets the admin promote a draft version
|
||||
* to production. Existing reports keep their bound version unchanged.
|
||||
*/
|
||||
const result = await publishTemplateVersion(
|
||||
req.params.templateCode,
|
||||
Number(req.params.versionNumber)
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
return res.status(404).json({ message: 'Template version not found.' });
|
||||
}
|
||||
|
||||
templateCache.clear();
|
||||
|
||||
await logAuditEvent({
|
||||
entityType: 'template_version',
|
||||
entityCode: `${req.params.templateCode}::v${req.params.versionNumber}`,
|
||||
action: 'publish',
|
||||
newValue: { templateCode: req.params.templateCode, version: Number(req.params.versionNumber) }
|
||||
});
|
||||
|
||||
return res.json(result);
|
||||
})
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user