@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,60 +0,0 @@
1
- /**
2
- * Native comments as the conversation sees them.
3
- *
4
- * `web/conversation.ts` deliberately knows nothing about where an answer came
5
- * from, so this is the translation: a stored comment becomes an
6
- * {@link Interaction} and joins the fediverse replies in the same thread,
7
- * nested by the same rule. Nothing about a comment survives the trip that a
8
- * theme would have to special-case — the source is on the record so a theme
9
- * *may*, not so it must.
10
- *
11
- * Only approved comments come through. A comment waiting for a moderator and
12
- * one filed as spam are both in the file and both in the index, and neither is
13
- * anything a reader should be shown.
14
- */
15
- /** The approved comments on a post, as interactions, oldest first. */
16
- export function commentInteractions(admin, document) {
17
- return admin
18
- .listCommentsFor(document.slug)
19
- .filter((comment) => comment.status === 'approved')
20
- .map((comment) => interactionOf(comment, document));
21
- }
22
- /** One stored comment as the thread's own shape. */
23
- export function interactionOf(comment, document) {
24
- return {
25
- id: comment.id,
26
- source: comment.source,
27
- kind: comment.kind,
28
- author: {
29
- name: comment.author.name,
30
- // A commenter has no fediverse identity: they gave a name, maybe a
31
- // website, and nothing that names them anywhere else.
32
- handle: null,
33
- url: comment.author.url,
34
- // Only a webmention has one: it came out of the source page's `h-card`,
35
- // and a form asks nobody for a picture.
36
- avatar: comment.author.avatar,
37
- actorId: null,
38
- },
39
- // Where it can be read. A comment written here lives here, at its own
40
- // anchor; a webmention lives on the page it was sent from, and its `url`
41
- // says so. A theme that prints `url` for a fediverse reply prints a
42
- // working link for either.
43
- url: comment.url ?? `${document.permalink}#${commentAnchor(comment.id)}`,
44
- content: comment.content.html,
45
- published: new Date(comment.submitted),
46
- inReplyTo: comment.inReplyTo,
47
- status: comment.status,
48
- replies: [],
49
- };
50
- }
51
- /**
52
- * The `id` a comment is anchored at on the page.
53
- *
54
- * Prefixed rather than bare so a comment can never collide with a heading
55
- * anchor the post's own Markdown produced.
56
- */
57
- export function commentAnchor(id) {
58
- return `comment-${id}`;
59
- }
60
- //# sourceMappingURL=conversation.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/comments/conversation.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AAEH,sEAAsE;AACtE,MAAM,UAAU,mBAAmB,CAAC,KAAiB,EAAE,QAAkB;IACvE,OAAO,KAAK;SACT,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC;SAC9B,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC;SAClD,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,aAAa,CAAC,OAAoB,EAAE,QAAkB;IACpE,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE;YACN,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;YACzB,mEAAmE;YACnE,sDAAsD;YACtD,MAAM,EAAE,IAAI;YACZ,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG;YACvB,wEAAwE;YACxE,wCAAwC;YACxC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM;YAC7B,OAAO,EAAE,IAAI;SACd;QACD,sEAAsE;QACtE,yEAAyE;QACzE,oEAAoE;QACpE,2BAA2B;QAC3B,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,QAAQ,CAAC,SAAS,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;QACxE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI;QAC7B,SAAS,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACtC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,OAAO,WAAW,EAAE,EAAE,CAAC;AACzB,CAAC"}
@@ -1,128 +0,0 @@
1
- import type { AdminStore } from '../admin/store.ts';
2
- import type { ActorKeyAlgorithm } from './keys.ts';
3
- /** What {@link importWordPressActor} is given. */
4
- export interface ImportWordPressActorOptions {
5
- /** The index the followers are written into as the files are written. */
6
- readonly admin: AdminStore;
7
- /** Where `users.json` and `keys/` live. */
8
- readonly dataDir: string;
9
- /** Where `_data/federation/{username}/followers.json` lives. */
10
- readonly contentDir: string;
11
- /** Which account on this site the plugin's actor becomes. */
12
- readonly username: string;
13
- /**
14
- * The actor's id exactly as WordPress published it, query string and all:
15
- * `https://example.com/?author=2`.
16
- */
17
- readonly actorId: string;
18
- /** The WordPress user id, which is the number in every one of its paths. */
19
- readonly wordpressActorId: number;
20
- /** The private key as PEM, PKCS#1 or PKCS#8, as `wp` printed it. */
21
- readonly privateKeyPem: string;
22
- /**
23
- * The public key as PEM, when the export carried one.
24
- *
25
- * Checked against the private half rather than stored: the public key is
26
- * derivable, so the only thing a second file can tell us is whether the two
27
- * halves of the export belong together — and a pair that does not is an
28
- * export somebody got wrong, which is worth finding out now rather than
29
- * after the DNS has moved.
30
- */
31
- readonly publicKeyPem?: string | undefined;
32
- /**
33
- * Where the followers come from.
34
- *
35
- * Left off, the plugin's public followers collection on the actor id's own
36
- * origin, which is the whole point of running this before the DNS moves. A
37
- * URL asks a different one; a path reads a saved collection out of a file,
38
- * for a site that is already gone; `false` skips the step, for a run that is
39
- * only there to put the key and the ids in place.
40
- */
41
- readonly followers?: string | false | undefined;
42
- /** Replace a key pair this user already has. Refused without it. */
43
- readonly force?: boolean | undefined;
44
- }
45
- /** What happened to one key file. */
46
- export interface ImportedKey {
47
- /** The file, absolute. */
48
- readonly file: string;
49
- /** Which pair it holds. */
50
- readonly algorithm: ActorKeyAlgorithm;
51
- /** What the run did with it. */
52
- readonly state: 'imported' | 'generated' | 'unchanged' | 'replaced';
53
- }
54
- /** One follower the collection named and nothing could be made of. */
55
- export interface UnreachableFollower {
56
- /** The actor the collection named. */
57
- readonly actor: string;
58
- /** Why it was skipped, for the report. */
59
- readonly reason: string;
60
- }
61
- /** What the followers step did. */
62
- export interface FollowersImportReport {
63
- /** The collection it read, or `undefined` when the step was skipped. */
64
- readonly source: string | undefined;
65
- /** Actors written into the file for the first time. */
66
- readonly added: readonly string[];
67
- /** Actors the file already held, with nothing about them changed. */
68
- readonly unchanged: readonly string[];
69
- /** Actors whose profile the file held and this run refreshed. */
70
- readonly refreshed: readonly string[];
71
- /**
72
- * Actors that could not be turned into followers, and why.
73
- *
74
- * Reported rather than fatal: an instance that is down, or an account that
75
- * has been deleted, must not stop the other hundred followers from coming
76
- * across, and the owner can run the command again once it is back.
77
- */
78
- readonly failed: readonly UnreachableFollower[];
79
- }
80
- /** What the run did. */
81
- export interface ImportWordPressActorReport {
82
- /** Who was imported. */
83
- readonly username: string;
84
- /** The id their followers hold. */
85
- readonly actorId: string;
86
- /** The number the plugin's paths are built from. */
87
- readonly wordpressActorId: number;
88
- /**
89
- * Whether the two ids had to be written, or the record already said so.
90
- */
91
- readonly identity: 'set' | 'unchanged';
92
- /** One entry per key file, RSA first, as the key loader orders them. */
93
- readonly keys: readonly ImportedKey[];
94
- /** What came of the followers collection. */
95
- readonly followers: FollowersImportReport;
96
- /** Whether anything on disk is different because of this run. */
97
- readonly changed: boolean;
98
- }
99
- /**
100
- * Thrown when the user already has a key pair and this run was not told to
101
- * replace it.
102
- *
103
- * Its own class because the answer is a flag rather than a correction, and
104
- * because losing a private key is the one mistake this CMS cannot help with:
105
- * every follower's server has cached the public half, and a new key means none
106
- * of them can verify this person again.
107
- */
108
- export declare class ExistingKeyPairError extends Error {
109
- readonly name = "ExistingKeyPairError";
110
- /** The file that is already there. */
111
- readonly file: string;
112
- constructor(username: string, file: string);
113
- }
114
- /** Thrown when the PEM is not an RSA private key at all. */
115
- export declare class UnusableKeyPemError extends Error {
116
- readonly name = "UnusableKeyPemError";
117
- constructor(message: string, options?: {
118
- cause?: unknown;
119
- });
120
- }
121
- /**
122
- * Bring one WordPress actor's key pair across, and say what changed.
123
- *
124
- * Run twice with the same inputs it changes nothing and says so, which is what
125
- * makes it safe to run again after a follower's server was down.
126
- */
127
- export declare function importWordPressActor(options: ImportWordPressActorOptions): Promise<ImportWordPressActorReport>;
128
- //# sourceMappingURL=import-wordpress.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"import-wordpress.d.ts","sourceRoot":"","sources":["../../src/federation/import-wordpress.ts"],"names":[],"mappings":"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"}
@@ -1,426 +0,0 @@
1
- import { createPrivateKey, createPublicKey } from 'node:crypto';
2
- import { exportJwk } from '@fedify/fedify';
3
- import { setUserWordPressActor } from '../admin/accounts.js';
4
- import { readFileIfPresentSync } from '../files/atomic.js';
5
- import { actorKeyFile, loadActorKeyPairs, writeActorKeyFile } from './keys.js';
6
- import { addFollower, readFollowers } from './records.js';
7
- import { actorHandle, uriOf } from './replies.js';
8
- /**
9
- * Bringing one person across from the WordPress ActivityPub plugin
10
- * (decision-14, TASK-71).
11
- *
12
- * Three things a site leaving the plugin cannot regenerate, and this is where
13
- * they arrive: the RSA key pair its followers have cached, the actor id its
14
- * followers key the account by, and the followers themselves. Everything else
15
- * about the move — the posts, their ids, the compatibility paths — comes in
16
- * with the content or is a setting.
17
- *
18
- * Nothing here is a screen, on purpose. A stored actor id is identity for the
19
- * life of the account (doc-4), and identity is not something a form should be
20
- * able to retype.
21
- */
22
- /** The algorithm the plugin signs with, and the only one it has a key for. */
23
- const RSA = 'RSASSA-PKCS1-v1_5';
24
- /**
25
- * Thrown when the user already has a key pair and this run was not told to
26
- * replace it.
27
- *
28
- * Its own class because the answer is a flag rather than a correction, and
29
- * because losing a private key is the one mistake this CMS cannot help with:
30
- * every follower's server has cached the public half, and a new key means none
31
- * of them can verify this person again.
32
- */
33
- export class ExistingKeyPairError extends Error {
34
- name = 'ExistingKeyPairError';
35
- /** The file that is already there. */
36
- file;
37
- constructor(username, file) {
38
- super(`${username} already has a different key pair, in ${file}. Importing over it would ` +
39
- 'change their identity: every follower has cached the public half of the key that is ' +
40
- 'there, and none of them could verify this person again. Pass --force only if you are ' +
41
- 'sure the pair being imported is the one the followers hold.');
42
- this.file = file;
43
- }
44
- }
45
- /** Thrown when the PEM is not an RSA private key at all. */
46
- export class UnusableKeyPemError extends Error {
47
- name = 'UnusableKeyPemError';
48
- constructor(message, options) {
49
- super(message, options);
50
- }
51
- }
52
- /**
53
- * Bring one WordPress actor's key pair across, and say what changed.
54
- *
55
- * Run twice with the same inputs it changes nothing and says so, which is what
56
- * makes it safe to run again after a follower's server was down.
57
- */
58
- export async function importWordPressActor(options) {
59
- const { dataDir, username } = options;
60
- // Everything that can be refused is decided before anything is written, so a
61
- // run that is going to stop stops with the disk as it was.
62
- const planned = await planKeyImport(options);
63
- const identity = (await setUserWordPressActor({
64
- dataDir,
65
- username,
66
- actorId: options.actorId,
67
- wordpressActorId: options.wordpressActorId,
68
- }))
69
- ? 'set'
70
- : 'unchanged';
71
- const keys = [applyKeyImport(dataDir, username, planned)];
72
- // The Ed25519 pair is minted here rather than left to the first request,
73
- // because this is also what proves the file just written imports: Fedify
74
- // swallows what a key pairs dispatcher throws, so an unusable key found at
75
- // request time would be found by nobody.
76
- const ed25519File = actorKeyFile(dataDir, username, 'Ed25519');
77
- const hadEd25519 = readFileIfPresentSync(ed25519File) !== undefined;
78
- await loadActorKeyPairs(dataDir, username);
79
- keys.push({
80
- file: ed25519File,
81
- algorithm: 'Ed25519',
82
- state: hadEd25519 ? 'unchanged' : 'generated',
83
- });
84
- const followers = await importFollowers(options);
85
- return {
86
- username,
87
- actorId: options.actorId,
88
- wordpressActorId: options.wordpressActorId,
89
- identity,
90
- keys,
91
- followers,
92
- changed: identity === 'set' ||
93
- keys.some((key) => key.state !== 'unchanged') ||
94
- followers.added.length > 0 ||
95
- followers.refreshed.length > 0,
96
- };
97
- }
98
- /**
99
- * Bring the followers across: read the collection, work out who each item is,
100
- * and put them in the user's `followers.json` and the index.
101
- *
102
- * The plugin's collection carries nothing but a `first` page, and the page's
103
- * `orderedItems` are bare actor URLs, so almost every follower here costs one
104
- * fetch. That is the one step of the import that is really a network
105
- * operation, which is why it can be pointed at a file instead: an owner who
106
- * saved the collection before the DNS moved has everything they need offline.
107
- *
108
- * {@link addFollower} is what writes, so a follower already in the file keeps
109
- * the place and the follow time it has — running this again after an instance
110
- * came back adds the missing people and moves nobody.
111
- */
112
- async function importFollowers(options) {
113
- const empty = {
114
- source: undefined,
115
- added: [],
116
- unchanged: [],
117
- refreshed: [],
118
- failed: [],
119
- };
120
- if (options.followers === false)
121
- return empty;
122
- const { admin, contentDir, username } = options;
123
- const source = options.followers ?? followersCollectionUrl(options);
124
- const added = [];
125
- const unchanged = [];
126
- const refreshed = [];
127
- const failed = [];
128
- for (const item of await collectionItems(source)) {
129
- const actor = uriOf(item) ?? String(item);
130
- try {
131
- const document = isRecord(item) && uriOf(item['inbox']) !== null ? item : await fetchAs(actor);
132
- const follower = followerFrom(document);
133
- const held = readFollowers(contentDir, username).find((entry) => entry.actorId === follower.actorId);
134
- await addFollower({ admin, contentDir }, username, follower);
135
- if (held === undefined)
136
- added.push(follower.actorId);
137
- else if (sameFollower(held, follower))
138
- unchanged.push(follower.actorId);
139
- else
140
- refreshed.push(follower.actorId);
141
- }
142
- catch (error) {
143
- failed.push({ actor, reason: messageOf(error) });
144
- }
145
- }
146
- return { source, added, unchanged, refreshed, failed };
147
- }
148
- /**
149
- * The plugin's public followers collection for this actor.
150
- *
151
- * Built from the stored actor id's own origin rather than from anything the
152
- * command was told separately: the id is the one URL an owner is certain to
153
- * have right, and it is the site the followers are still pointed at.
154
- */
155
- function followersCollectionUrl(options) {
156
- const number = String(options.wordpressActorId);
157
- return new URL(`/wp-json/activitypub/1.0/actors/${number}/followers`, options.actorId).href;
158
- }
159
- /**
160
- * Everything a followers collection names, in the order it names them.
161
- *
162
- * A collection with no items of its own is followed through `first`, and a
163
- * page through `next`, until there is nowhere left to go; either may be a URL
164
- * to fetch or a page already embedded in the document. URLs already visited
165
- * are not visited again, because a server whose `next` points back at itself
166
- * would otherwise be an endless import.
167
- */
168
- async function collectionItems(source) {
169
- const items = [];
170
- const seen = new Set();
171
- let document = isUrl(source) ? await fetchAs(source) : readJsonFile(source);
172
- if (isUrl(source))
173
- seen.add(source);
174
- while (isRecord(document)) {
175
- const listed = document['orderedItems'] ?? document['items'];
176
- const here = Array.isArray(listed) ? listed : undefined;
177
- if (here !== undefined)
178
- items.push(...here);
179
- // `next` on a page; `first` only when this document listed nothing itself,
180
- // which is the plugin's outer collection.
181
- const onward = document['next'] ?? (here === undefined ? document['first'] : undefined);
182
- if (onward === undefined || onward === null)
183
- break;
184
- if (isRecord(onward)) {
185
- document = onward;
186
- continue;
187
- }
188
- const url = uriOf(onward);
189
- if (url === null || seen.has(url))
190
- break;
191
- seen.add(url);
192
- document = await fetchAs(url);
193
- }
194
- return items;
195
- }
196
- /** A saved collection, as the JSON it holds. */
197
- function readJsonFile(file) {
198
- const source = readFileIfPresentSync(file);
199
- if (source === undefined) {
200
- throw new Error(`There is no followers collection at ${file}.`);
201
- }
202
- try {
203
- return JSON.parse(source);
204
- }
205
- catch (error) {
206
- throw new Error(`${file} could not be read as JSON: ${messageOf(error)}`, { cause: error });
207
- }
208
- }
209
- /** Whether a `--followers` value is a URL to fetch rather than a file to read. */
210
- function isUrl(source) {
211
- try {
212
- const url = new URL(source);
213
- return url.protocol === 'http:' || url.protocol === 'https:';
214
- }
215
- catch {
216
- return false;
217
- }
218
- }
219
- /** What a peer should ask a fediverse server for. */
220
- const ACTIVITY_STREAMS_ACCEPT = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
221
- /** One ActivityStreams document, or a thrown error naming the URL and the status. */
222
- async function fetchAs(url) {
223
- let response;
224
- try {
225
- response = await fetch(url, { headers: { accept: ACTIVITY_STREAMS_ACCEPT } });
226
- }
227
- catch (error) {
228
- throw new Error(`${url} could not be fetched: ${messageOf(error)}`, { cause: error });
229
- }
230
- if (!response.ok) {
231
- throw new Error(`${url} answered ${String(response.status)} ${response.statusText}`.trim());
232
- }
233
- try {
234
- return await response.json();
235
- }
236
- catch (error) {
237
- throw new Error(`${url} did not answer with JSON: ${messageOf(error)}`, { cause: error });
238
- }
239
- }
240
- /**
241
- * One actor document as the follower it stands for.
242
- *
243
- * Read straight out of the JSON rather than through Fedify's vocabulary,
244
- * because this is the one place in the CMS that has a plain document and no
245
- * federation context to dereference with. The six fields are the ones
246
- * `followers.json` holds (doc-4): two to deliver with, four to show.
247
- */
248
- function followerFrom(document) {
249
- if (!isRecord(document))
250
- throw new Error('that is not an actor document');
251
- const actorId = uriOf(document['id']);
252
- const inboxId = uriOf(document['inbox']);
253
- if (actorId === null)
254
- throw new Error('that actor document has no id');
255
- if (inboxId === null)
256
- throw new Error(`${actorId} publishes no inbox to deliver to`);
257
- const endpoints = document['endpoints'];
258
- const icon = document['icon'];
259
- return {
260
- actorId,
261
- inboxId,
262
- sharedInboxId: isRecord(endpoints) ? uriOf(endpoints['sharedInbox']) : null,
263
- handle: handleOf(document, actorId),
264
- name: textOf(document['name']) ?? null,
265
- iconUrl: uriOf(isRecord(icon) ? icon['url'] : icon),
266
- url: uriOf(document['url']),
267
- };
268
- }
269
- /**
270
- * `@name@host` for an actor, from its `preferredUsername` where it has one.
271
- *
272
- * Falling back to {@link actorHandle}, which guesses from the URL, for the
273
- * server that publishes no `preferredUsername` at all.
274
- */
275
- function handleOf(document, actorId) {
276
- const preferred = textOf(document['preferredUsername']);
277
- if (preferred === undefined || preferred === '')
278
- return actorHandle(actorId) ?? null;
279
- try {
280
- return `@${preferred.replace(/^@/, '')}@${new URL(actorId).host}`;
281
- }
282
- catch {
283
- return null;
284
- }
285
- }
286
- /** Whether a stored follower already says everything this one does. */
287
- function sameFollower(held, incoming) {
288
- return (held.inboxId === incoming.inboxId &&
289
- held.sharedInboxId === incoming.sharedInboxId &&
290
- held.handle === incoming.handle &&
291
- held.name === incoming.name &&
292
- held.iconUrl === incoming.iconUrl &&
293
- held.url === incoming.url);
294
- }
295
- /** A JSON-LD string value, which may be wrapped in a language map. */
296
- function textOf(value) {
297
- if (Array.isArray(value)) {
298
- for (const item of value) {
299
- const found = textOf(item);
300
- if (found !== undefined)
301
- return found;
302
- }
303
- return undefined;
304
- }
305
- if (isRecord(value))
306
- return textOf(value['@value']);
307
- return typeof value === 'string' && value !== '' ? value : undefined;
308
- }
309
- function isRecord(value) {
310
- return typeof value === 'object' && value !== null && !Array.isArray(value);
311
- }
312
- /**
313
- * Work out what the exported PEM means for
314
- * `data/keys/{username}.rsassa-pkcs1-v1_5.jwk`, without touching it.
315
- *
316
- * A file already holding this very key is left alone and reported unchanged —
317
- * that is what makes the whole command idempotent — and one holding a
318
- * different key is refused unless the run was told to replace it.
319
- */
320
- async function planKeyImport(options) {
321
- const { dataDir, username } = options;
322
- const privateKey = privateKeyFrom(options.privateKeyPem);
323
- assertPairMatches(privateKey, options.publicKeyPem);
324
- const jwk = `${JSON.stringify(await privateJwkOf(privateKey), undefined, 2)}\n`;
325
- const file = actorKeyFile(dataDir, username, RSA);
326
- const held = readFileIfPresentSync(file);
327
- if (held === undefined)
328
- return { file, algorithm: RSA, state: 'imported', jwk };
329
- if (sameKeyFile(held, jwk))
330
- return { file, algorithm: RSA, state: 'unchanged', jwk: undefined };
331
- if (options.force !== true)
332
- throw new ExistingKeyPairError(username, file);
333
- return { file, algorithm: RSA, state: 'replaced', jwk };
334
- }
335
- /** Carry out what {@link planKeyImport} decided. */
336
- function applyKeyImport(dataDir, username, planned) {
337
- const { jwk, ...key } = planned;
338
- if (jwk !== undefined)
339
- writeActorKeyFile(dataDir, username, key.algorithm, jwk);
340
- return key;
341
- }
342
- /**
343
- * The PEM as a key object, whichever of the two encodings the export used.
344
- *
345
- * PHP writes `-----BEGIN PRIVATE KEY-----` (PKCS#8) on OpenSSL 3 and
346
- * `-----BEGIN RSA PRIVATE KEY-----` (PKCS#1) on older builds, and an owner
347
- * copying one out of `wp option get` has no reason to know which they have.
348
- * `createPrivateKey` reads both.
349
- */
350
- function privateKeyFrom(pem) {
351
- const text = pem.trim();
352
- if (text === '') {
353
- throw new UnusableKeyPemError('The private key is empty. Check what the export wrote.');
354
- }
355
- let key;
356
- try {
357
- key = createPrivateKey({ key: text, format: 'pem' });
358
- }
359
- catch (error) {
360
- throw new UnusableKeyPemError(`That is not a PEM private key: ${messageOf(error)}. It should begin with ` +
361
- '"-----BEGIN PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----".', { cause: error });
362
- }
363
- if (key.asymmetricKeyType !== 'rsa') {
364
- throw new UnusableKeyPemError(`That is a ${String(key.asymmetricKeyType)} key, and the WordPress ActivityPub plugin ` +
365
- 'signs with RSA. Check that the right key was exported.');
366
- }
367
- return key;
368
- }
369
- /** Refuse an export whose two halves are not halves of one pair. */
370
- function assertPairMatches(privateKey, publicKeyPem) {
371
- const text = (publicKeyPem ?? '').trim();
372
- if (text === '')
373
- return;
374
- let given;
375
- try {
376
- given = createPublicKey({ key: text, format: 'pem' });
377
- }
378
- catch (error) {
379
- throw new UnusableKeyPemError(`That is not a PEM public key: ${messageOf(error)}.`, {
380
- cause: error,
381
- });
382
- }
383
- const derived = createPublicKey(privateKey);
384
- if (!given
385
- .export({ type: 'spki', format: 'der' })
386
- .equals(derived.export({ type: 'spki', format: 'der' }))) {
387
- throw new UnusableKeyPemError('The public and private keys are not two halves of one pair. Export both again from the ' +
388
- 'same WordPress user, or leave the public key off — it is derived from the private one.');
389
- }
390
- }
391
- /**
392
- * The private JWK the key file holds, produced the way a generated one is.
393
- *
394
- * The PEM goes to DER through node:crypto, into WebCrypto, and out through
395
- * Fedify's `exportJwk` — the same call `loadActorKeyPairs` makes when it mints
396
- * a pair. Going the long way round rather than asking node:crypto for the JWK
397
- * directly is what guarantees an imported file and a generated one carry the
398
- * same members in the same shape, `alg: "RS256"` included, which is what the
399
- * loader's boot check insists on.
400
- */
401
- async function privateJwkOf(privateKey) {
402
- const pkcs8 = privateKey.export({ type: 'pkcs8', format: 'der' });
403
- const key = await crypto.subtle.importKey('pkcs8', pkcs8, { name: RSA, hash: 'SHA-256' }, true, [
404
- 'sign',
405
- ]);
406
- return await exportJwk(key);
407
- }
408
- /**
409
- * Whether a key file already holds the key being imported.
410
- *
411
- * Compared as the parsed JWK rather than as bytes, so a file somebody
412
- * reindented is still the same key.
413
- */
414
- function sameKeyFile(held, incoming) {
415
- try {
416
- return (JSON.stringify(JSON.parse(held)) ===
417
- JSON.stringify(JSON.parse(incoming)));
418
- }
419
- catch {
420
- return false;
421
- }
422
- }
423
- function messageOf(error) {
424
- return error instanceof Error ? error.message : String(error);
425
- }
426
- //# sourceMappingURL=import-wordpress.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"import-wordpress.js","sourceRoot":"","sources":["../../src/federation/import-wordpress.ts"],"names":[],"mappings":"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"}