Files
netfelix-audio-fix/server/api/setup.ts
Felix Förtsch 511a3c1ace
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m6s
remove path mappings from settings UI, fix clear-scan blocking by deleting children first
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:52:26 +01:00

114 lines
3.5 KiB
TypeScript

import { Hono } from 'hono';
import { setConfig, getAllConfig, getDb, getEnvLockedKeys } from '../db/index';
import { testConnection as testJellyfin, getUsers } from '../services/jellyfin';
import { testConnection as testRadarr } from '../services/radarr';
import { testConnection as testSonarr } from '../services/sonarr';
const app = new Hono();
app.get('/', (c) => {
const config = getAllConfig();
const envLocked = Array.from(getEnvLockedKeys());
return c.json({ config, envLocked });
});
app.post('/jellyfin', async (c) => {
const body = await c.req.json<{ url: string; api_key: string }>();
const url = body.url?.replace(/\/$/, '');
const apiKey = body.api_key;
if (!url || !apiKey) return c.json({ ok: false, error: 'URL and API key are required' }, 400);
const result = await testJellyfin({ url, apiKey });
if (!result.ok) return c.json({ ok: false, error: result.error });
setConfig('jellyfin_url', url);
setConfig('jellyfin_api_key', apiKey);
setConfig('setup_complete', '1');
try {
const users = await getUsers({ url, apiKey });
const admin = users.find((u) => u.Name === 'admin') ?? users[0];
if (admin?.Id) setConfig('jellyfin_user_id', admin.Id);
} catch { /* ignore */ }
return c.json({ ok: true });
});
app.post('/radarr', async (c) => {
const body = await c.req.json<{ url?: string; api_key?: string }>();
const url = body.url?.replace(/\/$/, '');
const apiKey = body.api_key;
if (!url || !apiKey) {
setConfig('radarr_enabled', '0');
return c.json({ ok: false, error: 'URL and API key are required' }, 400);
}
const result = await testRadarr({ url, apiKey });
if (!result.ok) return c.json({ ok: false, error: result.error });
setConfig('radarr_url', url);
setConfig('radarr_api_key', apiKey);
setConfig('radarr_enabled', '1');
return c.json({ ok: true });
});
app.post('/sonarr', async (c) => {
const body = await c.req.json<{ url?: string; api_key?: string }>();
const url = body.url?.replace(/\/$/, '');
const apiKey = body.api_key;
if (!url || !apiKey) {
setConfig('sonarr_enabled', '0');
return c.json({ ok: false, error: 'URL and API key are required' }, 400);
}
const result = await testSonarr({ url, apiKey });
if (!result.ok) return c.json({ ok: false, error: result.error });
setConfig('sonarr_url', url);
setConfig('sonarr_api_key', apiKey);
setConfig('sonarr_enabled', '1');
return c.json({ ok: true });
});
app.post('/subtitle-languages', async (c) => {
const body = await c.req.json<{ langs: string[] }>();
if (body.langs?.length > 0) {
setConfig('subtitle_languages', JSON.stringify(body.langs));
}
return c.json({ ok: true });
});
app.post('/audio-languages', async (c) => {
const body = await c.req.json<{ langs: string[] }>();
setConfig('audio_languages', JSON.stringify(body.langs ?? []));
return c.json({ ok: true });
});
app.post('/path-mappings', async (c) => {
const body = await c.req.json<{ mappings: [string, string][] }>();
setConfig('path_mappings', JSON.stringify(body.mappings ?? []));
return c.json({ ok: true });
});
app.post('/clear-scan', (c) => {
const db = getDb();
// Delete children first to avoid slow cascade deletes
db.transaction(() => {
db.prepare('DELETE FROM stream_decisions').run();
db.prepare('DELETE FROM jobs').run();
db.prepare('DELETE FROM subtitle_files').run();
db.prepare('DELETE FROM review_plans').run();
db.prepare('DELETE FROM media_streams').run();
db.prepare('DELETE FROM media_items').run();
db.prepare("UPDATE config SET value = '0' WHERE key = 'scan_running'").run();
})();
return c.json({ ok: true });
});
export default app;