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
package/lib/device/account.mjs
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
// account.mjs — create a brand-new account + pod on a CSS server (v7 account
|
|
2
|
-
// API), the `setup --new-account` path. Public-server sibling of
|
|
3
|
-
// data-kitchen's seed-account.cjs, but simpler: the pod.create control makes
|
|
4
|
-
// the pod AND its WebID in one step — no ownership challenge, because the
|
|
5
|
-
// server authors the WebID itself.
|
|
6
|
-
//
|
|
7
|
-
// Flow: POST .account/account/ (or login when the email already exists) →
|
|
8
|
-
// password.create → account.pod {name} → { pod, webId }.
|
|
9
|
-
|
|
10
|
-
async function jfetch(url, { method = 'GET', body, cookie } = {}) {
|
|
11
|
-
const headers = { accept: 'application/json' };
|
|
12
|
-
if (body !== undefined) headers['content-type'] = 'application/json';
|
|
13
|
-
if (cookie) headers.cookie = cookie;
|
|
14
|
-
const res = await fetch(url, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
|
|
15
|
-
let json = null;
|
|
16
|
-
try { json = await res.json(); } catch { /* non-JSON */ }
|
|
17
|
-
return { status: res.status, json };
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function createAccountWithPod({ issuer, email, password, podName }) {
|
|
21
|
-
const origin = issuer.replace(/\/+$/, '');
|
|
22
|
-
const accountRoot = `${origin}/.account/`;
|
|
23
|
-
|
|
24
|
-
// Authenticate: log in when the email is already registered (idempotent
|
|
25
|
-
// re-runs), otherwise create the account and set its password.
|
|
26
|
-
let cookie;
|
|
27
|
-
const login = await jfetch(`${accountRoot}login/password/`, { method: 'POST', body: { email, password } });
|
|
28
|
-
if (login.status < 400 && login.json?.authorization) {
|
|
29
|
-
cookie = `css-account=${login.json.authorization}`;
|
|
30
|
-
} else {
|
|
31
|
-
const create = await jfetch(`${accountRoot}account/`, { method: 'POST' });
|
|
32
|
-
if (!create.json?.authorization) {
|
|
33
|
-
throw new Error(`account create failed (HTTP ${create.status}): ${create.json?.message || 'is this a CSS server with signup enabled?'}`);
|
|
34
|
-
}
|
|
35
|
-
cookie = `css-account=${create.json.authorization}`;
|
|
36
|
-
const pwCreate = (await jfetch(accountRoot, { cookie })).json?.controls?.password?.create;
|
|
37
|
-
if (!pwCreate) throw new Error('password.create control missing — not a CSS v7 account API');
|
|
38
|
-
const pw = await jfetch(pwCreate, { method: 'POST', cookie, body: { email, password } });
|
|
39
|
-
if (pw.status >= 400) throw new Error(`password create failed (HTTP ${pw.status}): ${pw.json?.message || ''}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Create the pod (server picks root-path or subdomain layout). When the
|
|
43
|
-
// name is already taken BY THIS ACCOUNT — a setup re-run, or a pod made
|
|
44
|
-
// earlier by hand — reuse it; taken by someone else stays a hard error.
|
|
45
|
-
const controls = (await jfetch(accountRoot, { cookie })).json?.controls;
|
|
46
|
-
const podCreate = controls?.account?.pod;
|
|
47
|
-
if (!podCreate) throw new Error('pod create control missing — not a CSS v7 account API');
|
|
48
|
-
// Scheme-agnostic on purpose: matching `https://name.` missed every pod on an
|
|
49
|
-
// http server, so a re-run after a crashed setup always tried to create again
|
|
50
|
-
// and died on "already registered to this account".
|
|
51
|
-
const findOwn = async () => {
|
|
52
|
-
const pods = (await jfetch(podCreate, { cookie })).json?.pods || {};
|
|
53
|
-
return Object.entries(pods).find(([url]) => {
|
|
54
|
-
try {
|
|
55
|
-
const u = new URL(url);
|
|
56
|
-
return u.hostname === podName || u.hostname.startsWith(`${podName}.`)
|
|
57
|
-
|| u.pathname.split('/').filter(Boolean).includes(podName);
|
|
58
|
-
} catch { return false; }
|
|
59
|
-
}) || null;
|
|
60
|
-
};
|
|
61
|
-
// One pod per account, refused BEFORE anything is created. A second pod gives
|
|
62
|
-
// the account a second WebID, and a credential can only be bound to one of
|
|
63
|
-
// them — so the second actor authenticates and then 403s every write to its
|
|
64
|
-
// own pod. Reusing a pod of the same name is still fine: that is a re-run of
|
|
65
|
-
// a setup, not a second identity.
|
|
66
|
-
const owned = Object.keys((await jfetch(podCreate, { cookie })).json?.pods || {});
|
|
67
|
-
if (owned.length && !await findOwn()) {
|
|
68
|
-
throw new Error(`${email} already has a pod on ${new URL(origin).host}: ${owned[0]} — `
|
|
69
|
-
+ 'one account, one pod. A second actor needs its own account and email. '
|
|
70
|
-
+ 'Nothing was created.');
|
|
71
|
-
}
|
|
72
|
-
const made = await jfetch(podCreate, { method: 'POST', cookie, body: { name: podName } });
|
|
73
|
-
let pod = made.json?.pod || made.json?.podBaseUrl || null;
|
|
74
|
-
let webId = made.json?.webId || null;
|
|
75
|
-
if (made.status >= 400 || !pod) {
|
|
76
|
-
const own = await findOwn();
|
|
77
|
-
if (own) {
|
|
78
|
-
pod = own[0];
|
|
79
|
-
webId = webId || own[1]?.webId || null;
|
|
80
|
-
} else if (made.status >= 400) {
|
|
81
|
-
throw new Error(`pod create failed (HTTP ${made.status}): ${made.json?.message || `is the name "${podName}" taken by another account?`}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (!pod) throw new Error('pod created but its URL was not reported — check the account dashboard');
|
|
85
|
-
pod = pod.endsWith('/') ? pod : pod + '/';
|
|
86
|
-
return { pod, webId: webId || new URL('profile/card#me', pod).href };
|
|
87
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// admin.mjs — loopback HTTP front: admin API + Mastodon client-API facade +
|
|
2
|
-
// the bundled Phanpy UI served same-origin (no CORS, no mixed content — the
|
|
3
|
-
// role data-kitchen's router plays for the in-app pane). Loopback-bound; the
|
|
4
|
-
// gate (vendor/gate.cjs) engages only when a token is configured, exactly
|
|
5
|
-
// its standalone behavior.
|
|
6
|
-
//
|
|
7
|
-
// The surface is surface.mjs, its routes are the modules under routes/, the
|
|
8
|
-
// static serving is static.mjs, the listener is server.mjs. This file is the
|
|
9
|
-
// door the rest of the agent imports through.
|
|
10
|
-
|
|
11
|
-
export { buildAdminSurface } from './surface.mjs';
|
|
12
|
-
export { startAdmin } from './server.mjs';
|
|
13
|
-
export { namedOrigin, secureOrigin, wsOrigins } from './origins.mjs';
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// origins.mjs — the addresses an identity answers on, as a browser must be
|
|
2
|
-
// sent to them.
|
|
3
|
-
|
|
4
|
-
import { allowedAuthorities, hostLabel } from '../../shared/guard.mjs';
|
|
5
|
-
|
|
6
|
-
// The origin a browser should be sent to for an identity. A browser keys
|
|
7
|
-
// storage per ORIGIN, so linking every identity at localhost:<port> files them
|
|
8
|
-
// all in one bucket — which is how a client ends up holding one actor's login
|
|
9
|
-
// and showing it on another's page. Named whenever the handle is a legal host
|
|
10
|
-
// label; bare loopback when it is not, because a mangled name would be an
|
|
11
|
-
// origin the other agent's own guard refuses.
|
|
12
|
-
export function namedOrigin(handle, port) {
|
|
13
|
-
const label = hostLabel(handle);
|
|
14
|
-
return `http://${label ? label + '.' : ''}localhost:${port}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// The https origin for an identity, on the one port it was given.
|
|
18
|
-
// This is the advertised default — what gets printed, opened and linked; the
|
|
19
|
-
// http listener stays served beside it for anything that needs cleartext.
|
|
20
|
-
export function secureOrigin(handle, httpsPort) {
|
|
21
|
-
const label = hostLabel(handle);
|
|
22
|
-
return `https://${label ? label + '.' : ''}localhost:${httpsPort}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function wsOrigins(port, labels = []) {
|
|
26
|
-
const out = new Set();
|
|
27
|
-
for (const a of allowedAuthorities(port, labels)) {
|
|
28
|
-
// No IPv6 at all. Bare `::1` cannot carry a port, and Chrome rejects the
|
|
29
|
-
// bracketed form inside a CSP source expression — one invalid source makes
|
|
30
|
-
// it drop the whole directive, which is worse than not listing the socket.
|
|
31
|
-
if (a.startsWith('::') || a.startsWith('[')) continue;
|
|
32
|
-
out.add(`wss://${/:\d+$/.test(a) ? a : `${a}:${port}`}`);
|
|
33
|
-
}
|
|
34
|
-
return [...out];
|
|
35
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// connections.mjs — the accounts held elsewhere: a Bluesky account and
|
|
2
|
-
// accounts on other fediverse servers, connected, adjusted, disconnected;
|
|
3
|
-
// and the page the other server's sign-in comes back to.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// The page the other server's redirect lands on. Self-contained on purpose:
|
|
8
|
-
// the browser arrives here from somewhere else, and nothing may load from
|
|
9
|
-
// that somewhere.
|
|
10
|
-
function callbackPage(ok, msg) {
|
|
11
|
-
const esc = (s) => String(s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
|
|
12
|
-
const head = ok ? 'Account connected' : 'Not connected';
|
|
13
|
-
return `<!doctype html>
|
|
14
|
-
<html lang="en"><head><meta charset="utf-8">
|
|
15
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
16
|
-
<title>${head}</title>
|
|
17
|
-
<style>
|
|
18
|
-
:root { color-scheme: light dark; }
|
|
19
|
-
body { font: 16px/1.7 system-ui, -apple-system, sans-serif; margin: 0;
|
|
20
|
-
padding: 2.5rem 2rem; background: #ffffff; color: #1a1a1a; }
|
|
21
|
-
main { max-width: 32rem; margin: 0 auto; }
|
|
22
|
-
h1 { font-size: 1.375rem; font-weight: 500; margin: 0 0 1rem; }
|
|
23
|
-
p { margin: 0 0 1rem; }
|
|
24
|
-
@media (prefers-color-scheme: dark) {
|
|
25
|
-
body { background: #16161a; color: #ececf0; }
|
|
26
|
-
}
|
|
27
|
-
</style></head>
|
|
28
|
-
<body><main>
|
|
29
|
-
<h1>${head}</h1>
|
|
30
|
-
<p>${esc(msg)}</p>
|
|
31
|
-
<p>You can close this tab and go back to FediPod.</p>
|
|
32
|
-
</main></body></html>
|
|
33
|
-
`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function get(p, ctx, req, res, url) { // eslint-disable-line no-unused-vars
|
|
37
|
-
const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
|
|
38
|
-
const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
|
|
39
|
-
|
|
40
|
-
if (req.method === 'GET' && p === '/fediacct') {
|
|
41
|
-
return json(res, 200, { accounts: agent.fediaccts?.status() || [] });
|
|
42
|
-
}
|
|
43
|
-
// Where the other server sends the browser back. A GET, so it gets none
|
|
44
|
-
// of the POST branch's local-only protection for free and asks for its
|
|
45
|
-
// own — a code arriving from anywhere else is not the owner's.
|
|
46
|
-
if (req.method === 'GET' && p === '/fediacct/callback') {
|
|
47
|
-
if (!embedded && !allowed.isLocalRequest(req)) {
|
|
48
|
-
return json(res, 403, { error: 'connecting an account is available on this machine only' });
|
|
49
|
-
}
|
|
50
|
-
const q = new URL(req.url, 'https://x.invalid').searchParams;
|
|
51
|
-
const done = (ok, msg) => {
|
|
52
|
-
res.writeHead(ok ? 200 : 400, { 'content-type': 'text/html; charset=utf-8' });
|
|
53
|
-
res.end(callbackPage(ok, msg));
|
|
54
|
-
return true;
|
|
55
|
-
};
|
|
56
|
-
if (q.get('error')) return done(false, q.get('error_description') || q.get('error'));
|
|
57
|
-
if (!q.get('code') || !q.get('state')) return done(false, 'that sign-in came back incomplete');
|
|
58
|
-
try {
|
|
59
|
-
const row = await agent.fediaccts.complete({ state: q.get('state'), code: q.get('code') });
|
|
60
|
-
const cfg = agent.store.getConfig();
|
|
61
|
-
agent.store.setConfig({ ...cfg, fediAccounts: agent.fediaccts.roster() });
|
|
62
|
-
await agent.store.flush();
|
|
63
|
-
agent.restartAccts?.();
|
|
64
|
-
return done(true, `${row.handle} is connected.`);
|
|
65
|
-
} catch (e) { return done(false, e.message); }
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export async function post(p, body, ctx, req, res) { // eslint-disable-line no-unused-vars
|
|
72
|
-
const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
|
|
73
|
-
const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
|
|
74
|
-
switch (p) {
|
|
75
|
-
case '/atproto/connect': {
|
|
76
|
-
// The one endpoint that ever sees the app password; loopback-only.
|
|
77
|
-
if (!body.identifier || !body.appPassword) {
|
|
78
|
-
return json(res, 400, { error: 'identifier and appPassword required' });
|
|
79
|
-
}
|
|
80
|
-
const conn = await agent.atproto.connect({
|
|
81
|
-
service: body.service, identifier: body.identifier, appPassword: body.appPassword,
|
|
82
|
-
});
|
|
83
|
-
const cfg = agent.store.getConfig();
|
|
84
|
-
agent.store.setConfig({
|
|
85
|
-
...cfg,
|
|
86
|
-
atproto: { ...conn, crossPost: cfg.atproto?.crossPost ?? true },
|
|
87
|
-
});
|
|
88
|
-
await agent.store.flush();
|
|
89
|
-
agent.startBsky?.();
|
|
90
|
-
return json(res, 200, { ok: true, ...agent.atproto.status() });
|
|
91
|
-
}
|
|
92
|
-
case '/atproto/disconnect': {
|
|
93
|
-
agent.stopBsky?.();
|
|
94
|
-
await agent.atproto.disconnect();
|
|
95
|
-
const { atproto, ...rest } = agent.store.getConfig();
|
|
96
|
-
agent.store.setConfig(rest);
|
|
97
|
-
await agent.store.flush();
|
|
98
|
-
return json(res, 200, { ok: true });
|
|
99
|
-
}
|
|
100
|
-
case '/atproto': {
|
|
101
|
-
// Non-secret settings only — today that is the cross-post toggle.
|
|
102
|
-
const cfg = agent.store.getConfig();
|
|
103
|
-
if (!cfg.atproto) return json(res, 400, { error: 'no bluesky account connected' });
|
|
104
|
-
agent.store.setConfig({
|
|
105
|
-
...cfg, atproto: { ...cfg.atproto, crossPost: !!body.crossPost },
|
|
106
|
-
});
|
|
107
|
-
await agent.store.flush();
|
|
108
|
-
return json(res, 200, { ok: true, atproto: { ...cfg.atproto, crossPost: !!body.crossPost } });
|
|
109
|
-
}
|
|
110
|
-
case '/fediacct/connect': {
|
|
111
|
-
// Starts the sign-in at the other server. Loopback-only, and the
|
|
112
|
-
// redirect returns to the origin the owner is actually using, not a
|
|
113
|
-
// guess — a client reaches this agent by several names.
|
|
114
|
-
if (!body.host) return json(res, 400, { error: 'the address of the server is required' });
|
|
115
|
-
try {
|
|
116
|
-
const { url } = await agent.fediaccts.begin({
|
|
117
|
-
host: body.host, redirectUri: `https://${req.headers.host}/fediacct/callback`,
|
|
118
|
-
});
|
|
119
|
-
return json(res, 200, { ok: true, authorize: url });
|
|
120
|
-
} catch (e) { return json(res, 400, { error: e.message }); }
|
|
121
|
-
}
|
|
122
|
-
case '/fediacct/disconnect': {
|
|
123
|
-
if (!body.id) return json(res, 400, { error: 'id required' });
|
|
124
|
-
if (!await agent.fediaccts.remove(body.id)) return json(res, 404, { error: 'no such account' });
|
|
125
|
-
const cfg = agent.store.getConfig();
|
|
126
|
-
agent.store.setConfig({ ...cfg, fediAccounts: agent.fediaccts.roster() });
|
|
127
|
-
await agent.store.flush();
|
|
128
|
-
agent.restartAccts?.();
|
|
129
|
-
return json(res, 200, { ok: true });
|
|
130
|
-
}
|
|
131
|
-
case '/fediacct': {
|
|
132
|
-
// Non-secret settings only — today that is whether it is polled.
|
|
133
|
-
if (!body.id) return json(res, 400, { error: 'id required' });
|
|
134
|
-
const row = agent.fediaccts.setEnabled(body.id, !!body.enabled);
|
|
135
|
-
if (!row) return json(res, 404, { error: 'no such account' });
|
|
136
|
-
const cfg = agent.store.getConfig();
|
|
137
|
-
agent.store.setConfig({ ...cfg, fediAccounts: agent.fediaccts.roster() });
|
|
138
|
-
await agent.store.flush();
|
|
139
|
-
agent.restartAccts?.();
|
|
140
|
-
return json(res, 200, { ok: true, account: row });
|
|
141
|
-
}
|
|
142
|
-
default: return false;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
// gateway.mjs — the inbox gateway lifecycle: configure, mode, check, attach
|
|
2
|
-
// through a front, forget.
|
|
3
|
-
|
|
4
|
-
import crypto from 'node:crypto';
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { spawn } from 'node:child_process';
|
|
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
|
-
// The inbox gateway lifecycle (opt-in). `configure` points at a
|
|
14
|
-
// deployed gateway (and reveals the HMAC secret once, to paste into
|
|
15
|
-
// its env); `mode` walks off→shadow→trust→locked and back, each step
|
|
16
|
-
// reversible; `forget` clears it and restores the pod inbox.
|
|
17
|
-
case '/gateway': {
|
|
18
|
-
const cfg = { ...agent.store.getConfig() };
|
|
19
|
-
const g = { ...(cfg.gateway || {}) };
|
|
20
|
-
const inboxUrl = agent.publisher?.urls?.inbox;
|
|
21
|
-
// Availability is a read: answered before the lease takeover below,
|
|
22
|
-
// which every real gateway change does want.
|
|
23
|
-
if (body.action === 'check') {
|
|
24
|
-
const front = String(body.front || '').replace(/\/+$/, '');
|
|
25
|
-
try { new URL(front); } catch { return json(res, 400, { error: 'front must be a gateway origin URL' }); }
|
|
26
|
-
const chk = await fetch(`${front}/api/handle?handle=${encodeURIComponent(String(body.handle || '').toLowerCase())}`,
|
|
27
|
-
{ headers: { accept: 'application/json' } }).then((r) => r.json()).catch(() => null);
|
|
28
|
-
if (!chk) return json(res, 502, { error: `${front} did not answer its handle check` });
|
|
29
|
-
return json(res, 200, { available: !!chk.available, reason: chk.reason || null });
|
|
30
|
-
}
|
|
31
|
-
await agent.requestTakeover?.();
|
|
32
|
-
const persist = async () => {
|
|
33
|
-
cfg.gateway = g; agent.store.setConfig(cfg);
|
|
34
|
-
if (agent.publisher) agent.publisher.config.gateway = g;
|
|
35
|
-
await agent.store.flush();
|
|
36
|
-
};
|
|
37
|
-
// Fronting renames the actor's ids; the signing key is the same
|
|
38
|
-
// identity's and moves with it, wherever the key record lives.
|
|
39
|
-
const restampKeys = async (actorId) => {
|
|
40
|
-
try {
|
|
41
|
-
const kp = path.join(agent.home, 'keys.json');
|
|
42
|
-
const rec = JSON.parse(fs.readFileSync(kp, 'utf8'));
|
|
43
|
-
if (rec.mintedFor) { rec.mintedFor = actorId; fs.writeFileSync(kp, JSON.stringify(rec)); return; }
|
|
44
|
-
} catch { /* not local — try pod state */ }
|
|
45
|
-
const podRec = agent.store.read('keys.json', null);
|
|
46
|
-
if (podRec?.mintedFor) { podRec.mintedFor = actorId; agent.store.write('keys.json', podRec); await agent.store.flush(); }
|
|
47
|
-
};
|
|
48
|
-
const podActorId = () => {
|
|
49
|
-
const base = cfg.remotePod.endsWith('/') ? cfg.remotePod : `${cfg.remotePod}/`;
|
|
50
|
-
const root = cfg.root ? (cfg.root.endsWith('/') ? cfg.root : `${cfg.root}/`) : 'fedipod/';
|
|
51
|
-
return `${base}${root}ap/actor`;
|
|
52
|
-
};
|
|
53
|
-
// The reply first, the restart a beat later — same shape as /update.
|
|
54
|
-
const selfRestart = (why) => setTimeout(() => {
|
|
55
|
-
log(why);
|
|
56
|
-
if (process.env.INVOCATION_ID) { process.exit(1); return; } // systemd: Restart=on-failure respawns
|
|
57
|
-
const child = spawn(process.execPath, process.argv.slice(1),
|
|
58
|
-
{ detached: true, stdio: 'ignore', env: process.env });
|
|
59
|
-
child.unref();
|
|
60
|
-
setTimeout(() => process.exit(0), 300);
|
|
61
|
-
}, 200);
|
|
62
|
-
if (body.action === 'configure') {
|
|
63
|
-
if (!/^https:\/\/\S+$/.test(String(body.url || ''))) return json(res, 400, { error: 'gateway url must be https' });
|
|
64
|
-
if (!/^https?:\/\/\S+$/.test(String(body.webId || ''))) return json(res, 400, { error: 'gateway webId must be a URL' });
|
|
65
|
-
g.url = String(body.url); g.webId = String(body.webId);
|
|
66
|
-
g.hmacSecret = g.hmacSecret || crypto.randomBytes(32).toString('base64');
|
|
67
|
-
g.mode = g.mode || 'off';
|
|
68
|
-
// The fronted actor URL, for a multi-user front that gives this
|
|
69
|
-
// identity a @name@front handle: the agent then publishes and signs
|
|
70
|
-
// under it. Optional; setting it on a published identity renames
|
|
71
|
-
// every id (a Move), so it belongs at setup or a fresh identity.
|
|
72
|
-
if ('frontActor' in body) {
|
|
73
|
-
const fa = String(body.frontActor || '');
|
|
74
|
-
if (fa && !/^https:\/\/\S+\/ap\/actor$/.test(fa)) {
|
|
75
|
-
return json(res, 400, { error: 'frontActor must be an https …/ap/actor URL' });
|
|
76
|
-
}
|
|
77
|
-
g.frontActor = fa || undefined;
|
|
78
|
-
}
|
|
79
|
-
await persist();
|
|
80
|
-
// The one time the secret is returned: the operator pastes it into
|
|
81
|
-
// the gateway's deploy env. /gateway GET never reveals it again.
|
|
82
|
-
return json(res, 200, { ok: true, mode: g.mode, url: g.url, webId: g.webId, hmacSecret: g.hmacSecret });
|
|
83
|
-
}
|
|
84
|
-
if (body.action === 'mode') {
|
|
85
|
-
const target = body.mode;
|
|
86
|
-
if (!['off', 'shadow', 'trust', 'locked'].includes(target)) {
|
|
87
|
-
return json(res, 400, { error: 'mode must be off, shadow, trust or locked' });
|
|
88
|
-
}
|
|
89
|
-
if (target !== 'off' && !g.url) return json(res, 400, { error: 'point at a gateway first (action: configure)' });
|
|
90
|
-
// Locking needs the door's own WebID to name in the ACL. Without it
|
|
91
|
-
// the mode used to persist and the ACL be written for an undefined
|
|
92
|
-
// agent, leaving config and pod disagreeing about who may write.
|
|
93
|
-
if (target === 'locked' && !g.webId) {
|
|
94
|
-
return json(res, 400, {
|
|
95
|
-
error: 'locked needs the gateway\'s WebID — set it with action: configure, webId: <door-webid>',
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
const prev = g.mode || 'off';
|
|
99
|
-
// The ACL first: a lock that fails must not leave the mode recorded
|
|
100
|
-
// as locked while the pod still accepts anyone's writes.
|
|
101
|
-
if (target === 'locked') await agent.publisher?.lockInboxToGateway(g.webId);
|
|
102
|
-
else if (prev === 'locked' && inboxUrl) await agent.remote.setAcl(inboxUrl, ['Append']);
|
|
103
|
-
g.mode = target;
|
|
104
|
-
await persist();
|
|
105
|
-
// Advertisement flips only when crossing the off boundary (the actor
|
|
106
|
-
// doc's inbox changes → publishProfile republishes, digest-gated).
|
|
107
|
-
if ((prev === 'off') !== (target === 'off')) await agent.publisher?.publishProfile();
|
|
108
|
-
else if (target !== 'off') await agent.publisher?.publishGatewayPolicy().catch(() => {});
|
|
109
|
-
return json(res, 200, { ok: true, mode: g.mode });
|
|
110
|
-
}
|
|
111
|
-
// Attach through a multi-user front (fedipod.net and kin): the agent
|
|
112
|
-
// proves the pod with its own credential — no browser, no password —
|
|
113
|
-
// and the front answers with the door and the receipt secret.
|
|
114
|
-
if (body.action === 'attach') {
|
|
115
|
-
const front = String(body.front || '').replace(/\/+$/, '');
|
|
116
|
-
let fu;
|
|
117
|
-
try { fu = new URL(front); } catch { return json(res, 400, { error: 'front must be the gateway origin, like https://fedipod.net' }); }
|
|
118
|
-
if (fu.protocol !== 'https:' && !/^(localhost|127\.0\.0\.1)$|\.localhost$/.test(fu.hostname)) {
|
|
119
|
-
return json(res, 400, { error: 'front must be https' });
|
|
120
|
-
}
|
|
121
|
-
if (agent.embedded) {
|
|
122
|
-
return json(res, 400, { error: 'this identity runs inside its pod server and has no portable credential — attach from a standalone agent' });
|
|
123
|
-
}
|
|
124
|
-
const named = !!String(body.handle || '').trim();
|
|
125
|
-
let handle = String(body.handle || cfg.handle || '').toLowerCase().trim();
|
|
126
|
-
if (!handle) return json(res, 400, { error: 'a name at the gateway is required' });
|
|
127
|
-
const fronted = body.fronted === true;
|
|
128
|
-
// Availability first, for a clean answer before anything is created.
|
|
129
|
-
const avail = async (h) => fetch(`${front}/api/handle?handle=${encodeURIComponent(h)}`,
|
|
130
|
-
{ headers: { accept: 'application/json' } }).then((r) => r.json()).catch(() => null);
|
|
131
|
-
let chk = await avail(handle);
|
|
132
|
-
if (!chk) return json(res, 502, { error: `${front} did not answer its handle check` });
|
|
133
|
-
// An unnamed door name is plumbing nobody reads: walk to a free
|
|
134
|
-
// variant instead of failing over a label the user never chose.
|
|
135
|
-
if (!chk.available && !named) {
|
|
136
|
-
for (let i = 2; i <= 9 && !chk.available; i++) {
|
|
137
|
-
const cand = `${handle}${i}`;
|
|
138
|
-
const c = await avail(cand);
|
|
139
|
-
if (c?.available) { handle = cand; chk = c; }
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (!chk.available) return json(res, 409, { error: chk.reason || `the name ${handle} is taken at ${front}` });
|
|
143
|
-
const frontActor = `${front}/u/${handle}/ap/actor`;
|
|
144
|
-
if (g.frontActor && (!fronted || g.frontActor !== frontActor)) {
|
|
145
|
-
return json(res, 400, { error: `this identity already fronts through ${g.frontActor} — changing a published front renames every id; detach first if you mean it` });
|
|
146
|
-
}
|
|
147
|
-
const attach = await agent.remote.session.fetch(`${front}/api/attach`, {
|
|
148
|
-
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
149
|
-
body: JSON.stringify({ handle, podHome: agent.urls.home, kind: cfg.kind || 'person', fronted }),
|
|
150
|
-
}).catch(() => null);
|
|
151
|
-
if (!attach) return json(res, 502, { error: `${front} did not answer the attach` });
|
|
152
|
-
const d = await attach.json().catch(() => ({}));
|
|
153
|
-
if (attach.status !== 201) {
|
|
154
|
-
return json(res, attach.status >= 400 && attach.status < 500 ? attach.status : 502,
|
|
155
|
-
{ error: d.error || `attach failed (HTTP ${attach.status})` });
|
|
156
|
-
}
|
|
157
|
-
g.url = String(d.doorInbox || `${front}/u/${handle}/ap/inbox/`);
|
|
158
|
-
if (d.hmacSecret) g.hmacSecret = String(d.hmacSecret);
|
|
159
|
-
if (fronted) g.frontActor = String(d.frontActor || frontActor);
|
|
160
|
-
if (!g.mode || g.mode === 'off') g.mode = 'shadow';
|
|
161
|
-
await persist();
|
|
162
|
-
// Inbox-only applies live: the actor republishes advertising the
|
|
163
|
-
// door. A front carries new ids, which are wired at startup — so a
|
|
164
|
-
// fronted attach restarts this agent itself, the reply going out
|
|
165
|
-
// first so it is not taken down with the process.
|
|
166
|
-
if (!fronted) {
|
|
167
|
-
await agent.publisher?.publishProfile();
|
|
168
|
-
await agent.publisher?.publishGatewayPolicy?.().catch(() => {});
|
|
169
|
-
return json(res, 200, { ok: true, mode: g.mode, url: g.url });
|
|
170
|
-
}
|
|
171
|
-
await restampKeys(g.frontActor);
|
|
172
|
-
json(res, 200, { ok: true, mode: g.mode, url: g.url,
|
|
173
|
-
frontActor: g.frontActor, address: d.address || null, restarting: true });
|
|
174
|
-
selfRestart('restarting to publish under the front');
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
if (body.action === 'forget') {
|
|
178
|
-
const wasLocked = g.mode === 'locked';
|
|
179
|
-
const wasFronted = !!g.frontActor;
|
|
180
|
-
delete cfg.gateway; agent.store.setConfig(cfg);
|
|
181
|
-
if (agent.publisher) agent.publisher.config.gateway = undefined;
|
|
182
|
-
await agent.store.flush();
|
|
183
|
-
if (wasLocked && inboxUrl) await agent.remote.setAcl(inboxUrl, ['Append']).catch(() => {});
|
|
184
|
-
if (wasFronted) {
|
|
185
|
-
// Going home renames every id back to the pod: key and process
|
|
186
|
-
// follow, the same way attach came.
|
|
187
|
-
await restampKeys(podActorId());
|
|
188
|
-
json(res, 200, { ok: true, mode: 'off', forgotten: true, restarting: true });
|
|
189
|
-
selfRestart('restarting under the pod\'s own ids');
|
|
190
|
-
return true;
|
|
191
|
-
}
|
|
192
|
-
await agent.publisher?.publishProfile(); // re-advertise the pod inbox
|
|
193
|
-
return json(res, 200, { ok: true, mode: 'off', forgotten: true });
|
|
194
|
-
}
|
|
195
|
-
return json(res, 400, { error: 'action must be configure, mode, check, attach or forget' });
|
|
196
|
-
}
|
|
197
|
-
default: return false;
|
|
198
|
-
}
|
|
199
|
-
}
|