diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-07-27 21:17:55 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-07-27 21:17:55 +0200 |
| commit | 329ab5a990df5e460afa31094e5e827c9743565b (patch) | |
| tree | 8d05dfd75cc56e7e9b30b1daad11892557247b0d | |
| parent | d9a200cd35a3d06173846a45c9f58acb1adf61aa (diff) | |
feat: production Docker image with health check and data persistence
- Non-root user (node), data volume at /app/data
- Startup migration script applies drizzle SQL before server starts
- HEALTHCHECK on port 3000 every 30s
| -rw-r--r-- | Dockerfile | 11 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | scripts/migrate.js | 22 |
3 files changed, 33 insertions, 2 deletions
@@ -11,7 +11,14 @@ WORKDIR /app COPY --from=builder /app/build build/ COPY --from=builder /app/node_modules node_modules/ COPY --from=builder /app/package.json ./ +COPY --from=builder /app/drizzle drizzle/ +COPY --from=builder /app/scripts/migrate.js scripts/migrate.js +RUN mkdir -p /app/data && chown -R node:node /app +VOLUME /app/data EXPOSE 3000 ENV NODE_ENV=production -ENV DATABASE_URL=file:./data.db -CMD ["node", "build"] +ENV DATABASE_URL=file:./data/data.db +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD node -e "require('http').get('http://localhost:3000/', r => {process.exit(r.statusCode === 200 ? 0 : 1)})" +USER node +CMD ["sh", "-c", "node scripts/migrate.js && node build"] @@ -57,3 +57,5 @@ efficient shopping walk. - [ ] Full FR - [ ] Find a design identity +- [ ] Deploy +- [ ] Auth diff --git a/scripts/migrate.js b/scripts/migrate.js new file mode 100644 index 0000000..dee9411 --- /dev/null +++ b/scripts/migrate.js @@ -0,0 +1,22 @@ +import { createClient } from '@libsql/client'; +import { readdirSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +const dbUrl = process.env.DATABASE_URL ?? 'file:./data/data.db'; +const client = createClient({ url: dbUrl }); + +const dir = '/app/drizzle'; +const files = readdirSync(dir) + .filter((f) => f.endsWith('.sql')) + .sort(); + +for (const file of files) { + const sql = readFileSync(join(dir, file), 'utf8'); + const stmts = sql.split('--> statement-breakpoint').filter(Boolean); + for (const stmt of stmts) { + await client.execute(stmt); + } +} + +console.log(`${files.length} migration(s) applied.`); +client.close(); |
