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.
Files changed (440) hide show
  1. package/README.md +11 -6
  2. package/dist/door.d.ts +4 -0
  3. package/dist/door.js +31 -0
  4. package/dist/fedipod.d.ts +16 -0
  5. package/dist/fedipod.js +13 -0
  6. package/dist/front.d.ts +3 -0
  7. package/dist/front.js +94 -0
  8. package/dist/handler.d.ts +36 -101
  9. package/dist/handler.js +85 -645
  10. package/dist/handler.jsonld +2 -30
  11. package/dist/identities.d.ts +62 -0
  12. package/dist/identities.js +236 -0
  13. package/dist/mounts.d.ts +43 -0
  14. package/dist/mounts.js +123 -0
  15. package/dist/opt-in.d.ts +46 -0
  16. package/dist/opt-in.js +176 -0
  17. package/package.json +10 -18
  18. package/lib/client/c2s.mjs +0 -636
  19. package/lib/client/localapi.mjs +0 -87
  20. package/lib/client/masto/accounts.mjs +0 -310
  21. package/lib/client/masto/body.mjs +0 -156
  22. package/lib/client/masto/index.mjs +0 -206
  23. package/lib/client/masto/instance.mjs +0 -112
  24. package/lib/client/masto/media.mjs +0 -79
  25. package/lib/client/masto/oauth.mjs +0 -656
  26. package/lib/client/masto/render.mjs +0 -518
  27. package/lib/client/masto/statuses.mjs +0 -372
  28. package/lib/client/masto/timelines.mjs +0 -438
  29. package/lib/client/oidc-auth.mjs +0 -62
  30. package/lib/client/streaming.mjs +0 -154
  31. package/lib/client/webpush.mjs +0 -98
  32. package/lib/connections/acctfeed.mjs +0 -257
  33. package/lib/connections/atproto.mjs +0 -309
  34. package/lib/connections/bskyfeed.mjs +0 -243
  35. package/lib/connections/bskygroup.mjs +0 -162
  36. package/lib/connections/fediacct.mjs +0 -253
  37. package/lib/connections/import.mjs +0 -357
  38. package/lib/connections/tagfeed.mjs +0 -212
  39. package/lib/connections/vault.mjs +0 -114
  40. package/lib/core/as2.mjs +0 -206
  41. package/lib/core/contexts/activitystreams.json +0 -379
  42. package/lib/core/contexts/anno.json +0 -126
  43. package/lib/core/contexts/did-v1.json +0 -57
  44. package/lib/core/contexts/fep-5711.json +0 -36
  45. package/lib/core/contexts/gotosocial.json +0 -86
  46. package/lib/core/contexts/identity-v1.json +0 -152
  47. package/lib/core/contexts/index.mjs +0 -63
  48. package/lib/core/contexts/join-lemmy.json +0 -33
  49. package/lib/core/contexts/joinmastodon.json +0 -28
  50. package/lib/core/contexts/litepub-0.1.json +0 -60
  51. package/lib/core/contexts/map.json +0 -19
  52. package/lib/core/contexts/miscellany.json +0 -19
  53. package/lib/core/contexts/quotes.json +0 -18
  54. package/lib/core/contexts/schemaorg.json +0 -8845
  55. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  56. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  57. package/lib/core/contexts/security-multikey-v1.json +0 -35
  58. package/lib/core/contexts/security-v1.json +0 -74
  59. package/lib/core/contexts/webfinger.json +0 -10
  60. package/lib/core/deliver.mjs +0 -387
  61. package/lib/core/graphview.mjs +0 -327
  62. package/lib/core/intake/activities.mjs +0 -649
  63. package/lib/core/intake/activity.mjs +0 -318
  64. package/lib/core/intake/channel.mjs +0 -144
  65. package/lib/core/intake/group.mjs +0 -258
  66. package/lib/core/intake/index.mjs +0 -732
  67. package/lib/core/intake/notes.mjs +0 -367
  68. package/lib/core/intake/verify.mjs +0 -142
  69. package/lib/core/keys.mjs +0 -153
  70. package/lib/core/lease.mjs +0 -206
  71. package/lib/core/place.mjs +0 -82
  72. package/lib/core/polls.mjs +0 -105
  73. package/lib/core/profile-page.mjs +0 -104
  74. package/lib/core/proof.mjs +0 -79
  75. package/lib/core/publisher/collections.mjs +0 -272
  76. package/lib/core/publisher/index.mjs +0 -522
  77. package/lib/core/publisher/notes.mjs +0 -519
  78. package/lib/core/publisher/own.mjs +0 -143
  79. package/lib/core/publisher/questions.mjs +0 -241
  80. package/lib/core/publisher/restore.mjs +0 -224
  81. package/lib/core/shapes/activitystreams.ttl +0 -157
  82. package/lib/core/shapes/index.mjs +0 -132
  83. package/lib/core/shapes/shapes-text.mjs +0 -13
  84. package/lib/core/social.mjs +0 -561
  85. package/lib/core/storage.mjs +0 -176
  86. package/lib/core/store.mjs +0 -578
  87. package/lib/core/wire-quotes.mjs +0 -71
  88. package/lib/core/wire.mjs +0 -925
  89. package/lib/device/account.mjs +0 -87
  90. package/lib/device/admin/index.mjs +0 -13
  91. package/lib/device/admin/origins.mjs +0 -35
  92. package/lib/device/admin/routes/connections.mjs +0 -144
  93. package/lib/device/admin/routes/gateway.mjs +0 -199
  94. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  95. package/lib/device/admin/routes/owner.mjs +0 -322
  96. package/lib/device/admin/routes/setup.mjs +0 -428
  97. package/lib/device/admin/routes/social.mjs +0 -188
  98. package/lib/device/admin/server.mjs +0 -95
  99. package/lib/device/admin/static.mjs +0 -247
  100. package/lib/device/admin/surface.mjs +0 -315
  101. package/lib/device/certs.mjs +0 -281
  102. package/lib/device/cli/commands/account.mjs +0 -586
  103. package/lib/device/cli/commands/run.mjs +0 -278
  104. package/lib/device/cli/commands/service.mjs +0 -221
  105. package/lib/device/cli/commands/setup.mjs +0 -489
  106. package/lib/device/cli/commands/state.mjs +0 -561
  107. package/lib/device/cli/context.mjs +0 -288
  108. package/lib/device/export-collections.mjs +0 -161
  109. package/lib/device/gateway-move.mjs +0 -58
  110. package/lib/device/home.mjs +0 -161
  111. package/lib/device/migrate.mjs +0 -115
  112. package/lib/device/ports.mjs +0 -27
  113. package/lib/device/remote.mjs +0 -54
  114. package/lib/device/setup.mjs +0 -496
  115. package/lib/device/update.mjs +0 -96
  116. package/lib/gateway/caches.mjs +0 -36
  117. package/lib/gateway/directory.mjs +0 -90
  118. package/lib/gateway/front-core.mjs +0 -966
  119. package/lib/gateway/gateway-core.mjs +0 -244
  120. package/lib/gateway/headers.mjs +0 -49
  121. package/lib/gateway/httpsig.mjs +0 -159
  122. package/lib/gateway/notices.mjs +0 -84
  123. package/lib/gateway/quiet.mjs +0 -189
  124. package/lib/gateway/relay-extras.mjs +0 -89
  125. package/lib/gateway/token-claims.mjs +0 -16
  126. package/lib/pod/README.md +0 -61
  127. package/lib/pod/actor.mjs +0 -96
  128. package/lib/pod/collection.mjs +0 -77
  129. package/lib/pod/containers.mjs +0 -140
  130. package/lib/pod/discovery.mjs +0 -46
  131. package/lib/pod/featured.mjs +0 -18
  132. package/lib/pod/followers.mjs +0 -24
  133. package/lib/pod/following.mjs +0 -10
  134. package/lib/pod/http.mjs +0 -74
  135. package/lib/pod/inbox.mjs +0 -166
  136. package/lib/pod/links.mjs +0 -35
  137. package/lib/pod/location.mjs +0 -52
  138. package/lib/pod/media.mjs +0 -27
  139. package/lib/pod/notes.mjs +0 -78
  140. package/lib/pod/notifications.mjs +0 -89
  141. package/lib/pod/outbox.mjs +0 -18
  142. package/lib/pod/package.json +0 -19
  143. package/lib/pod/policy.mjs +0 -58
  144. package/lib/pod/private.mjs +0 -19
  145. package/lib/pod/root.mjs +0 -104
  146. package/lib/pod/state.mjs +0 -40
  147. package/lib/pod/transport.mjs +0 -674
  148. package/lib/pod/type-index.mjs +0 -101
  149. package/lib/pod/urls.mjs +0 -91
  150. package/lib/server/embed.mjs +0 -424
  151. package/lib/session/README.md +0 -137
  152. package/lib/session/demo.html +0 -94
  153. package/lib/session/fedi-account.mjs +0 -417
  154. package/lib/session/fedi-login.mjs +0 -123
  155. package/lib/session/oidc-session.mjs +0 -233
  156. package/lib/session/package.json +0 -49
  157. package/lib/shared/guard.mjs +0 -213
  158. package/lib/shared/links.mjs +0 -9
  159. package/lib/shared/safefetch.mjs +0 -235
  160. package/lib/shared/ua.mjs +0 -29
  161. package/phanpy/dist/404.html +0 -40
  162. package/phanpy/dist/_headers +0 -2
  163. package/phanpy/dist/apple-touch-icon.png +0 -0
  164. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  165. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  166. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  167. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  168. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  169. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  170. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  171. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  172. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  173. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  174. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  175. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  176. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  177. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  178. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  179. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  180. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  181. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  182. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  183. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  184. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  185. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  186. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  187. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  188. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  189. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  190. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  191. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  192. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  193. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  195. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  196. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  197. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  198. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  199. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  200. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  201. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  202. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  203. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  204. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  205. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  206. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  207. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  208. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  209. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  210. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  211. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  212. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  213. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  214. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  215. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  216. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  217. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  218. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  219. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  220. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  221. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  222. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  223. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  224. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  225. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  226. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  227. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  228. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  229. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  230. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  231. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  232. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  233. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  234. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  235. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  236. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  237. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  238. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  239. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  240. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  241. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  242. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  243. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  244. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  245. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  246. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  247. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  248. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  249. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  250. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  251. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  252. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  253. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  254. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  255. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  256. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  257. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  258. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  259. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  260. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  261. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  262. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  263. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  264. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  265. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  266. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  267. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  268. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  269. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  270. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  271. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  272. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  273. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  274. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  275. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  276. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  277. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  278. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  279. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  280. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  281. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  282. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  283. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  284. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  285. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  286. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  287. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  288. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  289. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  290. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  291. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  292. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  293. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  294. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  295. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  296. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  297. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  298. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  299. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  300. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  301. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  302. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  303. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  304. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  305. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  306. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  307. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  308. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  309. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  310. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  311. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  312. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  313. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  314. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  315. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  316. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  317. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  318. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  319. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  320. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  321. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  322. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  323. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  324. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  325. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  326. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  327. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  328. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  329. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  330. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  331. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  332. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  333. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  334. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  335. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  336. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  337. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  338. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  339. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  340. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  341. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  342. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  343. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  344. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  345. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  346. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  347. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  348. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  349. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  350. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  351. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  352. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  353. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  354. package/phanpy/dist/compose/index.html +0 -20
  355. package/phanpy/dist/favicon.ico +0 -0
  356. package/phanpy/dist/index.html +0 -73
  357. package/phanpy/dist/logo-192.png +0 -0
  358. package/phanpy/dist/logo-512.png +0 -0
  359. package/phanpy/dist/logo-badge-72.png +0 -0
  360. package/phanpy/dist/logo-maskable-512.png +0 -0
  361. package/phanpy/dist/logo-monochrome-512.png +0 -0
  362. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  363. package/phanpy/dist/manifest.webmanifest +0 -1
  364. package/phanpy/dist/og-image-2.jpg +0 -0
  365. package/phanpy/dist/og-image.png +0 -0
  366. package/phanpy/dist/robots.txt +0 -2
  367. package/phanpy/dist/sw.js +0 -2
  368. package/phanpy/dist/sw.js.map +0 -1
  369. package/phanpy/dist/version.json +0 -1
  370. package/run-agent.mjs +0 -899
  371. package/vendor/gate.cjs +0 -138
  372. package/vendor/idp-grant.cjs +0 -394
  373. package/vendor/jfetch.cjs +0 -16
  374. package/web/admin/actors.js +0 -147
  375. package/web/admin/bar.css +0 -126
  376. package/web/admin/bar.js +0 -99
  377. package/web/admin/client/client.js +0 -66
  378. package/web/admin/client/index.html +0 -69
  379. package/web/admin/client-pick.js +0 -79
  380. package/web/admin/common.js +0 -23
  381. package/web/admin/connections.js +0 -112
  382. package/web/admin/gateway.js +0 -133
  383. package/web/admin/group.js +0 -258
  384. package/web/admin/index.html +0 -612
  385. package/web/admin/notices-bar.js +0 -90
  386. package/web/admin/oauth-signin.mjs +0 -37
  387. package/web/admin/record.js +0 -393
  388. package/web/admin/setup/index.html +0 -257
  389. package/web/admin/setup/setup.js +0 -339
  390. package/web/admin/tokens.css +0 -68
  391. package/web/admin/upkeep.js +0 -177
  392. package/web/admin/window.css +0 -55
  393. package/web/admin/window.js +0 -136
  394. package/web/app/README.md +0 -81
  395. package/web/app/admin-facade.mjs +0 -588
  396. package/web/app/agent.mjs +0 -672
  397. package/web/app/atproto-browser.mjs +0 -86
  398. package/web/app/authorize_interaction.html +0 -56
  399. package/web/app/authorize_interaction.js +0 -69
  400. package/web/app/boot.mjs +0 -616
  401. package/web/app/deliver-relay.mjs +0 -129
  402. package/web/app/dist/boot.js +0 -34562
  403. package/web/app/dist/boot.js.map +0 -7
  404. package/web/app/dist/sw.js +0 -73456
  405. package/web/app/dist/sw.js.map +0 -7
  406. package/web/app/fediacct-browser.mjs +0 -146
  407. package/web/app/gateway-move.mjs +0 -60
  408. package/web/app/idb-kv.mjs +0 -35
  409. package/web/app/index.html +0 -258
  410. package/web/app/keys-browser.mjs +0 -71
  411. package/web/app/keystore.mjs +0 -82
  412. package/web/app/oidc-session.mjs +0 -6
  413. package/web/app/pod-auth.mjs +0 -248
  414. package/web/app/pod-remote.mjs +0 -84
  415. package/web/app/shims/_globals.mjs +0 -2
  416. package/web/app/shims/fedify-sig.mjs +0 -77
  417. package/web/app/shims/node-crypto.mjs +0 -113
  418. package/web/app/shims/node-fs.mjs +0 -17
  419. package/web/app/shims/node-path.mjs +0 -19
  420. package/web/app/shims/node-url.mjs +0 -7
  421. package/web/app/shims/prelude.js +0 -58
  422. package/web/app/shims/safefetch.mjs +0 -58
  423. package/web/app/shims/shapes-text.mjs +0 -8
  424. package/web/app/shims/web-push.mjs +0 -14
  425. package/web/app/signup.mjs +0 -357
  426. package/web/app/sw-src.mjs +0 -220
  427. package/web/app/update.js +0 -70
  428. package/web/front/admin.html +0 -55
  429. package/web/front/admin.js +0 -89
  430. package/web/front/install.sh +0 -69
  431. package/web/front/new-account.html +0 -35
  432. package/web/front/new-account.js +0 -13
  433. package/web/front/notices.html +0 -69
  434. package/web/front/notices.js +0 -107
  435. package/web/front/run.html +0 -64
  436. package/web/front/run.js +0 -221
  437. package/web/front/solid-oidc-client.js +0 -6
  438. package/web/icons/forum.svg +0 -11
  439. package/web/icons/gateway.svg +0 -14
  440. package/web/icons/local.svg +0 -11
