···110110- [x] handle shutdown cleanly -- be nice to rocksdb
111111- [x] add user-agent to jetstream request
112112- [ ] wow the shutdown stuff i wrote is really bad and doesn't work a lot
113113-- [ ] serve html for browser requests
113113+- [~] serve html for browser requests
114114- [ ] add a health check endpoint
115115- [ ] add seq numbers to metrics
116116- [ ] persist the jetstream server url, error if started with a different one (maybe with --switch-streams or something)
···120120- [x] add rkey to linkers 🤦♀️
121121- [x] don't remove deleted links from the reverse records -- null them out. this will keep things stable for paging.
122122- [x] don't show deactivated accounts in link responses
123123+- [ ] canonicalize handles to dids!
123124- [ ] links:
124125 - [~] pull `$type`/`type` from object children of arrays (distinguish replies, quotes, etc)
125126 - just $type to start
···11+{% extends "base.html.j2" %}
22+33+{% block title %}Hello!{% endblock %}
44+55+{% block content %}
66+ <h1>This is an <a href="https://github.com/at-ucosm/links/tree/main/link_aggregator">atproto link aggregator</a> server from <a href="https://github.com/at-ucosm">µcosm</a>!</h1>
77+88+ <p>Every interaction in Bluesky and atproto at large tends to appear as a <em>link</em> from a new repository record to <em>somewhere</em>: liking a post creates a record with a link to the post, blocking a spammer creates a record with reference to their DID.</p>
99+1010+ <p>This service attempts to aggregate all of these links, globally, from all content coming throught the firehose. It provides generic API endpoints to answer questions like <strong>how many likes does a post have</strong> and <strong>who follows a user</strong>.</p>
1111+1212+ <p>It is very much a <strong>work in progress</strong>. The database has not been backfilled, so any interactions occurring before its last reset will be missing.</p>
1313+1414+ <p><small>µcosm ("microcosm") is a project by <a href="https://bsky.app/profile/bad-example.com">@bad-example.com</a>, making building apps on atproto easier.</small></p>
1515+1616+ <h2>Endpoints</h2>
1717+1818+ <h3><code>GET /links</code></h3>
1919+2020+ <p>A list of records linking to a target.</p>
2121+2222+ <h4>Query parameters:</h4>
2323+2424+ <ul>
2525+ <li><code>target</code>: required, must url-encode. Example: <code>did:plc:vc7f4oafdgxsihk4cry2xpze</code></li>
2626+ <li><code>collection</code>: required. Example: <code>app.bsky.feed.follow</code></li>
2727+ <li><code>path</code>: required, must url-encode. Example: <code>.subject</code></li>
2828+ </ul>
2929+3030+ <p><strong>Live example:</strong> <code><a href="/links?target=at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lgwdn7vd722r&collection=app.bsky.feed.like&path=.subject.uri">/links?target=at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lgwdn7vd722r&collection=app.bsky.feed.like&path=.subject.uri</a></code></p>
3131+3232+3333+ <h3><code>GET /links/count</code></h3>
3434+3535+ <p>The total number of links pointing at a given target.</p>
3636+3737+ <h4>Query parameters:</h4>
3838+3939+ <ul>
4040+ <li><code>target</code>: required, must url-encode. Example: <code>did:plc:vc7f4oafdgxsihk4cry2xpze</code></li>
4141+ <li><code>collection</code>: required. Example: <code>app.bsky.feed.follow</code></li>
4242+ <li><code>path</code>: required, must url-encode. Example: <code>.subject</code></li>
4343+ <li><code>cursor</code>: optional, see Definitions.</li>
4444+ </ul>
4545+4646+ <p><strong>Live example:</strong> <code><a href="/links/count?target=at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lgwdn7vd722r&collection=app.bsky.feed.like&path=.subject.uri">/links/count?target=at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lgwdn7vd722r&collection=app.bsky.feed.like&path=.subject.uri</a></code></p>
4747+4848+4949+ <h3><code>GET /links/all/count</code></h3>
5050+5151+ <p>The total number of links pointing at a given target from any collection and path.</p>
5252+5353+ <h4>Query parameters:</h4>
5454+5555+ <ul>
5656+ <li><code>target</code>: required, must url-encode. Example: <code>did:plc:vc7f4oafdgxsihk4cry2xpze</code></li>
5757+ </ul>
5858+5959+ <p><strong>Live example:</strong> <code><a href="/links/all/count?target=did:plc:vc7f4oafdgxsihk4cry2xpze">/links/all/count?target=did:plc:vc7f4oafdgxsihk4cry2xpze</a></code></p>
6060+6161+6262+ <h2>Definitions</h2>
6363+6464+ <h3>Target</h3>
6565+6666+ <p>A DID like <code>did:plc:hdhoaan3xa3jiuq4fg4mefid</code>, or an AT-URI like <code>at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3lgu4lg6j2k2v</code>, or a URI like <code>https://example.com</code>.</p>
6767+6868+ <h3>Collection</h3>
6969+7070+ <p>A record NSID like <code>app.bsky.feed.like</code>.</p>
7171+7272+ <h3>Path</h3>
7373+7474+ <p>A (currently-very-very-hacky) json-path-ish representation of the source of a link in a record. Records may contain multiple links with different meanings, so this specifies which specific link is of interest. Like <code>.subject.uri</code>.</p>
7575+7676+ <h3>Cursor</h3>
7777+7878+ <p>Paged responses include a <code>cursor</code> property. When it's <code>null</code>, no more data is available. If it's not null, you can repeat the request with <code>&cursor=<cursor></code> in the URL query to get the next set of responses.</p>
7979+8080+8181+{% endblock %}