From 98dcc42db25ce1ad13fe806e53884cdfe3e638da Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sat, 8 Apr 2023 22:17:38 +0300 Subject: [PATCH] Begin Railway integration, frontend, getApps --- backend/src/integrations/apps.ts | 61 +++++++++ backend/src/models/integration.ts | 3 + backend/src/models/integrationAuth.ts | 4 +- backend/src/variables/index.ts | 4 + backend/src/variables/integration.ts | 13 ++ .../public/images/integrations/Railway.png | Bin 0 -> 51017 bytes frontend/src/pages/integrations/[id].tsx | 7 + .../pages/integrations/railway/authorize.tsx | 77 +++++++++++ .../src/pages/integrations/railway/create.tsx | 123 ++++++++++++++++++ .../src/pages/integrations/render/create.tsx | 2 +- 10 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 frontend/public/images/integrations/Railway.png create mode 100644 frontend/src/pages/integrations/railway/authorize.tsx create mode 100644 frontend/src/pages/integrations/railway/create.tsx diff --git a/backend/src/integrations/apps.ts b/backend/src/integrations/apps.ts index f7eef6263..dae146bc6 100644 --- a/backend/src/integrations/apps.ts +++ b/backend/src/integrations/apps.ts @@ -12,6 +12,7 @@ import { INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_RENDER, + INTEGRATION_RAILWAY, INTEGRATION_FLYIO, INTEGRATION_CIRCLECI, INTEGRATION_TRAVISCI, @@ -20,6 +21,7 @@ import { INTEGRATION_VERCEL_API_URL, INTEGRATION_NETLIFY_API_URL, INTEGRATION_RENDER_API_URL, + INTEGRATION_RAILWAY_API_URL, INTEGRATION_FLYIO_API_URL, INTEGRATION_CIRCLECI_API_URL, INTEGRATION_TRAVISCI_API_URL, @@ -94,6 +96,11 @@ const getApps = async ({ accessToken, }); break; + case INTEGRATION_RAILWAY: + apps = await getAppsRailway({ + accessToken + }); + break; case INTEGRATION_FLYIO: apps = await getAppsFlyio({ accessToken, @@ -319,6 +326,60 @@ const getAppsRender = async ({ accessToken }: { accessToken: string }) => { return apps; }; +/** + * Return list of projects for Railway integration + * @param {Object} obj + * @param {String} obj.accessToken - access token for Railway API + * @returns {Object[]} apps - names and ids of Railway services + * @returns {String} apps.name - name of Railway project + * @returns {String} apps.appId - id of Railway project + * +*/ +const getAppsRailway = async ({ accessToken }: { accessToken: string }) => { + let apps: any[] = []; + try { + const query = ` + query GetProjects($userId: String, $teamId: String) { + projects(userId: $userId, teamId: $teamId) { + edges { + node { + id + name + } + } + } + } + `; + + const variables = { + // userId: '', // Replace with the desired user ID or remove if not needed + // teamId: '', // Replace with the desired team ID or remove if not needed + }; + + const { data: { data: { projects: { edges }}} } = await request.post(INTEGRATION_RAILWAY_API_URL, { + query, + variables, + }, { + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + }); + + apps = edges.map((e: any) => ({ + name: e.node.name, + appId: e.node.id + })); + + } catch (err) { + Sentry.setUser(null); + Sentry.captureException(err); + throw new Error("Failed to get Railway services"); + } + + return apps; +} + /** * Return list of apps for Fly.io integration * @param {Object} obj diff --git a/backend/src/models/integration.ts b/backend/src/models/integration.ts index 698846ea7..4cfa739e6 100644 --- a/backend/src/models/integration.ts +++ b/backend/src/models/integration.ts @@ -9,6 +9,7 @@ import { INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_RENDER, + INTEGRATION_RAILWAY, INTEGRATION_FLYIO, INTEGRATION_CIRCLECI, INTEGRATION_TRAVISCI, @@ -35,6 +36,7 @@ export interface IIntegration { | 'github' | 'gitlab' | 'render' + | 'railway' | 'flyio' | 'circleci' | 'travisci'; @@ -98,6 +100,7 @@ const integrationSchema = new Schema( INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_RENDER, + INTEGRATION_RAILWAY, INTEGRATION_FLYIO, INTEGRATION_CIRCLECI, INTEGRATION_TRAVISCI, diff --git a/backend/src/models/integrationAuth.ts b/backend/src/models/integrationAuth.ts index 43bef63ad..64c4f2d69 100644 --- a/backend/src/models/integrationAuth.ts +++ b/backend/src/models/integrationAuth.ts @@ -9,6 +9,7 @@ import { INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_RENDER, + INTEGRATION_RAILWAY, INTEGRATION_FLYIO, INTEGRATION_CIRCLECI, INTEGRATION_TRAVISCI, @@ -17,7 +18,7 @@ import { export interface IIntegrationAuth { _id: Types.ObjectId; workspace: Types.ObjectId; - integration: 'heroku' | 'vercel' | 'netlify' | 'github' | 'gitlab' | 'render' | 'flyio' | 'azure-key-vault' | 'circleci' | 'travisci' | 'aws-parameter-store' | 'aws-secret-manager'; + integration: 'heroku' | 'vercel' | 'netlify' | 'github' | 'gitlab' | 'render' | 'railway' | 'flyio' | 'azure-key-vault' | 'circleci' | 'travisci' | 'aws-parameter-store' | 'aws-secret-manager'; teamId: string; accountId: string; refreshCiphertext?: string; @@ -51,6 +52,7 @@ const integrationAuthSchema = new Schema( INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_RENDER, + INTEGRATION_RAILWAY, INTEGRATION_FLYIO, INTEGRATION_CIRCLECI, INTEGRATION_TRAVISCI, diff --git a/backend/src/variables/index.ts b/backend/src/variables/index.ts index 1e2ed4ae9..3029b265f 100644 --- a/backend/src/variables/index.ts +++ b/backend/src/variables/index.ts @@ -15,6 +15,7 @@ import { INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_RENDER, + INTEGRATION_RAILWAY, INTEGRATION_FLYIO, INTEGRATION_CIRCLECI, INTEGRATION_TRAVISCI, @@ -31,6 +32,7 @@ import { INTEGRATION_VERCEL_API_URL, INTEGRATION_NETLIFY_API_URL, INTEGRATION_RENDER_API_URL, + INTEGRATION_RAILWAY_API_URL, INTEGRATION_FLYIO_API_URL, INTEGRATION_CIRCLECI_API_URL, INTEGRATION_TRAVISCI_API_URL, @@ -96,6 +98,7 @@ export { INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_RENDER, + INTEGRATION_RAILWAY, INTEGRATION_FLYIO, INTEGRATION_CIRCLECI, INTEGRATION_TRAVISCI, @@ -112,6 +115,7 @@ export { INTEGRATION_VERCEL_API_URL, INTEGRATION_NETLIFY_API_URL, INTEGRATION_RENDER_API_URL, + INTEGRATION_RAILWAY_API_URL, INTEGRATION_FLYIO_API_URL, INTEGRATION_CIRCLECI_API_URL, INTEGRATION_TRAVISCI_API_URL, diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index 52bfcc614..3c25c79da 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -17,6 +17,7 @@ const INTEGRATION_NETLIFY = "netlify"; const INTEGRATION_GITHUB = "github"; const INTEGRATION_GITLAB = "gitlab"; const INTEGRATION_RENDER = "render"; +const INTEGRATION_RAILWAY = "railway"; const INTEGRATION_FLYIO = "flyio"; const INTEGRATION_CIRCLECI = "circleci"; const INTEGRATION_TRAVISCI = "travisci"; @@ -52,6 +53,7 @@ const INTEGRATION_GITLAB_API_URL = "https://gitlab.com/api"; const INTEGRATION_VERCEL_API_URL = "https://api.vercel.com"; const INTEGRATION_NETLIFY_API_URL = "https://api.netlify.com"; const INTEGRATION_RENDER_API_URL = "https://api.render.com"; +const INTEGRATION_RAILWAY_API_URL = "https://backboard.railway.app/graphql/v2"; const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql"; const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api"; const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com"; @@ -104,6 +106,15 @@ const getIntegrationOptions = () => { clientId: '', docsLink: '' }, + { + name: 'Railway', + slug: 'railway', + image: 'Railway.png', + isAvailable: true, + type: 'pat', + clientId: '', + docsLink: '' + }, { name: 'Fly.io', slug: 'flyio', @@ -192,6 +203,7 @@ export { INTEGRATION_GITHUB, INTEGRATION_GITLAB, INTEGRATION_RENDER, + INTEGRATION_RAILWAY, INTEGRATION_FLYIO, INTEGRATION_CIRCLECI, INTEGRATION_TRAVISCI, @@ -208,6 +220,7 @@ export { INTEGRATION_VERCEL_API_URL, INTEGRATION_NETLIFY_API_URL, INTEGRATION_RENDER_API_URL, + INTEGRATION_RAILWAY_API_URL, INTEGRATION_FLYIO_API_URL, INTEGRATION_CIRCLECI_API_URL, INTEGRATION_TRAVISCI_API_URL, diff --git a/frontend/public/images/integrations/Railway.png b/frontend/public/images/integrations/Railway.png new file mode 100644 index 0000000000000000000000000000000000000000..61d53cf62ffebacdf73f0ac65fb4affe663607b6 GIT binary patch literal 51017 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

+mH&im>839F6?ee?AlAXKK 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 TZgJUwKF-F{-lFJ;SKNO9)bSFV literal 0 HcmV?d00001 diff --git a/frontend/src/pages/integrations/[id].tsx b/frontend/src/pages/integrations/[id].tsx index c3c625a79..f58d1580d 100644 --- a/frontend/src/pages/integrations/[id].tsx +++ b/frontend/src/pages/integrations/[id].tsx @@ -207,6 +207,10 @@ export default function Integrations() { case 'travisci': link = `${window.location.origin}/integrations/travisci/authorize`; break; + case 'railway': + console.log('handleUnauthorized Railway: ', integrationOption); + link = `${window.location.origin}/integrations/railway/authorize`; + break; default: break; } @@ -259,6 +263,9 @@ export default function Integrations() { case 'travisci': link = `${window.location.origin}/integrations/travisci/create?integrationAuthId=${integrationAuth._id}`; break; + case 'railway': + console.log('handleAuthorized Railway: ', integrationAuth); + break; default: break; } diff --git a/frontend/src/pages/integrations/railway/authorize.tsx b/frontend/src/pages/integrations/railway/authorize.tsx new file mode 100644 index 000000000..94d3f06bf --- /dev/null +++ b/frontend/src/pages/integrations/railway/authorize.tsx @@ -0,0 +1,77 @@ +import { useState } from 'react'; +import { useRouter } from 'next/router'; + +import { getTranslatedServerSideProps } from '../../../components/utilities/withTranslateProps'; +import { + Button, + Card, + CardTitle, + FormControl, + Input, +} from '../../../components/v2'; +import saveIntegrationAccessToken from "../../api/integrations/saveIntegrationAccessToken"; + +export default function RailwayAuthorizeIntegrationPage() { + const router = useRouter(); + const [apiKey, setApiKey] = useState(''); + const [apiKeyErrorText, setApiKeyErrorText] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const handleButtonClick = async () => { + try { + setApiKeyErrorText(''); + if (apiKey.length === 0) { + setApiKeyErrorText('API Key cannot be blank'); + return; + } + + setIsLoading(true); + + const integrationAuth = await saveIntegrationAccessToken({ + workspaceId: localStorage.getItem('projectData.id'), + integration: 'railway', + accessId: null, + accessToken: apiKey + }); + + setIsLoading(false); + + router.push( + `/integrations/railway/create?integrationAuthId=${integrationAuth._id}` + ); + } catch (err) { + console.error(err); + } + } + + return ( +

+ + Railway Integration + + setApiKey(e.target.value)} + /> + + + +
+ ); +} + +RailwayAuthorizeIntegrationPage.requireAuth = true; + +export const getServerSideProps = getTranslatedServerSideProps(['integrations']); \ No newline at end of file diff --git a/frontend/src/pages/integrations/railway/create.tsx b/frontend/src/pages/integrations/railway/create.tsx new file mode 100644 index 000000000..11e948628 --- /dev/null +++ b/frontend/src/pages/integrations/railway/create.tsx @@ -0,0 +1,123 @@ +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; +import queryString from 'query-string'; + +import { getTranslatedServerSideProps } from '../../../components/utilities/withTranslateProps'; +import { + Button, + Card, + CardTitle, + FormControl, + Select, + SelectItem +} from '../../../components/v2'; +import { useGetIntegrationAuthApps, useGetIntegrationAuthById } from '../../../hooks/api/integrationAuth'; +import { useGetWorkspaceById } from '../../../hooks/api/workspace'; + +export default function RailwayCreateIntegrationPage() { + const router = useRouter(); + + const [targetAppId, setTargetAppId] = useState(''); + const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const { integrationAuthId } = queryString.parse(router.asPath.split('?')[1]); + const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); + const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); + const { data: integrationAuthApps } = useGetIntegrationAuthApps({ + integrationAuthId: integrationAuthId as string ?? '' + }); + + + useEffect(() => { + if (workspace) { + setSelectedSourceEnvironment(workspace.environments[0].slug); + } + }, [workspace]); + + useEffect(() => { + if (integrationAuthApps) { + if (integrationAuthApps.length > 0) { + setTargetAppId(integrationAuthApps[0].appId as string); + } else { + setTargetAppId('none'); + } + } + }, [integrationAuthApps]); + + const handleButtonClick = async () => { + try { + setIsLoading(true); + + if (!integrationAuth?._id) return; + + // const targetApp = integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId))?.name; + + console.log('handleButtonClick'); + + setIsLoading(false); + } catch (err) { + console.error(err); + } + } + + console.log('integrationAuthApps', integrationAuthApps); + + return workspace && selectedSourceEnvironment && integrationAuthApps ? ( +
+ + Railway Integration + + + + + + + + +
+ ) :
+} + +RailwayCreateIntegrationPage.requireAuth = true; + +export const getServerSideProps = getTranslatedServerSideProps(['integrations']); + diff --git a/frontend/src/pages/integrations/render/create.tsx b/frontend/src/pages/integrations/render/create.tsx index cc61eec3e..e5ee04e0a 100644 --- a/frontend/src/pages/integrations/render/create.tsx +++ b/frontend/src/pages/integrations/render/create.tsx @@ -11,7 +11,7 @@ import { Select, SelectItem } from '../../../components/v2'; -import { useGetIntegrationAuthApps,useGetIntegrationAuthById } from '../../../hooks/api/integrationAuth'; +import { useGetIntegrationAuthApps, useGetIntegrationAuthById } from '../../../hooks/api/integrationAuth'; import { useGetWorkspaceById } from '../../../hooks/api/workspace'; import createIntegration from "../../api/integrations/createIntegration";