132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import { createFileRoute } from "@tanstack/react-router";
|
|
|
|
const SPOTIFY_CLIENT_ID = process.env.VITE_SPOTIFY_CLIENT_ID!;
|
|
const SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET!;
|
|
|
|
export const Route = createFileRoute("/api/spotify/token")({
|
|
server: {
|
|
handlers: {
|
|
POST: async ({ request }: { request: Request }) => {
|
|
try {
|
|
const body = await request.json();
|
|
const { refresh_token } = body;
|
|
|
|
if (!refresh_token) {
|
|
return new Response(
|
|
JSON.stringify({ error: "refresh_token is required" }),
|
|
{
|
|
status: 400,
|
|
headers: { "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
|
|
const tokenResponse = await fetch(
|
|
"https://accounts.spotify.com/api/token",
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
Authorization: `Basic ${Buffer.from(`${SPOTIFY_CLIENT_ID}:${SPOTIFY_CLIENT_SECRET}`).toString("base64")}`,
|
|
},
|
|
body: new URLSearchParams({
|
|
grant_type: "refresh_token",
|
|
refresh_token,
|
|
}),
|
|
}
|
|
);
|
|
|
|
if (!tokenResponse.ok) {
|
|
const error = await tokenResponse.json();
|
|
console.error("Token refresh error:", error);
|
|
return new Response(
|
|
JSON.stringify({
|
|
error: "Failed to refresh token",
|
|
details: error,
|
|
}),
|
|
{
|
|
status: tokenResponse.status,
|
|
headers: { "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
|
|
const tokens = await tokenResponse.json();
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
access_token: tokens.access_token,
|
|
expires_in: tokens.expires_in,
|
|
scope: tokens.scope,
|
|
token_type: tokens.token_type,
|
|
}),
|
|
{
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
} catch (error) {
|
|
console.error("Token refresh endpoint error:", error);
|
|
return new Response(
|
|
JSON.stringify({ error: "Internal server error" }),
|
|
{
|
|
status: 500,
|
|
headers: { "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
},
|
|
|
|
GET: async ({ request }: { request: Request }) => {
|
|
try {
|
|
const cookieHeader = request.headers.get("cookie");
|
|
if (!cookieHeader) {
|
|
return new Response(JSON.stringify({ error: "No cookies found" }), {
|
|
status: 401,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
}
|
|
|
|
const cookies = Object.fromEntries(
|
|
cookieHeader.split("; ").map((c: string) => c.split("="))
|
|
);
|
|
|
|
const accessToken = cookies.spotify_access_token;
|
|
const refreshToken = cookies.spotify_refresh_token;
|
|
|
|
if (!accessToken && !refreshToken) {
|
|
return new Response(
|
|
JSON.stringify({ error: "No Spotify tokens found" }),
|
|
{
|
|
status: 401,
|
|
headers: { "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
access_token: accessToken || null,
|
|
refresh_token: refreshToken || null,
|
|
has_tokens: !!(accessToken || refreshToken),
|
|
}),
|
|
{
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
} catch (error) {
|
|
console.error("Get tokens endpoint error:", error);
|
|
return new Response(
|
|
JSON.stringify({ error: "Internal server error" }),
|
|
{
|
|
status: 500,
|
|
headers: { "Content-Type": "application/json" },
|
|
}
|
|
);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
});
|