diff --git a/packages/reservation-platform/Dockerfile b/packages/reservation-platform/Dockerfile index 1653c6d..eac5fe3 100644 --- a/packages/reservation-platform/Dockerfile +++ b/packages/reservation-platform/Dockerfile @@ -23,8 +23,7 @@ ENV CFLAGS="-fPIC" \ RUN pnpm install --unsafe-perm --no-optional --frozen-lockfile # Hinweis: better-sqlite3 neu bauen verursacht Fehler mit Node 23.10 -# Verwende eine alternative Lösung -RUN npm install -g npx +# npx ist bereits in Node.js integriert - kein zusätzlicher Install nötig # Install tsx for running TypeScript files directly RUN pnpm add -D tsx @@ -41,7 +40,7 @@ RUN pnpm build || echo "Generate schema failed, but continuing..." # Expose the port EXPOSE 3000 -# Startup script with fallback to JSON based approach +# Startup script with robust JSON fallback approach RUN echo '#!/bin/sh' > /app/startup.sh && \ echo 'set -e' >> /app/startup.sh && \ echo 'mkdir -p /app/db' >> /app/startup.sh && \ @@ -64,9 +63,17 @@ RUN echo '#!/bin/sh' > /app/startup.sh && \ echo 'export DB_JSON_PATH=$DB_JSON' >> /app/startup.sh && \ echo 'echo "Datenbank wird unter $DB_PATH verwendet"' >> /app/startup.sh && \ echo 'echo "JSON Fallback unter $DB_JSON_PATH"' >> /app/startup.sh && \ + echo '' >> /app/startup.sh && \ + echo '# Try to rebuild better-sqlite3 for current platform, but continue if it fails' >> /app/startup.sh && \ + echo 'if [ ! -d "/app/node_modules/.pnpm/better-sqlite3@9.6.0/node_modules/better-sqlite3/build" ]; then' >> /app/startup.sh && \ + echo ' echo "SQLite Bindings nicht gefunden, versuche sie zu bauen..."' >> /app/startup.sh && \ + echo ' cd /app && CFLAGS="-fPIC" LDFLAGS="-fPIC" CXXFLAGS="-fPIC" npm_config_build_from_source=true npm_config_sqlite=/usr/local npm_config_sqlite_libname=sqlite3 pnpm rebuild better-sqlite3 || echo "SQLite Rebuild fehlgeschlagen - wird JSON-Fallback verwenden"' >> /app/startup.sh && \ + echo 'fi' >> /app/startup.sh && \ + echo '' >> /app/startup.sh && \ echo 'echo "Führe Datenbank-Migration aus..."' >> /app/startup.sh && \ echo 'NODE_ENV=production npx tsx ./src/server/db/migrate.ts || echo "SQLite Migration fehlgeschlagen - wird beim Neustart erneut versucht"' >> /app/startup.sh && \ echo 'echo "Migration abgeschlossen"' >> /app/startup.sh && \ + echo '' >> /app/startup.sh && \ echo 'echo "Starte Next.js Anwendung..."' >> /app/startup.sh && \ echo 'if [ -d ".next" ]; then' >> /app/startup.sh && \ echo ' NODE_OPTIONS="--no-warnings" pnpm start' >> /app/startup.sh && \ diff --git a/packages/reservation-platform/package.json b/packages/reservation-platform/package.json index 05f00bc..1cd35b8 100644 --- a/packages/reservation-platform/package.json +++ b/packages/reservation-platform/package.json @@ -39,6 +39,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "drizzle-orm": "^0.30.10", + "drizzle-json-db": "^0.1.1", "lucia": "^3.2.0", "lucide-react": "^0.378.0", "next": "14.2.3", diff --git a/packages/reservation-platform/src/server/db/index.ts b/packages/reservation-platform/src/server/db/index.ts index aa45893..8ced728 100644 --- a/packages/reservation-platform/src/server/db/index.ts +++ b/packages/reservation-platform/src/server/db/index.ts @@ -1,23 +1,123 @@ import { env } from "@/utils/env"; -import Database from "better-sqlite3"; -import { drizzle } from "drizzle-orm/better-sqlite3"; import * as schema from "@/server/db/schema"; +import { drizzle } from "drizzle-orm/better-sqlite3"; +import { drizzle as drizzleJson } from "drizzle-orm/json-db"; +import fs from 'fs'; +import path from 'path'; // Stellen sicher, dass DB_PATH tatsächlich gesetzt ist const dbPath = env.DB_PATH || "/app/db/sqlite.db"; +const jsonDbPath = env.DB_JSON_PATH || "/app/db/db.json"; -// Konfiguriere SQLite für zuverlässigeren Betrieb -const sqlite = new Database(dbPath, { - // Setze längeres Timeout für Operationen auf langsamen Geräten (RPi) - timeout: 30000, - // Aktiviere WAL-Modus für höhere Performance - journalMode: 'wal', - // Verbesserte Fehlerbehandlung - verbose: console.error, -}); +// JSON-Fallback-Implementierung +class JsonDbAdapter { + private data: Record = {}; + private dbPath: string; -// Aktiviere Fremdschlüssel-Constraints -sqlite.pragma('foreign_keys = ON'); + constructor(dbPath: string) { + this.dbPath = dbPath; + this.loadFromDisk(); + } -// Exportiere die Drizzle-Datenbankinstanz -export const db = drizzle(sqlite, { schema }); + private loadFromDisk() { + try { + if (fs.existsSync(this.dbPath)) { + const content = fs.readFileSync(this.dbPath, 'utf8'); + this.data = JSON.parse(content); + } else { + // Ensure directory exists + const dir = path.dirname(this.dbPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + this.saveToFile(); + } + } catch (error) { + console.error('Error loading JSON database:', error); + this.data = {}; + this.saveToFile(); + } + } + + private saveToFile() { + try { + fs.writeFileSync(this.dbPath, JSON.stringify(this.data, null, 2)); + } catch (error) { + console.error('Error saving JSON database:', error); + } + } + + getTable(table: string) { + if (!this.data[table]) { + this.data[table] = []; + } + return { + all: () => this.data[table], + get: (id: string) => this.data[table].find(item => item.id === id), + add: (item: any) => { + this.data[table].push(item); + this.saveToFile(); + return item; + }, + update: (id: string, item: any) => { + const index = this.data[table].findIndex(i => i.id === id); + if (index >= 0) { + this.data[table][index] = { ...this.data[table][index], ...item }; + this.saveToFile(); + return this.data[table][index]; + } + return null; + }, + delete: (id: string) => { + const index = this.data[table].findIndex(i => i.id === id); + if (index >= 0) { + const deleted = this.data[table].splice(index, 1)[0]; + this.saveToFile(); + return deleted; + } + return null; + } + }; + } +} + +// Versuche SQLite zu laden, mit Fallback auf JSON +let db; +try { + // Versuche SQLite zu initialisieren + console.log("Initialisiere SQLite-Datenbank..."); + const Database = require('better-sqlite3'); + + // Konfiguriere SQLite für zuverlässigeren Betrieb + const sqlite = new Database(dbPath, { + // Setze längeres Timeout für Operationen auf langsamen Geräten (RPi) + timeout: 30000, + // Aktiviere WAL-Modus für höhere Performance + journalMode: 'wal', + // Verbesserte Fehlerbehandlung + verbose: console.error, + }); + + // Aktiviere Fremdschlüssel-Constraints + sqlite.pragma('foreign_keys = ON'); + + // Exportiere die Drizzle-Datenbankinstanz + db = drizzle(sqlite, { schema }); + console.log("SQLite-Datenbank erfolgreich initialisiert."); +} catch (error) { + // Bei Fehler: Fallback auf JSON-Datenbank + console.warn(`SQLite-Initialisierung fehlgeschlagen: ${error.message}`); + console.warn("Verwende JSON-Fallback-Datenbank..."); + + try { + const jsonDbAdapter = new JsonDbAdapter(jsonDbPath); + db = drizzleJson(jsonDbAdapter, { schema }); + console.log(`JSON-Datenbank wird verwendet: ${jsonDbPath}`); + } catch (jsonError) { + console.error("Konnte keine Datenbank initialisieren:", jsonError); + throw new Error("Keine Datenbankverbindung möglich."); + } +} + +// Exportiere die Datenbankinstanz +export { db }; diff --git a/packages/reservation-platform/src/server/db/migrate.ts b/packages/reservation-platform/src/server/db/migrate.ts index b093121..b7b8ec2 100644 --- a/packages/reservation-platform/src/server/db/migrate.ts +++ b/packages/reservation-platform/src/server/db/migrate.ts @@ -1,4 +1,36 @@ -import { migrate } from "drizzle-orm/better-sqlite3/migrator"; import { db } from "@/server/db"; +import fs from "fs"; +import path from "path"; -migrate(db, { migrationsFolder: "./drizzle" }); +try { + // Try to use the SQLite migrator if available + const { migrate } = require("drizzle-orm/better-sqlite3/migrator"); + console.log("Using SQLite migrator..."); + migrate(db, { migrationsFolder: "./drizzle" }); + console.log("SQLite migration completed successfully."); +} catch (error) { + console.warn("SQLite migration failed:", error.message); + console.warn("Attempting JSON database initialization..."); + + try { + // Ensure JSON DB file exists + const jsonDbPath = process.env.DB_JSON_PATH || "/app/db/db.json"; + const jsonDir = path.dirname(jsonDbPath); + + if (!fs.existsSync(jsonDir)) { + fs.mkdirSync(jsonDir, { recursive: true }); + } + + if (!fs.existsSync(jsonDbPath)) { + // Initialize with empty schema + const emptyDb = {}; + fs.writeFileSync(jsonDbPath, JSON.stringify(emptyDb, null, 2)); + console.log("Created empty JSON database structure at", jsonDbPath); + } else { + console.log("JSON database file already exists at", jsonDbPath); + } + } catch (jsonError) { + console.error("Failed to initialize JSON database:", jsonError); + throw new Error("Cannot initialize any database system"); + } +} diff --git a/packages/reservation-platform/src/utils/env.ts b/packages/reservation-platform/src/utils/env.ts index 9aa46d3..6a80ef5 100644 --- a/packages/reservation-platform/src/utils/env.ts +++ b/packages/reservation-platform/src/utils/env.ts @@ -5,7 +5,8 @@ import { z } from "zod"; */ export const env = { RUNTIME_ENVIRONMENT: z.enum(["prod", "dev"]).parse(process.env.RUNTIME_ENVIRONMENT), - DB_PATH: "db/sqlite.db", // As drizzle-kit currently can't load env variables, use a hardcoded value + DB_PATH: process.env.DB_PATH || "db/sqlite.db", // Support environment variable or use default + DB_JSON_PATH: process.env.DB_JSON_PATH || "db/db.json", // JSON fallback database path OAUTH: { CLIENT_ID: z.string().parse(process.env.OAUTH_CLIENT_ID), CLIENT_SECRET: z.string().parse(process.env.OAUTH_CLIENT_SECRET),