alepha 0.15.1 → 0.15.3
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 +68 -80
- package/dist/api/audits/index.d.ts +10 -33
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +10 -33
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.d.ts +10 -3
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +10 -3
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +162 -155
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +10 -3
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +413 -0
- package/dist/api/keys/index.d.ts.map +1 -0
- package/dist/api/keys/index.js +476 -0
- package/dist/api/keys/index.js.map +1 -0
- package/dist/api/notifications/index.d.ts +10 -4
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +10 -4
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.d.ts +43 -50
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +30 -37
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.d.ts +1081 -760
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +2539 -218
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts +138 -132
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +12 -4
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/batch/index.d.ts +20 -40
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +31 -44
- package/dist/batch/index.js.map +1 -1
- package/dist/bucket/index.d.ts +440 -8
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +1861 -12
- package/dist/bucket/index.js.map +1 -1
- package/dist/cache/core/index.d.ts +179 -7
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +213 -7
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/redis/index.d.ts +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -1
- package/dist/cache/redis/index.js +4 -0
- package/dist/cache/redis/index.js.map +1 -1
- package/dist/cli/index.d.ts +638 -5645
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2550 -368
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +203 -45
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +2060 -71
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +70 -40
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +34 -13
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +90 -40
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +70 -40
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts +15 -0
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +15 -0
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/index.d.ts +323 -20
- package/dist/email/index.d.ts.map +1 -1
- package/dist/email/index.js +1857 -7
- package/dist/email/index.js.map +1 -1
- package/dist/fake/index.d.ts +90 -8
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +91 -20
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +11 -4
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +11 -4
- package/dist/lock/core/index.js.map +1 -1
- package/dist/logger/index.d.ts +17 -66
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +14 -63
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +10 -30
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +12 -35
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/index.browser.js +3 -3
- package/dist/orm/index.browser.js.map +1 -1
- package/dist/orm/index.bun.js +39 -20
- package/dist/orm/index.bun.js.map +1 -1
- package/dist/orm/index.d.ts +517 -540
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +58 -71
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +18 -10
- package/dist/queue/core/index.d.ts.map +1 -1
- package/dist/queue/core/index.js +14 -6
- package/dist/queue/core/index.js.map +1 -1
- package/dist/react/auth/index.browser.js +108 -0
- package/dist/react/auth/index.browser.js.map +1 -0
- package/dist/react/auth/index.d.ts +100 -0
- package/dist/react/auth/index.d.ts.map +1 -0
- package/dist/react/auth/index.js +145 -0
- package/dist/react/auth/index.js.map +1 -0
- package/dist/react/core/index.d.ts +469 -0
- package/dist/react/core/index.d.ts.map +1 -0
- package/dist/react/core/index.js +464 -0
- package/dist/react/core/index.js.map +1 -0
- package/dist/react/form/index.d.ts +232 -0
- package/dist/react/form/index.d.ts.map +1 -0
- package/dist/react/form/index.js +432 -0
- package/dist/react/form/index.js.map +1 -0
- package/dist/react/head/index.browser.js +423 -0
- package/dist/react/head/index.browser.js.map +1 -0
- package/dist/react/head/index.d.ts +288 -0
- package/dist/react/head/index.d.ts.map +1 -0
- package/dist/react/head/index.js +465 -0
- package/dist/react/head/index.js.map +1 -0
- package/dist/react/i18n/index.d.ts +175 -0
- package/dist/react/i18n/index.d.ts.map +1 -0
- package/dist/react/i18n/index.js +224 -0
- package/dist/react/i18n/index.js.map +1 -0
- package/dist/react/router/index.browser.js +1974 -0
- package/dist/react/router/index.browser.js.map +1 -0
- package/dist/react/router/index.d.ts +1956 -0
- package/dist/react/router/index.d.ts.map +1 -0
- package/dist/react/router/index.js +4722 -0
- package/dist/react/router/index.js.map +1 -0
- package/dist/react/websocket/index.d.ts +117 -0
- package/dist/react/websocket/index.d.ts.map +1 -0
- package/dist/react/websocket/index.js +107 -0
- package/dist/react/websocket/index.js.map +1 -0
- package/dist/redis/index.bun.js +4 -0
- package/dist/redis/index.bun.js.map +1 -1
- package/dist/redis/index.d.ts +41 -44
- package/dist/redis/index.d.ts.map +1 -1
- package/dist/redis/index.js +16 -25
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts +11 -2
- package/dist/retry/index.d.ts.map +1 -1
- package/dist/retry/index.js +11 -2
- package/dist/retry/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +11 -2
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +11 -2
- package/dist/scheduler/index.js.map +1 -1
- package/dist/security/index.d.ts +140 -49
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +164 -32
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +12 -7
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +12 -7
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cache/index.d.ts +7 -22
- package/dist/server/cache/index.d.ts.map +1 -1
- package/dist/server/cache/index.js +7 -22
- package/dist/server/cache/index.js.map +1 -1
- package/dist/server/compress/index.d.ts +10 -2
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +10 -2
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/cookies/index.d.ts +40 -16
- package/dist/server/cookies/index.d.ts.map +1 -1
- package/dist/server/cookies/index.js +7 -5
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.d.ts +124 -23
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +231 -14
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts +13 -23
- package/dist/server/cors/index.d.ts.map +1 -1
- package/dist/server/cors/index.js +7 -21
- package/dist/server/cors/index.js.map +1 -1
- package/dist/server/health/index.d.ts +8 -2
- package/dist/server/health/index.d.ts.map +1 -1
- package/dist/server/health/index.js +8 -2
- package/dist/server/health/index.js.map +1 -1
- package/dist/server/helmet/index.d.ts +11 -3
- package/dist/server/helmet/index.d.ts.map +1 -1
- package/dist/server/helmet/index.js +11 -3
- package/dist/server/helmet/index.js.map +1 -1
- package/dist/server/links/index.d.ts +11 -6
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +11 -6
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +10 -3
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +10 -3
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/multipart/index.d.ts +9 -3
- package/dist/server/multipart/index.d.ts.map +1 -1
- package/dist/server/multipart/index.js +9 -3
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts +8 -2
- package/dist/server/proxy/index.d.ts.map +1 -1
- package/dist/server/proxy/index.js +8 -2
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts +30 -35
- package/dist/server/rate-limit/index.d.ts.map +1 -1
- package/dist/server/rate-limit/index.js +18 -55
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.d.ts +137 -4
- package/dist/server/static/index.d.ts.map +1 -1
- package/dist/server/static/index.js +1853 -5
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts +309 -6
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +1854 -6
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.d.ts +309 -7
- package/dist/sms/index.d.ts.map +1 -1
- package/dist/sms/index.js +1856 -7
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js +1218 -0
- package/dist/system/index.browser.js.map +1 -0
- package/dist/{file → system}/index.d.ts +343 -16
- package/dist/system/index.d.ts.map +1 -0
- package/dist/{file → system}/index.js +419 -22
- package/dist/system/index.js.map +1 -0
- package/dist/thread/index.d.ts +11 -2
- package/dist/thread/index.d.ts.map +1 -1
- package/dist/thread/index.js +11 -2
- package/dist/thread/index.js.map +1 -1
- package/dist/topic/core/index.d.ts +12 -5
- package/dist/topic/core/index.d.ts.map +1 -1
- package/dist/topic/core/index.js +12 -5
- package/dist/topic/core/index.js.map +1 -1
- package/dist/vite/index.d.ts +5 -6272
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +23 -10
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +12 -8
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +12 -8
- package/dist/websocket/index.js.map +1 -1
- package/package.json +82 -11
- package/src/api/audits/index.ts +10 -33
- package/src/api/files/__tests__/$bucket.spec.ts +1 -1
- package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
- package/src/api/files/controllers/FileController.spec.ts +1 -1
- package/src/api/files/index.ts +10 -3
- package/src/api/files/jobs/FileJobs.spec.ts +1 -1
- package/src/api/files/services/FileService.spec.ts +1 -1
- package/src/api/jobs/index.ts +10 -3
- package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
- package/src/api/keys/controllers/ApiKeyController.ts +103 -0
- package/src/api/keys/entities/apiKeyEntity.ts +41 -0
- package/src/api/keys/index.ts +49 -0
- package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
- package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
- package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
- package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
- package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
- package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
- package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
- package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
- package/src/api/keys/services/ApiKeyService.ts +306 -0
- package/src/api/logs/TODO.md +55 -0
- package/src/api/notifications/index.ts +10 -4
- package/src/api/parameters/index.ts +9 -30
- package/src/api/parameters/primitives/$config.ts +12 -4
- package/src/api/parameters/services/ConfigStore.ts +9 -3
- package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
- package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
- package/src/api/users/index.ts +14 -3
- package/src/api/users/primitives/$realm.ts +33 -5
- package/src/api/users/providers/RealmProvider.ts +1 -12
- package/src/api/users/services/SessionService.ts +1 -1
- package/src/api/verifications/controllers/VerificationController.ts +2 -0
- package/src/api/verifications/index.ts +10 -4
- package/src/batch/index.ts +9 -36
- package/src/batch/primitives/$batch.ts +0 -8
- package/src/batch/providers/BatchProvider.ts +29 -2
- package/src/bucket/__tests__/shared.ts +1 -1
- package/src/bucket/index.ts +13 -6
- package/src/bucket/primitives/$bucket.ts +1 -1
- package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
- package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
- package/src/cache/core/__tests__/shared.ts +30 -0
- package/src/cache/core/index.ts +11 -6
- package/src/cache/core/primitives/$cache.spec.ts +5 -0
- package/src/cache/core/providers/CacheProvider.ts +17 -0
- package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
- package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
- package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
- package/src/cli/apps/AlephaCli.ts +1 -14
- package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -1
- package/src/cli/atoms/buildOptions.ts +99 -9
- package/src/cli/commands/build.ts +150 -37
- package/src/cli/commands/db.ts +22 -18
- package/src/cli/commands/deploy.ts +1 -1
- package/src/cli/commands/dev.ts +1 -20
- package/src/cli/commands/gen/env.ts +5 -2
- package/src/cli/commands/gen/openapi.ts +5 -2
- package/src/cli/commands/init.spec.ts +588 -0
- package/src/cli/commands/init.ts +115 -58
- package/src/cli/commands/lint.ts +7 -1
- package/src/cli/commands/typecheck.ts +11 -0
- package/src/cli/providers/AppEntryProvider.ts +1 -1
- package/src/cli/providers/ViteBuildProvider.ts +8 -50
- package/src/cli/providers/ViteDevServerProvider.ts +35 -16
- package/src/cli/services/AlephaCliUtils.ts +52 -121
- package/src/cli/services/PackageManagerUtils.ts +129 -11
- package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
- package/src/cli/services/ProjectScaffolder.ts +148 -81
- package/src/cli/services/ViteUtils.ts +82 -0
- package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +37 -24
- package/src/cli/templates/apiAppSecurityTs.ts +11 -0
- package/src/cli/templates/apiIndexTs.ts +30 -0
- package/src/cli/templates/gitignore.ts +39 -0
- package/src/cli/{assets → templates}/mainCss.ts +11 -2
- package/src/cli/templates/mainServerTs.ts +33 -0
- package/src/cli/templates/webAppRouterTs.ts +74 -0
- package/src/cli/templates/webHelloComponentTsx.ts +30 -0
- package/src/command/helpers/Runner.spec.ts +139 -0
- package/src/command/helpers/Runner.ts +7 -22
- package/src/command/index.ts +12 -4
- package/src/command/providers/CliProvider.spec.ts +1392 -0
- package/src/command/providers/CliProvider.ts +320 -47
- package/src/core/Alepha.ts +34 -27
- package/src/core/__tests__/Alepha-start.spec.ts +4 -4
- package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
- package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
- package/src/core/index.shared.ts +1 -0
- package/src/core/index.ts +20 -0
- package/src/core/providers/EventManager.spec.ts +0 -71
- package/src/core/providers/EventManager.ts +3 -15
- package/src/core/providers/Json.ts +2 -14
- package/src/datetime/index.ts +15 -0
- package/src/email/index.ts +10 -5
- package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
- package/src/email/providers/LocalEmailProvider.ts +1 -1
- package/src/fake/__tests__/keyName.example.ts +1 -1
- package/src/fake/__tests__/keyName.spec.ts +5 -5
- package/src/fake/index.ts +9 -6
- package/src/fake/providers/FakeProvider.spec.ts +258 -40
- package/src/fake/providers/FakeProvider.ts +133 -19
- package/src/lock/core/index.ts +11 -4
- package/src/logger/index.ts +17 -66
- package/src/mcp/index.ts +10 -27
- package/src/mcp/transports/SseMcpTransport.ts +0 -11
- package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
- package/src/orm/index.browser.ts +2 -2
- package/src/orm/index.bun.ts +5 -3
- package/src/orm/index.ts +23 -53
- package/src/orm/providers/drivers/BunSqliteProvider.ts +5 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
- package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
- package/src/orm/services/Repository.ts +7 -3
- package/src/queue/core/index.ts +14 -6
- package/src/react/auth/__tests__/$auth.spec.ts +202 -0
- package/src/react/auth/hooks/useAuth.ts +32 -0
- package/src/react/auth/index.browser.ts +13 -0
- package/src/react/auth/index.shared.ts +2 -0
- package/src/react/auth/index.ts +48 -0
- package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
- package/src/react/auth/services/ReactAuth.ts +135 -0
- package/src/react/core/__tests__/Router.spec.tsx +169 -0
- package/src/react/core/components/ClientOnly.tsx +49 -0
- package/src/react/core/components/ErrorBoundary.tsx +73 -0
- package/src/react/core/contexts/AlephaContext.ts +7 -0
- package/src/react/core/contexts/AlephaProvider.tsx +42 -0
- package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
- package/src/react/core/hooks/useAction.ts +480 -0
- package/src/react/core/hooks/useAlepha.ts +26 -0
- package/src/react/core/hooks/useClient.ts +17 -0
- package/src/react/core/hooks/useEvents.ts +51 -0
- package/src/react/core/hooks/useInject.ts +12 -0
- package/src/react/core/hooks/useStore.ts +52 -0
- package/src/react/core/index.ts +90 -0
- package/src/react/form/components/FormState.tsx +17 -0
- package/src/react/form/errors/FormValidationError.ts +18 -0
- package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
- package/src/react/form/hooks/useForm.ts +47 -0
- package/src/react/form/hooks/useFormState.ts +130 -0
- package/src/react/form/index.ts +44 -0
- package/src/react/form/services/FormModel.ts +614 -0
- package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
- package/src/react/head/helpers/SeoExpander.ts +142 -0
- package/src/react/head/hooks/useHead.spec.tsx +288 -0
- package/src/react/head/hooks/useHead.ts +62 -0
- package/src/react/head/index.browser.ts +26 -0
- package/src/react/head/index.ts +44 -0
- package/src/react/head/interfaces/Head.ts +105 -0
- package/src/react/head/primitives/$head.ts +25 -0
- package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
- package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
- package/src/react/head/providers/HeadProvider.ts +168 -0
- package/src/react/head/providers/ServerHeadProvider.ts +31 -0
- package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
- package/src/react/i18n/components/Localize.spec.tsx +357 -0
- package/src/react/i18n/components/Localize.tsx +35 -0
- package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
- package/src/react/i18n/hooks/useI18n.ts +18 -0
- package/src/react/i18n/index.ts +41 -0
- package/src/react/i18n/primitives/$dictionary.ts +69 -0
- package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
- package/src/react/i18n/providers/I18nProvider.ts +278 -0
- package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
- package/src/react/router/__tests__/page-head.spec.ts +48 -0
- package/src/react/router/__tests__/seo-head.spec.ts +125 -0
- package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
- package/src/react/router/components/ErrorViewer.tsx +872 -0
- package/src/react/router/components/Link.tsx +23 -0
- package/src/react/router/components/NestedView.tsx +223 -0
- package/src/react/router/components/NotFound.tsx +30 -0
- package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
- package/src/react/router/contexts/RouterLayerContext.ts +12 -0
- package/src/react/router/errors/Redirection.ts +28 -0
- package/src/react/router/hooks/useActive.ts +52 -0
- package/src/react/router/hooks/useQueryParams.ts +63 -0
- package/src/react/router/hooks/useRouter.ts +20 -0
- package/src/react/router/hooks/useRouterState.ts +11 -0
- package/src/react/router/index.browser.ts +45 -0
- package/src/react/router/index.shared.ts +19 -0
- package/src/react/router/index.ts +146 -0
- package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
- package/src/react/router/primitives/$page.spec.tsx +676 -0
- package/src/react/router/primitives/$page.ts +489 -0
- package/src/react/router/providers/ReactBrowserProvider.ts +312 -0
- package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
- package/src/react/router/providers/ReactPageProvider.ts +726 -0
- package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
- package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
- package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
- package/src/react/router/providers/ReactServerProvider.ts +487 -0
- package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
- package/src/react/router/providers/ReactServerTemplateProvider.ts +542 -0
- package/src/react/router/providers/SSRManifestProvider.ts +334 -0
- package/src/react/router/services/ReactPageServerService.ts +48 -0
- package/src/react/router/services/ReactPageService.ts +27 -0
- package/src/react/router/services/ReactRouter.ts +262 -0
- package/src/react/websocket/hooks/useRoom.tsx +242 -0
- package/src/react/websocket/index.ts +7 -0
- package/src/redis/__tests__/redis.spec.ts +13 -0
- package/src/redis/index.ts +9 -25
- package/src/redis/providers/BunRedisProvider.ts +9 -0
- package/src/redis/providers/NodeRedisProvider.ts +8 -0
- package/src/redis/providers/RedisProvider.ts +16 -0
- package/src/retry/index.ts +11 -2
- package/src/router/index.ts +15 -0
- package/src/scheduler/index.ts +11 -2
- package/src/security/__tests__/BasicAuth.spec.ts +2 -0
- package/src/security/__tests__/ServerSecurityProvider.spec.ts +90 -5
- package/src/security/index.ts +15 -10
- package/src/security/interfaces/IssuerResolver.ts +27 -0
- package/src/security/primitives/$issuer.ts +55 -0
- package/src/security/providers/SecurityProvider.ts +179 -0
- package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
- package/src/security/providers/ServerSecurityProvider.ts +63 -41
- package/src/server/auth/index.ts +12 -7
- package/src/server/cache/index.ts +7 -22
- package/src/server/compress/index.ts +10 -2
- package/src/server/cookies/index.ts +7 -5
- package/src/server/cookies/primitives/$cookie.ts +33 -11
- package/src/server/core/index.ts +16 -6
- package/src/server/core/interfaces/ServerRequest.ts +83 -1
- package/src/server/core/primitives/$action.spec.ts +1 -1
- package/src/server/core/primitives/$action.ts +8 -3
- package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
- package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
- package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
- package/src/server/core/services/ServerRequestParser.ts +306 -13
- package/src/server/cors/index.ts +7 -21
- package/src/server/cors/primitives/$cors.ts +6 -2
- package/src/server/health/index.ts +8 -2
- package/src/server/helmet/index.ts +11 -3
- package/src/server/links/index.ts +11 -6
- package/src/server/metrics/index.ts +10 -3
- package/src/server/multipart/index.ts +9 -3
- package/src/server/proxy/index.ts +8 -2
- package/src/server/rate-limit/index.ts +21 -25
- package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
- package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
- package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
- package/src/server/static/index.ts +8 -2
- package/src/server/static/providers/ServerStaticProvider.ts +1 -1
- package/src/server/swagger/index.ts +9 -4
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
- package/src/sms/index.ts +9 -5
- package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
- package/src/sms/providers/LocalSmsProvider.ts +1 -1
- package/src/system/index.browser.ts +36 -0
- package/src/system/index.ts +62 -0
- package/src/system/index.workerd.ts +1 -0
- package/src/{file → system}/providers/FileSystemProvider.ts +24 -0
- package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
- package/src/system/providers/MemoryShellProvider.ts +164 -0
- package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
- package/src/{file → system}/providers/NodeFileSystemProvider.ts +47 -2
- package/src/system/providers/NodeShellProvider.ts +184 -0
- package/src/system/providers/ShellProvider.ts +74 -0
- package/src/{file → system}/services/FileDetector.spec.ts +2 -2
- package/src/thread/index.ts +11 -2
- package/src/topic/core/index.ts +12 -5
- package/src/vite/tasks/buildClient.ts +2 -7
- package/src/vite/tasks/buildServer.ts +19 -13
- package/src/vite/tasks/generateCloudflare.ts +10 -7
- package/src/vite/tasks/generateDocker.ts +4 -0
- package/src/websocket/index.ts +12 -8
- package/dist/file/index.d.ts.map +0 -1
- package/dist/file/index.js.map +0 -1
- package/src/cli/assets/apiIndexTs.ts +0 -16
- package/src/cli/assets/mainServerTs.ts +0 -24
- package/src/cli/assets/webAppRouterTs.ts +0 -16
- package/src/cli/assets/webHelloComponentTsx.ts +0 -20
- package/src/cli/providers/ViteTemplateProvider.ts +0 -27
- package/src/file/index.ts +0 -43
- /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
- /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
- /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
- /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
- /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
- /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
- /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
- /package/src/{file → system}/errors/FileError.ts +0 -0
- /package/src/{file → system}/services/FileDetector.ts +0 -0
|
@@ -5,8 +5,10 @@ import {
|
|
|
5
5
|
type DurationLike,
|
|
6
6
|
} from "alepha/datetime";
|
|
7
7
|
import { $logger } from "alepha/logger";
|
|
8
|
+
import type { ServerRequest } from "alepha/server";
|
|
8
9
|
import type { JSONWebKeySet, JWTPayload } from "jose";
|
|
9
10
|
import { SecurityError } from "../errors/SecurityError.ts";
|
|
11
|
+
import type { IssuerResolver } from "../interfaces/IssuerResolver.ts";
|
|
10
12
|
import { JwtProvider } from "../providers/JwtProvider.ts";
|
|
11
13
|
import { SecurityProvider } from "../providers/SecurityProvider.ts";
|
|
12
14
|
import type { Role } from "../schemas/roleSchema.ts";
|
|
@@ -50,6 +52,11 @@ export type IssuerPrimitiveOptions = {
|
|
|
50
52
|
* Parse the JWT payload to create a user account info.
|
|
51
53
|
*/
|
|
52
54
|
profile?: (jwtPayload: Record<string, any>) => UserAccount;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Custom resolvers (in addition to default JWT resolver).
|
|
58
|
+
*/
|
|
59
|
+
resolvers?: IssuerResolver[];
|
|
53
60
|
} & (IssuerInternal | IssuerExternal);
|
|
54
61
|
|
|
55
62
|
export interface IssuerSettings {
|
|
@@ -147,7 +154,55 @@ export class IssuerPrimitive extends Primitive<IssuerPrimitiveOptions> {
|
|
|
147
154
|
profile: this.options.profile,
|
|
148
155
|
secret: "jwks" in this.options ? this.options.jwks : this.options.secret,
|
|
149
156
|
roles,
|
|
157
|
+
resolvers: [],
|
|
150
158
|
});
|
|
159
|
+
|
|
160
|
+
// Register custom resolvers first (they usually have lower priority)
|
|
161
|
+
for (const resolver of this.options.resolvers ?? []) {
|
|
162
|
+
this.registerResolver(resolver);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Register default JWT resolver (priority 100)
|
|
166
|
+
this.registerResolver(this.createJwtResolver());
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Creates the default JWT resolver.
|
|
171
|
+
*/
|
|
172
|
+
protected createJwtResolver(): IssuerResolver {
|
|
173
|
+
return {
|
|
174
|
+
priority: 100,
|
|
175
|
+
onRequest: async (req: ServerRequest) => {
|
|
176
|
+
const auth = req.headers.authorization;
|
|
177
|
+
if (!auth?.startsWith("Bearer ")) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const token = auth.slice(7);
|
|
182
|
+
|
|
183
|
+
// Check if it looks like a JWT (has dots)
|
|
184
|
+
if (!token.includes(".")) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Parse and validate JWT
|
|
189
|
+
const { result } = await this.jwt.parse(token, this.name);
|
|
190
|
+
|
|
191
|
+
// Extract user info from JWT payload
|
|
192
|
+
return this.securityProvider.createUserFromPayload(
|
|
193
|
+
result.payload,
|
|
194
|
+
this.name,
|
|
195
|
+
);
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Register a resolver to this issuer.
|
|
202
|
+
* Resolvers are sorted by priority (lower = first).
|
|
203
|
+
*/
|
|
204
|
+
public registerResolver(resolver: IssuerResolver): void {
|
|
205
|
+
this.securityProvider.registerResolver(resolver, this.name);
|
|
151
206
|
}
|
|
152
207
|
|
|
153
208
|
/**
|
|
@@ -15,6 +15,7 @@ import { InvalidPermissionError } from "../errors/InvalidPermissionError.ts";
|
|
|
15
15
|
import { InvalidTokenError } from "../errors/InvalidTokenError.ts";
|
|
16
16
|
import { RealmNotFoundError } from "../errors/RealmNotFoundError.ts";
|
|
17
17
|
import { SecurityError } from "../errors/SecurityError.ts";
|
|
18
|
+
import type { IssuerResolver, UserInfo } from "../interfaces/IssuerResolver.ts";
|
|
18
19
|
import type { UserAccountToken } from "../interfaces/UserAccountToken.ts";
|
|
19
20
|
import type { Permission } from "../schemas/permissionSchema.ts";
|
|
20
21
|
import type { Role } from "../schemas/roleSchema.ts";
|
|
@@ -90,10 +91,46 @@ export class SecurityProvider {
|
|
|
90
91
|
typeof realm.secret === "function" ? realm.secret() : realm.secret;
|
|
91
92
|
this.jwt.setKeyLoader(realm.name, secret);
|
|
92
93
|
}
|
|
94
|
+
|
|
95
|
+
// Register default JWT resolver for realms without resolvers
|
|
96
|
+
if (!realm.resolvers || realm.resolvers.length === 0) {
|
|
97
|
+
this.registerResolver(
|
|
98
|
+
this.createDefaultJwtResolver(realm.name),
|
|
99
|
+
realm.name,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
93
102
|
}
|
|
94
103
|
},
|
|
95
104
|
});
|
|
96
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Creates a default JWT resolver for a realm.
|
|
108
|
+
*/
|
|
109
|
+
protected createDefaultJwtResolver(realmName: string): IssuerResolver {
|
|
110
|
+
return {
|
|
111
|
+
priority: 100,
|
|
112
|
+
onRequest: async (req) => {
|
|
113
|
+
const auth = req.headers.authorization;
|
|
114
|
+
if (!auth?.startsWith("Bearer ")) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const token = auth.slice(7);
|
|
119
|
+
|
|
120
|
+
// Check if it looks like a JWT (has dots)
|
|
121
|
+
if (!token.includes(".")) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Parse and validate JWT
|
|
126
|
+
const { result } = await this.jwt.parse(token, realmName);
|
|
127
|
+
|
|
128
|
+
// Extract user info from JWT payload
|
|
129
|
+
return this.createUserFromPayload(result.payload, realmName);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
97
134
|
/**
|
|
98
135
|
* Adds a role to one or more realms.
|
|
99
136
|
*
|
|
@@ -305,6 +342,143 @@ export class SecurityProvider {
|
|
|
305
342
|
};
|
|
306
343
|
}
|
|
307
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Generic user creation from any source (JWT, API key, etc.).
|
|
347
|
+
* Handles permission checking, ownership, default roles.
|
|
348
|
+
*/
|
|
349
|
+
public createUser(
|
|
350
|
+
userInfo: UserInfo,
|
|
351
|
+
options: {
|
|
352
|
+
realm?: string;
|
|
353
|
+
permission?: Permission | string;
|
|
354
|
+
} = {},
|
|
355
|
+
): UserAccountToken {
|
|
356
|
+
const realmRoles = this.getRoles(options.realm).filter((it) => it.default);
|
|
357
|
+
const roles = [...(userInfo.roles ?? [])];
|
|
358
|
+
|
|
359
|
+
// Add default roles
|
|
360
|
+
for (const role of realmRoles) {
|
|
361
|
+
if (!roles.includes(role.name)) {
|
|
362
|
+
roles.push(role.name);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
let ownership: string | boolean | undefined;
|
|
367
|
+
|
|
368
|
+
// Permission check
|
|
369
|
+
if (options.permission) {
|
|
370
|
+
const check = this.checkPermission(options.permission, ...roles);
|
|
371
|
+
if (!check.isAuthorized) {
|
|
372
|
+
throw new SecurityError(
|
|
373
|
+
`User is not allowed to access '${this.permissionToString(options.permission)}'`,
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
ownership = check.ownership;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
...userInfo,
|
|
381
|
+
roles,
|
|
382
|
+
ownership,
|
|
383
|
+
realm: options.realm,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Register a resolver to a realm.
|
|
389
|
+
* Resolvers are sorted by priority (lower = first).
|
|
390
|
+
*/
|
|
391
|
+
public registerResolver(resolver: IssuerResolver, realmName?: string): void {
|
|
392
|
+
const realm = this.getRealm(realmName);
|
|
393
|
+
if (!realm.resolvers) {
|
|
394
|
+
realm.resolvers = [];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
realm.resolvers.push(resolver);
|
|
398
|
+
realm.resolvers.sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get a realm by name.
|
|
403
|
+
* Throws if realm not found.
|
|
404
|
+
*/
|
|
405
|
+
public getRealm(realmName?: string): Realm {
|
|
406
|
+
const realm = realmName
|
|
407
|
+
? this.realms.find((it) => it.name === realmName)
|
|
408
|
+
: this.realms[0];
|
|
409
|
+
|
|
410
|
+
if (!realm) {
|
|
411
|
+
throw new RealmNotFoundError(realmName ?? "default");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return realm;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Resolve user from request using registered resolvers.
|
|
419
|
+
* Returns undefined if no resolver could authenticate (no auth provided).
|
|
420
|
+
* Throws UnauthorizedError if auth was provided but invalid.
|
|
421
|
+
*
|
|
422
|
+
* Note: This method tries resolvers from ALL realms to find a match,
|
|
423
|
+
* regardless of the `realm` option. The `realm` option is only used for
|
|
424
|
+
* permission checking after the user is resolved.
|
|
425
|
+
*/
|
|
426
|
+
public async resolveUserFromServerRequest(
|
|
427
|
+
req: { url: URL | string; headers: { authorization?: string } },
|
|
428
|
+
options: {
|
|
429
|
+
realm?: string;
|
|
430
|
+
permission?: Permission | string;
|
|
431
|
+
} = {},
|
|
432
|
+
): Promise<UserAccountToken | undefined> {
|
|
433
|
+
// Collect all resolvers from all realms with their realm name
|
|
434
|
+
const allResolvers: Array<{
|
|
435
|
+
resolver: IssuerResolver;
|
|
436
|
+
realmName: string;
|
|
437
|
+
}> = [];
|
|
438
|
+
|
|
439
|
+
for (const realm of this.realms) {
|
|
440
|
+
for (const resolver of realm.resolvers ?? []) {
|
|
441
|
+
allResolvers.push({ resolver, realmName: realm.name });
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Sort by priority
|
|
446
|
+
allResolvers.sort(
|
|
447
|
+
(a, b) => (a.resolver.priority ?? 100) - (b.resolver.priority ?? 100),
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
// Try resolvers in priority order
|
|
451
|
+
for (const { resolver, realmName } of allResolvers) {
|
|
452
|
+
let userInfo: UserInfo | null;
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
userInfo = await resolver.onRequest(req as any);
|
|
456
|
+
} catch {
|
|
457
|
+
// Resolver failed (e.g., wrong key), try next
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (userInfo) {
|
|
462
|
+
// User was resolved - now create user and check permissions
|
|
463
|
+
// (errors from createUser should propagate, not be caught)
|
|
464
|
+
const user = this.createUser(userInfo, {
|
|
465
|
+
realm: realmName,
|
|
466
|
+
permission: options.permission,
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
await this.alepha.events.emit("security:user:created", {
|
|
470
|
+
realm: realmName,
|
|
471
|
+
user,
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
return user;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// No resolver matched = no auth provided
|
|
479
|
+
return undefined;
|
|
480
|
+
}
|
|
481
|
+
|
|
308
482
|
/**
|
|
309
483
|
* Checks if the user has the specified permission.
|
|
310
484
|
*
|
|
@@ -783,6 +957,11 @@ export interface Realm {
|
|
|
783
957
|
* By default, SecurityProvider has his own implementation, but this method allow to override it.
|
|
784
958
|
*/
|
|
785
959
|
profile?: (raw: Record<string, any>) => UserAccount;
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Custom resolvers for this realm (sorted by priority).
|
|
963
|
+
*/
|
|
964
|
+
resolvers?: IssuerResolver[];
|
|
786
965
|
}
|
|
787
966
|
|
|
788
967
|
export interface SecurityCheckResult {
|
|
@@ -15,9 +15,13 @@ export interface BasicAuthOptions {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface BasicAuthPrimitiveConfig extends BasicAuthOptions {
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Name identifier for this basic auth (default: property key).
|
|
20
|
+
*/
|
|
19
21
|
name?: string;
|
|
20
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Path patterns to match (supports wildcards like /devtools/*).
|
|
24
|
+
*/
|
|
21
25
|
paths?: string[];
|
|
22
26
|
}
|
|
23
27
|
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type ServerRequest,
|
|
8
8
|
UnauthorizedError,
|
|
9
9
|
} from "alepha/server";
|
|
10
|
+
import { InvalidTokenError } from "../errors/InvalidTokenError.ts";
|
|
10
11
|
import type { UserAccountToken } from "../interfaces/UserAccountToken.ts";
|
|
11
12
|
import type { Permission } from "../schemas/permissionSchema.ts";
|
|
12
13
|
import { userAccountInfoSchema } from "../schemas/userAccountInfoSchema.ts";
|
|
@@ -23,30 +24,30 @@ export class ServerSecurityProvider {
|
|
|
23
24
|
protected readonly jwtProvider = $inject(JwtProvider);
|
|
24
25
|
protected readonly alepha = $inject(Alepha);
|
|
25
26
|
|
|
27
|
+
protected readonly resolvers: Array<ServerSecurityUserResolver> = [];
|
|
28
|
+
|
|
26
29
|
protected readonly onConfigure = $hook({
|
|
27
30
|
on: "configure",
|
|
28
31
|
handler: async () => {
|
|
29
32
|
for (const action of this.alepha.primitives($action)) {
|
|
30
33
|
// -------------------------------------------------------------------------------------------------------------
|
|
31
|
-
//
|
|
34
|
+
// Only create permission when secure is explicitly set to true
|
|
35
|
+
// Actions are public by default (like $route)
|
|
32
36
|
// -------------------------------------------------------------------------------------------------------------
|
|
33
37
|
if (
|
|
34
38
|
action.options.disabled ||
|
|
35
|
-
action.options.secure
|
|
39
|
+
action.options.secure !== true ||
|
|
36
40
|
this.securityProvider.getRealms().length === 0
|
|
37
41
|
) {
|
|
38
42
|
continue;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
path: action.route.path,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
45
|
+
this.securityProvider.createPermission({
|
|
46
|
+
name: action.name,
|
|
47
|
+
group: action.group,
|
|
48
|
+
method: action.route.method,
|
|
49
|
+
path: action.route.path,
|
|
50
|
+
});
|
|
50
51
|
}
|
|
51
52
|
},
|
|
52
53
|
});
|
|
@@ -56,10 +57,12 @@ export class ServerSecurityProvider {
|
|
|
56
57
|
protected readonly onActionRequest = $hook({
|
|
57
58
|
on: "action:onRequest",
|
|
58
59
|
handler: async ({ action, request, options }) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
|
|
60
|
+
const secure = action.options.secure;
|
|
61
|
+
|
|
62
|
+
// Skip security if not explicitly enabled (secure: true or secure: { realm: ... })
|
|
63
|
+
// Actions are public by default (like $route)
|
|
64
|
+
if (secure !== true && typeof secure !== "object" && !options.user) {
|
|
65
|
+
this.log.trace("Skipping security check for action - not secured");
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -90,7 +93,7 @@ export class ServerSecurityProvider {
|
|
|
90
93
|
this.alepha.codec.decode(userAccountInfoSchema, request.user),
|
|
91
94
|
);
|
|
92
95
|
} catch (error) {
|
|
93
|
-
if (
|
|
96
|
+
if (secure === true || typeof secure === "object" || permission) {
|
|
94
97
|
throw error;
|
|
95
98
|
}
|
|
96
99
|
// else, we skip the security check
|
|
@@ -103,7 +106,7 @@ export class ServerSecurityProvider {
|
|
|
103
106
|
on: "server:onRequest",
|
|
104
107
|
priority: "last",
|
|
105
108
|
handler: async ({ request, route }) => {
|
|
106
|
-
//
|
|
109
|
+
// Skip entirely only if explicitly disabled
|
|
107
110
|
if (route.secure === false) {
|
|
108
111
|
this.log.trace(
|
|
109
112
|
"Skipping security check for route - explicitly disabled",
|
|
@@ -119,20 +122,39 @@ export class ServerSecurityProvider {
|
|
|
119
122
|
.getPermissions()
|
|
120
123
|
.find((it) => it.path === route.path && it.method === route.method);
|
|
121
124
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
"Skipping security check for route - no authorization header and not secure",
|
|
125
|
-
);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
125
|
+
const realm =
|
|
126
|
+
typeof route.secure === "object" ? route.secure.realm : undefined;
|
|
128
127
|
|
|
129
128
|
try {
|
|
130
|
-
//
|
|
131
|
-
request.user = await this.securityProvider.
|
|
132
|
-
request
|
|
133
|
-
{ permission },
|
|
129
|
+
// Try to resolve user (JWT, API key, etc.) - even for public routes (optional auth)
|
|
130
|
+
request.user = await this.securityProvider.resolveUserFromServerRequest(
|
|
131
|
+
request,
|
|
132
|
+
{ permission, realm },
|
|
134
133
|
);
|
|
135
134
|
|
|
135
|
+
// No user resolved?
|
|
136
|
+
if (!request.user) {
|
|
137
|
+
// Route requires auth → throw
|
|
138
|
+
if (
|
|
139
|
+
route.secure === true ||
|
|
140
|
+
typeof route.secure === "object" ||
|
|
141
|
+
permission
|
|
142
|
+
) {
|
|
143
|
+
// Provide a more specific error message when no auth header was provided
|
|
144
|
+
if (!request.headers.authorization) {
|
|
145
|
+
throw new InvalidTokenError(
|
|
146
|
+
"Invalid authorization header, maybe token is missing ?",
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
throw new UnauthorizedError("Authentication required");
|
|
150
|
+
}
|
|
151
|
+
// Route is public → skip (but we tried to resolve user for optional auth)
|
|
152
|
+
this.log.trace(
|
|
153
|
+
"Skipping security check for route - no auth provided and not required",
|
|
154
|
+
);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
136
158
|
if (typeof route.secure === "object") {
|
|
137
159
|
this.check(request.user, route.secure);
|
|
138
160
|
}
|
|
@@ -143,16 +165,20 @@ export class ServerSecurityProvider {
|
|
|
143
165
|
this.alepha.codec.decode(userAccountInfoSchema, request.user),
|
|
144
166
|
);
|
|
145
167
|
|
|
146
|
-
this.log.trace("User set from request
|
|
168
|
+
this.log.trace("User set from request", {
|
|
147
169
|
user: request.user,
|
|
148
170
|
permission,
|
|
149
171
|
});
|
|
150
172
|
} catch (error) {
|
|
151
|
-
if (
|
|
173
|
+
if (
|
|
174
|
+
route.secure === true ||
|
|
175
|
+
typeof route.secure === "object" ||
|
|
176
|
+
permission
|
|
177
|
+
) {
|
|
152
178
|
throw error;
|
|
153
179
|
}
|
|
154
180
|
|
|
155
|
-
// else, we skip the security check
|
|
181
|
+
// else, we skip the security check (route is public)
|
|
156
182
|
this.log.trace(
|
|
157
183
|
"Skipping security check for route - error occurred",
|
|
158
184
|
error,
|
|
@@ -209,19 +235,10 @@ export class ServerSecurityProvider {
|
|
|
209
235
|
}
|
|
210
236
|
|
|
211
237
|
if (!user) {
|
|
212
|
-
// in testing mode, we create a test user
|
|
213
|
-
if (this.alepha.isTest() && !("user" in options)) {
|
|
214
|
-
return this.createTestUser();
|
|
215
|
-
}
|
|
216
|
-
|
|
217
238
|
throw new UnauthorizedError("User is required for calling this action");
|
|
218
239
|
}
|
|
219
240
|
|
|
220
|
-
const roles =
|
|
221
|
-
user.roles ??
|
|
222
|
-
(this.alepha.isTest()
|
|
223
|
-
? this.securityProvider.getRoles().map((role) => role.name)
|
|
224
|
-
: []);
|
|
241
|
+
const roles = user.roles ?? [];
|
|
225
242
|
let ownership: boolean | string | undefined;
|
|
226
243
|
|
|
227
244
|
if (permission) {
|
|
@@ -264,7 +281,8 @@ export class ServerSecurityProvider {
|
|
|
264
281
|
}
|
|
265
282
|
|
|
266
283
|
// skip helper if user is explicitly set to undefined
|
|
267
|
-
if ("user" in options && options.user === undefined) {
|
|
284
|
+
//if ("user" in options && options.user === undefined) {
|
|
285
|
+
if (!options.user) {
|
|
268
286
|
return;
|
|
269
287
|
}
|
|
270
288
|
|
|
@@ -295,3 +313,7 @@ export type ServerRouteSecure = {
|
|
|
295
313
|
realm?: string;
|
|
296
314
|
basic?: BasicAuthOptions;
|
|
297
315
|
};
|
|
316
|
+
|
|
317
|
+
export type ServerSecurityUserResolver = (
|
|
318
|
+
request: ServerRequest,
|
|
319
|
+
) => Promise<UserAccountToken | undefined>;
|
package/src/server/auth/index.ts
CHANGED
|
@@ -29,16 +29,21 @@ declare module "alepha" {
|
|
|
29
29
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* | type | quality | stability |
|
|
33
|
+
* |------|---------|-----------|
|
|
34
|
+
* | backend | rare | stable |
|
|
34
35
|
*
|
|
35
|
-
*
|
|
36
|
-
* You can also delegate authentication to your own OIDC/OAuth2, for example using Keycloak or Auth0.
|
|
36
|
+
* OAuth2/OIDC authentication with social login providers.
|
|
37
37
|
*
|
|
38
|
-
*
|
|
38
|
+
* **Features:**
|
|
39
|
+
* - OAuth authentication provider
|
|
40
|
+
* - Username/password authentication
|
|
41
|
+
* - Google OAuth integration
|
|
42
|
+
* - GitHub OAuth integration
|
|
43
|
+
* - Apple OAuth integration
|
|
44
|
+
* - Cookie-based, SSR-friendly authentication
|
|
45
|
+
* - Token management and refresh
|
|
39
46
|
*
|
|
40
|
-
* @see {@link $auth}
|
|
41
|
-
* @see {@link ServerAuthProvider}
|
|
42
47
|
* @module alepha.server.auth
|
|
43
48
|
*/
|
|
44
49
|
export const AlephaServerAuth = $module({
|
|
@@ -9,31 +9,16 @@ export * from "./providers/ServerCacheProvider.ts";
|
|
|
9
9
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* | type | quality | stability |
|
|
13
|
+
* |------|---------|-----------|
|
|
14
|
+
* | backend | standard | stable |
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* ```ts
|
|
18
|
-
* import { Alepha } from "alepha";
|
|
19
|
-
* import { $action } from "alepha/server";
|
|
20
|
-
* import { AlephaServerCache } from "alepha/server/cache";
|
|
16
|
+
* ETag-based response caching.
|
|
21
17
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* handler: () => "Hello, World!",
|
|
26
|
-
* });
|
|
27
|
-
* }
|
|
18
|
+
* **Features:**
|
|
19
|
+
* - ETag generation and validation
|
|
20
|
+
* - Conditional request handling
|
|
28
21
|
*
|
|
29
|
-
* const alepha = Alepha.create()
|
|
30
|
-
* .with(AlephaServerCache)
|
|
31
|
-
* .with(ApiServer);
|
|
32
|
-
*
|
|
33
|
-
* run(alepha);
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
|
-
* @see {@link ServerCacheProvider}
|
|
37
22
|
* @module alepha.server.cache
|
|
38
23
|
*/
|
|
39
24
|
export const AlephaServerCache = $module({
|
|
@@ -9,9 +9,17 @@ export * from "./providers/ServerCompressProvider.ts";
|
|
|
9
9
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* | type | quality | stability |
|
|
13
|
+
* |------|---------|-----------|
|
|
14
|
+
* | backend | standard | stable |
|
|
13
15
|
*
|
|
14
|
-
*
|
|
16
|
+
* Response compression.
|
|
17
|
+
*
|
|
18
|
+
* **Features:**
|
|
19
|
+
* - Gzip compression
|
|
20
|
+
* - Brotli compression
|
|
21
|
+
*
|
|
22
|
+
* @module alepha.server.compress
|
|
15
23
|
*/
|
|
16
24
|
export const AlephaServerCompress = $module({
|
|
17
25
|
name: "alepha.server.compress",
|
|
@@ -18,13 +18,15 @@ declare module "alepha/server" {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* | type | quality | stability |
|
|
22
|
+
* |------|---------|-----------|
|
|
23
|
+
* | backend | standard | stable |
|
|
22
24
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
25
|
+
* Server and browser-safe cookie handling.
|
|
26
|
+
*
|
|
27
|
+
* **Features:**
|
|
28
|
+
* - Cookie management on server and browser
|
|
26
29
|
*
|
|
27
|
-
* @see {@link $cookie}
|
|
28
30
|
* @module alepha.server.cookies
|
|
29
31
|
*/
|
|
30
32
|
export const AlephaServerCookies = $module({
|
|
@@ -23,37 +23,59 @@ export const $cookie = <T extends TSchema>(
|
|
|
23
23
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
24
24
|
|
|
25
25
|
export interface CookiePrimitiveOptions<T extends TSchema> {
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The schema for the cookie's value, used for validation and type safety.
|
|
28
|
+
*/
|
|
27
29
|
schema: T;
|
|
28
30
|
|
|
29
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* The name of the cookie.
|
|
33
|
+
*/
|
|
30
34
|
name?: string;
|
|
31
35
|
|
|
32
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* The cookie's path. Defaults to "/".
|
|
38
|
+
*/
|
|
33
39
|
path?: string;
|
|
34
40
|
|
|
35
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Time-to-live for the cookie. Maps to `Max-Age`.
|
|
43
|
+
*/
|
|
36
44
|
ttl?: DurationLike;
|
|
37
45
|
|
|
38
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* If true, the cookie is only sent over HTTPS. Defaults to true in production.
|
|
48
|
+
*/
|
|
39
49
|
secure?: boolean;
|
|
40
50
|
|
|
41
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* If true, the cookie cannot be accessed by client-side scripts.
|
|
53
|
+
*/
|
|
42
54
|
httpOnly?: boolean;
|
|
43
55
|
|
|
44
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* SameSite policy for the cookie. Defaults to "lax".
|
|
58
|
+
*/
|
|
45
59
|
sameSite?: "strict" | "lax" | "none";
|
|
46
60
|
|
|
47
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* The domain for the cookie.
|
|
63
|
+
*/
|
|
48
64
|
domain?: string;
|
|
49
65
|
|
|
50
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* If true, the cookie value will be compressed using zlib.
|
|
68
|
+
*/
|
|
51
69
|
compress?: boolean;
|
|
52
70
|
|
|
53
|
-
/**
|
|
71
|
+
/**
|
|
72
|
+
* If true, the cookie value will be encrypted. Requires `COOKIE_SECRET` env var.
|
|
73
|
+
*/
|
|
54
74
|
encrypt?: boolean;
|
|
55
75
|
|
|
56
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* If true, the cookie will be signed to prevent tampering. Requires `COOKIE_SECRET` env var.
|
|
78
|
+
*/
|
|
57
79
|
sign?: boolean;
|
|
58
80
|
}
|
|
59
81
|
|