Commit 8605ff94 authored by Hendrik Garske's avatar Hendrik Garske

feat: Automatische Datenbank-Initialisierung ohne .env Datei - Default-Config für Development

parent 0ac1b018
Pipeline #26 passed with stages
in 1 minute and 40 seconds
......@@ -95,33 +95,11 @@ yarn install
pnpm install
```
### 3. Umgebungsvariablen konfigurieren
Erstellen Sie eine `.env` Datei im Root-Verzeichnis:
```bash
cp .env.example .env
```
**WICHTIG**: Bearbeiten Sie die `.env` Datei und setzen Sie die `DATABASE_URL` mit Ihren PostgreSQL-Daten:
```env
DATABASE_URL="postgresql://username:password@localhost:5432/corex_dashboard?schema=public"
NODE_ENV="development"
NEXT_TELEMETRY_DISABLED=1
```
**Für Production/Deployment**: Stellen Sie sicher, dass `DATABASE_URL` in Ihrer Deployment-Umgebung (z.B. CapRover) als Environment Variable gesetzt ist.
### 4. Datenbank Setup
### 3. Datenbank Setup
Siehe [Datenbank Setup](#datenbank-setup) für detaillierte Anweisungen.
### 4. Datenbank Setup
Siehe [Datenbank Setup](#datenbank-setup) für detaillierte Anweisungen.
### 5. Development Server starten
### 4. Development Server starten
```bash
npm run dev
......@@ -129,21 +107,27 @@ npm run dev
Die Anwendung läuft jetzt auf [http://localhost:3000](http://localhost:3000)
## ⚙️ Konfiguration
## 🗄️ Datenbank Setup
### Umgebungsvariablen
Die Anwendung konfiguriert sich automatisch. Die Datenbank-Tabellen werden beim ersten Start automatisch erstellt.
| Variable | Beschreibung | Beispiel |
|----------|--------------|----------|
| `DATABASE_URL` | PostgreSQL Verbindungs-URL | `postgresql://user:pass@localhost:5432/dbname` |
### Automatische Konfiguration
### Datenbank-URL Format
- **Default-Datenbank**: `postgresql://postgres:postgres@localhost:5432/corex_dashboard`
- **Tabellen**: Werden automatisch beim ersten API-Aufruf erstellt
- **Keine .env Datei erforderlich** für lokale Entwicklung
```
postgresql://[user]:[password]@[host]:[port]/[database]?schema=public
### Manuelle Konfiguration (Optional)
Falls Sie eine andere Datenbank verwenden möchten, können Sie optional eine `.env` Datei erstellen:
```env
DATABASE_URL="postgresql://username:password@host:port/database?schema=public"
```
## 🗄️ Datenbank Setup
### Production Deployment
Für Production stellen Sie `DATABASE_URL` in Ihrer Deployment-Umgebung (z.B. CapRover) als Environment Variable ein.
### PostgreSQL Installation (Linux)
......@@ -459,7 +443,6 @@ FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npx prisma generate
RUN npm run build
# Production image, copy all the files and run next
......
import { NextRequest, NextResponse } from "next/server"
import { query } from "@/lib/db"
import { query, initDatabase } from "@/lib/db"
import { rowToCustomer } from "@/lib/utils/db-helpers"
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
// Ensure database is initialized
await initDatabase()
try {
const { id } = await params
......@@ -72,6 +75,9 @@ export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
// Ensure database is initialized
await initDatabase()
try {
const { id } = await params
const body = await request.json()
......@@ -114,6 +120,9 @@ export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
// Ensure database is initialized
await initDatabase()
try {
const { id } = await params
......
import { NextRequest, NextResponse } from "next/server"
import { query } from "@/lib/db"
import { query, initDatabase } from "@/lib/db"
import { generateId, rowToCustomer } from "@/lib/utils/db-helpers"
export async function GET(request: NextRequest) {
// Ensure database is initialized
await initDatabase()
try {
const searchParams = request.nextUrl.searchParams
const search = searchParams.get("search")
......@@ -74,6 +76,9 @@ export async function GET(request: NextRequest) {
}
export async function POST(request: NextRequest) {
// Ensure database is initialized
await initDatabase()
try {
const body = await request.json()
const { name, email, phone, company, address, notes } = body
......
import { NextResponse } from "next/server"
import { initDatabase } from "@/lib/db"
export async function GET() {
try {
await initDatabase()
return NextResponse.json({ success: true, message: "Database initialized" })
} catch (error) {
console.error("Error initializing database:", error)
const errorMessage = error instanceof Error ? error.message : String(error)
return NextResponse.json(
{ success: false, error: errorMessage },
{ status: 500 }
)
}
}
import { NextRequest, NextResponse } from "next/server"
import { query } from "@/lib/db"
import { query, initDatabase } from "@/lib/db"
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
// Ensure database is initialized
await initDatabase()
try {
const { id } = await params
......
import { NextRequest, NextResponse } from "next/server"
import { query } from "@/lib/db"
import { query, initDatabase } from "@/lib/db"
import { generateId, rowToTimeEntry } from "@/lib/utils/db-helpers"
export async function GET(request: NextRequest) {
// Ensure database is initialized
await initDatabase()
try {
const searchParams = request.nextUrl.searchParams
const customerId = searchParams.get("customerId")
......@@ -74,6 +76,9 @@ export async function GET(request: NextRequest) {
}
export async function POST(request: NextRequest) {
// Ensure database is initialized
await initDatabase()
try {
const body = await request.json()
const { description, startTime, endTime, duration, customerId, userId } = body
......
import { NextRequest, NextResponse } from "next/server"
import { query } from "@/lib/db"
import { query, initDatabase } from "@/lib/db"
import { generateId, rowToUser } from "@/lib/utils/db-helpers"
export async function GET() {
// Ensure database is initialized
await initDatabase()
try {
const result = await query(
`SELECT * FROM users ORDER BY "createdAt" DESC`
......@@ -17,6 +19,9 @@ export async function GET() {
}
export async function POST(request: NextRequest) {
// Ensure database is initialized
await initDatabase()
try {
const body = await request.json()
const { email, name, image } = body
......
import { initDatabase } from './db'
// Initialize database on module load (server-side only)
if (typeof window === 'undefined') {
initDatabase().catch((error) => {
console.error('Failed to initialize database on startup:', error)
})
}
......@@ -2,24 +2,28 @@ import { Pool } from 'pg'
let pool: Pool | undefined
// Default database configuration for development
const DEFAULT_DATABASE_URL = 'postgresql://postgres:postgres@localhost:5432/corex_dashboard?schema=public'
// Lazy initialization of the database pool
function getPool(): Pool {
if (!pool) {
const connectionString = process.env.DATABASE_URL
if (!connectionString) {
throw new Error('DATABASE_URL environment variable is not set')
}
const connectionString = process.env.DATABASE_URL || DEFAULT_DATABASE_URL
pool = new Pool({
connectionString,
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
ssl: process.env.NODE_ENV === 'production' && process.env.DATABASE_URL?.includes('sslmode=require')
? { rejectUnauthorized: false }
: false,
})
// Handle pool errors
pool.on('error', (err) => {
console.error('Unexpected error on idle client', err)
process.exit(-1)
// Don't exit in development, just log
if (process.env.NODE_ENV === 'production') {
process.exit(-1)
}
})
}
......@@ -40,3 +44,120 @@ export async function query(text: string, params?: unknown[]) {
return res
}
// Initialize database tables if they don't exist
export async function initDatabase() {
try {
const dbPool = getPool()
// Check if tables exist
const result = await dbPool.query(`
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'customers'
);
`)
if (!result.rows[0].exists) {
console.log('Initializing database tables...')
// Read and execute init SQL
const fs = await import('fs/promises')
const path = await import('path')
try {
const sqlPath = path.join(process.cwd(), 'scripts', 'init-db.sql')
const sql = await fs.readFile(sqlPath, 'utf-8')
// Split by semicolon and execute each statement
const statements = sql
.split(';')
.map(s => s.trim())
.filter(s => s.length > 0 && !s.startsWith('--'))
for (const statement of statements) {
if (statement.trim()) {
await dbPool.query(statement)
}
}
console.log('Database tables initialized successfully')
} catch (fileError) {
console.error('Error reading init-db.sql, trying direct SQL...', fileError)
// Fallback: execute SQL directly
await dbPool.query(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
image TEXT,
"createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS customers (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
email TEXT,
phone TEXT,
company TEXT,
address TEXT,
notes TEXT,
"createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS time_entries (
id TEXT PRIMARY KEY,
description TEXT NOT NULL,
"startTime" TIMESTAMP NOT NULL,
"endTime" TIMESTAMP NOT NULL,
duration INTEGER NOT NULL,
"createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"customerId" TEXT NOT NULL REFERENCES customers(id) ON DELETE CASCADE ON UPDATE CASCADE,
"userId" TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_time_entries_customer_id ON time_entries("customerId");
CREATE INDEX IF NOT EXISTS idx_time_entries_user_id ON time_entries("userId");
CREATE INDEX IF NOT EXISTS idx_time_entries_created_at ON time_entries("createdAt");
`)
// Create trigger function and triggers
await dbPool.query(`
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW."updatedAt" = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
`)
await dbPool.query(`
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
`)
await dbPool.query(`
DROP TRIGGER IF EXISTS update_customers_updated_at ON customers;
CREATE TRIGGER update_customers_updated_at BEFORE UPDATE ON customers
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
`)
await dbPool.query(`
DROP TRIGGER IF EXISTS update_time_entries_updated_at ON time_entries;
CREATE TRIGGER update_time_entries_updated_at BEFORE UPDATE ON time_entries
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
`)
console.log('Database tables initialized successfully (fallback method)')
}
}
} catch (error) {
console.error('Error initializing database:', error)
// Don't throw - allow app to continue, database might not be available yet
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment