Public Access
feat: update settings management to use site_name and add batch update schema
This commit is contained in:
1 parent
1b5fd04040
commit
6dd87972f6
5 files changed
+44
-13
No files matched your search
@@ -6,7 +6,7 @@ import { AppError } from '../middleware/error.js'
|
||||
import * as userRepo from '../repositories/userRepo.js'
|
||||
import * as resultRepo from '../repositories/resultRepo.js'
|
||||
import * as settingsRepo from '../repositories/settingsRepo.js'
|
||||
import { updateUserRoleSchema, updateSettingSchema, listUsersQuerySchema, userIdParamSchema } from '../validators/adminSchemas.js'
|
||||
import { updateUserRoleSchema, updateSettingSchema, updateSettingsBatchSchema, listUsersQuerySchema, userIdParamSchema } from '../validators/adminSchemas.js'
|
||||
import type { Request } from 'express'
|
||||
|
||||
const router = Router()
|
||||
@@ -124,17 +124,18 @@ router.get(
|
||||
router.get(
|
||||
'/settings',
|
||||
asyncHandler(async (_req, res) => {
|
||||
res.json(settingsRepo.getAllSettings())
|
||||
res.json({ settings: settingsRepo.getAllSettings() })
|
||||
})
|
||||
)
|
||||
|
||||
// PUT /api/admin/settings
|
||||
router.put(
|
||||
'/settings',
|
||||
validateBody(updateSettingSchema),
|
||||
validateBody(updateSettingsBatchSchema),
|
||||
asyncHandler(async (req: Request, res) => {
|
||||
const { key, value } = req.body
|
||||
settingsRepo.setSetting(key, value)
|
||||
const { settings } = req.body
|
||||
const entries = Object.entries(settings) as Array<[string, string]>
|
||||
settingsRepo.setSettingsBatch(entries)
|
||||
res.json({ success: true })
|
||||
})
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ const router = Router()
|
||||
const SALT_ROUNDS = 10
|
||||
|
||||
function getSiteTitle(): string | null {
|
||||
return settingsRepo.getSetting('site_title') ?? null
|
||||
return settingsRepo.getSetting('site_name') ?? null
|
||||
}
|
||||
|
||||
// GET /api/setup/status
|
||||
@@ -38,7 +38,7 @@ router.post(
|
||||
|
||||
// Atomic transaction prevents race conditions between concurrent setup requests
|
||||
const tx = db.transaction(() => {
|
||||
const title = settingsRepo.getSetting('site_title')
|
||||
const title = settingsRepo.getSetting('site_name')
|
||||
if (title) {
|
||||
throw new AppError(400, 'Setup already completed')
|
||||
}
|
||||
@@ -74,7 +74,7 @@ router.post(
|
||||
})
|
||||
|
||||
const settings: Array<[string, string]> = [
|
||||
['site_title', siteTitle?.trim() || 'BrainWave'],
|
||||
['site_name', siteTitle?.trim() || 'BrainWave'],
|
||||
['site_language', language || 'en'],
|
||||
['maintenance_mode', 'false'],
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { updateUserRoleSchema, updateSettingSchema, listUsersQuerySchema, userIdParamSchema } from './adminSchemas'
|
||||
import { updateUserRoleSchema, updateSettingSchema, updateSettingsBatchSchema, listUsersQuerySchema, userIdParamSchema } from './adminSchemas'
|
||||
|
||||
describe('updateUserRoleSchema', () => {
|
||||
it('accepts valid role change', () => {
|
||||
@@ -20,7 +20,7 @@ describe('updateUserRoleSchema', () => {
|
||||
|
||||
describe('updateSettingSchema', () => {
|
||||
it('accepts valid setting', () => {
|
||||
const result = updateSettingSchema.safeParse({ key: 'site_title', value: 'BrainWave' })
|
||||
const result = updateSettingSchema.safeParse({ key: 'site_name', value: 'BrainWave' })
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
@@ -30,7 +30,30 @@ describe('updateSettingSchema', () => {
|
||||
})
|
||||
|
||||
it('rejects empty value', () => {
|
||||
const result = updateSettingSchema.safeParse({ key: 'site_title', value: '' })
|
||||
const result = updateSettingSchema.safeParse({ key: 'site_name', value: '' })
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateSettingsBatchSchema', () => {
|
||||
it('accepts valid batch', () => {
|
||||
const result = updateSettingsBatchSchema.safeParse({
|
||||
settings: { site_name: 'BrainWave', maintenance_mode: 'false' },
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects invalid key in batch', () => {
|
||||
const result = updateSettingsBatchSchema.safeParse({
|
||||
settings: { site_name: 'BrainWave', unknown_key: 'value' },
|
||||
})
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
it('rejects empty value in batch', () => {
|
||||
const result = updateSettingsBatchSchema.safeParse({
|
||||
settings: { site_name: '' },
|
||||
})
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ export const updateUserRoleSchema = z.object({
|
||||
})
|
||||
|
||||
const ALLOWED_SETTING_KEYS = [
|
||||
'site_title',
|
||||
'site_name',
|
||||
'site_language',
|
||||
'maintenance_mode',
|
||||
'maintenance_message',
|
||||
@@ -28,6 +28,13 @@ export const updateSettingSchema = z.object({
|
||||
value: z.string().min(1),
|
||||
})
|
||||
|
||||
export const updateSettingsBatchSchema = z.object({
|
||||
settings: z.record(z.string().min(1)).refine(
|
||||
(obj) => Object.keys(obj).every((k) => ALLOWED_SETTING_KEYS.includes(k as typeof ALLOWED_SETTING_KEYS[number])),
|
||||
{ message: 'One or more setting keys are not allowed' }
|
||||
),
|
||||
})
|
||||
|
||||
export const listUsersQuerySchema = z.object({
|
||||
page: z.string().regex(/^\d+$/).optional().transform((v: string | undefined) => (v ? Number(v) : 1)),
|
||||
search: z.string().optional(),
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":"4.1.5","results":[[":frontend/src/utils/grading.test.ts",{"duration":7.415200000000027,"failed":false}],[":backend/dist/validators/resultSchemas.test.js",{"duration":6.726200000000006,"failed":false}],[":backend/src/validators/authSchemas.test.ts",{"duration":7.416300000000035,"failed":false}],[":backend/src/validators/resultSchemas.test.ts",{"duration":6.132000000000005,"failed":false}],[":backend/dist/validators/authSchemas.test.js",{"duration":7.569299999999998,"failed":false}],[":frontend/src/utils/storage.test.ts",{"duration":7.313600000000008,"failed":false}],[":backend/dist/validators/examSchemas.test.js",{"duration":6.513400000000047,"failed":false}],[":backend/dist/config.test.js",{"duration":3.9307000000000016,"failed":false}],[":backend/src/validators/adminSchemas.test.ts",{"duration":7.670799999999986,"failed":false}],[":frontend/src/utils/latex.test.ts",{"duration":10.730400000000031,"failed":false}],[":backend/dist/validators/adminSchemas.test.js",{"duration":7.168000000000006,"failed":false}],[":backend/src/config.test.ts",{"duration":4.0274,"failed":false}],[":backend/src/validators/examSchemas.test.ts",{"duration":6.865000000000009,"failed":false}],[":backend/dist/utils/tokens.test.js",{"duration":130.3977,"failed":false}],[":frontend/src/utils/validation.test.ts",{"duration":4.94319999999999,"failed":false}],[":backend/src/utils/tokens.test.ts",{"duration":130.18120000000005,"failed":false}],[":frontend/src/utils/slug.test.ts",{"duration":5.06280000000001,"failed":false}],[":backend/src/utils/asyncHandler.test.ts",{"duration":4.779500000000013,"failed":false}],[":backend/src/validators/setupSchemas.test.ts",{"duration":5.795000000000016,"failed":false}],[":backend/dist/validators/setupSchemas.test.js",{"duration":5.715100000000007,"failed":false}],[":backend/dist/middleware/error.test.js",{"duration":3.313999999999993,"failed":false}],[":backend/dist/utils/asyncHandler.test.js",{"duration":5.088700000000017,"failed":false}],[":frontend/src/utils/formatting.test.ts",{"duration":3.638200000000012,"failed":false}],[":backend/src/middleware/error.test.ts",{"duration":3.546299999999974,"failed":false}],[":frontend/src/utils/api.test.ts",{"duration":3.1704000000000008,"failed":false}]]}
|
||||
{"version":"4.1.5","results":[[":frontend/src/utils/grading.test.ts",{"duration":7.013800000000003,"failed":false}],[":backend/dist/validators/resultSchemas.test.js",{"duration":5.808800000000019,"failed":false}],[":backend/src/validators/authSchemas.test.ts",{"duration":7.428100000000029,"failed":false}],[":backend/src/validators/resultSchemas.test.ts",{"duration":5.740099999999984,"failed":false}],[":backend/dist/validators/authSchemas.test.js",{"duration":7.838999999999999,"failed":false}],[":frontend/src/utils/storage.test.ts",{"duration":6.458100000000002,"failed":false}],[":backend/dist/validators/examSchemas.test.js",{"duration":6.4299000000000035,"failed":false}],[":backend/dist/config.test.js",{"duration":3.8840999999999894,"failed":false}],[":backend/src/validators/adminSchemas.test.ts",{"duration":8.53079999999997,"failed":false}],[":frontend/src/utils/latex.test.ts",{"duration":9.639899999999955,"failed":false}],[":backend/dist/validators/adminSchemas.test.js",{"duration":7.222399999999993,"failed":false}],[":backend/src/config.test.ts",{"duration":4.612200000000001,"failed":false}],[":backend/src/validators/examSchemas.test.ts",{"duration":6.7660000000000196,"failed":false}],[":backend/dist/utils/tokens.test.js",{"duration":135.6874,"failed":false}],[":frontend/src/utils/validation.test.ts",{"duration":5.303899999999999,"failed":false}],[":backend/src/utils/tokens.test.ts",{"duration":133.43280000000001,"failed":false}],[":frontend/src/utils/slug.test.ts",{"duration":4.7246000000000095,"failed":false}],[":backend/src/utils/asyncHandler.test.ts",{"duration":5.102000000000004,"failed":false}],[":backend/src/validators/setupSchemas.test.ts",{"duration":5.396100000000018,"failed":false}],[":backend/dist/validators/setupSchemas.test.js",{"duration":5.6451999999999884,"failed":false}],[":backend/dist/middleware/error.test.js",{"duration":2.9522999999999797,"failed":false}],[":backend/dist/utils/asyncHandler.test.js",{"duration":5.4879999999999995,"failed":false}],[":frontend/src/utils/formatting.test.ts",{"duration":3.3067000000000064,"failed":false}],[":backend/src/middleware/error.test.ts",{"duration":3.663899999999984,"failed":false}],[":frontend/src/utils/api.test.ts",{"duration":2.3597999999999786,"failed":false}]]}
|
||||
Reference in new issue
Block a user