feat: add CI/CD pipeline, production Dockerfile, and deployment architecture
- Add production Dockerfile with bun build --compile, multi-stage Alpine build - Add .dockerignore - Swap bcrypt -> bcryptjs (pure JS, no native addons) - Add programmatic migrations on startup via drizzle migrator - Add /v1/version endpoint with APP_VERSION baked in at build time - Add .gitea/workflows/ci.yml (lint + test with postgres/valkey services) - Add .gitea/workflows/build.yml (version bump, build, push to registry) - Update CLAUDE.md and docs/architecture.md to remove multi-tenancy - Add docs/deployment.md covering DOKS + ArgoCD architecture Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import Fastify from 'fastify'
|
||||
import { drizzle } from 'drizzle-orm/postgres-js'
|
||||
import { migrate } from 'drizzle-orm/postgres-js/migrator'
|
||||
import postgres from 'postgres'
|
||||
import rateLimit from '@fastify/rate-limit'
|
||||
import { databasePlugin } from './plugins/database.js'
|
||||
import { redisPlugin } from './plugins/redis.js'
|
||||
@@ -8,6 +11,7 @@ import { authPlugin } from './plugins/auth.js'
|
||||
import { devAuthPlugin } from './plugins/dev-auth.js'
|
||||
import { storagePlugin } from './plugins/storage.js'
|
||||
import { healthRoutes } from './routes/v1/health.js'
|
||||
import { versionRoutes } from './routes/v1/version.js'
|
||||
import { authRoutes } from './routes/v1/auth.js'
|
||||
import { accountRoutes } from './routes/v1/accounts.js'
|
||||
import { inventoryRoutes } from './routes/v1/inventory.js'
|
||||
@@ -92,6 +96,7 @@ export async function buildApp() {
|
||||
|
||||
// Core routes — always available
|
||||
await app.register(healthRoutes, { prefix: '/v1' })
|
||||
await app.register(versionRoutes, { prefix: '/v1' })
|
||||
await app.register(authRoutes, { prefix: '/v1' })
|
||||
await app.register(accountRoutes, { prefix: '/v1' })
|
||||
await app.register(rbacRoutes, { prefix: '/v1' })
|
||||
@@ -138,7 +143,20 @@ export async function buildApp() {
|
||||
return app
|
||||
}
|
||||
|
||||
async function runMigrations() {
|
||||
const connectionString = process.env.DATABASE_URL
|
||||
if (!connectionString) throw new Error('DATABASE_URL is required')
|
||||
|
||||
const migrationsFolder = process.env.MIGRATIONS_DIR ?? './src/db/migrations'
|
||||
const sql = postgres(connectionString, { max: 1 })
|
||||
const db = drizzle(sql)
|
||||
|
||||
await migrate(db, { migrationsFolder })
|
||||
await sql.end()
|
||||
}
|
||||
|
||||
async function start() {
|
||||
await runMigrations()
|
||||
const app = await buildApp()
|
||||
|
||||
const port = parseInt(process.env.PORT ?? '8000', 10)
|
||||
|
||||
Reference in New Issue
Block a user