@getstrata/bootstrap 0.2.49 → 0.2.50
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/CHANGELOG.md +8 -0
- package/README.md +3 -1
- package/dist/bootstrap/config.d.ts +1 -1
- package/dist/bootstrap/health.d.ts +8 -2
- package/dist/bootstrap/public-api.d.ts +4 -2
- package/dist/bootstrap/web/index.d.ts +1 -1
- package/dist/bootstrap/web/session.d.ts +25 -3
- package/dist/entries/buildModuleRoutes.js +19 -4
- package/dist/entries/buildWebModuleRoutes.js +19 -4
- package/dist/entries/config.js +4 -0
- package/dist/entries/context.js +2 -0
- package/dist/entries/createRoutes.js +203 -155
- package/dist/entries/createSpaRoutes.js +1 -1
- package/dist/entries/createWebRoutes.js +19 -4
- package/dist/entries/dependencies.js +2 -0
- package/dist/entries/health.js +69 -152
- package/dist/entries/httpKernel.js +19 -4
- package/dist/entries/providers.js +2 -0
- package/dist/entries/secretsGuard.js +64 -10
- package/dist/entries/web/routing.js +19 -4
- package/dist/entries/web/session.js +59 -7
- package/dist/index.js +240 -20
- package/package.json +5 -4
|
@@ -63,6 +63,7 @@ import { applyMiddlewareToRoutes } from "@getstrata/core/http/middleware";
|
|
|
63
63
|
|
|
64
64
|
// ../../src/bootstrap/httpKernel.ts
|
|
65
65
|
import { createMembershipMiddleware } from "@getstrata/core/auth/membershipMiddleware";
|
|
66
|
+
import { resolveAbilityChecker } from "@getstrata/core/contracts/serviceTokens";
|
|
66
67
|
import { createAuthMiddleware } from "@getstrata/core/http/authMiddleware";
|
|
67
68
|
import { createAuthorizeMiddleware } from "@getstrata/core/http/authorizeMiddleware";
|
|
68
69
|
import { createBodySizeLimitMiddleware } from "@getstrata/core/http/bodySizeLimitMiddleware";
|
|
@@ -105,24 +106,38 @@ function parsePositiveInt(value, fallback) {
|
|
|
105
106
|
}
|
|
106
107
|
return Math.trunc(parsed);
|
|
107
108
|
}
|
|
109
|
+
function parseWindowSeconds(secondsValue, msValue, fallback) {
|
|
110
|
+
if (secondsValue !== undefined && secondsValue.trim() !== "") {
|
|
111
|
+
return parsePositiveInt(secondsValue, fallback);
|
|
112
|
+
}
|
|
113
|
+
if (msValue !== undefined && msValue.trim() !== "") {
|
|
114
|
+
const parsedMs = Number(msValue);
|
|
115
|
+
if (Number.isFinite(parsedMs) && parsedMs > 0) {
|
|
116
|
+
return Math.max(1, Math.trunc(parsedMs / 1000));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return fallback;
|
|
120
|
+
}
|
|
108
121
|
function resolveLoginRateLimit() {
|
|
109
122
|
const defaults = isLocalAppEnv() ? LOCAL_LOGIN_RATE_LIMIT : PRODUCTION_LOGIN_RATE_LIMIT;
|
|
110
123
|
return {
|
|
111
124
|
maxAttempts: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
|
|
112
|
-
decaySeconds:
|
|
125
|
+
decaySeconds: parseWindowSeconds(process.env.LOGIN_RATE_LIMIT_WINDOW_SECONDS, process.env.LOGIN_RATE_LIMIT_WINDOW_MS, defaults.decaySeconds)
|
|
113
126
|
};
|
|
114
127
|
}
|
|
115
128
|
function resolveRegisterRateLimit() {
|
|
116
129
|
const defaults = isLocalAppEnv() ? { maxAttempts: 100, decaySeconds: 60 } : { maxAttempts: 10, decaySeconds: 60 };
|
|
117
130
|
return {
|
|
118
131
|
maxAttempts: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
|
|
119
|
-
decaySeconds:
|
|
132
|
+
decaySeconds: parseWindowSeconds(process.env.REGISTER_RATE_LIMIT_WINDOW_SECONDS, process.env.REGISTER_RATE_LIMIT_WINDOW_MS, defaults.decaySeconds)
|
|
120
133
|
};
|
|
121
134
|
}
|
|
122
135
|
|
|
123
136
|
// ../../src/bootstrap/config.ts
|
|
124
137
|
import {
|
|
138
|
+
CORE_ABILITY_CHECKER_TOKEN,
|
|
125
139
|
CORE_AUTH_TOKEN,
|
|
140
|
+
CORE_AUTH_USER_DIRECTORY_TOKEN,
|
|
126
141
|
CORE_CACHE_TOKEN,
|
|
127
142
|
CORE_CONFIG_TOKEN,
|
|
128
143
|
CORE_EVENT_BUS_TOKEN,
|
|
@@ -226,7 +241,7 @@ class HttpKernel {
|
|
|
226
241
|
}
|
|
227
242
|
wrapWebAbility(ability, handler) {
|
|
228
243
|
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
229
|
-
const abilityChecker = this.dependencies.container
|
|
244
|
+
const abilityChecker = resolveAbilityChecker(this.dependencies.container);
|
|
230
245
|
const requireAbility = createRequireAbilityMiddleware(abilityChecker);
|
|
231
246
|
const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
|
|
232
247
|
return this.wrapWeb(withMiddleware(...middleware)(handler));
|
|
@@ -250,7 +265,7 @@ class HttpKernel {
|
|
|
250
265
|
return withMiddleware(...middleware)(handler);
|
|
251
266
|
}
|
|
252
267
|
wrapAbility(ability, handler) {
|
|
253
|
-
const abilityChecker = this.dependencies.container
|
|
268
|
+
const abilityChecker = resolveAbilityChecker(this.dependencies.container);
|
|
254
269
|
const requireAbility = createRequireAbilityMiddleware(abilityChecker);
|
|
255
270
|
const middleware = [...this.group("authenticated"), requireAbility(ability)];
|
|
256
271
|
return withMiddleware(...middleware)(handler);
|
|
@@ -475,7 +490,7 @@ function createSpaRoutes(_dependencies) {
|
|
|
475
490
|
});
|
|
476
491
|
}
|
|
477
492
|
return jsonResponse({
|
|
478
|
-
error: "SPA build not found. Run `
|
|
493
|
+
error: "SPA build not found. Run `bun run build:frontend`."
|
|
479
494
|
}, { status: 503 });
|
|
480
495
|
},
|
|
481
496
|
"/": async () => Response.redirect("/app/", 302)
|
|
@@ -554,157 +569,46 @@ function mergeWebRoutes(dependencies, routes) {
|
|
|
554
569
|
}
|
|
555
570
|
|
|
556
571
|
// ../../src/bootstrap/health.ts
|
|
572
|
+
import { getBoundDatabaseConnection } from "@getstrata/core/database/boundConnection";
|
|
573
|
+
import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
|
|
557
574
|
import { jsonResponse as jsonResponse2 } from "@getstrata/core/http/response";
|
|
558
575
|
var {RedisClient } = globalThis.Bun;
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const parsed = Number.parseInt(process.env[name] ?? String(fallback), 10);
|
|
563
|
-
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
564
|
-
}
|
|
565
|
-
var databaseConfig = {
|
|
566
|
-
url: process.env.DATABASE_URL ?? "",
|
|
567
|
-
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
568
|
-
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
569
|
-
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
570
|
-
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
571
|
-
};
|
|
572
|
-
|
|
573
|
-
// ../../src/core/runtime/asyncContextStore.ts
|
|
574
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
575
|
-
function createAsyncContextStore(key) {
|
|
576
|
-
const symbol = Symbol.for(key);
|
|
577
|
-
const globalRecord = globalThis;
|
|
578
|
-
const existing = globalRecord[symbol];
|
|
579
|
-
if (existing) {
|
|
580
|
-
return existing;
|
|
581
|
-
}
|
|
582
|
-
const store = new AsyncLocalStorage;
|
|
583
|
-
globalRecord[symbol] = store;
|
|
584
|
-
return store;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// ../../src/core/database/connectionContext.ts
|
|
588
|
-
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
589
|
-
function getActiveDatabaseConnection(fallback) {
|
|
590
|
-
return activeConnection.getStore() ?? fallback;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
// ../../src/core/database/queryProxy.ts
|
|
594
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
595
|
-
function createDatabaseQueryProxy(pool) {
|
|
596
|
-
function resolveDatabase() {
|
|
597
|
-
return getActiveDatabaseConnection(pool);
|
|
598
|
-
}
|
|
599
|
-
function resolveDatabaseForProperty(property) {
|
|
600
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
601
|
-
return pool;
|
|
602
|
-
}
|
|
603
|
-
return resolveDatabase();
|
|
604
|
-
}
|
|
605
|
-
return new Proxy(function database() {}, {
|
|
606
|
-
apply(_target, _thisArg, args) {
|
|
607
|
-
return resolveDatabase()(...args);
|
|
608
|
-
},
|
|
609
|
-
get(_target, property) {
|
|
610
|
-
const connection = resolveDatabaseForProperty(property);
|
|
611
|
-
const value = connection[property];
|
|
612
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
613
|
-
}
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// ../../src/core/database/defaultConnection.ts
|
|
618
|
-
var defaultPool = {
|
|
619
|
-
connection: null
|
|
620
|
-
};
|
|
621
|
-
var defaultQuery = {
|
|
622
|
-
connection: null
|
|
623
|
-
};
|
|
624
|
-
function registerDefaultDatabasePool(connection) {
|
|
625
|
-
defaultPool.connection = connection;
|
|
626
|
-
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
627
|
-
}
|
|
628
|
-
function getDefaultDatabaseQuery() {
|
|
629
|
-
if (!defaultQuery.connection) {
|
|
630
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
576
|
+
function resolveRedisUrl(dependencies) {
|
|
577
|
+
if (!dependencies.container.has(CORE_CONFIG_TOKEN)) {
|
|
578
|
+
return process.env.REDIS_URL?.trim() || undefined;
|
|
631
579
|
}
|
|
632
|
-
|
|
580
|
+
const config = dependencies.container.resolve(CORE_CONFIG_TOKEN);
|
|
581
|
+
const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim();
|
|
582
|
+
return redisUrl || undefined;
|
|
633
583
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
if (!config.url) {
|
|
639
|
-
throw new Error("DATABASE_URL is not configured. Set DATABASE_URL before starting the app or running integration tests.");
|
|
584
|
+
function resolveHealthDatabase() {
|
|
585
|
+
const bound = getBoundDatabaseConnection();
|
|
586
|
+
if (bound) {
|
|
587
|
+
return bound;
|
|
640
588
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
maxLifetime: config.maxLifetimeSeconds,
|
|
646
|
-
connectionTimeout: config.connectionTimeoutSeconds
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
// ../../src/db/connection/index.ts
|
|
651
|
-
var connectionHolder = {
|
|
652
|
-
connection: null
|
|
653
|
-
};
|
|
654
|
-
function getDatabase() {
|
|
655
|
-
if (!connectionHolder.connection) {
|
|
656
|
-
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
657
|
-
registerDefaultDatabasePool(connectionHolder.connection);
|
|
589
|
+
try {
|
|
590
|
+
return getDefaultDatabasePool();
|
|
591
|
+
} catch {
|
|
592
|
+
return null;
|
|
658
593
|
}
|
|
659
|
-
return connectionHolder.connection;
|
|
660
594
|
}
|
|
661
|
-
function
|
|
662
|
-
getDatabase();
|
|
663
|
-
return getDefaultDatabaseQuery();
|
|
664
|
-
}
|
|
665
|
-
async function pingDatabase(connection = getDatabase()) {
|
|
595
|
+
async function pingDatabaseClient(connection) {
|
|
666
596
|
try {
|
|
667
|
-
await connection
|
|
597
|
+
await connection.unsafe("SELECT 1");
|
|
668
598
|
return true;
|
|
669
599
|
} catch {
|
|
670
600
|
return false;
|
|
671
601
|
}
|
|
672
602
|
}
|
|
673
|
-
async function
|
|
674
|
-
|
|
675
|
-
|
|
603
|
+
async function checkDatabase() {
|
|
604
|
+
const connection = resolveHealthDatabase();
|
|
605
|
+
if (!connection) {
|
|
606
|
+
return false;
|
|
676
607
|
}
|
|
677
|
-
await
|
|
678
|
-
return;
|
|
679
|
-
});
|
|
680
|
-
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
681
|
-
registerDefaultDatabasePool(connectionHolder.connection);
|
|
682
|
-
return getDatabase();
|
|
608
|
+
return await pingDatabaseClient(connection);
|
|
683
609
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
return getDb()(...args);
|
|
687
|
-
},
|
|
688
|
-
get(_target, property) {
|
|
689
|
-
const connection = getDb();
|
|
690
|
-
const value = connection[property];
|
|
691
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
692
|
-
}
|
|
693
|
-
});
|
|
694
|
-
var connection_default = db;
|
|
695
|
-
|
|
696
|
-
// ../../src/bootstrap/health.ts
|
|
697
|
-
function resolveRedisUrl(dependencies) {
|
|
698
|
-
if (!dependencies.container.has(CORE_CONFIG_TOKEN)) {
|
|
699
|
-
return process.env.REDIS_URL?.trim() || undefined;
|
|
700
|
-
}
|
|
701
|
-
const config = dependencies.container.resolve(CORE_CONFIG_TOKEN);
|
|
702
|
-
const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim();
|
|
703
|
-
return redisUrl || undefined;
|
|
704
|
-
}
|
|
705
|
-
async function checkDatabase() {
|
|
706
|
-
await ensureDatabaseConnection();
|
|
707
|
-
return await pingDatabase();
|
|
610
|
+
async function pingDatabase() {
|
|
611
|
+
return await checkDatabase();
|
|
708
612
|
}
|
|
709
613
|
async function checkRedis(redisUrl) {
|
|
710
614
|
try {
|
|
@@ -715,23 +619,46 @@ async function checkRedis(redisUrl) {
|
|
|
715
619
|
return false;
|
|
716
620
|
}
|
|
717
621
|
}
|
|
718
|
-
function
|
|
622
|
+
async function resolveExtraFields(extra) {
|
|
623
|
+
if (!extra) {
|
|
624
|
+
return {};
|
|
625
|
+
}
|
|
626
|
+
return typeof extra === "function" ? await extra() : extra;
|
|
627
|
+
}
|
|
628
|
+
async function collectDependencyChecks(dependencies) {
|
|
629
|
+
const checks = {
|
|
630
|
+
database: await checkDatabase() ? "ok" : "error"
|
|
631
|
+
};
|
|
632
|
+
const redisUrl = resolveRedisUrl(dependencies);
|
|
633
|
+
if (redisUrl) {
|
|
634
|
+
checks.redis = await checkRedis(redisUrl) ? "ok" : "error";
|
|
635
|
+
} else {
|
|
636
|
+
checks.redis = "skipped";
|
|
637
|
+
}
|
|
638
|
+
const ready = checks.database === "ok" && (checks.redis === "ok" || checks.redis === "skipped");
|
|
639
|
+
return { checks, ready };
|
|
640
|
+
}
|
|
641
|
+
function createHealthRoutes(dependencies, options = {}) {
|
|
719
642
|
return {
|
|
720
|
-
"/health": async () =>
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
};
|
|
725
|
-
const redisUrl = resolveRedisUrl(dependencies);
|
|
726
|
-
if (redisUrl) {
|
|
727
|
-
checks.redis = await checkRedis(redisUrl) ? "ok" : "error";
|
|
728
|
-
} else {
|
|
729
|
-
checks.redis = "skipped";
|
|
643
|
+
"/health": async () => {
|
|
644
|
+
const extra = await resolveExtraFields(options.extra);
|
|
645
|
+
if (!options.pingOnHealth) {
|
|
646
|
+
return jsonResponse2({ status: "ok", ...extra });
|
|
730
647
|
}
|
|
731
|
-
const
|
|
648
|
+
const { checks, ready } = await collectDependencyChecks(dependencies);
|
|
649
|
+
return jsonResponse2({
|
|
650
|
+
status: ready ? "ok" : "error",
|
|
651
|
+
checks,
|
|
652
|
+
...extra
|
|
653
|
+
}, { status: ready ? 200 : 503 });
|
|
654
|
+
},
|
|
655
|
+
"/ready": async () => {
|
|
656
|
+
const extra = await resolveExtraFields(options.extra);
|
|
657
|
+
const { checks, ready } = await collectDependencyChecks(dependencies);
|
|
732
658
|
return jsonResponse2({
|
|
733
659
|
status: ready ? "ready" : "not_ready",
|
|
734
|
-
checks
|
|
660
|
+
checks,
|
|
661
|
+
...extra
|
|
735
662
|
}, { status: ready ? 200 : 503 });
|
|
736
663
|
}
|
|
737
664
|
};
|
|
@@ -838,6 +765,123 @@ var SCIM_SCHEMAS = {
|
|
|
838
765
|
serviceProviderConfig: "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"
|
|
839
766
|
};
|
|
840
767
|
|
|
768
|
+
// ../../src/config/database.ts
|
|
769
|
+
function readInteger(name, fallback) {
|
|
770
|
+
const parsed = Number.parseInt(process.env[name] ?? String(fallback), 10);
|
|
771
|
+
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
772
|
+
}
|
|
773
|
+
var databaseConfig = {
|
|
774
|
+
url: process.env.DATABASE_URL ?? "",
|
|
775
|
+
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
776
|
+
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
777
|
+
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
778
|
+
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
// ../../src/core/runtime/asyncContextStore.ts
|
|
782
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
783
|
+
function createAsyncContextStore(key) {
|
|
784
|
+
const symbol = Symbol.for(key);
|
|
785
|
+
const globalRecord = globalThis;
|
|
786
|
+
const existing = globalRecord[symbol];
|
|
787
|
+
if (existing) {
|
|
788
|
+
return existing;
|
|
789
|
+
}
|
|
790
|
+
const store = new AsyncLocalStorage;
|
|
791
|
+
globalRecord[symbol] = store;
|
|
792
|
+
return store;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// ../../src/core/database/connectionContext.ts
|
|
796
|
+
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
797
|
+
function getActiveDatabaseConnection(fallback) {
|
|
798
|
+
return activeConnection.getStore() ?? fallback;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// ../../src/core/database/queryProxy.ts
|
|
802
|
+
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
803
|
+
function createDatabaseQueryProxy(pool) {
|
|
804
|
+
function resolveDatabase() {
|
|
805
|
+
return getActiveDatabaseConnection(pool);
|
|
806
|
+
}
|
|
807
|
+
function resolveDatabaseForProperty(property) {
|
|
808
|
+
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
809
|
+
return pool;
|
|
810
|
+
}
|
|
811
|
+
return resolveDatabase();
|
|
812
|
+
}
|
|
813
|
+
return new Proxy(function database() {}, {
|
|
814
|
+
apply(_target, _thisArg, args) {
|
|
815
|
+
return resolveDatabase()(...args);
|
|
816
|
+
},
|
|
817
|
+
get(_target, property) {
|
|
818
|
+
const connection = resolveDatabaseForProperty(property);
|
|
819
|
+
const value = connection[property];
|
|
820
|
+
return typeof value === "function" ? value.bind(connection) : value;
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// ../../src/core/database/defaultConnection.ts
|
|
826
|
+
var defaultPool = {
|
|
827
|
+
connection: null
|
|
828
|
+
};
|
|
829
|
+
var defaultQuery = {
|
|
830
|
+
connection: null
|
|
831
|
+
};
|
|
832
|
+
function registerDefaultDatabasePool(connection) {
|
|
833
|
+
defaultPool.connection = connection;
|
|
834
|
+
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
835
|
+
}
|
|
836
|
+
function getDefaultDatabaseQuery() {
|
|
837
|
+
if (!defaultQuery.connection) {
|
|
838
|
+
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
839
|
+
}
|
|
840
|
+
return defaultQuery.connection;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// ../../src/db/connection/createConnection.ts
|
|
844
|
+
var {SQL } = globalThis.Bun;
|
|
845
|
+
function createDatabaseConnection(config) {
|
|
846
|
+
if (!config.url) {
|
|
847
|
+
throw new Error("DATABASE_URL is not configured. Set DATABASE_URL before starting the app or running integration tests.");
|
|
848
|
+
}
|
|
849
|
+
return new SQL({
|
|
850
|
+
url: config.url,
|
|
851
|
+
max: config.poolMax,
|
|
852
|
+
idleTimeout: config.idleTimeoutSeconds,
|
|
853
|
+
maxLifetime: config.maxLifetimeSeconds,
|
|
854
|
+
connectionTimeout: config.connectionTimeoutSeconds
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// ../../src/db/connection/index.ts
|
|
859
|
+
var connectionHolder = {
|
|
860
|
+
connection: null
|
|
861
|
+
};
|
|
862
|
+
function getDatabase() {
|
|
863
|
+
if (!connectionHolder.connection) {
|
|
864
|
+
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
865
|
+
registerDefaultDatabasePool(connectionHolder.connection);
|
|
866
|
+
}
|
|
867
|
+
return connectionHolder.connection;
|
|
868
|
+
}
|
|
869
|
+
function getDb() {
|
|
870
|
+
getDatabase();
|
|
871
|
+
return getDefaultDatabaseQuery();
|
|
872
|
+
}
|
|
873
|
+
var db = new Proxy(function database() {}, {
|
|
874
|
+
apply(_target, _thisArg, args) {
|
|
875
|
+
return getDb()(...args);
|
|
876
|
+
},
|
|
877
|
+
get(_target, property) {
|
|
878
|
+
const connection = getDb();
|
|
879
|
+
const value = connection[property];
|
|
880
|
+
return typeof value === "function" ? value.bind(connection) : value;
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
var connection_default = db;
|
|
884
|
+
|
|
841
885
|
// ../../src/modules/organization/memberRepository.ts
|
|
842
886
|
class OrganizationMemberRepository {
|
|
843
887
|
constructor() {}
|
|
@@ -938,7 +982,11 @@ class OrganizationRepository extends BaseRepository {
|
|
|
938
982
|
var repository_default = OrganizationRepository;
|
|
939
983
|
|
|
940
984
|
// ../../src/modules/user/provider.ts
|
|
941
|
-
import {
|
|
985
|
+
import {
|
|
986
|
+
CORE_ABILITY_CHECKER_TOKEN as CORE_ABILITY_CHECKER_TOKEN2,
|
|
987
|
+
CORE_AUTH_USER_DIRECTORY_TOKEN as CORE_AUTH_USER_DIRECTORY_TOKEN2,
|
|
988
|
+
CORE_TOKEN_SERVICE_TOKEN as CORE_TOKEN_SERVICE_TOKEN2
|
|
989
|
+
} from "@getstrata/bootstrap/config";
|
|
942
990
|
import { OidcProvider } from "@getstrata/core/auth/oauth/oidcProvider";
|
|
943
991
|
import { GitHubOAuthProvider, MockOAuthProvider } from "@getstrata/core/auth/oauth/providers";
|
|
944
992
|
import { SamlProvider } from "@getstrata/core/auth/oauth/samlProvider";
|
|
@@ -42,7 +42,7 @@ function createSpaRoutes(_dependencies) {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
return jsonResponse({
|
|
45
|
-
error: "SPA build not found. Run `
|
|
45
|
+
error: "SPA build not found. Run `bun run build:frontend`."
|
|
46
46
|
}, { status: 503 });
|
|
47
47
|
},
|
|
48
48
|
"/": async () => Response.redirect("/app/", 302)
|
|
@@ -32,6 +32,7 @@ import { applyMiddlewareToRoutes } from "@getstrata/core/http/middleware";
|
|
|
32
32
|
|
|
33
33
|
// ../../src/bootstrap/httpKernel.ts
|
|
34
34
|
import { createMembershipMiddleware } from "@getstrata/core/auth/membershipMiddleware";
|
|
35
|
+
import { resolveAbilityChecker } from "@getstrata/core/contracts/serviceTokens";
|
|
35
36
|
import { createAuthMiddleware } from "@getstrata/core/http/authMiddleware";
|
|
36
37
|
import { createAuthorizeMiddleware } from "@getstrata/core/http/authorizeMiddleware";
|
|
37
38
|
import { createBodySizeLimitMiddleware } from "@getstrata/core/http/bodySizeLimitMiddleware";
|
|
@@ -74,24 +75,38 @@ function parsePositiveInt(value, fallback) {
|
|
|
74
75
|
}
|
|
75
76
|
return Math.trunc(parsed);
|
|
76
77
|
}
|
|
78
|
+
function parseWindowSeconds(secondsValue, msValue, fallback) {
|
|
79
|
+
if (secondsValue !== undefined && secondsValue.trim() !== "") {
|
|
80
|
+
return parsePositiveInt(secondsValue, fallback);
|
|
81
|
+
}
|
|
82
|
+
if (msValue !== undefined && msValue.trim() !== "") {
|
|
83
|
+
const parsedMs = Number(msValue);
|
|
84
|
+
if (Number.isFinite(parsedMs) && parsedMs > 0) {
|
|
85
|
+
return Math.max(1, Math.trunc(parsedMs / 1000));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return fallback;
|
|
89
|
+
}
|
|
77
90
|
function resolveLoginRateLimit() {
|
|
78
91
|
const defaults = isLocalAppEnv() ? LOCAL_LOGIN_RATE_LIMIT : PRODUCTION_LOGIN_RATE_LIMIT;
|
|
79
92
|
return {
|
|
80
93
|
maxAttempts: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
|
|
81
|
-
decaySeconds:
|
|
94
|
+
decaySeconds: parseWindowSeconds(process.env.LOGIN_RATE_LIMIT_WINDOW_SECONDS, process.env.LOGIN_RATE_LIMIT_WINDOW_MS, defaults.decaySeconds)
|
|
82
95
|
};
|
|
83
96
|
}
|
|
84
97
|
function resolveRegisterRateLimit() {
|
|
85
98
|
const defaults = isLocalAppEnv() ? { maxAttempts: 100, decaySeconds: 60 } : { maxAttempts: 10, decaySeconds: 60 };
|
|
86
99
|
return {
|
|
87
100
|
maxAttempts: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
|
|
88
|
-
decaySeconds:
|
|
101
|
+
decaySeconds: parseWindowSeconds(process.env.REGISTER_RATE_LIMIT_WINDOW_SECONDS, process.env.REGISTER_RATE_LIMIT_WINDOW_MS, defaults.decaySeconds)
|
|
89
102
|
};
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
// ../../src/bootstrap/config.ts
|
|
93
106
|
import {
|
|
107
|
+
CORE_ABILITY_CHECKER_TOKEN,
|
|
94
108
|
CORE_AUTH_TOKEN,
|
|
109
|
+
CORE_AUTH_USER_DIRECTORY_TOKEN,
|
|
95
110
|
CORE_CACHE_TOKEN,
|
|
96
111
|
CORE_CONFIG_TOKEN,
|
|
97
112
|
CORE_EVENT_BUS_TOKEN,
|
|
@@ -195,7 +210,7 @@ class HttpKernel {
|
|
|
195
210
|
}
|
|
196
211
|
wrapWebAbility(ability, handler) {
|
|
197
212
|
const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
|
|
198
|
-
const abilityChecker = this.dependencies.container
|
|
213
|
+
const abilityChecker = resolveAbilityChecker(this.dependencies.container);
|
|
199
214
|
const requireAbility = createRequireAbilityMiddleware(abilityChecker);
|
|
200
215
|
const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
|
|
201
216
|
return this.wrapWeb(withMiddleware(...middleware)(handler));
|
|
@@ -219,7 +234,7 @@ class HttpKernel {
|
|
|
219
234
|
return withMiddleware(...middleware)(handler);
|
|
220
235
|
}
|
|
221
236
|
wrapAbility(ability, handler) {
|
|
222
|
-
const abilityChecker = this.dependencies.container
|
|
237
|
+
const abilityChecker = resolveAbilityChecker(this.dependencies.container);
|
|
223
238
|
const requireAbility = createRequireAbilityMiddleware(abilityChecker);
|
|
224
239
|
const middleware = [...this.group("authenticated"), requireAbility(ability)];
|
|
225
240
|
return withMiddleware(...middleware)(handler);
|