@open-skills-hub/api 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/controllers/audit.d.ts +33 -0
  2. package/dist/controllers/audit.d.ts.map +1 -0
  3. package/dist/controllers/audit.js +122 -0
  4. package/dist/controllers/audit.js.map +1 -0
  5. package/dist/controllers/cache.d.ts +42 -0
  6. package/dist/controllers/cache.d.ts.map +1 -0
  7. package/dist/controllers/cache.js +247 -0
  8. package/dist/controllers/cache.js.map +1 -0
  9. package/dist/controllers/feedback.d.ts +44 -0
  10. package/dist/controllers/feedback.d.ts.map +1 -0
  11. package/dist/controllers/feedback.js +216 -0
  12. package/dist/controllers/feedback.js.map +1 -0
  13. package/dist/controllers/index.d.ts +9 -0
  14. package/dist/controllers/index.d.ts.map +1 -0
  15. package/dist/controllers/index.js +9 -0
  16. package/dist/controllers/index.js.map +1 -0
  17. package/dist/controllers/skills.d.ts +66 -0
  18. package/dist/controllers/skills.d.ts.map +1 -0
  19. package/dist/controllers/skills.js +355 -0
  20. package/dist/controllers/skills.js.map +1 -0
  21. package/dist/controllers/versions.d.ts +43 -0
  22. package/dist/controllers/versions.d.ts.map +1 -0
  23. package/dist/controllers/versions.js +298 -0
  24. package/dist/controllers/versions.js.map +1 -0
  25. package/dist/index.d.ts +9 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +78 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/middleware/auth.d.ts +34 -0
  30. package/dist/middleware/auth.d.ts.map +1 -0
  31. package/dist/middleware/auth.js +148 -0
  32. package/dist/middleware/auth.js.map +1 -0
  33. package/dist/middleware/error.d.ts +26 -0
  34. package/dist/middleware/error.d.ts.map +1 -0
  35. package/dist/middleware/error.js +102 -0
  36. package/dist/middleware/error.js.map +1 -0
  37. package/dist/middleware/index.d.ts +8 -0
  38. package/dist/middleware/index.d.ts.map +1 -0
  39. package/dist/middleware/index.js +8 -0
  40. package/dist/middleware/index.js.map +1 -0
  41. package/dist/middleware/logger.d.ts +19 -0
  42. package/dist/middleware/logger.d.ts.map +1 -0
  43. package/dist/middleware/logger.js +54 -0
  44. package/dist/middleware/logger.js.map +1 -0
  45. package/dist/middleware/validation.d.ts +671 -0
  46. package/dist/middleware/validation.d.ts.map +1 -0
  47. package/dist/middleware/validation.js +225 -0
  48. package/dist/middleware/validation.js.map +1 -0
  49. package/dist/routes/audit.d.ts +6 -0
  50. package/dist/routes/audit.d.ts.map +1 -0
  51. package/dist/routes/audit.js +54 -0
  52. package/dist/routes/audit.js.map +1 -0
  53. package/dist/routes/cache.d.ts +6 -0
  54. package/dist/routes/cache.d.ts.map +1 -0
  55. package/dist/routes/cache.js +70 -0
  56. package/dist/routes/cache.js.map +1 -0
  57. package/dist/routes/feedback.d.ts +6 -0
  58. package/dist/routes/feedback.d.ts.map +1 -0
  59. package/dist/routes/feedback.js +68 -0
  60. package/dist/routes/feedback.js.map +1 -0
  61. package/dist/routes/health.d.ts +6 -0
  62. package/dist/routes/health.d.ts.map +1 -0
  63. package/dist/routes/health.js +122 -0
  64. package/dist/routes/health.js.map +1 -0
  65. package/dist/routes/index.d.ts +12 -0
  66. package/dist/routes/index.d.ts.map +1 -0
  67. package/dist/routes/index.js +12 -0
  68. package/dist/routes/index.js.map +1 -0
  69. package/dist/routes/scan.d.ts +8 -0
  70. package/dist/routes/scan.d.ts.map +1 -0
  71. package/dist/routes/scan.js +315 -0
  72. package/dist/routes/scan.js.map +1 -0
  73. package/dist/routes/search.d.ts +6 -0
  74. package/dist/routes/search.d.ts.map +1 -0
  75. package/dist/routes/search.js +44 -0
  76. package/dist/routes/search.js.map +1 -0
  77. package/dist/routes/skills.d.ts +6 -0
  78. package/dist/routes/skills.d.ts.map +1 -0
  79. package/dist/routes/skills.js +74 -0
  80. package/dist/routes/skills.js.map +1 -0
  81. package/dist/routes/versions.d.ts +6 -0
  82. package/dist/routes/versions.d.ts.map +1 -0
  83. package/dist/routes/versions.js +66 -0
  84. package/dist/routes/versions.js.map +1 -0
  85. package/dist/server.d.ts +26 -0
  86. package/dist/server.d.ts.map +1 -0
  87. package/dist/server.js +166 -0
  88. package/dist/server.js.map +1 -0
  89. package/package.json +42 -0
  90. package/src/controllers/audit.ts +175 -0
  91. package/src/controllers/cache.ts +344 -0
  92. package/src/controllers/feedback.ts +309 -0
  93. package/src/controllers/index.ts +9 -0
  94. package/src/controllers/skills.ts +489 -0
  95. package/src/controllers/versions.ts +427 -0
  96. package/src/index.ts +87 -0
  97. package/src/middleware/auth.ts +219 -0
  98. package/src/middleware/error.ts +180 -0
  99. package/src/middleware/index.ts +8 -0
  100. package/src/middleware/logger.ts +71 -0
  101. package/src/middleware/validation.ts +270 -0
  102. package/src/routes/audit.ts +74 -0
  103. package/src/routes/cache.ts +93 -0
  104. package/src/routes/feedback.ts +93 -0
  105. package/src/routes/health.ts +151 -0
  106. package/src/routes/index.ts +12 -0
  107. package/src/routes/scan.ts +428 -0
  108. package/src/routes/search.ts +51 -0
  109. package/src/routes/skills.ts +102 -0
  110. package/src/routes/versions.ts +91 -0
  111. package/src/server.ts +205 -0
  112. package/tsconfig.json +13 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Open Skills Hub - Authentication Middleware
