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
@@ -13,6 +13,7 @@ import type {
13
13
  CreateAdminSessionResponse,
14
14
  AuthMetadataSchema,
15
15
  OAuthProvidersSchema,
16
+ AuthOptions,
16
17
  } from '@insforge/shared-schemas';
17
18
  import { OAuthConfigService } from '@/services/auth/oauth-config.service.js';
18
19
  import { AuthConfigService } from './auth-config.service.js';
@@ -109,7 +110,12 @@ export class AuthService {
109
110
  * User registration
110
111
  * Otherwise, returns user with access token for immediate login
111
112
  */
112
- async register(email: string, password: string, name?: string): Promise<CreateUserResponse> {
113
+ async register(
114
+ email: string,
115
+ password: string,
116
+ name?: string,
117
+ options?: AuthOptions
118
+ ): Promise<CreateUserResponse> {
113
119
  // Get email auth configuration and validate password
114
120
  const authConfigService = AuthConfigService.getInstance();
115
121
  const emailAuthConfig = await authConfigService.getAuthConfig();
@@ -130,16 +136,11 @@ export class AuthService {
130
136
  try {
131
137
  await client.query('BEGIN');
132
138
 
139
+ const profile = name ? JSON.stringify({ name }) : '{}';
133
140
  await client.query(
134
- `INSERT INTO _accounts (id, email, password, name, email_verified, created_at, updated_at)
135
- VALUES ($1, $2, $3, $4, $5, NOW(), NOW())`,
136
- [userId, email, hashedPassword, name || null, false]
137
- );
138
-
139
- await client.query(
140
- `INSERT INTO users (id, nickname, created_at, updated_at)
141
- VALUES ($1, $2, NOW(), NOW())`,
142
- [userId, name || null]
141
+ `INSERT INTO auth.users (id, email, password, profile, email_verified, created_at, updated_at)
142
+ VALUES ($1, $2, $3, $4::jsonb, $5, NOW(), NOW())`,
143
+ [userId, email, hashedPassword, profile, false]
143
144
  );
144
145
 
145
146
  await client.query('COMMIT');
@@ -163,7 +164,8 @@ export class AuthService {
163
164
  if (emailAuthConfig.requireEmailVerification) {
164
165
  try {
165
166
  if (emailAuthConfig.verifyEmailMethod === 'link') {
166
- await this.sendVerificationEmailWithLink(email);
167
+ const redirectTo = emailAuthConfig.signInRedirectTo || options?.emailRedirectTo;
168
+ await this.sendVerificationEmailWithLink(email, redirectTo);
167
169
  } else {
168
170
  await this.sendVerificationEmailWithCode(email);
169
171
  }
@@ -243,7 +245,7 @@ export class AuthService {
243
245
  async sendVerificationEmailWithCode(email: string): Promise<void> {
244
246
  // Check if user exists
245
247
  const pool = this.getPool();
246
- const result = await pool.query('SELECT * FROM _accounts WHERE email = $1', [email]);
248
+ const result = await pool.query('SELECT * FROM auth.users WHERE email = $1', [email]);
247
249
  const dbUser = result.rows[0];
248
250
  if (!dbUser) {
249
251
  // Silently succeed to prevent user enumeration
@@ -261,7 +263,8 @@ export class AuthService {
261
263
 
262
264
  // Send email with verification code
263
265
  const emailService = EmailService.getInstance();
264
- await emailService.sendWithTemplate(email, dbUser.name || 'User', 'email-verification-code', {
266
+ const userName = dbUser.profile?.name || 'User';
267
+ await emailService.sendWithTemplate(email, userName, 'email-verification-code', {
265
268
  token: code,
266
269
  });
267
270
  }
@@ -271,10 +274,10 @@ export class AuthService {
271
274
  * Creates a long cryptographic token and sends it via email as a clickable link
272
275
  * The link contains only the token (no email) for better privacy and security
273
276
  */
274
- async sendVerificationEmailWithLink(email: string): Promise<void> {
277
+ async sendVerificationEmailWithLink(email: string, emailRedirectTo?: string): Promise<void> {
275
278
  // Check if user exists
276
279
  const pool = this.getPool();
277
- const result = await pool.query('SELECT * FROM _accounts WHERE email = $1', [email]);
280
+ const result = await pool.query('SELECT * FROM auth.users WHERE email = $1', [email]);
278
281
  const dbUser = result.rows[0];
279
282
  if (!dbUser) {
280
283
  // Silently succeed to prevent user enumeration
@@ -291,11 +294,13 @@ export class AuthService {
291
294
  );
292
295
 
293
296
  // Build verification link URL using backend API endpoint
294
- const linkUrl = `${getApiBaseUrl()}/auth/verify-email?token=${token}`;
297
+ // Include redirectTo parameter if provided
298
+ const linkUrl = `${getApiBaseUrl()}/auth/verify-email?token=${token}${emailRedirectTo ? `&redirectTo=${encodeURIComponent(emailRedirectTo)}` : ''}`;
295
299
 
296
300
  // Send email with verification link
297
301
  const emailService = EmailService.getInstance();
298
- await emailService.sendWithTemplate(email, dbUser.name || 'User', 'email-verification-link', {
302
+ const userName = dbUser.profile?.name || 'User';
303
+ await emailService.sendWithTemplate(email, userName, 'email-verification-link', {
299
304
  link: linkUrl,
300
305
  });
301
306
  }
@@ -323,7 +328,7 @@ export class AuthService {
323
328
 
324
329
  // Update account email verification status
325
330
  const result = await client.query(
326
- `UPDATE _accounts
331
+ `UPDATE auth.users
327
332
  SET email_verified = true, updated_at = NOW()
328
333
  WHERE email = $1
329
334
  RETURNING id`,
@@ -389,7 +394,7 @@ export class AuthService {
389
394
 
390
395
  // Update account email verification status
391
396
  const result = await client.query(
392
- `UPDATE _accounts
397
+ `UPDATE auth.users
393
398
  SET email_verified = true, updated_at = NOW()
394
399
  WHERE email = $1
395
400
  RETURNING id`,
@@ -439,7 +444,7 @@ export class AuthService {
439
444
  async sendResetPasswordEmailWithCode(email: string): Promise<void> {
440
445
  // Check if user exists
441
446
  const pool = this.getPool();
442
- const result = await pool.query('SELECT * FROM _accounts WHERE email = $1', [email]);
447
+ const result = await pool.query('SELECT * FROM auth.users WHERE email = $1', [email]);
443
448
  const dbUser = result.rows[0];
444
449
  if (!dbUser) {
445
450
  // Silently succeed to prevent user enumeration
@@ -457,7 +462,8 @@ export class AuthService {
457
462
 
458
463
  // Send email with reset password code
459
464
  const emailService = EmailService.getInstance();
460
- await emailService.sendWithTemplate(email, dbUser.name || 'User', 'reset-password-code', {
465
+ const userName = dbUser.profile?.name || 'User';
466
+ await emailService.sendWithTemplate(email, userName, 'reset-password-code', {
461
467
  token: code,
462
468
  });
463
469
  }
@@ -470,7 +476,7 @@ export class AuthService {
470
476
  async sendResetPasswordEmailWithLink(email: string): Promise<void> {
471
477
  // Check if user exists
472
478
  const pool = this.getPool();
473
- const result = await pool.query('SELECT * FROM _accounts WHERE email = $1', [email]);
479
+ const result = await pool.query('SELECT * FROM auth.users WHERE email = $1', [email]);
474
480
  const dbUser = result.rows[0];
475
481
  if (!dbUser) {
476
482
  // Silently succeed to prevent user enumeration
@@ -491,7 +497,8 @@ export class AuthService {
491
497
 
492
498
  // Send email with password reset link
493
499
  const emailService = EmailService.getInstance();
494
- await emailService.sendWithTemplate(email, dbUser.name || 'User', 'reset-password-link', {
500
+ const userName = dbUser.profile?.name || 'User';
501
+ await emailService.sendWithTemplate(email, userName, 'reset-password-link', {
495
502
  link: linkUrl,
496
503
  });
497
504
  }
@@ -562,7 +569,7 @@ export class AuthService {
562
569
 
563
570
  // Update password in the database
564
571
  const result = await client.query(
565
- `UPDATE _accounts
572
+ `UPDATE auth.users
566
573
  SET password = $1, updated_at = NOW()
567
574
  WHERE email = $2
568
575
  RETURNING id`,
@@ -612,10 +619,11 @@ export class AuthService {
612
619
  user: {
613
620
  id: ADMIN_ID,
614
621
  email: email,
615
- name: 'Administrator',
616
622
  emailVerified: true,
617
623
  createdAt: new Date().toISOString(),
618
624
  updatedAt: new Date().toISOString(),
625
+ profile: { name: 'Administrator' },
626
+ metadata: null,
619
627
  },
620
628
  accessToken,
621
629
  };
@@ -643,10 +651,11 @@ export class AuthService {
643
651
  user: {
644
652
  id: ADMIN_ID,
645
653
  email: email as string,
646
- name: 'Administrator',
647
654
  emailVerified: true,
648
655
  createdAt: new Date().toISOString(),
649
656
  updatedAt: new Date().toISOString(),
657
+ profile: { name: 'Administrator' },
658
+ metadata: null,
650
659
  },
651
660
  accessToken,
652
661
  };
@@ -679,9 +688,9 @@ export class AuthService {
679
688
  ): Promise<CreateSessionResponse> {
680
689
  const pool = this.getPool();
681
690
 
682
- // First, try to find existing user by provider ID in _account_providers table
691
+ // First, try to find existing user by provider ID in auth.user_providers table
683
692
  const accountResult = await pool.query(
684
- 'SELECT * FROM _account_providers WHERE provider = $1 AND provider_account_id = $2',
693
+ 'SELECT * FROM auth.user_providers WHERE provider = $1 AND provider_account_id = $2',
685
694
  [provider, providerId]
686
695
  );
687
696
  const account = accountResult.rows[0];
@@ -689,13 +698,13 @@ export class AuthService {
689
698
  if (account) {
690
699
  // Found existing OAuth user, update last login time
691
700
  await pool.query(
692
- 'UPDATE _account_providers SET updated_at = CURRENT_TIMESTAMP WHERE provider = $1 AND provider_account_id = $2',
701
+ 'UPDATE auth.user_providers SET updated_at = CURRENT_TIMESTAMP WHERE provider = $1 AND provider_account_id = $2',
693
702
  [provider, providerId]
694
703
  );
695
704
 
696
705
  // Update email_verified to true if not already verified (OAuth login means email is trusted)
697
706
  await pool.query(
698
- 'UPDATE _accounts SET email_verified = true WHERE id = $1 AND email_verified = false',
707
+ 'UPDATE auth.users SET email_verified = true WHERE id = $1 AND email_verified = false',
699
708
  [account.user_id]
700
709
  );
701
710
 
@@ -715,16 +724,16 @@ export class AuthService {
715
724
  }
716
725
 
717
726
  // If not found by provider_id, try to find by email in _user table
718
- const existingUserResult = await pool.query('SELECT * FROM _accounts WHERE email = $1', [
727
+ const existingUserResult = await pool.query('SELECT * FROM auth.users WHERE email = $1', [
719
728
  email,
720
729
  ]);
721
730
  const existingUser = existingUserResult.rows[0];
722
731
 
723
732
  if (existingUser) {
724
- // Found existing user by email, create _account_providers record to link OAuth
733
+ // Found existing user by email, create auth.user_providers record to link OAuth
725
734
  await pool.query(
726
735
  `
727
- INSERT INTO _account_providers (
736
+ INSERT INTO auth.user_providers (
728
737
  user_id, provider, provider_account_id,
729
738
  provider_data, created_at, updated_at
730
739
  )
@@ -735,7 +744,7 @@ export class AuthService {
735
744
 
736
745
  // Update email_verified to true (OAuth login means email is trusted)
737
746
  await pool.query(
738
- 'UPDATE _accounts SET email_verified = true WHERE id = $1 AND email_verified = false',
747
+ 'UPDATE auth.users SET email_verified = true WHERE id = $1 AND email_verified = false',
739
748
  [existingUser.id]
740
749
  );
741
750
 
@@ -795,26 +804,19 @@ export class AuthService {
795
804
  await client.query('BEGIN');
796
805
 
797
806
  // Create user record (without password for OAuth users)
807
+ const profile = JSON.stringify({ name: userName, avatar_url: avatarUrl });
798
808
  await client.query(
799
809
  `
800
- INSERT INTO _accounts (id, email, name, email_verified, created_at, updated_at)
801
- VALUES ($1, $2, $3, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
802
- `,
803
- [userId, email, userName]
804
- );
805
-
806
- await client.query(
807
- `
808
- INSERT INTO users (id, nickname, avatar_url, created_at, updated_at)
809
- VALUES ($1, $2, $3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
810
+ INSERT INTO auth.users (id, email, profile, email_verified, created_at, updated_at)
811
+ VALUES ($1, $2, $3::jsonb, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
810
812
  `,
811
- [userId, userName, avatarUrl]
813
+ [userId, email, profile]
812
814
  );
813
815
 
814
- // Create _account_providers record
816
+ // Create auth.user_providers record
815
817
  await client.query(
816
818
  `
817
- INSERT INTO _account_providers (
819
+ INSERT INTO auth.user_providers (
818
820
  user_id, provider, provider_account_id,
819
821
  provider_data, created_at, updated_at
820
822
  )
@@ -828,10 +830,11 @@ export class AuthService {
828
830
  const user: UserSchema = {
829
831
  id: userId,
830
832
  email,
831
- name: userName,
832
833
  emailVerified: true,
833
834
  createdAt: new Date().toISOString(),
834
835
  updatedAt: new Date().toISOString(),
836
+ profile: { name: userName, avatar_url: avatarUrl },
837
+ metadata: null,
835
838
  };
836
839
 
837
840
  const accessToken = this.tokenManager.generateToken({
@@ -850,10 +853,15 @@ export class AuthService {
850
853
  }
851
854
 
852
855
  async getMetadata(): Promise<AuthMetadataSchema> {
856
+ const authConfigService = AuthConfigService.getInstance();
853
857
  const oAuthConfigService = OAuthConfigService.getInstance();
854
- const oAuthConfigs = await oAuthConfigService.getAllConfigs();
858
+ const [oAuthProviders, authConfigs] = await Promise.all([
859
+ oAuthConfigService.getConfiguredProviders(),
860
+ authConfigService.getPublicAuthConfig(),
861
+ ]);
855
862
  return {
856
- oauths: oAuthConfigs,
863
+ oAuthProviders,
864
+ ...authConfigs,
857
865
  };
858
866
  }
859
867
 
@@ -989,14 +997,17 @@ export class AuthService {
989
997
  SELECT
990
998
  u.id,
991
999
  u.email,
992
- u.name,
1000
+ u.profile,
1001
+ u.metadata,
993
1002
  u.email_verified,
1003
+ u.is_project_admin,
1004
+ u.is_anonymous,
994
1005
  u.created_at,
995
1006
  u.updated_at,
996
1007
  u.password,
997
1008
  STRING_AGG(a.provider, ',') as providers
998
- FROM _accounts u
999
- LEFT JOIN _account_providers a ON u.id = a.user_id
1009
+ FROM auth.users u
1010
+ LEFT JOIN auth.user_providers a ON u.id = a.user_id
1000
1011
  WHERE u.email = $1
1001
1012
  GROUP BY u.id
1002
1013
  `,
@@ -1017,14 +1028,17 @@ export class AuthService {
1017
1028
  SELECT
1018
1029
  u.id,
1019
1030
  u.email,
1020
- u.name,
1031
+ u.profile,
1032
+ u.metadata,
1021
1033
  u.email_verified,
1034
+ u.is_project_admin,
1035
+ u.is_anonymous,
1022
1036
  u.created_at,
1023
1037
  u.updated_at,
1024
1038
  u.password,
1025
1039
  STRING_AGG(a.provider, ',') as providers
1026
- FROM _accounts u
1027
- LEFT JOIN _account_providers a ON u.id = a.user_id
1040
+ FROM auth.users u
1041
+ LEFT JOIN auth.user_providers a ON u.id = a.user_id
1028
1042
  WHERE u.id = $1
1029
1043
  GROUP BY u.id
1030
1044
  `,
@@ -1039,36 +1053,29 @@ export class AuthService {
1039
1053
  * @private
1040
1054
  */
1041
1055
  private transformUserRecordToSchema(dbUser: UserRecord): UserSchema {
1042
- const identities = [];
1043
1056
  const providers: string[] = [];
1044
1057
 
1045
1058
  // Add social providers if any
1046
1059
  if (dbUser.providers) {
1047
1060
  dbUser.providers.split(',').forEach((provider: string) => {
1048
- identities.push({ provider });
1049
1061
  providers.push(provider);
1050
1062
  });
1051
1063
  }
1052
1064
 
1053
1065
  // Add email provider if password exists
1054
1066
  if (dbUser.password) {
1055
- identities.push({ provider: 'email' });
1056
1067
  providers.push('email');
1057
1068
  }
1058
1069
 
1059
- // Use first provider to determine type: 'email' or 'social'
1060
- const firstProvider = providers[0];
1061
- const providerType = firstProvider === 'email' ? 'email' : 'social';
1062
-
1063
1070
  return {
1064
1071
  id: dbUser.id,
1065
1072
  email: dbUser.email,
1066
- name: dbUser.name,
1067
1073
  emailVerified: dbUser.email_verified,
1068
1074
  createdAt: dbUser.created_at,
1069
1075
  updatedAt: dbUser.updated_at,
1070
- identities: identities,
1071
- providerType: providerType,
1076
+ providers: providers,
1077
+ profile: dbUser.profile,
1078
+ metadata: dbUser.metadata,
1072
1079
  };
1073
1080
  }
1074
1081
 
@@ -1085,19 +1092,23 @@ export class AuthService {
1085
1092
  SELECT
1086
1093
  u.id,
1087
1094
  u.email,
1088
- u.name,
1095
+ u.profile,
1096
+ u.metadata,
1089
1097
  u.email_verified,
1098
+ u.is_project_admin,
1099
+ u.is_anonymous,
1090
1100
  u.created_at,
1091
1101
  u.updated_at,
1092
1102
  u.password,
1093
1103
  STRING_AGG(a.provider, ',') as providers
1094
- FROM _accounts u
1095
- LEFT JOIN _account_providers a ON u.id = a.user_id
1104
+ FROM auth.users u
1105
+ LEFT JOIN auth.user_providers a ON u.id = a.user_id
1106
+ WHERE u.is_project_admin = false AND u.is_anonymous = false
1096
1107
  `;
1097
1108
  const params: (string | number)[] = [];
1098
1109
 
1099
1110
  if (search) {
1100
- query += ' WHERE u.email LIKE $1 OR u.name LIKE $2';
1111
+ query += ` AND (u.email LIKE $1 OR u.profile->>'name' LIKE $2)`;
1101
1112
  params.push(`%${search}%`, `%${search}%`);
1102
1113
  }
1103
1114
 
@@ -1110,11 +1121,12 @@ export class AuthService {
1110
1121
  // Transform users
1111
1122
  const users = dbUsers.map((dbUser) => this.transformUserRecordToSchema(dbUser));
1112
1123
 
1113
- // Get total count
1114
- let countQuery = 'SELECT COUNT(*) as count FROM _accounts';
1124
+ // Get total count (exclude admins and anonymous users)
1125
+ let countQuery =
1126
+ 'SELECT COUNT(*) as count FROM auth.users WHERE is_project_admin = false AND is_anonymous = false';
1115
1127
  const countParams: string[] = [];
1116
1128
  if (search) {
1117
- countQuery += ' WHERE email LIKE $1 OR name LIKE $2';
1129
+ countQuery += ` AND (email LIKE $1 OR profile->>'name' LIKE $2)`;
1118
1130
  countParams.push(`%${search}%`, `%${search}%`);
1119
1131
  }
1120
1132
  const countResult = await pool.query(countQuery, countParams);
@@ -1137,13 +1149,61 @@ export class AuthService {
1137
1149
  return this.transformUserRecordToSchema(dbUser);
1138
1150
  }
1139
1151
 
1152
+ /**
1153
+ * Get user profile by ID (public endpoint - returns id and profile)
1154
+ */
1155
+ async getProfileById(
1156
+ userId: string
1157
+ ): Promise<{ id: string; profile: Record<string, unknown> | null } | null> {
1158
+ const pool = this.getPool();
1159
+ const result = await pool.query(`SELECT id, profile FROM auth.users WHERE id = $1`, [userId]);
1160
+
1161
+ if (result.rows.length === 0) {
1162
+ return null;
1163
+ }
1164
+
1165
+ return {
1166
+ id: result.rows[0].id,
1167
+ profile: result.rows[0].profile,
1168
+ };
1169
+ }
1170
+
1171
+ /**
1172
+ * Update user profile (for authenticated user updating their own profile)
1173
+ */
1174
+ async updateProfile(
1175
+ userId: string,
1176
+ profile: Record<string, unknown>
1177
+ ): Promise<{ id: string; profile: Record<string, unknown> | null }> {
1178
+ const pool = this.getPool();
1179
+ const result = await pool.query(
1180
+ `UPDATE auth.users
1181
+ SET profile = COALESCE(profile, '{}'::jsonb) || $1::jsonb, updated_at = NOW()
1182
+ WHERE id = $2
1183
+ RETURNING id, profile`,
1184
+ [profile, userId]
1185
+ );
1186
+
1187
+ if (result.rows.length === 0) {
1188
+ throw new AppError('User not found', 404, ERROR_CODES.NOT_FOUND);
1189
+ }
1190
+
1191
+ return {
1192
+ id: result.rows[0].id,
1193
+ profile: result.rows[0].profile,
1194
+ };
1195
+ }
1196
+
1140
1197
  /**
1141
1198
  * Delete multiple users by IDs
1142
1199
  */
1143
1200
  async deleteUsers(userIds: string[]): Promise<number> {
1144
1201
  const pool = this.getPool();
1145
1202
  const placeholders = userIds.map((_, i) => `$${i + 1}`).join(',');
1146
- const result = await pool.query(`DELETE FROM _accounts WHERE id IN (${placeholders})`, userIds);
1203
+ const result = await pool.query(
1204
+ `DELETE FROM auth.users WHERE id IN (${placeholders})`,
1205
+ userIds
1206
+ );
1147
1207
 
1148
1208
  return result.rowCount || 0;
1149
1209
  }
@@ -1,4 +1,4 @@
1
- export { AuthService } from './auth.service.js';
2
- export { AuthConfigService } from './auth-config.service.js';
3
- export { AuthOTPService } from './auth-otp.service.js';
4
- export { OAuthConfigService } from './oauth-config.service.js';
1
+ export { AuthService } from './auth.service.js';
2
+ export { AuthConfigService } from './auth-config.service.js';
3
+ export { AuthOTPService } from './auth-otp.service.js';
4
+ export { OAuthConfigService } from './oauth-config.service.js';
@@ -62,7 +62,7 @@ export class OAuthConfigService {
62
62
  use_shared_key as "useSharedKey",
63
63
  created_at as "createdAt",
64
64
  updated_at as "updatedAt"
65
- FROM _oauth_configs
65
+ FROM auth.oauth_configs
66
66
  ORDER BY provider ASC`
67
67
  );
68
68
 
@@ -82,7 +82,7 @@ export class OAuthConfigService {
82
82
  const result = await this.getPool().query(
83
83
  `SELECT
84
84
  provider
85
- FROM _oauth_configs
85
+ FROM auth.oauth_configs
86
86
  ORDER BY provider ASC`
87
87
  );
88
88
 
@@ -108,7 +108,7 @@ export class OAuthConfigService {
108
108
  use_shared_key as "useSharedKey",
109
109
  created_at as "createdAt",
110
110
  updated_at as "updatedAt"
111
- FROM _oauth_configs
111
+ FROM auth.oauth_configs
112
112
  WHERE LOWER(provider) = LOWER($1)
113
113
  LIMIT 1`,
114
114
  [provider]
@@ -133,7 +133,7 @@ export class OAuthConfigService {
133
133
  const result = await this.getPool().query(
134
134
  `SELECT
135
135
  secret_id as "secretId"
136
- FROM _oauth_configs
136
+ FROM auth.oauth_configs
137
137
  WHERE LOWER(provider) = LOWER($1)
138
138
  LIMIT 1`,
139
139
  [provider]
@@ -167,7 +167,7 @@ export class OAuthConfigService {
167
167
 
168
168
  // Check if config already exists for this provider
169
169
  const existingConfig = await client.query(
170
- 'SELECT id FROM _oauth_configs WHERE LOWER(provider) = LOWER($1)',
170
+ 'SELECT id FROM auth.oauth_configs WHERE LOWER(provider) = LOWER($1)',
171
171
  [input.provider]
172
172
  );
173
173
 
@@ -235,7 +235,7 @@ export class OAuthConfigService {
235
235
 
236
236
  // Create new OAuth config
237
237
  const result = await client.query(
238
- `INSERT INTO _oauth_configs (provider, client_id, secret_id, redirect_uri, scopes, use_shared_key)
238
+ `INSERT INTO auth.oauth_configs (provider, client_id, secret_id, redirect_uri, scopes, use_shared_key)
239
239
  VALUES ($1, $2, $3, $4, $5, $6)
240
240
  RETURNING
241
241
  id,
@@ -282,7 +282,7 @@ export class OAuthConfigService {
282
282
 
283
283
  // Get existing config with secret_id
284
284
  const existingResult = await client.query(
285
- `SELECT id, secret_id as "secretId" FROM _oauth_configs WHERE LOWER(provider) = LOWER($1)`,
285
+ `SELECT id, secret_id as "secretId" FROM auth.oauth_configs WHERE LOWER(provider) = LOWER($1)`,
286
286
  [provider]
287
287
  );
288
288
 
@@ -306,7 +306,7 @@ export class OAuthConfigService {
306
306
  value: input.clientSecret,
307
307
  });
308
308
  // Add secret_id to the update query
309
- await client.query(`UPDATE _oauth_configs SET secret_id = $1 WHERE id = $2`, [
309
+ await client.query(`UPDATE auth.oauth_configs SET secret_id = $1 WHERE id = $2`, [
310
310
  secret.id,
311
311
  existingConfig.id,
312
312
  ]);
@@ -353,7 +353,7 @@ export class OAuthConfigService {
353
353
  values.push(provider.toLowerCase());
354
354
 
355
355
  const result = await client.query(
356
- `UPDATE _oauth_configs
356
+ `UPDATE auth.oauth_configs
357
357
  SET ${updates.join(', ')}
358
358
  WHERE LOWER(provider) = $${paramCount}
359
359
  RETURNING
@@ -406,7 +406,7 @@ export class OAuthConfigService {
406
406
 
407
407
  // Get existing config with secret_id
408
408
  const existingResult = await client.query(
409
- `SELECT id, secret_id as "secretId" FROM _oauth_configs WHERE LOWER(provider) = LOWER($1)`,
409
+ `SELECT id, secret_id as "secretId" FROM auth.oauth_configs WHERE LOWER(provider) = LOWER($1)`,
410
410
  [provider]
411
411
  );
412
412
 
@@ -419,13 +419,13 @@ export class OAuthConfigService {
419
419
 
420
420
  // Delete OAuth config (secret will be restricted due to foreign key)
421
421
  const result = await client.query(
422
- 'DELETE FROM _oauth_configs WHERE LOWER(provider) = LOWER($1)',
422
+ 'DELETE FROM auth.oauth_configs WHERE LOWER(provider) = LOWER($1)',
423
423
  [provider]
424
424
  );
425
425
 
426
426
  // Try to delete the associated secret (will fail if still referenced)
427
427
  try {
428
- await client.query('DELETE FROM _secrets WHERE id = $1', [existingConfig.secretId]);
428
+ await client.query('DELETE FROM system.secrets WHERE id = $1', [existingConfig.secretId]);
429
429
  logger.info('Associated secret deleted', { secretId: existingConfig.secretId });
430
430
  } catch {
431
431
  logger.warn('Could not delete associated secret, it may be in use elsewhere', {