@de-otio/trellis 0.9.0 → 0.10.1
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/lambda/delete-account-worker.d.ts.map +1 -1
- package/dist/lambda/delete-account-worker.js +1 -16
- package/dist/lambda/delete-account-worker.js.map +1 -1
- package/dist/lambda/hourly-cron.d.ts.map +1 -1
- package/dist/lambda/hourly-cron.js +1 -17
- package/dist/lambda/hourly-cron.js.map +1 -1
- package/dist/lambda/maintenance-cron.d.ts.map +1 -1
- package/dist/lambda/maintenance-cron.js +1 -16
- package/dist/lambda/maintenance-cron.js.map +1 -1
- package/dist/lambda/media-processing-worker.js +1 -1
- package/dist/lambda/media-processing-worker.js.map +1 -1
- package/dist/lambda/nightly-cron.d.ts.map +1 -1
- package/dist/lambda/nightly-cron.js +2 -18
- package/dist/lambda/nightly-cron.js.map +1 -1
- package/dist/lambda/post-confirmation.d.ts.map +1 -1
- package/dist/lambda/post-confirmation.js +59 -19
- package/dist/lambda/post-confirmation.js.map +1 -1
- package/dist/lambda/pre-token-generation.d.ts.map +1 -1
- package/dist/lambda/pre-token-generation.js +1 -16
- package/dist/lambda/pre-token-generation.js.map +1 -1
- package/dist/lib/compliance/baseline.d.ts.map +1 -1
- package/dist/lib/compliance/baseline.js +1 -9
- package/dist/lib/compliance/baseline.js.map +1 -1
- package/dist/lib/data-router.d.ts.map +1 -1
- package/dist/lib/data-router.js +7 -0
- package/dist/lib/data-router.js.map +1 -1
- package/dist/lib/entity-handler.d.ts.map +1 -1
- package/dist/lib/entity-handler.js +7 -1
- package/dist/lib/entity-handler.js.map +1 -1
- package/dist/lib/graph/graph-service.d.ts +7 -12
- package/dist/lib/graph/graph-service.d.ts.map +1 -1
- package/dist/lib/graph/graph-service.js +6 -11
- package/dist/lib/graph/graph-service.js.map +1 -1
- package/dist/lib/graph/scoring-engine.d.ts +0 -3
- package/dist/lib/graph/scoring-engine.d.ts.map +1 -1
- package/dist/lib/graph/scoring-engine.js +0 -3
- package/dist/lib/graph/scoring-engine.js.map +1 -1
- package/dist/lib/lambda-prisma.d.ts +16 -0
- package/dist/lib/lambda-prisma.d.ts.map +1 -0
- package/dist/lib/lambda-prisma.js +29 -0
- package/dist/lib/lambda-prisma.js.map +1 -0
- package/dist/lib/services/media-upload-service.js +2 -2
- package/dist/lib/services/media-upload-service.js.map +1 -1
- package/dist/lib/user/derive-handle.d.ts +22 -0
- package/dist/lib/user/derive-handle.d.ts.map +1 -1
- package/dist/lib/user/derive-handle.js +16 -0
- package/dist/lib/user/derive-handle.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20260606000000_handle_canonical_identity/migration.sql +18 -0
- package/prisma/schema.prisma +19 -13
- package/src/lambda/delete-account-worker.ts +1 -16
- package/src/lambda/hourly-cron.ts +1 -28
- package/src/lambda/maintenance-cron.ts +1 -21
- package/src/lambda/media-processing-worker.ts +1 -1
- package/src/lambda/nightly-cron.ts +2 -24
- package/src/lambda/post-confirmation.ts +77 -40
- package/src/lambda/pre-token-generation.ts +1 -24
|
@@ -67,7 +67,7 @@ export class MediaUploadService {
|
|
|
67
67
|
const fileBuffer = preReadBuffer ?? await file.arrayBuffer();
|
|
68
68
|
const contentHash = await generateContentHash(fileBuffer);
|
|
69
69
|
const ext = getExtensionFromMimeType(file.type);
|
|
70
|
-
const originalKey = `
|
|
70
|
+
const originalKey = `originals/user-${userId}/${contentHash}.${ext}`;
|
|
71
71
|
logger.info("[MediaUpload] Starting upload", {
|
|
72
72
|
contentHash,
|
|
73
73
|
mimeType: file.type,
|
|
@@ -155,7 +155,7 @@ export class MediaUploadService {
|
|
|
155
155
|
const fileBuffer = await file.arrayBuffer();
|
|
156
156
|
const contentHash = await generateContentHash(fileBuffer);
|
|
157
157
|
const ext = getExtensionFromMimeType(file.type);
|
|
158
|
-
const originalKey = `
|
|
158
|
+
const originalKey = `originals/user-${userId}/${contentHash}.${ext}`;
|
|
159
159
|
const metadata = metadataArray?.[index];
|
|
160
160
|
const r2Metadata = {
|
|
161
161
|
contentHash,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-upload-service.js","sourceRoot":"","sources":["../../../src/lib/services/media-upload-service.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAU,MAAM,cAAc,CAAC;AAOjD;;GAEG;AACH,SAAS,eAAe;IACtB,OAAO,SAAS,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC7E,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,mBAAmB,CAAC,IAAiB;IAClD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,QAAgB;IAChD,MAAM,OAAO,GAA2B;QACtC,YAAY,EAAE,KAAK;QACnB,WAAW,EAAE,KAAK;QAClB,WAAW,EAAE,KAAK;QAClB,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,MAAM;QACpB,YAAY,EAAE,MAAM;QACpB,YAAY,EAAE,MAAM;QACpB,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,MAAM;QACpB,iBAAiB,EAAE,KAAK;KACzB,CAAC;IACF,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC;AACpC,CAAC;AAED,MAAM,OAAO,kBAAkB;IACrB,QAAQ,CAAW;IACnB,KAAK,CAAoC;IACzC,SAAS,CAAS;IAClB,GAAG,CAAM;IAEjB,YAAY,GAAQ;QAClB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,eAAe,CAAC;QACpC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,0BAA0B,CAAC;QAC5C,IAAI,CAAC,SAAS;YACZ,GAAG,CAAC,WAAW,KAAK,MAAM;gBACxB,CAAC,CAAC,yBAAyB;gBAC3B,CAAC,CAAC,qBAAqB,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,IAAU,EACV,MAAc,EACd,QAAiE,EACjE,aAA2B;QAE3B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;QAElC,IAAI,CAAC;YACH,2BAA2B;YAC3B,yEAAyE;YACzE,wEAAwE;YACxE,MAAM,UAAU,GAAG,aAAa,IAAI,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;YAC7D,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,CAAC;YAC1D,MAAM,GAAG,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChD,MAAM,WAAW,GAAG,
|
|
1
|
+
{"version":3,"file":"media-upload-service.js","sourceRoot":"","sources":["../../../src/lib/services/media-upload-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,4 +26,26 @@ export declare function normalizeHandleBase(emailOrLocalPart: string | null | un
|
|
|
26
26
|
* use by some other user. Inject the Prisma lookup here.
|
|
27
27
|
*/
|
|
28
28
|
export declare function deriveHandle(email: string | null | undefined, exists: HandleExistsCheck): Promise<string>;
|
|
29
|
+
/** Minimal Prisma surface needed to collision-check a handle. */
|
|
30
|
+
type HandleCollisionDb = {
|
|
31
|
+
user: {
|
|
32
|
+
findFirst: (args: {
|
|
33
|
+
where: Record<string, unknown>;
|
|
34
|
+
select: {
|
|
35
|
+
id: true;
|
|
36
|
+
};
|
|
37
|
+
}) => Promise<{
|
|
38
|
+
id: string;
|
|
39
|
+
} | null>;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Convenience wrapper that wires {@link deriveHandle}'s collision check to a
|
|
44
|
+
* Prisma client, returning a globally-unique handle. Use this at every
|
|
45
|
+
* User-creation site so the non-null + unique `handle` invariant (S-CP2) holds
|
|
46
|
+
* regardless of which path provisions the user. Pass `excludeUserId` when
|
|
47
|
+
* back-filling a handle onto an existing row.
|
|
48
|
+
*/
|
|
49
|
+
export declare function deriveUniqueHandle(db: HandleCollisionDb, email: string | null | undefined, excludeUserId?: string): Promise<string>;
|
|
50
|
+
export {};
|
|
29
51
|
//# sourceMappingURL=derive-handle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-handle.d.ts","sourceRoot":"","sources":["../../../src/lib/user/derive-handle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAErE,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CASvF;AAMD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
|
|
1
|
+
{"version":3,"file":"derive-handle.d.ts","sourceRoot":"","sources":["../../../src/lib/user/derive-handle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,MAAM,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAErE,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CASvF;AAMD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED,iEAAiE;AACjE,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE;QACJ,SAAS,EAAE,CAAC,IAAI,EAAE;YAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC/B,MAAM,EAAE;gBAAE,EAAE,EAAE,IAAI,CAAA;aAAE,CAAC;SACtB,KAAK,OAAO,CAAC;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;KACtC,CAAC;CACH,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,iBAAiB,EACrB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CAQjB"}
|
|
@@ -58,4 +58,20 @@ export async function deriveHandle(email, exists) {
|
|
|
58
58
|
}
|
|
59
59
|
return `${base.slice(0, HANDLE_TOTAL_MAX - 8)}${randomSuffix()}${randomSuffix().slice(0, 4)}`.slice(0, HANDLE_TOTAL_MAX);
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Convenience wrapper that wires {@link deriveHandle}'s collision check to a
|
|
63
|
+
* Prisma client, returning a globally-unique handle. Use this at every
|
|
64
|
+
* User-creation site so the non-null + unique `handle` invariant (S-CP2) holds
|
|
65
|
+
* regardless of which path provisions the user. Pass `excludeUserId` when
|
|
66
|
+
* back-filling a handle onto an existing row.
|
|
67
|
+
*/
|
|
68
|
+
export async function deriveUniqueHandle(db, email, excludeUserId) {
|
|
69
|
+
return deriveHandle(email, async (h) => {
|
|
70
|
+
const where = excludeUserId
|
|
71
|
+
? { handle: h, NOT: { id: excludeUserId } }
|
|
72
|
+
: { handle: h };
|
|
73
|
+
const found = await db.user.findFirst({ where, select: { id: true } });
|
|
74
|
+
return !!found;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
61
77
|
//# sourceMappingURL=derive-handle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-handle.js","sourceRoot":"","sources":["../../../src/lib/user/derive-handle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAItC,MAAM,UAAU,mBAAmB,CAAC,gBAA2C;IAC7E,IAAI,CAAC,gBAAgB;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC9C,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC,CAAC,gBAAgB,CAAC;IACrB,MAAM,OAAO,GAAG,SAAS;SACtB,WAAW,EAAE;SACb,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAC/B,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,YAAY;IACnB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAgC,EAChC,MAAyB;IAEzB,IAAI,IAAI,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,GAAG,GAAG,sBAAsB,GAAG,YAAY,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC;IACvF,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,IAAI,sBAAsB,EAAE,MAAM,EAAE,EAAE,CAAC;QAChE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAC,MAAM,CAAC;QAC3D,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,GAAG,aAAa,GAAG,SAAS,EAAE,CAAC;QACjD,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;IACnD,CAAC;IAED,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CACjG,CAAC,EACD,gBAAgB,CACjB,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"derive-handle.js","sourceRoot":"","sources":["../../../src/lib/user/derive-handle.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- S-CP2: make `handle` the canonical, non-null, globally-unique identifier.
|
|
2
|
+
-- Backfill NULL handles and de-duplicate any pre-existing collisions BEFORE
|
|
3
|
+
-- adding the constraints, so the migration is safe against existing data
|
|
4
|
+
-- (handle previously had no DB-level uniqueness; app-layer checks could race).
|
|
5
|
+
|
|
6
|
+
-- 1. Backfill NULL handles deterministically from the row id (cuid; unique).
|
|
7
|
+
UPDATE "users" SET "handle" = 'user_' || "id" WHERE "handle" IS NULL;
|
|
8
|
+
|
|
9
|
+
-- 2. De-duplicate: any handle shared by >1 row gets suffixed with the row id
|
|
10
|
+
-- (id is the PK, so the result is guaranteed unique). Non-colliding handles
|
|
11
|
+
-- are left untouched.
|
|
12
|
+
UPDATE "users" u
|
|
13
|
+
SET "handle" = u."handle" || '_' || u."id"
|
|
14
|
+
WHERE (SELECT count(*) FROM "users" u2 WHERE u2."handle" = u."handle") > 1;
|
|
15
|
+
|
|
16
|
+
-- 3. Enforce the invariants: non-null + globally unique.
|
|
17
|
+
ALTER TABLE "users" ALTER COLUMN "handle" SET NOT NULL;
|
|
18
|
+
CREATE UNIQUE INDEX "users_handle_key" ON "users"("handle");
|
package/prisma/schema.prisma
CHANGED
|
@@ -43,7 +43,7 @@ model Entity {
|
|
|
43
43
|
|
|
44
44
|
// Co-ownership (replaces single ownerId)
|
|
45
45
|
owners EntityOwnership[]
|
|
46
|
-
// Relationships and follower counts live in the graph
|
|
46
|
+
// Relationships and follower counts live in the graph edge tables (served via graph-service.ts), not on this model
|
|
47
47
|
|
|
48
48
|
subjectPosts PostSubject[]
|
|
49
49
|
primaryPosts Post[] @relation("PrimaryEntityPosts")
|
|
@@ -93,10 +93,9 @@ model PostGeoIndex {
|
|
|
93
93
|
@@map("post_geo_index")
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/// Entity geo-location for proximity discovery (C7).
|
|
97
|
-
///
|
|
98
|
-
///
|
|
99
|
-
/// dual-write (syncEntity). Location is sensitive → own tenant_id for direct RLS
|
|
96
|
+
/// Entity geo-location for proximity discovery (C7). Geo-proximity is queried
|
|
97
|
+
/// with PostGIS (ST_DWithin / KNN over a GiST index) via $queryRaw. Populated from
|
|
98
|
+
/// syncEntity. Location is sensitive → own tenant_id for direct RLS
|
|
100
99
|
/// (mirrors post_geo_index). See plans/redesign/entity-location-subsystem.md.
|
|
101
100
|
model EntityLocation {
|
|
102
101
|
entityId String @id @map("entity_id")
|
|
@@ -173,8 +172,15 @@ model User {
|
|
|
173
172
|
id String @id @default(cuid())
|
|
174
173
|
email String @unique
|
|
175
174
|
role UserRole @default(END_USER)
|
|
176
|
-
actorUri String? @unique @map("actor_uri") // ActivityPub actor URI (e.g., "https://example.com/users/{
|
|
177
|
-
|
|
175
|
+
actorUri String? @unique @map("actor_uri") // ActivityPub actor URI (e.g., "https://example.com/users/{handle}")
|
|
176
|
+
// Canonical, portable, AP-actor-shaped identifier (S-CP2). The bare local
|
|
177
|
+
// label (e.g. "alice"); the addressable handle is `@{handle}@{domain}` and
|
|
178
|
+
// the actor URI is `{baseUrl}/users/{handle}`. Derived + collision-checked at
|
|
179
|
+
// provisioning; non-null + globally unique so federation is additive, never a
|
|
180
|
+
// migration. (WebFinger/AP still resolve on `username` until the federation
|
|
181
|
+
// repoint — that change is code-only, no data migration, since `handle` is the
|
|
182
|
+
// stable key.)
|
|
183
|
+
handle String @unique
|
|
178
184
|
createdAt DateTime @default(now()) @map("created_at")
|
|
179
185
|
|
|
180
186
|
// Cognito Auth integration
|
|
@@ -269,7 +275,7 @@ model User {
|
|
|
269
275
|
ownedEntities EntityOwnership[] @relation("EntityOwners")
|
|
270
276
|
addedOwnerships EntityOwnership[] @relation("OwnershipAddedBy")
|
|
271
277
|
|
|
272
|
-
// Relationships and follower counts live in the graph
|
|
278
|
+
// Relationships and follower counts live in the graph edge tables (served via graph-service.ts), not on this model
|
|
273
279
|
|
|
274
280
|
// Direct messages
|
|
275
281
|
sentMessages DirectMessage[] @relation("SentMessages")
|
|
@@ -1126,8 +1132,8 @@ model ProductTaxonomyTag {
|
|
|
1126
1132
|
}
|
|
1127
1133
|
|
|
1128
1134
|
// ============================================================================
|
|
1129
|
-
// Relationships (scored edges
|
|
1130
|
-
// Follow and Friendship models removed — replaced by
|
|
1135
|
+
// Relationships (scored edges; served via graph-service.ts over these tables)
|
|
1136
|
+
// Follow and Friendship models removed — replaced by the relationship edge tables below
|
|
1131
1137
|
// ============================================================================
|
|
1132
1138
|
|
|
1133
1139
|
// ============================================================================
|
|
@@ -1659,7 +1665,7 @@ model NotificationPreference {
|
|
|
1659
1665
|
}
|
|
1660
1666
|
|
|
1661
1667
|
// Out-of-band invite codes — users share codes privately; redemption establishes
|
|
1662
|
-
// a relationship
|
|
1668
|
+
// a relationship edge. Entity-scoped codes can be created by entity owners.
|
|
1663
1669
|
model ConnectionCode {
|
|
1664
1670
|
id String @id @default(cuid())
|
|
1665
1671
|
// Multi-tenancy (v0.7) — code's owning tenant (creator's active tenant at create time).
|
|
@@ -1927,8 +1933,8 @@ model TenantInvitation {
|
|
|
1927
1933
|
// Graph edges in Postgres (graph-db revisit 2026-06: see
|
|
1928
1934
|
// plans/redesign/graph-backend-contract.md)
|
|
1929
1935
|
//
|
|
1930
|
-
// These two tables hold the edges
|
|
1931
|
-
//
|
|
1936
|
+
// These two tables hold the relationship edges (user↔user and typed
|
|
1937
|
+
// entity-to-entity). OWNS (entity_ownerships) and
|
|
1932
1938
|
// ABOUT (post_subjects) already existed in Postgres. Modeled as standalone
|
|
1933
1939
|
// tables (no Prisma @relation) so the migration is self-contained and
|
|
1934
1940
|
// non-breaking; targetId is polymorphic (user|entity) so it cannot carry a
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { SQSHandler } from "aws-lambda";
|
|
2
|
-
import { PrismaPg } from "@prisma/adapter-pg";
|
|
3
2
|
import { PrismaClient } from "@prisma/client";
|
|
4
3
|
import { S3Client, DeleteObjectsCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
|
|
5
4
|
import { CognitoIdentityProviderClient, AdminDeleteUserCommand } from "@aws-sdk/client-cognito-identity-provider";
|
|
6
5
|
import { Logger } from "@aws-lambda-powertools/logger";
|
|
7
|
-
import {
|
|
6
|
+
import { getLambdaPrisma as getPrisma } from "../lib/lambda-prisma.js";
|
|
8
7
|
|
|
9
8
|
const logger = new Logger({ serviceName: "delete-account-worker" });
|
|
10
9
|
|
|
@@ -12,20 +11,6 @@ const s3 = new S3Client({ region: process.env.AWS_REGION });
|
|
|
12
11
|
|
|
13
12
|
const MAX_PAGES = 100;
|
|
14
13
|
|
|
15
|
-
let prisma: PrismaClient | null = null;
|
|
16
|
-
|
|
17
|
-
async function getPrisma(): Promise<PrismaClient> {
|
|
18
|
-
if (prisma) return prisma;
|
|
19
|
-
const { username, password, host, port, dbname } = (await getSecret(process.env.DB_SECRET_ARN!, { transform: "json" })) as unknown as { username: string; password: string; host: string; port: string | number; dbname: string };
|
|
20
|
-
// Prisma 7 removed the `datasources` constructor option; the connection URL
|
|
21
|
-
// is now supplied via a driver adapter.
|
|
22
|
-
const adapter = new PrismaPg({
|
|
23
|
-
connectionString: `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${dbname}?connection_limit=1`,
|
|
24
|
-
});
|
|
25
|
-
prisma = new PrismaClient({ adapter });
|
|
26
|
-
return prisma;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
14
|
async function deleteUserMedia(userId: string, mediaBucket: string): Promise<void> {
|
|
30
15
|
const prefix = `originals/user-${userId}/`;
|
|
31
16
|
let continuationToken: string | undefined;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Logger } from "@aws-lambda-powertools/logger";
|
|
2
2
|
import { Metrics, MetricUnit } from "@aws-lambda-powertools/metrics";
|
|
3
|
-
import { getSecret } from "@aws-lambda-powertools/parameters/secrets";
|
|
4
3
|
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
|
|
5
4
|
import { marshall } from "@aws-sdk/util-dynamodb";
|
|
6
|
-
import { PrismaPg } from "@prisma/adapter-pg";
|
|
7
5
|
import { PrismaClient } from "@prisma/client";
|
|
6
|
+
import { getLambdaPrisma as getPrisma } from "../lib/lambda-prisma.js";
|
|
8
7
|
import {
|
|
9
8
|
batchedPruneExpired,
|
|
10
9
|
resolveInteractionEventConfig,
|
|
@@ -23,32 +22,6 @@ const metrics = new Metrics({
|
|
|
23
22
|
const dynamo = new DynamoDBClient({ region: process.env.AWS_REGION });
|
|
24
23
|
const TABLE = process.env.DYNAMODB_TABLE!;
|
|
25
24
|
|
|
26
|
-
let prisma: PrismaClient | null = null;
|
|
27
|
-
|
|
28
|
-
interface DbSecret {
|
|
29
|
-
username: string;
|
|
30
|
-
password: string;
|
|
31
|
-
host: string;
|
|
32
|
-
port: string | number;
|
|
33
|
-
dbname: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function getPrisma(): Promise<PrismaClient> {
|
|
37
|
-
if (prisma) return prisma;
|
|
38
|
-
// getSecret caches + KMS-decrypts; transform:"json" parses the secret value.
|
|
39
|
-
const { username, password, host, port, dbname } = (await getSecret(
|
|
40
|
-
process.env.DB_SECRET_ARN!,
|
|
41
|
-
{ transform: "json" },
|
|
42
|
-
)) as unknown as DbSecret;
|
|
43
|
-
// Prisma 7 removed the `datasources` constructor option; the connection URL
|
|
44
|
-
// is now supplied via a driver adapter.
|
|
45
|
-
const adapter = new PrismaPg({
|
|
46
|
-
connectionString: `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${dbname}?connection_limit=1`,
|
|
47
|
-
});
|
|
48
|
-
prisma = new PrismaClient({ adapter });
|
|
49
|
-
return prisma;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
25
|
export const handler = async (): Promise<void> => {
|
|
53
26
|
const now = Math.floor(Date.now() / 1000);
|
|
54
27
|
|
|
@@ -1,34 +1,14 @@
|
|
|
1
1
|
import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
|
|
2
2
|
import { marshall } from "@aws-sdk/util-dynamodb";
|
|
3
|
-
import { PrismaPg } from "@prisma/adapter-pg";
|
|
4
3
|
import { PrismaClient } from "@prisma/client";
|
|
5
4
|
import { Logger } from "@aws-lambda-powertools/logger";
|
|
6
|
-
import {
|
|
5
|
+
import { getLambdaPrisma as getPrisma } from "../lib/lambda-prisma.js";
|
|
7
6
|
|
|
8
7
|
const logger = new Logger({ serviceName: "maintenance-cron" });
|
|
9
8
|
|
|
10
9
|
const dynamo = new DynamoDBClient({ region: process.env.AWS_REGION });
|
|
11
10
|
const TABLE = process.env.DYNAMODB_TABLE!;
|
|
12
11
|
|
|
13
|
-
let prisma: PrismaClient | null = null;
|
|
14
|
-
|
|
15
|
-
async function getPrisma(): Promise<PrismaClient> {
|
|
16
|
-
if (prisma) return prisma;
|
|
17
|
-
const { username, password, host, port, dbname } = (await getSecret(process.env.DB_SECRET_ARN!, { transform: "json" })) as unknown as {
|
|
18
|
-
username: string;
|
|
19
|
-
password: string;
|
|
20
|
-
host: string;
|
|
21
|
-
port: string | number;
|
|
22
|
-
dbname: string;
|
|
23
|
-
};
|
|
24
|
-
// Prisma 7 removed the `datasources` constructor option; the connection URL
|
|
25
|
-
// is now supplied via a driver adapter.
|
|
26
|
-
const adapter = new PrismaPg({
|
|
27
|
-
connectionString: `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${dbname}?connection_limit=1`,
|
|
28
|
-
});
|
|
29
|
-
prisma = new PrismaClient({ adapter });
|
|
30
|
-
return prisma;
|
|
31
|
-
}
|
|
32
12
|
|
|
33
13
|
export const handler = async (): Promise<void> => {
|
|
34
14
|
const now = Math.floor(Date.now() / 1000);
|
|
@@ -32,7 +32,7 @@ export const handler: SQSHandler = async (event) => {
|
|
|
32
32
|
// Process with Sharp (must be installed as ARM64 binary)
|
|
33
33
|
// dynamic import to avoid bundling issues
|
|
34
34
|
const sharp = (await import("sharp")).default;
|
|
35
|
-
const hash = key.
|
|
35
|
+
const hash = key.split("/").pop()!.replace(/\.[^.]+$/, "");
|
|
36
36
|
|
|
37
37
|
// Thumbnail: 300px WebP
|
|
38
38
|
const thumbnail = await sharp(buffer)
|
|
@@ -3,11 +3,10 @@ import { S3Client, DeleteObjectsCommand, ListObjectsV2Command } from "@aws-sdk/c
|
|
|
3
3
|
import { CognitoIdentityProviderClient, AdminDeleteUserCommand } from "@aws-sdk/client-cognito-identity-provider";
|
|
4
4
|
import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses";
|
|
5
5
|
import { marshall } from "@aws-sdk/util-dynamodb";
|
|
6
|
-
import { PrismaPg } from "@prisma/adapter-pg";
|
|
7
6
|
import { PrismaClient } from "@prisma/client";
|
|
8
7
|
import { Logger } from "@aws-lambda-powertools/logger";
|
|
9
8
|
import { Metrics, MetricUnit } from "@aws-lambda-powertools/metrics";
|
|
10
|
-
import {
|
|
9
|
+
import { getLambdaPrisma as getPrisma } from "../lib/lambda-prisma.js";
|
|
11
10
|
|
|
12
11
|
const logger = new Logger({ serviceName: "nightly-cron" });
|
|
13
12
|
const metrics = new Metrics({ namespace: "Trellis/Deletion", serviceName: "nightly-cron" });
|
|
@@ -17,26 +16,6 @@ const s3 = new S3Client({ region: process.env.AWS_REGION });
|
|
|
17
16
|
const TABLE = process.env.DYNAMODB_TABLE!;
|
|
18
17
|
const MEDIA_BUCKET = process.env.MEDIA_BUCKET_NAME!;
|
|
19
18
|
|
|
20
|
-
let prisma: PrismaClient | null = null;
|
|
21
|
-
|
|
22
|
-
async function getPrisma(): Promise<PrismaClient> {
|
|
23
|
-
if (prisma) return prisma;
|
|
24
|
-
const { username, password, host, port, dbname } = (await getSecret(process.env.DB_SECRET_ARN!, { transform: "json" })) as unknown as {
|
|
25
|
-
username: string;
|
|
26
|
-
password: string;
|
|
27
|
-
host: string;
|
|
28
|
-
port: string | number;
|
|
29
|
-
dbname: string;
|
|
30
|
-
};
|
|
31
|
-
// Prisma 7 removed the `datasources` constructor option; the connection URL
|
|
32
|
-
// is now supplied via a driver adapter.
|
|
33
|
-
const adapter = new PrismaPg({
|
|
34
|
-
connectionString: `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${dbname}?connection_limit=1`,
|
|
35
|
-
});
|
|
36
|
-
prisma = new PrismaClient({ adapter });
|
|
37
|
-
return prisma;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
19
|
export const handler = async (): Promise<void> => {
|
|
41
20
|
const now = Math.floor(Date.now() / 1000);
|
|
42
21
|
|
|
@@ -117,8 +96,7 @@ export const handler = async (): Promise<void> => {
|
|
|
117
96
|
logger.error("Invitation cleanup failed", { error: err });
|
|
118
97
|
}
|
|
119
98
|
|
|
120
|
-
// 3. Follower counts removed — relationships now live in graph
|
|
121
|
-
// TODO: Add graph-side consistency check when reconciliation service is wired up
|
|
99
|
+
// 3. Follower counts removed — relationships now live in graph edge tables (see graph-service.ts)
|
|
122
100
|
|
|
123
101
|
// 4. Process scheduled account deletions (GDPR Article 17 compliance)
|
|
124
102
|
try {
|
|
@@ -34,12 +34,11 @@ import type {
|
|
|
34
34
|
PostConfirmationTriggerHandler,
|
|
35
35
|
} from "aws-lambda";
|
|
36
36
|
import { Logger } from "@aws-lambda-powertools/logger";
|
|
37
|
-
import {
|
|
38
|
-
import { PrismaPg } from "@prisma/adapter-pg";
|
|
37
|
+
import { getLambdaPrisma as getPrisma } from "../lib/lambda-prisma.js";
|
|
39
38
|
import {
|
|
40
39
|
PrismaClient,
|
|
40
|
+
Prisma,
|
|
41
41
|
type AgeTier,
|
|
42
|
-
type Prisma,
|
|
43
42
|
type TenantRole,
|
|
44
43
|
type UserRole,
|
|
45
44
|
} from "@prisma/client";
|
|
@@ -55,30 +54,8 @@ import {
|
|
|
55
54
|
import type { SignupMethod } from "@prisma/client";
|
|
56
55
|
|
|
57
56
|
const logger = new Logger({ serviceName: "post-confirmation" });
|
|
58
|
-
let prisma: PrismaClient | null = null;
|
|
59
57
|
let cache: ClaimsCache | null = null;
|
|
60
58
|
|
|
61
|
-
async function getPrisma(): Promise<PrismaClient> {
|
|
62
|
-
if (prisma) return prisma;
|
|
63
|
-
const { username, password, host, port, dbname } = (await getSecret(
|
|
64
|
-
process.env.DB_SECRET_ARN!,
|
|
65
|
-
{ transform: "json" },
|
|
66
|
-
)) as unknown as {
|
|
67
|
-
username: string;
|
|
68
|
-
password: string;
|
|
69
|
-
host: string;
|
|
70
|
-
port: string | number;
|
|
71
|
-
dbname: string;
|
|
72
|
-
};
|
|
73
|
-
// Prisma 7 removed the `datasources` constructor option; the connection URL
|
|
74
|
-
// is now supplied via a driver adapter.
|
|
75
|
-
const adapter = new PrismaPg({
|
|
76
|
-
connectionString: `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${dbname}?connection_limit=1`,
|
|
77
|
-
});
|
|
78
|
-
prisma = new PrismaClient({ adapter });
|
|
79
|
-
return prisma;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
59
|
function getCache(): ClaimsCache {
|
|
83
60
|
if (!cache) cache = createClaimsCacheFromEnv();
|
|
84
61
|
return cache;
|
|
@@ -199,20 +176,22 @@ export const handler: PostConfirmationTriggerHandler = async (event) => {
|
|
|
199
176
|
|
|
200
177
|
const db = await getPrisma();
|
|
201
178
|
|
|
202
|
-
const result = await
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
179
|
+
const result = await withHandleConflictRetry(() =>
|
|
180
|
+
db.$transaction(
|
|
181
|
+
async (tx) => provisionUserAndTenancy(tx, {
|
|
182
|
+
cognitoSub,
|
|
183
|
+
email,
|
|
184
|
+
emailVerified: attrs.email_verified,
|
|
185
|
+
federated,
|
|
186
|
+
idpGroups,
|
|
187
|
+
dateOfBirth,
|
|
188
|
+
ageTier,
|
|
189
|
+
providedHandle: attrs["custom:handle"],
|
|
190
|
+
invitationCode,
|
|
191
|
+
requestedMethod,
|
|
192
|
+
}),
|
|
193
|
+
{ timeout: 8000 },
|
|
194
|
+
),
|
|
216
195
|
);
|
|
217
196
|
|
|
218
197
|
if (ageTier === "CHILD") {
|
|
@@ -280,6 +259,58 @@ interface ProvisioningInput {
|
|
|
280
259
|
requestedMethod: SignupMethod | undefined;
|
|
281
260
|
}
|
|
282
261
|
|
|
262
|
+
/**
|
|
263
|
+
* S-CP2: the canonical ActivityPub actor URI for a user, derived from the
|
|
264
|
+
* stable, unique `handle` — `{baseUrl}/users/{handle}`. Returns null when no
|
|
265
|
+
* base domain is configured for this lambda; the actorUri column then stays
|
|
266
|
+
* null and the AP dispatcher derives it on demand. No hard dependency on the
|
|
267
|
+
* env var, so this is safe whether or not the deploy plumbs APP_DOMAIN in.
|
|
268
|
+
*/
|
|
269
|
+
function canonicalActorUri(handle: string): string | null {
|
|
270
|
+
const raw = process.env.ACTIVITYPUB_BASE_URL || process.env.APP_DOMAIN;
|
|
271
|
+
if (!raw) return null;
|
|
272
|
+
try {
|
|
273
|
+
// Accept both a full URL ("https://host") and a bare hostname ("host").
|
|
274
|
+
const withProtocol = /^https?:\/\//.test(raw) ? raw : `https://${raw}`;
|
|
275
|
+
const url = new URL(withProtocol);
|
|
276
|
+
return `${url.protocol}//${url.hostname}/users/${encodeURIComponent(handle)}`;
|
|
277
|
+
} catch {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* S-CP2: retry the provisioning transaction when two concurrent signups race to
|
|
284
|
+
* the same derived handle. `handle` is DB-unique, so the loser of the race gets
|
|
285
|
+
* a P2002; re-running the transaction re-derives the handle (now seeing the
|
|
286
|
+
* committed conflict) and picks the next suffix. The transaction rolls back
|
|
287
|
+
* fully on failure, so retries have no partial-write side effects.
|
|
288
|
+
*/
|
|
289
|
+
async function withHandleConflictRetry<T>(
|
|
290
|
+
fn: () => Promise<T>,
|
|
291
|
+
maxAttempts = 3,
|
|
292
|
+
): Promise<T> {
|
|
293
|
+
for (let attempt = 1; ; attempt++) {
|
|
294
|
+
try {
|
|
295
|
+
return await fn();
|
|
296
|
+
} catch (err) {
|
|
297
|
+
const isHandleConflict =
|
|
298
|
+
err instanceof Prisma.PrismaClientKnownRequestError &&
|
|
299
|
+
err.code === "P2002" &&
|
|
300
|
+
String(
|
|
301
|
+
(err.meta as { target?: unknown } | undefined)?.target ?? "",
|
|
302
|
+
).includes("handle");
|
|
303
|
+
if (isHandleConflict && attempt < maxAttempts) {
|
|
304
|
+
logger.warn("Handle collision during provisioning; retrying", {
|
|
305
|
+
attempt,
|
|
306
|
+
});
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
throw err;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
283
314
|
async function provisionUserAndTenancy(
|
|
284
315
|
tx: Prisma.TransactionClient,
|
|
285
316
|
input: ProvisioningInput,
|
|
@@ -334,6 +365,9 @@ async function provisionUserAndTenancy(
|
|
|
334
365
|
cognitoSub,
|
|
335
366
|
email,
|
|
336
367
|
handle: initialHandle,
|
|
368
|
+
// S-CP2: lock in the AP-actor-shaped URI from the stable handle at
|
|
369
|
+
// creation (null when no base domain is configured for this lambda).
|
|
370
|
+
actorUri: canonicalActorUri(initialHandle),
|
|
337
371
|
role: federated ? "B2B_PARTNER" : "END_USER",
|
|
338
372
|
// Fail-CLOSED research age signal: a new account is NOT age-verified
|
|
339
373
|
// until an explicit verification flow sets it. Distinct from `ageTier`
|
|
@@ -350,13 +384,16 @@ async function provisionUserAndTenancy(
|
|
|
350
384
|
const updates: Prisma.UserUpdateInput = {};
|
|
351
385
|
if (!user.cognitoSub) updates.cognitoSub = cognitoSub;
|
|
352
386
|
if (!user.handle) {
|
|
353
|
-
|
|
387
|
+
const backfilledHandle = await deriveHandle(email, async (h) => {
|
|
354
388
|
const found = await tx.user.findFirst({
|
|
355
389
|
where: { handle: h, NOT: { id: user!.id } },
|
|
356
390
|
select: { id: true },
|
|
357
391
|
});
|
|
358
392
|
return !!found;
|
|
359
393
|
});
|
|
394
|
+
updates.handle = backfilledHandle;
|
|
395
|
+
// S-CP2: derive the AP actor URI from the handle we just assigned.
|
|
396
|
+
if (!user.actorUri) updates.actorUri = canonicalActorUri(backfilledHandle);
|
|
360
397
|
}
|
|
361
398
|
if (Object.keys(updates).length > 0) {
|
|
362
399
|
user = await tx.user.update({ where: { id: user.id }, data: updates });
|
|
@@ -24,9 +24,8 @@ import type {
|
|
|
24
24
|
PreTokenGenerationV2TriggerHandler,
|
|
25
25
|
} from "aws-lambda";
|
|
26
26
|
import { Logger } from "@aws-lambda-powertools/logger";
|
|
27
|
-
import { getSecret } from "@aws-lambda-powertools/parameters/secrets";
|
|
28
|
-
import { PrismaPg } from "@prisma/adapter-pg";
|
|
29
27
|
import { PrismaClient, type TenantRole } from "@prisma/client";
|
|
28
|
+
import { getLambdaPrisma as getPrisma } from "../lib/lambda-prisma.js";
|
|
30
29
|
import {
|
|
31
30
|
ClaimsCache,
|
|
32
31
|
createClaimsCacheFromEnv,
|
|
@@ -36,30 +35,8 @@ import {
|
|
|
36
35
|
import { resolveTenantRole, type RoleMappingInput } from "../lib/tenant/resolve-role.js";
|
|
37
36
|
|
|
38
37
|
const logger = new Logger({ serviceName: "pre-token-generation" });
|
|
39
|
-
let prisma: PrismaClient | null = null;
|
|
40
38
|
let cache: ClaimsCache | null = null;
|
|
41
39
|
|
|
42
|
-
async function getPrisma(): Promise<PrismaClient> {
|
|
43
|
-
if (prisma) return prisma;
|
|
44
|
-
const { username, password, host, port, dbname } = (await getSecret(
|
|
45
|
-
process.env.DB_SECRET_ARN!,
|
|
46
|
-
{ transform: "json" },
|
|
47
|
-
)) as unknown as {
|
|
48
|
-
username: string;
|
|
49
|
-
password: string;
|
|
50
|
-
host: string;
|
|
51
|
-
port: string | number;
|
|
52
|
-
dbname: string;
|
|
53
|
-
};
|
|
54
|
-
// Prisma 7 removed the `datasources` constructor option; the connection URL
|
|
55
|
-
// is now supplied via a driver adapter.
|
|
56
|
-
const adapter = new PrismaPg({
|
|
57
|
-
connectionString: `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${dbname}?connection_limit=1`,
|
|
58
|
-
});
|
|
59
|
-
prisma = new PrismaClient({ adapter });
|
|
60
|
-
return prisma;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
40
|
function getCache(): ClaimsCache {
|
|
64
41
|
if (!cache) cache = createClaimsCacheFromEnv();
|
|
65
42
|
return cache;
|