Aurora SpotiRip desktop and web app
- Spotify playlist, library, album and artist browsing with in-app playback - Song, playlist and album downloads via spotdl with YouTube and Bandcamp fallbacks - Electron desktop app (castLabs Widevine) with a permanent sidebar layout - Linux and Windows packaging scripts
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user