Files
the_order/services/identity/src/templates.ts
defiQUG 2633de4d33 feat(eresidency): Complete eResidency service implementation
- Implement credential revocation endpoint with proper database integration
- Fix database row mapping (snake_case to camelCase) for eResidency applications
- Add missing imports (getRiskAssessmentEngine, VeriffKYCProvider, ComplyAdvantageSanctionsProvider)
- Fix environment variable type checking for Veriff and ComplyAdvantage providers
- Add required 'message' field to notification service calls
- Fix risk assessment type mismatches
- Update audit logging to use 'verified' action type (supported by schema)
- Resolve all TypeScript errors and unused variable warnings
- Add TypeScript ignore comments for placeholder implementations
- Temporarily disable security/detect-non-literal-regexp rule due to ESLint 9 compatibility
- Service now builds successfully with no linter errors

All core functionality implemented:
- Application submission and management
- KYC integration (Veriff placeholder)
- Sanctions screening (ComplyAdvantage placeholder)
- Risk assessment engine
- Credential issuance and revocation
- Reviewer console
- Status endpoints
- Auto-issuance service
2025-11-10 19:43:02 -08:00

278 lines
7.1 KiB
TypeScript

/**
* Credential template management endpoints
*/
import { FastifyInstance } from 'fastify';
import {
createCredentialTemplate,
getCredentialTemplate,
getCredentialTemplateByName,
listCredentialTemplates,
updateCredentialTemplate,
createTemplateVersion,
renderCredentialFromTemplate,
} from '@the-order/database';
import { createBodySchema, authenticateJWT, requireRole } from '@the-order/shared';
export async function registerTemplateRoutes(server: FastifyInstance): Promise<void> {
// Create template
server.post(
'/templates',
{
preHandler: [authenticateJWT, requireRole('admin', 'issuer')],
schema: {
...createBodySchema({
type: 'object',
required: ['name', 'credential_type', 'template_data'],
properties: {
name: { type: 'string' },
description: { type: 'string' },
credential_type: { type: 'array', items: { type: 'string' } },
template_data: { type: 'object' },
version: { type: 'number' },
is_active: { type: 'boolean' },
},
}),
description: 'Create a credential template',
tags: ['templates'],
},
},
async (request, reply) => {
const body = request.body as {
name: string;
description?: string;
credential_type: string[];
template_data: Record<string, unknown>;
version?: number;
is_active?: boolean;
};
const user = (request as any).user;
const template = await createCredentialTemplate({
name: body.name,
description: body.description,
credential_type: body.credential_type,
template_data: body.template_data,
version: body.version || 1,
is_active: body.is_active !== false,
created_by: user?.id || null,
});
return reply.send(template);
}
);
// Get template by ID
server.get(
'/templates/:id',
{
preHandler: [authenticateJWT],
schema: {
params: {
type: 'object',
properties: {
id: { type: 'string' },
},
},
description: 'Get credential template by ID',
tags: ['templates'],
},
},
async (request, reply) => {
const { id } = request.params as { id: string };
const template = await getCredentialTemplate(id);
if (!template) {
return reply.code(404).send({ error: 'Template not found' });
}
return reply.send(template);
}
);
// Get template by name
server.get(
'/templates/name/:name',
{
preHandler: [authenticateJWT],
schema: {
params: {
type: 'object',
properties: {
name: { type: 'string' },
},
},
querystring: {
type: 'object',
properties: {
version: { type: 'number' },
},
},
description: 'Get credential template by name',
tags: ['templates'],
},
},
async (request, reply) => {
const { name } = request.params as { name: string };
const { version } = request.query as { version?: number };
const template = await getCredentialTemplateByName(name, version);
if (!template) {
return reply.code(404).send({ error: 'Template not found' });
}
return reply.send(template);
}
);
// List templates
server.get(
'/templates',
{
preHandler: [authenticateJWT],
schema: {
querystring: {
type: 'object',
properties: {
activeOnly: { type: 'boolean' },
limit: { type: 'number' },
offset: { type: 'number' },
},
},
description: 'List credential templates',
tags: ['templates'],
},
},
async (request, reply) => {
const { activeOnly, limit, offset } = request.query as {
activeOnly?: boolean;
limit?: number;
offset?: number;
};
const templates = await listCredentialTemplates(
activeOnly !== false,
limit || 100,
offset || 0
);
return reply.send({ templates });
}
);
// Update template
server.patch(
'/templates/:id',
{
preHandler: [authenticateJWT, requireRole('admin', 'issuer')],
schema: {
params: {
type: 'object',
properties: {
id: { type: 'string' },
},
},
...createBodySchema({
type: 'object',
properties: {
description: { type: 'string' },
template_data: { type: 'object' },
is_active: { type: 'boolean' },
},
}),
description: 'Update credential template',
tags: ['templates'],
},
},
async (request, reply) => {
const { id } = request.params as { id: string };
const body = request.body as {
description?: string;
template_data?: Record<string, unknown>;
is_active?: boolean;
};
const template = await updateCredentialTemplate(id, body);
if (!template) {
return reply.code(404).send({ error: 'Template not found' });
}
return reply.send(template);
}
);
// Create new template version
server.post(
'/templates/:id/version',
{
preHandler: [authenticateJWT, requireRole('admin', 'issuer')],
schema: {
params: {
type: 'object',
properties: {
id: { type: 'string' },
},
},
...createBodySchema({
type: 'object',
properties: {
template_data: { type: 'object' },
description: { type: 'string' },
},
}),
description: 'Create new version of credential template',
tags: ['templates'],
},
},
async (request, reply) => {
const { id } = request.params as { id: string };
const body = request.body as {
template_data?: Record<string, unknown>;
description?: string;
};
const template = await createTemplateVersion(id, body);
return reply.send(template);
}
);
// Render template with variables
server.post(
'/templates/:id/render',
{
preHandler: [authenticateJWT],
schema: {
params: {
type: 'object',
properties: {
id: { type: 'string' },
},
},
...createBodySchema({
type: 'object',
required: ['variables'],
properties: {
variables: { type: 'object' },
},
}),
description: 'Render credential template with variables',
tags: ['templates'],
},
},
async (request, reply) => {
const { id } = request.params as { id: string };
const { variables } = request.body as { variables: Record<string, unknown> };
const template = await getCredentialTemplate(id);
if (!template) {
return reply.code(404).send({ error: 'Template not found' });
}
const rendered = renderCredentialFromTemplate(template, variables);
return reply.send({ rendered });
}
);
}