settings: add factory reset button that wipes every table incl. config
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled

the existing clear-scan button only drops media_items + related; settings
survived. useful when schema changes or corrupt state make you want a full
do-over on a running container without ssh-ing in to rm data/netfelix.db.

POST /api/settings/reset truncates everything (config included) then re-seeds
DEFAULT_CONFIG via the exported reseedDefaults helper. env-var overrides keep
working through getConfig's env fallback. ui lives next to clear-scan in the
danger zone with a double confirm and reload to /, so the setup wizard shows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 15:16:07 +02:00
parent c06172f412
commit f4859317fa
4 changed files with 53 additions and 4 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "netfelix-audio-fix",
"version": "2026.04.13.4",
"version": "2026.04.13.5",
"scripts": {
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
"dev:client": "vite",

View File

@@ -1,5 +1,5 @@
import { Hono } from "hono";
import { getAllConfig, getDb, getEnvLockedKeys, setConfig } from "../db/index";
import { getAllConfig, getDb, getEnvLockedKeys, reseedDefaults, setConfig } from "../db/index";
import { getUsers, testConnection as testJellyfin } from "../services/jellyfin";
import { testConnection as testRadarr } from "../services/radarr";
import { getScheduleConfig, type ScheduleConfig, updateScheduleConfig } from "../services/scheduler";
@@ -122,4 +122,26 @@ app.post("/clear-scan", (c) => {
return c.json({ ok: true });
});
/**
* Full factory reset. Truncates every table including config, re-seeds the
* defaults so the setup wizard reappears, and returns. Env-backed config
* keys (JELLYFIN_URL, etc.) continue to resolve via getConfig's env fallback
* — they don't live in the DB to begin with.
*/
app.post("/reset", (c) => {
const db = getDb();
db.transaction(() => {
// Order matters when ON DELETE CASCADE isn't consistent across versions.
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("DELETE FROM config").run();
})();
reseedDefaults();
return c.json({ ok: true });
});
export default app;

View File

@@ -102,6 +102,11 @@ function seedDefaults(db: Database): void {
}
}
/** Re-seed config defaults after a truncating reset. Caller owns the delete. */
export function reseedDefaults(): void {
seedDefaults(getDb());
}
export function getConfig(key: string): string | null {
// Env vars take precedence over DB
const fromEnv = envValue(key);

View File

@@ -401,6 +401,20 @@ export function SettingsPage() {
setClearStatus("Cleared.");
};
const factoryReset = async () => {
if (
!confirm(
"Reset to first-run state? This wipes EVERYTHING — scan data, settings, languages, schedule, Jellyfin/Radarr/Sonarr credentials. You'll land back on the setup wizard. This cannot be undone.",
)
)
return;
if (!confirm("Really reset? Type-nothing-just-click to confirm.")) return;
await api.post("/api/settings/reset");
// Invalidate client-side caches and reload so the setup gate re-evaluates.
settingsCache = null;
window.location.href = "/";
};
return (
<div>
<div className="flex items-center gap-3 mb-4">
@@ -487,11 +501,19 @@ export function SettingsPage() {
<p className="text-gray-500 text-sm mb-3">
These actions are irreversible. Scan data can be regenerated by running a new scan.
</p>
<div className="flex items-center gap-4">
<div className="flex items-center gap-4 mb-3">
<Button variant="danger" onClick={clearScan}>
Clear all scan data
</Button>
<span className="text-gray-400 text-sm">Removes all scanned items, review plans, and jobs.</span>
<span className="text-gray-400 text-sm">Removes all scanned items, review plans, and jobs. Keeps settings.</span>
</div>
<div className="flex items-center gap-4">
<Button variant="danger" onClick={factoryReset}>
Reset to first-run state
</Button>
<span className="text-gray-400 text-sm">
Wipes everything scan data and settings. Sends you back to the setup wizard.
</span>
</div>
{clearStatus && <p className="text-green-700 text-sm mt-2">{clearStatus}</p>}
</div>