initial commit
Some checks failed
CI / Go — node-agent tests (push) Has been cancelled
CI / Go — edge-gateway build (push) Has been cancelled
CI / Node — lint, typecheck, test, build (push) Has been cancelled

This commit is contained in:
smueller
2026-06-26 10:45:08 +02:00
commit e37ea87b35
118 changed files with 7726 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
HttpStatus,
} from '@nestjs/common';
import type { FastifyReply, FastifyRequest } from 'fastify';
import { createProblemDetails, type ProblemDetails } from '@hexahost/contracts';
@Catch()
export class Rfc7807ExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost): void {
const ctx = host.switchToHttp();
const response = ctx.getResponse<FastifyReply>();
const request = ctx.getRequest<FastifyRequest>();
const status = this.resolveStatus(exception);
const problem = this.toProblemDetails(exception, status, request.url);
void response
.status(status)
.header('Content-Type', 'application/problem+json')
.send(problem);
}
private resolveStatus(exception: unknown): number {
if (exception instanceof HttpException) {
return exception.getStatus();
}
return HttpStatus.INTERNAL_SERVER_ERROR;
}
private toProblemDetails(
exception: unknown,
status: number,
instance: string,
): ProblemDetails {
if (exception instanceof HttpException) {
const response = exception.getResponse();
if (typeof response === 'object' && response !== null) {
const body = response as Record<string, unknown>;
if (typeof body['status'] === 'string' && Array.isArray(body['checks'])) {
return createProblemDetails({
title: 'Service Unavailable',
status,
detail: 'One or more readiness checks failed',
instance,
});
}
if (typeof body['message'] === 'string') {
return createProblemDetails({
title: exception.name,
status,
detail: body['message'],
instance,
});
}
if (Array.isArray(body['message'])) {
return createProblemDetails({
title: 'Validation Error',
status,
detail: 'Request validation failed',
instance,
errors: body['message'].map((message) => ({
message: String(message),
})),
});
}
}
return createProblemDetails({
title: exception.name,
status,
detail: exception.message,
instance,
});
}
if (exception instanceof Error) {
return createProblemDetails({
title: 'Internal Server Error',
status,
detail: exception.message,
instance,
});
}
return createProblemDetails({
title: 'Internal Server Error',
status,
detail: 'An unexpected error occurred',
instance,
});
}
}

View File

@@ -0,0 +1,20 @@
import { randomUUID } from 'node:crypto';
import { Injectable, NestMiddleware } from '@nestjs/common';
import type { FastifyReply, FastifyRequest } from 'fastify';
@Injectable()
export class RequestIdMiddleware implements NestMiddleware {
use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void): void {
const incoming =
(req.headers['x-request-id'] as string | undefined) ??
(req.headers['x-correlation-id'] as string | undefined);
const requestId = incoming && incoming.trim().length > 0 ? incoming : randomUUID();
req.headers['x-request-id'] = requestId;
res.setHeader('x-request-id', requestId);
next();
}
}