refactor(server): IMAGE_DIR/DATABASE_PATH zentralisieren + Doku-Drift fixen
src/lib/server/paths.ts: zentrale Auflösung der env-vars; vorher 6× IMAGE_DIR und 2× DATABASE_PATH dupliziert mit identischen Defaults. Migrierte Sites: - src/lib/server/db/index.ts (DATABASE_PATH + IMAGE_DIR) - src/routes/api/admin/backup/+server.ts - src/routes/api/domains/+server.ts - src/routes/api/domains/[id]/+server.ts - src/routes/api/recipes/import/+server.ts - src/routes/api/recipes/[id]/image/+server.ts - src/routes/images/[filename]/+server.ts ARCHITECTURE.md: - 49 Flachwitze -> 150 (waren tatsaechlich 150) - 'search/' Route entfernt — wurde nie als eigene Route gebaut, Suche laeuft direkt auf der Homepage via API-Calls Findings aus zweiter Review-Runde (siehe OPEN-ISSUES-NEXT.md)
This commit is contained in:
@@ -31,14 +31,13 @@ src/
|
|||||||
│ │ ├── search/ # searxng.ts (Web-Suche + Thumbnail-Cache)
|
│ │ ├── search/ # searxng.ts (Web-Suche + Thumbnail-Cache)
|
||||||
│ │ ├── wishlist/ # Repo
|
│ │ ├── wishlist/ # Repo
|
||||||
│ │ └── backup/ # ZIP-Export via archiver, Import via yauzl
|
│ │ └── backup/ # ZIP-Export via archiver, Import via yauzl
|
||||||
│ ├── quotes.ts # 49 Flachwitze für die Homepage
|
│ ├── quotes.ts # 150 Flachwitze für die Homepage
|
||||||
│ └── types.ts # shared types
|
│ └── types.ts # shared types
|
||||||
└── routes/
|
└── routes/
|
||||||
├── +layout.svelte # Header, Confirm-Dialog-Mount, Header-Search-Dropdown
|
├── +layout.svelte # Header, Confirm-Dialog-Mount, Header-Search-Dropdown
|
||||||
├── +page.svelte # Home: Hero + Live-Search + Zuletzt-hinzugefügt
|
├── +page.svelte # Home: Hero + Live-Search + Zuletzt-hinzugefügt
|
||||||
├── recipes/[id]/ # Rezept-Detail
|
├── recipes/[id]/ # Rezept-Detail
|
||||||
├── preview/ # Vorschau vor dem Speichern
|
├── preview/ # Vorschau vor dem Speichern
|
||||||
├── search/ # /search (lokal), /search/web (Internet)
|
|
||||||
├── wishlist/
|
├── wishlist/
|
||||||
├── admin/ # Whitelist, Profile, Backup/Restore
|
├── admin/ # Whitelist, Profile, Backup/Restore
|
||||||
├── images/[filename] # Statische Auslieferung lokaler Bilder
|
├── images/[filename] # Statische Auslieferung lokaler Bilder
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import Database from 'better-sqlite3';
|
import Database from 'better-sqlite3';
|
||||||
import { mkdirSync } from 'node:fs';
|
import { mkdirSync } from 'node:fs';
|
||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
|
import { DATABASE_PATH, IMAGE_DIR } from '$lib/server/paths';
|
||||||
import { runMigrations } from './migrate';
|
import { runMigrations } from './migrate';
|
||||||
|
|
||||||
let instance: Database.Database | null = null;
|
let instance: Database.Database | null = null;
|
||||||
|
|
||||||
export function getDb(path = process.env.DATABASE_PATH ?? './data/kochwas.db'): Database.Database {
|
export function getDb(path = DATABASE_PATH): Database.Database {
|
||||||
if (instance) return instance;
|
if (instance) return instance;
|
||||||
mkdirSync(dirname(path), { recursive: true });
|
mkdirSync(dirname(path), { recursive: true });
|
||||||
const imageDir = process.env.IMAGE_DIR ?? './data/images';
|
mkdirSync(IMAGE_DIR, { recursive: true });
|
||||||
mkdirSync(imageDir, { recursive: true });
|
|
||||||
instance = new Database(path);
|
instance = new Database(path);
|
||||||
instance.pragma('journal_mode = WAL');
|
instance.pragma('journal_mode = WAL');
|
||||||
instance.pragma('foreign_keys = ON');
|
instance.pragma('foreign_keys = ON');
|
||||||
|
|||||||
6
src/lib/server/paths.ts
Normal file
6
src/lib/server/paths.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Filesystem paths read from env at module load. Centralized so a misset
|
||||||
|
// env var only causes one place to be wrong, not six. Both defaults match
|
||||||
|
// the docker-compose volume mounts under `/app/data`.
|
||||||
|
|
||||||
|
export const DATABASE_PATH = process.env.DATABASE_PATH ?? './data/kochwas.db';
|
||||||
|
export const IMAGE_DIR = process.env.IMAGE_DIR ?? './data/images';
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
import type { RequestHandler } from './$types';
|
import type { RequestHandler } from './$types';
|
||||||
import { createBackupStream, backupFilename } from '$lib/server/backup/export';
|
import { createBackupStream, backupFilename } from '$lib/server/backup/export';
|
||||||
|
import { DATABASE_PATH, IMAGE_DIR } from '$lib/server/paths';
|
||||||
import { Readable } from 'node:stream';
|
import { Readable } from 'node:stream';
|
||||||
|
|
||||||
const DB_PATH = process.env.DATABASE_PATH ?? './data/kochwas.db';
|
|
||||||
const IMAGE_DIR = process.env.IMAGE_DIR ?? './data/images';
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => {
|
export const GET: RequestHandler = async () => {
|
||||||
const archive = createBackupStream({ dbPath: DB_PATH, imagesDir: IMAGE_DIR });
|
const archive = createBackupStream({ dbPath: DATABASE_PATH, imagesDir: IMAGE_DIR });
|
||||||
const filename = backupFilename();
|
const filename = backupFilename();
|
||||||
return new Response(Readable.toWeb(archive) as ReadableStream, {
|
return new Response(Readable.toWeb(archive) as ReadableStream, {
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { getDb } from '$lib/server/db';
|
|||||||
import { validateBody } from '$lib/server/api-helpers';
|
import { validateBody } from '$lib/server/api-helpers';
|
||||||
import { addDomain, listDomains, setDomainFavicon } from '$lib/server/domains/repository';
|
import { addDomain, listDomains, setDomainFavicon } from '$lib/server/domains/repository';
|
||||||
import { ensureFavicons, fetchAndStoreFavicon } from '$lib/server/domains/favicons';
|
import { ensureFavicons, fetchAndStoreFavicon } from '$lib/server/domains/favicons';
|
||||||
|
import { IMAGE_DIR } from '$lib/server/paths';
|
||||||
|
|
||||||
const CreateSchema = z.object({
|
const CreateSchema = z.object({
|
||||||
domain: z.string().min(3).max(253),
|
domain: z.string().min(3).max(253),
|
||||||
@@ -12,8 +13,6 @@ const CreateSchema = z.object({
|
|||||||
added_by_profile_id: z.number().int().positive().nullable().optional()
|
added_by_profile_id: z.number().int().positive().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
const IMAGE_DIR = process.env.IMAGE_DIR ?? './data/images';
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => {
|
export const GET: RequestHandler = async () => {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
// Favicons lazy nachziehen — beim zweiten Aufruf gibt es nichts mehr zu tun.
|
// Favicons lazy nachziehen — beim zweiten Aufruf gibt es nichts mehr zu tun.
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import {
|
|||||||
setDomainFavicon
|
setDomainFavicon
|
||||||
} from '$lib/server/domains/repository';
|
} from '$lib/server/domains/repository';
|
||||||
import { fetchAndStoreFavicon } from '$lib/server/domains/favicons';
|
import { fetchAndStoreFavicon } from '$lib/server/domains/favicons';
|
||||||
|
import { IMAGE_DIR } from '$lib/server/paths';
|
||||||
const IMAGE_DIR = process.env.IMAGE_DIR ?? './data/images';
|
|
||||||
|
|
||||||
const UpdateSchema = z.object({
|
const UpdateSchema = z.object({
|
||||||
domain: z.string().min(3).max(253).optional(),
|
domain: z.string().min(3).max(253).optional(),
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { join } from 'node:path';
|
|||||||
import { getDb } from '$lib/server/db';
|
import { getDb } from '$lib/server/db';
|
||||||
import { parsePositiveIntParam } from '$lib/server/api-helpers';
|
import { parsePositiveIntParam } from '$lib/server/api-helpers';
|
||||||
import { getRecipeById, updateImagePath } from '$lib/server/recipes/repository';
|
import { getRecipeById, updateImagePath } from '$lib/server/recipes/repository';
|
||||||
|
import { IMAGE_DIR } from '$lib/server/paths';
|
||||||
|
|
||||||
const IMAGE_DIR = process.env.IMAGE_DIR ?? './data/images';
|
|
||||||
const MAX_BYTES = 10 * 1024 * 1024;
|
const MAX_BYTES = 10 * 1024 * 1024;
|
||||||
|
|
||||||
const EXT_BY_MIME: Record<string, string> = {
|
const EXT_BY_MIME: Record<string, string> = {
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import { getDb } from '$lib/server/db';
|
|||||||
import { validateBody } from '$lib/server/api-helpers';
|
import { validateBody } from '$lib/server/api-helpers';
|
||||||
import { importRecipe } from '$lib/server/recipes/importer';
|
import { importRecipe } from '$lib/server/recipes/importer';
|
||||||
import { mapImporterError } from '$lib/server/errors';
|
import { mapImporterError } from '$lib/server/errors';
|
||||||
|
import { IMAGE_DIR } from '$lib/server/paths';
|
||||||
|
|
||||||
const ImportSchema = z.object({ url: z.string().url() });
|
const ImportSchema = z.object({ url: z.string().url() });
|
||||||
|
|
||||||
const IMAGE_DIR = process.env.IMAGE_DIR ?? './data/images';
|
|
||||||
|
|
||||||
export const POST: RequestHandler = async ({ request }) => {
|
export const POST: RequestHandler = async ({ request }) => {
|
||||||
const data = validateBody(await request.json().catch(() => null), ImportSchema);
|
const data = validateBody(await request.json().catch(() => null), ImportSchema);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import type { RequestHandler } from './$types';
|
|||||||
import { error } from '@sveltejs/kit';
|
import { error } from '@sveltejs/kit';
|
||||||
import { createReadStream, existsSync, statSync } from 'node:fs';
|
import { createReadStream, existsSync, statSync } from 'node:fs';
|
||||||
import { join, basename, extname } from 'node:path';
|
import { join, basename, extname } from 'node:path';
|
||||||
|
import { IMAGE_DIR } from '$lib/server/paths';
|
||||||
const IMAGE_DIR = process.env.IMAGE_DIR ?? './data/images';
|
|
||||||
|
|
||||||
const MIME: Record<string, string> = {
|
const MIME: Record<string, string> = {
|
||||||
'.jpg': 'image/jpeg',
|
'.jpg': 'image/jpeg',
|
||||||
|
|||||||
Reference in New Issue
Block a user