From 877e1a09ccc6ceb5369809415fe7f71ceb7ef453 Mon Sep 17 00:00:00 2001 From: Marek Maslowski Date: Sat, 4 Apr 2026 14:20:52 +0200 Subject: [PATCH] removing the need of suplementing provider links in config --- server/src/app.ts | 7 ++++--- server/src/db/migrations.ts | 19 ++++++++++--------- server/src/db/schema.ts | 1 - server/src/db/seeds.ts | 16 ++-------------- server/src/services/adminService.ts | 14 +++++++------- .../src/services/memories/helpersService.ts | 19 +++++++++++++++++++ 6 files changed, 42 insertions(+), 34 deletions(-) diff --git a/server/src/app.ts b/server/src/app.ts index 60ddcb8..c46cfe7 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -38,6 +38,7 @@ import notificationRoutes from './routes/notifications'; import shareRoutes from './routes/share'; import { mcpHandler } from './mcp'; import { Addon } from './types'; +import { getPhotoProviderConfig } from './services/memories/helpersService'; export function createApp(): express.Application { const app = express(); @@ -194,11 +195,11 @@ export function createApp(): express.Application { app.get('/api/addons', authenticate, (_req: Request, res: Response) => { const addons = db.prepare('SELECT id, name, type, icon, enabled FROM addons WHERE enabled = 1 ORDER BY sort_order').all() as Pick[]; const providers = db.prepare(` - SELECT id, name, icon, enabled, config, sort_order + SELECT id, name, icon, enabled, sort_order FROM photo_providers WHERE enabled = 1 ORDER BY sort_order, id - `).all() as Array<{ id: string; name: string; icon: string; enabled: number; config: string; sort_order: number }>; + `).all() as Array<{ id: string; name: string; icon: string; enabled: number; 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 @@ -232,7 +233,7 @@ export function createApp(): express.Application { type: 'photo_provider', icon: p.icon, enabled: !!p.enabled, - config: JSON.parse(p.config || '{}'), + config: getPhotoProviderConfig(p.id), fields: (fieldsByProvider.get(p.id) || []).map(f => ({ key: f.field_key, label: f.label, diff --git a/server/src/db/migrations.ts b/server/src/db/migrations.ts index 20f160d..aee415e 100644 --- a/server/src/db/migrations.ts +++ b/server/src/db/migrations.ts @@ -643,14 +643,13 @@ function runMigrations(db: Database.Database): void { // Seed Synology Photos provider and fields in existing databases try { db.prepare(` - INSERT INTO photo_providers (id, name, description, icon, enabled, config, sort_order) - VALUES (?, ?, ?, ?, ?, ?, ?) + INSERT INTO photo_providers (id, name, description, icon, enabled, sort_order) + VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET name = excluded.name, description = excluded.description, icon = excluded.icon, enabled = excluded.enabled, - config = excluded.config, sort_order = excluded.sort_order `).run( 'synologyphotos', @@ -658,12 +657,6 @@ function runMigrations(db: Database.Database): void { 'Synology Photos integration with separate account settings', 'Image', 0, - JSON.stringify({ - settings_get: '/integrations/synologyphotos/settings', - settings_put: '/integrations/synologyphotos/settings', - status_get: '/integrations/synologyphotos/status', - test_post: '/integrations/synologyphotos/test', - }), 1, ); } catch (err: any) { @@ -691,6 +684,14 @@ function runMigrations(db: Database.Database): void { if (!err.message?.includes('no such table')) throw err; } }, + () => { + // Remove the stored config column from photo_providers now that it is generated from provider id. + const columns = db.prepare("PRAGMA table_info('photo_providers')").all() as Array<{ name: string }>; + const names = new Set(columns.map(c => c.name)); + if (!names.has('config')) return; + + db.exec('ALTER TABLE photo_providers DROP COLUMN config'); + }, ]; if (currentVersion < migrations.length) { diff --git a/server/src/db/schema.ts b/server/src/db/schema.ts index 9e243b6..e053df6 100644 --- a/server/src/db/schema.ts +++ b/server/src/db/schema.ts @@ -232,7 +232,6 @@ function createTables(db: Database.Database): void { description TEXT, icon TEXT DEFAULT 'Image', enabled INTEGER DEFAULT 0, - config TEXT DEFAULT '{}', sort_order INTEGER DEFAULT 0 ); diff --git a/server/src/db/seeds.ts b/server/src/db/seeds.ts index ef849d9..2e233f6 100644 --- a/server/src/db/seeds.ts +++ b/server/src/db/seeds.ts @@ -101,12 +101,6 @@ function seedAddons(db: Database.Database): void { icon: 'Image', enabled: 0, sort_order: 0, - config: JSON.stringify({ - settings_get: '/integrations/immich/settings', - settings_put: '/integrations/immich/settings', - status_get: '/integrations/immich/status', - test_post: '/integrations/immich/test', - }), }, { id: 'synologyphotos', @@ -115,16 +109,10 @@ function seedAddons(db: Database.Database): void { icon: 'Image', enabled: 0, sort_order: 1, - config: JSON.stringify({ - settings_get: '/integrations/synologyphotos/settings', - settings_put: '/integrations/synologyphotos/settings', - status_get: '/integrations/synologyphotos/status', - test_post: '/integrations/synologyphotos/test', - }), }, ]; - const insertProvider = db.prepare('INSERT OR IGNORE INTO photo_providers (id, name, description, icon, enabled, config, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?)'); - for (const p of providerRows) insertProvider.run(p.id, p.name, p.description, p.icon, p.enabled, p.config, p.sort_order); + const insertProvider = db.prepare('INSERT OR IGNORE INTO photo_providers (id, name, description, icon, enabled, sort_order) VALUES (?, ?, ?, ?, ?, ?)'); + for (const p of providerRows) insertProvider.run(p.id, p.name, p.description, p.icon, p.enabled, p.sort_order); const providerFields = [ { provider_id: 'immich', field_key: 'immich_url', label: 'Immich URL', input_type: 'url', placeholder: 'https://immich.example.com', required: 1, secret: 0, settings_key: 'immich_url', payload_key: 'immich_url', sort_order: 0 }, diff --git a/server/src/services/adminService.ts b/server/src/services/adminService.ts index f7df1f4..167eaeb 100644 --- a/server/src/services/adminService.ts +++ b/server/src/services/adminService.ts @@ -9,6 +9,7 @@ import { maybe_encrypt_api_key, decrypt_api_key } from './apiKeyCrypto'; import { getAllPermissions, savePermissions as savePerms, PERMISSION_ACTIONS } from './permissions'; import { revokeUserSessions } from '../mcp'; import { validatePassword } from './passwordPolicy'; +import { getPhotoProviderConfig } from './memories/helpersService'; // ── Helpers ──────────────────────────────────────────────────────────────── @@ -466,10 +467,10 @@ export function deleteTemplateItem(itemId: string) { export function 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 + SELECT id, name, description, icon, enabled, 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 }>; + `).all() as Array<{ id: string; name: string; description?: string | null; icon: string; enabled: number; 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 @@ -502,7 +503,7 @@ export function listAddons() { type: 'photo_provider', icon: p.icon, enabled: !!p.enabled, - config: JSON.parse(p.config || '{}'), + config: getPhotoProviderConfig(p.id), fields: (fieldsByProvider.get(p.id) || []).map(f => ({ key: f.field_key, label: f.label, @@ -521,7 +522,7 @@ export function listAddons() { export function updateAddon(id: string, data: { enabled?: boolean; config?: Record }) { const addon = db.prepare('SELECT * FROM addons WHERE id = ?').get(id) as Addon | undefined; - const provider = db.prepare('SELECT * FROM photo_providers WHERE id = ?').get(id) as { id: string; name: string; description?: string | null; icon: string; enabled: number; config: string; sort_order: number } | undefined; + const provider = db.prepare('SELECT * FROM photo_providers WHERE id = ?').get(id) as { id: string; name: string; description?: string | null; icon: string; enabled: number; sort_order: number } | undefined; if (!addon && !provider) return { error: 'Addon not found', status: 404 }; if (addon) { @@ -529,11 +530,10 @@ export function updateAddon(id: string, data: { enabled?: boolean; config?: Reco if (data.config !== undefined) db.prepare('UPDATE addons SET config = ? WHERE id = ?').run(JSON.stringify(data.config), id); } else { if (data.enabled !== undefined) db.prepare('UPDATE photo_providers SET enabled = ? WHERE id = ?').run(data.enabled ? 1 : 0, id); - if (data.config !== undefined) db.prepare('UPDATE photo_providers SET config = ? WHERE id = ?').run(JSON.stringify(data.config), id); } const updatedAddon = db.prepare('SELECT * FROM addons WHERE id = ?').get(id) as Addon | undefined; - const updatedProvider = db.prepare('SELECT * FROM photo_providers WHERE id = ?').get(id) as { id: string; name: string; description?: string | null; icon: string; enabled: number; config: string; sort_order: number } | undefined; + const updatedProvider = db.prepare('SELECT * FROM photo_providers WHERE id = ?').get(id) as { id: string; name: string; description?: string | null; icon: string; enabled: number; sort_order: number } | undefined; const updated = updatedAddon ? { ...updatedAddon, enabled: !!updatedAddon.enabled, config: JSON.parse(updatedAddon.config || '{}') } : updatedProvider @@ -544,7 +544,7 @@ export function updateAddon(id: string, data: { enabled?: boolean; config?: Reco type: 'photo_provider', icon: updatedProvider.icon, enabled: !!updatedProvider.enabled, - config: JSON.parse(updatedProvider.config || '{}'), + config: getPhotoProviderConfig(updatedProvider.id), sort_order: updatedProvider.sort_order, } : null; diff --git a/server/src/services/memories/helpersService.ts b/server/src/services/memories/helpersService.ts index 51c899a..465842f 100644 --- a/server/src/services/memories/helpersService.ts +++ b/server/src/services/memories/helpersService.ts @@ -32,6 +32,25 @@ export type Selection = { }; +//for loading routes to settings page, and validating which services user has connected +type PhotoProviderConfig = { + settings_get: string; + settings_put: string; + status_get: string; + test_post: string; +}; + + +export function getPhotoProviderConfig(providerId: string): PhotoProviderConfig { + const prefix = `/integrations/memories/${providerId}`; + return { + settings_get: `${prefix}/settings`, + settings_put: `${prefix}/settings`, + status_get: `${prefix}/status`, + test_post: `${prefix}/test`, + }; +} + //----------------------------------------------- //access check helper