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
@@ -1,8 +1,10 @@
1
1
  import { DatabaseManager } from '@/infra/database/database.manager.js';
2
2
  import {
3
3
  EdgeFunctionMetadataSchema,
4
- FunctionUploadRequest,
5
- FunctionUpdateRequest,
4
+ UploadFunctionRequest,
5
+ UpdateFunctionRequest,
6
+ FunctionSchema,
7
+ ListFunctionsResponse,
6
8
  } from '@insforge/shared-schemas';
7
9
  import logger from '@/utils/logger.js';
8
10
  import { DatabaseError, Pool } from 'pg';
@@ -10,13 +12,6 @@ import fetch from 'node-fetch';
10
12
  import { AppError } from '@/api/middlewares/error.js';
11
13
  import { ERROR_CODES } from '@/types/error-constants.js';
12
14
 
13
- export interface FunctionWithRuntime {
14
- functions: Record<string, unknown>[];
15
- runtime: {
16
- status: 'running' | 'unavailable';
17
- };
18
- }
19
-
20
15
  export class FunctionService {
21
16
  private static instance: FunctionService;
22
17
  private pool: Pool | null = null;
@@ -41,13 +36,19 @@ export class FunctionService {
41
36
  /**
42
37
  * List all functions with runtime health check
43
38
  */
44
- async listFunctions(): Promise<FunctionWithRuntime> {
39
+ async listFunctions(): Promise<ListFunctionsResponse> {
45
40
  try {
46
41
  const result = await this.getPool().query(
47
42
  `SELECT
48
- id, slug, name, description, status,
49
- created_at, updated_at, deployed_at
50
- FROM _functions
43
+ id,
44
+ slug,
45
+ name,
46
+ description,
47
+ status,
48
+ created_at as "createdAt",
49
+ updated_at as "updatedAt",
50
+ deployed_at as "deployedAt"
51
+ FROM functions.definitions
51
52
  ORDER BY created_at DESC`
52
53
  );
53
54
 
@@ -86,13 +87,20 @@ export class FunctionService {
86
87
  /**
87
88
  * Get a specific function by slug
88
89
  */
89
- async getFunction(slug: string): Promise<Record<string, unknown> | undefined> {
90
+ async getFunction(slug: string): Promise<FunctionSchema | undefined> {
90
91
  try {
91
92
  const result = await this.getPool().query(
92
93
  `SELECT
93
- id, slug, name, description, code, status,
94
- created_at, updated_at, deployed_at
95
- FROM _functions
94
+ id,
95
+ slug,
96
+ name,
97
+ description,
98
+ code,
99
+ status,
100
+ created_at as "createdAt",
101
+ updated_at as "updatedAt",
102
+ deployed_at as "deployedAt"
103
+ FROM functions.definitions
96
104
  WHERE slug = $1`,
97
105
  [slug]
98
106
  );
@@ -111,7 +119,7 @@ export class FunctionService {
111
119
  /**
112
120
  * Create a new function
113
121
  */
114
- async createFunction(data: FunctionUploadRequest): Promise<Record<string, unknown>> {
122
+ async createFunction(data: UploadFunctionRequest): Promise<FunctionSchema> {
115
123
  const client = await this.getPool().connect();
116
124
  try {
117
125
  const { name, code, description, status } = data;
@@ -125,22 +133,23 @@ export class FunctionService {
125
133
 
126
134
  // Insert function
127
135
  await client.query(
128
- `INSERT INTO _functions (id, slug, name, description, code, status)
136
+ `INSERT INTO functions.definitions (id, slug, name, description, code, status)
129
137
  VALUES ($1, $2, $3, $4, $5, $6)`,
130
138
  [id, slug, name, description || null, code, status]
131
139
  );
132
140
 
133
141
  // If status is active, update deployed_at
134
142
  if (status === 'active') {
135
- await client.query(`UPDATE _functions SET deployed_at = CURRENT_TIMESTAMP WHERE id = $1`, [
136
- id,
137
- ]);
143
+ await client.query(
144
+ `UPDATE functions.definitions SET deployed_at = CURRENT_TIMESTAMP WHERE id = $1`,
145
+ [id]
146
+ );
138
147
  }
139
148
 
140
149
  // Fetch the created function
141
150
  const result = await client.query(
142
- `SELECT id, slug, name, description, status, created_at
143
- FROM _functions WHERE id = $1`,
151
+ `SELECT id, slug, name, description, status, created_at as "createdAt"
152
+ FROM functions.definitions WHERE id = $1`,
144
153
  [id]
145
154
  );
146
155
 
@@ -176,14 +185,15 @@ export class FunctionService {
176
185
  */
177
186
  async updateFunction(
178
187
  slug: string,
179
- updates: FunctionUpdateRequest
180
- ): Promise<Record<string, unknown> | null> {
188
+ updates: UpdateFunctionRequest
189
+ ): Promise<FunctionSchema | null> {
181
190
  const client = await this.getPool().connect();
182
191
  try {
183
192
  // Check if function exists
184
- const existingResult = await client.query('SELECT id FROM _functions WHERE slug = $1', [
185
- slug,
186
- ]);
193
+ const existingResult = await client.query(
194
+ 'SELECT id FROM functions.definitions WHERE slug = $1',
195
+ [slug]
196
+ );
187
197
  if (existingResult.rows.length === 0) {
188
198
  return null;
189
199
  }
@@ -195,22 +205,28 @@ export class FunctionService {
195
205
 
196
206
  // Update fields
197
207
  if (updates.name !== undefined) {
198
- await client.query('UPDATE _functions SET name = $1 WHERE slug = $2', [updates.name, slug]);
208
+ await client.query('UPDATE functions.definitions SET name = $1 WHERE slug = $2', [
209
+ updates.name,
210
+ slug,
211
+ ]);
199
212
  }
200
213
 
201
214
  if (updates.description !== undefined) {
202
- await client.query('UPDATE _functions SET description = $1 WHERE slug = $2', [
215
+ await client.query('UPDATE functions.definitions SET description = $1 WHERE slug = $2', [
203
216
  updates.description,
204
217
  slug,
205
218
  ]);
206
219
  }
207
220
 
208
221
  if (updates.code !== undefined) {
209
- await client.query('UPDATE _functions SET code = $1 WHERE slug = $2', [updates.code, slug]);
222
+ await client.query('UPDATE functions.definitions SET code = $1 WHERE slug = $2', [
223
+ updates.code,
224
+ slug,
225
+ ]);
210
226
  }
211
227
 
212
228
  if (updates.status !== undefined) {
213
- await client.query('UPDATE _functions SET status = $1 WHERE slug = $2', [
229
+ await client.query('UPDATE functions.definitions SET status = $1 WHERE slug = $2', [
214
230
  updates.status,
215
231
  slug,
216
232
  ]);
@@ -218,21 +234,22 @@ export class FunctionService {
218
234
  // Update deployed_at if status changes to active
219
235
  if (updates.status === 'active') {
220
236
  await client.query(
221
- 'UPDATE _functions SET deployed_at = CURRENT_TIMESTAMP WHERE slug = $1',
237
+ 'UPDATE functions.definitions SET deployed_at = CURRENT_TIMESTAMP WHERE slug = $1',
222
238
  [slug]
223
239
  );
224
240
  }
225
241
  }
226
242
 
227
243
  // Update updated_at
228
- await client.query('UPDATE _functions SET updated_at = CURRENT_TIMESTAMP WHERE slug = $1', [
229
- slug,
230
- ]);
244
+ await client.query(
245
+ 'UPDATE functions.definitions SET updated_at = CURRENT_TIMESTAMP WHERE slug = $1',
246
+ [slug]
247
+ );
231
248
 
232
249
  // Fetch updated function
233
250
  const result = await client.query(
234
- `SELECT id, slug, name, description, status, updated_at
235
- FROM _functions WHERE slug = $1`,
251
+ `SELECT id, slug, name, description, status, updated_at as "updatedAt", deployed_at as "deployedAt"
252
+ FROM functions.definitions WHERE slug = $1`,
236
253
  [slug]
237
254
  );
238
255
 
@@ -254,7 +271,10 @@ export class FunctionService {
254
271
  */
255
272
  async deleteFunction(slug: string): Promise<boolean> {
256
273
  try {
257
- const result = await this.getPool().query('DELETE FROM _functions WHERE slug = $1', [slug]);
274
+ const result = await this.getPool().query(
275
+ 'DELETE FROM functions.definitions WHERE slug = $1',
276
+ [slug]
277
+ );
258
278
 
259
279
  if (result.rowCount === 0) {
260
280
  return false;
@@ -278,7 +298,7 @@ export class FunctionService {
278
298
  try {
279
299
  const result = await this.getPool().query(
280
300
  `SELECT slug, name, description, status
281
- FROM _functions
301
+ FROM functions.definitions
282
302
  ORDER BY created_at DESC`
283
303
  );
284
304
 
@@ -35,7 +35,7 @@ export class AuditService {
35
35
  try {
36
36
  const pool = this.getPool();
37
37
  const result = await pool.query(
38
- `INSERT INTO _audit_logs (actor, action, module, details, ip_address)
38
+ `INSERT INTO system.audit_logs (actor, action, module, details, ip_address)
39
39
  VALUES ($1, $2, $3, $4, $5)
40
40
  RETURNING *`,
41
41
  [
@@ -109,12 +109,12 @@ export class AuditService {
109
109
  }
110
110
 
111
111
  // Get total count first
112
- const countSql = `SELECT COUNT(*) as count FROM _audit_logs ${whereClause}`;
112
+ const countSql = `SELECT COUNT(*) as count FROM system.audit_logs ${whereClause}`;
113
113
  const countResult = await pool.query(countSql, params);
114
114
  const total = parseInt(countResult.rows[0].count, 10);
115
115
 
116
116
  // Get paginated records
117
- let dataSql = `SELECT * FROM _audit_logs ${whereClause} ORDER BY created_at DESC`;
117
+ let dataSql = `SELECT * FROM system.audit_logs ${whereClause} ORDER BY created_at DESC`;
118
118
  const dataParams = [...params];
119
119
 
120
120
  if (query.limit) {
@@ -154,7 +154,7 @@ export class AuditService {
154
154
  async getById(id: string): Promise<AuditLogSchema | null> {
155
155
  try {
156
156
  const pool = this.getPool();
157
- const result = await pool.query('SELECT * FROM _audit_logs WHERE id = $1', [id]);
157
+ const result = await pool.query('SELECT * FROM system.audit_logs WHERE id = $1', [id]);
158
158
 
159
159
  const row = result.rows[0];
160
160
 
@@ -186,30 +186,30 @@ export class AuditService {
186
186
  startDate.setDate(startDate.getDate() - days);
187
187
 
188
188
  const totalLogsResult = await pool.query(
189
- 'SELECT COUNT(*) as count FROM _audit_logs WHERE created_at >= $1',
189
+ 'SELECT COUNT(*) as count FROM system.audit_logs WHERE created_at >= $1',
190
190
  [startDate.toISOString()]
191
191
  );
192
192
 
193
193
  const uniqueActorsResult = await pool.query(
194
- 'SELECT COUNT(DISTINCT actor) as count FROM _audit_logs WHERE created_at >= $1',
194
+ 'SELECT COUNT(DISTINCT actor) as count FROM system.audit_logs WHERE created_at >= $1',
195
195
  [startDate.toISOString()]
196
196
  );
197
197
 
198
198
  const uniqueModulesResult = await pool.query(
199
- 'SELECT COUNT(DISTINCT module) as count FROM _audit_logs WHERE created_at >= $1',
199
+ 'SELECT COUNT(DISTINCT module) as count FROM system.audit_logs WHERE created_at >= $1',
200
200
  [startDate.toISOString()]
201
201
  );
202
202
 
203
203
  const actionsByModuleResult = await pool.query(
204
204
  `SELECT module, COUNT(*) as count
205
- FROM _audit_logs
205
+ FROM system.audit_logs
206
206
  WHERE created_at >= $1
207
207
  GROUP BY module`,
208
208
  [startDate.toISOString()]
209
209
  );
210
210
 
211
211
  const recentActivityResult = await pool.query(
212
- `SELECT * FROM _audit_logs
212
+ `SELECT * FROM system.audit_logs
213
213
  WHERE created_at >= $1
214
214
  ORDER BY created_at DESC
215
215
  LIMIT 10`,
@@ -253,7 +253,7 @@ export class AuditService {
253
253
  cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
254
254
 
255
255
  const result = await pool.query(
256
- 'DELETE FROM _audit_logs WHERE created_at < $1 RETURNING id',
256
+ 'DELETE FROM system.audit_logs WHERE created_at < $1 RETURNING id',
257
257
  [cutoffDate.toISOString()]
258
258
  );
259
259
 
@@ -3,21 +3,9 @@ import crypto from 'crypto';
3
3
  import { DatabaseManager } from '@/infra/database/database.manager.js';
4
4
  import logger from '@/utils/logger.js';
5
5
  import { EncryptionManager } from '@/infra/security/encryption.manager.js';
6
+ import { SecretSchema, CreateSecretRequest } from '@insforge/shared-schemas';
6
7
 
7
- export interface SecretSchema {
8
- id: string;
9
- key: string;
10
- isActive: boolean;
11
- isReserved: boolean;
12
- lastUsedAt: Date | null;
13
- expiresAt: Date | null;
14
- createdAt: Date;
15
- updatedAt: Date;
16
- }
17
-
18
- export interface CreateSecretInput {
19
- key: string;
20
- value: string;
8
+ export interface CreateSecretInput extends CreateSecretRequest {
21
9
  isReserved?: boolean;
22
10
  expiresAt?: Date;
23
11
  }
@@ -59,7 +47,7 @@ export class SecretService {
59
47
  const encryptedValue = EncryptionManager.encrypt(input.value);
60
48
 
61
49
  const result = await this.getPool().query(
62
- `INSERT INTO _secrets (key, value_ciphertext, is_reserved, expires_at)
50
+ `INSERT INTO system.secrets (key, value_ciphertext, is_reserved, expires_at)
63
51
  VALUES ($1, $2, $3, $4)
64
52
  RETURNING id`,
65
53
  [input.key, encryptedValue, input.isReserved || false, input.expiresAt || null]
@@ -79,7 +67,7 @@ export class SecretService {
79
67
  async getSecretById(id: string): Promise<string | null> {
80
68
  try {
81
69
  const result = await this.getPool().query(
82
- `UPDATE _secrets
70
+ `UPDATE system.secrets
83
71
  SET last_used_at = NOW()
84
72
  WHERE id = $1 AND is_active = true
85
73
  AND (expires_at IS NULL OR expires_at > NOW())
@@ -106,7 +94,7 @@ export class SecretService {
106
94
  async getSecretByKey(key: string): Promise<string | null> {
107
95
  try {
108
96
  const result = await this.getPool().query(
109
- `UPDATE _secrets
97
+ `UPDATE system.secrets
110
98
  SET last_used_at = NOW()
111
99
  WHERE key = $1 AND is_active = true
112
100
  AND (expires_at IS NULL OR expires_at > NOW())
@@ -142,7 +130,7 @@ export class SecretService {
142
130
  expires_at as "expiresAt",
143
131
  created_at as "createdAt",
144
132
  updated_at as "updatedAt"
145
- FROM _secrets
133
+ FROM system.secrets
146
134
  ORDER BY created_at DESC`
147
135
  );
148
136
 
@@ -183,10 +171,14 @@ export class SecretService {
183
171
  values.push(input.expiresAt);
184
172
  }
185
173
 
174
+ if (updates.length === 0) {
175
+ return false;
176
+ }
177
+
186
178
  values.push(id);
187
179
 
188
180
  const result = await this.getPool().query(
189
- `UPDATE _secrets
181
+ `UPDATE system.secrets
190
182
  SET ${updates.join(', ')}
191
183
  WHERE id = $${paramCount}`,
192
184
  values
@@ -203,6 +195,81 @@ export class SecretService {
203
195
  }
204
196
  }
205
197
 
198
+ /**
199
+ * Update a secret by key
200
+ */
201
+ async updateSecretByKey(key: string, input: UpdateSecretInput): Promise<boolean> {
202
+ try {
203
+ const updates: string[] = [];
204
+ const values: (string | boolean | Date | null)[] = [];
205
+ let paramCount = 1;
206
+
207
+ if (input.value !== undefined) {
208
+ const encryptedValue = EncryptionManager.encrypt(input.value);
209
+ updates.push(`value_ciphertext = $${paramCount++}`);
210
+ values.push(encryptedValue);
211
+ }
212
+
213
+ if (input.isActive !== undefined) {
214
+ updates.push(`is_active = $${paramCount++}`);
215
+ values.push(input.isActive);
216
+ }
217
+
218
+ if (input.isReserved !== undefined) {
219
+ updates.push(`is_reserved = $${paramCount++}`);
220
+ values.push(input.isReserved);
221
+ }
222
+
223
+ if (input.expiresAt !== undefined) {
224
+ updates.push(`expires_at = $${paramCount++}`);
225
+ values.push(input.expiresAt);
226
+ }
227
+
228
+ if (updates.length === 0) {
229
+ return false;
230
+ }
231
+
232
+ values.push(key);
233
+
234
+ const result = await this.getPool().query(
235
+ `UPDATE system.secrets
236
+ SET ${updates.join(', ')}
237
+ WHERE key = $${paramCount}`,
238
+ values
239
+ );
240
+
241
+ const success = (result.rowCount ?? 0) > 0;
242
+ if (success) {
243
+ logger.info('Secret updated by key', { key });
244
+ }
245
+ return success;
246
+ } catch (error) {
247
+ logger.error('Failed to update secret by key', { error, key });
248
+ throw new Error('Failed to update secret');
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Delete a secret by key
254
+ */
255
+ async deleteSecretByKey(key: string): Promise<boolean> {
256
+ try {
257
+ const result = await this.getPool().query(
258
+ 'DELETE FROM system.secrets WHERE key = $1 AND is_reserved = false',
259
+ [key]
260
+ );
261
+
262
+ const success = (result.rowCount ?? 0) > 0;
263
+ if (success) {
264
+ logger.info('Secret deleted by key', { key });
265
+ }
266
+ return success;
267
+ } catch (error) {
268
+ logger.error('Failed to delete secret by key', { error, key });
269
+ throw new Error('Failed to delete secret');
270
+ }
271
+ }
272
+
206
273
  /**
207
274
  * Check if a secret value matches the stored value
208
275
  */
@@ -210,7 +277,7 @@ export class SecretService {
210
277
  try {
211
278
  // Optimized: Single query that retrieves and updates in one operation
212
279
  const result = await this.getPool().query(
213
- `UPDATE _secrets
280
+ `UPDATE system.secrets
214
281
  SET last_used_at = NOW()
215
282
  WHERE key = $1
216
283
  AND is_active = true
@@ -225,7 +292,12 @@ export class SecretService {
225
292
  }
226
293
 
227
294
  const decryptedValue = EncryptionManager.decrypt(result.rows[0].value_ciphertext);
228
- const matches = decryptedValue === value;
295
+ // Use constant-time comparison to prevent timing attacks
296
+ const decryptedBuffer = Buffer.from(decryptedValue);
297
+ const valueBuffer = Buffer.from(value);
298
+ const matches =
299
+ decryptedBuffer.length === valueBuffer.length &&
300
+ crypto.timingSafeEqual(decryptedBuffer, valueBuffer);
229
301
 
230
302
  if (matches) {
231
303
  logger.info('Secret check successful', { key });
@@ -247,7 +319,7 @@ export class SecretService {
247
319
  try {
248
320
  // Optimized: Single query with WHERE clause to prevent deleting reserved secrets
249
321
  const result = await this.getPool().query(
250
- 'DELETE FROM _secrets WHERE id = $1 AND is_reserved = false',
322
+ 'DELETE FROM system.secrets WHERE id = $1 AND is_reserved = false',
251
323
  [id]
252
324
  );
253
325
 
@@ -257,7 +329,7 @@ export class SecretService {
257
329
  } else {
258
330
  // Check if it exists but is reserved
259
331
  const checkResult = await this.getPool().query(
260
- 'SELECT is_reserved FROM _secrets WHERE id = $1',
332
+ 'SELECT is_reserved FROM system.secrets WHERE id = $1',
261
333
  [id]
262
334
  );
263
335
  if (checkResult.rows.length && checkResult.rows[0].is_reserved) {
@@ -279,7 +351,9 @@ export class SecretService {
279
351
  try {
280
352
  await client.query('BEGIN');
281
353
 
282
- const oldSecretResult = await client.query(`SELECT key FROM _secrets WHERE id = $1`, [id]);
354
+ const oldSecretResult = await client.query(`SELECT key FROM system.secrets WHERE id = $1`, [
355
+ id,
356
+ ]);
283
357
 
284
358
  if (!oldSecretResult.rows.length) {
285
359
  throw new Error('Secret not found');
@@ -288,7 +362,7 @@ export class SecretService {
288
362
  const secretKey = oldSecretResult.rows[0].key;
289
363
 
290
364
  await client.query(
291
- `UPDATE _secrets
365
+ `UPDATE system.secrets
292
366
  SET is_active = false,
293
367
  expires_at = NOW() + INTERVAL '24 hours'
294
368
  WHERE id = $1`,
@@ -297,7 +371,7 @@ export class SecretService {
297
371
 
298
372
  const encryptedValue = EncryptionManager.encrypt(newValue);
299
373
  const newSecretResult = await client.query(
300
- `INSERT INTO _secrets (key, value_ciphertext)
374
+ `INSERT INTO system.secrets (key, value_ciphertext)
301
375
  VALUES ($1, $2)
302
376
  RETURNING id`,
303
377
  [secretKey, encryptedValue]
@@ -327,7 +401,7 @@ export class SecretService {
327
401
  async cleanupExpiredSecrets(): Promise<number> {
328
402
  try {
329
403
  const result = await this.getPool().query(
330
- `DELETE FROM _secrets
404
+ `DELETE FROM system.secrets
331
405
  WHERE expires_at IS NOT NULL
332
406
  AND expires_at < NOW()
333
407
  RETURNING id`