@rebasepro/server-core 0.7.0 → 0.9.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 +2 -2
- package/dist/api/ast-schema-editor.d.ts +21 -0
- package/dist/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
- package/dist/api/errors.d.ts +53 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/logs-routes.d.ts +37 -0
- package/dist/api/openapi-generator.d.ts +16 -0
- package/dist/api/rest/api-generator.d.ts +68 -0
- package/{src/api/rest/index.ts → dist/api/rest/index.d.ts} +0 -1
- package/dist/api/rest/query-parser.d.ts +6 -0
- package/dist/api/schema-editor-routes.d.ts +3 -0
- package/dist/api/types.d.ts +56 -0
- package/dist/auth/adapter-middleware.d.ts +43 -0
- package/dist/auth/admin-roles-route.d.ts +18 -0
- package/dist/auth/admin-user-ops.d.ts +79 -0
- package/dist/auth/admin-users-route.d.ts +28 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +39 -0
- package/{src/auth/api-keys/api-key-permission-guard.ts → dist/auth/api-keys/api-key-permission-guard.d.ts} +2 -34
- package/dist/auth/api-keys/api-key-routes.d.ts +20 -0
- package/dist/auth/api-keys/api-key-store.d.ts +35 -0
- package/{src/auth/api-keys/api-key-types.ts → dist/auth/api-keys/api-key-types.d.ts} +0 -6
- package/{src/auth/api-keys/index.ts → dist/auth/api-keys/index.d.ts} +2 -22
- package/dist/auth/apple-oauth.d.ts +30 -0
- package/{src/auth/auth-hooks.ts → dist/auth/auth-hooks.d.ts} +15 -73
- package/dist/auth/bitbucket-oauth.d.ts +11 -0
- package/dist/auth/builtin-auth-adapter.d.ts +55 -0
- package/dist/auth/cookie-utils.d.ts +32 -0
- package/{src/auth/crypto-utils.ts → dist/auth/crypto-utils.d.ts} +1 -16
- package/dist/auth/custom-auth-adapter.d.ts +39 -0
- package/dist/auth/discord-oauth.d.ts +14 -0
- package/dist/auth/facebook-oauth.d.ts +14 -0
- package/dist/auth/github-oauth.d.ts +15 -0
- package/dist/auth/gitlab-oauth.d.ts +13 -0
- package/dist/auth/google-oauth.d.ts +47 -0
- package/{src/auth/index.ts → dist/auth/index.d.ts} +4 -22
- package/{src/auth/interfaces.ts → dist/auth/interfaces.d.ts} +20 -89
- package/dist/auth/jwt.d.ts +67 -0
- package/dist/auth/linkedin-oauth.d.ts +18 -0
- package/dist/auth/magic-link-routes.d.ts +32 -0
- package/dist/auth/mfa-crypto.d.ts +23 -0
- package/dist/auth/mfa-routes.d.ts +7 -0
- package/dist/auth/mfa.d.ts +49 -0
- package/dist/auth/microsoft-oauth.d.ts +16 -0
- package/dist/auth/middleware.d.ts +158 -0
- package/dist/auth/password.d.ts +22 -0
- package/dist/auth/rate-limiter.d.ts +50 -0
- package/dist/auth/reset-password-admin.d.ts +29 -0
- package/dist/auth/rls-scope.d.ts +31 -0
- package/dist/auth/routes.d.ts +66 -0
- package/dist/auth/session-routes.d.ts +29 -0
- package/dist/auth/slack-oauth.d.ts +12 -0
- package/dist/auth/spotify-oauth.d.ts +12 -0
- package/dist/auth/twitter-oauth.d.ts +18 -0
- package/dist/collections/BackendCollectionRegistry.d.ts +13 -0
- package/dist/collections/loader.d.ts +5 -0
- package/dist/cron/cron-loader.d.ts +18 -0
- package/dist/cron/cron-routes.d.ts +14 -0
- package/dist/cron/cron-scheduler.d.ts +106 -0
- package/dist/cron/cron-store.d.ts +32 -0
- package/dist/cron/define-cron.d.ts +28 -0
- package/{src/cron/index.ts → dist/cron/index.d.ts} +1 -0
- package/dist/db/interfaces.d.ts +18 -0
- package/dist/dynamic-import-Dvh-K5fl.js +22 -0
- package/dist/dynamic-import-Dvh-K5fl.js.map +1 -0
- package/dist/email/index.d.ts +6 -0
- package/dist/email/smtp-email-service.d.ts +30 -0
- package/dist/email/templates.d.ts +50 -0
- package/{src/email/types.ts → dist/email/types.d.ts} +40 -36
- package/dist/env.d.ts +102 -0
- package/dist/functions/define-function.d.ts +55 -0
- package/dist/functions/function-loader.d.ts +18 -0
- package/dist/functions/function-routes.d.ts +10 -0
- package/{src/functions/index.ts → dist/functions/index.d.ts} +2 -0
- package/dist/history/history-routes.d.ts +23 -0
- package/{src/history/index.ts → dist/history/index.d.ts} +0 -1
- package/dist/index.d.ts +35 -0
- package/dist/index.es.js +18184 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +48629 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/init/docs.d.ts +4 -0
- package/dist/init/health.d.ts +2 -0
- package/dist/init/middlewares.d.ts +10 -0
- package/dist/init/shutdown.d.ts +65 -0
- package/dist/init/storage.d.ts +5 -0
- package/dist/init.d.ts +344 -0
- package/dist/jwt-BwIn8xmk.js +4200 -0
- package/dist/jwt-BwIn8xmk.js.map +1 -0
- package/dist/logger-BYU66ENZ.js +94 -0
- package/dist/logger-BYU66ENZ.js.map +1 -0
- package/dist/ms-DnYXB-Wd.js +162 -0
- package/dist/ms-DnYXB-Wd.js.map +1 -0
- package/dist/openapi-generator-Z9oYWLf_.js +586 -0
- package/dist/openapi-generator-Z9oYWLf_.js.map +1 -0
- package/dist/schema-editor-routes-D3ef8zu1.js +219 -0
- package/dist/schema-editor-routes-D3ef8zu1.js.map +1 -0
- package/dist/serve-spa.d.ts +36 -0
- package/dist/services/driver-registry.d.ts +78 -0
- package/dist/services/routed-realtime-service.d.ts +43 -0
- package/dist/services/webhook-service.d.ts +29 -0
- package/dist/singleton.d.ts +51 -0
- package/dist/src-B4OLmNVa.js +437 -0
- package/dist/src-B4OLmNVa.js.map +1 -0
- package/dist/src-B5WkP1Rv.js +24778 -0
- package/dist/src-B5WkP1Rv.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +43 -0
- package/dist/storage/LocalStorageController.d.ts +46 -0
- package/dist/storage/S3StorageController.d.ts +40 -0
- package/dist/storage/image-transform.d.ts +50 -0
- package/{src/storage/index.ts → dist/storage/index.d.ts} +6 -21
- package/dist/storage/routes.d.ts +70 -0
- package/dist/storage/storage-registry.d.ts +78 -0
- package/dist/storage/tus-handler.d.ts +53 -0
- package/{src/storage/types.ts → dist/storage/types.d.ts} +32 -38
- package/dist/types/index.d.ts +11 -0
- package/dist/utils/dev-port.d.ts +45 -0
- package/dist/utils/dynamic-import.d.ts +25 -0
- package/dist/utils/logger.d.ts +31 -0
- package/dist/utils/logging.d.ts +9 -0
- package/dist/utils/request-id.d.ts +4 -0
- package/dist/utils/request-logger.d.ts +19 -0
- package/{src/utils/sql.ts → dist/utils/sql.d.ts} +5 -16
- package/package.json +49 -22
- package/coverage/clover.xml +0 -3739
- package/coverage/coverage-final.json +0 -31
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -266
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +0 -952
- package/coverage/lcov-report/src/api/errors.ts.html +0 -472
- package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +0 -1069
- package/coverage/lcov-report/src/api/graphql/index.html +0 -116
- package/coverage/lcov-report/src/api/index.html +0 -176
- package/coverage/lcov-report/src/api/openapi-generator.ts.html +0 -565
- package/coverage/lcov-report/src/api/rest/api-generator.ts.html +0 -994
- package/coverage/lcov-report/src/api/rest/index.html +0 -131
- package/coverage/lcov-report/src/api/rest/query-parser.ts.html +0 -550
- package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +0 -202
- package/coverage/lcov-report/src/api/server.ts.html +0 -823
- package/coverage/lcov-report/src/auth/admin-routes.ts.html +0 -973
- package/coverage/lcov-report/src/auth/index.html +0 -176
- package/coverage/lcov-report/src/auth/jwt.ts.html +0 -574
- package/coverage/lcov-report/src/auth/middleware.ts.html +0 -745
- package/coverage/lcov-report/src/auth/password.ts.html +0 -310
- package/coverage/lcov-report/src/auth/services.ts.html +0 -2074
- package/coverage/lcov-report/src/collections/index.html +0 -116
- package/coverage/lcov-report/src/collections/loader.ts.html +0 -232
- package/coverage/lcov-report/src/db/auth-schema.ts.html +0 -523
- package/coverage/lcov-report/src/db/data-transformer.ts.html +0 -1753
- package/coverage/lcov-report/src/db/entityService.ts.html +0 -700
- package/coverage/lcov-report/src/db/index.html +0 -146
- package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +0 -4048
- package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +0 -883
- package/coverage/lcov-report/src/db/services/RelationService.ts.html +0 -3121
- package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +0 -442
- package/coverage/lcov-report/src/db/services/index.html +0 -176
- package/coverage/lcov-report/src/db/services/index.ts.html +0 -124
- package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +0 -1960
- package/coverage/lcov-report/src/index.html +0 -116
- package/coverage/lcov-report/src/services/driver-registry.ts.html +0 -631
- package/coverage/lcov-report/src/services/index.html +0 -131
- package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +0 -3025
- package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +0 -1189
- package/coverage/lcov-report/src/storage/S3StorageController.ts.html +0 -970
- package/coverage/lcov-report/src/storage/index.html +0 -161
- package/coverage/lcov-report/src/storage/storage-registry.ts.html +0 -646
- package/coverage/lcov-report/src/storage/types.ts.html +0 -451
- package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +0 -3082
- package/coverage/lcov-report/src/utils/index.html +0 -116
- package/coverage/lcov.info +0 -7179
- package/jest.config.cjs +0 -19
- package/src/api/ast-schema-editor.ts +0 -289
- package/src/api/collections_for_test/callbacks_test_collection.ts +0 -61
- package/src/api/errors.ts +0 -278
- package/src/api/graphql/graphql-schema-generator.ts +0 -383
- package/src/api/graphql/index.ts +0 -2
- package/src/api/index.ts +0 -11
- package/src/api/logs-routes.ts +0 -132
- package/src/api/openapi-generator.ts +0 -727
- package/src/api/rest/api-generator-count.test.ts +0 -126
- package/src/api/rest/api-generator.ts +0 -747
- package/src/api/rest/query-parser.ts +0 -300
- package/src/api/schema-editor-routes.ts +0 -41
- package/src/api/server.ts +0 -271
- package/src/api/types.ts +0 -104
- package/src/auth/adapter-middleware.ts +0 -114
- package/src/auth/admin-roles-route.ts +0 -36
- package/src/auth/admin-user-ops.ts +0 -236
- package/src/auth/admin-users-route.ts +0 -302
- package/src/auth/api-keys/api-key-middleware.ts +0 -133
- package/src/auth/api-keys/api-key-routes.ts +0 -193
- package/src/auth/api-keys/api-key-store.ts +0 -340
- package/src/auth/apple-oauth.ts +0 -122
- package/src/auth/bitbucket-oauth.ts +0 -84
- package/src/auth/builtin-auth-adapter.ts +0 -380
- package/src/auth/custom-auth-adapter.ts +0 -86
- package/src/auth/discord-oauth.ts +0 -85
- package/src/auth/facebook-oauth.ts +0 -74
- package/src/auth/github-oauth.ts +0 -112
- package/src/auth/gitlab-oauth.ts +0 -72
- package/src/auth/google-oauth.ts +0 -223
- package/src/auth/jwt.ts +0 -208
- package/src/auth/linkedin-oauth.ts +0 -83
- package/src/auth/magic-link-routes.ts +0 -167
- package/src/auth/mfa-crypto.ts +0 -91
- package/src/auth/mfa-routes.ts +0 -323
- package/src/auth/mfa.ts +0 -161
- package/src/auth/microsoft-oauth.ts +0 -90
- package/src/auth/middleware.ts +0 -369
- package/src/auth/password.ts +0 -77
- package/src/auth/rate-limiter.ts +0 -225
- package/src/auth/reset-password-admin.ts +0 -160
- package/src/auth/rls-scope.ts +0 -58
- package/src/auth/routes.ts +0 -802
- package/src/auth/session-routes.ts +0 -353
- package/src/auth/slack-oauth.ts +0 -73
- package/src/auth/spotify-oauth.ts +0 -69
- package/src/auth/twitter-oauth.ts +0 -128
- package/src/collections/BackendCollectionRegistry.ts +0 -20
- package/src/collections/loader.ts +0 -50
- package/src/cron/cron-loader.ts +0 -84
- package/src/cron/cron-routes.test.ts +0 -265
- package/src/cron/cron-routes.ts +0 -85
- package/src/cron/cron-scheduler.test.ts +0 -559
- package/src/cron/cron-scheduler.ts +0 -581
- package/src/cron/cron-store.ts +0 -166
- package/src/db/interfaces.ts +0 -60
- package/src/email/index.ts +0 -19
- package/src/email/smtp-email-service.ts +0 -123
- package/src/email/templates.ts +0 -470
- package/src/env.ts +0 -227
- package/src/functions/function-loader.ts +0 -116
- package/src/functions/function-routes.ts +0 -31
- package/src/history/history-routes.ts +0 -129
- package/src/index.ts +0 -70
- package/src/init/docs.ts +0 -47
- package/src/init/health.ts +0 -37
- package/src/init/middlewares.ts +0 -61
- package/src/init/shutdown.ts +0 -56
- package/src/init/storage.ts +0 -57
- package/src/init.ts +0 -1041
- package/src/serve-spa.ts +0 -89
- package/src/services/driver-registry.ts +0 -183
- package/src/services/routed-realtime-service.ts +0 -113
- package/src/services/webhook-service.ts +0 -155
- package/src/singleton.ts +0 -70
- package/src/storage/LocalStorageController.ts +0 -389
- package/src/storage/S3StorageController.ts +0 -300
- package/src/storage/image-transform.ts +0 -218
- package/src/storage/routes.ts +0 -368
- package/src/storage/storage-registry.ts +0 -188
- package/src/storage/tus-handler.ts +0 -315
- package/src/types/index.ts +0 -27
- package/src/utils/dev-port.ts +0 -196
- package/src/utils/logger.ts +0 -143
- package/src/utils/logging.ts +0 -40
- package/src/utils/request-id.ts +0 -40
- package/src/utils/request-logger.ts +0 -72
- package/test/api-generator.test.ts +0 -588
- package/test/api-key-permission-guard.test.ts +0 -144
- package/test/ast-schema-editor.test.ts +0 -89
- package/test/auth-middleware-hono.test.ts +0 -556
- package/test/auth-routes.test.ts +0 -1098
- package/test/backend-hooks-data.test.ts +0 -477
- package/test/custom-auth-adapter.test.ts +0 -189
- package/test/driver-registry.test.ts +0 -282
- package/test/email-templates.test.ts +0 -174
- package/test/env.test.ts +0 -128
- package/test/error-propagation.test.ts +0 -226
- package/test/errors-hono.test.ts +0 -133
- package/test/errors.test.ts +0 -155
- package/test/function-loader.test.ts +0 -127
- package/test/graphql-schema-generator.test.ts +0 -554
- package/test/jwt-security.test.ts +0 -182
- package/test/jwt.test.ts +0 -326
- package/test/mfa.test.ts +0 -197
- package/test/middleware.test.ts +0 -305
- package/test/multi-datasource-routing.test.ts +0 -113
- package/test/password.test.ts +0 -165
- package/test/query-parser.test.ts +0 -233
- package/test/rate-limiter.test.ts +0 -102
- package/test/reset-password-admin.test.ts +0 -113
- package/test/routed-realtime-service.test.ts +0 -86
- package/test/safe-compare.test.ts +0 -66
- package/test/singleton.test.ts +0 -59
- package/test/smtp-email-service.test.ts +0 -169
- package/test/storage-local.test.ts +0 -271
- package/test/storage-registry.test.ts +0 -282
- package/test/storage-routes.test.ts +0 -222
- package/test/storage-s3.test.ts +0 -304
- package/test/transform-auth-response.test.ts +0 -305
- package/test/webhook-service.test.ts +0 -260
- package/tsconfig.json +0 -54
- package/tsconfig.prod.json +0 -23
- package/vite.config.ts +0 -86
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage Registry
|
|
3
|
-
*
|
|
4
|
-
* Manages multiple storage controllers for Rebase backend.
|
|
5
|
-
* Allows different storage backends for different use cases.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* - Single storage: Pass a single StorageController → maps to "(default)"
|
|
9
|
-
* - Multiple storages: Pass a map of { storageId: StorageController }
|
|
10
|
-
* - String properties use `storageId` in their config to specify which storage to use
|
|
11
|
-
* - Properties without `storageId` fallback to "(default)"
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { StorageController } from "./types";
|
|
15
|
-
import { logger } from "../utils/logger";
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The default storage identifier used when:
|
|
19
|
-
* - A single storage controller is provided (not a map)
|
|
20
|
-
* - A property doesn't specify a storageId
|
|
21
|
-
*/
|
|
22
|
-
export const DEFAULT_STORAGE_ID = "(default)";
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Registry for managing multiple storage controllers
|
|
26
|
-
*/
|
|
27
|
-
export interface StorageRegistry {
|
|
28
|
-
/**
|
|
29
|
-
* Register a storage controller with an ID
|
|
30
|
-
* @param id - Unique identifier for this storage (e.g., "media", "backups")
|
|
31
|
-
* @param controller - The StorageController instance
|
|
32
|
-
*/
|
|
33
|
-
register(id: string, controller: StorageController): void;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Get the default storage controller (id = "(default)")
|
|
37
|
-
* @throws Error if no default storage is registered
|
|
38
|
-
*/
|
|
39
|
-
getDefault(): StorageController;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Get a storage controller by ID
|
|
43
|
-
* @param id - Storage identifier, or undefined/null for default
|
|
44
|
-
* @returns The StorageController, or undefined if not found
|
|
45
|
-
*/
|
|
46
|
-
get(id: string | undefined | null): StorageController | undefined;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Get a storage controller by ID, with fallback to default
|
|
50
|
-
* @param id - Storage identifier, or undefined/null for default
|
|
51
|
-
* @returns The StorageController (falls back to default if id not found)
|
|
52
|
-
* @throws Error if neither the specified nor default storage exists
|
|
53
|
-
*/
|
|
54
|
-
getOrDefault(id: string | undefined | null): StorageController;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Check if a storage with the given ID exists
|
|
58
|
-
*/
|
|
59
|
-
has(id: string): boolean;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* List all registered storage IDs
|
|
63
|
-
*/
|
|
64
|
-
list(): string[];
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Get the number of registered storage controllers
|
|
68
|
-
*/
|
|
69
|
-
size(): number;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Default implementation of StorageRegistry
|
|
74
|
-
*/
|
|
75
|
-
export class DefaultStorageRegistry implements StorageRegistry {
|
|
76
|
-
private controllers = new Map<string, StorageController>();
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Create a StorageRegistry from either a single controller or a map
|
|
80
|
-
* @param input - Single StorageController (maps to "(default)") or Record<string, StorageController>
|
|
81
|
-
*/
|
|
82
|
-
static create(
|
|
83
|
-
input: StorageController | Record<string, StorageController>
|
|
84
|
-
): DefaultStorageRegistry {
|
|
85
|
-
const registry = new DefaultStorageRegistry();
|
|
86
|
-
|
|
87
|
-
if (isStorageController(input)) {
|
|
88
|
-
// Single controller → register as "(default)"
|
|
89
|
-
registry.register(DEFAULT_STORAGE_ID, input);
|
|
90
|
-
} else {
|
|
91
|
-
// Map of controllers → register each
|
|
92
|
-
for (const [id, controller] of Object.entries(input)) {
|
|
93
|
-
if (isStorageController(controller)) {
|
|
94
|
-
registry.register(id, controller);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// Ensure there's a default if not explicitly provided
|
|
98
|
-
if (!registry.has(DEFAULT_STORAGE_ID) && registry.size() > 0) {
|
|
99
|
-
// If no explicit "(default)", use the first one as default
|
|
100
|
-
const firstId = Object.keys(input).find(k => isStorageController(input[k]));
|
|
101
|
-
if (firstId) {
|
|
102
|
-
logger.warn(
|
|
103
|
-
`[StorageRegistry] No "${DEFAULT_STORAGE_ID}" storage provided. ` +
|
|
104
|
-
`Using "${firstId}" as the default.`
|
|
105
|
-
);
|
|
106
|
-
registry.register(DEFAULT_STORAGE_ID, input[firstId]);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return registry;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
register(id: string, controller: StorageController): void {
|
|
115
|
-
if (this.controllers.has(id)) {
|
|
116
|
-
logger.warn(`[StorageRegistry] Overwriting storage with id "${id}"`);
|
|
117
|
-
}
|
|
118
|
-
this.controllers.set(id, controller);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
getDefault(): StorageController {
|
|
122
|
-
const controller = this.controllers.get(DEFAULT_STORAGE_ID);
|
|
123
|
-
if (!controller) {
|
|
124
|
-
throw new Error(
|
|
125
|
-
"[StorageRegistry] No default storage registered. " +
|
|
126
|
-
`Register one with id "${DEFAULT_STORAGE_ID}" or pass a single StorageController.`
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
return controller;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
get(id: string | undefined | null): StorageController | undefined {
|
|
133
|
-
if (id === undefined || id === null) {
|
|
134
|
-
return this.controllers.get(DEFAULT_STORAGE_ID);
|
|
135
|
-
}
|
|
136
|
-
return this.controllers.get(id);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
getOrDefault(id: string | undefined | null): StorageController {
|
|
140
|
-
// If no ID specified, return default
|
|
141
|
-
if (id === undefined || id === null) {
|
|
142
|
-
return this.getDefault();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Try to get by ID
|
|
146
|
-
const controller = this.controllers.get(id);
|
|
147
|
-
if (controller) {
|
|
148
|
-
return controller;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Fallback to default with warning
|
|
152
|
-
logger.warn(
|
|
153
|
-
`[StorageRegistry] Storage "${id}" not found, falling back to "${DEFAULT_STORAGE_ID}"`
|
|
154
|
-
);
|
|
155
|
-
return this.getDefault();
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
has(id: string): boolean {
|
|
159
|
-
return this.controllers.has(id);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
list(): string[] {
|
|
163
|
-
return Array.from(this.controllers.keys());
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
size(): number {
|
|
167
|
-
return this.controllers.size;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Type guard to check if an object is a StorageController
|
|
173
|
-
* vs a Record<string, StorageController> (multiple storages)
|
|
174
|
-
*/
|
|
175
|
-
function isStorageController(obj: unknown): obj is StorageController {
|
|
176
|
-
if (typeof obj !== "object" || obj === null) {
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
const controller = obj as StorageController;
|
|
180
|
-
// Check for required StorageController properties
|
|
181
|
-
return (
|
|
182
|
-
typeof controller.putObject === "function" &&
|
|
183
|
-
typeof controller.getSignedUrl === "function" &&
|
|
184
|
-
typeof controller.deleteObject === "function" &&
|
|
185
|
-
typeof controller.listObjects === "function" &&
|
|
186
|
-
typeof controller.getType === "function"
|
|
187
|
-
);
|
|
188
|
-
}
|
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TUS Protocol Handler
|
|
3
|
-
*
|
|
4
|
-
* Implements the TUS v1.0.0 resumable upload protocol with the
|
|
5
|
-
* Creation and Termination extensions. Uploads are stored in a
|
|
6
|
-
* temporary directory and moved to final storage on completion.
|
|
7
|
-
*
|
|
8
|
-
* @see https://tus.io/protocols/resumable-upload
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { randomUUID } from "crypto";
|
|
12
|
-
import { writeFile, unlink, stat, mkdir, open } from "fs/promises";
|
|
13
|
-
import { existsSync } from "fs";
|
|
14
|
-
import { join } from "path";
|
|
15
|
-
import type { Context } from "hono";
|
|
16
|
-
import type { StorageController } from "./types";
|
|
17
|
-
import { logger } from "../utils/logger.js";
|
|
18
|
-
|
|
19
|
-
/** Metadata for an in-progress resumable upload. */
|
|
20
|
-
interface TusUpload {
|
|
21
|
-
id: string;
|
|
22
|
-
/** Total declared size in bytes. */
|
|
23
|
-
size: number;
|
|
24
|
-
/** Bytes received so far. */
|
|
25
|
-
offset: number;
|
|
26
|
-
/** TUS metadata parsed from the creation request. */
|
|
27
|
-
metadata: Record<string, string>;
|
|
28
|
-
/** Timestamp of creation (epoch ms). */
|
|
29
|
-
createdAt: number;
|
|
30
|
-
/** Absolute path to the temp file on disk. */
|
|
31
|
-
filePath: string;
|
|
32
|
-
/** Target bucket (from metadata). */
|
|
33
|
-
bucket?: string;
|
|
34
|
-
/** Target key / filename (from metadata). */
|
|
35
|
-
key?: string;
|
|
36
|
-
/** Whether the upload has been fully received and finalized. */
|
|
37
|
-
completed: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** Maximum upload size: 5 GB. */
|
|
41
|
-
const MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024;
|
|
42
|
-
|
|
43
|
-
/** Stale upload expiry: 24 hours. */
|
|
44
|
-
const UPLOAD_EXPIRY_MS = 24 * 60 * 60 * 1000;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* TUS resumable upload handler.
|
|
48
|
-
*
|
|
49
|
-
* Each instance manages uploads for a single storage root. The
|
|
50
|
-
* `storageController` is used to finalize completed uploads by
|
|
51
|
-
* calling `putObject`.
|
|
52
|
-
*/
|
|
53
|
-
export class TusHandler {
|
|
54
|
-
private uploads = new Map<string, TusUpload>();
|
|
55
|
-
private tusDir: string;
|
|
56
|
-
private cleanupTimer?: ReturnType<typeof setInterval>;
|
|
57
|
-
|
|
58
|
-
constructor(
|
|
59
|
-
storageBaseDir: string,
|
|
60
|
-
private storageController?: StorageController
|
|
61
|
-
) {
|
|
62
|
-
this.tusDir = join(storageBaseDir, ".tus-uploads");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Ensure the temp directory exists. */
|
|
66
|
-
private async ensureDir(): Promise<void> {
|
|
67
|
-
if (!existsSync(this.tusDir)) {
|
|
68
|
-
await mkdir(this.tusDir, { recursive: true });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Start periodic cleanup of stale uploads. */
|
|
73
|
-
startCleanup(): void {
|
|
74
|
-
if (this.cleanupTimer) return;
|
|
75
|
-
this.cleanupTimer = setInterval(() => {
|
|
76
|
-
void this.cleanupStale();
|
|
77
|
-
}, 60_000); // every minute
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Remove uploads that have been idle for longer than UPLOAD_EXPIRY_MS. */
|
|
81
|
-
private async cleanupStale(): Promise<void> {
|
|
82
|
-
const now = Date.now();
|
|
83
|
-
for (const [id, upload] of this.uploads) {
|
|
84
|
-
if (now - upload.createdAt > UPLOAD_EXPIRY_MS && !upload.completed) {
|
|
85
|
-
try { await unlink(upload.filePath); } catch { /* ok */ }
|
|
86
|
-
this.uploads.delete(id);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// -----------------------------------------------------------------------
|
|
92
|
-
// TUS Metadata Parsing
|
|
93
|
-
// -----------------------------------------------------------------------
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Parse the `Upload-Metadata` header.
|
|
97
|
-
*
|
|
98
|
-
* Format: `key base64value,key2 base64value2`
|
|
99
|
-
*/
|
|
100
|
-
private parseMetadata(header: string): Record<string, string> {
|
|
101
|
-
const metadata: Record<string, string> = {};
|
|
102
|
-
if (!header) return metadata;
|
|
103
|
-
for (const pair of header.split(",")) {
|
|
104
|
-
const trimmed = pair.trim();
|
|
105
|
-
const spaceIdx = trimmed.indexOf(" ");
|
|
106
|
-
if (spaceIdx === -1) {
|
|
107
|
-
metadata[trimmed] = "";
|
|
108
|
-
} else {
|
|
109
|
-
const key = trimmed.substring(0, spaceIdx);
|
|
110
|
-
const value = Buffer.from(trimmed.substring(spaceIdx + 1), "base64").toString("utf-8");
|
|
111
|
-
metadata[key] = value;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return metadata;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// -----------------------------------------------------------------------
|
|
118
|
-
// Protocol Endpoints
|
|
119
|
-
// -----------------------------------------------------------------------
|
|
120
|
-
|
|
121
|
-
/** `OPTIONS /tus` — TUS capability discovery. */
|
|
122
|
-
options(): Response {
|
|
123
|
-
return new Response(null, {
|
|
124
|
-
status: 204,
|
|
125
|
-
headers: {
|
|
126
|
-
"Tus-Resumable": "1.0.0",
|
|
127
|
-
"Tus-Version": "1.0.0",
|
|
128
|
-
"Tus-Extension": "creation,termination",
|
|
129
|
-
"Tus-Max-Size": String(MAX_UPLOAD_SIZE)
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** `POST /tus` — Create a new upload. */
|
|
135
|
-
async create(c: Context): Promise<Response> {
|
|
136
|
-
await this.ensureDir();
|
|
137
|
-
|
|
138
|
-
const uploadLengthHeader = c.req.header("Upload-Length");
|
|
139
|
-
if (!uploadLengthHeader) {
|
|
140
|
-
return c.json({ error: "Upload-Length header is required" }, 400);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const uploadLength = parseInt(uploadLengthHeader, 10);
|
|
144
|
-
if (Number.isNaN(uploadLength) || uploadLength <= 0) {
|
|
145
|
-
return c.json({ error: "Invalid Upload-Length" }, 400);
|
|
146
|
-
}
|
|
147
|
-
if (uploadLength > MAX_UPLOAD_SIZE) {
|
|
148
|
-
return c.json({ error: `Upload-Length exceeds maximum of ${MAX_UPLOAD_SIZE} bytes` }, 413);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const metadata = this.parseMetadata(c.req.header("Upload-Metadata") || "");
|
|
152
|
-
const id = randomUUID();
|
|
153
|
-
const filePath = join(this.tusDir, id);
|
|
154
|
-
|
|
155
|
-
// Create empty temp file
|
|
156
|
-
await writeFile(filePath, Buffer.alloc(0));
|
|
157
|
-
|
|
158
|
-
const upload: TusUpload = {
|
|
159
|
-
id,
|
|
160
|
-
size: uploadLength,
|
|
161
|
-
offset: 0,
|
|
162
|
-
metadata,
|
|
163
|
-
createdAt: Date.now(),
|
|
164
|
-
filePath,
|
|
165
|
-
bucket: metadata.bucket || undefined,
|
|
166
|
-
key: metadata.key || metadata.filename || undefined,
|
|
167
|
-
completed: false
|
|
168
|
-
};
|
|
169
|
-
this.uploads.set(id, upload);
|
|
170
|
-
|
|
171
|
-
// Build absolute Location
|
|
172
|
-
const reqUrl = new URL(c.req.url);
|
|
173
|
-
const location = `${reqUrl.origin}${reqUrl.pathname}/${id}`;
|
|
174
|
-
|
|
175
|
-
return new Response(null, {
|
|
176
|
-
status: 201,
|
|
177
|
-
headers: {
|
|
178
|
-
Location: location,
|
|
179
|
-
"Tus-Resumable": "1.0.0",
|
|
180
|
-
"Upload-Offset": "0"
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/** `HEAD /tus/:id` — Query upload progress. */
|
|
186
|
-
head(c: Context, id: string): Response {
|
|
187
|
-
const upload = this.uploads.get(id);
|
|
188
|
-
if (!upload) {
|
|
189
|
-
return c.json({ error: "Upload not found" }, 404);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return new Response(null, {
|
|
193
|
-
status: 200,
|
|
194
|
-
headers: {
|
|
195
|
-
"Tus-Resumable": "1.0.0",
|
|
196
|
-
"Upload-Offset": String(upload.offset),
|
|
197
|
-
"Upload-Length": String(upload.size),
|
|
198
|
-
"Cache-Control": "no-store"
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** `PATCH /tus/:id` — Append data to an upload. */
|
|
204
|
-
async patch(c: Context, id: string): Promise<Response> {
|
|
205
|
-
const upload = this.uploads.get(id);
|
|
206
|
-
if (!upload) {
|
|
207
|
-
return c.json({ error: "Upload not found" }, 404);
|
|
208
|
-
}
|
|
209
|
-
if (upload.completed) {
|
|
210
|
-
return c.json({ error: "Upload already completed" }, 400);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Validate offset
|
|
214
|
-
const offsetHeader = c.req.header("Upload-Offset");
|
|
215
|
-
if (!offsetHeader) {
|
|
216
|
-
return c.json({ error: "Upload-Offset header is required" }, 400);
|
|
217
|
-
}
|
|
218
|
-
const offset = parseInt(offsetHeader, 10);
|
|
219
|
-
if (offset !== upload.offset) {
|
|
220
|
-
return c.json({ error: "Offset mismatch" }, 409);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Validate content type
|
|
224
|
-
const contentType = c.req.header("Content-Type");
|
|
225
|
-
if (contentType !== "application/offset+octet-stream") {
|
|
226
|
-
return c.json({ error: "Content-Type must be application/offset+octet-stream" }, 415);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Read chunk and append to temp file
|
|
230
|
-
const body = await c.req.arrayBuffer();
|
|
231
|
-
const chunk = Buffer.from(body);
|
|
232
|
-
|
|
233
|
-
// Prevent overrun
|
|
234
|
-
if (upload.offset + chunk.length > upload.size) {
|
|
235
|
-
return c.json({ error: "Chunk exceeds declared Upload-Length" }, 413);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const fh = await open(upload.filePath, "a");
|
|
239
|
-
try {
|
|
240
|
-
await fh.write(chunk);
|
|
241
|
-
} finally {
|
|
242
|
-
await fh.close();
|
|
243
|
-
}
|
|
244
|
-
upload.offset += chunk.length;
|
|
245
|
-
|
|
246
|
-
// Finalize if complete
|
|
247
|
-
if (upload.offset >= upload.size) {
|
|
248
|
-
await this.finalize(upload);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return new Response(null, {
|
|
252
|
-
status: 204,
|
|
253
|
-
headers: {
|
|
254
|
-
"Tus-Resumable": "1.0.0",
|
|
255
|
-
"Upload-Offset": String(upload.offset)
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/** `DELETE /tus/:id` — Cancel and remove an upload. */
|
|
261
|
-
async delete(c: Context, id: string): Promise<Response> {
|
|
262
|
-
const upload = this.uploads.get(id);
|
|
263
|
-
if (!upload) {
|
|
264
|
-
return c.json({ error: "Upload not found" }, 404);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
try { await unlink(upload.filePath); } catch { /* ok */ }
|
|
268
|
-
this.uploads.delete(id);
|
|
269
|
-
|
|
270
|
-
return new Response(null, {
|
|
271
|
-
status: 204,
|
|
272
|
-
headers: { "Tus-Resumable": "1.0.0" }
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// -----------------------------------------------------------------------
|
|
277
|
-
// Finalization
|
|
278
|
-
// -----------------------------------------------------------------------
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Move a completed upload into the storage controller.
|
|
282
|
-
*/
|
|
283
|
-
private async finalize(upload: TusUpload): Promise<void> {
|
|
284
|
-
upload.completed = true;
|
|
285
|
-
|
|
286
|
-
if (!this.storageController) {
|
|
287
|
-
// No controller — leave temp file in place
|
|
288
|
-
logger.warn("[TUS] Upload completed but no StorageController configured. Temp file remains:", { filePath: upload.filePath });
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
try {
|
|
293
|
-
const { readFile } = await import("fs/promises");
|
|
294
|
-
const data = await readFile(upload.filePath);
|
|
295
|
-
const fileName = upload.key || upload.metadata.filename || upload.id;
|
|
296
|
-
const mimeType = upload.metadata.contentType || upload.metadata.filetype || "application/octet-stream";
|
|
297
|
-
|
|
298
|
-
const file = new File([data], fileName, { type: mimeType });
|
|
299
|
-
|
|
300
|
-
await this.storageController.putObject({
|
|
301
|
-
file,
|
|
302
|
-
key: fileName,
|
|
303
|
-
bucket: upload.bucket
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
// Clean up temp file
|
|
307
|
-
try { await unlink(upload.filePath); } catch { /* ok */ }
|
|
308
|
-
this.uploads.delete(upload.id);
|
|
309
|
-
|
|
310
|
-
logger.info(`[TUS] Upload ${upload.id} finalized → ${fileName}`);
|
|
311
|
-
} catch (err) {
|
|
312
|
-
logger.error(`[TUS] Failed to finalize upload ${upload.id}`, { error: err });
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
package/src/types/index.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Entity,
|
|
3
|
-
EntityCollection,
|
|
4
|
-
EntityStatus,
|
|
5
|
-
FilterValues,
|
|
6
|
-
FetchCollectionProps,
|
|
7
|
-
FetchEntityProps,
|
|
8
|
-
SaveEntityProps,
|
|
9
|
-
DeleteEntityProps,
|
|
10
|
-
WebSocketMessage,
|
|
11
|
-
CollectionUpdateMessage,
|
|
12
|
-
EntityUpdateMessage
|
|
13
|
-
} from "@rebasepro/types";
|
|
14
|
-
|
|
15
|
-
// Subscription types
|
|
16
|
-
export interface ListenCollectionRequest<M extends Record<string, unknown> = Record<string, unknown>> extends FetchCollectionProps<M> {
|
|
17
|
-
subscriptionId: string;
|
|
18
|
-
onUpdate: (entities: Entity<M>[]) => void;
|
|
19
|
-
onError?: (error: Error) => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface ListenEntityRequest<M extends Record<string, unknown> = Record<string, unknown>> extends FetchEntityProps<M> {
|
|
23
|
-
subscriptionId: string;
|
|
24
|
-
onUpdate: (entity: Entity<M> | null) => void;
|
|
25
|
-
onError?: (error: Error) => void;
|
|
26
|
-
}
|
|
27
|
-
|