feat static pages system with dynamic footer and safe html rendering

This commit is contained in:
sdarbinyan
2026-07-05 03:37:35 +04:00
parent 0089790d41
commit b0c5c5e051
11 changed files with 566 additions and 10 deletions

View File

@@ -0,0 +1,158 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ConfigService } from './config.service';
import { BootstrapConfig, LegacyStaticPageConfig, LocalizedHtmlContent, LocalizedTextContent, ResolvedStaticPage, StaticPageConfig } from '../../shared/models/config';
import { LanguageService } from '../../services/language.service';
@Injectable({ providedIn: 'root' })
export class StaticPageResolverService {
constructor(
private readonly configService: ConfigService,
private readonly languageService: LanguageService
) {}
resolveByKey(key: string, lang?: string): Observable<ResolvedStaticPage | null> {
return this.configService.loadBootstrap().pipe(
map(bootstrap => this.resolveByKeyFromBootstrap(bootstrap, key, lang ?? this.languageService.currentLanguage()))
);
}
resolveByRoute(route: string, lang?: string): Observable<ResolvedStaticPage | null> {
return this.configService.loadBootstrap().pipe(
map(bootstrap => this.resolveByRouteFromBootstrap(bootstrap, route, lang ?? this.languageService.currentLanguage()))
);
}
resolveByKeyFromBootstrap(bootstrap: BootstrapConfig, key: string, lang: string): ResolvedStaticPage | null {
const page = this.getPageByKey(bootstrap, key);
if (!page) {
return null;
}
const route = this.normalizeRoute(page.route || `/page/${key}`);
const title = this.resolveLocalizedText(page.title, lang, key);
const html = this.resolveLocalizedHtml(page.content, lang);
return {
key,
route,
title,
html
};
}
resolveByRouteFromBootstrap(bootstrap: BootstrapConfig, route: string, lang: string): ResolvedStaticPage | null {
const normalized = this.normalizeRoute(route);
const registry = this.getRegistry(bootstrap);
for (const [key, page] of Object.entries(registry)) {
const routePath = this.normalizeRoute(page.route || `/page/${key}`);
if (routePath === normalized) {
return {
key,
route: routePath,
title: this.resolveLocalizedText(page.title, lang, key),
html: this.resolveLocalizedHtml(page.content, lang)
};
}
}
return null;
}
resolveFooterTitle(groupTitle: string | LocalizedTextContent | undefined, lang: string): string {
if (!groupTitle) {
return '';
}
if (typeof groupTitle === 'string') {
return groupTitle;
}
return this.resolveLocalizedText(groupTitle, lang, '');
}
private getRegistry(bootstrap: BootstrapConfig): Record<string, StaticPageConfig> {
const raw = bootstrap.staticPages;
if (!raw) {
return {};
}
if (Array.isArray(raw)) {
return raw.reduce<Record<string, StaticPageConfig>>((acc, item) => {
const legacy = item as LegacyStaticPageConfig;
const key = legacy.key;
if (!key) {
return acc;
}
const route = typeof legacy.route === 'string' ? legacy.route : legacy.route?.path ?? `/page/${key}`;
let titleMap: LocalizedTextContent;
if (typeof legacy.title === 'string') {
titleMap = { en: legacy.title };
} else {
titleMap = legacy.title ?? { en: key };
}
let contentMap: LocalizedHtmlContent;
if (typeof legacy.content === 'string') {
contentMap = { en: legacy.content };
} else if (legacy.content && typeof legacy.content === 'object' && 'value' in legacy.content) {
contentMap = { en: legacy.content.value ?? '' };
} else {
contentMap = (legacy.content as LocalizedHtmlContent) ?? { en: '' };
}
acc[key] = {
route,
title: titleMap,
content: contentMap,
visible: legacy.visible
};
return acc;
}, {});
}
return raw;
}
private getPageByKey(bootstrap: BootstrapConfig, key: string): StaticPageConfig | null {
const registry = this.getRegistry(bootstrap);
const page = registry[key];
if (!page) {
return null;
}
if (page.visible === false) {
return null;
}
return page;
}
private resolveLocalizedText(text: LocalizedTextContent | undefined, lang: string, fallback: string): string {
if (!text) {
return fallback;
}
return text[lang] ?? text['en'] ?? Object.values(text)[0] ?? fallback;
}
private resolveLocalizedHtml(content: LocalizedHtmlContent | undefined, lang: string): string {
if (!content) {
return '';
}
return content[lang] ?? content['en'] ?? Object.values(content)[0] ?? '';
}
private normalizeRoute(route: string): string {
if (!route) {
return '/';
}
return route.startsWith('/') ? route : `/${route}`;
}
}