export const prerender = false; const TOKEN_ENDPOINT = "https://accounts.spotify.com/api/token"; function requiredEnv(name: string): string { const v = import.meta.env[name]; if (!v) throw new Error(`Missing env var: ${name}`); return v; } export async function GET({ request, url }: { request: Request; url: URL }) { try { const reqUrl = new URL(request.url); const code = reqUrl.searchParams.get("code") ?? url.searchParams.get("code"); if (!code) { return new Response( [ "Missing ?code", "", `request.url: ${request.url}`, `astro.url: ${url.toString()}`, ].join("\n"), { status: 400, headers: { "Content-Type": "text/plain; charset=utf-8" }, }, ); } const client_id = requiredEnv("SPOTIFY_CLIENT_ID"); const client_secret = requiredEnv("SPOTIFY_CLIENT_SECRET"); const redirect_uri = import.meta.env.SPOTIFY_REDIRECT_URI ?? `${reqUrl.origin}/api/spotify/callback`; const basic = Buffer.from(`${client_id}:${client_secret}`).toString( "base64", ); const res = await fetch(TOKEN_ENDPOINT, { method: "POST", headers: { Authorization: `Basic ${basic}`, "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri, }), }); const text = await res.text(); if (!res.ok) { return new Response( `Spotify token exchange failed (${res.status}).\n\n${text}`, { status: 500, headers: { "Content-Type": "text/plain; charset=utf-8" }, }, ); } const data = JSON.parse(text) as { access_token?: string; refresh_token?: string; scope?: string; expires_in?: number; token_type?: string; }; const refresh = data.refresh_token; if (!refresh) { return new Response( `No refresh_token returned.\n\nThis usually means you already authorized this app before.\nGo to https://www.spotify.com/account/apps/ and remove access, then try again.\n\nRaw response:\n${text}`, { status: 500, headers: { "Content-Type": "text/plain; charset=utf-8" }, }, ); } return new Response( [ "Refresh token generated successfully.", "", `SPOTIFY_REFRESH_TOKEN=${refresh}`, "", "Put that into your .env (server-side).", ].join("\n"), { status: 200, headers: { "Content-Type": "text/plain; charset=utf-8" } }, ); } catch (err) { const message = err instanceof Error ? err.message : String(err); return new Response(message, { status: 500, headers: { "Content-Type": "text/plain; charset=utf-8" }, }); } }