@digimuza/saas-core 0.0.2
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/CLAUDE.md +1165 -0
- package/LICENSE +21 -0
- package/README.md +234 -0
- package/dist/api-keys/index.d.ts +9 -0
- package/dist/api-keys/index.d.ts.map +1 -0
- package/dist/api-keys/index.js +37 -0
- package/dist/api-keys/index.js.map +1 -0
- package/dist/api-keys/keys.d.ts +55 -0
- package/dist/api-keys/keys.d.ts.map +1 -0
- package/dist/api-keys/keys.js +92 -0
- package/dist/api-keys/keys.js.map +1 -0
- package/dist/api-keys/service.d.ts +111 -0
- package/dist/api-keys/service.d.ts.map +1 -0
- package/dist/api-keys/service.js +210 -0
- package/dist/api-keys/service.js.map +1 -0
- package/dist/audit/index.d.ts +5 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +44 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/service.d.ts +88 -0
- package/dist/audit/service.d.ts.map +1 -0
- package/dist/audit/service.js +128 -0
- package/dist/audit/service.js.map +1 -0
- package/dist/auth/client.d.ts +5 -0
- package/dist/auth/client.d.ts.map +1 -0
- package/dist/auth/client.js +26 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/cookies.d.ts +51 -0
- package/dist/auth/cookies.d.ts.map +1 -0
- package/dist/auth/cookies.js +58 -0
- package/dist/auth/cookies.js.map +1 -0
- package/dist/auth/index.d.ts +14 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +46 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/service.d.ts +67 -0
- package/dist/auth/service.d.ts.map +1 -0
- package/dist/auth/service.js +275 -0
- package/dist/auth/service.js.map +1 -0
- package/dist/auth/types.d.ts +56 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/authorization/index.d.ts +4 -0
- package/dist/authorization/index.d.ts.map +1 -0
- package/dist/authorization/index.js +24 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/authorization/service.d.ts +92 -0
- package/dist/authorization/service.d.ts.map +1 -0
- package/dist/authorization/service.js +254 -0
- package/dist/authorization/service.js.map +1 -0
- package/dist/billing/index.d.ts +16 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +47 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/provider.d.ts +114 -0
- package/dist/billing/provider.d.ts.map +1 -0
- package/dist/billing/provider.js +2 -0
- package/dist/billing/provider.js.map +1 -0
- package/dist/billing/revolut.d.ts +68 -0
- package/dist/billing/revolut.d.ts.map +1 -0
- package/dist/billing/revolut.js +181 -0
- package/dist/billing/revolut.js.map +1 -0
- package/dist/billing/service.d.ts +124 -0
- package/dist/billing/service.d.ts.map +1 -0
- package/dist/billing/service.js +470 -0
- package/dist/billing/service.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +43 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +212 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/project.d.ts +12 -0
- package/dist/cli/commands/project.d.ts.map +1 -0
- package/dist/cli/commands/project.js +49 -0
- package/dist/cli/commands/project.js.map +1 -0
- package/dist/cli/docker.d.ts +50 -0
- package/dist/cli/docker.d.ts.map +1 -0
- package/dist/cli/docker.js +84 -0
- package/dist/cli/docker.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +198 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +30 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +66 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/templates.d.ts +52 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +225 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/config/client.d.ts +60 -0
- package/dist/config/client.d.ts.map +1 -0
- package/dist/config/client.js +94 -0
- package/dist/config/client.js.map +1 -0
- package/dist/config/config-loader.d.ts +2 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +2 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config/env.d.ts +24 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +40 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/errors.d.ts +17 -0
- package/dist/config/errors.d.ts.map +1 -0
- package/dist/config/errors.js +26 -0
- package/dist/config/errors.js.map +1 -0
- package/dist/config/index.d.ts +72 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +106 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/runtime.d.ts +15 -0
- package/dist/config/runtime.d.ts.map +1 -0
- package/dist/config/runtime.js +22 -0
- package/dist/config/runtime.js.map +1 -0
- package/dist/config/sections.d.ts +226 -0
- package/dist/config/sections.d.ts.map +1 -0
- package/dist/config/sections.js +141 -0
- package/dist/config/sections.js.map +1 -0
- package/dist/db/client.d.ts +43 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +77 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/errors.d.ts +39 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +67 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +10 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate.d.ts +21 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +28 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema/_shared.d.ts +30 -0
- package/dist/db/schema/_shared.d.ts.map +1 -0
- package/dist/db/schema/_shared.js +36 -0
- package/dist/db/schema/_shared.js.map +1 -0
- package/dist/db/schema/apikeys.d.ts +263 -0
- package/dist/db/schema/apikeys.d.ts.map +1 -0
- package/dist/db/schema/apikeys.js +70 -0
- package/dist/db/schema/apikeys.js.map +1 -0
- package/dist/db/schema/authorization.d.ts +465 -0
- package/dist/db/schema/authorization.d.ts.map +1 -0
- package/dist/db/schema/authorization.js +134 -0
- package/dist/db/schema/authorization.js.map +1 -0
- package/dist/db/schema/billing.d.ts +1100 -0
- package/dist/db/schema/billing.d.ts.map +1 -0
- package/dist/db/schema/billing.js +223 -0
- package/dist/db/schema/billing.js.map +1 -0
- package/dist/db/schema/events.d.ts +161 -0
- package/dist/db/schema/events.d.ts.map +1 -0
- package/dist/db/schema/events.js +66 -0
- package/dist/db/schema/events.js.map +1 -0
- package/dist/db/schema/features.d.ts +243 -0
- package/dist/db/schema/features.d.ts.map +1 -0
- package/dist/db/schema/features.js +87 -0
- package/dist/db/schema/features.js.map +1 -0
- package/dist/db/schema/files.d.ts +253 -0
- package/dist/db/schema/files.d.ts.map +1 -0
- package/dist/db/schema/files.js +74 -0
- package/dist/db/schema/files.js.map +1 -0
- package/dist/db/schema/index.d.ts +38 -0
- package/dist/db/schema/index.d.ts.map +1 -0
- package/dist/db/schema/index.js +38 -0
- package/dist/db/schema/index.js.map +1 -0
- package/dist/db/schema/invitations.d.ts +214 -0
- package/dist/db/schema/invitations.d.ts.map +1 -0
- package/dist/db/schema/invitations.js +80 -0
- package/dist/db/schema/invitations.js.map +1 -0
- package/dist/db/schema/messaging.d.ts +442 -0
- package/dist/db/schema/messaging.d.ts.map +1 -0
- package/dist/db/schema/messaging.js +120 -0
- package/dist/db/schema/messaging.js.map +1 -0
- package/dist/db/schema/users.d.ts +246 -0
- package/dist/db/schema/users.d.ts.map +1 -0
- package/dist/db/schema/users.js +60 -0
- package/dist/db/schema/users.js.map +1 -0
- package/dist/db/schema/webhooks.d.ts +693 -0
- package/dist/db/schema/webhooks.d.ts.map +1 -0
- package/dist/db/schema/webhooks.js +147 -0
- package/dist/db/schema/webhooks.js.map +1 -0
- package/dist/db/schema/workspaces.d.ts +196 -0
- package/dist/db/schema/workspaces.d.ts.map +1 -0
- package/dist/db/schema/workspaces.js +57 -0
- package/dist/db/schema/workspaces.js.map +1 -0
- package/dist/db/types.d.ts +18 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/email/adapters.d.ts +29 -0
- package/dist/email/adapters.d.ts.map +1 -0
- package/dist/email/adapters.js +60 -0
- package/dist/email/adapters.js.map +1 -0
- package/dist/email/index.d.ts +20 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +79 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/provider.d.ts +47 -0
- package/dist/email/provider.d.ts.map +1 -0
- package/dist/email/provider.js +7 -0
- package/dist/email/provider.js.map +1 -0
- package/dist/email/resend.d.ts +22 -0
- package/dist/email/resend.d.ts.map +1 -0
- package/dist/email/resend.js +51 -0
- package/dist/email/resend.js.map +1 -0
- package/dist/email/service.d.ts +55 -0
- package/dist/email/service.d.ts.map +1 -0
- package/dist/email/service.js +87 -0
- package/dist/email/service.js.map +1 -0
- package/dist/email/templates.d.ts +81 -0
- package/dist/email/templates.d.ts.map +1 -0
- package/dist/email/templates.js +84 -0
- package/dist/email/templates.js.map +1 -0
- package/dist/entitlements/index.d.ts +10 -0
- package/dist/entitlements/index.d.ts.map +1 -0
- package/dist/entitlements/index.js +28 -0
- package/dist/entitlements/index.js.map +1 -0
- package/dist/entitlements/service.d.ts +83 -0
- package/dist/entitlements/service.d.ts.map +1 -0
- package/dist/entitlements/service.js +158 -0
- package/dist/entitlements/service.js.map +1 -0
- package/dist/errors.d.ts +85 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +95 -0
- package/dist/errors.js.map +1 -0
- package/dist/events/core-events.d.ts +120 -0
- package/dist/events/core-events.d.ts.map +1 -0
- package/dist/events/core-events.js +109 -0
- package/dist/events/core-events.js.map +1 -0
- package/dist/events/index.d.ts +15 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +58 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/publisher.d.ts +46 -0
- package/dist/events/publisher.d.ts.map +1 -0
- package/dist/events/publisher.js +54 -0
- package/dist/events/publisher.js.map +1 -0
- package/dist/events/registry.d.ts +87 -0
- package/dist/events/registry.d.ts.map +1 -0
- package/dist/events/registry.js +34 -0
- package/dist/events/registry.js.map +1 -0
- package/dist/events/service.d.ts +78 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/service.js +182 -0
- package/dist/events/service.js.map +1 -0
- package/dist/features/bucket.d.ts +22 -0
- package/dist/features/bucket.d.ts.map +1 -0
- package/dist/features/bucket.js +42 -0
- package/dist/features/bucket.js.map +1 -0
- package/dist/features/index.d.ts +15 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +78 -0
- package/dist/features/index.js.map +1 -0
- package/dist/features/registry.d.ts +51 -0
- package/dist/features/registry.d.ts.map +1 -0
- package/dist/features/registry.js +52 -0
- package/dist/features/registry.js.map +1 -0
- package/dist/features/service.d.ts +78 -0
- package/dist/features/service.d.ts.map +1 -0
- package/dist/features/service.js +207 -0
- package/dist/features/service.js.map +1 -0
- package/dist/files/index.d.ts +6 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +64 -0
- package/dist/files/index.js.map +1 -0
- package/dist/files/service.d.ts +92 -0
- package/dist/files/service.d.ts.map +1 -0
- package/dist/files/service.js +303 -0
- package/dist/files/service.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs/backend.d.ts +23 -0
- package/dist/jobs/backend.d.ts.map +1 -0
- package/dist/jobs/backend.js +23 -0
- package/dist/jobs/backend.js.map +1 -0
- package/dist/jobs/index.d.ts +29 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +114 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/service.d.ts +67 -0
- package/dist/jobs/service.d.ts.map +1 -0
- package/dist/jobs/service.js +139 -0
- package/dist/jobs/service.js.map +1 -0
- package/dist/jobs/types.d.ts +78 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +4 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/logger/default.d.ts +7 -0
- package/dist/logger/default.d.ts.map +1 -0
- package/dist/logger/default.js +28 -0
- package/dist/logger/default.js.map +1 -0
- package/dist/logger/index.d.ts +24 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +90 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/redact.d.ts +40 -0
- package/dist/logger/redact.d.ts.map +1 -0
- package/dist/logger/redact.js +178 -0
- package/dist/logger/redact.js.map +1 -0
- package/dist/logger/service.d.ts +14 -0
- package/dist/logger/service.d.ts.map +1 -0
- package/dist/logger/service.js +56 -0
- package/dist/logger/service.js.map +1 -0
- package/dist/logger/sinks.d.ts +37 -0
- package/dist/logger/sinks.d.ts.map +1 -0
- package/dist/logger/sinks.js +135 -0
- package/dist/logger/sinks.js.map +1 -0
- package/dist/logger/types.d.ts +47 -0
- package/dist/logger/types.d.ts.map +1 -0
- package/dist/logger/types.js +8 -0
- package/dist/logger/types.js.map +1 -0
- package/dist/notifications/index.d.ts +8 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +72 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/service.d.ts +79 -0
- package/dist/notifications/service.d.ts.map +1 -0
- package/dist/notifications/service.js +164 -0
- package/dist/notifications/service.js.map +1 -0
- package/dist/realtime/channels.d.ts +37 -0
- package/dist/realtime/channels.d.ts.map +1 -0
- package/dist/realtime/channels.js +58 -0
- package/dist/realtime/channels.js.map +1 -0
- package/dist/realtime/index.d.ts +19 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/index.js +118 -0
- package/dist/realtime/index.js.map +1 -0
- package/dist/realtime/postgres.d.ts +48 -0
- package/dist/realtime/postgres.d.ts.map +1 -0
- package/dist/realtime/postgres.js +151 -0
- package/dist/realtime/postgres.js.map +1 -0
- package/dist/realtime/service.d.ts +75 -0
- package/dist/realtime/service.d.ts.map +1 -0
- package/dist/realtime/service.js +103 -0
- package/dist/realtime/service.js.map +1 -0
- package/dist/realtime/sse.d.ts +55 -0
- package/dist/realtime/sse.d.ts.map +1 -0
- package/dist/realtime/sse.js +123 -0
- package/dist/realtime/sse.js.map +1 -0
- package/dist/realtime/transport.d.ts +52 -0
- package/dist/realtime/transport.d.ts.map +1 -0
- package/dist/realtime/transport.js +2 -0
- package/dist/realtime/transport.js.map +1 -0
- package/dist/storage/index.d.ts +18 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +89 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/keys.d.ts +50 -0
- package/dist/storage/keys.d.ts.map +1 -0
- package/dist/storage/keys.js +120 -0
- package/dist/storage/keys.js.map +1 -0
- package/dist/storage/memory.d.ts +32 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +84 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/provider.d.ts +93 -0
- package/dist/storage/provider.d.ts.map +1 -0
- package/dist/storage/provider.js +2 -0
- package/dist/storage/provider.js.map +1 -0
- package/dist/storage/s3.d.ts +34 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +175 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/storage/service.d.ts +86 -0
- package/dist/storage/service.d.ts.map +1 -0
- package/dist/storage/service.js +107 -0
- package/dist/storage/service.js.map +1 -0
- package/dist/storage/sigv4.d.ts +58 -0
- package/dist/storage/sigv4.d.ts.map +1 -0
- package/dist/storage/sigv4.js +114 -0
- package/dist/storage/sigv4.js.map +1 -0
- package/dist/streams/client.d.ts +91 -0
- package/dist/streams/client.d.ts.map +1 -0
- package/dist/streams/client.js +128 -0
- package/dist/streams/client.js.map +1 -0
- package/dist/streams/events.d.ts +68 -0
- package/dist/streams/events.d.ts.map +1 -0
- package/dist/streams/events.js +81 -0
- package/dist/streams/events.js.map +1 -0
- package/dist/streams/index.d.ts +47 -0
- package/dist/streams/index.d.ts.map +1 -0
- package/dist/streams/index.js +47 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/realtime.d.ts +27 -0
- package/dist/streams/realtime.d.ts.map +1 -0
- package/dist/streams/realtime.js +50 -0
- package/dist/streams/realtime.js.map +1 -0
- package/dist/streams/state.d.ts +26 -0
- package/dist/streams/state.d.ts.map +1 -0
- package/dist/streams/state.js +34 -0
- package/dist/streams/state.js.map +1 -0
- package/dist/users/index.d.ts +9 -0
- package/dist/users/index.d.ts.map +1 -0
- package/dist/users/index.js +23 -0
- package/dist/users/index.js.map +1 -0
- package/dist/users/service.d.ts +43 -0
- package/dist/users/service.d.ts.map +1 -0
- package/dist/users/service.js +113 -0
- package/dist/users/service.js.map +1 -0
- package/dist/webhooks/inbound.d.ts +70 -0
- package/dist/webhooks/inbound.d.ts.map +1 -0
- package/dist/webhooks/inbound.js +146 -0
- package/dist/webhooks/inbound.js.map +1 -0
- package/dist/webhooks/index.d.ts +12 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +65 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/outgoing.d.ts +89 -0
- package/dist/webhooks/outgoing.d.ts.map +1 -0
- package/dist/webhooks/outgoing.js +375 -0
- package/dist/webhooks/outgoing.js.map +1 -0
- package/dist/webhooks/signature.d.ts +40 -0
- package/dist/webhooks/signature.d.ts.map +1 -0
- package/dist/webhooks/signature.js +77 -0
- package/dist/webhooks/signature.js.map +1 -0
- package/dist/workspaces/context.d.ts +30 -0
- package/dist/workspaces/context.d.ts.map +1 -0
- package/dist/workspaces/context.js +12 -0
- package/dist/workspaces/context.js.map +1 -0
- package/dist/workspaces/index.d.ts +5 -0
- package/dist/workspaces/index.d.ts.map +1 -0
- package/dist/workspaces/index.js +21 -0
- package/dist/workspaces/index.js.map +1 -0
- package/dist/workspaces/service.d.ts +126 -0
- package/dist/workspaces/service.d.ts.map +1 -0
- package/dist/workspaces/service.js +495 -0
- package/dist/workspaces/service.js.map +1 -0
- package/docs/README.md +71 -0
- package/docs/api-keys.md +184 -0
- package/docs/audit.md +198 -0
- package/docs/auth.md +196 -0
- package/docs/authorization.md +225 -0
- package/docs/billing.md +293 -0
- package/docs/cli.md +238 -0
- package/docs/config.md +163 -0
- package/docs/db.md +219 -0
- package/docs/email.md +222 -0
- package/docs/entitlements.md +194 -0
- package/docs/errors.md +158 -0
- package/docs/events.md +279 -0
- package/docs/feature-flags.md +237 -0
- package/docs/files.md +229 -0
- package/docs/jobs.md +288 -0
- package/docs/logger.md +269 -0
- package/docs/notifications.md +179 -0
- package/docs/publishing.md +128 -0
- package/docs/realtime.md +360 -0
- package/docs/storage.md +279 -0
- package/docs/streams.md +296 -0
- package/docs/users.md +117 -0
- package/docs/webhooks.md +272 -0
- package/docs/workspaces.md +269 -0
- package/drizzle/0000_init_users.sql +21 -0
- package/drizzle/0001_workspaces_roles_permissions.sql +86 -0
- package/drizzle/0002_billing_entitlements.sql +97 -0
- package/drizzle/0003_events.sql +17 -0
- package/drizzle/0004_notifications_audit.sql +64 -0
- package/drizzle/0005_api_keys_webhooks.sql +77 -0
- package/drizzle/0006_feature_flags.sql +22 -0
- package/drizzle/0007_files.sql +23 -0
- package/drizzle/meta/0000_snapshot.json +160 -0
- package/drizzle/meta/0001_snapshot.json +837 -0
- package/drizzle/meta/0002_snapshot.json +1580 -0
- package/drizzle/meta/0003_snapshot.json +1751 -0
- package/drizzle/meta/0004_snapshot.json +2109 -0
- package/drizzle/meta/0005_snapshot.json +2719 -0
- package/drizzle/meta/0006_snapshot.json +2909 -0
- package/drizzle/meta/0007_snapshot.json +3113 -0
- package/drizzle/meta/_journal.json +62 -0
- package/package.json +180 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { CoreDatabase } from "../db/types.ts";
|
|
2
|
+
import { type CoreEventPublisher } from "../events/publisher.ts";
|
|
3
|
+
import type { Role, Workspace, WorkspaceInvitation } from "../db/schema/index.ts";
|
|
4
|
+
import { type WorkspaceContext } from "./context.ts";
|
|
5
|
+
/** Roles created with every new workspace, so a workspace is never role-less. */
|
|
6
|
+
export interface SystemRoleDefinition {
|
|
7
|
+
readonly key: string;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly description?: string;
|
|
10
|
+
/** Granted to new members when an invitation names no role. */
|
|
11
|
+
readonly isDefault?: boolean;
|
|
12
|
+
/** Permission keys to grant. Must already exist in `permissions`. */
|
|
13
|
+
readonly permissions?: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Default role set for a new workspace.
|
|
17
|
+
*
|
|
18
|
+
* `admin` receives permissions explicitly registered by the application, so the
|
|
19
|
+
* core does not invent a permission vocabulary. See
|
|
20
|
+
* `authorization.registerPermissions()`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEFAULT_SYSTEM_ROLES: readonly SystemRoleDefinition[];
|
|
23
|
+
export interface WorkspaceMemberSummary {
|
|
24
|
+
readonly userId: string;
|
|
25
|
+
readonly email: string | null;
|
|
26
|
+
readonly displayName: string | null;
|
|
27
|
+
readonly avatarUrl: string | null;
|
|
28
|
+
readonly joinedAt: Date;
|
|
29
|
+
readonly roles: readonly Pick<Role, "id" | "key" | "name">[];
|
|
30
|
+
}
|
|
31
|
+
export interface CreateWorkspaceInput {
|
|
32
|
+
readonly name: string;
|
|
33
|
+
/** URL identifier. Derived from `name` when omitted. */
|
|
34
|
+
readonly slug?: string;
|
|
35
|
+
/** The creating user. Becomes the first member, with the `admin` role. */
|
|
36
|
+
readonly ownerUserId: string;
|
|
37
|
+
/** Overrides {@link DEFAULT_SYSTEM_ROLES}. */
|
|
38
|
+
readonly systemRoles?: readonly SystemRoleDefinition[];
|
|
39
|
+
}
|
|
40
|
+
export interface InviteMemberInput {
|
|
41
|
+
readonly email: string;
|
|
42
|
+
/** Role granted on acceptance. Falls back to the workspace default role. */
|
|
43
|
+
readonly roleId?: string;
|
|
44
|
+
readonly expiresInSeconds?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface InvitationWithToken {
|
|
47
|
+
readonly invitation: WorkspaceInvitation;
|
|
48
|
+
/**
|
|
49
|
+
* The single-use token, to be emailed to the invitee.
|
|
50
|
+
*
|
|
51
|
+
* Only the hash is stored, so this is the one and only time the token is
|
|
52
|
+
* available. Losing it means revoking the invitation and issuing a new one.
|
|
53
|
+
*/
|
|
54
|
+
readonly token: string;
|
|
55
|
+
}
|
|
56
|
+
export interface AcceptInvitationInput {
|
|
57
|
+
readonly token: string;
|
|
58
|
+
/** The authenticated user accepting. Never taken from the invitation payload. */
|
|
59
|
+
readonly userId: string;
|
|
60
|
+
}
|
|
61
|
+
export declare function slugify(value: string): string;
|
|
62
|
+
export interface WorkspacesService {
|
|
63
|
+
create(input: CreateWorkspaceInput): Promise<Workspace>;
|
|
64
|
+
/** Look up a workspace the caller is a member of. */
|
|
65
|
+
get(context: WorkspaceContext): Promise<Workspace>;
|
|
66
|
+
update(context: WorkspaceContext, patch: {
|
|
67
|
+
name?: string;
|
|
68
|
+
slug?: string;
|
|
69
|
+
}): Promise<Workspace>;
|
|
70
|
+
delete(context: WorkspaceContext): Promise<void>;
|
|
71
|
+
/** Every workspace the user belongs to. */
|
|
72
|
+
listForUser(userId: string): Promise<Workspace[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Verify that `userId` is a member of `workspaceId` and return a context.
|
|
75
|
+
*
|
|
76
|
+
* This is the only sanctioned way to turn an untrusted workspace identifier
|
|
77
|
+
* into something the rest of the core will accept.
|
|
78
|
+
*/
|
|
79
|
+
resolveContext(input: {
|
|
80
|
+
userId: string;
|
|
81
|
+
workspaceId: string;
|
|
82
|
+
}): Promise<WorkspaceContext | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* As {@link resolveContext}, but throws.
|
|
85
|
+
*
|
|
86
|
+
* Raises {@link NotFoundError} rather than a permission error: telling a
|
|
87
|
+
* non-member that a workspace exists would let them enumerate other tenants.
|
|
88
|
+
*/
|
|
89
|
+
requireContext(input: {
|
|
90
|
+
userId: string;
|
|
91
|
+
workspaceId: string;
|
|
92
|
+
}): Promise<WorkspaceContext>;
|
|
93
|
+
/** Resolve by slug, still verifying membership. */
|
|
94
|
+
requireContextBySlug(input: {
|
|
95
|
+
userId: string;
|
|
96
|
+
slug: string;
|
|
97
|
+
}): Promise<WorkspaceContext>;
|
|
98
|
+
readonly members: {
|
|
99
|
+
list(context: WorkspaceContext): Promise<WorkspaceMemberSummary[]>;
|
|
100
|
+
/** Add an existing user directly, bypassing the invitation flow. */
|
|
101
|
+
add(context: WorkspaceContext, input: {
|
|
102
|
+
userId: string;
|
|
103
|
+
roleIds?: readonly string[];
|
|
104
|
+
}): Promise<void>;
|
|
105
|
+
remove(context: WorkspaceContext, userId: string): Promise<void>;
|
|
106
|
+
invite(context: WorkspaceContext, input: InviteMemberInput): Promise<InvitationWithToken>;
|
|
107
|
+
listInvitations(context: WorkspaceContext): Promise<WorkspaceInvitation[]>;
|
|
108
|
+
revokeInvitation(context: WorkspaceContext, invitationId: string): Promise<void>;
|
|
109
|
+
/** Redeem a token. Not workspace-scoped: the token identifies the workspace. */
|
|
110
|
+
acceptInvitation(input: AcceptInvitationInput): Promise<WorkspaceContext>;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export interface WorkspacesServiceDeps {
|
|
114
|
+
readonly db: CoreDatabase;
|
|
115
|
+
/**
|
|
116
|
+
* Optional event publisher.
|
|
117
|
+
*
|
|
118
|
+
* Omit it and the service behaves exactly as before, emitting nothing — so
|
|
119
|
+
* events are opt-in rather than a hidden dependency. When provided, each event
|
|
120
|
+
* is written in the same transaction as the state change it describes and
|
|
121
|
+
* dispatched only after that transaction commits.
|
|
122
|
+
*/
|
|
123
|
+
readonly events?: CoreEventPublisher;
|
|
124
|
+
}
|
|
125
|
+
export declare function createWorkspacesService(deps: WorkspacesServiceDeps): WorkspacesService;
|
|
126
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/workspaces/service.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAe,KAAK,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAE7E,iFAAiF;AACjF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,oBAAoB,EAG/D,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC;CAC9D;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACxD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAKD,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAa7C;AAOD,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAExD,qDAAqD;IACrD,GAAG,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAEnD,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAE/F,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD,2CAA2C;IAC3C,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAElD;;;;;OAKG;IACH,cAAc,CAAC,KAAK,EAAE;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;IAE1C;;;;;OAKG;IACH,cAAc,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE1F,mDAAmD;IACnD,oBAAoB,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAEzF,QAAQ,CAAC,OAAO,EAAE;QAChB,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;QACnE,oEAAoE;QACpE,GAAG,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;SAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACtG,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAC1F,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;QAC3E,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACjF,gFAAgF;QAChF,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;KAC3E,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B;;;;;;;OAOG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;CACtC;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,qBAAqB,GAAG,iBAAiB,CAkmBtF"}
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { and, eq, isNull, sql } from "drizzle-orm";
|
|
3
|
+
import { ConflictError, InvalidInputError, NotFoundError } from "../errors.js";
|
|
4
|
+
import { isUniqueViolation } from "../db/errors.js";
|
|
5
|
+
import { roles, userProfiles, users, workspaceInvitations, workspaceMemberRoles, workspaceMembers, workspaces, } from "../db/schema/index.js";
|
|
6
|
+
import { EventOutbox } from "../events/publisher.js";
|
|
7
|
+
import { unsafeWorkspaceContext } from "./context.js";
|
|
8
|
+
/**
|
|
9
|
+
* Default role set for a new workspace.
|
|
10
|
+
*
|
|
11
|
+
* `admin` receives permissions explicitly registered by the application, so the
|
|
12
|
+
* core does not invent a permission vocabulary. See
|
|
13
|
+
* `authorization.registerPermissions()`.
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_SYSTEM_ROLES = [
|
|
16
|
+
{ key: "admin", name: "Admin", description: "Full access to the workspace." },
|
|
17
|
+
{ key: "member", name: "Member", description: "Standard access.", isDefault: true },
|
|
18
|
+
];
|
|
19
|
+
/** 7 days. */
|
|
20
|
+
const DEFAULT_INVITATION_TTL_SECONDS = 60 * 60 * 24 * 7;
|
|
21
|
+
export function slugify(value) {
|
|
22
|
+
const slug = value
|
|
23
|
+
.normalize("NFKD")
|
|
24
|
+
.replace(/[̀-ͯ]/g, "")
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
27
|
+
.replace(/^-+|-+$/g, "")
|
|
28
|
+
.slice(0, 60);
|
|
29
|
+
if (slug.length === 0) {
|
|
30
|
+
throw new InvalidInputError("Workspace name must contain at least one alphanumeric character.");
|
|
31
|
+
}
|
|
32
|
+
return slug;
|
|
33
|
+
}
|
|
34
|
+
/** Invitation tokens are compared by hash, so the digest must be deterministic. */
|
|
35
|
+
function hashToken(token) {
|
|
36
|
+
return createHash("sha256").update(token).digest("hex");
|
|
37
|
+
}
|
|
38
|
+
export function createWorkspacesService(deps) {
|
|
39
|
+
const outbox = () => new EventOutbox(deps.events);
|
|
40
|
+
async function findMembership(userId, workspaceId) {
|
|
41
|
+
const rows = await deps.db
|
|
42
|
+
.select({ userId: workspaceMembers.userId })
|
|
43
|
+
.from(workspaceMembers)
|
|
44
|
+
.where(and(eq(workspaceMembers.workspaceId, workspaceId), eq(workspaceMembers.userId, userId)))
|
|
45
|
+
.limit(1);
|
|
46
|
+
return rows.length > 0;
|
|
47
|
+
}
|
|
48
|
+
async function seedRoles(tx, workspaceId, definitions) {
|
|
49
|
+
return tx
|
|
50
|
+
.insert(roles)
|
|
51
|
+
.values(definitions.map((definition) => ({
|
|
52
|
+
workspaceId,
|
|
53
|
+
key: definition.key,
|
|
54
|
+
name: definition.name,
|
|
55
|
+
description: definition.description ?? null,
|
|
56
|
+
isSystem: true,
|
|
57
|
+
isDefault: definition.isDefault ?? false,
|
|
58
|
+
})))
|
|
59
|
+
.returning();
|
|
60
|
+
}
|
|
61
|
+
const members = {
|
|
62
|
+
async list(context) {
|
|
63
|
+
const rows = await deps.db
|
|
64
|
+
.select({
|
|
65
|
+
userId: workspaceMembers.userId,
|
|
66
|
+
joinedAt: workspaceMembers.createdAt,
|
|
67
|
+
email: userProfiles.email,
|
|
68
|
+
displayName: userProfiles.displayName,
|
|
69
|
+
avatarUrl: userProfiles.avatarUrl,
|
|
70
|
+
roleId: roles.id,
|
|
71
|
+
roleKey: roles.key,
|
|
72
|
+
roleName: roles.name,
|
|
73
|
+
})
|
|
74
|
+
.from(workspaceMembers)
|
|
75
|
+
.leftJoin(userProfiles, eq(userProfiles.userId, workspaceMembers.userId))
|
|
76
|
+
.leftJoin(workspaceMemberRoles, and(eq(workspaceMemberRoles.workspaceId, workspaceMembers.workspaceId), eq(workspaceMemberRoles.userId, workspaceMembers.userId)))
|
|
77
|
+
.leftJoin(roles, eq(roles.id, workspaceMemberRoles.roleId))
|
|
78
|
+
.where(eq(workspaceMembers.workspaceId, context.workspaceId));
|
|
79
|
+
// One row per member/role pair; fold roles into their member.
|
|
80
|
+
const byUser = new Map();
|
|
81
|
+
const collected = new Map();
|
|
82
|
+
for (const row of rows) {
|
|
83
|
+
if (!byUser.has(row.userId)) {
|
|
84
|
+
byUser.set(row.userId, {
|
|
85
|
+
userId: row.userId,
|
|
86
|
+
email: row.email,
|
|
87
|
+
displayName: row.displayName,
|
|
88
|
+
avatarUrl: row.avatarUrl,
|
|
89
|
+
joinedAt: row.joinedAt,
|
|
90
|
+
roles: [],
|
|
91
|
+
});
|
|
92
|
+
collected.set(row.userId, []);
|
|
93
|
+
}
|
|
94
|
+
if (row.roleId !== null && row.roleKey !== null && row.roleName !== null) {
|
|
95
|
+
collected.get(row.userId).push({ id: row.roleId, key: row.roleKey, name: row.roleName });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return [...byUser.values()].map((member) => ({
|
|
99
|
+
...member,
|
|
100
|
+
roles: collected.get(member.userId) ?? [],
|
|
101
|
+
}));
|
|
102
|
+
},
|
|
103
|
+
async add(context, input) {
|
|
104
|
+
const events = outbox();
|
|
105
|
+
await deps.db.transaction(async (tx) => {
|
|
106
|
+
await tx
|
|
107
|
+
.insert(workspaceMembers)
|
|
108
|
+
.values({ workspaceId: context.workspaceId, userId: input.userId })
|
|
109
|
+
.onConflictDoNothing();
|
|
110
|
+
let roleIds;
|
|
111
|
+
if (input.roleIds !== undefined) {
|
|
112
|
+
roleIds = input.roleIds;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const defaultRole = await tx
|
|
116
|
+
.select({ id: roles.id })
|
|
117
|
+
.from(roles)
|
|
118
|
+
.where(and(eq(roles.workspaceId, context.workspaceId), eq(roles.isDefault, true)))
|
|
119
|
+
.limit(1);
|
|
120
|
+
roleIds = defaultRole.map((row) => row.id);
|
|
121
|
+
}
|
|
122
|
+
if (roleIds.length > 0) {
|
|
123
|
+
await tx
|
|
124
|
+
.insert(workspaceMemberRoles)
|
|
125
|
+
.values(roleIds.map((roleId) => ({
|
|
126
|
+
workspaceId: context.workspaceId,
|
|
127
|
+
userId: input.userId,
|
|
128
|
+
roleId,
|
|
129
|
+
})))
|
|
130
|
+
.onConflictDoNothing();
|
|
131
|
+
}
|
|
132
|
+
await events.record({
|
|
133
|
+
type: "workspace.member.created",
|
|
134
|
+
workspaceId: context.workspaceId,
|
|
135
|
+
actorId: context.userId,
|
|
136
|
+
data: { userId: input.userId, roleIds: [...roleIds] },
|
|
137
|
+
}, tx);
|
|
138
|
+
});
|
|
139
|
+
await events.flush();
|
|
140
|
+
},
|
|
141
|
+
async remove(context, userId) {
|
|
142
|
+
/**
|
|
143
|
+
* Delete first, then check what is left, inside one transaction.
|
|
144
|
+
*
|
|
145
|
+
* Counting before deleting would be wrong twice over: it reports
|
|
146
|
+
* "last member" for a user who is not a member at all — leaking the
|
|
147
|
+
* workspace's member count to an outsider — and it races, since two
|
|
148
|
+
* concurrent removals could each see two members and both proceed.
|
|
149
|
+
* Throwing here rolls the delete back.
|
|
150
|
+
*/
|
|
151
|
+
const events = outbox();
|
|
152
|
+
await deps.db.transaction(async (tx) => {
|
|
153
|
+
const deleted = await tx
|
|
154
|
+
.delete(workspaceMembers)
|
|
155
|
+
.where(and(eq(workspaceMembers.workspaceId, context.workspaceId), eq(workspaceMembers.userId, userId)))
|
|
156
|
+
.returning({ userId: workspaceMembers.userId });
|
|
157
|
+
// Scoped to the caller's workspace, so a member of another tenant
|
|
158
|
+
// matches nothing and is reported as not found.
|
|
159
|
+
if (deleted.length === 0) {
|
|
160
|
+
throw new NotFoundError("Workspace member not found.");
|
|
161
|
+
}
|
|
162
|
+
const remaining = await tx
|
|
163
|
+
.select({ count: sql `count(*)::int` })
|
|
164
|
+
.from(workspaceMembers)
|
|
165
|
+
.where(eq(workspaceMembers.workspaceId, context.workspaceId));
|
|
166
|
+
// An empty workspace is unreachable: nobody left to administer it.
|
|
167
|
+
if ((remaining[0]?.count ?? 0) === 0) {
|
|
168
|
+
throw new ConflictError("A workspace must keep at least one member.");
|
|
169
|
+
}
|
|
170
|
+
await events.record({
|
|
171
|
+
type: "workspace.member.removed",
|
|
172
|
+
workspaceId: context.workspaceId,
|
|
173
|
+
actorId: context.userId,
|
|
174
|
+
data: { userId },
|
|
175
|
+
}, tx);
|
|
176
|
+
});
|
|
177
|
+
await events.flush();
|
|
178
|
+
// Role grants cascade via workspace_member_roles_member_fk.
|
|
179
|
+
},
|
|
180
|
+
async invite(context, input) {
|
|
181
|
+
const email = input.email.trim().toLowerCase();
|
|
182
|
+
if (email.length === 0 || !email.includes("@")) {
|
|
183
|
+
throw new InvalidInputError("A valid email address is required.");
|
|
184
|
+
}
|
|
185
|
+
const token = randomBytes(32).toString("base64url");
|
|
186
|
+
const expiresAt = new Date(Date.now() + (input.expiresInSeconds ?? DEFAULT_INVITATION_TTL_SECONDS) * 1000);
|
|
187
|
+
const events = outbox();
|
|
188
|
+
try {
|
|
189
|
+
const invitation = await deps.db.transaction(async (tx) => {
|
|
190
|
+
const [created] = await tx
|
|
191
|
+
.insert(workspaceInvitations)
|
|
192
|
+
.values({
|
|
193
|
+
workspaceId: context.workspaceId,
|
|
194
|
+
email,
|
|
195
|
+
tokenHash: hashToken(token),
|
|
196
|
+
invitedByUserId: context.userId,
|
|
197
|
+
roleId: input.roleId ?? null,
|
|
198
|
+
expiresAt,
|
|
199
|
+
})
|
|
200
|
+
.returning();
|
|
201
|
+
await events.record({
|
|
202
|
+
type: "workspace.invitation.created",
|
|
203
|
+
workspaceId: context.workspaceId,
|
|
204
|
+
actorId: context.userId,
|
|
205
|
+
// The token is never included: events are durable and readable,
|
|
206
|
+
// and an invitation token is a bearer credential.
|
|
207
|
+
data: { invitationId: created.id, email, roleId: created.roleId },
|
|
208
|
+
}, tx);
|
|
209
|
+
return created;
|
|
210
|
+
});
|
|
211
|
+
await events.flush();
|
|
212
|
+
return { invitation, token };
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (isUniqueViolation(error, "workspace_invitations_pending_idx")) {
|
|
216
|
+
throw new ConflictError("An invitation for this address is already outstanding.", {
|
|
217
|
+
cause: error,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
async listInvitations(context) {
|
|
224
|
+
return deps.db
|
|
225
|
+
.select()
|
|
226
|
+
.from(workspaceInvitations)
|
|
227
|
+
.where(and(eq(workspaceInvitations.workspaceId, context.workspaceId), isNull(workspaceInvitations.acceptedAt), isNull(workspaceInvitations.revokedAt)));
|
|
228
|
+
},
|
|
229
|
+
async revokeInvitation(context, invitationId) {
|
|
230
|
+
const events = outbox();
|
|
231
|
+
const revoked = await deps.db
|
|
232
|
+
.update(workspaceInvitations)
|
|
233
|
+
.set({ revokedAt: new Date(), updatedAt: new Date() })
|
|
234
|
+
.where(and(eq(workspaceInvitations.id, invitationId),
|
|
235
|
+
// Scoped to the caller's workspace, so an id from another tenant
|
|
236
|
+
// cannot be revoked.
|
|
237
|
+
eq(workspaceInvitations.workspaceId, context.workspaceId), isNull(workspaceInvitations.acceptedAt), isNull(workspaceInvitations.revokedAt)))
|
|
238
|
+
.returning({ id: workspaceInvitations.id });
|
|
239
|
+
if (revoked.length === 0) {
|
|
240
|
+
throw new NotFoundError("Outstanding invitation not found.");
|
|
241
|
+
}
|
|
242
|
+
await events.record({
|
|
243
|
+
type: "workspace.invitation.revoked",
|
|
244
|
+
workspaceId: context.workspaceId,
|
|
245
|
+
actorId: context.userId,
|
|
246
|
+
data: { invitationId },
|
|
247
|
+
}, deps.db);
|
|
248
|
+
await events.flush();
|
|
249
|
+
},
|
|
250
|
+
async acceptInvitation(input) {
|
|
251
|
+
const tokenHash = hashToken(input.token);
|
|
252
|
+
const events = outbox();
|
|
253
|
+
const accepted = await deps.db.transaction(async (tx) => {
|
|
254
|
+
const rows = await tx
|
|
255
|
+
.select()
|
|
256
|
+
.from(workspaceInvitations)
|
|
257
|
+
.where(eq(workspaceInvitations.tokenHash, tokenHash))
|
|
258
|
+
.limit(1);
|
|
259
|
+
const invitation = rows[0];
|
|
260
|
+
/**
|
|
261
|
+
* A single "invalid invitation" for missing, revoked, accepted and
|
|
262
|
+
* expired tokens: distinguishing them would tell a token-guessing
|
|
263
|
+
* attacker which guesses corresponded to real invitations.
|
|
264
|
+
*/
|
|
265
|
+
if (invitation === undefined ||
|
|
266
|
+
invitation.acceptedAt !== null ||
|
|
267
|
+
invitation.revokedAt !== null ||
|
|
268
|
+
invitation.expiresAt.getTime() <= Date.now()) {
|
|
269
|
+
throw new NotFoundError("Invitation is invalid or has expired.");
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Mark accepted with the same guard in the WHERE clause, so two
|
|
273
|
+
* concurrent redemptions cannot both succeed.
|
|
274
|
+
*/
|
|
275
|
+
const claimed = await tx
|
|
276
|
+
.update(workspaceInvitations)
|
|
277
|
+
.set({ acceptedAt: new Date(), updatedAt: new Date() })
|
|
278
|
+
.where(and(eq(workspaceInvitations.id, invitation.id), isNull(workspaceInvitations.acceptedAt), isNull(workspaceInvitations.revokedAt)))
|
|
279
|
+
.returning({ id: workspaceInvitations.id });
|
|
280
|
+
if (claimed.length === 0) {
|
|
281
|
+
throw new NotFoundError("Invitation is invalid or has expired.");
|
|
282
|
+
}
|
|
283
|
+
await tx
|
|
284
|
+
.insert(workspaceMembers)
|
|
285
|
+
.values({ workspaceId: invitation.workspaceId, userId: input.userId })
|
|
286
|
+
.onConflictDoNothing();
|
|
287
|
+
const roleId = invitation.roleId ??
|
|
288
|
+
(await tx
|
|
289
|
+
.select({ id: roles.id })
|
|
290
|
+
.from(roles)
|
|
291
|
+
.where(and(eq(roles.workspaceId, invitation.workspaceId), eq(roles.isDefault, true)))
|
|
292
|
+
.limit(1))[0]?.id;
|
|
293
|
+
if (roleId !== undefined) {
|
|
294
|
+
await tx
|
|
295
|
+
.insert(workspaceMemberRoles)
|
|
296
|
+
.values({
|
|
297
|
+
workspaceId: invitation.workspaceId,
|
|
298
|
+
userId: input.userId,
|
|
299
|
+
roleId,
|
|
300
|
+
})
|
|
301
|
+
.onConflictDoNothing();
|
|
302
|
+
}
|
|
303
|
+
await events.record({
|
|
304
|
+
type: "workspace.invitation.accepted",
|
|
305
|
+
workspaceId: invitation.workspaceId,
|
|
306
|
+
actorId: input.userId,
|
|
307
|
+
data: { invitationId: invitation.id, userId: input.userId },
|
|
308
|
+
}, tx);
|
|
309
|
+
await events.record({
|
|
310
|
+
type: "workspace.member.created",
|
|
311
|
+
workspaceId: invitation.workspaceId,
|
|
312
|
+
actorId: input.userId,
|
|
313
|
+
data: { userId: input.userId, roleIds: roleId !== undefined ? [roleId] : [] },
|
|
314
|
+
}, tx);
|
|
315
|
+
return unsafeWorkspaceContext(input.userId, invitation.workspaceId);
|
|
316
|
+
});
|
|
317
|
+
await events.flush();
|
|
318
|
+
return accepted;
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
return {
|
|
322
|
+
async create(input) {
|
|
323
|
+
const slug = input.slug !== undefined ? slugify(input.slug) : slugify(input.name);
|
|
324
|
+
const definitions = input.systemRoles ?? DEFAULT_SYSTEM_ROLES;
|
|
325
|
+
const events = outbox();
|
|
326
|
+
try {
|
|
327
|
+
const workspace = await deps.db.transaction(async (tx) => {
|
|
328
|
+
const [workspace] = await tx
|
|
329
|
+
.insert(workspaces)
|
|
330
|
+
.values({ name: input.name, slug })
|
|
331
|
+
.returning();
|
|
332
|
+
const created = await seedRoles(tx, workspace.id, definitions);
|
|
333
|
+
await tx
|
|
334
|
+
.insert(workspaceMembers)
|
|
335
|
+
.values({ workspaceId: workspace.id, userId: input.ownerUserId });
|
|
336
|
+
/**
|
|
337
|
+
* The creator gets `admin` if it exists, otherwise the default role.
|
|
338
|
+
* Without this a new workspace would have no one able to administer it.
|
|
339
|
+
*/
|
|
340
|
+
const ownerRole = created.find((role) => role.key === "admin") ??
|
|
341
|
+
created.find((role) => role.isDefault) ??
|
|
342
|
+
created[0];
|
|
343
|
+
if (ownerRole !== undefined) {
|
|
344
|
+
await tx.insert(workspaceMemberRoles).values({
|
|
345
|
+
workspaceId: workspace.id,
|
|
346
|
+
userId: input.ownerUserId,
|
|
347
|
+
roleId: ownerRole.id,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
await events.record({
|
|
351
|
+
type: "workspace.created",
|
|
352
|
+
workspaceId: workspace.id,
|
|
353
|
+
actorId: input.ownerUserId,
|
|
354
|
+
data: { workspaceId: workspace.id, slug },
|
|
355
|
+
}, tx);
|
|
356
|
+
await events.record({
|
|
357
|
+
type: "workspace.member.created",
|
|
358
|
+
workspaceId: workspace.id,
|
|
359
|
+
actorId: input.ownerUserId,
|
|
360
|
+
data: {
|
|
361
|
+
userId: input.ownerUserId,
|
|
362
|
+
roleIds: ownerRole !== undefined ? [ownerRole.id] : [],
|
|
363
|
+
},
|
|
364
|
+
}, tx);
|
|
365
|
+
return workspace;
|
|
366
|
+
});
|
|
367
|
+
await events.flush();
|
|
368
|
+
return workspace;
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
if (isUniqueViolation(error, "workspaces_slug_unique")) {
|
|
372
|
+
throw new ConflictError(`Workspace slug "${slug}" is already taken.`, { cause: error });
|
|
373
|
+
}
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
async get(context) {
|
|
378
|
+
const rows = await deps.db
|
|
379
|
+
.select()
|
|
380
|
+
.from(workspaces)
|
|
381
|
+
.where(eq(workspaces.id, context.workspaceId))
|
|
382
|
+
.limit(1);
|
|
383
|
+
const workspace = rows[0];
|
|
384
|
+
if (workspace === undefined) {
|
|
385
|
+
throw new NotFoundError("Workspace not found.");
|
|
386
|
+
}
|
|
387
|
+
return workspace;
|
|
388
|
+
},
|
|
389
|
+
async update(context, patch) {
|
|
390
|
+
const next = { updatedAt: new Date() };
|
|
391
|
+
if (patch.name !== undefined) {
|
|
392
|
+
next.name = patch.name;
|
|
393
|
+
}
|
|
394
|
+
if (patch.slug !== undefined) {
|
|
395
|
+
next.slug = slugify(patch.slug);
|
|
396
|
+
}
|
|
397
|
+
const events = outbox();
|
|
398
|
+
try {
|
|
399
|
+
const workspace = await deps.db.transaction(async (tx) => {
|
|
400
|
+
const [updated] = await tx
|
|
401
|
+
.update(workspaces)
|
|
402
|
+
.set(next)
|
|
403
|
+
.where(eq(workspaces.id, context.workspaceId))
|
|
404
|
+
.returning();
|
|
405
|
+
if (updated === undefined) {
|
|
406
|
+
throw new NotFoundError("Workspace not found.");
|
|
407
|
+
}
|
|
408
|
+
const changed = ["name", "slug"].filter((field) => patch[field] !== undefined);
|
|
409
|
+
await events.record({
|
|
410
|
+
type: "workspace.updated",
|
|
411
|
+
workspaceId: context.workspaceId,
|
|
412
|
+
actorId: context.userId,
|
|
413
|
+
data: { workspaceId: context.workspaceId, changed },
|
|
414
|
+
}, tx);
|
|
415
|
+
return updated;
|
|
416
|
+
});
|
|
417
|
+
await events.flush();
|
|
418
|
+
return workspace;
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
if (isUniqueViolation(error, "workspaces_slug_unique")) {
|
|
422
|
+
throw new ConflictError("Workspace slug is already taken.", { cause: error });
|
|
423
|
+
}
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
async delete(context) {
|
|
428
|
+
/**
|
|
429
|
+
* The event is recorded before the delete in the same transaction, because
|
|
430
|
+
* `events.workspace_id` cascades — writing it afterwards would insert a row
|
|
431
|
+
* referencing a workspace that no longer exists.
|
|
432
|
+
*/
|
|
433
|
+
const events = outbox();
|
|
434
|
+
await deps.db.transaction(async (tx) => {
|
|
435
|
+
const rows = await tx
|
|
436
|
+
.select({ slug: workspaces.slug })
|
|
437
|
+
.from(workspaces)
|
|
438
|
+
.where(eq(workspaces.id, context.workspaceId))
|
|
439
|
+
.limit(1);
|
|
440
|
+
const existing = rows[0];
|
|
441
|
+
if (existing === undefined) {
|
|
442
|
+
throw new NotFoundError("Workspace not found.");
|
|
443
|
+
}
|
|
444
|
+
await events.record({
|
|
445
|
+
type: "workspace.deleted",
|
|
446
|
+
// Deliberately not workspace-scoped: the cascade would remove it.
|
|
447
|
+
workspaceId: null,
|
|
448
|
+
actorId: context.userId,
|
|
449
|
+
data: { workspaceId: context.workspaceId, slug: existing.slug },
|
|
450
|
+
}, tx);
|
|
451
|
+
await tx.delete(workspaces).where(eq(workspaces.id, context.workspaceId));
|
|
452
|
+
});
|
|
453
|
+
await events.flush();
|
|
454
|
+
},
|
|
455
|
+
async listForUser(userId) {
|
|
456
|
+
return deps.db
|
|
457
|
+
.select({
|
|
458
|
+
id: workspaces.id,
|
|
459
|
+
name: workspaces.name,
|
|
460
|
+
slug: workspaces.slug,
|
|
461
|
+
createdAt: workspaces.createdAt,
|
|
462
|
+
updatedAt: workspaces.updatedAt,
|
|
463
|
+
})
|
|
464
|
+
.from(workspaces)
|
|
465
|
+
.innerJoin(workspaceMembers, eq(workspaceMembers.workspaceId, workspaces.id))
|
|
466
|
+
.where(eq(workspaceMembers.userId, userId));
|
|
467
|
+
},
|
|
468
|
+
async resolveContext({ userId, workspaceId }) {
|
|
469
|
+
const isMember = await findMembership(userId, workspaceId);
|
|
470
|
+
return isMember ? unsafeWorkspaceContext(userId, workspaceId) : undefined;
|
|
471
|
+
},
|
|
472
|
+
async requireContext({ userId, workspaceId }) {
|
|
473
|
+
const context = await this.resolveContext({ userId, workspaceId });
|
|
474
|
+
if (context === undefined) {
|
|
475
|
+
throw new NotFoundError("Workspace not found.");
|
|
476
|
+
}
|
|
477
|
+
return context;
|
|
478
|
+
},
|
|
479
|
+
async requireContextBySlug({ userId, slug }) {
|
|
480
|
+
const rows = await deps.db
|
|
481
|
+
.select({ workspaceId: workspaces.id })
|
|
482
|
+
.from(workspaces)
|
|
483
|
+
.innerJoin(workspaceMembers, and(eq(workspaceMembers.workspaceId, workspaces.id), eq(workspaceMembers.userId, userId)))
|
|
484
|
+
.where(eq(workspaces.slug, slug))
|
|
485
|
+
.limit(1);
|
|
486
|
+
const row = rows[0];
|
|
487
|
+
if (row === undefined) {
|
|
488
|
+
throw new NotFoundError("Workspace not found.");
|
|
489
|
+
}
|
|
490
|
+
return unsafeWorkspaceContext(userId, row.workspaceId);
|
|
491
|
+
},
|
|
492
|
+
members,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/workspaces/service.ts"],"names":[],"mappings":"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"}
|