s3db.js 13.4.0 → 13.6.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.
- package/README.md +25 -10
- package/dist/{s3db.cjs.js → s3db.cjs} +38801 -32446
- package/dist/s3db.cjs.map +1 -0
- package/dist/s3db.es.js +38653 -32291
- package/dist/s3db.es.js.map +1 -1
- package/package.json +218 -22
- package/src/concerns/id.js +90 -6
- package/src/concerns/index.js +2 -1
- package/src/concerns/password-hashing.js +150 -0
- package/src/database.class.js +6 -2
- package/src/plugins/api/auth/basic-auth.js +40 -10
- package/src/plugins/api/auth/index.js +49 -3
- package/src/plugins/api/auth/oauth2-auth.js +171 -0
- package/src/plugins/api/auth/oidc-auth.js +789 -0
- package/src/plugins/api/auth/oidc-client.js +462 -0
- package/src/plugins/api/auth/path-auth-matcher.js +284 -0
- package/src/plugins/api/concerns/event-emitter.js +134 -0
- package/src/plugins/api/concerns/failban-manager.js +651 -0
- package/src/plugins/api/concerns/guards-helpers.js +402 -0
- package/src/plugins/api/concerns/metrics-collector.js +346 -0
- package/src/plugins/api/index.js +510 -57
- package/src/plugins/api/middlewares/failban.js +305 -0
- package/src/plugins/api/middlewares/rate-limit.js +301 -0
- package/src/plugins/api/middlewares/request-id.js +74 -0
- package/src/plugins/api/middlewares/security-headers.js +120 -0
- package/src/plugins/api/middlewares/session-tracking.js +194 -0
- package/src/plugins/api/routes/auth-routes.js +119 -78
- package/src/plugins/api/routes/resource-routes.js +73 -30
- package/src/plugins/api/server.js +1139 -45
- package/src/plugins/api/utils/custom-routes.js +102 -0
- package/src/plugins/api/utils/guards.js +213 -0
- package/src/plugins/api/utils/mime-types.js +154 -0
- package/src/plugins/api/utils/openapi-generator.js +91 -12
- package/src/plugins/api/utils/path-matcher.js +173 -0
- package/src/plugins/api/utils/static-filesystem.js +262 -0
- package/src/plugins/api/utils/static-s3.js +231 -0
- package/src/plugins/api/utils/template-engine.js +188 -0
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
- package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
- package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
- package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
- package/src/plugins/cloud-inventory/index.js +20 -0
- package/src/plugins/cloud-inventory/registry.js +146 -0
- package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
- package/src/plugins/cloud-inventory.plugin.js +1333 -0
- package/src/plugins/concerns/plugin-dependencies.js +62 -2
- package/src/plugins/eventual-consistency/analytics.js +1 -0
- package/src/plugins/eventual-consistency/consolidation.js +2 -2
- package/src/plugins/eventual-consistency/garbage-collection.js +2 -2
- package/src/plugins/eventual-consistency/install.js +2 -2
- package/src/plugins/identity/README.md +335 -0
- package/src/plugins/identity/concerns/mfa-manager.js +204 -0
- package/src/plugins/identity/concerns/password.js +138 -0
- package/src/plugins/identity/concerns/resource-schemas.js +273 -0
- package/src/plugins/identity/concerns/token-generator.js +172 -0
- package/src/plugins/identity/email-service.js +422 -0
- package/src/plugins/identity/index.js +1052 -0
- package/src/plugins/identity/oauth2-server.js +1033 -0
- package/src/plugins/identity/oidc-discovery.js +285 -0
- package/src/plugins/identity/rsa-keys.js +323 -0
- package/src/plugins/identity/server.js +500 -0
- package/src/plugins/identity/session-manager.js +453 -0
- package/src/plugins/identity/ui/layouts/base.js +251 -0
- package/src/plugins/identity/ui/middleware.js +135 -0
- package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
- package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
- package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
- package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
- package/src/plugins/identity/ui/pages/admin/users.js +263 -0
- package/src/plugins/identity/ui/pages/consent.js +262 -0
- package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
- package/src/plugins/identity/ui/pages/login.js +144 -0
- package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
- package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
- package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
- package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
- package/src/plugins/identity/ui/pages/profile.js +361 -0
- package/src/plugins/identity/ui/pages/register.js +226 -0
- package/src/plugins/identity/ui/pages/reset-password.js +128 -0
- package/src/plugins/identity/ui/pages/verify-email.js +172 -0
- package/src/plugins/identity/ui/routes.js +2541 -0
- package/src/plugins/identity/ui/styles/main.css +465 -0
- package/src/plugins/index.js +4 -1
- package/src/plugins/ml/base-model.class.js +65 -16
- package/src/plugins/ml/classification-model.class.js +1 -1
- package/src/plugins/ml/timeseries-model.class.js +3 -1
- package/src/plugins/ml.plugin.js +584 -31
- package/src/plugins/shared/error-handler.js +147 -0
- package/src/plugins/shared/index.js +9 -0
- package/src/plugins/shared/middlewares/compression.js +117 -0
- package/src/plugins/shared/middlewares/cors.js +49 -0
- package/src/plugins/shared/middlewares/index.js +11 -0
- package/src/plugins/shared/middlewares/logging.js +54 -0
- package/src/plugins/shared/middlewares/rate-limit.js +73 -0
- package/src/plugins/shared/middlewares/security.js +158 -0
- package/src/plugins/shared/response-formatter.js +264 -0
- package/src/plugins/state-machine.plugin.js +57 -2
- package/src/resource.class.js +140 -12
- package/src/schema.class.js +30 -1
- package/src/validator.class.js +57 -6
- package/dist/s3db.cjs.js.map +0 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { asyncHandler } from '../utils/error-handler.js';
|
|
8
8
|
import * as formatter from '../utils/response-formatter.js';
|
|
9
|
+
import { guardMiddleware } from '../utils/guards.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Parse custom route definition (e.g., "GET /healthcheck" or "async POST /custom")
|
|
@@ -58,11 +59,16 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
58
59
|
const {
|
|
59
60
|
methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
|
|
60
61
|
customMiddleware = [],
|
|
61
|
-
enableValidation = true
|
|
62
|
+
enableValidation = true,
|
|
63
|
+
versionPrefix = '', // Empty string by default (calculated in server.js)
|
|
64
|
+
events = null // Event emitter for lifecycle hooks
|
|
62
65
|
} = config;
|
|
63
66
|
|
|
64
67
|
const resourceName = resource.name;
|
|
65
|
-
const basePath = `/${
|
|
68
|
+
const basePath = versionPrefix ? `/${versionPrefix}/${resourceName}` : `/${resourceName}`;
|
|
69
|
+
|
|
70
|
+
// Get guards configuration from resource config
|
|
71
|
+
const guards = resource.config?.guards || null;
|
|
66
72
|
|
|
67
73
|
// Apply custom middleware
|
|
68
74
|
customMiddleware.forEach(middleware => {
|
|
@@ -111,7 +117,7 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
111
117
|
|
|
112
118
|
// LIST - GET /{version}/{resource}
|
|
113
119
|
if (methods.includes('GET')) {
|
|
114
|
-
app.get('/', asyncHandler(async (c) => {
|
|
120
|
+
app.get('/', guardMiddleware(guards, 'list'), asyncHandler(async (c) => {
|
|
115
121
|
const query = c.req.query();
|
|
116
122
|
const limit = parseInt(query.limit) || 100;
|
|
117
123
|
const offset = parseInt(query.offset) || 0;
|
|
@@ -140,22 +146,23 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
140
146
|
|
|
141
147
|
// Use query if filters are present
|
|
142
148
|
if (Object.keys(filters).length > 0) {
|
|
143
|
-
|
|
144
|
-
items =
|
|
149
|
+
// Query with native offset support (efficient!)
|
|
150
|
+
items = await resource.query(filters, { limit, offset });
|
|
151
|
+
// Note: total is approximate (length of returned items)
|
|
152
|
+
// For exact total count with filters, would need separate count query
|
|
145
153
|
total = items.length;
|
|
146
154
|
} else if (partition && partitionValues) {
|
|
147
155
|
// Query specific partition
|
|
148
156
|
items = await resource.listPartition({
|
|
149
157
|
partition,
|
|
150
158
|
partitionValues,
|
|
151
|
-
limit
|
|
159
|
+
limit,
|
|
160
|
+
offset
|
|
152
161
|
});
|
|
153
|
-
items = items.slice(offset, offset + limit);
|
|
154
162
|
total = items.length;
|
|
155
163
|
} else {
|
|
156
164
|
// Regular list
|
|
157
|
-
items = await resource.list({ limit
|
|
158
|
-
items = items.slice(offset, offset + limit);
|
|
165
|
+
items = await resource.list({ limit, offset });
|
|
159
166
|
total = items.length;
|
|
160
167
|
}
|
|
161
168
|
|
|
@@ -176,7 +183,7 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
176
183
|
|
|
177
184
|
// GET ONE - GET /{version}/{resource}/:id
|
|
178
185
|
if (methods.includes('GET')) {
|
|
179
|
-
app.get('/:id', asyncHandler(async (c) => {
|
|
186
|
+
app.get('/:id', guardMiddleware(guards, 'get'), asyncHandler(async (c) => {
|
|
180
187
|
const id = c.req.param('id');
|
|
181
188
|
const query = c.req.query();
|
|
182
189
|
const partition = query.partition;
|
|
@@ -210,12 +217,22 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
210
217
|
|
|
211
218
|
// CREATE - POST /{version}/{resource}
|
|
212
219
|
if (methods.includes('POST')) {
|
|
213
|
-
app.post('/', asyncHandler(async (c) => {
|
|
220
|
+
app.post('/', guardMiddleware(guards, 'create'), asyncHandler(async (c) => {
|
|
214
221
|
const data = await c.req.json();
|
|
215
222
|
|
|
216
223
|
// Validation middleware will run if enabled
|
|
217
224
|
const item = await resource.insert(data);
|
|
218
225
|
|
|
226
|
+
// Emit resource:created event
|
|
227
|
+
if (events) {
|
|
228
|
+
events.emitResourceEvent('created', {
|
|
229
|
+
resource: resourceName,
|
|
230
|
+
id: item.id,
|
|
231
|
+
data: item,
|
|
232
|
+
user: c.get('user')
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
219
236
|
const location = `${basePath}/${item.id}`;
|
|
220
237
|
const response = formatter.created(item, location);
|
|
221
238
|
|
|
@@ -226,7 +243,7 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
226
243
|
|
|
227
244
|
// UPDATE (full) - PUT /{version}/{resource}/:id
|
|
228
245
|
if (methods.includes('PUT')) {
|
|
229
|
-
app.put('/:id', asyncHandler(async (c) => {
|
|
246
|
+
app.put('/:id', guardMiddleware(guards, 'update'), asyncHandler(async (c) => {
|
|
230
247
|
const id = c.req.param('id');
|
|
231
248
|
const data = await c.req.json();
|
|
232
249
|
|
|
@@ -240,6 +257,17 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
240
257
|
// Full update
|
|
241
258
|
const updated = await resource.update(id, data);
|
|
242
259
|
|
|
260
|
+
// Emit resource:updated event
|
|
261
|
+
if (events) {
|
|
262
|
+
events.emitResourceEvent('updated', {
|
|
263
|
+
resource: resourceName,
|
|
264
|
+
id: updated.id,
|
|
265
|
+
data: updated,
|
|
266
|
+
previous: existing,
|
|
267
|
+
user: c.get('user')
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
243
271
|
const response = formatter.success(updated);
|
|
244
272
|
return c.json(response, response._status);
|
|
245
273
|
}));
|
|
@@ -247,7 +275,7 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
247
275
|
|
|
248
276
|
// UPDATE (partial) - PATCH /{version}/{resource}/:id
|
|
249
277
|
if (methods.includes('PATCH')) {
|
|
250
|
-
app.patch('/:id', asyncHandler(async (c) => {
|
|
278
|
+
app.patch('/:id', guardMiddleware(guards, 'update'), asyncHandler(async (c) => {
|
|
251
279
|
const id = c.req.param('id');
|
|
252
280
|
const data = await c.req.json();
|
|
253
281
|
|
|
@@ -262,6 +290,18 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
262
290
|
const merged = { ...existing, ...data, id };
|
|
263
291
|
const updated = await resource.update(id, merged);
|
|
264
292
|
|
|
293
|
+
// Emit resource:updated event
|
|
294
|
+
if (events) {
|
|
295
|
+
events.emitResourceEvent('updated', {
|
|
296
|
+
resource: resourceName,
|
|
297
|
+
id: updated.id,
|
|
298
|
+
data: updated,
|
|
299
|
+
previous: existing,
|
|
300
|
+
partial: true,
|
|
301
|
+
user: c.get('user')
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
265
305
|
const response = formatter.success(updated);
|
|
266
306
|
return c.json(response, response._status);
|
|
267
307
|
}));
|
|
@@ -269,7 +309,7 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
269
309
|
|
|
270
310
|
// DELETE - DELETE /{version}/{resource}/:id
|
|
271
311
|
if (methods.includes('DELETE')) {
|
|
272
|
-
app.delete('/:id', asyncHandler(async (c) => {
|
|
312
|
+
app.delete('/:id', guardMiddleware(guards, 'delete'), asyncHandler(async (c) => {
|
|
273
313
|
const id = c.req.param('id');
|
|
274
314
|
|
|
275
315
|
// Check if exists
|
|
@@ -281,6 +321,16 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
281
321
|
|
|
282
322
|
await resource.delete(id);
|
|
283
323
|
|
|
324
|
+
// Emit resource:deleted event
|
|
325
|
+
if (events) {
|
|
326
|
+
events.emitResourceEvent('deleted', {
|
|
327
|
+
resource: resourceName,
|
|
328
|
+
id,
|
|
329
|
+
previous: existing,
|
|
330
|
+
user: c.get('user')
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
284
334
|
const response = formatter.noContent();
|
|
285
335
|
return c.json(response, response._status);
|
|
286
336
|
}));
|
|
@@ -291,22 +341,11 @@ export function createResourceRoutes(resource, version, config = {}, Hono) {
|
|
|
291
341
|
app.on('HEAD', '/', asyncHandler(async (c) => {
|
|
292
342
|
// Get statistics
|
|
293
343
|
const total = await resource.count();
|
|
344
|
+
const version = resource.config?.currentVersion || resource.version || 'v1';
|
|
294
345
|
|
|
295
|
-
//
|
|
296
|
-
// For large datasets, this might need optimization
|
|
297
|
-
const allItems = await resource.list({ limit: 1000 });
|
|
298
|
-
|
|
299
|
-
// Calculate statistics
|
|
300
|
-
const stats = {
|
|
301
|
-
total,
|
|
302
|
-
version: resource.config?.currentVersion || resource.version || 'v1'
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
// Add resource-specific stats
|
|
346
|
+
// Set resource metadata headers
|
|
306
347
|
c.header('X-Total-Count', total.toString());
|
|
307
|
-
c.header('X-Resource-Version',
|
|
308
|
-
|
|
309
|
-
// Add schema info
|
|
348
|
+
c.header('X-Resource-Version', version);
|
|
310
349
|
c.header('X-Schema-Fields', Object.keys(resource.config?.attributes || {}).length.toString());
|
|
311
350
|
|
|
312
351
|
return c.body(null, 200);
|
|
@@ -392,8 +431,12 @@ export function createRelationalRoutes(sourceResource, relationName, relationCon
|
|
|
392
431
|
|
|
393
432
|
// GET /{version}/{resource}/:id/{relation}
|
|
394
433
|
// Examples: GET /v1/users/user123/posts, GET /v1/users/user123/profile
|
|
395
|
-
|
|
396
|
-
|
|
434
|
+
// Note: The :id param comes from parent route mounting (see server.js:469)
|
|
435
|
+
app.get('/', asyncHandler(async (c) => {
|
|
436
|
+
// Get parent route's :id param
|
|
437
|
+
const pathParts = c.req.path.split('/');
|
|
438
|
+
const relationNameIndex = pathParts.lastIndexOf(relationName);
|
|
439
|
+
const id = pathParts[relationNameIndex - 1];
|
|
397
440
|
const query = c.req.query();
|
|
398
441
|
|
|
399
442
|
// Check if source resource exists
|