insforge 1.2.10 → 1.3.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/.claude-plugin/marketplace.json +20 -20
- package/.dockerignore +60 -60
- package/.env.example +83 -77
- package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -36
- package/.github/ISSUE_TEMPLATE/config.yml +11 -11
- package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -26
- package/.github/PULL_REQUEST_TEMPLATE.md +7 -7
- package/.github/copilot-instructions.md +146 -146
- package/.github/workflows/build-image.yml +65 -65
- package/.github/workflows/ci-premerge-check.yml +23 -23
- package/.github/workflows/e2e.yml +63 -63
- package/.github/workflows/lint-and-format.yml +32 -32
- package/.prettierignore +64 -64
- package/CHANGELOG.md +44 -44
- package/CLAUDE_PLUGIN.md +104 -104
- package/CODE_OF_CONDUCT.md +128 -128
- package/CONTRIBUTING.md +125 -125
- package/Dockerfile +30 -30
- package/GITHUB_OAUTH_SETUP.md +49 -49
- package/GOOGLE_OAUTH_SETUP.md +148 -148
- package/LICENSE +201 -201
- package/README.md +182 -182
- package/assets/Dark.svg +23 -23
- package/auth/package.json +28 -28
- package/auth/src/lib/broadcastService.ts +117 -115
- package/auth/src/pages/SignInPage.tsx +60 -57
- package/auth/src/pages/SignUpPage.tsx +60 -57
- package/auth/tsconfig.json +32 -32
- package/auth/tsconfig.node.json +11 -11
- package/backend/package.json +78 -75
- package/backend/src/api/routes/ai/index.routes.ts +3 -3
- package/backend/src/api/routes/auth/index.routes.ts +667 -570
- package/backend/src/api/routes/auth/oauth.routes.ts +473 -448
- package/backend/src/api/routes/database/advance.routes.ts +37 -16
- package/backend/src/api/routes/database/index.routes.ts +78 -1
- package/backend/src/api/routes/database/records.routes.ts +10 -10
- package/backend/src/api/routes/database/tables.routes.ts +0 -14
- package/backend/src/api/routes/docs/index.routes.ts +75 -76
- package/backend/src/api/routes/email/index.routes.ts +35 -0
- package/backend/src/api/routes/functions/index.routes.ts +18 -12
- package/backend/src/api/routes/metadata/index.routes.ts +12 -0
- package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
- package/backend/src/api/routes/realtime/index.routes.ts +12 -0
- package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
- package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
- package/backend/src/api/routes/storage/index.routes.ts +18 -12
- package/backend/src/api/routes/usage/index.routes.ts +6 -4
- package/backend/src/infra/database/database.manager.ts +14 -1
- package/backend/src/infra/database/migrations/000_create-base-tables.sql +141 -141
- package/backend/src/infra/database/migrations/001_create-helper-functions.sql +40 -40
- package/backend/src/infra/database/migrations/002_rename-auth-tables.sql +29 -29
- package/backend/src/infra/database/migrations/003_create-users-table.sql +55 -55
- package/backend/src/infra/database/migrations/004_add-reload-postgrest-func.sql +23 -23
- package/backend/src/infra/database/migrations/005_enable-project-admin-modify-users.sql +29 -29
- package/backend/src/infra/database/migrations/006_modify-ai-usage-table.sql +24 -24
- package/backend/src/infra/database/migrations/007_drop-metadata-table.sql +1 -1
- package/backend/src/infra/database/migrations/008_add-system-tables.sql +76 -76
- package/backend/src/infra/database/migrations/009_add-function-secrets.sql +23 -23
- package/backend/src/infra/database/migrations/010_modify-ai-config-modalities.sql +93 -93
- package/backend/src/infra/database/migrations/011_refactor-secrets-table.sql +15 -15
- package/backend/src/infra/database/migrations/012_add-storage-uploaded-by.sql +7 -7
- package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -44
- package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +7 -7
- package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +59 -59
- package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -24
- package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
- package/backend/src/infra/realtime/realtime.manager.ts +246 -0
- package/backend/src/infra/realtime/webhook-sender.ts +82 -0
- package/backend/src/infra/security/token.manager.ts +219 -125
- package/backend/src/infra/socket/socket.manager.ts +198 -64
- package/backend/src/providers/ai/openrouter.provider.ts +12 -9
- package/backend/src/providers/email/base.provider.ts +4 -7
- package/backend/src/providers/email/cloud.provider.ts +84 -0
- package/backend/src/providers/oauth/apple.provider.ts +266 -0
- package/backend/src/providers/oauth/index.ts +1 -0
- package/backend/src/server.ts +317 -284
- package/backend/src/services/ai/ai-model.service.ts +5 -5
- package/backend/src/services/ai/chat-completion.service.ts +4 -4
- package/backend/src/services/ai/image-generation.service.ts +3 -3
- package/backend/src/services/auth/auth.service.ts +14 -0
- package/backend/src/services/database/database-table.service.ts +0 -9
- package/backend/src/services/database/database.service.ts +127 -0
- package/backend/src/services/email/email.service.ts +5 -7
- package/backend/src/services/realtime/index.ts +3 -0
- package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
- package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
- package/backend/src/services/realtime/realtime-message.service.ts +260 -0
- package/backend/src/types/auth.ts +11 -0
- package/backend/src/types/realtime.ts +18 -0
- package/backend/src/types/socket.ts +7 -31
- package/backend/src/utils/cookies.ts +35 -0
- package/backend/src/utils/s3-config-loader.ts +64 -0
- package/backend/src/utils/seed.ts +301 -298
- package/backend/src/utils/sql-parser.ts +90 -0
- package/backend/tests/README.md +133 -133
- package/backend/tests/cleanup-all-test-data.sh +230 -230
- package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
- package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
- package/backend/tests/local/test-ai-config.sh +129 -129
- package/backend/tests/local/test-ai-usage.sh +80 -80
- package/backend/tests/local/test-auth-router.sh +143 -143
- package/backend/tests/local/test-database-router.sh +222 -222
- package/backend/tests/local/test-e2e.sh +240 -240
- package/backend/tests/local/test-fk-errors.sh +96 -96
- package/backend/tests/local/test-functions.sh +123 -123
- package/backend/tests/local/test-id-field.sh +200 -200
- package/backend/tests/local/test-logs.sh +132 -132
- package/backend/tests/local/test-public-bucket.sh +264 -264
- package/backend/tests/local/test-secrets.sh +249 -249
- package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
- package/backend/tests/local/test-traditional-rest.sh +208 -208
- package/backend/tests/manual/README.md +50 -50
- package/backend/tests/manual/create-large-table-simple.sql +10 -10
- package/backend/tests/manual/seed-large-table.sql +100 -100
- package/backend/tests/manual/setup-large-table-extras.sql +33 -33
- package/backend/tests/manual/test-bulk-upsert.sh +409 -409
- package/backend/tests/manual/test-database-advance.sh +296 -296
- package/backend/tests/manual/test-postgrest-stability.sh +191 -191
- package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
- package/backend/tests/manual/test-rawsql-modes.sh +244 -244
- package/backend/tests/manual/test-universal-storage.sh +263 -263
- package/backend/tests/manual/test-users.sql +17 -17
- package/backend/tests/run-all-tests.sh +139 -139
- package/backend/tests/setup.ts +0 -0
- package/backend/tests/test-config.sh +338 -338
- package/backend/tests/unit/analyze-query.test.ts +697 -0
- package/backend/tsconfig.json +22 -22
- package/claude-plugin/.claude-plugin/plugin.json +24 -24
- package/claude-plugin/README.md +133 -133
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -270
- package/docker-compose.prod.yml +204 -200
- package/docker-compose.yml +232 -228
- package/docker-init/db/db-init.sql +97 -97
- package/docker-init/db/jwt.sql +5 -5
- package/docker-init/db/postgresql.conf +16 -16
- package/docker-init/logs/vector.yml +236 -236
- package/docs/README.md +44 -44
- package/docs/agent-docs/real-time.md +269 -0
- package/docs/changelog.mdx +119 -67
- package/docs/core-concepts/ai/architecture.mdx +372 -372
- package/docs/core-concepts/ai/sdk.mdx +213 -213
- package/docs/core-concepts/authentication/architecture.mdx +278 -278
- package/docs/core-concepts/authentication/sdk.mdx +414 -414
- package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -529
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -221
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -184
- package/docs/core-concepts/authentication/ui-components/react.mdx +129 -129
- package/docs/core-concepts/database/architecture.mdx +255 -255
- package/docs/core-concepts/database/sdk.mdx +382 -382
- package/docs/core-concepts/email/architecture.mdx +101 -0
- package/docs/core-concepts/email/sdk.mdx +53 -0
- package/docs/core-concepts/functions/architecture.mdx +105 -105
- package/docs/core-concepts/functions/sdk.mdx +184 -184
- package/docs/core-concepts/realtime/architecture.mdx +446 -0
- package/docs/core-concepts/realtime/sdk.mdx +409 -0
- package/docs/core-concepts/storage/architecture.mdx +243 -243
- package/docs/core-concepts/storage/sdk.mdx +253 -253
- package/docs/deployment/README.md +94 -94
- package/docs/deployment/deploy-to-aws-ec2.md +564 -564
- package/docs/deployment/deploy-to-azure-virtual-machines.md +312 -312
- package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -613
- package/docs/deployment/deploy-to-render.md +441 -441
- package/docs/deprecated/insforge-auth-api.md +214 -214
- package/docs/deprecated/insforge-auth-sdk.md +99 -99
- package/docs/deprecated/insforge-db-api.md +358 -358
- package/docs/deprecated/insforge-db-sdk.md +139 -139
- package/docs/deprecated/insforge-debug-sdk.md +156 -156
- package/docs/deprecated/insforge-debug.md +64 -64
- package/docs/deprecated/insforge-instructions.md +123 -123
- package/docs/deprecated/insforge-project.md +117 -117
- package/docs/deprecated/insforge-storage-api.md +278 -278
- package/docs/deprecated/insforge-storage-sdk.md +158 -158
- package/docs/docs.json +232 -210
- package/docs/examples/framework-guides/nextjs.mdx +131 -131
- package/docs/examples/framework-guides/nuxt.mdx +165 -165
- package/docs/examples/framework-guides/react.mdx +165 -165
- package/docs/examples/framework-guides/svelte.mdx +153 -153
- package/docs/examples/framework-guides/vue.mdx +159 -159
- package/docs/examples/overview.mdx +67 -67
- package/docs/favicon.svg +19 -19
- package/docs/images/changelog/dec-2025/ai-integration.png +0 -0
- package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
- package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
- package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
- package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
- package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
- package/docs/images/icons/ai.svg +4 -4
- package/docs/images/logos/nextjs.svg +4 -4
- package/docs/images/logos/nuxt.svg +4 -4
- package/docs/images/logos/react.svg +5 -5
- package/docs/images/logos/svelte.svg +4 -4
- package/docs/images/logos/vue.svg +5 -5
- package/docs/insforge-instructions-sdk.md +89 -88
- package/docs/introduction.mdx +45 -45
- package/docs/logo/dark.svg +22 -22
- package/docs/logo/light.svg +20 -20
- package/docs/partnership.mdx +651 -646
- package/docs/quickstart.mdx +82 -82
- package/docs/showcase.mdx +52 -52
- package/docs/snippets/sdk-installation.mdx +21 -21
- package/docs/snippets/service-icons.mdx +27 -27
- package/examples/oauth/frontend-oauth-example.html +250 -250
- package/examples/response-examples.md +443 -443
- package/frontend/components.json +17 -17
- package/frontend/package.json +69 -69
- package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
- package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
- package/frontend/src/assets/icons/checked.svg +3 -3
- package/frontend/src/assets/icons/connected.svg +3 -3
- package/frontend/src/assets/icons/error.svg +3 -3
- package/frontend/src/assets/icons/loader.svg +9 -9
- package/frontend/src/assets/icons/pencil.svg +4 -4
- package/frontend/src/assets/icons/refresh.svg +4 -4
- package/frontend/src/assets/icons/step_active.svg +3 -3
- package/frontend/src/assets/icons/step_inactive.svg +11 -11
- package/frontend/src/assets/icons/warning.svg +3 -3
- package/frontend/src/assets/logos/apple.svg +3 -3
- package/frontend/src/assets/logos/claude_code.svg +3 -3
- package/frontend/src/assets/logos/cline.svg +6 -6
- package/frontend/src/assets/logos/cursor.svg +20 -20
- package/frontend/src/assets/logos/discord.svg +8 -8
- package/frontend/src/assets/logos/facebook.svg +3 -3
- package/frontend/src/assets/logos/gemini.svg +19 -19
- package/frontend/src/assets/logos/github.svg +5 -5
- package/frontend/src/assets/logos/google.svg +13 -13
- package/frontend/src/assets/logos/grok.svg +10 -10
- package/frontend/src/assets/logos/insforge_dark.svg +15 -15
- package/frontend/src/assets/logos/insforge_light.svg +15 -15
- package/frontend/src/assets/logos/instagram.svg +1 -1
- package/frontend/src/assets/logos/linkedin.svg +3 -3
- package/frontend/src/assets/logos/openai.svg +10 -10
- package/frontend/src/assets/logos/roo_code.svg +9 -9
- package/frontend/src/assets/logos/spotify.svg +16 -16
- package/frontend/src/assets/logos/tiktok.svg +5 -5
- package/frontend/src/assets/logos/trae.svg +3 -3
- package/frontend/src/assets/logos/windsurf.svg +10 -10
- package/frontend/src/assets/logos/x.svg +3 -3
- package/frontend/src/components/layout/AppHeader.tsx +9 -10
- package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +1 -0
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +6 -0
- package/frontend/src/features/auth/helpers.tsx +8 -0
- package/frontend/src/features/auth/{page → pages}/UsersPage.tsx +0 -28
- package/frontend/src/features/database/components/SQLModal.tsx +75 -0
- package/frontend/src/features/database/components/TableForm.tsx +0 -4
- package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
- package/frontend/src/features/database/hooks/useTables.ts +32 -28
- package/frontend/src/features/database/index.ts +1 -0
- package/frontend/src/features/database/{page → pages}/FunctionsPage.tsx +29 -37
- package/frontend/src/features/database/{page → pages}/IndexesPage.tsx +35 -47
- package/frontend/src/features/database/{page → pages}/PoliciesPage.tsx +43 -54
- package/frontend/src/features/database/{page → pages}/TablesPage.tsx +0 -42
- package/frontend/src/features/database/{page → pages}/TriggersPage.tsx +35 -47
- package/frontend/src/features/database/services/advance.service.ts +0 -26
- package/frontend/src/features/database/services/database.service.ts +55 -0
- package/frontend/src/features/database/services/table.service.ts +0 -6
- package/frontend/src/features/functions/{page → pages}/FunctionsPage.tsx +21 -44
- package/frontend/src/features/functions/{page → pages}/SecretsPage.tsx +11 -9
- package/frontend/src/features/logs/hooks/useMcpUsage.ts +13 -66
- package/frontend/src/features/realtime/components/ChannelRow.tsx +83 -0
- package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
- package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
- package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
- package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
- package/frontend/src/features/realtime/index.ts +11 -0
- package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
- package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
- package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
- package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
- package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +1 -29
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +3 -3
- package/frontend/src/features/visualizer/{page → pages}/VisualizerPage.tsx +1 -35
- package/frontend/src/lib/contexts/SocketContext.tsx +119 -75
- package/frontend/src/lib/routing/AppRoutes.tsx +35 -20
- package/frontend/src/lib/utils/cloudMessaging.ts +1 -1
- package/frontend/src/lib/utils/menuItems.ts +24 -0
- package/frontend/src/lib/utils/utils.ts +14 -1
- package/frontend/tsconfig.json +25 -25
- package/frontend/tsconfig.node.json +9 -9
- package/functions/deno.json +24 -24
- package/functions/server.ts +315 -315
- package/i18n/README.ar.md +130 -130
- package/i18n/README.de.md +130 -130
- package/i18n/README.es.md +154 -154
- package/i18n/README.fr.md +134 -134
- package/i18n/README.hi.md +129 -129
- package/i18n/README.ja.md +174 -174
- package/i18n/README.ko.md +136 -136
- package/i18n/README.pt-BR.md +131 -131
- package/i18n/README.ru.md +129 -129
- package/i18n/README.zh-CN.md +133 -133
- package/openapi/ai.yaml +715 -715
- package/openapi/auth.yaml +1244 -1244
- package/openapi/email.yaml +158 -0
- package/openapi/functions.yaml +475 -475
- package/openapi/health.yaml +29 -29
- package/openapi/logs.yaml +223 -223
- package/openapi/metadata.yaml +177 -177
- package/openapi/realtime.yaml +699 -0
- package/openapi/records.yaml +381 -381
- package/openapi/secrets.yaml +370 -370
- package/openapi/storage.yaml +875 -875
- package/openapi/tables.yaml +463 -463
- package/package.json +97 -97
- package/shared-schemas/package.json +31 -31
- package/shared-schemas/src/ai.schema.ts +63 -59
- package/shared-schemas/src/auth-api.schema.ts +352 -339
- package/shared-schemas/src/auth.schema.ts +1 -1
- package/shared-schemas/src/database-api.schema.ts +32 -1
- package/shared-schemas/src/database.schema.ts +39 -0
- package/shared-schemas/src/docs.schema.ts +26 -0
- package/shared-schemas/src/email-api.schema.ts +30 -0
- package/shared-schemas/src/index.ts +4 -0
- package/shared-schemas/src/metadata.schema.ts +9 -0
- package/shared-schemas/src/realtime-api.schema.ts +111 -0
- package/shared-schemas/src/realtime.schema.ts +143 -0
- package/shared-schemas/tsconfig.json +21 -21
- package/tsconfig.json +7 -7
- package/zeabur/README.md +13 -13
- package/zeabur/template.yml +1032 -1032
- package/.cursor/rules/cursor-rules.mdc +0 -94
- package/frontend/src/features/database/hooks/useFullMetadata.ts +0 -18
- package/test-gemini.sh +0 -35
- package/test-usage-admin.sh +0 -57
- package/test-usage.sh +0 -50
- /package/frontend/src/features/ai/{page → pages}/AIPage.tsx +0 -0
- /package/frontend/src/features/auth/{page → pages}/AuthMethodsPage.tsx +0 -0
- /package/frontend/src/features/auth/{page → pages}/ConfigurationPage.tsx +0 -0
- /package/frontend/src/features/dashboard/{page → pages}/DashboardPage.tsx +0 -0
- /package/frontend/src/features/database/{page → pages}/SQLEditorPage.tsx +0 -0
- /package/frontend/src/features/database/{page → pages}/TemplatesPage.tsx +0 -0
- /package/frontend/src/features/login/{page → pages}/CloudLoginPage.tsx +0 -0
- /package/frontend/src/features/login/{page → pages}/LoginPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/LogsPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/MCPLogsPage.tsx +0 -0
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
-- Migration: 013 - Create auth schema and copy helper functions
|
|
2
|
-
-- Creates auth schema if it doesn't exist and copies JWT helper functions
|
|
3
|
-
|
|
4
|
-
-- Create auth schema if not exists
|
|
5
|
-
CREATE SCHEMA IF NOT EXISTS auth;
|
|
6
|
-
|
|
7
|
-
-- Function to get current user ID from JWT (in auth schema)
|
|
8
|
-
CREATE OR REPLACE FUNCTION auth.uid()
|
|
9
|
-
RETURNS uuid
|
|
10
|
-
LANGUAGE sql STABLE
|
|
11
|
-
AS $$
|
|
12
|
-
SELECT
|
|
13
|
-
nullif(
|
|
14
|
-
coalesce(
|
|
15
|
-
current_setting('request.jwt.claim.sub', true),
|
|
16
|
-
(current_setting('request.jwt.claims', true)::jsonb ->> 'sub')
|
|
17
|
-
),
|
|
18
|
-
''
|
|
19
|
-
)::uuid
|
|
20
|
-
$$;
|
|
21
|
-
|
|
22
|
-
-- Function to get current user role from JWT (in auth schema)
|
|
23
|
-
CREATE OR REPLACE FUNCTION auth.role()
|
|
24
|
-
RETURNS text
|
|
25
|
-
LANGUAGE sql STABLE
|
|
26
|
-
AS $$
|
|
27
|
-
SELECT
|
|
28
|
-
coalesce(
|
|
29
|
-
current_setting('request.jwt.claim.role', true),
|
|
30
|
-
(current_setting('request.jwt.claims', true)::jsonb ->> 'role')
|
|
31
|
-
)::text
|
|
32
|
-
$$;
|
|
33
|
-
|
|
34
|
-
-- Function to get current user email from JWT (in auth schema)
|
|
35
|
-
CREATE OR REPLACE FUNCTION auth.email()
|
|
36
|
-
RETURNS text
|
|
37
|
-
LANGUAGE sql STABLE
|
|
38
|
-
AS $$
|
|
39
|
-
SELECT
|
|
40
|
-
coalesce(
|
|
41
|
-
current_setting('request.jwt.claim.email', true),
|
|
42
|
-
(current_setting('request.jwt.claims', true)::jsonb ->> 'email')
|
|
43
|
-
)::text
|
|
44
|
-
$$;
|
|
1
|
+
-- Migration: 013 - Create auth schema and copy helper functions
|
|
2
|
+
-- Creates auth schema if it doesn't exist and copies JWT helper functions
|
|
3
|
+
|
|
4
|
+
-- Create auth schema if not exists
|
|
5
|
+
CREATE SCHEMA IF NOT EXISTS auth;
|
|
6
|
+
|
|
7
|
+
-- Function to get current user ID from JWT (in auth schema)
|
|
8
|
+
CREATE OR REPLACE FUNCTION auth.uid()
|
|
9
|
+
RETURNS uuid
|
|
10
|
+
LANGUAGE sql STABLE
|
|
11
|
+
AS $$
|
|
12
|
+
SELECT
|
|
13
|
+
nullif(
|
|
14
|
+
coalesce(
|
|
15
|
+
current_setting('request.jwt.claim.sub', true),
|
|
16
|
+
(current_setting('request.jwt.claims', true)::jsonb ->> 'sub')
|
|
17
|
+
),
|
|
18
|
+
''
|
|
19
|
+
)::uuid
|
|
20
|
+
$$;
|
|
21
|
+
|
|
22
|
+
-- Function to get current user role from JWT (in auth schema)
|
|
23
|
+
CREATE OR REPLACE FUNCTION auth.role()
|
|
24
|
+
RETURNS text
|
|
25
|
+
LANGUAGE sql STABLE
|
|
26
|
+
AS $$
|
|
27
|
+
SELECT
|
|
28
|
+
coalesce(
|
|
29
|
+
current_setting('request.jwt.claim.role', true),
|
|
30
|
+
(current_setting('request.jwt.claims', true)::jsonb ->> 'role')
|
|
31
|
+
)::text
|
|
32
|
+
$$;
|
|
33
|
+
|
|
34
|
+
-- Function to get current user email from JWT (in auth schema)
|
|
35
|
+
CREATE OR REPLACE FUNCTION auth.email()
|
|
36
|
+
RETURNS text
|
|
37
|
+
LANGUAGE sql STABLE
|
|
38
|
+
AS $$
|
|
39
|
+
SELECT
|
|
40
|
+
coalesce(
|
|
41
|
+
current_setting('request.jwt.claim.email', true),
|
|
42
|
+
(current_setting('request.jwt.claims', true)::jsonb ->> 'email')
|
|
43
|
+
)::text
|
|
44
|
+
$$;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
-- Migration: 014 - Add updated_at trigger to users table
|
|
2
|
-
-- Adds the updated_at trigger to the users table for automatic timestamp management
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
|
|
6
|
-
CREATE TRIGGER update_users_updated_at
|
|
7
|
-
BEFORE UPDATE ON users
|
|
1
|
+
-- Migration: 014 - Add updated_at trigger to users table
|
|
2
|
+
-- Adds the updated_at trigger to the users table for automatic timestamp management
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
DROP TRIGGER IF EXISTS update_users_updated_at ON users;
|
|
6
|
+
CREATE TRIGGER update_users_updated_at
|
|
7
|
+
BEFORE UPDATE ON users
|
|
8
8
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
-- Migration: 015 - Create email OTP verification table and email auth configs
|
|
2
|
-
-- This migration creates:
|
|
3
|
-
-- 1. _email_otps: Stores one-time tokens for email verification purposes
|
|
4
|
-
-- - Supports both short numeric codes (6 digits) for manual entry
|
|
5
|
-
-- - Supports long cryptographic tokens (64 chars) for magic links
|
|
6
|
-
-- - Uses dual hashing strategy:
|
|
7
|
-
-- * NUMERIC_CODE (6 digits): Bcrypt hash (slow, defense against brute force)
|
|
8
|
-
-- * LINK_TOKEN (64 hex chars): SHA-256 hash (fast, enables direct O(1) lookup)
|
|
9
|
-
-- 2. _auth_configs: Stores email authentication configuration (single-row table)
|
|
10
|
-
|
|
11
|
-
-- 1. Create email OTP verification table
|
|
12
|
-
CREATE TABLE IF NOT EXISTS _email_otps (
|
|
13
|
-
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
14
|
-
email TEXT NOT NULL,
|
|
15
|
-
purpose TEXT NOT NULL,
|
|
16
|
-
otp_hash TEXT NOT NULL, -- Hash of OTP: bcrypt for NUMERIC_CODE, SHA-256 for LINK_TOKEN
|
|
17
|
-
expires_at TIMESTAMPTZ NOT NULL,
|
|
18
|
-
consumed_at TIMESTAMPTZ,
|
|
19
|
-
attempts_count INTEGER DEFAULT 0 NOT NULL,
|
|
20
|
-
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
21
|
-
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
22
|
-
UNIQUE (email, purpose) -- Only one active token per email/purpose combination
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
-- Create indexes for better query performance
|
|
26
|
-
CREATE INDEX IF NOT EXISTS idx_email_otps_email_purpose ON _email_otps(email, purpose);
|
|
27
|
-
CREATE INDEX IF NOT EXISTS idx_email_otps_expires_at ON _email_otps(expires_at);
|
|
28
|
-
CREATE INDEX IF NOT EXISTS idx_email_otps_otp_hash ON _email_otps(otp_hash); -- For direct LINK_TOKEN lookup
|
|
29
|
-
|
|
30
|
-
-- Add trigger for updated_at
|
|
31
|
-
DROP TRIGGER IF EXISTS update__email_otps_updated_at ON _email_otps;
|
|
32
|
-
CREATE TRIGGER update__email_otps_updated_at
|
|
33
|
-
BEFORE UPDATE ON _email_otps
|
|
34
|
-
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
35
|
-
|
|
36
|
-
-- 2. Create email authentication configuration table (single-row design)
|
|
37
|
-
-- This table stores global email authentication settings for the project
|
|
38
|
-
CREATE TABLE IF NOT EXISTS _auth_configs (
|
|
39
|
-
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
40
|
-
require_email_verification BOOLEAN DEFAULT FALSE NOT NULL,
|
|
41
|
-
password_min_length INTEGER DEFAULT 6 NOT NULL CHECK (password_min_length >= 4 AND password_min_length <= 128),
|
|
42
|
-
require_number BOOLEAN DEFAULT FALSE NOT NULL,
|
|
43
|
-
require_lowercase BOOLEAN DEFAULT FALSE NOT NULL,
|
|
44
|
-
require_uppercase BOOLEAN DEFAULT FALSE NOT NULL,
|
|
45
|
-
require_special_char BOOLEAN DEFAULT FALSE NOT NULL,
|
|
46
|
-
verify_email_redirect_to TEXT, -- Custom URL to redirect after successful email verification (defaults to no redirect if NULL)
|
|
47
|
-
reset_password_redirect_to TEXT, -- Custom URL to redirect after successful password reset (defaults to no redirect if NULL)
|
|
48
|
-
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
49
|
-
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
-- Ensure only one row exists (singleton pattern)
|
|
53
|
-
-- This constraint prevents multiple configuration rows
|
|
54
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_auth_configs_singleton ON _auth_configs ((1));
|
|
55
|
-
|
|
56
|
-
-- Add trigger for updated_at
|
|
57
|
-
DROP TRIGGER IF EXISTS update__auth_configs_updated_at ON _auth_configs;
|
|
58
|
-
CREATE TRIGGER update__auth_configs_updated_at
|
|
59
|
-
BEFORE UPDATE ON _auth_configs
|
|
1
|
+
-- Migration: 015 - Create email OTP verification table and email auth configs
|
|
2
|
+
-- This migration creates:
|
|
3
|
+
-- 1. _email_otps: Stores one-time tokens for email verification purposes
|
|
4
|
+
-- - Supports both short numeric codes (6 digits) for manual entry
|
|
5
|
+
-- - Supports long cryptographic tokens (64 chars) for magic links
|
|
6
|
+
-- - Uses dual hashing strategy:
|
|
7
|
+
-- * NUMERIC_CODE (6 digits): Bcrypt hash (slow, defense against brute force)
|
|
8
|
+
-- * LINK_TOKEN (64 hex chars): SHA-256 hash (fast, enables direct O(1) lookup)
|
|
9
|
+
-- 2. _auth_configs: Stores email authentication configuration (single-row table)
|
|
10
|
+
|
|
11
|
+
-- 1. Create email OTP verification table
|
|
12
|
+
CREATE TABLE IF NOT EXISTS _email_otps (
|
|
13
|
+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
14
|
+
email TEXT NOT NULL,
|
|
15
|
+
purpose TEXT NOT NULL,
|
|
16
|
+
otp_hash TEXT NOT NULL, -- Hash of OTP: bcrypt for NUMERIC_CODE, SHA-256 for LINK_TOKEN
|
|
17
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
18
|
+
consumed_at TIMESTAMPTZ,
|
|
19
|
+
attempts_count INTEGER DEFAULT 0 NOT NULL,
|
|
20
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
21
|
+
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
22
|
+
UNIQUE (email, purpose) -- Only one active token per email/purpose combination
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
-- Create indexes for better query performance
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_email_otps_email_purpose ON _email_otps(email, purpose);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_email_otps_expires_at ON _email_otps(expires_at);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_email_otps_otp_hash ON _email_otps(otp_hash); -- For direct LINK_TOKEN lookup
|
|
29
|
+
|
|
30
|
+
-- Add trigger for updated_at
|
|
31
|
+
DROP TRIGGER IF EXISTS update__email_otps_updated_at ON _email_otps;
|
|
32
|
+
CREATE TRIGGER update__email_otps_updated_at
|
|
33
|
+
BEFORE UPDATE ON _email_otps
|
|
34
|
+
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
35
|
+
|
|
36
|
+
-- 2. Create email authentication configuration table (single-row design)
|
|
37
|
+
-- This table stores global email authentication settings for the project
|
|
38
|
+
CREATE TABLE IF NOT EXISTS _auth_configs (
|
|
39
|
+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
40
|
+
require_email_verification BOOLEAN DEFAULT FALSE NOT NULL,
|
|
41
|
+
password_min_length INTEGER DEFAULT 6 NOT NULL CHECK (password_min_length >= 4 AND password_min_length <= 128),
|
|
42
|
+
require_number BOOLEAN DEFAULT FALSE NOT NULL,
|
|
43
|
+
require_lowercase BOOLEAN DEFAULT FALSE NOT NULL,
|
|
44
|
+
require_uppercase BOOLEAN DEFAULT FALSE NOT NULL,
|
|
45
|
+
require_special_char BOOLEAN DEFAULT FALSE NOT NULL,
|
|
46
|
+
verify_email_redirect_to TEXT, -- Custom URL to redirect after successful email verification (defaults to no redirect if NULL)
|
|
47
|
+
reset_password_redirect_to TEXT, -- Custom URL to redirect after successful password reset (defaults to no redirect if NULL)
|
|
48
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
49
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
-- Ensure only one row exists (singleton pattern)
|
|
53
|
+
-- This constraint prevents multiple configuration rows
|
|
54
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_auth_configs_singleton ON _auth_configs ((1));
|
|
55
|
+
|
|
56
|
+
-- Add trigger for updated_at
|
|
57
|
+
DROP TRIGGER IF EXISTS update__auth_configs_updated_at ON _auth_configs;
|
|
58
|
+
CREATE TRIGGER update__auth_configs_updated_at
|
|
59
|
+
BEFORE UPDATE ON _auth_configs
|
|
60
60
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
-- Migration 016: Update _email_otps and _auth_configs tables
|
|
2
|
-
--
|
|
3
|
-
-- Changes:
|
|
4
|
-
-- 1. _email_otps table:
|
|
5
|
-
-- - Remove attempts_count column (brute force protection moved to API rate limiter)
|
|
6
|
-
-- 2. _auth_configs table:
|
|
7
|
-
-- - Remove verify_email_redirect_to and reset_password_redirect_to columns
|
|
8
|
-
-- - Add verify_email_method and reset_password_method columns (code or link)
|
|
9
|
-
-- - Add sign_in_redirect_to column
|
|
10
|
-
|
|
11
|
-
-- Update _email_otps: Remove attempts_count column
|
|
12
|
-
ALTER TABLE _email_otps DROP COLUMN IF EXISTS attempts_count;
|
|
13
|
-
|
|
14
|
-
-- Update _auth_configs: Remove old redirect columns
|
|
15
|
-
ALTER TABLE _auth_configs
|
|
16
|
-
DROP COLUMN IF EXISTS verify_email_redirect_to,
|
|
17
|
-
DROP COLUMN IF EXISTS reset_password_redirect_to;
|
|
18
|
-
|
|
19
|
-
-- Add new columns to _auth_configs
|
|
20
|
-
-- Note: DEFAULT 'code' NOT NULL ensures existing rows automatically get 'code' value
|
|
21
|
-
ALTER TABLE _auth_configs
|
|
22
|
-
ADD COLUMN IF NOT EXISTS verify_email_method TEXT DEFAULT 'code' NOT NULL CHECK (verify_email_method IN ('code', 'link')),
|
|
23
|
-
ADD COLUMN IF NOT EXISTS reset_password_method TEXT DEFAULT 'code' NOT NULL CHECK (reset_password_method IN ('code', 'link')),
|
|
24
|
-
ADD COLUMN IF NOT EXISTS sign_in_redirect_to TEXT;
|
|
1
|
+
-- Migration 016: Update _email_otps and _auth_configs tables
|
|
2
|
+
--
|
|
3
|
+
-- Changes:
|
|
4
|
+
-- 1. _email_otps table:
|
|
5
|
+
-- - Remove attempts_count column (brute force protection moved to API rate limiter)
|
|
6
|
+
-- 2. _auth_configs table:
|
|
7
|
+
-- - Remove verify_email_redirect_to and reset_password_redirect_to columns
|
|
8
|
+
-- - Add verify_email_method and reset_password_method columns (code or link)
|
|
9
|
+
-- - Add sign_in_redirect_to column
|
|
10
|
+
|
|
11
|
+
-- Update _email_otps: Remove attempts_count column
|
|
12
|
+
ALTER TABLE _email_otps DROP COLUMN IF EXISTS attempts_count;
|
|
13
|
+
|
|
14
|
+
-- Update _auth_configs: Remove old redirect columns
|
|
15
|
+
ALTER TABLE _auth_configs
|
|
16
|
+
DROP COLUMN IF EXISTS verify_email_redirect_to,
|
|
17
|
+
DROP COLUMN IF EXISTS reset_password_redirect_to;
|
|
18
|
+
|
|
19
|
+
-- Add new columns to _auth_configs
|
|
20
|
+
-- Note: DEFAULT 'code' NOT NULL ensures existing rows automatically get 'code' value
|
|
21
|
+
ALTER TABLE _auth_configs
|
|
22
|
+
ADD COLUMN IF NOT EXISTS verify_email_method TEXT DEFAULT 'code' NOT NULL CHECK (verify_email_method IN ('code', 'link')),
|
|
23
|
+
ADD COLUMN IF NOT EXISTS reset_password_method TEXT DEFAULT 'code' NOT NULL CHECK (reset_password_method IN ('code', 'link')),
|
|
24
|
+
ADD COLUMN IF NOT EXISTS sign_in_redirect_to TEXT;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
-- Migration 017: Create Realtime Schema
|
|
2
|
+
--
|
|
3
|
+
-- Creates the realtime schema with:
|
|
4
|
+
-- 1. channels table - Channel definitions with webhook configuration
|
|
5
|
+
-- 2. messages table - All realtime messages with delivery statistics
|
|
6
|
+
-- 3. publish() function - Called by developer triggers to publish events
|
|
7
|
+
--
|
|
8
|
+
-- Permission Model (Supabase pattern):
|
|
9
|
+
-- - SELECT on channels = 'subscribe' permission (subscribe to channel)
|
|
10
|
+
-- - INSERT on messages = 'publish' permission (publish to channel)
|
|
11
|
+
-- Developers define RLS policies on channels/messages table to control access.
|
|
12
|
+
|
|
13
|
+
-- ============================================================================
|
|
14
|
+
-- CREATE SCHEMA
|
|
15
|
+
-- ============================================================================
|
|
16
|
+
|
|
17
|
+
CREATE SCHEMA IF NOT EXISTS realtime;
|
|
18
|
+
|
|
19
|
+
-- ============================================================================
|
|
20
|
+
-- CHANNELS TABLE
|
|
21
|
+
-- ============================================================================
|
|
22
|
+
-- Stores channel definitions with delivery configuration.
|
|
23
|
+
-- RLS policies control subscribe permissions.
|
|
24
|
+
-- - SELECT policy = 'subscribe' permission (can subscribe to channel)
|
|
25
|
+
-- Channel names use : as separator and % for wildcards (LIKE pattern).
|
|
26
|
+
-- Examples: "orders", "order:%", "chat:%:messages"
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS realtime.channels (
|
|
29
|
+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
30
|
+
|
|
31
|
+
-- Channel name pattern (e.g., "orders", "order:%", "chat:%:messages")
|
|
32
|
+
-- Convention: use : as separator, % for wildcards (LIKE pattern)
|
|
33
|
+
pattern TEXT UNIQUE NOT NULL,
|
|
34
|
+
|
|
35
|
+
-- Human-readable description
|
|
36
|
+
description TEXT,
|
|
37
|
+
|
|
38
|
+
-- Webhook URLs to POST events to (NULL or empty array = no webhooks)
|
|
39
|
+
webhook_urls TEXT[],
|
|
40
|
+
|
|
41
|
+
-- Whether this channel is active
|
|
42
|
+
enabled BOOLEAN DEFAULT TRUE NOT NULL,
|
|
43
|
+
|
|
44
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
45
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
-- ============================================================================
|
|
49
|
+
-- MESSAGES TABLE
|
|
50
|
+
-- ============================================================================
|
|
51
|
+
-- Stores all realtime messages published through the system.
|
|
52
|
+
-- RLS policies on this table control publish permissions:
|
|
53
|
+
-- - INSERT policy = 'publish' permission (can publish to channel)
|
|
54
|
+
|
|
55
|
+
CREATE TABLE IF NOT EXISTS realtime.messages (
|
|
56
|
+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
57
|
+
|
|
58
|
+
-- Event metadata
|
|
59
|
+
event_name TEXT NOT NULL,
|
|
60
|
+
|
|
61
|
+
-- Channel reference (SET NULL on delete to preserve history)
|
|
62
|
+
channel_id UUID REFERENCES realtime.channels(id) ON DELETE SET NULL,
|
|
63
|
+
channel_name TEXT NOT NULL, -- Denormalized for query convenience after channel deletion
|
|
64
|
+
|
|
65
|
+
-- Event payload (stored for audit/replay purposes)
|
|
66
|
+
payload JSONB DEFAULT '{}'::jsonb NOT NULL,
|
|
67
|
+
|
|
68
|
+
-- Sender information
|
|
69
|
+
-- 'system' = triggered by database trigger (via publish() function)
|
|
70
|
+
-- 'user' = published by client via WebSocket
|
|
71
|
+
sender_type TEXT DEFAULT 'system' NOT NULL CHECK (sender_type IN ('system', 'user')),
|
|
72
|
+
sender_id UUID, -- User ID for 'user' type, NULL for 'system' type
|
|
73
|
+
|
|
74
|
+
-- Delivery statistics for WebSocket
|
|
75
|
+
ws_audience_count INTEGER DEFAULT 0 NOT NULL, -- How many clients were subscribed
|
|
76
|
+
|
|
77
|
+
-- Delivery statistics for Webhooks
|
|
78
|
+
wh_audience_count INTEGER DEFAULT 0 NOT NULL, -- How many webhook URLs configured
|
|
79
|
+
wh_delivered_count INTEGER DEFAULT 0 NOT NULL, -- How many succeeded (2xx response)
|
|
80
|
+
|
|
81
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
-- ============================================================================
|
|
85
|
+
-- INDEXES
|
|
86
|
+
-- ============================================================================
|
|
87
|
+
|
|
88
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_channels_pattern ON realtime.channels(pattern);
|
|
89
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_channels_enabled ON realtime.channels(enabled);
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_channel_id ON realtime.messages(channel_id);
|
|
91
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_channel_name ON realtime.messages(channel_name);
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_created_at ON realtime.messages(created_at DESC);
|
|
93
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_event_name ON realtime.messages(event_name);
|
|
94
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_sender ON realtime.messages(sender_type, sender_id);
|
|
95
|
+
|
|
96
|
+
-- ============================================================================
|
|
97
|
+
-- UPDATED_AT TRIGGER
|
|
98
|
+
-- ============================================================================
|
|
99
|
+
|
|
100
|
+
CREATE OR REPLACE FUNCTION realtime.update_updated_at()
|
|
101
|
+
RETURNS TRIGGER AS $$
|
|
102
|
+
BEGIN
|
|
103
|
+
NEW.updated_at = NOW();
|
|
104
|
+
RETURN NEW;
|
|
105
|
+
END;
|
|
106
|
+
$$ LANGUAGE plpgsql;
|
|
107
|
+
|
|
108
|
+
DROP TRIGGER IF EXISTS trg_channels_updated_at ON realtime.channels;
|
|
109
|
+
CREATE TRIGGER trg_channels_updated_at
|
|
110
|
+
BEFORE UPDATE ON realtime.channels
|
|
111
|
+
FOR EACH ROW EXECUTE FUNCTION realtime.update_updated_at();
|
|
112
|
+
|
|
113
|
+
-- ============================================================================
|
|
114
|
+
-- ROW LEVEL SECURITY (DISABLED BY DEFAULT)
|
|
115
|
+
-- ============================================================================
|
|
116
|
+
-- RLS is disabled by default for the best developer experience.
|
|
117
|
+
-- Channels and messages are open to all authenticated/anon users out of the box.
|
|
118
|
+
--
|
|
119
|
+
-- To enable access control, developers can:
|
|
120
|
+
-- 1. Enable RLS: ALTER TABLE realtime.channels ENABLE ROW LEVEL SECURITY;
|
|
121
|
+
-- 2. Add policies using the helper function realtime.channel_name()
|
|
122
|
+
--
|
|
123
|
+
-- See documentation for policy examples.
|
|
124
|
+
|
|
125
|
+
-- ============================================================================
|
|
126
|
+
-- HELPER FUNCTIONS FOR RLS POLICIES
|
|
127
|
+
-- ============================================================================
|
|
128
|
+
|
|
129
|
+
-- Returns the channel name being accessed (set by backend during permission checks)
|
|
130
|
+
-- Developers use this in policies instead of writing current_setting directly
|
|
131
|
+
CREATE OR REPLACE FUNCTION realtime.channel_name()
|
|
132
|
+
RETURNS TEXT AS $$
|
|
133
|
+
SELECT current_setting('realtime.channel_name', true);
|
|
134
|
+
$$ LANGUAGE SQL STABLE;
|
|
135
|
+
|
|
136
|
+
-- ============================================================================
|
|
137
|
+
-- PUBLISH FUNCTION
|
|
138
|
+
-- ============================================================================
|
|
139
|
+
-- Called by developer triggers to publish events to channels.
|
|
140
|
+
-- This function can only be executed by the backend (SECURITY DEFINER).
|
|
141
|
+
--
|
|
142
|
+
-- Usage in a trigger:
|
|
143
|
+
-- PERFORM realtime.publish(
|
|
144
|
+
-- 'order:' || NEW.id::text, -- channel name (resolved instance)
|
|
145
|
+
-- 'order_updated', -- event name
|
|
146
|
+
-- jsonb_build_object('id', NEW.id, 'status', NEW.status) -- payload
|
|
147
|
+
-- );
|
|
148
|
+
|
|
149
|
+
CREATE OR REPLACE FUNCTION realtime.publish(
|
|
150
|
+
p_channel_name TEXT,
|
|
151
|
+
p_event_name TEXT,
|
|
152
|
+
p_payload JSONB
|
|
153
|
+
)
|
|
154
|
+
RETURNS UUID AS $$
|
|
155
|
+
DECLARE
|
|
156
|
+
v_channel_id UUID;
|
|
157
|
+
v_message_id UUID;
|
|
158
|
+
BEGIN
|
|
159
|
+
-- Find matching channel: exact match first, then wildcard pattern match
|
|
160
|
+
-- For wildcard patterns like "order:%", check if p_channel_name LIKE pattern
|
|
161
|
+
SELECT id INTO v_channel_id
|
|
162
|
+
FROM realtime.channels
|
|
163
|
+
WHERE enabled = TRUE
|
|
164
|
+
AND (pattern = p_channel_name OR p_channel_name LIKE pattern)
|
|
165
|
+
ORDER BY pattern = p_channel_name DESC
|
|
166
|
+
LIMIT 1;
|
|
167
|
+
|
|
168
|
+
-- If no channel found, raise a warning and return NULL
|
|
169
|
+
IF v_channel_id IS NULL THEN
|
|
170
|
+
RAISE WARNING 'Realtime: No matching channel found for "%"', p_channel_name;
|
|
171
|
+
RETURN NULL;
|
|
172
|
+
END IF;
|
|
173
|
+
|
|
174
|
+
-- Insert message record (system-triggered, so sender_type = 'system')
|
|
175
|
+
INSERT INTO realtime.messages (
|
|
176
|
+
event_name,
|
|
177
|
+
channel_id,
|
|
178
|
+
channel_name,
|
|
179
|
+
payload,
|
|
180
|
+
sender_type
|
|
181
|
+
) VALUES (
|
|
182
|
+
p_event_name,
|
|
183
|
+
v_channel_id,
|
|
184
|
+
p_channel_name,
|
|
185
|
+
p_payload,
|
|
186
|
+
'system'
|
|
187
|
+
)
|
|
188
|
+
RETURNING id INTO v_message_id;
|
|
189
|
+
|
|
190
|
+
RETURN v_message_id;
|
|
191
|
+
END;
|
|
192
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
193
|
+
|
|
194
|
+
-- Revoke execute from public, only backend can call this
|
|
195
|
+
REVOKE ALL ON FUNCTION realtime.publish FROM PUBLIC;
|
|
196
|
+
|
|
197
|
+
-- ============================================================================
|
|
198
|
+
-- TRIGGER FOR PG_NOTIFY
|
|
199
|
+
-- ============================================================================
|
|
200
|
+
-- Trigger function that sends pg_notify for every new message (both system and client).
|
|
201
|
+
|
|
202
|
+
CREATE OR REPLACE FUNCTION realtime.notify_on_message_insert()
|
|
203
|
+
RETURNS TRIGGER AS $$
|
|
204
|
+
BEGIN
|
|
205
|
+
-- Send only message_id to bypass pg_notify 8KB payload limit
|
|
206
|
+
-- Backend will fetch full message from DB
|
|
207
|
+
PERFORM pg_notify('realtime_message', NEW.id::text);
|
|
208
|
+
RETURN NEW;
|
|
209
|
+
END;
|
|
210
|
+
$$ LANGUAGE plpgsql;
|
|
211
|
+
|
|
212
|
+
-- Create trigger on messages table
|
|
213
|
+
DROP TRIGGER IF EXISTS trg_message_notify ON realtime.messages;
|
|
214
|
+
CREATE TRIGGER trg_message_notify
|
|
215
|
+
AFTER INSERT ON realtime.messages
|
|
216
|
+
FOR EACH ROW
|
|
217
|
+
EXECUTE FUNCTION realtime.notify_on_message_insert();
|
|
218
|
+
|
|
219
|
+
-- ============================================================================
|
|
220
|
+
-- GRANTS
|
|
221
|
+
-- ============================================================================
|
|
222
|
+
|
|
223
|
+
-- Grant schema access to both authenticated and anonymous users
|
|
224
|
+
GRANT USAGE ON SCHEMA realtime TO authenticated, anon;
|
|
225
|
+
|
|
226
|
+
-- Grant SELECT on channels table (allows subscribe)
|
|
227
|
+
GRANT SELECT ON realtime.channels TO authenticated, anon;
|
|
228
|
+
|
|
229
|
+
-- Grant INSERT on messages table (allows publish)
|
|
230
|
+
GRANT INSERT ON realtime.messages TO authenticated, anon;
|
|
231
|
+
|
|
232
|
+
-- Grant execution permission on helper function (used when RLS is enabled)
|
|
233
|
+
GRANT EXECUTE ON FUNCTION realtime.channel_name() TO authenticated, anon;
|