fedipod-server 0.10.0 → 0.12.1
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 +15 -6
- package/dist/claims.js +1 -1
- package/dist/handler.d.ts +7 -1
- package/dist/handler.js +58 -12
- package/dist/handler.jsonld +5 -1
- package/dist/store-pod.js +18 -4
- package/lib/{c2s.mjs → client/c2s.mjs} +8 -3
- package/lib/{localapi.mjs → client/localapi.mjs} +2 -2
- package/lib/client/masto/accounts.mjs +264 -0
- package/lib/client/masto/body.mjs +69 -0
- package/lib/client/masto/index.mjs +183 -0
- package/lib/client/masto/instance.mjs +104 -0
- package/lib/client/masto/media.mjs +133 -0
- package/lib/client/masto/oauth.mjs +599 -0
- package/lib/client/masto/render.mjs +459 -0
- package/lib/client/masto/statuses.mjs +331 -0
- package/lib/client/masto/timelines.mjs +316 -0
- package/lib/{streaming.mjs → client/streaming.mjs} +1 -1
- package/lib/{acctfeed.mjs → connections/acctfeed.mjs} +1 -1
- package/lib/{atproto.mjs → connections/atproto.mjs} +15 -16
- package/lib/{bskygroup.mjs → connections/bskygroup.mjs} +1 -1
- package/lib/{fediacct.mjs → connections/fediacct.mjs} +31 -35
- package/lib/{import.mjs → connections/import.mjs} +2 -2
- package/lib/{tagfeed.mjs → connections/tagfeed.mjs} +21 -4
- package/lib/connections/vault.mjs +114 -0
- package/lib/core/as2.mjs +124 -0
- package/lib/core/contexts/activitystreams.json +379 -0
- package/lib/core/contexts/did-v1.json +57 -0
- package/lib/core/contexts/fep-5711.json +36 -0
- package/lib/core/contexts/gotosocial.json +86 -0
- package/lib/core/contexts/identity-v1.json +152 -0
- package/lib/core/contexts/index.mjs +45 -0
- package/lib/core/contexts/join-lemmy.json +33 -0
- package/lib/core/contexts/joinmastodon.json +28 -0
- package/lib/core/contexts/map.json +16 -0
- package/lib/core/contexts/miscellany.json +19 -0
- package/lib/core/contexts/schemaorg.json +8845 -0
- package/lib/core/contexts/security-data-integrity-v1.json +78 -0
- package/lib/core/contexts/security-data-integrity-v2.json +81 -0
- package/lib/core/contexts/security-multikey-v1.json +35 -0
- package/lib/core/contexts/security-v1.json +74 -0
- package/lib/core/contexts/webfinger.json +10 -0
- package/lib/{deliver.mjs → core/deliver.mjs} +8 -2
- package/lib/core/intake/activities.mjs +437 -0
- package/lib/core/intake/activity.mjs +240 -0
- package/lib/core/intake/channel.mjs +144 -0
- package/lib/core/intake/group.mjs +222 -0
- package/lib/core/intake/index.mjs +629 -0
- package/lib/core/intake/notes.mjs +288 -0
- package/lib/core/intake/verify.mjs +141 -0
- package/lib/{keys.mjs → core/keys.mjs} +1 -1
- package/lib/core/publisher/collections.mjs +229 -0
- package/lib/core/publisher/index.mjs +421 -0
- package/lib/core/publisher/notes.mjs +188 -0
- package/lib/core/publisher/questions.mjs +233 -0
- package/lib/core/publisher/restore.mjs +196 -0
- package/lib/core/shapes/activitystreams.ttl +129 -0
- package/lib/core/shapes/index.mjs +107 -0
- package/lib/core/shapes/shapes-text.mjs +13 -0
- package/lib/{social.mjs → core/social.mjs} +19 -10
- package/lib/{storage.mjs → core/storage.mjs} +2 -2
- package/lib/{store.mjs → core/store.mjs} +12 -1
- package/lib/{wire.mjs → core/wire.mjs} +48 -67
- package/lib/device/admin/index.mjs +13 -0
- package/lib/device/admin/origins.mjs +35 -0
- package/lib/device/admin/routes/connections.mjs +144 -0
- package/lib/device/admin/routes/gateway.mjs +199 -0
- package/lib/device/admin/routes/lifecycle.mjs +191 -0
- package/lib/device/admin/routes/owner.mjs +322 -0
- package/lib/device/admin/routes/setup.mjs +393 -0
- package/lib/device/admin/routes/social.mjs +188 -0
- package/lib/device/admin/server.mjs +95 -0
- package/lib/device/admin/static.mjs +244 -0
- package/lib/device/admin/surface.mjs +274 -0
- package/lib/device/cli/commands/account.mjs +586 -0
- package/lib/device/cli/commands/run.mjs +278 -0
- package/lib/device/cli/commands/service.mjs +221 -0
- package/lib/device/cli/commands/setup.mjs +410 -0
- package/lib/device/cli/commands/state.mjs +559 -0
- package/lib/device/cli/context.mjs +288 -0
- package/lib/{export-collections.mjs → device/export-collections.mjs} +1 -1
- package/lib/{migrate.mjs → device/migrate.mjs} +5 -19
- package/lib/device/remote.mjs +54 -0
- package/lib/{setup.mjs → device/setup.mjs} +3 -3
- package/lib/{update.mjs → device/update.mjs} +1 -1
- package/lib/{directory.mjs → gateway/directory.mjs} +1 -1
- package/lib/{front-core.mjs → gateway/front-core.mjs} +272 -49
- package/lib/{gateway-core.mjs → gateway/gateway-core.mjs} +3 -7
- package/lib/{httpsig.mjs → gateway/httpsig.mjs} +34 -3
- package/lib/pod/README.md +61 -0
- package/lib/pod/actor.mjs +96 -0
- package/lib/pod/collection.mjs +77 -0
- package/lib/pod/containers.mjs +136 -0
- package/lib/pod/discovery.mjs +46 -0
- package/lib/pod/featured.mjs +18 -0
- package/lib/pod/followers.mjs +24 -0
- package/lib/pod/following.mjs +10 -0
- package/lib/pod/http.mjs +74 -0
- package/lib/pod/inbox.mjs +144 -0
- package/lib/{links.mjs → pod/links.mjs} +1 -1
- package/lib/pod/media.mjs +27 -0
- package/lib/pod/notes.mjs +80 -0
- package/lib/pod/notifications.mjs +89 -0
- package/lib/pod/outbox.mjs +18 -0
- package/lib/pod/package.json +19 -0
- package/lib/pod/policy.mjs +58 -0
- package/lib/pod/private.mjs +19 -0
- package/lib/pod/root.mjs +63 -0
- package/lib/pod/state.mjs +39 -0
- package/lib/{remote.mjs → pod/transport.mjs} +217 -159
- package/lib/pod/urls.mjs +71 -0
- package/lib/{embed.mjs → server/embed.mjs} +149 -22
- package/lib/shared/links.mjs +9 -0
- package/lib/{ua.mjs → shared/ua.mjs} +1 -1
- package/package.json +1 -1
- package/run-agent.mjs +47 -64
- package/web/admin/actors.js +145 -0
- package/web/admin/bar.css +9 -13
- package/web/admin/bar.js +2 -2
- package/web/admin/client/client.js +1 -1
- package/web/admin/client/index.html +5 -6
- package/web/admin/common.js +23 -0
- package/web/admin/connections.js +112 -0
- package/web/admin/gateway.js +111 -0
- package/web/admin/group.js +258 -0
- package/web/admin/index.html +60 -66
- package/web/admin/record.js +378 -0
- package/web/admin/setup/index.html +3 -1
- package/web/admin/setup/setup.js +2 -13
- package/web/admin/tokens.css +56 -0
- package/web/admin/upkeep.js +170 -0
- package/web/app/README.md +77 -0
- package/web/app/admin-facade.mjs +549 -0
- package/web/app/agent.mjs +490 -0
- package/web/app/atproto-browser.mjs +86 -0
- package/web/app/boot.mjs +340 -0
- package/web/app/deliver-relay.mjs +72 -0
- package/web/app/dist/boot.js +34067 -0
- package/web/app/dist/boot.js.map +7 -0
- package/web/app/dist/sw.js +70014 -0
- package/web/app/dist/sw.js.map +7 -0
- package/web/app/fediacct-browser.mjs +146 -0
- package/web/app/idb-kv.mjs +35 -0
- package/web/app/index.html +203 -0
- package/web/app/index.html~ +152 -0
- package/web/app/keys-browser.mjs +46 -0
- package/web/app/keystore.mjs +99 -0
- package/web/app/oidc-session.mjs +189 -0
- package/web/app/pod-auth.mjs +248 -0
- package/web/app/pod-remote.mjs +84 -0
- package/web/app/shims/_globals.mjs +2 -0
- package/web/app/shims/fedify-sig.mjs +77 -0
- package/web/app/shims/node-crypto.mjs +113 -0
- package/web/app/shims/node-fs.mjs +17 -0
- package/web/app/shims/node-path.mjs +19 -0
- package/web/app/shims/node-url.mjs +7 -0
- package/web/app/shims/prelude.js +58 -0
- package/web/app/shims/safefetch.mjs +58 -0
- package/web/app/shims/shapes-text.mjs +8 -0
- package/web/app/shims/web-push.mjs +14 -0
- package/web/app/signup.mjs +248 -0
- package/web/app/site/_headers +9 -0
- package/web/app/site/_redirects +21 -0
- package/web/app/site/admin/actors.js +145 -0
- package/web/app/site/admin/bar.css +60 -0
- package/web/app/site/admin/bar.js +61 -0
- package/web/app/site/admin/client/client.js +66 -0
- package/web/app/site/admin/client/index.html +43 -0
- package/web/app/site/admin/common.js +23 -0
- package/web/app/site/admin/connections.js +112 -0
- package/web/app/site/admin/gateway.js +111 -0
- package/web/app/site/admin/group.js +258 -0
- package/web/app/site/admin/index.html +562 -0
- package/web/app/site/admin/record.js +378 -0
- package/web/app/site/admin/setup/index.html +192 -0
- package/web/app/site/admin/setup/setup.js +297 -0
- package/web/app/site/admin/tokens.css +56 -0
- package/web/app/site/admin/upkeep.js +170 -0
- package/web/app/site/admin/window.css +55 -0
- package/web/app/site/admin/window.js +136 -0
- package/web/app/site/app/404.html +40 -0
- package/web/app/site/app/_headers +2 -0
- package/web/app/site/app/apple-touch-icon.png +0 -0
- package/web/app/site/app/assets/ICONS-DIPJeU0C.js +2 -0
- package/web/app/site/app/assets/ICONS-DIPJeU0C.js.map +1 -0
- package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js +183 -0
- package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js.map +1 -0
- package/web/app/site/app/assets/Temml-CCjoacWt.woff2 +0 -0
- package/web/app/site/app/assets/__vite-browser-external-C7iut881.js +2 -0
- package/web/app/site/app/assets/__vite-browser-external-C7iut881.js.map +1 -0
- package/web/app/site/app/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
- package/web/app/site/app/assets/catch-up-O5q5C75Z.png +0 -0
- package/web/app/site/app/assets/chunk-aKtaBQYM.js +1 -0
- package/web/app/site/app/assets/compose-CFEx7wb4.js +2 -0
- package/web/app/site/app/assets/compose-CFEx7wb4.js.map +1 -0
- package/web/app/site/app/assets/compose-ZJN5xSb4.js +35 -0
- package/web/app/site/app/assets/compose-ZJN5xSb4.js.map +1 -0
- package/web/app/site/app/assets/debug.module-YFQgwnIr.js +52 -0
- package/web/app/site/app/assets/debug.module-YFQgwnIr.js.map +1 -0
- package/web/app/site/app/assets/dom-p1YcJ5Rw.js +2 -0
- package/web/app/site/app/assets/dom-p1YcJ5Rw.js.map +1 -0
- package/web/app/site/app/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
- package/web/app/site/app/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
- package/web/app/site/app/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
- package/web/app/site/app/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
- package/web/app/site/app/assets/icons/add-circle-line-WD0mwORD.js +1 -0
- package/web/app/site/app/assets/icons/alert-line-CpFUMf9s.js +1 -0
- package/web/app/site/app/assets/icons/android-2-line-Bzxnrd3S.js +1 -0
- package/web/app/site/app/assets/icons/announcement-line-5nwaJFJm.js +1 -0
- package/web/app/site/app/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +1 -0
- package/web/app/site/app/assets/icons/arrow-down-line-B8uzXzq6.js +1 -0
- package/web/app/site/app/assets/icons/arrow-left-line-CGrGT9C-.js +1 -0
- package/web/app/site/app/assets/icons/arrow-right-line-CE-HERfX.js +1 -0
- package/web/app/site/app/assets/icons/arrow-to-up-line-CvdQjmcy.js +1 -0
- package/web/app/site/app/assets/icons/arrow-up-circle-line-DEparSNF.js +1 -0
- package/web/app/site/app/assets/icons/arrow-up-line-C7yBeugc.js +1 -0
- package/web/app/site/app/assets/icons/arrows-right-line-BtbXmJGP.js +1 -0
- package/web/app/site/app/assets/icons/at-line-CF66Bznk.js +1 -0
- package/web/app/site/app/assets/icons/attachment-line-BY8It-l9.js +1 -0
- package/web/app/site/app/assets/icons/blockquote-line-BS6pU_hm.js +1 -0
- package/web/app/site/app/assets/icons/board-line-C0UWXyA6.js +1 -0
- package/web/app/site/app/assets/icons/bookmark-line-1CtX5JLF.js +1 -0
- package/web/app/site/app/assets/icons/building-5-line-Dd59MzbO.js +1 -0
- package/web/app/site/app/assets/icons/bus-2-line-6_dvA0GG.js +1 -0
- package/web/app/site/app/assets/icons/calendar-day-line-Crpl_fmu.js +1 -0
- package/web/app/site/app/assets/icons/calendar-month-line-vommts2C.js +1 -0
- package/web/app/site/app/assets/icons/calendar-time-add-line-BU_DTlQk.js +1 -0
- package/web/app/site/app/assets/icons/camera-line-Cv69MEei.js +1 -0
- package/web/app/site/app/assets/icons/celebrate-line-B8DvIoi0.js +1 -0
- package/web/app/site/app/assets/icons/chart-bar-line-FK-QOmtF.js +1 -0
- package/web/app/site/app/assets/icons/chart-line-line-BGcJAFWw.js +1 -0
- package/web/app/site/app/assets/icons/chat-3-line-YFK2-hwz.js +1 -0
- package/web/app/site/app/assets/icons/check-circle-line-DsAxFr5-.js +1 -0
- package/web/app/site/app/assets/icons/clipboard-line-BBp364gJ.js +1 -0
- package/web/app/site/app/assets/icons/close-circle-line-CICdiuGz.js +1 -0
- package/web/app/site/app/assets/icons/close-line-DYrFWcjv.js +1 -0
- package/web/app/site/app/assets/icons/cloud-line-tiQAbah8.js +1 -0
- package/web/app/site/app/assets/icons/code-line-fAl8iiV3.js +1 -0
- package/web/app/site/app/assets/icons/comment-2-line-CR3g7WeB.js +1 -0
- package/web/app/site/app/assets/icons/copy-2-line-CLW69yF5.js +1 -0
- package/web/app/site/app/assets/icons/delete-2-line-B8xmbr4n.js +1 -0
- package/web/app/site/app/assets/icons/document-3-line-DBGNhAg2.js +1 -0
- package/web/app/site/app/assets/icons/document-line-C7mb9sb7.js +1 -0
- package/web/app/site/app/assets/icons/down-line-BGqSSThd.js +1 -0
- package/web/app/site/app/assets/icons/edit-4-line-DcgGaEhx.js +1 -0
- package/web/app/site/app/assets/icons/emoji-2-line-D8YgnM-y.js +1 -0
- package/web/app/site/app/assets/icons/exit-line-Dc07BS-x.js +1 -0
- package/web/app/site/app/assets/icons/external-link-line-BvpSkmjO.js +1 -0
- package/web/app/site/app/assets/icons/eye-2-line-CiWXx-f_.js +1 -0
- package/web/app/site/app/assets/icons/eye-close-line-BgTaSJ4P.js +1 -0
- package/web/app/site/app/assets/icons/filter-2-line-B_mR2usy.js +1 -0
- package/web/app/site/app/assets/icons/filter-line-C_nm-3cU.js +1 -0
- package/web/app/site/app/assets/icons/flag-1-line-C2HB05I0.js +1 -0
- package/web/app/site/app/assets/icons/forbid-circle-line-D3LTT9yd.js +1 -0
- package/web/app/site/app/assets/icons/formula-line-DLrt2d6j.js +1 -0
- package/web/app/site/app/assets/icons/grid-line-CgUmEikf.js +1 -0
- package/web/app/site/app/assets/icons/group-line-C5fnX0wU.js +1 -0
- package/web/app/site/app/assets/icons/hand-finger-2-line-BGgxbuL9.js +1 -0
- package/web/app/site/app/assets/icons/hashtag-line-CyTEEXBb.js +1 -0
- package/web/app/site/app/assets/icons/heart-crack-line-BZddbO2C.js +1 -0
- package/web/app/site/app/assets/icons/heart-line-CJ7K47J4.js +1 -0
- package/web/app/site/app/assets/icons/history-2-line-kWWvFzqv.js +1 -0
- package/web/app/site/app/assets/icons/history-line-bGQiwbTH.js +1 -0
- package/web/app/site/app/assets/icons/home-3-line-fRU1zeLG.js +1 -0
- package/web/app/site/app/assets/icons/information-line-iNk6-sOY.js +1 -0
- package/web/app/site/app/assets/icons/keyboard-line-DsCyuQNz.js +1 -0
- package/web/app/site/app/assets/icons/layout-4-line-CZkOX52Z.js +1 -0
- package/web/app/site/app/assets/icons/layout-5-line-B_znt5wO.js +1 -0
- package/web/app/site/app/assets/icons/left-line-eUw0tya4.js +1 -0
- package/web/app/site/app/assets/icons/lightning-line-DaKoDdix.js +1 -0
- package/web/app/site/app/assets/icons/link-2-line-DCqLmMA7.js +1 -0
- package/web/app/site/app/assets/icons/list-check-line-BAxosBh9.js +1 -0
- package/web/app/site/app/assets/icons/lock-line-CPoaXeUW.js +1 -0
- package/web/app/site/app/assets/icons/mail-line-B0P8aa1L.js +1 -0
- package/web/app/site/app/assets/icons/moon-line-CEqNAToP.js +1 -0
- package/web/app/site/app/assets/icons/more-1-fill-Dw2e5xab.js +1 -0
- package/web/app/site/app/assets/icons/more-3-line-BIiVjYTz.js +1 -0
- package/web/app/site/app/assets/icons/notification-line-BZZq2Gtu.js +1 -0
- package/web/app/site/app/assets/icons/pencil-line-CKh8-A1v.js +1 -0
- package/web/app/site/app/assets/icons/photo-album-line-Bnfg2u40.js +1 -0
- package/web/app/site/app/assets/icons/pin-line-DpTolyhs.js +1 -0
- package/web/app/site/app/assets/icons/play-fill-B0-G_8lX.js +1 -0
- package/web/app/site/app/assets/icons/qrcode-2-line-B6SPCaHn.js +1 -0
- package/web/app/site/app/assets/icons/quill-pen-line-B6LUrnCP.js +1 -0
- package/web/app/site/app/assets/icons/quote-left-fill-hQZ1cj_6.js +1 -0
- package/web/app/site/app/assets/icons/radar-line-CKyroTth.js +1 -0
- package/web/app/site/app/assets/icons/react-line-CoIKhhtK.js +1 -0
- package/web/app/site/app/assets/icons/refresh-2-line-D0mTF97S.js +1 -0
- package/web/app/site/app/assets/icons/right-line-B3KWJaqc.js +1 -0
- package/web/app/site/app/assets/icons/rocket-line-4KZl537D.js +1 -0
- package/web/app/site/app/assets/icons/round-fill-C69NTrZ0.js +1 -0
- package/web/app/site/app/assets/icons/round-line-B9k_1uo-.js +1 -0
- package/web/app/site/app/assets/icons/route-line-B28ReHcE.js +1 -0
- package/web/app/site/app/assets/icons/rows-4-line-CrBniagM.js +1 -0
- package/web/app/site/app/assets/icons/scan-line-DSf74YdP.js +1 -0
- package/web/app/site/app/assets/icons/search-2-line-B6geIU1z.js +1 -0
- package/web/app/site/app/assets/icons/settings-3-line-azJwK47w.js +1 -0
- package/web/app/site/app/assets/icons/settings-6-line-C_5zc5YR.js +1 -0
- package/web/app/site/app/assets/icons/share-2-line-D5MxWjrm.js +1 -0
- package/web/app/site/app/assets/icons/share-forward-line-Cp61fZzZ.js +1 -0
- package/web/app/site/app/assets/icons/sparkles-2-line-DYYyXTaW.js +1 -0
- package/web/app/site/app/assets/icons/sparkles-line-C2hY1s6I.js +1 -0
- package/web/app/site/app/assets/icons/time-line-BF3eet1v.js +1 -0
- package/web/app/site/app/assets/icons/transfer-4-line-oisfvTBw.js +1 -0
- package/web/app/site/app/assets/icons/translate-line-BFWol2Ce.js +1 -0
- package/web/app/site/app/assets/icons/unlock-line-X_x5vwv3.js +1 -0
- package/web/app/site/app/assets/icons/upload-3-line-DgzwUJeW.js +1 -0
- package/web/app/site/app/assets/icons/user-4-line-BVSynZCp.js +1 -0
- package/web/app/site/app/assets/icons/user-add-2-line-BlGS5Q8q.js +1 -0
- package/web/app/site/app/assets/icons/user-add-line-BbXdHG4z.js +1 -0
- package/web/app/site/app/assets/icons/user-edit-line-BFAHOV0X.js +1 -0
- package/web/app/site/app/assets/icons/user-follow-line-BXux0wSd.js +1 -0
- package/web/app/site/app/assets/icons/user-star-line-DdYlpHLU.js +1 -0
- package/web/app/site/app/assets/icons/user-warning-line-Cj43j0_E.js +1 -0
- package/web/app/site/app/assets/icons/user-x-line-DIDgNlPH.js +1 -0
- package/web/app/site/app/assets/icons/volume-line-Bs390KQt.js +1 -0
- package/web/app/site/app/assets/icons/volume-mute-line-DzlzIqFZ.js +1 -0
- package/web/app/site/app/assets/icons/walk-line-yNXfB-md.js +1 -0
- package/web/app/site/app/assets/icons/world-2-line-CenkGIwj.js +1 -0
- package/web/app/site/app/assets/icons/zoom-in-line-CaENUphf.js +1 -0
- package/web/app/site/app/assets/icons/zoom-out-line-BDNk0EWo.js +1 -0
- package/web/app/site/app/assets/instances-BNPptnn-.json +333 -0
- package/web/app/site/app/assets/locales/ar-SA-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/ca-ES-DHRW86Tz.js +1 -0
- package/web/app/site/app/assets/locales/cs-CZ-CKoqEuA1.js +1 -0
- package/web/app/site/app/assets/locales/de-DE-ByRPMRFy.js +1 -0
- package/web/app/site/app/assets/locales/eo-UY-CPC7wLCS.js +1 -0
- package/web/app/site/app/assets/locales/es-ES-dJa0Fd9t.js +1 -0
- package/web/app/site/app/assets/locales/eu-ES-C3djlPR2.js +1 -0
- package/web/app/site/app/assets/locales/fa-IR-DTT490Ck.js +1 -0
- package/web/app/site/app/assets/locales/fi-FI-B23MCPuz.js +1 -0
- package/web/app/site/app/assets/locales/fr-FR-BcaQsO5C.js +1 -0
- package/web/app/site/app/assets/locales/gl-ES-BR-CVKBN.js +1 -0
- package/web/app/site/app/assets/locales/he-IL-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/hu-HU-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/it-IT-C1136UFV.js +1 -0
- package/web/app/site/app/assets/locales/ja-JP-CRZmRAhD.js +1 -0
- package/web/app/site/app/assets/locales/kab-Cz8xH_TT.js +1 -0
- package/web/app/site/app/assets/locales/ko-KR-CG9UiY4o.js +1 -0
- package/web/app/site/app/assets/locales/lt-LT-CJcI6sHZ.js +1 -0
- package/web/app/site/app/assets/locales/nb-NO-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/nl-NL-DxN4Yc1t.js +1 -0
- package/web/app/site/app/assets/locales/oc-FR-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/pl-PL-DkwGXf6P.js +1 -0
- package/web/app/site/app/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +1 -0
- package/web/app/site/app/assets/locales/pt-BR-nGL0HdFS.js +1 -0
- package/web/app/site/app/assets/locales/pt-PT-Cp17aj2z.js +1 -0
- package/web/app/site/app/assets/locales/ru-RU-0Y8cIoaK.js +1 -0
- package/web/app/site/app/assets/locales/th-TH-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/tok-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/tr-TR-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/locales/uk-UA-C5E3dFBf.js +1 -0
- package/web/app/site/app/assets/locales/zh-CN-BHJi7_et.js +1 -0
- package/web/app/site/app/assets/locales/zh-TW-CfheRbCu.js +1 -0
- package/web/app/site/app/assets/main-BdqNbG-a.js +26 -0
- package/web/app/site/app/assets/main-BdqNbG-a.js.map +1 -0
- package/web/app/site/app/assets/mock-home-DGhR__He.js +2 -0
- package/web/app/site/app/assets/mock-home-DGhR__He.js.map +1 -0
- package/web/app/site/app/assets/multi-column-ROck0NVt.jpg +0 -0
- package/web/app/site/app/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
- package/web/app/site/app/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
- package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js +2 -0
- package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js.map +1 -0
- package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js +20 -0
- package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js.map +1 -0
- package/web/app/site/app/assets/style-B1iLPTPR.css +2 -0
- package/web/app/site/app/assets/temml-DuyIQMei.js +97 -0
- package/web/app/site/app/assets/temml-DuyIQMei.js.map +1 -0
- package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js +2 -0
- package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js.map +1 -0
- package/web/app/site/app/assets/year-in-posts-B2lywqdx.png +0 -0
- package/web/app/site/app/assets/year-in-posts-DJv_star.js +3 -0
- package/web/app/site/app/assets/year-in-posts-DJv_star.js.map +1 -0
- package/web/app/site/app/compose/index.html +20 -0
- package/web/app/site/app/favicon.ico +0 -0
- package/web/app/site/app/index.html +73 -0
- package/web/app/site/app/logo-192.png +0 -0
- package/web/app/site/app/logo-512.png +0 -0
- package/web/app/site/app/logo-badge-72.png +0 -0
- package/web/app/site/app/logo-maskable-512.png +0 -0
- package/web/app/site/app/logo-monochrome-512.png +0 -0
- package/web/app/site/app/logo-monochrome-maskable-512.png +0 -0
- package/web/app/site/app/manifest.webmanifest +1 -0
- package/web/app/site/app/og-image-2.jpg +0 -0
- package/web/app/site/app/og-image.png +0 -0
- package/web/app/site/app/robots.txt +2 -0
- package/web/app/site/app/version.json +1 -0
- package/web/app/site/boot.js +34067 -0
- package/web/app/site/index.html +203 -0
- package/web/app/site/sw.js +70014 -0
- package/web/app/sw-src.mjs +220 -0
- package/web/front/#new-account.html# +43 -0
- package/web/front/admin.html +1 -81
- package/web/front/admin.js +85 -0
- package/web/front/new-account.html +1 -9
- package/web/front/new-account.js +13 -0
- package/web/front/run.html +1 -92
- package/web/front/run.js +107 -0
- package/lib/admin.mjs +0 -1913
- package/lib/intake.mjs +0 -1764
- package/lib/mastoapi.mjs +0 -1947
- package/lib/podrdf.mjs +0 -132
- package/lib/publisher.mjs +0 -1263
- package/web/admin/admin.js +0 -1114
- /package/lib/{oidc-auth.mjs → client/oidc-auth.mjs} +0 -0
- /package/lib/{webpush.mjs → client/webpush.mjs} +0 -0
- /package/lib/{bskyfeed.mjs → connections/bskyfeed.mjs} +0 -0
- /package/lib/{lease.mjs → core/lease.mjs} +0 -0
- /package/lib/{polls.mjs → core/polls.mjs} +0 -0
- /package/lib/{proof.mjs → core/proof.mjs} +0 -0
- /package/lib/{account.mjs → device/account.mjs} +0 -0
- /package/lib/{certs.mjs → device/certs.mjs} +0 -0
- /package/lib/{home.mjs → device/home.mjs} +0 -0
- /package/lib/{ports.mjs → device/ports.mjs} +0 -0
- /package/lib/{guard.mjs → shared/guard.mjs} +0 -0
- /package/lib/{safefetch.mjs → shared/safefetch.mjs} +0 -0
package/web/app/boot.mjs
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// boot.mjs — the page side of the browser agent.
|
|
2
|
+
//
|
|
3
|
+
// Login is a Solid-OIDC redirect: the page sends you to your pod's login, and on
|
|
4
|
+
// return the session is kept in IndexedDB (a non-extractable DPoP key + refresh
|
|
5
|
+
// token, see oidc-session.mjs). The agent runs in the service worker, which reads
|
|
6
|
+
// that same session from IndexedDB. So a returning visit restores silently, and
|
|
7
|
+
// a new browser signs in with one redirect.
|
|
8
|
+
//
|
|
9
|
+
// New account: fedipodSignup(answers) — create the account, then redirect.
|
|
10
|
+
// Returning: fedipodSignin({ issuer }) — redirect to the pod's login.
|
|
11
|
+
// On every load: fedipodOnLoad() — finish a redirect, or restore, then boot.
|
|
12
|
+
import { signUp, handleProblem, AP_ROOT } from './signup.mjs';
|
|
13
|
+
import * as podActor from '../../lib/pod/actor.mjs';
|
|
14
|
+
import * as podState from '../../lib/pod/state.mjs';
|
|
15
|
+
import { BrowserRemotePod } from './pod-remote.mjs';
|
|
16
|
+
import { beginLogin, completeLogin, getSession, signOut } from './oidc-session.mjs';
|
|
17
|
+
import { unwrapKeys, isKeyEnvelope } from './keystore.mjs';
|
|
18
|
+
import { kvPut } from './idb-kv.mjs';
|
|
19
|
+
import { keyCacheKey } from './keys-browser.mjs';
|
|
20
|
+
|
|
21
|
+
const REDIRECT = `${location.origin}/`; // the app root doubles as the OIDC callback
|
|
22
|
+
|
|
23
|
+
async function bootWorker({ reset = false } = {}) {
|
|
24
|
+
const reg = await navigator.serviceWorker.register('/sw.js', { type: 'module' });
|
|
25
|
+
await navigator.serviceWorker.ready;
|
|
26
|
+
if (!navigator.serviceWorker.controller) {
|
|
27
|
+
await new Promise((r) => navigator.serviceWorker.addEventListener('controllerchange', r, { once: true }));
|
|
28
|
+
}
|
|
29
|
+
const worker = reg.active || navigator.serviceWorker.controller;
|
|
30
|
+
// A fresh login means a possibly different identity, and the worker may still
|
|
31
|
+
// be holding the last one. Make it let go before it is asked to boot, and
|
|
32
|
+
// wait for the acknowledgement — posting and racing on would let the boot
|
|
33
|
+
// land while the old agent was still installed.
|
|
34
|
+
if (reset) {
|
|
35
|
+
await new Promise((res) => {
|
|
36
|
+
const on = (e) => {
|
|
37
|
+
if (e.data?.type !== 'reset-done') return;
|
|
38
|
+
navigator.serviceWorker.removeEventListener('message', on);
|
|
39
|
+
res();
|
|
40
|
+
};
|
|
41
|
+
navigator.serviceWorker.addEventListener('message', on);
|
|
42
|
+
worker.postMessage({ type: 'reset' });
|
|
43
|
+
setTimeout(res, 2000); // an older worker will not answer; boot anyway
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const booted = new Promise((res, rej) => {
|
|
47
|
+
const on = (e) => {
|
|
48
|
+
if (e.data?.type === 'booted') { navigator.serviceWorker.removeEventListener('message', on); res(); }
|
|
49
|
+
if (e.data?.type === 'boot-error') { navigator.serviceWorker.removeEventListener('message', on); const err = new Error(e.data.error); err.detail = e.data.stack || ''; err.code = e.data.code || null; rej(err); }
|
|
50
|
+
};
|
|
51
|
+
navigator.serviceWorker.addEventListener('message', on);
|
|
52
|
+
});
|
|
53
|
+
worker.postMessage({ type: 'boot', frontOrigin: location.origin });
|
|
54
|
+
await booted;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// A browser that has signed in but has never opened this account's signing key.
|
|
58
|
+
//
|
|
59
|
+
// The key on the pod is wrapped under the account password (signup.mjs), which
|
|
60
|
+
// is what keeps the pod's host from being able to sign as you. Opening it needs
|
|
61
|
+
// the password once per browser; after that the opened copy lives in this
|
|
62
|
+
// origin's IndexedDB and the worker boots from it with nothing to ask.
|
|
63
|
+
//
|
|
64
|
+
// The unwrap happens HERE, in the page, and not in the worker: the worker boots
|
|
65
|
+
// itself whenever the browser restarts it, with nobody present to type anything.
|
|
66
|
+
window.fedipodUnlock = async (password) => {
|
|
67
|
+
if (!password) throw new Error('Enter your account password.');
|
|
68
|
+
const session = await getSession();
|
|
69
|
+
if (!session) throw new Error('Sign in first.');
|
|
70
|
+
// The config on the pod says where this account's state lives; the key sits
|
|
71
|
+
// beside it. Both are read with the session, as the owner.
|
|
72
|
+
const podFromWebId = new URL(session.webId).origin + '/';
|
|
73
|
+
const state = `${podFromWebId}${AP_ROOT}ap-state/`;
|
|
74
|
+
// Through the transport rather than the bare session: this is a pod read
|
|
75
|
+
// like any other, and going round it skipped the retry ladder that exists
|
|
76
|
+
// because the pod host throttles bursts.
|
|
77
|
+
const remote = new BrowserRemotePod(session, { webId: session.webId, role: 'signup', log: () => {} });
|
|
78
|
+
const urls = { state };
|
|
79
|
+
const [cfg, doc] = await Promise.all([
|
|
80
|
+
podState.readConfig(remote, urls), podState.readWrappedKeys(remote, urls),
|
|
81
|
+
]);
|
|
82
|
+
if (!cfg || !doc) throw new Error(`could not read this account's config and key under ${state}`);
|
|
83
|
+
if (!isKeyEnvelope(doc)) throw new Error('this account\'s key is not locked — nothing to unlock');
|
|
84
|
+
const rec = await unwrapKeys(doc, password); // throws 'wrong password'
|
|
85
|
+
const actorUrl = `${cfg.remotePod}${cfg.root || AP_ROOT}ap/actor`;
|
|
86
|
+
await kvPut(keyCacheKey(actorUrl), rec);
|
|
87
|
+
await bootWorker();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// New account: create the account, pod, key, config and gateway attach (this
|
|
91
|
+
// needs the password once), then redirect to the pod's login to establish the
|
|
92
|
+
// durable session. The agent boots on return, reading config + key from the pod.
|
|
93
|
+
window.fedipodSignup = async ({ onStep, ...answers }) => {
|
|
94
|
+
await signUp(answers, { onStep, frontOrigin: location.origin });
|
|
95
|
+
const { authorizationUrl } = await beginLogin({ issuer: answers.issuer, redirectUri: REDIRECT });
|
|
96
|
+
location.href = authorizationUrl;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Returning / new browser: sign-in takes the full Fediverse address, @you@yourpod.
|
|
100
|
+
// The host part IS the pod (a FediPod address lives at a host root), so the pod is
|
|
101
|
+
// `https://<host>/`. The pod's actor says where to sign in; we fall back to the
|
|
102
|
+
// host's parent domain when the pod does not answer. Everything else — the account
|
|
103
|
+
// config and the key — is read from the pod on return.
|
|
104
|
+
export function parseAddress(input) {
|
|
105
|
+
let s = String(input || '').trim();
|
|
106
|
+
if (s.startsWith('@')) s = s.slice(1); // @you@yourpod → you@yourpod
|
|
107
|
+
const at = s.indexOf('@');
|
|
108
|
+
if (at < 1) return null; // needs a handle and a host
|
|
109
|
+
const handle = s.slice(0, at).toLowerCase();
|
|
110
|
+
const host = s.slice(at + 1).toLowerCase().replace(/\/+$/, '');
|
|
111
|
+
if (!handle || !host || !host.includes('.')) return null;
|
|
112
|
+
return { handle, host };
|
|
113
|
+
}
|
|
114
|
+
async function issuerForPod(pod) {
|
|
115
|
+
// The pod's actor says where a client signs in (oauthAuthorizationEndpoint's
|
|
116
|
+
// origin); failing that, the account provider is the pod host's parent domain.
|
|
117
|
+
try {
|
|
118
|
+
const authz = await podActor.readIssuer(`${pod}${AP_ROOT}ap/actor`);
|
|
119
|
+
if (authz) return new URL(authz).origin;
|
|
120
|
+
} catch { /* fall through */ }
|
|
121
|
+
const host = new URL(pod).host;
|
|
122
|
+
const parent = host.split('.').slice(1).join('.');
|
|
123
|
+
return `https://${parent || host}`;
|
|
124
|
+
}
|
|
125
|
+
window.fedipodSignin = async ({ address }) => {
|
|
126
|
+
const parsed = parseAddress(address);
|
|
127
|
+
if (!parsed) throw new Error('Enter your address as @you@yourpod (for example @alice@alice.solidcommunity.net).');
|
|
128
|
+
const bad = handleProblem(parsed.handle);
|
|
129
|
+
if (bad) throw new Error(bad);
|
|
130
|
+
const pod = `https://${parsed.host}/`;
|
|
131
|
+
const issuer = await issuerForPod(pod);
|
|
132
|
+
const { authorizationUrl } = await beginLogin({ issuer, redirectUri: REDIRECT });
|
|
133
|
+
location.href = authorizationUrl;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Called on every page load. Returns 'signed-in' | 'restored' | 'anonymous'.
|
|
137
|
+
window.fedipodOnLoad = async () => {
|
|
138
|
+
if (new URLSearchParams(location.search).get('code')) {
|
|
139
|
+
await completeLogin({ currentUrl: location.href });
|
|
140
|
+
history.replaceState({}, '', REDIRECT);
|
|
141
|
+
await bootWorker({ reset: true }); // this may be a different account
|
|
142
|
+
return 'signed-in';
|
|
143
|
+
}
|
|
144
|
+
if (await getSession()) { await bootWorker(); return 'restored'; }
|
|
145
|
+
return 'anonymous';
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
window.fedipodSignOut = async () => {
|
|
149
|
+
await signOut();
|
|
150
|
+
// The client (Phanpy) keeps its own account and token in this origin's
|
|
151
|
+
// localStorage, which outlives the OIDC sign-out and would silently sign you
|
|
152
|
+
// back in as the same identity — so signing out has to clear it too.
|
|
153
|
+
try { localStorage.clear(); sessionStorage.clear(); } catch { /* storage blocked */ }
|
|
154
|
+
// The worker holds a connection to the connected-accounts store, so stop it
|
|
155
|
+
// before deleting that store. Browser-stored Bluesky/fediverse keys are wiped
|
|
156
|
+
// here; pod-stored ones stay on the pod and return when you sign in again.
|
|
157
|
+
const reg = await navigator.serviceWorker.getRegistration(); await reg?.unregister();
|
|
158
|
+
await new Promise((res) => {
|
|
159
|
+
let req; try { req = indexedDB.deleteDatabase('fedipod-accounts'); } catch { res(); return; }
|
|
160
|
+
req.onsuccess = req.onerror = req.onblocked = () => res();
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
window.fedipodHandleProblem = handleProblem;
|
|
164
|
+
|
|
165
|
+
// --- page UI wiring (runs only in the page, never the worker) ---
|
|
166
|
+
//
|
|
167
|
+
// Three ways in, matching the front page:
|
|
168
|
+
// signed-in / restored session → straight to the feed (/app/)
|
|
169
|
+
// have an account, new browser → type @you@yourpod, redirect to your pod's login
|
|
170
|
+
// no account → "create an account" reveals the register form
|
|
171
|
+
if (typeof document !== 'undefined') (async () => {
|
|
172
|
+
const $ = (id) => document.getElementById(id);
|
|
173
|
+
const params = new URLSearchParams(location.search);
|
|
174
|
+
|
|
175
|
+
// --- unlock: this browser's first use of an account whose key is locked ---
|
|
176
|
+
//
|
|
177
|
+
// Wired FIRST, before anything that can leave this function early. Showing the
|
|
178
|
+
// pane is one of those early exits, so registering the handler further down
|
|
179
|
+
// meant the pane appeared with a dead button: the one browser that needs it is
|
|
180
|
+
// the only one that never got it.
|
|
181
|
+
const doUnlock = async () => {
|
|
182
|
+
$('unlock-error').textContent = '';
|
|
183
|
+
const btn = $('unlock-go'); btn.disabled = true;
|
|
184
|
+
try {
|
|
185
|
+
await window.fedipodUnlock($('unlock-password').value);
|
|
186
|
+
$('unlock-password').value = '';
|
|
187
|
+
location.href = '/admin/client/';
|
|
188
|
+
} catch (err) {
|
|
189
|
+
// 'wrong password' is what unwrapKeys throws on a failed AES-GCM auth,
|
|
190
|
+
// which is the only way to tell a typo from a real problem.
|
|
191
|
+
$('unlock-error').textContent = err.message || String(err);
|
|
192
|
+
btn.disabled = false;
|
|
193
|
+
$('unlock-password').select();
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
$('unlock-go')?.addEventListener('click', doUnlock);
|
|
197
|
+
$('unlock-password')?.addEventListener('keydown', (e) => { if (e.key === 'Enter') doUnlock(); });
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
// The client shell's bar returns here for two things, and neither continues
|
|
201
|
+
// into the client: `?signout` clears the FediPod session, the client's stored
|
|
202
|
+
// account, and the worker on this browser; `?add` comes back to reach a
|
|
203
|
+
// different pod (signing in anew replaces the session). Both show the sign-in
|
|
204
|
+
// page.
|
|
205
|
+
if (params.has('signout') || params.has('add')) {
|
|
206
|
+
if (params.has('signout')) { try { await window.fedipodSignOut(); } catch { /* nothing to tear down */ } }
|
|
207
|
+
history.replaceState({}, '', '/');
|
|
208
|
+
$('loading').hidden = true;
|
|
209
|
+
$('landing').hidden = false;
|
|
210
|
+
} else {
|
|
211
|
+
let state = 'anonymous';
|
|
212
|
+
try { state = await window.fedipodOnLoad(); }
|
|
213
|
+
catch (e) {
|
|
214
|
+
// A browser that has never opened this account's key is not a failure —
|
|
215
|
+
// it is the ordinary state of a NEW browser, and the answer is a password
|
|
216
|
+
// field rather than a stack trace. Everything else falls through below.
|
|
217
|
+
if (e.code === 'key-password-needed') {
|
|
218
|
+
$('loading').hidden = true; $('hero').hidden = true; $('landing').hidden = true;
|
|
219
|
+
$('brand').hidden = false; $('unlock').hidden = false;
|
|
220
|
+
$('unlock-password').focus();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// We logged in (or had a saved session) but the agent could not start.
|
|
224
|
+
// Show exactly why — including where it threw — instead of silently
|
|
225
|
+
// dropping back to the landing, which hides every real failure.
|
|
226
|
+
console.error(e);
|
|
227
|
+
$('loading').hidden = true; $('hero').hidden = true; $('landing').hidden = true;
|
|
228
|
+
$('brand').hidden = false; $('running').hidden = false;
|
|
229
|
+
$('running-title').textContent = 'Signed in, but the agent could not start';
|
|
230
|
+
$('run-error').style.whiteSpace = 'pre-wrap';
|
|
231
|
+
$('run-error').textContent = (e.message || String(e)) + (e.detail ? `\n\n${e.detail}` : '');
|
|
232
|
+
$('run-actions').hidden = false;
|
|
233
|
+
$('run-retry').textContent = 'Reload'; $('run-retry').addEventListener('click', () => location.reload());
|
|
234
|
+
$('run-back').textContent = 'Sign out'; $('run-back').addEventListener('click', async () => { try { await window.fedipodSignOut(); } catch {} location.href = '/'; });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// Into the client shell: the FediPod bar over the framed client (the same
|
|
238
|
+
// web/admin surface as local). Its own script signs the framed client in on
|
|
239
|
+
// a fresh session and forwards deep links on a returning one, so both a
|
|
240
|
+
// fresh sign-in and a restored session land in the same place.
|
|
241
|
+
if (state === 'signed-in' || state === 'restored') { location.href = '/admin/client/'; return; }
|
|
242
|
+
|
|
243
|
+
// Anonymous: show the landing (sign-in address + create-account); form stays hidden.
|
|
244
|
+
$('loading').hidden = true;
|
|
245
|
+
$('landing').hidden = false;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Views. The register form stands alone: the marketing hero and the sign-in
|
|
249
|
+
// landing give way to the short brand, so only "FediPod" and the form show.
|
|
250
|
+
const showLanding = () => { $('pane-form').hidden = true; $('running').hidden = true; $('brand').hidden = true; $('hero').hidden = false; $('landing').hidden = false; };
|
|
251
|
+
const showForm = () => { $('hero').hidden = true; $('landing').hidden = true; $('brand').hidden = false; $('pane-form').hidden = false; goStep(1); };
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
// --- sign in: the full @you@yourpod address → redirect to your pod's login ---
|
|
255
|
+
const doSignin = async () => {
|
|
256
|
+
$('signin-error').textContent = '';
|
|
257
|
+
try { await window.fedipodSignin({ address: $('signin-address').value }); }
|
|
258
|
+
catch (err) { $('signin-error').textContent = err.message; }
|
|
259
|
+
};
|
|
260
|
+
$('signin').addEventListener('click', doSignin);
|
|
261
|
+
$('signin-address').addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); doSignin(); } });
|
|
262
|
+
|
|
263
|
+
// --- register form: two screens, pod first then Fediverse identity ---
|
|
264
|
+
const f = () => $('form').elements;
|
|
265
|
+
// The pod provider is a free-text URL; default and normalise to a scheme.
|
|
266
|
+
const providerUrl = () => { let v = f().provider.value.trim(); if (!v) v = 'https://solidcommunity.net'; if (!/^https?:\/\//i.test(v)) v = 'https://' + v; return v; };
|
|
267
|
+
const providerHost = () => { try { return new URL(providerUrl()).host; } catch { return ''; } };
|
|
268
|
+
// The pod is always a subdomain of the provider, so both new and existing pods
|
|
269
|
+
// resolve to https://<subdomain>.<provider-host>/ and carry a host-root address.
|
|
270
|
+
const podHostOf = () => { const sub = f().podName.value.trim().toLowerCase(); const ph = providerHost(); return (sub && ph) ? `${sub}.${ph}` : ''; };
|
|
271
|
+
const answers = () => {
|
|
272
|
+
const mode = f().mode.value;
|
|
273
|
+
const a = { mode, handle: f().handle.value.trim().toLowerCase(), email: f().email.value.trim(),
|
|
274
|
+
password: f().password.value, issuer: providerUrl() };
|
|
275
|
+
if (mode === 'new') a.podName = f().podName.value.trim().toLowerCase();
|
|
276
|
+
else a.pod = `https://${podHostOf()}/`;
|
|
277
|
+
return a;
|
|
278
|
+
};
|
|
279
|
+
const previewAddr = () => {
|
|
280
|
+
const handle = f().handle.value.trim().toLowerCase(); const ph = podHostOf();
|
|
281
|
+
$('preview').textContent = (handle && ph) ? `@${handle}@${ph}` : '@…@…';
|
|
282
|
+
};
|
|
283
|
+
for (const el of $('form').elements) el.addEventListener('input', previewAddr);
|
|
284
|
+
|
|
285
|
+
// Step machine: one screen at a time, each gated by its own validation.
|
|
286
|
+
const STEP_IDS = ['step-1', 'step-2'];
|
|
287
|
+
const FOCUS = { 1: 'provider', 2: 'handle' };
|
|
288
|
+
const goStep = (n) => {
|
|
289
|
+
STEP_IDS.forEach((id, i) => { $(id).hidden = i !== n - 1; });
|
|
290
|
+
$('err-1').textContent = ''; $('form-error').textContent = '';
|
|
291
|
+
if (n === 2) previewAddr();
|
|
292
|
+
if (FOCUS[n]) $(FOCUS[n]).focus();
|
|
293
|
+
};
|
|
294
|
+
const validateStep1 = () => {
|
|
295
|
+
if (!providerHost()) return 'A valid pod provider URL is required.';
|
|
296
|
+
const sub = f().podName.value.trim().toLowerCase();
|
|
297
|
+
if (!sub) return 'A pod username/subdomain is required.';
|
|
298
|
+
const sp = window.fedipodHandleProblem(sub);
|
|
299
|
+
if (sp) return `Pod username: ${sp}`;
|
|
300
|
+
if (!f().email.value.trim()) return 'A pod email is required.';
|
|
301
|
+
if (!f().password.value) return 'A pod password is required.';
|
|
302
|
+
return null;
|
|
303
|
+
};
|
|
304
|
+
const validateStep2 = () => {
|
|
305
|
+
const hp = window.fedipodHandleProblem(f().handle.value.trim().toLowerCase());
|
|
306
|
+
if (hp) return `Fediverse handle: ${hp}`;
|
|
307
|
+
return null;
|
|
308
|
+
};
|
|
309
|
+
$('create').addEventListener('click', showForm);
|
|
310
|
+
$('cancel').addEventListener('click', () => { showLanding(); goStep(1); });
|
|
311
|
+
$('to-2').addEventListener('click', () => { const e = validateStep1(); if (e) { $('err-1').textContent = e; return; } goStep(2); });
|
|
312
|
+
$('back-1').addEventListener('click', () => goStep(1));
|
|
313
|
+
|
|
314
|
+
// From the setup screen back to the form (a failed run) or the landing.
|
|
315
|
+
const backToForm = (errMsg) => {
|
|
316
|
+
$('running').hidden = true; $('brand').hidden = false; $('pane-form').hidden = false;
|
|
317
|
+
goStep(1);
|
|
318
|
+
if (errMsg) $('err-1').textContent = errMsg; // remind them what failed
|
|
319
|
+
};
|
|
320
|
+
$('run-retry').addEventListener('click', () => backToForm($('run-error').textContent));
|
|
321
|
+
$('run-back').addEventListener('click', () => { showLanding(); goStep(1); });
|
|
322
|
+
|
|
323
|
+
const LABELS = { account: 'Creating your account and pod', credential: 'Preparing this browser', keys: 'Making your signing key', gateway: 'Connecting your mail door' };
|
|
324
|
+
$('form').addEventListener('submit', async (e) => {
|
|
325
|
+
e.preventDefault(); $('form-error').textContent = '';
|
|
326
|
+
const e1 = validateStep1(); if (e1) { $('form-error').textContent = e1; goStep(1); $('err-1').textContent = e1; return; }
|
|
327
|
+
const e2 = validateStep2(); if (e2) { $('form-error').textContent = e2; goStep(2); return; }
|
|
328
|
+
const a = answers();
|
|
329
|
+
$('pane-form').hidden = true; $('running').hidden = false;
|
|
330
|
+
$('running-title').textContent = 'Setting up…'; $('run-error').textContent = ''; $('run-actions').hidden = true;
|
|
331
|
+
const steps = $('steps'); steps.textContent = ''; const mark = {};
|
|
332
|
+
const onStep = (k, st) => { if (!mark[k]) { const li = document.createElement('li'); steps.appendChild(li); mark[k] = li; } mark[k].textContent = (st === 'ok' ? '✓ ' : st === 'running' ? '… ' : '') + (LABELS[k] || k); };
|
|
333
|
+
try { await window.fedipodSignup({ ...a, onStep }); }
|
|
334
|
+
catch (err) {
|
|
335
|
+
$('running-title').textContent = 'Setup did not finish';
|
|
336
|
+
$('run-error').textContent = err.message || String(err);
|
|
337
|
+
$('run-actions').hidden = false; // the way out of the failure screen
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
})();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// deliver-relay.mjs — delivery for the in-browser agent.
|
|
2
|
+
//
|
|
3
|
+
// The Node deliverer opens a signed connection straight to the remote inbox. A
|
|
4
|
+
// browser cannot: it may not set Date or Host, and it is blocked from posting
|
|
5
|
+
// cross-origin to a fediverse server anyway. So it signs with the agent's key
|
|
6
|
+
// and hands the signed request to the relay (POST /api/relay on the front),
|
|
7
|
+
// which sends it verbatim. Everything else — the retry queue, the cooling
|
|
8
|
+
// hosts, the dead-letter, FEP-8b32 proofs — is the Node Deliverer, reused by
|
|
9
|
+
// overriding only the one network step.
|
|
10
|
+
import { Deliverer } from '../../lib/core/deliver.mjs';
|
|
11
|
+
import { sign } from './shims/fedify-sig.mjs';
|
|
12
|
+
|
|
13
|
+
export class RelayDeliverer extends Deliverer {
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
super(opts);
|
|
16
|
+
this.relayUrl = opts.relayUrl; // <front>/api/relay
|
|
17
|
+
this.handle = opts.handle;
|
|
18
|
+
this.sessionFetch = opts.sessionFetch; // the DPoP session's fetch, to authenticate to the relay
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Same contract as Deliverer.signedFetch, DEFAULT INCLUDED: an init with no
|
|
22
|
+
// method is a read. The Node one builds a `Request`, whose default is GET, and
|
|
23
|
+
// every caller that means POST says so (deliver.mjs deliverNow). Defaulting to
|
|
24
|
+
// POST here sent every dereference — an actor, a note, a reply — to the relay
|
|
25
|
+
// as a delivery, and the relay returns a body only for a GET. So the agent
|
|
26
|
+
// never saw the document it asked for: a Follow from anyone new was rejected
|
|
27
|
+
// with "actor fetch failed", and nothing needing a lookup could be ingested.
|
|
28
|
+
async signedFetch(url, init = {}) {
|
|
29
|
+
const body = typeof init.body === 'string' ? init.body : (init.body ? new TextDecoder().decode(init.body) : '');
|
|
30
|
+
const s = await sign({ url, method: init.method || 'GET', headers: init.headers || {}, body }, this.rsaPrivate, this.keyId);
|
|
31
|
+
const relayReq = {
|
|
32
|
+
url: s.url, method: s.method, body,
|
|
33
|
+
headers: {
|
|
34
|
+
date: s.headers.date, digest: s.headers.digest,
|
|
35
|
+
'content-type': s.headers['content-type'], signature: s.headers.signature,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
let res;
|
|
39
|
+
try {
|
|
40
|
+
res = await this.sessionFetch(this.relayUrl, {
|
|
41
|
+
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
42
|
+
body: JSON.stringify({ handle: this.handle, requests: [relayReq] }),
|
|
43
|
+
});
|
|
44
|
+
} catch (e) { const err = new Error(`relay unreachable: ${e.message}`); err.status = 0; throw err; }
|
|
45
|
+
if (res.status >= 400) { const err = new Error(`relay ${res.status}`); err.status = 502; throw err; }
|
|
46
|
+
const out = await res.json().catch(() => ({}));
|
|
47
|
+
const r0 = (out.results && out.results[0]) || {};
|
|
48
|
+
const status = r0.status || 0;
|
|
49
|
+
if (status === 0) { const err = new Error(r0.error || 'relay could not send'); err.status = 502; throw err; }
|
|
50
|
+
if (status >= 400) {
|
|
51
|
+
const err = new Error(`${init.method || 'POST'} ${url} → ${status}`); err.status = status;
|
|
52
|
+
// The receiving server's own answer to "when should I try again", carried
|
|
53
|
+
// through the relay (lib/front-core.mjs). Spelled `retryAfterMs`, which is
|
|
54
|
+
// what the delivery queue reads (lib/deliver.mjs) — the queue's ladder is
|
|
55
|
+
// the fallback for when there is none, not a replacement for being told.
|
|
56
|
+
if (r0.retryAfter) {
|
|
57
|
+
const secs = Number(r0.retryAfter);
|
|
58
|
+
const ms = Number.isFinite(secs) ? Math.max(secs, 1) * 1000
|
|
59
|
+
: Math.max(Date.parse(r0.retryAfter) - Date.now(), 1000);
|
|
60
|
+
if (Number.isFinite(ms)) err.retryAfterMs = Math.min(ms, 24 * 60 * 60_000);
|
|
61
|
+
}
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
// A read (GET) comes back with the fetched body; a delivery (POST) has none.
|
|
65
|
+
// Hand back a real Response either way so the caller can read a remote
|
|
66
|
+
// object — the topical (tag) feed dereferences every note it mirrors, and
|
|
67
|
+
// intake.fetchAP reads that body off this Response.
|
|
68
|
+
const headers = new Headers();
|
|
69
|
+
if (r0.contentType) headers.set('content-type', r0.contentType);
|
|
70
|
+
return new Response(typeof r0.body === 'string' ? r0.body : null, { status, headers });
|
|
71
|
+
}
|
|
72
|
+
}
|