@geekity/cms 0.4.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.
Files changed (143) hide show
  1. package/README.md +234 -108
  2. package/admin/pages/documents/editor.njk +0 -13
  3. package/admin/pages/{settings/federation.njk → federation/settings.njk} +7 -5
  4. package/admin/pages/navigation/menus.njk +129 -0
  5. package/admin/pages/settings/reading.njk +2 -10
  6. package/admin/pages/tools/content-index.njk +89 -0
  7. package/admin/pages/users/list.njk +5 -2
  8. package/admin/static/admin.css +60 -0
  9. package/dist/access-log.d.ts +44 -0
  10. package/dist/access-log.d.ts.map +1 -0
  11. package/dist/access-log.js +71 -0
  12. package/dist/access-log.js.map +1 -0
  13. package/dist/admin/documents.d.ts +3 -13
  14. package/dist/admin/documents.d.ts.map +1 -1
  15. package/dist/admin/documents.js +2 -34
  16. package/dist/admin/documents.js.map +1 -1
  17. package/dist/admin/federation.d.ts +10 -0
  18. package/dist/admin/federation.d.ts.map +1 -1
  19. package/dist/admin/federation.js +10 -0
  20. package/dist/admin/federation.js.map +1 -1
  21. package/dist/admin/index.d.ts +3 -3
  22. package/dist/admin/index.d.ts.map +1 -1
  23. package/dist/admin/index.js +2 -2
  24. package/dist/admin/index.js.map +1 -1
  25. package/dist/admin/menu.d.ts.map +1 -1
  26. package/dist/admin/menu.js +19 -2
  27. package/dist/admin/menu.js.map +1 -1
  28. package/dist/admin/navigation.d.ts +155 -0
  29. package/dist/admin/navigation.d.ts.map +1 -0
  30. package/dist/admin/navigation.js +292 -0
  31. package/dist/admin/navigation.js.map +1 -0
  32. package/dist/admin/routes.d.ts.map +1 -1
  33. package/dist/admin/routes.js +9 -0
  34. package/dist/admin/routes.js.map +1 -1
  35. package/dist/admin/session.d.ts +17 -3
  36. package/dist/admin/session.d.ts.map +1 -1
  37. package/dist/admin/session.js +19 -5
  38. package/dist/admin/session.js.map +1 -1
  39. package/dist/admin/settings-federation.d.ts +10 -1
  40. package/dist/admin/settings-federation.d.ts.map +1 -1
  41. package/dist/admin/settings-federation.js +26 -6
  42. package/dist/admin/settings-federation.js.map +1 -1
  43. package/dist/admin/settings-page.d.ts +26 -9
  44. package/dist/admin/settings-page.d.ts.map +1 -1
  45. package/dist/admin/settings-page.js +18 -12
  46. package/dist/admin/settings-page.js.map +1 -1
  47. package/dist/admin/settings-pages.d.ts +14 -3
  48. package/dist/admin/settings-pages.d.ts.map +1 -1
  49. package/dist/admin/settings-pages.js +15 -5
  50. package/dist/admin/settings-pages.js.map +1 -1
  51. package/dist/admin/settings-reading.d.ts +5 -4
  52. package/dist/admin/settings-reading.d.ts.map +1 -1
  53. package/dist/admin/settings-reading.js +6 -5
  54. package/dist/admin/settings-reading.js.map +1 -1
  55. package/dist/admin/settings.d.ts +29 -15
  56. package/dist/admin/settings.d.ts.map +1 -1
  57. package/dist/admin/settings.js +28 -78
  58. package/dist/admin/settings.js.map +1 -1
  59. package/dist/admin/templates.d.ts +3 -1
  60. package/dist/admin/templates.d.ts.map +1 -1
  61. package/dist/admin/templates.js +3 -1
  62. package/dist/admin/templates.js.map +1 -1
  63. package/dist/admin/tools.d.ts +111 -0
  64. package/dist/admin/tools.d.ts.map +1 -0
  65. package/dist/admin/tools.js +182 -0
  66. package/dist/admin/tools.js.map +1 -0
  67. package/dist/cli.d.ts.map +1 -1
  68. package/dist/cli.js +6 -1
  69. package/dist/cli.js.map +1 -1
  70. package/dist/comments/form.d.ts +48 -0
  71. package/dist/comments/form.d.ts.map +1 -1
  72. package/dist/comments/form.js +15 -0
  73. package/dist/comments/form.js.map +1 -1
  74. package/dist/comments/index.d.ts +4 -3
  75. package/dist/comments/index.d.ts.map +1 -1
  76. package/dist/comments/index.js +2 -1
  77. package/dist/comments/index.js.map +1 -1
  78. package/dist/comments/routes.d.ts +3 -1
  79. package/dist/comments/routes.d.ts.map +1 -1
  80. package/dist/comments/routes.js +46 -11
  81. package/dist/comments/routes.js.map +1 -1
  82. package/dist/comments/submission.d.ts +37 -3
  83. package/dist/comments/submission.d.ts.map +1 -1
  84. package/dist/comments/submission.js +59 -20
  85. package/dist/comments/submission.js.map +1 -1
  86. package/dist/comments/viewer.d.ts +21 -0
  87. package/dist/comments/viewer.d.ts.map +1 -0
  88. package/dist/comments/viewer.js +45 -0
  89. package/dist/comments/viewer.js.map +1 -0
  90. package/dist/config.d.ts +35 -0
  91. package/dist/config.d.ts.map +1 -1
  92. package/dist/config.js +3 -0
  93. package/dist/config.js.map +1 -1
  94. package/dist/content/store.d.ts +18 -0
  95. package/dist/content/store.d.ts.map +1 -1
  96. package/dist/content/store.js +18 -0
  97. package/dist/content/store.js.map +1 -1
  98. package/dist/env.d.ts +12 -1
  99. package/dist/env.d.ts.map +1 -1
  100. package/dist/federation/mount.d.ts +4 -0
  101. package/dist/federation/mount.d.ts.map +1 -1
  102. package/dist/federation/mount.js +44 -12
  103. package/dist/federation/mount.js.map +1 -1
  104. package/dist/index.d.ts +5 -5
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +21 -4
  107. package/dist/index.js.map +1 -1
  108. package/dist/web/index.d.ts +3 -3
  109. package/dist/web/index.d.ts.map +1 -1
  110. package/dist/web/index.js +1 -1
  111. package/dist/web/index.js.map +1 -1
  112. package/dist/web/navigation.d.ts +158 -36
  113. package/dist/web/navigation.d.ts.map +1 -1
  114. package/dist/web/navigation.js +250 -60
  115. package/dist/web/navigation.js.map +1 -1
  116. package/dist/web/negotiate.d.ts +22 -0
  117. package/dist/web/negotiate.d.ts.map +1 -1
  118. package/dist/web/negotiate.js +25 -10
  119. package/dist/web/negotiate.js.map +1 -1
  120. package/dist/web/render.d.ts +16 -10
  121. package/dist/web/render.d.ts.map +1 -1
  122. package/dist/web/render.js +21 -14
  123. package/dist/web/render.js.map +1 -1
  124. package/dist/web/routes.d.ts.map +1 -1
  125. package/dist/web/routes.js +11 -2
  126. package/dist/web/routes.js.map +1 -1
  127. package/dist/web/themes.d.ts +21 -0
  128. package/dist/web/themes.d.ts.map +1 -1
  129. package/dist/web/themes.js +37 -0
  130. package/dist/web/themes.js.map +1 -1
  131. package/package.json +1 -1
  132. package/templates/site/content/_data/site.json +7 -0
  133. package/templates/site/content/pages/about.md +7 -0
  134. package/themes/default/README.md +200 -80
  135. package/themes/default/layouts/author.njk +3 -4
  136. package/themes/default/layouts/base.njk +38 -49
  137. package/themes/default/layouts/front-page.njk +1 -2
  138. package/themes/default/partials/bio.njk +15 -21
  139. package/themes/default/partials/comment-form.njk +31 -3
  140. package/themes/default/partials/menu.njk +32 -0
  141. package/themes/default/partials/search-form.njk +5 -0
  142. package/themes/default/static/style.css +31 -8
  143. package/themes/default/theme.json +5 -1
