feat: enhance setup process with SMTP configuration and token generation
Test / backend (push) Successful in 12s
Test / frontend (push) Successful in 23s

fix: require authentication for certain routes and improve exam data handling
chore: update .gitignore to include additional database files
This commit is contained in:
LaurensBartelds committed 2026-04-26 22:53:00 +02:00
1 parent ca9c646b65
commit 83bb412b35
8 files changed
+115 -28

No files matched your search

+5
View File
@@ -1,6 +1,7 @@
import { Router } from 'express'
import rateLimit from 'express-rate-limit'
import { validateBody } from '../middleware/validate.js'
import { requireAuth } from '../middleware/auth.js'
import { asyncHandler } from '../utils/asyncHandler.js'
import * as authService from '../services/authService.js'
import * as userRepo from '../repositories/userRepo.js'
@@ -51,6 +52,7 @@ router.post(
// GET /api/auth/me
router.get(
'/me',
requireAuth,
asyncHandler(async (req: Request, res) => {
const user = authService.getMe(req.user!.userId)
res.json(user)
@@ -60,6 +62,7 @@ router.get(
// PUT /api/auth/me
router.put(
'/me',
requireAuth,
validateBody(updateProfileSchema),
asyncHandler(async (req: Request, res) => {
const user = await authService.updateProfile(req.user!.userId, req.body)
@@ -70,6 +73,7 @@ router.put(
// POST /api/auth/change-password
router.post(
'/change-password',
requireAuth,
validateBody(changePasswordSchema),
asyncHandler(async (req: Request, res) => {
await authService.changePassword(req.user!.userId, req.body)
@@ -112,6 +116,7 @@ router.post(
// DELETE /api/auth/me
router.delete(
'/me',
requireAuth,
asyncHandler(async (req: Request, res) => {
authService.deleteUserAccount(req.user!.userId)
res.json({ success: true })
+2 -1
View File
@@ -31,7 +31,8 @@ router.get(
if (!exam) {
throw new AppError(404, 'Exam not found')
}
res.json(exam)
const parsedExam = JSON.parse(exam.exam_json)
res.json({ ...parsedExam, id: exam.id })
})
)
+46 -10
View File
@@ -6,7 +6,7 @@ import { AppError } from '../middleware/error.js'
import * as userRepo from '../repositories/userRepo.js'
import * as settingsRepo from '../repositories/settingsRepo.js'
import { setupSchema } from '../validators/setupSchemas.js'
import { sendTestEmail } from '../utils/email.js'
import { signToken } from '../utils/tokens.js'
const router = Router()
@@ -41,11 +41,24 @@ router.post(
throw new AppError(400, 'Users already exist')
}
const { email, password, displayName, siteTitle, language } = req.body
const {
email,
password,
displayName,
siteTitle,
language,
smtpHost,
smtpPort,
smtpUser,
smtpPass,
smtpFrom,
smtpSecure,
appUrl,
} = req.body
const passwordHash = await bcrypt.hash(password, SALT_ROUNDS)
userRepo.createUser({
const { id } = userRepo.createUser({
email,
passwordHash,
displayName,
@@ -53,16 +66,39 @@ router.post(
emailVerified: 1,
})
settingsRepo.setSettingsBatch([
['site_title', siteTitle.trim()],
['site_language', language],
const settings: Array<[string, string]> = [
['site_title', siteTitle?.trim() || 'BrainWave'],
['site_language', language || 'en'],
['maintenance_mode', 'false'],
])
]
// Send a test email to the new admin
sendTestEmail(email).catch(() => {})
if (smtpHost !== undefined) settings.push(['smtp_host', smtpHost])
if (smtpPort !== undefined) settings.push(['smtp_port', smtpPort])
if (smtpUser !== undefined) settings.push(['smtp_user', smtpUser])
if (smtpPass !== undefined) settings.push(['smtp_pass', smtpPass])
if (smtpFrom !== undefined) settings.push(['smtp_from', smtpFrom])
if (smtpSecure !== undefined) settings.push(['smtp_secure', smtpSecure])
if (appUrl !== undefined) settings.push(['app_url', appUrl])
res.status(201).json({ success: true })
settingsRepo.setSettingsBatch(settings)
const token = signToken({
userId: id,
email,
displayName,
role: 'admin',
})
res.status(201).json({
message: 'Setup completed successfully',
token,
user: {
id,
email,
displayName,
role: 'admin',
},
})
})
)
+10 -2
View File
@@ -4,11 +4,19 @@ export const setupSchema = z.object({
email: z.string().email().max(255),
password: z.string().min(8, 'Password must be at least 8 characters'),
displayName: z.string().min(1, 'Display name is required').max(100),
siteTitle: z.string().min(1, 'Site title is required').max(255),
language: z.enum(['en', 'nl']),
siteTitle: z.string().min(1, 'Site title is required').max(255).optional(),
language: z.enum(['en', 'nl']).optional(),
smtpHost: z.string().max(255).optional(),
smtpPort: z.string().max(10).optional(),
smtpUser: z.string().max(255).optional(),
smtpPass: z.string().max(255).optional(),
smtpFrom: z.string().max(255).optional(),
smtpSecure: z.enum(['auto', 'true', 'false']).optional(),
appUrl: z.string().max(255).optional(),
})
export const setupStatusSchema = z.object({
email: z.string().email().max(255).optional(),
password: z.string().min(8).optional(),
})