Add member identifiers table for ID documents (DL, passport, school ID)

member_identifier table with type, value, issuing authority, expiry,
front/back image storage (base64 in Postgres), primary flag. CRUD
endpoints under /members/:memberId/identifiers. Zod schemas with
constrained type enum.
This commit is contained in:
Ryan Moon
2026-03-28 09:38:01 -05:00
parent 727275af59
commit c7b460c0bf
7 changed files with 208 additions and 0 deletions

View File

@@ -11,10 +11,13 @@ import {
PaymentMethodUpdateSchema,
TaxExemptionCreateSchema,
TaxExemptionUpdateSchema,
MemberIdentifierCreateSchema,
MemberIdentifierUpdateSchema,
} from '@forte/shared/schemas'
import {
AccountService,
MemberService,
MemberIdentifierService,
ProcessorLinkService,
PaymentMethodService,
TaxExemptionService,
@@ -129,6 +132,42 @@ export const accountRoutes: FastifyPluginAsync = async (app) => {
return reply.send(member)
})
// --- Member Identifiers ---
app.post('/members/:memberId/identifiers', { preHandler: [app.authenticate] }, async (request, reply) => {
const { memberId } = request.params as { memberId: string }
const parsed = MemberIdentifierCreateSchema.safeParse({ ...(request.body as object), memberId })
if (!parsed.success) {
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
}
const identifier = await MemberIdentifierService.create(app.db, request.companyId, parsed.data)
return reply.status(201).send(identifier)
})
app.get('/members/:memberId/identifiers', { preHandler: [app.authenticate] }, async (request, reply) => {
const { memberId } = request.params as { memberId: string }
const identifiers = await MemberIdentifierService.listByMember(app.db, request.companyId, memberId)
return reply.send({ data: identifiers })
})
app.patch('/identifiers/:id', { preHandler: [app.authenticate] }, async (request, reply) => {
const { id } = request.params as { id: string }
const parsed = MemberIdentifierUpdateSchema.safeParse(request.body)
if (!parsed.success) {
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
}
const identifier = await MemberIdentifierService.update(app.db, request.companyId, id, parsed.data)
if (!identifier) return reply.status(404).send({ error: { message: 'Identifier not found', statusCode: 404 } })
return reply.send(identifier)
})
app.delete('/identifiers/:id', { preHandler: [app.authenticate] }, async (request, reply) => {
const { id } = request.params as { id: string }
const identifier = await MemberIdentifierService.delete(app.db, request.companyId, id)
if (!identifier) return reply.status(404).send({ error: { message: 'Identifier not found', statusCode: 404 } })
return reply.send(identifier)
})
app.delete('/members/:id', { preHandler: [app.authenticate] }, async (request, reply) => {
const { id } = request.params as { id: string }
const member = await MemberService.delete(app.db, request.companyId, id)