Files
jayjennings c69eccb5c1
CI / Lint, typecheck, test, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
feat: implement incident reporting and management system
- Add incident reporting routes and logic to handle creation, updating, and retrieval of incidents.
- Implement visibility rules for users based on roles (reporters, supervisors).
- Create tests for incident reporting functionality to ensure correctness and reliability.
- Introduce site and area management routes, including validation for area hierarchy.
- Establish user management routes with role-based access control for creating and updating users.
- Add setup routes for initializing the application with an organization and admin user.
- Implement shared enums and schemas for consistent validation and data handling across the application.
- Create a test helper for setting up an in-memory database and API client for testing.
- Add TypeScript configuration for server-side code with strict type checking and module resolution.
2026-10-06 10:51:18 -07:00

43 lines
1.8 KiB
TypeScript

import { sql } from 'drizzle-orm'
import { Hono } from 'hono'
import { setupSchema } from '../../shared/schemas.ts'
import { startSession } from '../auth/middleware.ts'
import { hashPassword } from '../auth/password.ts'
import type { Db } from '../db/client.ts'
import { organizations, users } from '../db/schema.ts'
import { audit } from '../lib/audit.ts'
import { validate, type AppEnv } from '../lib/http.ts'
import { publicUser } from './users.ts'
const SETUP_LOCK = 717_001
async function hasUsers(db: Db) {
const rows = await db.select({ id: users.id }).from(users).limit(1)
return rows.length > 0
}
/** First-run setup: creates the organization and its first admin. Only works while there are no users. */
export const setupRoutes = new Hono<AppEnv>()
.get('/', async (c) => c.json({ needsSetup: !(await hasUsers(c.get('db'))) }))
.post('/', validate('json', setupSchema), async (c) => {
const input = c.req.valid('json')
const passwordHash = await hashPassword(input.password)
const user = await c.get('db').transaction(async (tx) => {
await tx.execute(sql`select pg_advisory_xact_lock(${SETUP_LOCK})`)
if (await hasUsers(tx)) return null
const [organization] = await tx.insert(organizations).values({ name: input.organizationName }).returning()
const [admin] = await tx
.insert(users)
.values({ organizationId: organization.id, email: input.email, name: input.name, role: 'admin', passwordHash })
.returning(publicUser)
await audit(tx, { organizationId: organization.id, actorId: admin.id, entityType: 'organization', entityId: organization.id, action: 'setup' })
return admin
})
if (!user) return c.json({ error: 'Setup has already been completed' }, 409)
await startSession(c, user.id)
return c.json({ user }, 201)
})