import type { Context } from "hono"; import { Agent } from "@atproto/api"; import { isAtprotoDid, isAtprotoDidWeb } from "@atproto/did"; import { isValidHandle } from "@atproto/syntax"; import { env } from "@tealfmbot/common/constants"; import { logger } from "@tealfmbot/common/logger"; import { deleteCookie, generateSignedCookie, getSignedCookie } from "hono/cookie"; import { client } from "./client.js"; export const MAX_AGE = process.env.NODE_ENV === "production" ? 60 : 0; export async function createSession(value: string) { const cookie = await generateSignedCookie("__teal_fm_bot_session", value, env.COOKIE_SECRET, { path: "/", secure: process.env.NODE_ENV === "production", httpOnly: true, sameSite: "lax", maxAge: 60 * 60 * 24 * 7, }); return cookie; } export async function getSessionAgent(c: Context) { c.header("Vary", "Cookie"); const session = await getSignedCookie(c, env.COOKIE_SECRET, "__teal_fm_bot_session"); if (!session) return null; c.header("Cache-Control", `max-age=${MAX_AGE}, private`); try { const oauthSession = await client.restore(session); return oauthSession ? new Agent(oauthSession) : null; } catch (error) { logger.warn({ error }, "oauth restore failed"); deleteCookie(c, "__teal_fm_bot_session"); return null; } } export async function getSession(c: Context, e: typeof env) { const session = await getSignedCookie(c, e.COOKIE_SECRET, "__teal_fm_bot_session"); return session; } export function isValidUrl(url: string) { if (URL.canParse(url)) { const pdsUrl = new URL(url); return pdsUrl.protocol === "http:" || pdsUrl.protocol === "https:"; } return false; } // https://github.com/bluesky-social/social-app/blob/main/src/lib/strings/handles.ts#L51 export function validateIdentifier(input: string) { if (typeof input !== "string") return false; const results = { validHandle: isValidHandle(input), nonEmptyIdentifier: !input.trim(), validDid: isAtprotoDid(input), validUrl: isValidUrl(input), validDidWeb: isAtprotoDidWeb(input), }; return Object.values(results).includes(true); }