Skip to content

Commit 4400ca8

Browse files
Merge pull request #49 from DesmondSanctity/event-badging-submit-api
feat: endpoint for submitting/authorizing event badging
2 parents 19d6443 + eb9fe17 commit 4400ca8

File tree

6 files changed

+143
-2
lines changed

6 files changed

+143
-2
lines changed

helpers/crypto.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const crypto = require("crypto");
2+
const zlib = require("zlib");
3+
const TurndownService = require("turndown");
4+
require("dotenv").config();
5+
6+
const algorithm = "aes-256-ctr";
7+
const secretKey = crypto
8+
.createHash("sha256")
9+
.update(String(process.env.ENCRYPTION_SECRET_KEY))
10+
.digest("base64")
11+
.slice(0, 32);
12+
13+
const encrypt = (text) => {
14+
const compressed = zlib.deflateSync(text);
15+
const iv = crypto.randomBytes(16);
16+
const cipher = crypto.createCipheriv(algorithm, secretKey, iv);
17+
const encrypted = Buffer.concat([cipher.update(compressed), cipher.final()]);
18+
return `${iv.toString("base64url")}:${encrypted.toString("base64url")}`;
19+
};
20+
21+
const decrypt = (hash) => {
22+
const [iv, content] = hash.split(":");
23+
const decipher = crypto.createDecipheriv(
24+
algorithm,
25+
secretKey,
26+
Buffer.from(iv, "base64url")
27+
);
28+
const decrypted = Buffer.concat([
29+
decipher.update(Buffer.from(content, "base64url")),
30+
decipher.final(),
31+
]);
32+
return zlib.inflateSync(decrypted).toString();
33+
};
34+
35+
const convertToMarkdown = (html) => {
36+
const turndownService = new TurndownService({
37+
headingStyle: "atx",
38+
bulletListMarker: "-",
39+
});
40+
const markdown = turndownService.turndown(html);
41+
return markdown;
42+
};
43+
44+
module.exports = { encrypt, decrypt, convertToMarkdown };

package-lock.json

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
"nodemailer": "^6.9.3",
4141
"nodemon": "^2.0.22",
4242
"octokit": "^2.1.0",
43-
"sequelize": "^6.32.1"
43+
"sequelize": "^6.32.1",
44+
"turndown": "^7.2.0"
4445
},
4546
"devDependencies": {
4647
"eslint-config-prettier": "^8.8.0",

providers/event-github/auth.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const axios = require("axios");
2+
const { Octokit } = require("@octokit/rest");
3+
const { encrypt, decrypt, convertToMarkdown } = require("../../helpers/crypto");
4+
5+
const issueCreationAuth = (req, res) => {
6+
if (!process.env.GITHUB_AUTH_CLIENT_ID) {
7+
res.status(500).send("GitHub Issue Creation is not configured");
8+
return;
9+
}
10+
11+
const scopes = ["repo"];
12+
const encryptedFormData = encrypt(JSON.stringify(req.body));
13+
const url = `https://github.com/login/oauth/authorize?client_id=${
14+
process.env.GITHUB_AUTH_CLIENT_ID
15+
}&scope=${scopes.join(",")}&state=${encryptedFormData}`;
16+
17+
res.send({ authorizationLink: url });
18+
};
19+
20+
const issueCreationCallback = async (req, res) => {
21+
const code = req.query.code;
22+
const encryptedState = req.query.state;
23+
24+
const formData = decrypt(encryptedState);
25+
const parsedFormData = JSON.parse(formData);
26+
const issueTitle = parsedFormData.title;
27+
const markdown = convertToMarkdown(parsedFormData.body);
28+
29+
if (!formData) {
30+
return res.status(400).json({ error: "No form data found" });
31+
}
32+
33+
try {
34+
const tokenResponse = await axios.post(
35+
"https://github.com/login/oauth/access_token",
36+
{
37+
client_id: process.env.GITHUB_AUTH_CLIENT_ID,
38+
client_secret: process.env.GITHUB_AUTH_CLIENT_SECRET,
39+
code,
40+
},
41+
{
42+
headers: {
43+
Accept: "application/json",
44+
},
45+
}
46+
);
47+
48+
const accessToken = tokenResponse.data.access_token;
49+
50+
const octokit = new Octokit({ auth: accessToken });
51+
52+
const { data: issue } = await octokit.rest.issues.create({
53+
owner: "badging",
54+
repo: "event-diversity-and-inclusion",
55+
title: issueTitle,
56+
body: markdown,
57+
});
58+
59+
res.redirect(issue.html_url);
60+
} catch (error) {
61+
console.error("Error in issue creation callback:", error);
62+
res.status(500).send("An error occurred during issue creation");
63+
}
64+
};
65+
66+
module.exports = {
67+
issueCreationAuth,
68+
issueCreationCallback,
69+
};

providers/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
const {
2+
issueCreationCallback,
3+
issueCreationAuth,
4+
} = require("./event-github/auth.js");
15
const {
26
githubAuth,
37
githubAuthCallback,
@@ -11,4 +15,6 @@ module.exports = {
1115
githubApp,
1216
gitlabAuth,
1317
gitlabAuthCallback,
18+
issueCreationAuth,
19+
issueCreationCallback,
1420
};

routes/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const {
1010
githubApp,
1111
gitlabAuth,
1212
gitlabAuthCallback,
13+
issueCreationCallback,
14+
issueCreationAuth,
1315
} = require("../providers/index.js");
1416

1517
/**
@@ -159,6 +161,7 @@ const setupRoutes = (app) => {
159161
gitlabAuthCallback(app);
160162
app.get("/api/badgedRepos", badgedRepos);
161163
app.post("/api/repos-to-badge", reposToBadge);
164+
app.get("/api/issue-callback", issueCreationCallback);
162165

163166
// github app routes
164167
app.post("/api/event_badging", async (req, res) => {
@@ -174,6 +177,10 @@ const setupRoutes = (app) => {
174177
res.send("ok");
175178
});
176179

180+
app.post("/api/submit-form", (req, res) => {
181+
issueCreationAuth(req, res);
182+
});
183+
177184
// route to get all events
178185
app.get("/api/badged_events", getAllEvents);
179186

0 commit comments

Comments
 (0)