From bae657760e6968fa03ad91e02336fdc31ee8d460 Mon Sep 17 00:00:00 2001 From: Andrei Jiroh Halili Date: Wed, 24 Jul 2024 01:19:26 +0800 Subject: [PATCH] feat(golinks): work on interactivity features for Slack integration Just the bare bones for the moment, alongside some GitHub OAuth hacks. Signed-off-by: Andrei Jiroh Halili --- apps/golinks-v2/src/api/github.ts | 35 +++------------- apps/golinks-v2/src/api/slack.ts | 67 +++++++++++++++++++++++++++---- apps/golinks-v2/src/index.tsx | 3 +- apps/golinks-v2/src/lib/auth.ts | 13 ++++-- 4 files changed, 76 insertions(+), 42 deletions(-) diff --git a/apps/golinks-v2/src/api/github.ts b/apps/golinks-v2/src/api/github.ts index 9490fc6..fe82eac 100644 --- a/apps/golinks-v2/src/api/github.ts +++ b/apps/golinks-v2/src/api/github.ts @@ -8,8 +8,6 @@ export async function githubAuth(context: Context) { const appId = context.env.GITHUB_OAUTH_ID; const appSecret = context.env.GITHUB_OAUTH_SECRET; const redirect_uri = `${context.env.BASE_URL}/auth/github/callback`; - const adapter = new PrismaD1(context.env.golinks); - const prisma = new PrismaClient({ adapter }); const { slack_id, slack_team, state, client_id } = context.req.query(); if (client_id == "slack") { const requestState = encodeURIComponent( @@ -19,18 +17,6 @@ export async function githubAuth(context: Context) { state, }), ); - const github = await prisma.gitHubOAuthChallenge - .create({ - data: { - challenge: state, - metadata: JSON.stringify({ slack_team, slack_id }), - }, - }) - .catch((err) => { - console.error(err); - return context.newResponse("Something gone wrong on the backend"); - }); - console.log(`[db] ${github}`); return context.redirect( `https://github.com/login/oauth/authorize?client_id=${appId}&redirect_uri=${redirect_uri}&state=${requestState}&scope=read:user,user:email`, ); @@ -45,8 +31,8 @@ export async function githubOAuthCallback(context: Context) { const { state, code, error, error_description, error_uri } = context.req.query(); let payload = { code, - client_id: context.env.GITHUB_OAUTH_ID, - client_secret: context.env.GITHUB_OAUTH_SECRET, + client_id: appId, + client_secret: appSecret, redirect_uri, state, grant_type: "authorization_code", @@ -69,20 +55,9 @@ https://go.andreijiroh.xyz/feedback/oauth-bug with the details above`; return context.newResponse("OAuth flow was aborted because the auth code is invalid.", 400); } const ticket = generateSlug(12); - then((result) => { - const dbResult = prisma.gitHubOAuthChallenge.update({ - where: { - challenge: stateJson.challenge, - }, - data: { - code: ticket, - username, - userId: id, - }, - }); - console.log(`[prisma-client] ${JSON.stringify(dbResult)}`); - const prompt = `Here's your ticket code for requesting your API key: ${ticket} + + console.log(`[prisma-client] ${JSON.stringify(dbResult)}`); + const prompt = `Here's your ticket code for requesting your API key: ${ticket} Present this back to the bot by pressing "Enter code" in the message`; - }); } diff --git a/apps/golinks-v2/src/api/slack.ts b/apps/golinks-v2/src/api/slack.ts index 57054b2..ecd1cd4 100644 --- a/apps/golinks-v2/src/api/slack.ts +++ b/apps/golinks-v2/src/api/slack.ts @@ -23,7 +23,7 @@ type SlackSlashCommand = { }; async function helpMessage(context: Context, params: SlackSlashCommand) { - const challenge = await generateChallenge(context.env.golinks); + const challenge = generateSlug(24); const githubAuthUrl = `${context.env.BASE_URL}/auth/github?client_id=slack&slack_team=${params.team_id}&slack_id=${params.user_id}&state=${challenge}`; const templateJson = { blocks: [ @@ -97,7 +97,7 @@ async function helpMessage(context: Context, params: SlackSlashCommand) { type: "context", elements: [ { - text: "If something go wrong, or ping @ajhalili2006 on the fediverse.", + text: "If something go wrong, or ping @ajhalili2006 on the fediverse (or here in the Slack if he's here).", type: "mrkdwn", }, ], @@ -160,6 +160,48 @@ function authChallengePrompt(context: Context, params: SlackSlashCommand) { }; } +async function sneakyWIPScreen(context: Context, params: SlackSlashCommand) { + const blocks = { + type: "modal", + close: { + type: "plain_text", + text: "Close", + emoji: true, + }, + title: { + type: "plain_text", + text: "You're a bit sneaky!", + emoji: true, + }, + blocks: [ + { + type: "section", + text: { + type: "mrkdwn", + text: "This feature is currently under development and working on stablizing this for you to use them soon. ", + }, + }, + { + type: "section", + text: { + type: "mrkdwn", + text: ":speech_balloon: *Send feedback*\nWe want to know your experience with our Slack integration for @ajhalili2006's golinks service.", + }, + accessory: { + type: "button", + text: { + type: "plain_text", + text: "Open in GitHub", + emoji: true, + }, + url: "https://go.andreijiroh.xyz/feedback/slack-integration", + }, + }, + ], + }; + return context.json(blocks); +} + /** * Handle requests for OAuth-based app installation * @param context @@ -193,24 +235,28 @@ export async function slackOAuthCallback(context: Context) { return context.json({ ok: false, error: result.error }, api.status); } - const team = lookupBotToken( + const team = await lookupBotToken( context.env.golinks, result.is_enterprise_install == true ? result.enterprise.id : result.team.id, result.is_enterprise_install, ); + console.log(`[prisma-client] ${JSON.stringify(team)}`); - if (!team) { + if (team == null) { const deploy = await addBotToken( context.env.golinks, result.is_enterprise_install == true ? result.enterprise.id : result.team.id, result.is_enterprise_install, ); + console.log(`[db] ${deploy}`); } else { const deploy = await updateBotToken( context.env.golinks, - result.is_enterprise_install == true ? result.enterprise.id : result.team.id, - result.is_enterprise_install, + result.team.id, + result.access_token, + result.is_enterprise_install == true ? result.enterprise.id : false, ); + console.log(`[db] ${deploy}`); } return context.newResponse(`Installation success! Try it now with "/go help" command.`); } catch (err) { @@ -273,7 +319,14 @@ export async function handleSlackCommand(context: Context) { return context.newResponse("Unsupported command"); } -export async function handleSlackInteractivity(context: Context) {} +export async function handleSlackInteractivity(context: Context) { + const authToken = await context.req.query("token"); + const data = await context.req.parseBody(); + const headers = await context.req.header() + await console.log(`[slack-interactivity] data:`, data); + await console.log(`[slack-interactivity] headers:`, headers); + return await sneakyWIPScreen(context, data); +} /** * Validate request timestamps to ensure that we don't received forged requests diff --git a/apps/golinks-v2/src/index.tsx b/apps/golinks-v2/src/index.tsx index f3553c2..177d817 100644 --- a/apps/golinks-v2/src/index.tsx +++ b/apps/golinks-v2/src/index.tsx @@ -20,7 +20,7 @@ import { DeprecatedGoLinkPage } from "pages/deprecated-link"; import { CommitHash, PingPong } from "api/meta"; import { prettyJSON } from "hono/pretty-json"; import { generateNewIssueUrl, handleOldUrls } from "lib/utils"; -import { handleSlackCommand, slackOAuth, slackOAuthCallback } from "api/slack"; +import { handleSlackCommand, handleSlackInteractivity, slackOAuth, slackOAuthCallback } from "api/slack"; import { githubAuth } from "api/github"; // Start a Hono app @@ -75,6 +75,7 @@ openapi.post("/api/discord-invites", DiscordInviteLinkCreate); openapi.get("/api/ping", PingPong); openapi.get("/api/commit", CommitHash); app.post("/api/slack/slash-commands/:command", async (c) => handleSlackCommand(c)); +app.post("/api/slack/interactivity-feed", async (c) => handleSlackInteractivity(c)); // Slack bot and slash commands app.get("/slack", async (c) => slackOAuth(c)); diff --git a/apps/golinks-v2/src/lib/auth.ts b/apps/golinks-v2/src/lib/auth.ts index 4e009b6..b4b1362 100644 --- a/apps/golinks-v2/src/lib/auth.ts +++ b/apps/golinks-v2/src/lib/auth.ts @@ -3,6 +3,7 @@ import { PrismaClient } from "@prisma/client"; import { Context, Next } from "hono"; import { EnvBindings, Env } from "types"; import { generateSlug } from "./utils"; +import { add } from "date-fns"; import { error } from "console"; export async function adminApiKeyAuth(c: Context, next: Next) { @@ -65,7 +66,7 @@ export async function addBotToken(db: EnvBindings["golinks"], teamId: strin const result = await prisma.slackBotToken.create({ data: { teamId, - enterprise_install, + enterprise_install: enterprise_install !== undefined ? enterprise_install : false, token, }, }); @@ -121,17 +122,21 @@ export async function getUserInfoAndGenerateTicket(token: string, context: Conte } } -export async function generateChallenge(db: EnvBindings["golinks"], metadata) { +export async function addNewChallenge(db: EnvBindings["golinks"], challenge, metadata) { const adapter = new PrismaD1(db); const prisma = new PrismaClient({ adapter }); - const newchallenge = generateSlug(24); try { const result = await prisma.gitHubOAuthChallenge.create({ data: { - challenge: newchallenge, + challenge, metadata, + expires_on: add(Date(), { + minutes: 15, + }), }, }); + console.log(`[dbops] ${result}`); + return result; } catch (error) { console.error(error); return Promise.reject(Error(error)); -- 2.51.2