@getstrata/bootstrap 0.2.22 → 0.2.25
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/dist/bootstrap/applicationRegistry.d.ts +1 -1
- package/dist/bootstrap/buildModuleRoutes.d.ts +10 -0
- package/dist/bootstrap/buildWebModuleRoutes.d.ts +9 -0
- package/dist/bootstrap/config.d.ts +1 -1
- package/dist/bootstrap/contracts.d.ts +4 -4
- package/dist/bootstrap/env.d.ts +1 -1
- package/dist/bootstrap/membershipService.d.ts +1 -1
- package/dist/bootstrap/public-api.d.ts +6 -2
- package/dist/bootstrap/routeRegistry.d.ts +1 -1
- package/dist/entries/applicationRegistry.js +12 -115
- package/dist/entries/buildModuleRoutes.js +411 -0
- package/dist/entries/buildWebModuleRoutes.js +435 -0
- package/dist/entries/config.js +9 -9
- package/dist/entries/context.js +112 -192
- package/dist/entries/contracts.js +10 -85
- package/dist/entries/createWebRoutes.js +100 -682
- package/dist/entries/dependencies.js +112 -192
- package/dist/entries/http/securedRouteModelBinding.js +0 -37
- package/dist/entries/httpKernel.js +9 -9
- package/dist/entries/membershipService.js +2 -426
- package/dist/entries/providers/view.js +9 -20
- package/dist/entries/providers.js +102 -129
- package/dist/entries/queue/defaultJobs.js +0 -37
- package/dist/entries/web/routing.js +16 -44
- package/dist/index.js +507 -730
- package/package.json +14 -4
- package/dist/cli/commands/scheduleRun.d.ts +0 -3
- package/dist/config/app.d.ts +0 -10
- package/dist/config/auth.d.ts +0 -7
- package/dist/config/contentSecurityPolicy.d.ts +0 -5
- package/dist/config/cors.d.ts +0 -9
- package/dist/config/database.d.ts +0 -10
- package/dist/config/features.d.ts +0 -18
- package/dist/config/frontend.d.ts +0 -6
- package/dist/config/queue.d.ts +0 -8
- package/dist/config/rateLimit.d.ts +0 -10
- package/dist/config/uploads.d.ts +0 -5
- package/dist/core/admin/formatValue.d.ts +0 -3
- package/dist/core/admin/index.d.ts +0 -3
- package/dist/core/admin/registry.d.ts +0 -11
- package/dist/core/admin/types.d.ts +0 -24
- package/dist/core/audit/exportAuditLogs.d.ts +0 -8
- package/dist/core/audit/siemFormatter.d.ts +0 -30
- package/dist/core/auth/abilityChecker.d.ts +0 -6
- package/dist/core/auth/accessControl.d.ts +0 -8
- package/dist/core/auth/authContext.d.ts +0 -11
- package/dist/core/auth/guard.d.ts +0 -36
- package/dist/core/auth/membershipContext.d.ts +0 -15
- package/dist/core/auth/membershipContextMiddleware.d.ts +0 -3
- package/dist/core/auth/membershipMiddleware.d.ts +0 -2
- package/dist/core/auth/membershipScope.d.ts +0 -16
- package/dist/core/auth/membershipService.d.ts +0 -23
- package/dist/core/auth/oauth/oidcProvider.d.ts +0 -18
- package/dist/core/auth/oauth/providers.d.ts +0 -22
- package/dist/core/auth/oauth/samlProvider.d.ts +0 -9
- package/dist/core/auth/oauth/types.d.ts +0 -11
- package/dist/core/auth/password.d.ts +0 -3
- package/dist/core/auth/policy.d.ts +0 -15
- package/dist/core/auth/resolveMembershipService.d.ts +0 -3
- package/dist/core/auth/scimAuthMiddleware.d.ts +0 -3
- package/dist/core/auth/sessionCookie.d.ts +0 -6
- package/dist/core/auth/sessionGuard.d.ts +0 -9
- package/dist/core/auth/tokenHash.d.ts +0 -3
- package/dist/core/cache/createCacheStore.d.ts +0 -11
- package/dist/core/cache/redisCacheStore.d.ts +0 -23
- package/dist/core/cache/repository.d.ts +0 -17
- package/dist/core/cache/simpleCache.d.ts +0 -23
- package/dist/core/cache/simpleCacheStore.d.ts +0 -16
- package/dist/core/cache/store.d.ts +0 -12
- package/dist/core/cache/taggedCache.d.ts +0 -9
- package/dist/core/cache/tags.d.ts +0 -11
- package/dist/core/config/envSchema.d.ts +0 -12
- package/dist/core/contracts/container.d.ts +0 -30
- package/dist/core/contracts/di.d.ts +0 -31
- package/dist/core/contracts/serviceContainer.d.ts +0 -1
- package/dist/core/contracts/serviceTokens.d.ts +0 -8
- package/dist/core/crypto/fieldEncryption.d.ts +0 -13
- package/dist/core/crypto/mfaSecret.d.ts +0 -3
- package/dist/core/crypto/nonCryptographicHash.d.ts +0 -2
- package/dist/core/database/baseRepository.d.ts +0 -67
- package/dist/core/database/bindConnection.d.ts +0 -3
- package/dist/core/database/boundConnection.d.ts +0 -5
- package/dist/core/database/connection.d.ts +0 -7
- package/dist/core/database/connectionContext.d.ts +0 -8
- package/dist/core/database/defaultConnection.d.ts +0 -6
- package/dist/core/database/errors.d.ts +0 -12
- package/dist/core/database/index.d.ts +0 -18
- package/dist/core/database/migrations/advisoryLock.d.ts +0 -6
- package/dist/core/database/migrations/runner.d.ts +0 -25
- package/dist/core/database/migrations/types.d.ts +0 -15
- package/dist/core/database/model.d.ts +0 -57
- package/dist/core/database/query.d.ts +0 -63
- package/dist/core/database/queryProxy.d.ts +0 -3
- package/dist/core/database/relationships.d.ts +0 -100
- package/dist/core/database/repositoryConnection.d.ts +0 -4
- package/dist/core/database/repositoryQuery.d.ts +0 -38
- package/dist/core/database/schema/blueprint.d.ts +0 -47
- package/dist/core/database/schema/columnDefinition.d.ts +0 -36
- package/dist/core/database/schema/driver.d.ts +0 -8
- package/dist/core/database/schema/errors.d.ts +0 -4
- package/dist/core/database/schema/grammars/compileStatements.d.ts +0 -8
- package/dist/core/database/schema/grammars/createGrammar.d.ts +0 -4
- package/dist/core/database/schema/grammars/grammar.d.ts +0 -10
- package/dist/core/database/schema/grammars/index.d.ts +0 -7
- package/dist/core/database/schema/grammars/mysqlGrammar.d.ts +0 -2
- package/dist/core/database/schema/grammars/postgresGrammar.d.ts +0 -2
- package/dist/core/database/schema/grammars/sqliteGrammar.d.ts +0 -2
- package/dist/core/database/schema/index.d.ts +0 -12
- package/dist/core/database/schema/schema.d.ts +0 -21
- package/dist/core/database/seeders/runner.d.ts +0 -6
- package/dist/core/database/seeders/types.d.ts +0 -8
- package/dist/core/database/table.d.ts +0 -13
- package/dist/core/database/transaction.d.ts +0 -3
- package/dist/core/database/types.d.ts +0 -66
- package/dist/core/database/whereBuilder.d.ts +0 -17
- package/dist/core/errors/http.d.ts +0 -33
- package/dist/core/events/eventBus.d.ts +0 -10
- package/dist/core/events/index.d.ts +0 -4
- package/dist/core/facades/index.d.ts +0 -10
- package/dist/core/http/authMiddleware.d.ts +0 -5
- package/dist/core/http/authorizeMiddleware.d.ts +0 -5
- package/dist/core/http/bodySizeLimitMiddleware.d.ts +0 -4
- package/dist/core/http/conditionalResponse.d.ts +0 -2
- package/dist/core/http/contentNegotiation.d.ts +0 -2
- package/dist/core/http/cookies.d.ts +0 -4
- package/dist/core/http/corsMiddleware.d.ts +0 -3
- package/dist/core/http/csrfMiddleware.d.ts +0 -3
- package/dist/core/http/csrfProtection.d.ts +0 -12
- package/dist/core/http/csrfToken.d.ts +0 -14
- package/dist/core/http/etag.d.ts +0 -18
- package/dist/core/http/flashMiddleware.d.ts +0 -3
- package/dist/core/http/flashSession.d.ts +0 -12
- package/dist/core/http/formRequest.d.ts +0 -10
- package/dist/core/http/index.d.ts +0 -27
- package/dist/core/http/loginThrottleMiddleware.d.ts +0 -11
- package/dist/core/http/memoryThrottleMiddleware.d.ts +0 -9
- package/dist/core/http/metricsMiddleware.d.ts +0 -4
- package/dist/core/http/middleware.d.ts +0 -8
- package/dist/core/http/pagination.d.ts +0 -11
- package/dist/core/http/parseFormBody.d.ts +0 -6
- package/dist/core/http/parseMultipartUpload.d.ts +0 -10
- package/dist/core/http/requestMetaContext.d.ts +0 -15
- package/dist/core/http/requireAbilityMiddleware.d.ts +0 -4
- package/dist/core/http/requireAuthMiddleware.d.ts +0 -4
- package/dist/core/http/requireGlobalAdminMiddleware.d.ts +0 -3
- package/dist/core/http/requireWebAuthMiddleware.d.ts +0 -4
- package/dist/core/http/resources.d.ts +0 -7
- package/dist/core/http/route.d.ts +0 -6
- package/dist/core/http/routeMiddleware.d.ts +0 -3
- package/dist/core/http/routeModelBinding.d.ts +0 -3
- package/dist/core/http/scimThrottleMiddleware.d.ts +0 -9
- package/dist/core/http/securedRouteModelBinding.d.ts +0 -11
- package/dist/core/http/securityHeadersMiddleware.d.ts +0 -3
- package/dist/core/http/throttleMiddleware.d.ts +0 -11
- package/dist/core/http/validation.d.ts +0 -23
- package/dist/core/http/webErrorResponse.d.ts +0 -5
- package/dist/core/http/webFormRequest.d.ts +0 -7
- package/dist/core/jobs/dispatchWebhookJob.d.ts +0 -13
- package/dist/core/jobs/invalidateCacheTagsJob.d.ts +0 -12
- package/dist/core/lifecycle/gracefulShutdown.d.ts +0 -6
- package/dist/core/logging/logger.d.ts +0 -17
- package/dist/core/logging/requestLoggingMiddleware.d.ts +0 -3
- package/dist/core/mail/mailer.d.ts +0 -38
- package/dist/core/mail/markdownMail.d.ts +0 -15
- package/dist/core/mail/markdownMailable.d.ts +0 -12
- package/dist/core/metrics/prometheus.d.ts +0 -26
- package/dist/core/notifications/dispatcher.d.ts +0 -13
- package/dist/core/notifications/index.d.ts +0 -3
- package/dist/core/notifications/notification.d.ts +0 -7
- package/dist/core/notifications/types.d.ts +0 -25
- package/dist/core/openapi/registeredRoute.d.ts +0 -6
- package/dist/core/pagination/index.d.ts +0 -27
- package/dist/core/queue/createAppQueue.d.ts +0 -5
- package/dist/core/queue/failedJobRepository.d.ts +0 -6
- package/dist/core/queue/failedJobService.d.ts +0 -16
- package/dist/core/queue/failedJobTable.d.ts +0 -3
- package/dist/core/queue/index.d.ts +0 -19
- package/dist/core/queue/jobRegistry.d.ts +0 -13
- package/dist/core/queue/jobRunner.d.ts +0 -9
- package/dist/core/queue/publicQueue.d.ts +0 -16
- package/dist/core/queue/queueMetrics.d.ts +0 -15
- package/dist/core/queue/redisQueue.d.ts +0 -29
- package/dist/core/queue/resilientQueue.d.ts +0 -9
- package/dist/core/queue/types.d.ts +0 -8
- package/dist/core/runtime/applicationRegistry.d.ts +0 -17
- package/dist/core/runtime/asyncContextStore.d.ts +0 -3
- package/dist/core/scheduler/schedule.d.ts +0 -15
- package/dist/core/security/publicReads.d.ts +0 -3
- package/dist/core/security/safeFetch.d.ts +0 -10
- package/dist/core/security/safeUrl.d.ts +0 -20
- package/dist/core/security/scimTenantTokens.d.ts +0 -3
- package/dist/core/security/securityEvents.d.ts +0 -6
- package/dist/core/security/timingSafeCompare.d.ts +0 -2
- package/dist/core/security/tokenExpiry.d.ts +0 -2
- package/dist/core/security/totp.d.ts +0 -3
- package/dist/core/storage/storage.d.ts +0 -44
- package/dist/core/tenant/databaseTenantContext.d.ts +0 -2
- package/dist/core/tenant/resolveTenant.d.ts +0 -3
- package/dist/core/tenant/tenantContext.d.ts +0 -13
- package/dist/core/tenant/tenantDatabaseScope.d.ts +0 -8
- package/dist/core/tenant/tenantMiddleware.d.ts +0 -6
- package/dist/core/tracing/otel.d.ts +0 -26
- package/dist/core/tracing/traceContext.d.ts +0 -9
- package/dist/core/tracing/tracingMiddleware.d.ts +0 -3
- package/dist/core/validation/rules.d.ts +0 -17
- package/dist/core/view/etaViewEngine.d.ts +0 -15
- package/dist/core/view/htmlResponse.d.ts +0 -6
- package/dist/core/view/index.d.ts +0 -5
- package/dist/core/view/viewEngine.d.ts +0 -6
- package/dist/core/view/webLayoutData.d.ts +0 -17
- package/dist/db/connection/createConnection.d.ts +0 -6
- package/dist/db/connection/index.d.ts +0 -11
- package/dist/domain/abilities.d.ts +0 -5
- package/dist/domain/auth.d.ts +0 -4
- package/dist/domain/scim.d.ts +0 -9
- package/dist/domain/workhub.d.ts +0 -35
- package/dist/framework/public-api.d.ts +0 -116
- package/dist/modules/organization/memberRepository.d.ts +0 -14
- package/dist/modules/organization/memberTypes.d.ts +0 -9
- package/dist/modules/organization/repository.d.ts +0 -14
- package/dist/modules/organization/table.d.ts +0 -3
- package/dist/modules/organization/types.d.ts +0 -10
- package/dist/modules/scim/controller.d.ts +0 -16
- package/dist/modules/scim/scimResponse.d.ts +0 -10
- package/dist/modules/scim/service.d.ts +0 -214
- package/dist/modules/user/apiTokenRepository.d.ts +0 -8
- package/dist/modules/user/apiTokenTable.d.ts +0 -3
- package/dist/modules/user/authService.d.ts +0 -23
- package/dist/modules/user/notificationRepository.d.ts +0 -6
- package/dist/modules/user/notificationService.d.ts +0 -25
- package/dist/modules/user/notificationTable.d.ts +0 -3
- package/dist/modules/user/notificationTypes.d.ts +0 -12
- package/dist/modules/user/oauthIdentityRepository.d.ts +0 -9
- package/dist/modules/user/provider.d.ts +0 -11
- package/dist/modules/user/repository.d.ts +0 -13
- package/dist/modules/user/table.d.ts +0 -3
- package/dist/modules/user/tokenService.d.ts +0 -29
- package/dist/modules/user/types.d.ts +0 -46
- package/dist/types/services.d.ts +0 -16
package/dist/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// ../../src/bootstrap/public-api.ts
|
|
3
|
+
import { appSchedule as appSchedule2, runDueScheduledTasks as runDueScheduledTasks2, Schedule as Schedule2 } from "@getstrata/core/scheduler/schedule";
|
|
4
|
+
|
|
2
5
|
// ../../src/core/scheduler/schedule.ts
|
|
3
6
|
class Schedule {
|
|
4
7
|
tasks = [];
|
|
@@ -503,157 +506,107 @@ async function scheduleRunCommand() {
|
|
|
503
506
|
}
|
|
504
507
|
await runDueScheduledTasks(appSchedule);
|
|
505
508
|
}
|
|
506
|
-
// ../../src/
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
getRequiredDependency(dependencies, key);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
function resolveService(dependencies, token) {
|
|
525
|
-
return dependencies.container.resolve(token);
|
|
526
|
-
}
|
|
509
|
+
// ../../src/bootstrap/applicationRegistry.ts
|
|
510
|
+
import {
|
|
511
|
+
resolveApplicationAuth,
|
|
512
|
+
resolveApplicationCache,
|
|
513
|
+
resolveApplicationConfig,
|
|
514
|
+
resolveApplicationDependencies,
|
|
515
|
+
resolveApplicationEventBus,
|
|
516
|
+
resolveApplicationLogger,
|
|
517
|
+
resolveApplicationPolicyGate,
|
|
518
|
+
resolveApplicationQueue,
|
|
519
|
+
setActiveApplicationContext
|
|
520
|
+
} from "@getstrata/core/runtime/applicationRegistry.ts";
|
|
521
|
+
// ../../src/bootstrap/buildModuleRoutes.ts
|
|
522
|
+
import { conditionalJsonResponse } from "@getstrata/core/http";
|
|
523
|
+
import { applyMiddlewareToRoutes } from "@getstrata/core/http/middleware";
|
|
527
524
|
|
|
528
|
-
// ../../src/
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
525
|
+
// ../../src/bootstrap/httpKernel.ts
|
|
526
|
+
import {
|
|
527
|
+
createAuthMiddleware,
|
|
528
|
+
createAuthorizeMiddleware,
|
|
529
|
+
createBodySizeLimitMiddleware,
|
|
530
|
+
createCorsMiddleware,
|
|
531
|
+
createCsrfMiddleware,
|
|
532
|
+
createFlashMiddleware,
|
|
533
|
+
createLoginThrottleMiddleware,
|
|
534
|
+
createMembershipMiddleware,
|
|
535
|
+
createMemoryThrottleMiddleware,
|
|
536
|
+
createMetricsMiddleware,
|
|
537
|
+
createRequestLoggingMiddleware,
|
|
538
|
+
createRequireAbilityMiddleware,
|
|
539
|
+
createRequireAuthMiddleware,
|
|
540
|
+
createRequireGlobalAdminMiddleware,
|
|
541
|
+
createRequireWebAuthMiddleware,
|
|
542
|
+
createSecurityHeadersMiddleware,
|
|
543
|
+
createTenantMiddleware,
|
|
544
|
+
createThrottleMiddleware,
|
|
545
|
+
createTracingMiddleware,
|
|
546
|
+
isPublicReadsEnabled,
|
|
547
|
+
requestIdMiddleware,
|
|
548
|
+
withMiddleware
|
|
549
|
+
} from "@getstrata/core";
|
|
536
550
|
|
|
537
|
-
// ../../src/
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
return activeContext;
|
|
543
|
-
}
|
|
544
|
-
const globalContext = globalThis[APPLICATION_CONTEXT_KEY];
|
|
545
|
-
if (globalContext) {
|
|
546
|
-
activeContext = globalContext;
|
|
547
|
-
}
|
|
548
|
-
return activeContext;
|
|
549
|
-
}
|
|
550
|
-
function setActiveApplicationContext(context) {
|
|
551
|
-
activeContext = context;
|
|
552
|
-
globalThis[APPLICATION_CONTEXT_KEY] = context;
|
|
553
|
-
}
|
|
554
|
-
function requireActiveApplicationContext() {
|
|
555
|
-
const context = readStoredApplicationContext();
|
|
556
|
-
if (!context) {
|
|
557
|
-
throw new Error("The application context has not been bootstrapped.");
|
|
551
|
+
// ../../src/config/frontend.ts
|
|
552
|
+
function readFrontendMode() {
|
|
553
|
+
const mode = (process.env.FRONTEND_MODE ?? "api").trim();
|
|
554
|
+
if (mode === "server-htmx") {
|
|
555
|
+
return "server-htmx";
|
|
558
556
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
function resolveApplicationCache() {
|
|
562
|
-
return getRequiredDependency(requireActiveApplicationContext().dependencies, "cache");
|
|
563
|
-
}
|
|
564
|
-
function resolveApplicationQueue() {
|
|
565
|
-
return requireActiveApplicationContext().container.resolve(CORE_QUEUE_TOKEN);
|
|
566
|
-
}
|
|
567
|
-
function resolveApplicationEventBus() {
|
|
568
|
-
return requireActiveApplicationContext().container.resolve(CORE_EVENT_BUS_TOKEN);
|
|
569
|
-
}
|
|
570
|
-
function resolveApplicationAuth() {
|
|
571
|
-
return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN);
|
|
572
|
-
}
|
|
573
|
-
function resolveApplicationPolicyGate() {
|
|
574
|
-
return requireActiveApplicationContext().container.resolve(CORE_POLICY_GATE_TOKEN);
|
|
575
|
-
}
|
|
576
|
-
function resolveApplicationConfig() {
|
|
577
|
-
return requireActiveApplicationContext().config;
|
|
578
|
-
}
|
|
579
|
-
function resolveApplicationLogger() {
|
|
580
|
-
return appLogger;
|
|
581
|
-
}
|
|
582
|
-
function resolveApplicationDependencies() {
|
|
583
|
-
return requireActiveApplicationContext().dependencies;
|
|
584
|
-
}
|
|
585
|
-
// ../../src/bootstrap/discoverModules.ts
|
|
586
|
-
import { readdirSync } from "fs";
|
|
587
|
-
import { join } from "path";
|
|
588
|
-
import { pathToFileURL } from "url";
|
|
589
|
-
var DISCOVER_MODULES_STATE_KEY = Symbol.for("@getstrata/discoverModulesState");
|
|
590
|
-
function readDiscoverModulesState() {
|
|
591
|
-
const existing = globalThis[DISCOVER_MODULES_STATE_KEY];
|
|
592
|
-
if (existing) {
|
|
593
|
-
return existing;
|
|
557
|
+
if (mode === "spa-react") {
|
|
558
|
+
return "spa-react";
|
|
594
559
|
}
|
|
595
|
-
|
|
596
|
-
globalThis[DISCOVER_MODULES_STATE_KEY] = state;
|
|
597
|
-
return state;
|
|
560
|
+
return "api";
|
|
598
561
|
}
|
|
599
|
-
function
|
|
600
|
-
|
|
562
|
+
function isViewsEnabled() {
|
|
563
|
+
return readFrontendMode() === "server-htmx";
|
|
601
564
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
565
|
+
|
|
566
|
+
// ../../src/config/rateLimit.ts
|
|
567
|
+
var LOCAL_LOGIN_RATE_LIMIT = {
|
|
568
|
+
maxAttempts: 100,
|
|
569
|
+
decaySeconds: 60
|
|
570
|
+
};
|
|
571
|
+
var PRODUCTION_LOGIN_RATE_LIMIT = {
|
|
572
|
+
maxAttempts: 5,
|
|
573
|
+
decaySeconds: 900
|
|
574
|
+
};
|
|
575
|
+
function isLocalAppEnv() {
|
|
576
|
+
return (process.env.APP_ENV ?? "local") === "local";
|
|
611
577
|
}
|
|
612
|
-
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
moduleNames = readdirSync(modulesDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
617
|
-
} catch (error) {
|
|
618
|
-
if (error.code === "ENOENT") {
|
|
619
|
-
return [];
|
|
620
|
-
}
|
|
621
|
-
throw error;
|
|
578
|
+
function parsePositiveInt(value, fallback) {
|
|
579
|
+
const parsed = Number(value);
|
|
580
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
581
|
+
return fallback;
|
|
622
582
|
}
|
|
623
|
-
|
|
624
|
-
const moduleUrl = pathToFileURL(join(modulesDirectory, moduleName, "index.ts")).href;
|
|
625
|
-
const loaded = await import(moduleUrl);
|
|
626
|
-
return loaded.default;
|
|
627
|
-
}));
|
|
628
|
-
return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
|
|
583
|
+
return Math.trunc(parsed);
|
|
629
584
|
}
|
|
630
|
-
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
state.appModules.splice(0, state.appModules.length, ...modules);
|
|
637
|
-
return state.appModules;
|
|
638
|
-
});
|
|
639
|
-
return state.modulesReady;
|
|
585
|
+
function resolveLoginRateLimit() {
|
|
586
|
+
const defaults = isLocalAppEnv() ? LOCAL_LOGIN_RATE_LIMIT : PRODUCTION_LOGIN_RATE_LIMIT;
|
|
587
|
+
return {
|
|
588
|
+
maxAttempts: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
|
|
589
|
+
decaySeconds: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_WINDOW_SECONDS, defaults.decaySeconds)
|
|
590
|
+
};
|
|
640
591
|
}
|
|
641
|
-
function
|
|
642
|
-
|
|
592
|
+
function resolveRegisterRateLimit() {
|
|
593
|
+
const defaults = isLocalAppEnv() ? { maxAttempts: 100, decaySeconds: 60 } : { maxAttempts: 10, decaySeconds: 60 };
|
|
594
|
+
return {
|
|
595
|
+
maxAttempts: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
|
|
596
|
+
decaySeconds: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_WINDOW_SECONDS ?? process.env.REGISTER_RATE_LIMIT_WINDOW_MS ? String(Number(process.env.REGISTER_RATE_LIMIT_WINDOW_MS) / 1000) : undefined, defaults.decaySeconds)
|
|
597
|
+
};
|
|
643
598
|
}
|
|
644
599
|
|
|
645
|
-
// ../../src/bootstrap/cache/modelCacheTags.ts
|
|
646
|
-
function cacheTagsForModelWrite(tableName, action) {
|
|
647
|
-
const module = discoverModules().find((entry) => entry.tableName === tableName);
|
|
648
|
-
const baseTags = module?.cacheTags ?? [`${tableName}s`];
|
|
649
|
-
const isDelete = action === "deleted" || action === "force-deleted";
|
|
650
|
-
const extraTags = isDelete ? module?.cacheDeleteExtraTags ?? [] : [];
|
|
651
|
-
return [...new Set([...baseTags, ...extraTags])];
|
|
652
|
-
}
|
|
653
|
-
function discoverModelTableNames() {
|
|
654
|
-
return discoverModules().map((module) => module.tableName).filter((tableName) => tableName !== undefined);
|
|
655
|
-
}
|
|
656
600
|
// ../../src/bootstrap/config.ts
|
|
601
|
+
import {
|
|
602
|
+
CORE_AUTH_TOKEN,
|
|
603
|
+
CORE_CACHE_TOKEN,
|
|
604
|
+
CORE_CONFIG_TOKEN,
|
|
605
|
+
CORE_EVENT_BUS_TOKEN,
|
|
606
|
+
CORE_POLICY_GATE_TOKEN,
|
|
607
|
+
CORE_QUEUE_TOKEN,
|
|
608
|
+
CORE_TOKEN_SERVICE_TOKEN
|
|
609
|
+
} from "@getstrata/core/contracts/serviceTokens.ts";
|
|
657
610
|
var APP_PORT_CONFIG_KEY = "app.port";
|
|
658
611
|
var CACHE_TTL_MS_CONFIG_KEY = "cache.ttlMs";
|
|
659
612
|
var CACHE_MAX_ENTRIES_CONFIG_KEY = "cache.maxEntries";
|
|
@@ -666,70 +619,354 @@ var DEFAULT_CACHE_MAX_ENTRIES = 100;
|
|
|
666
619
|
var DEFAULT_CACHE_DRIVER = "array";
|
|
667
620
|
var DEFAULT_API_TOKEN = "";
|
|
668
621
|
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
this.singletonFactories.delete(key);
|
|
676
|
-
this.bindings.delete(key);
|
|
677
|
-
this.services.set(key, value);
|
|
678
|
-
return value;
|
|
679
|
-
}
|
|
680
|
-
singleton(key, factory) {
|
|
681
|
-
this.bindings.delete(key);
|
|
682
|
-
this.services.delete(key);
|
|
683
|
-
this.singletonFactories.set(key, factory);
|
|
622
|
+
|
|
623
|
+
// ../../src/bootstrap/httpKernel.ts
|
|
624
|
+
class HttpKernel {
|
|
625
|
+
dependencies;
|
|
626
|
+
constructor(dependencies) {
|
|
627
|
+
this.dependencies = dependencies;
|
|
684
628
|
}
|
|
685
|
-
|
|
686
|
-
this.
|
|
687
|
-
|
|
688
|
-
|
|
629
|
+
globalMiddleware() {
|
|
630
|
+
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
631
|
+
return [
|
|
632
|
+
createCorsMiddleware(),
|
|
633
|
+
createSecurityHeadersMiddleware(),
|
|
634
|
+
createBodySizeLimitMiddleware(),
|
|
635
|
+
createTracingMiddleware(),
|
|
636
|
+
createMetricsMiddleware(),
|
|
637
|
+
createRequestLoggingMiddleware(),
|
|
638
|
+
requestIdMiddleware,
|
|
639
|
+
createAuthMiddleware(auth),
|
|
640
|
+
createMembershipMiddleware(),
|
|
641
|
+
createTenantMiddleware()
|
|
642
|
+
];
|
|
689
643
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
644
|
+
group(name) {
|
|
645
|
+
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
646
|
+
switch (name) {
|
|
647
|
+
case "authenticated":
|
|
648
|
+
return [createRequireAuthMiddleware(auth)];
|
|
649
|
+
case "web":
|
|
650
|
+
return isViewsEnabled() ? [createFlashMiddleware(), createCsrfMiddleware()] : [];
|
|
651
|
+
case "api": {
|
|
652
|
+
if (!this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
|
|
653
|
+
return [];
|
|
654
|
+
}
|
|
655
|
+
const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
|
|
656
|
+
const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
|
|
657
|
+
if (!redisUrl) {
|
|
658
|
+
const maxAttempts2 = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
|
|
659
|
+
return [
|
|
660
|
+
createMemoryThrottleMiddleware({
|
|
661
|
+
maxAttempts: Number.isFinite(maxAttempts2) ? maxAttempts2 : 120,
|
|
662
|
+
decaySeconds: 60
|
|
663
|
+
})
|
|
664
|
+
];
|
|
665
|
+
}
|
|
666
|
+
const maxAttempts = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
|
|
667
|
+
return [
|
|
668
|
+
createThrottleMiddleware({
|
|
669
|
+
redisUrl,
|
|
670
|
+
maxAttempts: Number.isFinite(maxAttempts) ? maxAttempts : 120,
|
|
671
|
+
decaySeconds: 60
|
|
672
|
+
})
|
|
673
|
+
];
|
|
674
|
+
}
|
|
675
|
+
default:
|
|
676
|
+
throw new Error(`Unknown middleware group "${name}".`);
|
|
703
677
|
}
|
|
704
|
-
throw new Error(`Service "${key}" is not registered.`);
|
|
705
678
|
}
|
|
706
|
-
|
|
707
|
-
|
|
679
|
+
wrap(groups, handler) {
|
|
680
|
+
const names = Array.isArray(groups) ? groups : [groups];
|
|
681
|
+
const middleware = names.flatMap((name) => this.group(name));
|
|
682
|
+
if (middleware.length === 0) {
|
|
683
|
+
return handler;
|
|
684
|
+
}
|
|
685
|
+
return withMiddleware(...middleware)(handler);
|
|
708
686
|
}
|
|
709
|
-
|
|
710
|
-
return this.
|
|
687
|
+
wrapApi(handler) {
|
|
688
|
+
return this.wrap(["api", "authenticated"], handler);
|
|
711
689
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
class ConfigStore {
|
|
715
|
-
values = new Map;
|
|
716
|
-
set(key, value) {
|
|
717
|
-
this.values.set(key, value);
|
|
718
|
-
return value;
|
|
690
|
+
wrapWeb(handler) {
|
|
691
|
+
return handler;
|
|
719
692
|
}
|
|
720
|
-
|
|
721
|
-
|
|
693
|
+
wrapWebPublicRead(handler) {
|
|
694
|
+
if (isPublicReadsEnabled()) {
|
|
695
|
+
return this.wrapWeb(handler);
|
|
696
|
+
}
|
|
697
|
+
return this.wrapWebAuthenticated(handler);
|
|
698
|
+
}
|
|
699
|
+
wrapWebAuthenticated(handler) {
|
|
700
|
+
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
701
|
+
return withMiddleware(createRequireWebAuthMiddleware(auth))(handler);
|
|
702
|
+
}
|
|
703
|
+
wrapWebAbility(ability, handler) {
|
|
704
|
+
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
705
|
+
const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
|
|
706
|
+
const requireAbility = createRequireAbilityMiddleware(abilityChecker);
|
|
707
|
+
const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
|
|
708
|
+
return withMiddleware(...middleware)(handler);
|
|
709
|
+
}
|
|
710
|
+
wrapWebGlobalAdmin(handler) {
|
|
711
|
+
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
712
|
+
const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
|
|
713
|
+
return withMiddleware(...middleware)(handler);
|
|
714
|
+
}
|
|
715
|
+
wrapAuthenticated(handler) {
|
|
716
|
+
return this.wrap("authenticated", handler);
|
|
717
|
+
}
|
|
718
|
+
wrapPublicRead(handler) {
|
|
719
|
+
if (isPublicReadsEnabled()) {
|
|
720
|
+
return handler;
|
|
721
|
+
}
|
|
722
|
+
return this.wrapAuthenticated(handler);
|
|
723
|
+
}
|
|
724
|
+
wrapGlobalAdmin(handler) {
|
|
725
|
+
const middleware = [...this.group("authenticated"), createRequireGlobalAdminMiddleware()];
|
|
726
|
+
return withMiddleware(...middleware)(handler);
|
|
727
|
+
}
|
|
728
|
+
wrapAbility(ability, handler) {
|
|
729
|
+
const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
|
|
730
|
+
const requireAbility = createRequireAbilityMiddleware(abilityChecker);
|
|
731
|
+
const middleware = [...this.group("authenticated"), requireAbility(ability)];
|
|
732
|
+
return withMiddleware(...middleware)(handler);
|
|
733
|
+
}
|
|
734
|
+
wrapPolicy(resource, action, handler) {
|
|
735
|
+
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
736
|
+
const gate = this.dependencies.container.resolve(CORE_POLICY_GATE_TOKEN);
|
|
737
|
+
return withMiddleware(createAuthorizeMiddleware(gate, auth, resource, action))(handler);
|
|
738
|
+
}
|
|
739
|
+
wrapLogin(handler) {
|
|
740
|
+
return this.wrapThrottle("login", resolveLoginRateLimit(), handler);
|
|
741
|
+
}
|
|
742
|
+
wrapRegister(handler) {
|
|
743
|
+
return this.wrapThrottle("register", resolveRegisterRateLimit(), handler);
|
|
744
|
+
}
|
|
745
|
+
wrapThrottle(scope, rateLimit, handler) {
|
|
746
|
+
const middleware = [];
|
|
747
|
+
const memoryKeyPrefix = scope === "login" ? "login-throttle:" : "register-throttle:";
|
|
748
|
+
if (this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
|
|
749
|
+
const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
|
|
750
|
+
const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
|
|
751
|
+
if (redisUrl) {
|
|
752
|
+
const throttle = scope === "login" ? createLoginThrottleMiddleware({
|
|
753
|
+
redisUrl,
|
|
754
|
+
maxAttempts: rateLimit.maxAttempts,
|
|
755
|
+
decaySeconds: rateLimit.decaySeconds
|
|
756
|
+
}) : createThrottleMiddleware({
|
|
757
|
+
redisUrl,
|
|
758
|
+
maxAttempts: rateLimit.maxAttempts,
|
|
759
|
+
decaySeconds: rateLimit.decaySeconds,
|
|
760
|
+
keyPrefix: memoryKeyPrefix
|
|
761
|
+
});
|
|
762
|
+
middleware.push(throttle);
|
|
763
|
+
} else {
|
|
764
|
+
middleware.push(createMemoryThrottleMiddleware({
|
|
765
|
+
maxAttempts: rateLimit.maxAttempts,
|
|
766
|
+
decaySeconds: rateLimit.decaySeconds,
|
|
767
|
+
keyPrefix: memoryKeyPrefix
|
|
768
|
+
}));
|
|
769
|
+
}
|
|
770
|
+
} else {
|
|
771
|
+
middleware.push(createMemoryThrottleMiddleware({
|
|
772
|
+
maxAttempts: rateLimit.maxAttempts,
|
|
773
|
+
decaySeconds: rateLimit.decaySeconds,
|
|
774
|
+
keyPrefix: memoryKeyPrefix
|
|
775
|
+
}));
|
|
776
|
+
}
|
|
777
|
+
if (middleware.length === 0) {
|
|
778
|
+
return handler;
|
|
779
|
+
}
|
|
780
|
+
return withMiddleware(...middleware)(handler);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
function createHttpKernel(dependencies) {
|
|
784
|
+
return new HttpKernel(dependencies);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// ../../src/bootstrap/discoverModules.ts
|
|
788
|
+
import { readdirSync } from "fs";
|
|
789
|
+
import { join } from "path";
|
|
790
|
+
import { pathToFileURL } from "url";
|
|
791
|
+
var DISCOVER_MODULES_STATE_KEY = Symbol.for("@getstrata/discoverModulesState");
|
|
792
|
+
function readDiscoverModulesState() {
|
|
793
|
+
const existing = globalThis[DISCOVER_MODULES_STATE_KEY];
|
|
794
|
+
if (existing) {
|
|
795
|
+
return existing;
|
|
796
|
+
}
|
|
797
|
+
const state = { appModules: [] };
|
|
798
|
+
globalThis[DISCOVER_MODULES_STATE_KEY] = state;
|
|
799
|
+
return state;
|
|
800
|
+
}
|
|
801
|
+
function configureModulesDirectory(modulesDir) {
|
|
802
|
+
readDiscoverModulesState().configuredModulesDir = modulesDir;
|
|
803
|
+
}
|
|
804
|
+
function resolveModulesDirectory(options) {
|
|
805
|
+
const state = readDiscoverModulesState();
|
|
806
|
+
if (options?.modulesDir) {
|
|
807
|
+
return options.modulesDir;
|
|
808
|
+
}
|
|
809
|
+
if (state.configuredModulesDir) {
|
|
810
|
+
return state.configuredModulesDir;
|
|
811
|
+
}
|
|
812
|
+
return join(import.meta.dir, "../modules");
|
|
813
|
+
}
|
|
814
|
+
async function loadDiscoveredModules(options) {
|
|
815
|
+
const modulesDirectory = resolveModulesDirectory(options);
|
|
816
|
+
let moduleNames;
|
|
817
|
+
try {
|
|
818
|
+
moduleNames = readdirSync(modulesDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
819
|
+
} catch (error) {
|
|
820
|
+
if (error.code === "ENOENT") {
|
|
821
|
+
return [];
|
|
822
|
+
}
|
|
823
|
+
throw error;
|
|
824
|
+
}
|
|
825
|
+
const modules = await Promise.all(moduleNames.map(async (moduleName) => {
|
|
826
|
+
const moduleUrl = pathToFileURL(join(modulesDirectory, moduleName, "index.ts")).href;
|
|
827
|
+
const loaded = await import(moduleUrl);
|
|
828
|
+
return loaded.default;
|
|
829
|
+
}));
|
|
830
|
+
return modules.filter((module) => module?.name !== undefined).sort((left, right) => (left.order ?? 100) - (right.order ?? 100));
|
|
831
|
+
}
|
|
832
|
+
async function ensureModulesLoaded(options) {
|
|
833
|
+
const state = readDiscoverModulesState();
|
|
834
|
+
if (state.appModules.length > 0) {
|
|
835
|
+
return state.appModules;
|
|
836
|
+
}
|
|
837
|
+
state.modulesReady ??= loadDiscoveredModules(options).then((modules) => {
|
|
838
|
+
state.appModules.splice(0, state.appModules.length, ...modules);
|
|
839
|
+
return state.appModules;
|
|
840
|
+
});
|
|
841
|
+
return state.modulesReady;
|
|
842
|
+
}
|
|
843
|
+
function discoverModules() {
|
|
844
|
+
return readDiscoverModulesState().appModules;
|
|
845
|
+
}
|
|
846
|
+
// ../../src/bootstrap/prefixRouteMap.ts
|
|
847
|
+
function prefixRouteMap(prefix, routes) {
|
|
848
|
+
const normalizedPrefix = prefix.replace(/\/$/, "");
|
|
849
|
+
const prefixed = {};
|
|
850
|
+
for (const [path, handler] of Object.entries(routes)) {
|
|
851
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
852
|
+
prefixed[`${normalizedPrefix}${normalizedPath}`] = handler;
|
|
853
|
+
}
|
|
854
|
+
return prefixed;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// ../../src/bootstrap/routeRegistry.ts
|
|
858
|
+
class RouteRegistry {
|
|
859
|
+
routes = [];
|
|
860
|
+
register(route) {
|
|
861
|
+
this.routes.push(route);
|
|
862
|
+
}
|
|
863
|
+
clear() {
|
|
864
|
+
this.routes.length = 0;
|
|
865
|
+
}
|
|
866
|
+
list() {
|
|
867
|
+
return [...this.routes].sort((left, right) => left.path.localeCompare(right.path));
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
var ROUTE_REGISTRY_KEY = Symbol.for("@getstrata/routeRegistry");
|
|
871
|
+
function readSharedRouteRegistry() {
|
|
872
|
+
const globalRegistry = globalThis[ROUTE_REGISTRY_KEY];
|
|
873
|
+
if (globalRegistry) {
|
|
874
|
+
return globalRegistry;
|
|
875
|
+
}
|
|
876
|
+
const registry = new RouteRegistry;
|
|
877
|
+
globalThis[ROUTE_REGISTRY_KEY] = registry;
|
|
878
|
+
return registry;
|
|
879
|
+
}
|
|
880
|
+
var routeRegistry = readSharedRouteRegistry();
|
|
881
|
+
|
|
882
|
+
// ../../src/bootstrap/buildModuleRoutes.ts
|
|
883
|
+
function registerOpenApiRoute(method, path, middleware) {
|
|
884
|
+
routeRegistry.register({ method, path, middleware });
|
|
885
|
+
}
|
|
886
|
+
function registerOpenApiRouteMap(routes, middleware) {
|
|
887
|
+
const registered = {};
|
|
888
|
+
for (const [path, handler] of Object.entries(routes)) {
|
|
889
|
+
if (handler && typeof handler === "object" && !Array.isArray(handler)) {
|
|
890
|
+
const methodMap = handler;
|
|
891
|
+
registered[path] = methodMap;
|
|
892
|
+
for (const method of Object.keys(methodMap)) {
|
|
893
|
+
registerOpenApiRoute(method.toUpperCase(), path, middleware);
|
|
894
|
+
}
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
registered[path] = handler;
|
|
898
|
+
registerOpenApiRoute("GET", path, middleware);
|
|
899
|
+
}
|
|
900
|
+
return registered;
|
|
901
|
+
}
|
|
902
|
+
function createCachedJson(dependencies) {
|
|
903
|
+
return async (cacheKey, loader, tags = [], request) => {
|
|
904
|
+
const data = tags.length > 0 ? await dependencies.cache.tags(...tags).remember(cacheKey, loader) : await dependencies.cache.remember(cacheKey, loader);
|
|
905
|
+
return conditionalJsonResponse(request, data);
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
function buildModuleRoutes(dependencies, options = {}) {
|
|
909
|
+
const { apiPrefix = "", modules = discoverModules(), clearRegistry = true } = options;
|
|
910
|
+
if (clearRegistry) {
|
|
911
|
+
routeRegistry.clear();
|
|
722
912
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
913
|
+
const kernel = createHttpKernel(dependencies);
|
|
914
|
+
const middleware = [...kernel.globalMiddleware(), ...kernel.group("api")];
|
|
915
|
+
const cachedJson = createCachedJson(dependencies);
|
|
916
|
+
const moduleRoutes = {};
|
|
917
|
+
for (const module of modules) {
|
|
918
|
+
if (!module.routes) {
|
|
919
|
+
continue;
|
|
726
920
|
}
|
|
727
|
-
|
|
921
|
+
Object.assign(moduleRoutes, module.routes({ dependencies, cachedJson, kernel }));
|
|
922
|
+
}
|
|
923
|
+
const prefixedModuleRoutes = prefixRouteMap(apiPrefix, moduleRoutes);
|
|
924
|
+
return applyMiddlewareToRoutes(registerOpenApiRouteMap(prefixedModuleRoutes, ["global", "api"]), middleware);
|
|
925
|
+
}
|
|
926
|
+
// ../../src/bootstrap/buildWebModuleRoutes.ts
|
|
927
|
+
import { applyMiddlewareToRoutes as applyMiddlewareToRoutes2 } from "@getstrata/core/http/middleware";
|
|
928
|
+
function buildWebModuleRoutes(dependencies, options = {}) {
|
|
929
|
+
const { modules = discoverModules(), clearRegistry = true, seedRoutes = {} } = options;
|
|
930
|
+
if (clearRegistry) {
|
|
931
|
+
routeRegistry.clear();
|
|
728
932
|
}
|
|
729
|
-
|
|
730
|
-
|
|
933
|
+
const kernel = createHttpKernel(dependencies);
|
|
934
|
+
const middleware = [...kernel.globalMiddleware(), ...kernel.group("web")];
|
|
935
|
+
const moduleRoutes = { ...seedRoutes };
|
|
936
|
+
for (const module of modules) {
|
|
937
|
+
if (!module.webRoutes) {
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
Object.assign(moduleRoutes, module.webRoutes({
|
|
941
|
+
dependencies,
|
|
942
|
+
cachedJson: async () => new Response(""),
|
|
943
|
+
kernel
|
|
944
|
+
}));
|
|
731
945
|
}
|
|
946
|
+
return applyMiddlewareToRoutes2(registerOpenApiRouteMap(moduleRoutes, ["global", "web"]), middleware);
|
|
947
|
+
}
|
|
948
|
+
// ../../src/bootstrap/cache/modelCacheTags.ts
|
|
949
|
+
function cacheTagsForModelWrite(tableName, action) {
|
|
950
|
+
const module = discoverModules().find((entry) => entry.tableName === tableName);
|
|
951
|
+
const baseTags = module?.cacheTags ?? [`${tableName}s`];
|
|
952
|
+
const isDelete = action === "deleted" || action === "force-deleted";
|
|
953
|
+
const extraTags = isDelete ? module?.cacheDeleteExtraTags ?? [] : [];
|
|
954
|
+
return [...new Set([...baseTags, ...extraTags])];
|
|
732
955
|
}
|
|
956
|
+
function discoverModelTableNames() {
|
|
957
|
+
return discoverModules().map((module) => module.tableName).filter((tableName) => tableName !== undefined);
|
|
958
|
+
}
|
|
959
|
+
// ../../src/bootstrap/contracts.ts
|
|
960
|
+
import {
|
|
961
|
+
ConfigStore,
|
|
962
|
+
ServiceContainer
|
|
963
|
+
} from "@getstrata/core/contracts/container";
|
|
964
|
+
import {
|
|
965
|
+
assertAppDependenciesComplete,
|
|
966
|
+
getRequiredDependency,
|
|
967
|
+
resolveService
|
|
968
|
+
} from "@getstrata/core/contracts/di";
|
|
969
|
+
|
|
733
970
|
// ../../src/config/auth.ts
|
|
734
971
|
var authConfig = {
|
|
735
972
|
allowDevHeaders: (process.env.AUTH_DEV_HEADERS ?? "true") !== "false",
|
|
@@ -835,7 +1072,6 @@ var db = new Proxy(function database() {}, {
|
|
|
835
1072
|
return typeof value === "function" ? value.bind(connection) : value;
|
|
836
1073
|
}
|
|
837
1074
|
});
|
|
838
|
-
var connection_default = db;
|
|
839
1075
|
|
|
840
1076
|
// ../../src/modules/user/apiTokenTable.ts
|
|
841
1077
|
import { defineTable } from "@getstrata/core/database";
|
|
@@ -1667,9 +1903,6 @@ var queueConfig = {
|
|
|
1667
1903
|
};
|
|
1668
1904
|
|
|
1669
1905
|
// ../../src/core/config/envSchema.ts
|
|
1670
|
-
function defineEnvSchema(schema) {
|
|
1671
|
-
return schema;
|
|
1672
|
-
}
|
|
1673
1906
|
function validateEnv(schema, env = process.env) {
|
|
1674
1907
|
const resolved = {};
|
|
1675
1908
|
for (const [name, rule] of Object.entries(schema)) {
|
|
@@ -1698,6 +1931,7 @@ function validateEnv(schema, env = process.env) {
|
|
|
1698
1931
|
}
|
|
1699
1932
|
|
|
1700
1933
|
// ../../src/bootstrap/env.ts
|
|
1934
|
+
import { defineEnvSchema } from "@getstrata/core/config/envSchema";
|
|
1701
1935
|
var appEnvSchema = defineEnvSchema({
|
|
1702
1936
|
DATABASE_URL: { required: true, pattern: /^postgres(ql)?:\/\// },
|
|
1703
1937
|
PORT: {
|
|
@@ -3947,22 +4181,65 @@ class DispatchWebhookJob extends Job {
|
|
|
3947
4181
|
}
|
|
3948
4182
|
var dispatchWebhookJob_default = DispatchWebhookJob;
|
|
3949
4183
|
|
|
3950
|
-
// ../../src/
|
|
3951
|
-
function
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
4184
|
+
// ../../src/core/contracts/di.ts
|
|
4185
|
+
function getRequiredDependency2(dependencies, key) {
|
|
4186
|
+
const dependency = dependencies[key];
|
|
4187
|
+
if (dependency === undefined) {
|
|
4188
|
+
throw new Error(`Required dependency "${key}" is not registered.`);
|
|
4189
|
+
}
|
|
4190
|
+
return dependency;
|
|
3956
4191
|
}
|
|
3957
4192
|
|
|
3958
|
-
// ../../src/
|
|
3959
|
-
var
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
4193
|
+
// ../../src/core/contracts/serviceTokens.ts
|
|
4194
|
+
var CORE_POLICY_GATE_TOKEN2 = "core.policyGate";
|
|
4195
|
+
var CORE_AUTH_TOKEN2 = "core.auth";
|
|
4196
|
+
|
|
4197
|
+
// ../../src/core/runtime/applicationRegistry.ts
|
|
4198
|
+
var APPLICATION_CONTEXT_KEY = Symbol.for("@getstrata/applicationContext");
|
|
4199
|
+
var activeContext;
|
|
4200
|
+
function readStoredApplicationContext() {
|
|
4201
|
+
if (activeContext) {
|
|
4202
|
+
return activeContext;
|
|
4203
|
+
}
|
|
4204
|
+
const globalContext = globalThis[APPLICATION_CONTEXT_KEY];
|
|
4205
|
+
if (globalContext) {
|
|
4206
|
+
activeContext = globalContext;
|
|
4207
|
+
}
|
|
4208
|
+
return activeContext;
|
|
4209
|
+
}
|
|
4210
|
+
function requireActiveApplicationContext() {
|
|
4211
|
+
const context = readStoredApplicationContext();
|
|
4212
|
+
if (!context) {
|
|
4213
|
+
throw new Error("The application context has not been bootstrapped.");
|
|
4214
|
+
}
|
|
4215
|
+
return context;
|
|
4216
|
+
}
|
|
4217
|
+
function resolveApplicationCache2() {
|
|
4218
|
+
return getRequiredDependency2(requireActiveApplicationContext().dependencies, "cache");
|
|
4219
|
+
}
|
|
4220
|
+
function resolveApplicationAuth2() {
|
|
4221
|
+
return requireActiveApplicationContext().container.resolve(CORE_AUTH_TOKEN2);
|
|
4222
|
+
}
|
|
4223
|
+
function resolveApplicationPolicyGate2() {
|
|
4224
|
+
return requireActiveApplicationContext().container.resolve(CORE_POLICY_GATE_TOKEN2);
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
// ../../src/bootstrap/queue/defaultJobs.ts
|
|
4228
|
+
function registerDefaultJobs() {
|
|
4229
|
+
jobRegistry.register("cache.invalidate-tags", () => {
|
|
4230
|
+
return new invalidateCacheTagsJob_default(resolveApplicationCache2());
|
|
4231
|
+
});
|
|
4232
|
+
jobRegistry.register("webhook.dispatch", () => new dispatchWebhookJob_default);
|
|
4233
|
+
}
|
|
4234
|
+
|
|
4235
|
+
// ../../src/bootstrap/providers/queue.ts
|
|
4236
|
+
var queueProvider = {
|
|
4237
|
+
name: "core.queue",
|
|
4238
|
+
register({ container, config }) {
|
|
4239
|
+
const configuredDriver = process.env.QUEUE_DRIVER ?? DEFAULT_QUEUE_DRIVER;
|
|
4240
|
+
const driver = configuredDriver === "async" || configuredDriver === "redis" || configuredDriver === "sync" ? configuredDriver : DEFAULT_QUEUE_DRIVER;
|
|
4241
|
+
config.set("queue.driver", driver);
|
|
4242
|
+
const failedJobs = createFailedJobService();
|
|
3966
4243
|
container.set(FAILED_JOB_SERVICE_TOKEN, failedJobs);
|
|
3967
4244
|
container.set(CORE_QUEUE_TOKEN, createAppQueue(driver, config.get(REDIS_URL_CONFIG_KEY) ?? process.env.REDIS_URL, failedJobs, registerDefaultJobs));
|
|
3968
4245
|
}
|
|
@@ -4091,21 +4368,6 @@ var storageProvider = {
|
|
|
4091
4368
|
};
|
|
4092
4369
|
var storage_default = storageProvider;
|
|
4093
4370
|
|
|
4094
|
-
// ../../src/config/frontend.ts
|
|
4095
|
-
function readFrontendMode() {
|
|
4096
|
-
const mode = (process.env.FRONTEND_MODE ?? "api").trim();
|
|
4097
|
-
if (mode === "server-htmx") {
|
|
4098
|
-
return "server-htmx";
|
|
4099
|
-
}
|
|
4100
|
-
if (mode === "spa-react") {
|
|
4101
|
-
return "spa-react";
|
|
4102
|
-
}
|
|
4103
|
-
return "api";
|
|
4104
|
-
}
|
|
4105
|
-
function isViewsEnabled() {
|
|
4106
|
-
return readFrontendMode() === "server-htmx";
|
|
4107
|
-
}
|
|
4108
|
-
|
|
4109
4371
|
// ../../src/core/http/requestMetaContext.ts
|
|
4110
4372
|
var requestMetaContext = createAsyncContextStore("@getstrata/requestMetaContext");
|
|
4111
4373
|
function currentRequestMeta() {
|
|
@@ -4147,16 +4409,6 @@ class EtaViewEngine {
|
|
|
4147
4409
|
});
|
|
4148
4410
|
}
|
|
4149
4411
|
}
|
|
4150
|
-
// ../../src/core/view/htmlResponse.ts
|
|
4151
|
-
function htmlResponse(html, init = {}) {
|
|
4152
|
-
return new Response(html, {
|
|
4153
|
-
status: init.status ?? 200,
|
|
4154
|
-
statusText: init.statusText,
|
|
4155
|
-
headers: {
|
|
4156
|
-
"Content-Type": "text/html; charset=utf-8"
|
|
4157
|
-
}
|
|
4158
|
-
});
|
|
4159
|
-
}
|
|
4160
4412
|
// ../../src/core/http/cookies.ts
|
|
4161
4413
|
function readRequestCookie(request, name) {
|
|
4162
4414
|
const cookies = request.cookies;
|
|
@@ -4445,352 +4697,25 @@ function createAppContext() {
|
|
|
4445
4697
|
}
|
|
4446
4698
|
// ../../src/bootstrap/createWebRoutes.ts
|
|
4447
4699
|
import { join as join5 } from "path";
|
|
4448
|
-
|
|
4449
|
-
// ../../src/core/http/middleware.ts
|
|
4450
|
-
function isRouteHandler(value) {
|
|
4451
|
-
return typeof value === "function";
|
|
4452
|
-
}
|
|
4453
|
-
function isMethodRouteMap(value) {
|
|
4454
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4455
|
-
return false;
|
|
4456
|
-
}
|
|
4457
|
-
const entries = Object.entries(value);
|
|
4458
|
-
return entries.length > 0 && entries.every(([, handler]) => isRouteHandler(handler));
|
|
4459
|
-
}
|
|
4460
|
-
function composeMiddleware(...middleware) {
|
|
4461
|
-
return (handler) => {
|
|
4462
|
-
return async (request) => {
|
|
4463
|
-
let index = 0;
|
|
4464
|
-
const dispatch = async () => {
|
|
4465
|
-
if (index >= middleware.length) {
|
|
4466
|
-
return await handler(request);
|
|
4467
|
-
}
|
|
4468
|
-
const current = middleware[index];
|
|
4469
|
-
index += 1;
|
|
4470
|
-
if (!current) {
|
|
4471
|
-
return await handler(request);
|
|
4472
|
-
}
|
|
4473
|
-
return await current(request, dispatch);
|
|
4474
|
-
};
|
|
4475
|
-
return await dispatch();
|
|
4476
|
-
};
|
|
4477
|
-
};
|
|
4478
|
-
}
|
|
4479
|
-
function wrapRouteHandler(handler, middleware) {
|
|
4480
|
-
if (isMethodRouteMap(handler)) {
|
|
4481
|
-
const wrapped = {};
|
|
4482
|
-
for (const [method, routeHandler] of Object.entries(handler)) {
|
|
4483
|
-
wrapped[method] = composeMiddleware(...middleware)(routeHandler);
|
|
4484
|
-
}
|
|
4485
|
-
return wrapped;
|
|
4486
|
-
}
|
|
4487
|
-
if (isRouteHandler(handler)) {
|
|
4488
|
-
return composeMiddleware(...middleware)(handler);
|
|
4489
|
-
}
|
|
4490
|
-
return handler;
|
|
4491
|
-
}
|
|
4492
|
-
function applyMiddlewareToRoutes(routes, middleware) {
|
|
4493
|
-
const wrapped = {};
|
|
4494
|
-
for (const [path, routeHandler] of Object.entries(routes)) {
|
|
4495
|
-
wrapped[path] = wrapRouteHandler(routeHandler, middleware);
|
|
4496
|
-
}
|
|
4497
|
-
return wrapped;
|
|
4498
|
-
}
|
|
4499
|
-
|
|
4500
|
-
// ../../src/bootstrap/httpKernel.ts
|
|
4501
|
-
import {
|
|
4502
|
-
createAuthMiddleware,
|
|
4503
|
-
createAuthorizeMiddleware,
|
|
4504
|
-
createBodySizeLimitMiddleware,
|
|
4505
|
-
createCorsMiddleware,
|
|
4506
|
-
createCsrfMiddleware,
|
|
4507
|
-
createFlashMiddleware,
|
|
4508
|
-
createLoginThrottleMiddleware,
|
|
4509
|
-
createMembershipMiddleware,
|
|
4510
|
-
createMemoryThrottleMiddleware,
|
|
4511
|
-
createMetricsMiddleware,
|
|
4512
|
-
createRequestLoggingMiddleware,
|
|
4513
|
-
createRequireAbilityMiddleware,
|
|
4514
|
-
createRequireAuthMiddleware,
|
|
4515
|
-
createRequireGlobalAdminMiddleware,
|
|
4516
|
-
createRequireWebAuthMiddleware,
|
|
4517
|
-
createSecurityHeadersMiddleware,
|
|
4518
|
-
createTenantMiddleware,
|
|
4519
|
-
createThrottleMiddleware,
|
|
4520
|
-
createTracingMiddleware,
|
|
4521
|
-
isPublicReadsEnabled,
|
|
4522
|
-
requestIdMiddleware,
|
|
4523
|
-
withMiddleware
|
|
4524
|
-
} from "@getstrata/core";
|
|
4525
|
-
|
|
4526
|
-
// ../../src/config/rateLimit.ts
|
|
4527
|
-
var LOCAL_LOGIN_RATE_LIMIT = {
|
|
4528
|
-
maxAttempts: 100,
|
|
4529
|
-
decaySeconds: 60
|
|
4530
|
-
};
|
|
4531
|
-
var PRODUCTION_LOGIN_RATE_LIMIT = {
|
|
4532
|
-
maxAttempts: 5,
|
|
4533
|
-
decaySeconds: 900
|
|
4534
|
-
};
|
|
4535
|
-
function isLocalAppEnv() {
|
|
4536
|
-
return (process.env.APP_ENV ?? "local") === "local";
|
|
4537
|
-
}
|
|
4538
|
-
function parsePositiveInt(value, fallback) {
|
|
4539
|
-
const parsed = Number(value);
|
|
4540
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
4541
|
-
return fallback;
|
|
4542
|
-
}
|
|
4543
|
-
return Math.trunc(parsed);
|
|
4544
|
-
}
|
|
4545
|
-
function resolveLoginRateLimit() {
|
|
4546
|
-
const defaults = isLocalAppEnv() ? LOCAL_LOGIN_RATE_LIMIT : PRODUCTION_LOGIN_RATE_LIMIT;
|
|
4547
|
-
return {
|
|
4548
|
-
maxAttempts: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
|
|
4549
|
-
decaySeconds: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_WINDOW_SECONDS, defaults.decaySeconds)
|
|
4550
|
-
};
|
|
4551
|
-
}
|
|
4552
|
-
function resolveRegisterRateLimit() {
|
|
4553
|
-
const defaults = isLocalAppEnv() ? { maxAttempts: 100, decaySeconds: 60 } : { maxAttempts: 10, decaySeconds: 60 };
|
|
4554
|
-
return {
|
|
4555
|
-
maxAttempts: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
|
|
4556
|
-
decaySeconds: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_WINDOW_SECONDS ?? process.env.REGISTER_RATE_LIMIT_WINDOW_MS ? String(Number(process.env.REGISTER_RATE_LIMIT_WINDOW_MS) / 1000) : undefined, defaults.decaySeconds)
|
|
4557
|
-
};
|
|
4558
|
-
}
|
|
4559
|
-
|
|
4560
|
-
// ../../src/bootstrap/httpKernel.ts
|
|
4561
|
-
class HttpKernel {
|
|
4562
|
-
dependencies;
|
|
4563
|
-
constructor(dependencies) {
|
|
4564
|
-
this.dependencies = dependencies;
|
|
4565
|
-
}
|
|
4566
|
-
globalMiddleware() {
|
|
4567
|
-
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
4568
|
-
return [
|
|
4569
|
-
createCorsMiddleware(),
|
|
4570
|
-
createSecurityHeadersMiddleware(),
|
|
4571
|
-
createBodySizeLimitMiddleware(),
|
|
4572
|
-
createTracingMiddleware(),
|
|
4573
|
-
createMetricsMiddleware(),
|
|
4574
|
-
createRequestLoggingMiddleware(),
|
|
4575
|
-
requestIdMiddleware,
|
|
4576
|
-
createAuthMiddleware(auth),
|
|
4577
|
-
createMembershipMiddleware(),
|
|
4578
|
-
createTenantMiddleware()
|
|
4579
|
-
];
|
|
4580
|
-
}
|
|
4581
|
-
group(name) {
|
|
4582
|
-
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
4583
|
-
switch (name) {
|
|
4584
|
-
case "authenticated":
|
|
4585
|
-
return [createRequireAuthMiddleware(auth)];
|
|
4586
|
-
case "web":
|
|
4587
|
-
return isViewsEnabled() ? [createFlashMiddleware(), createCsrfMiddleware()] : [];
|
|
4588
|
-
case "api": {
|
|
4589
|
-
if (!this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
|
|
4590
|
-
return [];
|
|
4591
|
-
}
|
|
4592
|
-
const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
|
|
4593
|
-
const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
|
|
4594
|
-
if (!redisUrl) {
|
|
4595
|
-
const maxAttempts2 = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
|
|
4596
|
-
return [
|
|
4597
|
-
createMemoryThrottleMiddleware({
|
|
4598
|
-
maxAttempts: Number.isFinite(maxAttempts2) ? maxAttempts2 : 120,
|
|
4599
|
-
decaySeconds: 60
|
|
4600
|
-
})
|
|
4601
|
-
];
|
|
4602
|
-
}
|
|
4603
|
-
const maxAttempts = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
|
|
4604
|
-
return [
|
|
4605
|
-
createThrottleMiddleware({
|
|
4606
|
-
redisUrl,
|
|
4607
|
-
maxAttempts: Number.isFinite(maxAttempts) ? maxAttempts : 120,
|
|
4608
|
-
decaySeconds: 60
|
|
4609
|
-
})
|
|
4610
|
-
];
|
|
4611
|
-
}
|
|
4612
|
-
default:
|
|
4613
|
-
throw new Error(`Unknown middleware group "${name}".`);
|
|
4614
|
-
}
|
|
4615
|
-
}
|
|
4616
|
-
wrap(groups, handler) {
|
|
4617
|
-
const names = Array.isArray(groups) ? groups : [groups];
|
|
4618
|
-
const middleware = names.flatMap((name) => this.group(name));
|
|
4619
|
-
if (middleware.length === 0) {
|
|
4620
|
-
return handler;
|
|
4621
|
-
}
|
|
4622
|
-
return withMiddleware(...middleware)(handler);
|
|
4623
|
-
}
|
|
4624
|
-
wrapApi(handler) {
|
|
4625
|
-
return this.wrap(["api", "authenticated"], handler);
|
|
4626
|
-
}
|
|
4627
|
-
wrapWeb(handler) {
|
|
4628
|
-
return handler;
|
|
4629
|
-
}
|
|
4630
|
-
wrapWebPublicRead(handler) {
|
|
4631
|
-
if (isPublicReadsEnabled()) {
|
|
4632
|
-
return this.wrapWeb(handler);
|
|
4633
|
-
}
|
|
4634
|
-
return this.wrapWebAuthenticated(handler);
|
|
4635
|
-
}
|
|
4636
|
-
wrapWebAuthenticated(handler) {
|
|
4637
|
-
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
4638
|
-
return withMiddleware(createRequireWebAuthMiddleware(auth))(handler);
|
|
4639
|
-
}
|
|
4640
|
-
wrapWebAbility(ability, handler) {
|
|
4641
|
-
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
4642
|
-
const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
|
|
4643
|
-
const requireAbility = createRequireAbilityMiddleware(abilityChecker);
|
|
4644
|
-
const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
|
|
4645
|
-
return withMiddleware(...middleware)(handler);
|
|
4646
|
-
}
|
|
4647
|
-
wrapWebGlobalAdmin(handler) {
|
|
4648
|
-
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
4649
|
-
const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
|
|
4650
|
-
return withMiddleware(...middleware)(handler);
|
|
4651
|
-
}
|
|
4652
|
-
wrapAuthenticated(handler) {
|
|
4653
|
-
return this.wrap("authenticated", handler);
|
|
4654
|
-
}
|
|
4655
|
-
wrapPublicRead(handler) {
|
|
4656
|
-
if (isPublicReadsEnabled()) {
|
|
4657
|
-
return handler;
|
|
4658
|
-
}
|
|
4659
|
-
return this.wrapAuthenticated(handler);
|
|
4660
|
-
}
|
|
4661
|
-
wrapGlobalAdmin(handler) {
|
|
4662
|
-
const middleware = [...this.group("authenticated"), createRequireGlobalAdminMiddleware()];
|
|
4663
|
-
return withMiddleware(...middleware)(handler);
|
|
4664
|
-
}
|
|
4665
|
-
wrapAbility(ability, handler) {
|
|
4666
|
-
const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
|
|
4667
|
-
const requireAbility = createRequireAbilityMiddleware(abilityChecker);
|
|
4668
|
-
const middleware = [...this.group("authenticated"), requireAbility(ability)];
|
|
4669
|
-
return withMiddleware(...middleware)(handler);
|
|
4670
|
-
}
|
|
4671
|
-
wrapPolicy(resource, action, handler) {
|
|
4672
|
-
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
4673
|
-
const gate = this.dependencies.container.resolve(CORE_POLICY_GATE_TOKEN);
|
|
4674
|
-
return withMiddleware(createAuthorizeMiddleware(gate, auth, resource, action))(handler);
|
|
4675
|
-
}
|
|
4676
|
-
wrapLogin(handler) {
|
|
4677
|
-
return this.wrapThrottle("login", resolveLoginRateLimit(), handler);
|
|
4678
|
-
}
|
|
4679
|
-
wrapRegister(handler) {
|
|
4680
|
-
return this.wrapThrottle("register", resolveRegisterRateLimit(), handler);
|
|
4681
|
-
}
|
|
4682
|
-
wrapThrottle(scope, rateLimit, handler) {
|
|
4683
|
-
const middleware = [];
|
|
4684
|
-
const memoryKeyPrefix = scope === "login" ? "login-throttle:" : "register-throttle:";
|
|
4685
|
-
if (this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
|
|
4686
|
-
const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
|
|
4687
|
-
const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
|
|
4688
|
-
if (redisUrl) {
|
|
4689
|
-
const throttle = scope === "login" ? createLoginThrottleMiddleware({
|
|
4690
|
-
redisUrl,
|
|
4691
|
-
maxAttempts: rateLimit.maxAttempts,
|
|
4692
|
-
decaySeconds: rateLimit.decaySeconds
|
|
4693
|
-
}) : createThrottleMiddleware({
|
|
4694
|
-
redisUrl,
|
|
4695
|
-
maxAttempts: rateLimit.maxAttempts,
|
|
4696
|
-
decaySeconds: rateLimit.decaySeconds,
|
|
4697
|
-
keyPrefix: memoryKeyPrefix
|
|
4698
|
-
});
|
|
4699
|
-
middleware.push(throttle);
|
|
4700
|
-
} else {
|
|
4701
|
-
middleware.push(createMemoryThrottleMiddleware({
|
|
4702
|
-
maxAttempts: rateLimit.maxAttempts,
|
|
4703
|
-
decaySeconds: rateLimit.decaySeconds,
|
|
4704
|
-
keyPrefix: memoryKeyPrefix
|
|
4705
|
-
}));
|
|
4706
|
-
}
|
|
4707
|
-
} else {
|
|
4708
|
-
middleware.push(createMemoryThrottleMiddleware({
|
|
4709
|
-
maxAttempts: rateLimit.maxAttempts,
|
|
4710
|
-
decaySeconds: rateLimit.decaySeconds,
|
|
4711
|
-
keyPrefix: memoryKeyPrefix
|
|
4712
|
-
}));
|
|
4713
|
-
}
|
|
4714
|
-
if (middleware.length === 0) {
|
|
4715
|
-
return handler;
|
|
4716
|
-
}
|
|
4717
|
-
return withMiddleware(...middleware)(handler);
|
|
4718
|
-
}
|
|
4719
|
-
}
|
|
4720
|
-
function createHttpKernel(dependencies) {
|
|
4721
|
-
return new HttpKernel(dependencies);
|
|
4722
|
-
}
|
|
4723
|
-
|
|
4724
|
-
// ../../src/bootstrap/routeRegistry.ts
|
|
4725
|
-
class RouteRegistry {
|
|
4726
|
-
routes = [];
|
|
4727
|
-
register(route) {
|
|
4728
|
-
this.routes.push(route);
|
|
4729
|
-
}
|
|
4730
|
-
clear() {
|
|
4731
|
-
this.routes.length = 0;
|
|
4732
|
-
}
|
|
4733
|
-
list() {
|
|
4734
|
-
return [...this.routes].sort((left, right) => left.path.localeCompare(right.path));
|
|
4735
|
-
}
|
|
4736
|
-
}
|
|
4737
|
-
var ROUTE_REGISTRY_KEY = Symbol.for("@getstrata/routeRegistry");
|
|
4738
|
-
function readSharedRouteRegistry() {
|
|
4739
|
-
const globalRegistry = globalThis[ROUTE_REGISTRY_KEY];
|
|
4740
|
-
if (globalRegistry) {
|
|
4741
|
-
return globalRegistry;
|
|
4742
|
-
}
|
|
4743
|
-
const registry = new RouteRegistry;
|
|
4744
|
-
globalThis[ROUTE_REGISTRY_KEY] = registry;
|
|
4745
|
-
return registry;
|
|
4746
|
-
}
|
|
4747
|
-
var routeRegistry = readSharedRouteRegistry();
|
|
4748
|
-
|
|
4749
|
-
// ../../src/bootstrap/createWebRoutes.ts
|
|
4700
|
+
import { htmlResponse as htmlResponse2 } from "@getstrata/core/view";
|
|
4750
4701
|
function registerRoute(method, path, middleware) {
|
|
4751
4702
|
routeRegistry.register({ method, path, middleware });
|
|
4752
4703
|
}
|
|
4753
|
-
function registerRouteMap(routes, middleware) {
|
|
4754
|
-
const registered = {};
|
|
4755
|
-
for (const [path, handler] of Object.entries(routes)) {
|
|
4756
|
-
if (handler && typeof handler === "object" && !Array.isArray(handler)) {
|
|
4757
|
-
const methodMap = handler;
|
|
4758
|
-
registered[path] = methodMap;
|
|
4759
|
-
for (const method of Object.keys(methodMap)) {
|
|
4760
|
-
registerRoute(method.toUpperCase(), path, middleware);
|
|
4761
|
-
}
|
|
4762
|
-
continue;
|
|
4763
|
-
}
|
|
4764
|
-
registered[path] = handler;
|
|
4765
|
-
registerRoute("GET", path, middleware);
|
|
4766
|
-
}
|
|
4767
|
-
return registered;
|
|
4768
|
-
}
|
|
4769
4704
|
function createWebRoutes(dependencies) {
|
|
4770
|
-
const
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
};
|
|
4775
|
-
registerRoute("GET", "/", ["global", "web"]);
|
|
4776
|
-
for (const module of discoverModules()) {
|
|
4777
|
-
if (!module.webRoutes) {
|
|
4778
|
-
continue;
|
|
4705
|
+
const wrappedRoutes = buildWebModuleRoutes(dependencies, {
|
|
4706
|
+
clearRegistry: false,
|
|
4707
|
+
seedRoutes: {
|
|
4708
|
+
"/": () => Response.redirect("/organizations", 302)
|
|
4779
4709
|
}
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
cachedJson: async () => htmlResponse(""),
|
|
4783
|
-
kernel
|
|
4784
|
-
}));
|
|
4785
|
-
}
|
|
4786
|
-
const wrappedRoutes = applyMiddlewareToRoutes(registerRouteMap(moduleRoutes, ["global", "web"]), middleware);
|
|
4710
|
+
});
|
|
4711
|
+
registerRoute("GET", "/", ["global", "web"]);
|
|
4787
4712
|
wrappedRoutes["/assets/*"] = async (request) => {
|
|
4788
4713
|
registerRoute("GET", "/assets/*", ["global", "web"]);
|
|
4789
4714
|
const pathname = new URL(request.url).pathname;
|
|
4790
4715
|
const relativePath = pathname.replace(/^\//, "");
|
|
4791
4716
|
const file = Bun.file(join5(process.cwd(), "public", relativePath));
|
|
4792
4717
|
if (!await file.exists()) {
|
|
4793
|
-
return
|
|
4718
|
+
return htmlResponse2("Not Found", { status: 404 });
|
|
4794
4719
|
}
|
|
4795
4720
|
return new Response(file);
|
|
4796
4721
|
};
|
|
@@ -4921,8 +4846,8 @@ function securedBindRouteModel(param, resolver, authorization, handler) {
|
|
|
4921
4846
|
return async (request) => {
|
|
4922
4847
|
const id = parsePositiveIntParam(String(request.params[param]), String(param));
|
|
4923
4848
|
const model = await resolver(id, request);
|
|
4924
|
-
const gate =
|
|
4925
|
-
const auth =
|
|
4849
|
+
const gate = resolveApplicationPolicyGate2();
|
|
4850
|
+
const auth = resolveApplicationAuth2();
|
|
4926
4851
|
const user = currentAuthUser() ?? await auth.resolve(request);
|
|
4927
4852
|
gate.authorize(authorization.resource, authorization.action, user, model);
|
|
4928
4853
|
if (isEtagEnabled() && isMutatingPolicyAction(authorization.action)) {
|
|
@@ -4944,8 +4869,8 @@ function securedBindRouteModelByKey(param, resolver, authorization, handler) {
|
|
|
4944
4869
|
throw new BadRequestError(`Missing route parameter "${String(param)}".`);
|
|
4945
4870
|
}
|
|
4946
4871
|
const model = await resolver(key, request);
|
|
4947
|
-
const gate =
|
|
4948
|
-
const auth =
|
|
4872
|
+
const gate = resolveApplicationPolicyGate2();
|
|
4873
|
+
const auth = resolveApplicationAuth2();
|
|
4949
4874
|
const user = currentAuthUser() ?? await auth.resolve(request);
|
|
4950
4875
|
gate.authorize(authorization.resource, authorization.action, user, model);
|
|
4951
4876
|
if (isEtagEnabled() && isMutatingPolicyAction(authorization.action)) {
|
|
@@ -4960,158 +4885,8 @@ function securedBindRouteModelByKey(param, resolver, authorization, handler) {
|
|
|
4960
4885
|
return response;
|
|
4961
4886
|
};
|
|
4962
4887
|
}
|
|
4963
|
-
// ../../src/
|
|
4964
|
-
|
|
4965
|
-
member: 1,
|
|
4966
|
-
admin: 2,
|
|
4967
|
-
owner: 3
|
|
4968
|
-
};
|
|
4969
|
-
function isGlobalAdmin(user) {
|
|
4970
|
-
return user?.role === "admin";
|
|
4971
|
-
}
|
|
4972
|
-
function hasMinimumOrgRole(role, minimum) {
|
|
4973
|
-
if (!role) {
|
|
4974
|
-
return false;
|
|
4975
|
-
}
|
|
4976
|
-
return ROLE_RANK[role] >= ROLE_RANK[minimum];
|
|
4977
|
-
}
|
|
4978
|
-
function resolveUserId(user) {
|
|
4979
|
-
const userId = typeof user.id === "number" ? user.id : Number(user.id);
|
|
4980
|
-
if (!Number.isInteger(userId) || userId <= 0) {
|
|
4981
|
-
throw new ForbiddenError("Invalid authenticated user.");
|
|
4982
|
-
}
|
|
4983
|
-
return userId;
|
|
4984
|
-
}
|
|
4985
|
-
|
|
4986
|
-
// ../../src/modules/organization/memberRepository.ts
|
|
4987
|
-
class OrganizationMemberRepository {
|
|
4988
|
-
constructor() {}
|
|
4989
|
-
async findMembership(userId, organizationId) {
|
|
4990
|
-
const rows = await connection_default`
|
|
4991
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
4992
|
-
FROM organization_member
|
|
4993
|
-
WHERE user_id = ${userId} AND organization_id = ${organizationId}
|
|
4994
|
-
LIMIT 1
|
|
4995
|
-
`;
|
|
4996
|
-
return rows[0] ?? null;
|
|
4997
|
-
}
|
|
4998
|
-
async listForUser(userId) {
|
|
4999
|
-
return await connection_default`
|
|
5000
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
5001
|
-
FROM organization_member
|
|
5002
|
-
WHERE user_id = ${userId}
|
|
5003
|
-
ORDER BY organization_id
|
|
5004
|
-
`;
|
|
5005
|
-
}
|
|
5006
|
-
async listForOrganization(organizationId) {
|
|
5007
|
-
return await connection_default`
|
|
5008
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
5009
|
-
FROM organization_member
|
|
5010
|
-
WHERE organization_id = ${organizationId}
|
|
5011
|
-
ORDER BY id
|
|
5012
|
-
`;
|
|
5013
|
-
}
|
|
5014
|
-
async addMember(input) {
|
|
5015
|
-
const rows = await connection_default`
|
|
5016
|
-
INSERT INTO organization_member (organization_id, user_id, role)
|
|
5017
|
-
VALUES (${input.organizationId}, ${input.userId}, ${input.role ?? "member"})
|
|
5018
|
-
RETURNING id, organization_id, user_id, role, created_at
|
|
5019
|
-
`;
|
|
5020
|
-
const row = rows[0];
|
|
5021
|
-
if (!row) {
|
|
5022
|
-
throw new Error("Organization member insert did not return a row.");
|
|
5023
|
-
}
|
|
5024
|
-
return row;
|
|
5025
|
-
}
|
|
5026
|
-
async removeMember(organizationId, userId) {
|
|
5027
|
-
const rows = await connection_default`
|
|
5028
|
-
DELETE FROM organization_member
|
|
5029
|
-
WHERE organization_id = ${organizationId} AND user_id = ${userId}
|
|
5030
|
-
RETURNING id
|
|
5031
|
-
`;
|
|
5032
|
-
return rows.length > 0;
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
var memberRepository_default = OrganizationMemberRepository;
|
|
5036
|
-
|
|
5037
|
-
// ../../src/core/auth/membershipContext.ts
|
|
5038
|
-
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
5039
|
-
var membershipRepository = new memberRepository_default;
|
|
5040
|
-
|
|
5041
|
-
// ../../src/core/auth/membershipService.ts
|
|
5042
|
-
class MembershipService {
|
|
5043
|
-
members;
|
|
5044
|
-
constructor(members = membershipRepository) {
|
|
5045
|
-
this.members = members;
|
|
5046
|
-
}
|
|
5047
|
-
async listOrganizationIdsForUser(userId) {
|
|
5048
|
-
const memberships = await this.members.listForUser(userId);
|
|
5049
|
-
return memberships.map((membership) => membership.organization_id);
|
|
5050
|
-
}
|
|
5051
|
-
async getOrgRole(userId, organizationId) {
|
|
5052
|
-
const membership = await this.members.findMembership(userId, organizationId);
|
|
5053
|
-
return membership?.role ?? null;
|
|
5054
|
-
}
|
|
5055
|
-
async requireOrgAccess(organizationId, minimumRole = "member", user = currentAuthUser()) {
|
|
5056
|
-
if (!user) {
|
|
5057
|
-
throw new ForbiddenError("Authentication required.");
|
|
5058
|
-
}
|
|
5059
|
-
if (isGlobalAdmin(user)) {
|
|
5060
|
-
return "owner";
|
|
5061
|
-
}
|
|
5062
|
-
const role = await this.getOrgRole(resolveUserId(user), organizationId);
|
|
5063
|
-
if (!role || !hasMinimumOrgRole(role, minimumRole)) {
|
|
5064
|
-
throw new ForbiddenError("Organization membership required.");
|
|
5065
|
-
}
|
|
5066
|
-
return role;
|
|
5067
|
-
}
|
|
5068
|
-
async filterAccessibleOrganizationIds(organizationIds, user = currentAuthUser()) {
|
|
5069
|
-
if (!user) {
|
|
5070
|
-
return [];
|
|
5071
|
-
}
|
|
5072
|
-
if (isGlobalAdmin(user)) {
|
|
5073
|
-
return organizationIds;
|
|
5074
|
-
}
|
|
5075
|
-
const allowed = new Set(await this.listOrganizationIdsForUser(resolveUserId(user)));
|
|
5076
|
-
return organizationIds.filter((organizationId) => allowed.has(organizationId));
|
|
5077
|
-
}
|
|
5078
|
-
async addOwnerOnOrganizationCreate(organizationId, userId) {
|
|
5079
|
-
await this.members.addMember({
|
|
5080
|
-
organizationId,
|
|
5081
|
-
userId,
|
|
5082
|
-
role: "owner"
|
|
5083
|
-
});
|
|
5084
|
-
}
|
|
5085
|
-
listMembersForOrganization(organizationId) {
|
|
5086
|
-
return this.members.listForOrganization(organizationId);
|
|
5087
|
-
}
|
|
5088
|
-
addMember(input) {
|
|
5089
|
-
return this.members.addMember(input);
|
|
5090
|
-
}
|
|
5091
|
-
removeMember(organizationId, userId) {
|
|
5092
|
-
return this.members.removeMember(organizationId, userId);
|
|
5093
|
-
}
|
|
5094
|
-
}
|
|
5095
|
-
var membershipService_default = MembershipService;
|
|
5096
|
-
|
|
5097
|
-
// ../../src/core/auth/resolveMembershipService.ts
|
|
5098
|
-
function resolveMembershipService() {
|
|
5099
|
-
const dependencies = resolveApplicationDependencies();
|
|
5100
|
-
if (dependencies.container.has("core.membership")) {
|
|
5101
|
-
return dependencies.container.resolve("core.membership");
|
|
5102
|
-
}
|
|
5103
|
-
return new membershipService_default;
|
|
5104
|
-
}
|
|
5105
|
-
// ../../src/bootstrap/prefixRouteMap.ts
|
|
5106
|
-
function prefixRouteMap(prefix, routes) {
|
|
5107
|
-
const normalizedPrefix = prefix.replace(/\/$/, "");
|
|
5108
|
-
const prefixed = {};
|
|
5109
|
-
for (const [path, handler] of Object.entries(routes)) {
|
|
5110
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
5111
|
-
prefixed[`${normalizedPrefix}${normalizedPath}`] = handler;
|
|
5112
|
-
}
|
|
5113
|
-
return prefixed;
|
|
5114
|
-
}
|
|
4888
|
+
// ../../src/bootstrap/membershipService.ts
|
|
4889
|
+
import { resolveMembershipService } from "@getstrata/core/auth/membershipService";
|
|
5115
4890
|
// ../../src/core/http/csrfProtection.ts
|
|
5116
4891
|
var DEFAULT_CSRF_TTL_MS = 60 * 60 * 1000;
|
|
5117
4892
|
function createCsrfProtection(secret, options = {}) {
|
|
@@ -5206,7 +4981,7 @@ function createRouteKernel(dependencies) {
|
|
|
5206
4981
|
return createHttpKernel(dependencies);
|
|
5207
4982
|
}
|
|
5208
4983
|
// ../../src/bootstrap/web/server.ts
|
|
5209
|
-
function
|
|
4984
|
+
function wrapRouteHandler(handler) {
|
|
5210
4985
|
return async (request) => {
|
|
5211
4986
|
const response = await handler(request);
|
|
5212
4987
|
return response ?? new Response("Not Found", { status: 404 });
|
|
@@ -5216,7 +4991,7 @@ function convertAppRoutesToBunRoutes(routes) {
|
|
|
5216
4991
|
const bunRoutes = {};
|
|
5217
4992
|
for (const [path, handler] of Object.entries(routes)) {
|
|
5218
4993
|
if (typeof handler === "function") {
|
|
5219
|
-
bunRoutes[path] = { GET:
|
|
4994
|
+
bunRoutes[path] = { GET: wrapRouteHandler(handler) };
|
|
5220
4995
|
continue;
|
|
5221
4996
|
}
|
|
5222
4997
|
if (handler && typeof handler === "object" && !Array.isArray(handler)) {
|
|
@@ -5225,7 +5000,7 @@ function convertAppRoutesToBunRoutes(routes) {
|
|
|
5225
5000
|
if (typeof methodHandler !== "function") {
|
|
5226
5001
|
continue;
|
|
5227
5002
|
}
|
|
5228
|
-
methods[method.toUpperCase()] =
|
|
5003
|
+
methods[method.toUpperCase()] = wrapRouteHandler(methodHandler);
|
|
5229
5004
|
}
|
|
5230
5005
|
if (Object.keys(methods).length > 0) {
|
|
5231
5006
|
bunRoutes[path] = methods;
|
|
@@ -5340,7 +5115,7 @@ export {
|
|
|
5340
5115
|
securedBindRouteModel,
|
|
5341
5116
|
scheduleRunCommand,
|
|
5342
5117
|
runProviderPhase,
|
|
5343
|
-
runDueScheduledTasks,
|
|
5118
|
+
runDueScheduledTasks2 as runDueScheduledTasks,
|
|
5344
5119
|
routeRegistry,
|
|
5345
5120
|
routeParams,
|
|
5346
5121
|
resolveService,
|
|
@@ -5372,11 +5147,13 @@ export {
|
|
|
5372
5147
|
configureModulesDirectory,
|
|
5373
5148
|
collectProviders,
|
|
5374
5149
|
cacheTagsForModelWrite,
|
|
5150
|
+
buildWebModuleRoutes,
|
|
5151
|
+
buildModuleRoutes,
|
|
5375
5152
|
assertProductionSecrets,
|
|
5376
5153
|
assertAppDependenciesComplete,
|
|
5377
|
-
appSchedule,
|
|
5154
|
+
appSchedule2 as appSchedule,
|
|
5378
5155
|
ServiceContainer,
|
|
5379
|
-
Schedule,
|
|
5156
|
+
Schedule2 as Schedule,
|
|
5380
5157
|
RouteRegistry,
|
|
5381
5158
|
REDIS_URL_CONFIG_KEY,
|
|
5382
5159
|
DEFAULT_APP_PORT,
|