A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at main 78 lines 2.5 kB view raw
1import { JoseKey } from "@atproto/jwk-jose"; 2import type { RuntimeLock } from "@atproto/oauth-client-node"; 3import Redis from "ioredis"; 4import Redlock from "redlock"; 5import type { Database } from "../db"; 6import { env } from "../lib/env"; 7import { SessionStore, StateStore } from "./storage"; 8import { CustomOAuthClient } from "./oauth-client"; 9 10export const SCOPES = [ 11 "atproto", 12 "repo:app.rocksky.album", 13 "repo:app.rocksky.artist", 14 "repo:app.rocksky.graph.follow", 15 "repo:app.rocksky.like", 16 "repo:app.rocksky.playlist", 17 "repo:app.rocksky.scrobble", 18 "repo:app.rocksky.shout", 19 "repo:app.rocksky.song", 20 "repo:app.rocksky.feed.generator", 21 "repo:fm.teal.alpha.feed.play", 22 "repo:fm.teal.alpha.actor.status", 23]; 24 25export const createClient = async (db: Database) => { 26 const publicUrl = env.PUBLIC_URL; 27 const url = publicUrl.includes("localhost") 28 ? `http://127.0.0.1:${env.PORT}` 29 : publicUrl; 30 const enc = encodeURIComponent; 31 32 const redis = new Redis(env.REDIS_URL); 33 const redlock = new Redlock([redis]); 34 35 const requestLock: RuntimeLock = async (key, fn) => { 36 const lock = await redlock.acquire([key], 45e3); // 45 seconds 37 try { 38 return await fn(); 39 } finally { 40 await lock.release(); 41 } 42 }; 43 44 return new CustomOAuthClient({ 45 clientMetadata: { 46 client_name: "Rocksky", 47 client_id: !publicUrl.includes("localhost") 48 ? `${url}/oauth-client-metadata.json` 49 : `http://localhost?redirect_uri=${enc( 50 `${url}/oauth/callback`, 51 )}&scope=${enc(SCOPES.join(" "))}`, 52 client_uri: url, 53 redirect_uris: [`${url}/oauth/callback`], 54 scope: SCOPES.join(" "), 55 grant_types: ["authorization_code", "refresh_token"], 56 response_types: ["code"], 57 application_type: "web", 58 token_endpoint_auth_method: url.startsWith("https") 59 ? "private_key_jwt" 60 : "none", 61 token_endpoint_auth_signing_alg: url.startsWith("https") 62 ? "ES256" 63 : undefined, 64 dpop_bound_access_tokens: true, 65 jwks_uri: url.startsWith("https") ? `${url}/jwks.json` : undefined, 66 }, 67 keyset: url.startsWith("https") 68 ? await Promise.all([ 69 JoseKey.fromImportable(env.PRIVATE_KEY_1), 70 JoseKey.fromImportable(env.PRIVATE_KEY_2), 71 JoseKey.fromImportable(env.PRIVATE_KEY_3), 72 ]) 73 : undefined, 74 stateStore: new StateStore(db), 75 sessionStore: new SessionStore(db), 76 requestLock, 77 }); 78};