3
+ */
4
+ import * as crypto from 'crypto';
5
+ import { getConfig, AppError, ErrorCodes, } from '@open-skills-hub/core';
6
+ import { logger } from './logger.js';
7
+ /**
8
+ * Extract Bearer token from Authorization header
9
+ */
10
+ function extractBearerToken(request) {
11
+ const authHeader = request.headers.authorization;
12
+ if (!authHeader)
13
+ return null;
14
+ const parts = authHeader.split(' ');
15
+ if (parts.length !== 2 || parts[0]?.toLowerCase() !== 'bearer') {
16
+ return null;
17
+ }
18
+ return parts[1] ?? null;
19
+ }
20
+ /**
21
+ * Verify JWT token (simplified - in production use jsonwebtoken library)
22
+ */
23
+ function verifyToken(token, secret) {
24
+ try {
25
+ // Simple JWT verification (header.payload.signature)
26
+ const parts = token.split('.');
27
+ if (parts.length !== 3)
28
+ return null;
29
+ const [headerB64, payloadB64, signatureB64] = parts;
30
+ // Verify signature
31
+ const data = `${headerB64}.${payloadB64}`;
32
+ const expectedSignature = crypto
33
+ .createHmac('sha256', secret)
34
+ .update(data)
35
+ .digest('base64url');
36
+ if (signatureB64 !== expectedSignature) {
37
+ return null;
38
+ }
39
+ // Decode payload
40
+ const payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString());
41
+ // Check expiration
42
+ if (payload.exp && Date.now() / 1000 > payload.exp) {
43
+ return null;
44
+ }
45
+ return {
46
+ id: payload.sub,
47
+ username: payload.username,
48
+ type: payload.type || 'user',
49
+ permissions: payload.permissions || [],
50
+ };
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * Authentication middleware - requires valid token
58
+ */
59
+ export async function requireAuth(request, reply) {
60
+ const config = getConfig().get();
61
+ // Skip auth if not required
62
+ if (!config.auth.requireAuth) {
63
+ // Set a default system user for unauthenticated requests
64
+ request.user = {
65
+ id: 'anonymous',
66
+ type: 'user',
67
+ permissions: ['read'],
68
+ };
69
+ return;
70
+ }
71
+ const token = extractBearerToken(request);
72
+ if (!token) {
73
+ throw new AppError(ErrorCodes.UNAUTHORIZED, 'Authentication required', 401);
74
+ }
75
+ const secret = config.auth.jwtSecret;
76
+ if (!secret) {
77
+ logger.error('JWT secret not configured');
78
+ throw new AppError(ErrorCodes.INTERNAL_ERROR, 'Authentication not configured', 500);
79
+ }
80
+ const user = verifyToken(token, secret);
81
+ if (!user) {
82
+ throw new AppError(ErrorCodes.TOKEN_INVALID, 'Invalid or expired token', 401);
83
+ }
84
+ request.user = user;
85
+ }
86
+ /**
87
+ * Optional authentication - attaches user if token provided
88
+ */
89
+ export async function optionalAuth(request, _reply) {
90
+ const config = getConfig().get();
91
+ const token = extractBearerToken(request);
92
+ if (!token) {
93
+ request.user = {
94
+ id: 'anonymous',
95
+ type: 'user',
96
+ permissions: ['read'],
97
+ };
98
+ return;
99
+ }
100
+ const secret = config.auth.jwtSecret;
101
+ if (!secret) {
102
+ request.user = {
103
+ id: 'anonymous',
104
+ type: 'user',
105
+ permissions: ['read'],
106
+ };
107
+ return;
108
+ }
109
+ const user = verifyToken(token, secret);
110
+ request.user = user ?? {
111
+ id: 'anonymous',
112
+ type: 'user',
113
+ permissions: ['read'],
114
+ };
115
+ }
116
+ /**
117
+ * Permission check middleware
118
+ */
119
+ export function requirePermission(permission) {
120
+ return async function (request, _reply) {
121
+ if (!request.user) {
122
+ throw new AppError(ErrorCodes.UNAUTHORIZED, 'Authentication required', 401);
123
+ }
124
+ if (!request.user.permissions.includes(permission) && !request.user.permissions.includes('admin')) {
125
+ throw new AppError(ErrorCodes.INSUFFICIENT_PERMISSIONS, `Permission '${permission}' required`, 403);
126
+ }
127
+ };
128
+ }
129
+ /**
130
+ * Generate a simple JWT token (for testing)
131
+ */
132
+ export function generateToken(user, secret, expiresIn = 86400) {
133
+ const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
134
+ const payload = Buffer.from(JSON.stringify({
135
+ sub: user.id,
136
+ username: user.username,
137
+ type: user.type,
138
+ permissions: user.permissions || [],
139
+ iat: Math.floor(Date.now() / 1000),
140
+ exp: Math.floor(Date.now() / 1000) + expiresIn,
141
+ })).toString('base64url');
142
+ const signature = crypto
143
+ .createHmac('sha256', secret)
144
+ .update(`${header}.${payload}`)
145
+ .digest('base64url');
146
+ return `${header}.${payload}.${signature}`;
147
+ }
148
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Open Skills Hub - Error Handling Middleware
3
+ */
4
+ import type { FastifyRequest, FastifyReply, FastifyError } from 'fastify';
5
+ import type { ApiResponse, ApiError } from '@open-skills-hub/core';
6
+ /**
7
+ * Build standard API response
8
+ */
9
+ export declare function buildApiResponse<T>(request: FastifyRequest, data?: T, error?: ApiError, pagination?: ApiResponse['pagination']): ApiResponse<T>;
10
+ /**
11
+ * Send success response
12
+ */
13
+ export declare function sendSuccess<T>(request: FastifyRequest, reply: FastifyReply, data: T, statusCode?: number, pagination?: ApiResponse['pagination']): void;
14
+ /**
15
+ * Send error response
16
+ */
17
+ export declare function sendError(request: FastifyRequest, reply: FastifyReply, code: string, message: string, statusCode?: number, details?: Record<string, unknown>): void;
18
+ /**
19
+ * Global error handler
20
+ */
21
+ export declare function errorHandler(error: FastifyError | Error, request: FastifyRequest, reply: FastifyReply): void;
22
+ /**
23
+ * Not found handler
24
+ */
25
+ export declare function notFoundHandler(request: FastifyRequest, reply: FastifyReply): void;
26
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/middleware/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQ1E,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGnE;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,OAAO,EAAE,cAAc,EACvB,IAAI,CAAC,EAAE,CAAC,EACR,KAAK,CAAC,EAAE,QAAQ,EAChB,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,GACrC,WAAW,CAAC,CAAC,CAAC,CAWhB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,CAAC,EACP,UAAU,GAAE,MAAY,EACxB,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,GACrC,IAAI,CAEN;AAED;;GAEG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,MAAY,EACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAIN;AAqBD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,GAAG,KAAK,EAC3B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,GAClB,IAAI,CAwEN;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,GAClB,IAAI,CAQN"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Open Skills Hub - Error Handling Middleware
3
+ */
4
+ import { ZodError } from 'zod';
5
+ import { AppError, ErrorCodes, now, } from '@open-skills-hub/core';
6
+ import { logger } from './logger.js';
7
+ /**
8
+ * Build standard API response
9
+ */
10
+ export function buildApiResponse(request, data, error, pagination) {
11
+ return {
12
+ success: !error,
13
+ data,
14
+ error,
15
+ pagination,
16
+ meta: {
17
+ requestId: request.id,
18
+ timestamp: now(),
19
+ },
20
+ };
21
+ }
22
+ /**
23
+ * Send success response
24
+ */
25
+ export function sendSuccess(request, reply, data, statusCode = 200, pagination) {
26
+ reply.status(statusCode).send(buildApiResponse(request, data, undefined, pagination));
27
+ }
28
+ /**
29
+ * Send error response
30
+ */
31
+ export function sendError(request, reply, code, message, statusCode = 500, details) {
32
+ reply.status(statusCode).send(buildApiResponse(request, undefined, { code, message, details }));
33
+ }
34
+ /**
35
+ * Convert Zod validation error to API error format
36
+ */
37
+ function formatZodError(error) {
38
+ const issues = error.issues.map(issue => ({
39
+ path: issue.path.join('.'),
40
+ message: issue.message,
41
+ code: issue.code,
42
+ }));
43
+ return {
44
+ message: 'Validation failed',
45
+ details: {
46
+ issues,
47
+ issueCount: issues.length,
48
+ },
49
+ };
50
+ }
51
+ /**
52
+ * Global error handler
53
+ */
54
+ export function errorHandler(error, request, reply) {
55
+ // Log the error
56
+ logger.error('Request error', {
57
+ error: {
58
+ name: error.name,
59
+ message: error.message,
60
+ stack: error.stack,
61
+ },
62
+ requestId: request.id,
63
+ url: request.url,
64
+ method: request.method,
65
+ });
66
+ // Handle Zod validation errors
67
+ if (error instanceof ZodError) {
68
+ const { message, details } = formatZodError(error);
69
+ sendError(request, reply, ErrorCodes.VALIDATION_FAILED, message, 422, details);
70
+ return;
71
+ }
72
+ // Handle AppError
73
+ if (error instanceof AppError) {
74
+ sendError(request, reply, error.code, error.message, error.statusCode, error.details);
75
+ return;
76
+ }
77
+ // Handle Fastify errors
78
+ if ('statusCode' in error) {
79
+ const fastifyError = error;
80
+ // Rate limit exceeded
81
+ if (fastifyError.statusCode === 429) {
82
+ sendError(request, reply, ErrorCodes.RATE_LIMITED, 'Too many requests, please try again later', 429);
83
+ return;
84
+ }
85
+ // Bad request
86
+ if (fastifyError.statusCode === 400) {
87
+ sendError(request, reply, ErrorCodes.INVALID_REQUEST, fastifyError.message || 'Bad request', 400);
88
+ return;
89
+ }
90
+ }
91
+ // Generic internal server error
92
+ sendError(request, reply, ErrorCodes.INTERNAL_ERROR, process.env['NODE_ENV'] === 'production'
93
+ ? 'An internal error occurred'
94
+ : error.message, 500);
95
+ }
96
+ /**
97
+ * Not found handler
98
+ */
99
+ export function notFoundHandler(request, reply) {
100
+ sendError(request, reply, 'NOT_FOUND', `Route ${request.method} ${request.url} not found`, 404);
101
+ }
102
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/middleware/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC/B,OAAO,EACL,QAAQ,EACR,UAAU,EAEV,GAAG,GACJ,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAuB,EACvB,IAAQ,EACR,KAAgB,EAChB,UAAsC;IAEtC,OAAO;QACL,OAAO,EAAE,CAAC,KAAK;QACf,IAAI;QACJ,KAAK;QACL,UAAU;QACV,IAAI,EAAE;YACJ,SAAS,EAAE,OAAO,CAAC,EAAY;YAC/B,SAAS,EAAE,GAAG,EAAE;SACjB;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,OAAuB,EACvB,KAAmB,EACnB,IAAO,EACP,aAAqB,GAAG,EACxB,UAAsC;IAEtC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;AACxF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CACvB,OAAuB,EACvB,KAAmB,EACnB,IAAY,EACZ,OAAe,EACf,aAAqB,GAAG,EACxB,OAAiC;IAEjC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAC3B,gBAAgB,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CACjE,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,KAAe;IACrC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,OAAO,EAAE,mBAAmB;QAC5B,OAAO,EAAE;YACP,MAAM;YACN,UAAU,EAAE,MAAM,CAAC,MAAM;SAC1B;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,KAA2B,EAC3B,OAAuB,EACvB,KAAmB;IAEnB,gBAAgB;IAChB,MAAM,CAAC,KAAK,CAAC,eAAe,EAAE;QAC5B,KAAK,EAAE;YACL,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB;QACD,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,+BAA+B;IAC/B,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACnD,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,iBAAiB,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/E,OAAO;IACT,CAAC;IAED,kBAAkB;IAClB,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,SAAS,CACP,OAAO,EACP,KAAK,EACL,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,OAAO,CACd,CAAC;QACF,OAAO;IACT,CAAC;IAED,wBAAwB;IACxB,IAAI,YAAY,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,YAAY,GAAG,KAAqB,CAAC;QAE3C,sBAAsB;QACtB,IAAI,YAAY,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;YACpC,SAAS,CACP,OAAO,EACP,KAAK,EACL,UAAU,CAAC,YAAY,EACvB,2CAA2C,EAC3C,GAAG,CACJ,CAAC;YACF,OAAO;QACT,CAAC;QAED,cAAc;QACd,IAAI,YAAY,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;YACpC,SAAS,CACP,OAAO,EACP,KAAK,EACL,UAAU,CAAC,eAAe,EAC1B,YAAY,CAAC,OAAO,IAAI,aAAa,EACrC,GAAG,CACJ,CAAC;YACF,OAAO;QACT,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,SAAS,CACP,OAAO,EACP,KAAK,EACL,UAAU,CAAC,cAAc,EACzB,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY;QACtC,CAAC,CAAC,4BAA4B;QAC9B,CAAC,CAAC,KAAK,CAAC,OAAO,EACjB,GAAG,CACJ,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAuB,EACvB,KAAmB;IAEnB,SAAS,CACP,OAAO,EACP,KAAK,EACL,WAAW,EACX,SAAS,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,YAAY,EAClD,GAAG,CACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Open Skills Hub - Middleware Exports
3
+ */
4
+ export * from './error.js';
5
+ export * from './logger.js';
6
+ export * from './auth.js';
7
+ export * from './validation.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Open Skills Hub - Middleware Exports
3
+ */
4
+ export * from './error.js';
5
+ export * from './logger.js';
6
+ export * from './auth.js';
7
+ export * from './validation.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Open Skills Hub - Request Logger Middleware
3
+ */
4
+ import type { FastifyRequest, FastifyReply } from 'fastify';
5
+ import pino from 'pino';
6
+ export declare const logger: pino.Logger;
7
+ /**
8
+ * Request logging hook
9
+ */
10
+ export declare function requestLogger(request: FastifyRequest, _reply: FastifyReply): Promise<void>;
11
+ /**
12
+ * Response logging hook
13
+ */
14
+ export declare function responseLogger(request: FastifyRequest, reply: FastifyReply, payload: unknown): Promise<unknown>;
15
+ /**
16
+ * Create a child logger with request context
17
+ */
18
+ export declare function createRequestLogger(request: FastifyRequest): pino.Logger;
19
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5D,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,eAAO,MAAM,MAAM,aAAsC,CAAC;AAE1D;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC,MAAM,CAMxE"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Open Skills Hub - Request Logger Middleware
3
+ */
4
+ import { logger as baseLogger } from '@open-skills-hub/core';
5
+ // Create API-specific logger
6
+ export const logger = baseLogger.child({ module: 'api' });
7
+ /**
8
+ * Request logging hook
9
+ */
10
+ export async function requestLogger(request, _reply) {
11
+ // Skip health check logging to reduce noise
12
+ if (request.url === '/health' || request.url === '/ready') {
13
+ return;
14
+ }
15
+ logger.info('Incoming request', {
16
+ requestId: request.id,
17
+ method: request.method,
18
+ url: request.url,
19
+ query: request.query,
20
+ userAgent: request.headers['user-agent'],
21
+ ip: request.ip,
22
+ contentLength: request.headers['content-length'],
23
+ });
24
+ }
25
+ /**
26
+ * Response logging hook
27
+ */
28
+ export async function responseLogger(request, reply, payload) {
29
+ // Skip health check logging
30
+ if (request.url === '/health' || request.url === '/ready') {
31
+ return payload;
32
+ }
33
+ const duration = request.startTime ? Date.now() - request.startTime : 0;
34
+ logger.info('Request completed', {
35
+ requestId: request.id,
36
+ method: request.method,
37
+ url: request.url,
38
+ statusCode: reply.statusCode,
39
+ duration: `${duration}ms`,
40
+ contentLength: reply.getHeader('content-length'),
41
+ });
42
+ return payload;
43
+ }
44
+ /**
45
+ * Create a child logger with request context
46
+ */
47
+ export function createRequestLogger(request) {
48
+ return logger.child({
49
+ requestId: request.id,
50
+ method: request.method,
51
+ url: request.url,
52
+ });
53
+ }
54
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAG7D,6BAA6B;AAC7B,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAuB,EACvB,MAAoB;IAEpB,4CAA4C;IAC5C,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1D,OAAO;IACT,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE;QAC9B,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC;QACxC,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC;KACjD,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAuB,EACvB,KAAmB,EACnB,OAAgB;IAEhB,4BAA4B;IAC5B,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC1D,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAExE,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;QAC/B,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,QAAQ,EAAE,GAAG,QAAQ,IAAI;QACzB,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC;KACjD,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAuB;IACzD,OAAO,MAAM,CAAC,KAAK,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,GAAG,EAAE,OAAO,CAAC,GAAG;KACjB,CAAC,CAAC;AACL,CAAC"}