@@ -0,0 +1,129 @@
1
+ {# /admin/navigation: every menu this site holds, and what renders it.
2
+
3
+ Two lists, and a menu is in exactly one of them. First the areas the active
4
+ theme declares, in the theme's own order and under the theme's own labels,
5
+ each a box to type links into — an area the site has never filled in is an
6
+ empty box rather than a missing one. Then every other menu the site stores,
7
+ under a heading that says this theme renders them nowhere, each with a
8
+ Delete, which is the only way a menu is removed. #}
9
+ {% extends "layouts/shell.njk" %}
10
+ {% import "components/fields.njk" as field %}
11
+
12
+ {% block title %}{{ heading }}{% endblock %}
13
+
14
+ {# One menu: its heading, the box its items are typed into, and the button
15
+ that saves it. `extra` is whatever else belongs under the box — the Delete
16
+ form, on a menu nothing renders. #}
17
+ {% macro box(menu, fields, url, csrfToken, extra='') %}
18
+ <li class="admin-menu-box">
19
+ <h3>{{ menu.label }}</h3>
20
+
21
+ <p class="admin-menu-name">
22
+ <code>menus.{{ menu.name }}</code>
23
+ {% if menu.stored %}
24
+ <span class="admin-status">{{ menu.count }} item{% if menu.count != 1 %}s{% endif %}</span>
25
+ {% else %}
26
+ <span class="admin-status">empty — nothing has been typed here yet</span>
27
+ {% endif %}
28
+ </p>
29
+
30
+ <form method="post" action="{{ url }}">
31
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
32
+ <input type="hidden" name="{{ fields.menu }}" value="{{ menu.name }}" />
33
+ {% set itemsHint %}
34
+ One item per line, written <code>Label | URL</code> &mdash; for example
35
+ <code>About | /about/</code> or
36
+ <code>Mastodon | https://example.social/@me</code>. They are the whole of
37
+ this menu, in this order: nothing else puts a link in it. A page serving
38
+ as the front page goes in as <code>Home | /</code>, where a reader lands.
39
+ End a line <code>| me</code> to mark the link
40
+ <strong>a profile that is you</strong>: it is given
41
+ <code>rel="me"</code>, which is how Mastodon and the rest of the
42
+ IndieWeb verify that the site and the profile it links are the same
43
+ person.
44
+ {% endset %}
45
+ {{ field.textarea('menu-' + menu.name, fields.items, 'Items', menu.items,
46
+ error=menu.problem, hint=itemsHint, rows='5', spellcheck='false') }}
47
+ <p class="admin-menu-buttons">
48
+ <button type="submit">Save menu</button>
49
+ </p>
50
+ </form>
51
+
52
+ {{ extra | safe }}
53
+ </li>
54
+ {% endmacro %}
55
+
56
+ {% block content %}
57
+ <h1>{{ heading }}</h1>
58
+
59
+ {% if hasProblems %}
60
+ <p class="admin-field-error">Nothing was saved. See below.</p>
61
+ {% endif %}
62
+
63
+ <h2>Where {{ themeName }} puts a menu</h2>
64
+
65
+ {% if areas.length %}
66
+ <p class="admin-hint">
67
+ These are the areas the active theme declares in its <code>theme.json</code>, in the order it
68
+ declares them. A theme renders a menu by asking for it by name, and an area with nothing in it
69
+ renders nothing at all.
70
+ </p>
71
+
72
+ <ul class="admin-menu-boxes">
73
+ {% for menu in areas %}
74
+ {{ box(menu, fields, navigationUrl, csrfToken) }}
75
+ {% endfor %}
76
+ </ul>
77
+ {% else %}
78
+ <p class="admin-hint">
79
+ {{ themeName }} declares no areas, so it asks for no menu by name. A theme declares them as an
80
+ <code>areas</code> list in its <code>theme.json</code>, each entry a <code>name</code> &mdash;
81
+ the name the template loops over as <code>menus.&lt;name&gt;</code> &mdash; and a
82
+ <code>label</code> to head the box here. A menu added below is still stored, ready for the
83
+ theme that asks for it.
84
+ </p>
85
+ {% endif %}
86
+
87
+ {% if others.length %}
88
+ <h2>Kept, rendered nowhere</h2>
89
+
90
+ <p class="admin-hint">
91
+ This site stores these menus and {{ themeName }} renders none of them: no template loops over
92
+ those names, so nothing on the public site shows them. They are kept, because that is how the
93
+ menu a theme will use gets written before the site switches to it. Delete is the only way one
94
+ is removed.
95
+ </p>
96
+
97
+ <ul class="admin-menu-boxes">
98
+ {% for menu in others %}
99
+ {% set remove %}
100
+ <form class="admin-menu-delete" method="post" action="{{ deleteUrl }}">
101
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
102
+ <input type="hidden" name="{{ fields.menu }}" value="{{ menu.name }}" />
103
+ <button type="submit">Delete this menu</button>
104
+ </form>
105
+ {% endset %}
106
+ {{ box(menu, fields, navigationUrl, csrfToken, remove) }}
107
+ {% endfor %}
108
+ </ul>
109
+ {% endif %}
110
+
111
+ <h2>Add a menu</h2>
112
+
113
+ <form class="admin-menu-add" method="post" action="{{ addUrl }}">
114
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
115
+ {% set nameHint %}
116
+ The name a theme asks for the menu by: lower-case letters, digits and
117
+ underscores, starting with a letter. It is written after the dot &mdash;
118
+ <code>{% raw %}{% for item in menus.footer %}{% endraw %}</code> &mdash; which is why a dash
119
+ is not allowed in one. A name this theme declares puts the menu in its place above; any other
120
+ name is a menu kept and rendered nowhere, which is how you fill one in before switching to the
121
+ theme that will use it.
122
+ {% endset %}
123
+ {{ field.text('menu-name', fields.name, 'Name', newName,
124
+ error=nameProblem, hint=nameHint, spellcheck='false', required=true) }}
125
+ <p class="admin-menu-buttons">
126
+ <button type="submit">Add menu</button>
127
+ </p>
128
+ </form>
129
+ {% endblock %}
@@ -1,5 +1,6 @@
1
1
  {# /admin/settings/reading: what a visitor is shown, and who is told when it
2
- changes. #}
2
+ changes. The site menu was here until TASK-108; it is on /admin/navigation,
3
+ with every other menu the site holds. #}
3
4
  {% extends "layouts/settings-page.njk" %}
4
5
  {% import "components/fields.njk" as field %}
5
6
 
@@ -53,15 +54,6 @@
53
54
  error=problems.postsPerPage, inputmode='numeric', required=true,
54
55
  hint='How many posts a listing page holds, on the home page and on every archive.') }}
