- 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
278 lines
7.1 KiB
TypeScript
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 });
|
|
}
|
|
);
|
|
}
|
|
|