@@ -1,428 +0,0 @@
1
- // setup.mjs — the first run and the process: setup driven from the page,
2
- // a new actor in a new home, starting a stopped one, updating and
3
- // restarting, moving the private half, and stopping this process.
4
-
5
- import fs from 'node:fs';
6
- import path from 'node:path';
7
- import os from 'node:os';
8
- import { spawn } from 'node:child_process';
9
- import { pathToFileURL } from 'node:url';
10
- import { isCrossSiteNavigation } from '../../../shared/guard.mjs';
11
- import { identityHomes, rootOf, tildify, writeJsonAtomic } from '../../home.mjs';
12
- import { copyPrivateHalf, isCurrent, CURRENT_LAYOUT } from '../../migrate.mjs';
13
- import { insecureUrlReason } from '../../../shared/safefetch.mjs';
14
- import { newRun, preflight, runSetup, setupInputError, hasCredential, credentialPath } from '../../setup.mjs';
15
- import { podLayout } from '../../../pod/root.mjs';
16
- import { chosenRoot, publicIndexKnown } from '../../../core/place.mjs';
17
- import { portFree, freePortFrom } from '../../ports.mjs';
18
- import { yieldDirectory } from '../../../gateway/directory.mjs';
19
- import { localFetch } from '../../../client/localapi.mjs';
20
- import { projectRoot, SETUP_PAGE } from '../static.mjs';
21
- import { secureOrigin } from '../origins.mjs';
22
-
23
- // The signup carry-over: gateway and identity parameters a signup page
24
- // recorded for this install (written by the installer, consumed by the first
25
- // setup run). Read fresh each time — the file is deleted once consumed.
26
- function readFirstRun() {
27
- const file = process.env.AP_FIRST_RUN;
28
- if (!file) return null;
29
- try {
30
- const fr = JSON.parse(fs.readFileSync(file, 'utf8'));
31
- return fr && typeof fr === 'object' ? fr : null;
32
- } catch { return null; }
33
- }
34
-
35
- // The same config shape the `gateway` CLI command writes.
36
- function gatewayConfigFrom(fr) {
37
- if (!fr?.gateway || !fr?.secret) return null;
38
- const target = String(fr.gateway);
39
- return fr.fronted
40
- ? { url: target.replace(/ap\/actor$/, 'ap/inbox/'), frontActor: target,
41
- mode: 'shadow', hmacSecret: String(fr.secret) }
42
- : { url: target.replace(/\/?$/, '/'), mode: 'shadow', hmacSecret: String(fr.secret) };
43
- }
44
-
45
- export async function get(p, ctx, req, res, url) { // eslint-disable-line no-unused-vars
46
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
47
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
48
-
49
- // ---- setup and configuration, for the pages under /admin/ ----
50
- // These answer for a group too: a group is set up in the browser like
51
- // anything else, and it has a display name to change.
52
- if (req.method === 'GET' && p === '/setup/state') {
53
- const home = agent.home || null;
54
- const held = home && hasCredential(home) ? agent.readCredential?.() : null;
55
- return json(res, 200, {
56
- hasCredential: !!(home && hasCredential(home)),
57
- configured: agent.configured(),
58
- // Not the same question: a crashed setup leaves a credential with no
59
- // actor behind it, and that is finishable without minting a second.
60
- resumable: !!(home && hasCredential(home)) && !agent.configured(),
61
- running: ctx.setup.run?.phase === 'running',
62
- phase: ctx.setup.run?.phase || 'idle',
63
- home,
64
- port,
65
- handle: agent.store.getConfig()?.handle || allowed.label || null,
66
- kind: agent.store.getConfig()?.kind || null,
67
- origins: {
68
- loopback: publicOrigin
69
- || `https://localhost:${port}/`,
70
- named: publicOrigin
71
- ? null
72
- : allowed.label
73
- ? (true
74
- ? `https://${allowed.label}.localhost:${port}/`
75
- : `http://${allowed.label}.localhost:${port}/`)
76
- : null,
77
- },
78
- // Set in the environment, the password never needs to reach the page.
79
- passwordSupplied: !!process.env.AP_PASSWORD,
80
- identity: held ? { pod: held.remotePod, issuer: held.issuerOrigin, root: held.root || null } : null,
81
- defaults: { issuer: 'https://solidcommunity.net', port: 8030 },
82
- // What a signup page arranged for this install, so the form opens
83
- // pre-filled. The receipt secret stays out of it — the /setup run
84
- // reads that from the file itself.
85
- firstRun: (() => {
86
- if (agent.configured()) return null;
87
- const fr = readFirstRun();
88
- if (!fr?.handle) return null;
89
- let gatewayHost = null;
90
- try { gatewayHost = new URL(fr.gateway).host; } catch { /* no gateway named */ }
91
- return { pod: fr.pod || null, issuer: fr.issuer || null, handle: String(fr.handle),
92
- kind: fr.kind === 'group' ? 'group' : 'person', fronted: !!fr.fronted, gatewayHost };
93
- })(),
94
- });
95
- }
96
- if (req.method === 'GET' && p === '/setup/progress') {
97
- return json(res, 200, ctx.setup.run || { phase: 'idle', steps: [], error: null, result: null });
98
- }
99
-
100
- return false;
101
- }
102
-
103
- export async function post(p, body, ctx, req, res) { // eslint-disable-line no-unused-vars
104
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
105
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
106
- switch (p) {
107
- // Stop an agent whose pidfile is gone and which no terminal owns
108
- // (backgrounded, orphaned by a closed shell). It used to sit ABOVE both
109
- // the configured() gate and the isLocal one, so it was the only
110
- // state-changing route that needed neither — reachable from any host
111
- // AP_ALLOWED_HOSTS named, on an agent that had never been set up.
112
- case '/shutdown': {
113
- json(res, 200, { ok: true, stopping: process.pid });
114
- setTimeout(() => process.kill(process.pid, 'SIGTERM'), 50);
115
- return true;
116
- }
117
- // ---- setup, driven by the page at /admin/setup/ ----
118
- case '/setup/check': {
119
- const pre = { ...preflight(body), ...await placeCheck(body) };
120
- // For a new pod, ask the provider where it puts pods, so the page can
121
- // show the Gateway address before the pod exists. A provider on paths
122
- // means the address must live at the Gateway; the run decides for real.
123
- if (pre.ok && body.mode === 'new' && body.shape !== 'front') {
124
- const layout = await podLayout(fetch, body.issuer || '').catch(() => null);
125
- if (layout === 'path') {
126
- if ((body.kind || 'person') === 'group') {
127
- return json(res, 200, { ...pre, ok: false, layout, refusal: 'group-needs-host-root' });
128
- }
129
- let gwHost; try { gwHost = new URL(body.gatewayOrigin || 'https://fedipod.net').host; } catch { gwHost = 'fedipod.net'; }
130
- return json(res, 200, { ...pre, layout, fronted: true, forced: true, shape: 'front', gatewayHost: gwHost,
131
- address: `@${body.handle}@${gwHost}`, resolvable: true });
132
- }
133
- return json(res, 200, { ...pre, layout });
134
- }
135
- return json(res, 200, pre);
136
- }
137
- // Discard a credential that never finished setup, so the account and
138
- // pod can be entered again. The credential a CSS server mints is shown
139
- // once, so a setup that stops after the mint (a wrong pod answers 401
140
- // to the first write) leaves the form in "finish" mode with no way to
141
- // re-enter what was wrong. This removes it locally and reopens the full
142
- // form. It does NOT revoke server-side — that needs the account
143
- // password (`fedipod revoke-credential`); the old credential is left on
144
- // the account, revocable from its dashboard.
145
- case '/setup/reset': {
146
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
147
- // A working identity is never swapped out this way — that is a
148
- // teardown (`fedipod retire`), not a half-finished setup.
149
- if (agent.configured()) {
150
- return json(res, 409, { error: 'this home holds a working identity — retire it, do not reset' });
151
- }
152
- if (ctx.setup.run?.phase === 'running') {
153
- return json(res, 409, { error: 'setup is running — let it finish or stop it first', phase: 'running' });
154
- }
155
- const home = agent.home;
156
- if (!home) return json(res, 500, { error: 'this agent has no AP_HOME to reset' });
157
- const removed = hasCredential(home);
158
- if (removed) fs.rmSync(credentialPath(home), { force: true });
159
- // Drop the pod handle too, so configured() cannot flicker true off a
160
- // stale in-memory session while the fresh form is filled in.
161
- agent.remote = null;
162
- ctx.setup.run = null;
163
- return json(res, 200, { ok: true, removed });
164
- }
165
- case '/setup': {
166
- // A visited page must not be able to navigate this into existence.
167
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
168
- if (agent.configured()) {
169
- return json(res, 409, {
170
- error: 'this home already holds an identity',
171
- pod: agent.store.getConfig()?.remotePod || null,
172
- });
173
- }
174
- if (ctx.setup.run?.phase === 'running') {
175
- return json(res, 409, { error: 'setup is already running', phase: 'running' });
176
- }
177
- const home = agent.home;
178
- if (!home) return json(res, 500, { error: 'this agent has no AP_HOME to set up' });
179
- // A crashed setup left a credential with no actor behind it. That is
180
- // finishable, and must NOT mint a second credential — the first is
181
- // unrecoverable and would be orphaned.
182
- const resuming = hasCredential(home);
183
- const answers = { ...body, password: body.password || process.env.AP_PASSWORD || '' };
184
- // The signup carry-over: attach to the gateway the signup arranged,
185
- // set before the first publish so the actor never advertises the pod
186
- // inbox first. The secret comes from the installer's file, never the
187
- // page.
188
- const gatewayCfg = gatewayConfigFrom(readFirstRun());
189
- if (gatewayCfg) answers.gateway = gatewayCfg;
190
- const bad = setupInputError(answers, resuming);
191
- if (bad) return json(res, 400, { error: bad });
192
- if (!resuming) {
193
- const pre = preflight(answers);
194
- if (!pre.ok) return json(res, 400, { ...pre, error: pre.error || pre.refusal });
195
- }
196
- // Widen the allowlist now, not at the end: the page offers to move to
197
- // the named origin, which has to answer before it is offered.
198
- allowed.setHandle(answers.handle);
199
- ctx.setup.run = newRun();
200
- // Deliberately not awaited. The credential a CSS server mints is
201
- // shown once, so what a run depends on is the file it writes, never
202
- // a connection a closed tab can take with it.
203
- runSetup({ home, agent, answers, run: ctx.setup.run, log })
204
- .then((r) => {
205
- // The carried secret is consumed; a finished setup deletes it.
206
- if (r?.phase === 'done' && process.env.AP_FIRST_RUN) {
207
- try { fs.unlinkSync(process.env.AP_FIRST_RUN); } catch { /* already gone */ }
208
- }
209
- })
210
- .catch(e => { ctx.setup.run.phase = 'error'; ctx.setup.run.error = e.message; });
211
- return json(res, 202, { ok: true, running: true, resuming });
212
- }
213
- // One identity per home, so a new actor means a new home, a free port
214
- // and a process there — then its own setup, run with the answers this
215
- // page collected. The reply is the address of the new actor's page,
216
- // which is where the progress of that setup is reported.
217
- case '/new-actor': {
218
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
219
- const handle = String(body.handle || '').trim().toLowerCase();
220
- // The name first, and before anything is created: it is what becomes
221
- // a directory, and "mode must be new or existing" is a poor answer to
222
- // a handle that could climb out of profiles/.
223
- // Named after the handle rather than generated, so `profiles` and
224
- // `--profile <name>` stay legible a year from now.
225
- if (!/^[a-z0-9][a-z0-9_-]{0,30}$/.test(handle)) {
226
- return json(res, 400, { error: 'a handle is letters, digits, hyphens and underscores' });
227
- }
228
- const dir = path.join(rootOf(path.resolve(agent.home || '')), 'profiles', handle);
229
- if (fs.existsSync(path.join(dir, 'credential.json'))) {
230
- return json(res, 409, { error: `${handle} already exists — it is in the list above` });
231
- }
232
- // Then everything else setup needs, so a missing password is refused
233
- // here rather than after a process has been started for it.
234
- const bad = setupInputError({ ...body, handle });
235
- if (bad) return json(res, 400, { error: bad });
236
- const newPort = await freePortFrom(port + 1);
237
- if (!newPort) return json(res, 503, { error: 'no free port in the next 50' });
238
- fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
239
- const child = spawn(process.execPath, [path.join(projectRoot, 'run-agent.mjs')], {
240
- detached: true, stdio: 'ignore',
241
- env: { ...process.env, AP_HOME: dir, AP_PORT: String(newPort), AP_PROFILE: '' },
242
- });
243
- child.unref();
244
- // Answer only once it is listening: handing back a URL that is not
245
- // up yet shows a connection error instead of the setup form.
246
- let listening = false;
247
- for (let i = 0; i < 60 && !listening; i++) {
248
- await new Promise(r => setTimeout(r, 250));
249
- listening = await localFetch(agent.home, newPort, '/status', { timeout: 1000 })
250
- .then(r => r.ok).catch(() => false);
251
- }
252
- if (!listening) return json(res, 504, { error: `started it on ${newPort} but it never answered` });
253
-
254
- // Its own /setup owns the work — minting the credential, provisioning
255
- // the pod, publishing. It answers 202 and runs in the background, so
256
- // the page that opens next is the one reporting progress.
257
- const started = await localFetch(agent.home, newPort, '/setup', {
258
- method: 'POST',
259
- headers: { 'content-type': 'application/json' },
260
- body: JSON.stringify({ ...body, handle }),
261
- }).then(async r => ({ status: r.status, json: await r.json().catch(() => null) }))
262
- .catch(e => ({ status: 0, json: { error: e.message } }));
263
- if (started.status !== 202) {
264
- return json(res, started.status || 502, {
265
- error: started.json?.error || `the new agent refused setup (HTTP ${started.status})`,
266
- port: newPort, url: `${secureOrigin(handle, newPort)}${SETUP_PAGE}`,
267
- });
268
- }
269
- return json(res, 200,
270
- { ok: true, handle, port: newPort,
271
- url: `${secureOrigin(handle, newPort)}${SETUP_PAGE}` });
272
- }
273
- // Start an actor that is not running, so its page can be visited. The
274
- // same spawn as /new-actor, minus the making: this one has a home
275
- // already. Its recorded port is preferred so its address stays what it
276
- // was, but a port something else has taken is walked past, not fought.
277
- case '/start-actor': {
278
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
279
- const name = String(body.name || '');
280
- const found = identityHomes(rootOf(path.resolve(agent.home || ''))).find(h => h.name === name);
281
- if (!found) return json(res, 404, { error: `no identity called ${name}` });
282
-
283
- let want = null;
284
- try { want = JSON.parse(fs.readFileSync(path.join(found.dir, 'agent.json'), 'utf8')).port || null; } catch {}
285
- if (want) {
286
- // Its own handle, not the profile directory's name: usually the
287
- // same, but a renamed profile makes them differ and the origin has
288
- // to be one that agent's own guard will accept.
289
- const live = await localFetch(agent.home, want, '/status', { timeout: 1000 })
290
- .then(r => (r.ok ? r.json() : null)).catch(() => null);
291
- if (live) {
292
- return json(res, 200, {
293
- ok: true, name, port: want, already: true,
294
- url: `${secureOrigin(live.handle || name, want)}/`,
295
- });
296
- }
297
- }
298
- // The port is not the only evidence it is running. An agent from
299
- // before agent.json was written at startup has a live pidfile and no
300
- // recorded port, and spawning past that gives one home two agents —
301
- // which is exactly how this machine ended up with two on profiles/jeff.
302
- let held = null;
303
- try { held = Number(fs.readFileSync(path.join(found.dir, 'agent.pid'), 'utf8').trim()) || null; } catch {}
304
- if (held) {
305
- let alive = false;
306
- try { process.kill(held, 0); alive = true; } catch { /* gone */ }
307
- if (alive) {
308
- return json(res, 409, {
309
- error: `${name} is already running as pid ${held}, on a port it never recorded. `
310
- + `Stop it first: kill ${held}`,
311
- });
312
- }
313
- }
314
- // A recorded port held by the directory door is still this
315
- // identity's port — the door steps aside for its owner.
316
- if (want && !await portFree(want)) await yieldDirectory(want, { portFree });
317
- const on = (want && await portFree(want)) ? want : await freePortFrom(port + 1);
318
- if (!on) return json(res, 503, { error: 'no free port in the next 50' });
319
-
320
- const started = spawn(process.execPath, [path.join(projectRoot, 'run-agent.mjs')], {
321
- detached: true, stdio: 'ignore',
322
- env: { ...process.env, AP_HOME: found.dir, AP_PORT: String(on), AP_PROFILE: '' },
323
- });
324
- started.unref();
325
- for (let i = 0; i < 60; i++) {
326
- await new Promise(r => setTimeout(r, 250));
327
- const up = await localFetch(agent.home, on, '/status', { timeout: 1000 })
328
- .then(r => (r.ok ? r.json() : null)).catch(() => null);
329
- if (up) {
330
- return json(res, 200, { ok: true, name, port: on,
331
- url: `${secureOrigin(up.handle || name, on)}/` });
332
- }
333
- }
334
- return json(res, 504, { error: `started ${name} on ${on} but it never answered` });
335
- }
336
- // Pull the latest published FediPod and restart the agents. What the
337
- // installer does on a re-run, offered where the version line is.
338
- case '/update': {
339
- if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
340
- const { runUpdate, restartAgents, repoRoot } = await import('../../update.mjs');
341
- const r = runUpdate({ log });
342
- if (!r.ok) return json(res, 409, { error: r.note });
343
- json(res, 200, { ok: true, note: r.note, restarting: true });
344
- // The response first, the restart a beat later — a self-restart must
345
- // not take the answer down with it.
346
- setTimeout(() => {
347
- if (restartAgents({ log }) === 'self') {
348
- const child = spawn(process.execPath, [path.join(repoRoot(), 'run-agent.mjs')],
349
- { detached: true, stdio: 'ignore', env: process.env });
350
- child.unref();
351
- setTimeout(() => process.exit(0), 300);
352
- }
353
- }, 200);
354
- return true;
355
- }
356
- // Move the private half — every state document and the RDF tree — while
357
- // the agent runs: quiesce this process's writers, flush, copy, verify,
358
- // repoint the credential, then reconnect on the new location. The old
359
- // copy is left where it was, exactly as the CLI move leaves it.
360
- case '/state-move': {
361
- const cred = agent.readCredential?.();
362
- if (!cred) return json(res, 409, { error: 'no credential — run setup first' });
363
- const to = String(body.to || '').trim();
364
- if (!to) return json(res, 400, { error: 'say where it should go' });
365
- let target = null;
366
- if (to !== 'pod') {
367
- // A path or a URL — two chars before the colon, so a Windows drive
368
- // letter reads as a path rather than a scheme.
369
- const asPath = !/^[a-z][a-z0-9+.-]+:/i.test(to);
370
- const raw = asPath
371
- ? pathToFileURL(path.resolve(to.replace(/^~(?=[/\\]|$)/, os.homedir()))).href
372
- : to;
373
- target = raw.endsWith('/') ? raw : raw + '/';
374
- try { new URL(target); } catch { return json(res, 400, { error: `"${to}" is not a container URL or a path` }); }
375
- if (/^https?:/i.test(target)) {
376
- const bad = insecureUrlReason(target, 'private-data address');
377
- if (bad) return json(res, 400, { error: bad });
378
- }
379
- }
380
- const whereState = (c) => (c.privateRoot ? tildify(c.privateRoot) : 'on the pod');
381
- if ((cred.privateRoot || null) === target) {
382
- return json(res, 200, { ok: true, docs: 0, notes: 0, unchanged: true, now: whereState(cred) });
383
- }
384
- await agent.requestTakeover?.();
385
- agent.intake?.stop(); agent.deliverer?.stop(); agent.tagfeed?.stop(); agent.importer?.stop();
386
- try {
387
- await agent.store.flush();
388
- const destCred = { ...cred, privateRoot: target };
389
- const copied = await copyPrivateHalf({
390
- from: { state: agent.privateStorage(cred, 'state') },
391
- to: { state: agent.privateStorage(destCred, 'state') },
392
- log,
393
- });
394
- if (target) cred.privateRoot = target; else delete cred.privateRoot;
395
- if (isCurrent(cred)) cred.layout = CURRENT_LAYOUT; else delete cred.layout;
396
- writeJsonAtomic(path.join(agent.home, 'credential.json'), cred);
397
- // connect() rebuilds everything that pointed at the old location —
398
- // store, publisher, intake — and restarts them.
399
- agent.store.attach(agent.privateStorage(cred, 'state'));
400
- agent.stateLoaded = false;
401
- await agent.connect();
402
- return json(res, 200, { ok: true, ...copied, now: whereState(cred) });
403
- } catch (e) {
404
- // The credential is only repointed after a verified copy, so on any
405
- // failure reconnect puts the agent back to work where it was.
406
- agent.stateLoaded = false;
407
- await agent.connect().catch(() => {});
408
- return json(res, 502, { error: e.message });
409
- }
410
- }
411
- default: return false;
412
- }
413
- }
414
-
415
- // Where the data will go and whether the type index question has to be asked:
416
- // a pod you bring is read as a stranger would (its profile is public); a new
417
- // pod has no index, so the question is always asked for one.
418
- async function placeCheck(body) {
419
- if (body.mode === 'new') return { typeIndex: false };
420
- let base;
421
- try { base = new URL(String(body.pod || '')).href; } catch { return {}; }
422
- if (!base.endsWith('/')) base += '/';
423
- const place = chosenRoot(base, body.container);
424
- return {
425
- typeIndex: await publicIndexKnown(fetch, base),
426
- ...(place.problem ? { placeProblem: place.problem } : { place: base + place.root }),
427
- };
428
- }
@@ -1,188 +0,0 @@
1
- // social.mjs — the owner acting on the social graph, and a group operator
2
- // moderating: posting, the tag feed, follows, mutes, blocks, ejecting,
3
- // retracting, review, joins, the moderation queue.
4
-
5
- import { followHandle, followActor, unfollowActor, ejectFollower, retractAnnouncement, admitRequest, refuseRequest, applyModeration } from '../../../core/social.mjs';
6
-
7
- export async function get(p, ctx, req, res, url) { // eslint-disable-line no-unused-vars
8
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
9
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
10
-
11
- if (req.method === 'GET' && p === '/tagfeed') {
12
- return json(res, 200, agent.tagfeed
13
- ? { ...agent.tagfeed.config(), lastSweep: agent.tagfeed.lastSweep, lastAdded: agent.tagfeed.lastAdded }
14
- : { error: 'agent not configured' });
15
- }
16
-
17
- return false;
18
- }
19
-
20
- export async function post(p, body, ctx, req, res) { // eslint-disable-line no-unused-vars
21
- const { agent, log, allowed, embedded, port, publicOrigin, versionOnDisk, isGroup } = ctx; // eslint-disable-line no-unused-vars
22
- const json = (res, status, obj) => { ctx.json(res, status, obj); return true; };
23
- switch (p) {
24
- case '/post': {
25
- if (!body.content) return json(res, 400, { error: 'content required' });
26
- const note = await agent.publisher.publishNote(body.content, { inReplyTo: body.inReplyTo });
27
- return json(res, 200, { ok: true, id: note.id });
28
- }
29
- case '/tagfeed': return json(res, 200, agent.tagfeed.setConfig(body));
30
- case '/archive': {
31
- // Whether drained mail's original bytes are kept in the private
32
- // half's inbox-archive/. Absent means on.
33
- const cfg = agent.store.getConfig();
34
- agent.store.setConfig({ ...cfg, archiveInbox: !!body.on });
35
- await agent.store.flush();
36
- return json(res, 200, { ok: true, archiveInbox: !!body.on });
37
- }
38
- case '/follow': {
39
- // By handle normally; by actor URL when there is no handle to resolve —
40
- // a pod on a path, or anything WebFinger cannot answer for. Pasting an
41
- // actor URL is a normal way to follow in Mastodon too.
42
- if (body.actor) { await followActor(agent, body.actor); return json(res, 200, { ok: true, actor: body.actor }); }
43
- if (!body.handle) return json(res, 400, { error: 'handle or actor required' });
44
- return json(res, 200, await followHandle(agent, body.handle));
45
- }
46
- case '/unfollow': return json(res, 200, await unfollowActor(agent, body.actor));
47
- case '/mute':
48
- case '/unmute': {
49
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
50
- if (!body.actor) return json(res, 400, { error: 'actor required' });
51
- const m = agent.store.getMuted();
52
- m.actors = p === '/mute'
53
- ? [...new Set([...m.actors, body.actor])]
54
- : m.actors.filter(a => a !== body.actor);
55
- agent.store.setMuted(m);
56
- await agent.store.flush();
57
- return json(res, 200, { ok: true, actors: m.actors });
58
- }
59
- case '/eject': {
60
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
61
- if (!body.actor) return json(res, 400, { error: 'actor required' });
62
- const r = await ejectFollower(agent, body.actor);
63
- await agent.store.flush();
64
- return json(res, 200, r);
65
- }
66
- case '/retract': {
67
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
68
- if (!body.noteId) return json(res, 400, { error: 'noteId required' });
69
- const r = await retractAnnouncement(agent, body.noteId);
70
- await agent.store.flush();
71
- return json(res, 200, r);
72
- }
73
- case '/review': {
74
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
75
- agent.store.setConfig({ ...agent.store.getConfig(), review: !!body.on });
76
- await agent.store.flush();
77
- return json(res, 200, { ok: true, review: !!body.on });
78
- }
79
- case '/describe': {
80
- const cfg = { ...agent.store.getConfig() };
81
- if ('summary' in body) cfg.summary = body.summary || undefined;
82
- if ('icon' in body) cfg.icon = body.icon || undefined;
83
- agent.store.setConfig(cfg);
84
- Object.assign(agent.publisher.config, { summary: cfg.summary, icon: cfg.icon });
85
- await agent.store.flush();
86
- await agent.publisher.publishProfile(); // the bio and avatar are on the wire
87
- return json(res, 200, { ok: true, summary: cfg.summary || null, icon: cfg.icon || null });
88
- }
89
- case '/joins': {
90
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
91
- const cfg = { ...agent.store.getConfig(), approveJoins: !!body.approve };
92
- agent.store.setConfig(cfg);
93
- agent.publisher.config.approveJoins = !!body.approve;
94
- await agent.store.flush();
95
- // Unlike review, this one is visible to the fediverse: the actor
96
- // document carries manuallyApprovesFollowers, so nothing changes for
97
- // anyone until it is republished.
98
- await agent.publisher.publishProfile();
99
- return json(res, 200, { ok: true, approveJoins: !!body.approve });
100
- }
101
- case '/admit':
102
- case '/refuse': {
103
- // Kind-agnostic, like the queue they act on: admitting adds a
104
- // follower and Accepts, refusing Rejects. A person needs both.
105
- // `all` answers the whole queue — the shape a migration wave arrives
106
- // in — with one collections republish for the lot.
107
- if (p === '/admit' && body.all === true) {
108
- const waiting = agent.store.getRequests().map(r => r.actor);
109
- let admitted = 0;
110
- for (const actor of waiting) {
111
- try { await admitRequest(agent, actor, { publish: false }); admitted++; }
112
- catch (e) { log(`admit ${actor} failed: ${e.message}`); }
113
- }
114
- if (admitted) await agent.publisher.publishCollections({ followers: true, pending: true });
115
- await agent.store.flush();
116
- return json(res, 200, { ok: true, admitted, requests: agent.store.getRequests().length });
117
- }
118
- if (!body.actor) return json(res, 400, { error: 'actor required' });
119
- const r = p === '/admit' ? await admitRequest(agent, body.actor)
120
- : await refuseRequest(agent, body.actor);
121
- await agent.store.flush();
122
- return json(res, 200, { ...r, requests: agent.store.getRequests().length });
123
- }
124
- case '/approve':
125
- case '/decline': {
126
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
127
- if (!body.noteId) return json(res, 400, { error: 'noteId required' });
128
- const held = agent.store.getPending().some(x => x.noteId === body.noteId);
129
- if (!held) return json(res, 404, { error: 'not held for review' });
130
- if (p === '/approve') await agent.intake.amplify(body.noteId, { approved: true });
131
- else agent.store.setPending(agent.store.getPending().filter(x => x.noteId !== body.noteId));
132
- await agent.store.flush();
133
- return json(res, 200, { ok: true, noteId: body.noteId, pending: agent.store.getPending().length });
134
- }
135
- // Answer one queued moderation ask: apply it (the operator vouching
136
- // for a delivery nothing else can vouch for) or dismiss it.
137
- case '/modqueue': {
138
- if (!isGroup()) return json(res, 404, { error: 'not a group' });
139
- const q = agent.store.read('modqueue.json', []);
140
- const entry = q.find(e => e.id === body.id);
141
- if (!entry) return json(res, 404, { error: 'no such queued action' });
142
- if (body.action !== 'apply' && body.action !== 'dismiss') {
143
- return json(res, 400, { error: 'action must be apply or dismiss' });
144
- }
145
- let applied = null;
146
- if (body.action === 'apply') applied = await applyModeration(agent, entry);
147
- agent.store.write('modqueue.json', q.filter(e => e.id !== entry.id));
148
- await agent.store.flush();
149
- return json(res, 200, {
150
- ok: true, action: body.action, type: entry.type, moderator: entry.moderator,
151
- ...(applied && typeof applied === 'object' ? { result: applied } : {}),
152
- remaining: agent.store.read('modqueue.json', []).length,
153
- });
154
- }
155
- // Symmetrical with /block, and open for the same reason: a block made
156
- // by mistake is worth undoing before federation is even configured.
157
- case '/unblock': {
158
- const b = agent.store.getBlocklist();
159
- const before = b.domains.length + b.actors.length;
160
- if (body.actor) b.actors = b.actors.filter(a => a !== body.actor);
161
- else if (body.domain) b.domains = b.domains.filter(d => d !== body.domain);
162
- else return json(res, 400, { error: 'domain or actor required' });
163
- agent.store.setBlocklist(b);
164
- // The published blocked collection (FEP-c648) follows, best-effort.
165
- Promise.resolve(agent.publisher?.publishCollections?.({ blocked: true })).catch(() => {});
166
- return json(res, 200, {
167
- ok: true, removed: before - (b.domains.length + b.actors.length),
168
- domains: b.domains, actors: b.actors,
169
- });
170
- }
171
- case '/block': {
172
- const b = agent.store.getBlocklist();
173
- if (body.actor) {
174
- if (!/^https?:\/\/\S+$/.test(body.actor)) return json(res, 400, { error: 'actor must be a URL' });
175
- if (!b.actors.includes(body.actor)) b.actors.push(body.actor);
176
- } else if (body.domain) {
177
- if (!b.domains.includes(body.domain)) b.domains.push(body.domain);
178
- } else {
179
- return json(res, 400, { error: 'domain or actor required' });
180
- }
181
- agent.store.setBlocklist(b);
182
- // The published blocked collection (FEP-c648) follows, best-effort.
183
- Promise.resolve(agent.publisher?.publishCollections?.({ blocked: true })).catch(() => {});
184
- return json(res, 200, { ok: true, domains: b.domains, actors: b.actors });
185
- }
186
- default: return false;
187
- }
188
- }