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.
- package/CHANGELOG.md +2 -0
- package/auth/package.json +5 -3
- package/auth/src/lib/broadcastService.ts +115 -117
- package/auth/src/lib/insforge.ts +8 -0
- package/auth/src/main.tsx +2 -4
- package/auth/src/pages/SignInPage.tsx +60 -60
- package/auth/src/pages/SignUpPage.tsx +60 -60
- package/auth/src/pages/VerifyEmailPage.tsx +18 -0
- package/auth/tsconfig.json +2 -1
- package/backend/package.json +10 -6
- package/backend/src/api/middlewares/rate-limiters.ts +127 -127
- package/backend/src/api/routes/ai/index.routes.ts +475 -468
- package/backend/src/api/routes/auth/index.routes.ts +85 -32
- package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
- package/backend/src/api/routes/database/index.routes.ts +2 -0
- package/backend/src/api/routes/database/records.routes.ts +39 -175
- package/backend/src/api/routes/database/rpc.routes.ts +69 -0
- package/backend/src/api/routes/deployments/index.routes.ts +192 -0
- package/backend/src/api/routes/docs/index.routes.ts +3 -2
- package/backend/src/api/routes/email/index.routes.ts +35 -35
- package/backend/src/api/routes/functions/index.routes.ts +3 -3
- package/backend/src/api/routes/metadata/index.routes.ts +26 -0
- package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
- package/backend/src/infra/database/database.manager.ts +0 -10
- package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
- package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
- package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
- package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
- package/backend/src/infra/security/token.manager.ts +1 -4
- package/backend/src/providers/ai/openrouter.provider.ts +12 -3
- package/backend/src/providers/database/base.provider.ts +39 -0
- package/backend/src/providers/database/cloud.provider.ts +159 -0
- package/backend/src/providers/deployments/vercel.provider.ts +516 -0
- package/backend/src/server.ts +19 -7
- package/backend/src/services/ai/ai-config.service.ts +6 -6
- package/backend/src/services/ai/ai-model.service.ts +60 -60
- package/backend/src/services/ai/ai-usage.service.ts +7 -7
- package/backend/src/services/ai/chat-completion.service.ts +415 -220
- package/backend/src/services/ai/helpers.ts +64 -64
- package/backend/src/services/ai/index.ts +13 -13
- package/backend/src/services/auth/auth-config.service.ts +4 -4
- package/backend/src/services/auth/auth-otp.service.ts +6 -6
- package/backend/src/services/auth/auth.service.ts +134 -74
- package/backend/src/services/auth/index.ts +4 -4
- package/backend/src/services/auth/oauth-config.service.ts +12 -12
- package/backend/src/services/database/database-advance.service.ts +19 -55
- package/backend/src/services/database/database-table.service.ts +38 -85
- package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
- package/backend/src/services/deployments/deployment.service.ts +693 -0
- package/backend/src/services/functions/function.service.ts +61 -41
- package/backend/src/services/logs/audit.service.ts +10 -10
- package/backend/src/services/secrets/secret.service.ts +101 -27
- package/backend/src/services/storage/storage.service.ts +30 -30
- package/backend/src/services/usage/usage.service.ts +6 -6
- package/backend/src/types/ai.ts +8 -0
- package/backend/src/types/auth.ts +5 -1
- package/backend/src/types/database.ts +2 -0
- package/backend/src/types/deployments.ts +33 -0
- package/backend/src/types/storage.ts +1 -1
- package/backend/src/types/webhooks.ts +45 -0
- package/backend/src/utils/cookies.ts +34 -35
- package/backend/src/utils/environment.ts +0 -14
- package/backend/src/utils/s3-config-loader.ts +64 -64
- package/backend/src/utils/seed.ts +334 -301
- package/backend/src/utils/sql-parser.ts +126 -0
- package/backend/src/utils/utils.ts +114 -114
- package/backend/src/utils/validations.ts +10 -10
- package/backend/tests/local/test-rpc.sh +141 -0
- package/backend/tests/local/test-secrets.sh +1 -1
- package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
- package/backend/tests/manual/test-rawsql-modes.sh +24 -24
- package/backend/tests/unit/database-advance.test.ts +326 -0
- package/backend/tests/unit/helpers.test.ts +2 -2
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
- package/docker-compose.prod.yml +1 -1
- package/docker-compose.yml +1 -1
- package/docs/agent-docs/deployment.md +79 -0
- package/docs/changelog.mdx +165 -72
- package/docs/core-concepts/ai/architecture.mdx +1 -23
- package/docs/core-concepts/ai/sdk.mdx +26 -1
- package/docs/core-concepts/authentication/architecture.mdx +6 -8
- package/docs/core-concepts/authentication/sdk.mdx +387 -91
- package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
- package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
- package/docs/core-concepts/database/architecture.mdx +58 -21
- package/docs/core-concepts/database/pgvector.mdx +138 -0
- package/docs/core-concepts/database/sdk.mdx +17 -17
- package/docs/core-concepts/deployments/architecture.mdx +152 -0
- package/docs/core-concepts/email/architecture.mdx +4 -2
- package/docs/core-concepts/functions/architecture.mdx +1 -1
- package/docs/core-concepts/functions/sdk.mdx +0 -1
- package/docs/core-concepts/realtime/architecture.mdx +1 -1
- package/docs/core-concepts/storage/architecture.mdx +1 -1
- package/docs/core-concepts/storage/sdk.mdx +25 -25
- package/docs/docs.json +14 -6
- package/docs/favicon.png +0 -0
- package/docs/favicon.svg +3 -18
- package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
- package/docs/images/changelog/dec-2025/moreModels.png +0 -0
- package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
- package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
- package/docs/images/changelog/dec-2025/realtime2.png +0 -0
- package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
- package/docs/images/mcp-setup/claude-code-connect.png +0 -0
- package/docs/images/mcp-setup/cline-1.png +0 -0
- package/docs/images/mcp-setup/cline-2.png +0 -0
- package/docs/images/mcp-setup/cline-3.png +0 -0
- package/docs/images/mcp-setup/connect-project.png +0 -0
- package/docs/images/mcp-setup/copilot-1.png +0 -0
- package/docs/images/mcp-setup/copilot-2.png +0 -0
- package/docs/images/mcp-setup/copilot-3.png +0 -0
- package/docs/images/mcp-setup/mcp-json-1.png +0 -0
- package/docs/images/mcp-setup/mcp-json-2.png +0 -0
- package/docs/images/mcp-setup/qoder-1.png +0 -0
- package/docs/images/mcp-setup/qoder-2.png +0 -0
- package/docs/images/mcp-setup/roocode-1.png +0 -0
- package/docs/images/mcp-setup/roocode-2.png +0 -0
- package/docs/images/mcp-setup/trae-1.png +0 -0
- package/docs/images/mcp-setup/trae-2.png +0 -0
- package/docs/images/mcp-setup/trae-3.png +0 -0
- package/docs/images/mcp-setup/trae-4.png +0 -0
- package/docs/images/mcp-setup/trae-5.png +0 -0
- package/docs/images/mcp-setup/windsurf-1.png +0 -0
- package/docs/images/mcp-setup/windsurf-2.png +0 -0
- package/docs/insforge-instructions-sdk.md +7 -3
- package/docs/introduction.mdx +9 -8
- package/docs/mcp-setup.mdx +332 -0
- package/docs/oauth-server.mdx +563 -0
- package/docs/partnership.mdx +79 -10
- package/docs/quickstart.mdx +1 -1
- package/docs/vscode-extension.mdx +74 -0
- package/eslint.config.js +1 -0
- package/examples/response-examples.md +1 -1
- package/frontend/package.json +1 -1
- package/frontend/src/App.tsx +8 -3
- package/frontend/src/assets/logos/antigravity.svg +1 -0
- package/frontend/src/assets/logos/copilot.svg +10 -0
- package/frontend/src/assets/logos/deepseek.svg +139 -0
- package/frontend/src/assets/logos/kiro.svg +9 -0
- package/frontend/src/assets/logos/qoder.svg +4 -0
- package/frontend/src/assets/logos/qwen.svg +15 -0
- package/frontend/src/components/CodeBlock.tsx +2 -2
- package/frontend/src/components/ConnectCTA.tsx +3 -2
- package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
- package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
- package/frontend/src/components/datagrid/index.ts +1 -1
- package/frontend/src/components/index.ts +0 -1
- package/frontend/src/components/layout/AppHeader.tsx +4 -27
- package/frontend/src/components/layout/AppSidebar.tsx +85 -100
- package/frontend/src/components/layout/Layout.tsx +34 -32
- package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
- package/frontend/src/components/radix/Select.tsx +151 -151
- package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
- package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
- package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
- package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
- package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
- package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
- package/frontend/src/features/ai/components/index.ts +6 -6
- package/frontend/src/features/ai/helpers.ts +147 -141
- package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
- package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
- package/frontend/src/features/auth/components/index.ts +5 -5
- package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
- package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
- package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
- package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
- package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
- package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
- package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
- package/frontend/src/features/database/constants.ts +16 -28
- package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
- package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
- package/frontend/src/features/database/hooks/useTables.ts +5 -7
- package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
- package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
- package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
- package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
- package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
- package/frontend/src/features/database/services/advance.service.ts +1 -15
- package/frontend/src/features/database/services/record.service.ts +4 -20
- package/frontend/src/features/database/services/table.service.ts +1 -4
- package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
- package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
- package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
- package/frontend/src/features/database/templates/notion-clone.ts +8 -8
- package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
- package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
- package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
- package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
- package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
- package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
- package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
- package/frontend/src/features/functions/components/index.ts +5 -5
- package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
- package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
- package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
- package/frontend/src/features/functions/services/function.service.ts +8 -25
- package/frontend/src/features/functions/services/secret.service.ts +23 -41
- package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
- package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
- package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
- package/frontend/src/features/logs/components/index.ts +1 -0
- package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
- package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
- package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
- package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
- package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
- package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
- package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
- package/frontend/src/features/onboard/components/index.ts +9 -4
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
- package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
- package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
- package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
- package/frontend/src/features/onboard/index.ts +17 -13
- package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
- package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
- package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
- package/frontend/src/index.css +249 -249
- package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
- package/frontend/src/lib/hooks/useMetadata.ts +45 -1
- package/frontend/src/lib/hooks/useModal.tsx +2 -0
- package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
- package/frontend/src/lib/services/metadata.service.ts +20 -3
- package/frontend/src/lib/utils/menuItems.ts +223 -207
- package/frontend/src/lib/utils/utils.ts +196 -196
- package/functions/server.ts +315 -315
- package/functions/worker-template.js +1 -1
- package/openapi/ai.yaml +115 -5
- package/openapi/auth.yaml +97 -17
- package/openapi/logs.yaml +0 -2
- package/openapi/metadata.yaml +0 -2
- package/openapi/records.yaml +21 -21
- package/openapi/tables.yaml +1 -2
- package/package.json +1 -1
- package/shared-schemas/package.json +1 -1
- package/shared-schemas/src/ai-api.schema.ts +251 -143
- package/shared-schemas/src/ai.schema.ts +63 -63
- package/shared-schemas/src/auth-api.schema.ts +34 -6
- package/shared-schemas/src/auth.schema.ts +17 -10
- package/shared-schemas/src/cloud-events.schema.ts +26 -0
- package/shared-schemas/src/deployments-api.schema.ts +55 -0
- package/shared-schemas/src/deployments.schema.ts +30 -0
- package/shared-schemas/src/docs.schema.ts +8 -2
- package/shared-schemas/src/email-api.schema.ts +30 -30
- package/shared-schemas/src/functions-api.schema.ts +13 -4
- package/shared-schemas/src/functions.schema.ts +1 -1
- package/shared-schemas/src/index.ts +22 -18
- package/shared-schemas/src/metadata.schema.ts +30 -4
- package/shared-schemas/src/secrets-api.schema.ts +44 -0
- package/shared-schemas/src/secrets.schema.ts +15 -0
- package/zeabur/README.md +13 -0
- package/zeabur/template.yml +20 -51
- package/backend/src/types/profile.ts +0 -55
- 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(
|
|
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
|
|
135
|
-
VALUES ($1, $2, $3, $4, $5, NOW(), NOW())`,
|
|
136
|
-
[userId, email, hashedPassword,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
733
|
+
// Found existing user by email, create auth.user_providers record to link OAuth
|
|
725
734
|
await pool.query(
|
|
726
735
|
`
|
|
727
|
-
INSERT INTO
|
|
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
|
|
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
|
|
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,
|
|
813
|
+
[userId, email, profile]
|
|
812
814
|
);
|
|
813
815
|
|
|
814
|
-
// Create
|
|
816
|
+
// Create auth.user_providers record
|
|
815
817
|
await client.query(
|
|
816
818
|
`
|
|
817
|
-
INSERT INTO
|
|
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
|
|
858
|
+
const [oAuthProviders, authConfigs] = await Promise.all([
|
|
859
|
+
oAuthConfigService.getConfiguredProviders(),
|
|
860
|
+
authConfigService.getPublicAuthConfig(),
|
|
861
|
+
]);
|
|
855
862
|
return {
|
|
856
|
-
|
|
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.
|
|
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
|
|
999
|
-
LEFT JOIN
|
|
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.
|
|
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
|
|
1027
|
-
LEFT JOIN
|
|
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
|
-
|
|
1071
|
-
|
|
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.
|
|
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
|
|
1095
|
-
LEFT JOIN
|
|
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 +=
|
|
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 =
|
|
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 +=
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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', {
|