A discord bot for teal.fm
discord tealfm music

dashboard

besaid.zone c1b7cf90 9715f1cf

verified
Changed files
+38 -12
apps
+37 -11
apps/web/index.ts
··· 2 2 import { COOKIE_SECRET } from "@tealfmbot/common/constants.js"; 3 3 import { logger } from "@tealfmbot/common/logger.js"; 4 4 import { Hono } from "hono"; 5 - import { getSignedCookie } from "hono/cookie"; 5 + import { deleteCookie, getSignedCookie } from "hono/cookie"; 6 6 import { html } from "hono/html"; 7 7 import { validator } from "hono/validator"; 8 8 import pinoHttpLogger from "pino-http"; 9 9 10 10 import { client } from "./client"; 11 - import { createSession, MAX_AGE, validateIdentifier } from "./utils"; 11 + import { createSession, getSessionAgent, MAX_AGE, validateIdentifier } from "./utils"; 12 12 13 13 type Variables = { 14 14 logger: typeof logger; ··· 32 32 return c.text("yo!!"); 33 33 }); 34 34 35 + app.get("/dashboard", async (c) => { 36 + const agent = await getSessionAgent(c); 37 + if (agent?.assertAuthenticated()) { 38 + return c.redirect("/login"); 39 + } 40 + return c.html(html` 41 + <h1>Dashboard</h1> 42 + <p>DID: ${agent?.assertDid}</p> 43 + <form method="post" action="/logout"> 44 + <button type="submit">Log out</button> 45 + </form> 46 + `); 47 + }); 48 + 35 49 app.get("/health", (c) => { 36 50 return c.json({ message: "OK" }, 200); 37 51 }); ··· 61 75 } 62 76 } 63 77 const oauth = await client.callback(params); 64 - await createSession(oauth.session.did); 78 + const cookie = await createSession(oauth.session.did); 79 + c.header("Set-Cookie", cookie); 65 80 } catch (error) { 66 81 logger.error({ error }, "oauth callback failed"); 67 82 } 68 83 69 - return c.redirect("/"); 84 + return c.redirect("/dashboard"); 70 85 }); 71 86 72 87 app.get("/login", (c) => { ··· 98 113 const { identifier } = c.req.valid("form"); 99 114 const ac = new AbortController(); 100 115 try { 101 - // if (identifier.startsWith("did:web")) { 102 - // const id = identifier.split(":")[2] 103 - // const url = await client.authorize(identifier, { 104 - // signal: ac.signal 105 - // }) 106 - // } 107 116 const url = await client.authorize(identifier, { 108 117 signal: ac.signal, 109 118 }); 110 - return c.redirect(url.href); 119 + return c.redirect(url.href.toString()); 111 120 } catch (error) { 112 121 logger.error({ error }, "oauth authorize failed"); 113 122 return c.json({ message: "oauth authorize failed" }, 500); 114 123 } 115 124 }, 116 125 ); 126 + 127 + app.post("/logout", async (c) => { 128 + c.header("Cache-Control", "no-store"); 129 + const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session"); 130 + if (session) { 131 + try { 132 + const oauthSession = await client.restore(session); 133 + if (oauthSession) await oauthSession.signOut(); 134 + } catch (error) { 135 + logger.warn({ error }, "Failed to revoke credentials"); 136 + } 137 + } 138 + 139 + deleteCookie(c, "__teal_fm_bot_session"); 140 + 141 + return c.redirect("/login"); 142 + }); 117 143 118 144 const server = serve({ 119 145 fetch: app.fetch,
+1 -1
apps/web/utils.ts
··· 14 14 export async function createSession(value: string) { 15 15 const cookie = await generateSignedCookie("__teal_fm_bot_session", value, COOKIE_SECRET, { 16 16 path: "/", 17 - secure: process.env.NODE_ENV === "development" ? false : true, 17 + secure: process.env.NODE_ENV === "production", 18 18 httpOnly: true, 19 19 sameSite: "lax", 20 20 maxAge: 60 * 60 * 24 * 7,