fix the errors in dashboard

This commit is contained in:
root
2026-05-23 19:03:15 -04:00
parent c6cebdd125
commit f5292f8b6c
29 changed files with 1085 additions and 126 deletions
+13 -1
View File
@@ -78,6 +78,15 @@ export function createApp() {
res.status(404).end()
})
// Public storage assets (logos, vehicle photos, etc.) must be loadable cross-origin
// by the browser. Without this header, helmet's default CORP: same-origin reaches
// 404 responses (when express.static calls next() on a miss) and the browser blocks
// the response even for broken-image cases, producing ERR_BLOCKED_BY_RESPONSE.
app.use('/storage', (_req, res, next) => {
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin')
next()
})
// Serve uploaded assets from the configured storage root, with a legacy fallback
// for older files that were written under the previous API-local path.
app.use('/storage', express.static(getStorageRoot()))
@@ -88,7 +97,10 @@ export function createApp() {
// Webhook must use raw body BEFORE express.json()
app.use('/api/v1/webhooks', express.raw({ type: 'application/json' }), webhookRouter)
app.use(helmet())
// Let /storage responses manage CORP explicitly so missing files still return
// a normal cross-origin 404 instead of being blocked by Helmet's default
// same-origin policy.
app.use(helmet({ crossOriginResourcePolicy: false }))
if (process.env.NODE_ENV !== 'test') app.use(morgan('combined'))
app.use(express.json({ limit: '10mb' }))
+19 -19
View File
@@ -23,7 +23,7 @@ router.post('/auth/login', async (req, res, next) => {
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 })
ok(res, { data: result })
ok(res, result)
} catch (err) { next(err) }
})
@@ -31,7 +31,7 @@ router.post('/auth/forgot-password', async (req, res, next) => {
try {
const { email } = parseBody(forgotPasswordSchema, req)
await service.forgotPassword(email)
ok(res, { data: { message: 'If that email is registered, a reset link has been sent.' } })
ok(res, { message: 'If that email is registered, a reset link has been sent.' })
} catch (err) { next(err) }
})
@@ -40,19 +40,19 @@ router.post('/auth/reset-password', async (req, res, next) => {
const { token, password } = parseBody(resetPasswordSchema, req)
const ok2 = await service.resetPassword(token, password)
if (!ok2) return res.status(400).json({ error: 'invalid_token', message: 'Reset link is invalid or has expired.', statusCode: 400 })
ok(res, { data: { message: 'Password updated successfully. You can now sign in.' } })
ok(res, { message: 'Password updated successfully. You can now sign in.' })
} catch (err) { next(err) }
})
// ─── Auth (protected) ──────────────────────────────────────────
router.get('/auth/me', requireAdminAuth, (req, res) => {
ok(res, { data: presentAdminUser(req.admin as any) })
ok(res, presentAdminUser(req.admin as any))
})
router.post('/auth/2fa/setup', requireAdminAuth, async (req, res, next) => {
try {
ok(res, { data: await service.setupTotp(req.admin.id, req.admin.email) })
ok(res, await service.setupTotp(req.admin.id, req.admin.email))
} catch (err) { next(err) }
})
@@ -61,7 +61,7 @@ router.post('/auth/2fa/verify', requireAdminAuth, async (req, res, next) => {
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, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
@@ -76,7 +76,7 @@ router.get('/companies', requireAdminAuth, async (req, res, next) => {
router.get('/companies/:id', requireAdminAuth, async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, { data: await service.getCompany(id) })
ok(res, await service.getCompany(id))
} catch (err) { next(err) }
})
@@ -84,7 +84,7 @@ router.patch('/companies/:id', requireAdminAuth, requireAdminRole('SUPPORT'), as
try {
const { id } = parseParams(idParamSchema, req)
const body = parseBody(adminCompanyUpdateSchema, req)
ok(res, { data: await service.updateCompany(id, body, req.admin.id, req.ip) })
ok(res, await service.updateCompany(id, body, req.admin.id, req.ip))
} catch (err) { next(err) }
})
@@ -92,7 +92,7 @@ router.patch('/companies/:id/status', requireAdminAuth, requireAdminRole('SUPPOR
try {
const { id } = parseParams(idParamSchema, req)
const { status, reason } = parseBody(companyStatusSchema, req)
ok(res, { data: await service.setCompanyStatus(id, status, reason, req.admin.id, req.ip) })
ok(res, await service.setCompanyStatus(id, status, reason, req.admin.id, req.ip))
} catch (err) { next(err) }
})
@@ -100,14 +100,14 @@ router.delete('/companies/:id', requireAdminAuth, requireAdminRole('ADMIN'), asy
try {
const { id } = parseParams(idParamSchema, req)
await service.deleteCompany(id, req.admin.id, req.ip)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
router.post('/companies/:id/impersonate', requireAdminAuth, requireAdminRole('ADMIN'), async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, { data: await service.impersonateCompany(id, req.admin.id, req.ip) })
ok(res, await service.impersonateCompany(id, req.admin.id, req.ip))
} catch (err) { next(err) }
})
@@ -123,7 +123,7 @@ router.post('/renters/:id/block', requireAdminAuth, requireAdminRole('SUPPORT'),
try {
const { id } = parseParams(idParamSchema, req)
await service.setRenterActive(id, false)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
@@ -131,7 +131,7 @@ router.post('/renters/:id/unblock', requireAdminAuth, requireAdminRole('SUPPORT'
try {
const { id } = parseParams(idParamSchema, req)
await service.setRenterActive(id, true)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
@@ -139,7 +139,7 @@ router.post('/renters/:id/unblock', requireAdminAuth, requireAdminRole('SUPPORT'
router.get('/metrics', requireAdminAuth, requireAdminRole('FINANCE'), async (req, res, next) => {
try {
ok(res, { data: await service.getPlatformMetrics() })
ok(res, await service.getPlatformMetrics())
} catch (err) { next(err) }
})
@@ -155,7 +155,7 @@ router.get('/audit-logs', requireAdminAuth, requireAdminRole('ADMIN'), async (re
router.get('/admins', requireAdminAuth, requireAdminRole('SUPER_ADMIN'), async (req, res, next) => {
try {
ok(res, { data: await service.listAdmins() })
ok(res, await service.listAdmins())
} catch (err) { next(err) }
})
@@ -170,7 +170,7 @@ router.patch('/admins/:id/role', requireAdminAuth, requireAdminRole('SUPER_ADMIN
const { id } = parseParams(idParamSchema, req)
const { role } = parseBody(adminRoleSchema, req)
await service.updateAdminRole(id, role)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
@@ -178,7 +178,7 @@ router.patch('/admins/:id/permissions', requireAdminAuth, requireAdminRole('SUPE
try {
const { id } = parseParams(idParamSchema, req)
const { permissions } = parseBody(adminPermissionsSchema, req)
ok(res, { data: await service.updateAdminPermissions(id, permissions) })
ok(res, await service.updateAdminPermissions(id, permissions))
} catch (err) { next(err) }
})
@@ -212,14 +212,14 @@ router.get('/billing/invoices/:invoiceId/pdf', requireAdminAuth, requireAdminRol
router.get('/site-config/marketplace-homepage', requireAdminAuth, async (req, res, next) => {
try {
ok(res, { data: await service.getMarketplaceHomepage() })
ok(res, await service.getMarketplaceHomepage())
} catch (err) { next(err) }
})
router.patch('/site-config/marketplace-homepage', requireAdminAuth, requireAdminRole('SUPPORT'), async (req, res, next) => {
try {
const { homepage } = parseBody(homepageUpdateSchema, req)
ok(res, { data: await service.updateMarketplaceHomepage(homepage, req.admin.id, req.ip) })
ok(res, await service.updateMarketplaceHomepage(homepage, req.admin.id, req.ip))
} catch (err) { next(err) }
})
@@ -15,19 +15,19 @@ router.use(requireCompanyAuth, requireTenant, requireSubscription)
router.get('/summary', async (req, res, next) => {
try {
const { period } = parseQuery(summaryQuerySchema, req)
ok(res, { data: await service.getSummary(req.companyId, period) })
ok(res, await service.getSummary(req.companyId, period))
} catch (err) { next(err) }
})
router.get('/dashboard', async (req, res, next) => {
try {
ok(res, { data: await service.getDashboard(req.companyId) })
ok(res, await service.getDashboard(req.companyId))
} catch (err) { next(err) }
})
router.get('/sources', async (req, res, next) => {
try {
ok(res, { data: await service.getSources(req.companyId) })
ok(res, await service.getSources(req.companyId))
} catch (err) { next(err) }
})
@@ -42,7 +42,7 @@ router.get('/report', requireRole('MANAGER'), async (req, res, next) => {
res.setHeader('Content-Disposition', `attachment; filename="report-${start}-${end}.csv"`)
return res.send(result.csv)
}
ok(res, { data: result.report })
ok(res, result.report)
} catch (err) { next(err) }
})
@@ -1,27 +1,91 @@
import { prisma } from '../../lib/prisma'
let customerLicenseImageColumnSupported: boolean | null = null
async function hasCustomerLicenseImageColumn() {
if (customerLicenseImageColumnSupported !== null) {
return customerLicenseImageColumnSupported
}
const rows = await prisma.$queryRawUnsafe<Array<{ exists: boolean }>>(
"select exists(select 1 from information_schema.columns where table_schema = 'public' and table_name = 'customers' and column_name = 'licenseImageUrl') as exists"
)
customerLicenseImageColumnSupported = Boolean(rows[0]?.exists)
return customerLicenseImageColumnSupported
}
function buildCustomerSelect(includeLicenseImageUrl: boolean) {
return {
id: true,
companyId: true,
renterId: true,
firstName: true,
lastName: true,
email: true,
phone: true,
driverLicense: true,
dateOfBirth: true,
nationality: true,
address: true,
notes: true,
flagged: true,
flagReason: true,
licenseExpiry: true,
licenseIssuedAt: true,
licenseCountry: true,
licenseNumber: true,
licenseCategory: true,
licenseExpired: true,
licenseExpiringSoon: true,
licenseValidationStatus: true,
licenseApprovedBy: true,
licenseApprovedAt: true,
licenseApprovalNote: true,
...(includeLicenseImageUrl ? { licenseImageUrl: true } : {}),
createdAt: true,
updatedAt: true,
}
}
async function getCustomerSelect() {
return buildCustomerSelect(await hasCustomerLicenseImageColumn())
}
async function sanitizeCustomerData(data: Record<string, unknown>) {
if (await hasCustomerLicenseImageColumn()) {
return data
}
const { licenseImageUrl, ...rest } = data
void licenseImageUrl
return rest as Record<string, unknown>
}
export async function findMany(where: any, skip: number, take: number) {
const select = await getCustomerSelect()
return Promise.all([
prisma.customer.findMany({ where, skip, take, orderBy: { createdAt: 'desc' } }),
prisma.customer.findMany({ where, skip, take, orderBy: { createdAt: 'desc' }, select }),
prisma.customer.count({ where }),
])
}
export async function findById(id: string, companyId: string) {
const select = await getCustomerSelect()
return prisma.customer.findFirst({
where: { id, companyId },
include: {
select: {
...select,
reservations: { include: { vehicle: true }, orderBy: { createdAt: 'desc' }, take: 20 },
},
})
}
export async function findByIdSimple(id: string, companyId: string) {
return prisma.customer.findFirst({ where: { id, companyId } })
return prisma.customer.findFirst({ where: { id, companyId }, select: await getCustomerSelect() })
}
export async function create(data: any) {
return prisma.customer.create({ data })
return prisma.customer.create({ data: (await sanitizeCustomerData(data)) as any, select: await getCustomerSelect() })
}
export async function updateMany(id: string, companyId: string, data: any) {
@@ -29,9 +93,13 @@ export async function updateMany(id: string, companyId: string, data: any) {
}
export async function updateById(id: string, data: any) {
return prisma.customer.update({ where: { id }, data })
return prisma.customer.update({
where: { id },
data: (await sanitizeCustomerData(data)) as any,
select: await getCustomerSelect(),
})
}
export async function findUniqueOrThrow(id: string) {
return prisma.customer.findUniqueOrThrow({ where: { id } })
return prisma.customer.findUniqueOrThrow({ where: { id }, select: await getCustomerSelect() })
}
@@ -17,13 +17,13 @@ const companyAuth = [requireCompanyAuth, requireTenant, requireSubscription] as
router.get('/company', ...companyAuth, async (req, res, next) => {
try {
const { unread } = parseQuery(unreadQuerySchema, req)
ok(res, { data: await service.listCompany(req.companyId, unread) })
ok(res, await service.listCompany(req.companyId, unread))
} catch (err) { next(err) }
})
router.get('/unread-count', ...companyAuth, async (req, res, next) => {
try {
ok(res, { data: { unread: await service.countUnread(req.companyId) } })
ok(res, { unread: await service.countUnread(req.companyId) })
} catch (err) { next(err) }
})
@@ -31,20 +31,20 @@ router.post('/company/:id/read', ...companyAuth, async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
await service.markRead(id, req.companyId)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
router.post('/company/read-all', ...companyAuth, async (req, res, next) => {
try {
await service.markAllRead(req.companyId)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
router.get('/company/preferences', ...companyAuth, async (req, res, next) => {
try {
ok(res, { data: await service.getPreferences(req.employee.id) })
ok(res, await service.getPreferences(req.employee.id))
} catch (err) { next(err) }
})
@@ -52,7 +52,7 @@ router.patch('/company/preferences', ...companyAuth, async (req, res, next) => {
try {
const prefs = parseBody(preferencesSchema, req)
await service.setPreferences(req.employee.id, prefs)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
@@ -60,7 +60,7 @@ router.patch('/company/preferences', ...companyAuth, async (req, res, next) => {
router.get('/renter', requireRenterAuth, async (req, res, next) => {
try {
ok(res, { data: await service.listRenter(req.renterId) })
ok(res, await service.listRenter(req.renterId))
} catch (err) { next(err) }
})
@@ -68,20 +68,20 @@ router.post('/renter/:id/read', requireRenterAuth, async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
await service.markRenterRead(id, req.renterId)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
router.post('/renter/read-all', requireRenterAuth, async (req, res, next) => {
try {
await service.markAllRenterRead(req.renterId)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
router.get('/renter/preferences', requireRenterAuth, async (req, res, next) => {
try {
ok(res, { data: await service.getRenterPreferences(req.renterId) })
ok(res, await service.getRenterPreferences(req.renterId))
} catch (err) { next(err) }
})
@@ -89,7 +89,7 @@ router.patch('/renter/preferences', requireRenterAuth, async (req, res, next) =>
try {
const prefs = parseBody(preferencesSchema, req)
await service.setRenterPreferences(req.renterId, prefs)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
+8 -8
View File
@@ -15,21 +15,21 @@ router.use(requireCompanyAuth, requireTenant, requireSubscription)
router.get('/', async (req, res, next) => {
try {
const query = parseQuery(listQuerySchema, req)
ok(res, { data: await service.listOffers(req.companyId, query) })
ok(res, await service.listOffers(req.companyId, query))
} catch (err) { next(err) }
})
router.post('/', requireRole('MANAGER'), async (req, res, next) => {
try {
const { vehicleIds, ...body } = parseBody(offerSchema, req)
created(res, { data: await service.createOffer(req.companyId, body, vehicleIds) })
created(res, await service.createOffer(req.companyId, body, vehicleIds))
} catch (err) { next(err) }
})
router.get('/:id', async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, { data: await service.getOffer(id, req.companyId) })
ok(res, await service.getOffer(id, req.companyId))
} catch (err) { next(err) }
})
@@ -37,7 +37,7 @@ router.patch('/:id', requireRole('MANAGER'), async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
const { vehicleIds, ...body } = parseBody(offerSchema.partial(), req)
ok(res, { data: await service.updateOffer(id, req.companyId, body, vehicleIds) })
ok(res, await service.updateOffer(id, req.companyId, body, vehicleIds))
} catch (err) { next(err) }
})
@@ -45,7 +45,7 @@ router.delete('/:id', requireRole('MANAGER'), async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
await service.deleteOffer(id, req.companyId)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
@@ -53,7 +53,7 @@ router.post('/:id/activate', requireRole('MANAGER'), async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
await service.setOfferActive(id, req.companyId, true)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
@@ -61,14 +61,14 @@ router.post('/:id/deactivate', requireRole('MANAGER'), async (req, res, next) =>
try {
const { id } = parseParams(idParamSchema, req)
await service.setOfferActive(id, req.companyId, false)
ok(res, { data: { success: true } })
ok(res, { success: true })
} catch (err) { next(err) }
})
router.get('/:id/stats', async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, { data: await service.getOfferStats(id, req.companyId) })
ok(res, await service.getOfferStats(id, req.companyId))
} catch (err) { next(err) }
})
@@ -42,14 +42,14 @@ router.use(requireCompanyAuth, requireTenant, requireSubscription)
router.get('/company', async (req, res, next) => {
try {
ok(res, { data: await service.listByCompany(req.companyId) })
ok(res, await service.listByCompany(req.companyId))
} catch (err) { next(err) }
})
router.get('/reservations/:id', async (req, res, next) => {
try {
const { id } = parseParams(reservationParamSchema, req)
ok(res, { data: await service.listByReservation(id, req.companyId) })
ok(res, await service.listByReservation(id, req.companyId))
} catch (err) { next(err) }
})
@@ -57,7 +57,7 @@ router.post('/reservations/:id/charge', requireRole('MANAGER'), async (req, res,
try {
const { id } = parseParams(reservationParamSchema, req)
const body = parseBody(chargeSchema, req)
ok(res, { data: await service.initCharge(id, req.companyId, body) })
ok(res, await service.initCharge(id, req.companyId, body))
} catch (err) { next(err) }
})
@@ -65,7 +65,7 @@ router.post('/reservations/:id/capture-paypal', requireRole('MANAGER'), async (r
try {
const { id } = parseParams(reservationParamSchema, req)
const { paypalOrderId } = parseBody(capturePaypalSchema, req)
ok(res, { data: await service.capturePaypal(id, req.companyId, paypalOrderId) })
ok(res, await service.capturePaypal(id, req.companyId, paypalOrderId))
} catch (err) { next(err) }
})
@@ -73,7 +73,7 @@ router.post('/reservations/:id/manual', requireRole('MANAGER'), async (req, res,
try {
const { id } = parseParams(reservationParamSchema, req)
const body = parseBody(manualPaymentSchema, req)
ok(res, { data: await service.recordManualPayment(id, req.companyId, body) })
ok(res, await service.recordManualPayment(id, req.companyId, body))
} catch (err) { next(err) }
})
@@ -81,7 +81,7 @@ router.post('/reservations/:reservationId/payments/:paymentId/refund', requireRo
try {
const { reservationId, paymentId } = parseParams(paymentParamSchema, req)
const { amount, reason } = parseBody(refundSchema, req)
ok(res, { data: await service.refundPayment(reservationId, paymentId, req.companyId, amount, reason) })
ok(res, await service.refundPayment(reservationId, paymentId, req.companyId, amount, reason))
} catch (err) { next(err) }
})
@@ -14,11 +14,11 @@ const router = Router()
// ─── Public ────────────────────────────────────────────────────
router.get('/plans', (_req, res) => {
ok(res, { data: service.getPlans() })
ok(res, service.getPlans())
})
router.get('/providers', (_req, res) => {
ok(res, { data: service.getProviders() })
ok(res, service.getProviders())
})
// ─── Webhooks (no auth) ────────────────────────────────────────
@@ -50,7 +50,7 @@ router.post('/webhooks/paypal', async (req, res, next) => {
router.post('/capture-paypal', requireCompanyAuth, requireTenant, async (req, res, next) => {
try {
const { paypalOrderId } = parseBody(capturePaypalSchema, req)
ok(res, { data: await service.capturePaypal(req.companyId, paypalOrderId) })
ok(res, await service.capturePaypal(req.companyId, paypalOrderId))
} catch (err) { next(err) }
})
@@ -60,39 +60,39 @@ router.use(requireCompanyAuth, requireTenant)
router.get('/me', async (req, res, next) => {
try {
ok(res, { data: await service.getSubscription(req.companyId) })
ok(res, await service.getSubscription(req.companyId))
} catch (err) { next(err) }
})
router.get('/invoices', async (req, res, next) => {
try {
ok(res, { data: await service.getInvoices(req.companyId) })
ok(res, await service.getInvoices(req.companyId))
} catch (err) { next(err) }
})
router.post('/checkout', requireRole('OWNER'), async (req, res, next) => {
try {
const body = parseBody(checkoutSchema, req)
ok(res, { data: await service.checkout(req.companyId, body) })
ok(res, await service.checkout(req.companyId, body))
} catch (err) { next(err) }
})
router.post('/change-plan', requireRole('OWNER'), async (req, res, next) => {
try {
const body = parseBody(changePlanSchema, req)
ok(res, { data: await service.changePlan(req.companyId, body) })
ok(res, await service.changePlan(req.companyId, body))
} catch (err) { next(err) }
})
router.post('/cancel', requireRole('OWNER'), async (req, res, next) => {
try {
ok(res, { data: await service.cancel(req.companyId) })
ok(res, await service.cancel(req.companyId))
} catch (err) { next(err) }
})
router.post('/resume', requireRole('OWNER'), async (req, res, next) => {
try {
ok(res, { data: await service.resume(req.companyId) })
ok(res, await service.resume(req.companyId))
} catch (err) { next(err) }
})
+5 -5
View File
@@ -14,7 +14,7 @@ router.use(requireCompanyAuth, requireTenant, requireSubscription)
router.get('/', async (req, res, next) => {
try {
ok(res, { data: await service.getMembers(req.companyId) })
ok(res, await service.getMembers(req.companyId))
} catch (err) { next(err) }
})
@@ -35,28 +35,28 @@ router.patch('/:id/role', requireRole('OWNER'), async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
const { role } = parseBody(roleSchema, req)
ok(res, { data: await service.updateEmployeeRole(req.companyId, req.employee.id, req.employee.role, id, { role }) })
ok(res, await service.updateEmployeeRole(req.companyId, req.employee.id, req.employee.role, id, { role }))
} catch (err) { next(err) }
})
router.post('/:id/deactivate', requireRole('OWNER'), async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, { data: await service.deactivateEmployee(req.companyId, req.employee.role, id) })
ok(res, await service.deactivateEmployee(req.companyId, req.employee.role, id))
} catch (err) { next(err) }
})
router.post('/:id/reactivate', requireRole('OWNER'), async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, { data: await service.reactivateEmployee(req.companyId, req.employee.role, id) })
ok(res, await service.reactivateEmployee(req.companyId, req.employee.role, id))
} catch (err) { next(err) }
})
router.delete('/:id', requireRole('OWNER'), async (req, res, next) => {
try {
const { id } = parseParams(idParamSchema, req)
ok(res, { data: await service.removeEmployee(req.companyId, req.employee.role, id) })
ok(res, await service.removeEmployee(req.companyId, req.employee.role, id))
} catch (err) { next(err) }
})