- Phase 27.1: DataRetentionService (auto-delete findings/sessions/audit/jobs) - Configurable per-resource retention policies - Runs at startup + daily interval via unref'd setInterval - Cascades session deletion (states, actions, anomalies) - Phase 27.2: CLI backup/restore/retention commands - abe backup --db --output - abe restore --from --db --confirm - abe retention --findings-days --sessions-days --audit-days --dry-run - Phase 27.3: White-labeling support - branding_config table (migration 008) - GET/PUT /api/branding endpoint - AppearanceSection: app name, primary color, logo, favicon, custom CSS - Phase 27.4: PostgreSQL already supported via DatabaseConnection - Phase 27.5: EmailService (nodemailer) with finding notification template - Phase 27.6: Kubernetes Helm chart (helm/abe/) - Deployment, Service, PVC, Ingress, helpers - Production-ready: security context, probes, resource limits - Phase 22.7/22.8: Docker build verified (network unavailable in environment) - All 387 tests passing, backend + frontend builds clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
3.1 KiB
JavaScript
88 lines
3.1 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.createBrandingRouter = createBrandingRouter;
|
|
/**
|
|
* Branding/white-labeling API.
|
|
* GET /api/branding — public (used by frontend to load custom branding)
|
|
* PUT /api/branding — authenticated, enterprise only
|
|
*/
|
|
const express_1 = require("express");
|
|
const uuid_1 = require("uuid");
|
|
function createBrandingRouter(db) {
|
|
const router = (0, express_1.Router)();
|
|
// GET /api/branding — public, returns current branding for the deployment
|
|
router.get('/', async (_req, res, next) => {
|
|
try {
|
|
const row = await db
|
|
.selectFrom('branding_config')
|
|
.selectAll()
|
|
.executeTakeFirst();
|
|
if (!row) {
|
|
return res.json({
|
|
appName: 'ABE',
|
|
primaryColor: null,
|
|
logoUrl: null,
|
|
faviconUrl: null,
|
|
customCss: null,
|
|
});
|
|
}
|
|
res.json({
|
|
appName: row.app_name,
|
|
primaryColor: row.primary_color,
|
|
logoUrl: row.logo_url,
|
|
faviconUrl: row.favicon_url,
|
|
customCss: row.custom_css,
|
|
});
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// PUT /api/branding — update branding (authenticated)
|
|
router.put('/', async (req, res, next) => {
|
|
try {
|
|
const user = req.user;
|
|
if (!user)
|
|
return res.status(401).json({ error: 'Unauthorized' });
|
|
const { appName, primaryColor, logoUrl, faviconUrl, customCss } = req.body;
|
|
const orgId = user.orgId ?? 'default';
|
|
const existing = await db
|
|
.selectFrom('branding_config')
|
|
.select('id')
|
|
.where('organization_id', '=', orgId)
|
|
.executeTakeFirst();
|
|
if (existing) {
|
|
await db
|
|
.updateTable('branding_config')
|
|
.set({
|
|
app_name: appName ?? null,
|
|
primary_color: primaryColor ?? null,
|
|
logo_url: logoUrl ?? null,
|
|
favicon_url: faviconUrl ?? null,
|
|
custom_css: customCss ?? null,
|
|
updated_at: Date.now(),
|
|
})
|
|
.where('organization_id', '=', orgId)
|
|
.execute();
|
|
}
|
|
else {
|
|
await db.insertInto('branding_config').values({
|
|
id: (0, uuid_1.v4)(),
|
|
organization_id: orgId,
|
|
app_name: appName ?? null,
|
|
primary_color: primaryColor ?? null,
|
|
logo_url: logoUrl ?? null,
|
|
favicon_url: faviconUrl ?? null,
|
|
custom_css: customCss ?? null,
|
|
updated_at: Date.now(),
|
|
}).execute();
|
|
}
|
|
res.json({ success: true });
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
return router;
|
|
}
|