Files
HIPCTF2/frontend/src/app/core/services/bootstrap.service.ts
T
2026-07-21 15:32:48 +00:00

85 lines
2.5 KiB
TypeScript

import { Injectable, signal, computed } from '@angular/core';
export interface PasswordPolicyInfo {
minLength: number;
requireMixed: boolean;
description: string;
}
export interface BootstrapPayload {
initialized: boolean;
pageTitle: string;
logo: string;
welcomeMarkdown: string;
theme: any;
defaultChallengeIp: string;
registrationsEnabled: boolean;
passwordPolicy: PasswordPolicyInfo;
}
const DEFAULT_POLICY: PasswordPolicyInfo = {
minLength: 12,
requireMixed: true,
description: 'At least 12 characters with upper, lower, digit and symbol',
};
@Injectable({ providedIn: 'root' })
export class BootstrapService {
readonly payload = signal<BootstrapPayload | null>(null);
readonly initialized = signal<boolean>(false);
readonly passwordPolicy = computed<PasswordPolicyInfo>(
() => this.payload()?.passwordPolicy ?? DEFAULT_POLICY,
);
private loadPromise: Promise<BootstrapPayload | null> | null = null;
async load(): Promise<BootstrapPayload | null> {
if (!this.loadPromise) {
this.loadPromise = this._load();
}
return this.loadPromise;
}
private async _load(): Promise<BootstrapPayload | null> {
try {
const res = await fetch('/api/v1/bootstrap', { credentials: 'include' });
const data = (await res.json()) as BootstrapPayload;
this.payload.set(data);
this.initialized.set(data.initialized);
this.applyTheme(data.theme);
return data;
} catch (e) {
console.error('Bootstrap load failed', e);
return null;
}
}
ready(): Promise<BootstrapPayload | null> {
if (!this.loadPromise) {
this.loadPromise = this._load();
}
return this.loadPromise;
}
markInitialized(): void {
this.initialized.set(true);
}
private applyTheme(theme: any): void {
if (!theme?.tokens) return;
const root = document.documentElement.style;
const t = theme.tokens;
root.setProperty('--color-primary', t.primary);
root.setProperty('--color-secondary', t.secondary);
root.setProperty('--color-accent', t.accent);
root.setProperty('--color-surface', t.surface);
root.setProperty('--color-text', t.text);
root.setProperty('--color-success', t.success);
root.setProperty('--color-warning', t.warning);
root.setProperty('--color-danger', t.danger);
root.setProperty('--font-family', t.fontFamily);
root.setProperty('--radius-sm', t.radii.sm);
root.setProperty('--radius-md', t.radii.md);
root.setProperty('--radius-lg', t.radii.lg);
}
}