@de-otio/trellis 0.6.0 → 0.7.0
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/env.d.ts +21 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +12 -0
- package/dist/env.js.map +1 -1
- package/dist/lambda/nightly-cron.d.ts.map +1 -1
- package/dist/lambda/nightly-cron.js +5 -2
- package/dist/lambda/nightly-cron.js.map +1 -1
- package/dist/lambda/post-confirmation.d.ts +30 -0
- package/dist/lambda/post-confirmation.d.ts.map +1 -1
- package/dist/lambda/post-confirmation.js +333 -29
- package/dist/lambda/post-confirmation.js.map +1 -1
- package/dist/lambda/pre-token-generation.d.ts +20 -0
- package/dist/lambda/pre-token-generation.d.ts.map +1 -1
- package/dist/lambda/pre-token-generation.js +233 -48
- package/dist/lambda/pre-token-generation.js.map +1 -1
- package/dist/lib/activitypub/activity-processor.d.ts.map +1 -1
- package/dist/lib/activitypub/activity-processor.js +2 -1
- package/dist/lib/activitypub/activity-processor.js.map +1 -1
- package/dist/lib/activitypub/group-service.d.ts +2 -2
- package/dist/lib/activitypub/group-service.d.ts.map +1 -1
- package/dist/lib/activitypub/group-service.js +5 -2
- package/dist/lib/activitypub/group-service.js.map +1 -1
- package/dist/lib/age-tier-transition.d.ts.map +1 -1
- package/dist/lib/age-tier-transition.js +19 -10
- package/dist/lib/age-tier-transition.js.map +1 -1
- package/dist/lib/audit/csv-export.d.ts +25 -0
- package/dist/lib/audit/csv-export.d.ts.map +1 -0
- package/dist/lib/audit/csv-export.js +54 -0
- package/dist/lib/audit/csv-export.js.map +1 -0
- package/dist/lib/audit/emit.d.ts +56 -0
- package/dist/lib/audit/emit.d.ts.map +1 -0
- package/dist/lib/audit/emit.js +124 -0
- package/dist/lib/audit/emit.js.map +1 -0
- package/dist/lib/audit/event-types.d.ts +36 -0
- package/dist/lib/audit/event-types.d.ts.map +1 -0
- package/dist/lib/audit/event-types.js +69 -0
- package/dist/lib/audit/event-types.js.map +1 -0
- package/dist/lib/audit/pii-filter.d.ts +22 -0
- package/dist/lib/audit/pii-filter.d.ts.map +1 -0
- package/dist/lib/audit/pii-filter.js +51 -0
- package/dist/lib/audit/pii-filter.js.map +1 -0
- package/dist/lib/audit-logger.js +1 -1
- package/dist/lib/audit-logger.js.map +1 -1
- package/dist/lib/auth/auth-context.d.ts +34 -0
- package/dist/lib/auth/auth-context.d.ts.map +1 -0
- package/dist/lib/auth/auth-context.js +10 -0
- package/dist/lib/auth/auth-context.js.map +1 -0
- package/dist/lib/auth/auth-middleware.d.ts +50 -0
- package/dist/lib/auth/auth-middleware.d.ts.map +1 -0
- package/dist/lib/auth/auth-middleware.js +153 -0
- package/dist/lib/auth/auth-middleware.js.map +1 -0
- package/dist/lib/auth/capabilities.d.ts +40 -0
- package/dist/lib/auth/capabilities.d.ts.map +1 -0
- package/dist/lib/auth/capabilities.js +44 -0
- package/dist/lib/auth/capabilities.js.map +1 -0
- package/dist/lib/auth/claims-cache.d.ts +70 -0
- package/dist/lib/auth/claims-cache.d.ts.map +1 -0
- package/dist/lib/auth/claims-cache.js +139 -0
- package/dist/lib/auth/claims-cache.js.map +1 -0
- package/dist/lib/auth/cognito-jwt.d.ts +6 -0
- package/dist/lib/auth/cognito-jwt.d.ts.map +1 -1
- package/dist/lib/auth/cognito-jwt.js.map +1 -1
- package/dist/lib/auth/idp-redirect-builder.d.ts +43 -0
- package/dist/lib/auth/idp-redirect-builder.d.ts.map +1 -0
- package/dist/lib/auth/idp-redirect-builder.js +48 -0
- package/dist/lib/auth/idp-redirect-builder.js.map +1 -0
- package/dist/lib/auth/require.d.ts +51 -0
- package/dist/lib/auth/require.d.ts.map +1 -0
- package/dist/lib/auth/require.js +99 -0
- package/dist/lib/auth/require.js.map +1 -0
- package/dist/lib/auth/role-grants.d.ts +18 -0
- package/dist/lib/auth/role-grants.d.ts.map +1 -0
- package/dist/lib/auth/role-grants.js +62 -0
- package/dist/lib/auth/role-grants.js.map +1 -0
- package/dist/lib/cognito/idp-sdk.d.ts +80 -0
- package/dist/lib/cognito/idp-sdk.d.ts.map +1 -0
- package/dist/lib/cognito/idp-sdk.js +186 -0
- package/dist/lib/cognito/idp-sdk.js.map +1 -0
- package/dist/lib/cognito/issuer-probe.d.ts +47 -0
- package/dist/lib/cognito/issuer-probe.d.ts.map +1 -0
- package/dist/lib/cognito/issuer-probe.js +319 -0
- package/dist/lib/cognito/issuer-probe.js.map +1 -0
- package/dist/lib/comment-handler.d.ts +7 -7
- package/dist/lib/comment-handler.d.ts.map +1 -1
- package/dist/lib/comment-handler.js +23 -20
- package/dist/lib/comment-handler.js.map +1 -1
- package/dist/lib/compliance/baseline.d.ts +15 -0
- package/dist/lib/compliance/baseline.d.ts.map +1 -0
- package/dist/lib/compliance/baseline.js +205 -0
- package/dist/lib/compliance/baseline.js.map +1 -0
- package/dist/lib/compliance/tenant-merge.d.ts +35 -0
- package/dist/lib/compliance/tenant-merge.d.ts.map +1 -0
- package/dist/lib/compliance/tenant-merge.js +80 -0
- package/dist/lib/compliance/tenant-merge.js.map +1 -0
- package/dist/lib/compliance/types.d.ts +135 -0
- package/dist/lib/compliance/types.d.ts.map +1 -0
- package/dist/lib/compliance/types.js +9 -0
- package/dist/lib/compliance/types.js.map +1 -0
- package/dist/lib/connection-code-handler.d.ts +4 -4
- package/dist/lib/connection-code-handler.d.ts.map +1 -1
- package/dist/lib/connection-code-handler.js +21 -11
- package/dist/lib/connection-code-handler.js.map +1 -1
- package/dist/lib/feed-handler.d.ts +2 -2
- package/dist/lib/feed-handler.d.ts.map +1 -1
- package/dist/lib/feed-handler.js +5 -9
- package/dist/lib/feed-handler.js.map +1 -1
- package/dist/lib/middleware/idempotency-store.d.ts +86 -0
- package/dist/lib/middleware/idempotency-store.d.ts.map +1 -0
- package/dist/lib/middleware/idempotency-store.js +109 -0
- package/dist/lib/middleware/idempotency-store.js.map +1 -0
- package/dist/lib/middleware/idempotency.d.ts +37 -0
- package/dist/lib/middleware/idempotency.d.ts.map +1 -0
- package/dist/lib/middleware/idempotency.js +358 -0
- package/dist/lib/middleware/idempotency.js.map +1 -0
- package/dist/lib/net/trusted-client-ip.d.ts +39 -0
- package/dist/lib/net/trusted-client-ip.d.ts.map +1 -0
- package/dist/lib/net/trusted-client-ip.js +100 -0
- package/dist/lib/net/trusted-client-ip.js.map +1 -0
- package/dist/lib/notification-handler.d.ts +5 -5
- package/dist/lib/notification-handler.d.ts.map +1 -1
- package/dist/lib/notification-handler.js +11 -9
- package/dist/lib/notification-handler.js.map +1 -1
- package/dist/lib/oauth/cognito-issuer.d.ts +34 -0
- package/dist/lib/oauth/cognito-issuer.d.ts.map +1 -0
- package/dist/lib/oauth/cognito-issuer.js +53 -0
- package/dist/lib/oauth/cognito-issuer.js.map +1 -0
- package/dist/lib/oauth/device-authorization.d.ts +145 -0
- package/dist/lib/oauth/device-authorization.d.ts.map +1 -0
- package/dist/lib/oauth/device-authorization.js +312 -0
- package/dist/lib/oauth/device-authorization.js.map +1 -0
- package/dist/lib/oauth/envelope-crypto.d.ts +101 -0
- package/dist/lib/oauth/envelope-crypto.d.ts.map +1 -0
- package/dist/lib/oauth/envelope-crypto.js +223 -0
- package/dist/lib/oauth/envelope-crypto.js.map +1 -0
- package/dist/lib/oauth/refresh-detection.d.ts +126 -0
- package/dist/lib/oauth/refresh-detection.d.ts.map +1 -0
- package/dist/lib/oauth/refresh-detection.js +248 -0
- package/dist/lib/oauth/refresh-detection.js.map +1 -0
- package/dist/lib/openapi/generator.d.ts +78 -0
- package/dist/lib/openapi/generator.d.ts.map +1 -0
- package/dist/lib/openapi/generator.js +201 -0
- package/dist/lib/openapi/generator.js.map +1 -0
- package/dist/lib/post-handler.d.ts +1 -1
- package/dist/lib/post-handler.d.ts.map +1 -1
- package/dist/lib/post-handler.js +4 -15
- package/dist/lib/post-handler.js.map +1 -1
- package/dist/lib/rate-limit.d.ts.map +1 -1
- package/dist/lib/rate-limit.js +11 -3
- package/dist/lib/rate-limit.js.map +1 -1
- package/dist/lib/routes/agent-authorize.d.ts +32 -0
- package/dist/lib/routes/agent-authorize.d.ts.map +1 -0
- package/dist/lib/routes/agent-authorize.js +479 -0
- package/dist/lib/routes/agent-authorize.js.map +1 -0
- package/dist/lib/routes/agent-sessions.d.ts +20 -0
- package/dist/lib/routes/agent-sessions.d.ts.map +1 -0
- package/dist/lib/routes/agent-sessions.js +124 -0
- package/dist/lib/routes/agent-sessions.js.map +1 -0
- package/dist/lib/routes/agent-surface.d.ts +37 -0
- package/dist/lib/routes/agent-surface.d.ts.map +1 -0
- package/dist/lib/routes/agent-surface.js +208 -0
- package/dist/lib/routes/agent-surface.js.map +1 -0
- package/dist/lib/routes/auth-discover.d.ts +18 -0
- package/dist/lib/routes/auth-discover.d.ts.map +1 -0
- package/dist/lib/routes/auth-discover.js +177 -0
- package/dist/lib/routes/auth-discover.js.map +1 -0
- package/dist/lib/routes/comments.d.ts.map +1 -1
- package/dist/lib/routes/comments.js +36 -7
- package/dist/lib/routes/comments.js.map +1 -1
- package/dist/lib/routes/connection-codes.d.ts.map +1 -1
- package/dist/lib/routes/connection-codes.js +21 -4
- package/dist/lib/routes/connection-codes.js.map +1 -1
- package/dist/lib/routes/content-discovery.d.ts.map +1 -1
- package/dist/lib/routes/content-discovery.js +18 -13
- package/dist/lib/routes/content-discovery.js.map +1 -1
- package/dist/lib/routes/dashboard.js +1 -1
- package/dist/lib/routes/dashboard.js.map +1 -1
- package/dist/lib/routes/employees.d.ts.map +1 -1
- package/dist/lib/routes/employees.js +57 -15
- package/dist/lib/routes/employees.js.map +1 -1
- package/dist/lib/routes/entities.d.ts.map +1 -1
- package/dist/lib/routes/entities.js +35 -19
- package/dist/lib/routes/entities.js.map +1 -1
- package/dist/lib/routes/errors.d.ts +34 -0
- package/dist/lib/routes/errors.d.ts.map +1 -0
- package/dist/lib/routes/errors.js +57 -0
- package/dist/lib/routes/errors.js.map +1 -0
- package/dist/lib/routes/feeds.d.ts.map +1 -1
- package/dist/lib/routes/feeds.js +12 -2
- package/dist/lib/routes/feeds.js.map +1 -1
- package/dist/lib/routes/index.d.ts.map +1 -1
- package/dist/lib/routes/index.js +50 -0
- package/dist/lib/routes/index.js.map +1 -1
- package/dist/lib/routes/mfa.d.ts.map +1 -1
- package/dist/lib/routes/mfa.js +1 -0
- package/dist/lib/routes/mfa.js.map +1 -1
- package/dist/lib/routes/notifications.d.ts.map +1 -1
- package/dist/lib/routes/notifications.js +21 -4
- package/dist/lib/routes/notifications.js.map +1 -1
- package/dist/lib/routes/oauth.d.ts +15 -0
- package/dist/lib/routes/oauth.d.ts.map +1 -0
- package/dist/lib/routes/oauth.js +139 -0
- package/dist/lib/routes/oauth.js.map +1 -0
- package/dist/lib/routes/posts.d.ts.map +1 -1
- package/dist/lib/routes/posts.js +30 -19
- package/dist/lib/routes/posts.js.map +1 -1
- package/dist/lib/routes/products.d.ts.map +1 -1
- package/dist/lib/routes/products.js +19 -22
- package/dist/lib/routes/products.js.map +1 -1
- package/dist/lib/routes/setup-status.d.ts +34 -0
- package/dist/lib/routes/setup-status.d.ts.map +1 -0
- package/dist/lib/routes/setup-status.js +87 -0
- package/dist/lib/routes/setup-status.js.map +1 -0
- package/dist/lib/routes/taxonomy-analytics.d.ts.map +1 -1
- package/dist/lib/routes/taxonomy-analytics.js +15 -14
- package/dist/lib/routes/taxonomy-analytics.js.map +1 -1
- package/dist/lib/routes/taxonomy.d.ts.map +1 -1
- package/dist/lib/routes/taxonomy.js +19 -16
- package/dist/lib/routes/taxonomy.js.map +1 -1
- package/dist/lib/routes/tenant-audit.d.ts +19 -0
- package/dist/lib/routes/tenant-audit.d.ts.map +1 -0
- package/dist/lib/routes/tenant-audit.js +244 -0
- package/dist/lib/routes/tenant-audit.js.map +1 -0
- package/dist/lib/routes/tenant-compliance.d.ts +21 -0
- package/dist/lib/routes/tenant-compliance.d.ts.map +1 -0
- package/dist/lib/routes/tenant-compliance.js +122 -0
- package/dist/lib/routes/tenant-compliance.js.map +1 -0
- package/dist/lib/routes/tenant-domains.d.ts +11 -0
- package/dist/lib/routes/tenant-domains.d.ts.map +1 -0
- package/dist/lib/routes/tenant-domains.js +95 -0
- package/dist/lib/routes/tenant-domains.js.map +1 -0
- package/dist/lib/routes/tenant-idp.d.ts +3 -0
- package/dist/lib/routes/tenant-idp.d.ts.map +1 -0
- package/dist/lib/routes/tenant-idp.js +89 -0
- package/dist/lib/routes/tenant-idp.js.map +1 -0
- package/dist/lib/routes/tenant-members.d.ts +13 -0
- package/dist/lib/routes/tenant-members.d.ts.map +1 -0
- package/dist/lib/routes/tenant-members.js +75 -0
- package/dist/lib/routes/tenant-members.js.map +1 -0
- package/dist/lib/routes/tenant-role-mappings.d.ts +11 -0
- package/dist/lib/routes/tenant-role-mappings.d.ts.map +1 -0
- package/dist/lib/routes/tenant-role-mappings.js +90 -0
- package/dist/lib/routes/tenant-role-mappings.js.map +1 -0
- package/dist/lib/routes/tenants.d.ts +13 -0
- package/dist/lib/routes/tenants.d.ts.map +1 -0
- package/dist/lib/routes/tenants.js +121 -0
- package/dist/lib/routes/tenants.js.map +1 -0
- package/dist/lib/routes/types.d.ts +9 -0
- package/dist/lib/routes/types.d.ts.map +1 -1
- package/dist/lib/schemas.d.ts +2 -2
- package/dist/lib/secrets/idp-secrets.d.ts +51 -0
- package/dist/lib/secrets/idp-secrets.d.ts.map +1 -0
- package/dist/lib/secrets/idp-secrets.js +111 -0
- package/dist/lib/secrets/idp-secrets.js.map +1 -0
- package/dist/lib/security-monitor.d.ts.map +1 -1
- package/dist/lib/security-monitor.js +6 -1
- package/dist/lib/security-monitor.js.map +1 -1
- package/dist/lib/session-manager.d.ts +1 -0
- package/dist/lib/session-manager.d.ts.map +1 -1
- package/dist/lib/session-manager.js.map +1 -1
- package/dist/lib/taxonomy-handler-factory.d.ts +4 -2
- package/dist/lib/taxonomy-handler-factory.d.ts.map +1 -1
- package/dist/lib/taxonomy-handler-factory.js +8 -7
- package/dist/lib/taxonomy-handler-factory.js.map +1 -1
- package/dist/lib/tenant/audit-emit.d.ts +18 -0
- package/dist/lib/tenant/audit-emit.d.ts.map +1 -0
- package/dist/lib/tenant/audit-emit.js +16 -0
- package/dist/lib/tenant/audit-emit.js.map +1 -0
- package/dist/lib/tenant/derive-domain.d.ts +19 -0
- package/dist/lib/tenant/derive-domain.d.ts.map +1 -0
- package/dist/lib/tenant/derive-domain.js +38 -0
- package/dist/lib/tenant/derive-domain.js.map +1 -0
- package/dist/lib/tenant/domain-handler.d.ts +42 -0
- package/dist/lib/tenant/domain-handler.d.ts.map +1 -0
- package/dist/lib/tenant/domain-handler.js +344 -0
- package/dist/lib/tenant/domain-handler.js.map +1 -0
- package/dist/lib/tenant/domain-validator.d.ts +28 -0
- package/dist/lib/tenant/domain-validator.d.ts.map +1 -0
- package/dist/lib/tenant/domain-validator.js +145 -0
- package/dist/lib/tenant/domain-validator.js.map +1 -0
- package/dist/lib/tenant/domain-verifier.d.ts +30 -0
- package/dist/lib/tenant/domain-verifier.d.ts.map +1 -0
- package/dist/lib/tenant/domain-verifier.js +53 -0
- package/dist/lib/tenant/domain-verifier.js.map +1 -0
- package/dist/lib/tenant/idp-handler.d.ts +29 -0
- package/dist/lib/tenant/idp-handler.d.ts.map +1 -0
- package/dist/lib/tenant/idp-handler.js +693 -0
- package/dist/lib/tenant/idp-handler.js.map +1 -0
- package/dist/lib/tenant/idp-name.d.ts +2 -0
- package/dist/lib/tenant/idp-name.d.ts.map +1 -0
- package/dist/lib/tenant/idp-name.js +20 -0
- package/dist/lib/tenant/idp-name.js.map +1 -0
- package/dist/lib/tenant/member-handler.d.ts +31 -0
- package/dist/lib/tenant/member-handler.d.ts.map +1 -0
- package/dist/lib/tenant/member-handler.js +343 -0
- package/dist/lib/tenant/member-handler.js.map +1 -0
- package/dist/lib/tenant/reserved-slugs.d.ts +37 -0
- package/dist/lib/tenant/reserved-slugs.d.ts.map +1 -0
- package/dist/lib/tenant/reserved-slugs.js +116 -0
- package/dist/lib/tenant/reserved-slugs.js.map +1 -0
- package/dist/lib/tenant/resolve-role.d.ts +39 -0
- package/dist/lib/tenant/resolve-role.d.ts.map +1 -0
- package/dist/lib/tenant/resolve-role.js +60 -0
- package/dist/lib/tenant/resolve-role.js.map +1 -0
- package/dist/lib/tenant/role-mapping-handler.d.ts +26 -0
- package/dist/lib/tenant/role-mapping-handler.d.ts.map +1 -0
- package/dist/lib/tenant/role-mapping-handler.js +260 -0
- package/dist/lib/tenant/role-mapping-handler.js.map +1 -0
- package/dist/lib/tenant/setup-status.d.ts +83 -0
- package/dist/lib/tenant/setup-status.d.ts.map +1 -0
- package/dist/lib/tenant/setup-status.js +201 -0
- package/dist/lib/tenant/setup-status.js.map +1 -0
- package/dist/lib/tenant/slug-validator.d.ts +31 -0
- package/dist/lib/tenant/slug-validator.d.ts.map +1 -0
- package/dist/lib/tenant/slug-validator.js +42 -0
- package/dist/lib/tenant/slug-validator.js.map +1 -0
- package/dist/lib/tenant/tenant-handler.d.ts +49 -0
- package/dist/lib/tenant/tenant-handler.d.ts.map +1 -0
- package/dist/lib/tenant/tenant-handler.js +377 -0
- package/dist/lib/tenant/tenant-handler.js.map +1 -0
- package/dist/lib/tenant/transfer-ownership.d.ts +39 -0
- package/dist/lib/tenant/transfer-ownership.d.ts.map +1 -0
- package/dist/lib/tenant/transfer-ownership.js +66 -0
- package/dist/lib/tenant/transfer-ownership.js.map +1 -0
- package/dist/lib/user/derive-handle.d.ts +29 -0
- package/dist/lib/user/derive-handle.d.ts.map +1 -0
- package/dist/lib/user/derive-handle.js +65 -0
- package/dist/lib/user/derive-handle.js.map +1 -0
- package/dist/lib/user-deprovisioning.d.ts +11 -1
- package/dist/lib/user-deprovisioning.d.ts.map +1 -1
- package/dist/lib/user-deprovisioning.js +46 -2
- package/dist/lib/user-deprovisioning.js.map +1 -1
- package/dist/lib/validation/feature-toggle-schemas.d.ts +10 -10
- package/package.json +6 -3
- package/prisma/migrations/20260502094501_add_tenancy_model/migration.sql +334 -0
- package/prisma/migrations/20260503000000_add_tenant_region/migration.sql +4 -0
- package/prisma/schema.prisma +324 -74
- package/src/lambda/nightly-cron.ts +4 -1
- package/src/lambda/post-confirmation.ts +405 -29
- package/src/lambda/pre-token-generation.ts +300 -59
package/prisma/schema.prisma
CHANGED
|
@@ -15,6 +15,8 @@ datasource db {
|
|
|
15
15
|
// NO tenantId - schema-per-tenant provides isolation
|
|
16
16
|
model Entity {
|
|
17
17
|
id String @id @default(cuid())
|
|
18
|
+
// Multi-tenancy (v0.7) — every entity belongs to exactly one tenant.
|
|
19
|
+
tenantId String @map("tenant_id")
|
|
18
20
|
name String
|
|
19
21
|
entityType String? @map("entity_type") // 'pet', 'product', 'event', etc. (defaults to 'dog' for MVP)
|
|
20
22
|
metadata Json? // Flexible schema for entity-specific fields (breed, bio, birthdate, breedSize, etc.)
|
|
@@ -41,7 +43,7 @@ model Entity {
|
|
|
41
43
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
42
44
|
|
|
43
45
|
// Co-ownership (replaces single ownerId)
|
|
44
|
-
owners
|
|
46
|
+
owners EntityOwnership[]
|
|
45
47
|
// Relationships and follower counts live in the graph DB (AuraDB), not Prisma
|
|
46
48
|
|
|
47
49
|
subjectPosts PostSubject[]
|
|
@@ -50,6 +52,10 @@ model Entity {
|
|
|
50
52
|
|
|
51
53
|
connectionCodes ConnectionCode[]
|
|
52
54
|
|
|
55
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
56
|
+
|
|
57
|
+
@@index([tenantId])
|
|
58
|
+
@@index([tenantId, entityType, status])
|
|
53
59
|
@@index([status])
|
|
54
60
|
@@index([entityType, status])
|
|
55
61
|
@@map("entities")
|
|
@@ -133,8 +139,10 @@ model User {
|
|
|
133
139
|
suspended Boolean @default(false)
|
|
134
140
|
suspendedAt DateTime? @map("suspended_at")
|
|
135
141
|
suspendedReason String? @map("suspended_reason")
|
|
136
|
-
|
|
137
|
-
|
|
142
|
+
|
|
143
|
+
// Multi-tenancy (v0.7) — user's auto-created personal tenant.
|
|
144
|
+
// Nullable to allow brief moments during sign-up before personal tenant exists.
|
|
145
|
+
personalTenantId String? @unique @map("personal_tenant_id")
|
|
138
146
|
|
|
139
147
|
// Account Deletion Grace Period
|
|
140
148
|
deletionRequestedAt DateTime? @map("deletion_requested_at") // When user requested deletion
|
|
@@ -267,8 +275,8 @@ model User {
|
|
|
267
275
|
guardianLinks ParentalLink[] @relation("GuardianParentalLinks")
|
|
268
276
|
|
|
269
277
|
// Safer Social Design: Quiet hours (Phase 2)
|
|
270
|
-
quietHoursStart Int? @map("quiet_hours_start")
|
|
271
|
-
quietHoursEnd Int? @map("quiet_hours_end")
|
|
278
|
+
quietHoursStart Int? @map("quiet_hours_start") // minutes from midnight (e.g., 1320 = 10PM)
|
|
279
|
+
quietHoursEnd Int? @map("quiet_hours_end") // minutes from midnight (e.g., 420 = 7AM)
|
|
272
280
|
quietHoursEnabled Boolean @default(false) @map("quiet_hours_enabled")
|
|
273
281
|
|
|
274
282
|
// Safer Social Design: Profile visibility and DM access (Phase 5)
|
|
@@ -283,11 +291,17 @@ model User {
|
|
|
283
291
|
createdConnectionCodes ConnectionCode[] @relation("ConnectionCodeCreator")
|
|
284
292
|
redeemedConnectionCodes ConnectionCodeRedemption[]
|
|
285
293
|
|
|
294
|
+
// Multi-tenancy relations (v0.7)
|
|
295
|
+
personalTenant Tenant? @relation("PersonalTenantOwner")
|
|
296
|
+
tenantMemberships TenantMember[] @relation("TenantMemberships")
|
|
297
|
+
invitedTenantMembers TenantMember[] @relation("TenantInvitedBy")
|
|
298
|
+
sentInvitations TenantInvitation[] @relation("TenantInvitationInviter")
|
|
299
|
+
acceptedInvitations TenantInvitation[] @relation("TenantInvitationAcceptedBy")
|
|
300
|
+
|
|
286
301
|
@@index([role])
|
|
287
302
|
@@index([region])
|
|
288
303
|
@@index([dataRegion])
|
|
289
304
|
@@index([suspended])
|
|
290
|
-
@@index([partnerId])
|
|
291
305
|
@@index([username])
|
|
292
306
|
@@index([emailVerified])
|
|
293
307
|
@@index([identityVerified])
|
|
@@ -298,6 +312,7 @@ model User {
|
|
|
298
312
|
@@index([emailHash]) // Index for email hash lookups
|
|
299
313
|
@@index([anonymousId]) // Index for anonymous ID lookups
|
|
300
314
|
@@index([cognitoSub]) // Index for Cognito sub lookups
|
|
315
|
+
@@index([personalTenantId])
|
|
301
316
|
@@map("users")
|
|
302
317
|
}
|
|
303
318
|
|
|
@@ -373,9 +388,9 @@ model CrossRegionConsent {
|
|
|
373
388
|
// Posting radius — how far content radiates on the author's social graph
|
|
374
389
|
enum PostRadius {
|
|
375
390
|
WHISPER // Inner circle only (tier 0)
|
|
376
|
-
NORMAL
|
|
377
|
-
LOUD
|
|
378
|
-
SHOUT
|
|
391
|
+
NORMAL // Close friends + inner circle (tiers 0-1)
|
|
392
|
+
LOUD // Community and closer (tiers 0-2)
|
|
393
|
+
SHOUT // Everyone (all tiers)
|
|
379
394
|
}
|
|
380
395
|
|
|
381
396
|
// Privacy levels for follow relationships
|
|
@@ -387,30 +402,33 @@ enum Privacy {
|
|
|
387
402
|
|
|
388
403
|
// Post model (unified for all visibility levels)
|
|
389
404
|
model Post {
|
|
390
|
-
id String
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
405
|
+
id String @id @default(cuid())
|
|
406
|
+
// Multi-tenancy (v0.7) — set to author's active tenant at write time.
|
|
407
|
+
tenantId String @map("tenant_id")
|
|
408
|
+
authorId String @map("author_id")
|
|
409
|
+
text String @db.Text
|
|
410
|
+
radius PostRadius @default(NORMAL)
|
|
411
|
+
geoData Json? @map("geo_data")
|
|
395
412
|
uri String? // ActivityPub URI (for public posts)
|
|
396
|
-
contentWarnings String[]
|
|
397
|
-
deletedAt DateTime?
|
|
398
|
-
hiddenByAuthor Boolean
|
|
399
|
-
createdAt DateTime
|
|
400
|
-
updatedAt DateTime
|
|
401
|
-
editedAt DateTime?
|
|
402
|
-
|
|
403
|
-
author
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
413
|
+
contentWarnings String[] @map("content_warnings")
|
|
414
|
+
deletedAt DateTime? @map("deleted_at")
|
|
415
|
+
hiddenByAuthor Boolean @default(false) @map("hidden_by_author")
|
|
416
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
417
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
418
|
+
editedAt DateTime? @map("edited_at") // Timestamp of last user edit (null if never edited)
|
|
419
|
+
|
|
420
|
+
author User @relation(fields: [authorId], references: [id])
|
|
421
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
422
|
+
group Group? @relation(fields: [groupId], references: [id])
|
|
423
|
+
groupId String? @map("group_id") // Group this post belongs to
|
|
424
|
+
media PostMedia[]
|
|
425
|
+
sentiments PostSentiment[]
|
|
426
|
+
comments PostComment[]
|
|
409
427
|
subjectEntities PostSubject[]
|
|
410
|
-
primaryEntityId String?
|
|
411
|
-
primaryEntity Entity?
|
|
412
|
-
taxonomyTags
|
|
413
|
-
linkChecks
|
|
428
|
+
primaryEntityId String? @map("primary_entity_id")
|
|
429
|
+
primaryEntity Entity? @relation("PrimaryEntityPosts", fields: [primaryEntityId], references: [id])
|
|
430
|
+
taxonomyTags PostTaxonomyTag[]
|
|
431
|
+
linkChecks LinkCheck[]
|
|
414
432
|
|
|
415
433
|
// PREPARATORY CHANGE: Region tracking for China expansion
|
|
416
434
|
// dataRegion: Where post data is stored (for compliance)
|
|
@@ -447,6 +465,7 @@ model Post {
|
|
|
447
465
|
// Content category (optional classification)
|
|
448
466
|
contentCategory String? @map("content_category")
|
|
449
467
|
|
|
468
|
+
@@index([tenantId, createdAt])
|
|
450
469
|
@@index([authorId, createdAt])
|
|
451
470
|
@@index([authorId, radius, createdAt])
|
|
452
471
|
@@index([primaryEntityId])
|
|
@@ -596,6 +615,8 @@ model PostSentiment {
|
|
|
596
615
|
// Post comments
|
|
597
616
|
model PostComment {
|
|
598
617
|
id String @id @default(cuid())
|
|
618
|
+
// Multi-tenancy (v0.7) — denormalized from the parent post; same scope.
|
|
619
|
+
tenantId String @map("tenant_id")
|
|
599
620
|
postId String @map("post_id")
|
|
600
621
|
postUri String? @map("post_uri")
|
|
601
622
|
authorId String @map("author_id")
|
|
@@ -607,7 +628,7 @@ model PostComment {
|
|
|
607
628
|
|
|
608
629
|
// Edit tracking
|
|
609
630
|
editedAt DateTime? @map("edited_at") // When last edited
|
|
610
|
-
originalText String? @
|
|
631
|
+
originalText String? @map("original_text") @db.Text // Preserve original on first edit
|
|
611
632
|
|
|
612
633
|
// Soft delete tracking
|
|
613
634
|
deletedAt DateTime? @map("deleted_at") // When deleted (soft delete)
|
|
@@ -633,10 +654,12 @@ model PostComment {
|
|
|
633
654
|
contentCategory String? @map("content_category")
|
|
634
655
|
|
|
635
656
|
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
|
|
657
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
636
658
|
media PostCommentMedia[]
|
|
637
659
|
sentiments CommentSentiment[]
|
|
638
660
|
linkChecks LinkCheck[]
|
|
639
661
|
|
|
662
|
+
@@index([tenantId])
|
|
640
663
|
@@index([postId, createdAt])
|
|
641
664
|
@@index([postUri, createdAt])
|
|
642
665
|
@@index([authorId])
|
|
@@ -709,13 +732,16 @@ model PostSubject {
|
|
|
709
732
|
@@map("post_subjects")
|
|
710
733
|
}
|
|
711
734
|
|
|
712
|
-
// Security Events for SSO monitoring and compliance
|
|
735
|
+
// Security Events for SSO monitoring and compliance.
|
|
736
|
+
// In v0.7, this also serves as the audit-log substrate (T7 wires admin actions here).
|
|
713
737
|
model SecurityEvent {
|
|
714
738
|
id String @id @default(cuid())
|
|
715
|
-
type String // sso_login, sso_failed, sso_config_error, rate_limit_exceeded, suspicious_activity, unauthorized_access
|
|
739
|
+
type String // sso_login, sso_failed, sso_config_error, rate_limit_exceeded, suspicious_activity, unauthorized_access, tenant_*, member_*, idp_*, etc.
|
|
716
740
|
severity String // low, medium, high, critical
|
|
717
741
|
userId String?
|
|
718
|
-
partnerId
|
|
742
|
+
// Tenancy scope (v0.7) — replaces the old `partnerId` field.
|
|
743
|
+
// Nullable because some events (system-level) have no tenant context.
|
|
744
|
+
tenantId String? @map("tenant_id")
|
|
719
745
|
ipAddress String? @map("ip_address")
|
|
720
746
|
userAgent String? @map("user_agent")
|
|
721
747
|
details String // JSON string
|
|
@@ -727,31 +753,18 @@ model SecurityEvent {
|
|
|
727
753
|
// Retention periods: critical=365 days, high=90 days, medium=30 days, low=7 days
|
|
728
754
|
retentionUntil DateTime? @map("retention_until")
|
|
729
755
|
|
|
730
|
-
user
|
|
731
|
-
partner Partner? @relation(fields: [partnerId], references: [id])
|
|
756
|
+
user User? @relation(fields: [userId], references: [id])
|
|
732
757
|
|
|
733
758
|
@@index([type])
|
|
734
759
|
@@index([severity])
|
|
735
760
|
@@index([timestamp])
|
|
736
761
|
@@index([userId])
|
|
737
|
-
@@index([
|
|
762
|
+
@@index([tenantId])
|
|
738
763
|
@@index([ipAddress])
|
|
739
764
|
@@index([retentionUntil]) // Index for efficient cleanup job queries
|
|
740
765
|
@@map("security_events")
|
|
741
766
|
}
|
|
742
767
|
|
|
743
|
-
// Partner model for B2B SSO
|
|
744
|
-
model Partner {
|
|
745
|
-
id String @id @default(cuid())
|
|
746
|
-
name String
|
|
747
|
-
createdAt DateTime @default(now()) @map("created_at")
|
|
748
|
-
|
|
749
|
-
users User[]
|
|
750
|
-
securityEvents SecurityEvent[]
|
|
751
|
-
|
|
752
|
-
@@map("partners")
|
|
753
|
-
}
|
|
754
|
-
|
|
755
768
|
// Feature Toggle model for global feature flags
|
|
756
769
|
model FeatureToggle {
|
|
757
770
|
id String @id @default(cuid())
|
|
@@ -942,10 +955,12 @@ model ProductTaxonomyTag {
|
|
|
942
955
|
|
|
943
956
|
// Entity co-ownership — replaces single Entity.ownerId FK
|
|
944
957
|
model EntityOwnership {
|
|
945
|
-
id String
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
958
|
+
id String @id @default(cuid())
|
|
959
|
+
// Multi-tenancy (v0.7) — denormalized from entity; same scope.
|
|
960
|
+
tenantId String @map("tenant_id")
|
|
961
|
+
entityId String @map("entity_id")
|
|
962
|
+
userId String @map("user_id")
|
|
963
|
+
role OwnershipRole @default(CO_OWNER)
|
|
949
964
|
|
|
950
965
|
addedByUserId String @map("added_by_user_id")
|
|
951
966
|
addedAt DateTime @default(now()) @map("added_at")
|
|
@@ -954,9 +969,11 @@ model EntityOwnership {
|
|
|
954
969
|
|
|
955
970
|
entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
|
|
956
971
|
user User @relation("EntityOwners", fields: [userId], references: [id], onDelete: Cascade)
|
|
972
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
957
973
|
addedBy User @relation("OwnershipAddedBy", fields: [addedByUserId], references: [id])
|
|
958
974
|
|
|
959
975
|
@@unique([entityId, userId])
|
|
976
|
+
@@index([tenantId])
|
|
960
977
|
@@index([entityId])
|
|
961
978
|
@@index([userId])
|
|
962
979
|
@@index([entityId, role])
|
|
@@ -965,8 +982,8 @@ model EntityOwnership {
|
|
|
965
982
|
|
|
966
983
|
enum OwnershipRole {
|
|
967
984
|
PRIMARY_OWNER // Full control, can transfer primary, can delete entity
|
|
968
|
-
CO_OWNER
|
|
969
|
-
CARETAKER
|
|
985
|
+
CO_OWNER // Post and manage, cannot delete entity
|
|
986
|
+
CARETAKER // Can post about entity, cannot modify profile
|
|
970
987
|
}
|
|
971
988
|
|
|
972
989
|
enum OwnershipStatus {
|
|
@@ -985,13 +1002,13 @@ model CircleConfig {
|
|
|
985
1002
|
userId String @unique @map("user_id")
|
|
986
1003
|
|
|
987
1004
|
// Tier thresholds (score >= threshold = in this tier)
|
|
988
|
-
innerThreshold Float @default(0.8) @map("inner_threshold")
|
|
1005
|
+
innerThreshold Float @default(0.8) @map("inner_threshold") // tier 0
|
|
989
1006
|
closeFriendThreshold Float @default(0.5) @map("close_friend_threshold") // tier 1
|
|
990
|
-
communityThreshold Float @default(0.2) @map("community_threshold")
|
|
1007
|
+
communityThreshold Float @default(0.2) @map("community_threshold") // tier 2
|
|
991
1008
|
// Below communityThreshold = ambient (tier 3)
|
|
992
1009
|
|
|
993
1010
|
// View preferences
|
|
994
|
-
dailyDeckSize Int? @map("daily_deck_size")
|
|
1011
|
+
dailyDeckSize Int? @map("daily_deck_size") // null = no daily limit
|
|
995
1012
|
glanceLimit Int @default(20) @map("glance_limit") // Max items in glance mode
|
|
996
1013
|
depthWindowDays Int @default(7) @map("depth_window_days") // How far back depth mode goes
|
|
997
1014
|
|
|
@@ -1007,7 +1024,7 @@ model CircleConfig {
|
|
|
1007
1024
|
model CircleReadState {
|
|
1008
1025
|
id String @id @default(cuid())
|
|
1009
1026
|
userId String @map("user_id")
|
|
1010
|
-
tier Int
|
|
1027
|
+
tier Int // 0 = inner, 1 = close friends, 2 = community, 3 = ambient
|
|
1011
1028
|
|
|
1012
1029
|
lastReadAt DateTime @map("last_read_at")
|
|
1013
1030
|
lastReadPostId String? @map("last_read_post_id") // Cursor for pagination
|
|
@@ -1029,6 +1046,8 @@ model CircleReadState {
|
|
|
1029
1046
|
// Group model for ActivityPub private groups
|
|
1030
1047
|
model Group {
|
|
1031
1048
|
id String @id @default(cuid())
|
|
1049
|
+
// Multi-tenancy (v0.7) — group is owned by exactly one tenant.
|
|
1050
|
+
tenantId String @map("tenant_id")
|
|
1032
1051
|
name String
|
|
1033
1052
|
description String? @db.Text
|
|
1034
1053
|
actorUri String @unique @map("actor_uri") // Group actor URI
|
|
@@ -1043,7 +1062,9 @@ model Group {
|
|
|
1043
1062
|
|
|
1044
1063
|
members GroupMember[]
|
|
1045
1064
|
posts Post[]
|
|
1065
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1046
1066
|
|
|
1067
|
+
@@index([tenantId])
|
|
1047
1068
|
@@index([actorUri])
|
|
1048
1069
|
@@index([privacy])
|
|
1049
1070
|
@@map("groups")
|
|
@@ -1052,14 +1073,18 @@ model Group {
|
|
|
1052
1073
|
// Group membership model
|
|
1053
1074
|
model GroupMember {
|
|
1054
1075
|
id String @id @default(cuid())
|
|
1076
|
+
// Multi-tenancy (v0.7) — denormalized from group.
|
|
1077
|
+
tenantId String @map("tenant_id")
|
|
1055
1078
|
groupId String @map("group_id")
|
|
1056
1079
|
actorUri String @map("actor_uri") // User's actor URI
|
|
1057
1080
|
role GroupRole
|
|
1058
1081
|
joinedAt DateTime @default(now()) @map("joined_at")
|
|
1059
1082
|
|
|
1060
|
-
group
|
|
1083
|
+
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
1084
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1061
1085
|
|
|
1062
1086
|
@@unique([groupId, actorUri])
|
|
1087
|
+
@@index([tenantId])
|
|
1063
1088
|
@@index([groupId])
|
|
1064
1089
|
@@index([actorUri])
|
|
1065
1090
|
@@index([groupId, role])
|
|
@@ -1244,8 +1269,8 @@ model LinkReport {
|
|
|
1244
1269
|
model EmailSuppression {
|
|
1245
1270
|
id String @id @default(cuid())
|
|
1246
1271
|
email String @unique
|
|
1247
|
-
reason String
|
|
1248
|
-
bounceType String?
|
|
1272
|
+
reason String // "bounce" | "complaint"
|
|
1273
|
+
bounceType String? // "permanent" | "transient" for bounces
|
|
1249
1274
|
suppressedAt DateTime @default(now())
|
|
1250
1275
|
createdAt DateTime @default(now())
|
|
1251
1276
|
updatedAt DateTime @updatedAt
|
|
@@ -1263,7 +1288,7 @@ model DeletionAuditLog {
|
|
|
1263
1288
|
email String
|
|
1264
1289
|
requestedAt DateTime @map("requested_at")
|
|
1265
1290
|
confirmedAt DateTime? @map("confirmed_at")
|
|
1266
|
-
completedAt DateTime @map("completed_at")
|
|
1291
|
+
completedAt DateTime @default(now()) @map("completed_at")
|
|
1267
1292
|
itemsDeleted Json @map("items_deleted")
|
|
1268
1293
|
createdAt DateTime @default(now()) @map("created_at")
|
|
1269
1294
|
|
|
@@ -1278,22 +1303,22 @@ model DeletionAuditLog {
|
|
|
1278
1303
|
// Age tiers for age-gated behavior
|
|
1279
1304
|
enum AgeTier {
|
|
1280
1305
|
CHILD // under 13
|
|
1281
|
-
TEEN
|
|
1306
|
+
TEEN // 13-17
|
|
1282
1307
|
ADULT // 18+
|
|
1283
1308
|
}
|
|
1284
1309
|
|
|
1285
1310
|
// Profile visibility levels
|
|
1286
1311
|
enum ProfileVisibility {
|
|
1287
|
-
PUBLIC
|
|
1312
|
+
PUBLIC // discoverable in search, visible to all
|
|
1288
1313
|
CONNECTIONS // visible only to mutual follows
|
|
1289
|
-
PRIVATE
|
|
1314
|
+
PRIVATE // visible only to self and guardian
|
|
1290
1315
|
}
|
|
1291
1316
|
|
|
1292
1317
|
// DM access control
|
|
1293
1318
|
enum DmAccess {
|
|
1294
|
-
ANYONE
|
|
1319
|
+
ANYONE // anyone can DM
|
|
1295
1320
|
CONNECTIONS // only mutual follows
|
|
1296
|
-
NOBODY
|
|
1321
|
+
NOBODY // DMs disabled
|
|
1297
1322
|
}
|
|
1298
1323
|
|
|
1299
1324
|
// Parental link status
|
|
@@ -1339,6 +1364,8 @@ enum NotificationType {
|
|
|
1339
1364
|
// Notification model (poll-based, never pushed)
|
|
1340
1365
|
model Notification {
|
|
1341
1366
|
id String @id @default(cuid())
|
|
1367
|
+
// Multi-tenancy (v0.7) — set to recipient's active tenant at write time.
|
|
1368
|
+
tenantId String @map("tenant_id")
|
|
1342
1369
|
userId String @map("user_id")
|
|
1343
1370
|
type NotificationType
|
|
1344
1371
|
title String
|
|
@@ -1349,8 +1376,10 @@ model Notification {
|
|
|
1349
1376
|
deliveredAt DateTime? @map("delivered_at")
|
|
1350
1377
|
batchId String? @map("batch_id")
|
|
1351
1378
|
|
|
1352
|
-
user
|
|
1379
|
+
user User @relation(fields: [userId], references: [id])
|
|
1380
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1353
1381
|
|
|
1382
|
+
@@index([tenantId, userId, read, createdAt])
|
|
1354
1383
|
@@index([userId, read, createdAt])
|
|
1355
1384
|
@@index([userId, batchId])
|
|
1356
1385
|
@@map("notifications")
|
|
@@ -1358,8 +1387,8 @@ model Notification {
|
|
|
1358
1387
|
|
|
1359
1388
|
// Notification preferences (safety and parental always enabled, not configurable)
|
|
1360
1389
|
model NotificationPreference {
|
|
1361
|
-
id
|
|
1362
|
-
userId
|
|
1390
|
+
id String @id @default(cuid())
|
|
1391
|
+
userId String @unique @map("user_id")
|
|
1363
1392
|
dmEnabled Boolean @default(true) @map("dm_enabled")
|
|
1364
1393
|
followEnabled Boolean @default(true) @map("follow_enabled")
|
|
1365
1394
|
digestEnabled Boolean @default(true) @map("digest_enabled")
|
|
@@ -1375,6 +1404,8 @@ model NotificationPreference {
|
|
|
1375
1404
|
// a relationship in the graph DB. Entity-scoped codes can be created by entity owners.
|
|
1376
1405
|
model ConnectionCode {
|
|
1377
1406
|
id String @id @default(cuid())
|
|
1407
|
+
// Multi-tenancy (v0.7) — code's owning tenant (creator's active tenant at create time).
|
|
1408
|
+
tenantId String @map("tenant_id")
|
|
1378
1409
|
code String @unique
|
|
1379
1410
|
creatorId String @map("creator_id")
|
|
1380
1411
|
entityId String? @map("entity_id")
|
|
@@ -1385,8 +1416,10 @@ model ConnectionCode {
|
|
|
1385
1416
|
|
|
1386
1417
|
creator User @relation("ConnectionCodeCreator", fields: [creatorId], references: [id])
|
|
1387
1418
|
entity Entity? @relation(fields: [entityId], references: [id])
|
|
1419
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1388
1420
|
redemptions ConnectionCodeRedemption[]
|
|
1389
1421
|
|
|
1422
|
+
@@index([tenantId])
|
|
1390
1423
|
@@index([creatorId, expiresAt])
|
|
1391
1424
|
@@index([entityId])
|
|
1392
1425
|
@@map("connection_codes")
|
|
@@ -1397,12 +1430,229 @@ model ConnectionCodeRedemption {
|
|
|
1397
1430
|
id String @id @default(cuid())
|
|
1398
1431
|
codeId String @map("code_id")
|
|
1399
1432
|
userId String @map("user_id")
|
|
1433
|
+
tenantId String @map("tenant_id")
|
|
1400
1434
|
createdAt DateTime @default(now()) @map("created_at")
|
|
1401
1435
|
|
|
1402
|
-
code
|
|
1403
|
-
user
|
|
1436
|
+
code ConnectionCode @relation(fields: [codeId], references: [id])
|
|
1437
|
+
user User @relation(fields: [userId], references: [id])
|
|
1438
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1404
1439
|
|
|
1405
1440
|
@@unique([codeId, userId])
|
|
1406
1441
|
@@index([userId])
|
|
1442
|
+
@@index([tenantId])
|
|
1407
1443
|
@@map("connection_code_redemptions")
|
|
1408
1444
|
}
|
|
1445
|
+
|
|
1446
|
+
// ============================================================================
|
|
1447
|
+
// MULTI-TENANCY MODEL (added in v0.7 — see plans/mvp/10-trellis-stages/01-schema-migration.md)
|
|
1448
|
+
// ============================================================================
|
|
1449
|
+
|
|
1450
|
+
model Tenant {
|
|
1451
|
+
id String @id @default(cuid())
|
|
1452
|
+
slug String @unique
|
|
1453
|
+
displayName String @map("display_name")
|
|
1454
|
+
type TenantType
|
|
1455
|
+
status TenantStatus @default(ACTIVE)
|
|
1456
|
+
|
|
1457
|
+
// Data-residency region for the tenant. Surfaced in the per-tenant
|
|
1458
|
+
// compliance bundle (`/api/tenants/:id/compliance.json`). Defaults to
|
|
1459
|
+
// "EU" since the platform's primary region is Frankfurt; tenants can
|
|
1460
|
+
// elect a different region during onboarding.
|
|
1461
|
+
region String @default("EU")
|
|
1462
|
+
|
|
1463
|
+
// For PERSONAL tenants only — the single owner. Always equals the User.id who created it.
|
|
1464
|
+
personalOwnerUserId String? @unique @map("personal_owner_user_id")
|
|
1465
|
+
|
|
1466
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
1467
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
1468
|
+
suspendedAt DateTime? @map("suspended_at")
|
|
1469
|
+
suspendReason String? @map("suspend_reason")
|
|
1470
|
+
|
|
1471
|
+
members TenantMember[]
|
|
1472
|
+
domains TenantDomain[]
|
|
1473
|
+
identityProvider TenantIdentityProvider?
|
|
1474
|
+
roleMappings TenantRoleMapping[]
|
|
1475
|
+
invitations TenantInvitation[]
|
|
1476
|
+
|
|
1477
|
+
// Tenant-scoped resources
|
|
1478
|
+
entities Entity[]
|
|
1479
|
+
posts Post[]
|
|
1480
|
+
postComments PostComment[]
|
|
1481
|
+
groups Group[]
|
|
1482
|
+
groupMembers GroupMember[]
|
|
1483
|
+
entityOwnerships EntityOwnership[]
|
|
1484
|
+
connectionCodes ConnectionCode[]
|
|
1485
|
+
connectionCodeRedemptions ConnectionCodeRedemption[]
|
|
1486
|
+
notifications Notification[]
|
|
1487
|
+
|
|
1488
|
+
// Personal-tenant owner back-relation (uses @relation name to disambiguate)
|
|
1489
|
+
personalOwner User? @relation("PersonalTenantOwner", fields: [personalOwnerUserId], references: [id])
|
|
1490
|
+
|
|
1491
|
+
@@index([slug])
|
|
1492
|
+
@@index([type, status])
|
|
1493
|
+
@@map("tenants")
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
enum TenantType {
|
|
1497
|
+
PERSONAL
|
|
1498
|
+
ORGANIZATION
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
enum TenantStatus {
|
|
1502
|
+
ACTIVE
|
|
1503
|
+
SUSPENDED
|
|
1504
|
+
DELETING // Phase 3 — soft state during cascade-delete
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
model TenantMember {
|
|
1508
|
+
id String @id @default(cuid())
|
|
1509
|
+
tenantId String @map("tenant_id")
|
|
1510
|
+
userId String @map("user_id")
|
|
1511
|
+
role TenantRole
|
|
1512
|
+
status TenantMemberStatus @default(ACTIVE)
|
|
1513
|
+
|
|
1514
|
+
isJitProvisioned Boolean @default(false) @map("is_jit_provisioned")
|
|
1515
|
+
invitedByUserId String? @map("invited_by_user_id")
|
|
1516
|
+
invitedAt DateTime? @map("invited_at")
|
|
1517
|
+
joinedAt DateTime? @map("joined_at")
|
|
1518
|
+
removedAt DateTime? @map("removed_at")
|
|
1519
|
+
lastActiveAt DateTime? @map("last_active_at")
|
|
1520
|
+
|
|
1521
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1522
|
+
user User @relation("TenantMemberships", fields: [userId], references: [id], onDelete: Cascade)
|
|
1523
|
+
invitedBy User? @relation("TenantInvitedBy", fields: [invitedByUserId], references: [id])
|
|
1524
|
+
|
|
1525
|
+
@@unique([tenantId, userId])
|
|
1526
|
+
@@index([userId])
|
|
1527
|
+
@@index([tenantId, status])
|
|
1528
|
+
@@index([tenantId, role])
|
|
1529
|
+
@@map("tenant_members")
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
enum TenantRole {
|
|
1533
|
+
OWNER
|
|
1534
|
+
ADMIN
|
|
1535
|
+
MEMBER
|
|
1536
|
+
GUEST
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
enum TenantMemberStatus {
|
|
1540
|
+
INVITED
|
|
1541
|
+
ACTIVE
|
|
1542
|
+
SUSPENDED
|
|
1543
|
+
REMOVED
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
model TenantDomain {
|
|
1547
|
+
id String @id @default(cuid())
|
|
1548
|
+
tenantId String @map("tenant_id")
|
|
1549
|
+
domain String @unique // lowercased; no protocol
|
|
1550
|
+
verificationToken String @map("verification_token") // 32 hex chars
|
|
1551
|
+
// Token expires 7 days after creation; admin must re-claim if expired (sec finding #5)
|
|
1552
|
+
tokenExpiresAt DateTime @map("token_expires_at")
|
|
1553
|
+
verifiedAt DateTime? @map("verified_at")
|
|
1554
|
+
verifyAttemptedAt DateTime? @map("verify_attempted_at")
|
|
1555
|
+
verifyAttempts Int @default(0) @map("verify_attempts")
|
|
1556
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
1557
|
+
|
|
1558
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1559
|
+
|
|
1560
|
+
@@index([tenantId])
|
|
1561
|
+
@@index([verifiedAt])
|
|
1562
|
+
@@index([tokenExpiresAt])
|
|
1563
|
+
@@map("tenant_domains")
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
model TenantIdentityProvider {
|
|
1567
|
+
id String @id @default(cuid())
|
|
1568
|
+
tenantId String @unique @map("tenant_id")
|
|
1569
|
+
|
|
1570
|
+
kind IdpKind
|
|
1571
|
+
|
|
1572
|
+
// Cognito's IdP record name. Max 32 chars (Cognito quota), unique within the user pool.
|
|
1573
|
+
cognitoIdpName String @unique @map("cognito_idp_name")
|
|
1574
|
+
|
|
1575
|
+
// SAML configuration
|
|
1576
|
+
metadataUrl String? @map("metadata_url")
|
|
1577
|
+
metadataXml String? @map("metadata_xml") @db.Text
|
|
1578
|
+
|
|
1579
|
+
// OIDC configuration
|
|
1580
|
+
issuerUrl String? @map("issuer_url")
|
|
1581
|
+
clientId String? @map("client_id")
|
|
1582
|
+
clientSecretArn String? @map("client_secret_arn") // never the raw secret — Secrets Manager ARN
|
|
1583
|
+
scopes String @default("openid email profile groups")
|
|
1584
|
+
|
|
1585
|
+
attributeMapping Json @default("{}") @map("attribute_mapping")
|
|
1586
|
+
|
|
1587
|
+
// Default role applied when no TenantRoleMapping matches the user's IdP groups.
|
|
1588
|
+
// null means "deny if no group match."
|
|
1589
|
+
defaultRole TenantRole? @map("default_role")
|
|
1590
|
+
|
|
1591
|
+
status IdpStatus @default(PENDING)
|
|
1592
|
+
enabledAt DateTime? @map("enabled_at")
|
|
1593
|
+
lastError String? @map("last_error") @db.Text
|
|
1594
|
+
lastErrorAt DateTime? @map("last_error_at")
|
|
1595
|
+
|
|
1596
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
1597
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
1598
|
+
|
|
1599
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1600
|
+
|
|
1601
|
+
@@index([cognitoIdpName])
|
|
1602
|
+
@@index([status])
|
|
1603
|
+
@@map("tenant_identity_providers")
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
enum IdpKind {
|
|
1607
|
+
SAML
|
|
1608
|
+
OIDC
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
enum IdpStatus {
|
|
1612
|
+
PENDING
|
|
1613
|
+
ACTIVE
|
|
1614
|
+
DISABLED
|
|
1615
|
+
ERROR
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
model TenantRoleMapping {
|
|
1619
|
+
id String @id @default(cuid())
|
|
1620
|
+
tenantId String @map("tenant_id")
|
|
1621
|
+
idpGroupName String @map("idp_group_name")
|
|
1622
|
+
tenantRole TenantRole @map("tenant_role")
|
|
1623
|
+
// Lower priority = higher precedence (admin-grants beat member-grants).
|
|
1624
|
+
priority Int @default(100)
|
|
1625
|
+
|
|
1626
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
1627
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
1628
|
+
|
|
1629
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1630
|
+
|
|
1631
|
+
@@unique([tenantId, idpGroupName])
|
|
1632
|
+
@@index([tenantId, priority])
|
|
1633
|
+
@@map("tenant_role_mappings")
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
model TenantInvitation {
|
|
1637
|
+
id String @id @default(cuid())
|
|
1638
|
+
tenantId String @map("tenant_id")
|
|
1639
|
+
email String
|
|
1640
|
+
role TenantRole
|
|
1641
|
+
token String @unique // signed JWT or random opaque token; one-shot
|
|
1642
|
+
expiresAt DateTime @map("expires_at")
|
|
1643
|
+
acceptedAt DateTime? @map("accepted_at")
|
|
1644
|
+
acceptedByUserId String? @map("accepted_by_user_id")
|
|
1645
|
+
|
|
1646
|
+
invitedByUserId String @map("invited_by_user_id")
|
|
1647
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
1648
|
+
|
|
1649
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
1650
|
+
invitedBy User @relation("TenantInvitationInviter", fields: [invitedByUserId], references: [id])
|
|
1651
|
+
acceptedBy User? @relation("TenantInvitationAcceptedBy", fields: [acceptedByUserId], references: [id])
|
|
1652
|
+
|
|
1653
|
+
@@unique([tenantId, email])
|
|
1654
|
+
@@index([token])
|
|
1655
|
+
@@index([email])
|
|
1656
|
+
@@index([expiresAt])
|
|
1657
|
+
@@map("tenant_invitations")
|
|
1658
|
+
}
|