fedipod-server 0.29.0 → 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 +11 -6
- 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 +36 -101
- package/dist/handler.js +85 -645
- package/dist/handler.jsonld +2 -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/package.json +10 -18
- package/lib/client/c2s.mjs +0 -636
- package/lib/client/localapi.mjs +0 -87
- package/lib/client/masto/accounts.mjs +0 -310
- 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 -387
- package/lib/core/graphview.mjs +0 -327
- package/lib/core/intake/activities.mjs +0 -649
- package/lib/core/intake/activity.mjs +0 -318
- package/lib/core/intake/channel.mjs +0 -144
- package/lib/core/intake/group.mjs +0 -258
- package/lib/core/intake/index.mjs +0 -732
- package/lib/core/intake/notes.mjs +0 -367
- 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/place.mjs +0 -82
- 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 -272
- package/lib/core/publisher/index.mjs +0 -522
- package/lib/core/publisher/notes.mjs +0 -519
- package/lib/core/publisher/own.mjs +0 -143
- package/lib/core/publisher/questions.mjs +0 -241
- 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 -561
- package/lib/core/storage.mjs +0 -176
- package/lib/core/store.mjs +0 -578
- package/lib/core/wire-quotes.mjs +0 -71
- package/lib/core/wire.mjs +0 -925
- 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 -428
- 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 -489
- 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 -496
- 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 -966
- package/lib/gateway/gateway-core.mjs +0 -244
- 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/gateway/relay-extras.mjs +0 -89
- package/lib/gateway/token-claims.mjs +0 -16
- 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/location.mjs +0 -52
- package/lib/pod/media.mjs +0 -27
- package/lib/pod/notes.mjs +0 -78
- 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 -674
- package/lib/pod/type-index.mjs +0 -101
- package/lib/pod/urls.mjs +0 -91
- package/lib/server/embed.mjs +0 -424
- package/lib/session/README.md +0 -137
- package/lib/session/demo.html +0 -94
- package/lib/session/fedi-account.mjs +0 -417
- 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 -899
- 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 -147
- 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 -612
- package/web/admin/notices-bar.js +0 -90
- package/web/admin/oauth-signin.mjs +0 -37
- package/web/admin/record.js +0 -393
- package/web/admin/setup/index.html +0 -257
- package/web/admin/setup/setup.js +0 -339
- 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 -672
- 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 -616
- package/web/app/deliver-relay.mjs +0 -129
- package/web/app/dist/boot.js +0 -34562
- package/web/app/dist/boot.js.map +0 -7
- package/web/app/dist/sw.js +0 -73456
- 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 -258
- 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 -357
- 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 -64
- package/web/front/run.js +0 -221
- 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/web/admin/actors.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
// actors.js — the other actors on this machine: the picker, going to one,
|
|
2
|
-
// starting a stopped one, and the form for a new one.
|
|
3
|
-
|
|
4
|
-
// Somewhere to go, and nothing else. A stopped actor is left out — the link
|
|
5
|
-
// would land on nothing. The one you are on keeps its place in the row but
|
|
6
|
-
// offers only `app`, because its admin page is the page you are reading.
|
|
7
|
-
|
|
8
|
-
let actors = [];
|
|
9
|
-
|
|
10
|
-
async function renderOthers() {
|
|
11
|
-
const { json } = await api('/profiles');
|
|
12
|
-
actors = json?.identities || [];
|
|
13
|
-
const sel = $('actor-pick');
|
|
14
|
-
sel.textContent = '';
|
|
15
|
-
// The fediverse address, which is what the actor IS to everyone else, and the
|
|
16
|
-
// only form that stays distinct: two identities can share a local handle, but
|
|
17
|
-
// never a handle AND a pod. A stopped one has no address to report — nothing
|
|
18
|
-
// answered — so it falls back to the name it is filed under.
|
|
19
|
-
const label = (r) => (r.address || r.handle || r.name)
|
|
20
|
-
+ (r.mode && r.mode !== 'active' ? ` (${r.mode})` : '') + (r.mode ? '' : ' (stopped)');
|
|
21
|
-
const seen = {};
|
|
22
|
-
for (const r of actors) seen[label(r)] = (seen[label(r)] || 0) + 1;
|
|
23
|
-
|
|
24
|
-
for (const [i, r] of actors.entries()) {
|
|
25
|
-
const o = document.createElement('option');
|
|
26
|
-
o.value = String(i);
|
|
27
|
-
o.selected = !!r.current; // it opens showing where you already are
|
|
28
|
-
o.textContent = label(r) + (seen[label(r)] > 1 && r.port ? ` :${r.port}` : '');
|
|
29
|
-
sel.appendChild(o);
|
|
30
|
-
}
|
|
31
|
-
// The way to a NEW actor rides the same dropdown as the existing ones.
|
|
32
|
-
const add = document.createElement('option');
|
|
33
|
-
add.value = '__add';
|
|
34
|
-
add.textContent = '+ add a new account…';
|
|
35
|
-
sel.appendChild(add);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Going to an actor commits a navigation and, for a stopped one, a POST that
|
|
39
|
-
// boots its pod — so it must run once per choice. Pressing Enter on the select
|
|
40
|
-
// fires BOTH a keydown (handled below) and a native `change`, so without a
|
|
41
|
-
// guard a keyboard commit would start the actor twice.
|
|
42
|
-
let goingToActor = false;
|
|
43
|
-
const goToActor = async () => {
|
|
44
|
-
if (goingToActor) return;
|
|
45
|
-
if ($('actor-pick').value === '__add') { renderOthers(); openNewActor(); return; }
|
|
46
|
-
const r = actors[Number($('actor-pick').value)];
|
|
47
|
-
if (!r || r.current) return;
|
|
48
|
-
goingToActor = true;
|
|
49
|
-
try {
|
|
50
|
-
if (r.mode) { location.href = r.admin; return; }
|
|
51
|
-
$('actor-pick').disabled = true;
|
|
52
|
-
say(`starting ${r.name}`);
|
|
53
|
-
const started = await write('/start-actor', { name: r.name }, `${r.name} is up`);
|
|
54
|
-
$('actor-pick').disabled = false;
|
|
55
|
-
if (started?.url) location.href = `${started.url}admin/`;
|
|
56
|
-
else renderOthers(); // put the picker back on the current actor
|
|
57
|
-
} finally {
|
|
58
|
-
goingToActor = false;
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
// Choosing one goes to its record, as it always has.
|
|
62
|
-
//
|
|
63
|
-
// Arrowing is the one case that must not: on a closed select every Arrow
|
|
64
|
-
// keypress fires `change`, so a keyboard user browsing the list would be
|
|
65
|
-
// carried off to the first actor they passed — and for a stopped one that
|
|
66
|
-
// boots its pod. An arrow key arms a flag and the change it causes is ignored;
|
|
67
|
-
// Enter commits. A mouse never sets the flag, so clicking behaves as before.
|
|
68
|
-
let arrowing = false;
|
|
69
|
-
$('actor-pick').addEventListener('keydown', (ev) => {
|
|
70
|
-
if (['ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'].includes(ev.key)) arrowing = true;
|
|
71
|
-
if (ev.key === 'Enter') { arrowing = false; goToActor(); }
|
|
72
|
-
});
|
|
73
|
-
$('actor-pick').addEventListener('pointerdown', () => { arrowing = false; });
|
|
74
|
-
$('actor-pick').addEventListener('change', () => {
|
|
75
|
-
if (!arrowing) goToActor();
|
|
76
|
-
arrowing = false;
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Setting up a new actor asks for exactly what this page cannot already tell
|
|
80
|
-
// it: the pod and the account behind it, and its permanent name. Display name,
|
|
81
|
-
// bio and pictures are the client's job now — Phanpy's profile editor writes
|
|
82
|
-
// them through /api/v1/accounts/update_credentials. Where the private data goes
|
|
83
|
-
// is not a question either: it lands beside the credential, and `state --to`
|
|
84
|
-
// moves it afterwards.
|
|
85
|
-
|
|
86
|
-
const picked = (name) => document.querySelector(`input[name=${name}]:checked`)?.value;
|
|
87
|
-
|
|
88
|
-
function newActorRows() {
|
|
89
|
-
const mode = picked('newMode');
|
|
90
|
-
$('new-row-podname').hidden = mode !== 'new';
|
|
91
|
-
$('new-row-pod').hidden = mode !== 'existing';
|
|
92
|
-
}
|
|
93
|
-
for (const el of document.querySelectorAll('input[name=newMode]')) {
|
|
94
|
-
el.addEventListener('change', newActorRows);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// While the form is up the page IS the new actor's setup — so the record of
|
|
98
|
-
// the actor you came from goes away, list included. Which panes were showing
|
|
99
|
-
// is remembered rather than recomputed: whether Group and Inbox belong is a
|
|
100
|
-
// decision render() already made.
|
|
101
|
-
const RECORD_PANES = ['pane-identity', 'pane-group', 'pane-inbox', 'rail'];
|
|
102
|
-
let putBack = [];
|
|
103
|
-
|
|
104
|
-
function showNewActor(on) {
|
|
105
|
-
if (on) closePanels(); // nothing of the old actor left open behind it
|
|
106
|
-
document.body.classList.toggle('adding', on); // see body.adding in the CSS
|
|
107
|
-
$('new-actor-form').hidden = !on;
|
|
108
|
-
if (on) {
|
|
109
|
-
putBack = RECORD_PANES.filter(id => !$(id).hidden);
|
|
110
|
-
for (const id of putBack) $(id).hidden = true;
|
|
111
|
-
} else {
|
|
112
|
-
for (const id of putBack) $(id).hidden = false;
|
|
113
|
-
putBack = [];
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// `add a new account` is the last item of the Local Actors dropdown; choosing
|
|
118
|
-
// it opens the form in place. Arriving with ?new does the same.
|
|
119
|
-
function openNewActor() {
|
|
120
|
-
showNewActor(true);
|
|
121
|
-
newActorRows();
|
|
122
|
-
$('new-handle').focus();
|
|
123
|
-
}
|
|
124
|
-
$('new-actor-cancel').addEventListener('click', () => { showNewActor(false); say('nothing changed'); });
|
|
125
|
-
|
|
126
|
-
$('new-actor-form').addEventListener('submit', async (ev) => {
|
|
127
|
-
ev.preventDefault();
|
|
128
|
-
const handle = $('new-handle').value.trim();
|
|
129
|
-
const answers = {
|
|
130
|
-
handle,
|
|
131
|
-
kind: picked('newKind'),
|
|
132
|
-
mode: picked('newMode'),
|
|
133
|
-
issuer: $('new-issuer').value.trim(),
|
|
134
|
-
email: $('new-email').value.trim(),
|
|
135
|
-
password: $('new-password').value,
|
|
136
|
-
};
|
|
137
|
-
if (answers.mode === 'new') answers.podName = $('new-podname').value.trim() || handle;
|
|
138
|
-
else answers.pod = $('new-pod').value.trim();
|
|
139
|
-
answers.container = $('new-container').value.trim();
|
|
140
|
-
answers.createIndex = $('new-createIndex').checked;
|
|
141
|
-
$('new-actor-go').disabled = true;
|
|
142
|
-
say(`setting up ${handle || 'the new actor'} — this takes a while`);
|
|
143
|
-
const r = await write('/new-actor', answers, `setting up ${handle}`);
|
|
144
|
-
$('new-actor-go').disabled = false;
|
|
145
|
-
if (r?.url) location.href = r.url; // its own page, where the progress is
|
|
146
|
-
});
|
|
147
|
-
|
package/web/admin/bar.css
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/* The bar every page of ours carries — the record, the setup form and the
|
|
2
|
-
client view. One file so the three cannot drift apart. */
|
|
3
|
-
/* One column, 960px, centred — the bar included, so the title sits over the
|
|
4
|
-
page's left edge and the buttons over its right rather than out at the window
|
|
5
|
-
borders. `width: 100%` with `box-sizing: border-box` keeps the padding inside
|
|
6
|
-
that 960 instead of adding to it, which is what makes the two margins equal. */
|
|
7
|
-
/* Colours come from tokens.css (the front's palette), linked by every page
|
|
8
|
-
before this file, so the bar matches the front and the record rather than
|
|
9
|
-
Phanpy: --surface is the page ground, --raised the bar band on top of it. */
|
|
10
|
-
body { background: var(--surface); }
|
|
11
|
-
/* The BAND spans the window; its CONTENTS stay in the 960 column. Constraining
|
|
12
|
-
the element itself left the colour as a stripe with bare page either side.
|
|
13
|
-
The inline padding is the column's own margin plus the 1rem the page uses
|
|
14
|
-
inside it, so the title lands exactly on the page's left edge — and `max`
|
|
15
|
-
keeps it at 1rem once the window is narrower than the column.
|
|
16
|
-
No rule underneath: the tone change is the boundary, and a line as well
|
|
17
|
-
would be drawing it twice. */
|
|
18
|
-
/* Wraps, because it does not fit: at 320px the title plus three destinations
|
|
19
|
-
ran 67px past the viewport, and the bar is on every page — so the whole
|
|
20
|
-
document scrolled sideways whatever was under it. */
|
|
21
|
-
#bar { flex: 0 0 auto; display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
|
|
22
|
-
width: 100%; max-width: none; margin: 0; box-sizing: border-box;
|
|
23
|
-
/* The column width is the page's to declare (--bar-col); 960 stands where a
|
|
24
|
-
page says nothing. */
|
|
25
|
-
padding: .5rem max(1rem, calc((100% - var(--bar-col, 960px)) / 2 + 1rem));
|
|
26
|
-
background: var(--raised); }
|
|
27
|
-
/* The actor's fediverse handle, in the row between the title and the
|
|
28
|
-
destinations. */
|
|
29
|
-
#bar-handle { color: var(--heading); font-size: .95rem; margin-right: .6rem; }
|
|
30
|
-
#bar-handle:empty { display: none; }
|
|
31
|
-
/* The actors dropdown, where a page's bar carries one instead of the static
|
|
32
|
-
handle. Styled here so every page's bar shows the same control: the same
|
|
33
|
-
light well and drawn caret every setting on the record page uses. */
|
|
34
|
-
#bar select { font: inherit; font-size: .95rem; max-width: min(24rem, 55vw);
|
|
35
|
-
margin-right: .6rem; appearance: none; min-width: 0;
|
|
36
|
-
padding: .15rem 1.7rem .15rem .55rem;
|
|
37
|
-
border: 1px solid var(--field-edge); border-radius: .3rem;
|
|
38
|
-
color: var(--ink); cursor: pointer;
|
|
39
|
-
background-color: var(--field-bg); background-image: var(--chevron);
|
|
40
|
-
background-repeat: no-repeat; background-position: right .55rem center;
|
|
41
|
-
background-size: .6rem auto; }
|
|
42
|
-
#bar select:hover { background-color: var(--field-bg-hover); }
|
|
43
|
-
/* The app title, in px rather than rem: it is chrome, the same on every page,
|
|
44
|
-
not something that should move when a page changes its base size. */
|
|
45
|
-
#bar .name { flex: 1 1 auto; font-weight: 600; font-size: 40px; color: var(--heading);
|
|
46
|
-
margin: 0; line-height: 1.1; }
|
|
47
|
-
/* The browser build wraps the brand text in a link (to /?add) — the general
|
|
48
|
-
#bar a rule below would otherwise pin it back down to nav-link size and
|
|
49
|
-
colour, since a rule on the anchor itself always wins over inheriting the
|
|
50
|
-
heading's size. */
|
|
51
|
-
#bar .name a { font: inherit; color: inherit; text-decoration: none; }
|
|
52
|
-
/* Plain text, not chrome: three destinations in a row read better without a
|
|
53
|
-
box round each. */
|
|
54
|
-
#bar button, #bar a { font: inherit; font-size: .95rem; padding: .15rem .2rem;
|
|
55
|
-
border: 0; background: none; color: var(--link); cursor: pointer;
|
|
56
|
-
text-decoration: none; }
|
|
57
|
-
/* The page you are already on stays in the row, so the set never moves — it is
|
|
58
|
-
underlined rather than removed. */
|
|
59
|
-
#bar button[aria-current="page"], #bar a[aria-current="page"] {
|
|
60
|
-
text-decoration: underline; text-underline-offset: .3rem; }
|
|
61
|
-
/* The client control: which fediverse client this browser opens. The label is
|
|
62
|
-
quieter than the links; the one in use is marked `aria-current="true"` — the
|
|
63
|
-
current item of a set, which on the record page is not the current PAGE — so
|
|
64
|
-
it needs its own mark rather than borrowing the rule above. */
|
|
65
|
-
#bar #client-pick, #bar #account-pick { display: inline-flex; gap: .6rem; align-items: baseline;
|
|
66
|
-
white-space: nowrap; margin-left: 1rem; }
|
|
67
|
-
#bar #client-now, #bar #account-now { opacity: .75; }
|
|
68
|
-
/* The notices bell: the one filled control in the bar, because it is the one
|
|
69
|
-
thing there that is not a place to go but something to be told. The count is
|
|
70
|
-
how many notices this browser has not opened yet; with none it is quiet. */
|
|
71
|
-
#bar #bar-notices { display: inline-flex; align-items: center; gap: .35rem; margin-left: 1rem;
|
|
72
|
-
padding: .3rem .7rem; border-radius: 999px; font-size: 1.05rem; line-height: 1;
|
|
73
|
-
background: var(--btn); color: var(--btn-text); border: 1px solid var(--btn); }
|
|
74
|
-
/* `display` on an id rule outranks the browser's own [hidden] rule, and the
|
|
75
|
-
shell has no !important of its own, so the bell hid on the record page and
|
|
76
|
-
showed on the client with nothing to show. Said here, once, for every page. */
|
|
77
|
-
#bar #bar-notices[hidden] { display: none; }
|
|
78
|
-
#bar #bar-notices:hover { background: var(--btn-hover); border-color: var(--btn-hover); }
|
|
79
|
-
#bar #bar-notices:focus-visible { outline: 3px solid var(--ring); outline-offset: 2px; }
|
|
80
|
-
#bar #bar-notices-count { font-size: .9rem; font-weight: 700; min-width: 1.3em; text-align: center;
|
|
81
|
-
padding: .05rem .4rem; border-radius: 999px; background: var(--btn-text); color: var(--btn); }
|
|
82
|
-
#bar #bar-notices-count:empty { display: none; }
|
|
83
|
-
#bar #bar-notices[data-new="0"] { background: var(--field-bg); color: var(--ink); border-color: var(--field-edge); }
|
|
84
|
-
#bar a[aria-current="true"] {
|
|
85
|
-
font-weight: 700; text-decoration: underline; text-underline-offset: .3rem; }
|
|
86
|
-
|
|
87
|
-
/* The two-clients notice: said once, to somebody who knew this page when it
|
|
88
|
-
had one client. Sized and placed rather than left to the browser's default,
|
|
89
|
-
which stretched it edge to edge and put it over the very links it names. */
|
|
90
|
-
#client-news, #notices-list, #notice-view {
|
|
91
|
-
/* Low rather than centred: it points at the links in the bar, so the bar has
|
|
92
|
-
to stay in view while it is read. */
|
|
93
|
-
max-width: 34rem; margin: auto auto 2.5rem; padding: 1.5rem 1.7rem;
|
|
94
|
-
border: 1px solid var(--dialog-edge); border-radius: .6rem;
|
|
95
|
-
background: var(--raised); color: var(--fg);
|
|
96
|
-
font: 1rem/1.55 var(--font);
|
|
97
|
-
box-shadow: 0 .6rem 2rem rgba(0, 0, 0, .25);
|
|
98
|
-
}
|
|
99
|
-
#client-news::backdrop, #notices-list::backdrop, #notice-view::backdrop { background: rgba(0, 0, 0, .45); }
|
|
100
|
-
#client-news h2, #notices-list h2, #notice-view h2 { font-size: 1.25rem; margin: 0 0 .9rem; color: var(--heading); }
|
|
101
|
-
#client-news p, #notices-list p, #notice-view p { margin: 0 0 1.2rem; }
|
|
102
|
-
#client-news p:last-child, #notices-list p:last-child, #notice-view p:last-child { margin: 0; }
|
|
103
|
-
#client-news button, #notices-list button, #notice-view button {
|
|
104
|
-
font: inherit; padding: .55rem 1.3rem; border-radius: .35rem; cursor: pointer;
|
|
105
|
-
background: var(--btn); color: var(--btn-text); border: 1px solid var(--btn);
|
|
106
|
-
}
|
|
107
|
-
#client-news button:hover, #notices-list button:hover, #notice-view button:hover { background: var(--btn-hover); border-color: var(--btn-hover); }
|
|
108
|
-
#client-news button:focus-visible, #notices-list button:focus-visible, #notice-view button:focus-visible { outline: 3px solid var(--ring); outline-offset: 2px; }
|
|
109
|
-
|
|
110
|
-
/* The notices dialogs are centred and never taller than the window: the
|
|
111
|
-
client-news rule above sits low, and a short window pushed a list of
|
|
112
|
-
notices up past the top edge where nothing could reach it. */
|
|
113
|
-
#notices-list, #notice-view { margin: auto; max-height: calc(100dvh - 3rem); overflow: auto; box-sizing: border-box; }
|
|
114
|
-
/* The list of notices: each title a button that opens that notice, the date
|
|
115
|
-
beside it, the ones not yet opened here in bold. */
|
|
116
|
-
#notices-items { list-style: none; margin: 0 0 1.2rem; padding: 0; }
|
|
117
|
-
#notices-items li { display: flex; gap: .8rem; align-items: baseline; padding: .45rem 0;
|
|
118
|
-
border-bottom: 1px solid var(--line); }
|
|
119
|
-
#notices-items li:last-child { border-bottom: 0; }
|
|
120
|
-
#notices-items button.notice-open { flex: 1 1 auto; text-align: left; padding: .1rem 0;
|
|
121
|
-
background: none; border: 0; color: var(--link); text-decoration: underline; text-underline-offset: .2rem; }
|
|
122
|
-
#notices-items li.new button.notice-open { font-weight: 700; }
|
|
123
|
-
#notices-items .when, #notice-view .when { color: var(--sub); font-size: .9rem; white-space: nowrap; }
|
|
124
|
-
#notices-items p.none { color: var(--sub); }
|
|
125
|
-
#notice-view-body p { margin: 0 0 .9rem; }
|
|
126
|
-
#notice-view-body a { color: var(--link); word-break: break-all; }
|
package/web/admin/bar.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
// bar.js — the three destinations, shared by the record, the setup form and the
|
|
2
|
-
// client view, so they cannot drift apart. Nothing here touches the client in
|
|
3
|
-
// the frame: it is this agent's own root, and framing it is the whole of the
|
|
4
|
-
// relationship.
|
|
5
|
-
|
|
6
|
-
(() => {
|
|
7
|
-
// The three destinations are <a href> now: they are navigations, so they
|
|
8
|
-
// belong in a screen reader's links list, and middle-click and open-in-new-tab
|
|
9
|
-
// work the way they do everywhere else. Nothing here has to drive them.
|
|
10
|
-
//
|
|
11
|
-
// One exception, and it keeps its href as the fallback: on the record page
|
|
12
|
-
// `add new account` opens the form IN PLACE rather than reloading. admin.js
|
|
13
|
-
// binds that and calls preventDefault; without the form it is a plain link to
|
|
14
|
-
// ?new=1, which opens it on arrival.
|
|
15
|
-
|
|
16
|
-
// The bar says the product; the TAB says which actor, which is what tells two
|
|
17
|
-
// open windows apart without putting the handle in the page twice.
|
|
18
|
-
// /status, not /config: an agent that is not set up yet answers this one and
|
|
19
|
-
// 409s the other, which logged a failed request on every setup page.
|
|
20
|
-
fetch(location.pathname.replace(/\/admin\/.*$/u, '') + '/status', { headers: { 'x-fedipod-page': '1' } }).then(r => (r.ok ? r.json() : null)).then((s) => {
|
|
21
|
-
if (s?.handle) document.title = `FediPod — ${s.handle}`;
|
|
22
|
-
// The full fediverse handle, centred in the bar on every page.
|
|
23
|
-
const el = document.getElementById('bar-handle');
|
|
24
|
-
if (el && s?.handle && s?.actor) {
|
|
25
|
-
try { el.textContent = `@${s.handle}@${new URL(s.actor).host}`; } catch { /* odd actor url */ }
|
|
26
|
-
}
|
|
27
|
-
// A viewer does nothing: it publishes no profile, carries nothing and
|
|
28
|
-
// leaves the account looking broken from outside. Say so where the
|
|
29
|
-
// account is, and let the owner take it back from here.
|
|
30
|
-
if (s?.mode === 'viewer') viewerBanner();
|
|
31
|
-
}).catch(() => { /* not up yet; the bar still works */ });
|
|
32
|
-
|
|
33
|
-
// The actors dropdown, on a page whose bar carries one. The record page
|
|
34
|
-
// wires its own richer version (it can start a stopped actor and open the
|
|
35
|
-
// new-account form in place), so this one stands down there.
|
|
36
|
-
const pick = document.getElementById('actor-pick');
|
|
37
|
-
if (pick && !document.getElementById('new-actor-form')) {
|
|
38
|
-
const base = location.pathname.replace(/\/admin\/.*$/u, '');
|
|
39
|
-
fetch(base + '/profiles', { headers: { 'x-fedipod-page': '1' } }).then(r => (r.ok ? r.json() : null)).then((j) => {
|
|
40
|
-
const actors = j?.identities || [];
|
|
41
|
-
pick.textContent = '';
|
|
42
|
-
const label = (r) => (r.address || r.handle || r.name)
|
|
43
|
-
+ (r.mode && r.mode !== 'active' ? ` (${r.mode})` : '') + (r.mode ? '' : ' (stopped)');
|
|
44
|
-
for (const [i, r] of actors.entries()) {
|
|
45
|
-
const o = document.createElement('option');
|
|
46
|
-
o.value = String(i);
|
|
47
|
-
o.selected = !!r.current;
|
|
48
|
-
o.textContent = label(r);
|
|
49
|
-
pick.appendChild(o);
|
|
50
|
-
}
|
|
51
|
-
const add = document.createElement('option');
|
|
52
|
-
add.value = '__add';
|
|
53
|
-
add.textContent = '+ add a new account…';
|
|
54
|
-
pick.appendChild(add);
|
|
55
|
-
pick.addEventListener('change', () => {
|
|
56
|
-
if (pick.value === '__add') { location.href = base + '/admin/?new=1'; return; }
|
|
57
|
-
const r2 = actors[Number(pick.value)];
|
|
58
|
-
if (!r2 || r2.current) return;
|
|
59
|
-
// A running actor's client; a stopped one is started from its record.
|
|
60
|
-
if (r2.mode && r2.admin) location.href = r2.admin + 'client/';
|
|
61
|
-
else if (r2.admin) location.href = r2.admin;
|
|
62
|
-
});
|
|
63
|
-
}).catch(() => { pick.hidden = true; });
|
|
64
|
-
}
|
|
65
|
-
})();
|
|
66
|
-
|
|
67
|
-
// "Another device is active": what it means for this account, and the way out.
|
|
68
|
-
// The lease is a document on the pod with a five-minute life; a browser that
|
|
69
|
-
// was closed leaves one behind that nothing renews, and every reload asks
|
|
70
|
-
// under a new name, so waiting can look like waiting forever.
|
|
71
|
-
function viewerBanner() {
|
|
72
|
-
if (document.getElementById('viewer-banner')) return;
|
|
73
|
-
const base = location.pathname.replace(/\/admin\/.*$/u, '');
|
|
74
|
-
const bar = document.createElement('div');
|
|
75
|
-
bar.id = 'viewer-banner';
|
|
76
|
-
bar.setAttribute('role', 'status');
|
|
77
|
-
bar.style.cssText = 'padding:.7rem 1rem;background:var(--field-bg,#ececec);color:var(--fg,#1a1a1a);'
|
|
78
|
-
+ 'border-bottom:1px solid var(--line,#b0b0b0);display:flex;gap:1rem;align-items:center;flex-wrap:wrap;font:inherit';
|
|
79
|
-
const said = document.createElement('span');
|
|
80
|
-
said.textContent = 'Another device holds this account, so nothing is being published or delivered from here.';
|
|
81
|
-
const go = document.createElement('button');
|
|
82
|
-
go.type = 'button';
|
|
83
|
-
go.textContent = 'Take it over';
|
|
84
|
-
go.style.cssText = 'font:inherit;font-weight:600;padding:.4rem 1rem;border-radius:.4rem;border:1px solid var(--btn,#3a5f43);'
|
|
85
|
-
+ 'background:var(--btn,#3a5f43);color:var(--btn-text,#fff);cursor:pointer';
|
|
86
|
-
go.addEventListener('click', async () => {
|
|
87
|
-
go.disabled = true;
|
|
88
|
-
said.textContent = 'Taking it over…';
|
|
89
|
-
try {
|
|
90
|
-
const r = await fetch(base + '/takeover', { method: 'POST', headers: { 'x-fedipod-page': '1' } });
|
|
91
|
-
const j = await r.json().catch(() => null);
|
|
92
|
-
if (r.ok && j?.mode === 'active') { location.reload(); return; }
|
|
93
|
-
said.textContent = j?.error || 'The lease could not be taken.';
|
|
94
|
-
} catch (e) { said.textContent = e.message; }
|
|
95
|
-
go.disabled = false;
|
|
96
|
-
});
|
|
97
|
-
bar.append(said, go);
|
|
98
|
-
document.body.prepend(bar);
|
|
99
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// client.js — make the framed client be THIS agent's actor.
|
|
2
|
-
//
|
|
3
|
-
// The client is a general fediverse client: it keeps a list of accounts in
|
|
4
|
-
// localStorage and one of them is current. Nothing makes that one agree with
|
|
5
|
-
// the agent serving the page. A leftover account from another identity wins,
|
|
6
|
-
// and you get somebody else's timeline on this actor's page, with the profile
|
|
7
|
-
// editor pointed at a host that cannot answer.
|
|
8
|
-
//
|
|
9
|
-
// Per-origin storage is what keeps identities apart, and it does work — each
|
|
10
|
-
// actor answers at <handle>.localhost:<port> and each origin has its own
|
|
11
|
-
// accounts list. What was missing is the other half: telling the client which
|
|
12
|
-
// account it is meant to be showing.
|
|
13
|
-
//
|
|
14
|
-
// The frame's markup declares what it loads (`src="/"`), and that does not
|
|
15
|
-
// change. This navigates the loaded app to its own login route, which is app
|
|
16
|
-
// navigation, not an include: `?instance=` prefills and `?submit=` runs it, so
|
|
17
|
-
// the round trip needs no typing. Hash-routed, so the query lives after the
|
|
18
|
-
// hash; `submit` must be non-empty, because the empty string is falsy and the
|
|
19
|
-
// auto-submit never fires.
|
|
20
|
-
|
|
21
|
-
(async () => {
|
|
22
|
-
const frame = document.getElementById('client');
|
|
23
|
-
if (!frame) return;
|
|
24
|
-
|
|
25
|
-
// Who this agent is, in the form the client stores: `info.uri` is the actor
|
|
26
|
-
// URI on the pod. Matching on that rather than on the origin is deliberate —
|
|
27
|
-
// "which account logged in here" is a different question, and it is the one
|
|
28
|
-
// that goes wrong when the two origins get mixed.
|
|
29
|
-
const base = location.pathname.replace(/\/admin\/.*$/u, '');
|
|
30
|
-
const status = await fetch(base + '/status', { headers: { 'x-fedipod-page': '1' } }).then(r => (r.ok ? r.json() : null)).catch(() => null);
|
|
31
|
-
if (!status?.actor) return; // not set up yet; setup owns that
|
|
32
|
-
|
|
33
|
-
// A deep link into the client — the record links an actor's own page this way
|
|
34
|
-
// — arrives as a hash on THIS page, and the frame never sees it. Forward it,
|
|
35
|
-
// so the link lands where it says while keeping the bar above it.
|
|
36
|
-
const wanted = location.hash && location.hash !== '#' ? location.hash : null;
|
|
37
|
-
const sendTo = (hash) => {
|
|
38
|
-
try { frame.contentWindow.location.hash = hash; } catch { /* not ready yet */ }
|
|
39
|
-
};
|
|
40
|
-
const whenReady = (fn) => {
|
|
41
|
-
if (frame.contentWindow?.location?.href && frame.contentWindow.location.href !== 'about:blank') fn();
|
|
42
|
-
else frame.addEventListener('load', fn, { once: true });
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
let accounts = [];
|
|
46
|
-
try { accounts = JSON.parse(localStorage.getItem('accounts') || '[]'); } catch { /* unreadable */ }
|
|
47
|
-
const ours = accounts.find(a => a?.info?.uri === status.actor);
|
|
48
|
-
if (ours) {
|
|
49
|
-
// A stored login can be dead — its token revoked or minted by a run that
|
|
50
|
-
// never finished. Trusting it shows an empty client with no way out, so
|
|
51
|
-
// ask the facade first; a dead one falls through to the same one-trip
|
|
52
|
-
// login a missing account gets.
|
|
53
|
-
const alive = ours.accessToken && await fetch('/api/v1/accounts/verify_credentials', {
|
|
54
|
-
headers: { authorization: `Bearer ${ours.accessToken}` },
|
|
55
|
-
}).then(r => r.ok).catch(() => false);
|
|
56
|
-
if (alive) {
|
|
57
|
-
if (wanted) whenReady(() => sendTo(wanted));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Same origin, so the frame's own host is the instance to log into. A deep
|
|
63
|
-
// link is dropped here on purpose: logging in navigates the frame anyway, and
|
|
64
|
-
// arriving somewhere unexpected afterwards is worse than arriving home.
|
|
65
|
-
whenReady(() => sendTo(`#/login?instance=${encodeURIComponent(location.host)}&submit=1`));
|
|
66
|
-
})();
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>FediPod — client</title>
|
|
7
|
-
<link rel="icon" href="/icons/local.svg" type="image/svg+xml">
|
|
8
|
-
<link rel="stylesheet" href="../tokens.css">
|
|
9
|
-
<style>
|
|
10
|
-
/* Colours are the shared tokens (tokens.css); only layout is here. The bar is
|
|
11
|
-
a fixed height, the client takes the rest — the page never scrolls, the frame
|
|
12
|
-
does. */
|
|
13
|
-
:root { color-scheme: light dark; font-size: 125%; }
|
|
14
|
-
html { height: 100%; }
|
|
15
|
-
body { font: 1rem/1.5 Arial, Helvetica, sans-serif; margin: 0; height: 100dvh;
|
|
16
|
-
display: flex; flex-direction: column; overflow: hidden; }
|
|
17
|
-
main { flex: 1 1 auto; min-height: 0; display: flex; }
|
|
18
|
-
#client { flex: 1 1 auto; min-height: 0; width: 100%; border: 0; }
|
|
19
|
-
</style>
|
|
20
|
-
<link rel="stylesheet" href="../bar.css">
|
|
21
|
-
</head>
|
|
22
|
-
<body>
|
|
23
|
-
|
|
24
|
-
<!-- The same bar the record carries, so the two surfaces are one thing with two
|
|
25
|
-
views. The client below is this agent's own, at its own root: same origin,
|
|
26
|
-
which is why framing it needs nothing opened beyond frame-ancestors 'self'. -->
|
|
27
|
-
<nav id="bar" aria-label="Site">
|
|
28
|
-
<h1 class="name">FediPod</h1>
|
|
29
|
-
<select id="actor-pick" aria-label="Local actors"
|
|
30
|
-
title=" Every actor on this machine; choosing one opens its client"></select>
|
|
31
|
-
<!-- The account group, styled like the client group beside it: one label,
|
|
32
|
-
then the places to go. The browser build adds "sign out" to it. -->
|
|
33
|
-
<span id="account-pick"><span id="account-now">account:</span>
|
|
34
|
-
<a id="bar-fediverse" href="./" title=" Open the client and read your timeline" aria-current="page">visit</a>
|
|
35
|
-
<a id="bar-manage" href="../" title=" The record: what this actor is, and what you can do to it">manage</a>
|
|
36
|
-
</span>
|
|
37
|
-
<!-- Notices from whoever runs this site. Shown only when the site has any
|
|
38
|
-
(notices-bar.js asks /api/notices); the count is how many are new here. -->
|
|
39
|
-
<button type="button" id="bar-notices" hidden aria-haspopup="dialog" aria-controls="notices-list"
|
|
40
|
-
title=" Notices from the operator of this site">🔔<span id="bar-notices-count" aria-live="polite"></span></button>
|
|
41
|
-
</nav>
|
|
42
|
-
|
|
43
|
-
<main>
|
|
44
|
-
<iframe id="client" src="../../" title="fediverse client"></iframe>
|
|
45
|
-
</main>
|
|
46
|
-
|
|
47
|
-
<script src="../bar.js"></script>
|
|
48
|
-
<script src="client.js"></script>
|
|
49
|
-
|
|
50
|
-
<!-- The notices: the list, and one notice open. Declared here and filled by
|
|
51
|
-
notices-bar.js from /api/notices; nothing in them is markup from a notice. -->
|
|
52
|
-
<dialog id="notices-list" aria-labelledby="notices-title">
|
|
53
|
-
<section>
|
|
54
|
-
<h2 id="notices-title">Notices</h2>
|
|
55
|
-
<ul id="notices-items"></ul>
|
|
56
|
-
<p class="row"><button type="button" id="notices-close" class="primary" title=" Close the list of notices">Close</button></p>
|
|
57
|
-
</section>
|
|
58
|
-
</dialog>
|
|
59
|
-
<dialog id="notice-view" aria-labelledby="notice-view-title">
|
|
60
|
-
<section>
|
|
61
|
-
<h2 id="notice-view-title"></h2>
|
|
62
|
-
<p class="when" id="notice-view-when"></p>
|
|
63
|
-
<div id="notice-view-body"></div>
|
|
64
|
-
<p class="row"><button type="button" id="notice-view-close" class="primary" title=" Back to the list of notices">Close</button></p>
|
|
65
|
-
</section>
|
|
66
|
-
</dialog>
|
|
67
|
-
<script src="../notices-bar.js"></script>
|
|
68
|
-
</body>
|
|
69
|
-
</html>
|
package/web/admin/client-pick.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// client-pick.js — which fediverse client this browser opens.
|
|
2
|
-
//
|
|
3
|
-
// The site carries more than one client. Each one has its own shell page whose
|
|
4
|
-
// markup declares the app it frames, and every switch is an ordinary link from
|
|
5
|
-
// one shell to another. This file adds one thing only: it REMEMBERS which shell
|
|
6
|
-
// you last chose, so /admin/client/ — where signing in lands, and where the
|
|
7
|
-
// record's "visit account" goes — hands you back the same client next time.
|
|
8
|
-
//
|
|
9
|
-
// The choice is this browser's, not the account's. It decides which page you
|
|
10
|
-
// are given and nothing else: both clients talk to the same agent, with the
|
|
11
|
-
// same bearer, on this same origin. Nothing about the identity moves with it,
|
|
12
|
-
// which is why it lives in localStorage and is never written to the pod.
|
|
13
|
-
//
|
|
14
|
-
// Staged into the site by scripts/stage-site.mjs, which also generates the
|
|
15
|
-
// shells and the links in the bar. The device agent carries one client and
|
|
16
|
-
// never loads this.
|
|
17
|
-
|
|
18
|
-
const KEY = 'fedipod-client';
|
|
19
|
-
// The default client's shell. It is also the page that redirects, so a browser
|
|
20
|
-
// that has chosen something else does not have to pass through the default.
|
|
21
|
-
const DEFAULT_SHELL = '/admin/client/';
|
|
22
|
-
// A stored value is a path this site generated, and nothing else is followed.
|
|
23
|
-
// Without this, anything that can write localStorage — including the framed
|
|
24
|
-
// client itself — could send the next visit off this origin.
|
|
25
|
-
const SHELL = /^\/admin\/client(-[a-z0-9-]+)?\/$/;
|
|
26
|
-
|
|
27
|
-
const read = () => {
|
|
28
|
-
// A private window, or a browser told to keep no site data, throws on the
|
|
29
|
-
// read rather than answering empty. There is no choice to honour then, which
|
|
30
|
-
// is the same as not having made one.
|
|
31
|
-
try { return localStorage.getItem(KEY) || ''; } catch { return ''; }
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// Before anything renders: if this browser last chose another client, that is
|
|
35
|
-
// the page it asked for. `replace` and not `href`, so Back does not bounce
|
|
36
|
-
// between the two shells.
|
|
37
|
-
if (location.pathname === DEFAULT_SHELL) {
|
|
38
|
-
const chosen = read();
|
|
39
|
-
if (chosen && chosen !== DEFAULT_SHELL && SHELL.test(chosen)) location.replace(chosen);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Somebody who knew this page when it had one client should be told, once,
|
|
43
|
-
// that it now has two and where to change it. Marked as said as soon as it is
|
|
44
|
-
// shown rather than when it is dismissed: a reader who closes the tab instead
|
|
45
|
-
// of pressing the button has still read it, and being told twice is worse than
|
|
46
|
-
// not being told a second time.
|
|
47
|
-
const SAID = 'fedipod-client-news';
|
|
48
|
-
function sayOnce() {
|
|
49
|
-
const dlg = document.getElementById('client-news');
|
|
50
|
-
if (!dlg) return;
|
|
51
|
-
let told = true;
|
|
52
|
-
try { told = !!localStorage.getItem(SAID); } catch { /* keeps no site data */ }
|
|
53
|
-
if (told) return;
|
|
54
|
-
try { localStorage.setItem(SAID, '1'); } catch { /* keeps no site data */ }
|
|
55
|
-
dlg.showModal();
|
|
56
|
-
document.getElementById('client-news-ok')?.addEventListener('click', () => dlg.close());
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
60
|
-
sayOnce();
|
|
61
|
-
// Which one is current. On a shell it is the page you are on — you may have
|
|
62
|
-
// typed its address, and what is in front of you is the true answer whatever
|
|
63
|
-
// storage says. Anywhere else (the record page) it is the remembered choice.
|
|
64
|
-
const here = SHELL.test(location.pathname) ? location.pathname : (read() || DEFAULT_SHELL);
|
|
65
|
-
|
|
66
|
-
for (const a of document.querySelectorAll('a.client-switch')) {
|
|
67
|
-
// `aria-current` and not a class alone: this is one of a set of links, one
|
|
68
|
-
// of which is the one in use — which is the state the attribute exists to
|
|
69
|
-
// carry, and the only way a screen reader is told about it at all.
|
|
70
|
-
if (a.getAttribute('href') === here) a.setAttribute('aria-current', 'true');
|
|
71
|
-
// Each link records the choice it stands for and then follows its own href,
|
|
72
|
-
// the destination written in the markup. Recorded on the way OUT rather
|
|
73
|
-
// than on arrival, so a shell opened deliberately by address — or the one
|
|
74
|
-
// the redirect above lands on — does not silently become the default.
|
|
75
|
-
a.addEventListener('click', () => {
|
|
76
|
-
try { localStorage.setItem(KEY, a.getAttribute('href')); } catch { /* keeps no site data */ }
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
});
|
package/web/admin/common.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// common.js — what every admin page starts from: the element lookup, the
|
|
2
|
-
// door the page is served behind, and the two ways it talks to the agent.
|
|
3
|
-
// Loaded first by the record page and by the setup page.
|
|
4
|
-
|
|
5
|
-
const $ = (id) => document.getElementById(id);
|
|
6
|
-
// The door this page is served behind: nothing when the agent runs on this
|
|
7
|
-
// machine, `/app` when the identity is hosted by its own pod server. Read from
|
|
8
|
-
// the page's own address, so one build serves both.
|
|
9
|
-
const BASE = location.pathname.replace(/\/admin\/.*$/u, '');
|
|
10
|
-
// Every call carries `x-fedipod-page`. On the Node agent it is ignored — the
|
|
11
|
-
// Origin check there already refuses a page on somebody else's site. In the
|
|
12
|
-
// browser build the agent IS this origin's service worker, which cannot see
|
|
13
|
-
// Sec-Fetch-* and answers these routes with no password, so this header is the
|
|
14
|
-
// whole door: a cross-site form cannot set one, and a cross-origin fetch that
|
|
15
|
-
// sets one is stopped at a preflight the worker never grants. See sw-src.mjs.
|
|
16
|
-
const PAGE_HEADER = { 'x-fedipod-page': '1' };
|
|
17
|
-
const api = async (path, init) => {
|
|
18
|
-
const res = await fetch(BASE + path, { ...init, headers: { ...(init?.headers || {}), ...PAGE_HEADER } });
|
|
19
|
-
return { status: res.status, json: await res.json().catch(() => null) };
|
|
20
|
-
};
|
|
21
|
-
const postJson = (path, body) => api(path, {
|
|
22
|
-
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body || {}),
|
|
23
|
-
});
|