feat static pages system with dynamic footer and safe html rendering
This commit is contained in:
158
src/app/core/config/static-page-resolver.service.ts
Normal file
158
src/app/core/config/static-page-resolver.service.ts
Normal 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}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user