Faithful port of AtomCMS's "atom" theme look (light, Habbo-retro, golden #eeb425 / amber #f59e0b accents, white cards, 12px radii, Nunito-first stack): - globals.css: full token set + components (.site-header/.nav-item, .btn-*, .card, .hero, currency pills, article cards, inputs, tables, footer). - SiteHeader (brand + nav + currency pills + auth box, staff-aware) + SiteFooter; layout wraps header/content/footer. Removed the bare SiteNav. - Restyled home (hero + article cards), login (centered card). - siteSettings now falls back to DEFAULTS on missing key OR DB error, so pages render without a DB; DATABASE_CONNECT_TIMEOUT_MS env + pool acquireTimeout make the no-DB fallback fast. Verified in a real browser (computed styles): header white/sticky, golden logo gradient, uppercase nav, amber primary button @12px radius, golden outline, hero gradient — all correct. tsc exit 0, vitest 48/48, next build exit 0.
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import { prisma } from "@/lib/prisma";
|
|
|
|
// Fallback values used when a setting row is missing OR the DB is unreachable,
|
|
// so the app still renders (e.g. local dev without a DATABASE_URL).
|
|
const DEFAULTS: Record<string, string> = {
|
|
hotel_name: "Atom",
|
|
habbo_imaging_url: "https://www.habbo.com/habbo-imaging/avatarimage",
|
|
logo_url: "",
|
|
nitro_client_url: "",
|
|
};
|
|
|
|
/**
|
|
* DB-driven CMS config, mirroring AtomCMS's `setting()` / habbo-next's
|
|
* `siteSettings`. Loads the whole website_settings table into a key→value map,
|
|
* cached in-process, with graceful fallback to DEFAULTS. Booleans are stored as
|
|
* the strings '1' / '0'.
|
|
*/
|
|
class SiteSettings {
|
|
private cache: Map<string, string> | null = null;
|
|
|
|
private async load(): Promise<Map<string, string>> {
|
|
if (this.cache === null) {
|
|
try {
|
|
const rows = await prisma.websiteSetting.findMany({
|
|
select: { key: true, value: true },
|
|
});
|
|
this.cache = new Map(rows.map((r) => [r.key, r.value]));
|
|
} catch {
|
|
// DB unavailable — serve defaults without caching so we retry later.
|
|
return new Map(Object.entries(DEFAULTS));
|
|
}
|
|
}
|
|
return this.cache;
|
|
}
|
|
|
|
async get(key: string, fallback: string | null = null): Promise<string | null> {
|
|
const map = await this.load();
|
|
if (map.has(key)) return map.get(key) as string;
|
|
if (key in DEFAULTS) return DEFAULTS[key] as string;
|
|
return fallback;
|
|
}
|
|
|
|
async getBool(key: string, fallback = false): Promise<boolean> {
|
|
const v = await this.get(key, null);
|
|
if (v === null) return fallback;
|
|
const s = v.toLowerCase();
|
|
return s === "1" || s === "true";
|
|
}
|
|
|
|
/** Invalidate the in-process cache after a settings write. */
|
|
reload(): void {
|
|
this.cache = null;
|
|
}
|
|
}
|
|
|
|
export const siteSettings = new SiteSettings();
|