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
|
@@ -92,6 +92,132 @@ function extractChange(stmt: Record<string, unknown>): DatabaseResourceUpdate |
|
|
|
92
92
|
return { type };
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Extract schema name from a relation/RangeVar node in the AST
|
|
97
|
+
*/
|
|
98
|
+
function getSchemaName(relation: Record<string, unknown> | undefined): string | null {
|
|
99
|
+
if (!relation) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Direct schemaname property (for DeleteStmt.relation)
|
|
104
|
+
if (relation.schemaname) {
|
|
105
|
+
return relation.schemaname as string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// RangeVar structure (for TruncateStmt.relations)
|
|
109
|
+
if (relation.RangeVar) {
|
|
110
|
+
const rangeVar = relation.RangeVar as Record<string, unknown>;
|
|
111
|
+
if (rangeVar.schemaname) {
|
|
112
|
+
return rangeVar.schemaname as string;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check if a query contains dangerous operations on the auth schema
|
|
121
|
+
* Returns an error message if blocked, null if allowed
|
|
122
|
+
*/
|
|
123
|
+
export function checkAuthSchemaOperations(query: string): string | null {
|
|
124
|
+
try {
|
|
125
|
+
const { stmts } = parseSync(query);
|
|
126
|
+
|
|
127
|
+
for (const stmtWrapper of stmts) {
|
|
128
|
+
const stmt = stmtWrapper.stmt as Record<string, unknown>;
|
|
129
|
+
const [stmtType, data] = Object.entries(stmt)[0] as [string, Record<string, unknown>];
|
|
130
|
+
|
|
131
|
+
// Check DELETE statements
|
|
132
|
+
if (stmtType === 'DeleteStmt') {
|
|
133
|
+
const relation = data.relation as Record<string, unknown> | undefined;
|
|
134
|
+
const schemaName = getSchemaName(relation);
|
|
135
|
+
if (schemaName?.toLowerCase() === 'auth') {
|
|
136
|
+
return 'DELETE operations on auth schema are not allowed. User deletion must be done through dedicated authentication APIs.';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check TRUNCATE statements
|
|
141
|
+
if (stmtType === 'TruncateStmt') {
|
|
142
|
+
const relations = (data.relations as Array<Record<string, unknown>>) || [];
|
|
143
|
+
for (const relation of relations) {
|
|
144
|
+
const schemaName = getSchemaName(relation);
|
|
145
|
+
if (schemaName?.toLowerCase() === 'auth') {
|
|
146
|
+
return 'TRUNCATE operations on auth schema are not allowed. This would delete all users and must be done through dedicated authentication APIs.';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check DROP statements
|
|
152
|
+
if (stmtType === 'DropStmt') {
|
|
153
|
+
const objects = (data.objects as Array<unknown>) || [];
|
|
154
|
+
for (const obj of objects) {
|
|
155
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
156
|
+
const objRecord = obj as Record<string, unknown>;
|
|
157
|
+
let schemaName: string | null = null;
|
|
158
|
+
|
|
159
|
+
// DROP SCHEMA: direct String object
|
|
160
|
+
if (objRecord.String) {
|
|
161
|
+
const stringObj = objRecord.String as Record<string, unknown>;
|
|
162
|
+
if (stringObj.sval) {
|
|
163
|
+
schemaName = stringObj.sval as string;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// DROP TABLE/INDEX/VIEW/etc: List with [schema, name] items
|
|
167
|
+
else if (objRecord.List) {
|
|
168
|
+
const list = objRecord.List as Record<string, unknown>;
|
|
169
|
+
const items = (list.items as Array<Record<string, unknown>>) || [];
|
|
170
|
+
// First item is typically the schema name
|
|
171
|
+
if (items.length > 0) {
|
|
172
|
+
const firstItem = items[0];
|
|
173
|
+
if (firstItem.String) {
|
|
174
|
+
const stringObj = firstItem.String as Record<string, unknown>;
|
|
175
|
+
schemaName = stringObj.sval as string;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// DROP FUNCTION/PROCEDURE: ObjectWithArgs with objname array
|
|
180
|
+
else if (objRecord.ObjectWithArgs) {
|
|
181
|
+
const objectWithArgs = objRecord.ObjectWithArgs as Record<string, unknown>;
|
|
182
|
+
const objname = (objectWithArgs.objname as Array<Record<string, unknown>>) || [];
|
|
183
|
+
// First item is typically the schema name
|
|
184
|
+
if (objname.length > 0) {
|
|
185
|
+
const firstItem = objname[0];
|
|
186
|
+
if (firstItem.String) {
|
|
187
|
+
const stringObj = firstItem.String as Record<string, unknown>;
|
|
188
|
+
schemaName = stringObj.sval as string;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// DROP TYPE/DOMAIN: TypeName with names array
|
|
193
|
+
else if (objRecord.TypeName) {
|
|
194
|
+
const typeName = objRecord.TypeName as Record<string, unknown>;
|
|
195
|
+
const names = (typeName.names as Array<Record<string, unknown>>) || [];
|
|
196
|
+
// First item is typically the schema name
|
|
197
|
+
if (names.length > 0) {
|
|
198
|
+
const firstItem = names[0];
|
|
199
|
+
if (firstItem.String) {
|
|
200
|
+
const stringObj = firstItem.String as Record<string, unknown>;
|
|
201
|
+
schemaName = stringObj.sval as string;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (schemaName?.toLowerCase() === 'auth') {
|
|
207
|
+
return 'DROP operations on auth schema are not allowed. This would destroy authentication resources and break the system.';
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return null; // No dangerous operations found
|
|
215
|
+
} catch (parseError) {
|
|
216
|
+
logger.warn('SQL parse error in checkAuthSchemaOperations, letting query through:', parseError);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
95
221
|
/**
|
|
96
222
|
* Parse a SQL string into individual statements, properly handling:
|
|
97
223
|
* - String literals with embedded semicolons
|
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
import { ColumnType, type AuthConfigSchema } from '@insforge/shared-schemas';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Generates a user-friendly error message listing all password requirements
|
|
6
|
-
* @param config - Authentication configuration with password requirements
|
|
7
|
-
* @returns A formatted message listing all enabled password requirements
|
|
8
|
-
*/
|
|
9
|
-
export function getPasswordRequirementsMessage(config: AuthConfigSchema): string {
|
|
10
|
-
const requirements: string[] = [];
|
|
11
|
-
|
|
12
|
-
requirements.push(`at least ${config.passwordMinLength} characters long`);
|
|
13
|
-
|
|
14
|
-
if (config.requireNumber) {
|
|
15
|
-
requirements.push('at least one number');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (config.requireLowercase) {
|
|
19
|
-
requirements.push('at least one lowercase letter');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (config.requireUppercase) {
|
|
23
|
-
requirements.push('at least one uppercase letter');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (config.requireSpecialChar) {
|
|
27
|
-
requirements.push('at least one special character');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return `Password must contain ${requirements.join(', ')}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const convertSqlTypeToColumnType = (sqlType: string): ColumnType | string => {
|
|
34
|
-
switch (sqlType.toLowerCase()) {
|
|
35
|
-
case 'uuid':
|
|
36
|
-
return ColumnType.UUID;
|
|
37
|
-
case 'timestamptz':
|
|
38
|
-
case 'timestamp with time zone':
|
|
39
|
-
return ColumnType.DATETIME;
|
|
40
|
-
case 'date':
|
|
41
|
-
return ColumnType.DATE;
|
|
42
|
-
case 'integer':
|
|
43
|
-
case 'bigint':
|
|
44
|
-
case 'smallint':
|
|
45
|
-
case 'int':
|
|
46
|
-
case 'int2':
|
|
47
|
-
case 'int4':
|
|
48
|
-
case 'serial':
|
|
49
|
-
case 'serial2':
|
|
50
|
-
case 'serial4':
|
|
51
|
-
case 'serial8':
|
|
52
|
-
case 'smallserial':
|
|
53
|
-
case 'bigserial':
|
|
54
|
-
return ColumnType.INTEGER;
|
|
55
|
-
case 'double precision':
|
|
56
|
-
case 'real':
|
|
57
|
-
case 'numeric':
|
|
58
|
-
case 'float':
|
|
59
|
-
case 'float4':
|
|
60
|
-
case 'float8':
|
|
61
|
-
case 'decimal':
|
|
62
|
-
return ColumnType.FLOAT;
|
|
63
|
-
case 'boolean':
|
|
64
|
-
case 'bool':
|
|
65
|
-
return ColumnType.BOOLEAN;
|
|
66
|
-
case 'json':
|
|
67
|
-
case 'jsonb':
|
|
68
|
-
case 'array':
|
|
69
|
-
return ColumnType.JSON;
|
|
70
|
-
case 'text':
|
|
71
|
-
case 'varchar':
|
|
72
|
-
case 'char':
|
|
73
|
-
case 'character varying':
|
|
74
|
-
case 'character':
|
|
75
|
-
return ColumnType.STRING;
|
|
76
|
-
default:
|
|
77
|
-
return sqlType.slice(0,
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Generate a UUID v4
|
|
83
|
-
* @returns A UUID v4 string
|
|
84
|
-
*/
|
|
85
|
-
export function generateUUID(): string {
|
|
86
|
-
return crypto.randomUUID();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Generate a random numeric string of specified length
|
|
91
|
-
* @param length - The length of the numeric string to generate
|
|
92
|
-
* @returns A random string containing only digits (0-9)
|
|
93
|
-
*/
|
|
94
|
-
export function generateNumericCode(length: number): string {
|
|
95
|
-
// Generate each digit independently
|
|
96
|
-
let result = '';
|
|
97
|
-
for (let i = 0; i < length; i++) {
|
|
98
|
-
result += crypto.randomInt(0, 10).toString();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return result;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Generate a cryptographically secure random token
|
|
106
|
-
* @param bytes - Number of random bytes to generate (default: 32)
|
|
107
|
-
* @returns Hex-encoded string (length = bytes * 2 characters)
|
|
108
|
-
* @example
|
|
109
|
-
* generateSecureToken(32) // Returns 64-character hex string (256 bits entropy)
|
|
110
|
-
* generateSecureToken(16) // Returns 32-character hex string (128 bits entropy)
|
|
111
|
-
*/
|
|
112
|
-
export function generateSecureToken(bytes: number = 32): string {
|
|
113
|
-
return crypto.randomBytes(bytes).toString('hex');
|
|
114
|
-
}
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { ColumnType, type AuthConfigSchema } from '@insforge/shared-schemas';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates a user-friendly error message listing all password requirements
|
|
6
|
+
* @param config - Authentication configuration with password requirements
|
|
7
|
+
* @returns A formatted message listing all enabled password requirements
|
|
8
|
+
*/
|
|
9
|
+
export function getPasswordRequirementsMessage(config: AuthConfigSchema): string {
|
|
10
|
+
const requirements: string[] = [];
|
|
11
|
+
|
|
12
|
+
requirements.push(`at least ${config.passwordMinLength} characters long`);
|
|
13
|
+
|
|
14
|
+
if (config.requireNumber) {
|
|
15
|
+
requirements.push('at least one number');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (config.requireLowercase) {
|
|
19
|
+
requirements.push('at least one lowercase letter');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (config.requireUppercase) {
|
|
23
|
+
requirements.push('at least one uppercase letter');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (config.requireSpecialChar) {
|
|
27
|
+
requirements.push('at least one special character');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return `Password must contain ${requirements.join(', ')}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const convertSqlTypeToColumnType = (sqlType: string): ColumnType | string => {
|
|
34
|
+
switch (sqlType.toLowerCase()) {
|
|
35
|
+
case 'uuid':
|
|
36
|
+
return ColumnType.UUID;
|
|
37
|
+
case 'timestamptz':
|
|
38
|
+
case 'timestamp with time zone':
|
|
39
|
+
return ColumnType.DATETIME;
|
|
40
|
+
case 'date':
|
|
41
|
+
return ColumnType.DATE;
|
|
42
|
+
case 'integer':
|
|
43
|
+
case 'bigint':
|
|
44
|
+
case 'smallint':
|
|
45
|
+
case 'int':
|
|
46
|
+
case 'int2':
|
|
47
|
+
case 'int4':
|
|
48
|
+
case 'serial':
|
|
49
|
+
case 'serial2':
|
|
50
|
+
case 'serial4':
|
|
51
|
+
case 'serial8':
|
|
52
|
+
case 'smallserial':
|
|
53
|
+
case 'bigserial':
|
|
54
|
+
return ColumnType.INTEGER;
|
|
55
|
+
case 'double precision':
|
|
56
|
+
case 'real':
|
|
57
|
+
case 'numeric':
|
|
58
|
+
case 'float':
|
|
59
|
+
case 'float4':
|
|
60
|
+
case 'float8':
|
|
61
|
+
case 'decimal':
|
|
62
|
+
return ColumnType.FLOAT;
|
|
63
|
+
case 'boolean':
|
|
64
|
+
case 'bool':
|
|
65
|
+
return ColumnType.BOOLEAN;
|
|
66
|
+
case 'json':
|
|
67
|
+
case 'jsonb':
|
|
68
|
+
case 'array':
|
|
69
|
+
return ColumnType.JSON;
|
|
70
|
+
case 'text':
|
|
71
|
+
case 'varchar':
|
|
72
|
+
case 'char':
|
|
73
|
+
case 'character varying':
|
|
74
|
+
case 'character':
|
|
75
|
+
return ColumnType.STRING;
|
|
76
|
+
default:
|
|
77
|
+
return sqlType.slice(0, 8);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Generate a UUID v4
|
|
83
|
+
* @returns A UUID v4 string
|
|
84
|
+
*/
|
|
85
|
+
export function generateUUID(): string {
|
|
86
|
+
return crypto.randomUUID();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generate a random numeric string of specified length
|
|
91
|
+
* @param length - The length of the numeric string to generate
|
|
92
|
+
* @returns A random string containing only digits (0-9)
|
|
93
|
+
*/
|
|
94
|
+
export function generateNumericCode(length: number): string {
|
|
95
|
+
// Generate each digit independently
|
|
96
|
+
let result = '';
|
|
97
|
+
for (let i = 0; i < length; i++) {
|
|
98
|
+
result += crypto.randomInt(0, 10).toString();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Generate a cryptographically secure random token
|
|
106
|
+
* @param bytes - Number of random bytes to generate (default: 32)
|
|
107
|
+
* @returns Hex-encoded string (length = bytes * 2 characters)
|
|
108
|
+
* @example
|
|
109
|
+
* generateSecureToken(32) // Returns 64-character hex string (256 bits entropy)
|
|
110
|
+
* generateSecureToken(16) // Returns 32-character hex string (128 bits entropy)
|
|
111
|
+
*/
|
|
112
|
+
export function generateSecureToken(bytes: number = 32): string {
|
|
113
|
+
return crypto.randomBytes(bytes).toString('hex');
|
|
114
|
+
}
|
|
@@ -100,17 +100,17 @@ export function isValidIdentifier(identifier: string): boolean {
|
|
|
100
100
|
*/
|
|
101
101
|
export function validateTableName(tableName: string): boolean {
|
|
102
102
|
validateIdentifier(tableName, 'table');
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
103
105
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Validates PostgreSQL function name for RPC calls
|
|
108
|
+
* @param functionName - The function name to validate
|
|
109
|
+
* @returns true if valid
|
|
110
|
+
* @throws AppError if invalid
|
|
111
|
+
*/
|
|
112
|
+
export function validateFunctionName(functionName: string): boolean {
|
|
113
|
+
validateIdentifier(functionName, 'function');
|
|
114
114
|
return true;
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# RPC endpoint test script
|
|
4
|
+
|
|
5
|
+
# Get the directory where this script is located
|
|
6
|
+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
7
|
+
|
|
8
|
+
# Source the test configuration
|
|
9
|
+
source "$SCRIPT_DIR/../test-config.sh"
|
|
10
|
+
|
|
11
|
+
echo "๐งช Testing RPC endpoint..."
|
|
12
|
+
|
|
13
|
+
# Configuration
|
|
14
|
+
API_BASE="$TEST_API_BASE"
|
|
15
|
+
AUTH_TOKEN=""
|
|
16
|
+
TEST_FUNC="test_rpc_add_$(date +%s)"
|
|
17
|
+
|
|
18
|
+
# 1. Login to get token
|
|
19
|
+
echo "๐ Logging in to get authentication token..."
|
|
20
|
+
AUTH_TOKEN=$(get_admin_token)
|
|
21
|
+
|
|
22
|
+
if [ -n "$AUTH_TOKEN" ]; then
|
|
23
|
+
print_success "Login successful"
|
|
24
|
+
else
|
|
25
|
+
print_fail "Login failed"
|
|
26
|
+
echo "Please ensure the service is running and admin account exists"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# 2. Create test PostgreSQL function
|
|
31
|
+
echo ""
|
|
32
|
+
print_info "๐ Creating test RPC function ($TEST_FUNC)..."
|
|
33
|
+
|
|
34
|
+
create_func_response=$(curl -s -X POST "$API_BASE/database/advance/rawsql/unrestricted" \
|
|
35
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
36
|
+
-H "Content-Type: application/json" \
|
|
37
|
+
-d '{"query": "CREATE OR REPLACE FUNCTION '"$TEST_FUNC"'(a integer, b integer) RETURNS integer LANGUAGE sql AS $$ SELECT a + b; $$;"}')
|
|
38
|
+
|
|
39
|
+
if echo "$create_func_response" | grep -q '"error"'; then
|
|
40
|
+
print_fail "Failed to create test function"
|
|
41
|
+
echo "Response: $create_func_response"
|
|
42
|
+
exit 1
|
|
43
|
+
else
|
|
44
|
+
print_success "Test function created"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Wait for PostgREST schema cache to refresh
|
|
48
|
+
echo "โณ Waiting for schema cache refresh..."
|
|
49
|
+
sleep 3
|
|
50
|
+
|
|
51
|
+
# 3. Test RPC POST with JSON body
|
|
52
|
+
echo ""
|
|
53
|
+
print_info "๐ง Test 1: RPC POST with JSON body"
|
|
54
|
+
|
|
55
|
+
rpc_post_response=$(curl -s -w "\n%{http_code}" -X POST "$API_BASE/database/rpc/$TEST_FUNC" \
|
|
56
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
57
|
+
-H "Content-Type: application/json" \
|
|
58
|
+
-d '{"a": 5, "b": 3}')
|
|
59
|
+
|
|
60
|
+
body=$(echo "$rpc_post_response" | sed '$d')
|
|
61
|
+
status=$(echo "$rpc_post_response" | tail -n 1)
|
|
62
|
+
|
|
63
|
+
if [ "$status" -eq 200 ] && [ "$body" = "8" ]; then
|
|
64
|
+
print_success "RPC POST: 5 + 3 = $body"
|
|
65
|
+
else
|
|
66
|
+
print_fail "RPC POST failed (status: $status)"
|
|
67
|
+
echo "Expected: 8, Got: $body"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# 4. Test RPC GET with query params
|
|
71
|
+
echo ""
|
|
72
|
+
print_info "๐ง Test 2: RPC GET with query params"
|
|
73
|
+
|
|
74
|
+
rpc_get_response=$(curl -s -w "\n%{http_code}" "$API_BASE/database/rpc/$TEST_FUNC?a=10&b=20" \
|
|
75
|
+
-H "Authorization: Bearer $AUTH_TOKEN")
|
|
76
|
+
|
|
77
|
+
body=$(echo "$rpc_get_response" | sed '$d')
|
|
78
|
+
status=$(echo "$rpc_get_response" | tail -n 1)
|
|
79
|
+
|
|
80
|
+
if [ "$status" -eq 200 ] && [ "$body" = "30" ]; then
|
|
81
|
+
print_success "RPC GET: 10 + 20 = $body"
|
|
82
|
+
else
|
|
83
|
+
print_fail "RPC GET failed (status: $status)"
|
|
84
|
+
echo "Expected: 30, Got: $body"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# 5. Test RPC with non-existent function (should return 404)
|
|
88
|
+
echo ""
|
|
89
|
+
print_info "๐ง Test 3: RPC call to non-existent function (expect 404)"
|
|
90
|
+
|
|
91
|
+
rpc_404_response=$(curl -s -w "\n%{http_code}" -X POST "$API_BASE/database/rpc/nonexistent_function_xyz" \
|
|
92
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
93
|
+
-H "Content-Type: application/json" \
|
|
94
|
+
-d '{}')
|
|
95
|
+
|
|
96
|
+
body=$(echo "$rpc_404_response" | sed '$d')
|
|
97
|
+
status=$(echo "$rpc_404_response" | tail -n 1)
|
|
98
|
+
|
|
99
|
+
if [ "$status" -eq 404 ]; then
|
|
100
|
+
print_success "Non-existent function returns 404"
|
|
101
|
+
else
|
|
102
|
+
print_fail "Expected 404, got $status"
|
|
103
|
+
echo "Response: $body"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# 6. Test RPC with wrong parameter types
|
|
107
|
+
echo ""
|
|
108
|
+
print_info "๐ง Test 4: RPC with wrong parameter types (expect error)"
|
|
109
|
+
|
|
110
|
+
rpc_error_response=$(curl -s -w "\n%{http_code}" -X POST "$API_BASE/database/rpc/$TEST_FUNC" \
|
|
111
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
112
|
+
-H "Content-Type: application/json" \
|
|
113
|
+
-d '{"a": "not_a_number", "b": 3}')
|
|
114
|
+
|
|
115
|
+
body=$(echo "$rpc_error_response" | sed '$d')
|
|
116
|
+
status=$(echo "$rpc_error_response" | tail -n 1)
|
|
117
|
+
|
|
118
|
+
if [ "$status" -ge 400 ]; then
|
|
119
|
+
print_success "Invalid params return error ($status)"
|
|
120
|
+
else
|
|
121
|
+
print_fail "Expected error status, got $status"
|
|
122
|
+
echo "Response: $body"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# 7. Cleanup - drop the test function
|
|
126
|
+
echo ""
|
|
127
|
+
print_info "๐งน Cleaning up test function..."
|
|
128
|
+
|
|
129
|
+
cleanup_response=$(curl -s -X POST "$API_BASE/database/advance/rawsql/unrestricted" \
|
|
130
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
131
|
+
-H "Content-Type: application/json" \
|
|
132
|
+
-d '{"query": "DROP FUNCTION IF EXISTS '"$TEST_FUNC"'(integer, integer);"}')
|
|
133
|
+
|
|
134
|
+
if echo "$cleanup_response" | grep -q '"error"'; then
|
|
135
|
+
print_fail "Cleanup failed"
|
|
136
|
+
else
|
|
137
|
+
print_success "Test function dropped"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
echo ""
|
|
141
|
+
echo -e "${GREEN}๐ RPC endpoint tests completed!${NC}"
|