@rebasepro/server-core 0.0.1-canary.09e5ec5
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/LICENSE +6 -0
- package/README.md +40 -0
- package/build-errors.txt +52 -0
- package/coverage/clover.xml +3739 -0
- package/coverage/coverage-final.json +31 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +266 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +952 -0
- package/coverage/lcov-report/src/api/errors.ts.html +472 -0
- package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +1069 -0
- package/coverage/lcov-report/src/api/graphql/index.html +116 -0
- package/coverage/lcov-report/src/api/index.html +176 -0
- package/coverage/lcov-report/src/api/openapi-generator.ts.html +565 -0
- package/coverage/lcov-report/src/api/rest/api-generator.ts.html +994 -0
- package/coverage/lcov-report/src/api/rest/index.html +131 -0
- package/coverage/lcov-report/src/api/rest/query-parser.ts.html +550 -0
- package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +202 -0
- package/coverage/lcov-report/src/api/server.ts.html +823 -0
- package/coverage/lcov-report/src/auth/admin-routes.ts.html +973 -0
- package/coverage/lcov-report/src/auth/index.html +176 -0
- package/coverage/lcov-report/src/auth/jwt.ts.html +574 -0
- package/coverage/lcov-report/src/auth/middleware.ts.html +745 -0
- package/coverage/lcov-report/src/auth/password.ts.html +310 -0
- package/coverage/lcov-report/src/auth/services.ts.html +2074 -0
- package/coverage/lcov-report/src/collections/index.html +116 -0
- package/coverage/lcov-report/src/collections/loader.ts.html +232 -0
- package/coverage/lcov-report/src/db/auth-schema.ts.html +523 -0
- package/coverage/lcov-report/src/db/data-transformer.ts.html +1753 -0
- package/coverage/lcov-report/src/db/entityService.ts.html +700 -0
- package/coverage/lcov-report/src/db/index.html +146 -0
- package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +4048 -0
- package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +883 -0
- package/coverage/lcov-report/src/db/services/RelationService.ts.html +3121 -0
- package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +442 -0
- package/coverage/lcov-report/src/db/services/index.html +176 -0
- package/coverage/lcov-report/src/db/services/index.ts.html +124 -0
- package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +1960 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/services/driver-registry.ts.html +631 -0
- package/coverage/lcov-report/src/services/index.html +131 -0
- package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +3025 -0
- package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +1189 -0
- package/coverage/lcov-report/src/storage/S3StorageController.ts.html +970 -0
- package/coverage/lcov-report/src/storage/index.html +161 -0
- package/coverage/lcov-report/src/storage/storage-registry.ts.html +646 -0
- package/coverage/lcov-report/src/storage/types.ts.html +451 -0
- package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +3082 -0
- package/coverage/lcov-report/src/utils/index.html +116 -0
- package/coverage/lcov.info +7179 -0
- package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/data/buildRebaseData.d.ts +14 -0
- package/dist/common/src/index.d.ts +3 -0
- package/dist/common/src/util/builders.d.ts +57 -0
- package/dist/common/src/util/callbacks.d.ts +6 -0
- package/dist/common/src/util/collections.d.ts +11 -0
- package/dist/common/src/util/common.d.ts +2 -0
- package/dist/common/src/util/conditions.d.ts +26 -0
- package/dist/common/src/util/entities.d.ts +58 -0
- package/dist/common/src/util/enums.d.ts +3 -0
- package/dist/common/src/util/index.d.ts +16 -0
- package/dist/common/src/util/navigation_from_path.d.ts +34 -0
- package/dist/common/src/util/navigation_utils.d.ts +20 -0
- package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
- package/dist/common/src/util/paths.d.ts +14 -0
- package/dist/common/src/util/permissions.d.ts +5 -0
- package/dist/common/src/util/references.d.ts +2 -0
- package/dist/common/src/util/relations.d.ts +22 -0
- package/dist/common/src/util/resolutions.d.ts +72 -0
- package/dist/common/src/util/storage.d.ts +24 -0
- package/dist/index-DXVBFp5V.js +37 -0
- package/dist/index-DXVBFp5V.js.map +1 -0
- package/dist/index.es.js +49934 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +49968 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/server-core/src/api/ast-schema-editor.d.ts +21 -0
- package/dist/server-core/src/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
- package/dist/server-core/src/api/errors.d.ts +35 -0
- package/dist/server-core/src/api/graphql/graphql-schema-generator.d.ts +35 -0
- package/dist/server-core/src/api/graphql/index.d.ts +1 -0
- package/dist/server-core/src/api/index.d.ts +9 -0
- package/dist/server-core/src/api/openapi-generator.d.ts +16 -0
- package/dist/server-core/src/api/rest/api-generator.d.ts +64 -0
- package/dist/server-core/src/api/rest/index.d.ts +1 -0
- package/dist/server-core/src/api/rest/query-parser.d.ts +9 -0
- package/dist/server-core/src/api/schema-editor-routes.d.ts +3 -0
- package/dist/server-core/src/api/server.d.ts +40 -0
- package/dist/server-core/src/api/types.d.ts +90 -0
- package/dist/server-core/src/auth/admin-routes.d.ts +16 -0
- package/dist/server-core/src/auth/apple-oauth.d.ts +30 -0
- package/dist/server-core/src/auth/bitbucket-oauth.d.ts +11 -0
- package/dist/server-core/src/auth/discord-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/facebook-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/github-oauth.d.ts +15 -0
- package/dist/server-core/src/auth/gitlab-oauth.d.ts +13 -0
- package/dist/server-core/src/auth/google-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/index.d.ts +23 -0
- package/dist/server-core/src/auth/interfaces.d.ts +309 -0
- package/dist/server-core/src/auth/jwt.d.ts +43 -0
- package/dist/server-core/src/auth/linkedin-oauth.d.ts +18 -0
- package/dist/server-core/src/auth/microsoft-oauth.d.ts +16 -0
- package/dist/server-core/src/auth/middleware.d.ts +81 -0
- package/dist/server-core/src/auth/password.d.ts +22 -0
- package/dist/server-core/src/auth/rate-limiter.d.ts +31 -0
- package/dist/server-core/src/auth/routes.d.ts +27 -0
- package/dist/server-core/src/auth/slack-oauth.d.ts +12 -0
- package/dist/server-core/src/auth/spotify-oauth.d.ts +12 -0
- package/dist/server-core/src/auth/twitter-oauth.d.ts +18 -0
- package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
- package/dist/server-core/src/collections/BackendCollectionRegistry.d.ts +13 -0
- package/dist/server-core/src/collections/loader.d.ts +5 -0
- package/dist/server-core/src/cron/cron-loader.d.ts +17 -0
- package/dist/server-core/src/cron/cron-routes.d.ts +14 -0
- package/dist/server-core/src/cron/cron-scheduler.d.ts +61 -0
- package/dist/server-core/src/cron/cron-store.d.ts +32 -0
- package/dist/server-core/src/cron/index.d.ts +6 -0
- package/dist/server-core/src/db/interfaces.d.ts +18 -0
- package/dist/server-core/src/email/index.d.ts +6 -0
- package/dist/server-core/src/email/smtp-email-service.d.ts +25 -0
- package/dist/server-core/src/email/templates.d.ts +42 -0
- package/dist/server-core/src/email/types.d.ts +107 -0
- package/dist/server-core/src/functions/function-loader.d.ts +17 -0
- package/dist/server-core/src/functions/function-routes.d.ts +10 -0
- package/dist/server-core/src/functions/index.d.ts +3 -0
- package/dist/server-core/src/history/history-routes.d.ts +23 -0
- package/dist/server-core/src/history/index.d.ts +1 -0
- package/dist/server-core/src/index.d.ts +29 -0
- package/dist/server-core/src/init.d.ts +159 -0
- package/dist/server-core/src/serve-spa.d.ts +30 -0
- package/dist/server-core/src/services/driver-registry.d.ts +78 -0
- package/dist/server-core/src/singleton.d.ts +35 -0
- package/dist/server-core/src/storage/LocalStorageController.d.ts +46 -0
- package/dist/server-core/src/storage/S3StorageController.d.ts +36 -0
- package/dist/server-core/src/storage/index.d.ts +25 -0
- package/dist/server-core/src/storage/routes.d.ts +38 -0
- package/dist/server-core/src/storage/storage-registry.d.ts +78 -0
- package/dist/server-core/src/storage/types.d.ts +103 -0
- package/dist/server-core/src/types/index.d.ts +11 -0
- package/dist/server-core/src/utils/dev-port.d.ts +35 -0
- package/dist/server-core/src/utils/logger.d.ts +31 -0
- package/dist/server-core/src/utils/logging.d.ts +9 -0
- package/dist/server-core/src/utils/request-logger.d.ts +19 -0
- package/dist/server-core/src/utils/sql.d.ts +27 -0
- package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
- package/dist/types/src/controllers/auth.d.ts +119 -0
- package/dist/types/src/controllers/client.d.ts +170 -0
- package/dist/types/src/controllers/collection_registry.d.ts +45 -0
- package/dist/types/src/controllers/customization_controller.d.ts +60 -0
- package/dist/types/src/controllers/data.d.ts +168 -0
- package/dist/types/src/controllers/data_driver.d.ts +160 -0
- package/dist/types/src/controllers/database_admin.d.ts +11 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
- package/dist/types/src/controllers/effective_role.d.ts +4 -0
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +18 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
- package/dist/types/src/controllers/navigation.d.ts +213 -0
- package/dist/types/src/controllers/registry.d.ts +54 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
- package/dist/types/src/controllers/snackbar.d.ts +24 -0
- package/dist/types/src/controllers/storage.d.ts +171 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/rebase_context.d.ts +105 -0
- package/dist/types/src/types/backend.d.ts +536 -0
- package/dist/types/src/types/builders.d.ts +15 -0
- package/dist/types/src/types/chips.d.ts +5 -0
- package/dist/types/src/types/collections.d.ts +856 -0
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +64 -0
- package/dist/types/src/types/entities.d.ts +145 -0
- package/dist/types/src/types/entity_actions.d.ts +98 -0
- package/dist/types/src/types/entity_callbacks.d.ts +173 -0
- package/dist/types/src/types/entity_link_builder.d.ts +7 -0
- package/dist/types/src/types/entity_overrides.d.ts +10 -0
- package/dist/types/src/types/entity_views.d.ts +61 -0
- package/dist/types/src/types/export_import.d.ts +21 -0
- package/dist/types/src/types/index.d.ts +23 -0
- package/dist/types/src/types/locales.d.ts +4 -0
- package/dist/types/src/types/modify_collections.d.ts +5 -0
- package/dist/types/src/types/plugins.d.ts +279 -0
- package/dist/types/src/types/properties.d.ts +1176 -0
- package/dist/types/src/types/property_config.d.ts +70 -0
- package/dist/types/src/types/relations.d.ts +336 -0
- package/dist/types/src/types/slots.d.ts +252 -0
- package/dist/types/src/types/translations.d.ts +870 -0
- package/dist/types/src/types/user_management_delegate.d.ts +121 -0
- package/dist/types/src/types/websockets.d.ts +78 -0
- package/dist/types/src/users/index.d.ts +2 -0
- package/dist/types/src/users/roles.d.ts +22 -0
- package/dist/types/src/users/user.d.ts +46 -0
- package/history_diff.log +385 -0
- package/jest.config.cjs +16 -0
- package/package.json +86 -0
- package/scratch.ts +9 -0
- package/src/api/ast-schema-editor.ts +289 -0
- package/src/api/collections_for_test/callbacks_test_collection.ts +60 -0
- package/src/api/errors.ts +179 -0
- package/src/api/graphql/graphql-schema-generator.ts +336 -0
- package/src/api/graphql/index.ts +2 -0
- package/src/api/index.ts +11 -0
- package/src/api/openapi-generator.ts +715 -0
- package/src/api/rest/api-generator.ts +472 -0
- package/src/api/rest/index.ts +2 -0
- package/src/api/rest/query-parser.ts +155 -0
- package/src/api/schema-editor-routes.ts +41 -0
- package/src/api/server.ts +248 -0
- package/src/api/types.ts +90 -0
- package/src/auth/admin-routes.ts +529 -0
- package/src/auth/apple-oauth.ts +130 -0
- package/src/auth/bitbucket-oauth.ts +82 -0
- package/src/auth/discord-oauth.ts +83 -0
- package/src/auth/facebook-oauth.ts +72 -0
- package/src/auth/github-oauth.ts +110 -0
- package/src/auth/gitlab-oauth.ts +70 -0
- package/src/auth/google-oauth.ts +48 -0
- package/src/auth/index.ts +34 -0
- package/src/auth/interfaces.ts +363 -0
- package/src/auth/jwt.ts +181 -0
- package/src/auth/linkedin-oauth.ts +81 -0
- package/src/auth/microsoft-oauth.ts +88 -0
- package/src/auth/middleware.ts +384 -0
- package/src/auth/password.ts +77 -0
- package/src/auth/rate-limiter.ts +129 -0
- package/src/auth/routes.ts +788 -0
- package/src/auth/slack-oauth.ts +71 -0
- package/src/auth/spotify-oauth.ts +67 -0
- package/src/auth/twitter-oauth.ts +120 -0
- package/src/bootstrappers/index.ts +1 -0
- package/src/collections/BackendCollectionRegistry.ts +20 -0
- package/src/collections/loader.ts +49 -0
- package/src/cron/cron-loader.ts +89 -0
- package/src/cron/cron-routes.test.ts +265 -0
- package/src/cron/cron-routes.ts +85 -0
- package/src/cron/cron-scheduler.test.ts +421 -0
- package/src/cron/cron-scheduler.ts +413 -0
- package/src/cron/cron-store.ts +163 -0
- package/src/cron/index.ts +6 -0
- package/src/db/interfaces.ts +60 -0
- package/src/email/index.ts +18 -0
- package/src/email/smtp-email-service.ts +91 -0
- package/src/email/templates.ts +388 -0
- package/src/email/types.ts +105 -0
- package/src/functions/function-loader.ts +119 -0
- package/src/functions/function-routes.ts +31 -0
- package/src/functions/index.ts +3 -0
- package/src/history/history-routes.ts +129 -0
- package/src/history/index.ts +2 -0
- package/src/index.ts +66 -0
- package/src/init.ts +727 -0
- package/src/serve-spa.ts +81 -0
- package/src/services/driver-registry.ts +182 -0
- package/src/singleton.test.ts +28 -0
- package/src/singleton.ts +70 -0
- package/src/storage/LocalStorageController.ts +365 -0
- package/src/storage/S3StorageController.ts +298 -0
- package/src/storage/index.ts +43 -0
- package/src/storage/routes.ts +264 -0
- package/src/storage/storage-registry.ts +187 -0
- package/src/storage/types.ts +134 -0
- package/src/types/index.ts +27 -0
- package/src/utils/dev-port.ts +176 -0
- package/src/utils/logger.ts +143 -0
- package/src/utils/logging.ts +38 -0
- package/src/utils/request-logger.ts +66 -0
- package/src/utils/sql.ts +38 -0
- package/test/admin-routes.test.ts +640 -0
- package/test/api-generator.test.ts +501 -0
- package/test/ast-schema-editor.test.ts +63 -0
- package/test/auth-middleware-hono.test.ts +556 -0
- package/test/auth-routes.test.ts +1047 -0
- package/test/driver-registry.test.ts +282 -0
- package/test/error-propagation.test.ts +226 -0
- package/test/errors-hono.test.ts +133 -0
- package/test/errors.test.ts +155 -0
- package/test/jwt-security.test.ts +182 -0
- package/test/jwt.test.ts +324 -0
- package/test/middleware.test.ts +300 -0
- package/test/password.test.ts +165 -0
- package/test/query-parser.test.ts +263 -0
- package/test/rate-limiter.test.ts +102 -0
- package/test/safe-compare.test.ts +66 -0
- package/test/singleton.test.ts +59 -0
- package/test/storage-local.test.ts +271 -0
- package/test/storage-registry.test.ts +282 -0
- package/test/storage-routes.test.ts +222 -0
- package/test/storage-s3.test.ts +304 -0
- package/test-ast.ts +28 -0
- package/test.ts +6 -0
- package/test_output.txt +1133 -0
- package/tsconfig.json +49 -0
- package/tsconfig.prod.json +20 -0
- package/vite.config.ts +80 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage REST API routes using Hono
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Hono } from "hono";
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import { StorageController } from "./types";
|
|
8
|
+
import { LocalStorageController } from "./LocalStorageController";
|
|
9
|
+
import { requireAuth as jwtRequireAuth, optionalAuth } from "../auth/middleware";
|
|
10
|
+
import { ApiError, errorHandler } from "../api/errors";
|
|
11
|
+
import { HonoEnv } from "../api/types";
|
|
12
|
+
|
|
13
|
+
export interface StorageRoutesConfig {
|
|
14
|
+
controller: StorageController;
|
|
15
|
+
/** Base path for storage routes (default: '/api/storage') */
|
|
16
|
+
basePath?: string;
|
|
17
|
+
/** Require authentication for write operations (default: true) */
|
|
18
|
+
requireAuth?: boolean;
|
|
19
|
+
/** Allow unauthenticated read access to stored files (default: false).
|
|
20
|
+
* When false and requireAuth is true, reads also require authentication. */
|
|
21
|
+
publicRead?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Extract the wildcard portion of a route path from the full request path.
|
|
26
|
+
*
|
|
27
|
+
* Hono's `c.req.param('*')` does not work reliably in sub-routers mounted
|
|
28
|
+
* via `app.route(prefix, subRouter)`. Instead we derive the wildcard value
|
|
29
|
+
* from the fully-resolved `c.req.path` and `c.req.routePath`.
|
|
30
|
+
*
|
|
31
|
+
* For a route `/metadata/*` mounted at `/api/storage`, a request to
|
|
32
|
+
* `/api/storage/metadata/default/file.jpg` yields routePath
|
|
33
|
+
* `/api/storage/metadata/*`. We strip the prefix (everything before `/*`)
|
|
34
|
+
* plus one character for the trailing `/` to obtain `default/file.jpg`.
|
|
35
|
+
*/
|
|
36
|
+
export function extractWildcardPath(c: { req: { path: string; routePath: string } }): string {
|
|
37
|
+
const routePath = c.req.routePath; // e.g. "/api/storage/metadata/*"
|
|
38
|
+
const prefix = routePath.replace("/*", ""); // e.g. "/api/storage/metadata"
|
|
39
|
+
const fullPath = c.req.path; // e.g. "/api/storage/metadata/default/file.jpg"
|
|
40
|
+
const idx = fullPath.indexOf(prefix);
|
|
41
|
+
if (idx < 0) return "";
|
|
42
|
+
// +1 to skip the '/' after the prefix
|
|
43
|
+
return fullPath.substring(idx + prefix.length + 1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create storage REST API routes
|
|
48
|
+
*/
|
|
49
|
+
export function createStorageRoutes(config: StorageRoutesConfig): Hono<HonoEnv> {
|
|
50
|
+
const router = new Hono<HonoEnv>();
|
|
51
|
+
router.onError(errorHandler);
|
|
52
|
+
const { controller, requireAuth = true, publicRead = false } = config;
|
|
53
|
+
|
|
54
|
+
// Use actual JWT auth middleware from auth module
|
|
55
|
+
const writeAuthMiddleware = requireAuth ? jwtRequireAuth : optionalAuth;
|
|
56
|
+
|
|
57
|
+
// For read operations: respect publicRead config.
|
|
58
|
+
const readAuthMiddleware = (publicRead || !requireAuth) ? optionalAuth : jwtRequireAuth;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse bucket and path from a combined file path.
|
|
62
|
+
*/
|
|
63
|
+
const parseBucketAndPath = (filePath: string): { bucket: string; resolvedPath: string } => {
|
|
64
|
+
const parts = filePath.split("/");
|
|
65
|
+
|
|
66
|
+
// Only recognize 'default' as an explicit bucket prefix
|
|
67
|
+
if (parts.length > 1 && parts[0].toLowerCase() === "default") {
|
|
68
|
+
return {
|
|
69
|
+
bucket: "default",
|
|
70
|
+
resolvedPath: parts.slice(1).join("/")
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// All other paths use 'default' bucket with the full path
|
|
75
|
+
return {
|
|
76
|
+
bucket: "default",
|
|
77
|
+
resolvedPath: filePath
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* POST /upload - Upload a file
|
|
83
|
+
* Body: multipart/form-data with 'file' field
|
|
84
|
+
* Request body can also contain metadata keys 'metadata_*'
|
|
85
|
+
*/
|
|
86
|
+
router.post("/upload", writeAuthMiddleware, async (c) => {
|
|
87
|
+
const body = await c.req.parseBody();
|
|
88
|
+
const uploadedFile = body["file"];
|
|
89
|
+
|
|
90
|
+
if (!uploadedFile || typeof uploadedFile === "string") {
|
|
91
|
+
throw ApiError.badRequest("No file provided");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const key = typeof body["key"] === "string" ? body["key"] : "";
|
|
95
|
+
const bucket = typeof body["bucket"] === "string" ? body["bucket"] : undefined;
|
|
96
|
+
|
|
97
|
+
// Backward compatibility support for older clients sending path and fileName
|
|
98
|
+
const legacyPath = typeof body["path"] === "string" ? body["path"] : "";
|
|
99
|
+
const legacyFileName = typeof body["fileName"] === "string" ? body["fileName"] : undefined;
|
|
100
|
+
|
|
101
|
+
let finalKey = key;
|
|
102
|
+
if (!finalKey) {
|
|
103
|
+
if (legacyPath || legacyFileName) {
|
|
104
|
+
const parts = [];
|
|
105
|
+
if (legacyPath) parts.push(legacyPath);
|
|
106
|
+
if (legacyFileName) {
|
|
107
|
+
parts.push(legacyFileName);
|
|
108
|
+
} else {
|
|
109
|
+
parts.push(uploadedFile.name || "unnamed");
|
|
110
|
+
}
|
|
111
|
+
finalKey = parts.join("/");
|
|
112
|
+
} else {
|
|
113
|
+
finalKey = uploadedFile.name || "unnamed";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Extract custom metadata from request body
|
|
118
|
+
const metadata: Record<string, unknown> = {};
|
|
119
|
+
for (const [k, value] of Object.entries(body)) {
|
|
120
|
+
if (k.startsWith("metadata_")) {
|
|
121
|
+
metadata[k.replace("metadata_", "")] = value;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const result = await controller.putObject({
|
|
126
|
+
file: uploadedFile,
|
|
127
|
+
key: finalKey,
|
|
128
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
129
|
+
bucket
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return c.json({
|
|
133
|
+
success: true,
|
|
134
|
+
data: result
|
|
135
|
+
}, 201);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* GET /file/* - Download/serve a file
|
|
140
|
+
* Path: /file/{bucket}/{path} or /file/{path}
|
|
141
|
+
*/
|
|
142
|
+
router.get("/file/*", readAuthMiddleware, async (c) => {
|
|
143
|
+
const rawPath = extractWildcardPath(c);
|
|
144
|
+
if (!rawPath) {
|
|
145
|
+
throw ApiError.notFound("File not found");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const filePath = decodeURIComponent(rawPath);
|
|
149
|
+
|
|
150
|
+
// For local storage, serve the file directly from disk
|
|
151
|
+
if (controller.getType() === "local") {
|
|
152
|
+
const localController = controller as LocalStorageController;
|
|
153
|
+
const { bucket, resolvedPath } = parseBucketAndPath(filePath);
|
|
154
|
+
|
|
155
|
+
const absolutePath = localController.getAbsolutePath(resolvedPath, bucket);
|
|
156
|
+
|
|
157
|
+
// Check if file exists
|
|
158
|
+
if (!fs.existsSync(absolutePath)) {
|
|
159
|
+
throw ApiError.notFound("File not found");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Get content type from metadata or infer from extension
|
|
163
|
+
let contentType = "application/octet-stream";
|
|
164
|
+
const metadataPath = `${absolutePath}.metadata.json`;
|
|
165
|
+
if (fs.existsSync(metadataPath)) {
|
|
166
|
+
try {
|
|
167
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
|
|
168
|
+
contentType = metadata.contentType || contentType;
|
|
169
|
+
} catch {
|
|
170
|
+
// Ignore metadata errors
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
c.header("Content-Type", contentType);
|
|
175
|
+
// In a better scenario, we should pipe the stream instead of reading whole file
|
|
176
|
+
const fileContent = fs.readFileSync(absolutePath);
|
|
177
|
+
return c.body(new Uint8Array(fileContent));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// For remote storage (S3, GCS, etc.), redirect to a signed URL
|
|
181
|
+
const downloadConfig = await controller.getSignedUrl(filePath);
|
|
182
|
+
if (downloadConfig.fileNotFound || !downloadConfig.url) {
|
|
183
|
+
throw ApiError.notFound("File not found");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return c.redirect(downloadConfig.url);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* GET /metadata/* - Get file metadata
|
|
191
|
+
*/
|
|
192
|
+
router.get("/metadata/*", readAuthMiddleware, async (c) => {
|
|
193
|
+
const rawPath = extractWildcardPath(c);
|
|
194
|
+
if (!rawPath) {
|
|
195
|
+
return c.json({
|
|
196
|
+
success: true,
|
|
197
|
+
data: null,
|
|
198
|
+
fileNotFound: true
|
|
199
|
+
}, 404);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const filePath = decodeURIComponent(rawPath);
|
|
203
|
+
const { bucket, resolvedPath } = parseBucketAndPath(filePath);
|
|
204
|
+
|
|
205
|
+
const downloadConfig = await controller.getSignedUrl(resolvedPath, bucket);
|
|
206
|
+
|
|
207
|
+
if (downloadConfig.fileNotFound) {
|
|
208
|
+
throw ApiError.notFound("File not found");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return c.json({
|
|
212
|
+
success: true,
|
|
213
|
+
data: downloadConfig.metadata
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* DELETE /file/* - Delete a file
|
|
219
|
+
*/
|
|
220
|
+
router.delete("/file/*", writeAuthMiddleware, async (c) => {
|
|
221
|
+
const rawPath = extractWildcardPath(c);
|
|
222
|
+
if (!rawPath) {
|
|
223
|
+
return c.json({ success: true,
|
|
224
|
+
message: "No file to delete" });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const filePath = decodeURIComponent(rawPath);
|
|
228
|
+
const { bucket, resolvedPath } = parseBucketAndPath(filePath);
|
|
229
|
+
|
|
230
|
+
await controller.deleteObject(resolvedPath, bucket);
|
|
231
|
+
|
|
232
|
+
return c.json({
|
|
233
|
+
success: true,
|
|
234
|
+
message: "File deleted"
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* GET /list - List files in a path
|
|
240
|
+
*/
|
|
241
|
+
router.get("/list", writeAuthMiddleware, async (c) => {
|
|
242
|
+
// Fallback to path for backward compatibility
|
|
243
|
+
const storagePrefix = c.req.query("prefix") || c.req.query("path") || "";
|
|
244
|
+
const bucket = c.req.query("bucket");
|
|
245
|
+
const maxResults = c.req.query("maxResults");
|
|
246
|
+
const pageToken = c.req.query("pageToken");
|
|
247
|
+
|
|
248
|
+
const result = await controller.listObjects(
|
|
249
|
+
storagePrefix,
|
|
250
|
+
{
|
|
251
|
+
bucket,
|
|
252
|
+
maxResults: maxResults ? parseInt(maxResults, 10) : undefined,
|
|
253
|
+
pageToken
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
return c.json({
|
|
258
|
+
success: true,
|
|
259
|
+
data: result
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
return router;
|
|
264
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
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
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The default storage identifier used when:
|
|
18
|
+
* - A single storage controller is provided (not a map)
|
|
19
|
+
* - A property doesn't specify a storageId
|
|
20
|
+
*/
|
|
21
|
+
export const DEFAULT_STORAGE_ID = "(default)";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Registry for managing multiple storage controllers
|
|
25
|
+
*/
|
|
26
|
+
export interface StorageRegistry {
|
|
27
|
+
/**
|
|
28
|
+
* Register a storage controller with an ID
|
|
29
|
+
* @param id - Unique identifier for this storage (e.g., "media", "backups")
|
|
30
|
+
* @param controller - The StorageController instance
|
|
31
|
+
*/
|
|
32
|
+
register(id: string, controller: StorageController): void;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the default storage controller (id = "(default)")
|
|
36
|
+
* @throws Error if no default storage is registered
|
|
37
|
+
*/
|
|
38
|
+
getDefault(): StorageController;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get a storage controller by ID
|
|
42
|
+
* @param id - Storage identifier, or undefined/null for default
|
|
43
|
+
* @returns The StorageController, or undefined if not found
|
|
44
|
+
*/
|
|
45
|
+
get(id: string | undefined | null): StorageController | undefined;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get a storage controller by ID, with fallback to default
|
|
49
|
+
* @param id - Storage identifier, or undefined/null for default
|
|
50
|
+
* @returns The StorageController (falls back to default if id not found)
|
|
51
|
+
* @throws Error if neither the specified nor default storage exists
|
|
52
|
+
*/
|
|
53
|
+
getOrDefault(id: string | undefined | null): StorageController;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if a storage with the given ID exists
|
|
57
|
+
*/
|
|
58
|
+
has(id: string): boolean;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* List all registered storage IDs
|
|
62
|
+
*/
|
|
63
|
+
list(): string[];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get the number of registered storage controllers
|
|
67
|
+
*/
|
|
68
|
+
size(): number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Default implementation of StorageRegistry
|
|
73
|
+
*/
|
|
74
|
+
export class DefaultStorageRegistry implements StorageRegistry {
|
|
75
|
+
private controllers = new Map<string, StorageController>();
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Create a StorageRegistry from either a single controller or a map
|
|
79
|
+
* @param input - Single StorageController (maps to "(default)") or Record<string, StorageController>
|
|
80
|
+
*/
|
|
81
|
+
static create(
|
|
82
|
+
input: StorageController | Record<string, StorageController>
|
|
83
|
+
): DefaultStorageRegistry {
|
|
84
|
+
const registry = new DefaultStorageRegistry();
|
|
85
|
+
|
|
86
|
+
if (isStorageController(input)) {
|
|
87
|
+
// Single controller → register as "(default)"
|
|
88
|
+
registry.register(DEFAULT_STORAGE_ID, input);
|
|
89
|
+
} else {
|
|
90
|
+
// Map of controllers → register each
|
|
91
|
+
for (const [id, controller] of Object.entries(input)) {
|
|
92
|
+
if (isStorageController(controller)) {
|
|
93
|
+
registry.register(id, controller);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Ensure there's a default if not explicitly provided
|
|
97
|
+
if (!registry.has(DEFAULT_STORAGE_ID) && registry.size() > 0) {
|
|
98
|
+
// If no explicit "(default)", use the first one as default
|
|
99
|
+
const firstId = Object.keys(input).find(k => isStorageController(input[k]));
|
|
100
|
+
if (firstId) {
|
|
101
|
+
console.warn(
|
|
102
|
+
`[StorageRegistry] No "${DEFAULT_STORAGE_ID}" storage provided. ` +
|
|
103
|
+
`Using "${firstId}" as the default.`
|
|
104
|
+
);
|
|
105
|
+
registry.register(DEFAULT_STORAGE_ID, input[firstId]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return registry;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
register(id: string, controller: StorageController): void {
|
|
114
|
+
if (this.controllers.has(id)) {
|
|
115
|
+
console.warn(`[StorageRegistry] Overwriting storage with id "${id}"`);
|
|
116
|
+
}
|
|
117
|
+
this.controllers.set(id, controller);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
getDefault(): StorageController {
|
|
121
|
+
const controller = this.controllers.get(DEFAULT_STORAGE_ID);
|
|
122
|
+
if (!controller) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
"[StorageRegistry] No default storage registered. " +
|
|
125
|
+
`Register one with id "${DEFAULT_STORAGE_ID}" or pass a single StorageController.`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return controller;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
get(id: string | undefined | null): StorageController | undefined {
|
|
132
|
+
if (id === undefined || id === null) {
|
|
133
|
+
return this.controllers.get(DEFAULT_STORAGE_ID);
|
|
134
|
+
}
|
|
135
|
+
return this.controllers.get(id);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getOrDefault(id: string | undefined | null): StorageController {
|
|
139
|
+
// If no ID specified, return default
|
|
140
|
+
if (id === undefined || id === null) {
|
|
141
|
+
return this.getDefault();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Try to get by ID
|
|
145
|
+
const controller = this.controllers.get(id);
|
|
146
|
+
if (controller) {
|
|
147
|
+
return controller;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Fallback to default with warning
|
|
151
|
+
console.warn(
|
|
152
|
+
`[StorageRegistry] Storage "${id}" not found, falling back to "${DEFAULT_STORAGE_ID}"`
|
|
153
|
+
);
|
|
154
|
+
return this.getDefault();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
has(id: string): boolean {
|
|
158
|
+
return this.controllers.has(id);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
list(): string[] {
|
|
162
|
+
return Array.from(this.controllers.keys());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
size(): number {
|
|
166
|
+
return this.controllers.size;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Type guard to check if an object is a StorageController
|
|
172
|
+
* vs a Record<string, StorageController> (multiple storages)
|
|
173
|
+
*/
|
|
174
|
+
function isStorageController(obj: unknown): obj is StorageController {
|
|
175
|
+
if (typeof obj !== "object" || obj === null) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const controller = obj as StorageController;
|
|
179
|
+
// Check for required StorageController properties
|
|
180
|
+
return (
|
|
181
|
+
typeof controller.putObject === "function" &&
|
|
182
|
+
typeof controller.getSignedUrl === "function" &&
|
|
183
|
+
typeof controller.deleteObject === "function" &&
|
|
184
|
+
typeof controller.listObjects === "function" &&
|
|
185
|
+
typeof controller.getType === "function"
|
|
186
|
+
);
|
|
187
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage configuration and types for Rebase backend
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { StorageSource, UploadFileProps, UploadFileResult, DownloadConfig, StorageListResult, StorageReference } from "@rebasepro/types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Local filesystem storage configuration
|
|
9
|
+
*/
|
|
10
|
+
export interface LocalStorageConfig {
|
|
11
|
+
type: "local";
|
|
12
|
+
/** Base directory for file storage (e.g., './uploads') */
|
|
13
|
+
basePath: string;
|
|
14
|
+
/** Maximum file size in bytes (default: 50MB) */
|
|
15
|
+
maxFileSize?: number;
|
|
16
|
+
/** Allowed MIME types (if not set, all types allowed) */
|
|
17
|
+
allowedMimeTypes?: string[];
|
|
18
|
+
/** Base URL for generating download URLs (default: auto-detected from request) */
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* S3-compatible storage configuration (works with AWS S3 and MinIO)
|
|
24
|
+
*/
|
|
25
|
+
export interface S3StorageConfig {
|
|
26
|
+
type: "s3";
|
|
27
|
+
/** S3 bucket name */
|
|
28
|
+
bucket: string;
|
|
29
|
+
/** AWS region (e.g., 'us-east-1') */
|
|
30
|
+
region?: string;
|
|
31
|
+
/** Custom endpoint URL (required for MinIO, Cloudflare R2, Hetzner Object Storage) */
|
|
32
|
+
endpoint?: string;
|
|
33
|
+
/** AWS access key ID */
|
|
34
|
+
accessKeyId: string;
|
|
35
|
+
/** AWS secret access key */
|
|
36
|
+
secretAccessKey: string;
|
|
37
|
+
/** Use path-style URLs (required for MinIO) */
|
|
38
|
+
forcePathStyle?: boolean;
|
|
39
|
+
/** Maximum file size in bytes (default: 50MB) */
|
|
40
|
+
maxFileSize?: number;
|
|
41
|
+
/** Allowed MIME types (if not set, all types allowed) */
|
|
42
|
+
allowedMimeTypes?: string[];
|
|
43
|
+
/** URL expiration time in seconds for signed URLs (default: 3600) */
|
|
44
|
+
signedUrlExpiration?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Storage configuration — local filesystem or S3-compatible.
|
|
49
|
+
*
|
|
50
|
+
* **Built-in providers:**
|
|
51
|
+
* - `local` — Zero-config filesystem storage. Great for dev and single-server deployments (Hetzner, bare metal).
|
|
52
|
+
* - `s3` — Any S3-compatible provider. AWS S3, Cloudflare R2, MinIO, Hetzner Object Storage,
|
|
53
|
+
* Backblaze B2, DigitalOcean Spaces, and even GCS (via its S3-compatible interoperability API).
|
|
54
|
+
*
|
|
55
|
+
* **Custom providers:**
|
|
56
|
+
* For cloud-native storage (GCS, Azure Blob, etc.), implement the `StorageController`
|
|
57
|
+
* interface and pass the instance directly to the `storage` config.
|
|
58
|
+
*/
|
|
59
|
+
export type BackendStorageConfig = LocalStorageConfig | S3StorageConfig;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Storage controller interface for backend implementations
|
|
63
|
+
*/
|
|
64
|
+
export interface StorageController {
|
|
65
|
+
/**
|
|
66
|
+
* Upload an object
|
|
67
|
+
*/
|
|
68
|
+
putObject(props: UploadFileProps): Promise<UploadFileResult>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get a download URL (signed URL equivalent) for an object
|
|
72
|
+
*/
|
|
73
|
+
getSignedUrl(key: string, bucket?: string): Promise<DownloadConfig>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get object as a File
|
|
77
|
+
*/
|
|
78
|
+
getObject(key: string, bucket?: string): Promise<File | null>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Delete an object
|
|
82
|
+
*/
|
|
83
|
+
deleteObject(key: string, bucket?: string): Promise<void>;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* List objects in a prefix
|
|
87
|
+
*/
|
|
88
|
+
listObjects(prefix: string, options?: {
|
|
89
|
+
bucket?: string;
|
|
90
|
+
maxResults?: number;
|
|
91
|
+
pageToken?: string;
|
|
92
|
+
}): Promise<StorageListResult>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get the storage provider identifier.
|
|
96
|
+
*
|
|
97
|
+
* Built-in values are `'local'` and `'s3'`. Custom implementations
|
|
98
|
+
* should return their own identifier (e.g. `'gcs'`, `'azure'`).
|
|
99
|
+
*/
|
|
100
|
+
getType(): string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Default maximum file size (50MB)
|
|
105
|
+
*/
|
|
106
|
+
export const DEFAULT_MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Common image MIME types
|
|
110
|
+
*/
|
|
111
|
+
export const IMAGE_MIME_TYPES = [
|
|
112
|
+
"image/jpeg",
|
|
113
|
+
"image/png",
|
|
114
|
+
"image/gif",
|
|
115
|
+
"image/webp",
|
|
116
|
+
"image/svg+xml",
|
|
117
|
+
"image/bmp",
|
|
118
|
+
"image/tiff"
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Common document MIME types
|
|
123
|
+
*/
|
|
124
|
+
export const DOCUMENT_MIME_TYPES = [
|
|
125
|
+
"application/pdf",
|
|
126
|
+
"application/msword",
|
|
127
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
128
|
+
"application/vnd.ms-excel",
|
|
129
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
130
|
+
"application/vnd.ms-powerpoint",
|
|
131
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
132
|
+
"text/plain",
|
|
133
|
+
"text/csv"
|
|
134
|
+
];
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
|