Add user auth with JWT, switch to bun test

- User table with company_id FK, unique email, role enum
- Register/login routes with bcrypt + JWT token generation
- Auth plugin with authenticate decorator and role guards
- Login uses globally unique email (no company header needed)
- Dev-auth plugin kept as fallback when JWT_SECRET not set
- Switched from vitest to bun:test (vitest had ESM resolution
  issues with zod in Bun's module structure)
- Upgraded to zod 4
- Added Dockerfile.dev and API service to docker-compose
- 8 tests passing (health + auth)
This commit is contained in:
Ryan Moon
2026-03-27 17:33:05 -05:00
parent c1cddd6b74
commit 979a9a2c00
28 changed files with 1181 additions and 39 deletions

View File

@@ -3,8 +3,10 @@ import { databasePlugin } from './plugins/database.js'
import { redisPlugin } from './plugins/redis.js'
import { corsPlugin } from './plugins/cors.js'
import { errorHandlerPlugin } from './plugins/error-handler.js'
import { authPlugin } from './plugins/auth.js'
import { devAuthPlugin } from './plugins/dev-auth.js'
import { healthRoutes } from './routes/v1/health.js'
import { authRoutes } from './routes/v1/auth.js'
export async function buildApp() {
const app = Fastify({
@@ -20,10 +22,17 @@ export async function buildApp() {
await app.register(errorHandlerPlugin)
await app.register(databasePlugin)
await app.register(redisPlugin)
await app.register(devAuthPlugin)
// Auth — use JWT if secret is set, otherwise dev bypass
if (process.env.JWT_SECRET) {
await app.register(authPlugin)
} else {
await app.register(devAuthPlugin)
}
// Routes
await app.register(healthRoutes, { prefix: '/v1' })
await app.register(authRoutes, { prefix: '/v1' })
return app
}
@@ -42,4 +51,7 @@ async function start() {
}
}
start()
// Only auto-start when not imported by tests
if (process.env.NODE_ENV !== 'test') {
start()
}