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

Add webhook service, Dockerfile and admin auth

+31 -1
+6
tap/.dockerignore
··· 1 + .env 2 + data/ 3 + *.db 4 + *.db-shm 5 + *.db-wal 6 + node_modules/
+11
tap/Dockerfile
··· 1 + FROM denoland/deno:2.6.5 2 + 3 + WORKDIR /app 4 + 5 + COPY . . 6 + 7 + RUN deno cache src/main.ts 8 + 9 + EXPOSE 2481 10 + 11 + CMD ["run", "-A", "src/main.ts"]
+6
tap/compose.yaml
··· 9 9 environment: 10 10 TAP_DATABASE_URL: sqlite:///data/tap.db 11 11 TAP_RELAY_URL: https://relay1.us-east.bsky.network 12 + TAP_WEBHOOK_URL: http://webhook:2481 12 13 TAP_SIGNAL_COLLECTION: app.rocksky.scrobble 13 14 TAP_COLLECTION_FILTERS: app.rocksky.graph.follow,app.rocksky.* 15 + webhook: 16 + build: . 17 + restart: always 18 + ports: 19 + - "2481:2481"
+8 -1
tap/src/main.ts
··· 4 4 import { asc, inArray } from "drizzle-orm"; 5 5 import { omit } from "@es-toolkit/es-toolkit/compat"; 6 6 import type { SelectEvent } from "./schema/event.ts"; 7 - import { parseTapEvent } from "@atproto/tap"; 7 + import { assureAdminAuth, parseTapEvent } from "@atproto/tap"; 8 8 import { addToBatch, flushBatch } from "./batch.ts"; 9 9 10 10 const PAGE_SIZE = 100; 11 11 const YIELD_EVERY_N_PAGES = 5; 12 12 const YIELD_DELAY_MS = 100; 13 + const ADMIN_PASSWORD = Deno.env.get("TAP_ADMIN_PASSWORD")!; 13 14 14 15 interface ClientState { 15 16 socket: WebSocket; ··· 71 72 72 73 Deno.serve({ port: parseInt(Deno.env.get("WS_PORT") || "2481") }, (req) => { 73 74 if (req.method === "POST") { 75 + try { 76 + assureAdminAuth(ADMIN_PASSWORD, req.headers.get("authorization")!); 77 + } catch { 78 + logger.warn`Unauthorized access attempt ${req.headers.get("authorization")}`; 79 + return new Response(null, { status: 401 }); 80 + } 74 81 const evt = parseTapEvent(req.body); 75 82 switch (evt.type) { 76 83 case "identity": {