- New app_config key-value table for system settings, with in-memory cache (mirrors ModuleService pattern) - GET/PATCH /v1/config endpoints for reading and updating config (settings.view/settings.edit permissions) - Runtime log level: PATCH /v1/config/log_level applies immediately, persists across restarts - Startup loads log level from DB in onReady hook (env var is default, DB overrides) - Add structured request.log.info() to POS routes: transaction create/complete/void, drawer open/close, discount create/update/delete Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
49 lines
2.0 KiB
TypeScript
49 lines
2.0 KiB
TypeScript
import type { FastifyPluginAsync } from 'fastify'
|
|
import { AppConfigService } from '../../services/config.service.js'
|
|
import { AppConfigUpdateSchema, LogLevel } from '@lunarfront/shared/schemas'
|
|
|
|
export const configRoutes: FastifyPluginAsync = async (app) => {
|
|
app.get('/config', { preHandler: [app.authenticate, app.requirePermission('settings.view')] }, async (_request, reply) => {
|
|
const configs = await AppConfigService.getAll(app.db)
|
|
return reply.send({ data: configs })
|
|
})
|
|
|
|
app.get('/config/:key', { preHandler: [app.authenticate, app.requirePermission('settings.view')] }, async (request, reply) => {
|
|
const { key } = request.params as { key: string }
|
|
const configs = await AppConfigService.getAll(app.db)
|
|
const entry = configs.find((c) => c.key === key)
|
|
if (!entry) return reply.status(404).send({ error: { message: 'Config key not found', statusCode: 404 } })
|
|
return reply.send(entry)
|
|
})
|
|
|
|
app.patch('/config/:key', { preHandler: [app.authenticate, app.requirePermission('settings.edit')] }, async (request, reply) => {
|
|
const { key } = request.params as { key: string }
|
|
const parsed = AppConfigUpdateSchema.safeParse(request.body)
|
|
if (!parsed.success) {
|
|
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
|
|
}
|
|
|
|
const value = parsed.data.value === null ? null : String(parsed.data.value)
|
|
|
|
// Key-specific validation
|
|
if (key === 'log_level') {
|
|
const levelResult = LogLevel.safeParse(value)
|
|
if (!levelResult.success) {
|
|
return reply.status(400).send({
|
|
error: { message: 'Invalid log level. Must be one of: fatal, error, warn, info, debug, trace', statusCode: 400 },
|
|
})
|
|
}
|
|
}
|
|
|
|
const updated = await AppConfigService.set(app.db, key, value)
|
|
|
|
// Apply log level change immediately
|
|
if (key === 'log_level' && value) {
|
|
app.log.level = value
|
|
request.log.info({ level: value, changedBy: request.user.id }, 'Log level changed')
|
|
}
|
|
|
|
return reply.send(updated)
|
|
})
|
|
}
|