| [8ef8c3e] | 1 | // One-time migration: convert the old Cirkels (pull-protocol circle_links) into
|
|---|
| 2 | // ActivityPub follows with auto-boost ("feature an artist"). Run per instance:
|
|---|
| 3 | // cd ~/apps/<instance> && node scripts/migrate-circles.mjs
|
|---|
| 4 | // Idempotent: followActor upserts ap_following, so re-running is safe.
|
|---|
| 5 |
|
|---|
| 6 | import 'dotenv/config';
|
|---|
| 7 | import db from '../src/config/database.js';
|
|---|
| 8 | import ActivityPubService from '../src/services/ActivityPubService.js';
|
|---|
| 9 |
|
|---|
| 10 | // A Klonkt site's AP actor is reachable from its root via content negotiation:
|
|---|
| 11 | // an AP-Accept GET on the root 302s to /ap/users/<slug>.
|
|---|
| 12 | async function resolveActor(siteUrl) {
|
|---|
| 13 | try {
|
|---|
| 14 | const r = await fetch(siteUrl, { headers: { Accept: 'application/activity+json' }, redirect: 'manual' });
|
|---|
| 15 | if (r.status >= 300 && r.status < 400) { const loc = r.headers.get('location'); if (loc) return loc; }
|
|---|
| 16 | if (r.ok) return siteUrl; // root already serves the actor
|
|---|
| 17 | } catch (e) { /* unreachable */ }
|
|---|
| 18 | return null;
|
|---|
| 19 | }
|
|---|
| 20 |
|
|---|
| 21 | let links = [];
|
|---|
| 22 | try {
|
|---|
| 23 | links = db.prepare(`
|
|---|
| 24 | SELECT cl.remote_url AS url, s.id AS sid, s.slug AS slug
|
|---|
| 25 | FROM circle_links cl JOIN sites s ON s.id = cl.local_site_id
|
|---|
| 26 | WHERE cl.status = 'active'
|
|---|
| 27 | `).all();
|
|---|
| 28 | } catch (e) { console.log('no circle_links table — nothing to migrate'); process.exit(0); }
|
|---|
| 29 |
|
|---|
| 30 | if (!links.length) { console.log('no active circle_links — nothing to migrate'); process.exit(0); }
|
|---|
| 31 |
|
|---|
| 32 | for (const l of links) {
|
|---|
| 33 | const actor = await resolveActor(l.url);
|
|---|
| 34 | if (!actor) { console.log(`SKIP ${l.slug} -> ${l.url} (actor unresolvable)`); continue; }
|
|---|
| 35 | try {
|
|---|
| 36 | const r = await ActivityPubService.followActor({ id: l.sid, slug: l.slug }, actor, true);
|
|---|
| 37 | console.log(`${l.slug} -> ${actor} : ${r && r.error ? 'ERR ' + r.error : 'OK (auto-boost)'}`);
|
|---|
| 38 | } catch (e) {
|
|---|
| 39 | console.log(`${l.slug} -> ${actor} : EXC ${e.message}`);
|
|---|
| 40 | }
|
|---|
| 41 | }
|
|---|
| 42 | process.exit(0);
|
|---|