diff --git a/app/api/avatars/[userId]/route.ts b/app/api/avatars/[userId]/route.ts index fb1a917..a957144 100644 --- a/app/api/avatars/[userId]/route.ts +++ b/app/api/avatars/[userId]/route.ts @@ -1,8 +1,6 @@ import path from "node:path"; import { type NextRequest, NextResponse } from "next/server"; - -const DATA_DIR = process.env.DATA_DIR || "./data"; -const AVATAR_DIR = path.join(DATA_DIR, "avatars"); +import { AVATAR_DIR } from "@/lib/constants"; const IMMUTABLE_CACHE = "public, max-age=31536000, immutable"; diff --git a/lib/actions/settings.ts b/lib/actions/settings.ts index 9ad1770..7f60f51 100644 --- a/lib/actions/settings.ts +++ b/lib/actions/settings.ts @@ -7,6 +7,7 @@ import { headers } from "next/headers"; import { z } from "zod"; import { auth } from "@/lib/auth/server"; import { requireAdmin, requireSession } from "@/lib/auth/session"; +import { AVATAR_DIR } from "@/lib/constants"; import { type BackupFrequency, rescheduleBackup, triggerJob } from "@/lib/cron"; import { db } from "@/lib/db/client"; import { integrations } from "@/lib/db/schema"; @@ -268,8 +269,6 @@ export async function getUpdateCheckAction(): Promise // --- Avatar actions --- -const DATA_DIR = process.env.DATA_DIR || "./data"; -const AVATAR_DIR = path.join(DATA_DIR, "avatars"); const MAX_AVATAR_SIZE = 2 * 1024 * 1024; // 2MB const ALLOWED_AVATAR_TYPES = new Set([ "image/jpeg", diff --git a/lib/constants.ts b/lib/constants.ts new file mode 100644 index 0000000..a295331 --- /dev/null +++ b/lib/constants.ts @@ -0,0 +1,20 @@ +import path from "node:path"; + +export const DATA_DIR = process.env.DATA_DIR || "./data"; + +export const DATABASE_URL = + process.env.DATABASE_URL || path.join(DATA_DIR, "sqlite.db"); + +export const CACHE_DIR = process.env.CACHE_DIR + ? path.join(process.env.CACHE_DIR, "images") + : path.join(DATA_DIR, "images"); + +export const BACKUP_DIR = path.join(DATA_DIR, "backups"); + +export const AVATAR_DIR = path.join(DATA_DIR, "avatars"); + +export const TMDB_API_BASE_URL = + process.env.TMDB_API_BASE_URL || "https://api.themoviedb.org/3"; + +export const TMDB_IMAGE_BASE_URL = + process.env.TMDB_IMAGE_BASE_URL || "https://image.tmdb.org/t/p"; diff --git a/lib/db/client.ts b/lib/db/client.ts index 253d5d5..4d9cbc4 100644 --- a/lib/db/client.ts +++ b/lib/db/client.ts @@ -1,7 +1,7 @@ import { Database } from "bun:sqlite"; -import path from "node:path"; import type { Logger } from "drizzle-orm"; import { drizzle } from "drizzle-orm/bun-sqlite"; +import { DATABASE_URL } from "@/lib/constants"; import { createLogger } from "@/lib/logger"; import * as schema from "./schema"; @@ -29,10 +29,6 @@ const globalForDb = globalThis as unknown as { _client: Database | undefined; }; -const DATABASE_URL = - process.env.DATABASE_URL || - path.join(process.env.DATA_DIR || "./data", "sqlite.db"); - function getClient() { if (!globalForDb._client) { globalForDb._client = new Database(DATABASE_URL); diff --git a/lib/services/backup.ts b/lib/services/backup.ts index e713fe7..c62a52c 100644 --- a/lib/services/backup.ts +++ b/lib/services/backup.ts @@ -4,17 +4,13 @@ import { mkdir, readdir } from "node:fs/promises"; import path from "node:path"; import { format } from "date-fns"; import { sql } from "drizzle-orm"; +import { BACKUP_DIR, DATABASE_URL } from "@/lib/constants"; import { closeDatabase, db } from "@/lib/db/client"; import { runMigrations } from "@/lib/db/migrate"; import { createLogger } from "@/lib/logger"; const log = createLogger("backup"); -const DATA_DIR = process.env.DATA_DIR || "./data"; -const DATABASE_URL = - process.env.DATABASE_URL || path.join(DATA_DIR, "sqlite.db"); -const BACKUP_DIR = path.join(DATA_DIR, "backups"); - const MANUAL_PATTERN = /^sofa-manual-\d{4}-\d{2}-\d{2}-\d{6}(?:\d{3})?\.db$/; const SCHEDULED_PATTERN = /^sofa-scheduled-\d{4}-\d{2}-\d{2}-\d{6}(?:\d{3})?\.db$/; diff --git a/lib/services/image-cache.ts b/lib/services/image-cache.ts index dba2c74..f2d5b02 100644 --- a/lib/services/image-cache.ts +++ b/lib/services/image-cache.ts @@ -1,6 +1,7 @@ import { mkdir, rename } from "node:fs/promises"; import path from "node:path"; import { eq, inArray } from "drizzle-orm"; +import { CACHE_DIR, TMDB_IMAGE_BASE_URL } from "@/lib/constants"; import { db } from "@/lib/db/client"; import { availabilityOffers, @@ -29,14 +30,6 @@ const CATEGORY_SIZES: Record = { profiles: "w185", }; -const IMAGE_BASE_URL = - process.env.TMDB_IMAGE_BASE_URL || "https://image.tmdb.org/t/p"; - -const DATA_DIR = process.env.DATA_DIR || "./data"; -const CACHE_DIR = process.env.CACHE_DIR - ? path.join(process.env.CACHE_DIR, "images") - : path.join(DATA_DIR, "images"); - export function imageCacheEnabled(): boolean { return process.env.IMAGE_CACHE_ENABLED !== "false"; } @@ -76,7 +69,7 @@ export async function downloadAndCacheImage( category: ImageCategory, ): Promise { const size = CATEGORY_SIZES[category]; - const url = `${IMAGE_BASE_URL}/${size}${tmdbPath}`; + const url = `${TMDB_IMAGE_BASE_URL}/${size}${tmdbPath}`; const res = await fetch(url); if (!res.ok) { @@ -121,7 +114,7 @@ export async function fetchAndMaybeCache( // Fetch from TMDB const size = CATEGORY_SIZES[category]; - const url = `${IMAGE_BASE_URL}/${size}${tmdbPath}`; + const url = `${TMDB_IMAGE_BASE_URL}/${size}${tmdbPath}`; const res = await fetch(url); if (!res.ok) { log.warn(`Fetch failed: ${url} -> ${res.status}`); diff --git a/lib/services/system-health.ts b/lib/services/system-health.ts index a895a15..a8256c8 100644 --- a/lib/services/system-health.ts +++ b/lib/services/system-health.ts @@ -1,6 +1,12 @@ import { access, constants, readdir } from "node:fs/promises"; import path from "node:path"; import { count, desc, eq } from "drizzle-orm"; +import { + CACHE_DIR, + DATA_DIR, + DATABASE_URL, + TMDB_API_BASE_URL, +} from "@/lib/constants"; import { db } from "@/lib/db/client"; import { cronRuns, episodes, titles, user } from "@/lib/db/schema"; import { listBackups } from "@/lib/services/backup"; @@ -8,15 +14,6 @@ import { imageCacheEnabled } from "@/lib/services/image-cache"; import { getSetting } from "@/lib/services/settings"; import { isUpdateCheckEnabled } from "@/lib/services/update-check"; -const DATA_DIR = process.env.DATA_DIR || "./data"; -const DATABASE_URL = - process.env.DATABASE_URL || path.join(DATA_DIR, "sqlite.db"); -const CACHE_DIR = process.env.CACHE_DIR - ? path.join(process.env.CACHE_DIR, "images") - : path.join(DATA_DIR, "images"); -const TMDB_API_BASE_URL = - process.env.TMDB_API_BASE_URL || "https://api.themoviedb.org/3"; - export interface SystemHealthData { database: { dbSizeBytes: number; diff --git a/lib/tmdb/client.ts b/lib/tmdb/client.ts index 57a525f..eda3758 100644 --- a/lib/tmdb/client.ts +++ b/lib/tmdb/client.ts @@ -1,3 +1,4 @@ +import { TMDB_API_BASE_URL } from "@/lib/constants"; import { createLogger } from "@/lib/logger"; import type { TmdbExternalIds, @@ -18,9 +19,6 @@ import type { } from "./types"; const log = createLogger("tmdb"); - -const BASE_URL = - process.env.TMDB_API_BASE_URL || "https://api.themoviedb.org/3"; function getApiKey() { const key = process.env.TMDB_API_READ_ACCESS_TOKEN; if (!key) throw new Error("TMDB_API_READ_ACCESS_TOKEN is not set"); @@ -32,7 +30,7 @@ async function tmdbFetch( params?: Record, fetchOptions?: RequestInit, ): Promise { - const url = new URL(`${BASE_URL}${path}`); + const url = new URL(`${TMDB_API_BASE_URL}${path}`); if (params) { for (const [k, v] of Object.entries(params)) { url.searchParams.set(k, v);