From 1dd33505f64cafef98324c64704026d74ff63d56 Mon Sep 17 00:00:00 2001 From: Esteban Dalel R Date: Tue, 1 Aug 2023 16:25:14 -0500 Subject: [PATCH] Feature/login to service in app router (#218) * send correct props * Add notion back * Add notion querying * Add logging of result * Fix getting of issue comments * correctly pass props to notion (#179) * correctly pass props to notion * Update README.md * fix undefined id error * Fix logging * Add better error logging * Fix error due to no notion version * Fix result returning * add logging * Fix logging * Add notion to settings * Add notion to app * fix newline * Remove extra character * Add page image support * fix title * fix image display * fix no token error * Filter out private messages * Allow getting less than standard results * Create getHoverData.ts * fix token passing * Fix typo * Now uses commitTitle * Add new users to Sendgrid contact list * Scrub action for app * Fixes to slack matches * Fix/remove action (#178) * Delete watermelon.yml * Change dropdown link * Change login link * Change dashboard link * small fixes * Move text writers to helpers * Fix comment editing * Export helpers * Add necessary imports * Change logging * Add non-registered user response * Earlier counting of action uses * reduce logging * Add counting to comment * Add error checking * Add check for open repos * Add error writing * Extract count markdown * Fix/typings improvements (#183) * Add typings * Fix possible nulls * Add LoginGridProps type * Fix possible null * remove breaklines * Add export * Add import * Update README.md, add title (#180) * Update README.md, add title * Update README.md * Update README.md * Update README.md (#181) * Chore/codebase typings and nulls (#185) * Allow max to be 5 * check nulls * add typings * fix possible null * Add typings * Add linear link for oauth * Feature/linear (#186) * Allow max to be 5 * Create linear.tsx * Feature/linear (#187) * Allow max to be 5 * Create linear.tsx * Update linear.tsx * Add logging * improve logging * Fix linear link * Change encoding of request * Fix body of request * Fix userquery * Save user, pass team name * remove unused code * logging * try text * Create LinearLoginLink.tsx * Try new body * Update linear.tsx * Fix logo * Create linear.svg * add content type * Add linear * try fix * Fix data matching * Fix team * Removed unused link * Fix email * Feature/team and navbar (#182) * add server-only * add client-only * Move to RSC * remove logging * fix typings * fix typings * add LoginGridProps * Fix possibly null * Add types * Check nulls * fix possible nulls * ignore errors in lightly used api * Set conditionally * Check null * check null * fix typings * fix null errors * fix null * add typings * fix props passing * Add typing * Add typing * fix typings * Fix typing errors * use nonnull assertion * Add styles * Move to app folder * Adhere to app api route * Make it non default * upgrade next-auth * restore session provider * Fixes in adapter * move auth to pages to test use * Fix login route * fix import * Pass authprovider * Added sidebar test * Add sidebar * Fix navbar, extract navbar * Fix layout * Hide elements if no session * Update Navbar.tsx * Fix layout * Make app dark * Extract form * Extract navbar * Move to app * Move layout out * Remove logging * REmove logging * Move logingrid to RSC * Remove logingrid * Add layout * Remove logging * Remove logging * Create getTeammates.ts * Create Team page * Fix heading * Delete github.tsx * Move to App router * Update README.md, add title (#180) * Update README.md, add title * Update README.md * Update README.md * Update README.md (#181) * Move to RSC * Fix Try app ui * Remove data logging * Make card details a page * Move layout to master layout * Fix type * Fix layout order * Remove billing link * log response * Move back to pages * Add search params to billing page * Create loading.tsx * Get settings on load * Adde repo owner and number to charge * Pass number from param * Force prompt * fix params * Add payment success page * Add billing link to navbar * Remove text that explains repo and seats in Card Elements * Add texts that explains purchase amount * Add linear to form (#190) * Add linear to form * Add Linear to query * Create getLinear.ts * Add linear fetching * Change query to add limit * Add logging * Fix search terms * Improve logging * Update linear.ts * Add linear to app (#192) * Create linear.ts * add no token handling * Check nulls * Fix search terms * export module * Naming fixes * manage empty results * Fix text * Fix newline * Code fixes * Fix undefined team count * Add teammates (#193) * Add button * Code cleanup * Create page.tsx * Execute request on landing * fix empty teammates * fix null teammates * add logging * better logging * Fix params * Rmove logging * Get team and copy to clipboard * paralelize requests, shorten code * send correct object * Add interaction * Add plaintext to copy * Fix url * Create loading.tsx * Check for data before render * Add catch to data fetch * Fix return * Update layout.tsx * Update loading.tsx * Update loading.tsx * Update loading.tsx * Move app link up * fix button text * Glow up * Create loading.tsx * Change email to info * Create sendTeammateInvite.ts * Add emailer form * Fix template id * Fix sending handler * Move to API call * Update sendInviteForm.tsx * Update sendTeammateInvite.ts * Cleanup * Create loading.tsx * Remove params * Feature/save gh response (#194) * Add saving query * fix randomwords * Improve logging * stringify responses * Check nulls * Fix count number in log saving * Correct wm user * Extract to function (#196) * Create addActionLog.ts * Use extracted method * Reduce file size * Remove logging * Reorg imports (#197) * Reorg imports * Update github.ts * Update github.ts * Feature/extract gh action loggin (#198) * Create addActionLog.ts * Use extracted method * Reduce file size * Remove logging * fix id for team * Fix saved string * Feature/extract gh action loggin (#200) * Create addActionLog.ts * Use extracted method * Reduce file size * Remove logging * fix id for team * Fix saved string * Add replacing of apostrophe to fix query * add \n to AI summary error (#199) * add \n to AI summary error * Update github.ts * Feature/extract gh action logging (#202) * Feature/page titles (#203) * Delete sidebar.tsx (#201) * Feature/posthog analytics (#204) * Add posthogjs * Create providers.tsx * Add posthog * Add posthognode * Create posthogTracker.ts * Add tracker to gh action * make posthog early * fix list filtering * Rename tracker * Add tracker * Add tracker * Streamline data fetching * Add tracker * Feature/confluence login (#206) * Create ConfluenceLoginLink.tsx * Add confluence * Create atlassian page * Remove primer * Add primer * change link * Fix link * fix link, text * Emergency removal of posthog * REmove all posthog instances * Correct redirect uri * better logging * Update atlassian.tsx * conditionally add pic * add logging * Add discrimination to jira and confluence * Save confluence * fix link * Fix img * debug logging * Fix query * Debug logging * change grant typ * Go back a grant type * change logging * Fix url * Some more data wrangling * fix saving * Fix image * Reorg dashboard * Add coming soon ides * Style navbar better * Make sidebar sticky, add logout button * Add workspace query * Ignore name * Remove unused code * ignore calling * try other import to release * Move email to client * Change logging * fix obj accessing * Add workspace, request emails * Add email, optimize requests * fix query * Get refresh token on login * Add confluence * Improve logging * reduce logging * Better logging * Create confluence.ts * Add confluence * Fix value * Create confluence.ts * Add logging * logging * Check if null * Fix nulls * Change how tokens are updated * Rename * Fixes to query executing * Rename action * Fixes and error handling * Fix missing param * Add offline access scope * Several fixes to tokens * Logging improvements * Delete test Api * Reduce throwing * Print full payload in this repo * Stringify full response * Remove data dumping * Change pricing calculation and text to * Update README.md (#207) * Update README.md * Update README.md * Update README.md * Update README.md * Feature/confluence settings (#211) * Make all unlogged services lose title * Create watermelon.ts * Use standard type * use standard types * Use standard types * Create general case helper * Make code more readable * Allow number to be a string, like a slack channel * Add possible body * Standardize helpers * Remove unused helpers * Add possible image element * Remove unused code * use standard response * remove logging, fix text * Fix token errors in confluence * Better responses on success and failure * Check nulls * Add max results * Add limit using amount * create StandardAPIInput * Fix type * Create OptionDropdown component * Change to max 5 * Fix code * Make it component based, add confluence * Remove component unused * Fix loading page * Feature/standard api responses (#209) * Make all unlogged services lose title * Create watermelon.ts * Use standard type * use standard types * Use standard types * Create general case helper * Make code more readable * Allow number to be a string, like a slack channel * Add possible body * Standardize helpers * Remove unused helpers * Add possible image element * Remove unused code * use standard response * remove logging, fix text * Fix token errors in confluence * Better responses on success and failure * Check nulls * Add max results * Add limit using amount * create StandardAPIInput * Fix type * Create OptionDropdown component * Change to max 5 * Fix code * Removed unused element * Reduce code size * Add confluence * Reduce code * Further reduce code * Remove unneeded brackets * Fix links (#215) * Create LICENSE (#216) Adding an Apache 2.0 license with a Commons clause to this repo to be able to make it source-available and become a buyer based open core company * Feature/all uses approuter (#217) * Use const instead of let * Change to app router * export page * Fix redirect with client component * redirect instead of opening new window * Test suggesting other services * Fix file * fix json * Fix suggestions UI * Correctly use ternary * Fix small error * Create loading.tsx * standardize token name * Standardize service name * Move gitlab to approuter * Move github to approuter * Standardize name * Add comments * Remove page replication * Move notion to approuter * Move slack to approuter * Move bitbucket to approuter * Create general page component * Use component page * Use component page * Remove unused imports * Ignore fleet * Fix username * Move Jira to App router * Move Discord to App Router * Move Atlassian to App Router * Create general loginArray function * Remove conflicting files --------- Co-authored-by: baristaGeek --- app/linear/page.tsx | 88 +++++++------------------ pages/atlassian.tsx | 155 -------------------------------------------- pages/discord.tsx | 99 ---------------------------- pages/notion.tsx | 142 ---------------------------------------- 4 files changed, 22 insertions(+), 462 deletions(-) delete mode 100644 pages/atlassian.tsx delete mode 100644 pages/discord.tsx delete mode 100644 pages/notion.tsx diff --git a/app/linear/page.tsx b/app/linear/page.tsx index d6ffa99ba..683145b3d 100644 --- a/app/linear/page.tsx +++ b/app/linear/page.tsx @@ -1,18 +1,14 @@ -import Link from "next/link"; import { getServerSession } from "next-auth"; - +//change this to import correctly import saveUserInfo from "../../utils/db/linear/saveUser"; import { authOptions } from "../api/auth/[...nextauth]/route"; -import TimeToRedirect from "../../components/redirect"; import getAllPublicUserData from "../../utils/api/getAllUserPublicData"; -import SlackLoginLink from "../../components/SlackLoginLink"; -import NotionLoginLink from "../../components/NotionLoginLink"; -import ConfluenceLoginLink from "../../components/ConfluenceLoginLink"; -import GitHubLoginLink from "../../components/GitHubLoginLink"; +import ConnectedService from "../../utils/services/page"; +import LoginArray from "../../utils/services/loginArray"; -export default async function Linear({ +export default async function ServicePage({ searchParams, }: { searchParams: { [key: string]: string | string[] | undefined }; @@ -22,12 +18,14 @@ export default async function Linear({ const userName = session?.user?.name; const { code, state } = searchParams; let error = ""; - - const [userData, linearToken] = await Promise.all([ + // change service name + const serviceName = "Linear"; + const [userData, serviceToken] = await Promise.all([ getAllPublicUserData({ userEmail }).catch((e) => { console.error(e); return null; }), + // change this fetch fetch(`https://api.linear.app/oauth/token`, { method: "POST", headers: { @@ -38,35 +36,11 @@ export default async function Linear({ }), ]); - const services = [ - { - name: "GitHub", - dataProp: "github_data", - loginComponent: , - }, - { - name: "Slack", - dataProp: "slack_data", - loginComponent: , - }, - { - name: "Confluence", - dataProp: "confluence_data", - loginComponent: , - }, - { - name: "Notion", - dataProp: "notion_data", - loginComponent: , - }, - ]; - const loginArray = services - .map((service) => - userData?.[service.dataProp] ? null : service.loginComponent - ) - .filter((component) => component !== null); + // the recommended services should not be of the same category as the current one + const nameList = ["GitHub", "Slack", "Notion", "Confluence"]; + const loginArray = LoginArray({ nameList, userEmail, userData }); - const json = await linearToken.json(); + const json = await serviceToken.json(); if (json.error) { error = json.error; } else { @@ -75,6 +49,7 @@ export default async function Linear({ "query Me {\nviewer {\n id,\n name,\n displayName, email,\n avatarUrl\n},\nteams {\n nodes {\n id,\n name\n }\n}\n}", variables: {}, }); + // get user correctly let user = await fetch(`https://api.linear.app/graphql`, { method: "POST", headers: { @@ -85,6 +60,7 @@ export default async function Linear({ }); let userText = await user.text(); let userJson = JSON.parse(userText).data; + // save user correctly await saveUserInfo({ access_token: json.access_token, id: userJson.viewer.id, @@ -98,34 +74,14 @@ export default async function Linear({ }); return ( -
-
-

- You have logged in with Linear as {userJson.viewer.displayName} in - the team {userJson.teams.nodes[0].name} -

-
- linear user image -
- -

- If you are not redirected, please click here -

- {loginArray.length ? ( -
-

You might also be interested:

- {loginArray.map((login) => ( - <>{login} - ))} -
- ) : null} - {error &&

{error}

} -
-
+ ); } } diff --git a/pages/atlassian.tsx b/pages/atlassian.tsx deleted file mode 100644 index 8ff382ffe..000000000 --- a/pages/atlassian.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { useEffect, useState } from "react"; -import { useRouter } from "next/router"; -import Link from "next/link"; -import saveJiraUserInfo from "../utils/db/jira/saveUserInfo"; -import saveConfluenceUserInfo from "../utils/db/confluence/saveUserInfo"; -import GitHubLoginLink from "../components/GitHubLoginLink"; -export default function Jira({ organization, avatar_url, userEmail, error }) { - const [timeToRedirect, setTimeToRedirect] = useState(10); - const router = useRouter(); - useEffect(() => { - const interval = setInterval(() => { - setTimeToRedirect(timeToRedirect - 1); - if (timeToRedirect === 0) { - router.push("/"); - } - }, 1000); - return () => clearInterval(interval); - }, [timeToRedirect]); - let isConfluence = userEmail.startsWith("c"); - - return ( -
-
-

- You have logged in with {isConfluence ? "Confluence" : "Jira"} to{" "} - {organization} -

-
- {`${isConfluence -
-

We recommend you login to GitHub

- -
-
-

You will be redirected in {timeToRedirect}...

-

- If you are not redirected, please click here -

- {error &&

{error}

} -
-
- ); -} -export async function getServerSideProps(context) { - let f; - - if (context.query.code) { - f = await fetch(`https://auth.atlassian.com/oauth/token`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - grant_type: "authorization_code", - code: context.query.code, - redirect_uri: "https://app.watermelontools.com/atlassian", - client_id: process.env.NEXT_PUBLIC_JIRA_CLIENT_ID, - client_secret: process.env.JIRA_CLIENT_SECRET, - }), - }); - } else - return { - props: { - error: "no code", - }, - }; - const json = await f.json(); - if (json.error) { - console.error("Atlassian error", json); - return { - props: { - error: json.error, - }, - }; - } else { - const { access_token } = json; - const orgInfo = await fetch( - "https://api.atlassian.com/oauth/token/accessible-resources", - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${access_token}`, - }, - } - ); - const orgInfoJson = await orgInfo.json(); - let isConfluence = context.query.state.startsWith("c"); - if (isConfluence) { - const userInfo = await fetch( - `https://api.atlassian.com/ex/confluence/${orgInfoJson[0].id}/rest/api/user/current`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${access_token}`, - }, - } - ); - const userInfoJson = await userInfo.json(); - await saveConfluenceUserInfo({ - access_token: json.access_token, - refresh_token: json.refresh_token, - confluence_id: orgInfoJson[0].id, - organization: orgInfoJson[0].name, - url: orgInfoJson[0].url, - org_avatar_url: orgInfoJson[0].avatarUrl, - scopes: orgInfoJson[0].scopes, - watermelon_user: context.query.state.slice(1), - user_email: userInfoJson.email, - user_avatar_url: - orgInfoJson[0].url + userInfoJson?.profilePicture?.path, - user_id: userInfoJson.accountId, - user_displayname: userInfoJson.displayName, - }); - } else { - const userInfo = await fetch( - `https://api.atlassian.com/ex/jira/${orgInfoJson[0].id}/rest/api/3/myself`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${access_token}`, - }, - } - ); - const userInfoJson = await userInfo.json(); - await saveJiraUserInfo({ - access_token: json.access_token, - refresh_token: json.refresh_token, - jira_id: orgInfoJson[0].id, - organization: orgInfoJson[0].name, - url: orgInfoJson[0].url, - org_avatar_url: orgInfoJson[0].avatarUrl, - scopes: orgInfoJson[0].scopes, - watermelon_user: context.query.state.slice(1), - user_email: userInfoJson.emailAddress, - user_avatar_url: userInfoJson?.avatarUrls?.["48x48"], - user_id: userInfoJson.accountId, - user_displayname: userInfoJson.displayName, - }); - } - return { - props: { - userEmail: context.query.state, - organization: orgInfoJson[0]?.name, - avatar_url: orgInfoJson[0]?.avatarUrl, - }, - }; - } -} diff --git a/pages/discord.tsx b/pages/discord.tsx deleted file mode 100644 index 62e933152..000000000 --- a/pages/discord.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { useEffect, useState } from "react"; -import { useRouter } from "next/router"; -import Link from "next/link"; -import JiraLoginLink from "../components/JiraLoginLink"; -import saveUser from "../utils/db/discord/saveUser"; -export default function Discord({ userData, userEmail, error }) { - const [timeToRedirect, setTimeToRedirect] = useState(10); - const router = useRouter(); - useEffect(() => { - const interval = setInterval(() => { - setTimeToRedirect(timeToRedirect - 1); - if (timeToRedirect === 0) { - router.push("/"); - } - }, 1000); - return () => clearInterval(interval); - }, [timeToRedirect]); - - return ( -
-
-

- You have logged in with Discord as {userData.username} -

- github user image -
- -
-

We recommend you login to Jira

- -
-
-

You will be redirected in {timeToRedirect}...

-

- If you are not redirected, please click here -

- {error &&

{error}

} -
-
- ); -} - -export async function getServerSideProps(context) { - let f; - if (context.query.code) { - const API_ENDPOINT = "https://discord.com/api/v10"; - const CLIENT_ID = process.env.DISCORD_CLIENT_ID; - const CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET; - const REDIRECT_URI = "https://app.watermelontools.com/discord"; - const data = { - client_id: CLIENT_ID, - client_secret: CLIENT_SECRET, - grant_type: "authorization_code", - code: context.query.code, - redirect_uri: REDIRECT_URI, - }; - const headers = { - "Content-Type": "application/x-www-form-urlencoded", - }; - const response = await fetch(`${API_ENDPOINT}/oauth2/token`, { - method: "POST", - headers: headers, - body: new URLSearchParams(data.toString()), - }); - const json = await response.json(); - const user = await fetch(`${API_ENDPOINT}/users/@me`, { - headers: { - Authorization: `Bearer ${json.access_token}`, - }, - }); - - const userJson = await user.json(); - await saveUser({ - access_token: json.access_token, - scope: json.scope, - username: userJson.username, - id: userJson.id, - avatar_url: userJson.avatar, - watermelon_user: context.query.state, - email: userJson.email, - refresh_token: json.refresh_token, - }); - return { - props: { - userData: userJson, - userEmail: context.query.state, - }, - }; - } else - return { - props: { - error: "no code", - }, - }; -} diff --git a/pages/notion.tsx b/pages/notion.tsx deleted file mode 100644 index ca5e93cee..000000000 --- a/pages/notion.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { useEffect, useState } from "react"; -import { useRouter } from "next/router"; -import Link from "next/link"; -import saveUser from "../utils/db/notion/saveUser"; -import JiraLoginLink from "../components/JiraLoginLink"; -export default function GitHub({ - login, - avatar_url, - userEmail, - workspace, - error, -}) { - const [hasPaid, setHasPaid] = useState(false); - const [timeToRedirect, setTimeToRedirect] = useState(10); - - const router = useRouter(); - - useEffect(() => { - const interval = setInterval(() => { - setTimeToRedirect(timeToRedirect - 1); - if (timeToRedirect === 0) { - router.push("/"); - } - }, 1000); - return () => clearInterval(interval); - }, [timeToRedirect]); - - useEffect(() => { - // use getByEmail to check if user has paid - fetch("/api/payments/getByEmail", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ email: userEmail }), - }) - .then((res) => res.json()) - .then((data) => { - if (data.email) { - setHasPaid(true); - } - }); - }, []); - - return ( -
-
-

- You have logged in with Notion as {login} in the workspace {workspace} -

-
- github user image -
-

We recommend you login to Jira

- -
-
-

You will be redirected in {timeToRedirect}...

-

- If you are not redirected, please click here -

- {error &&

{error}

} -
-
- ); -} - -export async function getServerSideProps(context) { - let f; - if (context.query.code) { - f = await fetch(`https://api.notion.com/v1/oauth/token`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - Authorization: `Basic "${btoa( - process.env.NOTION_CLIENT_ID + ":" + process.env.NOTION_CLIENT_SECRET - )}"`, - }, - body: JSON.stringify({ - grant_type: "authorization_code", - code: context.query.code, - redirect_uri: "https://app.watermelontools.com/notion", - }), - }); - } else - return { - props: { - error: "no code", - }, - }; - const json = await f.json(); - if (json.error) { - return { - props: { - error: json.error, - }, - }; - } else { - let userInfo = await fetch( - `https://api.notion.com/v1/users/${json.owner.user.id}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${json.access_token}`, - "Notion-Version": "2021-05-13", - }, - } - ).then((res) => res.json()); - await saveUser({ - watermelon_user: context.query.state, - access_token: json.access_token, - token_type: json.token_type, - bot_id: json.bot_id, - workspace_name: json.workspace_name, - workspace_icon: json.workspace_icon, - workspace_id: json.workspace_id, - owner_type: json.owner, - owner_user_object: json.owner.user, - owner_user_id: json.owner.user.id, - duplicated_template_id: json.duplicated_template_id, - user_id: userInfo.id, - user_name: userInfo.name, - user_avatar_url: userInfo.avatar_url, - user_email: userInfo.person.email, - }); - return { - props: { - loggedIn: true, - userEmail: context.query.state, - login: userInfo.name, - avatar_url: userInfo.avatar_url, - workspace: json.workspace_name, - }, - }; - } -}