A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz

Merge branch 'main' into feat/feed-generator

+37 -15
+4 -2
apps/api/src/auth/client.ts
··· 8 8 9 9 export const createClient = async (db: Database) => { 10 10 const publicUrl = env.PUBLIC_URL; 11 - const url = publicUrl || `http://127.0.0.1:${env.PORT}`; 11 + const url = publicUrl.includes("localhost") 12 + ? `http://127.0.0.1:${env.PORT}` 13 + : publicUrl; 12 14 const enc = encodeURIComponent; 13 15 14 16 const redis = new Redis(env.REDIS_URL); ··· 26 28 return new NodeOAuthClient({ 27 29 clientMetadata: { 28 30 client_name: "Rocksky", 29 - client_id: publicUrl 31 + client_id: !publicUrl.includes("localhost") 30 32 ? `${url}/oauth-client-metadata.json` 31 33 : `http://localhost?redirect_uri=${enc( 32 34 `${url}/oauth/callback`,
+1 -1
apps/api/src/lib/env.ts
··· 10 10 }), 11 11 HOST: host({ devDefault: testOnly("localhost") }), 12 12 PORT: port({ devDefault: testOnly(8000) }), 13 - PUBLIC_URL: str({}), 13 + PUBLIC_URL: str({ devDefault: "http://localhost:8000" }), 14 14 DB_PATH: str({ devDefault: ":memory:" }), 15 15 KV_DB_PATH: str({ devDefault: ":memory:" }), 16 16 COOKIE_SECRET: str({ devDefault: "00000000000000000000000000000000" }),
+16 -6
apps/api/src/xrpc/app/rocksky/charts/getScrobblesChart.ts
··· 1 1 import type { Context } from "context"; 2 2 import { eq } from "drizzle-orm"; 3 - import { Effect, Match, pipe } from "effect"; 3 + import { Effect, Match, pipe, Cache, Duration } from "effect"; 4 4 import type { Server } from "lexicon"; 5 5 import type { ChartsView } from "lexicon/types/app/rocksky/charts/defs"; 6 6 import type { QueryParams } from "lexicon/types/app/rocksky/charts/getScrobblesChart"; 7 7 import tables from "schema"; 8 8 9 9 export default function (server: Server, ctx: Context) { 10 + const getScrobblesCache = Cache.make({ 11 + capacity: 100, 12 + timeToLive: Duration.seconds(30), 13 + lookup: (params: QueryParams) => 14 + pipe( 15 + { params, ctx }, 16 + retrieve, 17 + Effect.flatMap(presentation), 18 + Effect.retry({ times: 3 }), 19 + Effect.timeout("10 seconds"), 20 + ), 21 + }); 22 + 10 23 const getScrobblesChart = (params) => 11 24 pipe( 12 - { params, ctx }, 13 - retrieve, 14 - Effect.flatMap(presentation), 15 - Effect.retry({ times: 3 }), 16 - Effect.timeout("10 seconds"), 25 + getScrobblesCache, 26 + Effect.flatMap((cache) => cache.get(params)), 17 27 Effect.catchAll((err) => { 18 28 console.error(err); 19 29 return Effect.succeed({ scrobbles: [] });
+16 -6
apps/api/src/xrpc/app/rocksky/feed/getNowPlayings.ts
··· 1 1 import type { Context } from "context"; 2 - import { Effect, pipe } from "effect"; 2 + import { Effect, pipe, Cache, Duration } from "effect"; 3 3 import type { Server } from "lexicon"; 4 4 import type { NowPlayingView } from "lexicon/types/app/rocksky/feed/defs"; 5 5 import type { QueryParams } from "lexicon/types/app/rocksky/feed/getNowPlayings"; 6 6 import { deepCamelCaseKeys } from "lib"; 7 7 8 8 export default function (server: Server, ctx: Context) { 9 + const nowPlayingCache = Cache.make({ 10 + capacity: 100, 11 + timeToLive: Duration.seconds(30), 12 + lookup: (params: QueryParams) => 13 + pipe( 14 + { params, ctx }, 15 + retrieve, 16 + Effect.flatMap(presentation), 17 + Effect.retry({ times: 3 }), 18 + Effect.timeout("10 seconds"), 19 + ), 20 + }); 21 + 9 22 const getNowPlayings = (params) => 10 23 pipe( 11 - { params, ctx }, 12 - retrieve, 13 - Effect.flatMap(presentation), 14 - Effect.retry({ times: 3 }), 15 - Effect.timeout("10 seconds"), 24 + nowPlayingCache, 25 + Effect.flatMap((cache) => cache.get(params)), 16 26 Effect.catchAll((err) => { 17 27 console.error(err); 18 28 return Effect.succeed({});