@geekity/cms 0.3.0 → 0.5.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 +2217 -214
- package/admin/components/fields.njk +165 -0
- package/admin/layouts/base.njk +1 -1
- package/admin/layouts/settings-page.njk +26 -0
- package/admin/layouts/shell.njk +17 -3
- package/admin/pages/account/forgot.njk +47 -0
- package/admin/pages/account/login.njk +26 -0
- package/admin/pages/account/reset.njk +38 -0
- package/admin/pages/account/setup.njk +28 -0
- package/admin/pages/appearance/themes.njk +69 -0
- package/admin/pages/comments/all.njk +133 -0
- package/admin/{layouts/dashboard.njk → pages/dashboard/home.njk} +8 -15
- package/admin/{layouts/document-conflict.njk → pages/documents/conflict.njk} +1 -0
- package/admin/{layouts/document-editor.njk → pages/documents/editor.njk} +75 -59
- package/admin/{layouts/document-list.njk → pages/documents/list.njk} +19 -2
- package/admin/pages/documents/taxonomy.njk +95 -0
- package/admin/pages/federation/followers.njk +238 -0
- package/admin/pages/federation/settings.njk +77 -0
- package/admin/pages/media/library.njk +160 -0
- package/admin/pages/messages/all.njk +110 -0
- package/admin/pages/navigation/menus.njk +129 -0
- package/admin/{layouts → pages}/placeholder.njk +5 -1
- package/admin/pages/settings/discussion.njk +99 -0
- package/admin/pages/settings/email.njk +203 -0
- package/admin/pages/settings/general.njk +29 -0
- package/admin/pages/settings/permalinks.njk +50 -0
- package/admin/pages/settings/reading.njk +64 -0
- package/admin/pages/tools/content-index.njk +89 -0
- package/admin/pages/users/edit.njk +239 -0
- package/admin/pages/users/list.njk +51 -0
- package/admin/pages/users/new.njk +41 -0
- package/admin/static/admin.css +558 -3
- package/admin/static/copy.js +52 -0
- package/admin/static/editor.js +16 -16
- package/admin/static/slug.js +61 -0
- package/dist/access-log.d.ts +44 -0
- package/dist/access-log.d.ts.map +1 -0
- package/dist/access-log.js +71 -0
- package/dist/access-log.js.map +1 -0
- package/dist/admin/accounts.d.ts +413 -0
- package/dist/admin/accounts.d.ts.map +1 -0
- package/dist/admin/accounts.js +680 -0
- package/dist/admin/accounts.js.map +1 -0
- package/dist/admin/appearance.d.ts +80 -0
- package/dist/admin/appearance.d.ts.map +1 -0
- package/dist/admin/appearance.js +136 -0
- package/dist/admin/appearance.js.map +1 -0
- package/dist/admin/comments.d.ts +90 -0
- package/dist/admin/comments.d.ts.map +1 -0
- package/dist/admin/comments.js +244 -0
- package/dist/admin/comments.js.map +1 -0
- package/dist/admin/credentials.d.ts +26 -2
- package/dist/admin/credentials.d.ts.map +1 -1
- package/dist/admin/credentials.js +35 -2
- package/dist/admin/credentials.js.map +1 -1
- package/dist/admin/documents.d.ts +117 -10
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +328 -41
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/federation.d.ts +161 -50
- package/dist/admin/federation.d.ts.map +1 -1
- package/dist/admin/federation.js +235 -93
- package/dist/admin/federation.js.map +1 -1
- package/dist/admin/formatting.d.ts +20 -0
- package/dist/admin/formatting.d.ts.map +1 -0
- package/dist/admin/formatting.js +72 -0
- package/dist/admin/formatting.js.map +1 -0
- package/dist/admin/headers.d.ts +65 -0
- package/dist/admin/headers.d.ts.map +1 -0
- package/dist/admin/headers.js +104 -0
- package/dist/admin/headers.js.map +1 -0
- package/dist/admin/index.d.ts +38 -12
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +25 -7
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/media.d.ts +168 -0
- package/dist/admin/media.d.ts.map +1 -0
- package/dist/admin/media.js +295 -0
- package/dist/admin/media.js.map +1 -0
- package/dist/admin/menu.d.ts +79 -0
- package/dist/admin/menu.d.ts.map +1 -0
- package/dist/admin/menu.js +143 -0
- package/dist/admin/menu.js.map +1 -0
- package/dist/admin/messages.d.ts +80 -0
- package/dist/admin/messages.d.ts.map +1 -0
- package/dist/admin/messages.js +152 -0
- package/dist/admin/messages.js.map +1 -0
- package/dist/admin/navigation.d.ts +155 -0
- package/dist/admin/navigation.d.ts.map +1 -0
- package/dist/admin/navigation.js +292 -0
- package/dist/admin/navigation.js.map +1 -0
- package/dist/admin/preview.d.ts.map +1 -1
- package/dist/admin/preview.js +31 -7
- package/dist/admin/preview.js.map +1 -1
- package/dist/admin/recovery.d.ts +92 -0
- package/dist/admin/recovery.d.ts.map +1 -0
- package/dist/admin/recovery.js +229 -0
- package/dist/admin/recovery.js.map +1 -0
- package/dist/admin/routes.d.ts +0 -20
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +164 -105
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/session.d.ts +17 -3
- package/dist/admin/session.d.ts.map +1 -1
- package/dist/admin/session.js +19 -5
- package/dist/admin/session.js.map +1 -1
- package/dist/admin/settings-discussion.d.ts +37 -0
- package/dist/admin/settings-discussion.d.ts.map +1 -0
- package/dist/admin/settings-discussion.js +117 -0
- package/dist/admin/settings-discussion.js.map +1 -0
- package/dist/admin/settings-email.d.ts +54 -0
- package/dist/admin/settings-email.d.ts.map +1 -0
- package/dist/admin/settings-email.js +189 -0
- package/dist/admin/settings-email.js.map +1 -0
- package/dist/admin/settings-federation.d.ts +48 -0
- package/dist/admin/settings-federation.d.ts.map +1 -0
- package/dist/admin/settings-federation.js +123 -0
- package/dist/admin/settings-federation.js.map +1 -0
- package/dist/admin/settings-general.d.ts +12 -0
- package/dist/admin/settings-general.d.ts.map +1 -0
- package/dist/admin/settings-general.js +48 -0
- package/dist/admin/settings-general.js.map +1 -0
- package/dist/admin/settings-page.d.ts +119 -0
- package/dist/admin/settings-page.d.ts.map +1 -0
- package/dist/admin/settings-page.js +132 -0
- package/dist/admin/settings-page.js.map +1 -0
- package/dist/admin/settings-pages.d.ts +30 -0
- package/dist/admin/settings-pages.d.ts.map +1 -0
- package/dist/admin/settings-pages.js +43 -0
- package/dist/admin/settings-pages.js.map +1 -0
- package/dist/admin/settings-permalinks.d.ts +16 -0
- package/dist/admin/settings-permalinks.d.ts.map +1 -0
- package/dist/admin/settings-permalinks.js +32 -0
- package/dist/admin/settings-permalinks.js.map +1 -0
- package/dist/admin/settings-reading.d.ts +34 -0
- package/dist/admin/settings-reading.d.ts.map +1 -0
- package/dist/admin/settings-reading.js +49 -0
- package/dist/admin/settings-reading.js.map +1 -0
- package/dist/admin/settings.d.ts +339 -96
- package/dist/admin/settings.d.ts.map +1 -1
- package/dist/admin/settings.js +600 -308
- package/dist/admin/settings.js.map +1 -1
- package/dist/admin/store.d.ts +606 -152
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +863 -268
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/taxonomy.d.ts +114 -0
- package/dist/admin/taxonomy.d.ts.map +1 -0
- package/dist/admin/taxonomy.js +289 -0
- package/dist/admin/taxonomy.js.map +1 -0
- package/dist/admin/templates.d.ts +46 -14
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +46 -14
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/throttle.d.ts +84 -0
- package/dist/admin/throttle.d.ts.map +1 -0
- package/dist/admin/throttle.js +143 -0
- package/dist/admin/throttle.js.map +1 -0
- package/dist/admin/tools.d.ts +111 -0
- package/dist/admin/tools.d.ts.map +1 -0
- package/dist/admin/tools.js +182 -0
- package/dist/admin/tools.js.map +1 -0
- package/dist/admin/uploads.d.ts +28 -2
- package/dist/admin/uploads.d.ts.map +1 -1
- package/dist/admin/uploads.js +43 -4
- package/dist/admin/uploads.js.map +1 -1
- package/dist/admin/users.d.ts +100 -13
- package/dist/admin/users.d.ts.map +1 -1
- package/dist/admin/users.js +412 -39
- package/dist/admin/users.js.map +1 -1
- package/dist/cache.d.ts +141 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +207 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +14 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +429 -21
- package/dist/cli.js.map +1 -1
- package/dist/comments/akismet.d.ts +130 -0
- package/dist/comments/akismet.d.ts.map +1 -0
- package/dist/comments/akismet.js +274 -0
- package/dist/comments/akismet.js.map +1 -0
- package/dist/comments/conversation.d.ts +29 -0
- package/dist/comments/conversation.d.ts.map +1 -0
- package/dist/comments/conversation.js +60 -0
- package/dist/comments/conversation.js.map +1 -0
- package/dist/comments/form.d.ts +123 -0
- package/dist/comments/form.d.ts.map +1 -0
- package/dist/comments/form.js +79 -0
- package/dist/comments/form.js.map +1 -0
- package/dist/comments/index.d.ts +16 -0
- package/dist/comments/index.d.ts.map +1 -0
- package/dist/comments/index.js +10 -0
- package/dist/comments/index.js.map +1 -0
- package/dist/comments/markdown.d.ts +8 -0
- package/dist/comments/markdown.d.ts.map +1 -0
- package/dist/comments/markdown.js +85 -0
- package/dist/comments/markdown.js.map +1 -0
- package/dist/comments/moderate.d.ts +71 -0
- package/dist/comments/moderate.d.ts.map +1 -0
- package/dist/comments/moderate.js +88 -0
- package/dist/comments/moderate.js.map +1 -0
- package/dist/comments/policy.d.ts +57 -0
- package/dist/comments/policy.d.ts.map +1 -0
- package/dist/comments/policy.js +80 -0
- package/dist/comments/policy.js.map +1 -0
- package/dist/comments/records.d.ts +266 -0
- package/dist/comments/records.d.ts.map +1 -0
- package/dist/comments/records.js +487 -0
- package/dist/comments/records.js.map +1 -0
- package/dist/comments/routes.d.ts +60 -0
- package/dist/comments/routes.d.ts.map +1 -0
- package/dist/comments/routes.js +212 -0
- package/dist/comments/routes.js.map +1 -0
- package/dist/comments/submission.d.ts +297 -0
- package/dist/comments/submission.d.ts.map +1 -0
- package/dist/comments/submission.js +280 -0
- package/dist/comments/submission.js.map +1 -0
- package/dist/comments/viewer.d.ts +21 -0
- package/dist/comments/viewer.d.ts.map +1 -0
- package/dist/comments/viewer.js +45 -0
- package/dist/comments/viewer.js.map +1 -0
- package/dist/config.d.ts +225 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +121 -6
- package/dist/config.js.map +1 -1
- package/dist/contact/delivery.d.ts +60 -0
- package/dist/contact/delivery.d.ts.map +1 -0
- package/dist/contact/delivery.js +87 -0
- package/dist/contact/delivery.js.map +1 -0
- package/dist/contact/form.d.ts +130 -0
- package/dist/contact/form.d.ts.map +1 -0
- package/dist/contact/form.js +163 -0
- package/dist/contact/form.js.map +1 -0
- package/dist/contact/index.d.ts +17 -0
- package/dist/contact/index.d.ts.map +1 -0
- package/dist/contact/index.js +13 -0
- package/dist/contact/index.js.map +1 -0
- package/dist/contact/records.d.ts +124 -0
- package/dist/contact/records.d.ts.map +1 -0
- package/dist/contact/records.js +218 -0
- package/dist/contact/records.js.map +1 -0
- package/dist/contact/routes.d.ts +17 -0
- package/dist/contact/routes.d.ts.map +1 -0
- package/dist/contact/routes.js +127 -0
- package/dist/contact/routes.js.map +1 -0
- package/dist/contact/submission.d.ts +103 -0
- package/dist/contact/submission.d.ts.map +1 -0
- package/dist/contact/submission.js +166 -0
- package/dist/contact/submission.js.map +1 -0
- package/dist/content/document.d.ts +7 -2
- package/dist/content/document.d.ts.map +1 -1
- package/dist/content/document.js +1 -0
- package/dist/content/document.js.map +1 -1
- package/dist/content/index.d.ts +7 -2
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -1
- package/dist/content/index.js.map +1 -1
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +24 -2
- package/dist/content/markdown.js.map +1 -1
- package/dist/content/parser.d.ts.map +1 -1
- package/dist/content/parser.js +11 -4
- package/dist/content/parser.js.map +1 -1
- package/dist/content/save.d.ts +5 -0
- package/dist/content/save.d.ts.map +1 -1
- package/dist/content/save.js +44 -1
- package/dist/content/save.js.map +1 -1
- package/dist/content/schedule.d.ts +125 -0
- package/dist/content/schedule.d.ts.map +1 -0
- package/dist/content/schedule.js +207 -0
- package/dist/content/schedule.js.map +1 -0
- package/dist/content/search.d.ts +64 -0
- package/dist/content/search.d.ts.map +1 -0
- package/dist/content/search.js +136 -0
- package/dist/content/search.js.map +1 -0
- package/dist/content/store.d.ts +220 -7
- package/dist/content/store.d.ts.map +1 -1
- package/dist/content/store.js +392 -67
- package/dist/content/store.js.map +1 -1
- package/dist/content/sync.d.ts +7 -1
- package/dist/content/sync.d.ts.map +1 -1
- package/dist/content/sync.js +18 -10
- package/dist/content/sync.js.map +1 -1
- package/dist/content/time.d.ts +56 -0
- package/dist/content/time.d.ts.map +1 -0
- package/dist/content/time.js +141 -0
- package/dist/content/time.js.map +1 -0
- package/dist/content/writer.d.ts.map +1 -1
- package/dist/content/writer.js +3 -0
- package/dist/content/writer.js.map +1 -1
- package/dist/env.d.ts +70 -5
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/actor.d.ts +95 -41
- package/dist/federation/actor.d.ts.map +1 -1
- package/dist/federation/actor.js +180 -53
- package/dist/federation/actor.js.map +1 -1
- package/dist/federation/article.d.ts +44 -17
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +98 -46
- package/dist/federation/article.js.map +1 -1
- package/dist/federation/delivery.d.ts +63 -15
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +154 -80
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/federation.d.ts +22 -17
- package/dist/federation/federation.d.ts.map +1 -1
- package/dist/federation/federation.js +98 -77
- package/dist/federation/federation.js.map +1 -1
- package/dist/federation/followers.d.ts +3 -2
- package/dist/federation/followers.d.ts.map +1 -1
- package/dist/federation/followers.js +6 -5
- package/dist/federation/followers.js.map +1 -1
- package/dist/federation/import-wordpress.d.ts +128 -0
- package/dist/federation/import-wordpress.d.ts.map +1 -0
- package/dist/federation/import-wordpress.js +426 -0
- package/dist/federation/import-wordpress.js.map +1 -0
- package/dist/federation/inbox.d.ts +36 -13
- package/dist/federation/inbox.d.ts.map +1 -1
- package/dist/federation/inbox.js +120 -49
- package/dist/federation/inbox.js.map +1 -1
- package/dist/federation/index.d.ts +22 -10
- package/dist/federation/index.d.ts.map +1 -1
- package/dist/federation/index.js +13 -8
- package/dist/federation/index.js.map +1 -1
- package/dist/federation/keys.d.ts +98 -20
- package/dist/federation/keys.d.ts.map +1 -1
- package/dist/federation/keys.js +269 -39
- package/dist/federation/keys.js.map +1 -1
- package/dist/federation/mount.d.ts +46 -10
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +300 -28
- package/dist/federation/mount.js.map +1 -1
- package/dist/federation/paths.d.ts +23 -35
- package/dist/federation/paths.d.ts.map +1 -1
- package/dist/federation/paths.js +26 -39
- package/dist/federation/paths.js.map +1 -1
- package/dist/federation/records.d.ts +223 -0
- package/dist/federation/records.d.ts.map +1 -0
- package/dist/federation/records.js +506 -0
- package/dist/federation/records.js.map +1 -0
- package/dist/federation/relays.d.ts +114 -0
- package/dist/federation/relays.d.ts.map +1 -0
- package/dist/federation/relays.js +290 -0
- package/dist/federation/relays.js.map +1 -0
- package/dist/federation/replies.d.ts +80 -0
- package/dist/federation/replies.d.ts.map +1 -0
- package/dist/federation/replies.js +166 -0
- package/dist/federation/replies.js.map +1 -0
- package/dist/federation/wordpress.d.ts +131 -0
- package/dist/federation/wordpress.d.ts.map +1 -0
- package/dist/federation/wordpress.js +341 -0
- package/dist/federation/wordpress.js.map +1 -0
- package/dist/files/atomic.d.ts +103 -0
- package/dist/files/atomic.d.ts.map +1 -0
- package/dist/files/atomic.js +150 -0
- package/dist/files/atomic.js.map +1 -0
- package/dist/files/index.d.ts +3 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +2 -0
- package/dist/files/index.js.map +1 -0
- package/dist/forms/protection.d.ts +85 -0
- package/dist/forms/protection.d.ts.map +1 -0
- package/dist/forms/protection.js +116 -0
- package/dist/forms/protection.js.map +1 -0
- package/dist/images/icons.d.ts +59 -0
- package/dist/images/icons.d.ts.map +1 -0
- package/dist/images/icons.js +131 -0
- package/dist/images/icons.js.map +1 -0
- package/dist/images/index.d.ts +7 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +4 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/markup.d.ts +47 -0
- package/dist/images/markup.d.ts.map +1 -0
- package/dist/images/markup.js +175 -0
- package/dist/images/markup.js.map +1 -0
- package/dist/images/paths.d.ts +34 -0
- package/dist/images/paths.d.ts.map +1 -0
- package/dist/images/paths.js +41 -0
- package/dist/images/paths.js.map +1 -0
- package/dist/images/variants.d.ts +104 -0
- package/dist/images/variants.d.ts.map +1 -0
- package/dist/images/variants.js +320 -0
- package/dist/images/variants.js.map +1 -0
- package/dist/index.d.ts +123 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +344 -25
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +18 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +34 -3
- package/dist/init.js.map +1 -1
- package/dist/mail/brevo.d.ts +35 -0
- package/dist/mail/brevo.d.ts.map +1 -0
- package/dist/mail/brevo.js +85 -0
- package/dist/mail/brevo.js.map +1 -0
- package/dist/mail/credentials.d.ts +62 -0
- package/dist/mail/credentials.d.ts.map +1 -0
- package/dist/mail/credentials.js +86 -0
- package/dist/mail/credentials.js.map +1 -0
- package/dist/mail/index.d.ts +15 -0
- package/dist/mail/index.d.ts.map +1 -0
- package/dist/mail/index.js +8 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/mail/memory.d.ts +35 -0
- package/dist/mail/memory.d.ts.map +1 -0
- package/dist/mail/memory.js +29 -0
- package/dist/mail/memory.js.map +1 -0
- package/dist/mail/provider.d.ts +80 -0
- package/dist/mail/provider.d.ts.map +1 -0
- package/dist/mail/provider.js +23 -0
- package/dist/mail/provider.js.map +1 -0
- package/dist/mail/service.d.ts +156 -0
- package/dist/mail/service.d.ts.map +1 -0
- package/dist/mail/service.js +265 -0
- package/dist/mail/service.js.map +1 -0
- package/dist/mail/smtp.d.ts +39 -0
- package/dist/mail/smtp.d.ts.map +1 -0
- package/dist/mail/smtp.js +58 -0
- package/dist/mail/smtp.js.map +1 -0
- package/dist/mail/templates.d.ts +91 -0
- package/dist/mail/templates.d.ts.map +1 -0
- package/dist/mail/templates.js +69 -0
- package/dist/mail/templates.js.map +1 -0
- package/dist/notifications/comments.d.ts +66 -0
- package/dist/notifications/comments.d.ts.map +1 -0
- package/dist/notifications/comments.js +151 -0
- package/dist/notifications/comments.js.map +1 -0
- package/dist/notifications/digest.d.ts +135 -0
- package/dist/notifications/digest.d.ts.map +1 -0
- package/dist/notifications/digest.js +287 -0
- package/dist/notifications/digest.js.map +1 -0
- package/dist/notifications/index.d.ts +13 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +8 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/links.d.ts +60 -0
- package/dist/notifications/links.d.ts.map +1 -0
- package/dist/notifications/links.js +63 -0
- package/dist/notifications/links.js.map +1 -0
- package/dist/notifications/optouts.d.ts +48 -0
- package/dist/notifications/optouts.d.ts.map +1 -0
- package/dist/notifications/optouts.js +93 -0
- package/dist/notifications/optouts.js.map +1 -0
- package/dist/notifications/preferences.d.ts +158 -0
- package/dist/notifications/preferences.d.ts.map +1 -0
- package/dist/notifications/preferences.js +195 -0
- package/dist/notifications/preferences.js.map +1 -0
- package/dist/notifications/routes.d.ts +28 -0
- package/dist/notifications/routes.d.ts.map +1 -0
- package/dist/notifications/routes.js +184 -0
- package/dist/notifications/routes.js.map +1 -0
- package/dist/notifications/tokens.d.ts +60 -0
- package/dist/notifications/tokens.d.ts.map +1 -0
- package/dist/notifications/tokens.js +125 -0
- package/dist/notifications/tokens.js.map +1 -0
- package/dist/notify.d.ts +84 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +149 -0
- package/dist/notify.js.map +1 -0
- package/dist/web/archive.d.ts +49 -0
- package/dist/web/archive.d.ts.map +1 -0
- package/dist/web/archive.js +59 -0
- package/dist/web/archive.js.map +1 -0
- package/dist/web/assets.d.ts +6 -5
- package/dist/web/assets.d.ts.map +1 -1
- package/dist/web/assets.js +5 -7
- package/dist/web/assets.js.map +1 -1
- package/dist/web/authors.d.ts +143 -0
- package/dist/web/authors.d.ts.map +1 -0
- package/dist/web/authors.js +178 -0
- package/dist/web/authors.js.map +1 -0
- package/dist/web/comments.d.ts +60 -0
- package/dist/web/comments.d.ts.map +1 -0
- package/dist/web/comments.js +207 -0
- package/dist/web/comments.js.map +1 -0
- package/dist/web/context.d.ts +162 -28
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +123 -26
- package/dist/web/context.js.map +1 -1
- package/dist/web/conversation.d.ts +209 -0
- package/dist/web/conversation.d.ts.map +1 -0
- package/dist/web/conversation.js +557 -0
- package/dist/web/conversation.js.map +1 -0
- package/dist/web/documents.d.ts +36 -6
- package/dist/web/documents.d.ts.map +1 -1
- package/dist/web/documents.js +52 -10
- package/dist/web/documents.js.map +1 -1
- package/dist/web/feed-atom.d.ts +22 -0
- package/dist/web/feed-atom.d.ts.map +1 -0
- package/dist/web/feed-atom.js +83 -0
- package/dist/web/feed-atom.js.map +1 -0
- package/dist/web/feed-item.d.ts +145 -0
- package/dist/web/feed-item.d.ts.map +1 -0
- package/dist/web/feed-item.js +120 -0
- package/dist/web/feed-item.js.map +1 -0
- package/dist/web/feed-json.d.ts +79 -0
- package/dist/web/feed-json.d.ts.map +1 -0
- package/dist/web/feed-json.js +53 -0
- package/dist/web/feed-json.js.map +1 -0
- package/dist/web/feed-rss.d.ts +26 -0
- package/dist/web/feed-rss.d.ts.map +1 -0
- package/dist/web/feed-rss.js +203 -0
- package/dist/web/feed-rss.js.map +1 -0
- package/dist/web/feed-source.d.ts +244 -0
- package/dist/web/feed-source.d.ts.map +1 -0
- package/dist/web/feed-source.js +201 -0
- package/dist/web/feed-source.js.map +1 -0
- package/dist/web/feed-xml.d.ts +63 -0
- package/dist/web/feed-xml.d.ts.map +1 -0
- package/dist/web/feed-xml.js +116 -0
- package/dist/web/feed-xml.js.map +1 -0
- package/dist/web/feeds.d.ts +31 -96
- package/dist/web/feeds.d.ts.map +1 -1
- package/dist/web/feeds.js +94 -198
- package/dist/web/feeds.js.map +1 -1
- package/dist/web/health.d.ts +31 -0
- package/dist/web/health.d.ts.map +1 -0
- package/dist/web/health.js +44 -0
- package/dist/web/health.js.map +1 -0
- package/dist/web/index.d.ts +30 -10
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +18 -7
- package/dist/web/index.js.map +1 -1
- package/dist/web/navigation.d.ts +195 -0
- package/dist/web/navigation.d.ts.map +1 -0
- package/dist/web/navigation.js +300 -0
- package/dist/web/navigation.js.map +1 -0
- package/dist/web/negotiate.d.ts +31 -0
- package/dist/web/negotiate.d.ts.map +1 -1
- package/dist/web/negotiate.js +31 -11
- package/dist/web/negotiate.js.map +1 -1
- package/dist/web/recent.d.ts +43 -0
- package/dist/web/recent.d.ts.map +1 -0
- package/dist/web/recent.js +37 -0
- package/dist/web/recent.js.map +1 -0
- package/dist/web/render.d.ts +203 -9
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +341 -17
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts +25 -8
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +854 -93
- package/dist/web/routes.js.map +1 -1
- package/dist/web/sanitize.d.ts +19 -0
- package/dist/web/sanitize.d.ts.map +1 -0
- package/dist/web/sanitize.js +272 -0
- package/dist/web/sanitize.js.map +1 -0
- package/dist/web/search.d.ts +79 -0
- package/dist/web/search.d.ts.map +1 -0
- package/dist/web/search.js +94 -0
- package/dist/web/search.js.map +1 -0
- package/dist/web/sitemap.d.ts +120 -0
- package/dist/web/sitemap.d.ts.map +1 -0
- package/dist/web/sitemap.js +199 -0
- package/dist/web/sitemap.js.map +1 -0
- package/dist/web/taxonomy.d.ts +152 -0
- package/dist/web/taxonomy.d.ts.map +1 -0
- package/dist/web/taxonomy.js +237 -0
- package/dist/web/taxonomy.js.map +1 -0
- package/dist/web/templates.d.ts +54 -16
- package/dist/web/templates.d.ts.map +1 -1
- package/dist/web/templates.js +109 -28
- package/dist/web/templates.js.map +1 -1
- package/dist/web/themes.d.ts +226 -0
- package/dist/web/themes.d.ts.map +1 -0
- package/dist/web/themes.js +314 -0
- package/dist/web/themes.js.map +1 -0
- package/dist/webmention/discovery.d.ts +51 -0
- package/dist/webmention/discovery.d.ts.map +1 -0
- package/dist/webmention/discovery.js +176 -0
- package/dist/webmention/discovery.js.map +1 -0
- package/dist/webmention/html.d.ts +71 -0
- package/dist/webmention/html.d.ts.map +1 -0
- package/dist/webmention/html.js +285 -0
- package/dist/webmention/html.js.map +1 -0
- package/dist/webmention/index.d.ts +12 -0
- package/dist/webmention/index.d.ts.map +1 -0
- package/dist/webmention/index.js +8 -0
- package/dist/webmention/index.js.map +1 -0
- package/dist/webmention/links.d.ts +18 -0
- package/dist/webmention/links.d.ts.map +1 -0
- package/dist/webmention/links.js +64 -0
- package/dist/webmention/links.js.map +1 -0
- package/dist/webmention/microformats.d.ts +93 -0
- package/dist/webmention/microformats.d.ts.map +1 -0
- package/dist/webmention/microformats.js +376 -0
- package/dist/webmention/microformats.js.map +1 -0
- package/dist/webmention/receive.d.ts +141 -0
- package/dist/webmention/receive.d.ts.map +1 -0
- package/dist/webmention/receive.js +264 -0
- package/dist/webmention/receive.js.map +1 -0
- package/dist/webmention/routes.d.ts +37 -0
- package/dist/webmention/routes.d.ts.map +1 -0
- package/dist/webmention/routes.js +75 -0
- package/dist/webmention/routes.js.map +1 -0
- package/dist/webmention/service.d.ts +94 -0
- package/dist/webmention/service.d.ts.map +1 -0
- package/dist/webmention/service.js +207 -0
- package/dist/webmention/service.js.map +1 -0
- package/package.json +11 -6
- package/templates/site/content/_data/site.json +8 -0
- package/templates/site/content/pages/about.md +7 -0
- package/templates/site/content/posts/2026-01-01-hello-world.md +5 -3
- package/templates/site/geekity.config.ts +11 -1
- package/templates/site/tsconfig.json +1 -1
- package/themes/default/README.md +1216 -50
- package/themes/default/layouts/404.njk +2 -3
- package/themes/default/layouts/author.njk +36 -0
- package/themes/default/layouts/base.njk +161 -31
- package/themes/default/layouts/category.njk +27 -0
- package/themes/default/layouts/front-page.njk +56 -0
- package/themes/default/layouts/home.njk +17 -1
- package/themes/default/layouts/page.njk +57 -5
- package/themes/default/layouts/post.njk +78 -19
- package/themes/default/layouts/search.njk +57 -0
- package/themes/default/layouts/tag.njk +3 -6
- package/themes/default/mail/comment-digest.html.njk +37 -0
- package/themes/default/mail/comment-digest.subject.njk +3 -0
- package/themes/default/mail/comment-digest.txt.njk +40 -0
- package/themes/default/mail/comment-pending.html.njk +27 -0
- package/themes/default/mail/comment-pending.subject.njk +2 -0
- package/themes/default/mail/comment-pending.txt.njk +24 -0
- package/themes/default/mail/comment-reply.html.njk +21 -0
- package/themes/default/mail/comment-reply.subject.njk +2 -0
- package/themes/default/mail/comment-reply.txt.njk +22 -0
- package/themes/default/mail/contact-message.html.njk +24 -0
- package/themes/default/mail/contact-message.subject.njk +5 -0
- package/themes/default/mail/contact-message.txt.njk +20 -0
- package/themes/default/mail/password-changed.html.njk +18 -0
- package/themes/default/mail/password-changed.subject.njk +2 -0
- package/themes/default/mail/password-changed.txt.njk +17 -0
- package/themes/default/mail/password-reset.html.njk +22 -0
- package/themes/default/mail/password-reset.subject.njk +2 -0
- package/themes/default/mail/password-reset.txt.njk +18 -0
- package/themes/default/mail/test.html.njk +14 -0
- package/themes/default/mail/test.subject.njk +2 -0
- package/themes/default/mail/test.txt.njk +12 -0
- package/themes/default/partials/archive.njk +27 -0
- package/themes/default/partials/bio.njk +71 -0
- package/themes/default/partials/comment-form.njk +137 -0
- package/themes/default/partials/contact-form.njk +88 -0
- package/themes/default/partials/conversation.njk +139 -0
- package/themes/default/partials/feeds.njk +26 -0
- package/themes/default/partials/jsonld.njk +120 -0
- package/themes/default/partials/menu.njk +32 -0
- package/themes/default/partials/pagination.njk +12 -6
- package/themes/default/partials/post-list.njk +51 -18
- package/themes/default/partials/search-form.njk +22 -0
- package/themes/default/partials/tags.njk +26 -5
- package/themes/default/static/highlight.js +4 -0
- package/themes/default/static/style.css +1142 -194
- package/themes/default/theme.json +9 -0
- package/admin/layouts/federation.njk +0 -145
- package/admin/layouts/login.njk +0 -23
- package/admin/layouts/settings.njk +0 -116
- package/admin/layouts/setup.njk +0 -27
- package/admin/layouts/users.njk +0 -91
- /package/admin/{layouts/_flash.njk → components/flash.njk} +0 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { readdirSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { COMMENT_KINDS, COMMENT_SOURCES, COMMENT_STATUSES } from '../admin/store.js';
|
|
5
|
+
import { readFileIfPresentSync, withFileLock, writeFileAtomicallySync } from '../files/atomic.js';
|
|
6
|
+
import { hashClientAddress } from '../forms/protection.js';
|
|
7
|
+
/**
|
|
8
|
+
* Native comments as the files that hold them, and the one door into them.
|
|
9
|
+
*
|
|
10
|
+
* decision-9 makes the filesystem the source of truth for everything durable,
|
|
11
|
+
* and a comment somebody typed into this site is exactly that: nothing else
|
|
12
|
+
* holds it, and no other server can be asked for it again. So it lives under
|
|
13
|
+
* `content/_data/comments/`, one JSON file per post, which puts it in git
|
|
14
|
+
* beside the posts, hands it to an Eleventy build of the same directory, and
|
|
15
|
+
* leaves SQLite holding nothing but an index — one rebuilt from these files on
|
|
16
|
+
* every boot and deletable at rest.
|
|
17
|
+
*
|
|
18
|
+
* The two rules `federation/records.ts` follows apply here for the same
|
|
19
|
+
* reasons, and are why every writer in this module goes through
|
|
20
|
+
* {@link withFileLock}:
|
|
21
|
+
*
|
|
22
|
+
* - **The file first, the index inside the same step.** Two comments arriving
|
|
23
|
+
* at once cannot leave the index saying something the file does not.
|
|
24
|
+
* - **The index is derived, never told.** A row is built from a file entry by
|
|
25
|
+
* {@link commentFrom} whether it is being written for the first time or
|
|
26
|
+
* rebuilt at boot.
|
|
27
|
+
*
|
|
28
|
+
* The entry shape is deliberately wider than a form submission. A webmention
|
|
29
|
+
* (TASK-51) is somebody else's post pointing at this one: it has a `url` and no
|
|
30
|
+
* email, it may be a like rather than a reply, and it belongs in the same file
|
|
31
|
+
* and the same thread. So every entry says its `source` and its `kind`, and
|
|
32
|
+
* nothing here assumes a person filled in a form.
|
|
33
|
+
*
|
|
34
|
+
* Three things write comments — the form under a post, the webmention endpoint
|
|
35
|
+
* and a moderator's reply on the admin screen — and they all go through
|
|
36
|
+
* {@link intakeComment}, which is the whole of what happens between a proposed
|
|
37
|
+
* comment and a comment existing: the address hashed, the approved-author rule,
|
|
38
|
+
* the {@link CommentChecker}, one verdict-to-status rule, the write, and the
|
|
39
|
+
* message to whoever was waiting to hear. What the callers keep is what is
|
|
40
|
+
* really theirs — parsing a form and its cheap defences, fetching and verifying
|
|
41
|
+
* a source, and knowing who is signed in.
|
|
42
|
+
*/
|
|
43
|
+
/** Where the comment files live, relative to the content directory. */
|
|
44
|
+
export const COMMENTS_DATA_DIRECTORY = '_data/comments';
|
|
45
|
+
/** The absolute path of one site's `content/_data/comments`. */
|
|
46
|
+
export function commentsDirectory(contentDir) {
|
|
47
|
+
return path.join(contentDir, ...COMMENTS_DATA_DIRECTORY.split('/'));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The absolute path of one post's comment file.
|
|
51
|
+
*
|
|
52
|
+
* Named by the post's slug, which is what a post keeps for its whole life: the
|
|
53
|
+
* editor gives an existing document the slug it already has, so the file that
|
|
54
|
+
* holds a post's comments never has to move. A slug carrying anything a
|
|
55
|
+
* filename should not is refused rather than sanitised, because two slugs that
|
|
56
|
+
* sanitised to one name would silently share a thread.
|
|
57
|
+
*/
|
|
58
|
+
export function commentsFile(contentDir, slug) {
|
|
59
|
+
if (!SAFE_SLUG.test(slug)) {
|
|
60
|
+
throw new Error(`"${slug}" is not a slug a comment file can be named after.`);
|
|
61
|
+
}
|
|
62
|
+
return path.join(commentsDirectory(contentDir), `${slug}.json`);
|
|
63
|
+
}
|
|
64
|
+
/** Slugs a file may be named after: what {@link slugify} produces, and no more. */
|
|
65
|
+
const SAFE_SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
66
|
+
/**
|
|
67
|
+
* One post's comments, as its file says them, oldest first.
|
|
68
|
+
*
|
|
69
|
+
* A missing file is no comments, which is what almost every post has. Unlike
|
|
70
|
+
* `followers.json`, an entry that will not parse is dropped rather than
|
|
71
|
+
* throwing: a comment file is public, in git and editable by hand, and a typo
|
|
72
|
+
* in one post's file should cost that entry rather than take the whole site
|
|
73
|
+
* down on the next boot. Nothing irreplaceable is at stake — a comment that
|
|
74
|
+
* cannot be read is one nobody could have shown either way.
|
|
75
|
+
*/
|
|
76
|
+
export function readComments(contentDir, slug) {
|
|
77
|
+
const source = readFileIfPresentSync(commentsFile(contentDir, slug));
|
|
78
|
+
if (source === undefined)
|
|
79
|
+
return [];
|
|
80
|
+
return commentsIn(source);
|
|
81
|
+
}
|
|
82
|
+
/** One post's comment file as the post it names and the comments it holds. */
|
|
83
|
+
function fileContents(source) {
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(source);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { post: '', comments: [] };
|
|
90
|
+
}
|
|
91
|
+
if (!isRecord(parsed))
|
|
92
|
+
return { post: '', comments: [] };
|
|
93
|
+
const entries = parsed['comments'];
|
|
94
|
+
return {
|
|
95
|
+
post: typeof parsed['post'] === 'string' ? parsed['post'] : '',
|
|
96
|
+
comments: Array.isArray(entries)
|
|
97
|
+
? entries.map(commentFrom).filter((entry) => entry !== undefined)
|
|
98
|
+
: [],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** Just the comments, for the readers that have the post already. */
|
|
102
|
+
function commentsIn(source) {
|
|
103
|
+
return fileContents(source).comments;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* One file entry as the record it is, or `undefined` when it is not one.
|
|
107
|
+
*
|
|
108
|
+
* An entry needs an id and a name and nothing else: everything a hand-written
|
|
109
|
+
* file leaves out gets the value that means "the site was not told", so a
|
|
110
|
+
* template printing it has something to print. An entry with no id is refused,
|
|
111
|
+
* because an id is what a reply names and what a moderation action moves.
|
|
112
|
+
*/
|
|
113
|
+
function commentFrom(value) {
|
|
114
|
+
if (!isRecord(value))
|
|
115
|
+
return undefined;
|
|
116
|
+
const id = value['id'];
|
|
117
|
+
if (typeof id !== 'string' || id === '')
|
|
118
|
+
return undefined;
|
|
119
|
+
const author = isRecord(value['author']) ? value['author'] : {};
|
|
120
|
+
const content = isRecord(value['content']) ? value['content'] : {};
|
|
121
|
+
const name = typeof author['name'] === 'string' ? author['name'] : '';
|
|
122
|
+
if (name === '')
|
|
123
|
+
return undefined;
|
|
124
|
+
return {
|
|
125
|
+
id,
|
|
126
|
+
source: oneOf(value['source'], COMMENT_SOURCES, 'comment'),
|
|
127
|
+
kind: oneOf(value['kind'], COMMENT_KINDS, 'reply'),
|
|
128
|
+
// An entry that does not say where it stands is waiting: showing something
|
|
129
|
+
// nobody approved would be the one mistake this cannot make.
|
|
130
|
+
status: oneOf(value['status'], COMMENT_STATUSES, 'pending'),
|
|
131
|
+
author: {
|
|
132
|
+
name,
|
|
133
|
+
url: optionalText(author['url']),
|
|
134
|
+
email: optionalText(author['email']),
|
|
135
|
+
avatar: optionalText(author['avatar']),
|
|
136
|
+
},
|
|
137
|
+
content: {
|
|
138
|
+
markdown: typeof content['markdown'] === 'string' ? content['markdown'] : '',
|
|
139
|
+
html: typeof content['html'] === 'string' ? content['html'] : '',
|
|
140
|
+
},
|
|
141
|
+
submitted: typeof value['submitted'] === 'string' ? value['submitted'] : '',
|
|
142
|
+
addressHash: optionalText(value['addressHash']),
|
|
143
|
+
inReplyTo: optionalText(value['inReplyTo']),
|
|
144
|
+
url: optionalText(value['url']),
|
|
145
|
+
// An entry that does not say means one that never asked: a comment file
|
|
146
|
+
// written before TASK-55 shipped, or edited by hand, has nobody waiting on
|
|
147
|
+
// it, and defaulting the other way would email people who never opted in.
|
|
148
|
+
notify: value['notify'] === true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Record a comment: append it to its post's file and index it.
|
|
153
|
+
*
|
|
154
|
+
* The id is minted here unless the caller brought one, and it is a UUID rather
|
|
155
|
+
* than a counter so two comments written at once on two machines holding the
|
|
156
|
+
* same content directory can never be given the same name.
|
|
157
|
+
*/
|
|
158
|
+
export async function addComment(records, comment) {
|
|
159
|
+
const { admin, contentDir } = records;
|
|
160
|
+
const file = commentsFile(contentDir, comment.slug);
|
|
161
|
+
const stored = {
|
|
162
|
+
...commentRecordOf(comment),
|
|
163
|
+
slug: comment.slug,
|
|
164
|
+
permalink: comment.permalink,
|
|
165
|
+
};
|
|
166
|
+
return await withFileLock(file, () => {
|
|
167
|
+
const held = readCommentFile(file);
|
|
168
|
+
writeFileAtomicallySync(file, commentsJson(comment.permalink, [...held, entryOf(stored)]));
|
|
169
|
+
admin.putComment(stored);
|
|
170
|
+
return stored;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The one door into `content/_data/comments/`.
|
|
175
|
+
*
|
|
176
|
+
* Everything between a proposed comment and a comment existing happens here,
|
|
177
|
+
* so the form, the webmention endpoint and the admin screen cannot drift into
|
|
178
|
+
* three different answers to the same four questions.
|
|
179
|
+
*
|
|
180
|
+
* **Where the site would put it, before anybody judged it.** A form comment
|
|
181
|
+
* whose name and email together have been approved before is approved again —
|
|
182
|
+
* WordPress's rule, and the whole of the auto-approval decision. A webmention
|
|
183
|
+
* waits, because a page linking here is as much a stranger's words as a form
|
|
184
|
+
* submission is. A moderator's reply is approved, because the person writing it
|
|
185
|
+
* is the person who would have approved it.
|
|
186
|
+
*
|
|
187
|
+
* **What the checker says.** Everything but a moderator's own words goes
|
|
188
|
+
* through {@link CommentChecker}; a checker that is down or throws is no
|
|
189
|
+
* opinion, so a service having a bad afternoon never stops a site taking
|
|
190
|
+
* comments. A moderator's reply is not offered to it at all: a spam service has
|
|
191
|
+
* no say in what the owner of the site says.
|
|
192
|
+
*
|
|
193
|
+
* **The one verdict-to-status rule.**
|
|
194
|
+
*
|
|
195
|
+
* | Verdict | A new comment | A webmention sent again |
|
|
196
|
+
* | --------- | ------------------------------------ | ------------------------------------ |
|
|
197
|
+
* | `discard` | Nothing is stored. | The held entry is deleted. |
|
|
198
|
+
* | `spam` | Filed as spam. | Filed as spam. |
|
|
199
|
+
* | `ham` | Approved. | The moderator's decision stands. |
|
|
200
|
+
* | `unknown` | Where the site would have put it. | The moderator's decision stands. |
|
|
201
|
+
*
|
|
202
|
+
* A source re-sending its webmention must not take an approved mention back
|
|
203
|
+
* into the queue, and must not quietly let a spam one out. A checker that has
|
|
204
|
+
* changed its mind to `spam` is the one thing that moves it, because that is a
|
|
205
|
+
* new fact about the content rather than a repeat of an old one.
|
|
206
|
+
*
|
|
207
|
+
* **Who hears about it.** A new entry that is waiting sends the moderation
|
|
208
|
+
* notice, once. A webmention that was merely rewritten sends nothing — the
|
|
209
|
+
* moderators heard the first time and it has been in the queue ever since —
|
|
210
|
+
* and neither does one that was approved or filed as spam, because neither is
|
|
211
|
+
* waiting for anybody. A new comment that is approved instead tells whoever it
|
|
212
|
+
* answers, when they asked to be told.
|
|
213
|
+
*/
|
|
214
|
+
export async function intakeComment(options) {
|
|
215
|
+
const { records, origin, comment } = options;
|
|
216
|
+
// What this site would do about it on its own, which is both what a checker
|
|
217
|
+
// is shown and what `unknown` falls back to.
|
|
218
|
+
const proposed = {
|
|
219
|
+
...comment,
|
|
220
|
+
status: siteStatusFor(origin, records, comment),
|
|
221
|
+
addressHash: hashClientAddress(options.dataDir, options.address),
|
|
222
|
+
};
|
|
223
|
+
const held = origin === 'webmention' ? heldWebmention(records, comment.slug, comment.url) : undefined;
|
|
224
|
+
const verdict = origin === 'moderator' ? 'unknown' : await ask(options, proposed);
|
|
225
|
+
if (verdict === 'discard') {
|
|
226
|
+
if (held === undefined)
|
|
227
|
+
return { kind: 'discarded', removed: false };
|
|
228
|
+
await deleteComment(records, held.id);
|
|
229
|
+
return { kind: 'discarded', removed: true };
|
|
230
|
+
}
|
|
231
|
+
const status = statusFor(verdict, proposed.status, held);
|
|
232
|
+
if (held !== undefined) {
|
|
233
|
+
// Its id, its post and its source never move: that is what makes it the
|
|
234
|
+
// same comment. What the page now says about itself replaces what it said.
|
|
235
|
+
const moved = await updateComment(records, held.id, {
|
|
236
|
+
kind: comment.kind,
|
|
237
|
+
status,
|
|
238
|
+
author: comment.author,
|
|
239
|
+
content: comment.content,
|
|
240
|
+
submitted: comment.submitted,
|
|
241
|
+
});
|
|
242
|
+
return moved === undefined
|
|
243
|
+
? { kind: 'gone' }
|
|
244
|
+
: { kind: 'stored', comment: moved, created: false };
|
|
245
|
+
}
|
|
246
|
+
const stored = await addComment(records, { ...proposed, status });
|
|
247
|
+
announce(options.notices, stored);
|
|
248
|
+
return { kind: 'stored', comment: stored, created: true };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* The webmention this site already holds from that page, if any.
|
|
252
|
+
*
|
|
253
|
+
* The source URL is a webmention's identity (doc-7), so this is what stops a
|
|
254
|
+
* blog post that is edited and re-sent adding a second entry — and what the
|
|
255
|
+
* endpoint asks before it fetches, so a source that has stopped linking here
|
|
256
|
+
* knows what to take away.
|
|
257
|
+
*/
|
|
258
|
+
export function heldWebmention(records, slug, source) {
|
|
259
|
+
if (source === null || source === '')
|
|
260
|
+
return undefined;
|
|
261
|
+
return records.admin
|
|
262
|
+
.listCommentsFor(slug)
|
|
263
|
+
.find((comment) => comment.source === 'webmention' && comment.url === source);
|
|
264
|
+
}
|
|
265
|
+
/** Where this site's own rules put a comment, before a checker has spoken. */
|
|
266
|
+
function siteStatusFor(origin, records, comment) {
|
|
267
|
+
if (origin === 'moderator')
|
|
268
|
+
return 'approved';
|
|
269
|
+
if (origin === 'webmention')
|
|
270
|
+
return 'pending';
|
|
271
|
+
return records.admin.hasApprovedAuthor(comment.author.name, comment.author.email)
|
|
272
|
+
? 'approved'
|
|
273
|
+
: 'pending';
|
|
274
|
+
}
|
|
275
|
+
/** The verdict-to-status rule, in the one place it is written. */
|
|
276
|
+
function statusFor(verdict, site, held) {
|
|
277
|
+
if (verdict === 'spam')
|
|
278
|
+
return 'spam';
|
|
279
|
+
if (held !== undefined)
|
|
280
|
+
return held.status;
|
|
281
|
+
return verdict === 'ham' ? 'approved' : site;
|
|
282
|
+
}
|
|
283
|
+
/** Ask the checker, if there is one, and treat a broken one as no opinion. */
|
|
284
|
+
async function ask(options, comment) {
|
|
285
|
+
const checker = options.checker;
|
|
286
|
+
if (checker === undefined)
|
|
287
|
+
return 'unknown';
|
|
288
|
+
const post = options.post;
|
|
289
|
+
const submission = {
|
|
290
|
+
comment,
|
|
291
|
+
post: {
|
|
292
|
+
slug: comment.slug,
|
|
293
|
+
title: post?.title ?? comment.slug,
|
|
294
|
+
url: post?.url ?? absolute(comment.permalink, options.baseUrl),
|
|
295
|
+
},
|
|
296
|
+
address: options.address,
|
|
297
|
+
userAgent: options.userAgent,
|
|
298
|
+
referrer: options.referrer,
|
|
299
|
+
baseUrl: options.baseUrl,
|
|
300
|
+
};
|
|
301
|
+
try {
|
|
302
|
+
return await checker.check(submission);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
// A checker that is down must not stop a site taking comments; the comment
|
|
306
|
+
// falls back to whatever the site's own rules said about it.
|
|
307
|
+
const logger = options.logger ?? console;
|
|
308
|
+
logger.warn(`The comment checker refused to answer: ${messageOf(error)}`);
|
|
309
|
+
return 'unknown';
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/** Tell whoever was waiting on a comment that has just been written. */
|
|
313
|
+
function announce(notices, stored) {
|
|
314
|
+
if (notices === undefined)
|
|
315
|
+
return;
|
|
316
|
+
// Only `pending`: one a checker filed as spam is not waiting for anybody,
|
|
317
|
+
// and one it said to discard was never stored at all.
|
|
318
|
+
if (stored.status === 'pending')
|
|
319
|
+
notices.pending(stored);
|
|
320
|
+
// A comment the site let straight through is on the page already, so
|
|
321
|
+
// whoever it answers hears about it now rather than at a moderator's hand.
|
|
322
|
+
else if (stored.status === 'approved')
|
|
323
|
+
notices.replyApproved(stored);
|
|
324
|
+
}
|
|
325
|
+
/** A site-root path as an absolute URL. */
|
|
326
|
+
function absolute(pathname, baseUrl) {
|
|
327
|
+
try {
|
|
328
|
+
return new URL(pathname, baseUrl).href;
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
return pathname;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function messageOf(error) {
|
|
335
|
+
return error instanceof Error ? error.message : String(error);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Move a stored comment: approve it, mark it spam, put it back, or rewrite
|
|
339
|
+
* what it says.
|
|
340
|
+
*
|
|
341
|
+
* The rewrite is what a webmention needs (TASK-51): a page that links here and
|
|
342
|
+
* is then edited sends its webmention again, and what this site shows should
|
|
343
|
+
* be what that page says now rather than what it said last month. Its id, the
|
|
344
|
+
* post it is on and the source it came from never move, which is what makes it
|
|
345
|
+
* the same comment.
|
|
346
|
+
*
|
|
347
|
+
* Returns the comment as it now stands, or `undefined` when the index has no
|
|
348
|
+
* such comment. The index is what says which post's file to open, which is the
|
|
349
|
+
* one thing an id alone does not carry.
|
|
350
|
+
*/
|
|
351
|
+
export async function updateComment(records, id, change) {
|
|
352
|
+
const { admin, contentDir } = records;
|
|
353
|
+
const known = admin.getComment(id);
|
|
354
|
+
if (known === undefined)
|
|
355
|
+
return undefined;
|
|
356
|
+
const file = commentsFile(contentDir, known.slug);
|
|
357
|
+
return await withFileLock(file, () => {
|
|
358
|
+
const held = readCommentFile(file);
|
|
359
|
+
const at = held.findIndex((entry) => entry.id === id);
|
|
360
|
+
if (at === -1)
|
|
361
|
+
return undefined;
|
|
362
|
+
const moved = { ...known, ...held[at], ...change };
|
|
363
|
+
writeFileAtomicallySync(file, commentsJson(known.permalink, held.with(at, entryOf(moved))));
|
|
364
|
+
admin.putComment(moved);
|
|
365
|
+
return moved;
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Forget a comment altogether.
|
|
370
|
+
*
|
|
371
|
+
* Returns whether there was anything to forget. The index is emptied of the id
|
|
372
|
+
* whether or not the file held it, so a row left over from a file somebody
|
|
373
|
+
* edited by hand cannot go on being shown.
|
|
374
|
+
*/
|
|
375
|
+
export async function deleteComment(records, id) {
|
|
376
|
+
const { admin, contentDir } = records;
|
|
377
|
+
const known = admin.getComment(id);
|
|
378
|
+
if (known === undefined)
|
|
379
|
+
return false;
|
|
380
|
+
const file = commentsFile(contentDir, known.slug);
|
|
381
|
+
return await withFileLock(file, () => {
|
|
382
|
+
const held = readCommentFile(file);
|
|
383
|
+
const next = held.filter((entry) => entry.id !== id);
|
|
384
|
+
if (next.length !== held.length) {
|
|
385
|
+
writeFileAtomicallySync(file, commentsJson(known.permalink, next));
|
|
386
|
+
}
|
|
387
|
+
const indexed = admin.deleteComment(id);
|
|
388
|
+
return next.length !== held.length || indexed;
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Rebuild the comment index from the files.
|
|
393
|
+
*
|
|
394
|
+
* This is what makes `data/geekity.db` disposable for comments (decision-9):
|
|
395
|
+
* the table holds nothing but what these files say, so every boot throws away
|
|
396
|
+
* what it holds and reads them again. Editing a comment file by hand and
|
|
397
|
+
* restarting is therefore a supported thing to do, and a database deleted at
|
|
398
|
+
* rest costs a site nothing.
|
|
399
|
+
*
|
|
400
|
+
* Exported so `geekity rebuild` can call the very function boot calls, and
|
|
401
|
+
* synchronous because both callers are.
|
|
402
|
+
*/
|
|
403
|
+
export function rebuildCommentIndexes(records) {
|
|
404
|
+
const { admin, contentDir } = records;
|
|
405
|
+
const comments = [];
|
|
406
|
+
for (const file of commentFiles(contentDir)) {
|
|
407
|
+
const source = readFileIfPresentSync(file);
|
|
408
|
+
if (source === undefined)
|
|
409
|
+
continue;
|
|
410
|
+
const slug = path.basename(file, '.json');
|
|
411
|
+
const held = fileContents(source);
|
|
412
|
+
for (const entry of held.comments) {
|
|
413
|
+
comments.push({ ...entry, slug, permalink: held.post });
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
admin.replaceComments(comments);
|
|
417
|
+
return { comments: comments.length };
|
|
418
|
+
}
|
|
419
|
+
/** Every comment file a site has, in a stable order. */
|
|
420
|
+
function commentFiles(contentDir) {
|
|
421
|
+
let entries;
|
|
422
|
+
try {
|
|
423
|
+
entries = readdirSync(commentsDirectory(contentDir));
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
// No directory is no comments, which is what a site nobody has answered
|
|
427
|
+
// has. Every other way this can fail fails again on the next boot.
|
|
428
|
+
return [];
|
|
429
|
+
}
|
|
430
|
+
return entries
|
|
431
|
+
.filter((name) => name.endsWith('.json') && SAFE_SLUG.test(name.slice(0, -'.json'.length)))
|
|
432
|
+
.sort()
|
|
433
|
+
.map((name) => path.join(commentsDirectory(contentDir), name));
|
|
434
|
+
}
|
|
435
|
+
/** One file's comments, read inside a lock the caller already holds. */
|
|
436
|
+
function readCommentFile(file) {
|
|
437
|
+
const source = readFileIfPresentSync(file);
|
|
438
|
+
return source === undefined ? [] : commentsIn(source);
|
|
439
|
+
}
|
|
440
|
+
/** A new comment as the record it becomes, id and all. */
|
|
441
|
+
function commentRecordOf(comment) {
|
|
442
|
+
return {
|
|
443
|
+
id: comment.id ?? randomUUID(),
|
|
444
|
+
source: comment.source,
|
|
445
|
+
kind: comment.kind,
|
|
446
|
+
status: comment.status,
|
|
447
|
+
author: { ...comment.author },
|
|
448
|
+
content: { ...comment.content },
|
|
449
|
+
submitted: comment.submitted,
|
|
450
|
+
addressHash: comment.addressHash,
|
|
451
|
+
inReplyTo: comment.inReplyTo,
|
|
452
|
+
url: comment.url,
|
|
453
|
+
notify: comment.notify,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
/** A row as the file spells it: the post it is on is the file's, not the entry's. */
|
|
457
|
+
function entryOf(comment) {
|
|
458
|
+
return {
|
|
459
|
+
id: comment.id,
|
|
460
|
+
source: comment.source,
|
|
461
|
+
kind: comment.kind,
|
|
462
|
+
status: comment.status,
|
|
463
|
+
author: { ...comment.author },
|
|
464
|
+
content: { ...comment.content },
|
|
465
|
+
submitted: comment.submitted,
|
|
466
|
+
addressHash: comment.addressHash,
|
|
467
|
+
inReplyTo: comment.inReplyTo,
|
|
468
|
+
url: comment.url,
|
|
469
|
+
notify: comment.notify,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
/** One post's comments as the file spells them: indented, newline ended. */
|
|
473
|
+
function commentsJson(permalink, comments) {
|
|
474
|
+
return `${JSON.stringify({ post: permalink, comments }, null, 2)}\n`;
|
|
475
|
+
}
|
|
476
|
+
/** A value the file may have left out, as the text it holds or `null`. */
|
|
477
|
+
function optionalText(value) {
|
|
478
|
+
return typeof value === 'string' && value !== '' ? value : null;
|
|
479
|
+
}
|
|
480
|
+
/** A stored enumeration value, or the fallback for one this version does not know. */
|
|
481
|
+
function oneOf(value, allowed, fallback) {
|
|
482
|
+
return allowed.includes(value) ? value : fallback;
|
|
483
|
+
}
|
|
484
|
+
function isRecord(value) {
|
|
485
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
486
|
+
}
|
|
487
|
+
//# sourceMappingURL=records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.js","sourceRoot":"","sources":["../../src/comments/records.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { AdminStore } from '../admin/store.ts';
|
|
3
|
+
import type { Document } from '../content/document.ts';
|
|
4
|
+
import type { GeekityEnv } from '../env.ts';
|
|
5
|
+
import type { CommentFormContext, CommentViewer } from './form.ts';
|
|
6
|
+
/**
|
|
7
|
+
* The one public endpoint comments add: where the form under a post posts to.
|
|
8
|
+
*
|
|
9
|
+
* It is a single path rather than one per post because the post is a field of
|
|
10
|
+
* the form. That keeps it out of the way of every permalink a site might ever
|
|
11
|
+
* mint — a path under `/_geekity/` is the CMS's own, like the health check —
|
|
12
|
+
* and means the route table does not grow with the site.
|
|
13
|
+
*
|
|
14
|
+
* Everything it answers is either a redirect or the post's own page rendered
|
|
15
|
+
* again with the form still filled in. There is no JSON, no fragment and no
|
|
16
|
+
* script: a comment form has worked without JavaScript since 2003 and there is
|
|
17
|
+
* no reason for this one to stop.
|
|
18
|
+
*/
|
|
19
|
+
export { COMMENT_POST_PATH, COMMENT_REPLY_PARAM } from './form.ts';
|
|
20
|
+
/** Register the comment endpoint. Mounted by the public site. */
|
|
21
|
+
export declare function mountComments(app: Hono<GeekityEnv>): void;
|
|
22
|
+
/**
|
|
23
|
+
* The thank-you a reader is shown after posting, from the query the redirect
|
|
24
|
+
* carried, or `undefined` when the URL says nothing.
|
|
25
|
+
*
|
|
26
|
+
* A query rather than a flash message, because a reader has no session to hang
|
|
27
|
+
* one on, and because a comment form that only works for people with cookies
|
|
28
|
+
* is not a comment form.
|
|
29
|
+
*/
|
|
30
|
+
export declare function commentNoticeFor(query: string | undefined): string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* The comment a `?reply_to=` query points at, as the theme needs it, or an
|
|
33
|
+
* empty object when the query names nothing on this post.
|
|
34
|
+
*
|
|
35
|
+
* Checked against the index rather than trusted, so a made-up id — or one from
|
|
36
|
+
* another page — cannot put a stranger's name on somebody's reply form, and so
|
|
37
|
+
* a comment waiting for a moderator cannot be discovered by guessing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function commentReplyTarget(options: {
|
|
40
|
+
admin: AdminStore;
|
|
41
|
+
document: Document;
|
|
42
|
+
id: string | undefined;
|
|
43
|
+
}): Record<string, unknown>;
|
|
44
|
+
/**
|
|
45
|
+
* The form for a post, or `undefined` when it is not taking comments.
|
|
46
|
+
*
|
|
47
|
+
* What the renderer is handed at boot. It reads the settings and the clock on
|
|
48
|
+
* every render, so a post that closed an hour ago stops offering a form on the
|
|
49
|
+
* very next request.
|
|
50
|
+
*/
|
|
51
|
+
export declare function commentFormFor(options: {
|
|
52
|
+
document: Document;
|
|
53
|
+
site: Record<string, unknown>;
|
|
54
|
+
now: Date;
|
|
55
|
+
/** Whether the site can send mail, which is whether the box is offered. */
|
|
56
|
+
notifiable?: boolean | undefined;
|
|
57
|
+
/** Who the request's session says is reading, when it says anybody. */
|
|
58
|
+
viewer?: CommentViewer | undefined;
|
|
59
|
+
}): CommentFormContext | undefined;
|
|
60
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/comments/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAY5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAYnE;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEnE,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAiHzD;AAqCD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAE9E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;CACxB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ1B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE;IACtC,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,GAAG,EAAE,IAAI,CAAC;IACV,2EAA2E;IAC3E,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,uEAAuE;IACvE,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CACpC,GAAG,kBAAkB,GAAG,SAAS,CAKjC"}
|