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,118 +1,125 @@
1
- import { useCallback, useEffect, useState } from 'react';
2
- import { useNavigate } from 'react-router-dom';
3
- import { LockIcon } from 'lucide-react';
4
- import { useAuth } from '@/lib/contexts/AuthContext';
5
- import { postMessageToParent } from '@/lib/utils/cloudMessaging';
6
- import { useMcpUsage } from '@/features/logs/hooks/useMcpUsage';
7
- import { usePartnerOrigin } from '../hooks/usePartnerOrigin';
8
-
9
- export default function CloudLoginPage() {
10
- const navigate = useNavigate();
11
- const { loginWithAuthorizationCode, isAuthenticated } = useAuth();
12
- const { hasCompletedOnboarding, isLoading: isMcpUsageLoading } = useMcpUsage();
13
- const { isPartnerOrigin } = usePartnerOrigin();
14
- const [authError, setAuthError] = useState<string | null>(null);
15
-
16
- // Handle authorization code from postMessage
17
- const onAuthorizationCodeReceived = useCallback(
18
- async (event: MessageEvent) => {
19
- try {
20
- // Validate origin - allow insforge.dev, *.insforge.dev, and partner domains
21
- const isInsforgeOrigin =
22
- event.origin.endsWith('.insforge.dev') || event.origin === 'https://insforge.dev';
23
-
24
- if (!isInsforgeOrigin) {
25
- const isPartner = await isPartnerOrigin(event.origin);
26
- if (!isPartner) {
27
- console.warn('Received message from unauthorized origin:', event.origin);
28
- return;
29
- }
30
- }
31
-
32
- const authorizationCode = event.data.code;
33
-
34
- setAuthError(null);
35
- // Exchange the authorization code for an access token
36
- const success = await loginWithAuthorizationCode(authorizationCode);
37
- if (success) {
38
- // Notify parent of success
39
- postMessageToParent(
40
- {
41
- type: 'AUTH_SUCCESS',
42
- },
43
- event.origin
44
- );
45
- } else {
46
- setAuthError('The authorization code may have expired or already been used.');
47
- postMessageToParent(
48
- {
49
- type: 'AUTH_ERROR',
50
- message: 'Authorization code validation failed',
51
- },
52
- event.origin
53
- );
54
- }
55
- } catch (error) {
56
- console.error('Authorization code exchange failed:', error);
57
- setAuthError('The authorization code may have expired or already been used.');
58
- postMessageToParent(
59
- {
60
- type: 'AUTH_ERROR',
61
- message: 'Authorization code validation failed',
62
- },
63
- event.origin
64
- );
65
- }
66
- },
67
- [loginWithAuthorizationCode, isPartnerOrigin]
68
- );
69
-
70
- useEffect(() => {
71
- const handleMessage = (event: MessageEvent) => {
72
- if (event.data?.type === 'AUTHORIZATION_CODE' && event.data?.code) {
73
- void onAuthorizationCodeReceived(event);
74
- }
75
- };
76
-
77
- window.addEventListener('message', handleMessage);
78
-
79
- return () => {
80
- window.removeEventListener('message', handleMessage);
81
- };
82
- }, [onAuthorizationCodeReceived]);
83
-
84
- useEffect(() => {
85
- if (isAuthenticated && !isMcpUsageLoading) {
86
- if (!hasCompletedOnboarding) {
87
- postMessageToParent({ type: 'SHOW_ONBOARDING_OVERLAY' });
88
- }
89
- void navigate('/dashboard', { replace: true });
90
- }
91
- }, [hasCompletedOnboarding, isAuthenticated, isMcpUsageLoading, navigate]);
92
-
93
- // Show error state if authentication failed
94
- if (authError) {
95
- return (
96
- <div className="min-h-screen bg-neutral-800 flex items-center justify-center px-4">
97
- <div className="text-center text-white">
98
- <LockIcon className="h-12 w-12 mx-auto mb-4 text-red-400" />
99
- <h2 className="text-xl font-semibold mb-2">Authentication Failed</h2>
100
- <p className="text-gray-400 text-sm max-w-md">{authError}</p>
101
- </div>
102
- </div>
103
- );
104
- }
105
-
106
- // Show authenticating state
107
- return (
108
- <div className="min-h-screen bg-neutral-800 flex items-center justify-center px-4">
109
- <div className="text-center">
110
- <div className="animate-spin mb-4">
111
- <LockIcon className="h-12 w-12 text-white mx-auto" />
112
- </div>
113
- <h2 className="text-xl font-semibold text-white mb-2">Authenticating...</h2>
114
- <p className="text-sm text-gray-400">Please wait while we verify your identity</p>
115
- </div>
116
- </div>
117
- );
118
- }
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { useNavigate } from 'react-router-dom';
3
+ import { LockIcon } from 'lucide-react';
4
+ import { useAuth } from '@/lib/contexts/AuthContext';
5
+ import { postMessageToParent } from '@/lib/utils/cloudMessaging';
6
+ import { useMcpUsage } from '@/features/logs/hooks/useMcpUsage';
7
+ import { usePartnerOrigin } from '../hooks/usePartnerOrigin';
8
+ import { useModal } from '@/lib/hooks/useModal';
9
+
10
+ export default function CloudLoginPage() {
11
+ const navigate = useNavigate();
12
+ const { loginWithAuthorizationCode, isAuthenticated } = useAuth();
13
+ const { hasCompletedOnboarding, isLoading: isMcpUsageLoading } = useMcpUsage();
14
+ const { isPartnerOrigin } = usePartnerOrigin();
15
+ const [authError, setAuthError] = useState<string | null>(null);
16
+ const { setOnboardingModalOpen } = useModal();
17
+ // Handle authorization code from postMessage
18
+ const onAuthorizationCodeReceived = useCallback(
19
+ async (event: MessageEvent) => {
20
+ try {
21
+ // Validate origin - allow insforge.dev, *.insforge.dev, and partner domains
22
+ const isInsforgeOrigin =
23
+ event.origin.endsWith('.insforge.dev') || event.origin === 'https://insforge.dev';
24
+
25
+ if (!isInsforgeOrigin) {
26
+ const isPartner = await isPartnerOrigin(event.origin);
27
+ if (!isPartner) {
28
+ console.warn('Received message from unauthorized origin:', event.origin);
29
+ return;
30
+ }
31
+ }
32
+
33
+ const authorizationCode = event.data.code;
34
+
35
+ setAuthError(null);
36
+ // Exchange the authorization code for an access token
37
+ const success = await loginWithAuthorizationCode(authorizationCode);
38
+ if (success) {
39
+ // Notify parent of success
40
+ postMessageToParent(
41
+ {
42
+ type: 'AUTH_SUCCESS',
43
+ },
44
+ event.origin
45
+ );
46
+ } else {
47
+ setAuthError('The authorization code may have expired or already been used.');
48
+ postMessageToParent(
49
+ {
50
+ type: 'AUTH_ERROR',
51
+ message: 'Authorization code validation failed',
52
+ },
53
+ event.origin
54
+ );
55
+ }
56
+ } catch (error) {
57
+ console.error('Authorization code exchange failed:', error);
58
+ setAuthError('The authorization code may have expired or already been used.');
59
+ postMessageToParent(
60
+ {
61
+ type: 'AUTH_ERROR',
62
+ message: 'Authorization code validation failed',
63
+ },
64
+ event.origin
65
+ );
66
+ }
67
+ },
68
+ [loginWithAuthorizationCode, isPartnerOrigin]
69
+ );
70
+
71
+ useEffect(() => {
72
+ const handleMessage = (event: MessageEvent) => {
73
+ if (event.data?.type === 'AUTHORIZATION_CODE' && event.data?.code) {
74
+ void onAuthorizationCodeReceived(event);
75
+ }
76
+ };
77
+
78
+ window.addEventListener('message', handleMessage);
79
+
80
+ return () => {
81
+ window.removeEventListener('message', handleMessage);
82
+ };
83
+ }, [onAuthorizationCodeReceived]);
84
+
85
+ useEffect(() => {
86
+ if (isAuthenticated && !isMcpUsageLoading) {
87
+ if (!hasCompletedOnboarding) {
88
+ setOnboardingModalOpen(true);
89
+ }
90
+ void navigate('/dashboard', { replace: true });
91
+ }
92
+ }, [
93
+ hasCompletedOnboarding,
94
+ isAuthenticated,
95
+ isMcpUsageLoading,
96
+ navigate,
97
+ setOnboardingModalOpen,
98
+ ]);
99
+
100
+ // Show error state if authentication failed
101
+ if (authError) {
102
+ return (
103
+ <div className="min-h-screen bg-neutral-800 flex items-center justify-center px-4">
104
+ <div className="text-center text-white">
105
+ <LockIcon className="h-12 w-12 mx-auto mb-4 text-red-400" />
106
+ <h2 className="text-xl font-semibold mb-2">Authentication Failed</h2>
107
+ <p className="text-gray-400 text-sm max-w-md">{authError}</p>
108
+ </div>
109
+ </div>
110
+ );
111
+ }
112
+
113
+ // Show authenticating state
114
+ return (
115
+ <div className="min-h-screen bg-neutral-800 flex items-center justify-center px-4">
116
+ <div className="text-center">
117
+ <div className="animate-spin mb-4">
118
+ <LockIcon className="h-12 w-12 text-white mx-auto" />
119
+ </div>
120
+ <h2 className="text-xl font-semibold text-white mb-2">Authenticating...</h2>
121
+ <p className="text-sm text-gray-400">Please wait while we verify your identity</p>
122
+ </div>
123
+ </div>
124
+ );
125
+ }
@@ -0,0 +1,41 @@
1
+ import { X } from 'lucide-react';
2
+ import type { LogSchema } from '@insforge/shared-schemas';
3
+
4
+ interface LogDetailPanelProps {
5
+ log: LogSchema | null;
6
+ onClose: () => void;
7
+ }
8
+
9
+ export function LogDetailPanel({ log, onClose }: LogDetailPanelProps) {
10
+ if (!log) {
11
+ return null;
12
+ }
13
+
14
+ // Format the log body as pretty JSON
15
+ const formattedContent = JSON.stringify(log.body, null, 2);
16
+
17
+ return (
18
+ <div className="flex flex-col h-full bg-gray-100 dark:bg-neutral-700 border border-gray-300 dark:border-neutral-600 overflow-hidden">
19
+ {/* Header */}
20
+ <div className="flex items-center justify-between px-3 py-2 border-b border-gray-300 dark:border-neutral-600">
21
+ <p className="text-sm text-gray-600 dark:text-neutral-300 font-normal leading-6">
22
+ Log Content
23
+ </p>
24
+ <button
25
+ onClick={onClose}
26
+ className="p-1 hover:bg-gray-200 dark:hover:bg-neutral-600 rounded transition-colors"
27
+ aria-label="Close panel"
28
+ >
29
+ <X className="w-4 h-4 text-gray-500 dark:text-neutral-400" />
30
+ </button>
31
+ </div>
32
+
33
+ {/* Content */}
34
+ <div className="flex-1 overflow-auto p-3">
35
+ <pre className="text-sm text-gray-900 dark:text-white font-normal leading-6 whitespace-pre-wrap break-all font-mono">
36
+ {formattedContent}
37
+ </pre>
38
+ </div>
39
+ </div>
40
+ );
41
+ }
@@ -1,4 +1,4 @@
1
- import { useMemo } from 'react';
1
+ import { useMemo, useCallback } from 'react';
2
2
  import {
3
3
  DataGrid,
4
4
  type DataGridProps,
@@ -6,6 +6,7 @@ import {
6
6
  type DataGridColumn,
7
7
  type DataGridRowType,
8
8
  } from '@/components/datagrid';
9
+ import type { CellClickArgs, CellMouseEvent } from 'react-data-grid';
9
10
 
10
11
  // Column definition type for LogsDataGrid
11
12
  export interface LogsColumnDef {
@@ -52,6 +53,9 @@ export interface LogsDataGridProps<T = Record<string, unknown>>
52
53
  columnDefs: LogsColumnDef[];
53
54
  data: T[];
54
55
  noPadding?: boolean;
56
+ selectedRowId?: string | null;
57
+ onRowClick?: (row: T) => void;
58
+ rightPanel?: React.ReactNode;
55
59
  }
56
60
 
57
61
  // Specialized DataGrid for logs
@@ -59,6 +63,9 @@ export function LogsDataGrid<T = Record<string, unknown>>({
59
63
  columnDefs,
60
64
  data,
61
65
  noPadding,
66
+ selectedRowId,
67
+ onRowClick,
68
+ rightPanel,
62
69
  ...restProps
63
70
  }: LogsDataGridProps<T>) {
64
71
  const columns = useMemo(() => {
@@ -76,6 +83,27 @@ export function LogsDataGrid<T = Record<string, unknown>>({
76
83
  }) as DataGridRowType[];
77
84
  }, [data]);
78
85
 
86
+ // Handle cell click to trigger row click
87
+ const handleCellClick = useCallback(
88
+ (args: CellClickArgs<DataGridRowType>, _event: CellMouseEvent) => {
89
+ if (onRowClick) {
90
+ onRowClick(args.row as T);
91
+ }
92
+ },
93
+ [onRowClick]
94
+ );
95
+
96
+ // Row class for highlighting selected row
97
+ const rowClass = useCallback(
98
+ (row: DataGridRowType) => {
99
+ if (selectedRowId && row.id === selectedRowId) {
100
+ return 'bg-gray-200 dark:bg-neutral-700';
101
+ }
102
+ return '';
103
+ },
104
+ [selectedRowId]
105
+ );
106
+
79
107
  return (
80
108
  <DataGrid<DataGridRowType>
81
109
  {...restProps}
@@ -84,6 +112,9 @@ export function LogsDataGrid<T = Record<string, unknown>>({
84
112
  showSelection={false}
85
113
  showPagination={true}
86
114
  noPadding={noPadding}
115
+ onCellClick={handleCellClick}
116
+ rowClass={rowClass}
117
+ rightPanel={rightPanel}
87
118
  />
88
119
  );
89
120
  }
@@ -1,2 +1,3 @@
1
1
  export { createLogsColumns, type LogsColumnDef } from './LogsDataGrid';
2
2
  export { SeverityBadge } from './SeverityBadge';
3
+ export { LogDetailPanel } from './LogDetailPanel';
@@ -1,4 +1,4 @@
1
- import { useMemo } from 'react';
1
+ import { useMemo, useState, useCallback, useEffect } from 'react';
2
2
  import { useParams } from 'react-router-dom';
3
3
  import { ChevronDown } from 'lucide-react';
4
4
  import { useLogs } from '../hooks/useLogs';
@@ -13,6 +13,7 @@ import {
13
13
  } from '@/components';
14
14
  import { LogsDataGrid, type LogsColumnDef } from '../components/LogsDataGrid';
15
15
  import { SeverityBadge } from '../components/SeverityBadge';
16
+ import { LogDetailPanel } from '../components/LogDetailPanel';
16
17
  import { SEVERITY_OPTIONS, LOGS_PAGE_SIZE } from '../helpers';
17
18
  import { formatTime } from '@/lib/utils/utils';
18
19
  import { LogSchema } from '@insforge/shared-schemas';
@@ -21,6 +22,9 @@ export default function LogsPage() {
21
22
  // Get the source from the URL params
22
23
  const { source = 'insforge.logs' } = useParams<{ source?: string }>();
23
24
 
25
+ // Selected log state for detail panel
26
+ const [selectedLog, setSelectedLog] = useState<LogSchema | null>(null);
27
+
24
28
  const {
25
29
  logs: paginatedLogs,
26
30
  filteredLogs,
@@ -36,12 +40,29 @@ export default function LogsPage() {
36
40
  getSeverity,
37
41
  } = useLogs(source);
38
42
 
43
+ // Close detail panel when switching log sources or changing filters
44
+ useEffect(() => {
45
+ setSelectedLog(null);
46
+ }, [source, severityFilter]);
47
+
48
+ // Handle row click to show log details
49
+ const handleRowClick = useCallback((log: LogSchema) => {
50
+ setSelectedLog(log);
51
+ }, []);
52
+
53
+ // Handle closing the detail panel
54
+ const handleClosePanel = useCallback(() => {
55
+ setSelectedLog(null);
56
+ }, []);
57
+
58
+ // Adjust column widths based on panel state
39
59
  const logsColumns: LogsColumnDef[] = useMemo(
40
60
  () => [
41
61
  {
42
62
  key: 'event_message',
43
63
  name: 'Logs',
44
- width: '10fr',
64
+ width: selectedLog ? '1fr' : '5fr',
65
+ minWidth: 200,
45
66
  renderCell: ({ row }) => (
46
67
  <p className="text-sm text-gray-900 dark:text-white font-normal leading-6 truncate">
47
68
  {String(row.eventMessage ?? '')}
@@ -51,7 +72,7 @@ export default function LogsPage() {
51
72
  {
52
73
  key: 'severity',
53
74
  name: 'Severity',
54
- width: '120px',
75
+ width: '100px',
55
76
  renderCell: ({ row }) => (
56
77
  <SeverityBadge severity={getSeverity(row as unknown as LogSchema)} />
57
78
  ),
@@ -59,7 +80,7 @@ export default function LogsPage() {
59
80
  {
60
81
  key: 'timestamp',
61
82
  name: 'Time',
62
- width: 'minmax(200px, 1fr)',
83
+ width: '200px',
63
84
  renderCell: ({ row }) => (
64
85
  <p className="text-sm text-gray-900 dark:text-white font-normal leading-6 flex-1">
65
86
  {formatTime(String(row.timestamp ?? ''))}
@@ -67,7 +88,7 @@ export default function LogsPage() {
67
88
  ),
68
89
  },
69
90
  ],
70
- [getSeverity]
91
+ [getSeverity, selectedLog]
71
92
  );
72
93
 
73
94
  return (
@@ -121,7 +142,7 @@ export default function LogsPage() {
121
142
  </div>
122
143
  </div>
123
144
 
124
- {/* Table with Pagination */}
145
+ {/* Table with Detail Panel */}
125
146
  <div className="flex-1 overflow-hidden">
126
147
  {logsError ? (
127
148
  <div className="flex items-center justify-center h-full">
@@ -137,6 +158,15 @@ export default function LogsPage() {
137
158
  pageSize={LOGS_PAGE_SIZE}
138
159
  totalRecords={filteredLogs.length}
139
160
  onPageChange={setCurrentPage}
161
+ selectedRowId={selectedLog?.id ?? null}
162
+ onRowClick={handleRowClick}
163
+ rightPanel={
164
+ selectedLog && (
165
+ <div className="w-[480px] h-full shrink-0">
166
+ <LogDetailPanel log={selectedLog} onClose={handleClosePanel} />
167
+ </div>
168
+ )
169
+ }
140
170
  emptyState={
141
171
  <div className="text-sm text-zinc-500 dark:text-zinc-400">
142
172
  {logsSearchQuery || severityFilter.length < 3
@@ -0,0 +1,59 @@
1
+ import { CopyButton } from '@/components';
2
+ import { cn } from '@/lib/utils/utils';
3
+
4
+ interface CredentialRowProps {
5
+ label: string;
6
+ value: string;
7
+ isLoading?: boolean;
8
+ }
9
+
10
+ function CredentialRow({ label, value, isLoading = false }: CredentialRowProps) {
11
+ return (
12
+ <div className="flex items-center gap-4 min-w-0">
13
+ <span className="text-gray-900 dark:text-white text-sm leading-6 w-25 shrink-0">{label}</span>
14
+ <div
15
+ className={cn(
16
+ 'flex-1 h-9 min-w-0 flex items-center justify-between gap-2 bg-white dark:bg-neutral-900 border border-gray-200 dark:border-neutral-700 rounded-lg px-3 py-2',
17
+ isLoading && 'animate-pulse'
18
+ )}
19
+ >
20
+ <span className="text-gray-900 dark:text-white text-sm truncate min-w-0 flex-1">
21
+ {value}
22
+ </span>
23
+ <CopyButton
24
+ text={value}
25
+ disabled={isLoading}
26
+ showText={false}
27
+ className="h-6 w-6 p-1 min-w-0 shrink-0 text-black dark:text-white bg-white dark:bg-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-600 border-none"
28
+ />
29
+ </div>
30
+ </div>
31
+ );
32
+ }
33
+
34
+ interface ApiCredentialsSectionProps {
35
+ apiKey: string;
36
+ appUrl: string;
37
+ isLoading?: boolean;
38
+ className?: string;
39
+ }
40
+
41
+ export function ApiCredentialsSection({
42
+ apiKey,
43
+ appUrl,
44
+ isLoading = false,
45
+ className,
46
+ }: ApiCredentialsSectionProps) {
47
+ return (
48
+ <div className={cn('flex flex-col gap-6', className)}>
49
+ <p className="text-gray-500 dark:text-neutral-400 text-base leading-7">
50
+ Use the project URL and API key to connect directly via REST API or any HTTP client.
51
+ </p>
52
+
53
+ <div className="flex flex-col gap-4">
54
+ <CredentialRow label="Project URL" value={appUrl} isLoading={isLoading} />
55
+ <CredentialRow label="API Key" value={apiKey} isLoading={isLoading} />
56
+ </div>
57
+ </div>
58
+ );
59
+ }