feat(fp): WP-23 — org-template backend + admin role

Second template axis (org identity: letterhead, footer, signature,
margins) server-side: OrgTemplateStore with JSON version history,
publish/rollback, sent-brief version pinning, admin role + capability,
5 admin endpoints, org-logo upload category. FE seam widened only
(Role/Capability unions, interceptor); WP-24/26 consume it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-05 11:17:05 +02:00
co-authored by Claude Fable 5
parent 44eb2d2186
commit 5a610c10f0
31 changed files with 5017 additions and 1834 deletions
@@ -2,11 +2,15 @@ import { HttpInterceptorFn } from '@angular/common/http';
import { currentRole } from './role';
/**
* Dev-only: stamps brief requests with the current `?role=` as an `X-Role` header so
* the backend can enforce the drafter/approver rules. Only brief endpoints carry it;
* everything else is untouched.
* Dev-only: stamps role-aware requests with the current `?role=` as an `X-Role`
* header so the backend can enforce the drafter/approver/admin rules. Only the
* brief, org-template and /me endpoints carry it (WP-23 widened the set — /me must
* see the role or `AccessStore` could never learn a capability); everything else
* is untouched.
*/
const ROLE_AWARE = ['/api/v1/brief', '/api/v1/admin/org-template', '/api/v1/me'];
export const roleInterceptor: HttpInterceptorFn = (req, next) => {
if (!req.url.includes('/api/v1/brief')) return next(req);
if (!ROLE_AWARE.some((prefix) => req.url.includes(prefix))) return next(req);
return next(req.clone({ setHeaders: { 'X-Role': currentRole() } }));
};