Skip to content
This repository was archived by the owner on Mar 12, 2024. It is now read-only.

Commit 3c8598b

Browse files
Update dev branch (#219)
* improve logging * Redraw confirmation page * Add logo to login * Add theming * Create NotionLoginLink.tsx * Create notion.tsx * Add notion * log returned json * even more logging * Remove logging, fix token getting * fix broken deploy * ad logging * encode in base64 the auth * Remove notion login link temporarily * Remove discord login * Save to the DB * Fix object to save * Await saving the user * Fix service name, get user * Fix path * Fix user saving * Debug logging * 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 --------- Co-authored-by: baristaGeek <estebanvargas94@gmail.com>
1 parent 6f42b81 commit 3c8598b

4 files changed

Lines changed: 462 additions & 22 deletions

File tree

app/linear/page.tsx

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1+
import Link from "next/link";
12
import { getServerSession } from "next-auth";
2-
//change this to import correctly
3+
34
import saveUserInfo from "../../utils/db/linear/saveUser";
45

56
import { authOptions } from "../api/auth/[...nextauth]/route";
7+
import TimeToRedirect from "../../components/redirect";
68
import getAllPublicUserData from "../../utils/api/getAllUserPublicData";
79

8-
import ConnectedService from "../../utils/services/page";
9-
import LoginArray from "../../utils/services/loginArray";
10+
import SlackLoginLink from "../../components/SlackLoginLink";
11+
import NotionLoginLink from "../../components/NotionLoginLink";
12+
import ConfluenceLoginLink from "../../components/ConfluenceLoginLink";
13+
import GitHubLoginLink from "../../components/GitHubLoginLink";
1014

11-
export default async function ServicePage({
15+
export default async function Linear({
1216
searchParams,
1317
}: {
1418
searchParams: { [key: string]: string | string[] | undefined };
@@ -18,14 +22,12 @@ export default async function ServicePage({
1822
const userName = session?.user?.name;
1923
const { code, state } = searchParams;
2024
let error = "";
21-
// change service name
22-
const serviceName = "Linear";
23-
const [userData, serviceToken] = await Promise.all([
25+
26+
const [userData, linearToken] = await Promise.all([
2427
getAllPublicUserData({ userEmail }).catch((e) => {
2528
console.error(e);
2629
return null;
2730
}),
28-
// change this fetch
2931
fetch(`https://api.linear.app/oauth/token`, {
3032
method: "POST",
3133
headers: {
@@ -36,11 +38,35 @@ export default async function ServicePage({
3638
}),
3739
]);
3840

39-
// the recommended services should not be of the same category as the current one
40-
const nameList = ["GitHub", "Slack", "Notion", "Confluence"];
41-
const loginArray = LoginArray({ nameList, userEmail, userData });
41+
const services = [
42+
{
43+
name: "GitHub",
44+
dataProp: "github_data",
45+
loginComponent: <GitHubLoginLink userEmail={userEmail} />,
46+
},
47+
{
48+
name: "Slack",
49+
dataProp: "slack_data",
50+
loginComponent: <SlackLoginLink userEmail={userEmail} />,
51+
},
52+
{
53+
name: "Confluence",
54+
dataProp: "confluence_data",
55+
loginComponent: <ConfluenceLoginLink userEmail={userEmail} />,
56+
},
57+
{
58+
name: "Notion",
59+
dataProp: "notion_data",
60+
loginComponent: <NotionLoginLink userEmail={userEmail} />,
61+
},
62+
];
63+
const loginArray = services
64+
.map((service) =>
65+
userData?.[service.dataProp] ? null : service.loginComponent
66+
)
67+
.filter((component) => component !== null);
4268

43-
const json = await serviceToken.json();
69+
const json = await linearToken.json();
4470
if (json.error) {
4571
error = json.error;
4672
} else {
@@ -49,7 +75,6 @@ export default async function ServicePage({
4975
"query Me {\nviewer {\n id,\n name,\n displayName, email,\n avatarUrl\n},\nteams {\n nodes {\n id,\n name\n }\n}\n}",
5076
variables: {},
5177
});
52-
// get user correctly
5378
let user = await fetch(`https://api.linear.app/graphql`, {
5479
method: "POST",
5580
headers: {
@@ -60,7 +85,6 @@ export default async function ServicePage({
6085
});
6186
let userText = await user.text();
6287
let userJson = JSON.parse(userText).data;
63-
// save user correctly
6488
await saveUserInfo({
6589
access_token: json.access_token,
6690
id: userJson.viewer.id,
@@ -74,14 +98,34 @@ export default async function ServicePage({
7498
});
7599

76100
return (
77-
<ConnectedService
78-
serviceName={serviceName}
79-
displayName={userJson.viewer.displayName}
80-
teamName={userJson.teams.nodes[0].name}
81-
avatarUrl={userJson.viewer.avatarUrl}
82-
loginArray={loginArray}
83-
error={error}
84-
/>
101+
<div className="Box" style={{ maxWidth: "100ch", margin: "auto" }}>
102+
<div className="Subhead">
103+
<h2 className="Subhead-heading px-2">
104+
You have logged in with Linear as {userJson.viewer.displayName} in
105+
the team {userJson.teams.nodes[0].name}
106+
</h2>
107+
</div>
108+
<img
109+
src={userJson.viewer.avatarUrl}
110+
alt="linear user image"
111+
className="avatar avatar-8"
112+
/>
113+
<div>
114+
<TimeToRedirect url={"/"} />
115+
<p>
116+
If you are not redirected, please click <Link href="/">here</Link>
117+
</p>
118+
{loginArray.length ? (
119+
<div>
120+
<h3>You might also be interested: </h3>
121+
{loginArray.map((login) => (
122+
<>{login}</>
123+
))}
124+
</div>
125+
) : null}
126+
{error && <p>{error}</p>}
127+
</div>
128+
</div>
85129
);
86130
}
87131
}

pages/atlassian.tsx

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { useEffect, useState } from "react";
2+
import { useRouter } from "next/router";
3+
import Link from "next/link";
4+
import saveJiraUserInfo from "../utils/db/jira/saveUserInfo";
5+
import saveConfluenceUserInfo from "../utils/db/confluence/saveUserInfo";
6+
import GitHubLoginLink from "../components/GitHubLoginLink";
7+
export default function Jira({ organization, avatar_url, userEmail, error }) {
8+
const [timeToRedirect, setTimeToRedirect] = useState(10);
9+
const router = useRouter();
10+
useEffect(() => {
11+
const interval = setInterval(() => {
12+
setTimeToRedirect(timeToRedirect - 1);
13+
if (timeToRedirect === 0) {
14+
router.push("/");
15+
}
16+
}, 1000);
17+
return () => clearInterval(interval);
18+
}, [timeToRedirect]);
19+
let isConfluence = userEmail.startsWith("c");
20+
21+
return (
22+
<div className="Box" style={{ maxWidth: "100ch", margin: "auto" }}>
23+
<div className="Subhead">
24+
<h2 className="Subhead-heading px-2">
25+
You have logged in with {isConfluence ? "Confluence" : "Jira"} to{" "}
26+
{organization}
27+
</h2>
28+
</div>
29+
<img
30+
src={avatar_url}
31+
alt={`${isConfluence ? "Confluence" : "Jira"} organization image`}
32+
className="avatar avatar-8"
33+
/>
34+
<div>
35+
<p className="text-emphasized">We recommend you login to GitHub</p>
36+
<GitHubLoginLink userEmail={userEmail} />
37+
</div>
38+
<div>
39+
<p>You will be redirected in {timeToRedirect}...</p>
40+
<p>
41+
If you are not redirected, please click <Link href="/">here</Link>
42+
</p>
43+
{error && <p>{error}</p>}
44+
</div>
45+
</div>
46+
);
47+
}
48+
export async function getServerSideProps(context) {
49+
let f;
50+
51+
if (context.query.code) {
52+
f = await fetch(`https://auth.atlassian.com/oauth/token`, {
53+
method: "POST",
54+
headers: {
55+
"Content-Type": "application/json",
56+
},
57+
body: JSON.stringify({
58+
grant_type: "authorization_code",
59+
code: context.query.code,
60+
redirect_uri: "https://app.watermelontools.com/atlassian",
61+
client_id: process.env.NEXT_PUBLIC_JIRA_CLIENT_ID,
62+
client_secret: process.env.JIRA_CLIENT_SECRET,
63+
}),
64+
});
65+
} else
66+
return {
67+
props: {
68+
error: "no code",
69+
},
70+
};
71+
const json = await f.json();
72+
if (json.error) {
73+
console.error("Atlassian error", json);
74+
return {
75+
props: {
76+
error: json.error,
77+
},
78+
};
79+
} else {
80+
const { access_token } = json;
81+
const orgInfo = await fetch(
82+
"https://api.atlassian.com/oauth/token/accessible-resources",
83+
{
84+
method: "GET",
85+
headers: {
86+
"Content-Type": "application/json",
87+
Authorization: `Bearer ${access_token}`,
88+
},
89+
}
90+
);
91+
const orgInfoJson = await orgInfo.json();
92+
let isConfluence = context.query.state.startsWith("c");
93+
if (isConfluence) {
94+
const userInfo = await fetch(
95+
`https://api.atlassian.com/ex/confluence/${orgInfoJson[0].id}/rest/api/user/current`,
96+
{
97+
method: "GET",
98+
headers: {
99+
"Content-Type": "application/json",
100+
Authorization: `Bearer ${access_token}`,
101+
},
102+
}
103+
);
104+
const userInfoJson = await userInfo.json();
105+
await saveConfluenceUserInfo({
106+
access_token: json.access_token,
107+
refresh_token: json.refresh_token,
108+
confluence_id: orgInfoJson[0].id,
109+
organization: orgInfoJson[0].name,
110+
url: orgInfoJson[0].url,
111+
org_avatar_url: orgInfoJson[0].avatarUrl,
112+
scopes: orgInfoJson[0].scopes,
113+
watermelon_user: context.query.state.slice(1),
114+
user_email: userInfoJson.email,
115+
user_avatar_url:
116+
orgInfoJson[0].url + userInfoJson?.profilePicture?.path,
117+
user_id: userInfoJson.accountId,
118+
user_displayname: userInfoJson.displayName,
119+
});
120+
} else {
121+
const userInfo = await fetch(
122+
`https://api.atlassian.com/ex/jira/${orgInfoJson[0].id}/rest/api/3/myself`,
123+
{
124+
method: "GET",
125+
headers: {
126+
"Content-Type": "application/json",
127+
Authorization: `Bearer ${access_token}`,
128+
},
129+
}
130+
);
131+
const userInfoJson = await userInfo.json();
132+
await saveJiraUserInfo({
133+
access_token: json.access_token,
134+
refresh_token: json.refresh_token,
135+
jira_id: orgInfoJson[0].id,
136+
organization: orgInfoJson[0].name,
137+
url: orgInfoJson[0].url,
138+
org_avatar_url: orgInfoJson[0].avatarUrl,
139+
scopes: orgInfoJson[0].scopes,
140+
watermelon_user: context.query.state.slice(1),
141+
user_email: userInfoJson.emailAddress,
142+
user_avatar_url: userInfoJson?.avatarUrls?.["48x48"],
143+
user_id: userInfoJson.accountId,
144+
user_displayname: userInfoJson.displayName,
145+
});
146+
}
147+
return {
148+
props: {
149+
userEmail: context.query.state,
150+
organization: orgInfoJson[0]?.name,
151+
avatar_url: orgInfoJson[0]?.avatarUrl,
152+
},
153+
};
154+
}
155+
}

pages/discord.tsx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { useEffect, useState } from "react";
2+
import { useRouter } from "next/router";
3+
import Link from "next/link";
4+
import JiraLoginLink from "../components/JiraLoginLink";
5+
import saveUser from "../utils/db/discord/saveUser";
6+
export default function Discord({ userData, userEmail, error }) {
7+
const [timeToRedirect, setTimeToRedirect] = useState(10);
8+
const router = useRouter();
9+
useEffect(() => {
10+
const interval = setInterval(() => {
11+
setTimeToRedirect(timeToRedirect - 1);
12+
if (timeToRedirect === 0) {
13+
router.push("/");
14+
}
15+
}, 1000);
16+
return () => clearInterval(interval);
17+
}, [timeToRedirect]);
18+
19+
return (
20+
<div className="Box" style={{ maxWidth: "100ch", margin: "auto" }}>
21+
<div className="Subhead">
22+
<h2 className="Subhead-heading px-2">
23+
You have logged in with Discord as {userData.username}
24+
</h2>
25+
<img
26+
src={`https://cdn.discordapp.com/avatars/${userData.id}/${userData.avatar}`}
27+
alt="github user image"
28+
className="avatar avatar-8"
29+
/>
30+
</div>
31+
32+
<div>
33+
<p className="text-emphasized">We recommend you login to Jira</p>
34+
<JiraLoginLink userEmail={userEmail} />
35+
</div>
36+
<div>
37+
<p>You will be redirected in {timeToRedirect}...</p>
38+
<p>
39+
If you are not redirected, please click <Link href="/">here</Link>
40+
</p>
41+
{error && <p>{error}</p>}
42+
</div>
43+
</div>
44+
);
45+
}
46+
47+
export async function getServerSideProps(context) {
48+
let f;
49+
if (context.query.code) {
50+
const API_ENDPOINT = "https://discord.com/api/v10";
51+
const CLIENT_ID = process.env.DISCORD_CLIENT_ID;
52+
const CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET;
53+
const REDIRECT_URI = "https://app.watermelontools.com/discord";
54+
const data = {
55+
client_id: CLIENT_ID,
56+
client_secret: CLIENT_SECRET,
57+
grant_type: "authorization_code",
58+
code: context.query.code,
59+
redirect_uri: REDIRECT_URI,
60+
};
61+
const headers = {
62+
"Content-Type": "application/x-www-form-urlencoded",
63+
};
64+
const response = await fetch(`${API_ENDPOINT}/oauth2/token`, {
65+
method: "POST",
66+
headers: headers,
67+
body: new URLSearchParams(data.toString()),
68+
});
69+
const json = await response.json();
70+
const user = await fetch(`${API_ENDPOINT}/users/@me`, {
71+
headers: {
72+
Authorization: `Bearer ${json.access_token}`,
73+
},
74+
});
75+
76+
const userJson = await user.json();
77+
await saveUser({
78+
access_token: json.access_token,
79+
scope: json.scope,
80+
username: userJson.username,
81+
id: userJson.id,
82+
avatar_url: userJson.avatar,
83+
watermelon_user: context.query.state,
84+
email: userJson.email,
85+
refresh_token: json.refresh_token,
86+
});
87+
return {
88+
props: {
89+
userData: userJson,
90+
userEmail: context.query.state,
91+
},
92+
};
93+
} else
94+
return {
95+
props: {
96+
error: "no code",
97+
},
98+
};
99+
}

0 commit comments

Comments
 (0)