@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
package/dist/admin/templates.js
CHANGED
|
@@ -6,23 +6,55 @@ import { formatDate } from '../web/templates.js';
|
|
|
6
6
|
* directory so they are found whether the CMS runs from `src/` under tsx or
|
|
7
7
|
* from `dist/` as an installed dependency.
|
|
8
8
|
*
|
|
9
|
-
* They are a set of their own, not part of the theme search path: a
|
|
10
|
-
*
|
|
11
|
-
* the login form or the CSRF field inside it.
|
|
9
|
+
* They are a set of their own, not part of the theme search path: the theme a
|
|
10
|
+
* site wears may override any public template, and must not be able to shadow
|
|
11
|
+
* the login form or the CSRF field inside it (decision-4, decision-15).
|
|
12
|
+
*
|
|
13
|
+
* Under it are three siblings, and a template is in exactly one of them:
|
|
14
|
+
*
|
|
15
|
+
* - `pages/` — the screens, one folder per section of the admin menu, plus
|
|
16
|
+
* `account/` for the four screens shown when nobody is signed in yet.
|
|
17
|
+
* - `layouts/` — the chrome a page extends: the document, the signed-in shell,
|
|
18
|
+
* and the shared settings page.
|
|
19
|
+
* - `components/` — what a page imports or includes: the field macros and the
|
|
20
|
+
* flash.
|
|
12
21
|
*/
|
|
13
22
|
export const PACKAGED_ADMIN_DIR = fileURLToPath(new URL('../../admin/', import.meta.url));
|
|
14
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Templates {@link mountAdmin} asks for by name.
|
|
25
|
+
*
|
|
26
|
+
* The path of each is where the menu says it is, with one exception:
|
|
27
|
+
* `pages/documents/` holds the four entries Posts, Pages, Categories and Tags
|
|
28
|
+
* are made of, because a post and a page differ by a `kind` rather than by a
|
|
29
|
+
* screen. `pages/documents/list.njk` says why at more length.
|
|
30
|
+
*/
|
|
15
31
|
export const ADMIN_TEMPLATES = {
|
|
16
|
-
login: '
|
|
17
|
-
setup: '
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
login: 'pages/account/login.njk',
|
|
33
|
+
setup: 'pages/account/setup.njk',
|
|
34
|
+
forgot: 'pages/account/forgot.njk',
|
|
35
|
+
reset: 'pages/account/reset.njk',
|
|
36
|
+
dashboard: 'pages/dashboard/home.njk',
|
|
37
|
+
placeholder: 'pages/placeholder.njk',
|
|
38
|
+
documentList: 'pages/documents/list.njk',
|
|
39
|
+
documentEditor: 'pages/documents/editor.njk',
|
|
40
|
+
documentConflict: 'pages/documents/conflict.njk',
|
|
41
|
+
taxonomy: 'pages/documents/taxonomy.njk',
|
|
42
|
+
navigation: 'pages/navigation/menus.njk',
|
|
43
|
+
media: 'pages/media/library.njk',
|
|
44
|
+
themes: 'pages/appearance/themes.njk',
|
|
45
|
+
comments: 'pages/comments/all.njk',
|
|
46
|
+
messages: 'pages/messages/all.njk',
|
|
47
|
+
settingsGeneral: 'pages/settings/general.njk',
|
|
48
|
+
settingsReading: 'pages/settings/reading.njk',
|
|
49
|
+
settingsPermalinks: 'pages/settings/permalinks.njk',
|
|
50
|
+
settingsDiscussion: 'pages/settings/discussion.njk',
|
|
51
|
+
settingsEmail: 'pages/settings/email.njk',
|
|
52
|
+
toolsContentIndex: 'pages/tools/content-index.njk',
|
|
53
|
+
usersList: 'pages/users/list.njk',
|
|
54
|
+
usersNew: 'pages/users/new.njk',
|
|
55
|
+
usersEdit: 'pages/users/edit.njk',
|
|
56
|
+
federation: 'pages/federation/followers.njk',
|
|
57
|
+
federationSettings: 'pages/federation/settings.njk',
|
|
26
58
|
};
|
|
27
59
|
/** A Nunjucks environment over {@link PACKAGED_ADMIN_DIR} and nothing else. */
|
|
28
60
|
export function createAdminTemplateEnvironment(options = {}) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/admin/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/admin/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAW,aAAa,CAAC,IAAI,GAAG,CAAC,cAAc,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAElG;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,KAAK,EAAE,yBAAyB;IAChC,KAAK,EAAE,yBAAyB;IAChC,MAAM,EAAE,0BAA0B;IAClC,KAAK,EAAE,yBAAyB;IAChC,SAAS,EAAE,0BAA0B;IACrC,WAAW,EAAE,uBAAuB;IACpC,YAAY,EAAE,0BAA0B;IACxC,cAAc,EAAE,4BAA4B;IAC5C,gBAAgB,EAAE,8BAA8B;IAChD,QAAQ,EAAE,8BAA8B;IACxC,UAAU,EAAE,4BAA4B;IACxC,KAAK,EAAE,yBAAyB;IAChC,MAAM,EAAE,6BAA6B;IACrC,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,eAAe,EAAE,4BAA4B;IAC7C,eAAe,EAAE,4BAA4B;IAC7C,kBAAkB,EAAE,+BAA+B;IACnD,kBAAkB,EAAE,+BAA+B;IACnD,aAAa,EAAE,0BAA0B;IACzC,iBAAiB,EAAE,+BAA+B;IAClD,SAAS,EAAE,sBAAsB;IACjC,QAAQ,EAAE,qBAAqB;IAC/B,SAAS,EAAE,sBAAsB;IACjC,UAAU,EAAE,gCAAgC;IAC5C,kBAAkB,EAAE,+BAA+B;CAC3C,CAAC;AAYX,+EAA+E;AAC/E,MAAM,UAAU,8BAA8B,CAC5C,OAAO,GAA0C,EAAE;IAEnD,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,CAAC,kBAAkB,CAAC,EAAE;QACxD,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,IAAI;KAClC,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,MAAM,EAAE;QAC1C,UAAU,EAAE,IAAI;QAChB,gBAAgB,EAAE,KAAK;QACvB,UAAU,EAAE,IAAI;QAChB,YAAY,EAAE,IAAI;KACnB,CAAC,CAAC;IAEH,yEAAyE;IACzE,sEAAsE;IACtE,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,KAAc,EAAE,MAAM,GAAY,UAAU,EAAE,EAAE,CAC7E,UAAU,CAAC,KAAK,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CACpE,CAAC;IAEF,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { ResolvedConfig } from '../config.ts';
|
|
3
|
+
import type { Clock } from '../content/store.ts';
|
|
4
|
+
import type { GeekityEnv } from '../env.ts';
|
|
5
|
+
/**
|
|
6
|
+
* How far the wait is allowed to double.
|
|
7
|
+
*
|
|
8
|
+
* The lockout doubles for every failure past the threshold, so the wait after
|
|
9
|
+
* `n` extra failures is `lockoutSeconds * 2^n`. Left alone that reaches years,
|
|
10
|
+
* which is a denial of service against the site's own owner rather than
|
|
11
|
+
* against an attacker: anybody who can guess a username could lock it shut for
|
|
12
|
+
* good. The multiplier stops here — with the default fifteen minutes, four
|
|
13
|
+
* hours.
|
|
14
|
+
*/
|
|
15
|
+
export declare const LOCKOUT_GROWTH_LIMIT = 16;
|
|
16
|
+
/** What {@link createLoginThrottle} needs to know. */
|
|
17
|
+
export interface LoginThrottleOptions {
|
|
18
|
+
/** Failed attempts a key may make before it is locked. */
|
|
19
|
+
attempts: number;
|
|
20
|
+
/** How long the first lockout lasts, in seconds. */
|
|
21
|
+
lockoutSeconds: number;
|
|
22
|
+
/** Where the time comes from. Defaults to the system clock; a test hands one it can move. */
|
|
23
|
+
now?: Clock;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A count of recent failures per key, and the lockout it earns.
|
|
27
|
+
*
|
|
28
|
+
* Deliberately in memory: a restart clears it. The thing it defends against is
|
|
29
|
+
* a run of guesses over minutes, which no restart interrupts, and keeping it
|
|
30
|
+
* out of SQLite means a failed login writes nothing an attacker can grow.
|
|
31
|
+
*/
|
|
32
|
+
export interface LoginThrottle {
|
|
33
|
+
/**
|
|
34
|
+
* Seconds left before any of these keys may try again, or `undefined` when
|
|
35
|
+
* none of them is locked. The largest wait wins, so a key that is locked on
|
|
36
|
+
* both its username and its address is told the longer of the two.
|
|
37
|
+
*/
|
|
38
|
+
retryAfter(keys: readonly string[]): number | undefined;
|
|
39
|
+
/** Record one failed attempt against every key. */
|
|
40
|
+
fail(keys: readonly string[]): void;
|
|
41
|
+
/** Forget every failure against these keys, which is what a success does. */
|
|
42
|
+
succeed(keys: readonly string[]): void;
|
|
43
|
+
/** How many keys are remembered. For the test that pins the sweeping. */
|
|
44
|
+
size(): number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build a login throttle.
|
|
48
|
+
*
|
|
49
|
+
* There is no artificial pause on a failed attempt. A `sleep` in the handler
|
|
50
|
+
* holds a connection open for as long as it lasts, so a hundred guesses in
|
|
51
|
+
* parallel would cost the server a hundred idle sockets and the attacker
|
|
52
|
+
* nothing — the delay would be the lever rather than the defence. The growing
|
|
53
|
+
* lockout is the delay: it is the server that decides when the next attempt is
|
|
54
|
+
* allowed, and refusing takes no time at all.
|
|
55
|
+
*/
|
|
56
|
+
export declare function createLoginThrottle(options: LoginThrottleOptions): LoginThrottle;
|
|
57
|
+
/**
|
|
58
|
+
* Where a request came from, as far as the site can tell, or `undefined`.
|
|
59
|
+
*
|
|
60
|
+
* `X-Forwarded-For` is only believed when the site says it is behind a proxy,
|
|
61
|
+
* because anybody may send it: on a site reached directly, trusting it would
|
|
62
|
+
* let one attacker put every guess on a different make-believe address and
|
|
63
|
+
* never be locked out by address at all. The leftmost entry is the client the
|
|
64
|
+
* first proxy saw. Otherwise it is the socket's own address, which is
|
|
65
|
+
* unavailable when the app is being driven in process rather than served — a
|
|
66
|
+
* test, or an embedding — and `undefined` is the honest answer there.
|
|
67
|
+
*/
|
|
68
|
+
export declare function clientAddress(c: Context<GeekityEnv>, config: Pick<ResolvedConfig, 'trustProxy'>): string | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* The keys one sign-in attempt is counted against: the username it named and
|
|
71
|
+
* the address it came from.
|
|
72
|
+
*
|
|
73
|
+
* The username is folded to lower case so `Ada` and `ada` are one key, and it
|
|
74
|
+
* is used whether or not any such user exists — a username nobody has locks
|
|
75
|
+
* out exactly as a real one does, so the lockout cannot be used to find out
|
|
76
|
+
* which usernames the site has.
|
|
77
|
+
*/
|
|
78
|
+
export declare function loginKeys(username: string, address: string | undefined): string[];
|
|
79
|
+
/**
|
|
80
|
+
* A wait in words, for the message the locked-out visitor reads. Rounded up,
|
|
81
|
+
* so it never says a shorter time than the one actually left.
|
|
82
|
+
*/
|
|
83
|
+
export declare function describeWait(seconds: number): string;
|
|
84
|
+
//# sourceMappingURL=throttle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.d.ts","sourceRoot":"","sources":["../../src/admin/throttle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACnC,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,GAAG,CAAC,EAAE,KAAK,CAAC;CACb;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IACxD,mDAAmD;IACnD,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACpC,6EAA6E;IAC7E,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACvC,yEAAyE;IACzE,IAAI,IAAI,MAAM,CAAC;CAChB;AAYD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAqEhF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,EACtB,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,GACzC,MAAM,GAAG,SAAS,CAapB;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAIjF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGpD"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { getConnInfo } from '@hono/node-server/conninfo';
|
|
2
|
+
import { systemClock } from '../content/store.js';
|
|
3
|
+
/**
|
|
4
|
+
* How far the wait is allowed to double.
|
|
5
|
+
*
|
|
6
|
+
* The lockout doubles for every failure past the threshold, so the wait after
|
|
7
|
+
* `n` extra failures is `lockoutSeconds * 2^n`. Left alone that reaches years,
|
|
8
|
+
* which is a denial of service against the site's own owner rather than
|
|
9
|
+
* against an attacker: anybody who can guess a username could lock it shut for
|
|
10
|
+
* good. The multiplier stops here — with the default fifteen minutes, four
|
|
11
|
+
* hours.
|
|
12
|
+
*/
|
|
13
|
+
export const LOCKOUT_GROWTH_LIMIT = 16;
|
|
14
|
+
/**
|
|
15
|
+
* Build a login throttle.
|
|
16
|
+
*
|
|
17
|
+
* There is no artificial pause on a failed attempt. A `sleep` in the handler
|
|
18
|
+
* holds a connection open for as long as it lasts, so a hundred guesses in
|
|
19
|
+
* parallel would cost the server a hundred idle sockets and the attacker
|
|
20
|
+
* nothing — the delay would be the lever rather than the defence. The growing
|
|
21
|
+
* lockout is the delay: it is the server that decides when the next attempt is
|
|
22
|
+
* allowed, and refusing takes no time at all.
|
|
23
|
+
*/
|
|
24
|
+
export function createLoginThrottle(options) {
|
|
25
|
+
const clock = options.now ?? systemClock;
|
|
26
|
+
const attempts = Math.max(1, Math.trunc(options.attempts));
|
|
27
|
+
const window = Math.max(1, options.lockoutSeconds) * 1000;
|
|
28
|
+
const entries = new Map();
|
|
29
|
+
/** The row for a key, or `undefined` when it has none or its row is stale. */
|
|
30
|
+
function live(key, at) {
|
|
31
|
+
const entry = entries.get(key);
|
|
32
|
+
if (entry === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
if (at >= entry.expiresAt) {
|
|
35
|
+
entries.delete(key);
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return entry;
|
|
39
|
+
}
|
|
40
|
+
/** Drop every stale row. Cheap: the map only holds keys that failed recently. */
|
|
41
|
+
function sweep(at) {
|
|
42
|
+
for (const [key, entry] of entries) {
|
|
43
|
+
if (at >= entry.expiresAt)
|
|
44
|
+
entries.delete(key);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
retryAfter(keys) {
|
|
49
|
+
const at = clock().getTime();
|
|
50
|
+
let longest = 0;
|
|
51
|
+
for (const key of keys) {
|
|
52
|
+
const entry = live(key, at);
|
|
53
|
+
if (entry === undefined)
|
|
54
|
+
continue;
|
|
55
|
+
longest = Math.max(longest, entry.lockedUntil - at);
|
|
56
|
+
}
|
|
57
|
+
return longest > 0 ? Math.ceil(longest / 1000) : undefined;
|
|
58
|
+
},
|
|
59
|
+
fail(keys) {
|
|
60
|
+
const at = clock().getTime();
|
|
61
|
+
sweep(at);
|
|
62
|
+
for (const key of keys) {
|
|
63
|
+
const entry = live(key, at) ?? { failures: 0, lockedUntil: 0, expiresAt: 0 };
|
|
64
|
+
entry.failures += 1;
|
|
65
|
+
if (entry.failures < attempts) {
|
|
66
|
+
// Still under the threshold: the row only has to outlive the window,
|
|
67
|
+
// so a slow trickle of typos never adds up to a lockout.
|
|
68
|
+
entry.expiresAt = at + window;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// The wait doubles for each failure past the threshold, and the row
|
|
72
|
+
// outlives the lockout by one window so the next failure is counted
|
|
73
|
+
// as the escalation it is rather than as a fresh start.
|
|
74
|
+
const wait = window * Math.min(2 ** (entry.failures - attempts), LOCKOUT_GROWTH_LIMIT);
|
|
75
|
+
entry.lockedUntil = at + wait;
|
|
76
|
+
entry.expiresAt = entry.lockedUntil + window;
|
|
77
|
+
}
|
|
78
|
+
entries.set(key, entry);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
succeed(keys) {
|
|
82
|
+
for (const key of keys)
|
|
83
|
+
entries.delete(key);
|
|
84
|
+
},
|
|
85
|
+
size() {
|
|
86
|
+
return entries.size;
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Where a request came from, as far as the site can tell, or `undefined`.
|
|
92
|
+
*
|
|
93
|
+
* `X-Forwarded-For` is only believed when the site says it is behind a proxy,
|
|
94
|
+
* because anybody may send it: on a site reached directly, trusting it would
|
|
95
|
+
* let one attacker put every guess on a different make-believe address and
|
|
96
|
+
* never be locked out by address at all. The leftmost entry is the client the
|
|
97
|
+
* first proxy saw. Otherwise it is the socket's own address, which is
|
|
98
|
+
* unavailable when the app is being driven in process rather than served — a
|
|
99
|
+
* test, or an embedding — and `undefined` is the honest answer there.
|
|
100
|
+
*/
|
|
101
|
+
export function clientAddress(c, config) {
|
|
102
|
+
if (config.trustProxy) {
|
|
103
|
+
const forwarded = c.req.header('x-forwarded-for');
|
|
104
|
+
const first = forwarded?.split(',')[0]?.trim();
|
|
105
|
+
if (first !== undefined && first !== '')
|
|
106
|
+
return first;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const address = getConnInfo(c).remote.address;
|
|
110
|
+
return address === undefined || address === '' ? undefined : address;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The keys one sign-in attempt is counted against: the username it named and
|
|
118
|
+
* the address it came from.
|
|
119
|
+
*
|
|
120
|
+
* The username is folded to lower case so `Ada` and `ada` are one key, and it
|
|
121
|
+
* is used whether or not any such user exists — a username nobody has locks
|
|
122
|
+
* out exactly as a real one does, so the lockout cannot be used to find out
|
|
123
|
+
* which usernames the site has.
|
|
124
|
+
*/
|
|
125
|
+
export function loginKeys(username, address) {
|
|
126
|
+
const keys = [`user:${username.toLowerCase()}`];
|
|
127
|
+
if (address !== undefined)
|
|
128
|
+
keys.push(`addr:${address}`);
|
|
129
|
+
return keys;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* A wait in words, for the message the locked-out visitor reads. Rounded up,
|
|
133
|
+
* so it never says a shorter time than the one actually left.
|
|
134
|
+
*/
|
|
135
|
+
export function describeWait(seconds) {
|
|
136
|
+
if (seconds < 60)
|
|
137
|
+
return plural(Math.max(1, Math.ceil(seconds)), 'second');
|
|
138
|
+
return plural(Math.ceil(seconds / 60), 'minute');
|
|
139
|
+
}
|
|
140
|
+
function plural(count, noun) {
|
|
141
|
+
return `${count} ${noun}${count === 1 ? '' : 's'}`;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=throttle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.js","sourceRoot":"","sources":["../../src/admin/throttle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools > Content index: reading every file again, on a site that is serving.
|
|
3
|
+
*
|
|
4
|
+
* `geekity rebuild` deletes `data/geekity.db` and boots a fresh CMS, so it
|
|
5
|
+
* refuses to run while a server holds the file — and in a container the server
|
|
6
|
+
* is PID 1, which makes the only route stopping the container, running a
|
|
7
|
+
* one-off one, and starting it again. This is the same repair without the
|
|
8
|
+
* downtime: nothing is deleted and no connection is swapped, so the services
|
|
9
|
+
* that captured the store objects at boot — the renderer, delivery, the
|
|
10
|
+
* scheduler, comments, webmentions, the relays — go on working against the
|
|
11
|
+
* same objects while the rows underneath them are replaced.
|
|
12
|
+
*
|
|
13
|
+
* It is also kinder than the command. Because nothing is deleted it keeps the
|
|
14
|
+
* sessions, so the admin who pressed the button is still signed in when the
|
|
15
|
+
* redirect lands; it keeps the delivery log and the relay handshakes; and it
|
|
16
|
+
* keeps the scheduler's watermark, so a post that came due during the rebuild
|
|
17
|
+
* is still announced. The command stays as it is for the one case an admin
|
|
18
|
+
* screen cannot be the door for: a database this version refuses to open,
|
|
19
|
+
* where there is no running server to press a button in.
|
|
20
|
+
*/
|
|
21
|
+
import type { Hono } from 'hono';
|
|
22
|
+
import type { ContentStore } from '../content/store.ts';
|
|
23
|
+
import type { SyncResult } from '../content/sync.ts';
|
|
24
|
+
import type { GeekityEnv } from '../env.ts';
|
|
25
|
+
import type { AdminRender } from './documents.ts';
|
|
26
|
+
import type { AdminStore } from './store.ts';
|
|
27
|
+
/**
|
|
28
|
+
* Where the Content index screen lives.
|
|
29
|
+
*
|
|
30
|
+
* `/admin/tools` itself rather than `/admin/tools/content-index`, the way
|
|
31
|
+
* General is `/admin/settings` itself: a section's heading lands on its first
|
|
32
|
+
* child, and this is the first one.
|
|
33
|
+
*/
|
|
34
|
+
export declare const TOOLS_PATH = "/admin/tools";
|
|
35
|
+
/** Where the Rebuild button posts. */
|
|
36
|
+
export declare const REBUILD_INDEX_PATH = "/admin/tools/rebuild-index";
|
|
37
|
+
/** The navigation section the screen marks as current. */
|
|
38
|
+
export declare const TOOLS_SECTION = "tools";
|
|
39
|
+
/** The child of that section it is. */
|
|
40
|
+
export declare const CONTENT_INDEX_CHILD = "index";
|
|
41
|
+
/** The fields the form on the screen submits. */
|
|
42
|
+
export declare const TOOLS_FIELDS: {
|
|
43
|
+
/** Set once the admin has been shown what a rebuild costs while it runs. */
|
|
44
|
+
readonly confirm: 'confirm';
|
|
45
|
+
};
|
|
46
|
+
/** What one in-place rebuild put back. */
|
|
47
|
+
export interface ContentIndexRebuild {
|
|
48
|
+
/** What the scan of `content/` did, exactly as `cms.sync()` reports it. */
|
|
49
|
+
scan: SyncResult;
|
|
50
|
+
/** How many followers the federation files held. */
|
|
51
|
+
followers: number;
|
|
52
|
+
/** How many inbox activities the log held. */
|
|
53
|
+
activities: number;
|
|
54
|
+
/** How many comments the comment files held. */
|
|
55
|
+
comments: number;
|
|
56
|
+
}
|
|
57
|
+
/** What {@link rebuildContentIndex} needs. */
|
|
58
|
+
export interface RebuildContentIndexOptions {
|
|
59
|
+
/** The index to empty and fill again. */
|
|
60
|
+
store: ContentStore;
|
|
61
|
+
/** The database the federation and comment indexes live in. */
|
|
62
|
+
admin: AdminStore;
|
|
63
|
+
/** The content directory every one of them is read from. */
|
|
64
|
+
contentDir: string;
|
|
65
|
+
/** The full scan, which is `cms.sync()`: see {@link GeekityEnv} `rescan`. */
|
|
66
|
+
rescan: () => Promise<SyncResult>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Read `content/` back into the index, in place, on the live connections.
|
|
70
|
+
*
|
|
71
|
+
* The three rebuilds are the three that already run on every boot, in the
|
|
72
|
+
* order boot runs them, so a rebuilt index and a booted one are the same
|
|
73
|
+
* index. The only thing here that boot does not do is the clear — boot is
|
|
74
|
+
* cold — and the clear is what makes the scan read every file: a row whose
|
|
75
|
+
* hash matches its file is a row the scan leaves alone, which is exactly the
|
|
76
|
+
* case an out-of-band edit or a damaged index leaves behind.
|
|
77
|
+
*
|
|
78
|
+
* The clear and the scan are deliberately **not** one transaction. One
|
|
79
|
+
* connection serves every request, so a write transaction held open across the
|
|
80
|
+
* scan would not isolate the rebuild from anybody else — it would pull their
|
|
81
|
+
* writes into it and roll them back with it if the scan threw. The cost is a
|
|
82
|
+
* window, between the clear and the end of the scan, in which the site answers
|
|
83
|
+
* 404 for documents whose files are perfectly fine; the screen says so before
|
|
84
|
+
* the button is pressed.
|
|
85
|
+
*
|
|
86
|
+
* It federates nothing. Every change the scan emits carries `origin: 'scan'`,
|
|
87
|
+
* which delivery, the webmentions and the feed notifier all ignore — the same
|
|
88
|
+
* reason a boot scan is silent.
|
|
89
|
+
*/
|
|
90
|
+
export declare function rebuildContentIndex(options: RebuildContentIndexOptions): Promise<ContentIndexRebuild>;
|
|
91
|
+
/** What {@link mountToolsScreen} needs from the admin around it. */
|
|
92
|
+
export interface MountToolsOptions {
|
|
93
|
+
/** The admin's renderer, which injects the chrome, the CSRF token and the flash. */
|
|
94
|
+
render: AdminRender;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Register Tools > Content index: what the index holds, and the button that
|
|
98
|
+
* reads the files back into it.
|
|
99
|
+
*
|
|
100
|
+
* The rebuild runs inside the POST, as the taxonomy rewrite does, and says
|
|
101
|
+
* what it did on the flash. There is no progress bar and nothing to poll:
|
|
102
|
+
* `node:sqlite` is synchronous, so a rebuild moved off the request would block
|
|
103
|
+
* the same event loop from somewhere nobody can see, and progress would need
|
|
104
|
+
* state the database is not allowed to keep (decision-9). A site whose scan
|
|
105
|
+
* takes long enough for that to matter is one that should be told, which is
|
|
106
|
+
* what the screen does.
|
|
107
|
+
*/
|
|
108
|
+
export declare function mountToolsScreen(app: Hono<GeekityEnv>, options: MountToolsOptions): void;
|
|
109
|
+
/** What a finished rebuild says on the flash. */
|
|
110
|
+
export declare function rebuildMessage(report: ContentIndexRebuild): string;
|
|
111
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/admin/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAC;AAG1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,iBAA0B,CAAC;AAElD,sCAAsC;AACtC,eAAO,MAAM,kBAAkB,+BAAwC,CAAC;AAExE,0DAA0D;AAC1D,eAAO,MAAM,aAAa,UAAU,CAAC;AAErC,uCAAuC;AACvC,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C,iDAAiD;AACjD,eAAO,MAAM,YAAY;IACvB,4EAA4E;aAC5E,OAAO,EAAE,SAAS;CACV,CAAC;AAEX,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,IAAI,EAAE,UAAU,CAAC;IACjB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,8CAA8C;AAC9C,MAAM,WAAW,0BAA0B;IACzC,yCAAyC;IACzC,KAAK,EAAE,YAAY,CAAC;IACpB,+DAA+D;IAC/D,KAAK,EAAE,UAAU,CAAC;IAClB,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAe9B;AAED,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,oFAAoF;IACpF,MAAM,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CA6ExF;AAED,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAYlE"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools > Content index: reading every file again, on a site that is serving.
|
|
3
|
+
*
|
|
4
|
+
* `geekity rebuild` deletes `data/geekity.db` and boots a fresh CMS, so it
|
|
5
|
+
* refuses to run while a server holds the file — and in a container the server
|
|
6
|
+
* is PID 1, which makes the only route stopping the container, running a
|
|
7
|
+
* one-off one, and starting it again. This is the same repair without the
|
|
8
|
+
* downtime: nothing is deleted and no connection is swapped, so the services
|
|
9
|
+
* that captured the store objects at boot — the renderer, delivery, the
|
|
10
|
+
* scheduler, comments, webmentions, the relays — go on working against the
|
|
11
|
+
* same objects while the rows underneath them are replaced.
|
|
12
|
+
*
|
|
13
|
+
* It is also kinder than the command. Because nothing is deleted it keeps the
|
|
14
|
+
* sessions, so the admin who pressed the button is still signed in when the
|
|
15
|
+
* redirect lands; it keeps the delivery log and the relay handshakes; and it
|
|
16
|
+
* keeps the scheduler's watermark, so a post that came due during the rebuild
|
|
17
|
+
* is still announced. The command stays as it is for the one case an admin
|
|
18
|
+
* screen cannot be the door for: a database this version refuses to open,
|
|
19
|
+
* where there is no running server to press a button in.
|
|
20
|
+
*/
|
|
21
|
+
import { rebuildCommentIndexes } from '../comments/records.js';
|
|
22
|
+
import { rebuildFederationIndexes } from '../federation/records.js';
|
|
23
|
+
import { flash } from './flash.js';
|
|
24
|
+
import { ADMIN_PREFIX } from './session.js';
|
|
25
|
+
import { ADMIN_TEMPLATES } from './templates.js';
|
|
26
|
+
/**
|
|
27
|
+
* Where the Content index screen lives.
|
|
28
|
+
*
|
|
29
|
+
* `/admin/tools` itself rather than `/admin/tools/content-index`, the way
|
|
30
|
+
* General is `/admin/settings` itself: a section's heading lands on its first
|
|
31
|
+
* child, and this is the first one.
|
|
32
|
+
*/
|
|
33
|
+
export const TOOLS_PATH = `${ADMIN_PREFIX}/tools`;
|
|
34
|
+
/** Where the Rebuild button posts. */
|
|
35
|
+
export const REBUILD_INDEX_PATH = `${ADMIN_PREFIX}/tools/rebuild-index`;
|
|
36
|
+
/** The navigation section the screen marks as current. */
|
|
37
|
+
export const TOOLS_SECTION = 'tools';
|
|
38
|
+
/** The child of that section it is. */
|
|
39
|
+
export const CONTENT_INDEX_CHILD = 'index';
|
|
40
|
+
/** The fields the form on the screen submits. */
|
|
41
|
+
export const TOOLS_FIELDS = {
|
|
42
|
+
/** Set once the admin has been shown what a rebuild costs while it runs. */
|
|
43
|
+
confirm: 'confirm',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Read `content/` back into the index, in place, on the live connections.
|
|
47
|
+
*
|
|
48
|
+
* The three rebuilds are the three that already run on every boot, in the
|
|
49
|
+
* order boot runs them, so a rebuilt index and a booted one are the same
|
|
50
|
+
* index. The only thing here that boot does not do is the clear — boot is
|
|
51
|
+
* cold — and the clear is what makes the scan read every file: a row whose
|
|
52
|
+
* hash matches its file is a row the scan leaves alone, which is exactly the
|
|
53
|
+
* case an out-of-band edit or a damaged index leaves behind.
|
|
54
|
+
*
|
|
55
|
+
* The clear and the scan are deliberately **not** one transaction. One
|
|
56
|
+
* connection serves every request, so a write transaction held open across the
|
|
57
|
+
* scan would not isolate the rebuild from anybody else — it would pull their
|
|
58
|
+
* writes into it and roll them back with it if the scan threw. The cost is a
|
|
59
|
+
* window, between the clear and the end of the scan, in which the site answers
|
|
60
|
+
* 404 for documents whose files are perfectly fine; the screen says so before
|
|
61
|
+
* the button is pressed.
|
|
62
|
+
*
|
|
63
|
+
* It federates nothing. Every change the scan emits carries `origin: 'scan'`,
|
|
64
|
+
* which delivery, the webmentions and the feed notifier all ignore — the same
|
|
65
|
+
* reason a boot scan is silent.
|
|
66
|
+
*/
|
|
67
|
+
export async function rebuildContentIndex(options) {
|
|
68
|
+
const { store, admin, contentDir, rescan } = options;
|
|
69
|
+
store.clear();
|
|
70
|
+
const federation = rebuildFederationIndexes({ admin, contentDir });
|
|
71
|
+
const comments = rebuildCommentIndexes({ admin, contentDir });
|
|
72
|
+
const scan = await rescan();
|
|
73
|
+
return {
|
|
74
|
+
scan,
|
|
75
|
+
followers: federation.followers,
|
|
76
|
+
activities: federation.activities,
|
|
77
|
+
comments: comments.comments,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Register Tools > Content index: what the index holds, and the button that
|
|
82
|
+
* reads the files back into it.
|
|
83
|
+
*
|
|
84
|
+
* The rebuild runs inside the POST, as the taxonomy rewrite does, and says
|
|
85
|
+
* what it did on the flash. There is no progress bar and nothing to poll:
|
|
86
|
+
* `node:sqlite` is synchronous, so a rebuild moved off the request would block
|
|
87
|
+
* the same event loop from somewhere nobody can see, and progress would need
|
|
88
|
+
* state the database is not allowed to keep (decision-9). A site whose scan
|
|
89
|
+
* takes long enough for that to matter is one that should be told, which is
|
|
90
|
+
* what the screen does.
|
|
91
|
+
*/
|
|
92
|
+
export function mountToolsScreen(app, options) {
|
|
93
|
+
const { render } = options;
|
|
94
|
+
/**
|
|
95
|
+
* The rebuild in flight, when there is one.
|
|
96
|
+
*
|
|
97
|
+
* A second press while the first is still scanning would clear an index the
|
|
98
|
+
* first one is halfway through filling, and leave two scans racing to write
|
|
99
|
+
* the same rows. One at a time, then, and the second press is told so rather
|
|
100
|
+
* than queued: by the time a queued one ran, the first would have done the
|
|
101
|
+
* very work it was asked for.
|
|
102
|
+
*/
|
|
103
|
+
let running;
|
|
104
|
+
app.get(TOOLS_PATH, (c) => render(c, ADMIN_TEMPLATES.toolsContentIndex, screen(c)));
|
|
105
|
+
app.post(REBUILD_INDEX_PATH, async (c) => {
|
|
106
|
+
const body = await c.req.parseBody();
|
|
107
|
+
// Offered rather than done, the way a merge is on the taxonomy screens:
|
|
108
|
+
// for as long as the scan runs the site answers 404 for every document,
|
|
109
|
+
// and that is not something to find out about afterwards.
|
|
110
|
+
if (field(body[TOOLS_FIELDS.confirm]) === '') {
|
|
111
|
+
return render(c, ADMIN_TEMPLATES.toolsContentIndex, { ...screen(c), confirm: true });
|
|
112
|
+
}
|
|
113
|
+
if (running !== undefined) {
|
|
114
|
+
flash(c, 'error', 'A rebuild is already running. Nothing was started; wait for it to finish and look here again.');
|
|
115
|
+
return c.redirect(TOOLS_PATH, 303);
|
|
116
|
+
}
|
|
117
|
+
running = rebuildContentIndex({
|
|
118
|
+
store: c.var.store,
|
|
119
|
+
admin: c.var.admin,
|
|
120
|
+
contentDir: c.var.config.contentDir,
|
|
121
|
+
rescan: c.var.rescan,
|
|
122
|
+
});
|
|
123
|
+
let report;
|
|
124
|
+
try {
|
|
125
|
+
report = await running;
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
running = undefined;
|
|
129
|
+
}
|
|
130
|
+
flash(c, report.scan.failed > 0 ? 'error' : 'notice', rebuildMessage(report));
|
|
131
|
+
return c.redirect(TOOLS_PATH, 303);
|
|
132
|
+
});
|
|
133
|
+
/** Everything the screen renders. */
|
|
134
|
+
function screen(c) {
|
|
135
|
+
const counts = c.var.store.counts();
|
|
136
|
+
return {
|
|
137
|
+
section: TOOLS_SECTION,
|
|
138
|
+
child: CONTENT_INDEX_CHILD,
|
|
139
|
+
heading: 'Content index',
|
|
140
|
+
rebuildUrl: REBUILD_INDEX_PATH,
|
|
141
|
+
toolsUrl: TOOLS_PATH,
|
|
142
|
+
fields: TOOLS_FIELDS,
|
|
143
|
+
confirm: false,
|
|
144
|
+
// What the index holds right now, which is how somebody who suspects it
|
|
145
|
+
// is wrong finds out that it is: a documents count that does not match
|
|
146
|
+
// the files, or a comment count of nought on a site with comments.
|
|
147
|
+
indexed: {
|
|
148
|
+
documents: counts.total,
|
|
149
|
+
followers: c.var.admin.countFollowers(),
|
|
150
|
+
activities: c.var.admin.countInboxActivities(),
|
|
151
|
+
comments: totalComments(c.var.admin),
|
|
152
|
+
},
|
|
153
|
+
contentDir: c.var.config.contentDir,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** What a finished rebuild says on the flash. */
|
|
158
|
+
export function rebuildMessage(report) {
|
|
159
|
+
const { scan } = report;
|
|
160
|
+
const counted = `Rebuilt the index from the files. Scanned ${count(scan.scanned, 'file')}: ` +
|
|
161
|
+
`${String(scan.created)} indexed, ${String(scan.removed)} dropped, ` +
|
|
162
|
+
`${String(scan.failed)} failed. Read back ${count(report.followers, 'follower')}, ` +
|
|
163
|
+
`${count(report.activities, 'inbox activity', 'inbox activities')} and ` +
|
|
164
|
+
`${count(report.comments, 'comment')}.`;
|
|
165
|
+
return scan.failed === 0
|
|
166
|
+
? counted
|
|
167
|
+
: `${counted} A file that will not parse is left out of the index and named in the site's log.`;
|
|
168
|
+
}
|
|
169
|
+
/** "1 file", "2 files". The plural is the singular plus s unless told. */
|
|
170
|
+
function count(howMany, singular, plural = `${singular}s`) {
|
|
171
|
+
return `${String(howMany)} ${howMany === 1 ? singular : plural}`;
|
|
172
|
+
}
|
|
173
|
+
/** How many comments the index holds, whatever a moderator has done with them. */
|
|
174
|
+
function totalComments(admin) {
|
|
175
|
+
const counts = admin.countCommentsByStatus();
|
|
176
|
+
return counts.pending + counts.approved + counts.spam;
|
|
177
|
+
}
|
|
178
|
+
/** One submitted field as a string. */
|
|
179
|
+
function field(value) {
|
|
180
|
+
return typeof value === 'string' ? value : '';
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/admin/tools.ts"],"names":[],"mappings":"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"}
|