covara 0.0.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +839 -0
- package/dist/auth/adapter.d.ts +47 -0
- package/dist/auth/adapter.d.ts.map +1 -0
- package/dist/auth/adapter.js +144 -0
- package/dist/auth/adapter.js.map +1 -0
- package/dist/auth/adapters/authjs.d.ts +31 -0
- package/dist/auth/adapters/authjs.d.ts.map +1 -0
- package/dist/auth/adapters/authjs.js +143 -0
- package/dist/auth/adapters/authjs.js.map +1 -0
- package/dist/auth/adapters/jwt.d.ts +70 -0
- package/dist/auth/adapters/jwt.d.ts.map +1 -0
- package/dist/auth/adapters/jwt.js +368 -0
- package/dist/auth/adapters/jwt.js.map +1 -0
- package/dist/auth/adapters/oidc.d.ts +120 -0
- package/dist/auth/adapters/oidc.d.ts.map +1 -0
- package/dist/auth/adapters/oidc.js +280 -0
- package/dist/auth/adapters/oidc.js.map +1 -0
- package/dist/auth/adapters/passport.d.ts +44 -0
- package/dist/auth/adapters/passport.d.ts.map +1 -0
- package/dist/auth/adapters/passport.js +206 -0
- package/dist/auth/adapters/passport.js.map +1 -0
- package/dist/auth/api-keys.d.ts +68 -0
- package/dist/auth/api-keys.d.ts.map +1 -0
- package/dist/auth/api-keys.js +117 -0
- package/dist/auth/api-keys.js.map +1 -0
- package/dist/auth/config.d.ts +36 -0
- package/dist/auth/config.d.ts.map +1 -0
- package/dist/auth/config.js +50 -0
- package/dist/auth/config.js.map +1 -0
- package/dist/auth/csrf.d.ts +18 -0
- package/dist/auth/csrf.d.ts.map +1 -0
- package/dist/auth/csrf.js +59 -0
- package/dist/auth/csrf.js.map +1 -0
- package/dist/auth/index.d.ts +25 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +22 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/login-throttle.d.ts +21 -0
- package/dist/auth/login-throttle.d.ts.map +1 -0
- package/dist/auth/login-throttle.js +50 -0
- package/dist/auth/login-throttle.js.map +1 -0
- package/dist/auth/magic-link.d.ts +13 -0
- package/dist/auth/magic-link.d.ts.map +1 -0
- package/dist/auth/magic-link.js +14 -0
- package/dist/auth/magic-link.js.map +1 -0
- package/dist/auth/middleware.d.ts +20 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +116 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/password-policy.d.ts +18 -0
- package/dist/auth/password-policy.d.ts.map +1 -0
- package/dist/auth/password-policy.js +72 -0
- package/dist/auth/password-policy.js.map +1 -0
- package/dist/auth/password-reset.d.ts +13 -0
- package/dist/auth/password-reset.d.ts.map +1 -0
- package/dist/auth/password-reset.js +17 -0
- package/dist/auth/password-reset.js.map +1 -0
- package/dist/auth/password.d.ts +11 -0
- package/dist/auth/password.d.ts.map +1 -0
- package/dist/auth/password.js +98 -0
- package/dist/auth/password.js.map +1 -0
- package/dist/auth/routes.d.ts +111 -0
- package/dist/auth/routes.d.ts.map +1 -0
- package/dist/auth/routes.js +417 -0
- package/dist/auth/routes.js.map +1 -0
- package/dist/auth/rsql.d.ts +33 -0
- package/dist/auth/rsql.d.ts.map +1 -0
- package/dist/auth/rsql.js +102 -0
- package/dist/auth/rsql.js.map +1 -0
- package/dist/auth/scope.d.ts +26 -0
- package/dist/auth/scope.d.ts.map +1 -0
- package/dist/auth/scope.js +166 -0
- package/dist/auth/scope.js.map +1 -0
- package/dist/auth/stores/drizzle.d.ts +39 -0
- package/dist/auth/stores/drizzle.d.ts.map +1 -0
- package/dist/auth/stores/drizzle.js +183 -0
- package/dist/auth/stores/drizzle.js.map +1 -0
- package/dist/auth/stores/index.d.ts +6 -0
- package/dist/auth/stores/index.d.ts.map +1 -0
- package/dist/auth/stores/index.js +4 -0
- package/dist/auth/stores/index.js.map +1 -0
- package/dist/auth/stores/redis.d.ts +25 -0
- package/dist/auth/stores/redis.d.ts.map +1 -0
- package/dist/auth/stores/redis.js +147 -0
- package/dist/auth/stores/redis.js.map +1 -0
- package/dist/auth/totp.d.ts +32 -0
- package/dist/auth/totp.d.ts.map +1 -0
- package/dist/auth/totp.js +126 -0
- package/dist/auth/totp.js.map +1 -0
- package/dist/auth/types.d.ts +82 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +53 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/verification.d.ts +32 -0
- package/dist/auth/verification.d.ts.map +1 -0
- package/dist/auth/verification.js +0 -0
- package/dist/auth/verification.js.map +1 -0
- package/dist/billing/credits.d.ts +29 -0
- package/dist/billing/credits.d.ts.map +1 -0
- package/dist/billing/credits.js +66 -0
- package/dist/billing/credits.js.map +1 -0
- package/dist/billing/index.d.ts +53 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +139 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/lemonsqueezy.d.ts +29 -0
- package/dist/billing/lemonsqueezy.d.ts.map +1 -0
- package/dist/billing/lemonsqueezy.js +276 -0
- package/dist/billing/lemonsqueezy.js.map +1 -0
- package/dist/billing/paddle.d.ts +30 -0
- package/dist/billing/paddle.d.ts.map +1 -0
- package/dist/billing/paddle.js +247 -0
- package/dist/billing/paddle.js.map +1 -0
- package/dist/billing/polar.d.ts +31 -0
- package/dist/billing/polar.d.ts.map +1 -0
- package/dist/billing/polar.js +284 -0
- package/dist/billing/polar.js.map +1 -0
- package/dist/billing/router.d.ts +9 -0
- package/dist/billing/router.d.ts.map +1 -0
- package/dist/billing/router.js +75 -0
- package/dist/billing/router.js.map +1 -0
- package/dist/billing/stripe.d.ts +31 -0
- package/dist/billing/stripe.d.ts.map +1 -0
- package/dist/billing/stripe.js +319 -0
- package/dist/billing/stripe.js.map +1 -0
- package/dist/billing/types.d.ts +97 -0
- package/dist/billing/types.d.ts.map +1 -0
- package/dist/billing/types.js +9 -0
- package/dist/billing/types.js.map +1 -0
- package/dist/cli/create.d.ts +10 -0
- package/dist/cli/create.d.ts.map +1 -0
- package/dist/cli/create.js +79 -0
- package/dist/cli/create.js.map +1 -0
- package/dist/cli/generate.d.ts +16 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +105 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +185 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/options.d.ts +11 -0
- package/dist/cli/options.d.ts.map +1 -0
- package/dist/cli/options.js +14 -0
- package/dist/cli/options.js.map +1 -0
- package/dist/cli/templates/configs.d.ts +12 -0
- package/dist/cli/templates/configs.d.ts.map +1 -0
- package/dist/cli/templates/configs.js +174 -0
- package/dist/cli/templates/configs.js.map +1 -0
- package/dist/cli/templates/deploy.d.ts +6 -0
- package/dist/cli/templates/deploy.d.ts.map +1 -0
- package/dist/cli/templates/deploy.js +147 -0
- package/dist/cli/templates/deploy.js.map +1 -0
- package/dist/cli/templates/index.d.ts +8 -0
- package/dist/cli/templates/index.d.ts.map +1 -0
- package/dist/cli/templates/index.js +35 -0
- package/dist/cli/templates/index.js.map +1 -0
- package/dist/cli/templates/package-json.d.ts +3 -0
- package/dist/cli/templates/package-json.d.ts.map +1 -0
- package/dist/cli/templates/package-json.js +76 -0
- package/dist/cli/templates/package-json.js.map +1 -0
- package/dist/cli/templates/readme.d.ts +3 -0
- package/dist/cli/templates/readme.d.ts.map +1 -0
- package/dist/cli/templates/readme.js +109 -0
- package/dist/cli/templates/readme.js.map +1 -0
- package/dist/cli/templates/source.d.ts +11 -0
- package/dist/cli/templates/source.d.ts.map +1 -0
- package/dist/cli/templates/source.js +158 -0
- package/dist/cli/templates/source.js.map +1 -0
- package/dist/client/auth/auth-transport.d.ts +24 -0
- package/dist/client/auth/auth-transport.d.ts.map +1 -0
- package/dist/client/auth/auth-transport.js +78 -0
- package/dist/client/auth/auth-transport.js.map +1 -0
- package/dist/client/auth/index.d.ts +52 -0
- package/dist/client/auth/index.d.ts.map +1 -0
- package/dist/client/auth/index.js +350 -0
- package/dist/client/auth/index.js.map +1 -0
- package/dist/client/auth/oidc-client.d.ts +17 -0
- package/dist/client/auth/oidc-client.d.ts.map +1 -0
- package/dist/client/auth/oidc-client.js +159 -0
- package/dist/client/auth/oidc-client.js.map +1 -0
- package/dist/client/auth/token-manager.d.ts +51 -0
- package/dist/client/auth/token-manager.d.ts.map +1 -0
- package/dist/client/auth/token-manager.js +222 -0
- package/dist/client/auth/token-manager.js.map +1 -0
- package/dist/client/auth/types.d.ts +82 -0
- package/dist/client/auth/types.d.ts.map +1 -0
- package/dist/client/auth/types.js +2 -0
- package/dist/client/auth/types.js.map +1 -0
- package/dist/client/billing.d.ts +54 -0
- package/dist/client/billing.d.ts.map +1 -0
- package/dist/client/billing.js +58 -0
- package/dist/client/billing.js.map +1 -0
- package/dist/client/dates.d.ts +40 -0
- package/dist/client/dates.d.ts.map +1 -0
- package/dist/client/dates.js +56 -0
- package/dist/client/dates.js.map +1 -0
- package/dist/client/env.d.ts +24 -0
- package/dist/client/env.d.ts.map +1 -0
- package/dist/client/env.js +105 -0
- package/dist/client/env.js.map +1 -0
- package/dist/client/file-upload.d.ts +53 -0
- package/dist/client/file-upload.d.ts.map +1 -0
- package/dist/client/file-upload.js +162 -0
- package/dist/client/file-upload.js.map +1 -0
- package/dist/client/globals.d.ts +6 -0
- package/dist/client/globals.d.ts.map +1 -0
- package/dist/client/globals.js +23 -0
- package/dist/client/globals.js.map +1 -0
- package/dist/client/index.d.ts +60 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +266 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/indexeddb-storage.d.ts +22 -0
- package/dist/client/indexeddb-storage.d.ts.map +1 -0
- package/dist/client/indexeddb-storage.js +79 -0
- package/dist/client/indexeddb-storage.js.map +1 -0
- package/dist/client/jwt.d.ts +62 -0
- package/dist/client/jwt.d.ts.map +1 -0
- package/dist/client/jwt.js +164 -0
- package/dist/client/jwt.js.map +1 -0
- package/dist/client/live-store.d.ts +49 -0
- package/dist/client/live-store.d.ts.map +1 -0
- package/dist/client/live-store.js +638 -0
- package/dist/client/live-store.js.map +1 -0
- package/dist/client/mutation.d.ts +73 -0
- package/dist/client/mutation.d.ts.map +1 -0
- package/dist/client/mutation.js +72 -0
- package/dist/client/mutation.js.map +1 -0
- package/dist/client/offline.d.ts +110 -0
- package/dist/client/offline.d.ts.map +1 -0
- package/dist/client/offline.js +551 -0
- package/dist/client/offline.js.map +1 -0
- package/dist/client/query-builder.d.ts +130 -0
- package/dist/client/query-builder.d.ts.map +1 -0
- package/dist/client/query-builder.js +300 -0
- package/dist/client/query-builder.js.map +1 -0
- package/dist/client/query-cache.d.ts +64 -0
- package/dist/client/query-cache.d.ts.map +1 -0
- package/dist/client/query-cache.js +112 -0
- package/dist/client/query-cache.js.map +1 -0
- package/dist/client/query-types.d.ts +61 -0
- package/dist/client/query-types.d.ts.map +1 -0
- package/dist/client/query-types.js +2 -0
- package/dist/client/query-types.js.map +1 -0
- package/dist/client/react-billing.d.ts +24 -0
- package/dist/client/react-billing.d.ts.map +1 -0
- package/dist/client/react-billing.js +101 -0
- package/dist/client/react-billing.js.map +1 -0
- package/dist/client/react-files.d.ts +46 -0
- package/dist/client/react-files.d.ts.map +1 -0
- package/dist/client/react-files.js +174 -0
- package/dist/client/react-files.js.map +1 -0
- package/dist/client/react-jwt.d.ts +17 -0
- package/dist/client/react-jwt.d.ts.map +1 -0
- package/dist/client/react-jwt.js +73 -0
- package/dist/client/react-jwt.js.map +1 -0
- package/dist/client/react.d.ts +210 -0
- package/dist/client/react.d.ts.map +1 -0
- package/dist/client/react.js +599 -0
- package/dist/client/react.js.map +1 -0
- package/dist/client/repository.d.ts +49 -0
- package/dist/client/repository.d.ts.map +1 -0
- package/dist/client/repository.js +269 -0
- package/dist/client/repository.js.map +1 -0
- package/dist/client/resource-query-builder.d.ts +35 -0
- package/dist/client/resource-query-builder.d.ts.map +1 -0
- package/dist/client/resource-query-builder.js +177 -0
- package/dist/client/resource-query-builder.js.map +1 -0
- package/dist/client/subscription-manager.d.ts +37 -0
- package/dist/client/subscription-manager.d.ts.map +1 -0
- package/dist/client/subscription-manager.js +164 -0
- package/dist/client/subscription-manager.js.map +1 -0
- package/dist/client/tab-sync.d.ts +35 -0
- package/dist/client/tab-sync.d.ts.map +1 -0
- package/dist/client/tab-sync.js +129 -0
- package/dist/client/tab-sync.js.map +1 -0
- package/dist/client/transport.d.ts +50 -0
- package/dist/client/transport.d.ts.map +1 -0
- package/dist/client/transport.js +205 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/client/typegen.d.ts +20 -0
- package/dist/client/typegen.d.ts.map +1 -0
- package/dist/client/typegen.js +578 -0
- package/dist/client/typegen.js.map +1 -0
- package/dist/client/types.d.ts +472 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/db/index.d.ts +9 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +5 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/internal-schema.d.ts +3831 -0
- package/dist/db/internal-schema.d.ts.map +1 -0
- package/dist/db/internal-schema.js +130 -0
- package/dist/db/internal-schema.js.map +1 -0
- package/dist/db/migrate.d.ts +15 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +127 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/pooling.d.ts +9 -0
- package/dist/db/pooling.d.ts.map +1 -0
- package/dist/db/pooling.js +42 -0
- package/dist/db/pooling.js.map +1 -0
- package/dist/db/seed.d.ts +24 -0
- package/dist/db/seed.d.ts.map +1 -0
- package/dist/db/seed.js +34 -0
- package/dist/db/seed.js.map +1 -0
- package/dist/email/builder.d.ts +38 -0
- package/dist/email/builder.d.ts.map +1 -0
- package/dist/email/builder.js +153 -0
- package/dist/email/builder.js.map +1 -0
- package/dist/email/cloudflare.d.ts +24 -0
- package/dist/email/cloudflare.d.ts.map +1 -0
- package/dist/email/cloudflare.js +146 -0
- package/dist/email/cloudflare.js.map +1 -0
- package/dist/email/index.d.ts +12 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +27 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/resend.d.ts +15 -0
- package/dist/email/resend.d.ts.map +1 -0
- package/dist/email/resend.js +114 -0
- package/dist/email/resend.js.map +1 -0
- package/dist/email/types.d.ts +36 -0
- package/dist/email/types.d.ts.map +1 -0
- package/dist/email/types.js +12 -0
- package/dist/email/types.js.map +1 -0
- package/dist/env/index.d.ts +52 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +153 -0
- package/dist/env/index.js.map +1 -0
- package/dist/health/checks.d.ts +40 -0
- package/dist/health/checks.d.ts.map +1 -0
- package/dist/health/checks.js +166 -0
- package/dist/health/checks.js.map +1 -0
- package/dist/health/index.d.ts +26 -0
- package/dist/health/index.d.ts.map +1 -0
- package/dist/health/index.js +104 -0
- package/dist/health/index.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/kv/durable-object.d.ts +192 -0
- package/dist/kv/durable-object.d.ts.map +1 -0
- package/dist/kv/durable-object.js +1059 -0
- package/dist/kv/durable-object.js.map +1 -0
- package/dist/kv/index.d.ts +56 -0
- package/dist/kv/index.d.ts.map +1 -0
- package/dist/kv/index.js +96 -0
- package/dist/kv/index.js.map +1 -0
- package/dist/kv/memory.d.ts +78 -0
- package/dist/kv/memory.d.ts.map +1 -0
- package/dist/kv/memory.js +599 -0
- package/dist/kv/memory.js.map +1 -0
- package/dist/kv/redis.d.ts +148 -0
- package/dist/kv/redis.d.ts.map +1 -0
- package/dist/kv/redis.js +405 -0
- package/dist/kv/redis.js.map +1 -0
- package/dist/kv/types.d.ts +132 -0
- package/dist/kv/types.d.ts.map +1 -0
- package/dist/kv/types.js +24 -0
- package/dist/kv/types.js.map +1 -0
- package/dist/middleware/error.d.ts +4 -0
- package/dist/middleware/error.d.ts.map +1 -0
- package/dist/middleware/error.js +63 -0
- package/dist/middleware/error.js.map +1 -0
- package/dist/middleware/idempotency.d.ts +21 -0
- package/dist/middleware/idempotency.d.ts.map +1 -0
- package/dist/middleware/idempotency.js +144 -0
- package/dist/middleware/idempotency.js.map +1 -0
- package/dist/middleware/logging.d.ts +3 -0
- package/dist/middleware/logging.d.ts.map +1 -0
- package/dist/middleware/logging.js +6 -0
- package/dist/middleware/logging.js.map +1 -0
- package/dist/middleware/observability.d.ts +104 -0
- package/dist/middleware/observability.d.ts.map +1 -0
- package/dist/middleware/observability.js +231 -0
- package/dist/middleware/observability.js.map +1 -0
- package/dist/middleware/rateLimit.d.ts +83 -0
- package/dist/middleware/rateLimit.d.ts.map +1 -0
- package/dist/middleware/rateLimit.js +255 -0
- package/dist/middleware/rateLimit.js.map +1 -0
- package/dist/middleware/securityHeaders.d.ts +18 -0
- package/dist/middleware/securityHeaders.d.ts.map +1 -0
- package/dist/middleware/securityHeaders.js +58 -0
- package/dist/middleware/securityHeaders.js.map +1 -0
- package/dist/middleware/versioning.d.ts +62 -0
- package/dist/middleware/versioning.d.ts.map +1 -0
- package/dist/middleware/versioning.js +178 -0
- package/dist/middleware/versioning.js.map +1 -0
- package/dist/oidc/backends/email-password.d.ts +3 -0
- package/dist/oidc/backends/email-password.d.ts.map +1 -0
- package/dist/oidc/backends/email-password.js +37 -0
- package/dist/oidc/backends/email-password.js.map +1 -0
- package/dist/oidc/backends/federated.d.ts +13 -0
- package/dist/oidc/backends/federated.d.ts.map +1 -0
- package/dist/oidc/backends/federated.js +225 -0
- package/dist/oidc/backends/federated.js.map +1 -0
- package/dist/oidc/backends/index.d.ts +3 -0
- package/dist/oidc/backends/index.d.ts.map +1 -0
- package/dist/oidc/backends/index.js +3 -0
- package/dist/oidc/backends/index.js.map +1 -0
- package/dist/oidc/body.d.ts +3 -0
- package/dist/oidc/body.d.ts.map +1 -0
- package/dist/oidc/body.js +23 -0
- package/dist/oidc/body.js.map +1 -0
- package/dist/oidc/discovery.d.ts +3 -0
- package/dist/oidc/discovery.d.ts.map +1 -0
- package/dist/oidc/discovery.js +66 -0
- package/dist/oidc/discovery.js.map +1 -0
- package/dist/oidc/endpoints/authorize.d.ts +12 -0
- package/dist/oidc/endpoints/authorize.d.ts.map +1 -0
- package/dist/oidc/endpoints/authorize.js +148 -0
- package/dist/oidc/endpoints/authorize.js.map +1 -0
- package/dist/oidc/endpoints/client-auth.d.ts +9 -0
- package/dist/oidc/endpoints/client-auth.d.ts.map +1 -0
- package/dist/oidc/endpoints/client-auth.js +52 -0
- package/dist/oidc/endpoints/client-auth.js.map +1 -0
- package/dist/oidc/endpoints/index.d.ts +9 -0
- package/dist/oidc/endpoints/index.d.ts.map +1 -0
- package/dist/oidc/endpoints/index.js +9 -0
- package/dist/oidc/endpoints/index.js.map +1 -0
- package/dist/oidc/endpoints/introspection.d.ts +9 -0
- package/dist/oidc/endpoints/introspection.d.ts.map +1 -0
- package/dist/oidc/endpoints/introspection.js +57 -0
- package/dist/oidc/endpoints/introspection.js.map +1 -0
- package/dist/oidc/endpoints/jwks.d.ts +4 -0
- package/dist/oidc/endpoints/jwks.d.ts.map +1 -0
- package/dist/oidc/endpoints/jwks.js +11 -0
- package/dist/oidc/endpoints/jwks.js.map +1 -0
- package/dist/oidc/endpoints/logout.d.ts +12 -0
- package/dist/oidc/endpoints/logout.d.ts.map +1 -0
- package/dist/oidc/endpoints/logout.js +92 -0
- package/dist/oidc/endpoints/logout.js.map +1 -0
- package/dist/oidc/endpoints/register.d.ts +9 -0
- package/dist/oidc/endpoints/register.d.ts.map +1 -0
- package/dist/oidc/endpoints/register.js +104 -0
- package/dist/oidc/endpoints/register.js.map +1 -0
- package/dist/oidc/endpoints/revocation.d.ts +9 -0
- package/dist/oidc/endpoints/revocation.d.ts.map +1 -0
- package/dist/oidc/endpoints/revocation.js +31 -0
- package/dist/oidc/endpoints/revocation.js.map +1 -0
- package/dist/oidc/endpoints/token.d.ts +11 -0
- package/dist/oidc/endpoints/token.d.ts.map +1 -0
- package/dist/oidc/endpoints/token.js +167 -0
- package/dist/oidc/endpoints/token.js.map +1 -0
- package/dist/oidc/endpoints/userinfo.d.ts +10 -0
- package/dist/oidc/endpoints/userinfo.d.ts.map +1 -0
- package/dist/oidc/endpoints/userinfo.js +58 -0
- package/dist/oidc/endpoints/userinfo.js.map +1 -0
- package/dist/oidc/index.d.ts +79 -0
- package/dist/oidc/index.d.ts.map +1 -0
- package/dist/oidc/index.js +44 -0
- package/dist/oidc/index.js.map +1 -0
- package/dist/oidc/keys/index.d.ts +2 -0
- package/dist/oidc/keys/index.d.ts.map +1 -0
- package/dist/oidc/keys/index.js +2 -0
- package/dist/oidc/keys/index.js.map +1 -0
- package/dist/oidc/keys/jwk.d.ts +3 -0
- package/dist/oidc/keys/jwk.d.ts.map +1 -0
- package/dist/oidc/keys/jwk.js +171 -0
- package/dist/oidc/keys/jwk.js.map +1 -0
- package/dist/oidc/provider.d.ts +10 -0
- package/dist/oidc/provider.d.ts.map +1 -0
- package/dist/oidc/provider.js +152 -0
- package/dist/oidc/provider.js.map +1 -0
- package/dist/oidc/rate-limit.d.ts +9 -0
- package/dist/oidc/rate-limit.d.ts.map +1 -0
- package/dist/oidc/rate-limit.js +61 -0
- package/dist/oidc/rate-limit.js.map +1 -0
- package/dist/oidc/stores/index.d.ts +124 -0
- package/dist/oidc/stores/index.d.ts.map +1 -0
- package/dist/oidc/stores/index.js +337 -0
- package/dist/oidc/stores/index.js.map +1 -0
- package/dist/oidc/tokens/index.d.ts +4 -0
- package/dist/oidc/tokens/index.d.ts.map +1 -0
- package/dist/oidc/tokens/index.js +158 -0
- package/dist/oidc/tokens/index.js.map +1 -0
- package/dist/oidc/types.d.ts +442 -0
- package/dist/oidc/types.d.ts.map +1 -0
- package/dist/oidc/types.js +2 -0
- package/dist/oidc/types.js.map +1 -0
- package/dist/oidc/ui/consent.d.ts +12 -0
- package/dist/oidc/ui/consent.d.ts.map +1 -0
- package/dist/oidc/ui/consent.js +180 -0
- package/dist/oidc/ui/consent.js.map +1 -0
- package/dist/oidc/ui/index.d.ts +3 -0
- package/dist/oidc/ui/index.d.ts.map +1 -0
- package/dist/oidc/ui/index.js +3 -0
- package/dist/oidc/ui/index.js.map +1 -0
- package/dist/oidc/ui/login.d.ts +12 -0
- package/dist/oidc/ui/login.d.ts.map +1 -0
- package/dist/oidc/ui/login.js +159 -0
- package/dist/oidc/ui/login.js.map +1 -0
- package/dist/oidc/util.d.ts +5 -0
- package/dist/oidc/util.d.ts.map +1 -0
- package/dist/oidc/util.js +67 -0
- package/dist/oidc/util.js.map +1 -0
- package/dist/openapi/generator.d.ts +131 -0
- package/dist/openapi/generator.d.ts.map +1 -0
- package/dist/openapi/generator.js +609 -0
- package/dist/openapi/generator.js.map +1 -0
- package/dist/openapi/index.d.ts +5 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +3 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi/schema.d.ts +51 -0
- package/dist/openapi/schema.d.ts.map +1 -0
- package/dist/openapi/schema.js +296 -0
- package/dist/openapi/schema.js.map +1 -0
- package/dist/resource/batch.d.ts +40 -0
- package/dist/resource/batch.d.ts.map +1 -0
- package/dist/resource/batch.js +144 -0
- package/dist/resource/batch.js.map +1 -0
- package/dist/resource/capabilities.d.ts +29 -0
- package/dist/resource/capabilities.d.ts.map +1 -0
- package/dist/resource/capabilities.js +224 -0
- package/dist/resource/capabilities.js.map +1 -0
- package/dist/resource/changelog.d.ts +41 -0
- package/dist/resource/changelog.d.ts.map +1 -0
- package/dist/resource/changelog.js +275 -0
- package/dist/resource/changelog.js.map +1 -0
- package/dist/resource/error.d.ts +110 -0
- package/dist/resource/error.d.ts.map +1 -0
- package/dist/resource/error.js +276 -0
- package/dist/resource/error.js.map +1 -0
- package/dist/resource/etag.d.ts +30 -0
- package/dist/resource/etag.d.ts.map +1 -0
- package/dist/resource/etag.js +148 -0
- package/dist/resource/etag.js.map +1 -0
- package/dist/resource/filter.d.ts +36 -0
- package/dist/resource/filter.d.ts.map +1 -0
- package/dist/resource/filter.js +1136 -0
- package/dist/resource/filter.js.map +1 -0
- package/dist/resource/hook.d.ts +13 -0
- package/dist/resource/hook.d.ts.map +1 -0
- package/dist/resource/hook.js +976 -0
- package/dist/resource/hook.js.map +1 -0
- package/dist/resource/mutate.d.ts +49 -0
- package/dist/resource/mutate.d.ts.map +1 -0
- package/dist/resource/mutate.js +247 -0
- package/dist/resource/mutate.js.map +1 -0
- package/dist/resource/operator-equivalence.d.ts +54 -0
- package/dist/resource/operator-equivalence.d.ts.map +1 -0
- package/dist/resource/operator-equivalence.js +342 -0
- package/dist/resource/operator-equivalence.js.map +1 -0
- package/dist/resource/pagination.d.ts +63 -0
- package/dist/resource/pagination.d.ts.map +1 -0
- package/dist/resource/pagination.js +266 -0
- package/dist/resource/pagination.js.map +1 -0
- package/dist/resource/procedures.d.ts +28 -0
- package/dist/resource/procedures.d.ts.map +1 -0
- package/dist/resource/procedures.js +197 -0
- package/dist/resource/procedures.js.map +1 -0
- package/dist/resource/query.d.ts +37 -0
- package/dist/resource/query.d.ts.map +1 -0
- package/dist/resource/query.js +246 -0
- package/dist/resource/query.js.map +1 -0
- package/dist/resource/relations.d.ts +70 -0
- package/dist/resource/relations.d.ts.map +1 -0
- package/dist/resource/relations.js +395 -0
- package/dist/resource/relations.js.map +1 -0
- package/dist/resource/search-outbox.d.ts +38 -0
- package/dist/resource/search-outbox.d.ts.map +1 -0
- package/dist/resource/search-outbox.js +121 -0
- package/dist/resource/search-outbox.js.map +1 -0
- package/dist/resource/search.d.ts +14 -0
- package/dist/resource/search.d.ts.map +1 -0
- package/dist/resource/search.js +267 -0
- package/dist/resource/search.js.map +1 -0
- package/dist/resource/secure-query.d.ts +55 -0
- package/dist/resource/secure-query.d.ts.map +1 -0
- package/dist/resource/secure-query.js +175 -0
- package/dist/resource/secure-query.js.map +1 -0
- package/dist/resource/subscription.d.ts +46 -0
- package/dist/resource/subscription.d.ts.map +1 -0
- package/dist/resource/subscription.js +684 -0
- package/dist/resource/subscription.js.map +1 -0
- package/dist/resource/track-mutations.d.ts +58 -0
- package/dist/resource/track-mutations.d.ts.map +1 -0
- package/dist/resource/track-mutations.js +721 -0
- package/dist/resource/track-mutations.js.map +1 -0
- package/dist/resource/types.d.ts +298 -0
- package/dist/resource/types.d.ts.map +1 -0
- package/dist/resource/types.js +2 -0
- package/dist/resource/types.js.map +1 -0
- package/dist/search/index.d.ts +11 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +22 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/memory.d.ts +6 -0
- package/dist/search/memory.d.ts.map +1 -0
- package/dist/search/memory.js +70 -0
- package/dist/search/memory.js.map +1 -0
- package/dist/search/opensearch.d.ts +47 -0
- package/dist/search/opensearch.d.ts.map +1 -0
- package/dist/search/opensearch.js +126 -0
- package/dist/search/opensearch.js.map +1 -0
- package/dist/search/postgres-fts.d.ts +16 -0
- package/dist/search/postgres-fts.d.ts.map +1 -0
- package/dist/search/postgres-fts.js +171 -0
- package/dist/search/postgres-fts.js.map +1 -0
- package/dist/search/sqlite-fts.d.ts +14 -0
- package/dist/search/sqlite-fts.d.ts.map +1 -0
- package/dist/search/sqlite-fts.js +185 -0
- package/dist/search/sqlite-fts.js.map +1 -0
- package/dist/search/types.d.ts +49 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/search/types.js +2 -0
- package/dist/search/types.js.map +1 -0
- package/dist/server/app.d.ts +33 -0
- package/dist/server/app.d.ts.map +1 -0
- package/dist/server/app.js +82 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/context.d.ts +16 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +12 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/env.d.ts +4 -0
- package/dist/server/env.d.ts.map +1 -0
- package/dist/server/env.js +7 -0
- package/dist/server/env.js.map +1 -0
- package/dist/server/lifecycle.d.ts +6 -0
- package/dist/server/lifecycle.d.ts.map +1 -0
- package/dist/server/lifecycle.js +28 -0
- package/dist/server/lifecycle.js.map +1 -0
- package/dist/server/logger.d.ts +28 -0
- package/dist/server/logger.d.ts.map +1 -0
- package/dist/server/logger.js +58 -0
- package/dist/server/logger.js.map +1 -0
- package/dist/server/node.d.ts +18 -0
- package/dist/server/node.d.ts.map +1 -0
- package/dist/server/node.js +47 -0
- package/dist/server/node.js.map +1 -0
- package/dist/server/request.d.ts +4 -0
- package/dist/server/request.d.ts.map +1 -0
- package/dist/server/request.js +29 -0
- package/dist/server/request.js.map +1 -0
- package/dist/server/sse.d.ts +26 -0
- package/dist/server/sse.d.ts.map +1 -0
- package/dist/server/sse.js +115 -0
- package/dist/server/sse.js.map +1 -0
- package/dist/storage/index.d.ts +15 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +41 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local.d.ts +25 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +172 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/memory.d.ts +20 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +82 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/r2.d.ts +62 -0
- package/dist/storage/r2.d.ts.map +1 -0
- package/dist/storage/r2.js +124 -0
- package/dist/storage/r2.js.map +1 -0
- package/dist/storage/resource.d.ts +50 -0
- package/dist/storage/resource.d.ts.map +1 -0
- package/dist/storage/resource.js +346 -0
- package/dist/storage/resource.js.map +1 -0
- package/dist/storage/s3.d.ts +29 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +202 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/storage/types.d.ts +75 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +17 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/storage/validation.d.ts +11 -0
- package/dist/storage/validation.d.ts.map +1 -0
- package/dist/storage/validation.js +38 -0
- package/dist/storage/validation.js.map +1 -0
- package/dist/tasks/cloudflare-queues.d.ts +63 -0
- package/dist/tasks/cloudflare-queues.d.ts.map +1 -0
- package/dist/tasks/cloudflare-queues.js +172 -0
- package/dist/tasks/cloudflare-queues.js.map +1 -0
- package/dist/tasks/concurrency.d.ts +8 -0
- package/dist/tasks/concurrency.d.ts.map +1 -0
- package/dist/tasks/concurrency.js +27 -0
- package/dist/tasks/concurrency.js.map +1 -0
- package/dist/tasks/define.d.ts +25 -0
- package/dist/tasks/define.d.ts.map +1 -0
- package/dist/tasks/define.js +17 -0
- package/dist/tasks/define.js.map +1 -0
- package/dist/tasks/dlq.d.ts +29 -0
- package/dist/tasks/dlq.d.ts.map +1 -0
- package/dist/tasks/dlq.js +185 -0
- package/dist/tasks/dlq.js.map +1 -0
- package/dist/tasks/idempotency.d.ts +13 -0
- package/dist/tasks/idempotency.d.ts.map +1 -0
- package/dist/tasks/idempotency.js +21 -0
- package/dist/tasks/idempotency.js.map +1 -0
- package/dist/tasks/index.d.ts +27 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +15 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/integration.d.ts +18 -0
- package/dist/tasks/integration.d.ts.map +1 -0
- package/dist/tasks/integration.js +86 -0
- package/dist/tasks/integration.js.map +1 -0
- package/dist/tasks/lock.d.ts +9 -0
- package/dist/tasks/lock.d.ts.map +1 -0
- package/dist/tasks/lock.js +31 -0
- package/dist/tasks/lock.js.map +1 -0
- package/dist/tasks/queue.d.ts +11 -0
- package/dist/tasks/queue.d.ts.map +1 -0
- package/dist/tasks/queue.js +75 -0
- package/dist/tasks/queue.js.map +1 -0
- package/dist/tasks/recurring.d.ts +16 -0
- package/dist/tasks/recurring.d.ts.map +1 -0
- package/dist/tasks/recurring.js +173 -0
- package/dist/tasks/recurring.js.map +1 -0
- package/dist/tasks/retry.d.ts +4 -0
- package/dist/tasks/retry.d.ts.map +1 -0
- package/dist/tasks/retry.js +29 -0
- package/dist/tasks/retry.js.map +1 -0
- package/dist/tasks/scheduler.d.ts +22 -0
- package/dist/tasks/scheduler.d.ts.map +1 -0
- package/dist/tasks/scheduler.js +125 -0
- package/dist/tasks/scheduler.js.map +1 -0
- package/dist/tasks/storage.d.ts +17 -0
- package/dist/tasks/storage.d.ts.map +1 -0
- package/dist/tasks/storage.js +231 -0
- package/dist/tasks/storage.js.map +1 -0
- package/dist/tasks/types.d.ts +144 -0
- package/dist/tasks/types.d.ts.map +1 -0
- package/dist/tasks/types.js +2 -0
- package/dist/tasks/types.js.map +1 -0
- package/dist/tasks/worker.d.ts +21 -0
- package/dist/tasks/worker.d.ts.map +1 -0
- package/dist/tasks/worker.js +267 -0
- package/dist/tasks/worker.js.map +1 -0
- package/dist/ui/admin-auth.d.ts +65 -0
- package/dist/ui/admin-auth.d.ts.map +1 -0
- package/dist/ui/admin-auth.js +248 -0
- package/dist/ui/admin-auth.js.map +1 -0
- package/dist/ui/data-explorer.d.ts +11 -0
- package/dist/ui/data-explorer.d.ts.map +1 -0
- package/dist/ui/data-explorer.js +414 -0
- package/dist/ui/data-explorer.js.map +1 -0
- package/dist/ui/html/components/index.d.ts +75 -0
- package/dist/ui/html/components/index.d.ts.map +1 -0
- package/dist/ui/html/components/index.js +154 -0
- package/dist/ui/html/components/index.js.map +1 -0
- package/dist/ui/html/index.d.ts +6 -0
- package/dist/ui/html/index.d.ts.map +1 -0
- package/dist/ui/html/index.js +6 -0
- package/dist/ui/html/index.js.map +1 -0
- package/dist/ui/html/layout.d.ts +19 -0
- package/dist/ui/html/layout.d.ts.map +1 -0
- package/dist/ui/html/layout.js +162 -0
- package/dist/ui/html/layout.js.map +1 -0
- package/dist/ui/html/pages/admin-audit.d.ts +21 -0
- package/dist/ui/html/pages/admin-audit.d.ts.map +1 -0
- package/dist/ui/html/pages/admin-audit.js +130 -0
- package/dist/ui/html/pages/admin-audit.js.map +1 -0
- package/dist/ui/html/pages/api-explorer.d.ts +40 -0
- package/dist/ui/html/pages/api-explorer.d.ts.map +1 -0
- package/dist/ui/html/pages/api-explorer.js +153 -0
- package/dist/ui/html/pages/api-explorer.js.map +1 -0
- package/dist/ui/html/pages/changelog.d.ts +27 -0
- package/dist/ui/html/pages/changelog.d.ts.map +1 -0
- package/dist/ui/html/pages/changelog.js +129 -0
- package/dist/ui/html/pages/changelog.js.map +1 -0
- package/dist/ui/html/pages/dashboard.d.ts +20 -0
- package/dist/ui/html/pages/dashboard.d.ts.map +1 -0
- package/dist/ui/html/pages/dashboard.js +49 -0
- package/dist/ui/html/pages/dashboard.js.map +1 -0
- package/dist/ui/html/pages/data-explorer.d.ts +48 -0
- package/dist/ui/html/pages/data-explorer.d.ts.map +1 -0
- package/dist/ui/html/pages/data-explorer.js +251 -0
- package/dist/ui/html/pages/data-explorer.js.map +1 -0
- package/dist/ui/html/pages/errors.d.ts +13 -0
- package/dist/ui/html/pages/errors.d.ts.map +1 -0
- package/dist/ui/html/pages/errors.js +38 -0
- package/dist/ui/html/pages/errors.js.map +1 -0
- package/dist/ui/html/pages/filter-tester.d.ts +21 -0
- package/dist/ui/html/pages/filter-tester.d.ts.map +1 -0
- package/dist/ui/html/pages/filter-tester.js +161 -0
- package/dist/ui/html/pages/filter-tester.js.map +1 -0
- package/dist/ui/html/pages/index.d.ts +15 -0
- package/dist/ui/html/pages/index.d.ts.map +1 -0
- package/dist/ui/html/pages/index.js +15 -0
- package/dist/ui/html/pages/index.js.map +1 -0
- package/dist/ui/html/pages/kv-inspector.d.ts +20 -0
- package/dist/ui/html/pages/kv-inspector.d.ts.map +1 -0
- package/dist/ui/html/pages/kv-inspector.js +130 -0
- package/dist/ui/html/pages/kv-inspector.js.map +1 -0
- package/dist/ui/html/pages/requests.d.ts +23 -0
- package/dist/ui/html/pages/requests.d.ts.map +1 -0
- package/dist/ui/html/pages/requests.js +127 -0
- package/dist/ui/html/pages/requests.js.map +1 -0
- package/dist/ui/html/pages/resources.d.ts +27 -0
- package/dist/ui/html/pages/resources.d.ts.map +1 -0
- package/dist/ui/html/pages/resources.js +85 -0
- package/dist/ui/html/pages/resources.js.map +1 -0
- package/dist/ui/html/pages/sessions.d.ts +24 -0
- package/dist/ui/html/pages/sessions.d.ts.map +1 -0
- package/dist/ui/html/pages/sessions.js +157 -0
- package/dist/ui/html/pages/sessions.js.map +1 -0
- package/dist/ui/html/pages/subscriptions.d.ts +32 -0
- package/dist/ui/html/pages/subscriptions.d.ts.map +1 -0
- package/dist/ui/html/pages/subscriptions.js +138 -0
- package/dist/ui/html/pages/subscriptions.js.map +1 -0
- package/dist/ui/html/pages/tasks.d.ts +42 -0
- package/dist/ui/html/pages/tasks.d.ts.map +1 -0
- package/dist/ui/html/pages/tasks.js +225 -0
- package/dist/ui/html/pages/tasks.js.map +1 -0
- package/dist/ui/html/pages/users.d.ts +29 -0
- package/dist/ui/html/pages/users.d.ts.map +1 -0
- package/dist/ui/html/pages/users.js +192 -0
- package/dist/ui/html/pages/users.js.map +1 -0
- package/dist/ui/html/styles.d.ts +2 -0
- package/dist/ui/html/styles.d.ts.map +1 -0
- package/dist/ui/html/styles.js +675 -0
- package/dist/ui/html/styles.js.map +1 -0
- package/dist/ui/html/utils.d.ts +14 -0
- package/dist/ui/html/utils.d.ts.map +1 -0
- package/dist/ui/html/utils.js +76 -0
- package/dist/ui/html/utils.js.map +1 -0
- package/dist/ui/index.d.ts +13 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +7 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/kv-inspector.d.ts +11 -0
- package/dist/ui/kv-inspector.d.ts.map +1 -0
- package/dist/ui/kv-inspector.js +332 -0
- package/dist/ui/kv-inspector.js.map +1 -0
- package/dist/ui/middleware.d.ts +76 -0
- package/dist/ui/middleware.d.ts.map +1 -0
- package/dist/ui/middleware.js +1175 -0
- package/dist/ui/middleware.js.map +1 -0
- package/dist/ui/schema-registry.d.ts +82 -0
- package/dist/ui/schema-registry.d.ts.map +1 -0
- package/dist/ui/schema-registry.js +165 -0
- package/dist/ui/schema-registry.js.map +1 -0
- package/dist/ui/task-monitor.d.ts +12 -0
- package/dist/ui/task-monitor.d.ts.map +1 -0
- package/dist/ui/task-monitor.js +264 -0
- package/dist/ui/task-monitor.js.map +1 -0
- package/package.json +201 -7
|
@@ -0,0 +1,1175 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { eq, and, count, getTableColumns } from "drizzle-orm";
|
|
3
|
+
import { getResourceSchema, getSchemaInfo, getAllResourcesForDisplay } from "./schema-registry.js";
|
|
4
|
+
import { createResourceFilter } from "../resource/filter.js";
|
|
5
|
+
import { createPagination, decodeCursorLegacy, parseOrderBy } from "../resource/pagination.js";
|
|
6
|
+
import { readJsonBody } from "../server/request.js";
|
|
7
|
+
import { readEnv } from "../server/env.js";
|
|
8
|
+
import { createAdminAuthMiddleware, getAdminAuditLog, detectEnvironment, setAdminAuditSink, } from "./admin-auth.js";
|
|
9
|
+
import { createDataExplorerRoutes } from "./data-explorer.js";
|
|
10
|
+
import { createTaskMonitorRoutes } from "./task-monitor.js";
|
|
11
|
+
import { createKVInspectorRoutes } from "./kv-inspector.js";
|
|
12
|
+
import { layout } from "./html/layout.js";
|
|
13
|
+
import * as pages from "./html/pages/index.js";
|
|
14
|
+
import { html, escapeHtml } from "./html/utils.js";
|
|
15
|
+
import { emptyState } from "./html/components/index.js";
|
|
16
|
+
const requestLogs = [];
|
|
17
|
+
const errorLogs = [];
|
|
18
|
+
const MAX_LOGS = 500;
|
|
19
|
+
const MAX_AUDIT_EXPORT = 1000;
|
|
20
|
+
export const logRequest = (log) => {
|
|
21
|
+
requestLogs.unshift(log);
|
|
22
|
+
if (requestLogs.length > MAX_LOGS)
|
|
23
|
+
requestLogs.pop();
|
|
24
|
+
};
|
|
25
|
+
export const logError = (log) => {
|
|
26
|
+
errorLogs.unshift(log);
|
|
27
|
+
if (errorLogs.length > MAX_LOGS)
|
|
28
|
+
errorLogs.pop();
|
|
29
|
+
};
|
|
30
|
+
export const createAdminUI = (config = {}) => {
|
|
31
|
+
const router = new Hono();
|
|
32
|
+
const basePath = config.basePath || "/__covara";
|
|
33
|
+
const title = config.title || "Covara Admin";
|
|
34
|
+
const mode = config.security?.mode ?? detectEnvironment();
|
|
35
|
+
// Admin auth middleware for protected routes
|
|
36
|
+
const adminAuth = createAdminAuthMiddleware(config.security ?? {});
|
|
37
|
+
if (config.security?.auditSink) {
|
|
38
|
+
setAdminAuditSink(config.security.auditSink);
|
|
39
|
+
}
|
|
40
|
+
// Gate the entire admin surface (UI pages, partials, and JSON APIs) behind
|
|
41
|
+
// the admin auth check so it cannot be reached without passing authn/authz.
|
|
42
|
+
router.use("/ui", adminAuth);
|
|
43
|
+
router.use("/ui/*", adminAuth);
|
|
44
|
+
router.use("/api", adminAuth);
|
|
45
|
+
router.use("/api/*", adminAuth);
|
|
46
|
+
router.use("/admin", adminAuth);
|
|
47
|
+
router.use("/admin/*", adminAuth);
|
|
48
|
+
// Mount sub-routers for new features
|
|
49
|
+
if (config.dataExplorer?.enabled !== false) {
|
|
50
|
+
const dataExplorerRouter = createDataExplorerRoutes(config.dataExplorer ?? {}, config.security ?? {});
|
|
51
|
+
router.use("/api/explorer", adminAuth);
|
|
52
|
+
router.use("/api/explorer/*", adminAuth);
|
|
53
|
+
router.route("/api/explorer", dataExplorerRouter);
|
|
54
|
+
}
|
|
55
|
+
if (config.taskMonitor?.enabled) {
|
|
56
|
+
const taskMonitorRouter = createTaskMonitorRoutes(config.taskMonitor);
|
|
57
|
+
router.use("/api/tasks", adminAuth);
|
|
58
|
+
router.use("/api/tasks/*", adminAuth);
|
|
59
|
+
router.route("/api/tasks", taskMonitorRouter);
|
|
60
|
+
}
|
|
61
|
+
if (config.kvInspector?.enabled) {
|
|
62
|
+
const kvInspectorRouter = createKVInspectorRoutes(config.kvInspector, config.security ?? {});
|
|
63
|
+
router.use("/api/kv", adminAuth);
|
|
64
|
+
router.use("/api/kv/*", adminAuth);
|
|
65
|
+
router.route("/api/kv", kvInspectorRouter);
|
|
66
|
+
}
|
|
67
|
+
// Admin audit log endpoint
|
|
68
|
+
router.get("/api/admin-audit", adminAuth, (c) => {
|
|
69
|
+
const limit = parseInt(String(c.req.query("limit"))) || 100;
|
|
70
|
+
const offset = parseInt(String(c.req.query("offset"))) || 0;
|
|
71
|
+
const entries = getAdminAuditLog(limit, offset);
|
|
72
|
+
return c.json({ entries, mode });
|
|
73
|
+
});
|
|
74
|
+
// Admin audit export endpoint
|
|
75
|
+
router.get("/api/admin-audit/export", adminAuth, (c) => {
|
|
76
|
+
const format = c.req.query("format") || 'json';
|
|
77
|
+
const entries = getAdminAuditLog(1000, 0);
|
|
78
|
+
if (format === 'csv') {
|
|
79
|
+
const headers = ['timestamp', 'userId', 'userEmail', 'operation', 'resource', 'resourceId', 'reason'];
|
|
80
|
+
const csvRows = [headers.join(',')];
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
const row = headers.map(h => {
|
|
83
|
+
const val = entry[h];
|
|
84
|
+
if (val === undefined || val === null)
|
|
85
|
+
return '';
|
|
86
|
+
const str = String(val);
|
|
87
|
+
return str.includes(',') || str.includes('"') ? `"${str.replace(/"/g, '""')}"` : str;
|
|
88
|
+
});
|
|
89
|
+
csvRows.push(row.join(','));
|
|
90
|
+
}
|
|
91
|
+
c.header('Content-Type', 'text/csv');
|
|
92
|
+
c.header('Content-Disposition', 'attachment; filename="audit-log.csv"');
|
|
93
|
+
return c.body(csvRows.join('\n'));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
c.header('Content-Disposition', 'attachment; filename="audit-log.json"');
|
|
97
|
+
return c.json(entries);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// Canonical JSON audit export endpoint (authz-gated via /admin/* router.use above)
|
|
101
|
+
router.get("/admin/audit/export", adminAuth, (c) => {
|
|
102
|
+
const entries = getAdminAuditLog(MAX_AUDIT_EXPORT, 0);
|
|
103
|
+
c.header("Content-Disposition", 'attachment; filename="audit-log.json"');
|
|
104
|
+
return c.json({ entries, mode, exportedAt: new Date().toISOString() });
|
|
105
|
+
});
|
|
106
|
+
// Environment info endpoint
|
|
107
|
+
router.get("/api/environment", (c) => {
|
|
108
|
+
const dataExplorerEnabled = config.dataExplorer?.enabled !== false;
|
|
109
|
+
const dataExplorerReadOnly = config.dataExplorer?.readOnly ?? (mode === "production" ? true : false);
|
|
110
|
+
return c.json({
|
|
111
|
+
mode,
|
|
112
|
+
version: readEnv("npm_package_version") ?? "unknown",
|
|
113
|
+
features: {
|
|
114
|
+
dataExplorer: dataExplorerEnabled,
|
|
115
|
+
dataExplorerReadOnly,
|
|
116
|
+
taskMonitor: config.taskMonitor?.enabled ?? false,
|
|
117
|
+
kvInspector: config.kvInspector?.enabled ?? false,
|
|
118
|
+
authRequired: config.security?.auth?.disabled !== true,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
// API endpoints
|
|
123
|
+
router.get("/api/resources", (c) => {
|
|
124
|
+
const resources = getAllResourcesForDisplay();
|
|
125
|
+
return c.json({ resources });
|
|
126
|
+
});
|
|
127
|
+
router.get("/api/metrics", (c) => {
|
|
128
|
+
if (!config.metricsCollector) {
|
|
129
|
+
return c.json({ metrics: [], enabled: false });
|
|
130
|
+
}
|
|
131
|
+
const recent = config.metricsCollector.getRecent(200);
|
|
132
|
+
const slow = config.metricsCollector.getSlow(500);
|
|
133
|
+
return c.json({ metrics: recent, slowQueries: slow, enabled: true });
|
|
134
|
+
});
|
|
135
|
+
router.get("/api/requests", (c) => {
|
|
136
|
+
return c.json({ requests: requestLogs.slice(0, 200) });
|
|
137
|
+
});
|
|
138
|
+
router.get("/api/errors", (c) => {
|
|
139
|
+
return c.json({ errors: errorLogs.slice(0, 100) });
|
|
140
|
+
});
|
|
141
|
+
router.get("/api/changelog", async (c) => {
|
|
142
|
+
if (!config.changelog) {
|
|
143
|
+
return c.json({ entries: [], currentSeq: 0, enabled: false });
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const currentSeq = await config.changelog.getCurrentSequence();
|
|
147
|
+
const entries = await config.changelog.getEntries(Math.max(0, currentSeq - 50), 50);
|
|
148
|
+
return c.json({ entries, currentSeq, enabled: true });
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return c.json({ entries: [], currentSeq: 0, enabled: false });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
router.get("/api/subscriptions", (c) => {
|
|
155
|
+
if (!config.getActiveSubscriptions) {
|
|
156
|
+
return c.json({ subscriptions: [], enabled: false });
|
|
157
|
+
}
|
|
158
|
+
return c.json({ subscriptions: config.getActiveSubscriptions(), enabled: true });
|
|
159
|
+
});
|
|
160
|
+
router.post("/api/query", async (c) => {
|
|
161
|
+
const { resource, filter, limit = 10 } = (await readJsonBody(c));
|
|
162
|
+
try {
|
|
163
|
+
const url = `${resource}?filter=${encodeURIComponent(filter || "")}&limit=${limit}`;
|
|
164
|
+
return c.json({ url, note: "Execute this query via the main API" });
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
return c.json({ error: e.message }, 400);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
// User management API endpoints
|
|
171
|
+
router.get("/api/users", async (c) => {
|
|
172
|
+
if (!config.userManager) {
|
|
173
|
+
return c.json({ users: [], total: 0, enabled: false });
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const limit = parseInt(String(c.req.query("limit"))) || 50;
|
|
177
|
+
const offset = parseInt(String(c.req.query("offset"))) || 0;
|
|
178
|
+
const result = await config.userManager.listUsers(limit, offset);
|
|
179
|
+
return c.json({ ...result, enabled: true });
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
return c.json({ error: e.message }, 500);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
router.get("/api/users/:id", async (c) => {
|
|
186
|
+
if (!config.userManager) {
|
|
187
|
+
return c.json({ error: "User management not configured" }, 501);
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const userId = c.req.param("id");
|
|
191
|
+
const user = await config.userManager.getUser(userId);
|
|
192
|
+
if (!user) {
|
|
193
|
+
return c.json({ error: "User not found" }, 404);
|
|
194
|
+
}
|
|
195
|
+
return c.json({ user });
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
return c.json({ error: e.message }, 500);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
router.post("/api/users", async (c) => {
|
|
202
|
+
if (!config.userManager) {
|
|
203
|
+
return c.json({ error: "User management not configured" }, 501);
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const body = (await readJsonBody(c));
|
|
207
|
+
const user = await config.userManager.createUser(body);
|
|
208
|
+
return c.json({ user }, 201);
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
return c.json({ error: e.message }, 400);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
router.patch("/api/users/:id", async (c) => {
|
|
215
|
+
if (!config.userManager) {
|
|
216
|
+
return c.json({ error: "User management not configured" }, 501);
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const userId = c.req.param("id");
|
|
220
|
+
const body = (await readJsonBody(c));
|
|
221
|
+
const user = await config.userManager.updateUser(userId, body);
|
|
222
|
+
return c.json({ user });
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
return c.json({ error: e.message }, 400);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
router.delete("/api/users/:id", async (c) => {
|
|
229
|
+
if (!config.userManager) {
|
|
230
|
+
return c.json({ error: "User management not configured" }, 501);
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const userId = c.req.param("id");
|
|
234
|
+
await config.userManager.deleteUser(userId);
|
|
235
|
+
return c.body(null, 204);
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
return c.json({ error: e.message }, 400);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
// Session management API endpoints
|
|
242
|
+
router.get("/api/sessions", async (c) => {
|
|
243
|
+
if (!config.sessionManager) {
|
|
244
|
+
return c.json({ sessions: [], enabled: false });
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
const limit = parseInt(String(c.req.query("limit"))) || 50;
|
|
248
|
+
const sessions = await config.sessionManager.listSessions(limit);
|
|
249
|
+
return c.json({ sessions, enabled: true });
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
return c.json({ error: e.message }, 500);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
router.get("/api/sessions/user/:userId", async (c) => {
|
|
256
|
+
if (!config.sessionManager) {
|
|
257
|
+
return c.json({ error: "Session management not configured" }, 501);
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
const userId = c.req.param("userId");
|
|
261
|
+
const sessions = await config.sessionManager.getSessionsByUser(userId);
|
|
262
|
+
return c.json({ sessions });
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
return c.json({ error: e.message }, 500);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
router.post("/api/sessions", async (c) => {
|
|
269
|
+
if (!config.sessionManager) {
|
|
270
|
+
return c.json({ error: "Session management not configured" }, 501);
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
const { userId, expiresIn } = (await readJsonBody(c));
|
|
274
|
+
if (!userId) {
|
|
275
|
+
return c.json({ error: "userId is required" }, 400);
|
|
276
|
+
}
|
|
277
|
+
const session = await config.sessionManager.createSession(userId, expiresIn);
|
|
278
|
+
return c.json({ session }, 201);
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
return c.json({ error: e.message }, 400);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
router.delete("/api/sessions/:id", async (c) => {
|
|
285
|
+
if (!config.sessionManager) {
|
|
286
|
+
return c.json({ error: "Session management not configured" }, 501);
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const sessionId = c.req.param("id");
|
|
290
|
+
await config.sessionManager.revokeSession(sessionId);
|
|
291
|
+
return c.body(null, 204);
|
|
292
|
+
}
|
|
293
|
+
catch (e) {
|
|
294
|
+
return c.json({ error: e.message }, 400);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
router.delete("/api/sessions/user/:userId", async (c) => {
|
|
298
|
+
if (!config.sessionManager) {
|
|
299
|
+
return c.json({ error: "Session management not configured" }, 501);
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
const userId = c.req.param("userId");
|
|
303
|
+
const count = await config.sessionManager.revokeAllUserSessions(userId);
|
|
304
|
+
return c.json({ revokedCount: count });
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
return c.json({ error: e.message }, 400);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
// Problem details documentation
|
|
311
|
+
router.get("/problems/:type", (c) => {
|
|
312
|
+
const problemDocs = {
|
|
313
|
+
"not-found": {
|
|
314
|
+
title: "Resource Not Found",
|
|
315
|
+
description: "The requested resource does not exist or you do not have permission to access it.",
|
|
316
|
+
solutions: [
|
|
317
|
+
"Verify the resource ID is correct",
|
|
318
|
+
"Check that the resource exists in the database",
|
|
319
|
+
"Ensure you have read permissions for this resource",
|
|
320
|
+
"If using auth scopes, verify your scope includes this resource"
|
|
321
|
+
]
|
|
322
|
+
},
|
|
323
|
+
"validation-error": {
|
|
324
|
+
title: "Validation Error",
|
|
325
|
+
description: "The request body failed validation against the schema.",
|
|
326
|
+
solutions: [
|
|
327
|
+
"Check the required fields are present",
|
|
328
|
+
"Verify field types match the schema",
|
|
329
|
+
"Review the 'errors' array in the response for specific issues",
|
|
330
|
+
"Use the Schema Viewer to see field requirements"
|
|
331
|
+
]
|
|
332
|
+
},
|
|
333
|
+
"unauthorized": {
|
|
334
|
+
title: "Unauthorized",
|
|
335
|
+
description: "Authentication is required to access this resource.",
|
|
336
|
+
solutions: [
|
|
337
|
+
"Include a valid authentication token",
|
|
338
|
+
"Check if your session has expired",
|
|
339
|
+
"Verify the auth middleware is configured correctly"
|
|
340
|
+
]
|
|
341
|
+
},
|
|
342
|
+
"forbidden": {
|
|
343
|
+
title: "Forbidden",
|
|
344
|
+
description: "You do not have permission to perform this operation.",
|
|
345
|
+
solutions: [
|
|
346
|
+
"Check your user role and permissions",
|
|
347
|
+
"Verify the auth scope allows this operation",
|
|
348
|
+
"Contact an administrator for access"
|
|
349
|
+
]
|
|
350
|
+
},
|
|
351
|
+
"rate-limit-exceeded": {
|
|
352
|
+
title: "Rate Limit Exceeded",
|
|
353
|
+
description: "Too many requests in the current time window.",
|
|
354
|
+
solutions: [
|
|
355
|
+
"Wait before making more requests",
|
|
356
|
+
"Check the Retry-After header for wait time",
|
|
357
|
+
"Consider implementing request batching",
|
|
358
|
+
"Review rate limit configuration"
|
|
359
|
+
]
|
|
360
|
+
},
|
|
361
|
+
"batch-limit-exceeded": {
|
|
362
|
+
title: "Batch Limit Exceeded",
|
|
363
|
+
description: "The batch operation exceeds the configured limit.",
|
|
364
|
+
solutions: [
|
|
365
|
+
"Reduce the number of items in the batch",
|
|
366
|
+
"Check the batch limits in resource configuration",
|
|
367
|
+
"Split the operation into multiple smaller batches"
|
|
368
|
+
]
|
|
369
|
+
},
|
|
370
|
+
"filter-parse-error": {
|
|
371
|
+
title: "Filter Parse Error",
|
|
372
|
+
description: "The filter expression could not be parsed.",
|
|
373
|
+
solutions: [
|
|
374
|
+
"Check the filter syntax",
|
|
375
|
+
"Use the Filter Tester to validate expressions",
|
|
376
|
+
"Ensure strings are properly quoted",
|
|
377
|
+
"Review supported operators"
|
|
378
|
+
]
|
|
379
|
+
},
|
|
380
|
+
"internal-error": {
|
|
381
|
+
title: "Internal Server Error",
|
|
382
|
+
description: "An unexpected error occurred on the server.",
|
|
383
|
+
solutions: [
|
|
384
|
+
"Check the server logs for details",
|
|
385
|
+
"Review the Error Log in the admin panel",
|
|
386
|
+
"If the issue persists, report it with the request ID"
|
|
387
|
+
]
|
|
388
|
+
},
|
|
389
|
+
"conflict": {
|
|
390
|
+
title: "Conflict",
|
|
391
|
+
description: "The request conflicts with the current state of the resource.",
|
|
392
|
+
solutions: [
|
|
393
|
+
"Check if the resource was modified by another request",
|
|
394
|
+
"Refetch the resource and retry",
|
|
395
|
+
"Use ETag headers for optimistic concurrency control"
|
|
396
|
+
]
|
|
397
|
+
},
|
|
398
|
+
"precondition-failed": {
|
|
399
|
+
title: "Precondition Failed",
|
|
400
|
+
description: "The resource was modified since you last fetched it (ETag mismatch).",
|
|
401
|
+
solutions: [
|
|
402
|
+
"Refetch the resource to get the latest ETag",
|
|
403
|
+
"Update your If-Match header with the new ETag",
|
|
404
|
+
"Retry the operation with the updated data"
|
|
405
|
+
]
|
|
406
|
+
},
|
|
407
|
+
"cursor-invalid": {
|
|
408
|
+
title: "Invalid Cursor",
|
|
409
|
+
description: "The pagination cursor is malformed or incompatible.",
|
|
410
|
+
solutions: [
|
|
411
|
+
"Request a fresh first page without a cursor",
|
|
412
|
+
"Ensure the cursor was not modified",
|
|
413
|
+
"Check if the orderBy parameters match the original request",
|
|
414
|
+
"Verify the API version matches the cursor version"
|
|
415
|
+
]
|
|
416
|
+
},
|
|
417
|
+
"cursor-expired": {
|
|
418
|
+
title: "Cursor Expired",
|
|
419
|
+
description: "The pagination cursor has expired and can no longer be used.",
|
|
420
|
+
solutions: [
|
|
421
|
+
"Request a fresh first page without a cursor",
|
|
422
|
+
"Cursors expire after a period of inactivity",
|
|
423
|
+
"Consider caching results if pagination takes a long time"
|
|
424
|
+
]
|
|
425
|
+
},
|
|
426
|
+
"idempotency-mismatch": {
|
|
427
|
+
title: "Idempotency Mismatch",
|
|
428
|
+
description: "The idempotency key was already used with different request parameters.",
|
|
429
|
+
solutions: [
|
|
430
|
+
"Use a new unique idempotency key for different requests",
|
|
431
|
+
"If retrying the same request, ensure body and path match exactly",
|
|
432
|
+
"Idempotency keys are tied to specific request signatures"
|
|
433
|
+
]
|
|
434
|
+
},
|
|
435
|
+
"unsupported-version": {
|
|
436
|
+
title: "Unsupported Client Version",
|
|
437
|
+
description: "The client version is below the minimum supported version.",
|
|
438
|
+
solutions: [
|
|
439
|
+
"Upgrade your client library to a newer version",
|
|
440
|
+
"Check the minVersion field in the response",
|
|
441
|
+
"Review the changelog for breaking changes"
|
|
442
|
+
]
|
|
443
|
+
},
|
|
444
|
+
"unknown-error": {
|
|
445
|
+
title: "Unknown Error",
|
|
446
|
+
description: "An unrecognized error occurred.",
|
|
447
|
+
solutions: [
|
|
448
|
+
"Check the server logs for details",
|
|
449
|
+
"Review the request ID in the response",
|
|
450
|
+
"Contact support with the error details"
|
|
451
|
+
]
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
const problemType = c.req.param("type");
|
|
455
|
+
const doc = problemDocs[problemType] || {
|
|
456
|
+
title: "Unknown Error",
|
|
457
|
+
description: "An unrecognized error type.",
|
|
458
|
+
solutions: ["Check the API documentation", "Review server logs"]
|
|
459
|
+
};
|
|
460
|
+
return c.json(doc);
|
|
461
|
+
});
|
|
462
|
+
// Helper to get layout props
|
|
463
|
+
const getLayoutProps = (activePage) => ({
|
|
464
|
+
title,
|
|
465
|
+
mode,
|
|
466
|
+
activePage,
|
|
467
|
+
});
|
|
468
|
+
// Helper to check if this is an HTMX request
|
|
469
|
+
const isHtmxRequest = (c) => c.req.header('hx-request') === 'true';
|
|
470
|
+
// Helper to send HTML response (full page or fragment for HTMX)
|
|
471
|
+
const sendHtml = (c, activePage, content) => {
|
|
472
|
+
if (isHtmxRequest(c)) {
|
|
473
|
+
return c.html(content);
|
|
474
|
+
}
|
|
475
|
+
return c.html(layout(getLayoutProps(activePage), content));
|
|
476
|
+
};
|
|
477
|
+
// ============================================
|
|
478
|
+
// HTMX UI Routes - Full Page Renders
|
|
479
|
+
// ============================================
|
|
480
|
+
// Dashboard
|
|
481
|
+
router.get("/ui", async (c) => {
|
|
482
|
+
const resources = getAllResourcesForDisplay();
|
|
483
|
+
const recentRequests = requestLogs.slice(0, 10).map(r => ({
|
|
484
|
+
id: r.id,
|
|
485
|
+
method: r.method,
|
|
486
|
+
path: r.path,
|
|
487
|
+
status: r.status,
|
|
488
|
+
duration: r.duration,
|
|
489
|
+
timestamp: new Date(r.timestamp).toISOString(),
|
|
490
|
+
}));
|
|
491
|
+
let currentSeq = 0;
|
|
492
|
+
let changelogCount = 0;
|
|
493
|
+
if (config.changelog) {
|
|
494
|
+
try {
|
|
495
|
+
currentSeq = await config.changelog.getCurrentSequence();
|
|
496
|
+
changelogCount = currentSeq;
|
|
497
|
+
}
|
|
498
|
+
catch { }
|
|
499
|
+
}
|
|
500
|
+
const subscriptions = config.getActiveSubscriptions?.() || [];
|
|
501
|
+
const content = pages.dashboardPage({
|
|
502
|
+
stats: {
|
|
503
|
+
resources: resources.length,
|
|
504
|
+
requests: requestLogs.length,
|
|
505
|
+
errors: errorLogs.length,
|
|
506
|
+
subscriptions: subscriptions.length,
|
|
507
|
+
changelog: changelogCount,
|
|
508
|
+
},
|
|
509
|
+
recentRequests,
|
|
510
|
+
mode,
|
|
511
|
+
});
|
|
512
|
+
return sendHtml(c, 'dashboard', content);
|
|
513
|
+
});
|
|
514
|
+
router.get("/ui/dashboard", (c) => {
|
|
515
|
+
// Redirect to main UI
|
|
516
|
+
return c.redirect(`${basePath}/ui`, 302);
|
|
517
|
+
});
|
|
518
|
+
// Resources
|
|
519
|
+
router.get("/ui/resources", (c) => {
|
|
520
|
+
const resources = getAllResourcesForDisplay();
|
|
521
|
+
const content = pages.resourcesPage({ resources });
|
|
522
|
+
return sendHtml(c, 'resources', content);
|
|
523
|
+
});
|
|
524
|
+
// Data Explorer
|
|
525
|
+
router.get("/ui/data-explorer", (c) => {
|
|
526
|
+
const resources = getAllResourcesForDisplay().map(r => r.name);
|
|
527
|
+
const readOnly = config.dataExplorer?.readOnly ?? (mode === "production");
|
|
528
|
+
const content = pages.dataExplorerPage({
|
|
529
|
+
resources,
|
|
530
|
+
readOnly,
|
|
531
|
+
mode,
|
|
532
|
+
});
|
|
533
|
+
return sendHtml(c, 'data-explorer', content);
|
|
534
|
+
});
|
|
535
|
+
// Requests
|
|
536
|
+
router.get("/ui/requests", (c) => {
|
|
537
|
+
const requests = requestLogs.slice(0, 200).map(r => ({
|
|
538
|
+
id: r.id,
|
|
539
|
+
method: r.method,
|
|
540
|
+
path: r.path,
|
|
541
|
+
status: r.status,
|
|
542
|
+
duration: r.duration,
|
|
543
|
+
timestamp: new Date(r.timestamp).toISOString(),
|
|
544
|
+
error: r.error,
|
|
545
|
+
}));
|
|
546
|
+
const content = pages.requestsPage({ requests });
|
|
547
|
+
return sendHtml(c, 'requests', content);
|
|
548
|
+
});
|
|
549
|
+
// Errors
|
|
550
|
+
router.get("/ui/errors", (c) => {
|
|
551
|
+
const errors = errorLogs.slice(0, 100).map(e => ({
|
|
552
|
+
id: e.id,
|
|
553
|
+
status: e.statusCode,
|
|
554
|
+
path: e.path,
|
|
555
|
+
message: e.error,
|
|
556
|
+
stack: e.stack,
|
|
557
|
+
timestamp: new Date(e.timestamp).toISOString(),
|
|
558
|
+
}));
|
|
559
|
+
const content = pages.errorsPage({ errors });
|
|
560
|
+
return sendHtml(c, 'errors', content);
|
|
561
|
+
});
|
|
562
|
+
// Users
|
|
563
|
+
router.get("/ui/users", async (c) => {
|
|
564
|
+
let users = [];
|
|
565
|
+
let totalCount = 0;
|
|
566
|
+
if (config.userManager) {
|
|
567
|
+
try {
|
|
568
|
+
const result = await config.userManager.listUsers(50, 0);
|
|
569
|
+
users = result.users;
|
|
570
|
+
totalCount = result.total;
|
|
571
|
+
}
|
|
572
|
+
catch { }
|
|
573
|
+
}
|
|
574
|
+
const content = pages.usersPage({ users, totalCount });
|
|
575
|
+
return sendHtml(c, 'users', content);
|
|
576
|
+
});
|
|
577
|
+
// Sessions
|
|
578
|
+
router.get("/ui/sessions", async (c) => {
|
|
579
|
+
let sessions = [];
|
|
580
|
+
if (config.sessionManager) {
|
|
581
|
+
try {
|
|
582
|
+
sessions = await config.sessionManager.listSessions(100);
|
|
583
|
+
}
|
|
584
|
+
catch { }
|
|
585
|
+
}
|
|
586
|
+
const content = pages.sessionsPage({
|
|
587
|
+
sessions,
|
|
588
|
+
totalCount: sessions.length,
|
|
589
|
+
});
|
|
590
|
+
return sendHtml(c, 'sessions', content);
|
|
591
|
+
});
|
|
592
|
+
// Subscriptions
|
|
593
|
+
router.get("/ui/subscriptions", (c) => {
|
|
594
|
+
const subscriptions = config.getActiveSubscriptions?.() || [];
|
|
595
|
+
const byResource = {};
|
|
596
|
+
for (const sub of subscriptions) {
|
|
597
|
+
byResource[sub.resource] = (byResource[sub.resource] || 0) + 1;
|
|
598
|
+
}
|
|
599
|
+
const content = pages.subscriptionsPage({
|
|
600
|
+
subscriptions,
|
|
601
|
+
stats: {
|
|
602
|
+
active: subscriptions.length,
|
|
603
|
+
totalEvents: subscriptions.reduce((sum, s) => sum + (s.eventCount || 0), 0),
|
|
604
|
+
byResource,
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
return sendHtml(c, 'subscriptions', content);
|
|
608
|
+
});
|
|
609
|
+
// Changelog
|
|
610
|
+
router.get("/ui/changelog", async (c) => {
|
|
611
|
+
let entries = [];
|
|
612
|
+
const stats = { total: 0, creates: 0, updates: 0, deletes: 0, currentSeq: 0 };
|
|
613
|
+
if (config.changelog) {
|
|
614
|
+
try {
|
|
615
|
+
stats.currentSeq = await config.changelog.getCurrentSequence();
|
|
616
|
+
entries = await config.changelog.getEntries(Math.max(0, stats.currentSeq - 100), 100);
|
|
617
|
+
stats.total = entries.length;
|
|
618
|
+
for (const e of entries) {
|
|
619
|
+
if (e.operation === 'create')
|
|
620
|
+
stats.creates++;
|
|
621
|
+
else if (e.operation === 'update')
|
|
622
|
+
stats.updates++;
|
|
623
|
+
else if (e.operation === 'delete')
|
|
624
|
+
stats.deletes++;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
catch { }
|
|
628
|
+
}
|
|
629
|
+
const content = pages.changelogPage({ entries, stats });
|
|
630
|
+
return sendHtml(c, 'changelog', content);
|
|
631
|
+
});
|
|
632
|
+
// Tasks
|
|
633
|
+
router.get("/ui/tasks", async (c) => {
|
|
634
|
+
const content = pages.tasksPage({
|
|
635
|
+
stats: { pending: 0, scheduled: 0, running: 0, completed: 0, failed: 0, dlq: 0 },
|
|
636
|
+
scheduled: [],
|
|
637
|
+
dlq: [],
|
|
638
|
+
workers: [],
|
|
639
|
+
});
|
|
640
|
+
return sendHtml(c, 'tasks', content);
|
|
641
|
+
});
|
|
642
|
+
// KV Inspector
|
|
643
|
+
router.get("/ui/kv-inspector", (c) => {
|
|
644
|
+
const enabled = config.kvInspector?.enabled ?? false;
|
|
645
|
+
const readOnly = config.kvInspector?.readOnly ?? (mode === "production");
|
|
646
|
+
const content = pages.kvInspectorPage({ enabled, readOnly, mode });
|
|
647
|
+
return sendHtml(c, 'kv-inspector', content);
|
|
648
|
+
});
|
|
649
|
+
// Admin Audit
|
|
650
|
+
router.get("/ui/admin-audit", (c) => {
|
|
651
|
+
const entries = getAdminAuditLog(100, 0);
|
|
652
|
+
const content = pages.adminAuditPage({ entries });
|
|
653
|
+
return sendHtml(c, 'admin-audit', content);
|
|
654
|
+
});
|
|
655
|
+
// Filter Tester
|
|
656
|
+
router.get("/ui/filter-tester", (c) => {
|
|
657
|
+
const resources = getAllResourcesForDisplay().map(r => r.name);
|
|
658
|
+
const content = pages.filterTesterPage({ resources });
|
|
659
|
+
return sendHtml(c, 'filter-tester', content);
|
|
660
|
+
});
|
|
661
|
+
// API Explorer
|
|
662
|
+
router.get("/ui/api-explorer", (c) => {
|
|
663
|
+
const resources = getAllResourcesForDisplay();
|
|
664
|
+
const endpoints = [];
|
|
665
|
+
for (const resource of resources) {
|
|
666
|
+
const caps = resource.capabilities || {};
|
|
667
|
+
// GET list
|
|
668
|
+
endpoints.push({
|
|
669
|
+
method: 'GET',
|
|
670
|
+
path: resource.name,
|
|
671
|
+
description: `List ${resource.name} with filtering and pagination`,
|
|
672
|
+
parameters: [
|
|
673
|
+
{ name: 'filter', in: 'query', type: 'string', description: 'RSQL filter expression' },
|
|
674
|
+
{ name: 'limit', in: 'query', type: 'number', description: 'Max results (default: 50)' },
|
|
675
|
+
{ name: 'cursor', in: 'query', type: 'string', description: 'Pagination cursor' },
|
|
676
|
+
{ name: 'orderBy', in: 'query', type: 'string', description: 'Sort field:direction' },
|
|
677
|
+
],
|
|
678
|
+
});
|
|
679
|
+
// GET single
|
|
680
|
+
endpoints.push({
|
|
681
|
+
method: 'GET',
|
|
682
|
+
path: `${resource.name}/:id`,
|
|
683
|
+
description: `Get a single ${resource.name} by ID`,
|
|
684
|
+
parameters: [
|
|
685
|
+
{ name: 'id', in: 'path', type: 'string', required: true },
|
|
686
|
+
],
|
|
687
|
+
});
|
|
688
|
+
if (caps.enableCreate) {
|
|
689
|
+
endpoints.push({
|
|
690
|
+
method: 'POST',
|
|
691
|
+
path: resource.name,
|
|
692
|
+
description: `Create a new ${resource.name}`,
|
|
693
|
+
requestBody: { contentType: 'application/json' },
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
if (caps.enableUpdate) {
|
|
697
|
+
endpoints.push({
|
|
698
|
+
method: 'PATCH',
|
|
699
|
+
path: `${resource.name}/:id`,
|
|
700
|
+
description: `Update a ${resource.name}`,
|
|
701
|
+
parameters: [{ name: 'id', in: 'path', type: 'string', required: true }],
|
|
702
|
+
requestBody: { contentType: 'application/json' },
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
if (caps.enableDelete) {
|
|
706
|
+
endpoints.push({
|
|
707
|
+
method: 'DELETE',
|
|
708
|
+
path: `${resource.name}/:id`,
|
|
709
|
+
description: `Delete a ${resource.name}`,
|
|
710
|
+
parameters: [{ name: 'id', in: 'path', type: 'string', required: true }],
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const content = pages.apiExplorerPage({ endpoints, baseUrl: '' });
|
|
715
|
+
return sendHtml(c, 'api-explorer', content);
|
|
716
|
+
});
|
|
717
|
+
// ============================================
|
|
718
|
+
// HTMX Partial Routes - For dynamic updates
|
|
719
|
+
// ============================================
|
|
720
|
+
// Empty fragment (for closing modals, etc.)
|
|
721
|
+
router.get("/ui/empty", (c) => {
|
|
722
|
+
return c.html('');
|
|
723
|
+
});
|
|
724
|
+
// Request list partial
|
|
725
|
+
router.get("/ui/requests/list", (c) => {
|
|
726
|
+
let requests = requestLogs.slice(0, 200);
|
|
727
|
+
const method = c.req.query("method");
|
|
728
|
+
const status = c.req.query("status");
|
|
729
|
+
const path = c.req.query("path");
|
|
730
|
+
if (method) {
|
|
731
|
+
requests = requests.filter(r => r.method === method);
|
|
732
|
+
}
|
|
733
|
+
if (status === 'success') {
|
|
734
|
+
requests = requests.filter(r => r.status >= 200 && r.status < 400);
|
|
735
|
+
}
|
|
736
|
+
else if (status === 'error') {
|
|
737
|
+
requests = requests.filter(r => r.status >= 400);
|
|
738
|
+
}
|
|
739
|
+
if (path) {
|
|
740
|
+
requests = requests.filter(r => r.path.includes(path));
|
|
741
|
+
}
|
|
742
|
+
const mapped = requests.map(r => ({
|
|
743
|
+
id: r.id,
|
|
744
|
+
method: r.method,
|
|
745
|
+
path: r.path,
|
|
746
|
+
status: r.status,
|
|
747
|
+
duration: r.duration,
|
|
748
|
+
timestamp: new Date(r.timestamp).toISOString(),
|
|
749
|
+
error: r.error,
|
|
750
|
+
}));
|
|
751
|
+
return c.html(pages.requestList(mapped));
|
|
752
|
+
});
|
|
753
|
+
// Request detail partial
|
|
754
|
+
router.get("/ui/requests/:id", (c) => {
|
|
755
|
+
const id = c.req.param("id");
|
|
756
|
+
const request = requestLogs.find(r => r.id === id);
|
|
757
|
+
if (!request) {
|
|
758
|
+
return c.html(emptyState('✕', 'Request not found', 'The request may have been purged from logs'));
|
|
759
|
+
}
|
|
760
|
+
return c.html(pages.requestDetail({
|
|
761
|
+
request: {
|
|
762
|
+
id: request.id,
|
|
763
|
+
method: request.method,
|
|
764
|
+
path: request.path,
|
|
765
|
+
status: request.status,
|
|
766
|
+
duration: request.duration,
|
|
767
|
+
timestamp: new Date(request.timestamp).toISOString(),
|
|
768
|
+
error: request.error,
|
|
769
|
+
headers: request.headers,
|
|
770
|
+
body: request.requestBody,
|
|
771
|
+
response: request.responseBody,
|
|
772
|
+
},
|
|
773
|
+
}));
|
|
774
|
+
});
|
|
775
|
+
// Users list partial
|
|
776
|
+
router.get("/ui/users/list", async (c) => {
|
|
777
|
+
if (!config.userManager) {
|
|
778
|
+
return c.html(pages.usersList([]));
|
|
779
|
+
}
|
|
780
|
+
try {
|
|
781
|
+
const search = c.req.query("search");
|
|
782
|
+
const result = await config.userManager.listUsers(50, 0);
|
|
783
|
+
let users = result.users;
|
|
784
|
+
if (search) {
|
|
785
|
+
const term = search.toLowerCase();
|
|
786
|
+
users = users.filter((u) => u.email?.toLowerCase().includes(term) ||
|
|
787
|
+
u.name?.toLowerCase().includes(term));
|
|
788
|
+
}
|
|
789
|
+
return c.html(pages.usersList(users));
|
|
790
|
+
}
|
|
791
|
+
catch (e) {
|
|
792
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
// User create form partial
|
|
796
|
+
router.get("/ui/users/new", (c) => {
|
|
797
|
+
return c.html(pages.userForm());
|
|
798
|
+
});
|
|
799
|
+
// User detail partial
|
|
800
|
+
router.get("/ui/users/:id", async (c) => {
|
|
801
|
+
if (!config.userManager) {
|
|
802
|
+
return c.html(emptyState('✕', 'User management not configured', ''));
|
|
803
|
+
}
|
|
804
|
+
try {
|
|
805
|
+
const id = c.req.param("id");
|
|
806
|
+
const user = await config.userManager.getUser(id);
|
|
807
|
+
if (!user) {
|
|
808
|
+
return c.html(emptyState('✕', 'User not found', ''));
|
|
809
|
+
}
|
|
810
|
+
let sessions = [];
|
|
811
|
+
if (config.sessionManager) {
|
|
812
|
+
try {
|
|
813
|
+
sessions = await config.sessionManager.getSessionsByUser(id);
|
|
814
|
+
}
|
|
815
|
+
catch { }
|
|
816
|
+
}
|
|
817
|
+
return c.html(pages.userDetail({ user: { ...user, sessions } }));
|
|
818
|
+
}
|
|
819
|
+
catch (e) {
|
|
820
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
// Session create form partial
|
|
824
|
+
router.get("/ui/sessions/new", async (c) => {
|
|
825
|
+
let users = [];
|
|
826
|
+
if (config.userManager) {
|
|
827
|
+
try {
|
|
828
|
+
const result = await config.userManager.listUsers(100, 0);
|
|
829
|
+
users = result.users.map((u) => ({ id: u.id, email: u.email }));
|
|
830
|
+
}
|
|
831
|
+
catch { }
|
|
832
|
+
}
|
|
833
|
+
return c.html(pages.sessionForm({ users }));
|
|
834
|
+
});
|
|
835
|
+
// Sessions list partial
|
|
836
|
+
router.get("/ui/sessions/list", async (c) => {
|
|
837
|
+
if (!config.sessionManager) {
|
|
838
|
+
return c.html(pages.sessionsList([]));
|
|
839
|
+
}
|
|
840
|
+
try {
|
|
841
|
+
const sessions = await config.sessionManager.listSessions(100);
|
|
842
|
+
return c.html(pages.sessionsList(sessions));
|
|
843
|
+
}
|
|
844
|
+
catch (e) {
|
|
845
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
// Subscriptions list partial
|
|
849
|
+
router.get("/ui/subscriptions/list", (c) => {
|
|
850
|
+
const subscriptions = config.getActiveSubscriptions?.() || [];
|
|
851
|
+
return c.html(pages.subscriptionsList(subscriptions));
|
|
852
|
+
});
|
|
853
|
+
// Changelog list partial
|
|
854
|
+
router.get("/ui/changelog/list", async (c) => {
|
|
855
|
+
if (!config.changelog) {
|
|
856
|
+
return c.html(pages.changelogList([]));
|
|
857
|
+
}
|
|
858
|
+
try {
|
|
859
|
+
const resource = c.req.query("resource");
|
|
860
|
+
const fromSeq = parseInt(c.req.query("fromSeq") ?? "") || 0;
|
|
861
|
+
const currentSeq = await config.changelog.getCurrentSequence();
|
|
862
|
+
let entries = await config.changelog.getEntries(fromSeq || Math.max(0, currentSeq - 100), 100);
|
|
863
|
+
if (resource) {
|
|
864
|
+
entries = entries.filter((e) => e.resource?.includes(resource));
|
|
865
|
+
}
|
|
866
|
+
return c.html(pages.changelogList(entries));
|
|
867
|
+
}
|
|
868
|
+
catch (e) {
|
|
869
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
// Changelog detail partial
|
|
873
|
+
router.get("/ui/changelog/:seq", async (c) => {
|
|
874
|
+
if (!config.changelog) {
|
|
875
|
+
return c.html(emptyState('✕', 'Changelog not configured', ''));
|
|
876
|
+
}
|
|
877
|
+
try {
|
|
878
|
+
const seq = parseInt(c.req.param("seq"));
|
|
879
|
+
const entries = await config.changelog.getEntries(seq, 1);
|
|
880
|
+
const entry = entries[0];
|
|
881
|
+
if (!entry) {
|
|
882
|
+
return c.html(emptyState('✕', 'Entry not found', ''));
|
|
883
|
+
}
|
|
884
|
+
return c.html(pages.changelogDetail({ entry }));
|
|
885
|
+
}
|
|
886
|
+
catch (e) {
|
|
887
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
// Audit list partial
|
|
891
|
+
router.get("/ui/audit/list", (c) => {
|
|
892
|
+
let entries = getAdminAuditLog(100, 0);
|
|
893
|
+
const operation = c.req.query("operation");
|
|
894
|
+
const user = c.req.query("user");
|
|
895
|
+
if (operation) {
|
|
896
|
+
entries = entries.filter(e => e.operation?.includes(operation));
|
|
897
|
+
}
|
|
898
|
+
if (user) {
|
|
899
|
+
entries = entries.filter(e => e.userEmail?.toLowerCase().includes(user.toLowerCase()) ||
|
|
900
|
+
e.userId?.includes(user));
|
|
901
|
+
}
|
|
902
|
+
return c.html(pages.auditList(entries));
|
|
903
|
+
});
|
|
904
|
+
// Audit detail partial
|
|
905
|
+
router.get("/ui/audit/:index", (c) => {
|
|
906
|
+
const index = parseInt(c.req.param("index"));
|
|
907
|
+
const entries = getAdminAuditLog(100, 0);
|
|
908
|
+
const entry = entries[index];
|
|
909
|
+
if (!entry) {
|
|
910
|
+
return c.html(emptyState('✕', 'Entry not found', ''));
|
|
911
|
+
}
|
|
912
|
+
return c.html(pages.auditDetail({ entry }));
|
|
913
|
+
});
|
|
914
|
+
// KV keys partial
|
|
915
|
+
router.get("/ui/kv/keys", (c) => {
|
|
916
|
+
if (!config.kvInspector?.enabled) {
|
|
917
|
+
return c.html(emptyState('⛁', 'KV Inspector Disabled', ''));
|
|
918
|
+
}
|
|
919
|
+
const readOnly = config.kvInspector?.readOnly ?? (mode === "production");
|
|
920
|
+
// This would need to be implemented with actual KV access
|
|
921
|
+
// For now, return empty
|
|
922
|
+
return c.html(pages.kvKeysList({ keys: [], readOnly }));
|
|
923
|
+
});
|
|
924
|
+
// KV value partial
|
|
925
|
+
router.get("/ui/kv/value/:key", (c) => {
|
|
926
|
+
if (!config.kvInspector?.enabled) {
|
|
927
|
+
return c.html(emptyState('⛁', 'KV Inspector Disabled', ''));
|
|
928
|
+
}
|
|
929
|
+
const key = decodeURIComponent(c.req.param("key"));
|
|
930
|
+
const readOnly = config.kvInspector?.readOnly ?? (mode === "production");
|
|
931
|
+
// This would need to be implemented with actual KV access
|
|
932
|
+
return c.html(pages.kvValueView({
|
|
933
|
+
key,
|
|
934
|
+
type: 'string',
|
|
935
|
+
value: '',
|
|
936
|
+
readOnly,
|
|
937
|
+
}));
|
|
938
|
+
});
|
|
939
|
+
// Data explorer partials
|
|
940
|
+
router.get("/ui/data/resources", (c) => {
|
|
941
|
+
const resources = getAllResourcesForDisplay().map(r => r.name);
|
|
942
|
+
const readOnly = config.dataExplorer?.readOnly ?? (mode === "production");
|
|
943
|
+
return c.html(pages.resourceSelector({ resources, readOnly }));
|
|
944
|
+
});
|
|
945
|
+
// Data table partial for a resource
|
|
946
|
+
router.get("/ui/data/:resource/table", async (c) => {
|
|
947
|
+
const resource = c.req.param("resource");
|
|
948
|
+
const filter = c.req.query("filter") || '';
|
|
949
|
+
const limit = parseInt(c.req.query("limit") ?? "") || 50;
|
|
950
|
+
const cursor = c.req.query("cursor") || '';
|
|
951
|
+
const orderBy = c.req.query("orderBy") || '';
|
|
952
|
+
const readOnly = config.dataExplorer?.readOnly ?? (mode === "production");
|
|
953
|
+
try {
|
|
954
|
+
const schemaInfo = getSchemaInfo(resource);
|
|
955
|
+
if (!schemaInfo) {
|
|
956
|
+
return c.html(emptyState('✕', 'Resource not found', `Resource '${resource}' is not registered`));
|
|
957
|
+
}
|
|
958
|
+
const entry = getResourceSchema(resource);
|
|
959
|
+
if (!entry) {
|
|
960
|
+
return c.html(emptyState('✕', 'Resource not found', ''));
|
|
961
|
+
}
|
|
962
|
+
const db = entry.db;
|
|
963
|
+
const schema = entry.schema;
|
|
964
|
+
const idColumnName = entry.idColumn.name;
|
|
965
|
+
const filterer = createResourceFilter(schema, {});
|
|
966
|
+
const pagination = createPagination(schema, entry.idColumn, {
|
|
967
|
+
defaultLimit: 20,
|
|
968
|
+
maxLimit: config.dataExplorer?.maxLimit ?? 100,
|
|
969
|
+
});
|
|
970
|
+
const orderByFields = parseOrderBy(orderBy || idColumnName);
|
|
971
|
+
let sqlFilter;
|
|
972
|
+
if (filter) {
|
|
973
|
+
sqlFilter = filterer.convert(filter);
|
|
974
|
+
}
|
|
975
|
+
let query = db.select().from(schema);
|
|
976
|
+
if (sqlFilter) {
|
|
977
|
+
query = query.where(sqlFilter);
|
|
978
|
+
}
|
|
979
|
+
if (cursor) {
|
|
980
|
+
const cursorData = decodeCursorLegacy(cursor);
|
|
981
|
+
if (cursorData) {
|
|
982
|
+
const cursorCondition = pagination.buildCursorCondition(cursorData, orderByFields);
|
|
983
|
+
if (cursorCondition) {
|
|
984
|
+
query = query.where(sqlFilter ? and(sqlFilter, cursorCondition) : cursorCondition);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
const orderByClauses = pagination.buildOrderBy(orderByFields);
|
|
989
|
+
if (orderByClauses.length > 0) {
|
|
990
|
+
query = query.orderBy(...orderByClauses);
|
|
991
|
+
}
|
|
992
|
+
query = query.limit(limit + 1);
|
|
993
|
+
const items = await query;
|
|
994
|
+
// Get total count
|
|
995
|
+
const [countResult] = await db
|
|
996
|
+
.select({ count: count() })
|
|
997
|
+
.from(schema)
|
|
998
|
+
.where(sqlFilter);
|
|
999
|
+
const totalCount = countResult?.count ?? 0;
|
|
1000
|
+
const result = pagination.processResults(items, limit, idColumnName, orderByFields, totalCount);
|
|
1001
|
+
return c.html(pages.dataTable({
|
|
1002
|
+
resource,
|
|
1003
|
+
schema: schemaInfo,
|
|
1004
|
+
items: result.items,
|
|
1005
|
+
totalCount: result.totalCount,
|
|
1006
|
+
hasMore: result.hasMore,
|
|
1007
|
+
nextCursor: result.nextCursor ?? undefined,
|
|
1008
|
+
filter,
|
|
1009
|
+
orderBy: orderBy || undefined,
|
|
1010
|
+
limit,
|
|
1011
|
+
readOnly,
|
|
1012
|
+
}));
|
|
1013
|
+
}
|
|
1014
|
+
catch (e) {
|
|
1015
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
// Row detail partial
|
|
1019
|
+
router.get("/ui/data/:resource/row/:id", async (c) => {
|
|
1020
|
+
const resource = c.req.param("resource");
|
|
1021
|
+
const id = c.req.param("id");
|
|
1022
|
+
try {
|
|
1023
|
+
const entry = getResourceSchema(resource);
|
|
1024
|
+
if (!entry) {
|
|
1025
|
+
return c.html(emptyState('✕', 'Resource not found', ''));
|
|
1026
|
+
}
|
|
1027
|
+
const db = entry.db;
|
|
1028
|
+
const schema = entry.schema;
|
|
1029
|
+
const columns = getTableColumns(schema);
|
|
1030
|
+
const idColumn = columns[entry.idColumn.name];
|
|
1031
|
+
const [item] = await db.select().from(schema).where(eq(idColumn, id));
|
|
1032
|
+
if (!item) {
|
|
1033
|
+
return c.html(emptyState('✕', 'Record not found', ''));
|
|
1034
|
+
}
|
|
1035
|
+
return c.html(pages.recordDetail({ resource, item: item }));
|
|
1036
|
+
}
|
|
1037
|
+
catch (e) {
|
|
1038
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
// Edit form partial
|
|
1042
|
+
router.get("/ui/data/:resource/edit/:id", async (c) => {
|
|
1043
|
+
const resource = c.req.param("resource");
|
|
1044
|
+
const id = c.req.param("id");
|
|
1045
|
+
const readOnly = config.dataExplorer?.readOnly ?? (mode === "production");
|
|
1046
|
+
if (readOnly) {
|
|
1047
|
+
return c.html(emptyState('⚠', 'Read-only mode', 'Data editing is disabled in this environment'));
|
|
1048
|
+
}
|
|
1049
|
+
try {
|
|
1050
|
+
const entry = getResourceSchema(resource);
|
|
1051
|
+
const schemaInfo = getSchemaInfo(resource);
|
|
1052
|
+
if (!entry || !schemaInfo) {
|
|
1053
|
+
return c.html(emptyState('✕', 'Resource not found', ''));
|
|
1054
|
+
}
|
|
1055
|
+
const db = entry.db;
|
|
1056
|
+
const schema = entry.schema;
|
|
1057
|
+
const columns = getTableColumns(schema);
|
|
1058
|
+
const idColumn = columns[entry.idColumn.name];
|
|
1059
|
+
const [item] = await db.select().from(schema).where(eq(idColumn, id));
|
|
1060
|
+
if (!item) {
|
|
1061
|
+
return c.html(emptyState('✕', 'Record not found', ''));
|
|
1062
|
+
}
|
|
1063
|
+
return c.html(pages.recordForm({
|
|
1064
|
+
resource,
|
|
1065
|
+
schema: schemaInfo,
|
|
1066
|
+
item: item,
|
|
1067
|
+
isEdit: true,
|
|
1068
|
+
}));
|
|
1069
|
+
}
|
|
1070
|
+
catch (e) {
|
|
1071
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
// New record form partial
|
|
1075
|
+
router.get("/ui/data/new", (c) => {
|
|
1076
|
+
const resource = c.req.query("resource");
|
|
1077
|
+
const readOnly = config.dataExplorer?.readOnly ?? (mode === "production");
|
|
1078
|
+
if (readOnly) {
|
|
1079
|
+
return c.html(emptyState('⚠', 'Read-only mode', 'Data editing is disabled in this environment'));
|
|
1080
|
+
}
|
|
1081
|
+
if (!resource) {
|
|
1082
|
+
return c.html(emptyState('⚠', 'No resource selected', 'Select a resource first'));
|
|
1083
|
+
}
|
|
1084
|
+
try {
|
|
1085
|
+
const schemaInfo = getSchemaInfo(resource);
|
|
1086
|
+
if (!schemaInfo) {
|
|
1087
|
+
return c.html(emptyState('✕', 'Resource not found', ''));
|
|
1088
|
+
}
|
|
1089
|
+
return c.html(pages.recordForm({
|
|
1090
|
+
resource,
|
|
1091
|
+
schema: schemaInfo,
|
|
1092
|
+
isEdit: false,
|
|
1093
|
+
}));
|
|
1094
|
+
}
|
|
1095
|
+
catch (e) {
|
|
1096
|
+
return c.html(html `<div class="alert alert-error">${escapeHtml(e.message)}</div>`);
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
// Filter tester parse partial
|
|
1100
|
+
router.post("/ui/filter/parse", async (c) => {
|
|
1101
|
+
const body = await c.req.parseBody();
|
|
1102
|
+
const filter = typeof body.filter === "string" ? body.filter : '';
|
|
1103
|
+
try {
|
|
1104
|
+
// Use createResourceFilter with a minimal dummy to parse the filter
|
|
1105
|
+
// This validates the syntax without needing a real schema
|
|
1106
|
+
const dummySchema = {};
|
|
1107
|
+
const filterer = createResourceFilter(dummySchema, {});
|
|
1108
|
+
// compile() returns the parsed AST and throws on syntax errors
|
|
1109
|
+
const ast = filterer.compile(filter);
|
|
1110
|
+
return c.html(pages.filterParseResult({ filter, ast: ast.toString() }));
|
|
1111
|
+
}
|
|
1112
|
+
catch (e) {
|
|
1113
|
+
return c.html(pages.filterParseResult({ filter, error: e.message }));
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
// API explorer endpoint detail partial
|
|
1117
|
+
router.get("/ui/api-explorer/endpoint/:index", (c) => {
|
|
1118
|
+
const index = parseInt(c.req.param("index"));
|
|
1119
|
+
const resources = getAllResourcesForDisplay();
|
|
1120
|
+
const endpoints = [];
|
|
1121
|
+
for (const resource of resources) {
|
|
1122
|
+
const caps = resource.capabilities || {};
|
|
1123
|
+
endpoints.push({
|
|
1124
|
+
method: 'GET',
|
|
1125
|
+
path: resource.name,
|
|
1126
|
+
description: `List ${resource.name} with filtering and pagination`,
|
|
1127
|
+
parameters: [
|
|
1128
|
+
{ name: 'filter', in: 'query', type: 'string', description: 'RSQL filter expression' },
|
|
1129
|
+
{ name: 'limit', in: 'query', type: 'number', description: 'Max results (default: 50)' },
|
|
1130
|
+
{ name: 'cursor', in: 'query', type: 'string', description: 'Pagination cursor' },
|
|
1131
|
+
{ name: 'orderBy', in: 'query', type: 'string', description: 'Sort field:direction' },
|
|
1132
|
+
],
|
|
1133
|
+
});
|
|
1134
|
+
endpoints.push({
|
|
1135
|
+
method: 'GET',
|
|
1136
|
+
path: `${resource.name}/:id`,
|
|
1137
|
+
description: `Get a single ${resource.name} by ID`,
|
|
1138
|
+
parameters: [{ name: 'id', in: 'path', type: 'string', required: true }],
|
|
1139
|
+
});
|
|
1140
|
+
if (caps.enableCreate) {
|
|
1141
|
+
endpoints.push({
|
|
1142
|
+
method: 'POST',
|
|
1143
|
+
path: resource.name,
|
|
1144
|
+
description: `Create a new ${resource.name}`,
|
|
1145
|
+
requestBody: { contentType: 'application/json' },
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
if (caps.enableUpdate) {
|
|
1149
|
+
endpoints.push({
|
|
1150
|
+
method: 'PATCH',
|
|
1151
|
+
path: `${resource.name}/:id`,
|
|
1152
|
+
description: `Update a ${resource.name}`,
|
|
1153
|
+
parameters: [{ name: 'id', in: 'path', type: 'string', required: true }],
|
|
1154
|
+
requestBody: { contentType: 'application/json' },
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
if (caps.enableDelete) {
|
|
1158
|
+
endpoints.push({
|
|
1159
|
+
method: 'DELETE',
|
|
1160
|
+
path: `${resource.name}/:id`,
|
|
1161
|
+
description: `Delete a ${resource.name}`,
|
|
1162
|
+
parameters: [{ name: 'id', in: 'path', type: 'string', required: true }],
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const endpoint = endpoints[index];
|
|
1167
|
+
if (!endpoint) {
|
|
1168
|
+
return c.html(emptyState('✕', 'Endpoint not found', ''));
|
|
1169
|
+
}
|
|
1170
|
+
return c.html(pages.endpointDetail({ endpoint, baseUrl: '' }));
|
|
1171
|
+
});
|
|
1172
|
+
return router;
|
|
1173
|
+
};
|
|
1174
|
+
export default createAdminUI;
|
|
1175
|
+
//# sourceMappingURL=middleware.js.map
|