import superjson from "superjson"; import { schema, type OutputType } from "./spotify-exchange_POST.schema"; import { appendSetCookies, clearCookie, exchangeSpotifyCode, makeCookie, parseCookies, } from "../helpers/spotifyServer"; type SpotifyProfile = { id: string; display_name?: string; email?: string; product?: string; images?: Array<{ url?: string }>; }; export async function handle(request: Request) { try { const body = schema.parse(superjson.parse(await request.text())); const cookies = parseCookies(request); if (!cookies.spotirip_oauth_state || cookies.spotirip_oauth_state !== body.state) { return new Response(superjson.stringify({ error: "Spotify login state did not match. Please try connecting again." }), { status: 400 }); } const token = await exchangeSpotifyCode(body.code); if (!token.refresh_token) { return new Response(superjson.stringify({ error: "Spotify did not return a refresh token. Reconnect your account." }), { status: 400 }); } const profileResponse = await fetch("https://api.spotify.com/v1/me", { headers: { Authorization: `Bearer ${token.access_token}` }, }); if (!profileResponse.ok) { return new Response(superjson.stringify({ error: "Spotify login succeeded, but the user profile could not be read." }), { status: 502 }); } const profile = (await profileResponse.json()) as SpotifyProfile; const expiresAt = Date.now() + token.expires_in * 1000; const headers = new Headers({ "Content-Type": "application/json", "Cache-Control": "no-store" }); appendSetCookies(headers, [ makeCookie("spotirip_spotify_access", token.access_token, { maxAge: token.expires_in }), makeCookie("spotirip_spotify_refresh", token.refresh_token, { maxAge: 60 * 60 * 24 * 30 }), makeCookie("spotirip_spotify_expires", String(expiresAt), { maxAge: token.expires_in }), clearCookie("spotirip_oauth_state"), ]); const output: OutputType = { connected: true, desktop: process.env.SPOTIRIP_DESKTOP === "1" || undefined, profile: { id: profile.id, displayName: profile.display_name || profile.id, email: profile.email, imageUrl: profile.images?.find((image) => image.url)?.url, product: profile.product, }, }; return new Response(superjson.stringify(output), { headers }); } catch (error) { const message = error instanceof Error ? error.message : "Spotify login failed."; return new Response(superjson.stringify({ error: message }), { status: 400 }); } }