Files
netfelix-audio-fix/server/index.tsx
Felix Förtsch 94a460be9d
All checks were successful
Build and Push Docker Image / build (push) Successful in 36s
rename setup → settings throughout; persist arr creds even on test failure
Two cleanups:

1. Rename the page from 'Setup' to 'Settings' all the way down. The H1
   already said Settings; the file/component/api path were lying.
   - src/features/setup/ → src/features/settings/
   - SetupPage.tsx → SettingsPage.tsx, SetupPage → SettingsPage,
     SetupData → SettingsData, setupCache → settingsCache
   - server/api/setup.ts → server/api/settings.ts
   - /api/setup → /api/settings (only consumer is our frontend)
   - server/index.tsx import + route mount renamed
   - ScanPage's local setupChecked → configChecked

2. Sonarr (and Radarr) save flow: persist the values BEFORE running the
   connection test. The previous code returned early if the test failed,
   silently dropping what the user typed — explained the user's report
   that Sonarr 'forgets' the input. Now setConfig fires unconditionally
   on a valid (non-empty) URL+key; the test result is returned as
   { ok, saved, testError } so the UI can show 'Saved & connected' on
   success or '⚠ Saved, but connection test failed: …' on failure
   instead of erasing the input.

Note: setup_complete config key kept as-is — it represents 'has the user
configured Jellyfin' which is conceptually setup and not user-visible.
2026-04-13 12:26:30 +02:00

76 lines
3.1 KiB
TypeScript

import { Hono } from "hono";
import { serveStatic } from "hono/bun";
import { cors } from "hono/cors";
import dashboardRoutes from "./api/dashboard";
import executeRoutes from "./api/execute";
import pathsRoutes from "./api/paths";
import reviewRoutes from "./api/review";
import scanRoutes from "./api/scan";
import settingsRoutes from "./api/settings";
import subtitlesRoutes from "./api/subtitles";
import { getDb } from "./db/index";
import { log } from "./lib/log";
const app = new Hono();
// ─── CORS (dev: Vite on :5173 talks to Hono on :3000) ────────────────────────
app.use("/api/*", cors({ origin: ["http://localhost:5173", "http://localhost:3000"] }));
// ─── Request logging ──────────────────────────────────────────────────────────
app.use("/api/*", async (c, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
// Skip noisy SSE/polling endpoints
if (c.req.path.endsWith("/events")) return;
log(`${c.req.method} ${c.req.path}${c.res.status} (${ms}ms)`);
});
// ─── API routes ───────────────────────────────────────────────────────────────
import pkg from "../package.json";
app.get("/api/version", (c) => c.json({ version: pkg.version }));
app.route("/api/dashboard", dashboardRoutes);
app.route("/api/settings", settingsRoutes);
app.route("/api/scan", scanRoutes);
app.route("/api/review", reviewRoutes);
app.route("/api/execute", executeRoutes);
app.route("/api/subtitles", subtitlesRoutes);
app.route("/api/paths", pathsRoutes);
// ─── Static assets (production: serve Vite build) ────────────────────────────
app.use("/assets/*", serveStatic({ root: "./dist" }));
app.use("/favicon.ico", serveStatic({ path: "./dist/favicon.ico" }));
// ─── SPA fallback ─────────────────────────────────────────────────────────────
// All non-API routes serve the React index.html so TanStack Router handles them.
app.get("*", async (c) => {
if (c.req.path.startsWith("/api/")) return c.notFound();
// In dev the Vite server handles the SPA. In production serve dist/index.html.
try {
const html = await Bun.file("./dist/index.html").text();
return c.html(html);
} catch {
return c.text("Run `bun build` first to generate the frontend.", 503);
}
});
// ─── Start ────────────────────────────────────────────────────────────────────
const port = Number(process.env.PORT ?? "3000");
log(`netfelix-audio-fix v${pkg.version} starting on http://localhost:${port}`);
getDb();
export default {
port,
fetch: app.fetch,
idleTimeout: 0,
};