insforge 1.3.0 → 1.4.8
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/CHANGELOG.md +2 -0
- package/auth/package.json +5 -3
- package/auth/src/lib/broadcastService.ts +115 -117
- package/auth/src/lib/insforge.ts +8 -0
- package/auth/src/main.tsx +2 -4
- package/auth/src/pages/SignInPage.tsx +60 -60
- package/auth/src/pages/SignUpPage.tsx +60 -60
- package/auth/src/pages/VerifyEmailPage.tsx +18 -0
- package/auth/tsconfig.json +2 -1
- package/backend/package.json +10 -6
- package/backend/src/api/middlewares/rate-limiters.ts +127 -127
- package/backend/src/api/routes/ai/index.routes.ts +475 -468
- package/backend/src/api/routes/auth/index.routes.ts +85 -32
- package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
- package/backend/src/api/routes/database/index.routes.ts +2 -0
- package/backend/src/api/routes/database/records.routes.ts +39 -175
- package/backend/src/api/routes/database/rpc.routes.ts +69 -0
- package/backend/src/api/routes/deployments/index.routes.ts +192 -0
- package/backend/src/api/routes/docs/index.routes.ts +3 -2
- package/backend/src/api/routes/email/index.routes.ts +35 -35
- package/backend/src/api/routes/functions/index.routes.ts +3 -3
- package/backend/src/api/routes/metadata/index.routes.ts +26 -0
- package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
- package/backend/src/infra/database/database.manager.ts +0 -10
- package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
- package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
- package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
- package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
- package/backend/src/infra/security/token.manager.ts +1 -4
- package/backend/src/providers/ai/openrouter.provider.ts +12 -3
- package/backend/src/providers/database/base.provider.ts +39 -0
- package/backend/src/providers/database/cloud.provider.ts +159 -0
- package/backend/src/providers/deployments/vercel.provider.ts +516 -0
- package/backend/src/server.ts +19 -7
- package/backend/src/services/ai/ai-config.service.ts +6 -6
- package/backend/src/services/ai/ai-model.service.ts +60 -60
- package/backend/src/services/ai/ai-usage.service.ts +7 -7
- package/backend/src/services/ai/chat-completion.service.ts +415 -220
- package/backend/src/services/ai/helpers.ts +64 -64
- package/backend/src/services/ai/index.ts +13 -13
- package/backend/src/services/auth/auth-config.service.ts +4 -4
- package/backend/src/services/auth/auth-otp.service.ts +6 -6
- package/backend/src/services/auth/auth.service.ts +134 -74
- package/backend/src/services/auth/index.ts +4 -4
- package/backend/src/services/auth/oauth-config.service.ts +12 -12
- package/backend/src/services/database/database-advance.service.ts +19 -55
- package/backend/src/services/database/database-table.service.ts +38 -85
- package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
- package/backend/src/services/deployments/deployment.service.ts +693 -0
- package/backend/src/services/functions/function.service.ts +61 -41
- package/backend/src/services/logs/audit.service.ts +10 -10
- package/backend/src/services/secrets/secret.service.ts +101 -27
- package/backend/src/services/storage/storage.service.ts +30 -30
- package/backend/src/services/usage/usage.service.ts +6 -6
- package/backend/src/types/ai.ts +8 -0
- package/backend/src/types/auth.ts +5 -1
- package/backend/src/types/database.ts +2 -0
- package/backend/src/types/deployments.ts +33 -0
- package/backend/src/types/storage.ts +1 -1
- package/backend/src/types/webhooks.ts +45 -0
- package/backend/src/utils/cookies.ts +34 -35
- package/backend/src/utils/environment.ts +0 -14
- package/backend/src/utils/s3-config-loader.ts +64 -64
- package/backend/src/utils/seed.ts +334 -301
- package/backend/src/utils/sql-parser.ts +126 -0
- package/backend/src/utils/utils.ts +114 -114
- package/backend/src/utils/validations.ts +10 -10
- package/backend/tests/local/test-rpc.sh +141 -0
- package/backend/tests/local/test-secrets.sh +1 -1
- package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
- package/backend/tests/manual/test-rawsql-modes.sh +24 -24
- package/backend/tests/unit/database-advance.test.ts +326 -0
- package/backend/tests/unit/helpers.test.ts +2 -2
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
- package/docker-compose.prod.yml +1 -1
- package/docker-compose.yml +1 -1
- package/docs/agent-docs/deployment.md +79 -0
- package/docs/changelog.mdx +165 -72
- package/docs/core-concepts/ai/architecture.mdx +1 -23
- package/docs/core-concepts/ai/sdk.mdx +26 -1
- package/docs/core-concepts/authentication/architecture.mdx +6 -8
- package/docs/core-concepts/authentication/sdk.mdx +387 -91
- package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
- package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
- package/docs/core-concepts/database/architecture.mdx +58 -21
- package/docs/core-concepts/database/pgvector.mdx +138 -0
- package/docs/core-concepts/database/sdk.mdx +17 -17
- package/docs/core-concepts/deployments/architecture.mdx +152 -0
- package/docs/core-concepts/email/architecture.mdx +4 -2
- package/docs/core-concepts/functions/architecture.mdx +1 -1
- package/docs/core-concepts/functions/sdk.mdx +0 -1
- package/docs/core-concepts/realtime/architecture.mdx +1 -1
- package/docs/core-concepts/storage/architecture.mdx +1 -1
- package/docs/core-concepts/storage/sdk.mdx +25 -25
- package/docs/docs.json +14 -6
- package/docs/favicon.png +0 -0
- package/docs/favicon.svg +3 -18
- package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
- package/docs/images/changelog/dec-2025/moreModels.png +0 -0
- package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
- package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
- package/docs/images/changelog/dec-2025/realtime2.png +0 -0
- package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
- package/docs/images/mcp-setup/claude-code-connect.png +0 -0
- package/docs/images/mcp-setup/cline-1.png +0 -0
- package/docs/images/mcp-setup/cline-2.png +0 -0
- package/docs/images/mcp-setup/cline-3.png +0 -0
- package/docs/images/mcp-setup/connect-project.png +0 -0
- package/docs/images/mcp-setup/copilot-1.png +0 -0
- package/docs/images/mcp-setup/copilot-2.png +0 -0
- package/docs/images/mcp-setup/copilot-3.png +0 -0
- package/docs/images/mcp-setup/mcp-json-1.png +0 -0
- package/docs/images/mcp-setup/mcp-json-2.png +0 -0
- package/docs/images/mcp-setup/qoder-1.png +0 -0
- package/docs/images/mcp-setup/qoder-2.png +0 -0
- package/docs/images/mcp-setup/roocode-1.png +0 -0
- package/docs/images/mcp-setup/roocode-2.png +0 -0
- package/docs/images/mcp-setup/trae-1.png +0 -0
- package/docs/images/mcp-setup/trae-2.png +0 -0
- package/docs/images/mcp-setup/trae-3.png +0 -0
- package/docs/images/mcp-setup/trae-4.png +0 -0
- package/docs/images/mcp-setup/trae-5.png +0 -0
- package/docs/images/mcp-setup/windsurf-1.png +0 -0
- package/docs/images/mcp-setup/windsurf-2.png +0 -0
- package/docs/insforge-instructions-sdk.md +7 -3
- package/docs/introduction.mdx +9 -8
- package/docs/mcp-setup.mdx +332 -0
- package/docs/oauth-server.mdx +563 -0
- package/docs/partnership.mdx +79 -10
- package/docs/quickstart.mdx +1 -1
- package/docs/vscode-extension.mdx +74 -0
- package/eslint.config.js +1 -0
- package/examples/response-examples.md +1 -1
- package/frontend/package.json +1 -1
- package/frontend/src/App.tsx +8 -3
- package/frontend/src/assets/logos/antigravity.svg +1 -0
- package/frontend/src/assets/logos/copilot.svg +10 -0
- package/frontend/src/assets/logos/deepseek.svg +139 -0
- package/frontend/src/assets/logos/kiro.svg +9 -0
- package/frontend/src/assets/logos/qoder.svg +4 -0
- package/frontend/src/assets/logos/qwen.svg +15 -0
- package/frontend/src/components/CodeBlock.tsx +2 -2
- package/frontend/src/components/ConnectCTA.tsx +3 -2
- package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
- package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
- package/frontend/src/components/datagrid/index.ts +1 -1
- package/frontend/src/components/index.ts +0 -1
- package/frontend/src/components/layout/AppHeader.tsx +4 -27
- package/frontend/src/components/layout/AppSidebar.tsx +85 -100
- package/frontend/src/components/layout/Layout.tsx +34 -32
- package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
- package/frontend/src/components/radix/Select.tsx +151 -151
- package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
- package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
- package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
- package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
- package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
- package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
- package/frontend/src/features/ai/components/index.ts +6 -6
- package/frontend/src/features/ai/helpers.ts +147 -141
- package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
- package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
- package/frontend/src/features/auth/components/index.ts +5 -5
- package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
- package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
- package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
- package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
- package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
- package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
- package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
- package/frontend/src/features/database/constants.ts +16 -28
- package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
- package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
- package/frontend/src/features/database/hooks/useTables.ts +5 -7
- package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
- package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
- package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
- package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
- package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
- package/frontend/src/features/database/services/advance.service.ts +1 -15
- package/frontend/src/features/database/services/record.service.ts +4 -20
- package/frontend/src/features/database/services/table.service.ts +1 -4
- package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
- package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
- package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
- package/frontend/src/features/database/templates/notion-clone.ts +8 -8
- package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
- package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
- package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
- package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
- package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
- package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
- package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
- package/frontend/src/features/functions/components/index.ts +5 -5
- package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
- package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
- package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
- package/frontend/src/features/functions/services/function.service.ts +8 -25
- package/frontend/src/features/functions/services/secret.service.ts +23 -41
- package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
- package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
- package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
- package/frontend/src/features/logs/components/index.ts +1 -0
- package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
- package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
- package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
- package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
- package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
- package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
- package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
- package/frontend/src/features/onboard/components/index.ts +9 -4
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
- package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
- package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
- package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
- package/frontend/src/features/onboard/index.ts +17 -13
- package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
- package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
- package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
- package/frontend/src/index.css +249 -249
- package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
- package/frontend/src/lib/hooks/useMetadata.ts +45 -1
- package/frontend/src/lib/hooks/useModal.tsx +2 -0
- package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
- package/frontend/src/lib/services/metadata.service.ts +20 -3
- package/frontend/src/lib/utils/menuItems.ts +223 -207
- package/frontend/src/lib/utils/utils.ts +196 -196
- package/functions/server.ts +315 -315
- package/functions/worker-template.js +1 -1
- package/openapi/ai.yaml +115 -5
- package/openapi/auth.yaml +97 -17
- package/openapi/logs.yaml +0 -2
- package/openapi/metadata.yaml +0 -2
- package/openapi/records.yaml +21 -21
- package/openapi/tables.yaml +1 -2
- package/package.json +1 -1
- package/shared-schemas/package.json +1 -1
- package/shared-schemas/src/ai-api.schema.ts +251 -143
- package/shared-schemas/src/ai.schema.ts +63 -63
- package/shared-schemas/src/auth-api.schema.ts +34 -6
- package/shared-schemas/src/auth.schema.ts +17 -10
- package/shared-schemas/src/cloud-events.schema.ts +26 -0
- package/shared-schemas/src/deployments-api.schema.ts +55 -0
- package/shared-schemas/src/deployments.schema.ts +30 -0
- package/shared-schemas/src/docs.schema.ts +8 -2
- package/shared-schemas/src/email-api.schema.ts +30 -30
- package/shared-schemas/src/functions-api.schema.ts +13 -4
- package/shared-schemas/src/functions.schema.ts +1 -1
- package/shared-schemas/src/index.ts +22 -18
- package/shared-schemas/src/metadata.schema.ts +30 -4
- package/shared-schemas/src/secrets-api.schema.ts +44 -0
- package/shared-schemas/src/secrets.schema.ts +15 -0
- package/zeabur/README.md +13 -0
- package/zeabur/template.yml +20 -51
- package/backend/src/types/profile.ts +0 -55
- package/frontend/src/components/ProjectInfoModal.tsx +0 -128
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { describe, test, expect } from 'vitest';
|
|
2
|
+
import { DatabaseAdvanceService } from '../../src/services/database/database-advance.service';
|
|
3
|
+
import { AppError } from '../../src/api/middlewares/error';
|
|
4
|
+
import { ERROR_CODES } from '../../src/types/error-constants';
|
|
5
|
+
|
|
6
|
+
describe('DatabaseAdvanceService - sanitizeQuery', () => {
|
|
7
|
+
const service = DatabaseAdvanceService.getInstance();
|
|
8
|
+
|
|
9
|
+
describe('auth schema blocking', () => {
|
|
10
|
+
test('blocks DELETE FROM auth.users', () => {
|
|
11
|
+
const query = "DELETE FROM auth.users WHERE id = '00000000-0000-0000-0000-000000000001'";
|
|
12
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
13
|
+
expect(() => service.sanitizeQuery(query)).toThrow(/auth schema/i);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('blocks DELETE FROM quoted auth schema', () => {
|
|
17
|
+
const query = 'DELETE FROM "auth"."users" WHERE id = $1';
|
|
18
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('blocks TRUNCATE auth.users', () => {
|
|
22
|
+
const query = 'TRUNCATE TABLE auth.users';
|
|
23
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('blocks TRUNCATE without TABLE keyword', () => {
|
|
27
|
+
const query = 'TRUNCATE auth.users';
|
|
28
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('blocks DROP TABLE auth.users', () => {
|
|
32
|
+
const query = 'DROP TABLE auth.users';
|
|
33
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('blocks DROP TABLE with IF EXISTS', () => {
|
|
37
|
+
const query = 'DROP TABLE IF EXISTS auth.users';
|
|
38
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('blocks DROP INDEX on auth schema', () => {
|
|
42
|
+
const query = 'DROP INDEX auth.users_email_idx';
|
|
43
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('blocks DROP TRIGGER on auth schema tables', () => {
|
|
47
|
+
const queries = [
|
|
48
|
+
'DROP TRIGGER user_created_trigger ON auth.users',
|
|
49
|
+
'DROP TRIGGER IF EXISTS user_created_trigger ON auth.users',
|
|
50
|
+
'DROP TRIGGER trigger_name ON "auth"."users"',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
queries.forEach((query) => {
|
|
54
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('blocks DROP FUNCTION on auth schema', () => {
|
|
59
|
+
const query = 'DROP FUNCTION auth.create_user_profile()';
|
|
60
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('blocks DROP VIEW on auth schema', () => {
|
|
64
|
+
const query = 'DROP VIEW auth.user_summary';
|
|
65
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('blocks DROP SCHEMA auth (no dot after auth)', () => {
|
|
69
|
+
const queries = [
|
|
70
|
+
'DROP SCHEMA auth',
|
|
71
|
+
'DROP SCHEMA auth CASCADE',
|
|
72
|
+
'DROP SCHEMA IF EXISTS auth',
|
|
73
|
+
'DROP SCHEMA "auth" CASCADE',
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
queries.forEach((query) => {
|
|
77
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('allows SELECT FROM auth.users (read-only)', () => {
|
|
82
|
+
const query = 'SELECT * FROM auth.users LIMIT 1';
|
|
83
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('blocks case-insensitive AUTH.users', () => {
|
|
87
|
+
const query = 'DELETE FROM AUTH.users WHERE id = $1';
|
|
88
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('blocks mixed case Auth.Users', () => {
|
|
92
|
+
const query = 'DELETE FROM Auth.Users WHERE id = $1';
|
|
93
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('blocks auth schema with quoted table name', () => {
|
|
97
|
+
const query = 'DELETE FROM auth."users" WHERE id = $1';
|
|
98
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('blocks DELETE with whitespace before dot in auth schema', () => {
|
|
102
|
+
const queries = [
|
|
103
|
+
'DELETE FROM auth . users WHERE id = $1',
|
|
104
|
+
'DELETE FROM auth .users WHERE id = $1',
|
|
105
|
+
'DELETE FROM auth\t.users WHERE id = $1',
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
queries.forEach((query) => {
|
|
109
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('allows UPDATE on auth schema (not blocked)', () => {
|
|
114
|
+
// UPDATE is allowed on auth schema
|
|
115
|
+
const queries = [
|
|
116
|
+
'UPDATE auth.users SET email = $1 WHERE id = $2',
|
|
117
|
+
'UPDATE auth.user_providers SET provider = $1 WHERE id = $2',
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
queries.forEach((query) => {
|
|
121
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('blocks DELETE on other auth schema tables', () => {
|
|
126
|
+
const queries = [
|
|
127
|
+
'DELETE FROM auth.user_providers WHERE id = $1',
|
|
128
|
+
'DELETE FROM auth.configs WHERE id = $1',
|
|
129
|
+
'DELETE FROM auth.oauth_configs WHERE id = $1',
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
queries.forEach((query) => {
|
|
133
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('blocks TRUNCATE on other auth schema tables', () => {
|
|
138
|
+
const queries = ['TRUNCATE TABLE auth.user_providers', 'TRUNCATE auth.configs'];
|
|
139
|
+
|
|
140
|
+
queries.forEach((query) => {
|
|
141
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('blocks DROP operations on other auth schema tables', () => {
|
|
146
|
+
const queries = [
|
|
147
|
+
'DROP TABLE auth.user_providers',
|
|
148
|
+
'DROP INDEX auth.configs_key_idx',
|
|
149
|
+
'DROP FUNCTION auth.some_function()',
|
|
150
|
+
'DROP VIEW auth.user_view',
|
|
151
|
+
'DROP SEQUENCE auth.user_id_seq',
|
|
152
|
+
'DROP POLICY auth_policy ON auth.users',
|
|
153
|
+
'DROP TYPE auth.user_type',
|
|
154
|
+
'DROP DOMAIN auth.email_domain',
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
queries.forEach((query) => {
|
|
158
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('allows SELECT on other auth schema tables', () => {
|
|
163
|
+
const queries = [
|
|
164
|
+
'SELECT * FROM auth.email_otps',
|
|
165
|
+
'SELECT * FROM auth.user_providers',
|
|
166
|
+
'SELECT * FROM auth.configs',
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
queries.forEach((query) => {
|
|
170
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('allows INSERT into auth schema (for test users)', () => {
|
|
175
|
+
const queries = [
|
|
176
|
+
"INSERT INTO auth.users (email, password) VALUES ('test@example.com', 'hashed')",
|
|
177
|
+
'INSERT INTO auth.user_providers (user_id, provider) VALUES ($1, $2)',
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
queries.forEach((query) => {
|
|
181
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('allows CREATE TRIGGER on auth schema', () => {
|
|
186
|
+
const query =
|
|
187
|
+
'CREATE TRIGGER user_profile_trigger AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION create_user_profile()';
|
|
188
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('allows ALTER TABLE on auth schema (for indexes, constraints)', () => {
|
|
192
|
+
const queries = [
|
|
193
|
+
'ALTER TABLE auth.users ADD CONSTRAINT email_unique UNIQUE (email)',
|
|
194
|
+
'CREATE INDEX idx_auth_users_email ON auth.users(email)',
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
queries.forEach((query) => {
|
|
198
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('throws AppError with FORBIDDEN error code', () => {
|
|
203
|
+
const query = 'DELETE FROM auth.users WHERE id = $1';
|
|
204
|
+
try {
|
|
205
|
+
service.sanitizeQuery(query);
|
|
206
|
+
expect.fail('Should have thrown an error');
|
|
207
|
+
} catch (error) {
|
|
208
|
+
expect(error).toBeInstanceOf(AppError);
|
|
209
|
+
if (error instanceof AppError) {
|
|
210
|
+
expect(error.statusCode).toBe(403);
|
|
211
|
+
expect(error.code).toBe(ERROR_CODES.FORBIDDEN);
|
|
212
|
+
expect(error.message).toContain('auth schema');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('allowed queries', () => {
|
|
219
|
+
test('allows DELETE from other tables even when auth is referenced in subquery', () => {
|
|
220
|
+
const queries = [
|
|
221
|
+
'DELETE FROM orders WHERE user_id NOT IN (SELECT id FROM auth.users)',
|
|
222
|
+
'DELETE FROM user_profiles WHERE id IN (SELECT user_id FROM auth.sessions WHERE expired = true)',
|
|
223
|
+
'DELETE FROM public.users WHERE email IN (SELECT email FROM auth.users WHERE verified = false)',
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
queries.forEach((query) => {
|
|
227
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('allows queries with auth schema in string literals or comments', () => {
|
|
232
|
+
const queries = [
|
|
233
|
+
"SELECT 'DELETE FROM auth.users' AS example_query",
|
|
234
|
+
'/* DELETE FROM auth.users */ SELECT * FROM public.users',
|
|
235
|
+
"SELECT 'DROP TABLE auth.users' AS test",
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
queries.forEach((query) => {
|
|
239
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('blocks DELETE FROM auth even when previous line has a comment', () => {
|
|
244
|
+
const queries = [
|
|
245
|
+
'SELECT 1; -- some comment\nDELETE FROM auth.users WHERE id = 1',
|
|
246
|
+
'-- previous comment\nDELETE FROM auth.users',
|
|
247
|
+
'/* block comment */\nDELETE FROM auth.users WHERE id = 1',
|
|
248
|
+
];
|
|
249
|
+
|
|
250
|
+
queries.forEach((query) => {
|
|
251
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('allows SELECT from public schema', () => {
|
|
256
|
+
const query = 'SELECT 1 as test';
|
|
257
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('allows auth.uid() function calls', () => {
|
|
261
|
+
const queries = [
|
|
262
|
+
'SELECT auth.uid()',
|
|
263
|
+
'SELECT * FROM users WHERE id = auth.uid()',
|
|
264
|
+
'CREATE POLICY test ON users FOR SELECT USING (id = auth.uid())',
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
queries.forEach((query) => {
|
|
268
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('allows auth.role() and auth.email() function calls', () => {
|
|
273
|
+
const queries = [
|
|
274
|
+
'SELECT auth.role()',
|
|
275
|
+
'SELECT auth.email()',
|
|
276
|
+
'SELECT * FROM users WHERE email = auth.email()',
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
queries.forEach((query) => {
|
|
280
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test('allows DELETE from public schema tables', () => {
|
|
285
|
+
const query = 'DELETE FROM users WHERE id = $1';
|
|
286
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test('allows INSERT into public schema tables', () => {
|
|
290
|
+
const query = "INSERT INTO products (name) VALUES ('test')";
|
|
291
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test('allows UPDATE public schema tables', () => {
|
|
295
|
+
const query = 'UPDATE products SET price = 100 WHERE id = $1';
|
|
296
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('allows CREATE TABLE in public schema', () => {
|
|
300
|
+
const query = 'CREATE TABLE test_table (id UUID PRIMARY KEY)';
|
|
301
|
+
expect(() => service.sanitizeQuery(query)).not.toThrow();
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe('other blocked operations', () => {
|
|
306
|
+
test('blocks DROP DATABASE', () => {
|
|
307
|
+
const query = 'DROP DATABASE testdb';
|
|
308
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test('blocks CREATE DATABASE', () => {
|
|
312
|
+
const query = 'CREATE DATABASE testdb';
|
|
313
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test('blocks ALTER DATABASE', () => {
|
|
317
|
+
const query = 'ALTER DATABASE testdb SET connection_limit = 100';
|
|
318
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test('blocks pg_catalog access', () => {
|
|
322
|
+
const query = 'SELECT * FROM pg_catalog.pg_tables';
|
|
323
|
+
expect(() => service.sanitizeQuery(query)).toThrow(AppError);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
});
|
|
@@ -57,7 +57,7 @@ describe('convertSqlTypeToColumnType', () => {
|
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
it('returns first
|
|
61
|
-
expect(convertSqlTypeToColumnType('customtype')).toBe('
|
|
60
|
+
it('returns first 8 chars for unknown types', () => {
|
|
61
|
+
expect(convertSqlTypeToColumnType('customtype')).toBe('customty');
|
|
62
62
|
});
|
|
63
63
|
});
|
|
@@ -15,8 +15,8 @@ Expert patterns for designing PostgreSQL schemas optimized for InsForge's PostgR
|
|
|
15
15
|
```sql
|
|
16
16
|
CREATE TABLE follows (
|
|
17
17
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
18
|
-
follower_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
19
|
-
following_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
18
|
+
follower_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
19
|
+
following_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
20
20
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
21
21
|
UNIQUE(follower_id, following_id)
|
|
22
22
|
);
|
|
@@ -44,16 +44,16 @@ CREATE POLICY "Users can unfollow" ON follows
|
|
|
44
44
|
|
|
45
45
|
**Query with InsForge SDK:**
|
|
46
46
|
```javascript
|
|
47
|
-
// Get users I follow
|
|
47
|
+
// Get users I follow
|
|
48
48
|
const { data: following } = await client.database
|
|
49
49
|
.from('follows')
|
|
50
|
-
.select(
|
|
50
|
+
.select()
|
|
51
51
|
.eq('follower_id', currentUserId);
|
|
52
52
|
|
|
53
53
|
// Get my followers
|
|
54
54
|
const { data: followers } = await client.database
|
|
55
55
|
.from('follows')
|
|
56
|
-
.select(
|
|
56
|
+
.select()
|
|
57
57
|
.eq('following_id', currentUserId);
|
|
58
58
|
|
|
59
59
|
// Check if user1 follows user2
|
|
@@ -80,7 +80,7 @@ await client.database
|
|
|
80
80
|
```sql
|
|
81
81
|
CREATE TABLE likes (
|
|
82
82
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
83
|
-
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
83
|
+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
84
84
|
post_id UUID REFERENCES posts(id) ON DELETE CASCADE,
|
|
85
85
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
86
86
|
UNIQUE(user_id, post_id) -- Prevent duplicate likes
|
|
@@ -143,7 +143,7 @@ await client.database
|
|
|
143
143
|
CREATE TABLE comments (
|
|
144
144
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
145
145
|
post_id UUID REFERENCES posts(id) ON DELETE CASCADE,
|
|
146
|
-
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
146
|
+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
147
147
|
parent_comment_id UUID REFERENCES comments(id) ON DELETE CASCADE,
|
|
148
148
|
content TEXT NOT NULL,
|
|
149
149
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
@@ -178,17 +178,20 @@ CREATE POLICY "Users can delete their comments" ON comments
|
|
|
178
178
|
**Query with InsForge SDK:**
|
|
179
179
|
```javascript
|
|
180
180
|
// Get top-level comments with author info
|
|
181
|
+
// Note: profile is a JSONB column containing { name, avatar_url, bio, birthday }
|
|
181
182
|
const { data: comments } = await client.database
|
|
182
183
|
.from('comments')
|
|
183
|
-
.select('*, author:user_id(
|
|
184
|
+
.select('*, author:user_id(id, profile)')
|
|
184
185
|
.eq('post_id', postId)
|
|
185
186
|
.is('parent_comment_id', null)
|
|
186
187
|
.order('created_at', { ascending: false });
|
|
187
188
|
|
|
189
|
+
// Access author info: comment.author.profile.name, comment.author.profile.avatar_url
|
|
190
|
+
|
|
188
191
|
// Get replies to a comment
|
|
189
192
|
const { data: replies } = await client.database
|
|
190
193
|
.from('comments')
|
|
191
|
-
.select('*, author:user_id(
|
|
194
|
+
.select('*, author:user_id(id, profile)')
|
|
192
195
|
.eq('parent_comment_id', commentId)
|
|
193
196
|
.order('created_at', { ascending: true });
|
|
194
197
|
```
|
|
@@ -209,7 +212,7 @@ CREATE TABLE organizations (
|
|
|
209
212
|
|
|
210
213
|
CREATE TABLE organization_members (
|
|
211
214
|
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
|
|
212
|
-
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
215
|
+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
213
216
|
role TEXT NOT NULL CHECK (role IN ('owner', 'admin', 'member')),
|
|
214
217
|
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
|
215
218
|
PRIMARY KEY (organization_id, user_id)
|
package/docker-compose.prod.yml
CHANGED
package/docker-compose.yml
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# InsForge Deployment - Agent Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Deploy frontend applications to InsForge using the `create-deployment` MCP tool. The tool handles uploading source files, building, and deploying automatically.
|
|
6
|
+
|
|
7
|
+
## Deploy with MCP Tool
|
|
8
|
+
|
|
9
|
+
Use the `create-deployment` tool with these parameters:
|
|
10
|
+
|
|
11
|
+
| Parameter | Required | Description |
|
|
12
|
+
|-----------|----------|-------------|
|
|
13
|
+
| `sourceDirectory` | Yes | **Absolute path** to source directory (e.g., `/Users/me/project/frontend`). Relative paths do not work. |
|
|
14
|
+
| `projectSettings.buildCommand` | No | Build command (default: auto-detected) |
|
|
15
|
+
| `projectSettings.outputDirectory` | No | Build output directory (default: auto-detected) |
|
|
16
|
+
| `projectSettings.installCommand` | No | Install command (default: `npm install`) |
|
|
17
|
+
| `projectSettings.rootDirectory` | No | Root directory within source |
|
|
18
|
+
| `envVars` | No | Array of `{key, value}` objects |
|
|
19
|
+
| `meta` | No | Custom metadata key-value pairs |
|
|
20
|
+
|
|
21
|
+
### Example
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"sourceDirectory": "/Users/me/project/frontend",
|
|
26
|
+
"projectSettings": {
|
|
27
|
+
"buildCommand": "npm run build",
|
|
28
|
+
"outputDirectory": "dist"
|
|
29
|
+
},
|
|
30
|
+
"envVars": [
|
|
31
|
+
{ "key": "VITE_INSFORGE_BASE_URL", "value": "https://your-project.insforge.app" },
|
|
32
|
+
{ "key": "VITE_INSFORGE_ANON_KEY", "value": "your-anon-key" }
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Important**:
|
|
38
|
+
- `sourceDirectory` must be an **absolute path** (relative paths don't work on Windows)
|
|
39
|
+
- Pass the source directory, NOT pre-built static files
|
|
40
|
+
- Include all required environment variables (e.g., `VITE_*` for Vite apps)
|
|
41
|
+
|
|
42
|
+
## Check Deployment Status
|
|
43
|
+
|
|
44
|
+
After creating a deployment, query the status using `run-raw-sql`:
|
|
45
|
+
|
|
46
|
+
```sql
|
|
47
|
+
SELECT id, status, url, created_at
|
|
48
|
+
FROM system.deployments
|
|
49
|
+
ORDER BY created_at DESC
|
|
50
|
+
LIMIT 1;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Status Values
|
|
54
|
+
|
|
55
|
+
| Status | Description |
|
|
56
|
+
|--------|-------------|
|
|
57
|
+
| `WAITING` | Waiting for source upload |
|
|
58
|
+
| `UPLOADING` | Uploading to build server |
|
|
59
|
+
| `QUEUED` | Queued for build |
|
|
60
|
+
| `BUILDING` | Building (typically ~1 min) |
|
|
61
|
+
| `READY` | Deployment complete, URL available |
|
|
62
|
+
| `ERROR` | Build or deployment failed |
|
|
63
|
+
| `CANCELED` | Deployment was cancelled |
|
|
64
|
+
|
|
65
|
+
### Get Deployment URL
|
|
66
|
+
|
|
67
|
+
Once status is `READY`, the `url` column contains the live deployment URL.
|
|
68
|
+
|
|
69
|
+
```sql
|
|
70
|
+
SELECT url FROM system.deployments WHERE id = '<deployment-id>';
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick Reference
|
|
74
|
+
|
|
75
|
+
| Task | Tool | Command |
|
|
76
|
+
|------|------|---------|
|
|
77
|
+
| Deploy app | `create-deployment` | Provide `sourceDirectory` and `envVars` |
|
|
78
|
+
| Check status | `run-raw-sql` | `SELECT status FROM system.deployments WHERE id = '...'` |
|
|
79
|
+
| List deployments | `run-raw-sql` | `SELECT * FROM system.deployments ORDER BY created_at DESC` |
|