Implement official Prisma solution for client generation
Following Prisma's official documentation for deployment caching issues: https://www.prisma.io/docs/orm/more/help-and-troubleshooting/vercel-caching-issue Changes: - Add 'prisma generate' to build script (official Prisma recommendation) - Add postinstall script for automatic client generation - Remove custom stub generator workaround - Keep runtime Prisma client generation in entrypoint.sh for reliability - Add openssl to runtime container (required for Prisma engines) This follows Prisma best practices: explicitly run prisma generate during the build process to ensure Prisma Client is always up-to-date. The entrypoint script regenerates the client at runtime to guarantee engine availability in the production environment.
This commit is contained in:
@@ -21,7 +21,8 @@ ENV DATABASE_PATH=/tmp/build.db
|
||||
ENV DATABASE_URL=file:/tmp/build.db
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
RUN npx prisma generate
|
||||
# Build the application (includes prisma generate as per Prisma best practices)
|
||||
# Real Prisma client will be regenerated at runtime to ensure engine availability
|
||||
RUN npx prisma db push --skip-generate
|
||||
RUN npm run build && rm -f /tmp/build.db
|
||||
|
||||
@@ -30,8 +31,11 @@ ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
WORKDIR /app
|
||||
|
||||
# Install gosu for privilege dropping
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends gosu && rm -rf /var/lib/apt/lists/*
|
||||
# Install gosu for privilege dropping and openssl for Prisma
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gosu \
|
||||
openssl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN groupadd -g 1001 nodejs && useradd -r -u 1001 -g nodejs nextjs
|
||||
|
||||
|
||||
@@ -18,6 +18,16 @@ gosu nextjs sh -c '
|
||||
# Set npm cache to writable directory
|
||||
export NPM_CONFIG_CACHE=/tmp/.npm
|
||||
|
||||
# Generate real Prisma client at runtime (replaces build-time stub)
|
||||
echo "Generating Prisma client..."
|
||||
npx prisma generate || {
|
||||
echo "Warning: Prisma generate failed, attempting with checksum ignore..."
|
||||
PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1 npx prisma generate || {
|
||||
echo "Error: Failed to generate Prisma client"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if [ ! -f "$DB_PATH" ]; then
|
||||
echo "Database not found, initializing..."
|
||||
npx prisma db push --skip-generate
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "node scripts/generate-prisma-stub.js && next build",
|
||||
"build": "prisma generate && next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"postinstall": "node scripts/generate-prisma-stub.js || prisma generate || echo 'Prisma generate failed, using stub'"
|
||||
"postinstall": "prisma generate || echo 'Prisma generate skipped'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* This script generates a minimal Prisma Client stub to allow building
|
||||
* when the Prisma engines cannot be downloaded (e.g., network restrictions).
|
||||
*
|
||||
* The actual Prisma engines must be available at runtime.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const clientDir = path.join(__dirname, '..', 'node_modules', '.prisma', 'client');
|
||||
|
||||
// Ensure directory exists
|
||||
fs.mkdirSync(clientDir, { recursive: true });
|
||||
|
||||
// Generate minimal client files
|
||||
const indexContent = `
|
||||
// Auto-generated stub for build-time type checking
|
||||
// Real Prisma Client will be generated at runtime
|
||||
|
||||
class PrismaClient {
|
||||
constructor(options = {}) {
|
||||
this.user = createModelProxy('User');
|
||||
this.session = createModelProxy('Session');
|
||||
this.oAuthState = createModelProxy('OAuthState');
|
||||
this.setting = createModelProxy('Setting');
|
||||
this.accessList = createModelProxy('AccessList');
|
||||
this.accessListEntry = createModelProxy('AccessListEntry');
|
||||
this.certificate = createModelProxy('Certificate');
|
||||
this.proxyHost = createModelProxy('ProxyHost');
|
||||
this.redirectHost = createModelProxy('RedirectHost');
|
||||
this.deadHost = createModelProxy('DeadHost');
|
||||
this.apiToken = createModelProxy('ApiToken');
|
||||
this.auditEvent = createModelProxy('AuditEvent');
|
||||
}
|
||||
|
||||
async $connect() {
|
||||
throw new Error('Prisma Client stub - engines not available at build time');
|
||||
}
|
||||
|
||||
async $disconnect() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async $executeRaw() {
|
||||
throw new Error('Prisma Client stub - engines not available at build time');
|
||||
}
|
||||
|
||||
async $queryRaw() {
|
||||
throw new Error('Prisma Client stub - engines not available at build time');
|
||||
}
|
||||
|
||||
async $transaction() {
|
||||
throw new Error('Prisma Client stub - engines not available at build time');
|
||||
}
|
||||
}
|
||||
|
||||
function createModelProxy(modelName) {
|
||||
return new Proxy({}, {
|
||||
get() {
|
||||
throw new Error(\`Prisma Client stub - \${modelName} operations not available at build time\`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.PrismaClient = PrismaClient;
|
||||
exports.Prisma = {
|
||||
ModelName: {
|
||||
User: 'User',
|
||||
Session: 'Session',
|
||||
OAuthState: 'OAuthState',
|
||||
Setting: 'Setting',
|
||||
AccessList: 'AccessList',
|
||||
AccessListEntry: 'AccessListEntry',
|
||||
Certificate: 'Certificate',
|
||||
ProxyHost: 'ProxyHost',
|
||||
RedirectHost: 'RedirectHost',
|
||||
DeadHost: 'DeadHost',
|
||||
ApiToken: 'ApiToken',
|
||||
AuditEvent: 'AuditEvent'
|
||||
}
|
||||
};
|
||||
`;
|
||||
|
||||
const indexDtsContent = `
|
||||
// Auto-generated stub for build-time type checking
|
||||
|
||||
export class PrismaClient {
|
||||
constructor(options?: any);
|
||||
user: any;
|
||||
session: any;
|
||||
oAuthState: any;
|
||||
setting: any;
|
||||
accessList: any;
|
||||
accessListEntry: any;
|
||||
certificate: any;
|
||||
proxyHost: any;
|
||||
redirectHost: any;
|
||||
deadHost: any;
|
||||
apiToken: any;
|
||||
auditEvent: any;
|
||||
$connect(): Promise<void>;
|
||||
$disconnect(): Promise<void>;
|
||||
$executeRaw(query: any, ...values: any[]): Promise<any>;
|
||||
$queryRaw(query: any, ...values: any[]): Promise<any>;
|
||||
$transaction(fn: any): Promise<any>;
|
||||
}
|
||||
|
||||
export namespace Prisma {
|
||||
export const ModelName: {
|
||||
User: 'User';
|
||||
Session: 'Session';
|
||||
OAuthState: 'OAuthState';
|
||||
Setting: 'Setting';
|
||||
AccessList: 'AccessList';
|
||||
AccessListEntry: 'AccessListEntry';
|
||||
Certificate: 'Certificate';
|
||||
ProxyHost: 'ProxyHost';
|
||||
RedirectHost: 'RedirectHost';
|
||||
DeadHost: 'DeadHost';
|
||||
ApiToken: 'ApiToken';
|
||||
AuditEvent: 'AuditEvent';
|
||||
};
|
||||
}
|
||||
`;
|
||||
|
||||
const defaultJsContent = indexContent;
|
||||
const defaultDtsContent = indexDtsContent;
|
||||
|
||||
// Write files
|
||||
fs.writeFileSync(path.join(clientDir, 'index.js'), indexContent);
|
||||
fs.writeFileSync(path.join(clientDir, 'index.d.ts'), indexDtsContent);
|
||||
fs.writeFileSync(path.join(clientDir, 'default.js'), defaultJsContent);
|
||||
fs.writeFileSync(path.join(clientDir, 'default.d.ts'), defaultDtsContent);
|
||||
fs.writeFileSync(path.join(clientDir, 'edge.js'), defaultJsContent);
|
||||
fs.writeFileSync(path.join(clientDir, 'edge.d.ts'), defaultDtsContent);
|
||||
fs.writeFileSync(path.join(clientDir, 'wasm.js'), defaultJsContent);
|
||||
fs.writeFileSync(path.join(clientDir, 'wasm.d.ts'), defaultDtsContent);
|
||||
fs.writeFileSync(path.join(clientDir, 'index-browser.js'), defaultJsContent);
|
||||
|
||||
console.log('✓ Generated Prisma Client stub for build');
|
||||
console.log('⚠️ Note: Actual Prisma engines must be available at runtime');
|
||||
Reference in New Issue
Block a user