refactor(memories): generalize photo providers and decouple from immich

This commit is contained in:
Marek Maslowski
2026-04-03 02:54:35 +02:00
parent 5be2e9b268
commit 8e9f8784dc
13 changed files with 1076 additions and 226 deletions
+83 -4
View File
@@ -264,12 +264,91 @@ router.delete('/packing-templates/:templateId/items/:itemId', (req: Request, res
// ── Addons ─────────────────────────────────────────────────────────────────
router.get('/addons', (_req: Request, res: Response) => {
res.json({ addons: svc.listAddons() });
const addons = db.prepare('SELECT * FROM addons ORDER BY sort_order, id').all() as Addon[];
const providers = db.prepare(`
SELECT id, name, description, icon, enabled, config, sort_order
FROM photo_providers
ORDER BY sort_order, id
`).all() as Array<{ id: string; name: string; description?: string | null; icon: string; enabled: number; config: string; sort_order: number }>;
const fields = db.prepare(`
SELECT provider_id, field_key, label, input_type, placeholder, required, secret, settings_key, payload_key, sort_order
FROM photo_provider_fields
ORDER BY sort_order, id
`).all() as Array<{
provider_id: string;
field_key: string;
label: string;
input_type: string;
placeholder?: string | null;
required: number;
secret: number;
settings_key?: string | null;
payload_key?: string | null;
sort_order: number;
}>;
const fieldsByProvider = new Map<string, typeof fields>();
for (const field of fields) {
const arr = fieldsByProvider.get(field.provider_id) || [];
arr.push(field);
fieldsByProvider.set(field.provider_id, arr);
}
res.json({
addons: [
...addons.map(a => ({ ...a, enabled: !!a.enabled, config: JSON.parse(a.config || '{}') })),
...providers.map(p => ({
id: p.id,
name: p.name,
description: p.description,
type: 'photo_provider',
icon: p.icon,
enabled: !!p.enabled,
config: JSON.parse(p.config || '{}'),
fields: (fieldsByProvider.get(p.id) || []).map(f => ({
key: f.field_key,
label: f.label,
input_type: f.input_type,
placeholder: f.placeholder || '',
required: !!f.required,
secret: !!f.secret,
settings_key: f.settings_key || null,
payload_key: f.payload_key || null,
sort_order: f.sort_order,
})),
sort_order: p.sort_order,
})),
],
});
});
router.put('/addons/:id', (req: Request, res: Response) => {
const result = svc.updateAddon(req.params.id, req.body);
if ('error' in result) return res.status(result.status!).json({ error: result.error });
const addon = db.prepare('SELECT * FROM addons WHERE id = ?').get(req.params.id) as Addon | undefined;
const provider = db.prepare('SELECT * FROM photo_providers WHERE id = ?').get(req.params.id) as { id: string; name: string; description?: string | null; icon: string; enabled: number; config: string; sort_order: number } | undefined;
if (!addon && !provider) return res.status(404).json({ error: 'Addon not found' });
const { enabled, config } = req.body;
if (addon) {
if (enabled !== undefined) db.prepare('UPDATE addons SET enabled = ? WHERE id = ?').run(enabled ? 1 : 0, req.params.id);
if (config !== undefined) db.prepare('UPDATE addons SET config = ? WHERE id = ?').run(JSON.stringify(config), req.params.id);
} else {
if (enabled !== undefined) db.prepare('UPDATE photo_providers SET enabled = ? WHERE id = ?').run(enabled ? 1 : 0, req.params.id);
if (config !== undefined) db.prepare('UPDATE photo_providers SET config = ? WHERE id = ?').run(JSON.stringify(config), req.params.id);
}
const updatedAddon = db.prepare('SELECT * FROM addons WHERE id = ?').get(req.params.id) as Addon | undefined;
const updatedProvider = db.prepare('SELECT * FROM photo_providers WHERE id = ?').get(req.params.id) as { id: string; name: string; description?: string | null; icon: string; enabled: number; config: string; sort_order: number } | undefined;
const updated = updatedAddon
? { ...updatedAddon, enabled: !!updatedAddon.enabled, config: JSON.parse(updatedAddon.config || '{}') }
: updatedProvider
? {
id: updatedProvider.id,
name: updatedProvider.name,
description: updatedProvider.description,
type: 'photo_provider',
icon: updatedProvider.icon,
enabled: !!updatedProvider.enabled,
config: JSON.parse(updatedProvider.config || '{}'),
sort_order: updatedProvider.sort_order,
}
: null;
const authReq = req as AuthRequest;
writeAudit({
userId: authReq.user.id,
@@ -278,7 +357,7 @@ router.put('/addons/:id', (req: Request, res: Response) => {
ip: getClientIp(req),
details: result.auditDetails,
});
res.json({ addon: result.addon });
res.json({ addon: updated });
});
// ── MCP Tokens ─────────────────────────────────────────────────────────────