feat: add org_config loader with caching and defaults
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
import { db } from './db';
|
||||
import { orgConfig } from './schema';
|
||||
|
||||
export interface OrgBranding {
|
||||
primaryColor: string;
|
||||
primaryColorLight: string;
|
||||
primaryColorDark: string;
|
||||
accentColor: string;
|
||||
accentColorLight: string;
|
||||
accentColorDark: string;
|
||||
logoUrl: string;
|
||||
}
|
||||
|
||||
export interface OrgInfo {
|
||||
name: string;
|
||||
tagline: string;
|
||||
city: string;
|
||||
state: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
facebookUrl: string;
|
||||
instagramUrl: string;
|
||||
}
|
||||
|
||||
export interface OrgLocale {
|
||||
language: string;
|
||||
currency: string;
|
||||
dateFormat: string;
|
||||
}
|
||||
|
||||
export interface OrgApplications {
|
||||
adoptionEnabled: boolean;
|
||||
fosterEnabled: boolean;
|
||||
volunteerEnabled: boolean;
|
||||
surrenderEnabled: boolean;
|
||||
adoptionFee: number;
|
||||
}
|
||||
|
||||
export interface OrgConfig {
|
||||
branding: OrgBranding;
|
||||
orgInfo: OrgInfo;
|
||||
locale: OrgLocale;
|
||||
applications: OrgApplications;
|
||||
setupComplete: boolean;
|
||||
}
|
||||
|
||||
const DEFAULTS: OrgConfig = {
|
||||
branding: {
|
||||
primaryColor: '#0d9488',
|
||||
primaryColorLight: '#14b8a6',
|
||||
primaryColorDark: '#0f766e',
|
||||
accentColor: '#f59e0b',
|
||||
accentColorLight: '#fbbf24',
|
||||
accentColorDark: '#d97706',
|
||||
logoUrl: ''
|
||||
},
|
||||
orgInfo: {
|
||||
name: 'My Rescue',
|
||||
tagline: '',
|
||||
city: '',
|
||||
state: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
facebookUrl: '',
|
||||
instagramUrl: ''
|
||||
},
|
||||
locale: {
|
||||
language: 'en',
|
||||
currency: 'USD',
|
||||
dateFormat: 'MM/DD/YYYY'
|
||||
},
|
||||
applications: {
|
||||
adoptionEnabled: true,
|
||||
fosterEnabled: true,
|
||||
volunteerEnabled: true,
|
||||
surrenderEnabled: true,
|
||||
adoptionFee: 250
|
||||
},
|
||||
setupComplete: false
|
||||
};
|
||||
|
||||
const KEY_MAP: Record<string, keyof OrgConfig> = {
|
||||
branding: 'branding',
|
||||
org_info: 'orgInfo',
|
||||
locale: 'locale',
|
||||
applications: 'applications',
|
||||
setup_complete: 'setupComplete'
|
||||
};
|
||||
|
||||
export function parseOrgConfig(rows: { key: string; value: unknown }[]): OrgConfig {
|
||||
const config = structuredClone(DEFAULTS);
|
||||
|
||||
for (const row of rows) {
|
||||
const configKey = KEY_MAP[row.key];
|
||||
if (!configKey) continue;
|
||||
|
||||
if (configKey === 'setupComplete') {
|
||||
config.setupComplete = Boolean(row.value);
|
||||
} else {
|
||||
config[configKey] = {
|
||||
...config[configKey],
|
||||
...(row.value as Record<string, unknown>)
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
let cachedConfig: OrgConfig | null = null;
|
||||
let cacheTime = 0;
|
||||
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
export async function getOrgConfig(): Promise<OrgConfig> {
|
||||
if (cachedConfig && Date.now() - cacheTime < CACHE_TTL) {
|
||||
return cachedConfig;
|
||||
}
|
||||
|
||||
const rows = await db.select().from(orgConfig);
|
||||
cachedConfig = parseOrgConfig(rows);
|
||||
cacheTime = Date.now();
|
||||
return cachedConfig;
|
||||
}
|
||||
|
||||
export function invalidateConfigCache(): void {
|
||||
cachedConfig = null;
|
||||
cacheTime = 0;
|
||||
}
|
||||
Reference in New Issue
Block a user