MyKindlebox/mykindlebox/server/routes/settings.ts

70 lines
2.4 KiB
TypeScript

import { Router, Request } from 'express';
import { eq } from 'drizzle-orm';
import { db } from '../db/client';
import { settings, users } from '../db/schema';
import { AuthedRequest } from '../auth';
const router = Router();
// The API shape is unchanged from the single-user days — one flat object with
// these keys — but storage is now split (FEATURE_User_Accounts.md): `theme` and
// `weight_unit` are per-user preferences on the caller's `users` row, the rest
// stay global in the settings table. A NULL user pref falls back to the global
// value, so a fresh account inherits the rabbitry-wide defaults until the user
// picks their own. Client consumers (ThemeSync, useWeightUnit, Settings page)
// are agnostic to the split.
const GLOBAL_KEYS = ['rabbitry_name', 'kit_prefix', 'default_breed'] as const;
const USER_KEYS = ['theme', 'weight_unit'] as const;
const ALLOWED_KEYS = [...GLOBAL_KEYS, ...USER_KEYS] as string[];
function mergedSettings(userId: number): Record<string, string | null> {
const result: Record<string, string | null> = {};
for (const row of db.select().from(settings).all()) {
result[row.key] = row.value;
}
const prefs = db
.select({ theme: users.theme, weightUnit: users.weightUnit })
.from(users)
.where(eq(users.id, userId))
.get();
if (prefs?.theme != null) result.theme = prefs.theme;
if (prefs?.weightUnit != null) result.weight_unit = prefs.weightUnit;
return result;
}
function currentUserId(req: Request): number {
return (req as AuthedRequest).user!.id;
}
router.get('/', (req, res) => {
res.json(mergedSettings(currentUserId(req)));
});
router.patch('/', (req, res) => {
const keys = Object.keys(req.body);
const unknownKey = keys.find((key) => !ALLOWED_KEYS.includes(key));
if (unknownKey) {
res.status(400).json({ error: `Unknown setting: ${unknownKey}` });
return;
}
const userId = currentUserId(req);
const userPatch: { theme?: string; weightUnit?: string } = {};
if ('theme' in req.body) userPatch.theme = req.body.theme;
if ('weight_unit' in req.body) userPatch.weightUnit = req.body.weight_unit;
if (Object.keys(userPatch).length > 0) {
db.update(users).set(userPatch).where(eq(users.id, userId)).run();
}
for (const key of keys) {
if ((USER_KEYS as readonly string[]).includes(key)) continue;
db.update(settings).set({ value: req.body[key] }).where(eq(settings.key, key)).run();
}
res.json(mergedSettings(userId));
});
export default router;