@geekity/cms 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -24
- package/admin/components/admin-bar.njk +26 -0
- package/admin/components/fields.njk +55 -12
- package/admin/components/public-admin-bar.njk +41 -0
- package/admin/layouts/base.njk +6 -1
- package/admin/layouts/settings-page.njk +7 -2
- package/admin/layouts/shell.njk +9 -20
- package/admin/pages/account/forgot.njk +1 -1
- package/admin/pages/account/login.njk +1 -1
- package/admin/pages/account/reset.njk +4 -2
- package/admin/pages/account/setup.njk +1 -1
- package/admin/pages/dashboard/home.njk +1 -0
- package/admin/pages/documents/list.njk +1 -0
- package/admin/pages/documents/taxonomy.njk +1 -0
- package/admin/pages/federation/followers.njk +3 -0
- package/admin/pages/federation/settings.njk +5 -0
- package/admin/pages/media/library.njk +39 -0
- package/admin/pages/navigation/menus.njk +6 -1
- package/admin/pages/settings/discussion.njk +7 -0
- package/admin/pages/settings/email.njk +10 -0
- package/admin/pages/settings/general.njk +7 -0
- package/admin/pages/settings/permalinks.njk +6 -0
- package/admin/pages/settings/reading.njk +7 -0
- package/admin/pages/tools/content-index.njk +1 -0
- package/admin/pages/tools/personal-data.njk +5 -0
- package/admin/pages/users/edit.njk +7 -5
- package/admin/pages/users/list.njk +1 -0
- package/admin/pages/users/new.njk +5 -1
- package/admin/static/admin-bar.css +153 -0
- package/admin/static/admin.css +95 -96
- package/dist/admin/comments.js +2 -1
- package/dist/admin/comments.js.map +1 -1
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +18 -0
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/media.d.ts +13 -2
- package/dist/admin/media.d.ts.map +1 -1
- package/dist/admin/media.js +58 -3
- package/dist/admin/media.js.map +1 -1
- package/dist/admin/personal-data.d.ts.map +1 -1
- package/dist/admin/personal-data.js +5 -1
- package/dist/admin/personal-data.js.map +1 -1
- package/dist/admin/preview.d.ts.map +1 -1
- package/dist/admin/preview.js +1 -1
- package/dist/admin/preview.js.map +1 -1
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +2 -1
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/store.d.ts +36 -1
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +63 -1
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/templates.d.ts +1 -0
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +1 -0
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/tools.d.ts +2 -0
- package/dist/admin/tools.d.ts.map +1 -1
- package/dist/admin/tools.js +3 -2
- package/dist/admin/tools.js.map +1 -1
- package/dist/admin/uploads.d.ts +5 -2
- package/dist/admin/uploads.d.ts.map +1 -1
- package/dist/admin/uploads.js +6 -2
- package/dist/admin/uploads.js.map +1 -1
- package/dist/comments/index.d.ts +2 -2
- package/dist/comments/index.d.ts.map +1 -1
- package/dist/comments/index.js +1 -1
- package/dist/comments/index.js.map +1 -1
- package/dist/comments/records.d.ts +43 -2
- package/dist/comments/records.d.ts.map +1 -1
- package/dist/comments/records.js +201 -54
- package/dist/comments/records.js.map +1 -1
- package/dist/comments/routes.js +1 -1
- package/dist/comments/routes.js.map +1 -1
- package/dist/comments/viewer.d.ts +13 -0
- package/dist/comments/viewer.d.ts.map +1 -1
- package/dist/comments/viewer.js +13 -1
- package/dist/comments/viewer.js.map +1 -1
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/env.d.ts +19 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +37 -1
- package/dist/federation/article.js.map +1 -1
- package/dist/federation/delivery.d.ts +3 -1
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +6 -1
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/federation.d.ts +3 -0
- package/dist/federation/federation.d.ts.map +1 -1
- package/dist/federation/federation.js.map +1 -1
- package/dist/federation/inbox.d.ts +7 -5
- package/dist/federation/inbox.d.ts.map +1 -1
- package/dist/federation/inbox.js +15 -32
- package/dist/federation/inbox.js.map +1 -1
- package/dist/federation/index.d.ts +2 -0
- package/dist/federation/index.d.ts.map +1 -1
- package/dist/federation/index.js +1 -0
- package/dist/federation/index.js.map +1 -1
- package/dist/federation/mount.js +6 -1
- package/dist/federation/mount.js.map +1 -1
- package/dist/federation/profiles.d.ts +95 -0
- package/dist/federation/profiles.d.ts.map +1 -0
- package/dist/federation/profiles.js +171 -0
- package/dist/federation/profiles.js.map +1 -0
- package/dist/federation/relays.d.ts +3 -1
- package/dist/federation/relays.d.ts.map +1 -1
- package/dist/federation/relays.js +6 -1
- package/dist/federation/relays.js.map +1 -1
- package/dist/federation/replies.d.ts +13 -4
- package/dist/federation/replies.d.ts.map +1 -1
- package/dist/federation/replies.js +25 -6
- package/dist/federation/replies.js.map +1 -1
- package/dist/images/alt-text.d.ts +51 -0
- package/dist/images/alt-text.d.ts.map +1 -0
- package/dist/images/alt-text.js +113 -0
- package/dist/images/alt-text.js.map +1 -0
- package/dist/images/icons.d.ts.map +1 -1
- package/dist/images/icons.js +3 -2
- package/dist/images/icons.js.map +1 -1
- package/dist/images/index.d.ts +2 -2
- package/dist/images/index.d.ts.map +1 -1
- package/dist/images/markup.d.ts +26 -4
- package/dist/images/markup.d.ts.map +1 -1
- package/dist/images/markup.js +36 -15
- package/dist/images/markup.js.map +1 -1
- package/dist/images/paths.d.ts +11 -0
- package/dist/images/paths.d.ts.map +1 -1
- package/dist/images/paths.js +17 -1
- package/dist/images/paths.js.map +1 -1
- package/dist/images/variants.d.ts +13 -3
- package/dist/images/variants.d.ts.map +1 -1
- package/dist/images/variants.js +13 -11
- package/dist/images/variants.js.map +1 -1
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -11
- package/dist/index.js.map +1 -1
- package/dist/notifications/routes.js +1 -1
- package/dist/notifications/routes.js.map +1 -1
- package/dist/privacy/erase.d.ts.map +1 -1
- package/dist/privacy/erase.js +2 -3
- package/dist/privacy/erase.js.map +1 -1
- package/dist/privacy/retention.js +1 -1
- package/dist/privacy/retention.js.map +1 -1
- package/dist/web/admin-bar.d.ts +21 -0
- package/dist/web/admin-bar.d.ts.map +1 -0
- package/dist/web/admin-bar.js +145 -0
- package/dist/web/admin-bar.js.map +1 -0
- package/dist/web/assets.d.ts +29 -1
- package/dist/web/assets.d.ts.map +1 -1
- package/dist/web/assets.js +48 -3
- package/dist/web/assets.js.map +1 -1
- package/dist/web/compression.d.ts +19 -0
- package/dist/web/compression.d.ts.map +1 -0
- package/dist/web/compression.js +160 -0
- package/dist/web/compression.js.map +1 -0
- package/dist/web/context.d.ts +5 -1
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +5 -2
- package/dist/web/context.js.map +1 -1
- package/dist/web/conversation.d.ts.map +1 -1
- package/dist/web/conversation.js +13 -12
- package/dist/web/conversation.js.map +1 -1
- package/dist/web/render.d.ts +27 -0
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +77 -38
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +63 -28
- package/dist/web/routes.js.map +1 -1
- package/dist/web/templates.d.ts.map +1 -1
- package/dist/web/templates.js +9 -2
- package/dist/web/templates.js.map +1 -1
- package/dist/webmention/service.d.ts.map +1 -1
- package/dist/webmention/service.js +5 -1
- package/dist/webmention/service.js.map +1 -1
- package/package.json +8 -8
- package/themes/default/README.md +176 -19
- package/themes/default/layouts/base.njk +51 -10
- package/themes/default/layouts/post.njk +10 -1
- package/themes/default/partials/comment-form.njk +33 -14
- package/themes/default/partials/contact-form.njk +32 -14
- package/themes/default/static/style.css +109 -24
package/dist/admin/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/admin/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAI/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,YAAY,QAAQ,CAAC;AAElD,sCAAsC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,YAAY,sBAAsB,CAAC;AAExE,0DAA0D;AAC1D,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC;AAErC,uCAAuC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAE3C,iDAAiD;AACjD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,4EAA4E;IAC5E,OAAO,EAAE,SAAS;CACV,CAAC;
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/admin/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/admin/uploads.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Hono, MiddlewareHandler } from 'hono';
|
|
|
2
2
|
import type { ResolvedConfig } from '../config.ts';
|
|
3
3
|
import type { UploadMediaType } from '../content/media.ts';
|
|
4
4
|
import type { GeekityEnv } from '../env.ts';
|
|
5
|
+
import type { AltText } from '../images/alt-text.ts';
|
|
5
6
|
import type { ImageConfig } from '../images/variants.ts';
|
|
6
7
|
/** Where the editor posts a file it wants stored. */
|
|
7
8
|
export declare const UPLOADS_PATH = "/admin/uploads";
|
|
@@ -38,7 +39,7 @@ export interface StoredUpload {
|
|
|
38
39
|
url: string;
|
|
39
40
|
/** What the CMS decided the file is; `media.image` says whether it is one. */
|
|
40
41
|
media: UploadMediaType;
|
|
41
|
-
/** The submitted name without its extension, for
|
|
42
|
+
/** The submitted name without its extension, for the text of a link to it. */
|
|
42
43
|
label: string;
|
|
43
44
|
}
|
|
44
45
|
/** Why an upload was refused, and the status that refusal answers with. */
|
|
@@ -103,10 +104,12 @@ export declare function mountUploads(app: Hono<GeekityEnv>): void;
|
|
|
103
104
|
export interface UploadMarkdownOptions {
|
|
104
105
|
/** The public URL of the file. */
|
|
105
106
|
url: string;
|
|
106
|
-
/** What
|
|
107
|
+
/** What a link says: the submitted name without its extension. Never an image's alt text. */
|
|
107
108
|
label: string;
|
|
108
109
|
/** Whether the Markdown is an embed rather than a link. */
|
|
109
110
|
image: boolean;
|
|
111
|
+
/** The library's alt text for an image. None, or decorative, embeds it with an empty one. */
|
|
112
|
+
alt?: AltText | undefined;
|
|
110
113
|
}
|
|
111
114
|
/**
|
|
112
115
|
* The Markdown for one upload: an embed for an image, a link for anything else.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../src/admin/uploads.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../src/admin/uploads.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAEpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIzD,qDAAqD;AACrD,eAAO,MAAM,YAAY,mBAA4B,CAAC;AAEtD,0CAA0C;AAC1C,eAAO,MAAM,YAAY,SAAS,CAAC;AAEnC;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,aAAa,GAAG,gBAAgB,CAAC,GAC9F,WAAW,CAAC;AAEd,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,KAAK,EAAE,eAAe,CAAC;IACvB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,8FAA8F;IAC9F,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACxB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAClC;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,GAAG,OAAO,IAAI,aAAa,CAE7F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,YAAY,GAAG,aAAa,CAAC,CAsEvC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,CAAC,UAAU,CAiB/D,CAAC;AAOF;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAmBxD;AA2BD,qDAAqD;AACrD,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,KAAK,EAAE,OAAO,CAAC;IACf,6FAA6F;IAC7F,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAMrE;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
package/dist/admin/uploads.js
CHANGED
|
@@ -188,8 +188,12 @@ async function writeWithoutOverwriting(directory, stem, extension, bytes) {
|
|
|
188
188
|
* linked from the other would be a difference nobody could explain.
|
|
189
189
|
*/
|
|
190
190
|
export function uploadMarkdown(options) {
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
if (!options.image)
|
|
192
|
+
return `[${escapeLabel(options.label)}](${options.url})`;
|
|
193
|
+
// A file name is not a description (TASK-141): an image with no alt text in
|
|
194
|
+
// the library is embedded with none, and publishing it says so.
|
|
195
|
+
const alt = options.alt?.kind === 'described' ? escapeLabel(options.alt.text) : '';
|
|
196
|
+
return ``;
|
|
193
197
|
}
|
|
194
198
|
/** What an upload over the site's limit is told, and the limit it went over. */
|
|
195
199
|
export function tooLargeMessage(limit) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.js","sourceRoot":"","sources":["../../src/admin/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAK7B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE3E,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"uploads.js","sourceRoot":"","sources":["../../src/admin/uploads.ts"],"names":[],"mappings":"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"}
|
package/dist/comments/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export { isModerationAction, moderateComment, MODERATION_ACTIONS } from './moder
|
|
|
7
7
|
export type { ModerateCommentOptions, ModerationAction, ModerationOutcome } from './moderate.ts';
|
|
8
8
|
export { COMMENTS_FRONT_MATTER_KEY, commentPolicyOf, commentsOpen, DEFAULT_COMMENTS_CLOSE_AFTER_DAYS, } from './policy.ts';
|
|
9
9
|
export type { CommentPolicy } from './policy.ts';
|
|
10
|
-
export { addComment, COMMENTS_DATA_DIRECTORY, commentsDirectory, commentsFile, deleteComment, heldWebmention, intakeComment, readComments, rebuildCommentIndexes, updateComment, } from './records.ts';
|
|
11
|
-
export type { CommentIndexReport, CommentIntakeOutcome, CommentNotices, CommentOrigin, CommentRecords, IntakeCommentOptions, NewComment, ProposedComment, } from './records.ts';
|
|
10
|
+
export { addComment, COMMENT_EMAILS_DIRECTORY, COMMENT_EMAILS_FILE_MODE, commentEmailsFile, COMMENTS_DATA_DIRECTORY, commentsDirectory, commentsFile, deleteComment, heldWebmention, intakeComment, migrateCommentEmails, readComments, rebuildCommentIndexes, updateComment, } from './records.ts';
|
|
11
|
+
export type { CommentEmailMigrationReport, CommentIndexReport, CommentIntakeOutcome, CommentNotices, CommentOrigin, CommentRecords, IntakeCommentOptions, NewComment, ProposedComment, } from './records.ts';
|
|
12
12
|
export { commentFormFor, commentNoticeFor, mountComments } from './routes.ts';
|
|
13
13
|
export { COMMENT_FIELDS, COMMENT_RATE_LIMIT, COMMENT_RATE_WINDOW_SECONDS, COMMENT_SALT_FILE, commentKeys, commentProblems, hashClientAddress, MAXIMUM_BODY_LENGTH, MAXIMUM_NAME_LENGTH, MAXIMUM_URL_LENGTH, MAXIMUM_FORM_AGE_SECONDS, MINIMUM_SUBMIT_SECONDS, normalizeWebsite, submitComment, } from './submission.ts';
|
|
14
14
|
export type { CommentChecker, CommentForm, CommentOutcome, CommentProblems, CommentRefusal, CommentReport, CommentSubmission, SubmissionType, CommentThrottle, CommentVerdict, SignedInAuthor, SubmitCommentOptions, } from './submission.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/comments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxF,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,YAAY,EACZ,iCAAiC,GAClC,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,uBAAuB,EACvB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,UAAU,EACV,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,2BAA2B,EAC3B,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,gBAAgB,EAChB,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,eAAe,EACf,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/comments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxF,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACjG,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,YAAY,EACZ,iCAAiC,GAClC,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,wBAAwB,EACxB,wBAAwB,EACxB,iBAAiB,EACjB,uBAAuB,EACvB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,2BAA2B,EAC3B,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,UAAU,EACV,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,2BAA2B,EAC3B,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,gBAAgB,EAChB,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,eAAe,EACf,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/comments/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { blankValues, COMMENT_NOTICE_PARAM, COMMENT_NOTICES, COMMENT_POST_PATH,
|
|
|
3
3
|
export { renderCommentMarkdown } from './markdown.js';
|
|
4
4
|
export { isModerationAction, moderateComment, MODERATION_ACTIONS } from './moderate.js';
|
|
5
5
|
export { COMMENTS_FRONT_MATTER_KEY, commentPolicyOf, commentsOpen, DEFAULT_COMMENTS_CLOSE_AFTER_DAYS, } from './policy.js';
|
|
6
|
-
export { addComment, COMMENTS_DATA_DIRECTORY, commentsDirectory, commentsFile, deleteComment, heldWebmention, intakeComment, readComments, rebuildCommentIndexes, updateComment, } from './records.js';
|
|
6
|
+
export { addComment, COMMENT_EMAILS_DIRECTORY, COMMENT_EMAILS_FILE_MODE, commentEmailsFile, COMMENTS_DATA_DIRECTORY, commentsDirectory, commentsFile, deleteComment, heldWebmention, intakeComment, migrateCommentEmails, readComments, rebuildCommentIndexes, updateComment, } from './records.js';
|
|
7
7
|
export { commentFormFor, commentNoticeFor, mountComments } from './routes.js';
|
|
8
8
|
export { COMMENT_FIELDS, COMMENT_RATE_LIMIT, COMMENT_RATE_WINDOW_SECONDS, COMMENT_SALT_FILE, commentKeys, commentProblems, hashClientAddress, MAXIMUM_BODY_LENGTH, MAXIMUM_NAME_LENGTH, MAXIMUM_URL_LENGTH, MAXIMUM_FORM_AGE_SECONDS, MINIMUM_SUBMIT_SECONDS, normalizeWebsite, submitComment, } from './submission.js';
|
|
9
9
|
export { signedInCommenter } from './viewer.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/comments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,GAChB,MAAM,cAAc,CAAC;AAOtB,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,QAAQ,GACT,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExF,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,YAAY,EACZ,iCAAiC,GAClC,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,uBAAuB,EACvB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,aAAa,GACd,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/comments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,GAChB,MAAM,cAAc,CAAC;AAOtB,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,QAAQ,GACT,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExF,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,YAAY,EACZ,iCAAiC,GAClC,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,wBAAwB,EACxB,wBAAwB,EACxB,iBAAiB,EACjB,uBAAuB,EACvB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,aAAa,GACd,MAAM,cAAc,CAAC;AAYtB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,2BAA2B,EAC3B,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,gBAAgB,EAChB,aAAa,GACd,MAAM,iBAAiB,CAAC;AAezB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -21,6 +21,14 @@ import type { CommentChecker } from './submission.ts';
|
|
|
21
21
|
* {@link commentFrom} whether it is being written for the first time or
|
|
22
22
|
* rebuilt at boot.
|
|
23
23
|
*
|
|
24
|
+
* **The email is not in the file.** Everything under `content/` is published
|
|
25
|
+
* and goes into git, where a removed line stays in history until somebody
|
|
26
|
+
* rewrites it, so a commenter's `author.email` and the `notify` that only
|
|
27
|
+
* means anything beside it live in `data/comments/{slug}.json` instead (mode
|
|
28
|
+
* `0600`), keyed by comment id. A record read here has both merged back in, and
|
|
29
|
+
* a record written here is split again, so nothing outside this module knows
|
|
30
|
+
* there are two files (TASK-182).
|
|
31
|
+
*
|
|
24
32
|
* The entry shape is deliberately wider than a form submission. A webmention
|
|
25
33
|
* (TASK-51) is somebody else's post pointing at this one: it has a `url` and no
|
|
26
34
|
* email, it may be a like rather than a reply, and it belongs in the same file
|
|
@@ -50,6 +58,16 @@ export declare function commentsDirectory(contentDir: string): string;
|
|
|
50
58
|
* sanitised to one name would silently share a thread.
|
|
51
59
|
*/
|
|
52
60
|
export declare function commentsFile(contentDir: string, slug: string): string;
|
|
61
|
+
/** Where commenters' emails live, relative to the data directory. */
|
|
62
|
+
export declare const COMMENT_EMAILS_DIRECTORY = "comments";
|
|
63
|
+
/** Nobody but the site's own user reads a commenter's email. */
|
|
64
|
+
export declare const COMMENT_EMAILS_FILE_MODE = 384;
|
|
65
|
+
/**
|
|
66
|
+
* The absolute path of the private file holding one post's commenters' emails:
|
|
67
|
+
* `data/comments/{slug}.json`, beside the post's own file in `content/` but
|
|
68
|
+
* never published with it.
|
|
69
|
+
*/
|
|
70
|
+
export declare function commentEmailsFile(dataDir: string, slug: string): string;
|
|
53
71
|
/** A {@link CommentRecord} on its way in, before the store has named it. */
|
|
54
72
|
export type NewComment = Omit<CommentRecord, 'id'> & {
|
|
55
73
|
/** The post it is on. */
|
|
@@ -65,9 +83,11 @@ export interface CommentRecords {
|
|
|
65
83
|
readonly admin: AdminStore;
|
|
66
84
|
/** The content directory the files live under. */
|
|
67
85
|
readonly contentDir: string;
|
|
86
|
+
/** The data directory the commenters' emails live under. */
|
|
87
|
+
readonly dataDir: string;
|
|
68
88
|
}
|
|
69
89
|
/**
|
|
70
|
-
* One post's comments, as its
|
|
90
|
+
* One post's comments, as its files say them, oldest first.
|
|
71
91
|
*
|
|
72
92
|
* A missing file is no comments, which is what almost every post has. Unlike
|
|
73
93
|
* `followers.json`, an entry that will not parse is dropped rather than
|
|
@@ -76,7 +96,7 @@ export interface CommentRecords {
|
|
|
76
96
|
* down on the next boot. Nothing irreplaceable is at stake — a comment that
|
|
77
97
|
* cannot be read is one nobody could have shown either way.
|
|
78
98
|
*/
|
|
79
|
-
export declare function readComments(
|
|
99
|
+
export declare function readComments(records: Pick<CommentRecords, 'contentDir' | 'dataDir'>, slug: string): CommentRecord[];
|
|
80
100
|
/**
|
|
81
101
|
* Record a comment: append it to its post's file and index it.
|
|
82
102
|
*
|
|
@@ -278,4 +298,25 @@ export interface CommentIndexReport {
|
|
|
278
298
|
* synchronous because both callers are.
|
|
279
299
|
*/
|
|
280
300
|
export declare function rebuildCommentIndexes(records: CommentRecords): CommentIndexReport;
|
|
301
|
+
/** What moving the emails out of `content/` changed. */
|
|
302
|
+
export interface CommentEmailMigrationReport {
|
|
303
|
+
/** How many comment files were rewritten. */
|
|
304
|
+
files: number;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Move every email still in a comment file into `data/` (TASK-182).
|
|
308
|
+
*
|
|
309
|
+
* A site that kept comments before the emails moved has them in
|
|
310
|
+
* `content/_data/comments/`, and a comment file somebody edits by hand may
|
|
311
|
+
* gain one again. Boot calls this before {@link rebuildCommentIndexes}, so
|
|
312
|
+
* both end up in the private file and out of the published one. It edits each
|
|
313
|
+
* file's JSON as it finds it, taking off `author.email` and `notify` and
|
|
314
|
+
* nothing else, so a key this version does not know and an entry it cannot
|
|
315
|
+
* read survive it. An email `data/` already holds for that comment wins. A file
|
|
316
|
+
* with nothing to take off is not written, so a second boot writes nothing.
|
|
317
|
+
*
|
|
318
|
+
* Synchronous, like the other boot migrations, and run before anything else
|
|
319
|
+
* can be writing a comment.
|
|
320
|
+
*/
|
|
321
|
+
export declare function migrateCommentEmails(records: Pick<CommentRecords, 'contentDir' | 'dataDir'>): CommentEmailMigrationReport;
|
|
281
322
|
//# sourceMappingURL=records.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/comments/records.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAiB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG/F,OAAO,KAAK,EAAE,cAAc,EAAqC,MAAM,iBAAiB,CAAC;AAEzF
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/comments/records.ts"],"names":[],"mappings":"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"}
|
package/dist/comments/records.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { readdirSync } from 'node:fs';
|
|
2
|
+
import { readdirSync, rmSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { COMMENT_KINDS, COMMENT_SOURCES, COMMENT_STATUSES, REDACTED_FIELDS, } from '../admin/store.js';
|
|
5
5
|
import { readFileIfPresentSync, withFileLock, writeFileAtomicallySync } from '../files/atomic.js';
|
|
@@ -25,6 +25,14 @@ import { hashClientAddress } from '../forms/protection.js';
|
|
|
25
25
|
* {@link commentFrom} whether it is being written for the first time or
|
|
26
26
|
* rebuilt at boot.
|
|
27
27
|
*
|
|
28
|
+
* **The email is not in the file.** Everything under `content/` is published
|
|
29
|
+
* and goes into git, where a removed line stays in history until somebody
|
|
30
|
+
* rewrites it, so a commenter's `author.email` and the `notify` that only
|
|
31
|
+
* means anything beside it live in `data/comments/{slug}.json` instead (mode
|
|
32
|
+
* `0600`), keyed by comment id. A record read here has both merged back in, and
|
|
33
|
+
* a record written here is split again, so nothing outside this module knows
|
|
34
|
+
* there are two files (TASK-182).
|
|
35
|
+
*
|
|
28
36
|
* The entry shape is deliberately wider than a form submission. A webmention
|
|
29
37
|
* (TASK-51) is somebody else's post pointing at this one: it has a `url` and no
|
|
30
38
|
* email, it may be a like rather than a reply, and it belongs in the same file
|
|
@@ -63,8 +71,23 @@ export function commentsFile(contentDir, slug) {
|
|
|
63
71
|
}
|
|
64
72
|
/** Slugs a file may be named after: what {@link slugify} produces, and no more. */
|
|
65
73
|
const SAFE_SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
74
|
+
/** Where commenters' emails live, relative to the data directory. */
|
|
75
|
+
export const COMMENT_EMAILS_DIRECTORY = 'comments';
|
|
76
|
+
/** Nobody but the site's own user reads a commenter's email. */
|
|
77
|
+
export const COMMENT_EMAILS_FILE_MODE = 0o600;
|
|
78
|
+
/**
|
|
79
|
+
* The absolute path of the private file holding one post's commenters' emails:
|
|
80
|
+
* `data/comments/{slug}.json`, beside the post's own file in `content/` but
|
|
81
|
+
* never published with it.
|
|
82
|
+
*/
|
|
83
|
+
export function commentEmailsFile(dataDir, slug) {
|
|
84
|
+
if (!SAFE_SLUG.test(slug)) {
|
|
85
|
+
throw new Error(`"${slug}" is not a slug a comment file can be named after.`);
|
|
86
|
+
}
|
|
87
|
+
return path.join(dataDir, COMMENT_EMAILS_DIRECTORY, `${slug}.json`);
|
|
88
|
+
}
|
|
66
89
|
/**
|
|
67
|
-
* One post's comments, as its
|
|
90
|
+
* One post's comments, as its files say them, oldest first.
|
|
68
91
|
*
|
|
69
92
|
* A missing file is no comments, which is what almost every post has. Unlike
|
|
70
93
|
* `followers.json`, an entry that will not parse is dropped rather than
|
|
@@ -73,14 +96,90 @@ const SAFE_SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
|
73
96
|
* down on the next boot. Nothing irreplaceable is at stake — a comment that
|
|
74
97
|
* cannot be read is one nobody could have shown either way.
|
|
75
98
|
*/
|
|
76
|
-
export function readComments(
|
|
77
|
-
|
|
99
|
+
export function readComments(records, slug) {
|
|
100
|
+
return readPost(records, slug).comments;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* One post's comments with their emails merged back in.
|
|
104
|
+
*
|
|
105
|
+
* `data/` wins over `content/`. An email still in a comment file is read too,
|
|
106
|
+
* so a site that has not booted since upgrading loses nothing before
|
|
107
|
+
* {@link migrateCommentEmails} moves it.
|
|
108
|
+
*/
|
|
109
|
+
function readPost(records, slug) {
|
|
110
|
+
const held = fileContents(readFileIfPresentSync(commentsFile(records.contentDir, slug)));
|
|
111
|
+
const contacts = contactsIn(readFileIfPresentSync(commentEmailsFile(records.dataDir, slug)));
|
|
112
|
+
return {
|
|
113
|
+
post: held.post,
|
|
114
|
+
comments: held.comments.map((comment) => {
|
|
115
|
+
const contact = contacts.get(comment.id);
|
|
116
|
+
if (contact === undefined)
|
|
117
|
+
return comment;
|
|
118
|
+
return {
|
|
119
|
+
...comment,
|
|
120
|
+
author: { ...comment.author, email: contact.email },
|
|
121
|
+
notify: contact.notify,
|
|
122
|
+
};
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Write one post's comments: the emails to `data/`, everything else to
|
|
128
|
+
* `content/`. Called inside the lock on the post's comment file, which is the
|
|
129
|
+
* lock on both, because nothing writes the private file but this.
|
|
130
|
+
*
|
|
131
|
+
* `data/` goes first, so a crash between the two writes leaves an email in
|
|
132
|
+
* both places or in `data/` alone, and never in neither.
|
|
133
|
+
*/
|
|
134
|
+
function writePost(records, slug, permalink, comments) {
|
|
135
|
+
const contacts = new Map();
|
|
136
|
+
for (const comment of comments) {
|
|
137
|
+
if (comment.author.email !== null) {
|
|
138
|
+
contacts.set(comment.id, { email: comment.author.email, notify: comment.notify });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const privateFile = commentEmailsFile(records.dataDir, slug);
|
|
142
|
+
if (contacts.size > 0) {
|
|
143
|
+
writeFileAtomicallySync(privateFile, contactsJson(contacts), {
|
|
144
|
+
mode: COMMENT_EMAILS_FILE_MODE,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
rmSync(privateFile, { force: true });
|
|
149
|
+
}
|
|
150
|
+
writeFileAtomicallySync(commentsFile(records.contentDir, slug), commentsJson(permalink, comments.map(publishedEntryOf)));
|
|
151
|
+
}
|
|
152
|
+
/** The emails a private file holds, by comment id. A damaged file holds none. */
|
|
153
|
+
function contactsIn(source) {
|
|
154
|
+
const contacts = new Map();
|
|
78
155
|
if (source === undefined)
|
|
79
|
-
return
|
|
80
|
-
|
|
156
|
+
return contacts;
|
|
157
|
+
let parsed;
|
|
158
|
+
try {
|
|
159
|
+
parsed = JSON.parse(source);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return contacts;
|
|
163
|
+
}
|
|
164
|
+
if (!isRecord(parsed) || !isRecord(parsed['comments']))
|
|
165
|
+
return contacts;
|
|
166
|
+
for (const [id, value] of Object.entries(parsed['comments'])) {
|
|
167
|
+
if (!isRecord(value))
|
|
168
|
+
continue;
|
|
169
|
+
const email = optionalText(value['email']);
|
|
170
|
+
if (email !== null)
|
|
171
|
+
contacts.set(id, { email, notify: value['notify'] === true });
|
|
172
|
+
}
|
|
173
|
+
return contacts;
|
|
174
|
+
}
|
|
175
|
+
/** A private file's contents: indented, newline ended. */
|
|
176
|
+
function contactsJson(contacts) {
|
|
177
|
+
return `${JSON.stringify({ comments: Object.fromEntries(contacts) }, null, 2)}\n`;
|
|
81
178
|
}
|
|
82
179
|
/** One post's comment file as the post it names and the comments it holds. */
|
|
83
180
|
function fileContents(source) {
|
|
181
|
+
if (source === undefined)
|
|
182
|
+
return { post: '', comments: [] };
|
|
84
183
|
let parsed;
|
|
85
184
|
try {
|
|
86
185
|
parsed = JSON.parse(source);
|
|
@@ -98,10 +197,6 @@ function fileContents(source) {
|
|
|
98
197
|
: [],
|
|
99
198
|
};
|
|
100
199
|
}
|
|
101
|
-
/** Just the comments, for the readers that have the post already. */
|
|
102
|
-
function commentsIn(source) {
|
|
103
|
-
return fileContents(source).comments;
|
|
104
|
-
}
|
|
105
200
|
/**
|
|
106
201
|
* One file entry as the record it is, or `undefined` when it is not one.
|
|
107
202
|
*
|
|
@@ -164,17 +259,16 @@ function redactedIn(value) {
|
|
|
164
259
|
* same content directory can never be given the same name.
|
|
165
260
|
*/
|
|
166
261
|
export async function addComment(records, comment) {
|
|
167
|
-
const
|
|
168
|
-
const file = commentsFile(contentDir, comment.slug);
|
|
262
|
+
const file = commentsFile(records.contentDir, comment.slug);
|
|
169
263
|
const stored = {
|
|
170
264
|
...commentRecordOf(comment),
|
|
171
265
|
slug: comment.slug,
|
|
172
266
|
permalink: comment.permalink,
|
|
173
267
|
};
|
|
174
268
|
return await withFileLock(file, () => {
|
|
175
|
-
const held =
|
|
176
|
-
|
|
177
|
-
admin.putComment(stored);
|
|
269
|
+
const held = readPost(records, comment.slug).comments;
|
|
270
|
+
writePost(records, comment.slug, comment.permalink, [...held, stored]);
|
|
271
|
+
records.admin.putComment(stored);
|
|
178
272
|
return stored;
|
|
179
273
|
});
|
|
180
274
|
}
|
|
@@ -357,19 +451,18 @@ function messageOf(error) {
|
|
|
357
451
|
* one thing an id alone does not carry.
|
|
358
452
|
*/
|
|
359
453
|
export async function updateComment(records, id, change) {
|
|
360
|
-
const
|
|
361
|
-
const known = admin.getComment(id);
|
|
454
|
+
const known = records.admin.getComment(id);
|
|
362
455
|
if (known === undefined)
|
|
363
456
|
return undefined;
|
|
364
|
-
const file = commentsFile(contentDir, known.slug);
|
|
457
|
+
const file = commentsFile(records.contentDir, known.slug);
|
|
365
458
|
return await withFileLock(file, () => {
|
|
366
|
-
const held =
|
|
459
|
+
const held = readPost(records, known.slug).comments;
|
|
367
460
|
const at = held.findIndex((entry) => entry.id === id);
|
|
368
461
|
if (at === -1)
|
|
369
462
|
return undefined;
|
|
370
463
|
const moved = { ...known, ...held[at], ...change };
|
|
371
|
-
|
|
372
|
-
admin.putComment(moved);
|
|
464
|
+
writePost(records, known.slug, known.permalink, held.with(at, moved));
|
|
465
|
+
records.admin.putComment(moved);
|
|
373
466
|
return moved;
|
|
374
467
|
});
|
|
375
468
|
}
|
|
@@ -381,18 +474,16 @@ export async function updateComment(records, id, change) {
|
|
|
381
474
|
* edited by hand cannot go on being shown.
|
|
382
475
|
*/
|
|
383
476
|
export async function deleteComment(records, id) {
|
|
384
|
-
const
|
|
385
|
-
const known = admin.getComment(id);
|
|
477
|
+
const known = records.admin.getComment(id);
|
|
386
478
|
if (known === undefined)
|
|
387
479
|
return false;
|
|
388
|
-
const file = commentsFile(contentDir, known.slug);
|
|
480
|
+
const file = commentsFile(records.contentDir, known.slug);
|
|
389
481
|
return await withFileLock(file, () => {
|
|
390
|
-
const held =
|
|
482
|
+
const held = readPost(records, known.slug).comments;
|
|
391
483
|
const next = held.filter((entry) => entry.id !== id);
|
|
392
|
-
if (next.length !== held.length)
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const indexed = admin.deleteComment(id);
|
|
484
|
+
if (next.length !== held.length)
|
|
485
|
+
writePost(records, known.slug, known.permalink, next);
|
|
486
|
+
const indexed = records.admin.deleteComment(id);
|
|
396
487
|
return next.length !== held.length || indexed;
|
|
397
488
|
});
|
|
398
489
|
}
|
|
@@ -409,13 +500,9 @@ export async function deleteComment(records, id) {
|
|
|
409
500
|
* that changed come back, each already put into the index.
|
|
410
501
|
*/
|
|
411
502
|
export async function rewriteComments(records, slug, change) {
|
|
412
|
-
const
|
|
413
|
-
const file = commentsFile(contentDir, slug);
|
|
503
|
+
const file = commentsFile(records.contentDir, slug);
|
|
414
504
|
return await withFileLock(file, () => {
|
|
415
|
-
const
|
|
416
|
-
if (source === undefined)
|
|
417
|
-
return [];
|
|
418
|
-
const held = fileContents(source);
|
|
505
|
+
const held = readPost(records, slug);
|
|
419
506
|
const changed = [];
|
|
420
507
|
const next = held.comments.map((entry) => {
|
|
421
508
|
const moved = change(entry);
|
|
@@ -426,9 +513,9 @@ export async function rewriteComments(records, slug, change) {
|
|
|
426
513
|
});
|
|
427
514
|
if (changed.length === 0)
|
|
428
515
|
return [];
|
|
429
|
-
|
|
516
|
+
writePost(records, slug, held.post, next);
|
|
430
517
|
for (const comment of changed)
|
|
431
|
-
admin.putComment(comment);
|
|
518
|
+
records.admin.putComment(comment);
|
|
432
519
|
return changed.map((comment) => comment.id);
|
|
433
520
|
});
|
|
434
521
|
}
|
|
@@ -449,21 +536,84 @@ export function commentSlugs(contentDir) {
|
|
|
449
536
|
* synchronous because both callers are.
|
|
450
537
|
*/
|
|
451
538
|
export function rebuildCommentIndexes(records) {
|
|
452
|
-
const { admin, contentDir } = records;
|
|
453
539
|
const comments = [];
|
|
454
|
-
for (const
|
|
455
|
-
const
|
|
456
|
-
if (source === undefined)
|
|
457
|
-
continue;
|
|
458
|
-
const slug = path.basename(file, '.json');
|
|
459
|
-
const held = fileContents(source);
|
|
540
|
+
for (const slug of commentSlugs(records.contentDir)) {
|
|
541
|
+
const held = readPost(records, slug);
|
|
460
542
|
for (const entry of held.comments) {
|
|
461
543
|
comments.push({ ...entry, slug, permalink: held.post });
|
|
462
544
|
}
|
|
463
545
|
}
|
|
464
|
-
admin.replaceComments(comments);
|
|
546
|
+
records.admin.replaceComments(comments);
|
|
465
547
|
return { comments: comments.length };
|
|
466
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* Move every email still in a comment file into `data/` (TASK-182).
|
|
551
|
+
*
|
|
552
|
+
* A site that kept comments before the emails moved has them in
|
|
553
|
+
* `content/_data/comments/`, and a comment file somebody edits by hand may
|
|
554
|
+
* gain one again. Boot calls this before {@link rebuildCommentIndexes}, so
|
|
555
|
+
* both end up in the private file and out of the published one. It edits each
|
|
556
|
+
* file's JSON as it finds it, taking off `author.email` and `notify` and
|
|
557
|
+
* nothing else, so a key this version does not know and an entry it cannot
|
|
558
|
+
* read survive it. An email `data/` already holds for that comment wins. A file
|
|
559
|
+
* with nothing to take off is not written, so a second boot writes nothing.
|
|
560
|
+
*
|
|
561
|
+
* Synchronous, like the other boot migrations, and run before anything else
|
|
562
|
+
* can be writing a comment.
|
|
563
|
+
*/
|
|
564
|
+
export function migrateCommentEmails(records) {
|
|
565
|
+
let files = 0;
|
|
566
|
+
for (const slug of commentSlugs(records.contentDir)) {
|
|
567
|
+
const file = commentsFile(records.contentDir, slug);
|
|
568
|
+
let parsed;
|
|
569
|
+
try {
|
|
570
|
+
parsed = JSON.parse(readFileIfPresentSync(file) ?? '');
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
if (!isRecord(parsed) || !Array.isArray(parsed['comments']))
|
|
576
|
+
continue;
|
|
577
|
+
const found = new Map();
|
|
578
|
+
let stripped = false;
|
|
579
|
+
const entries = parsed['comments'].map((entry) => {
|
|
580
|
+
if (!isRecord(entry))
|
|
581
|
+
return entry;
|
|
582
|
+
const author = isRecord(entry['author']) ? entry['author'] : undefined;
|
|
583
|
+
if (!('notify' in entry) && !(author !== undefined && 'email' in author))
|
|
584
|
+
return entry;
|
|
585
|
+
stripped = true;
|
|
586
|
+
const email = optionalText(author?.['email']);
|
|
587
|
+
const id = entry['id'];
|
|
588
|
+
if (typeof id === 'string' && id !== '' && email !== null) {
|
|
589
|
+
found.set(id, { email, notify: entry['notify'] === true });
|
|
590
|
+
}
|
|
591
|
+
const kept = without(entry, 'notify');
|
|
592
|
+
if (author !== undefined)
|
|
593
|
+
kept['author'] = without(author, 'email');
|
|
594
|
+
return kept;
|
|
595
|
+
});
|
|
596
|
+
if (!stripped)
|
|
597
|
+
continue;
|
|
598
|
+
const privateFile = commentEmailsFile(records.dataDir, slug);
|
|
599
|
+
const contacts = contactsIn(readFileIfPresentSync(privateFile));
|
|
600
|
+
for (const [id, contact] of found)
|
|
601
|
+
if (!contacts.has(id))
|
|
602
|
+
contacts.set(id, contact);
|
|
603
|
+
if (contacts.size > 0) {
|
|
604
|
+
writeFileAtomicallySync(privateFile, contactsJson(contacts), {
|
|
605
|
+
mode: COMMENT_EMAILS_FILE_MODE,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
writeFileAtomicallySync(file, `${JSON.stringify({ ...parsed, comments: entries }, null, 2)}\n`);
|
|
609
|
+
files += 1;
|
|
610
|
+
}
|
|
611
|
+
return { files };
|
|
612
|
+
}
|
|
613
|
+
/** A copy of a record without one key, every other key where it was. */
|
|
614
|
+
function without(record, key) {
|
|
615
|
+
return Object.fromEntries(Object.entries(record).filter(([name]) => name !== key));
|
|
616
|
+
}
|
|
467
617
|
/** Every comment file a site has, in a stable order. */
|
|
468
618
|
function commentFiles(contentDir) {
|
|
469
619
|
let entries;
|
|
@@ -480,11 +630,6 @@ function commentFiles(contentDir) {
|
|
|
480
630
|
.sort()
|
|
481
631
|
.map((name) => path.join(commentsDirectory(contentDir), name));
|
|
482
632
|
}
|
|
483
|
-
/** One file's comments, read inside a lock the caller already holds. */
|
|
484
|
-
function readCommentFile(file) {
|
|
485
|
-
const source = readFileIfPresentSync(file);
|
|
486
|
-
return source === undefined ? [] : commentsIn(source);
|
|
487
|
-
}
|
|
488
633
|
/** A new comment as the record it becomes, id and all. */
|
|
489
634
|
function commentRecordOf(comment) {
|
|
490
635
|
return {
|
|
@@ -502,20 +647,22 @@ function commentRecordOf(comment) {
|
|
|
502
647
|
...(comment.redacted === undefined ? {} : { redacted: [...comment.redacted] }),
|
|
503
648
|
};
|
|
504
649
|
}
|
|
505
|
-
/**
|
|
506
|
-
|
|
650
|
+
/**
|
|
651
|
+
* A row as `content/` spells it: the post it is on is the file's, not the
|
|
652
|
+
* entry's, and the email and `notify` are `data/`'s.
|
|
653
|
+
*/
|
|
654
|
+
function publishedEntryOf(comment) {
|
|
507
655
|
return {
|
|
508
656
|
id: comment.id,
|
|
509
657
|
source: comment.source,
|
|
510
658
|
kind: comment.kind,
|
|
511
659
|
status: comment.status,
|
|
512
|
-
author: {
|
|
660
|
+
author: { name: comment.author.name, url: comment.author.url, avatar: comment.author.avatar },
|
|
513
661
|
content: { ...comment.content },
|
|
514
662
|
submitted: comment.submitted,
|
|
515
663
|
addressHash: comment.addressHash,
|
|
516
664
|
inReplyTo: comment.inReplyTo,
|
|
517
665
|
url: comment.url,
|
|
518
|
-
notify: comment.notify,
|
|
519
666
|
...(comment.redacted === undefined ? {} : { redacted: [...comment.redacted] }),
|
|
520
667
|
};
|
|
521
668
|
}
|