@geekity/cms 0.27.0 → 0.28.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 +168 -33
- package/admin/layouts/shell.njk +6 -0
- package/admin/pages/documents/editor.njk +4 -0
- package/admin/pages/plugins/installed.njk +1 -1
- package/admin/pages/tools/dev-mode.njk +47 -0
- package/admin/static/admin.css +8 -0
- package/admin/static/editor-actions.js +13 -9
- package/admin/static/editor.js +31 -17
- package/dist/admin/dev-mode.d.ts +9 -0
- package/dist/admin/dev-mode.d.ts.map +1 -0
- package/dist/admin/dev-mode.js +42 -0
- package/dist/admin/dev-mode.js.map +1 -0
- package/dist/admin/editor-actions.js +3 -1
- package/dist/admin/editor-actions.js.map +1 -1
- package/dist/admin/federation.d.ts.map +1 -1
- package/dist/admin/federation.js +2 -0
- package/dist/admin/federation.js.map +1 -1
- package/dist/admin/menu.d.ts.map +1 -1
- package/dist/admin/menu.js +2 -0
- package/dist/admin/menu.js.map +1 -1
- package/dist/admin/own-paragraph.d.ts +7 -0
- package/dist/admin/own-paragraph.d.ts.map +1 -0
- package/dist/admin/own-paragraph.js +9 -0
- package/dist/admin/own-paragraph.js.map +1 -0
- package/dist/admin/plugins.d.ts.map +1 -1
- package/dist/admin/plugins.js +19 -8
- package/dist/admin/plugins.js.map +1 -1
- package/dist/admin/preview.d.ts.map +1 -1
- package/dist/admin/preview.js +4 -1
- package/dist/admin/preview.js.map +1 -1
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +4 -0
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/settings-email.d.ts.map +1 -1
- package/dist/admin/settings-email.js +4 -0
- package/dist/admin/settings-email.js.map +1 -1
- package/dist/admin/store.d.ts +4 -2
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +1 -1
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/templates.d.ts +1 -0
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +1 -0
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/uploads.d.ts +7 -1
- package/dist/admin/uploads.d.ts.map +1 -1
- package/dist/admin/uploads.js +33 -30
- package/dist/admin/uploads.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +76 -10
- package/dist/cli.js.map +1 -1
- package/dist/comments/index.d.ts +1 -1
- package/dist/comments/index.d.ts.map +1 -1
- package/dist/comments/index.js +1 -1
- package/dist/comments/index.js.map +1 -1
- package/dist/comments/records.d.ts +1 -0
- package/dist/comments/records.d.ts.map +1 -1
- package/dist/comments/records.js +39 -10
- package/dist/comments/records.js.map +1 -1
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +33 -1
- package/dist/content/markdown.js.map +1 -1
- package/dist/content/migrated.d.ts +6 -0
- package/dist/content/migrated.d.ts.map +1 -0
- package/dist/content/migrated.js +12 -0
- package/dist/content/migrated.js.map +1 -0
- package/dist/content/original.d.ts +3 -0
- package/dist/content/original.d.ts.map +1 -0
- package/dist/content/original.js +12 -0
- package/dist/content/original.js.map +1 -0
- package/dist/content/parser.d.ts +5 -0
- package/dist/content/parser.d.ts.map +1 -1
- package/dist/content/parser.js +1 -1
- package/dist/content/parser.js.map +1 -1
- package/dist/content/schedule.d.ts +7 -0
- package/dist/content/schedule.d.ts.map +1 -1
- package/dist/content/schedule.js +11 -2
- package/dist/content/schedule.js.map +1 -1
- package/dist/content/sync.d.ts.map +1 -1
- package/dist/content/sync.js +27 -5
- package/dist/content/sync.js.map +1 -1
- package/dist/content/video.d.ts +12 -0
- package/dist/content/video.d.ts.map +1 -0
- package/dist/content/video.js +91 -0
- package/dist/content/video.js.map +1 -0
- package/dist/dev-mode.d.ts +28 -0
- package/dist/dev-mode.d.ts.map +1 -0
- package/dist/dev-mode.js +64 -0
- package/dist/dev-mode.js.map +1 -0
- package/dist/drain.d.ts +9 -3
- package/dist/drain.d.ts.map +1 -1
- package/dist/drain.js +24 -3
- package/dist/drain.js.map +1 -1
- package/dist/federation/delivery.d.ts +2 -0
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +46 -16
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/inbox.d.ts.map +1 -1
- package/dist/federation/inbox.js +13 -3
- package/dist/federation/inbox.js.map +1 -1
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +8 -4
- package/dist/federation/mount.js.map +1 -1
- package/dist/federation/relays.d.ts.map +1 -1
- package/dist/federation/relays.js +16 -1
- package/dist/federation/relays.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/indexnow.d.ts +1 -1
- package/dist/indexnow.d.ts.map +1 -1
- package/dist/indexnow.js +7 -0
- package/dist/indexnow.js.map +1 -1
- package/dist/mail/service.d.ts +3 -1
- package/dist/mail/service.d.ts.map +1 -1
- package/dist/mail/service.js +17 -0
- package/dist/mail/service.js.map +1 -1
- package/dist/notify.d.ts +1 -1
- package/dist/notify.d.ts.map +1 -1
- package/dist/notify.js +7 -0
- package/dist/notify.js.map +1 -1
- package/dist/plugin.d.ts +67 -4
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/folder.d.ts +2 -0
- package/dist/plugins/folder.d.ts.map +1 -1
- package/dist/plugins/folder.js +26 -5
- package/dist/plugins/folder.js.map +1 -1
- package/dist/plugins/manage.d.ts +8 -6
- package/dist/plugins/manage.d.ts.map +1 -1
- package/dist/plugins/manage.js +1 -4
- package/dist/plugins/manage.js.map +1 -1
- package/dist/plugins/site.d.ts.map +1 -1
- package/dist/plugins/site.js +56 -0
- package/dist/plugins/site.js.map +1 -1
- package/dist/supervisor/control.d.ts +15 -0
- package/dist/supervisor/control.d.ts.map +1 -0
- package/dist/supervisor/control.js +89 -0
- package/dist/supervisor/control.js.map +1 -0
- package/dist/supervisor/primary.d.ts +18 -4
- package/dist/supervisor/primary.d.ts.map +1 -1
- package/dist/supervisor/primary.js +128 -16
- package/dist/supervisor/primary.js.map +1 -1
- package/dist/supervisor/protocol.d.ts +12 -0
- package/dist/supervisor/protocol.d.ts.map +1 -1
- package/dist/supervisor/protocol.js +21 -2
- package/dist/supervisor/protocol.js.map +1 -1
- package/dist/supervisor/supervision.d.ts +1 -0
- package/dist/supervisor/supervision.d.ts.map +1 -1
- package/dist/supervisor/supervision.js +14 -1
- package/dist/supervisor/supervision.js.map +1 -1
- package/dist/supervisor/worker.d.ts.map +1 -1
- package/dist/supervisor/worker.js +31 -6
- package/dist/supervisor/worker.js.map +1 -1
- package/dist/web/context.d.ts +7 -0
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +7 -4
- package/dist/web/context.js.map +1 -1
- package/dist/web/feed-item.d.ts +7 -6
- package/dist/web/feed-item.d.ts.map +1 -1
- package/dist/web/feed-item.js +5 -4
- package/dist/web/feed-item.js.map +1 -1
- package/dist/web/feed-rss.d.ts +2 -2
- package/dist/web/feed-rss.js +2 -2
- package/dist/web/redirects.d.ts +17 -10
- package/dist/web/redirects.d.ts.map +1 -1
- package/dist/web/redirects.js +83 -49
- package/dist/web/redirects.js.map +1 -1
- package/dist/webmention/service.d.ts +1 -1
- package/dist/webmention/service.d.ts.map +1 -1
- package/dist/webmention/service.js +12 -2
- package/dist/webmention/service.js.map +1 -1
- package/package.json +1 -1
- package/themes/default/README.md +152 -1
- package/themes/default/layouts/base.njk +11 -2
- package/themes/default/layouts/post.njk +8 -2
- package/themes/default/partials/body-end.njk +1 -0
- package/themes/default/partials/conversation.njk +1 -1
- package/themes/default/partials/head-end.njk +1 -0
- package/themes/default/src/style.css +4 -0
- package/themes/default/static/style.css +8 -0
package/README.md
CHANGED
|
@@ -86,26 +86,27 @@ message on a site whose `site.json` has not set a period.
|
|
|
86
86
|
site (or `npx geekity`, or a `package.json` script, which is how the generated
|
|
87
87
|
`sync` script calls it).
|
|
88
88
|
|
|
89
|
-
| Command | What it does
|
|
90
|
-
| ----------------------------------------- |
|
|
91
|
-
| `geekity serve` | Boot from the config file and listen, in a worker the command supervises:
|
|
92
|
-
| `geekity init <dir>` | Create a new site in `<dir>`. Refuses a directory that is not empty.
|
|
93
|
-
| `geekity sync` | Rebuild the content index once and exit. Exits non-zero if any file could not be parsed.
|
|
94
|
-
| `geekity rebuild` | Delete `data/geekity.db` and build it again from the files.
|
|
95
|
-
| `geekity resend --all`, `<slug>...` | Send announced posts to every follower and relay again, as they now read. See [Quote posts](#quote-posts).
|
|
96
|
-
| `geekity maintenance on`, `off`, `status` | Take the public site down on purpose with a 503 and `Retry-After`, or bring it back, without a restart. `on --until <time>` names when it should be back.
|
|
97
|
-
| `geekity
|
|
98
|
-
| `geekity
|
|
99
|
-
| `geekity
|
|
100
|
-
| `geekity
|
|
101
|
-
| `geekity --
|
|
89
|
+
| Command | What it does |
|
|
90
|
+
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
91
|
+
| `geekity serve` | Boot from the config file and listen, in a worker the command supervises: a change to the plugins folder replaces it, and a crash respawns it. The default when no command is given. |
|
|
92
|
+
| `geekity init <dir>` | Create a new site in `<dir>`. Refuses a directory that is not empty. |
|
|
93
|
+
| `geekity sync` | Rebuild the content index once and exit. Exits non-zero if any file could not be parsed. |
|
|
94
|
+
| `geekity rebuild` | Delete `data/geekity.db` and build it again from the files. |
|
|
95
|
+
| `geekity resend --all`, `<slug>...` | Send announced posts to every follower and relay again, as they now read. See [Quote posts](#quote-posts). |
|
|
96
|
+
| `geekity maintenance on`, `off`, `status` | Take the public site down on purpose with a 503 and `Retry-After`, or bring it back, without a restart. `on --until <time>` names when it should be back. |
|
|
97
|
+
| `geekity dev-mode on`, `off`, `status` | Hold everything the site would send to the outside world while it is built or migrated, or take it live. See [Dev mode](#dev-mode). |
|
|
98
|
+
| `geekity strip-metadata` | Remove location and camera metadata from files already in `content/uploads`. See [The media library](#the-media-library). |
|
|
99
|
+
| `geekity user add <name>` | Create an admin account, so a site can get its first login without the setup screen. |
|
|
100
|
+
| `geekity plugin add <package>`, `remove` | Install a plugin package's bundle from the npm registry into the plugins folder (`GEEKITY_PLUGINS_DIR`), or delete a disabled one. A running `geekity serve` reloads with the change. |
|
|
101
|
+
| `geekity --help`, `-h` | The same table, on the terminal. |
|
|
102
|
+
| `geekity --version` | The installed version. |
|
|
102
103
|
|
|
103
104
|
An installed plugin may add commands of its own, which `geekity --help` lists
|
|
104
105
|
under _Plugin commands_ and which run whether or not the plugin is enabled.
|
|
105
106
|
That includes a plugin in the plugins folder. The repository README's
|
|
106
107
|
_Plugins_ section describes `geekity plugin add` and writing a plugin.
|
|
107
108
|
|
|
108
|
-
`serve`, `sync`, `rebuild`, `resend`, `maintenance`, `strip-metadata`, `user add`, `plugin` and every plugin command take
|
|
109
|
+
`serve`, `sync`, `rebuild`, `resend`, `maintenance`, `dev-mode`, `strip-metadata`, `user add`, `plugin` and every plugin command take
|
|
109
110
|
`--config <file>`; without it they look for `geekity.config.ts`, then
|
|
110
111
|
`geekity.config.js`, then `geekity.config.mjs` in the working directory, and run
|
|
111
112
|
on defaults if there is none.
|
|
@@ -289,6 +290,7 @@ directory; absolute ones are used as given.
|
|
|
289
290
|
| `accessLogAddress` | `false` | `GEEKITY_ACCESS_LOG_ADDRESS` | Put the client address on the end of each access-log line. `trustProxy` decides which address that is. |
|
|
290
291
|
| `accessLogWriter` | stdout | — | Where the lines go instead. See [The access log](#the-access-log). |
|
|
291
292
|
| `maintenance` | `false` | `GEEKITY_MAINTENANCE` | Keep the site in maintenance mode, answering 503, until a restart without it. |
|
|
293
|
+
| `devMode` | `false` | `GEEKITY_DEV_MODE` | Start the site in dev mode, which holds every outbound side effect until `geekity dev-mode off`. See [Dev mode](#dev-mode). |
|
|
292
294
|
| `compression` | `true` | `GEEKITY_COMPRESSION` | Compress text responses with brotli or gzip. Turn it off behind a proxy that compresses. See [Compression](#compression). |
|
|
293
295
|
| `securityHeaders` | [see below](#security-headers) | — | Headers every response carries. A string replaces a default or adds a header, `false` removes one. |
|
|
294
296
|
| `onDocumentChange` | none | — | Hook run for every change to the index. See [Hooks](#hooks). |
|
|
@@ -426,7 +428,7 @@ something a site is told it may lose:
|
|
|
426
428
|
| `ap_deliveries` | Nothing. The federation screen shows its posts with "Nothing recorded." |
|
|
427
429
|
| `ap_relays` | The relay list in `site.json`: boot sends each of them a fresh `Follow`. |
|
|
428
430
|
| `password_resets`, `spent_tokens` | Nothing. Every reset link and every one-click moderation link stops working. |
|
|
429
|
-
| `cms_state` | Nothing.
|
|
431
|
+
| `cms_state` | Nothing. The scheduler's watermark and its first run. |
|
|
430
432
|
| `migrations`, `admin_migrations` | The package. They record which schema versions have run. |
|
|
431
433
|
|
|
432
434
|
So four things are actually lost:
|
|
@@ -790,9 +792,9 @@ ignored.
|
|
|
790
792
|
|
|
791
793
|
A site that moved here from the WordPress ActivityPub plugin keeps its people's
|
|
792
794
|
actor ids and its posts' object ids, which are identity and are the CMS's own
|
|
793
|
-
(above). What it needs
|
|
795
|
+
(above). What it needs besides is its content, the plugin's old paths, which
|
|
794
796
|
followers' servers keep delivering to until they refetch the actor, and an
|
|
795
|
-
import that brings each person's key pair and followers across.
|
|
797
|
+
import that brings each person's key pair and followers across. All three are the
|
|
796
798
|
[`@geekity/plugin-wordpress`](../plugin-wordpress/README.md) package, which a
|
|
797
799
|
site born on the CMS never installs. decision-14's switch is that plugin now:
|
|
798
800
|
where the cutover used to say "turn the switch on", it says install and enable
|
|
@@ -800,27 +802,43 @@ the WordPress plugin.
|
|
|
800
802
|
|
|
801
803
|
#### The cutover, end to end
|
|
802
804
|
|
|
803
|
-
1. **
|
|
805
|
+
1. **Start the new site in dev mode.** Set `GEEKITY_DEV_MODE=true` before
|
|
806
|
+
its first boot (see [Dev mode](#dev-mode)). Followers, relays, links and
|
|
807
|
+
mail are all real from here on, and dev mode keeps every one of them from
|
|
808
|
+
hearing about the import, the edits or a scheduled post until step 4.
|
|
809
|
+
2. **Export, while the WordPress site is still up.** Each person's key pair,
|
|
804
810
|
and the followers if the old site is going away before the import runs. The
|
|
805
811
|
plugin's README says how. Note the actor id and the numeric actor id off
|
|
806
|
-
`https://example.com/?author=2`,
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
812
|
+
`https://example.com/?author=2`, take a WXR export under Tools > Export,
|
|
813
|
+
and copy `wp-content/uploads`, which the export does not carry.
|
|
814
|
+
3. **Install the WordPress plugin and import.** Add `@geekity/plugin-wordpress`
|
|
815
|
+
to the site's dependencies and to `plugins` in its config, then run
|
|
816
|
+
`geekity import wordpress example.WordPress.xml --uploads wp-content/uploads`
|
|
817
|
+
for the content and media, and
|
|
818
|
+
`geekity import wordpress-actor ada --actor-id … --wordpress-id … --keypair ada.keypair.json`
|
|
819
|
+
for each person. Both work before the plugin is enabled. Check the reports:
|
|
820
|
+
every post should be written, every conflict or clash resolved, every
|
|
821
|
+
follower added, and any follower that was skipped re-run once its server
|
|
822
|
+
answers. Tools > Dev mode shows that the imports sent nothing. Both imports
|
|
823
|
+
are safe to run again: run them over a fresh export just before step 4 to
|
|
824
|
+
pick up what WordPress received in the meantime.
|
|
825
|
+
4. **Enable the WordPress plugin, move the DNS and go live.** Enable it under
|
|
826
|
+
`/admin/plugins`, then move the DNS. Remove `GEEKITY_DEV_MODE` and run
|
|
827
|
+
`geekity dev-mode off`: nothing held during the migration is sent.
|
|
828
|
+
Followers' servers go on delivering to the plugin's old inbox paths until
|
|
829
|
+
they next refetch the actor, and the plugin is what catches those
|
|
830
|
+
deliveries. Once WordPress no longer answers, run both imports one last time
|
|
831
|
+
over its final export: they merge what arrived between the export and the
|
|
832
|
+
DNS move, and leave alone whatever this site wrote or edited since.
|
|
833
|
+
5. **Watch.** `/admin/federation` lists the users with their actor ids and
|
|
818
834
|
followers; the plugin's own screen under Plugins lists each old path with
|
|
819
835
|
the instant it was last asked for. Deliveries should thin out as each
|
|
820
836
|
follower's server refetches the actor and learns the new endpoints.
|
|
821
|
-
|
|
837
|
+
6. **Disable it.** Once every path says _Never_ again for long enough — weeks
|
|
822
838
|
rather than days, since a quiet instance refetches rarely — disable the
|
|
823
|
-
plugin.
|
|
839
|
+
plugin. Disabling it also ends the redirects from the old
|
|
840
|
+
`/wp-content/uploads/` media URLs, so a site that still gets links to
|
|
841
|
+
those keeps it enabled. The paths go away on the very next request, with nothing restarted,
|
|
824
842
|
and if something was still using them you can enable it again just as
|
|
825
843
|
quickly.
|
|
826
844
|
|
|
@@ -918,13 +936,95 @@ half is sent by a peer built in the script rather than by `fedify inbox`.
|
|
|
918
936
|
[Fedify]: https://fedify.dev/
|
|
919
937
|
[`fedify tunnel`]: https://fedify.dev/cli
|
|
920
938
|
|
|
939
|
+
## Dev mode
|
|
940
|
+
|
|
941
|
+
Building or migrating a site means running it with production data: the real
|
|
942
|
+
base URL, the real actor and keys, real followers, real links. Dev mode keeps
|
|
943
|
+
that site quiet until you take it live (decision-35). While it is on, nothing
|
|
944
|
+
the site does reaches the outside world:
|
|
945
|
+
|
|
946
|
+
- No ActivityPub delivery to followers or relays, no relay `Follow` or `Undo`,
|
|
947
|
+
and no `Accept` or `Reject` answering an inbox. This holds whatever caused
|
|
948
|
+
the change: the watcher, the admin, the scheduler, `geekity resend` or a
|
|
949
|
+
reply context arriving.
|
|
950
|
+
- No webmention. Endpoint discovery is skipped too.
|
|
951
|
+
- No rssCloud or WebSub ping.
|
|
952
|
+
- No IndexNow submission.
|
|
953
|
+
- No email.
|
|
954
|
+
|
|
955
|
+
Reads still go out: reply contexts and cited pages, actor profiles, oEmbed,
|
|
956
|
+
avatars, the check of an incoming webmention, and a plugin's `host.fetch`. A
|
|
957
|
+
read tells nobody about the site, and without them it could not show its
|
|
958
|
+
replies or profiles. Inbound federation is accepted: follows, replies, likes
|
|
959
|
+
and webmentions are stored as on a live site, and the `Accept` a follow would
|
|
960
|
+
get is held. Plugins need no switch of their own. A plugin's federation hands
|
|
961
|
+
what it receives to the site's inbox, so its answers are held the same way.
|
|
962
|
+
|
|
963
|
+
Each held side effect is a line in `data/dev-mode.jsonl`: when, what kind,
|
|
964
|
+
what would have been sent, and to whom. Tools > Dev mode in the admin lists
|
|
965
|
+
them, newest first. Every admin screen shows a banner while the mode is on,
|
|
966
|
+
and `geekity serve` says so when it boots.
|
|
967
|
+
|
|
968
|
+
```sh
|
|
969
|
+
$ geekity dev-mode on
|
|
970
|
+
Dev mode is on: nothing the site does is sent until `geekity dev-mode off`.
|
|
971
|
+
$ geekity dev-mode status
|
|
972
|
+
Dev mode is on.
|
|
973
|
+
$ geekity dev-mode off
|
|
974
|
+
Dev mode is off: the site is live. Nothing held while it was on will be sent.
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
`devMode: true` in the config, or `GEEKITY_DEV_MODE=true`, starts a site in
|
|
978
|
+
dev mode: boot writes `data/dev-mode.json`, and the file keeps the mode on
|
|
979
|
+
from then on. Losing the variable does not take the site live. Only
|
|
980
|
+
`geekity dev-mode off` does, and it refuses while the config or the
|
|
981
|
+
environment still asks for dev mode, because the next boot would turn it on
|
|
982
|
+
again. The running site checks the file before every send, so going live
|
|
983
|
+
needs no restart, unless the server was started with the variable: that
|
|
984
|
+
process stays in dev mode until it restarts without it. Going live is a line
|
|
985
|
+
in the record too.
|
|
986
|
+
|
|
987
|
+
Going live replays nothing. A held side effect is dropped, not queued, and no
|
|
988
|
+
delivery or webmention is recorded as failed, so neither the Federation screen
|
|
989
|
+
nor `geekity resend` offers it again. A post announced while the mode was on
|
|
990
|
+
counts as announced, so its next edit goes out as an `Update`. A relay listed
|
|
991
|
+
while the mode was on stays pending: press Retry on the Federation screen once
|
|
992
|
+
the site is live.
|
|
993
|
+
|
|
994
|
+
Password recovery mail is held too, so in dev mode a password is reset from
|
|
995
|
+
the shell. A peer that followed while the mode was on never got its `Accept`,
|
|
996
|
+
and has to follow again.
|
|
997
|
+
|
|
998
|
+
### Dev mode on Docker
|
|
999
|
+
|
|
1000
|
+
Set the variable in the service's environment before the first boot, so the
|
|
1001
|
+
container never starts live:
|
|
1002
|
+
|
|
1003
|
+
```yaml
|
|
1004
|
+
services:
|
|
1005
|
+
server:
|
|
1006
|
+
environment:
|
|
1007
|
+
GEEKITY_DEV_MODE: 'true'
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
To go live, remove the variable, recreate the container (the file in the
|
|
1011
|
+
`data` volume keeps the mode on through the restart), then run:
|
|
1012
|
+
|
|
1013
|
+
```sh
|
|
1014
|
+
docker compose exec server geekity dev-mode off
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
`geekity dev-mode status` in the same container says which state the site is
|
|
1018
|
+
in. The record is in the `data` volume at `data/dev-mode.jsonl`.
|
|
1019
|
+
|
|
921
1020
|
## Comments
|
|
922
1021
|
|
|
923
1022
|
A reader can answer a post on the page, and what they leave joins the same
|
|
924
1023
|
thread as the fediverse replies rather than sitting in a section of its own.
|
|
925
1024
|
|
|
926
1025
|
A comment is a file. `content/_data/comments/{slug}.json` holds one post's
|
|
927
|
-
comments
|
|
1026
|
+
comments (a slug that is not plain ASCII is percent-encoded in the name, so
|
|
1027
|
+
`i-♥-rss` is `i-%E2%99%A5-rss.json`) — id, source, kind, status, author, the Markdown and the HTML it
|
|
928
1028
|
rendered to, when it was submitted, a salted hash of the address, and what it
|
|
929
1029
|
answers — so they are in git beside the posts, an Eleventy build of the same
|
|
930
1030
|
directory shows them, and the `comments` table is an index emptied and read
|
|
@@ -2439,6 +2539,11 @@ export default defineConfig({
|
|
|
2439
2539
|
A name that is not a header name, or a value that is empty or has a line break
|
|
2440
2540
|
in it, stops the site at boot. There is no environment variable for these.
|
|
2441
2541
|
|
|
2542
|
+
A policy that limits scripts has to allow whatever the site's theme adds to
|
|
2543
|
+
every page. For the Umami and Google Analytics snippets, the theme README's
|
|
2544
|
+
[Adding to every page](./themes/default/README.md#adding-to-every-page) lists
|
|
2545
|
+
the directives each one needs.
|
|
2546
|
+
|
|
2442
2547
|
Every response under `/admin`, static files and redirects included, gets three
|
|
2443
2548
|
stricter values in their place, whatever `securityHeaders` says:
|
|
2444
2549
|
|
|
@@ -2797,6 +2902,25 @@ first request after the file changes, and it is skipped. The following entries a
|
|
|
2797
2902
|
A file that is not a JSON list serves no redirects and is reported the same
|
|
2798
2903
|
way.
|
|
2799
2904
|
|
|
2905
|
+
#### More than one file
|
|
2906
|
+
|
|
2907
|
+
Every `*.json` file in `content/_data/redirects/` is read after
|
|
2908
|
+
`redirects.json`, in name order, and served the same way. A tool that generates
|
|
2909
|
+
redirects, such as an import, writes a file of its own there and never touches
|
|
2910
|
+
the list a person keeps. A file there, or `redirects.json` itself, may also be
|
|
2911
|
+
an object that maps each `from` to its `to`, every one a `301`:
|
|
2912
|
+
|
|
2913
|
+
```json
|
|
2914
|
+
{
|
|
2915
|
+
"/?p=123": "/2026/01/hello/",
|
|
2916
|
+
"/old-section/": "/new-section/"
|
|
2917
|
+
}
|
|
2918
|
+
```
|
|
2919
|
+
|
|
2920
|
+
Where two files declare the same `from`, the one read first wins, so
|
|
2921
|
+
`redirects.json` wins over every file in the folder, and the other declaration
|
|
2922
|
+
is reported. A loop across files is found and skipped as within one.
|
|
2923
|
+
|
|
2800
2924
|
Every redirect the CMS sends carries `X-Redirect-By: Geekity CMS`. This
|
|
2801
2925
|
includes the declared redirects, moved URLs, trailing slashes, feed spellings
|
|
2802
2926
|
and the admin's redirects. Someone who traces a chain of redirects through a
|
|
@@ -3677,6 +3801,17 @@ The admin is not themed. Its templates and its static files live in a tree of
|
|
|
3677
3801
|
their own with a loader of their own, deliberately off this search path, so no
|
|
3678
3802
|
theme can shadow the login form or the CSRF field inside it.
|
|
3679
3803
|
|
|
3804
|
+
Analytics and other markup that goes on every page need no layout at all. The
|
|
3805
|
+
packaged `layouts/base.njk` includes `partials/head-end.njk` last in `<head>`
|
|
3806
|
+
and `partials/body-end.njk` last in `<body>`, both empty in the package, so a
|
|
3807
|
+
theme of `theme.json` and `partials/head-end.njk` holding the Umami or Google
|
|
3808
|
+
Analytics snippet adds it to every public HTML page and keeps every other
|
|
3809
|
+
packaged template. Neither is printed in a feed, a Markdown, text or JSON
|
|
3810
|
+
representation, mail, the admin or the editor's preview. The theme README's
|
|
3811
|
+
[Adding to every page](./themes/default/README.md#adding-to-every-page) has the
|
|
3812
|
+
Umami and GA4 example, the Docker volume, and what a Content-Security-Policy
|
|
3813
|
+
has to allow for them.
|
|
3814
|
+
|
|
3680
3815
|
The packaged theme is the Paper design (doc-9) on the andrewshell.org shell
|
|
3681
3816
|
(decision-16). Its stylesheet is written in Tailwind v4 in
|
|
3682
3817
|
`themes/default/src/style.css` and compiled to `themes/default/static/style.css`
|
package/admin/layouts/shell.njk
CHANGED
|
@@ -32,6 +32,12 @@
|
|
|
32
32
|
<div class="drawer-content">
|
|
33
33
|
<label for="admin-menu" class="btn btn-sm drawer-button mx-4 mt-4 lg:hidden">Menu</label>
|
|
34
34
|
<main id="main" class="p-4 sm:p-6 lg:p-8">
|
|
35
|
+
{% if devModeUrl %}
|
|
36
|
+
{% from "components/alert.njk" import alert %}
|
|
37
|
+
<div class="mb-6">
|
|
38
|
+
{% call alert(color='warning', polite=true) %}<span>Dev mode is on: nothing this site does is sent to followers, relays, linked sites, feed hubs, search engines or inboxes. <a class="link" href="{{ devModeUrl }}">See what was held</a>.</span>{% endcall %}
|
|
39
|
+
</div>
|
|
40
|
+
{% endif %}
|
|
35
41
|
{% include "components/flash.njk" %}
|
|
36
42
|
{% block content %}{% endblock %}
|
|
37
43
|
</main>
|
|
@@ -46,7 +46,11 @@
|
|
|
46
46
|
<template data-editor-choice>
|
|
47
47
|
<li>
|
|
48
48
|
<label class="flex items-start gap-2">
|
|
49
|
+
{% if fieldId == 'editor-tags' %}
|
|
49
50
|
<input type="checkbox" class="checkbox checkbox-sm mt-0.5" form="editor-suggestions">
|
|
51
|
+
{% else %}
|
|
52
|
+
<input type="radio" class="radio radio-sm mt-0.5" name="{{ action.url }}" form="editor-suggestions">
|
|
53
|
+
{% endif %}
|
|
50
54
|
<span class="flex flex-col">
|
|
51
55
|
<span class="flex flex-wrap items-center gap-2">
|
|
52
56
|
<span class="break-all" data-choice-value></span>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
{% if reload %}
|
|
33
33
|
<div id="plugins-reload" class="mb-8 max-w-prose">
|
|
34
34
|
{% call card(title="The plugins folder has changed") %}
|
|
35
|
-
<p>Reload starts a new server with the plugins folder as it is now, then retires this one. No request is turned away
|
|
35
|
+
<p>Reload starts a new server with the plugins folder as it is now, then retires this one. No request is turned away.{% if reload.watching %} The site also reloads by itself once the folder has stood still for a few seconds.{% endif %}</p>
|
|
36
36
|
<dl class="text-sm">
|
|
37
37
|
{% for heading, names in [["Added", reload.changes.added], ["Removed", reload.changes.removed], ["Updated", reload.changes.updated]] %}
|
|
38
38
|
{% if names.length %}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{% extends "layouts/shell.njk" %}
|
|
2
|
+
{% from "components/table.njk" import table %}
|
|
3
|
+
|
|
4
|
+
{% block title %}{{ heading }}{% endblock %}
|
|
5
|
+
|
|
6
|
+
{% block content %}
|
|
7
|
+
<h1 class="mb-6 text-2xl font-bold">{{ heading }}</h1>
|
|
8
|
+
|
|
9
|
+
<div class="flex flex-col gap-6">
|
|
10
|
+
<p class="max-w-prose">
|
|
11
|
+
{% if on %}
|
|
12
|
+
Dev mode is on. Nothing this site does reaches the outside world: no ActivityPub
|
|
13
|
+
delivery or relay follow, no webmention, no rssCloud or WebSub ping, no IndexNow
|
|
14
|
+
submission and no email. Each one is recorded here instead. Run
|
|
15
|
+
<code>geekity dev-mode off</code> to take the site live; nothing held is sent then.
|
|
16
|
+
{% else %}
|
|
17
|
+
Dev mode is off, and the site is live. This is what it held while it was on. To hold
|
|
18
|
+
everything again, run <code>geekity dev-mode on</code>.
|
|
19
|
+
{% endif %}
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p>{{ heldCount }} held in all{% if rows.length >= shown %}; the newest {{ shown }} entries are listed, and <code>data/dev-mode.jsonl</code> has the rest{% endif %}.</p>
|
|
23
|
+
|
|
24
|
+
{% if rows.length %}
|
|
25
|
+
{% call table('What dev mode held') %}
|
|
26
|
+
<thead>
|
|
27
|
+
<tr>
|
|
28
|
+
<th scope="col">When</th>
|
|
29
|
+
<th scope="col">What</th>
|
|
30
|
+
<th scope="col">Detail</th>
|
|
31
|
+
<th scope="col">Would have gone to</th>
|
|
32
|
+
</tr>
|
|
33
|
+
</thead>
|
|
34
|
+
<tbody>
|
|
35
|
+
{% for row in rows %}
|
|
36
|
+
<tr>
|
|
37
|
+
<td class="whitespace-nowrap"><time datetime="{{ row.at }}">{{ row.at }}</time></td>
|
|
38
|
+
<td class="whitespace-nowrap">{{ row.what }}</td>
|
|
39
|
+
<td class="break-all">{{ row.detail }}</td>
|
|
40
|
+
<td class="break-all">{% for to in row.to %}<div>{{ to }}</div>{% endfor %}</td>
|
|
41
|
+
</tr>
|
|
42
|
+
{% endfor %}
|
|
43
|
+
</tbody>
|
|
44
|
+
{% endcall %}
|
|
45
|
+
{% endif %}
|
|
46
|
+
</div>
|
|
47
|
+
{% endblock %}
|
package/admin/static/admin.css
CHANGED
|
@@ -5728,6 +5728,14 @@
|
|
|
5728
5728
|
padding: .1875rem;
|
|
5729
5729
|
}
|
|
5730
5730
|
|
|
5731
|
+
.radio-sm {
|
|
5732
|
+
padding: .1875rem;
|
|
5733
|
+
}
|
|
5734
|
+
|
|
5735
|
+
.radio-sm[type="radio"] {
|
|
5736
|
+
--size: calc(var(--size-selector, .25rem) * 5);
|
|
5737
|
+
}
|
|
5738
|
+
|
|
5731
5739
|
.table-lg :not(thead, tfoot) tr {
|
|
5732
5740
|
font-size: 1.125rem;
|
|
5733
5741
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/* The buttons running plugins put beside the editor's title, description and
|
|
2
2
|
tags (decision-33). Each posts the form as it stands to its plugin's
|
|
3
|
-
endpoint, which answers { ok, value }, { ok, choices }
|
|
4
|
-
message }, and shows
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
endpoint, which answers { ok, value }, { ok, choices }, { ok, message }
|
|
4
|
+
or { ok: false, message }, and shows a value or choices with Accept and
|
|
5
|
+
Dismiss, and a message in the status line alone. Choices come beside the
|
|
6
|
+
tags field, each drawn from the page's template row with a box to tick,
|
|
7
|
+
under a heading row wherever its group changes, and Accept adds the ticked
|
|
8
|
+
ones; or beside the title, as radio buttons with the first picked, and
|
|
9
|
+
Accept puts the picked one in the title. Accept fills the field and saves
|
|
10
|
+
nothing; a save is still only the form's own buttons.
|
|
9
11
|
|
|
10
12
|
A file rather than an inline script, loaded from the admin's own origin
|
|
11
13
|
like slug.js, so the admin's policy stays `script-src 'self'` and
|
|
@@ -146,10 +148,12 @@
|
|
|
146
148
|
list.hidden = false;
|
|
147
149
|
suggestion.hidden = false;
|
|
148
150
|
say('');
|
|
149
|
-
list.querySelector('input')
|
|
151
|
+
var first = list.querySelector('input');
|
|
152
|
+
if (first.type === 'radio') first.checked = true;
|
|
153
|
+
first.focus();
|
|
150
154
|
return;
|
|
151
155
|
}
|
|
152
|
-
if (answer.ok) {
|
|
156
|
+
if (answer.ok && typeof answer.value === 'string') {
|
|
153
157
|
offered = String(answer.value);
|
|
154
158
|
text.textContent = offered;
|
|
155
159
|
text.hidden = false;
|
|
@@ -159,7 +163,7 @@
|
|
|
159
163
|
accept.focus();
|
|
160
164
|
return;
|
|
161
165
|
}
|
|
162
|
-
say(String(answer.message),
|
|
166
|
+
say(String(answer.message), !answer.ok);
|
|
163
167
|
if (answer.withdrawn) press.hidden = true;
|
|
164
168
|
})
|
|
165
169
|
.catch(function (error) {
|