import { mkdirSync, existsSync, readFileSync, writeFileSync, chmodSync, chownSync } from 'node:fs'; import { randomBytes } from 'node:crypto'; import { spawnSync } from 'node:child_process'; const passwordFile = process.env.APP_DATABASE_PASSWORD_FILE; if (!passwordFile) throw new Error('Runtime password file is required.'); const directory = passwordFile.slice(0, passwordFile.lastIndexOf('/')); mkdirSync(directory, { recursive: true, mode: 0o700 }); const password = existsSync(passwordFile) ? readFileSync(passwordFile, 'utf8').trim() : randomBytes(32).toString('hex'); if (!/^[a-f0-9]{64}$/.test(password)) throw new Error('Invalid runtime password file.'); if (!existsSync(passwordFile)) writeFileSync(passwordFile, password + '\n', { flag: 'wx', mode: 0o400 }); chmodSync(passwordFile, 0o400); chownSync(passwordFile, 1001, 1001); chownSync(directory, 1001, 1001); const ownerUrl = new URL('postgresql://postgres:5432/' + encodeURIComponent(process.env.POSTGRES_DB || 'kita_planer')); ownerUrl.username = process.env.POSTGRES_USER || 'kita_planer'; ownerUrl.password = process.env.POSTGRES_PASSWORD || ''; if (!ownerUrl.password) throw new Error('Database owner password is required.'); const environment = { ...process.env, DATABASE_URL: ownerUrl.toString(), APP_DATABASE_PASSWORD: password }; for (const args of [['node_modules/prisma/build/index.js', 'migrate', 'deploy'], ['scripts/provision-runtime-role.mjs']]) { const result = spawnSync(process.execPath, args, { env: environment, stdio: 'inherit' }); if (result.status !== 0) process.exit(result.status || 1); } for (const path of ['/app/uploads', '/app/uploads/announcements', '/app/uploads/announcements/announcements', '/app/runtime-cache']) { mkdirSync(path, { recursive: true, mode: 0o700 }); chownSync(path, 1001, 1001); } console.log('Database initialized; restricted application access and volumes are ready.');