fix architecture and write new tests

This commit is contained in:
root
2026-06-10 00:40:19 -04:00
parent 560da1cadf
commit 80a597bc10
377 changed files with 84020 additions and 1337 deletions
+46 -27
View File
@@ -1,7 +1,8 @@
import { Router } from 'express'
import { requireAdminAuth, requireAdminRole } from '../../middleware/requireAdminAuth'
import { requireAdminAuth, requireAdminRole, requireFreshAdmin2FA } from '../../middleware/requireAdminAuth'
import { parseBody, parseQuery, parseParams } from '../../http/validate'
import { ok, created } from '../../http/respond'
import { setSessionCookie, clearSessionCookie } from '../../security/sessionCookies'
import * as service from './admin.service'
import * as subService from '../subscriptions/subscription.service'
import * as menuService from '../menu/menu.service'
@@ -29,6 +30,10 @@ const adminExtendTrialSchema = z.object({
extraDays: z.number().int().positive(),
reason: z.string().min(1).max(500),
})
const adminImpersonationSchema = z.object({
reason: z.string().min(5).max(500),
durationMinutes: z.number().int().min(1).max(30).default(15),
})
const subIdParamSchema = z.object({ subscriptionId: z.string() })
const router = Router()
@@ -37,15 +42,21 @@ const router = Router()
router.post('/auth/login', async (req, res, next) => {
try {
const { email, password, totpCode } = parseBody(loginSchema, req)
const result = await service.login(email, password, totpCode)
const { email, password, totpCode, recoveryCode } = parseBody(loginSchema, req)
const result = await service.login(email, password, totpCode, recoveryCode)
if (!result) return res.status(401).json({ error: 'invalid_credentials', message: 'Invalid email or password', statusCode: 401 })
if ('totpRequired' in result) return res.status(401).json({ error: 'totp_required', message: '2FA code required', statusCode: 401 })
if ('invalidTotp' in result) return res.status(401).json({ error: 'invalid_totp', message: 'Invalid 2FA code', statusCode: 401 })
setSessionCookie(res, 'admin', result.token, 8 * 60 * 60 * 1000)
ok(res, result)
} catch (err) { next(err) }
})
router.post('/auth/logout', (_req, res) => {
clearSessionCookie(res, 'admin')
ok(res, { success: true })
})
router.post('/auth/forgot-password', async (req, res, next) => {
try {
const { email } = parseBody(forgotPasswordSchema, req)
@@ -78,9 +89,16 @@ router.post('/auth/2fa/setup', requireAdminAuth, async (req, res, next) => {
router.post('/auth/2fa/verify', requireAdminAuth, async (req, res, next) => {
try {
const { code } = parseBody(totpVerifySchema, req)
const valid = await service.verifyTotp(req.admin.id, code)
if (!valid) return res.status(400).json({ error: 'invalid_code', message: 'Invalid 2FA code', statusCode: 400 })
ok(res, { success: true })
const result = await service.verifyTotp(req.admin.id, code)
if (!result) return res.status(400).json({ error: 'invalid_code', message: 'Invalid 2FA code', statusCode: 400 })
setSessionCookie(res, 'admin', result.token, 8 * 60 * 60 * 1000)
ok(res, { success: true, admin: result.admin, recoveryCodes: result.recoveryCodes })
} catch (err) { next(err) }
})
router.post('/auth/2fa/recovery-codes/regenerate', requireAdminAuth, requireFreshAdmin2FA, async (req, res, next) => {
try {
ok(res, await service.regenerateRecoveryCodes(req.admin.id))
} catch (err) { next(err) }
})
@@ -115,7 +133,7 @@ router.patch('/companies/:id/status', requireAdminAuth, requireAdminRole('SUPPOR
} catch (err) { next(err) }
})
router.delete('/companies/:id', requireAdminAuth, requireAdminRole('ADMIN'), async (req, res, next) => {
router.delete('/companies/:id', requireAdminAuth, requireAdminRole('ADMIN'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
await service.deleteCompany(id, req.admin.id, req.ip)
@@ -123,10 +141,11 @@ router.delete('/companies/:id', requireAdminAuth, requireAdminRole('ADMIN'), asy
} catch (err) { next(err) }
})
router.post('/companies/:id/impersonate', requireAdminAuth, requireAdminRole('ADMIN'), async (req, res, next) => {
router.post('/companies/:id/impersonate', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, await service.impersonateCompany(id, req.admin.id, req.ip))
const { reason, durationMinutes } = parseBody(adminImpersonationSchema, req)
ok(res, await service.impersonateCompany(id, req.admin.id, req.ip, reason, durationMinutes))
} catch (err) { next(err) }
})
@@ -272,20 +291,20 @@ router.get('/admins', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), async (
} catch (err) { next(err) }
})
router.post('/admins', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), async (req, res, next) => {
router.post('/admins', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), requireFreshAdmin2FA, async (req, res, next) => {
try {
created(res, { data: await service.createAdmin(parseBody(createAdminSchema, req)) })
} catch (err) { next(err) }
})
router.patch('/admins/:id', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), async (req, res, next) => {
router.patch('/admins/:id', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, await service.updateAdmin(id, parseBody(updateAdminSchema, req)))
} catch (err) { next(err) }
})
router.patch('/admins/:id/role', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), async (req, res, next) => {
router.patch('/admins/:id/role', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
const { role } = parseBody(adminRoleSchema, req)
@@ -294,7 +313,7 @@ router.patch('/admins/:id/role', requireAdminAuth, requireAdminRole('SUPER_ADMIN
} catch (err) { next(err) }
})
router.patch('/admins/:id/permissions', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), async (req, res, next) => {
router.patch('/admins/:id/permissions', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
const { permissions } = parseBody(adminPermissionsSchema, req)
@@ -370,7 +389,7 @@ router.post('/billing/invoices/:invoiceId/finalize', requireAdminAuth, requireAd
} catch (err) { next(err) }
})
router.post('/billing/invoices/:invoiceId/pay', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.post('/billing/invoices/:invoiceId/pay', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { invoiceId } = parseParams(invoiceIdParamSchema, req)
ok(res, await service.payBillingInvoice(invoiceId, parseBody(payBillingInvoiceSchema, req), req.admin.id, req.ip))
@@ -407,7 +426,7 @@ router.post('/billing/invoices/:invoiceId/credit-notes', requireAdminAuth, requi
} catch (err) { next(err) }
})
router.post('/billing/invoices/:invoiceId/refunds', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.post('/billing/invoices/:invoiceId/refunds', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { invoiceId } = parseParams(invoiceIdParamSchema, req)
ok(res, await service.issueBillingRefund(invoiceId, parseBody(billingRefundSchema, req), req.admin.id, req.ip))
@@ -422,7 +441,7 @@ router.get('/pricing', requireAdminAuth, requireAdminRole('FINANCE'), async (req
} catch (err) { next(err) }
})
router.patch('/pricing', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.patch('/pricing', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { entries } = parseBody(pricingUpdateSchema, req)
ok(res, await service.updatePricingConfigs(entries, req.admin.id, req.ip))
@@ -435,14 +454,14 @@ router.get('/pricing/features', requireAdminAuth, requireAdminRole('FINANCE'), a
} catch (err) { next(err) }
})
router.post('/pricing/features', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.post('/pricing/features', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const data = parseBody(planFeatureCreateSchema, req)
created(res, await service.createPlanFeature(data, req.admin.id, req.ip))
} catch (err) { next(err) }
})
router.patch('/pricing/features/:featureId', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.patch('/pricing/features/:featureId', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { featureId } = parseParams(planFeatureIdParamSchema, req)
const data = parseBody(planFeatureUpdateSchema, req)
@@ -450,7 +469,7 @@ router.patch('/pricing/features/:featureId', requireAdminAuth, requireAdminRole(
} catch (err) { next(err) }
})
router.delete('/pricing/features/:featureId', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.delete('/pricing/features/:featureId', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { featureId } = parseParams(planFeatureIdParamSchema, req)
await service.deletePlanFeature(featureId, req.admin.id, req.ip)
@@ -466,14 +485,14 @@ router.get('/pricing/promotions', requireAdminAuth, requireAdminRole('FINANCE'),
} catch (err) { next(err) }
})
router.post('/pricing/promotions', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.post('/pricing/promotions', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const data = parseBody(promotionCreateSchema, req)
created(res, await service.createPromotion(data as any, req.admin.id, req.ip))
} catch (err) { next(err) }
})
router.patch('/pricing/promotions/:promotionId', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.patch('/pricing/promotions/:promotionId', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { promotionId } = parseParams(promotionIdParamSchema, req)
const data = parseBody(promotionUpdateSchema, req)
@@ -481,7 +500,7 @@ router.patch('/pricing/promotions/:promotionId', requireAdminAuth, requireAdminR
} catch (err) { next(err) }
})
router.delete('/pricing/promotions/:promotionId', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
router.delete('/pricing/promotions/:promotionId', requireAdminAuth, requireAdminRole('FINANCE'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { promotionId } = parseParams(promotionIdParamSchema, req)
await service.deletePromotion(promotionId, req.admin.id, req.ip)
@@ -498,7 +517,7 @@ router.get('/subscriptions/:subscriptionId/events', requireAdminAuth, requireAdm
} catch (err) { next(err) }
})
router.post('/subscriptions/:subscriptionId/extend-trial', requireAdminAuth, requireAdminRole('SUPPORT'), async (req, res, next) => {
router.post('/subscriptions/:subscriptionId/extend-trial', requireAdminAuth, requireAdminRole('SUPPORT'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = parseParams(subIdParamSchema, req)
const { extraDays, reason } = parseBody(adminExtendTrialSchema, req)
@@ -506,7 +525,7 @@ router.post('/subscriptions/:subscriptionId/extend-trial', requireAdminAuth, req
} catch (err) { next(err) }
})
router.post('/subscriptions/:subscriptionId/extend-grace-period', requireAdminAuth, requireAdminRole('SUPPORT'), async (req, res, next) => {
router.post('/subscriptions/:subscriptionId/extend-grace-period', requireAdminAuth, requireAdminRole('SUPPORT'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = parseParams(subIdParamSchema, req)
const { reason } = parseBody(adminSubOverrideSchema, req)
@@ -514,7 +533,7 @@ router.post('/subscriptions/:subscriptionId/extend-grace-period', requireAdminAu
} catch (err) { next(err) }
})
router.post('/subscriptions/:subscriptionId/suspend', requireAdminAuth, requireAdminRole('SUPPORT'), async (req, res, next) => {
router.post('/subscriptions/:subscriptionId/suspend', requireAdminAuth, requireAdminRole('SUPPORT'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = parseParams(subIdParamSchema, req)
const { reason } = parseBody(adminSubOverrideSchema, req)
@@ -522,7 +541,7 @@ router.post('/subscriptions/:subscriptionId/suspend', requireAdminAuth, requireA
} catch (err) { next(err) }
})
router.post('/subscriptions/:subscriptionId/reactivate', requireAdminAuth, requireAdminRole('SUPPORT'), async (req, res, next) => {
router.post('/subscriptions/:subscriptionId/reactivate', requireAdminAuth, requireAdminRole('SUPPORT'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = parseParams(subIdParamSchema, req)
const { reason } = parseBody(adminSubOverrideSchema, req)
@@ -530,7 +549,7 @@ router.post('/subscriptions/:subscriptionId/reactivate', requireAdminAuth, requi
} catch (err) { next(err) }
})
router.post('/subscriptions/:subscriptionId/cancel', requireAdminAuth, requireAdminRole('SUPPORT'), async (req, res, next) => {
router.post('/subscriptions/:subscriptionId/cancel', requireAdminAuth, requireAdminRole('SUPPORT'), requireFreshAdmin2FA, async (req, res, next) => {
try {
const { subscriptionId } = parseParams(subIdParamSchema, req)
const { reason } = parseBody(adminSubOverrideSchema, req)