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
@@ -0,0 +1,441 @@
1
+ -- Migration: 018 - Create system and auth schemas, move and rename system tables
2
+ -- This migration creates dedicated schemas for internal system and auth tables,
3
+ -- moves tables into them, and removes the underscore prefix from table names.
4
+
5
+ -- ============================================================================
6
+ -- PART 1: SYSTEM SCHEMA (secrets, audit_logs, mcp_usage)
7
+ -- Note: migrations table is handled by bootstrap/bootstrap-migrations.js before this runs
8
+ -- ============================================================================
9
+
10
+ -- 1.1 Create the system schema (may already exist from bootstrap)
11
+ CREATE SCHEMA IF NOT EXISTS system;
12
+
13
+ -- 1.2 Move _secrets table to system schema and rename to 'secrets'
14
+ -- First, drop the foreign key constraint from _oauth_configs that references _secrets
15
+ ALTER TABLE public._oauth_configs
16
+ DROP CONSTRAINT IF EXISTS _oauth_configs_secret_id_fkey;
17
+
18
+ -- Move and rename the _secrets table to system.secrets
19
+ ALTER TABLE public._secrets SET SCHEMA system;
20
+ ALTER TABLE system._secrets RENAME TO secrets;
21
+
22
+ -- Note: We'll recreate the FK constraint after moving _oauth_configs to auth schema
23
+
24
+ -- 1.3 Move _audit_logs table to system schema and rename to 'audit_logs'
25
+ ALTER TABLE public._audit_logs SET SCHEMA system;
26
+ ALTER TABLE system._audit_logs RENAME TO audit_logs;
27
+
28
+ -- 1.4 Move _mcp_usage table to system schema and rename to 'mcp_usage'
29
+ ALTER TABLE public._mcp_usage SET SCHEMA system;
30
+ ALTER TABLE system._mcp_usage RENAME TO mcp_usage;
31
+
32
+ -- Note: system schema is internal and should NOT be exposed to PUBLIC.
33
+ -- Access is controlled through the application's database connection.
34
+
35
+ -- ============================================================================
36
+ -- PART 2: AUTH SCHEMA (users, user_providers, configs, oauth_configs, email_otps)
37
+ -- ============================================================================
38
+
39
+ -- 2.1 Create the auth schema
40
+ CREATE SCHEMA IF NOT EXISTS auth;
41
+
42
+ -- 2.2 Drop all foreign key constraints that reference tables being moved
43
+ -- FK: _account_providers.user_id -> _accounts(id)
44
+ ALTER TABLE public._account_providers
45
+ DROP CONSTRAINT IF EXISTS _account_providers_user_id_fkey;
46
+
47
+ -- FK: users.id -> _accounts(id) (public users table references _accounts)
48
+ ALTER TABLE public.users
49
+ DROP CONSTRAINT IF EXISTS users_id_fkey;
50
+
51
+ -- FK: _storage.uploaded_by -> _accounts(id)
52
+ ALTER TABLE public._storage
53
+ DROP CONSTRAINT IF EXISTS _storage_uploaded_by_fkey;
54
+
55
+ -- 2.3 Move _accounts table to auth schema and rename to 'users'
56
+ ALTER TABLE public._accounts SET SCHEMA auth;
57
+ ALTER TABLE auth._accounts RENAME TO users;
58
+
59
+ -- 2.4 Move _account_providers table to auth schema and rename to 'user_providers'
60
+ ALTER TABLE public._account_providers SET SCHEMA auth;
61
+ ALTER TABLE auth._account_providers RENAME TO user_providers;
62
+
63
+ -- 2.5 Recreate FK: auth.user_providers.user_id -> auth.users(id)
64
+ ALTER TABLE auth.user_providers
65
+ ADD CONSTRAINT user_providers_user_id_fkey
66
+ FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE;
67
+
68
+ -- 2.6 Add profile and metadata JSONB columns to auth.users BEFORE migrating data
69
+ -- profile: stores user profile data (name, avatar_url, bio, etc.)
70
+ -- metadata: reserved for system use (device ID, login IP, etc.)
71
+ ALTER TABLE auth.users ADD COLUMN IF NOT EXISTS profile JSONB DEFAULT '{}'::jsonb;
72
+ ALTER TABLE auth.users ADD COLUMN IF NOT EXISTS metadata JSONB DEFAULT '{}'::jsonb;
73
+
74
+ -- 2.7 Migrate data from public.users into auth.users.profile
75
+ -- Priority for name: public.users.nickname first, then auth.users.name as fallback
76
+ -- Also migrate: avatar_url, bio, birthday from public.users
77
+ UPDATE auth.users AS au
78
+ SET profile = jsonb_strip_nulls(jsonb_build_object(
79
+ 'name', COALESCE(pu.nickname, au.name),
80
+ 'avatar_url', pu.avatar_url,
81
+ 'bio', pu.bio,
82
+ 'birthday', pu.birthday
83
+ ))
84
+ FROM public.users AS pu
85
+ WHERE au.id = pu.id;
86
+
87
+ -- 2.8 For users without a public.users row, migrate auth.users.name to profile
88
+ UPDATE auth.users
89
+ SET profile = jsonb_build_object('name', name)
90
+ WHERE name IS NOT NULL
91
+ AND (profile IS NULL OR profile = '{}'::jsonb)
92
+ AND id NOT IN (SELECT id FROM public.users);
93
+
94
+ -- 2.9 Update all foreign key constraints that reference public.users to use auth.users instead
95
+ -- This handles any custom tables developers may have created that reference public.users
96
+ DO $$
97
+ DECLARE
98
+ fk_record RECORD;
99
+ drop_sql TEXT;
100
+ create_sql TEXT;
101
+ BEGIN
102
+ -- Find all foreign keys that reference public.users
103
+ FOR fk_record IN
104
+ SELECT
105
+ tc.table_schema,
106
+ tc.table_name,
107
+ tc.constraint_name,
108
+ kcu.column_name,
109
+ rc.delete_rule,
110
+ rc.update_rule
111
+ FROM information_schema.table_constraints tc
112
+ JOIN information_schema.key_column_usage kcu
113
+ ON tc.constraint_name = kcu.constraint_name
114
+ AND tc.table_schema = kcu.table_schema
115
+ JOIN information_schema.referential_constraints rc
116
+ ON tc.constraint_name = rc.constraint_name
117
+ AND tc.table_schema = rc.constraint_schema
118
+ JOIN information_schema.constraint_column_usage ccu
119
+ ON rc.unique_constraint_name = ccu.constraint_name
120
+ AND rc.unique_constraint_schema = ccu.constraint_schema
121
+ WHERE tc.constraint_type = 'FOREIGN KEY'
122
+ AND ccu.table_schema = 'public'
123
+ AND ccu.table_name = 'users'
124
+ LOOP
125
+ -- Drop the old foreign key
126
+ drop_sql := format(
127
+ 'ALTER TABLE %I.%I DROP CONSTRAINT IF EXISTS %I',
128
+ fk_record.table_schema,
129
+ fk_record.table_name,
130
+ fk_record.constraint_name
131
+ );
132
+ EXECUTE drop_sql;
133
+
134
+ -- Recreate with reference to auth.users
135
+ create_sql := format(
136
+ 'ALTER TABLE %I.%I ADD CONSTRAINT %I FOREIGN KEY (%I) REFERENCES auth.users(id) ON DELETE %s ON UPDATE %s',
137
+ fk_record.table_schema,
138
+ fk_record.table_name,
139
+ fk_record.constraint_name,
140
+ fk_record.column_name,
141
+ fk_record.delete_rule,
142
+ fk_record.update_rule
143
+ );
144
+ EXECUTE create_sql;
145
+
146
+ RAISE NOTICE 'Updated FK constraint % on %.% to reference auth.users',
147
+ fk_record.constraint_name, fk_record.table_schema, fk_record.table_name;
148
+ END LOOP;
149
+ END $$;
150
+
151
+ -- 2.10 Drop public.users table (profile data now stored in auth.users.profile)
152
+ -- First drop RLS policies
153
+ DROP POLICY IF EXISTS "Enable read access for all users" ON public.users;
154
+ DROP POLICY IF EXISTS "Disable delete for users" ON public.users;
155
+ DROP POLICY IF EXISTS "Enable update for users based on user_id" ON public.users;
156
+ -- Drop the table (CASCADE will handle any remaining dependencies)
157
+ DROP TABLE IF EXISTS public.users CASCADE;
158
+
159
+ -- 2.11 Drop the name column from auth.users (data already migrated to profile)
160
+ ALTER TABLE auth.users DROP COLUMN IF EXISTS name;
161
+
162
+ -- Note: _storage.uploaded_by FK is handled in Part 4 when moving to storage schema
163
+
164
+ -- 2.12 Add is_project_admin and is_anonymous columns to auth.users
165
+ ALTER TABLE auth.users ADD COLUMN IF NOT EXISTS is_project_admin BOOLEAN NOT NULL DEFAULT false;
166
+ ALTER TABLE auth.users ADD COLUMN IF NOT EXISTS is_anonymous BOOLEAN NOT NULL DEFAULT false;
167
+
168
+ -- 2.13 Move _auth_configs table to auth schema and rename to 'configs'
169
+ ALTER TABLE public._auth_configs SET SCHEMA auth;
170
+ ALTER TABLE auth._auth_configs RENAME TO configs;
171
+
172
+ -- 2.14 Move _oauth_configs table to auth schema and rename to 'oauth_configs'
173
+ ALTER TABLE public._oauth_configs SET SCHEMA auth;
174
+ ALTER TABLE auth._oauth_configs RENAME TO oauth_configs;
175
+
176
+ -- 2.15 Recreate FK: auth.oauth_configs.secret_id -> system.secrets(id)
177
+ ALTER TABLE auth.oauth_configs
178
+ ADD CONSTRAINT oauth_configs_secret_id_fkey
179
+ FOREIGN KEY (secret_id) REFERENCES system.secrets(id) ON DELETE RESTRICT;
180
+
181
+ -- 2.16 Move _email_otps table to auth schema and rename to 'email_otps'
182
+ ALTER TABLE public._email_otps SET SCHEMA auth;
183
+ ALTER TABLE auth._email_otps RENAME TO email_otps;
184
+
185
+ -- Note: auth schema is internal and should NOT be exposed to PUBLIC.
186
+ -- However, we grant limited access to auth.users for public profile info.
187
+
188
+ -- 2.17 Grant limited public access to auth.users (only safe columns)
189
+ GRANT USAGE ON SCHEMA auth TO PUBLIC;
190
+
191
+ -- Grant SELECT on specific columns only (public profile info)
192
+ GRANT SELECT (id, profile, created_at) ON auth.users TO PUBLIC;
193
+
194
+ -- Grant UPDATE on profile column only (users can update their own profile)
195
+ GRANT UPDATE (profile) ON auth.users TO PUBLIC;
196
+
197
+ -- 2.18 Enable RLS on auth.users for row-level access control
198
+ -- Note: auth.uid() function already exists from migration 013
199
+ ALTER TABLE auth.users ENABLE ROW LEVEL SECURITY;
200
+
201
+ -- Policy: Everyone can SELECT public columns (id, profile, created_at)
202
+ -- Column-level GRANT above already restricts which columns can be read
203
+ CREATE POLICY "Public can view user profiles" ON auth.users
204
+ FOR SELECT
205
+ USING (true);
206
+
207
+ -- Policy: Users can only UPDATE their own row
208
+ CREATE POLICY "Users can update own profile" ON auth.users
209
+ FOR UPDATE
210
+ USING (id = auth.uid())
211
+ WITH CHECK (id = auth.uid());
212
+
213
+ -- 2.19 Drop obsolete public schema helper functions (migrated to auth schema)
214
+ -- NOTE: CASCADE intentionally used to force migration from public.uid() to auth.uid()
215
+ -- Any dependent objects (policies, views, functions, defaults) will be dropped.
216
+ -- Users must recreate them using auth.uid(), auth.role(), auth.email() instead.
217
+ DROP FUNCTION IF EXISTS public.uid() CASCADE;
218
+ DROP FUNCTION IF EXISTS public.role() CASCADE;
219
+ DROP FUNCTION IF EXISTS public.email() CASCADE;
220
+
221
+ -- ============================================================================
222
+ -- PART 3: AI SCHEMA (configs, usage)
223
+ -- ============================================================================
224
+
225
+ -- 3.1 Create the ai schema
226
+ CREATE SCHEMA IF NOT EXISTS ai;
227
+
228
+ -- 3.2 Drop FK constraint from _ai_usage to _ai_configs before moving
229
+ ALTER TABLE public._ai_usage
230
+ DROP CONSTRAINT IF EXISTS _ai_usage_config_id_fkey;
231
+
232
+ -- 3.3 Move _ai_configs table to ai schema and rename to 'configs'
233
+ ALTER TABLE public._ai_configs SET SCHEMA ai;
234
+ ALTER TABLE ai._ai_configs RENAME TO configs;
235
+
236
+ -- 3.4 Move _ai_usage table to ai schema and rename to 'usage'
237
+ ALTER TABLE public._ai_usage SET SCHEMA ai;
238
+ ALTER TABLE ai._ai_usage RENAME TO usage;
239
+
240
+ -- 3.5 Recreate FK: ai.usage.config_id -> ai.configs(id)
241
+ ALTER TABLE ai.usage
242
+ ADD CONSTRAINT usage_config_id_fkey
243
+ FOREIGN KEY (config_id) REFERENCES ai.configs(id) ON DELETE NO ACTION;
244
+
245
+ -- Note: ai schema is internal and should NOT be exposed to PUBLIC.
246
+ -- Access is controlled through the application's database connection.
247
+
248
+ -- ============================================================================
249
+ -- PART 4: STORAGE SCHEMA (buckets, objects)
250
+ -- ============================================================================
251
+
252
+ -- 4.1 Create the storage schema
253
+ CREATE SCHEMA IF NOT EXISTS storage;
254
+
255
+ -- 4.2 Drop FK constraints from _storage before moving
256
+ ALTER TABLE public._storage
257
+ DROP CONSTRAINT IF EXISTS _storage_bucket_fkey;
258
+
259
+ ALTER TABLE public._storage
260
+ DROP CONSTRAINT IF EXISTS _storage_uploaded_by_fkey;
261
+
262
+ -- 4.3 Move _storage_buckets table to storage schema and rename to 'buckets'
263
+ ALTER TABLE public._storage_buckets SET SCHEMA storage;
264
+ ALTER TABLE storage._storage_buckets RENAME TO buckets;
265
+
266
+ -- 4.4 Move _storage table to storage schema and rename to 'objects'
267
+ ALTER TABLE public._storage SET SCHEMA storage;
268
+ ALTER TABLE storage._storage RENAME TO objects;
269
+
270
+ -- 4.5 Recreate FK: storage.objects.bucket -> storage.buckets(name)
271
+ ALTER TABLE storage.objects
272
+ ADD CONSTRAINT objects_bucket_fkey
273
+ FOREIGN KEY (bucket) REFERENCES storage.buckets(name) ON DELETE CASCADE;
274
+
275
+ -- 4.6 Recreate FK: storage.objects.uploaded_by -> auth.users(id)
276
+ ALTER TABLE storage.objects
277
+ ADD CONSTRAINT objects_uploaded_by_fkey
278
+ FOREIGN KEY (uploaded_by) REFERENCES auth.users(id) ON DELETE SET NULL;
279
+
280
+ -- Note: storage schema is internal and should NOT be exposed to PUBLIC.
281
+ -- Access is controlled through the application's database connection.
282
+
283
+ -- ============================================================================
284
+ -- PART 5: FUNCTIONS SCHEMA (definitions)
285
+ -- ============================================================================
286
+
287
+ -- 5.1 Create the functions schema
288
+ CREATE SCHEMA IF NOT EXISTS functions;
289
+
290
+ -- 5.2 Move _functions table to functions schema and rename to 'definitions'
291
+ ALTER TABLE public._functions SET SCHEMA functions;
292
+ ALTER TABLE functions._functions RENAME TO definitions;
293
+
294
+ -- Note: functions schema is internal and should NOT be exposed to PUBLIC.
295
+ -- Access is controlled through the application's database connection.
296
+
297
+ -- ============================================================================
298
+ -- PART 6: UTILITY FUNCTIONS CLEANUP
299
+ -- ============================================================================
300
+
301
+ -- 6.1 Create system.update_updated_at() function (replaces public.update_updated_at_column)
302
+ CREATE OR REPLACE FUNCTION system.update_updated_at()
303
+ RETURNS trigger AS $$
304
+ BEGIN
305
+ NEW.updated_at = NOW();
306
+ RETURN NEW;
307
+ END;
308
+ $$ LANGUAGE plpgsql;
309
+
310
+ -- 6.2 Update all triggers to use system.update_updated_at() and rename (remove double underscore)
311
+
312
+ -- system.secrets: update__secrets_updated_at -> update_secrets_updated_at
313
+ DROP TRIGGER IF EXISTS update__secrets_updated_at ON system.secrets;
314
+ CREATE TRIGGER update_secrets_updated_at
315
+ BEFORE UPDATE ON system.secrets
316
+ FOR EACH ROW EXECUTE FUNCTION system.update_updated_at();
317
+
318
+ -- system.audit_logs: update__audit_logs_updated_at -> update_audit_logs_updated_at
319
+ DROP TRIGGER IF EXISTS update__audit_logs_updated_at ON system.audit_logs;
320
+ CREATE TRIGGER update_audit_logs_updated_at
321
+ BEFORE UPDATE ON system.audit_logs
322
+ FOR EACH ROW EXECUTE FUNCTION system.update_updated_at();
323
+
324
+ -- auth.configs: update__auth_configs_updated_at -> update_configs_updated_at
325
+ DROP TRIGGER IF EXISTS update__auth_configs_updated_at ON auth.configs;
326
+ CREATE TRIGGER update_configs_updated_at
327
+ BEFORE UPDATE ON auth.configs
328
+ FOR EACH ROW EXECUTE FUNCTION system.update_updated_at();
329
+
330
+ -- auth.oauth_configs: update__oauth_configs_updated_at -> update_oauth_configs_updated_at
331
+ DROP TRIGGER IF EXISTS update__oauth_configs_updated_at ON auth.oauth_configs;
332
+ CREATE TRIGGER update_oauth_configs_updated_at
333
+ BEFORE UPDATE ON auth.oauth_configs
334
+ FOR EACH ROW EXECUTE FUNCTION system.update_updated_at();
335
+
336
+ -- auth.email_otps: update__email_otps_updated_at -> update_email_otps_updated_at
337
+ DROP TRIGGER IF EXISTS update__email_otps_updated_at ON auth.email_otps;
338
+ CREATE TRIGGER update_email_otps_updated_at
339
+ BEFORE UPDATE ON auth.email_otps
340
+ FOR EACH ROW EXECUTE FUNCTION system.update_updated_at();
341
+
342
+ -- functions.definitions: update__edge_functions_updated_at -> update_definitions_updated_at
343
+ DROP TRIGGER IF EXISTS update__edge_functions_updated_at ON functions.definitions;
344
+ CREATE TRIGGER update_definitions_updated_at
345
+ BEFORE UPDATE ON functions.definitions
346
+ FOR EACH ROW EXECUTE FUNCTION system.update_updated_at();
347
+
348
+ -- realtime.channels: trg_channels_updated_at -> update_channels_updated_at
349
+ DROP TRIGGER IF EXISTS trg_channels_updated_at ON realtime.channels;
350
+ CREATE TRIGGER update_channels_updated_at
351
+ BEFORE UPDATE ON realtime.channels
352
+ FOR EACH ROW EXECUTE FUNCTION system.update_updated_at();
353
+
354
+ -- 6.3 Move reload_postgrest_schema to system schema
355
+ -- Recreate in system schema (ALTER FUNCTION SET SCHEMA doesn't work well with search_path)
356
+ CREATE OR REPLACE FUNCTION system.reload_postgrest_schema()
357
+ RETURNS void AS $$
358
+ BEGIN
359
+ NOTIFY pgrst, 'reload schema';
360
+ RAISE NOTICE 'PostgREST schema reload notification sent';
361
+ END
362
+ $$ LANGUAGE plpgsql;
363
+
364
+ -- 6.4 Move event trigger functions to system schema
365
+ -- These functions auto-create project_admin_policy when RLS is enabled on tables
366
+
367
+ -- First, drop the event triggers (they reference the old functions)
368
+ DROP EVENT TRIGGER IF EXISTS create_policies_on_table_create;
369
+ DROP EVENT TRIGGER IF EXISTS create_policies_on_rls_enable;
370
+
371
+ -- Recreate functions in system schema
372
+ CREATE OR REPLACE FUNCTION system.create_default_policies()
373
+ RETURNS event_trigger AS $$
374
+ DECLARE
375
+ obj record;
376
+ table_schema text;
377
+ table_name text;
378
+ has_rls boolean;
379
+ BEGIN
380
+ FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands() WHERE command_tag = 'CREATE TABLE'
381
+ LOOP
382
+ SELECT INTO table_schema, table_name
383
+ split_part(obj.object_identity, '.', 1),
384
+ trim(both '"' from split_part(obj.object_identity, '.', 2));
385
+ SELECT INTO has_rls
386
+ rowsecurity
387
+ FROM pg_tables
388
+ WHERE schemaname = table_schema
389
+ AND tablename = table_name;
390
+ IF has_rls THEN
391
+ EXECUTE format('CREATE POLICY "project_admin_policy" ON %s FOR ALL TO project_admin USING (true) WITH CHECK (true)', obj.object_identity);
392
+ END IF;
393
+ END LOOP;
394
+ END;
395
+ $$ LANGUAGE plpgsql;
396
+
397
+ CREATE OR REPLACE FUNCTION system.create_policies_after_rls()
398
+ RETURNS event_trigger AS $$
399
+ DECLARE
400
+ obj record;
401
+ table_schema text;
402
+ table_name text;
403
+ BEGIN
404
+ FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands() WHERE command_tag = 'ALTER TABLE'
405
+ LOOP
406
+ SELECT INTO table_schema, table_name
407
+ split_part(obj.object_identity, '.', 1),
408
+ trim(both '"' from split_part(obj.object_identity, '.', 2));
409
+ IF EXISTS (
410
+ SELECT 1 FROM pg_tables
411
+ WHERE schemaname = table_schema
412
+ AND tablename = table_name
413
+ AND rowsecurity = true
414
+ ) AND NOT EXISTS (
415
+ SELECT 1 FROM pg_policies
416
+ WHERE schemaname = table_schema
417
+ AND tablename = table_name
418
+ ) THEN
419
+ EXECUTE format('CREATE POLICY "project_admin_policy" ON %s FOR ALL TO project_admin USING (true) WITH CHECK (true)', obj.object_identity);
420
+ END IF;
421
+ END LOOP;
422
+ END;
423
+ $$ LANGUAGE plpgsql;
424
+
425
+ -- Recreate event triggers pointing to system schema functions
426
+ CREATE EVENT TRIGGER create_policies_on_table_create
427
+ ON ddl_command_end
428
+ WHEN TAG IN ('CREATE TABLE')
429
+ EXECUTE FUNCTION system.create_default_policies();
430
+
431
+ CREATE EVENT TRIGGER create_policies_on_rls_enable
432
+ ON ddl_command_end
433
+ WHEN TAG IN ('ALTER TABLE')
434
+ EXECUTE FUNCTION system.create_policies_after_rls();
435
+
436
+ -- 6.5 Drop obsolete functions
437
+ DROP FUNCTION IF EXISTS public.create_default_policies() CASCADE;
438
+ DROP FUNCTION IF EXISTS public.create_policies_after_rls() CASCADE;
439
+ DROP FUNCTION IF EXISTS public.reload_postgrest_schema() CASCADE;
440
+ DROP FUNCTION IF EXISTS public.update_updated_at_column() CASCADE;
441
+ DROP FUNCTION IF EXISTS realtime.update_updated_at() CASCADE;
@@ -0,0 +1,36 @@
1
+ -- Migration: 019 - Create deployments table in system schema
2
+
3
+ -- Create deployments table for tracking deployment requests and their status
4
+ -- Designed to be provider-agnostic (Vercel, Netlify, Cloudflare, etc.)
5
+ --
6
+ -- Status flow:
7
+ -- WAITING -> UPLOADING -> (Vercel statuses: QUEUED/BUILDING/READY/ERROR/CANCELED)
8
+ -- InsForge statuses:
9
+ -- - WAITING: Record created, waiting for client to upload zip to S3
10
+ -- - UPLOADING: Server is downloading from S3 and uploading to Vercel
11
+ -- Vercel statuses (stored directly):
12
+ -- - QUEUED: Deployment queued
13
+ -- - BUILDING: Deployment building
14
+ -- - READY: Deployment ready
15
+ -- - ERROR: Deployment failed
16
+ -- - CANCELED: Deployment canceled
17
+ CREATE TABLE IF NOT EXISTS system.deployments (
18
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
19
+ provider TEXT NOT NULL DEFAULT 'vercel',
20
+ provider_deployment_id TEXT UNIQUE, -- Provider's deployment ID, null until deployment starts
21
+ status TEXT NOT NULL DEFAULT 'WAITING',
22
+ url TEXT,
23
+ metadata JSONB,
24
+ created_at TIMESTAMPTZ DEFAULT NOW(),
25
+ updated_at TIMESTAMPTZ DEFAULT NOW()
26
+ );
27
+
28
+ -- Create indexes for better query performance
29
+ CREATE INDEX IF NOT EXISTS idx_deployments_status ON system.deployments(status);
30
+ CREATE INDEX IF NOT EXISTS idx_deployments_provider ON system.deployments(provider);
31
+ CREATE INDEX IF NOT EXISTS idx_deployments_created_at ON system.deployments(created_at DESC);
32
+
33
+ -- Add trigger for updated_at
34
+ DROP TRIGGER IF EXISTS update_system_deployments_updated_at ON system.deployments;
35
+ CREATE TRIGGER update_system_deployments_updated_at BEFORE UPDATE ON system.deployments
36
+ FOR EACH ROW EXECUTE FUNCTION system.update_updated_at();
@@ -0,0 +1,11 @@
1
+ -- Migration: 020 - Expand modality constraints to all OpenRouter modalities
2
+ -- Supports: text, image, audio, video, file
3
+ -- Filtering is handled in application layer (shared-schemas + helpers.ts)
4
+
5
+ ALTER TABLE ai.configs DROP CONSTRAINT IF EXISTS check_input_modality_valid;
6
+ ALTER TABLE ai.configs ADD CONSTRAINT check_input_modality_valid
7
+ CHECK (input_modality <@ '{text,image,audio,video,file}'::TEXT[]);
8
+
9
+ ALTER TABLE ai.configs DROP CONSTRAINT IF EXISTS check_output_modality_valid;
10
+ ALTER TABLE ai.configs ADD CONSTRAINT check_output_modality_valid
11
+ CHECK (output_modality <@ '{text,image,audio,video,file}'::TEXT[]);
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Bootstrap script for migrations table migration
3
+ *
4
+ * This script handles the one-time migration of the node-pg-migrate tracking table
5
+ * from `public._migrations` to `system.migrations`.
6
+ *
7
+ * Why this is needed:
8
+ * - node-pg-migrate checks for the migrations table BEFORE running any migrations
9
+ * - If we try to move the table inside a migration file, node-pg-migrate will have
10
+ * already looked for `system.migrations`, not found it, and created an empty one
11
+ * - This would cause all migrations to appear as "pending" and fail
12
+ *
13
+ * This script runs BEFORE node-pg-migrate and handles the table move gracefully.
14
+ */
15
+
16
+ import pg from 'pg';
17
+ // Note: This imports a TypeScript file. This works because the script is run with `tsx`
18
+ // (see package.json migrate:bootstrap script), which can handle TypeScript imports.
19
+ // The relative path goes up 4 levels: bootstrap -> migrations -> database -> infra -> src, then into utils.
20
+ import logger from '@/utils/logger.js';
21
+
22
+ const { Pool } = pg;
23
+
24
+ async function bootstrapMigrations() {
25
+ // Use DATABASE_URL from environment (set by dotenv-cli in npm scripts)
26
+ const connectionString = process.env.DATABASE_URL;
27
+
28
+ if (!connectionString) {
29
+ logger.error('DATABASE_URL environment variable is not set');
30
+ process.exit(1);
31
+ }
32
+
33
+ const pool = new Pool({ connectionString });
34
+
35
+ try {
36
+ const client = await pool.connect();
37
+
38
+ try {
39
+ // Check if old _migrations table exists in public schema
40
+ const oldTableExists = await client.query(`
41
+ SELECT EXISTS (
42
+ SELECT 1 FROM information_schema.tables
43
+ WHERE table_schema = 'public' AND table_name = '_migrations'
44
+ ) as exists
45
+ `);
46
+
47
+ // Check if new system.migrations table already exists
48
+ const newTableExists = await client.query(`
49
+ SELECT EXISTS (
50
+ SELECT 1 FROM information_schema.tables
51
+ WHERE table_schema = 'system' AND table_name = 'migrations'
52
+ ) as exists
53
+ `);
54
+
55
+ if (oldTableExists.rows[0].exists && !newTableExists.rows[0].exists) {
56
+ logger.info('Bootstrap: Moving _migrations table to system.migrations...');
57
+
58
+ // Create system schema if it doesn't exist
59
+ await client.query('CREATE SCHEMA IF NOT EXISTS system');
60
+
61
+ // Move the table in a transaction to avoid partial state
62
+ await client.query('BEGIN');
63
+ try {
64
+ await client.query('ALTER TABLE public._migrations SET SCHEMA system');
65
+ await client.query('ALTER TABLE system._migrations RENAME TO migrations');
66
+ await client.query('COMMIT');
67
+ } catch (error) {
68
+ await client.query('ROLLBACK');
69
+ throw error;
70
+ }
71
+
72
+ logger.info('Bootstrap: Successfully moved _migrations to system.migrations');
73
+ } else if (newTableExists.rows[0].exists) {
74
+ // Already migrated, nothing to do
75
+ logger.info('Bootstrap: system.migrations already exists, skipping');
76
+ } else if (!oldTableExists.rows[0].exists && !newTableExists.rows[0].exists) {
77
+ // Fresh install - create system schema so node-pg-migrate can create its table there
78
+ logger.info('Bootstrap: No existing migrations table, fresh install');
79
+ await client.query('CREATE SCHEMA IF NOT EXISTS system');
80
+ logger.info('Bootstrap: Created system schema for migrations');
81
+ }
82
+ } finally {
83
+ client.release();
84
+ }
85
+ } catch (error) {
86
+ logger.error('Bootstrap migration failed', {
87
+ error: error instanceof Error ? error.message : String(error),
88
+ stack: error instanceof Error ? error.stack : undefined,
89
+ });
90
+ process.exitCode = 1;
91
+ } finally {
92
+ await pool.end();
93
+ }
94
+ }
95
+
96
+ bootstrapMigrations().catch((error) => {
97
+ const message = error instanceof Error ? error.message : String(error);
98
+ logger.error('Bootstrap migration failed', {
99
+ error: message,
100
+ stack: error instanceof Error ? error.stack : undefined,
101
+ });
102
+ process.exitCode = 1;
103
+ });
@@ -195,10 +195,7 @@ export class TokenManager {
195
195
  * Generate CSRF token derived from refresh token using HMAC
196
196
  */
197
197
  generateCsrfToken(refreshToken: string): string {
198
- return crypto
199
- .createHmac('sha256', JWT_SECRET)
200
- .update(refreshToken)
201
- .digest('hex');
198
+ return crypto.createHmac('sha256', JWT_SECRET).update(refreshToken).digest('hex');
202
199
  }
203
200
 
204
201
  /**
@@ -185,7 +185,10 @@ export class OpenRouterProvider {
185
185
  };
186
186
  }
187
187
  } catch (error) {
188
- console.error('Failed to fetch remaining credits:', error);
188
+ logger.error('Failed to fetch remaining credits', {
189
+ error: error instanceof Error ? error.message : String(error),
190
+ stack: error instanceof Error ? error.stack : undefined,
191
+ });
189
192
  throw error;
190
193
  }
191
194
  }
@@ -243,7 +246,10 @@ export class OpenRouterProvider {
243
246
 
244
247
  return data.openrouter.api_key;
245
248
  } catch (error) {
246
- console.error('Failed to fetch cloud API key:', error);
249
+ logger.error('Failed to fetch cloud API key', {
250
+ error: error instanceof Error ? error.message : String(error),
251
+ stack: error instanceof Error ? error.stack : undefined,
252
+ });
247
253
  throw error;
248
254
  } finally {
249
255
  // Clear the promise after completion (success or failure)
@@ -317,7 +323,10 @@ export class OpenRouterProvider {
317
323
 
318
324
  return data.openrouter.api_key;
319
325
  } catch (error) {
320
- console.error('Failed to renew cloud API key:', error);
326
+ logger.error('Failed to renew cloud API key', {
327
+ error: error instanceof Error ? error.message : String(error),
328
+ stack: error instanceof Error ? error.stack : undefined,
329
+ });
321
330
  throw error;
322
331
  } finally {
323
332
  // Clear the promise after completion (success or failure)
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Database connection information
3
+ */
4
+ export interface DatabaseConnectionInfo {
5
+ connectionURL: string;
6
+ parameters: {
7
+ host: string;
8
+ port: number;
9
+ database: string;
10
+ user: string;
11
+ password: string;
12
+ sslmode: string;
13
+ };
14
+ }
15
+
16
+ /**
17
+ * Database password information
18
+ */
19
+ export interface DatabasePasswordInfo {
20
+ databasePassword: string;
21
+ }
22
+
23
+ /**
24
+ * Database provider interface
25
+ * Defines the contract for fetching database connection information
26
+ */
27
+ export interface DatabaseProvider {
28
+ /**
29
+ * Get database connection string
30
+ * @returns Database connection info with masked password
31
+ */
32
+ getDatabaseConnectionString(): Promise<DatabaseConnectionInfo>;
33
+
34
+ /**
35
+ * Get database password
36
+ * @returns Database password (unmasked)
37
+ */
38
+ getDatabasePassword(): Promise<DatabasePasswordInfo>;
39
+ }