Something went wrong. Try again.
A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091import { JoseKey } from "@atproto/jwk-jose";import type { RuntimeLock } from "@atproto/oauth-client-node";import Redis from "ioredis";import Redlock from "redlock";import type { Database } from "../db";import { env } from "../lib/env";import { pdsProxyFetch } from "../lib/pdsProxy";import { CustomOAuthClient } from "./oauth-client";import { SessionStore, StateStore } from "./storage";
export const SCOPES = [ "atproto", "repo:app.rocksky.album", "repo:app.rocksky.artist", "repo:app.rocksky.graph.follow", "repo:app.rocksky.like", "repo:app.rocksky.playlist", "repo:app.rocksky.playlist.song", "repo:app.rocksky.scrobble", "repo:app.rocksky.shout", "repo:app.rocksky.song", "repo:app.rocksky.feed.generator", "repo:fm.teal.feed.play", "repo:fm.teal.actor.status", "repo:fm.teal.alpha.feed.play", "repo:fm.teal.alpha.actor.status", "repo:app.rocksky.actor.status", "repo:app.rocksky.rockbox.audio.settings", "repo:app.rocksky.equalizer", "repo:community.lexicon.calendar.rsvp",];
export const createClient = async (db: Database) => { const publicUrl = env.PUBLIC_URL; const url = publicUrl.includes("localhost") ? `http://127.0.0.1:${env.PORT}` : publicUrl; const enc = encodeURIComponent;
const redis = new Redis(env.REDIS_URL); const redlock = new Redlock([redis]);
const requestLock: RuntimeLock = async (key, fn) => { const lock = await redlock.acquire([key], 45e3); // 45 seconds try { return await fn(); } finally { await lock.release(); } };
return new CustomOAuthClient({ clientMetadata: { client_name: "Rocksky", client_id: !publicUrl.includes("localhost") ? `${url}/oauth-client-metadata.json` : `http://localhost?redirect_uri=${enc( `${url}/oauth/callback`, )}&scope=${enc(SCOPES.join(" "))}`, client_uri: url, redirect_uris: [`${url}/oauth/callback`], scope: SCOPES.join(" "), grant_types: ["authorization_code", "refresh_token"], response_types: ["code"], application_type: "web", token_endpoint_auth_method: url.startsWith("https") ? "private_key_jwt" : "none", token_endpoint_auth_signing_alg: url.startsWith("https") ? "ES256" : undefined, dpop_bound_access_tokens: true, jwks_uri: url.startsWith("https") ? `${url}/jwks.json` : undefined, }, keyset: url.startsWith("https") ? await Promise.all([ JoseKey.fromImportable(env.PRIVATE_KEY_1), JoseKey.fromImportable(env.PRIVATE_KEY_2), JoseKey.fromImportable(env.PRIVATE_KEY_3), ]) : undefined, stateStore: new StateStore(db), sessionStore: new SessionStore(db), requestLock, // Metadata resolution, PAR/token calls and every DPoP-signed XRPC request // made through a restored session go through this. It only changes the host // we connect to for PDSes listed in PDS_HOST_OVERRIDES. fetch: pdsProxyFetch, });};