fedipod-server 0.28.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -34
- package/config/server.json +22 -0
- package/dist/claims.js +3 -2
- package/dist/directory.d.ts +2 -0
- package/dist/door.d.ts +4 -0
- package/dist/door.js +31 -0
- package/dist/fedipod.d.ts +16 -0
- package/dist/fedipod.js +13 -0
- package/dist/front.d.ts +3 -0
- package/dist/front.js +94 -0
- package/dist/handler.d.ts +39 -101
- package/dist/handler.js +86 -617
- package/dist/handler.jsonld +6 -30
- package/dist/identities.d.ts +62 -0
- package/dist/identities.js +236 -0
- package/dist/mounts.d.ts +43 -0
- package/dist/mounts.js +123 -0
- package/dist/opt-in.d.ts +46 -0
- package/dist/opt-in.js +176 -0
- package/dist/store-pod.js +37 -0
- package/package.json +10 -18
- package/lib/client/c2s.mjs +0 -582
- package/lib/client/localapi.mjs +0 -87
- package/lib/client/masto/accounts.mjs +0 -309
- package/lib/client/masto/body.mjs +0 -156
- package/lib/client/masto/index.mjs +0 -206
- package/lib/client/masto/instance.mjs +0 -112
- package/lib/client/masto/media.mjs +0 -79
- package/lib/client/masto/oauth.mjs +0 -656
- package/lib/client/masto/render.mjs +0 -518
- package/lib/client/masto/statuses.mjs +0 -372
- package/lib/client/masto/timelines.mjs +0 -438
- package/lib/client/oidc-auth.mjs +0 -62
- package/lib/client/streaming.mjs +0 -154
- package/lib/client/webpush.mjs +0 -98
- package/lib/connections/acctfeed.mjs +0 -257
- package/lib/connections/atproto.mjs +0 -309
- package/lib/connections/bskyfeed.mjs +0 -243
- package/lib/connections/bskygroup.mjs +0 -162
- package/lib/connections/fediacct.mjs +0 -253
- package/lib/connections/import.mjs +0 -357
- package/lib/connections/tagfeed.mjs +0 -212
- package/lib/connections/vault.mjs +0 -114
- package/lib/core/as2.mjs +0 -206
- package/lib/core/contexts/activitystreams.json +0 -379
- package/lib/core/contexts/anno.json +0 -126
- package/lib/core/contexts/did-v1.json +0 -57
- package/lib/core/contexts/fep-5711.json +0 -36
- package/lib/core/contexts/gotosocial.json +0 -86
- package/lib/core/contexts/identity-v1.json +0 -152
- package/lib/core/contexts/index.mjs +0 -63
- package/lib/core/contexts/join-lemmy.json +0 -33
- package/lib/core/contexts/joinmastodon.json +0 -28
- package/lib/core/contexts/litepub-0.1.json +0 -60
- package/lib/core/contexts/map.json +0 -19
- package/lib/core/contexts/miscellany.json +0 -19
- package/lib/core/contexts/quotes.json +0 -18
- package/lib/core/contexts/schemaorg.json +0 -8845
- package/lib/core/contexts/security-data-integrity-v1.json +0 -78
- package/lib/core/contexts/security-data-integrity-v2.json +0 -81
- package/lib/core/contexts/security-multikey-v1.json +0 -35
- package/lib/core/contexts/security-v1.json +0 -74
- package/lib/core/contexts/webfinger.json +0 -10
- package/lib/core/deliver.mjs +0 -371
- package/lib/core/graphview.mjs +0 -327
- package/lib/core/intake/activities.mjs +0 -633
- package/lib/core/intake/activity.mjs +0 -318
- package/lib/core/intake/channel.mjs +0 -144
- package/lib/core/intake/group.mjs +0 -256
- package/lib/core/intake/index.mjs +0 -701
- package/lib/core/intake/notes.mjs +0 -360
- package/lib/core/intake/verify.mjs +0 -142
- package/lib/core/keys.mjs +0 -153
- package/lib/core/lease.mjs +0 -206
- package/lib/core/polls.mjs +0 -105
- package/lib/core/profile-page.mjs +0 -104
- package/lib/core/proof.mjs +0 -79
- package/lib/core/publisher/collections.mjs +0 -237
- package/lib/core/publisher/index.mjs +0 -500
- package/lib/core/publisher/notes.mjs +0 -453
- package/lib/core/publisher/questions.mjs +0 -239
- package/lib/core/publisher/restore.mjs +0 -224
- package/lib/core/shapes/activitystreams.ttl +0 -157
- package/lib/core/shapes/index.mjs +0 -132
- package/lib/core/shapes/shapes-text.mjs +0 -13
- package/lib/core/social.mjs +0 -531
- package/lib/core/storage.mjs +0 -176
- package/lib/core/store.mjs +0 -568
- package/lib/core/wire-quotes.mjs +0 -71
- package/lib/core/wire.mjs +0 -889
- package/lib/device/account.mjs +0 -87
- package/lib/device/admin/index.mjs +0 -13
- package/lib/device/admin/origins.mjs +0 -35
- package/lib/device/admin/routes/connections.mjs +0 -144
- package/lib/device/admin/routes/gateway.mjs +0 -199
- package/lib/device/admin/routes/lifecycle.mjs +0 -200
- package/lib/device/admin/routes/owner.mjs +0 -322
- package/lib/device/admin/routes/setup.mjs +0 -412
- package/lib/device/admin/routes/social.mjs +0 -188
- package/lib/device/admin/server.mjs +0 -95
- package/lib/device/admin/static.mjs +0 -247
- package/lib/device/admin/surface.mjs +0 -315
- package/lib/device/certs.mjs +0 -281
- package/lib/device/cli/commands/account.mjs +0 -586
- package/lib/device/cli/commands/run.mjs +0 -278
- package/lib/device/cli/commands/service.mjs +0 -221
- package/lib/device/cli/commands/setup.mjs +0 -447
- package/lib/device/cli/commands/state.mjs +0 -561
- package/lib/device/cli/context.mjs +0 -288
- package/lib/device/export-collections.mjs +0 -161
- package/lib/device/gateway-move.mjs +0 -58
- package/lib/device/home.mjs +0 -161
- package/lib/device/migrate.mjs +0 -115
- package/lib/device/ports.mjs +0 -27
- package/lib/device/remote.mjs +0 -54
- package/lib/device/setup.mjs +0 -459
- package/lib/device/update.mjs +0 -96
- package/lib/gateway/caches.mjs +0 -36
- package/lib/gateway/directory.mjs +0 -90
- package/lib/gateway/front-core.mjs +0 -970
- package/lib/gateway/gateway-core.mjs +0 -165
- package/lib/gateway/headers.mjs +0 -49
- package/lib/gateway/httpsig.mjs +0 -159
- package/lib/gateway/notices.mjs +0 -84
- package/lib/gateway/quiet.mjs +0 -189
- package/lib/pod/README.md +0 -61
- package/lib/pod/actor.mjs +0 -96
- package/lib/pod/collection.mjs +0 -77
- package/lib/pod/containers.mjs +0 -140
- package/lib/pod/discovery.mjs +0 -46
- package/lib/pod/featured.mjs +0 -18
- package/lib/pod/followers.mjs +0 -24
- package/lib/pod/following.mjs +0 -10
- package/lib/pod/http.mjs +0 -74
- package/lib/pod/inbox.mjs +0 -166
- package/lib/pod/links.mjs +0 -35
- package/lib/pod/media.mjs +0 -27
- package/lib/pod/notes.mjs +0 -80
- package/lib/pod/notifications.mjs +0 -89
- package/lib/pod/outbox.mjs +0 -18
- package/lib/pod/package.json +0 -19
- package/lib/pod/policy.mjs +0 -58
- package/lib/pod/private.mjs +0 -19
- package/lib/pod/root.mjs +0 -104
- package/lib/pod/state.mjs +0 -40
- package/lib/pod/transport.mjs +0 -525
- package/lib/pod/urls.mjs +0 -85
- package/lib/server/embed.mjs +0 -423
- package/lib/session/README.md +0 -137
- package/lib/session/demo.html +0 -94
- package/lib/session/fedi-account.mjs +0 -408
- package/lib/session/fedi-login.mjs +0 -123
- package/lib/session/oidc-session.mjs +0 -233
- package/lib/session/package.json +0 -49
- package/lib/shared/guard.mjs +0 -213
- package/lib/shared/links.mjs +0 -9
- package/lib/shared/safefetch.mjs +0 -235
- package/lib/shared/ua.mjs +0 -29
- package/phanpy/dist/404.html +0 -40
- package/phanpy/dist/_headers +0 -2
- package/phanpy/dist/apple-touch-icon.png +0 -0
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
- package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
- package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
- package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
- package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
- package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
- package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
- package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
- package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
- package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
- package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
- package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
- package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
- package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
- package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
- package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
- package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
- package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
- package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
- package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
- package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
- package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
- package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
- package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
- package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
- package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
- package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
- package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
- package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
- package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
- package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
- package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
- package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
- package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
- package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
- package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
- package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
- package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
- package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
- package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
- package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
- package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
- package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
- package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
- package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
- package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
- package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
- package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
- package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
- package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
- package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
- package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
- package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
- package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
- package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
- package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
- package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
- package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
- package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
- package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
- package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
- package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
- package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
- package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
- package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
- package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
- package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
- package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
- package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
- package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
- package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
- package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
- package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
- package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
- package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
- package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
- package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
- package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
- package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
- package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
- package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
- package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
- package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
- package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
- package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
- package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
- package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
- package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
- package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
- package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
- package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
- package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
- package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
- package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
- package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
- package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
- package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
- package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
- package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
- package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
- package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
- package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
- package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
- package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
- package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
- package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
- package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
- package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
- package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
- package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
- package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
- package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
- package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
- package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
- package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
- package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
- package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
- package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
- package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
- package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
- package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
- package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
- package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
- package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
- package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
- package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
- package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
- package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
- package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
- package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
- package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
- package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
- package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
- package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
- package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
- package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
- package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
- package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
- package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
- package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
- package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
- package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
- package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
- package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
- package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
- package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
- package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
- package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
- package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
- package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
- package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
- package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
- package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
- package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
- package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
- package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
- package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
- package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
- package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
- package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
- package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
- package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
- package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
- package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
- package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
- package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
- package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
- package/phanpy/dist/compose/index.html +0 -20
- package/phanpy/dist/favicon.ico +0 -0
- package/phanpy/dist/index.html +0 -73
- package/phanpy/dist/logo-192.png +0 -0
- package/phanpy/dist/logo-512.png +0 -0
- package/phanpy/dist/logo-badge-72.png +0 -0
- package/phanpy/dist/logo-maskable-512.png +0 -0
- package/phanpy/dist/logo-monochrome-512.png +0 -0
- package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
- package/phanpy/dist/manifest.webmanifest +0 -1
- package/phanpy/dist/og-image-2.jpg +0 -0
- package/phanpy/dist/og-image.png +0 -0
- package/phanpy/dist/robots.txt +0 -2
- package/phanpy/dist/sw.js +0 -2
- package/phanpy/dist/sw.js.map +0 -1
- package/phanpy/dist/version.json +0 -1
- package/run-agent.mjs +0 -897
- package/vendor/gate.cjs +0 -138
- package/vendor/idp-grant.cjs +0 -394
- package/vendor/jfetch.cjs +0 -16
- package/web/admin/actors.js +0 -145
- package/web/admin/bar.css +0 -126
- package/web/admin/bar.js +0 -99
- package/web/admin/client/client.js +0 -66
- package/web/admin/client/index.html +0 -69
- package/web/admin/client-pick.js +0 -79
- package/web/admin/common.js +0 -23
- package/web/admin/connections.js +0 -112
- package/web/admin/gateway.js +0 -133
- package/web/admin/group.js +0 -258
- package/web/admin/index.html +0 -603
- package/web/admin/notices-bar.js +0 -90
- package/web/admin/oauth-signin.mjs +0 -37
- package/web/admin/record.js +0 -390
- package/web/admin/setup/index.html +0 -241
- package/web/admin/setup/setup.js +0 -326
- package/web/admin/tokens.css +0 -68
- package/web/admin/upkeep.js +0 -177
- package/web/admin/window.css +0 -55
- package/web/admin/window.js +0 -136
- package/web/app/README.md +0 -81
- package/web/app/admin-facade.mjs +0 -588
- package/web/app/agent.mjs +0 -663
- package/web/app/atproto-browser.mjs +0 -86
- package/web/app/authorize_interaction.html +0 -56
- package/web/app/authorize_interaction.js +0 -69
- package/web/app/boot.mjs +0 -576
- package/web/app/deliver-relay.mjs +0 -129
- package/web/app/dist/boot.js +0 -34218
- package/web/app/dist/boot.js.map +0 -7
- package/web/app/dist/sw.js +0 -72810
- package/web/app/dist/sw.js.map +0 -7
- package/web/app/fediacct-browser.mjs +0 -146
- package/web/app/gateway-move.mjs +0 -60
- package/web/app/idb-kv.mjs +0 -35
- package/web/app/index.html +0 -242
- package/web/app/keys-browser.mjs +0 -71
- package/web/app/keystore.mjs +0 -82
- package/web/app/oidc-session.mjs +0 -6
- package/web/app/pod-auth.mjs +0 -248
- package/web/app/pod-remote.mjs +0 -84
- package/web/app/shims/_globals.mjs +0 -2
- package/web/app/shims/fedify-sig.mjs +0 -77
- package/web/app/shims/node-crypto.mjs +0 -113
- package/web/app/shims/node-fs.mjs +0 -17
- package/web/app/shims/node-path.mjs +0 -19
- package/web/app/shims/node-url.mjs +0 -7
- package/web/app/shims/prelude.js +0 -58
- package/web/app/shims/safefetch.mjs +0 -58
- package/web/app/shims/shapes-text.mjs +0 -8
- package/web/app/shims/web-push.mjs +0 -14
- package/web/app/signup.mjs +0 -318
- package/web/app/sw-src.mjs +0 -220
- package/web/app/update.js +0 -70
- package/web/front/admin.html +0 -55
- package/web/front/admin.js +0 -89
- package/web/front/install.sh +0 -69
- package/web/front/new-account.html +0 -35
- package/web/front/new-account.js +0 -13
- package/web/front/notices.html +0 -69
- package/web/front/notices.js +0 -107
- package/web/front/run.html +0 -58
- package/web/front/run.js +0 -195
- package/web/front/solid-oidc-client.js +0 -6
- package/web/icons/forum.svg +0 -11
- package/web/icons/gateway.svg +0 -14
- package/web/icons/local.svg +0 -11
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
// lifecycle.mjs — what happens to the identity over time: draining and
|
|
2
|
-
// pruning the inbox, rebuilding posts, republishing, parking and reviving,
|
|
3
|
-
// rotating the key, moving, aliases, importing, retiring.
|
|
4
|
-
|
|
5
|
-
import { resolveHandle } from '../../../core/social.mjs';
|
|
6
|
-
import { webfingerHost } from '../../../core/wire.mjs';
|
|
7
|
-
import { normalizeImport, IMPORT_KINDS } from '../../../connections/import.mjs';
|
|
8
|
-
|
|
9
|
-
export async function post(p, body, ctx, req, res) { // eslint-disable-line no-unused-vars
|
|
10
|
-
const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
|
|
11
|
-
const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
|
|
12
|
-
switch (p) {
|
|
13
|
-
// Both of these DELETE from the pod's inbox, which is the one thing the
|
|
14
|
-
// lease exists to keep to a single agent. A viewer must claim it first
|
|
15
|
-
// and give up if it cannot — the facade has refused viewer writes since
|
|
16
|
-
// multi-device landed, and these two were simply missed.
|
|
17
|
-
// The account back in this agent's hands. A viewer publishes nothing and
|
|
18
|
-
// drains nothing, so a lease left behind by something that is gone leaves
|
|
19
|
-
// the account silent with nothing in the interface to say so.
|
|
20
|
-
case '/takeover': {
|
|
21
|
-
if (!await agent.requestTakeover?.()) {
|
|
22
|
-
return json(res, 503, { error: 'the lease could not be taken — the pod refused the write' });
|
|
23
|
-
}
|
|
24
|
-
return json(res, 200, { ok: true, mode: agent.status().mode });
|
|
25
|
-
}
|
|
26
|
-
case '/drain': {
|
|
27
|
-
if (!await agent.requestTakeover?.()) {
|
|
28
|
-
return json(res, 503, { error: 'another agent is active for this pod — it is doing the draining' });
|
|
29
|
-
}
|
|
30
|
-
await agent.intake.drain();
|
|
31
|
-
return json(res, 200, agent.status());
|
|
32
|
-
}
|
|
33
|
-
// The owner's answer to "your inbox is very full". Never automatic:
|
|
34
|
-
// discarding someone's mail is their call, not the agent's.
|
|
35
|
-
case '/inbox/prune': {
|
|
36
|
-
if (!body.before) return json(res, 400, { error: 'before (a date) required' });
|
|
37
|
-
if (!await agent.requestTakeover?.()) {
|
|
38
|
-
return json(res, 503, { error: 'another agent is active for this pod — discard from that one' });
|
|
39
|
-
}
|
|
40
|
-
return json(res, 200, await agent.intake.prune({
|
|
41
|
-
before: body.before,
|
|
42
|
-
...(body.keepConcerning ? { keepConcerning: true } : {}),
|
|
43
|
-
}));
|
|
44
|
-
}
|
|
45
|
-
// Put back the posts a lost or restored machine no longer knows about,
|
|
46
|
-
// from what the pod still serves. It writes the statuses store, which is
|
|
47
|
-
// the lease's business, so it refuses the same way the drain does.
|
|
48
|
-
case '/rebuild': {
|
|
49
|
-
if (!await agent.requestTakeover?.()) {
|
|
50
|
-
return json(res, 503, { error: 'another agent is active for this pod — rebuild from that one' });
|
|
51
|
-
}
|
|
52
|
-
return json(res, 200, await agent.publisher.rebuildStatuses({ fromNotes: !!body.fromNotes }));
|
|
53
|
-
}
|
|
54
|
-
case '/publish-profile': {
|
|
55
|
-
// The explicit "republish now" control. Asking for it IS the reason.
|
|
56
|
-
const r = await agent.publisher.publishProfile({ force: true });
|
|
57
|
-
return json(res, 200, { ok: true, ...(r?.unreachable?.length ? { unreachable: r.unreachable } : {}) });
|
|
58
|
-
}
|
|
59
|
-
// ---- lifecycle: what the CLI calls park | revive | rotate-key | retire ----
|
|
60
|
-
// Each claims the lease first, for the same reason /config does: someone
|
|
61
|
-
// acting on this page outranks an idle active agent on another device.
|
|
62
|
-
// Asking first is the page's job — see the warnings in its markup.
|
|
63
|
-
case '/park': {
|
|
64
|
-
await agent.requestTakeover?.();
|
|
65
|
-
return json(res, 200, { ok: true, ...await agent.park() });
|
|
66
|
-
}
|
|
67
|
-
case '/revive': {
|
|
68
|
-
await agent.requestTakeover?.();
|
|
69
|
-
return json(res, 200, { ok: true, ...await agent.revive() });
|
|
70
|
-
}
|
|
71
|
-
case '/rotate-key': {
|
|
72
|
-
await agent.requestTakeover?.();
|
|
73
|
-
const r = await agent.rotateKey();
|
|
74
|
-
return json(res, 200, { ok: true, changed: !!r?.changed });
|
|
75
|
-
}
|
|
76
|
-
// Hand the identity on. Federated and effectively one-way: the Move
|
|
77
|
-
// tells every follower's server to migrate them, and the actor is left
|
|
78
|
-
// advertising movedTo so the old handle redirects. Same typed-handle
|
|
79
|
-
// interlock as retire — a stray click cannot produce it.
|
|
80
|
-
case '/move': {
|
|
81
|
-
if (!body.target) return json(res, 400, { error: 'target required' });
|
|
82
|
-
if (!body.confirm || body.confirm !== agent.store.getConfig()?.handle) {
|
|
83
|
-
return json(res, 400, { error: 'type the handle to confirm' });
|
|
84
|
-
}
|
|
85
|
-
await agent.requestTakeover?.();
|
|
86
|
-
// A handle is not an actor URI, and the Move's target has to be one or
|
|
87
|
-
// the far side has nothing to migrate anyone to. The CLI resolves it
|
|
88
|
-
// the same way; doing it here rather than in the page keeps the one
|
|
89
|
-
// WebFinger lookup on the side that already knows how.
|
|
90
|
-
let target = String(body.target).trim();
|
|
91
|
-
if (!/^https?:\/\//.test(target)) {
|
|
92
|
-
const doc = await resolveHandle(agent, target);
|
|
93
|
-
if (!doc?.id) return json(res, 400, { error: `could not resolve ${target}` });
|
|
94
|
-
target = doc.id;
|
|
95
|
-
}
|
|
96
|
-
return json(res, 200, { ok: true, ...await agent.moveTo(target) });
|
|
97
|
-
}
|
|
98
|
-
// The inbound half of a migration: list an old account elsewhere in
|
|
99
|
-
// this actor's alsoKnownAs, which is what the old server checks for
|
|
100
|
-
// before it will send a Move here. Whatever is entered is resolved to
|
|
101
|
-
// the account's canonical id — the check on the far side is an exact
|
|
102
|
-
// string match, so a pasted profile URL stored as typed would fail it.
|
|
103
|
-
case '/alias': {
|
|
104
|
-
const urls = agent.publisher?.urls;
|
|
105
|
-
if (!urls) return json(res, 409, { error: 'agent not connected yet' });
|
|
106
|
-
if (!body.add && !body.remove) return json(res, 400, { error: 'add or remove required' });
|
|
107
|
-
await agent.requestTakeover?.();
|
|
108
|
-
const cfg = { ...agent.store.getConfig() };
|
|
109
|
-
const aliases = [...(cfg.aliases || [])];
|
|
110
|
-
if (body.add) {
|
|
111
|
-
// A Move target nobody can resolve is a landing pad nobody lands on.
|
|
112
|
-
if (!webfingerHost(urls.base) && !cfg.gateway?.frontActor) {
|
|
113
|
-
return json(res, 400, {
|
|
114
|
-
error: 'this pod is a suffix-based host, so WebFinger cannot answer for it '
|
|
115
|
-
+ '— other servers could never resolve this account as a Move target',
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
const input = String(body.add).trim().replace(/^@/, '');
|
|
119
|
-
let doc = /^https?:\/\//.test(input)
|
|
120
|
-
? await agent.intake.fetchAP(input).catch(() => null)
|
|
121
|
-
: await resolveHandle(agent, input).catch(() => null);
|
|
122
|
-
// A URL may answer with any id it likes; before that id is stored
|
|
123
|
-
// it must vouch for itself — the intake's verify-by-deref.
|
|
124
|
-
if (doc?.id && /^https?:\/\//.test(input) && doc.id !== input) {
|
|
125
|
-
const own = await agent.intake.fetchAP(doc.id).catch(() => null);
|
|
126
|
-
doc = own?.id === doc.id ? own : null;
|
|
127
|
-
}
|
|
128
|
-
if (!doc?.id) {
|
|
129
|
-
return json(res, 400, { error: `could not fetch the old account (${input}) — it must exist and answer` });
|
|
130
|
-
}
|
|
131
|
-
if (doc.id === urls.actor) return json(res, 400, { error: 'that is this account' });
|
|
132
|
-
if (!aliases.includes(doc.id)) aliases.push(doc.id);
|
|
133
|
-
} else {
|
|
134
|
-
const target = String(body.remove).trim();
|
|
135
|
-
if (!aliases.includes(target)) return json(res, 404, { error: 'no such alias' });
|
|
136
|
-
// Follower servers process a Move on their own retry schedules,
|
|
137
|
-
// over days — an alias removed early strands the late ones.
|
|
138
|
-
if (!body.confirm) {
|
|
139
|
-
return json(res, 409, {
|
|
140
|
-
error: 'servers still retrying the Move check this alias and would strand '
|
|
141
|
-
+ 'their followers — send confirm: true to remove it anyway',
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
aliases.splice(aliases.indexOf(target), 1);
|
|
145
|
-
}
|
|
146
|
-
cfg.aliases = aliases;
|
|
147
|
-
agent.store.setConfig(cfg);
|
|
148
|
-
agent.publisher.config.aliases = aliases;
|
|
149
|
-
await agent.store.flush();
|
|
150
|
-
const pub = await agent.publisher.publishProfile();
|
|
151
|
-
return json(res, 200, {
|
|
152
|
-
ok: true, aliases,
|
|
153
|
-
...(pub?.unreachable?.length ? { unreachable: pub.unreachable } : {}),
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
// Stage a CSV export from the old account. Rows are applied by the
|
|
157
|
-
// agent's paced worker, not in this request — big lists take a while,
|
|
158
|
-
// and GET /import is the window onto how far it has gotten.
|
|
159
|
-
case '/import': {
|
|
160
|
-
if (!agent.importer) return json(res, 409, { error: 'agent not connected yet' });
|
|
161
|
-
// Unlike the one-shot routes, staging arms a worker that keeps
|
|
162
|
-
// writing for minutes — a device that could not take the lease must
|
|
163
|
-
// not run one alongside the device that holds it.
|
|
164
|
-
const took = await agent.requestTakeover?.();
|
|
165
|
-
if (took === false) {
|
|
166
|
-
return json(res, 503, { error: 'another device is active for this pod — import from there' });
|
|
167
|
-
}
|
|
168
|
-
if (body.clear === true) {
|
|
169
|
-
agent.importer.clear();
|
|
170
|
-
return json(res, 200, { ok: true, cleared: true });
|
|
171
|
-
}
|
|
172
|
-
if (!IMPORT_KINDS.includes(body.kind)) {
|
|
173
|
-
return json(res, 400, { error: `kind must be one of: ${IMPORT_KINDS.join(', ')}` });
|
|
174
|
-
}
|
|
175
|
-
if (typeof body.text !== 'string' || !body.text.trim()) {
|
|
176
|
-
return json(res, 400, { error: 'text required — the CSV file contents' });
|
|
177
|
-
}
|
|
178
|
-
const { values, invalid } = normalizeImport(body.kind, body.text);
|
|
179
|
-
const r = agent.importer.stage(body.kind, values);
|
|
180
|
-
await agent.store.flush();
|
|
181
|
-
return json(res, 200, {
|
|
182
|
-
ok: true, kind: body.kind, ...r,
|
|
183
|
-
invalid: invalid.length,
|
|
184
|
-
...(invalid.length ? { invalidSample: invalid.slice(0, 5) } : {}),
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
case '/retire': {
|
|
188
|
-
// Irreversible and federated: a Delete leaves for every follower and
|
|
189
|
-
// the actor becomes a Tombstone. The typed handle is the interlock —
|
|
190
|
-
// a stray click cannot produce it, and nor can anything that never
|
|
191
|
-
// read the record.
|
|
192
|
-
if (!body.confirm || body.confirm !== agent.store.getConfig()?.handle) {
|
|
193
|
-
return json(res, 400, { error: 'type the handle to confirm' });
|
|
194
|
-
}
|
|
195
|
-
await agent.requestTakeover?.();
|
|
196
|
-
return json(res, 200, { ok: true, ...await agent.publisher.retireActor() });
|
|
197
|
-
}
|
|
198
|
-
default: return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
// owner.mjs — the record: what the owner's page reads about this identity,
|
|
2
|
-
// and the one write that edits it.
|
|
3
|
-
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { announceModeration } from '../../../core/social.mjs';
|
|
7
|
-
import { addRemoveActivity, publicHandle } from '../../../core/wire.mjs';
|
|
8
|
-
import { hashPassword } from '../../../client/masto/index.mjs';
|
|
9
|
-
import { identityHomes, rootOf, tildify, defaultProfile } from '../../home.mjs';
|
|
10
|
-
import { localFetch } from '../../../client/localapi.mjs';
|
|
11
|
-
import { AGENT_VERSION } from '../static.mjs';
|
|
12
|
-
import { secureOrigin } from '../origins.mjs';
|
|
13
|
-
|
|
14
|
-
// The identity itself. Changing any of these means a different actor at a
|
|
15
|
-
// different address, which is a new setup, not an edit.
|
|
16
|
-
const PERMANENT_CONFIG = ['handle', 'remotePod', 'issuer', 'root', 'kind'];
|
|
17
|
-
// Config the actor document carries, so a change is not real until it is
|
|
18
|
-
// republished.
|
|
19
|
-
const WIRE_CONFIG = ['name', 'summary', 'icon', 'image', 'fields', 'approveJoins', 'moderators'];
|
|
20
|
-
|
|
21
|
-
export async function get(p, ctx, req, res, url) { // eslint-disable-line no-unused-vars
|
|
22
|
-
const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
|
|
23
|
-
const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
|
|
24
|
-
|
|
25
|
-
if (req.method === 'GET' && p === '/status') return json(res, 200, agent.status());
|
|
26
|
-
// The moderation queue: what listed moderators asked for over federation,
|
|
27
|
-
// waiting for the operator to vouch (or not). See POST /modqueue.
|
|
28
|
-
if (req.method === 'GET' && p === '/modqueue') {
|
|
29
|
-
return json(res, 200, agent.store.getConfig()?.kind === 'group'
|
|
30
|
-
? agent.store.read('modqueue.json', []) : []);
|
|
31
|
-
}
|
|
32
|
-
// The inbox gateway (optional, opt-in). Its state, and the shadow-mode
|
|
33
|
-
// measurement that says whether it is worth trusting. The HMAC secret is
|
|
34
|
-
// never returned — only whether one is set.
|
|
35
|
-
if (req.method === 'GET' && p === '/gateway') {
|
|
36
|
-
const g = agent.store.getConfig()?.gateway || null;
|
|
37
|
-
return json(res, 200, {
|
|
38
|
-
configured: !!(g && g.url),
|
|
39
|
-
url: g?.url || null, webId: g?.webId || null,
|
|
40
|
-
frontActor: g?.frontActor || null,
|
|
41
|
-
mode: g?.mode || 'off', hasSecret: !!g?.hmacSecret,
|
|
42
|
-
stats: agent.store.read('gateway-stats.json', { verified: 0, unverified: 0, lastAt: null }),
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
// The other identities on this machine, so the page can link to each one.
|
|
46
|
-
// Their `agent.json` is the only file read — a port and a handle. A
|
|
47
|
-
// sibling's credential and keys are never opened; anything else shown
|
|
48
|
-
// here comes from that agent answering /status for itself.
|
|
49
|
-
if (req.method === 'GET' && p === '/profiles') {
|
|
50
|
-
const here = path.resolve(agent.home || '');
|
|
51
|
-
// Which identity a plain command means: the last one started. A
|
|
52
|
-
// different question from `current`, which is the page you are on.
|
|
53
|
-
const wasLast = defaultProfile(rootOf(here));
|
|
54
|
-
const rows = await Promise.all(identityHomes(rootOf(here)).map(async ({ name, dir }) => {
|
|
55
|
-
// agent.json holds a port AND a handle — setup records both, exactly
|
|
56
|
-
// so the named origin can be built before that agent has said a word.
|
|
57
|
-
let rec = {};
|
|
58
|
-
try { rec = JSON.parse(fs.readFileSync(path.join(dir, 'agent.json'), 'utf8')) || {}; } catch {}
|
|
59
|
-
const port = rec.port || null;
|
|
60
|
-
// A directory under profiles/ is not an identity until it holds a
|
|
61
|
-
// credential or has run somewhere. A half-finished setup leaves one
|
|
62
|
-
// behind, and listing it offers a page that cannot exist yet.
|
|
63
|
-
if (!port && !fs.existsSync(path.join(dir, 'credential.json'))) return null;
|
|
64
|
-
const current = path.resolve(dir) === here;
|
|
65
|
-
const live = current ? agent.status() : port
|
|
66
|
-
? await localFetch(agent.home, port, '/status', { timeout: 1200 })
|
|
67
|
-
.then(r => r.json()).catch(() => null)
|
|
68
|
-
: null;
|
|
69
|
-
// The handle is right here — spending it on the origin is the whole
|
|
70
|
-
// point of having asked that agent who it is. A stopped one never
|
|
71
|
-
// answers, so its recorded handle stands in: the link has to be named
|
|
72
|
-
// before you get there, or starting it lands you on the shared origin.
|
|
73
|
-
const handle = live?.handle || rec.handle || null;
|
|
74
|
-
const origin = port
|
|
75
|
-
? secureOrigin(handle, port)
|
|
76
|
-
: null;
|
|
77
|
-
// The fediverse address, assembled from the two things /status has:
|
|
78
|
-
// the handle, and the pod host its actor URL sits on.
|
|
79
|
-
let address = null;
|
|
80
|
-
if (live?.handle && live?.actor) {
|
|
81
|
-
try {
|
|
82
|
-
const front = live.actor.match(/\/u\/([^/]+)\/ap\/actor\/?$/)?.[1];
|
|
83
|
-
address = `${front || live.handle}@${new URL(live.actor).host}`;
|
|
84
|
-
} catch { /* not a URL yet */ }
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
name, port, current,
|
|
88
|
-
admin: origin ? `${origin}/admin/` : null,
|
|
89
|
-
app: origin ? `${origin}/` : null,
|
|
90
|
-
handle,
|
|
91
|
-
address,
|
|
92
|
-
lastUsed: name === wasLast,
|
|
93
|
-
kind: live?.kind || null,
|
|
94
|
-
mode: live ? live.mode : null,
|
|
95
|
-
};
|
|
96
|
-
}));
|
|
97
|
-
return json(res, 200, { identities: rows.filter(Boolean) });
|
|
98
|
-
}
|
|
99
|
-
if (req.method === 'GET' && p === '/blocks') return json(res, 200, agent.store.getBlocklist());
|
|
100
|
-
if (req.method === 'GET' && p === '/log') return json(res, 200, { lines: agent.logLines(200) });
|
|
101
|
-
if (req.method === 'GET' && p === '/deadletter') return json(res, 200, { items: agent.store.getDeadLetters() });
|
|
102
|
-
|
|
103
|
-
if (req.method === 'GET' && p === '/config') {
|
|
104
|
-
const cfg = agent.store.getConfig();
|
|
105
|
-
if (!cfg) return json(res, 409, { error: 'agent not configured — set it up at /admin/setup/' });
|
|
106
|
-
const urls = agent.urls || agent.publisher?.urls || null;
|
|
107
|
-
const wfHost = urls ? new URL(urls.base).host : null;
|
|
108
|
-
// A fronted identity's address is its name AT THE FRONT — the front
|
|
109
|
-
// serves the actor under that name, whatever this pod calls it.
|
|
110
|
-
const address = cfg.gateway?.frontActor
|
|
111
|
-
? `@${publicHandle(cfg)}@${new URL(cfg.gateway.frontActor).host}`
|
|
112
|
-
: (wfHost ? `@${cfg.handle}@${wfHost}` : null);
|
|
113
|
-
return json(res, 200, {
|
|
114
|
-
// permanent
|
|
115
|
-
handle: cfg.handle, remotePod: cfg.remotePod, issuer: cfg.issuer,
|
|
116
|
-
root: cfg.root || null, kind: cfg.kind || 'person',
|
|
117
|
-
actor: urls?.actor || null, webId: agent.readCredential?.()?.webId || null,
|
|
118
|
-
// The opaque id the client addresses this actor by, so the record can
|
|
119
|
-
// link straight at its profile there. Derived from the actor URL the
|
|
120
|
-
// same way every other id is — computed here rather than in the page,
|
|
121
|
-
// which has no business knowing how they are made.
|
|
122
|
-
accountId: urls?.actor ? agent.store.idFor(urls.actor) : null,
|
|
123
|
-
address,
|
|
124
|
-
// editable
|
|
125
|
-
name: cfg.name || null, summary: cfg.summary || null, icon: cfg.icon || null,
|
|
126
|
-
image: cfg.image || null, fields: cfg.fields || [],
|
|
127
|
-
approveJoins: !!cfg.approveJoins, review: !!cfg.review,
|
|
128
|
-
aliases: cfg.aliases || [],
|
|
129
|
-
autoAcceptFollows: !!cfg.autoAcceptFollows,
|
|
130
|
-
// never the record itself
|
|
131
|
-
hasUiPassword: !!cfg.uiPassword,
|
|
132
|
-
quiescedAt: cfg.quiescedAt || null, movedTo: cfg.movedTo || null,
|
|
133
|
-
// Per-machine, so it comes from the credential file, not pod config —
|
|
134
|
-
// and it is a fact here, not a setting: moving it means moving data,
|
|
135
|
-
// which is `fedipod state --to <url>`.
|
|
136
|
-
privateRoot: agent.readCredential?.()?.privateRoot || null,
|
|
137
|
-
mode: agent.status?.().mode || null, port, home: tildify(agent.home) || null,
|
|
138
|
-
update: agent.updateInfo || null,
|
|
139
|
-
// What this process is running, against what a restart would run:
|
|
140
|
-
// the checkout can move while an agent stays up.
|
|
141
|
-
version: AGENT_VERSION,
|
|
142
|
-
versionOnDisk: versionOnDisk(),
|
|
143
|
-
pendingUpgrade: agent.pendingUpgrade || [],
|
|
144
|
-
// The connected Bluesky account, non-secret half. `connected` is the
|
|
145
|
-
// credential's word, so a config entry orphaned by a deleted
|
|
146
|
-
// atproto.json shows as disconnected rather than pretending.
|
|
147
|
-
atproto: cfg.atproto
|
|
148
|
-
? { ...cfg.atproto, connected: !!agent.atproto?.connected() }
|
|
149
|
-
: null,
|
|
150
|
-
// Connected fediverse accounts, from the credentials themselves
|
|
151
|
-
// rather than from config — a roster entry whose file was deleted
|
|
152
|
-
// should stop being listed, not linger.
|
|
153
|
-
fediAccounts: agent.fediaccts?.status() || [],
|
|
154
|
-
origins: {
|
|
155
|
-
loopback: publicOrigin
|
|
156
|
-
|| `https://localhost:${port}/`,
|
|
157
|
-
named: publicOrigin
|
|
158
|
-
? null
|
|
159
|
-
: allowed.label
|
|
160
|
-
? (true
|
|
161
|
-
? `https://${allowed.label}.localhost:${port}/`
|
|
162
|
-
: `http://${allowed.label}.localhost:${port}/`)
|
|
163
|
-
: null,
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
// How far the CSV import has gotten — polled by the CLI while it runs.
|
|
168
|
-
if (req.method === 'GET' && p === '/import') {
|
|
169
|
-
if (!agent.importer) return json(res, 409, { error: 'agent not connected yet' });
|
|
170
|
-
return json(res, 200, agent.importer.progress());
|
|
171
|
-
}
|
|
172
|
-
// Group-only: who is here, and what the group has carried. A group cannot
|
|
173
|
-
// force an unfollow, so muting — declining to carry — is its whole lever.
|
|
174
|
-
if (req.method === 'GET' && p === '/members') {
|
|
175
|
-
if (!isGroup()) return json(res, 404, { error: 'not a group' });
|
|
176
|
-
const muted = agent.store.getMuted().actors;
|
|
177
|
-
return json(res, 200, {
|
|
178
|
-
members: agent.store.getContacts().followers
|
|
179
|
-
.map(f => ({
|
|
180
|
-
actor: f.actor,
|
|
181
|
-
handle: agent.store.handleOf(f.actor),
|
|
182
|
-
inbox: f.sharedInbox || f.inbox,
|
|
183
|
-
muted: muted.includes(f.actor),
|
|
184
|
-
})),
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
if (req.method === 'GET' && p === '/announced') {
|
|
188
|
-
if (!isGroup()) return json(res, 404, { error: 'not a group' });
|
|
189
|
-
return json(res, 200, {
|
|
190
|
-
announced: agent.store.getStatuses().filter(s => s.announcedAt)
|
|
191
|
-
.map(s => ({ noteId: s.noteId, actor: s.actor, announcedAt: s.announcedAt })),
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
if (req.method === 'GET' && p === '/requests') {
|
|
195
|
-
// Not group-only any more: a person queues unverifiable follows here
|
|
196
|
-
// too, and a queue with no way to read it is worse than no queue.
|
|
197
|
-
return json(res, 200, {
|
|
198
|
-
approveJoins: !!agent.store.getConfig()?.approveJoins,
|
|
199
|
-
requests: agent.store.getRequests().map(r => ({ actor: r.actor, at: r.at })),
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
if (req.method === 'GET' && p === '/pending') {
|
|
203
|
-
if (!isGroup()) return json(res, 404, { error: 'not a group' });
|
|
204
|
-
return json(res, 200, {
|
|
205
|
-
review: !!agent.store.getConfig()?.review,
|
|
206
|
-
pending: agent.store.getPending(),
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export async function post(p, body, ctx, req, res) { // eslint-disable-line no-unused-vars
|
|
214
|
-
const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
|
|
215
|
-
const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
|
|
216
|
-
switch (p) {
|
|
217
|
-
// ---- the record, edited from the page at /admin/ ----
|
|
218
|
-
case '/config': {
|
|
219
|
-
const fixed = PERMANENT_CONFIG.filter(k => k in body);
|
|
220
|
-
if (fixed.length) {
|
|
221
|
-
return json(res, 400, {
|
|
222
|
-
error: `${fixed.join(', ')} cannot be changed — that is the identity itself, not a setting`,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
for (const k of ['approveJoins', 'review']) {
|
|
226
|
-
if (k in body && !isGroup()) return json(res, 404, { error: 'not a group' });
|
|
227
|
-
}
|
|
228
|
-
// A user acting here outranks an idle active agent on another device:
|
|
229
|
-
// claim the lease rather than write state that one will clobber.
|
|
230
|
-
await agent.requestTakeover?.();
|
|
231
|
-
// Merge, never replace: the UI password and anything else set later
|
|
232
|
-
// must survive an edit that never mentions it.
|
|
233
|
-
const cfg = { ...agent.store.getConfig() };
|
|
234
|
-
if ('name' in body) {
|
|
235
|
-
if (!body.name) return json(res, 400, { error: 'a display name is required' });
|
|
236
|
-
cfg.name = String(body.name);
|
|
237
|
-
}
|
|
238
|
-
if ('summary' in body) cfg.summary = body.summary || undefined;
|
|
239
|
-
if ('icon' in body) cfg.icon = body.icon || undefined;
|
|
240
|
-
// The banner and the labelled rows, so a client and this page edit one
|
|
241
|
-
// record rather than each other's leftovers.
|
|
242
|
-
if ('image' in body) cfg.image = body.image || undefined;
|
|
243
|
-
if ('fields' in body) {
|
|
244
|
-
cfg.fields = (Array.isArray(body.fields) ? body.fields : [])
|
|
245
|
-
.filter(f => f?.name?.trim())
|
|
246
|
-
.map(f => ({ name: String(f.name).trim(), value: String(f.value ?? '').trim() }));
|
|
247
|
-
}
|
|
248
|
-
if ('approveJoins' in body) cfg.approveJoins = !!body.approveJoins;
|
|
249
|
-
if ('review' in body) cfg.review = !!body.review;
|
|
250
|
-
// A person's queue gate: on, and an inbound Follow is accepted on
|
|
251
|
-
// arrival instead of waiting in the requests queue. Local behavior,
|
|
252
|
-
// not on the wire — but intake reads the LIVE config, so it is
|
|
253
|
-
// patched below whether or not anything republishes.
|
|
254
|
-
if ('autoAcceptFollows' in body) cfg.autoAcceptFollows = !!body.autoAcceptFollows;
|
|
255
|
-
// The moderator roster (FEP-1b12): actor IRIs whose federated
|
|
256
|
-
// moderation asks are queued, published as attributedTo.
|
|
257
|
-
let rosterDiff = null;
|
|
258
|
-
if ('moderators' in body) {
|
|
259
|
-
if (!isGroup()) return json(res, 404, { error: 'not a group' });
|
|
260
|
-
const next = [...new Set((Array.isArray(body.moderators) ? body.moderators : [])
|
|
261
|
-
.map(String).filter(m => /^https?:\/\/\S+$/.test(m)))];
|
|
262
|
-
const prev = cfg.moderators || [];
|
|
263
|
-
rosterDiff = {
|
|
264
|
-
added: next.filter(m => !prev.includes(m)),
|
|
265
|
-
removed: prev.filter(m => !next.includes(m)),
|
|
266
|
-
};
|
|
267
|
-
cfg.moderators = next;
|
|
268
|
-
}
|
|
269
|
-
if ('password' in body) {
|
|
270
|
-
// '' clears it. The UI password is what turns the instant OAuth
|
|
271
|
-
// redirect into a login form; switching that off is a real choice.
|
|
272
|
-
if (body.password) cfg.uiPassword = hashPassword(body.password);
|
|
273
|
-
else delete cfg.uiPassword;
|
|
274
|
-
}
|
|
275
|
-
const republish = WIRE_CONFIG.some(k => k in body);
|
|
276
|
-
agent.store.setConfig(cfg);
|
|
277
|
-
if ('autoAcceptFollows' in body && agent.publisher) {
|
|
278
|
-
agent.publisher.config.autoAcceptFollows = cfg.autoAcceptFollows;
|
|
279
|
-
}
|
|
280
|
-
if (republish && agent.publisher) {
|
|
281
|
-
Object.assign(agent.publisher.config, {
|
|
282
|
-
name: cfg.name, summary: cfg.summary, icon: cfg.icon,
|
|
283
|
-
image: cfg.image, fields: cfg.fields,
|
|
284
|
-
approveJoins: !!cfg.approveJoins,
|
|
285
|
-
moderators: cfg.moderators,
|
|
286
|
-
aliases: cfg.aliases,
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
await agent.store.flush();
|
|
290
|
-
// Publishing is only half of it: publishProfile re-fetches the public
|
|
291
|
-
// documents unauthenticated and reports the ones a stranger's server
|
|
292
|
-
// could not read. Carrying that back is what lets this be the only
|
|
293
|
-
// republish control there is.
|
|
294
|
-
const pub = republish ? await agent.publisher.publishProfile() : null;
|
|
295
|
-
// Roster changes are announced to the membership (FEP-1b12), so
|
|
296
|
-
// their servers can mirror who moderates here.
|
|
297
|
-
if (rosterDiff && agent.publisher) {
|
|
298
|
-
const urls = agent.publisher.urls;
|
|
299
|
-
for (const [type, list] of [['Add', rosterDiff.added], ['Remove', rosterDiff.removed]]) {
|
|
300
|
-
for (const m of list) {
|
|
301
|
-
await announceModeration(agent, addRemoveActivity({
|
|
302
|
-
urls, type, object: m, target: urls.moderators, serial: Date.now(),
|
|
303
|
-
})).catch(e => log(`moderation announce failed: ${e.message}`));
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
return json(res, 200, {
|
|
308
|
-
ok: true, published: republish,
|
|
309
|
-
...(pub?.unreachable?.length ? { unreachable: pub.unreachable } : {}),
|
|
310
|
-
config: {
|
|
311
|
-
name: cfg.name || null, summary: cfg.summary || null, icon: cfg.icon || null,
|
|
312
|
-
image: cfg.image || null, fields: cfg.fields || [],
|
|
313
|
-
approveJoins: !!cfg.approveJoins, review: !!cfg.review,
|
|
314
|
-
autoAcceptFollows: !!cfg.autoAcceptFollows,
|
|
315
|
-
...(isGroup() ? { moderators: cfg.moderators || [] } : {}),
|
|
316
|
-
hasUiPassword: !!cfg.uiPassword,
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
default: return false;
|
|
321
|
-
}
|
|
322
|
-
}
|