@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,50 +0,0 @@
|
|
|
1
|
-
import { EntityCollection } from "@rebasepro/types";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import { pathToFileURL } from "url";
|
|
5
|
-
import { logger } from "../utils/logger";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Asynchronously load collection files from a directory for backend initialization
|
|
9
|
-
*/
|
|
10
|
-
export async function loadCollectionsFromDirectory(directory: string): Promise<EntityCollection[]> {
|
|
11
|
-
const collections: EntityCollection[] = [];
|
|
12
|
-
try {
|
|
13
|
-
if (!fs.existsSync(directory)) {
|
|
14
|
-
logger.warn(`[loadCollectionsFromDirectory] Collections directory not found: ${directory}`);
|
|
15
|
-
return collections;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const files = fs.readdirSync(directory);
|
|
19
|
-
for (const file of files) {
|
|
20
|
-
// Only load .ts and .js files, ignore test files and declaration files
|
|
21
|
-
if ((file.endsWith(".ts") || file.endsWith(".js")) &&
|
|
22
|
-
!file.includes(".test.") &&
|
|
23
|
-
!file.endsWith(".d.ts") &&
|
|
24
|
-
file !== "index.ts" && file !== "index.js") {
|
|
25
|
-
|
|
26
|
-
const filePath = path.join(directory, file);
|
|
27
|
-
try {
|
|
28
|
-
const fileUrl = pathToFileURL(filePath).href;
|
|
29
|
-
|
|
30
|
-
// Use standard import() so that tsx/loader hooks can
|
|
31
|
-
// resolve .ts files and workspace bare-specifiers.
|
|
32
|
-
const module = await import(fileUrl);
|
|
33
|
-
|
|
34
|
-
// Expect the collection to be the default export
|
|
35
|
-
if (module && module.default) {
|
|
36
|
-
collections.push(module.default);
|
|
37
|
-
} else {
|
|
38
|
-
logger.warn(`[loadCollectionsFromDirectory] File ${file} does not have a default export. Skipping.`);
|
|
39
|
-
}
|
|
40
|
-
} catch (err: unknown) {
|
|
41
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
42
|
-
logger.error(`[loadCollectionsFromDirectory] Failed to load collection from ${file}: ${message}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
} catch (err) {
|
|
47
|
-
logger.error(`[loadCollectionsFromDirectory] Error reading collections directory: ${err}`);
|
|
48
|
-
}
|
|
49
|
-
return collections;
|
|
50
|
-
}
|
package/src/cron/cron-loader.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { pathToFileURL } from "url";
|
|
4
|
-
import type { CronJobDefinition } from "@rebasepro/types";
|
|
5
|
-
import { logger } from "../utils/logger.js";
|
|
6
|
-
|
|
7
|
-
export interface LoadedCronJob {
|
|
8
|
-
/** Job ID derived from filename (e.g. "cleanup-sessions"). */
|
|
9
|
-
id: string;
|
|
10
|
-
/** The full definition. */
|
|
11
|
-
definition: CronJobDefinition;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Auto-discover cron job files from a directory.
|
|
16
|
-
*
|
|
17
|
-
* Each file should default-export a `CronJobDefinition`.
|
|
18
|
-
* The filename (without extension) becomes the job ID:
|
|
19
|
-
* `crons/cleanup-sessions.ts` → id = "cleanup-sessions"
|
|
20
|
-
*
|
|
21
|
-
* Follows the same discovery pattern as `loadFunctionsFromDirectory`.
|
|
22
|
-
*/
|
|
23
|
-
export async function loadCronJobsFromDirectory(
|
|
24
|
-
directory: string
|
|
25
|
-
): Promise<LoadedCronJob[]> {
|
|
26
|
-
const jobs: LoadedCronJob[] = [];
|
|
27
|
-
|
|
28
|
-
if (!fs.existsSync(directory)) {
|
|
29
|
-
return jobs;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const files = fs.readdirSync(directory);
|
|
33
|
-
for (const file of files) {
|
|
34
|
-
if (
|
|
35
|
-
(file.endsWith(".ts") || file.endsWith(".js")) &&
|
|
36
|
-
!file.includes(".test.") &&
|
|
37
|
-
!file.endsWith(".d.ts") &&
|
|
38
|
-
file !== "index.ts" &&
|
|
39
|
-
file !== "index.js"
|
|
40
|
-
) {
|
|
41
|
-
const filePath = path.join(directory, file);
|
|
42
|
-
try {
|
|
43
|
-
const fileUrl = pathToFileURL(filePath).href;
|
|
44
|
-
|
|
45
|
-
// Native dynamic import — bypasses tsc down-compilation
|
|
46
|
-
const dynamicImport = new Function("url", "return import(url)");
|
|
47
|
-
const mod = await dynamicImport(fileUrl);
|
|
48
|
-
|
|
49
|
-
const exported: unknown = mod.default;
|
|
50
|
-
|
|
51
|
-
if (!exported || typeof exported !== "object") {
|
|
52
|
-
logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const def = exported as Record<string, unknown>;
|
|
57
|
-
if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
|
|
58
|
-
logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const id = path.basename(file, path.extname(file));
|
|
63
|
-
const definition: CronJobDefinition = {
|
|
64
|
-
schedule: def.schedule as string,
|
|
65
|
-
name: (def.name as string) || id,
|
|
66
|
-
description: def.description as string | undefined,
|
|
67
|
-
enabled: def.enabled !== false,
|
|
68
|
-
timeoutSeconds: (def.timeoutSeconds as number) || 300,
|
|
69
|
-
handler: def.handler as CronJobDefinition["handler"]
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
jobs.push({ id,
|
|
73
|
-
definition });
|
|
74
|
-
logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
|
|
75
|
-
} catch (err: unknown) {
|
|
76
|
-
const message =
|
|
77
|
-
err instanceof Error ? err.message : String(err);
|
|
78
|
-
logger.error(`[cron] Failed to load ${file}: ${message}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return jobs;
|
|
84
|
-
}
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "@jest/globals";
|
|
2
|
-
import { Hono } from "hono";
|
|
3
|
-
import { CronScheduler } from "./cron-scheduler";
|
|
4
|
-
import { createCronRoutes } from "./cron-routes";
|
|
5
|
-
import type { LoadedCronJob } from "./cron-loader";
|
|
6
|
-
import type { CronJobDefinition } from "@rebasepro/types";
|
|
7
|
-
|
|
8
|
-
// ─── Helpers ────────────────────────────────────────────────────────
|
|
9
|
-
|
|
10
|
-
function makeJob(
|
|
11
|
-
id: string,
|
|
12
|
-
overrides: Partial<CronJobDefinition> = {}
|
|
13
|
-
): LoadedCronJob {
|
|
14
|
-
return {
|
|
15
|
-
id,
|
|
16
|
-
definition: {
|
|
17
|
-
schedule: "0 * * * *",
|
|
18
|
-
name: `Job ${id}`,
|
|
19
|
-
enabled: true,
|
|
20
|
-
timeoutSeconds: 5,
|
|
21
|
-
handler: async (ctx) => {
|
|
22
|
-
ctx.log("executed");
|
|
23
|
-
return { ran: true };
|
|
24
|
-
},
|
|
25
|
-
...overrides
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function jsonBody(res: Response): Promise<Record<string, unknown>> {
|
|
31
|
-
return res.json() as Promise<Record<string, unknown>>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// ─── Tests ──────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
describe("cron-routes", () => {
|
|
37
|
-
let scheduler: CronScheduler;
|
|
38
|
-
let app: Hono;
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
scheduler = new CronScheduler();
|
|
42
|
-
scheduler.registerJobs([
|
|
43
|
-
makeJob("job-a", { name: "Job A",
|
|
44
|
-
description: "First job" }),
|
|
45
|
-
makeJob("job-b", { name: "Job B" })
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
app = new Hono();
|
|
49
|
-
app.route("/cron", createCronRoutes(scheduler));
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// ── GET / (list) ────────────────────────────────────────────────
|
|
53
|
-
|
|
54
|
-
describe("GET /cron", () => {
|
|
55
|
-
it("returns all registered jobs", async () => {
|
|
56
|
-
const res = await app.request("/cron");
|
|
57
|
-
expect(res.status).toBe(200);
|
|
58
|
-
|
|
59
|
-
const body = await jsonBody(res);
|
|
60
|
-
const jobs = body.jobs as Array<Record<string, unknown>>;
|
|
61
|
-
expect(jobs).toHaveLength(2);
|
|
62
|
-
expect(jobs.map((j) => j.id).sort()).toEqual(["job-a", "job-b"]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("each job has the expected shape", async () => {
|
|
66
|
-
const res = await app.request("/cron");
|
|
67
|
-
const body = await jsonBody(res);
|
|
68
|
-
const jobs = body.jobs as Array<Record<string, unknown>>;
|
|
69
|
-
const jobA = jobs.find((j) => j.id === "job-a")!;
|
|
70
|
-
|
|
71
|
-
expect(jobA.name).toBe("Job A");
|
|
72
|
-
expect(jobA.description).toBe("First job");
|
|
73
|
-
expect(jobA.schedule).toBe("0 * * * *");
|
|
74
|
-
expect(jobA.enabled).toBe(true);
|
|
75
|
-
expect(jobA.state).toBe("idle");
|
|
76
|
-
expect(jobA.totalRuns).toBe(0);
|
|
77
|
-
expect(jobA.totalFailures).toBe(0);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// ── GET /:id (single) ───────────────────────────────────────────
|
|
82
|
-
|
|
83
|
-
describe("GET /cron/:id", () => {
|
|
84
|
-
it("returns a single job", async () => {
|
|
85
|
-
const res = await app.request("/cron/job-a");
|
|
86
|
-
expect(res.status).toBe(200);
|
|
87
|
-
|
|
88
|
-
const body = await jsonBody(res);
|
|
89
|
-
const job = body.job as Record<string, unknown>;
|
|
90
|
-
expect(job.id).toBe("job-a");
|
|
91
|
-
expect(job.name).toBe("Job A");
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("returns 404 for nonexistent job", async () => {
|
|
95
|
-
const res = await app.request("/cron/nonexistent");
|
|
96
|
-
expect(res.status).toBe(404);
|
|
97
|
-
|
|
98
|
-
const body = await jsonBody(res);
|
|
99
|
-
expect((body.error as Record<string, unknown>).code).toBe("NOT_FOUND");
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// ── POST /:id/trigger ───────────────────────────────────────────
|
|
104
|
-
|
|
105
|
-
describe("POST /cron/:id/trigger", () => {
|
|
106
|
-
it("triggers a job and returns log + updated status", async () => {
|
|
107
|
-
const res = await app.request("/cron/job-a/trigger", {
|
|
108
|
-
method: "POST"
|
|
109
|
-
});
|
|
110
|
-
expect(res.status).toBe(200);
|
|
111
|
-
|
|
112
|
-
const body = await jsonBody(res);
|
|
113
|
-
const log = body.log as Record<string, unknown>;
|
|
114
|
-
const job = body.job as Record<string, unknown>;
|
|
115
|
-
|
|
116
|
-
expect(log.jobId).toBe("job-a");
|
|
117
|
-
expect(log.success).toBe(true);
|
|
118
|
-
expect(log.manual).toBe(true);
|
|
119
|
-
expect((log.logs as string[])).toContain("executed");
|
|
120
|
-
expect(log.result).toEqual({ ran: true });
|
|
121
|
-
|
|
122
|
-
expect(job.totalRuns).toBe(1);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("returns 404 for nonexistent job", async () => {
|
|
126
|
-
const res = await app.request("/cron/ghost/trigger", {
|
|
127
|
-
method: "POST"
|
|
128
|
-
});
|
|
129
|
-
expect(res.status).toBe(404);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// ── GET /:id/logs ───────────────────────────────────────────────
|
|
134
|
-
|
|
135
|
-
describe("GET /cron/:id/logs", () => {
|
|
136
|
-
it("returns empty logs before any execution", async () => {
|
|
137
|
-
const res = await app.request("/cron/job-a/logs");
|
|
138
|
-
expect(res.status).toBe(200);
|
|
139
|
-
|
|
140
|
-
const body = await jsonBody(res);
|
|
141
|
-
expect(body.logs).toEqual([]);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("returns logs after trigger", async () => {
|
|
145
|
-
// Trigger first
|
|
146
|
-
await app.request("/cron/job-a/trigger", { method: "POST" });
|
|
147
|
-
|
|
148
|
-
const res = await app.request("/cron/job-a/logs");
|
|
149
|
-
expect(res.status).toBe(200);
|
|
150
|
-
|
|
151
|
-
const body = await jsonBody(res);
|
|
152
|
-
const logs = body.logs as Array<Record<string, unknown>>;
|
|
153
|
-
expect(logs).toHaveLength(1);
|
|
154
|
-
expect(logs[0].success).toBe(true);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("respects limit query parameter", async () => {
|
|
158
|
-
await app.request("/cron/job-a/trigger", { method: "POST" });
|
|
159
|
-
await app.request("/cron/job-a/trigger", { method: "POST" });
|
|
160
|
-
await app.request("/cron/job-a/trigger", { method: "POST" });
|
|
161
|
-
|
|
162
|
-
const res = await app.request("/cron/job-a/logs?limit=2");
|
|
163
|
-
const body = await jsonBody(res);
|
|
164
|
-
expect((body.logs as unknown[]).length).toBe(2);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("returns 404 for nonexistent job", async () => {
|
|
168
|
-
const res = await app.request("/cron/ghost/logs");
|
|
169
|
-
expect(res.status).toBe(404);
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// ── PUT /:id (enable/disable) ───────────────────────────────────
|
|
174
|
-
|
|
175
|
-
describe("PUT /cron/:id", () => {
|
|
176
|
-
it("disables a job", async () => {
|
|
177
|
-
const res = await app.request("/cron/job-a", {
|
|
178
|
-
method: "PUT",
|
|
179
|
-
headers: { "Content-Type": "application/json" },
|
|
180
|
-
body: JSON.stringify({ enabled: false })
|
|
181
|
-
});
|
|
182
|
-
expect(res.status).toBe(200);
|
|
183
|
-
|
|
184
|
-
const body = await jsonBody(res);
|
|
185
|
-
const job = body.job as Record<string, unknown>;
|
|
186
|
-
expect(job.enabled).toBe(false);
|
|
187
|
-
expect(job.state).toBe("disabled");
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("re-enables a disabled job", async () => {
|
|
191
|
-
// Disable first
|
|
192
|
-
await app.request("/cron/job-a", {
|
|
193
|
-
method: "PUT",
|
|
194
|
-
headers: { "Content-Type": "application/json" },
|
|
195
|
-
body: JSON.stringify({ enabled: false })
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// Re-enable
|
|
199
|
-
const res = await app.request("/cron/job-a", {
|
|
200
|
-
method: "PUT",
|
|
201
|
-
headers: { "Content-Type": "application/json" },
|
|
202
|
-
body: JSON.stringify({ enabled: true })
|
|
203
|
-
});
|
|
204
|
-
expect(res.status).toBe(200);
|
|
205
|
-
|
|
206
|
-
const body = await jsonBody(res);
|
|
207
|
-
expect((body.job as Record<string, unknown>).enabled).toBe(true);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it("returns 400 if enabled is missing from body", async () => {
|
|
211
|
-
const res = await app.request("/cron/job-a", {
|
|
212
|
-
method: "PUT",
|
|
213
|
-
headers: { "Content-Type": "application/json" },
|
|
214
|
-
body: JSON.stringify({ something: "else" })
|
|
215
|
-
});
|
|
216
|
-
expect(res.status).toBe(400);
|
|
217
|
-
|
|
218
|
-
const body = await jsonBody(res);
|
|
219
|
-
expect((body.error as Record<string, unknown>).code).toBe("BAD_REQUEST");
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it("returns 404 for nonexistent job", async () => {
|
|
223
|
-
const res = await app.request("/cron/ghost", {
|
|
224
|
-
method: "PUT",
|
|
225
|
-
headers: { "Content-Type": "application/json" },
|
|
226
|
-
body: JSON.stringify({ enabled: false })
|
|
227
|
-
});
|
|
228
|
-
expect(res.status).toBe(404);
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// ── Edge cases ──────────────────────────────────────────────────
|
|
233
|
-
|
|
234
|
-
describe("edge cases", () => {
|
|
235
|
-
it("handles a failing job trigger gracefully", async () => {
|
|
236
|
-
scheduler.registerJobs([
|
|
237
|
-
makeJob("fail-job", {
|
|
238
|
-
handler: async () => {
|
|
239
|
-
throw new Error("kaboom");
|
|
240
|
-
}
|
|
241
|
-
})
|
|
242
|
-
]);
|
|
243
|
-
|
|
244
|
-
const res = await app.request("/cron/fail-job/trigger", {
|
|
245
|
-
method: "POST"
|
|
246
|
-
});
|
|
247
|
-
expect(res.status).toBe(200); // trigger still returns 200
|
|
248
|
-
|
|
249
|
-
const body = await jsonBody(res);
|
|
250
|
-
const log = body.log as Record<string, unknown>;
|
|
251
|
-
expect(log.success).toBe(false);
|
|
252
|
-
expect(log.error).toBe("kaboom");
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it("handles URL-encoded job IDs", async () => {
|
|
256
|
-
scheduler.registerJobs([makeJob("has-dash", { name: "Dashed" })]);
|
|
257
|
-
|
|
258
|
-
const res = await app.request("/cron/has-dash");
|
|
259
|
-
expect(res.status).toBe(200);
|
|
260
|
-
|
|
261
|
-
const body = await jsonBody(res);
|
|
262
|
-
expect((body.job as Record<string, unknown>).id).toBe("has-dash");
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
});
|
package/src/cron/cron-routes.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
|
-
import type { HonoEnv } from "../api/types";
|
|
3
|
-
import type { CronScheduler } from "./cron-scheduler";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Create admin REST routes for managing cron jobs.
|
|
7
|
-
*
|
|
8
|
-
* Routes:
|
|
9
|
-
* GET / → list all cron jobs
|
|
10
|
-
* GET /:id → get a single job's status
|
|
11
|
-
* POST /:id/trigger → manually trigger a job
|
|
12
|
-
* GET /:id/logs → get execution logs for a job
|
|
13
|
-
* PUT /:id → update job (enable/disable)
|
|
14
|
-
*/
|
|
15
|
-
export function createCronRoutes(scheduler: CronScheduler): Hono<HonoEnv> {
|
|
16
|
-
const router = new Hono<HonoEnv>();
|
|
17
|
-
|
|
18
|
-
// List all jobs
|
|
19
|
-
router.get("/", (c) => {
|
|
20
|
-
const jobs = scheduler.listJobs();
|
|
21
|
-
return c.json({ jobs });
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// Get single job
|
|
25
|
-
router.get("/:id", (c) => {
|
|
26
|
-
const id = c.req.param("id");
|
|
27
|
-
const job = scheduler.getJob(id);
|
|
28
|
-
if (!job) {
|
|
29
|
-
return c.json({ error: { message: `Cron job "${id}" not found`,
|
|
30
|
-
code: "NOT_FOUND" } }, 404);
|
|
31
|
-
}
|
|
32
|
-
return c.json({ job });
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Trigger a job manually
|
|
36
|
-
router.post("/:id/trigger", async (c) => {
|
|
37
|
-
const id = c.req.param("id");
|
|
38
|
-
const job = scheduler.getJob(id);
|
|
39
|
-
if (!job) {
|
|
40
|
-
return c.json({ error: { message: `Cron job "${id}" not found`,
|
|
41
|
-
code: "NOT_FOUND" } }, 404);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const log = await scheduler.triggerJob(id);
|
|
45
|
-
return c.json({ log,
|
|
46
|
-
job: scheduler.getJob(id) });
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Get job logs
|
|
50
|
-
router.get("/:id/logs", async (c) => {
|
|
51
|
-
const id = c.req.param("id");
|
|
52
|
-
const limitStr = c.req.query("limit");
|
|
53
|
-
const limit = limitStr ? parseInt(limitStr, 10) : undefined;
|
|
54
|
-
|
|
55
|
-
const job = scheduler.getJob(id);
|
|
56
|
-
if (!job) {
|
|
57
|
-
return c.json({ error: { message: `Cron job "${id}" not found`,
|
|
58
|
-
code: "NOT_FOUND" } }, 404);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const logs = await scheduler.getJobLogsFromDb(id, limit);
|
|
62
|
-
return c.json({ logs });
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Enable/disable a job
|
|
66
|
-
router.put("/:id", async (c) => {
|
|
67
|
-
const id = c.req.param("id");
|
|
68
|
-
const body = await c.req.json().catch(() => ({})) as { enabled: boolean };
|
|
69
|
-
|
|
70
|
-
if (typeof body.enabled !== "boolean") {
|
|
71
|
-
return c.json({ error: { message: "Missing 'enabled' boolean in body",
|
|
72
|
-
code: "BAD_REQUEST" } }, 400);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const job = scheduler.setJobEnabled(id, body.enabled);
|
|
76
|
-
if (!job) {
|
|
77
|
-
return c.json({ error: { message: `Cron job "${id}" not found`,
|
|
78
|
-
code: "NOT_FOUND" } }, 404);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return c.json({ job });
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return router;
|
|
85
|
-
}
|