55
56
 
56
- {% set navigationHint %}
57
- One item per line, written <code>Label | URL</code> &mdash; for example
58
- <code>About | /about/</code> or <code>Mastodon | https://example.social/@me</code>.
59
- They are the menu in the site header, in this order. A page can put itself
60
- on the end of it by ticking <em>Show in navigation</em> in the editor.
61
- {% endset %}
62
- {{ field.textarea('settings-navigation', fields.navigation, 'Menu', form.navigation,
63
- error=problems.navigation, hint=navigationHint, rows='4', spellcheck='false') }}
64
-
65
57
  {% set notifyHint %}
66
58
  An rssCloud and WebSub server, told whenever a feed changes so subscribers
67
59
  hear at once instead of waiting for their next poll. Every feed advertises
@@ -0,0 +1,89 @@
1
+ {# /admin/tools: what the derived index holds, and the button that reads every
2
+ file back into it without stopping the site (TASK-95).
3
+
4
+ The counts are the point of the screen as much as the button is: an index
5
+ that disagrees with the files is not something the site complains about, so
6
+ the only way to notice is to be shown what it holds. #}
7
+ {% extends "layouts/shell.njk" %}
8
+
9
+ {% block title %}{{ heading }}{% endblock %}
10
+
11
+ {% block content %}
12
+ <h1>{{ heading }}</h1>
13
+
14
+ <p>
15
+ Everything in <code>data/geekity.db</code> is a reading of the files under
16
+ <code>{{ contentDir }}</code>, so it can always be read again. Rebuild it after
17
+ editing content outside the admin &mdash; over ssh, or with a
18
+ <code>git pull</code> while the site was down &mdash; or when what is listed here
19
+ disagrees with what is on disk.
20
+ </p>
21
+
22
+ <table class="admin-list">
23
+ <thead>
24
+ <tr>
25
+ <th scope="col">What the index holds</th>
26
+ <th scope="col">Count</th>
27
+ </tr>
28
+ </thead>
29
+ <tbody>
30
+ <tr>
31
+ <th scope="row">Documents</th>
32
+ <td>{{ indexed.documents }}</td>
33
+ </tr>
34
+ <tr>
35
+ <th scope="row">Followers</th>
36
+ <td>{{ indexed.followers }}</td>
37
+ </tr>
38
+ <tr>
39
+ <th scope="row">Inbox activities</th>
40
+ <td>{{ indexed.activities }}</td>
41
+ </tr>
42
+ <tr>
43
+ <th scope="row">Comments</th>
44
+ <td>{{ indexed.comments }}</td>
45
+ </tr>
46
+ </tbody>
47
+ </table>
48
+
49
+ {% if confirm %}
50
+ {# Offered rather than done, the way a merge is on the taxonomy screens.
51
+ The window in the middle is the whole reason: for as long as the scan
52
+ runs the site answers 404 for documents whose files are perfectly fine,
53
+ and that is not a thing to discover afterwards. #}
54
+ <form class="admin-settings" method="post" action="{{ rebuildUrl }}">
55
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
56
+ <input type="hidden" name="{{ fields.confirm }}" value="1" />
57
+
58
+ <h2>Rebuild the index now?</h2>
59
+
60
+ <p>
61
+ The index is emptied and every file read again. Until the scan finishes, this
62
+ site answers <strong>404</strong> for its posts and pages &mdash; under a second
63
+ on a small site, longer on a large one &mdash; and it is serving the whole time.
64
+ Nothing is deleted and nothing is sent: you stay signed in, the delivery log and
65
+ the relays are kept, and no follower, feed or linked page is told about a post
66
+ the scan re-indexes.
67
+ </p>
68
+
69
+ <p class="admin-settings-buttons">
70
+ <button type="submit">Rebuild it</button>
71
+ <a href="{{ toolsUrl }}">Cancel</a>
72
+ </p>
73
+ </form>
74
+ {% else %}
75
+ <form class="admin-settings" method="post" action="{{ rebuildUrl }}">
76
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
77
+ <p class="admin-settings-buttons">
78
+ <button type="submit">Rebuild the index</button>
79
+ </p>
80
+ </form>
81
+ {% endif %}
82
+
83
+ <p class="admin-hint">
84
+ This runs on the site as it is: nothing is deleted, no connection is swapped and
85
+ nobody is signed out. <code>geekity rebuild</code> at the command line is the other
86
+ door, and it is for the one case this screen cannot be: a database this version
87
+ refuses to open, where there is no site running to press a button in.
88
+ </p>
89
+ {% endblock %}
@@ -26,12 +26,14 @@
26
26
  <tr>
27
27
  <td>
28
28
  <a href="{{ entry.editUrl }}">{{ entry.username }}</a>{% if entry.you %} <span class="admin-status">(you)</span>{% endif %}
29
- <br /><a href="{{ entry.archiveUrl }}">{{ entry.archiveUrl }}</a>
30
29
  </td>
31
30
  <td>{% if entry.email %}{{ entry.email }}{% else %}<span class="admin-status">None</span>{% endif %}</td>
32
31
  <td>{{ entry.createdAt | date('html') }}</td>
32
+ {# Edit, then what can be done, then View: the order and the shape
33
+ `pages/documents/list.njk` uses, down to Edit being a plain link
34
+ rather than a button. A row here is a row there. #}
33
35
  <td class="admin-actions">
34
- <a class="admin-button admin-button-small" href="{{ entry.editUrl }}">Edit</a>
36
+ <a href="{{ entry.editUrl }}">Edit</a>
35
37
  {% if entry.deletable %}
36
38
  <form method="post" action="{{ deleteUserUrl }}">
37
39
  <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
@@ -39,6 +41,7 @@
39
41
  <button type="submit">Delete</button>
40
42
  </form>
41
43
  {% endif %}
44
+ <a href="{{ entry.archiveUrl }}">View</a>
42
45
  </td>
43
46
  </tr>
44
47
  {% endfor %}
@@ -1219,3 +1219,63 @@ button[type='submit']:hover,
1219
1219
  color: var(--admin-muted);
1220
1220
  overflow-wrap: anywhere;
1221
1221
  }
1222
+
1223
+ /* Navigation. One panel per menu, stacked down the page rather than laid out
1224
+ as cards: a menu is a box of text somebody types into, so it wants the width
1225
+ a paragraph wants rather than a column in a grid. The two lists — the areas
1226
+ the theme declares, and the menus it renders nowhere — are the same panel,
1227
+ because they are the same thing in two different situations. */
1228
+
1229
+ .admin-menu-boxes {
1230
+ margin: 1rem 0 2rem;
1231
+ padding: 0;
1232
+ list-style: none;
1233
+ }
1234
+
1235
+ .admin-menu-box {
1236
+ max-width: 42rem;
1237
+ padding: 0.9rem 1.1rem 1.1rem;
1238
+ background: var(--admin-surface);
1239
+ border: 1px solid var(--admin-border);
1240
+ }
1241
+
1242
+ .admin-menu-box + .admin-menu-box {
1243
+ margin-top: 1rem;
1244
+ }
1245
+
1246
+ .admin-menu-box h3 {
1247
+ margin: 0;
1248
+ font-size: 1.05rem;
1249
+ }
1250
+
1251
+ /* The name a theme asks for the menu by, and how much is in it. The name is
1252
+ the useful half: it is what a template loops over and what `site.json`
1253
+ holds, so it is printed rather than left implied by the label above it. */
1254
+
1255
+ .admin-menu-name {
1256
+ margin: 0.15rem 0 0.6rem;
1257
+ color: var(--admin-muted);
1258
+ font-size: 0.9em;
1259
+ overflow-wrap: anywhere;
1260
+ }
1261
+
1262
+ .admin-menu-buttons {
1263
+ margin: 0.8rem 0 0;
1264
+ }
1265
+
1266
+ /* Delete is the only way a menu is removed, and it is offered only on one the
1267
+ theme renders nowhere. It sits apart from the Save above it, so the two
1268
+ buttons of one panel are not a pair to be pressed carelessly. */
1269
+
1270
+ .admin-menu-delete {
1271
+ margin-top: 0.8rem;
1272
+ padding-top: 0.8rem;
1273
+ border-top: 1px solid var(--admin-field);
1274
+ }
1275
+
1276
+ .admin-menu-add {
1277
+ max-width: 42rem;
1278
+ padding: 0.9rem 1.1rem 1.1rem;
1279
+ background: var(--admin-surface);
1280
+ border: 1px solid var(--admin-border);
1281
+ }
@@ -0,0 +1,44 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ import type { ResolvedConfig } from './config.ts';
3
+ import type { GeekityEnv } from './env.ts';
4
+ /**
5
+ * Where one access-log line goes.
6
+ *
7
+ * The line arrives without a trailing newline; the sink decides the framing.
8
+ * The default sink is stdout — what a container collects, what dockge shows
9
+ * and what journald keeps — and a test hands in one that keeps the lines.
10
+ */
11
+ export type AccessLogWriter = (line: string) => void;
12
+ /** What {@link createAccessLog} needs to know. */
13
+ export interface AccessLogOptions {
14
+ /**
15
+ * Whether a client address goes on the line, and which one is right. Read
16
+ * once, at boot: the log is registered before the context is, so it cannot
17
+ * read the config off a request.
18
+ */
19
+ config: Pick<ResolvedConfig, 'accessLogAddress' | 'trustProxy'>;
20
+ /** Where the lines go. Defaults to stdout. */
21
+ write?: AccessLogWriter;
22
+ }
23
+ /**
24
+ * One line per request: what was asked, what was answered, and how long it
25
+ * took.
26
+ *
27
+ * ```
28
+ * GET /.well-known/webfinger?resource=acct:ada@example.com 200 3.1ms
29
+ * ```
30
+ *
31
+ * The fields are in a fixed order and separated by spaces, so the status is
32
+ * always the third and a line is readable by eye, by `grep` and by `awk`
33
+ * alike. The client address, when the site asks for one, goes last for that
34
+ * reason — turning it on moves nothing else.
35
+ *
36
+ * Only the request line is ever read: no body is touched, so nothing is
37
+ * buffered and no password posted to the login form can reach the log, and no
38
+ * header but the forwarded address a site has said to believe is looked at, so
39
+ * no cookie and no `Authorization` can either. The query string is logged as
40
+ * it arrived, because it is what a peer's WebFinger lookup carries and what a
41
+ * question about who asked for what is answered with.
42
+ */
43
+ export declare function createAccessLog(options: AccessLogOptions): MiddlewareHandler<GeekityEnv>;
44
+ //# sourceMappingURL=access-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-log.d.ts","sourceRoot":"","sources":["../src/access-log.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAG9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAErD,kDAAkD;AAClD,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,kBAAkB,GAAG,YAAY,CAAC,CAAC;IAChE,8CAA8C;IAC9C,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAsBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,CAAC,UAAU,CAAC,CA4BxF"}
@@ -0,0 +1,71 @@
1
+ import { clientAddress } from './admin/throttle.js';
2
+ /** The default sink: one write per line, the newline included, like the CLI's. */
3
+ function writeToStdout(line) {
4
+ process.stdout.write(`${line}\n`);
5
+ }
6
+ /**
7
+ * The path and query string of a request, taken out of its URL by hand.
8
+ *
9
+ * `new URL(...)` per request would parse and allocate the whole of an address
10
+ * this only ever wants the tail of, and this runs on every request the site
11
+ * answers. There is nothing to escape: Node's HTTP parser refuses a request
12
+ * target with a control character in it, so whatever survives to here is one
13
+ * line's worth of text.
14
+ */
15
+ function requestTarget(url) {
16
+ const afterScheme = url.indexOf('://');
17
+ const start = afterScheme < 0 ? -1 : url.indexOf('/', afterScheme + 3);
18
+ return start < 0 ? '/' : url.slice(start);
19
+ }
20
+ /**
21
+ * One line per request: what was asked, what was answered, and how long it
22
+ * took.
23
+ *
24
+ * ```
25
+ * GET /.well-known/webfinger?resource=acct:ada@example.com 200 3.1ms
26
+ * ```
27
+ *
28
+ * The fields are in a fixed order and separated by spaces, so the status is
29
+ * always the third and a line is readable by eye, by `grep` and by `awk`
30
+ * alike. The client address, when the site asks for one, goes last for that
31
+ * reason — turning it on moves nothing else.
32
+ *
33
+ * Only the request line is ever read: no body is touched, so nothing is
34
+ * buffered and no password posted to the login form can reach the log, and no
35
+ * header but the forwarded address a site has said to believe is looked at, so
36
+ * no cookie and no `Authorization` can either. The query string is logged as
37
+ * it arrived, because it is what a peer's WebFinger lookup carries and what a
38
+ * question about who asked for what is answered with.
39
+ */
40
+ export function createAccessLog(options) {
41
+ const write = options.write ?? writeToStdout;
42
+ const { accessLogAddress, trustProxy } = options.config;
43
+ return async function accessLog(c, next) {
44
+ const started = performance.now();
45
+ let threw = false;
46
+ try {
47
+ await next();
48
+ }
49
+ catch (error) {
50
+ // A handler that threw has no response of its own: the error is on its
51
+ // way up to Hono's error handler, which answers 500, and `c.res` would
52
+ // hand back a freshly made 200 if asked. A request that failed is the
53
+ // one most worth having a line for, so it gets the status it will be
54
+ // answered with rather than the one it never had.
55
+ threw = true;
56
+ throw error;
57
+ }
58
+ finally {
59
+ const elapsed = (performance.now() - started).toFixed(1);
60
+ const status = threw ? 500 : c.res.status;
61
+ let line = `${c.req.method} ${requestTarget(c.req.url)} ${String(status)} ${elapsed}ms`;
62
+ if (accessLogAddress) {
63
+ const address = clientAddress(c, { trustProxy });
64
+ if (address !== undefined)
65
+ line += ` ${address}`;
66
+ }
67
+ write(line);
68
+ }
69
+ };
70
+ }
71
+ //# sourceMappingURL=access-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-log.js","sourceRoot":"","sources":["../src/access-log.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAyBpD,kFAAkF;AAClF,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC;IACvE,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC;IAC7C,MAAM,EAAE,gBAAgB,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAExD,OAAO,KAAK,UAAU,SAAS,CAAC,CAAC,EAAE,IAAI;QACrC,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,uEAAuE;YACvE,uEAAuE;YACvE,sEAAsE;YACtE,qEAAqE;YACrE,kDAAkD;YAClD,KAAK,GAAG,IAAI,CAAC;YACb,MAAM,KAAK,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;YAC1C,IAAI,IAAI,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,OAAO,IAAI,CAAC;YACxF,IAAI,gBAAgB,EAAE,CAAC;gBACrB,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;gBACjD,IAAI,OAAO,KAAK,SAAS;oBAAE,IAAI,IAAI,IAAI,OAAO,EAAE,CAAC;YACnD,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -34,15 +34,9 @@ export interface DocumentKind {
34
34
  */
35
35
  excludable: boolean;
36
36
  /**
37
- * Whether the editor offers the site menu. Pages only: a post is in the
38
- * archive and in the feeds, and putting one in the header beside About is
39
- * not what a menu is for.
40
- */
41
- navigable: boolean;
42
- /**
43
- * Whether the editor offers the contact form (TASK-56). Pages only, for the
44
- * reason the menu is: a form for writing to the site belongs on a standing
45
- * page rather than under one post out of a thousand.
37
+ * Whether the editor offers the contact form (TASK-56). Pages only: a form
38
+ * for writing to the site belongs on a standing page rather than under one
39
+ * post out of a thousand.
46
40
  *
47
41
  * The front matter key itself is honoured wherever it is written, so a theme
48
42
  * that includes the partial in its post layout is free to; this only decides
@@ -168,10 +162,6 @@ export interface EditorForm {
168
162
  draft: boolean;
169
163
  /** Whether `eleventyExcludeFromCollections` is set. Pages only. */
170
164
  exclude: boolean;
171
- /** Whether the page put itself in the site menu. Pages only. */
172
- navigation: boolean;
173
- /** Where in the menu it goes, as typed. Empty for "after the ordered ones". */
174
- navigationOrder: string;
175
165
  /** Whether the page offers a contact form. Pages only. */
176
166
  contact: boolean;
177
167
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/admin/documents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/admin/documents.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,6 @@ import { normalizeBody, serializeDocument } from '../content/writer.js';
10
10
  import { isPublicDocument } from '../web/documents.js';
11
11
  import { COMMENTS_FRONT_MATTER_KEY } from '../comments/policy.js';
12
12
  import { CONTACT_FRONT_MATTER_KEY } from '../contact/form.js';
13
- import { NAVIGATION_KEY, NAVIGATION_ORDER_KEY, navigationOrder } from '../web/navigation.js';
14
13
  import { userForAuthor } from '../web/authors.js';
15
14
  import { findUserById, listUsers } from './accounts.js';
16
15
  import { flash } from './flash.js';
@@ -62,7 +61,6 @@ export const POST_KIND = {
62
61
  tagged: true,
63
62
  categorised: true,
64
63
  excludable: false,
65
- navigable: false,
66
64
  contactable: false,
67
65
  };
68
66
  /** The pages screens: standing content, no date prefix and no taxonomy. */
@@ -76,7 +74,6 @@ export const PAGE_KIND = {
76
74
  tagged: false,
77
75
  categorised: false,
78
76
  excludable: true,
79
- navigable: true,
80
77
  contactable: true,
81
78
  };
82
79
  /** The URL of the editor for one document. */
@@ -202,8 +199,6 @@ async function saveFromForm(c, options) {
202
199
  author: text(body['author']).trim(),
203
200
  draft: body['draft'] !== undefined,
204
201
  exclude: body['exclude'] !== undefined,
205
- navigation: body['navigation'] !== undefined,
206
- navigationOrder: text(body['navigation_order']).trim(),
207
202
  contact: body['contact'] !== undefined,
208
203
  comments: commentSetting(text(body['comments'])),
209
204
  body: normalizeBody(text(body['body'])),
@@ -225,9 +220,6 @@ async function saveFromForm(c, options) {
225
220
  }
226
221
  if (form.title === '')
227
222
  return refuse(`A ${kind.singular} needs a title.`);
228
- if (form.navigationOrder !== '' && !Number.isFinite(Number(form.navigationOrder))) {
229
- return refuse('A menu order is a number, and pulls the lower numbers to the front.');
230
- }
231
223
  const timezone = siteTimezone(c);
232
224
  // decision-11: what the file gets is a UTC instant, and an offset-less field
233
225
  // is the site's own wall clock rather than the server's.
@@ -486,29 +478,9 @@ function resolveExtra(kind, document, form) {
486
478
  else if (EXCLUDE_KEY in extra)
487
479
  extra[EXCLUDE_KEY] = false;
488
480
  }
489
- if (kind.navigable) {
490
- if (form.navigation) {
491
- extra[NAVIGATION_KEY] = true;
492
- if (form.navigationOrder === '')
493
- delete extra[NAVIGATION_ORDER_KEY];
494
- else
495
- extra[NAVIGATION_ORDER_KEY] = Number(form.navigationOrder);
496
- }
497
- else {
498
- // Both keys go rather than being written `false`, which is where this
499
- // parts company with `eleventyExcludeFromCollections` above. That one is
500
- // Eleventy's key and a site may have written it by hand for a build of
501
- // its own, so a `false` says something; these two are the CMS's own, and
502
- // absent and false mean the same thing to everything that reads them. An
503
- // order on a page that is not in the menu means nothing at all.
504
- delete extra[NAVIGATION_KEY];
505
- delete extra[NAVIGATION_ORDER_KEY];
506
- }
507
- }
508
481
  if (kind.contactable) {
509
- // Written or removed, never `false`, for the reason the menu keys are: it
510
- // is the CMS's own key, and absent and false mean the same thing to
511
- // everything that reads it (TASK-56).
482
+ // Written or removed, never `false`: it is the CMS's own key, and absent
483
+ // and false mean the same thing to everything that reads it (TASK-56).
512
484
  if (form.contact)
513
485
  extra[CONTACT_FRONT_MATTER_KEY] = true;
514
486
  else
@@ -760,8 +732,6 @@ export function blankForm(kind, timezone = DEFAULT_TIMEZONE, now = new Date()) {
760
732
  author: '',
761
733
  draft: false,
762
734
  exclude: false,
763
- navigation: false,
764
- navigationOrder: '',
765
735
  contact: false,
766
736
  comments: COMMENT_SETTINGS.site,
767
737
  body: '',
@@ -788,8 +758,6 @@ export function formFor(document, timezone = DEFAULT_TIMEZONE) {
788
758
  author: document.author ?? '',
789
759
  draft: document.draft,
790
760
  exclude: document.extra[EXCLUDE_KEY] === true,
791
- navigation: document.extra[NAVIGATION_KEY] === true,
792
- navigationOrder: navigationOrder(document)?.toString() ?? '',
793
761
  contact: document.extra[CONTACT_FRONT_MATTER_KEY] === true,
794
762
  comments: commentSettingOf(document),
795
763
  body: document.body,