feat(betrieb): Abbild, Compose-Stapel und Betriebsskripte
Dreistufiges Dockerfile mit standalone-Ausgabe; im Laufzeitabbild liegen weder Quelltext noch Bauwerkzeuge, und es läuft unter einem eigenen Benutzer. Die Migrationen erledigt ein eigener Einmaldienst aus der Bau-Stufe. Der Versuch, nur die Prisma-CLI ins Laufzeitabbild zu kopieren, scheiterte an ihren transitiven Modulen; sie alle mitzunehmen hätte das Abbild aufgebläht. Der Dienst läuft vor der App und muss sauber durchlaufen. Zwei Stellen mussten faul werden, weil Next beim Bauen Seitendaten sammelt und dabei Module lädt, die es zur Bauzeit noch ohne Umgebungsvariablen gibt: - Der Prisma-Client entsteht erst beim ersten Zugriff, über einen Proxy. - Die Auth.js-Konfiguration wird als Funktion übergeben. Ebenso liest prisma.config.ts die URL über process.env statt über env(), das beim Bauen ohne Datenbank abbricht. Dazu backup.sh, update.sh mit Rückrollpfad und scripts/release.sh. Der Dump wurde nachweislich in eine Wegwerf-Datenbank zurückgespielt und stimmt zeilengenau mit dem Original überein. Alle Skripte sind shellcheck-sauber. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
126ad93301
commit
f91d015cc4
@@ -3,8 +3,11 @@ import NextAuth from 'next-auth'
|
||||
import { authKonfiguration } from '@/server/auth/config'
|
||||
|
||||
/**
|
||||
* Einstiegspunkt fuer Auth.js. Die Konfiguration wird erst hier ausgewertet,
|
||||
* damit fehlende Umgebungsvariablen beim Start auffallen und nicht beim
|
||||
* Einlesen des Moduls.
|
||||
* Einstiegspunkt für Auth.js.
|
||||
*
|
||||
* Die Konfiguration wird als Funktion übergeben und dadurch erst bei der
|
||||
* ersten Anfrage ausgewertet. Sie prüft Umgebungsvariablen, die es beim Bauen
|
||||
* des Abbilds noch nicht gibt – bei sofortiger Auswertung bräche der Bau ab,
|
||||
* sobald Next die Seitendaten der Auth-Routen sammelt.
|
||||
*/
|
||||
export const { handlers, auth, signIn, signOut } = NextAuth(authKonfiguration())
|
||||
export const { handlers, auth, signIn, signOut } = NextAuth(() => authKonfiguration())
|
||||
|
||||
+26
-10
@@ -3,13 +3,19 @@ import { PrismaPg } from '@prisma/adapter-pg'
|
||||
import { PrismaClient } from '@/generated/prisma/client'
|
||||
|
||||
/**
|
||||
* Ein einziger Prisma-Client je Prozess. Im Entwicklungsbetrieb ueberlebt er
|
||||
* das Neuladen der Module, damit nicht bei jedem Speichern ein neuer
|
||||
* Verbindungspool entsteht.
|
||||
* Ein einziger Prisma-Client je Prozess.
|
||||
*
|
||||
* Der Client wird erst beim ersten Zugriff erzeugt, nicht beim Laden des
|
||||
* Moduls. Beim Bauen des Abbilds gibt es noch keine DATABASE_URL – Next sammelt
|
||||
* dabei aber Seitendaten und lädt dieses Modul. Würde hier sofort verbunden,
|
||||
* bräche der Bau ab.
|
||||
*
|
||||
* Im Entwicklungsbetrieb überlebt der Client das Neuladen der Module, damit
|
||||
* nicht bei jedem Speichern ein neuer Verbindungspool entsteht.
|
||||
*/
|
||||
|
||||
const globalerBereich = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined
|
||||
planerPrisma: PrismaClient | undefined
|
||||
}
|
||||
|
||||
function erzeugen(): PrismaClient {
|
||||
@@ -17,15 +23,25 @@ function erzeugen(): PrismaClient {
|
||||
if (!verbindung) {
|
||||
throw new Error('DATABASE_URL ist nicht gesetzt.')
|
||||
}
|
||||
const adapter = new PrismaPg({ connectionString: verbindung })
|
||||
return new PrismaClient({
|
||||
adapter,
|
||||
adapter: new PrismaPg({ connectionString: verbindung }),
|
||||
log: process.env.NODE_ENV === 'development' ? ['warn', 'error'] : ['error'],
|
||||
})
|
||||
}
|
||||
|
||||
export const db: PrismaClient = globalerBereich.prisma ?? erzeugen()
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
globalerBereich.prisma = db
|
||||
function klient(): PrismaClient {
|
||||
globalerBereich.planerPrisma ??= erzeugen()
|
||||
return globalerBereich.planerPrisma
|
||||
}
|
||||
|
||||
export const db: PrismaClient = new Proxy({} as PrismaClient, {
|
||||
get(_ziel, eigenschaft) {
|
||||
const echt = klient() as unknown as Record<string | symbol, unknown>
|
||||
const wert = echt[eigenschaft]
|
||||
// Methoden an den echten Client binden, sonst zeigt "this" auf den Proxy.
|
||||
if (typeof wert === 'function') {
|
||||
return (wert as (...argumente: unknown[]) => unknown).bind(echt)
|
||||
}
|
||||
return wert
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user