@getstrata/core 0.5.56 → 0.5.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{domain/abilities.d.ts → core/auth/abilityCatalog.d.ts} +9 -1
- package/dist/core/auth/accessControl.d.ts +1 -1
- package/dist/core/auth/membershipContext.d.ts +6 -4
- package/dist/core/auth/membershipService.d.ts +5 -6
- package/dist/core/contracts/authUserDirectory.d.ts +11 -0
- package/dist/core/contracts/membership.d.ts +20 -0
- package/dist/core/http/clientIp.d.ts +3 -0
- package/dist/core/jobs/dispatchWebhookJob.d.ts +2 -0
- package/dist/core/tenant/tenancyConfig.d.ts +5 -0
- package/dist/core/tenant/tenantMiddleware.d.ts +1 -1
- package/dist/entries/auth/membershipMiddleware.js +21 -161
- package/dist/entries/auth/membershipScope.js +20 -160
- package/dist/entries/auth/membershipService.js +21 -161
- package/dist/entries/auth/scimAuthMiddleware.js +22 -4
- package/dist/entries/auth/sessionGuard.js +12 -258
- package/dist/entries/http/clientIp.js +19 -0
- package/dist/entries/http/csrfMiddleware.js +2 -1
- package/dist/entries/http/csrfToken.js +2 -1
- package/dist/entries/http/loginThrottleMiddleware.js +18 -1
- package/dist/entries/http/memoryThrottleMiddleware.js +16 -1
- package/dist/entries/http/response.js +28 -297
- package/dist/entries/http/scimThrottleMiddleware.js +40 -1
- package/dist/entries/http/throttleMiddleware.js +18 -1
- package/dist/entries/http/webErrorResponse.js +28 -297
- package/dist/entries/jobs/dispatchWebhookJob.js +111 -5
- package/dist/entries/logging/requestLoggingMiddleware.js +16 -1
- package/dist/entries/security/scimTenantTokens.js +1 -4
- package/dist/entries/tenant/tenancyConfig.js +12 -0
- package/dist/entries/tenant/tenantDatabaseScope.js +11 -0
- package/dist/entries/view.js +26 -295
- package/dist/framework/public-api.d.ts +4 -1
- package/dist/index.js +2186 -2243
- package/package.json +12 -2
- package/dist/config/database.d.ts +0 -10
- package/dist/db/connection/createConnection.d.ts +0 -6
- package/dist/db/connection/index.d.ts +0 -11
- package/dist/domain/auth.d.ts +0 -4
- package/dist/domain/scim.d.ts +0 -9
- package/dist/modules/organization/memberRepository.d.ts +0 -14
- package/dist/modules/organization/memberTypes.d.ts +0 -9
- 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/README.md
CHANGED
|
@@ -82,7 +82,7 @@ import type { Migration } from "@getstrata/core/database/migrations/types";
|
|
|
82
82
|
Package name: **`@getstrata/core`** (npm org [`@getstrata`](https://www.npmjs.com/org/getstrata)).
|
|
83
83
|
|
|
84
84
|
1. Add `NPM_TOKEN` to GitHub repository secrets.
|
|
85
|
-
2. Tag a release: `git tag v0.5.
|
|
85
|
+
2. Tag a release: `git tag v0.5.58 && git push origin v0.5.58`
|
|
86
86
|
3. [Release workflow](../../.github/workflows/release.yml) builds and runs `npm publish --access public`.
|
|
87
87
|
|
|
88
88
|
Previously published as `@eyk-workhub/framework@0.1.0`, deprecated in favor of this package.
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
declare const MEMBER_ABILITIES: readonly ["organizations:read", "projects:read", "projects:create", "tasks:read", "tasks:create", "comments:read", "comments:create", "attachments:read", "attachments:create", "auth:tokens:read", "auth:tokens:write"];
|
|
2
2
|
declare const ADMIN_ABILITIES: readonly ["organizations:read", "projects:read", "projects:create", "tasks:read", "tasks:create", "comments:read", "comments:create", "attachments:read", "attachments:create", "auth:tokens:read", "auth:tokens:write", "organizations:create", "organizations:update", "organizations:delete", "projects:update", "projects:delete", "tasks:update", "tasks:delete", "comments:update", "comments:delete", "attachments:delete", "webhooks:read", "webhooks:write", "audit:read"];
|
|
3
3
|
declare const PLATFORM_ADMIN_ABILITIES: readonly ["*"];
|
|
4
|
+
interface AbilityCatalog {
|
|
5
|
+
member: readonly string[];
|
|
6
|
+
admin: readonly string[];
|
|
7
|
+
resolveForRole(role: string | null | undefined): string[];
|
|
8
|
+
}
|
|
4
9
|
declare function resolveAbilitiesForRole(role: string | null | undefined): string[];
|
|
5
|
-
|
|
10
|
+
declare function configureAbilityCatalog(next: AbilityCatalog): void;
|
|
11
|
+
declare function abilityCatalog(): AbilityCatalog;
|
|
12
|
+
export type { AbilityCatalog };
|
|
13
|
+
export { ADMIN_ABILITIES, abilityCatalog, configureAbilityCatalog, MEMBER_ABILITIES, PLATFORM_ADMIN_ABILITIES, resolveAbilitiesForRole, };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OrganizationMemberRole } from "
|
|
1
|
+
import type { OrganizationMemberRole } from "../contracts/membership";
|
|
2
2
|
import { type AuthUser } from "./authContext";
|
|
3
3
|
declare const ROLE_RANK: Record<OrganizationMemberRole, number>;
|
|
4
4
|
declare function isGlobalAdmin(user: AuthUser | null): boolean;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { OrganizationMemberRole } from "../../modules/organization/memberTypes";
|
|
1
|
+
import type { MembershipLookup, OrganizationMemberRole } from "../contracts/membership";
|
|
3
2
|
type MembershipContext = {
|
|
4
3
|
organizationIds: number[];
|
|
5
4
|
rolesByOrganizationId: Map<number, OrganizationMemberRole>;
|
|
6
5
|
};
|
|
7
6
|
declare const membershipContext: import("node:async_hooks").AsyncLocalStorage<MembershipContext>;
|
|
8
|
-
declare
|
|
7
|
+
declare let membershipRepository: MembershipLookup;
|
|
8
|
+
declare function configureMembershipLookup(lookup: MembershipLookup): void;
|
|
9
|
+
declare function resolveMembershipLookup(): MembershipLookup;
|
|
10
|
+
declare function resetMembershipLookupForTests(): void;
|
|
9
11
|
declare function runWithMembershipContext<T>(callback: () => T | Promise<T>): Promise<T | Promise<T>>;
|
|
10
12
|
declare function currentOrgRole(organizationId: number): OrganizationMemberRole | null;
|
|
11
13
|
declare function hasOrgMembership(organizationId: number): boolean;
|
|
12
14
|
declare function currentOrganizationIds(): number[];
|
|
13
15
|
declare function hasMinimumOrgRole(organizationId: number, minimum: OrganizationMemberRole): boolean;
|
|
14
16
|
export type { MembershipContext };
|
|
15
|
-
export { currentOrganizationIds, currentOrgRole, hasMinimumOrgRole, hasOrgMembership, membershipContext, membershipRepository, runWithMembershipContext, };
|
|
17
|
+
export { configureMembershipLookup, currentOrganizationIds, currentOrgRole, hasMinimumOrgRole, hasOrgMembership, membershipContext, membershipRepository, resetMembershipLookupForTests, resolveMembershipLookup, runWithMembershipContext, };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { OrganizationMemberRole } from "
|
|
1
|
+
import type { MembershipLookup, OrganizationMemberRole } from "../contracts/membership";
|
|
2
2
|
import { type AuthUser } from "./authContext";
|
|
3
|
-
|
|
4
|
-
type MembershipRepositoryLike = Pick<typeof defaultMembershipRepository, "listForUser" | "findMembership" | "listForOrganization" | "addMember" | "removeMember">;
|
|
3
|
+
type MembershipRepositoryLike = MembershipLookup;
|
|
5
4
|
declare class MembershipService {
|
|
6
5
|
private readonly members;
|
|
7
6
|
constructor(members?: MembershipRepositoryLike);
|
|
@@ -10,13 +9,13 @@ declare class MembershipService {
|
|
|
10
9
|
requireOrgAccess(organizationId: number, minimumRole?: OrganizationMemberRole, user?: AuthUser | null): Promise<OrganizationMemberRole>;
|
|
11
10
|
filterAccessibleOrganizationIds(organizationIds: number[], user?: AuthUser | null): Promise<number[]>;
|
|
12
11
|
addOwnerOnOrganizationCreate(organizationId: number, userId: number): Promise<void>;
|
|
13
|
-
listMembersForOrganization(organizationId: number): Promise<import("
|
|
12
|
+
listMembersForOrganization(organizationId: number): Promise<import("../contracts/membership").MembershipRecord[]>;
|
|
14
13
|
addMember(input: {
|
|
15
14
|
organizationId: number;
|
|
16
15
|
userId: number;
|
|
17
16
|
role?: OrganizationMemberRole;
|
|
18
|
-
}): Promise<import("
|
|
19
|
-
removeMember(organizationId: number, userId: number): Promise<
|
|
17
|
+
}): Promise<import("../contracts/membership").MembershipRecord>;
|
|
18
|
+
removeMember(organizationId: number, userId: number): Promise<unknown>;
|
|
20
19
|
}
|
|
21
20
|
export type { MembershipRepositoryLike };
|
|
22
21
|
export default MembershipService;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AuthUser } from "../auth/authContext";
|
|
2
|
+
interface AuthUserRecord {
|
|
3
|
+
id: number;
|
|
4
|
+
email?: string | null;
|
|
5
|
+
role: string;
|
|
6
|
+
}
|
|
7
|
+
interface AuthUserDirectory {
|
|
8
|
+
resolveUserFromToken(token: string): Promise<AuthUser | null>;
|
|
9
|
+
findByIdOrThrow(id: number): Promise<AuthUserRecord>;
|
|
10
|
+
}
|
|
11
|
+
export type { AuthUserDirectory, AuthUserRecord };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type OrganizationMemberRole = "owner" | "admin" | "member";
|
|
2
|
+
interface MembershipRecord {
|
|
3
|
+
id: number;
|
|
4
|
+
organization_id: number;
|
|
5
|
+
user_id: number;
|
|
6
|
+
role: OrganizationMemberRole;
|
|
7
|
+
created_at: Date;
|
|
8
|
+
}
|
|
9
|
+
interface MembershipLookup {
|
|
10
|
+
listForUser(userId: number): Promise<MembershipRecord[]>;
|
|
11
|
+
findMembership(userId: number, organizationId: number): Promise<MembershipRecord | null>;
|
|
12
|
+
listForOrganization(organizationId: number): Promise<MembershipRecord[]>;
|
|
13
|
+
addMember(input: {
|
|
14
|
+
organizationId: number;
|
|
15
|
+
userId: number;
|
|
16
|
+
role?: OrganizationMemberRole;
|
|
17
|
+
}): Promise<MembershipRecord>;
|
|
18
|
+
removeMember(organizationId: number, userId: number): Promise<unknown>;
|
|
19
|
+
}
|
|
20
|
+
export type { MembershipLookup, MembershipRecord, OrganizationMemberRole };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Job } from "../queue";
|
|
2
2
|
interface DispatchWebhookPayload {
|
|
3
3
|
webhookId: number;
|
|
4
|
+
tenantId: number;
|
|
4
5
|
event: string;
|
|
5
6
|
payload: Record<string, unknown>;
|
|
6
7
|
}
|
|
@@ -8,6 +9,7 @@ declare class DispatchWebhookJob extends Job<DispatchWebhookPayload> {
|
|
|
8
9
|
readonly maxAttempts = 3;
|
|
9
10
|
readonly backoffMs = 2000;
|
|
10
11
|
handle(payload: DispatchWebhookPayload): Promise<void>;
|
|
12
|
+
private deliver;
|
|
11
13
|
}
|
|
12
14
|
export { DispatchWebhookJob };
|
|
13
15
|
export default DispatchWebhookJob;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type TenancyDriver = "rls" | "none";
|
|
2
|
+
declare function readTenancyDriver(env?: Record<string, string | undefined>): TenancyDriver;
|
|
3
|
+
declare function isTenancyEnabled(env?: Record<string, string | undefined>): boolean;
|
|
4
|
+
export type { TenancyDriver };
|
|
5
|
+
export { isTenancyEnabled, readTenancyDriver };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type TenantContext } from "./tenantContext";
|
|
2
2
|
declare const DEFAULT_TENANT: TenantContext;
|
|
3
3
|
declare function resolveUserTenantId(userId: number): Promise<number>;
|
|
4
4
|
declare function auditChecksum(payload: Record<string, unknown>): string;
|
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// ../../src/config/database.ts
|
|
3
|
-
function readInteger(name, fallback) {
|
|
4
|
-
const parsed = Number.parseInt(process.env[name] ?? String(fallback), 10);
|
|
5
|
-
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
6
|
-
}
|
|
7
|
-
var databaseConfig = {
|
|
8
|
-
url: process.env.DATABASE_URL ?? "",
|
|
9
|
-
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
10
|
-
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
11
|
-
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
12
|
-
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
13
|
-
};
|
|
14
|
-
|
|
15
2
|
// ../../src/core/runtime/asyncContextStore.ts
|
|
16
3
|
import { AsyncLocalStorage } from "async_hooks";
|
|
17
4
|
function createAsyncContextStore(key) {
|
|
@@ -26,153 +13,6 @@ function createAsyncContextStore(key) {
|
|
|
26
13
|
return store;
|
|
27
14
|
}
|
|
28
15
|
|
|
29
|
-
// ../../src/core/database/connectionContext.ts
|
|
30
|
-
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
31
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
32
|
-
return activeConnection.run(connection, callback);
|
|
33
|
-
}
|
|
34
|
-
function getActiveDatabaseConnection(fallback) {
|
|
35
|
-
return activeConnection.getStore() ?? fallback;
|
|
36
|
-
}
|
|
37
|
-
function hasActiveDatabaseConnection() {
|
|
38
|
-
return activeConnection.getStore() !== undefined;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ../../src/core/database/queryProxy.ts
|
|
42
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
43
|
-
function createDatabaseQueryProxy(pool) {
|
|
44
|
-
function resolveDatabase() {
|
|
45
|
-
return getActiveDatabaseConnection(pool);
|
|
46
|
-
}
|
|
47
|
-
function resolveDatabaseForProperty(property) {
|
|
48
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
49
|
-
return pool;
|
|
50
|
-
}
|
|
51
|
-
return resolveDatabase();
|
|
52
|
-
}
|
|
53
|
-
return new Proxy(function database() {}, {
|
|
54
|
-
apply(_target, _thisArg, args) {
|
|
55
|
-
return resolveDatabase()(...args);
|
|
56
|
-
},
|
|
57
|
-
get(_target, property) {
|
|
58
|
-
const connection = resolveDatabaseForProperty(property);
|
|
59
|
-
const value = connection[property];
|
|
60
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ../../src/core/database/defaultConnection.ts
|
|
66
|
-
var defaultPool = {
|
|
67
|
-
connection: null
|
|
68
|
-
};
|
|
69
|
-
var defaultQuery = {
|
|
70
|
-
connection: null
|
|
71
|
-
};
|
|
72
|
-
function registerDefaultDatabasePool(connection) {
|
|
73
|
-
defaultPool.connection = connection;
|
|
74
|
-
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
75
|
-
}
|
|
76
|
-
function getDefaultDatabaseQuery() {
|
|
77
|
-
if (!defaultQuery.connection) {
|
|
78
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
79
|
-
}
|
|
80
|
-
return defaultQuery.connection;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ../../src/db/connection/createConnection.ts
|
|
84
|
-
var {SQL } = globalThis.Bun;
|
|
85
|
-
function createDatabaseConnection(config) {
|
|
86
|
-
if (!config.url) {
|
|
87
|
-
throw new Error("DATABASE_URL is not configured. Set DATABASE_URL before starting the app or running integration tests.");
|
|
88
|
-
}
|
|
89
|
-
return new SQL({
|
|
90
|
-
url: config.url,
|
|
91
|
-
max: config.poolMax,
|
|
92
|
-
idleTimeout: config.idleTimeoutSeconds,
|
|
93
|
-
maxLifetime: config.maxLifetimeSeconds,
|
|
94
|
-
connectionTimeout: config.connectionTimeoutSeconds
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ../../src/db/connection/index.ts
|
|
99
|
-
var connectionHolder = {
|
|
100
|
-
connection: null
|
|
101
|
-
};
|
|
102
|
-
function getDatabase() {
|
|
103
|
-
if (!connectionHolder.connection) {
|
|
104
|
-
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
105
|
-
registerDefaultDatabasePool(connectionHolder.connection);
|
|
106
|
-
}
|
|
107
|
-
return connectionHolder.connection;
|
|
108
|
-
}
|
|
109
|
-
function getDb() {
|
|
110
|
-
getDatabase();
|
|
111
|
-
return getDefaultDatabaseQuery();
|
|
112
|
-
}
|
|
113
|
-
var db = new Proxy(function database() {}, {
|
|
114
|
-
apply(_target, _thisArg, args) {
|
|
115
|
-
return getDb()(...args);
|
|
116
|
-
},
|
|
117
|
-
get(_target, property) {
|
|
118
|
-
const connection = getDb();
|
|
119
|
-
const value = connection[property];
|
|
120
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
var connection_default = db;
|
|
124
|
-
|
|
125
|
-
// ../../src/modules/organization/memberRepository.ts
|
|
126
|
-
class OrganizationMemberRepository {
|
|
127
|
-
constructor() {}
|
|
128
|
-
async findMembership(userId, organizationId) {
|
|
129
|
-
const rows = await connection_default`
|
|
130
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
131
|
-
FROM organization_member
|
|
132
|
-
WHERE user_id = ${userId} AND organization_id = ${organizationId}
|
|
133
|
-
LIMIT 1
|
|
134
|
-
`;
|
|
135
|
-
return rows[0] ?? null;
|
|
136
|
-
}
|
|
137
|
-
async listForUser(userId) {
|
|
138
|
-
return await connection_default`
|
|
139
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
140
|
-
FROM organization_member
|
|
141
|
-
WHERE user_id = ${userId}
|
|
142
|
-
ORDER BY organization_id
|
|
143
|
-
`;
|
|
144
|
-
}
|
|
145
|
-
async listForOrganization(organizationId) {
|
|
146
|
-
return await connection_default`
|
|
147
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
148
|
-
FROM organization_member
|
|
149
|
-
WHERE organization_id = ${organizationId}
|
|
150
|
-
ORDER BY id
|
|
151
|
-
`;
|
|
152
|
-
}
|
|
153
|
-
async addMember(input) {
|
|
154
|
-
const rows = await connection_default`
|
|
155
|
-
INSERT INTO organization_member (organization_id, user_id, role)
|
|
156
|
-
VALUES (${input.organizationId}, ${input.userId}, ${input.role ?? "member"})
|
|
157
|
-
RETURNING id, organization_id, user_id, role, created_at
|
|
158
|
-
`;
|
|
159
|
-
const row = rows[0];
|
|
160
|
-
if (!row) {
|
|
161
|
-
throw new Error("Organization member insert did not return a row.");
|
|
162
|
-
}
|
|
163
|
-
return row;
|
|
164
|
-
}
|
|
165
|
-
async removeMember(organizationId, userId) {
|
|
166
|
-
const rows = await connection_default`
|
|
167
|
-
DELETE FROM organization_member
|
|
168
|
-
WHERE organization_id = ${organizationId} AND user_id = ${userId}
|
|
169
|
-
RETURNING id
|
|
170
|
-
`;
|
|
171
|
-
return rows.length > 0;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
var memberRepository_default = OrganizationMemberRepository;
|
|
175
|
-
|
|
176
16
|
// ../../src/core/auth/accessControl.ts
|
|
177
17
|
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
178
18
|
|
|
@@ -214,7 +54,27 @@ function resolveUserId(user) {
|
|
|
214
54
|
|
|
215
55
|
// ../../src/core/auth/membershipContext.ts
|
|
216
56
|
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
217
|
-
var
|
|
57
|
+
var uninitializedMembershipLookup = {
|
|
58
|
+
async listForUser() {
|
|
59
|
+
return [];
|
|
60
|
+
},
|
|
61
|
+
async findMembership() {
|
|
62
|
+
return null;
|
|
63
|
+
},
|
|
64
|
+
async listForOrganization() {
|
|
65
|
+
return [];
|
|
66
|
+
},
|
|
67
|
+
async addMember() {
|
|
68
|
+
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
69
|
+
},
|
|
70
|
+
async removeMember() {
|
|
71
|
+
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var membershipRepository = uninitializedMembershipLookup;
|
|
75
|
+
function resolveMembershipLookup() {
|
|
76
|
+
return membershipRepository;
|
|
77
|
+
}
|
|
218
78
|
async function runWithMembershipContext(callback) {
|
|
219
79
|
const user = currentAuthUser();
|
|
220
80
|
if (!user || isGlobalAdmin(user)) {
|
|
@@ -56,169 +56,29 @@ function resolveUserId(user) {
|
|
|
56
56
|
return userId;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// ../../src/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
var databaseConfig = {
|
|
65
|
-
url: process.env.DATABASE_URL ?? "",
|
|
66
|
-
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
67
|
-
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
68
|
-
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
69
|
-
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// ../../src/core/database/connectionContext.ts
|
|
73
|
-
var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
74
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
75
|
-
return activeConnection.run(connection, callback);
|
|
76
|
-
}
|
|
77
|
-
function getActiveDatabaseConnection(fallback) {
|
|
78
|
-
return activeConnection.getStore() ?? fallback;
|
|
79
|
-
}
|
|
80
|
-
function hasActiveDatabaseConnection() {
|
|
81
|
-
return activeConnection.getStore() !== undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ../../src/core/database/queryProxy.ts
|
|
85
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
86
|
-
function createDatabaseQueryProxy(pool) {
|
|
87
|
-
function resolveDatabase() {
|
|
88
|
-
return getActiveDatabaseConnection(pool);
|
|
89
|
-
}
|
|
90
|
-
function resolveDatabaseForProperty(property) {
|
|
91
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
92
|
-
return pool;
|
|
93
|
-
}
|
|
94
|
-
return resolveDatabase();
|
|
95
|
-
}
|
|
96
|
-
return new Proxy(function database() {}, {
|
|
97
|
-
apply(_target, _thisArg, args) {
|
|
98
|
-
return resolveDatabase()(...args);
|
|
99
|
-
},
|
|
100
|
-
get(_target, property) {
|
|
101
|
-
const connection = resolveDatabaseForProperty(property);
|
|
102
|
-
const value = connection[property];
|
|
103
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ../../src/core/database/defaultConnection.ts
|
|
109
|
-
var defaultPool = {
|
|
110
|
-
connection: null
|
|
111
|
-
};
|
|
112
|
-
var defaultQuery = {
|
|
113
|
-
connection: null
|
|
114
|
-
};
|
|
115
|
-
function registerDefaultDatabasePool(connection) {
|
|
116
|
-
defaultPool.connection = connection;
|
|
117
|
-
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
118
|
-
}
|
|
119
|
-
function getDefaultDatabaseQuery() {
|
|
120
|
-
if (!defaultQuery.connection) {
|
|
121
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
122
|
-
}
|
|
123
|
-
return defaultQuery.connection;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ../../src/db/connection/createConnection.ts
|
|
127
|
-
var {SQL } = globalThis.Bun;
|
|
128
|
-
function createDatabaseConnection(config) {
|
|
129
|
-
if (!config.url) {
|
|
130
|
-
throw new Error("DATABASE_URL is not configured. Set DATABASE_URL before starting the app or running integration tests.");
|
|
131
|
-
}
|
|
132
|
-
return new SQL({
|
|
133
|
-
url: config.url,
|
|
134
|
-
max: config.poolMax,
|
|
135
|
-
idleTimeout: config.idleTimeoutSeconds,
|
|
136
|
-
maxLifetime: config.maxLifetimeSeconds,
|
|
137
|
-
connectionTimeout: config.connectionTimeoutSeconds
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ../../src/db/connection/index.ts
|
|
142
|
-
var connectionHolder = {
|
|
143
|
-
connection: null
|
|
144
|
-
};
|
|
145
|
-
function getDatabase() {
|
|
146
|
-
if (!connectionHolder.connection) {
|
|
147
|
-
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
148
|
-
registerDefaultDatabasePool(connectionHolder.connection);
|
|
149
|
-
}
|
|
150
|
-
return connectionHolder.connection;
|
|
151
|
-
}
|
|
152
|
-
function getDb() {
|
|
153
|
-
getDatabase();
|
|
154
|
-
return getDefaultDatabaseQuery();
|
|
155
|
-
}
|
|
156
|
-
var db = new Proxy(function database() {}, {
|
|
157
|
-
apply(_target, _thisArg, args) {
|
|
158
|
-
return getDb()(...args);
|
|
59
|
+
// ../../src/core/auth/membershipContext.ts
|
|
60
|
+
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
61
|
+
var uninitializedMembershipLookup = {
|
|
62
|
+
async listForUser() {
|
|
63
|
+
return [];
|
|
159
64
|
},
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
async findMembership(userId, organizationId) {
|
|
172
|
-
const rows = await connection_default`
|
|
173
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
174
|
-
FROM organization_member
|
|
175
|
-
WHERE user_id = ${userId} AND organization_id = ${organizationId}
|
|
176
|
-
LIMIT 1
|
|
177
|
-
`;
|
|
178
|
-
return rows[0] ?? null;
|
|
179
|
-
}
|
|
180
|
-
async listForUser(userId) {
|
|
181
|
-
return await connection_default`
|
|
182
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
183
|
-
FROM organization_member
|
|
184
|
-
WHERE user_id = ${userId}
|
|
185
|
-
ORDER BY organization_id
|
|
186
|
-
`;
|
|
187
|
-
}
|
|
188
|
-
async listForOrganization(organizationId) {
|
|
189
|
-
return await connection_default`
|
|
190
|
-
SELECT id, organization_id, user_id, role, created_at
|
|
191
|
-
FROM organization_member
|
|
192
|
-
WHERE organization_id = ${organizationId}
|
|
193
|
-
ORDER BY id
|
|
194
|
-
`;
|
|
195
|
-
}
|
|
196
|
-
async addMember(input) {
|
|
197
|
-
const rows = await connection_default`
|
|
198
|
-
INSERT INTO organization_member (organization_id, user_id, role)
|
|
199
|
-
VALUES (${input.organizationId}, ${input.userId}, ${input.role ?? "member"})
|
|
200
|
-
RETURNING id, organization_id, user_id, role, created_at
|
|
201
|
-
`;
|
|
202
|
-
const row = rows[0];
|
|
203
|
-
if (!row) {
|
|
204
|
-
throw new Error("Organization member insert did not return a row.");
|
|
205
|
-
}
|
|
206
|
-
return row;
|
|
207
|
-
}
|
|
208
|
-
async removeMember(organizationId, userId) {
|
|
209
|
-
const rows = await connection_default`
|
|
210
|
-
DELETE FROM organization_member
|
|
211
|
-
WHERE organization_id = ${organizationId} AND user_id = ${userId}
|
|
212
|
-
RETURNING id
|
|
213
|
-
`;
|
|
214
|
-
return rows.length > 0;
|
|
65
|
+
async findMembership() {
|
|
66
|
+
return null;
|
|
67
|
+
},
|
|
68
|
+
async listForOrganization() {
|
|
69
|
+
return [];
|
|
70
|
+
},
|
|
71
|
+
async addMember() {
|
|
72
|
+
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
73
|
+
},
|
|
74
|
+
async removeMember() {
|
|
75
|
+
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
215
76
|
}
|
|
77
|
+
};
|
|
78
|
+
var membershipRepository = uninitializedMembershipLookup;
|
|
79
|
+
function resolveMembershipLookup() {
|
|
80
|
+
return membershipRepository;
|
|
216
81
|
}
|
|
217
|
-
var memberRepository_default = OrganizationMemberRepository;
|
|
218
|
-
|
|
219
|
-
// ../../src/core/auth/membershipContext.ts
|
|
220
|
-
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
221
|
-
var membershipRepository = new memberRepository_default;
|
|
222
82
|
async function runWithMembershipContext(callback) {
|
|
223
83
|
const user = currentAuthUser();
|
|
224
84
|
if (!user || isGlobalAdmin(user)) {
|