diff --git a/backend/package-lock.json b/backend/package-lock.json index 6703e198f..189b1eccd 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -10,13 +10,13 @@ "license": "ISC", "dependencies": { "@godaddy/terminus": "^4.11.2", - "@sentry/node": "^7.21.1", "@octokit/rest": "^19.0.5", - "@sentry/tracing": "^7.21.1", + "@sentry/node": "^7.14.0", + "@sentry/tracing": "^7.19.0", "@types/crypto-js": "^4.1.1", - "axios": "^1.2.0", "@types/libsodium-wrappers": "^0.7.10", "await-to-js": "^3.0.0", + "axios": "^1.1.3", "bcrypt": "^5.1.0", "bigint-conversion": "^2.2.2", "builder-pattern": "^2.2.0", @@ -32,9 +32,9 @@ "js-yaml": "^4.1.0", "jsonwebtoken": "^9.0.0", "jsrp": "^0.2.4", - "mongoose": "^6.7.3", "libsodium-wrappers": "^0.7.10", "lodash": "^4.17.21", + "mongoose": "^6.7.2", "nodemailer": "^6.8.0", "posthog-node": "^2.2.2", "query-string": "^7.1.3", @@ -2838,19 +2838,6 @@ "@maxmind/geoip2-node": "^3.4.0" } }, - "node_modules/@sentry/core": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz", - "integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==", - "dependencies": { - "@sentry/types": "7.21.1", - "@sentry/utils": "7.21.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -2905,27 +2892,10 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, - "node_modules/@sentry/node": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz", - "integrity": "sha512-yG7Tx32WqOkEHVotFLrumCcT9qlaSDTkFNZ+yLSvZXx74ifsE781DzBA9W7K7bBdYO3op+p2YdsOKzf3nPpAyQ==", - "dependencies": { - "@sentry/core": "7.19.0", - "@sentry/types": "7.19.0", - "@sentry/utils": "7.19.0", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/node/node_modules/@sentry/core": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz", - "integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==", + "node_modules/@sentry/core": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz", + "integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==", "dependencies": { "@sentry/types": "7.21.1", "@sentry/utils": "7.21.1", @@ -2986,26 +2956,6 @@ "node": ">=8" } }, - "node_modules/@sentry/types": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz", - "integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/utils": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz", - "integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==", - "dependencies": { - "@sentry/types": "7.21.1", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", @@ -14306,16 +14256,6 @@ "@maxmind/geoip2-node": "^3.4.0" } }, - "@sentry/core": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz", - "integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==", - "requires": { - "@sentry/types": "7.21.1", - "@sentry/utils": "7.21.1", - "tslib": "^1.9.3" - } - }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -14370,6 +14310,16 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "@sentry/core": { + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz", + "integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==", + "requires": { + "@sentry/types": "7.21.1", + "@sentry/utils": "7.21.1", + "tslib": "^1.9.3" + } + }, "@sentry/node": { "version": "7.21.1", "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.21.1.tgz", @@ -14409,20 +14359,6 @@ "tslib": "^1.9.3" } }, - "@sentry/types": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz", - "integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==" - }, - "@sentry/utils": { - "version": "7.21.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz", - "integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==", - "requires": { - "@sentry/types": "7.21.1", - "tslib": "^1.9.3" - } - }, "@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", diff --git a/backend/src/controllers/v1/integrationAuthController.ts b/backend/src/controllers/v1/integrationAuthController.ts index cedc7e345..747004f44 100644 --- a/backend/src/controllers/v1/integrationAuthController.ts +++ b/backend/src/controllers/v1/integrationAuthController.ts @@ -40,14 +40,16 @@ export const oAuthExchange = async ( throw new Error("Failed to get environments") } - const integrationDetails = await IntegrationService.handleOAuthExchange({ + const integrationAuth = await IntegrationService.handleOAuthExchange({ workspaceId, integration, code, environment: environments[0].slug, }); - return res.status(200).send(integrationDetails); + return res.status(200).send({ + integrationAuth + }); } catch (err) { Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); diff --git a/backend/src/controllers/v1/integrationController.ts b/backend/src/controllers/v1/integrationController.ts index 2b52f9725..0ffb8cbb8 100644 --- a/backend/src/controllers/v1/integrationController.ts +++ b/backend/src/controllers/v1/integrationController.ts @@ -1,4 +1,5 @@ import { Request, Response } from 'express'; +import { Types } from 'mongoose'; import * as Sentry from '@sentry/node'; import { Integration, @@ -16,20 +17,31 @@ import { eventPushSecrets } from '../../events'; * @returns */ export const createIntegration = async (req: Request, res: Response) => { - - // TODO: make this more versatile - let integration; try { + const { + integrationAuthId, + app, + appId, + isActive, + targetEnvironment, + owner + } = req.body; + // initialize new integration after saving integration access token integration = await new Integration({ workspace: req.integrationAuth.workspace._id, - isActive: false, - app: null, environment: req.integrationAuth.workspace?.environments[0].slug, + isActive, + app, + appId, + targetEnvironment, integration: req.integrationAuth.integration, - integrationAuth: req.integrationAuth._id + integrationAuth: new Types.ObjectId(integrationAuthId) }).save(); + + // TODO: run sync function + } catch (err) { Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); diff --git a/backend/src/helpers/integration.ts b/backend/src/helpers/integration.ts index 595dbbb6f..30e370188 100644 --- a/backend/src/helpers/integration.ts +++ b/backend/src/helpers/integration.ts @@ -30,6 +30,7 @@ interface Update { * @param {String} obj.workspaceId - id of workspace * @param {String} obj.integration - name of integration * @param {String} obj.code - code + * @returns {IntegrationAuth} integrationAuth - integration auth after OAuth2 code-token exchange */ const handleOAuthExchangeHelper = async ({ workspaceId, @@ -44,7 +45,7 @@ const handleOAuthExchangeHelper = async ({ }) => { let action; let integrationAuth; - let newIntegration; + // let newIntegration; try { const bot = await Bot.findOne({ workspace: workspaceId, @@ -100,25 +101,22 @@ const handleOAuthExchangeHelper = async ({ }); } - // initialize new integration after exchange - newIntegration = await new Integration({ - workspace: workspaceId, - isActive: false, - app: null, - environment, - integration, - integrationAuth: integrationAuth._id - }).save(); + // // initialize new integration after exchange + // newIntegration = await new Integration({ + // workspace: workspaceId, + // isActive: false, + // app: null, + // environment, + // integration, + // integrationAuth: integrationAuth._id + // }).save(); } catch (err) { Sentry.setUser(null); Sentry.captureException(err); throw new Error('Failed to handle OAuth2 code-token exchange') } - return ({ - integrationAuth, - integration: newIntegration - }); + return integrationAuth; } /** * Sync/push environment variables in workspace with id [workspaceId] to diff --git a/backend/src/routes/v1/integration.ts b/backend/src/routes/v1/integration.ts index d587bd2e5..3db9c412e 100644 --- a/backend/src/routes/v1/integration.ts +++ b/backend/src/routes/v1/integration.ts @@ -10,7 +10,7 @@ import { ADMIN, MEMBER } from '../../variables'; import { body, param } from 'express-validator'; import { integrationController } from '../../controllers/v1'; -router.post( // new: add new integration +router.post( // new: add new integration for integration auth '/', requireAuth({ acceptedAuthModes: ['jwt', 'apiKey'] @@ -19,7 +19,12 @@ router.post( // new: add new integration acceptedRoles: [ADMIN, MEMBER], location: 'body' }), - body('integrationAuthId').exists().trim(), + body('integrationAuthId').exists().isString().trim(), + body('app').isString().trim(), + body('isActive').exists().isBoolean(), + body('appId').trim(), + body('targetEnvironment').trim(), + body('owner').trim(), validateRequest, integrationController.createIntegration ); diff --git a/backend/src/services/IntegrationService.ts b/backend/src/services/IntegrationService.ts index cb452f8c8..5b0195427 100644 --- a/backend/src/services/IntegrationService.ts +++ b/backend/src/services/IntegrationService.ts @@ -26,10 +26,7 @@ class IntegrationService { * @param {String} obj1.environment - workspace environment * @param {String} obj1.integration - name of integration * @param {String} obj1.code - code - * @returns {Object} obj2 - * @returns {IntegrationAuth} obj2.integrationAuth - integration authorization after OAuth2 code-token exchange - * @returns {Integration} obj2.integration - newly-initialized integration OAuth2 code-token exchange - * @retrun + * @returns {IntegrationAuth} integrationAuth - integration authorization after OAuth2 code-token exchange */ static async handleOAuthExchange({ workspaceId, diff --git a/frontend/src/hooks/api/workspace/index.tsx b/frontend/src/hooks/api/workspace/index.tsx index 5de95ab05..67d8b9dea 100644 --- a/frontend/src/hooks/api/workspace/index.tsx +++ b/frontend/src/hooks/api/workspace/index.tsx @@ -3,6 +3,7 @@ export { useDeleteWorkspace, useDeleteWsEnvironment, useGetUserWorkspaces, + useGetWorkspaceById, useRenameWorkspace, useUpdateWsEnvironment } from './queries'; diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index b0566a43d..efdf4a6a3 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -11,16 +11,30 @@ import { Workspace } from './types'; + const workspaceKeys = { + getWorkspaceById: (workspaceId: string) => [{ workspaceId }, 'workspace'] as const, getAllUserWorkspace: ['workspaces'] as const }; +const fetchWorkspaceById = async (workspaceId: string) => { + const { data } = await apiRequest.get<{ workspace: Workspace }>(`/api/v1/workspace/${workspaceId}`); + return data.workspace; +} + const fetchUserWorkspaces = async () => { const { data } = await apiRequest.get<{ workspaces: Workspace[] }>('/api/v1/workspace'); - return data.workspaces; }; +export const useGetWorkspaceById = (workspaceId: string) => { + return useQuery({ + queryKey: workspaceKeys.getWorkspaceById(workspaceId), + queryFn: () => fetchWorkspaceById(workspaceId), + enabled: true + }); +}; + export const useGetUserWorkspaces = () => useQuery(workspaceKeys.getAllUserWorkspace, fetchUserWorkspaces); diff --git a/frontend/src/pages/api/integrations/authorizeIntegration.ts b/frontend/src/pages/api/integrations/authorizeIntegration.ts index 666499be6..9c70796b7 100644 --- a/frontend/src/pages/api/integrations/authorizeIntegration.ts +++ b/frontend/src/pages/api/integrations/authorizeIntegration.ts @@ -26,7 +26,7 @@ const AuthorizeIntegration = ({ workspaceId, code, integration }: Props) => }) }).then(async (res) => { if (res && res.status === 200) { - return (res.json()); + return (await res.json()).integrationAuth; } console.log('Failed to authorize the integration'); return undefined; diff --git a/frontend/src/pages/api/integrations/createIntegration.ts b/frontend/src/pages/api/integrations/createIntegration.ts index 6b37f1281..c2da2378a 100644 --- a/frontend/src/pages/api/integrations/createIntegration.ts +++ b/frontend/src/pages/api/integrations/createIntegration.ts @@ -1,7 +1,9 @@ import SecurityClient from '@app/components/utilities/SecurityClient'; interface Props { - integrationAuthId: string; + integrationAuthId: string; + isActive: boolean; + app: string | null; } /** * This route creates a new integration based on the integration authorization with id [integrationAuthId] @@ -10,7 +12,9 @@ interface Props { * @returns */ const createIntegration = ({ - integrationAuthId + integrationAuthId, + isActive, + app }: Props) => SecurityClient.fetchCall('/api/v1/integration', { method: 'POST', @@ -18,7 +22,9 @@ const createIntegration = ({ 'Content-Type': 'application/json' }, body: JSON.stringify({ - integrationAuthId + integrationAuthId, + isActive, + app }) }).then(async (res) => { if (res && res.status === 200) { diff --git a/frontend/src/pages/azure-key-vault.tsx b/frontend/src/pages/azure-key-vault.tsx index 57f861387..71a3c6db7 100644 --- a/frontend/src/pages/azure-key-vault.tsx +++ b/frontend/src/pages/azure-key-vault.tsx @@ -13,71 +13,54 @@ import { Select, SelectItem } from '../components/v2'; +import { useGetWorkspaceById } from '../hooks/api/workspace'; import AuthorizeIntegration from './api/integrations/authorizeIntegration'; -import updateIntegration from './api/integrations/updateIntegration'; -import getAWorkspace from './api/workspace/getAWorkspace'; +import createIntegration from './api/integrations/createIntegration'; -interface Integration { +interface IntegrationAuth { _id: string; - isActive: boolean; - app: string | null; - appId: string | null; + integration: string; + workspace: string; createdAt: string; updatedAt: string; - environment: string; - integration: string; - targetEnvironment: string; - workspace: string; - integrationAuth: string; } export default function AzureKeyVault() { const router = useRouter(); + const workspaceResult = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); - // query-string variables - const parsedUrl = queryString.parse(router.asPath.split('?')[1]); - const {code} = parsedUrl; - const {state} = parsedUrl; + const { code, state } = queryString.parse(router.asPath.split('?')[1]); + + const [integrationAuth, setIntegrationAuth] = useState(null); + const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); - const [integration, setIntegration] = useState(null); - const [environments, setEnvironments] = useState< - { - name: string; - slug: string; - }[] - >([]); - const [environment, setEnvironment] = useState(''); const [vaultBaseUrl, setVaultBaseUrl] = useState(''); const [vaultBaseUrlErrorText, setVaultBaseUrlErrorText] = useState(''); + const [isLoading, setIsLoading] = useState(false); useEffect(() => { (async () => { try { - if (state === localStorage.getItem('latestCSRFToken')) { - localStorage.removeItem('latestCSRFToken'); + if (state !== localStorage.getItem('latestCSRFToken')) return; + localStorage.removeItem('latestCSRFToken'); - const integrationDetails = await AuthorizeIntegration({ - workspaceId: localStorage.getItem('projectData.id') as string, - code: code as string, - integration: 'azure-key-vault', - }); - - setIntegration(integrationDetails.integration); - - const workspaceId = localStorage.getItem('projectData.id'); - if (!workspaceId) return; - - const workspace = await getAWorkspace(workspaceId); - setEnvironment(workspace.environments[0].slug); - setEnvironments(workspace.environments); - - } + setIntegrationAuth(await AuthorizeIntegration({ + workspaceId: localStorage.getItem('projectData.id') as string, + code: code as string, + integration: 'azure-key-vault', + })); } catch (error) { console.error('Azure Key Vault integration error: ', error); } })(); }, []); + + useEffect(() => { + if (workspaceResult && workspaceResult.data) { + setSelectedSourceEnvironment(workspaceResult.data.environments[0].slug); + } + }, [workspaceResult]); const handleButtonClick = async () => { try { @@ -94,17 +77,13 @@ export default function AzureKeyVault() { return; } - if (!integration) return; + if (!integrationAuth?._id) return; setIsLoading(true); - await updateIntegration({ - integrationId: integration._id, + await createIntegration({ + integrationAuthId: integrationAuth?._id, isActive: true, - environment, - app: vaultBaseUrl, - appId: null, - targetEnvironment: null, - owner: null + app: vaultBaseUrl }); setIsLoading(false); @@ -117,7 +96,11 @@ export default function AzureKeyVault() { } } - return (integration && environments.length > 0) ? ( + if (!workspaceResult) return
+ + const { data: w } = workspaceResult; + + return (integrationAuth && w && selectedSourceEnvironment) ? (
Azure Key Vault Integration @@ -126,13 +109,13 @@ export default function AzureKeyVault() { className='mt-4' > diff --git a/frontend/src/pages/heroku.tsx b/frontend/src/pages/heroku.tsx index ff4f2263e..66facdc06 100644 --- a/frontend/src/pages/heroku.tsx +++ b/frontend/src/pages/heroku.tsx @@ -1,41 +1,159 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useRouter } from 'next/router'; import queryString from 'query-string'; +import { getTranslatedServerSideProps } from '@app/components/utilities/withTranslateProps'; + +import { + Button, + Card, + CardTitle, + FormControl, + Select, + SelectItem +} from '../components/v2'; import AuthorizeIntegration from './api/integrations/authorizeIntegration'; +import getIntegrationApps from './api/integrations/GetIntegrationApps'; +import getAWorkspace from './api/workspace/getAWorkspace'; + +interface Integration { + _id: string; + isActive: boolean; + app: string | null; + appId: string | null; + createdAt: string; + updatedAt: string; + environment: string; + integration: string; + targetEnvironment: string; + workspace: string; + integrationAuth: string; +} + +interface IntegrationApp { + name: string; + appId?: string; + owner?: string; +} export default function Heroku() { const router = useRouter(); const parsedUrl = queryString.parse(router.asPath.split('?')[1]); - const {code} = parsedUrl; - const {state} = parsedUrl; + const { code } = parsedUrl; + const { state } = parsedUrl; + + const [integration, setIntegration] = useState(null); + const [environments, setEnvironments] = useState< + { + name: string; + slug: string; + }[] + >([]); + const [environment, setEnvironment] = useState(''); + const [app, setApp] = useState(''); + const [apps, setApps] = useState([]); - /** - * Here we forward to the default workspace if a user opens this url - */ - // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { (async () => { try { if (state === localStorage.getItem('latestCSRFToken')) { localStorage.removeItem('latestCSRFToken'); - await AuthorizeIntegration({ + const integrationDetails = await AuthorizeIntegration({ workspaceId: localStorage.getItem('projectData.id') as string, code: code as string, integration: 'heroku', }); - router.push( - `/integrations/${ localStorage.getItem('projectData.id')}` - ); + + setIntegration(integrationDetails.integration); + + const workspaceId = localStorage.getItem('projectData.id'); + if (!workspaceId) return; + + const workspace = await getAWorkspace(workspaceId); + setEnvironment(workspace.environments[0].slug); + setEnvironments(workspace.environments); + + const tempApps: [IntegrationApp] = await getIntegrationApps({ + integrationAuthId: integrationDetails.integration.integrationAuth + }); + + console.log('tempApps: ', tempApps); + setApp(tempApps[0].name); + setApps(tempApps); + + // router.push( + // `/integrations/${ localStorage.getItem('projectData.id')}` + // ); } } catch (error) { console.error('Heroku integration error: ', error); } })(); - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + + const handleButtonClick = async () => { + try { + console.log('handleButtonClick'); + console.log('project environment: ', environment); + console.log('app', app); + } catch (err) { + console.error(err); + } + } + + console.log('integration: ', integration); + console.log('environments: ', environments); + console.log('apps: ', apps); - return
; + return (integration && environments.length > 0 && apps.length > 0) ? ( +
+ + Heroku Integration + + + + + + + + +
+ ) :
Hello
} Heroku.requireAuth = true; + +export const getServerSideProps = getTranslatedServerSideProps(['integrations']); \ No newline at end of file diff --git a/frontend/src/pages/integrations/[id].tsx b/frontend/src/pages/integrations/[id].tsx index 688d7c34f..f065494cd 100644 --- a/frontend/src/pages/integrations/[id].tsx +++ b/frontend/src/pages/integrations/[id].tsx @@ -235,7 +235,9 @@ export default function Integrations() { setIntegrationAuths([...integrationAuths, integrationAuth]) const integration = await createIntegration({ - integrationAuthId: integrationAuth._id + integrationAuthId: integrationAuth._id, + isActive: false, + app: null }); setIntegrations([...integrations, integration]);