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.
Files changed (269) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/auth/package.json +5 -3
  3. package/auth/src/lib/broadcastService.ts +115 -117
  4. package/auth/src/lib/insforge.ts +8 -0
  5. package/auth/src/main.tsx +2 -4
  6. package/auth/src/pages/SignInPage.tsx +60 -60
  7. package/auth/src/pages/SignUpPage.tsx +60 -60
  8. package/auth/src/pages/VerifyEmailPage.tsx +18 -0
  9. package/auth/tsconfig.json +2 -1
  10. package/backend/package.json +10 -6
  11. package/backend/src/api/middlewares/rate-limiters.ts +127 -127
  12. package/backend/src/api/routes/ai/index.routes.ts +475 -468
  13. package/backend/src/api/routes/auth/index.routes.ts +85 -32
  14. package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
  15. package/backend/src/api/routes/database/index.routes.ts +2 -0
  16. package/backend/src/api/routes/database/records.routes.ts +39 -175
  17. package/backend/src/api/routes/database/rpc.routes.ts +69 -0
  18. package/backend/src/api/routes/deployments/index.routes.ts +192 -0
  19. package/backend/src/api/routes/docs/index.routes.ts +3 -2
  20. package/backend/src/api/routes/email/index.routes.ts +35 -35
  21. package/backend/src/api/routes/functions/index.routes.ts +3 -3
  22. package/backend/src/api/routes/metadata/index.routes.ts +26 -0
  23. package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
  24. package/backend/src/infra/database/database.manager.ts +0 -10
  25. package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
  26. package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
  27. package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
  28. package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
  29. package/backend/src/infra/security/token.manager.ts +1 -4
  30. package/backend/src/providers/ai/openrouter.provider.ts +12 -3
  31. package/backend/src/providers/database/base.provider.ts +39 -0
  32. package/backend/src/providers/database/cloud.provider.ts +159 -0
  33. package/backend/src/providers/deployments/vercel.provider.ts +516 -0
  34. package/backend/src/server.ts +19 -7
  35. package/backend/src/services/ai/ai-config.service.ts +6 -6
  36. package/backend/src/services/ai/ai-model.service.ts +60 -60
  37. package/backend/src/services/ai/ai-usage.service.ts +7 -7
  38. package/backend/src/services/ai/chat-completion.service.ts +415 -220
  39. package/backend/src/services/ai/helpers.ts +64 -64
  40. package/backend/src/services/ai/index.ts +13 -13
  41. package/backend/src/services/auth/auth-config.service.ts +4 -4
  42. package/backend/src/services/auth/auth-otp.service.ts +6 -6
  43. package/backend/src/services/auth/auth.service.ts +134 -74
  44. package/backend/src/services/auth/index.ts +4 -4
  45. package/backend/src/services/auth/oauth-config.service.ts +12 -12
  46. package/backend/src/services/database/database-advance.service.ts +19 -55
  47. package/backend/src/services/database/database-table.service.ts +38 -85
  48. package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
  49. package/backend/src/services/deployments/deployment.service.ts +693 -0
  50. package/backend/src/services/functions/function.service.ts +61 -41
  51. package/backend/src/services/logs/audit.service.ts +10 -10
  52. package/backend/src/services/secrets/secret.service.ts +101 -27
  53. package/backend/src/services/storage/storage.service.ts +30 -30
  54. package/backend/src/services/usage/usage.service.ts +6 -6
  55. package/backend/src/types/ai.ts +8 -0
  56. package/backend/src/types/auth.ts +5 -1
  57. package/backend/src/types/database.ts +2 -0
  58. package/backend/src/types/deployments.ts +33 -0
  59. package/backend/src/types/storage.ts +1 -1
  60. package/backend/src/types/webhooks.ts +45 -0
  61. package/backend/src/utils/cookies.ts +34 -35
  62. package/backend/src/utils/environment.ts +0 -14
  63. package/backend/src/utils/s3-config-loader.ts +64 -64
  64. package/backend/src/utils/seed.ts +334 -301
  65. package/backend/src/utils/sql-parser.ts +126 -0
  66. package/backend/src/utils/utils.ts +114 -114
  67. package/backend/src/utils/validations.ts +10 -10
  68. package/backend/tests/local/test-rpc.sh +141 -0
  69. package/backend/tests/local/test-secrets.sh +1 -1
  70. package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
  71. package/backend/tests/manual/test-rawsql-modes.sh +24 -24
  72. package/backend/tests/unit/database-advance.test.ts +326 -0
  73. package/backend/tests/unit/helpers.test.ts +2 -2
  74. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
  75. package/docker-compose.prod.yml +1 -1
  76. package/docker-compose.yml +1 -1
  77. package/docs/agent-docs/deployment.md +79 -0
  78. package/docs/changelog.mdx +165 -72
  79. package/docs/core-concepts/ai/architecture.mdx +1 -23
  80. package/docs/core-concepts/ai/sdk.mdx +26 -1
  81. package/docs/core-concepts/authentication/architecture.mdx +6 -8
  82. package/docs/core-concepts/authentication/sdk.mdx +387 -91
  83. package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
  84. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
  85. package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
  86. package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
  87. package/docs/core-concepts/database/architecture.mdx +58 -21
  88. package/docs/core-concepts/database/pgvector.mdx +138 -0
  89. package/docs/core-concepts/database/sdk.mdx +17 -17
  90. package/docs/core-concepts/deployments/architecture.mdx +152 -0
  91. package/docs/core-concepts/email/architecture.mdx +4 -2
  92. package/docs/core-concepts/functions/architecture.mdx +1 -1
  93. package/docs/core-concepts/functions/sdk.mdx +0 -1
  94. package/docs/core-concepts/realtime/architecture.mdx +1 -1
  95. package/docs/core-concepts/storage/architecture.mdx +1 -1
  96. package/docs/core-concepts/storage/sdk.mdx +25 -25
  97. package/docs/docs.json +14 -6
  98. package/docs/favicon.png +0 -0
  99. package/docs/favicon.svg +3 -18
  100. package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
  101. package/docs/images/changelog/dec-2025/moreModels.png +0 -0
  102. package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
  103. package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
  104. package/docs/images/changelog/dec-2025/realtime2.png +0 -0
  105. package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
  106. package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
  107. package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
  108. package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
  109. package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
  110. package/docs/images/mcp-setup/claude-code-connect.png +0 -0
  111. package/docs/images/mcp-setup/cline-1.png +0 -0
  112. package/docs/images/mcp-setup/cline-2.png +0 -0
  113. package/docs/images/mcp-setup/cline-3.png +0 -0
  114. package/docs/images/mcp-setup/connect-project.png +0 -0
  115. package/docs/images/mcp-setup/copilot-1.png +0 -0
  116. package/docs/images/mcp-setup/copilot-2.png +0 -0
  117. package/docs/images/mcp-setup/copilot-3.png +0 -0
  118. package/docs/images/mcp-setup/mcp-json-1.png +0 -0
  119. package/docs/images/mcp-setup/mcp-json-2.png +0 -0
  120. package/docs/images/mcp-setup/qoder-1.png +0 -0
  121. package/docs/images/mcp-setup/qoder-2.png +0 -0
  122. package/docs/images/mcp-setup/roocode-1.png +0 -0
  123. package/docs/images/mcp-setup/roocode-2.png +0 -0
  124. package/docs/images/mcp-setup/trae-1.png +0 -0
  125. package/docs/images/mcp-setup/trae-2.png +0 -0
  126. package/docs/images/mcp-setup/trae-3.png +0 -0
  127. package/docs/images/mcp-setup/trae-4.png +0 -0
  128. package/docs/images/mcp-setup/trae-5.png +0 -0
  129. package/docs/images/mcp-setup/windsurf-1.png +0 -0
  130. package/docs/images/mcp-setup/windsurf-2.png +0 -0
  131. package/docs/insforge-instructions-sdk.md +7 -3
  132. package/docs/introduction.mdx +9 -8
  133. package/docs/mcp-setup.mdx +332 -0
  134. package/docs/oauth-server.mdx +563 -0
  135. package/docs/partnership.mdx +79 -10
  136. package/docs/quickstart.mdx +1 -1
  137. package/docs/vscode-extension.mdx +74 -0
  138. package/eslint.config.js +1 -0
  139. package/examples/response-examples.md +1 -1
  140. package/frontend/package.json +1 -1
  141. package/frontend/src/App.tsx +8 -3
  142. package/frontend/src/assets/logos/antigravity.svg +1 -0
  143. package/frontend/src/assets/logos/copilot.svg +10 -0
  144. package/frontend/src/assets/logos/deepseek.svg +139 -0
  145. package/frontend/src/assets/logos/kiro.svg +9 -0
  146. package/frontend/src/assets/logos/qoder.svg +4 -0
  147. package/frontend/src/assets/logos/qwen.svg +15 -0
  148. package/frontend/src/components/CodeBlock.tsx +2 -2
  149. package/frontend/src/components/ConnectCTA.tsx +3 -2
  150. package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
  151. package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
  152. package/frontend/src/components/datagrid/index.ts +1 -1
  153. package/frontend/src/components/index.ts +0 -1
  154. package/frontend/src/components/layout/AppHeader.tsx +4 -27
  155. package/frontend/src/components/layout/AppSidebar.tsx +85 -100
  156. package/frontend/src/components/layout/Layout.tsx +34 -32
  157. package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
  158. package/frontend/src/components/radix/Select.tsx +151 -151
  159. package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
  160. package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
  161. package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
  162. package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
  163. package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
  164. package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
  165. package/frontend/src/features/ai/components/index.ts +6 -6
  166. package/frontend/src/features/ai/helpers.ts +147 -141
  167. package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
  168. package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
  169. package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
  170. package/frontend/src/features/auth/components/index.ts +5 -5
  171. package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
  172. package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
  173. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
  174. package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
  175. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
  176. package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
  177. package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
  178. package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
  179. package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
  180. package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
  181. package/frontend/src/features/database/constants.ts +16 -28
  182. package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
  183. package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
  184. package/frontend/src/features/database/hooks/useTables.ts +5 -7
  185. package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
  186. package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
  187. package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
  188. package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
  189. package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
  190. package/frontend/src/features/database/services/advance.service.ts +1 -15
  191. package/frontend/src/features/database/services/record.service.ts +4 -20
  192. package/frontend/src/features/database/services/table.service.ts +1 -4
  193. package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
  194. package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
  195. package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
  196. package/frontend/src/features/database/templates/notion-clone.ts +8 -8
  197. package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
  198. package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
  199. package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
  200. package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
  201. package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
  202. package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
  203. package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
  204. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
  205. package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
  206. package/frontend/src/features/functions/components/index.ts +5 -5
  207. package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
  208. package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
  209. package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
  210. package/frontend/src/features/functions/services/function.service.ts +8 -25
  211. package/frontend/src/features/functions/services/secret.service.ts +23 -41
  212. package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
  213. package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
  214. package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
  215. package/frontend/src/features/logs/components/index.ts +1 -0
  216. package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
  217. package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
  218. package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
  219. package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
  220. package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
  221. package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
  222. package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
  223. package/frontend/src/features/onboard/components/index.ts +9 -4
  224. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
  225. package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
  226. package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
  227. package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
  228. package/frontend/src/features/onboard/index.ts +17 -13
  229. package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
  230. package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
  231. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
  232. package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
  233. package/frontend/src/index.css +249 -249
  234. package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
  235. package/frontend/src/lib/hooks/useMetadata.ts +45 -1
  236. package/frontend/src/lib/hooks/useModal.tsx +2 -0
  237. package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
  238. package/frontend/src/lib/services/metadata.service.ts +20 -3
  239. package/frontend/src/lib/utils/menuItems.ts +223 -207
  240. package/frontend/src/lib/utils/utils.ts +196 -196
  241. package/functions/server.ts +315 -315
  242. package/functions/worker-template.js +1 -1
  243. package/openapi/ai.yaml +115 -5
  244. package/openapi/auth.yaml +97 -17
  245. package/openapi/logs.yaml +0 -2
  246. package/openapi/metadata.yaml +0 -2
  247. package/openapi/records.yaml +21 -21
  248. package/openapi/tables.yaml +1 -2
  249. package/package.json +1 -1
  250. package/shared-schemas/package.json +1 -1
  251. package/shared-schemas/src/ai-api.schema.ts +251 -143
  252. package/shared-schemas/src/ai.schema.ts +63 -63
  253. package/shared-schemas/src/auth-api.schema.ts +34 -6
  254. package/shared-schemas/src/auth.schema.ts +17 -10
  255. package/shared-schemas/src/cloud-events.schema.ts +26 -0
  256. package/shared-schemas/src/deployments-api.schema.ts +55 -0
  257. package/shared-schemas/src/deployments.schema.ts +30 -0
  258. package/shared-schemas/src/docs.schema.ts +8 -2
  259. package/shared-schemas/src/email-api.schema.ts +30 -30
  260. package/shared-schemas/src/functions-api.schema.ts +13 -4
  261. package/shared-schemas/src/functions.schema.ts +1 -1
  262. package/shared-schemas/src/index.ts +22 -18
  263. package/shared-schemas/src/metadata.schema.ts +30 -4
  264. package/shared-schemas/src/secrets-api.schema.ts +44 -0
  265. package/shared-schemas/src/secrets.schema.ts +15 -0
  266. package/zeabur/README.md +13 -0
  267. package/zeabur/template.yml +20 -51
  268. package/backend/src/types/profile.ts +0 -55
  269. 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, 5);
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
- // Prevent access to all other system tables (starting with _)
105
- if (tableName.startsWith('_')) {
106
- throw new AppError(
107
- 'Access to system tables is not allowed',
108
- 403,
109
- ERROR_CODES.FORBIDDEN,
110
- 'System tables (starting with _) cannot be accessed directly'
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}"
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
 
3
- # Test secrets API endpoints (refactored to use _secrets table)
3
+ # Test secrets API endpoints (uses system.secrets table)
4
4
  # Tests CRUD operations for secrets and edge function integration
5
5
 
6
6
  # Get the directory where this script is located