@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.
Files changed (57) hide show
  1. package/dist/lambda/delete-account-worker.d.ts.map +1 -1
  2. package/dist/lambda/delete-account-worker.js +1 -16
  3. package/dist/lambda/delete-account-worker.js.map +1 -1
  4. package/dist/lambda/hourly-cron.d.ts.map +1 -1
  5. package/dist/lambda/hourly-cron.js +1 -17
  6. package/dist/lambda/hourly-cron.js.map +1 -1
  7. package/dist/lambda/maintenance-cron.d.ts.map +1 -1
  8. package/dist/lambda/maintenance-cron.js +1 -16
  9. package/dist/lambda/maintenance-cron.js.map +1 -1
  10. package/dist/lambda/media-processing-worker.js +1 -1
  11. package/dist/lambda/media-processing-worker.js.map +1 -1
  12. package/dist/lambda/nightly-cron.d.ts.map +1 -1
  13. package/dist/lambda/nightly-cron.js +2 -18
  14. package/dist/lambda/nightly-cron.js.map +1 -1
  15. package/dist/lambda/post-confirmation.d.ts.map +1 -1
  16. package/dist/lambda/post-confirmation.js +59 -19
  17. package/dist/lambda/post-confirmation.js.map +1 -1
  18. package/dist/lambda/pre-token-generation.d.ts.map +1 -1
  19. package/dist/lambda/pre-token-generation.js +1 -16
  20. package/dist/lambda/pre-token-generation.js.map +1 -1
  21. package/dist/lib/compliance/baseline.d.ts.map +1 -1
  22. package/dist/lib/compliance/baseline.js +1 -9
  23. package/dist/lib/compliance/baseline.js.map +1 -1
  24. package/dist/lib/data-router.d.ts.map +1 -1
  25. package/dist/lib/data-router.js +7 -0
  26. package/dist/lib/data-router.js.map +1 -1
  27. package/dist/lib/entity-handler.d.ts.map +1 -1
  28. package/dist/lib/entity-handler.js +7 -1
  29. package/dist/lib/entity-handler.js.map +1 -1
  30. package/dist/lib/graph/graph-service.d.ts +7 -12
  31. package/dist/lib/graph/graph-service.d.ts.map +1 -1
  32. package/dist/lib/graph/graph-service.js +6 -11
  33. package/dist/lib/graph/graph-service.js.map +1 -1
  34. package/dist/lib/graph/scoring-engine.d.ts +0 -3
  35. package/dist/lib/graph/scoring-engine.d.ts.map +1 -1
  36. package/dist/lib/graph/scoring-engine.js +0 -3
  37. package/dist/lib/graph/scoring-engine.js.map +1 -1
  38. package/dist/lib/lambda-prisma.d.ts +16 -0
  39. package/dist/lib/lambda-prisma.d.ts.map +1 -0
  40. package/dist/lib/lambda-prisma.js +29 -0
  41. package/dist/lib/lambda-prisma.js.map +1 -0
  42. package/dist/lib/services/media-upload-service.js +2 -2
  43. package/dist/lib/services/media-upload-service.js.map +1 -1
  44. package/dist/lib/user/derive-handle.d.ts +22 -0
  45. package/dist/lib/user/derive-handle.d.ts.map +1 -1
  46. package/dist/lib/user/derive-handle.js +16 -0
  47. package/dist/lib/user/derive-handle.js.map +1 -1
  48. package/package.json +1 -1
  49. package/prisma/migrations/20260606000000_handle_canonical_identity/migration.sql +18 -0
  50. package/prisma/schema.prisma +19 -13
  51. package/src/lambda/delete-account-worker.ts +1 -16
  52. package/src/lambda/hourly-cron.ts +1 -28
  53. package/src/lambda/maintenance-cron.ts +1 -21
  54. package/src/lambda/media-processing-worker.ts +1 -1
  55. package/src/lambda/nightly-cron.ts +2 -24
  56. package/src/lambda/post-confirmation.ts +77 -40
  57. 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 = `media/${contentHash}.${ext}`;
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 = `media/${contentHash}.${ext}`;
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":"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"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@de-otio/trellis",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "license": "AGPL-3.0-or-later",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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");
@@ -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 DB (AuraDB), not Prisma
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). Neptune (the graph DB) has
97
- /// no spatial type, so geo-proximity lives here and is queried with PostGIS
98
- /// (ST_DWithin / KNN over a GiST index) via $queryRaw. Populated from the
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/{username}")
177
- handle String? // ActivityPub handle (e.g., "@user@example.com")
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 DB (AuraDB), not Prisma
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 live in the graph DB — see graph-service.ts)
1130
- // Follow and Friendship models removed — replaced by :RELATES_TO edges in graph DB
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 in the graph DB. Entity-scoped codes can be created by entity owners.
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 that previously lived only in the graph DB
1931
- // (RELATES_TO and typed entity-to-entity edges). OWNS (entity_ownerships) and
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 { getSecret } from "@aws-lambda-powertools/parameters/secrets";
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 { getSecret } from "@aws-lambda-powertools/parameters/secrets";
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.replace("originals/", "").replace(/\.[^.]+$/, "");
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 { getSecret } from "@aws-lambda-powertools/parameters/secrets";
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 DB (AuraDB)
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 { getSecret } from "@aws-lambda-powertools/parameters/secrets";
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 db.$transaction(
203
- async (tx) => provisionUserAndTenancy(tx, {
204
- cognitoSub,
205
- email,
206
- emailVerified: attrs.email_verified,
207
- federated,
208
- idpGroups,
209
- dateOfBirth,
210
- ageTier,
211
- providedHandle: attrs["custom:handle"],
212
- invitationCode,
213
- requestedMethod,
214
- }),
215
- { timeout: 8000 },
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
- updates.handle = await deriveHandle(email, async (h) => {
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;