63 lines
1.5 KiB
TypeScript
63 lines
1.5 KiB
TypeScript
import { and, eq } from "drizzle-orm";
|
|
import Elysia from "elysia";
|
|
|
|
import { auth, betterAuthElysia } from "../auth";
|
|
import { db } from "../db";
|
|
import { account } from "../db/schema";
|
|
|
|
const SPOTIFY_PLAYBACK_SCOPES = ["streaming", "user-read-private"];
|
|
|
|
function parseScopes(scope: string | null | undefined) {
|
|
return new Set(scope?.split(/[\s,]+/).filter(Boolean) ?? []);
|
|
}
|
|
|
|
export const spotifyRoutes = new Elysia()
|
|
.use(betterAuthElysia)
|
|
.group("/spotify", (app) =>
|
|
app.get(
|
|
"/token",
|
|
async ({ user, set }) => {
|
|
const [spotifyAccount] = await db
|
|
.select({ scope: account.scope })
|
|
.from(account)
|
|
.where(
|
|
and(eq(account.userId, user.id), eq(account.providerId, "spotify")),
|
|
)
|
|
.limit(1);
|
|
|
|
const grantedScopes = parseScopes(spotifyAccount?.scope);
|
|
const missingScopes = SPOTIFY_PLAYBACK_SCOPES.filter(
|
|
(scope) => !grantedScopes.has(scope),
|
|
);
|
|
|
|
if (missingScopes.length > 0) {
|
|
set.status = 403;
|
|
return {
|
|
error: "Spotify playback permission required",
|
|
code: "SPOTIFY_RELINK_REQUIRED",
|
|
missingScopes,
|
|
};
|
|
}
|
|
|
|
const token = await auth.api.getAccessToken({
|
|
body: {
|
|
userId: user.id,
|
|
providerId: "spotify",
|
|
},
|
|
});
|
|
|
|
if (!token?.accessToken) {
|
|
set.status = 404;
|
|
return { error: "Spotify access token not found" };
|
|
}
|
|
|
|
return {
|
|
accessToken: token.accessToken,
|
|
expiresAt: token.accessTokenExpiresAt
|
|
? Number(token.accessTokenExpiresAt)
|
|
: null,
|
|
};
|
|
},
|
|
{ auth: true },
|
|
),
|
|
);
|