@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
package/dist/server.js ADDED
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Open Skills Hub - API Server Configuration
3
+ */
4
+ import Fastify from 'fastify';
5
+ import cors from '@fastify/cors';
6
+ import helmet from '@fastify/helmet';
7
+ import rateLimit from '@fastify/rate-limit';
8
+ import multipart from '@fastify/multipart';
9
+ import { getConfig, getStorage, logger, } from '@open-skills-hub/core';
10
+ import { errorHandler, notFoundHandler } from './middleware/error.js';
11
+ import { requestLogger } from './middleware/logger.js';
12
+ import { skillsRoutes } from './routes/skills.js';
13
+ import { versionsRoutes } from './routes/versions.js';
14
+ import { searchRoutes } from './routes/search.js';
15
+ import { scanRoutes } from './routes/scan.js';
16
+ import { feedbackRoutes } from './routes/feedback.js';
17
+ import { auditRoutes } from './routes/audit.js';
18
+ import { cacheRoutes } from './routes/cache.js';
19
+ import { healthRoutes } from './routes/health.js';
20
+ /**
21
+ * Create and configure the Fastify server
22
+ */
23
+ export async function createServer(options) {
24
+ const config = getConfig().get();
25
+ const serverConfig = config.server;
26
+ const server = Fastify({
27
+ logger: false, // We use our own logger
28
+ trustProxy: serverConfig.trustProxy,
29
+ requestIdHeader: 'x-request-id',
30
+ requestIdLogLabel: 'requestId',
31
+ genReqId: () => `req_${Date.now().toString(36)}_${Math.random().toString(36).substr(2, 9)}`,
32
+ // Increase body size limit to support large skills with many files (e.g., XSD schemas)
33
+ bodyLimit: 50 * 1024 * 1024, // 50MB
34
+ });
35
+ // Register plugins
36
+ await registerPlugins(server, config);
37
+ // Register middleware
38
+ await registerMiddleware(server);
39
+ // Register routes
40
+ await registerRoutes(server);
41
+ // Set error handlers
42
+ server.setNotFoundHandler(notFoundHandler);
43
+ server.setErrorHandler(errorHandler);
44
+ return server;
45
+ }
46
+ /**
47
+ * Register Fastify plugins
48
+ */
49
+ async function registerPlugins(server, config) {
50
+ // CORS
51
+ if (config.server.cors) {
52
+ await server.register(cors, {
53
+ origin: true,
54
+ methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
55
+ allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'],
56
+ exposedHeaders: ['X-Request-ID', 'X-RateLimit-Limit', 'X-RateLimit-Remaining'],
57
+ credentials: true,
58
+ maxAge: 86400,
59
+ });
60
+ }
61
+ // Security headers
62
+ await server.register(helmet, {
63
+ contentSecurityPolicy: false, // Disabled for API
64
+ crossOriginResourcePolicy: { policy: 'cross-origin' },
65
+ });
66
+ // Rate limiting
67
+ if (config.rateLimit.enabled) {
68
+ await server.register(rateLimit, {
69
+ global: true,
70
+ max: config.rateLimit.max,
71
+ timeWindow: config.rateLimit.windowMs,
72
+ allowList: ['127.0.0.1'],
73
+ addHeadersOnExceeding: {
74
+ 'x-ratelimit-limit': true,
75
+ 'x-ratelimit-remaining': true,
76
+ 'x-ratelimit-reset': true,
77
+ },
78
+ addHeaders: {
79
+ 'x-ratelimit-limit': true,
80
+ 'x-ratelimit-remaining': true,
81
+ 'x-ratelimit-reset': true,
82
+ 'retry-after': true,
83
+ },
84
+ });
85
+ }
86
+ // Multipart form data (for file uploads)
87
+ await server.register(multipart, {
88
+ limits: {
89
+ fileSize: 10 * 1024 * 1024, // 10MB max file size
90
+ files: 10, // Max number of files
91
+ },
92
+ });
93
+ }
94
+ /**
95
+ * Register middleware
96
+ */
97
+ async function registerMiddleware(server) {
98
+ // Request logging
99
+ server.addHook('onRequest', requestLogger);
100
+ // Add request timing
101
+ server.addHook('onRequest', async (request) => {
102
+ request.startTime = Date.now();
103
+ });
104
+ // Add response timing header
105
+ server.addHook('onSend', async (request, reply) => {
106
+ if (request.startTime) {
107
+ const duration = Date.now() - request.startTime;
108
+ reply.header('X-Response-Time', `${duration}ms`);
109
+ }
110
+ });
111
+ }
112
+ /**
113
+ * Register API routes
114
+ */
115
+ async function registerRoutes(server) {
116
+ // Health check (no prefix)
117
+ await server.register(healthRoutes);
118
+ // API v1 routes
119
+ await server.register(async (api) => {
120
+ await api.register(skillsRoutes, { prefix: '/skills' });
121
+ await api.register(versionsRoutes, { prefix: '/skills' });
122
+ await api.register(searchRoutes, { prefix: '/search' });
123
+ await api.register(scanRoutes, { prefix: '/scan' });
124
+ await api.register(feedbackRoutes, { prefix: '/feedback' });
125
+ await api.register(auditRoutes, { prefix: '/audit' });
126
+ await api.register(cacheRoutes, { prefix: '/cache' });
127
+ }, { prefix: '/v1' });
128
+ }
129
+ /**
130
+ * Start the server
131
+ */
132
+ export async function startServer(server, options) {
133
+ const config = getConfig().get();
134
+ const host = options?.host ?? config.server.host;
135
+ const port = options?.port ?? config.server.port;
136
+ try {
137
+ // Initialize storage
138
+ const storage = await getStorage();
139
+ await storage.initialize();
140
+ logger.info('Storage initialized');
141
+ // Start listening
142
+ const address = await server.listen({ host, port });
143
+ logger.info(`Server started`, { address, mode: config.mode });
144
+ return address;
145
+ }
146
+ catch (error) {
147
+ logger.error('Failed to start server', { error });
148
+ throw error;
149
+ }
150
+ }
151
+ /**
152
+ * Stop the server gracefully
153
+ */
154
+ export async function stopServer(server) {
155
+ try {
156
+ await server.close();
157
+ const storage = await getStorage();
158
+ await storage.close();
159
+ logger.info('Server stopped gracefully');
160
+ }
161
+ catch (error) {
162
+ logger.error('Error during server shutdown', { error });
163
+ throw error;
164
+ }
165
+ }
166
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@open-skills-hub/api",
3
+ "version": "1.0.0",
4
+ "description": "REST API server for Open Skills Hub",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "skills-api": "./dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsx watch src/index.ts",
14
+ "start": "node dist/index.js",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "dependencies": {
18
+ "@open-skills-hub/core": "1.0.0",
19
+ "fastify": "^4.25.2",
20
+ "@fastify/cors": "^8.5.0",
21
+ "@fastify/helmet": "^11.1.1",
22
+ "@fastify/rate-limit": "^9.1.0",
23
+ "@fastify/multipart": "^8.1.0",
24
+ "zod": "^3.22.4",
25
+ "pino": "^8.17.2"
26
+ },
27
+ "devDependencies": {
28
+ "tsx": "^4.7.0",
29
+ "@types/node": "^20.10.6"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/OpenSkillsHub/open-skills-hub.git",
34
+ "directory": "packages/api"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/OpenSkillsHub/open-skills-hub/issues"
38
+ },
39
+ "homepage": "https://github.com/OpenSkillsHub/open-skills-hub#readme",
40
+ "author": "Open Skills Hub Team",
41
+ "license": "MIT"
42
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Open Skills Hub - Audit Controller
3
+ */
4
+
5
+ import type { FastifyRequest, FastifyReply } from 'fastify';
6
+ import {
7
+ getStorage,
8
+ buildSkillFullName,
9
+ parseSkillFullName,
10
+ AppError,
11
+ ErrorCodes,
12
+ } from '@open-skills-hub/core';
13
+ import { sendSuccess } from '../middleware/error.js';
14
+ import type {
15
+ SkillNameParam,
16
+ IdParam,
17
+ AuditQuery,
18
+ } from '../middleware/validation.js';
19
+
20
+ /**
21
+ * Get audit logs
22
+ */
23
+ export async function getAuditLogs(
24
+ request: FastifyRequest<{ Querystring: AuditQuery }>,
25
+ reply: FastifyReply
26
+ ): Promise<void> {
27
+ const storage = await getStorage();
28
+ const { eventType, actor, resourceType, resourceName, from, to, cursor, limit } = request.query;
29
+
30
+ const result = await storage.getAuditLogs({
31
+ eventType,
32
+ actor,
33
+ resourceType,
34
+ resourceName,
35
+ from,
36
+ to,
37
+ cursor,
38
+ limit,
39
+ });
40
+
41
+ sendSuccess(request, reply, result.items, 200, result.pagination);
42
+ }
43
+
44
+ /**
45
+ * Get audit logs for a specific skill
46
+ */
47
+ export async function getSkillAuditLogs(
48
+ request: FastifyRequest<{ Params: SkillNameParam; Querystring: AuditQuery }>,
49
+ reply: FastifyReply
50
+ ): Promise<void> {
51
+ const storage = await getStorage();
52
+ const { name } = request.params;
53
+ const { from, to, cursor, limit } = request.query;
54
+
55
+ // Parse name
56
+ const { scope, name: skillName } = parseSkillFullName(name);
57
+ const fullName = buildSkillFullName(skillName, scope);
58
+
59
+ const skill = await storage.getSkillByName(fullName);
60
+ if (!skill) {
61
+ throw new AppError(
62
+ ErrorCodes.SKILL_NOT_FOUND,
63
+ `Skill '${fullName}' not found`,
64
+ 404
65
+ );
66
+ }
67
+
68
+ const result = await storage.getResourceAuditLogs('skill', skill.id, {
69
+ cursor,
70
+ limit,
71
+ });
72
+
73
+ // Filter by date if provided
74
+ let items = result.items;
75
+ if (from || to) {
76
+ items = items.filter(log => {
77
+ const timestamp = new Date(log.timestamp).getTime();
78
+ if (from && timestamp < new Date(from).getTime()) return false;
79
+ if (to && timestamp > new Date(to).getTime()) return false;
80
+ return true;
81
+ });
82
+ }
83
+
84
+ sendSuccess(request, reply, items, 200, result.pagination);
85
+ }
86
+
87
+ /**
88
+ * Get audit log by ID
89
+ */
90
+ export async function getAuditLogById(
91
+ request: FastifyRequest<{ Params: IdParam }>,
92
+ reply: FastifyReply
93
+ ): Promise<void> {
94
+ const storage = await getStorage();
95
+ const { id } = request.params;
96
+
97
+ // Search for the log
98
+ const result = await storage.getAuditLogs({ limit: 1000 });
99
+ const log = result.items.find(l => l.id === id);
100
+
101
+ if (!log) {
102
+ throw new AppError(
103
+ ErrorCodes.SKILL_NOT_FOUND, // Using generic not found
104
+ `Audit log '${id}' not found`,
105
+ 404
106
+ );
107
+ }
108
+
109
+ sendSuccess(request, reply, log);
110
+ }
111
+
112
+ /**
113
+ * Get audit statistics
114
+ */
115
+ export async function getAuditStats(
116
+ request: FastifyRequest<{ Querystring: { days?: number } }>,
117
+ reply: FastifyReply
118
+ ): Promise<void> {
119
+ const storage = await getStorage();
120
+ const days = request.query.days ?? 30;
121
+
122
+ const fromDate = new Date();
123
+ fromDate.setDate(fromDate.getDate() - days);
124
+
125
+ const result = await storage.getAuditLogs({
126
+ from: fromDate.toISOString(),
127
+ limit: 10000,
128
+ });
129
+
130
+ const logs = result.items;
131
+
132
+ // Calculate stats
133
+ const byEventType: Record<string, number> = {};
134
+ const byActorType: Record<string, number> = {};
135
+ const byResult: Record<string, number> = {};
136
+ const byDay: Record<string, number> = {};
137
+
138
+ for (const log of logs) {
139
+ // By event type
140
+ byEventType[log.eventType] = (byEventType[log.eventType] || 0) + 1;
141
+
142
+ // By actor type
143
+ byActorType[log.actor.type] = (byActorType[log.actor.type] || 0) + 1;
144
+
145
+ // By result
146
+ byResult[log.result] = (byResult[log.result] || 0) + 1;
147
+
148
+ // By day
149
+ const day = log.timestamp.split('T')[0];
150
+ if (day) {
151
+ byDay[day] = (byDay[day] || 0) + 1;
152
+ }
153
+ }
154
+
155
+ // Convert byDay to sorted array
156
+ const dailyActivity = Object.entries(byDay)
157
+ .map(([date, count]) => ({ date, count }))
158
+ .sort((a, b) => a.date.localeCompare(b.date));
159
+
160
+ sendSuccess(request, reply, {
161
+ total: logs.length,
162
+ period: {
163
+ from: fromDate.toISOString(),
164
+ to: new Date().toISOString(),
165
+ days,
166
+ },
167
+ byEventType,
168
+ byActorType,
169
+ byResult,
170
+ dailyActivity,
171
+ successRate: logs.length > 0
172
+ ? (byResult['success'] || 0) / logs.length
173
+ : 0,
174
+ });
175
+ }