@geekity/cms 0.23.0 → 0.25.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 (192) hide show
  1. package/README.md +96 -160
  2. package/admin/pages/documents/editor.njk +45 -0
  3. package/admin/pages/federation/settings.njk +1 -59
  4. package/admin/pages/plugins/installed.njk +113 -0
  5. package/admin/pages/plugins/screen.njk +129 -0
  6. package/admin/static/admin.css +136 -4
  7. package/admin/static/editor-actions.js +192 -0
  8. package/admin/static/editor.js +1 -1
  9. package/dist/admin/accounts.d.ts +18 -39
  10. package/dist/admin/accounts.d.ts.map +1 -1
  11. package/dist/admin/accounts.js +19 -48
  12. package/dist/admin/accounts.js.map +1 -1
  13. package/dist/admin/documents.d.ts +5 -0
  14. package/dist/admin/documents.d.ts.map +1 -1
  15. package/dist/admin/documents.js +17 -8
  16. package/dist/admin/documents.js.map +1 -1
  17. package/dist/admin/editor-actions.d.ts +35 -0
  18. package/dist/admin/editor-actions.d.ts.map +1 -0
  19. package/dist/admin/editor-actions.js +168 -0
  20. package/dist/admin/editor-actions.js.map +1 -0
  21. package/dist/admin/federation.d.ts +4 -5
  22. package/dist/admin/federation.d.ts.map +1 -1
  23. package/dist/admin/federation.js +4 -5
  24. package/dist/admin/federation.js.map +1 -1
  25. package/dist/admin/index.d.ts +1 -1
  26. package/dist/admin/index.d.ts.map +1 -1
  27. package/dist/admin/index.js +1 -1
  28. package/dist/admin/index.js.map +1 -1
  29. package/dist/admin/menu.d.ts +1 -1
  30. package/dist/admin/menu.d.ts.map +1 -1
  31. package/dist/admin/menu.js +10 -3
  32. package/dist/admin/menu.js.map +1 -1
  33. package/dist/admin/plugins.d.ts +24 -0
  34. package/dist/admin/plugins.d.ts.map +1 -0
  35. package/dist/admin/plugins.js +312 -0
  36. package/dist/admin/plugins.js.map +1 -0
  37. package/dist/admin/routes.d.ts.map +1 -1
  38. package/dist/admin/routes.js +5 -1
  39. package/dist/admin/routes.js.map +1 -1
  40. package/dist/admin/settings-federation.d.ts +0 -25
  41. package/dist/admin/settings-federation.d.ts.map +1 -1
  42. package/dist/admin/settings-federation.js +1 -54
  43. package/dist/admin/settings-federation.js.map +1 -1
  44. package/dist/admin/settings.d.ts +2 -14
  45. package/dist/admin/settings.d.ts.map +1 -1
  46. package/dist/admin/settings.js +1 -21
  47. package/dist/admin/settings.js.map +1 -1
  48. package/dist/admin/store.d.ts +8 -1
  49. package/dist/admin/store.d.ts.map +1 -1
  50. package/dist/admin/store.js +7 -3
  51. package/dist/admin/store.js.map +1 -1
  52. package/dist/admin/templates.d.ts +2 -0
  53. package/dist/admin/templates.d.ts.map +1 -1
  54. package/dist/admin/templates.js +2 -0
  55. package/dist/admin/templates.js.map +1 -1
  56. package/dist/cli.d.ts +2 -3
  57. package/dist/cli.d.ts.map +1 -1
  58. package/dist/cli.js +278 -212
  59. package/dist/cli.js.map +1 -1
  60. package/dist/config.d.ts +20 -0
  61. package/dist/config.d.ts.map +1 -1
  62. package/dist/config.js +6 -0
  63. package/dist/config.js.map +1 -1
  64. package/dist/content/store.d.ts +6 -0
  65. package/dist/content/store.d.ts.map +1 -1
  66. package/dist/content/store.js +3 -0
  67. package/dist/content/store.js.map +1 -1
  68. package/dist/drain.d.ts +28 -0
  69. package/dist/drain.d.ts.map +1 -0
  70. package/dist/drain.js +106 -0
  71. package/dist/drain.js.map +1 -0
  72. package/dist/env.d.ts +11 -0
  73. package/dist/env.d.ts.map +1 -1
  74. package/dist/federation/federation.d.ts +4 -2
  75. package/dist/federation/federation.d.ts.map +1 -1
  76. package/dist/federation/federation.js +16 -21
  77. package/dist/federation/federation.js.map +1 -1
  78. package/dist/federation/inbox.d.ts +23 -3
  79. package/dist/federation/inbox.d.ts.map +1 -1
  80. package/dist/federation/inbox.js +63 -1
  81. package/dist/federation/inbox.js.map +1 -1
  82. package/dist/federation/index.d.ts +0 -4
  83. package/dist/federation/index.d.ts.map +1 -1
  84. package/dist/federation/index.js +0 -2
  85. package/dist/federation/index.js.map +1 -1
  86. package/dist/federation/mount.d.ts +6 -12
  87. package/dist/federation/mount.d.ts.map +1 -1
  88. package/dist/federation/mount.js +3 -50
  89. package/dist/federation/mount.js.map +1 -1
  90. package/dist/federation/queue.d.ts +22 -0
  91. package/dist/federation/queue.d.ts.map +1 -0
  92. package/dist/federation/queue.js +58 -0
  93. package/dist/federation/queue.js.map +1 -0
  94. package/dist/files/atomic.d.ts +13 -0
  95. package/dist/files/atomic.d.ts.map +1 -1
  96. package/dist/files/atomic.js +45 -0
  97. package/dist/files/atomic.js.map +1 -1
  98. package/dist/index.d.ts +30 -3
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +156 -79
  101. package/dist/index.js.map +1 -1
  102. package/dist/plugin.d.ts +513 -0
  103. package/dist/plugin.d.ts.map +1 -0
  104. package/dist/plugin.js +23 -0
  105. package/dist/plugin.js.map +1 -0
  106. package/dist/plugins/enabled.d.ts +23 -0
  107. package/dist/plugins/enabled.d.ts.map +1 -0
  108. package/dist/plugins/enabled.js +49 -0
  109. package/dist/plugins/enabled.js.map +1 -0
  110. package/dist/plugins/federation.d.ts +26 -0
  111. package/dist/plugins/federation.d.ts.map +1 -0
  112. package/dist/plugins/federation.js +114 -0
  113. package/dist/plugins/federation.js.map +1 -0
  114. package/dist/plugins/fetch.d.ts +15 -0
  115. package/dist/plugins/fetch.d.ts.map +1 -0
  116. package/dist/plugins/fetch.js +33 -0
  117. package/dist/plugins/fetch.js.map +1 -0
  118. package/dist/plugins/folder.d.ts +50 -0
  119. package/dist/plugins/folder.d.ts.map +1 -0
  120. package/dist/plugins/folder.js +171 -0
  121. package/dist/plugins/folder.js.map +1 -0
  122. package/dist/plugins/install.d.ts +45 -0
  123. package/dist/plugins/install.d.ts.map +1 -0
  124. package/dist/plugins/install.js +275 -0
  125. package/dist/plugins/install.js.map +1 -0
  126. package/dist/plugins/mount.d.ts +17 -0
  127. package/dist/plugins/mount.d.ts.map +1 -0
  128. package/dist/plugins/mount.js +44 -0
  129. package/dist/plugins/mount.js.map +1 -0
  130. package/dist/plugins/registry.d.ts +110 -0
  131. package/dist/plugins/registry.d.ts.map +1 -0
  132. package/dist/plugins/registry.js +447 -0
  133. package/dist/plugins/registry.js.map +1 -0
  134. package/dist/plugins/settings.d.ts +64 -0
  135. package/dist/plugins/settings.d.ts.map +1 -0
  136. package/dist/plugins/settings.js +219 -0
  137. package/dist/plugins/settings.js.map +1 -0
  138. package/dist/plugins/site.d.ts +17 -0
  139. package/dist/plugins/site.d.ts.map +1 -0
  140. package/dist/plugins/site.js +67 -0
  141. package/dist/plugins/site.js.map +1 -0
  142. package/dist/supervisor/primary.d.ts +47 -0
  143. package/dist/supervisor/primary.d.ts.map +1 -0
  144. package/dist/supervisor/primary.js +208 -0
  145. package/dist/supervisor/primary.js.map +1 -0
  146. package/dist/supervisor/protocol.d.ts +37 -0
  147. package/dist/supervisor/protocol.d.ts.map +1 -0
  148. package/dist/supervisor/protocol.js +29 -0
  149. package/dist/supervisor/protocol.js.map +1 -0
  150. package/dist/supervisor/supervision.d.ts +25 -0
  151. package/dist/supervisor/supervision.d.ts.map +1 -0
  152. package/dist/supervisor/supervision.js +7 -0
  153. package/dist/supervisor/supervision.js.map +1 -0
  154. package/dist/supervisor/worker.d.ts +29 -0
  155. package/dist/supervisor/worker.d.ts.map +1 -0
  156. package/dist/supervisor/worker.js +97 -0
  157. package/dist/supervisor/worker.js.map +1 -0
  158. package/dist/web/errors.d.ts.map +1 -1
  159. package/dist/web/errors.js +5 -3
  160. package/dist/web/errors.js.map +1 -1
  161. package/dist/web/llms.d.ts +8 -0
  162. package/dist/web/llms.d.ts.map +1 -1
  163. package/dist/web/llms.js +25 -7
  164. package/dist/web/llms.js.map +1 -1
  165. package/dist/web/maintenance.d.ts +2 -2
  166. package/dist/web/maintenance.d.ts.map +1 -1
  167. package/dist/web/maintenance.js +7 -4
  168. package/dist/web/maintenance.js.map +1 -1
  169. package/dist/web/negotiate.d.ts +8 -5
  170. package/dist/web/negotiate.d.ts.map +1 -1
  171. package/dist/web/negotiate.js +21 -10
  172. package/dist/web/negotiate.js.map +1 -1
  173. package/dist/web/routes.d.ts.map +1 -1
  174. package/dist/web/routes.js +84 -36
  175. package/dist/web/routes.js.map +1 -1
  176. package/package.json +11 -5
  177. package/dist/comments/conversation.d.ts +0 -29
  178. package/dist/comments/conversation.d.ts.map +0 -1
  179. package/dist/comments/conversation.js +0 -60
  180. package/dist/comments/conversation.js.map +0 -1
  181. package/dist/federation/import-wordpress.d.ts +0 -128
  182. package/dist/federation/import-wordpress.d.ts.map +0 -1
  183. package/dist/federation/import-wordpress.js +0 -426
  184. package/dist/federation/import-wordpress.js.map +0 -1
  185. package/dist/federation/wordpress.d.ts +0 -131
  186. package/dist/federation/wordpress.d.ts.map +0 -1
  187. package/dist/federation/wordpress.js +0 -342
  188. package/dist/federation/wordpress.js.map +0 -1
  189. package/dist/web/comments.d.ts +0 -60
  190. package/dist/web/comments.d.ts.map +0 -1
  191. package/dist/web/comments.js +0 -207
  192. package/dist/web/comments.js.map +0 -1
