fix: API key save network error — add ENCRYPTION_KEY env and auto-migrate

The "Netzwerkfehler beim Speichern des Schlüssels" was caused by two issues:
1. ENCRYPTION_KEY env var was not passed to the Docker container, so
   AES-256-GCM encrypt() threw at runtime on every POST/PATCH.
2. The 0003_tenant_api_keys migration was not in the drizzle journal
   and no migration runner existed in the Docker image.

Changes:
- docker-compose.yml: pass ENCRYPTION_KEY to app container
- .env.example: document ENCRYPTION_KEY with generation command
- .gitignore: allow .env.example to be tracked
- Dockerfile: include drizzle/ migrations and entrypoint script
- entrypoint.sh: run migrations before starting the app
- migrate.mjs: runtime migration script using drizzle-orm migrator
- drizzle journal: register 0003_tenant_api_keys migration

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
CTO (LegalAI)
2026-04-09 14:28:19 +00:00
parent 2a7db07d46
commit b22bdd8425
7 changed files with 61 additions and 1 deletions

23
.env.example Normal file
View File

@@ -0,0 +1,23 @@
# Database
DATABASE_URL=postgresql://legalai:legalai@localhost:5432/legalai
# Authentication
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-here
# AI Providers (set the one you use: anthropic, openai, ollama)
AI_PROVIDER=anthropic
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
# Ollama (local LLM)
OLLAMA_URL=http://localhost:11434
OLLAMA_MODEL=llama3
# Search
MEILISEARCH_URL=http://localhost:7700
MEILISEARCH_API_KEY=masterKey
# Encryption (required for per-tenant API key storage)
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
ENCRYPTION_KEY=

1
.gitignore vendored
View File

@@ -32,6 +32,7 @@ yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
!.env.example
# vercel
.vercel

View File

@@ -19,8 +19,12 @@ RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./drizzle
COPY --from=builder --chown=nextjs:nodejs /app/migrate.mjs ./migrate.mjs
COPY --from=builder --chown=nextjs:nodejs /app/entrypoint.sh ./entrypoint.sh
RUN chmod +x ./entrypoint.sh
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
ENTRYPOINT ["./entrypoint.sh"]

View File

@@ -12,6 +12,7 @@ services:
- AI_PROVIDER=${AI_PROVIDER:-anthropic}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY}
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
depends_on:
postgres:
condition: service_healthy

View File

@@ -22,6 +22,13 @@
"when": 1775729813628,
"tag": "0002_wide_grandmaster",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1775775900000,
"tag": "0003_tenant_api_keys",
"breakpoints": true
}
]
}

4
entrypoint.sh Normal file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
set -e
node migrate.mjs
exec node server.js

20
migrate.mjs Normal file
View File

@@ -0,0 +1,20 @@
// Runtime migration script — runs drizzle SQL migrations against the database.
// Used by the Docker entrypoint before starting the app.
import { drizzle } from 'drizzle-orm/node-postgres';
import { migrate } from 'drizzle-orm/node-postgres/migrator';
import pg from 'pg';
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
try {
const db = drizzle(pool);
console.log('Running database migrations...');
await migrate(db, { migrationsFolder: './drizzle' });
console.log('Migrations complete.');
} catch (err) {
console.error('Migration failed:', err);
process.exit(1);
} finally {
await pool.end();
}