85 lines
2.5 KiB
TypeScript
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);
|
|
}
|
|
} |