feat: add org_config loader with caching and defaults

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-26 11:56:51 -05:00
co-authored by Claude Sonnet 4.6
parent 0367886ac6
commit 649c324f37
2 changed files with 152 additions and 0 deletions
+128
View File
@@ -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;
}