@@ -1,131 +0,0 @@
1
- import type { InboxContext } from '@fedify/fedify';
2
- import type { User } from '../admin/accounts.ts';
3
- import type { CreateSiteFederationOptions, FederationContextData } from './federation.ts';
4
- import type { SiteFederation } from './federation.ts';
5
- /**
6
- * The WordPress ActivityPub plugin's paths, served behind a switch (TASK-70).
7
- *
8
- * A follower's server delivers to the inbox URL it cached from the actor
9
- * document, and replaces it only when it next refetches the actor. A site that
10
- * moved here from the plugin therefore goes on being delivered to at
11
- * `/wp-json/activitypub/1.0/actors/2/inbox` and `/wp-json/activitypub/1.0/inbox`
12
- * for as long as those caches last, and decision-14 calls that **cache rather
13
- * than identity**: the CMS carries the plugin's layout until the caches have
14
- * moved on, and no longer.
15
- *
16
- * So everything here is behind the `wordpressActivityPub` site setting, and
17
- * everything a peer reads back is the canonical identity. The actor served at
18
- * `…/actors/2` is the same `Person` the author URL serves — same `id`, same
19
- * canonical `inbox` — so a peer that refetches here learns the new endpoints
20
- * rather than being handed the old ones again. That is what eventually makes
21
- * the switch safe to turn off.
22
- *
23
- * A second `Federation` is how Fedify serves a second inbox path at all: one
24
- * object may have exactly one pair of inbox listeners, and `ctx.routeActivity`
25
- * re-verifies from scratch in a way a Mastodon or WordPress `Follow` cannot
26
- * satisfy (doc-8). The second object shares the canonical one's KV store, and
27
- * both sets of listeners are set to `per-origin` idempotence, because the
28
- * default folds the recipient identifier into the key — so the same `Follow`
29
- * redelivered to `andrew` and to `2` would otherwise be handled twice.
30
- */
31
- /** Where the plugin's REST namespace lives, on the site's origin. */
32
- export declare const WORDPRESS_ACTIVITYPUB_BASE = "/wp-json/activitypub/1.0";
33
- /** The plugin's actor path. Its identifier is the WordPress user's number. */
34
- export declare const WORDPRESS_ACTOR_PATH: "/wp-json/activitypub/1.0/actors/{identifier}";
35
- /** The actor's inbox, outbox and two follow collections, as the plugin spells them. */
36
- export declare const WORDPRESS_INBOX_PATH: "/wp-json/activitypub/1.0/actors/{identifier}/inbox";
37
- export declare const WORDPRESS_OUTBOX_PATH: "/wp-json/activitypub/1.0/actors/{identifier}/outbox";
38
- export declare const WORDPRESS_FOLLOWERS_PATH: "/wp-json/activitypub/1.0/actors/{identifier}/followers";
39
- export declare const WORDPRESS_FOLLOWING_PATH: "/wp-json/activitypub/1.0/actors/{identifier}/following";
40
- /** The plugin's instance-wide inbox, which its `sharedInbox` endpoint names. */
41
- export declare const WORDPRESS_SHARED_INBOX_PATH: "/wp-json/activitypub/1.0/inbox";
42
- /** What {@link createWordPressFederation} takes beyond the canonical options. */
43
- export interface CreateWordPressFederationOptions extends CreateSiteFederationOptions {
44
- /**
45
- * The site's own federation, whose contexts every identity here is built
46
- * from.
47
- *
48
- * Fedify derives an actor's id, its key ids and its collection URLs from the
49
- * path it dispatches the actor at, and the paths here are the plugin's. A
50
- * document built off this object's own context would tell a peer that the
51
- * person *is* `…/wp-json/…/actors/2` and sign with a key id under it, which
52
- * is the opposite of what the switch is for.
53
- */
54
- canonical: SiteFederation;
55
- }
56
- /**
57
- * Build the compatibility federation: the plugin's inbox and collections,
58
- * mapped to users by the number WordPress gave them.
59
- *
60
- * Nothing else is registered. WebFinger, NodeInfo and the actor's real
61
- * endpoints belong to the canonical federation and stay there; this object
62
- * exists only so that deliveries and fetches aimed at the old layout land
63
- * somewhere, signature-verified at the real request path exactly as the
64
- * canonical inbox verifies them.
65
- */
66
- export declare function createWordPressFederation(options: CreateWordPressFederationOptions): SiteFederation;
67
- /**
68
- * The user the WordPress ActivityPub plugin numbered `identifier`, or
69
- * `undefined`.
70
- *
71
- * The identifier is a path segment, so it is compared as the decimal the
72
- * plugin writes: `02` and `2.0` name nobody, because neither is a URL the
73
- * plugin ever published.
74
- */
75
- export declare function userByWordPressActorId(dataDir: string, identifier: string): User | undefined;
76
- /** One of the plugin's paths, as the record of what was asked for names it. */
77
- export type WordPressRoute = 'actor' | 'inbox' | 'outbox' | 'followers' | 'following';
78
- /** Which compatibility path a request is for, and whose. */
79
- export interface WordPressRequestTarget {
80
- /** The path it is, or `sharedInbox` for the instance-wide one. */
81
- readonly route: WordPressRoute | 'sharedInbox';
82
- /** The WordPress number in the path, or `undefined` for the shared inbox. */
83
- readonly wordpressActorId?: string | undefined;
84
- }
85
- /**
86
- * The compatibility path a request asks for, or `undefined` for anything else.
87
- *
88
- * Matched here rather than left to Fedify because the record of what was last
89
- * asked for has to be written whether or not a user answers: a delivery to a
90
- * number nobody carries is still a peer holding the old URL, and it is exactly
91
- * the sort of thing the owner watching the screen wants to see. Fedify's
92
- * router still decides what is actually served.
93
- */
94
- export declare function wordPressRequestTarget(pathname: string): WordPressRequestTarget | undefined;
95
- /** The file the instants live in, relative to `dataDir`. */
96
- export declare const WORDPRESS_REQUESTS_FILE = "wordpress-activitypub.json";
97
- /** Where that file lives for a given data directory. */
98
- export declare function wordPressRequestsFile(dataDir: string): string;
99
- /**
100
- * When each compatibility path was last asked for.
101
- *
102
- * A file under `data/` rather than a table, because the database is a cache a
103
- * site may delete (decision-9) and this is the one thing the switch is watched
104
- * by: an owner deciding whether every follower's server has refetched the
105
- * actor should not have that answer reset by a rebuild.
106
- */
107
- export interface WordPressRequests {
108
- /** When the instance-wide inbox was last delivered to, or `undefined`. */
109
- readonly sharedInbox?: string | undefined;
110
- /** When each of a user's own paths was last asked for, by username. */
111
- readonly users: Readonly<Record<string, Readonly<Partial<Record<WordPressRoute, string>>>>>;
112
- }
113
- /** The file as it reads now; a site nobody has asked has nothing to say. */
114
- export declare function readWordPressRequests(dataDir: string): WordPressRequests;
115
- /**
116
- * Record that one compatibility path was just asked for.
117
- *
118
- * Written through {@link updateFileAtomically}, so two deliveries arriving
119
- * together cannot lose one another's instant and a reader never sees a
120
- * half-written file. A path whose number names nobody is not recorded: the
121
- * record is per user, and there is no user to record it against.
122
- */
123
- export declare function recordWordPressRequest(options: {
124
- dataDir: string;
125
- target: WordPressRequestTarget;
126
- username?: string | undefined;
127
- at: Date;
128
- }): Promise<void>;
129
- /** A compatibility context, for the handlers that are handed one. */
130
- export type WordPressInboxContext = InboxContext<FederationContextData>;
131
- //# sourceMappingURL=wordpress.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"wordpress.d.ts","sourceRoot":"","sources":["../../src/federation/wordpress.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAW,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAe5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAKjD,OAAO,KAAK,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAc1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,qEAAqE;AACrE,eAAO,MAAM,0BAA0B,6BAA6B,CAAC;AAErE,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,gDAA+D,CAAC;AAEjG,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,sDAA2C,CAAC;AAC7E,eAAO,MAAM,qBAAqB,uDAA4C,CAAC;AAC/E,eAAO,MAAM,wBAAwB,0DAA+C,CAAC;AACrF,eAAO,MAAM,wBAAwB,0DAA+C,CAAC;AAErF,gFAAgF;AAChF,eAAO,MAAM,2BAA2B,kCAAiD,CAAC;AAE1F,iFAAiF;AACjF,MAAM,WAAW,gCAAiC,SAAQ,2BAA2B;IACnF;;;;;;;;;OASG;IACH,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,gCAAgC,GACxC,cAAc,CAwIhB;AA6BD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAI5F;AAED,+EAA+E;AAC/E,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AAEtF,4DAA4D;AAC5D,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,aAAa,CAAC;IAC/C,6EAA6E;IAC7E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAc3F;AAED,4DAA4D;AAC5D,eAAO,MAAM,uBAAuB,+BAA+B,CAAC;AAEpE,wDAAwD;AACxD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7F;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAaxE;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,EAAE,EAAE,IAAI,CAAC;CACV,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBhB;AAsDD,qEAAqE;AACrE,MAAM,MAAM,qBAAqB,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC"}
@@ -1,342 +0,0 @@
1
- import path from 'node:path';
2
- import { createFederation, InProcessMessageQueue, MemoryKvStore } from '@fedify/fedify';
3
- import { Accept, Announce, Create, Delete, Follow, Like, QuoteRequest, Reject, Undo, } from '@fedify/vocab';
4
- import { listUsers } from '../admin/accounts.js';
5
- import { readFileIfPresentSync, updateFileAtomically } from '../files/atomic.js';
6
- import { authorNames } from '../web/authors.js';
7
- import { userActor } from './actor.js';
8
- import { federationOrigin } from './paths.js';
9
- import { outboxPage, OUTBOX_PAGE_SIZE } from './federation.js';
10
- import { followersPage, lastFollowersCursor } from './followers.js';
11
- import { handleAccept, handleDelete, handleFollow, handleLoggedActivity, handleQuoteRequest, handleReject, handleUndo, } from './inbox.js';
12
- import { loadActorKeyPairs } from './keys.js';
13
- /**
14
- * The WordPress ActivityPub plugin's paths, served behind a switch (TASK-70).
15
- *
16
- * A follower's server delivers to the inbox URL it cached from the actor
17
- * document, and replaces it only when it next refetches the actor. A site that
18
- * moved here from the plugin therefore goes on being delivered to at
19
- * `/wp-json/activitypub/1.0/actors/2/inbox` and `/wp-json/activitypub/1.0/inbox`
20
- * for as long as those caches last, and decision-14 calls that **cache rather
21
- * than identity**: the CMS carries the plugin's layout until the caches have
22
- * moved on, and no longer.
23
- *
24
- * So everything here is behind the `wordpressActivityPub` site setting, and
25
- * everything a peer reads back is the canonical identity. The actor served at
26
- * `…/actors/2` is the same `Person` the author URL serves — same `id`, same
27
- * canonical `inbox` — so a peer that refetches here learns the new endpoints
28
- * rather than being handed the old ones again. That is what eventually makes
29
- * the switch safe to turn off.
30
- *
31
- * A second `Federation` is how Fedify serves a second inbox path at all: one
32
- * object may have exactly one pair of inbox listeners, and `ctx.routeActivity`
33
- * re-verifies from scratch in a way a Mastodon or WordPress `Follow` cannot
34
- * satisfy (doc-8). The second object shares the canonical one's KV store, and
35
- * both sets of listeners are set to `per-origin` idempotence, because the
36
- * default folds the recipient identifier into the key — so the same `Follow`
37
- * redelivered to `andrew` and to `2` would otherwise be handled twice.
38
- */
39
- /** Where the plugin's REST namespace lives, on the site's origin. */
40
- export const WORDPRESS_ACTIVITYPUB_BASE = '/wp-json/activitypub/1.0';
41
- /** The plugin's actor path. Its identifier is the WordPress user's number. */
42
- export const WORDPRESS_ACTOR_PATH = `${WORDPRESS_ACTIVITYPUB_BASE}/actors/{identifier}`;
43
- /** The actor's inbox, outbox and two follow collections, as the plugin spells them. */
44
- export const WORDPRESS_INBOX_PATH = `${WORDPRESS_ACTOR_PATH}/inbox`;
45
- export const WORDPRESS_OUTBOX_PATH = `${WORDPRESS_ACTOR_PATH}/outbox`;
46
- export const WORDPRESS_FOLLOWERS_PATH = `${WORDPRESS_ACTOR_PATH}/followers`;
47
- export const WORDPRESS_FOLLOWING_PATH = `${WORDPRESS_ACTOR_PATH}/following`;
48
- /** The plugin's instance-wide inbox, which its `sharedInbox` endpoint names. */
49
- export const WORDPRESS_SHARED_INBOX_PATH = `${WORDPRESS_ACTIVITYPUB_BASE}/inbox`;
50
- /**
51
- * Build the compatibility federation: the plugin's inbox and collections,
52
- * mapped to users by the number WordPress gave them.
53
- *
54
- * Nothing else is registered. WebFinger, NodeInfo and the actor's real
55
- * endpoints belong to the canonical federation and stay there; this object
56
- * exists only so that deliveries and fetches aimed at the old layout land
57
- * somewhere, signature-verified at the real request path exactly as the
58
- * canonical inbox verifies them.
59
- */
60
- export function createWordPressFederation(options) {
61
- const { canonical } = options;
62
- const federation = createFederation({
63
- kv: options.kv ?? new MemoryKvStore(),
64
- origin: federationOrigin(options.baseUrl),
65
- ...(options.queue === null ? {} : { queue: options.queue ?? new InProcessMessageQueue() }),
66
- ...(options.allowPrivateAddress === undefined
67
- ? {}
68
- : { allowPrivateAddress: options.allowPrivateAddress }),
69
- ...options.federationOptions,
70
- });
71
- /** The user a dispatcher's identifier names, which here is a number. */
72
- function actorFor(context, identifier) {
73
- return userByWordPressActorId(context.data.config.dataDir, identifier);
74
- }
75
- /**
76
- * A context of the canonical federation over the same data.
77
- *
78
- * Every id a peer reads or verifies comes from here rather than from the
79
- * compatibility context, so the actor, the key ids, the `Accept` and the
80
- * collections a peer is pointed at are the ones the site really publishes.
81
- */
82
- function identity(context) {
83
- return canonical.createContext(new URL(context.origin), context.data);
84
- }
85
- /** Every stored `author` string that reads as this user (TASK-67). */
86
- function namesOf(context, user) {
87
- return authorNames(listUsers(context.data.config.dataDir), user);
88
- }
89
- federation
90
- .setActorDispatcher(WORDPRESS_ACTOR_PATH, async (context, identifier) => {
91
- const user = actorFor(context, identifier);
92
- if (user === undefined)
93
- return null;
94
- // The canonical document, verbatim: a peer refetching the actor at the
95
- // old URL is exactly the peer that should learn the new inbox.
96
- return await userActor(identity(context), user, { baseUrl: context.data.config.baseUrl });
97
- })
98
- .setKeyPairsDispatcher(async (context, identifier) => {
99
- const user = actorFor(context, identifier);
100
- return user === undefined
101
- ? []
102
- : await loadActorKeyPairs(context.data.config.dataDir, user.username);
103
- });
104
- federation
105
- .setOutboxDispatcher(WORDPRESS_OUTBOX_PATH, (context, identifier, cursor) => {
106
- const user = actorFor(context, identifier);
107
- return user === undefined
108
- ? null
109
- : outboxPage(identity(context), namesOf(context, user), cursor);
110
- })
111
- .setCounter((context, identifier) => {
112
- const user = actorFor(context, identifier);
113
- return user === undefined ? null : context.data.store.countByAuthor(namesOf(context, user));
114
- })
115
- .setFirstCursor((context, identifier) => actorFor(context, identifier) === undefined ? null : '0')
116
- .setLastCursor((context, identifier) => {
117
- const user = actorFor(context, identifier);
118
- if (user === undefined)
119
- return null;
120
- const total = context.data.store.countByAuthor(namesOf(context, user));
121
- return String(total === 0 ? 0 : Math.floor((total - 1) / OUTBOX_PAGE_SIZE) * OUTBOX_PAGE_SIZE);
122
- });
123
- federation
124
- .setFollowersDispatcher(WORDPRESS_FOLLOWERS_PATH, (context, identifier, cursor) => {
125
- const user = actorFor(context, identifier);
126
- return user === undefined ? null : followersPage(context, user.username, cursor);
127
- })
128
- .setCounter((context, identifier) => {
129
- const user = actorFor(context, identifier);
130
- return user === undefined ? null : context.data.admin.countFollowers(user.username);
131
- })
132
- .setFirstCursor((context, identifier) => actorFor(context, identifier) === undefined ? null : '0')
133
- .setLastCursor((context, identifier) => {
134
- const user = actorFor(context, identifier);
135
- return user === undefined
136
- ? null
137
- : lastFollowersCursor(context.data.admin.countFollowers(user.username));
138
- });
139
- federation.setFollowingDispatcher(WORDPRESS_FOLLOWING_PATH, (context, identifier) => actorFor(context, identifier) === undefined ? null : { items: [] });
140
- // The same handlers the canonical inbox runs, over a canonical context.
141
- // `per-origin` idempotence is what keeps one `Follow` redelivered to both
142
- // inboxes from being handled twice (doc-8); the canonical federation is set
143
- // the same way, and they share a KV store so the two agree about what they
144
- // have already seen.
145
- federation
146
- .setInboxListeners(WORDPRESS_INBOX_PATH, WORDPRESS_SHARED_INBOX_PATH)
147
- .withIdempotency('per-origin')
148
- .on(Follow, canonically(handleFollow))
149
- .on(Accept, canonically(handleAccept))
150
- .on(Reject, canonically(handleReject))
151
- .on(Undo, canonically(handleUndo))
152
- .on(Delete, canonically(handleDelete))
153
- .on(Like, canonically(handleLoggedActivity))
154
- .on(Announce, canonically(handleLoggedActivity))
155
- .on(Create, canonically(handleLoggedActivity))
156
- .on(QuoteRequest, canonically(handleQuoteRequest));
157
- /**
158
- * One inbox handler, run against the canonical federation.
159
- *
160
- * Two things are swapped. The context is the canonical one, so `parseUri`
161
- * recognises the site's own actor URLs, the `Accept` is signed with the key
162
- * the actor document publishes and sent from the id its followers hold. And
163
- * `recipient` — which arrives here as the WordPress number, straight off the
164
- * path — is the username, because that is what every handler and the inbox
165
- * log mean by a recipient.
166
- */
167
- function canonically(handle) {
168
- return async (context, activity) => {
169
- const user = context.recipient === null ? undefined : actorFor(context, context.recipient);
170
- await handle(withRecipient(identity(context), context, user?.username ?? null), activity);
171
- };
172
- }
173
- return federation;
174
- }
175
- /**
176
- * A canonical context wearing one inbox delivery's recipient.
177
- *
178
- * A `Proxy` rather than a copy: Fedify's context is a class with private state,
179
- * so its methods have to keep running against the object they were made for.
180
- * Everything but `recipient` and `forwardActivity` is the canonical context's
181
- * own.
182
- */
183
- function withRecipient(canonical, delivered, recipient) {
184
- return new Proxy(canonical, {
185
- get(target, property) {
186
- if (property === 'recipient')
187
- return recipient;
188
- // Forwarding is the one thing only the delivering context can do: it
189
- // holds the bytes that arrived, signatures and all.
190
- if (property === 'forwardActivity')
191
- return delivered.forwardActivity.bind(delivered);
192
- const value = Reflect.get(target, property, target);
193
- return typeof value === 'function'
194
- ? value.bind(target)
195
- : value;
196
- },
197
- });
198
- }
199
- /**
200
- * The user the WordPress ActivityPub plugin numbered `identifier`, or
201
- * `undefined`.
202
- *
203
- * The identifier is a path segment, so it is compared as the decimal the
204
- * plugin writes: `02` and `2.0` name nobody, because neither is a URL the
205
- * plugin ever published.
206
- */
207
- export function userByWordPressActorId(dataDir, identifier) {
208
- if (!/^[1-9][0-9]*$/.test(identifier))
209
- return undefined;
210
- const wanted = Number(identifier);
211
- return listUsers(dataDir).find((user) => user.wordpressActorId === wanted);
212
- }
213
- /**
214
- * The compatibility path a request asks for, or `undefined` for anything else.
215
- *
216
- * Matched here rather than left to Fedify because the record of what was last
217
- * asked for has to be written whether or not a user answers: a delivery to a
218
- * number nobody carries is still a peer holding the old URL, and it is exactly
219
- * the sort of thing the owner watching the screen wants to see. Fedify's
220
- * router still decides what is actually served.
221
- */
222
- export function wordPressRequestTarget(pathname) {
223
- if (pathname === WORDPRESS_SHARED_INBOX_PATH)
224
- return { route: 'sharedInbox' };
225
- const under = `${WORDPRESS_ACTIVITYPUB_BASE}/actors/`;
226
- if (!pathname.startsWith(under))
227
- return undefined;
228
- const [number, name, ...rest] = pathname.slice(under.length).split('/');
229
- if (number === undefined || number === '' || rest.length > 0)
230
- return undefined;
231
- if (name === undefined || name === '')
232
- return { route: 'actor', wordpressActorId: number };
233
- if (name === 'inbox' || name === 'outbox' || name === 'followers' || name === 'following') {
234
- return { route: name, wordpressActorId: number };
235
- }
236
- return undefined;
237
- }
238
- /** The file the instants live in, relative to `dataDir`. */
239
- export const WORDPRESS_REQUESTS_FILE = 'wordpress-activitypub.json';
240
- /** Where that file lives for a given data directory. */
241
- export function wordPressRequestsFile(dataDir) {
242
- return path.join(dataDir, WORDPRESS_REQUESTS_FILE);
243
- }
244
- /** The file as it reads now; a site nobody has asked has nothing to say. */
245
- export function readWordPressRequests(dataDir) {
246
- const source = readFileIfPresentSync(wordPressRequestsFile(dataDir));
247
- if (source === undefined)
248
- return { users: {} };
249
- let parsed;
250
- try {
251
- parsed = JSON.parse(source);
252
- }
253
- catch {
254
- // A record of who asked for what is not worth failing a request over, and
255
- // "never" is the honest answer when the file cannot be read.
256
- return { users: {} };
257
- }
258
- return requestsFrom(parsed);
259
- }
260
- /**
261
- * Record that one compatibility path was just asked for.
262
- *
263
- * Written through {@link updateFileAtomically}, so two deliveries arriving
264
- * together cannot lose one another's instant and a reader never sees a
265
- * half-written file. A path whose number names nobody is not recorded: the
266
- * record is per user, and there is no user to record it against.
267
- */
268
- export async function recordWordPressRequest(options) {
269
- const { target, username } = options;
270
- if (target.route !== 'sharedInbox' && username === undefined)
271
- return;
272
- const instant = options.at.toISOString();
273
- const file = wordPressRequestsFile(options.dataDir);
274
- await updateFileAtomically(file, (current) => {
275
- const requests = requestsFrom(parseOrEmpty(current));
276
- const next = target.route === 'sharedInbox'
277
- ? { ...requests, sharedInbox: instant }
278
- : {
279
- ...requests,
280
- users: {
281
- ...requests.users,
282
- [username ?? '']: {
283
- ...requests.users[username ?? ''],
284
- [target.route]: instant,
285
- },
286
- },
287
- };
288
- return `${JSON.stringify(next, null, 2)}\n`;
289
- });
290
- }
291
- /** The bytes of the file as whatever JSON they hold, or nothing usable. */
292
- function parseOrEmpty(source) {
293
- if (source === undefined)
294
- return undefined;
295
- try {
296
- return JSON.parse(source);
297
- }
298
- catch {
299
- return undefined;
300
- }
301
- }
302
- /**
303
- * Whatever the file held as the instants this version understands.
304
- *
305
- * Read key by key and dropped rather than refused, on the rule the users file
306
- * and `site.json` are read by: this is a record of what happened, and a
307
- * damaged one should cost the screen a column, not the site a boot.
308
- */
309
- function requestsFrom(value) {
310
- if (typeof value !== 'object' || value === null || Array.isArray(value))
311
- return { users: {} };
312
- const record = value;
313
- const users = {};
314
- const listed = record['users'];
315
- if (typeof listed === 'object' && listed !== null && !Array.isArray(listed)) {
316
- for (const [username, routes] of Object.entries(listed)) {
317
- if (typeof routes !== 'object' || routes === null || Array.isArray(routes))
318
- continue;
319
- const kept = {};
320
- for (const [name, instant] of Object.entries(routes)) {
321
- if (typeof instant === 'string' && isWordPressRoute(name))
322
- kept[name] = instant;
323
- }
324
- if (Object.keys(kept).length > 0)
325
- users[username] = kept;
326
- }
327
- }
328
- const shared = record['sharedInbox'];
329
- return {
330
- ...(typeof shared === 'string' ? { sharedInbox: shared } : {}),
331
- users,
332
- };
333
- }
334
- /** Whether a stored key names a path this version serves. */
335
- function isWordPressRoute(name) {
336
- return (name === 'actor' ||
337
- name === 'inbox' ||
338
- name === 'outbox' ||
339
- name === 'followers' ||
340
- name === 'following');
341
- }
342
- //# sourceMappingURL=wordpress.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"wordpress.js","sourceRoot":"","sources":["../../src/federation/wordpress.ts"],"names":[],"mappings":"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"}
@@ -1,60 +0,0 @@
1
- import type { AdminStore } from '../admin/store.ts';
2
- import type { Document } from '../content/document.ts';
3
- import type { ContentStore } from '../content/store.ts';
4
- import type { FeedComment } from './feeds.ts';
5
- /**
6
- * The comments a site has, in the two shapes it has them: replies its inbox
7
- * was sent, and comments people left on the page itself.
8
- *
9
- * A fediverse reply names the post it answers by the post's ActivityStreams
10
- * object id, and lives in the inbox log; a native comment names the post by
11
- * its slug and lives in `content/_data/comments/` (TASK-50). This module is
12
- * where the two meet for the feeds: a reader subscribed to a post's comments
13
- * should hear about an answer whichever door it came in by, and
14
- * `source:comments` should count what a reader would actually see.
15
- *
16
- * Only approved native comments are here. One waiting for a moderator and one
17
- * filed as spam are both stored and neither is anything to publish.
18
- */
19
- /** One comment, with the post it answers. */
20
- export interface Comment extends FeedComment {
21
- /** The post it answers, which the site-wide feed names on every item. */
22
- post: Document;
23
- }
24
- /** What the comment queries need to reach: both indexes and the site's origin. */
25
- export interface CommentContext {
26
- /** Where the replies are logged. */
27
- admin: AdminStore;
28
- /** Where the posts are. */
29
- store: ContentStore;
30
- /** The site's public origin, for the object ids. */
31
- baseUrl: string;
32
- }
33
- /**
34
- * One post's comments, newest first, from both sources.
35
- *
36
- * A post that has never been delivered has no object id and so can have no
37
- * fediverse replies — nothing out there has a name for it yet — but it can
38
- * still have native ones, which are found by its slug.
39
- */
40
- export declare function postComments(context: CommentContext, document: Document, limit: number): FeedComment[];
41
- /**
42
- * Every post's comments, newest first, each with the post it answers.
43
- *
44
- * One about a post that has since been unpublished or trashed is left out,
45
- * because the post it is about is not there to read: the feed would be showing
46
- * a conversation about nothing. That filtering happens after the database has
47
- * paged, so the pages are read until enough survive.
48
- */
49
- export declare function siteComments(context: CommentContext, limit: number): Comment[];
50
- /**
51
- * How many comments each of these documents has, by permalink: the fediverse
52
- * replies and the approved native ones together, which is what a reader
53
- * following `source:comments` would actually find on the page.
54
- *
55
- * Resolved rather than counted on demand, because the number goes into the
56
- * feed's own validator: a feed whose comment counts moved is a changed feed,
57
- * and a reader holding the old one should be told so.
58
- */
59
- export declare function commentCounts(context: CommentContext, documents: readonly Document[]): Map<string, number>;
60
- //# sourceMappingURL=comments.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/web/comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,mBAAmB,CAAC;AAEjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C;;;;;;;;;;;;;GAaG;AAEH,6CAA6C;AAC7C,MAAM,WAAW,OAAQ,SAAQ,WAAW;IAC1C,yEAAyE;IACzE,IAAI,EAAE,QAAQ,CAAC;CAChB;AAYD,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,KAAK,EAAE,UAAU,CAAC;IAClB,2BAA2B;IAC3B,KAAK,EAAE,YAAY,CAAC;IACpB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GACZ,WAAW,EAAE,CAYf;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAkC9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAWrB"}