@geekity/cms 0.7.0 → 0.9.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 +445 -63
- package/admin/layouts/shell.njk +13 -6
- package/admin/pages/comments/all.njk +3 -0
- package/admin/pages/error.njk +11 -0
- package/admin/pages/settings/discussion.njk +53 -0
- package/admin/pages/settings/email.njk +20 -0
- package/admin/pages/tools/personal-data.njk +67 -0
- package/admin/pages/users/edit.njk +1 -1
- package/admin/static/admin.css +69 -15
- package/dist/admin/comments.d.ts +5 -0
- package/dist/admin/comments.d.ts.map +1 -1
- package/dist/admin/comments.js +13 -0
- package/dist/admin/comments.js.map +1 -1
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +58 -31
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/headers.d.ts +14 -5
- package/dist/admin/headers.d.ts.map +1 -1
- package/dist/admin/headers.js +18 -6
- package/dist/admin/headers.js.map +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/menu.d.ts.map +1 -1
- package/dist/admin/menu.js +5 -1
- package/dist/admin/menu.js.map +1 -1
- package/dist/admin/personal-data.d.ts +35 -0
- package/dist/admin/personal-data.d.ts.map +1 -0
- package/dist/admin/personal-data.js +90 -0
- package/dist/admin/personal-data.js.map +1 -0
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +23 -4
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/session.d.ts +42 -1
- package/dist/admin/session.d.ts.map +1 -1
- package/dist/admin/session.js +74 -4
- package/dist/admin/session.js.map +1 -1
- package/dist/admin/settings-discussion.d.ts.map +1 -1
- package/dist/admin/settings-discussion.js +9 -1
- package/dist/admin/settings-discussion.js.map +1 -1
- package/dist/admin/settings-email.d.ts.map +1 -1
- package/dist/admin/settings-email.js +10 -1
- package/dist/admin/settings-email.js.map +1 -1
- package/dist/admin/settings.d.ts +39 -0
- package/dist/admin/settings.d.ts.map +1 -1
- package/dist/admin/settings.js +144 -0
- package/dist/admin/settings.js.map +1 -1
- package/dist/admin/store.d.ts +30 -0
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +68 -5
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/templates.d.ts +2 -0
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +2 -0
- package/dist/admin/templates.js.map +1 -1
- package/dist/avatars/avatars.d.ts +112 -0
- package/dist/avatars/avatars.d.ts.map +1 -0
- package/dist/avatars/avatars.js +259 -0
- package/dist/avatars/avatars.js.map +1 -0
- package/dist/avatars/index.d.ts +4 -0
- package/dist/avatars/index.d.ts.map +1 -0
- package/dist/avatars/index.js +3 -0
- package/dist/avatars/index.js.map +1 -0
- package/dist/avatars/routes.d.ts +11 -0
- package/dist/avatars/routes.d.ts.map +1 -0
- package/dist/avatars/routes.js +33 -0
- package/dist/avatars/routes.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +128 -3
- package/dist/cli.js.map +1 -1
- package/dist/comments/records.d.ts +15 -0
- package/dist/comments/records.d.ts.map +1 -1
- package/dist/comments/records.js +51 -1
- package/dist/comments/records.js.map +1 -1
- package/dist/comments/routes.d.ts.map +1 -1
- package/dist/comments/routes.js +7 -1
- package/dist/comments/routes.js.map +1 -1
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -1
- package/dist/contact/index.d.ts +1 -1
- package/dist/contact/index.d.ts.map +1 -1
- package/dist/contact/index.js +1 -1
- package/dist/contact/index.js.map +1 -1
- package/dist/contact/records.d.ts +8 -2
- package/dist/contact/records.d.ts.map +1 -1
- package/dist/contact/records.js +13 -6
- package/dist/contact/records.js.map +1 -1
- package/dist/content/document.d.ts +10 -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/parser.d.ts.map +1 -1
- package/dist/content/parser.js +13 -0
- package/dist/content/parser.js.map +1 -1
- package/dist/content/store.d.ts +13 -2
- package/dist/content/store.d.ts.map +1 -1
- package/dist/content/store.js +45 -1
- package/dist/content/store.js.map +1 -1
- package/dist/content/writer.d.ts.map +1 -1
- package/dist/content/writer.js +4 -0
- package/dist/content/writer.js.map +1 -1
- package/dist/env.d.ts +18 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/article.d.ts +9 -0
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +24 -2
- package/dist/federation/article.js.map +1 -1
- package/dist/federation/federation.d.ts.map +1 -1
- package/dist/federation/federation.js +24 -6
- package/dist/federation/federation.js.map +1 -1
- package/dist/federation/inbox.d.ts +17 -3
- package/dist/federation/inbox.d.ts.map +1 -1
- package/dist/federation/inbox.js +72 -6
- package/dist/federation/inbox.js.map +1 -1
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +8 -3
- package/dist/federation/mount.js.map +1 -1
- package/dist/federation/paths.d.ts +5 -0
- package/dist/federation/paths.d.ts.map +1 -1
- package/dist/federation/paths.js +5 -0
- package/dist/federation/paths.js.map +1 -1
- package/dist/federation/quotes.d.ts +58 -0
- package/dist/federation/quotes.d.ts.map +1 -0
- package/dist/federation/quotes.js +89 -0
- package/dist/federation/quotes.js.map +1 -0
- package/dist/federation/replies.d.ts +23 -0
- package/dist/federation/replies.d.ts.map +1 -1
- package/dist/federation/replies.js +47 -10
- package/dist/federation/replies.js.map +1 -1
- package/dist/federation/wordpress.d.ts.map +1 -1
- package/dist/federation/wordpress.js +5 -4
- package/dist/federation/wordpress.js.map +1 -1
- package/dist/index.d.ts +23 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +64 -7
- package/dist/index.js.map +1 -1
- package/dist/maintenance.d.ts +47 -0
- package/dist/maintenance.d.ts.map +1 -0
- package/dist/maintenance.js +78 -0
- package/dist/maintenance.js.map +1 -0
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/optouts.d.ts +6 -0
- package/dist/notifications/optouts.d.ts.map +1 -1
- package/dist/notifications/optouts.js +17 -0
- package/dist/notifications/optouts.js.map +1 -1
- package/dist/privacy/erase.d.ts +45 -0
- package/dist/privacy/erase.d.ts.map +1 -0
- package/dist/privacy/erase.js +77 -0
- package/dist/privacy/erase.js.map +1 -0
- package/dist/privacy/policy.d.ts +32 -0
- package/dist/privacy/policy.d.ts.map +1 -0
- package/dist/privacy/policy.js +37 -0
- package/dist/privacy/policy.js.map +1 -0
- package/dist/privacy/retention.d.ts +79 -0
- package/dist/privacy/retention.d.ts.map +1 -0
- package/dist/privacy/retention.js +133 -0
- package/dist/privacy/retention.js.map +1 -0
- package/dist/web/conversation.d.ts +9 -1
- package/dist/web/conversation.d.ts.map +1 -1
- package/dist/web/conversation.js +69 -39
- package/dist/web/conversation.js.map +1 -1
- package/dist/web/documents.d.ts +9 -0
- package/dist/web/documents.d.ts.map +1 -1
- package/dist/web/documents.js +31 -0
- package/dist/web/documents.js.map +1 -1
- package/dist/web/errors.d.ts +19 -0
- package/dist/web/errors.d.ts.map +1 -0
- package/dist/web/errors.js +93 -0
- package/dist/web/errors.js.map +1 -0
- package/dist/web/health.d.ts +6 -0
- package/dist/web/health.d.ts.map +1 -1
- package/dist/web/health.js +5 -1
- package/dist/web/health.js.map +1 -1
- package/dist/web/index.d.ts +5 -0
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +4 -0
- package/dist/web/index.js.map +1 -1
- package/dist/web/maintenance.d.ts +18 -0
- package/dist/web/maintenance.d.ts.map +1 -0
- package/dist/web/maintenance.js +113 -0
- package/dist/web/maintenance.js.map +1 -0
- package/dist/web/redirects.d.ts +75 -0
- package/dist/web/redirects.d.ts.map +1 -0
- package/dist/web/redirects.js +200 -0
- package/dist/web/redirects.js.map +1 -0
- package/dist/web/render.d.ts +9 -0
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +19 -0
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +57 -0
- package/dist/web/routes.js.map +1 -1
- package/dist/web/well-known.d.ts +21 -0
- package/dist/web/well-known.d.ts.map +1 -0
- package/dist/web/well-known.js +66 -0
- package/dist/web/well-known.js.map +1 -0
- package/dist/webmention/fetch-public.d.ts +34 -0
- package/dist/webmention/fetch-public.d.ts.map +1 -0
- package/dist/webmention/fetch-public.js +101 -0
- package/dist/webmention/fetch-public.js.map +1 -0
- package/dist/webmention/reply-context.d.ts.map +1 -1
- package/dist/webmention/reply-context.js +12 -89
- package/dist/webmention/reply-context.js.map +1 -1
- package/package.json +1 -1
- package/templates/site/content/_data/site.json +7 -1
- package/templates/site/content/pages/privacy.md +127 -0
- package/themes/default/README.md +58 -19
- package/themes/default/layouts/500.njk +8 -0
- package/themes/default/layouts/503.njk +8 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { discoverPostType } from '../content/post-type.js';
|
|
2
|
-
import {
|
|
2
|
+
import { fetchPublic, webUrl } from './fetch-public.js';
|
|
3
3
|
import { elementsIn, parseHtml, textOf } from './html.js';
|
|
4
4
|
import { citedEntry } from './microformats.js';
|
|
5
|
-
import { publicHost } from './public-address.js';
|
|
6
5
|
/**
|
|
7
6
|
* Reply context: what a reply shows of the post it answers (TASK-123).
|
|
8
7
|
*
|
|
@@ -16,8 +15,6 @@ import { publicHost } from './public-address.js';
|
|
|
16
15
|
export const REPLY_CONTEXT_TIMEOUT_MS = 10_000;
|
|
17
16
|
/** The most of a target page that is read. A bigger page is not read at all. */
|
|
18
17
|
export const REPLY_CONTEXT_MAX_BYTES = 1_000_000;
|
|
19
|
-
/** How many redirects are followed, each one checked like the first. */
|
|
20
|
-
const MAX_REDIRECTS = 5;
|
|
21
18
|
/** How many words of a target's text a preview keeps. */
|
|
22
19
|
const EXCERPT_WORDS = 40;
|
|
23
20
|
/** And how many characters, for a text with few spaces in it. */
|
|
@@ -30,49 +27,17 @@ const EXCERPT_CHARACTERS = 300;
|
|
|
30
27
|
* over the whole exchange, and no page over the byte limit. Nothing throws.
|
|
31
28
|
*/
|
|
32
29
|
export async function fetchReplyContext(target, options) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const response = await fetch(url, {
|
|
45
|
-
headers: { accept: 'text/html, */*;q=0.8', 'user-agent': WEBMENTION_USER_AGENT },
|
|
46
|
-
redirect: 'manual',
|
|
47
|
-
signal,
|
|
48
|
-
});
|
|
49
|
-
const location = response.headers.get('location');
|
|
50
|
-
if (response.status >= 300 && response.status < 400 && location !== null) {
|
|
51
|
-
await response.body?.cancel();
|
|
52
|
-
if (hop === MAX_REDIRECTS)
|
|
53
|
-
return refuse('too many redirects');
|
|
54
|
-
at = new URL(location, url).href;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (!response.ok) {
|
|
58
|
-
await response.body?.cancel();
|
|
59
|
-
return refuse(`answered ${String(response.status)}`);
|
|
60
|
-
}
|
|
61
|
-
const type = response.headers.get('content-type') ?? '';
|
|
62
|
-
if (!/^\s*(text\/html|application\/xhtml\+xml)/i.test(type)) {
|
|
63
|
-
await response.body?.cancel();
|
|
64
|
-
return refuse('not an HTML page');
|
|
65
|
-
}
|
|
66
|
-
const body = await readWithin(response, maxBytes);
|
|
67
|
-
if (body === undefined)
|
|
68
|
-
return refuse(`larger than ${String(maxBytes)} bytes`);
|
|
69
|
-
const context = readReplyContext(body, target, url.href);
|
|
70
|
-
return context === undefined ? refuse('nothing to show') : { ok: true, context };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
catch (thrown) {
|
|
74
|
-
return refuse(thrown instanceof Error ? thrown.message : String(thrown));
|
|
75
|
-
}
|
|
30
|
+
const fetched = await fetchPublic(target, {
|
|
31
|
+
lookup: options.lookup,
|
|
32
|
+
timeoutMs: options.timeoutMs ?? REPLY_CONTEXT_TIMEOUT_MS,
|
|
33
|
+
maxBytes: options.maxBytes ?? REPLY_CONTEXT_MAX_BYTES,
|
|
34
|
+
accept: 'text/html, */*;q=0.8',
|
|
35
|
+
contentType: { pattern: /^\s*(text\/html|application\/xhtml\+xml)/i, name: 'an HTML page' },
|
|
36
|
+
});
|
|
37
|
+
if (!fetched.ok)
|
|
38
|
+
return fetched;
|
|
39
|
+
const context = readReplyContext(new TextDecoder().decode(fetched.body), target, fetched.url);
|
|
40
|
+
return context === undefined ? refuse('nothing to show') : { ok: true, context };
|
|
76
41
|
}
|
|
77
42
|
/**
|
|
78
43
|
* What a target page says about itself, or `undefined` when it says nothing a
|
|
@@ -112,35 +77,6 @@ export function readReplyContext(html, target, base = target) {
|
|
|
112
77
|
return undefined;
|
|
113
78
|
return { url: target, ...(name === '' ? {} : { name }), ...(text === '' ? {} : { text }) };
|
|
114
79
|
}
|
|
115
|
-
/** A response body when it is no bigger than the limit, else `undefined`. */
|
|
116
|
-
async function readWithin(response, maxBytes) {
|
|
117
|
-
const declared = Number(response.headers.get('content-length') ?? '0');
|
|
118
|
-
if (declared > maxBytes) {
|
|
119
|
-
await response.body?.cancel();
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
122
|
-
const body = response.body;
|
|
123
|
-
if (body === null)
|
|
124
|
-
return '';
|
|
125
|
-
const reader = body.getReader();
|
|
126
|
-
const decoder = new TextDecoder();
|
|
127
|
-
const chunks = [];
|
|
128
|
-
let read = 0;
|
|
129
|
-
for (;;) {
|
|
130
|
-
const chunk = await reader.read();
|
|
131
|
-
if (chunk.done)
|
|
132
|
-
break;
|
|
133
|
-
const value = chunk.value;
|
|
134
|
-
read += value.length;
|
|
135
|
-
if (read > maxBytes) {
|
|
136
|
-
await reader.cancel();
|
|
137
|
-
return undefined;
|
|
138
|
-
}
|
|
139
|
-
chunks.push(decoder.decode(value, { stream: true }));
|
|
140
|
-
}
|
|
141
|
-
chunks.push(decoder.decode());
|
|
142
|
-
return chunks.join('');
|
|
143
|
-
}
|
|
144
80
|
/** The page's `<title>`, or empty. */
|
|
145
81
|
function titleOf(root) {
|
|
146
82
|
for (const element of elementsIn(root)) {
|
|
@@ -170,19 +106,6 @@ function excerpt(text) {
|
|
|
170
106
|
cut = cut.slice(0, EXCERPT_CHARACTERS).trimEnd();
|
|
171
107
|
return cut === normalized ? normalized : `${cut} …`;
|
|
172
108
|
}
|
|
173
|
-
/** A URL, when it is an http or https one with a host. */
|
|
174
|
-
function webUrl(value) {
|
|
175
|
-
let url;
|
|
176
|
-
try {
|
|
177
|
-
url = new URL(value);
|
|
178
|
-
}
|
|
179
|
-
catch {
|
|
180
|
-
return undefined;
|
|
181
|
-
}
|
|
182
|
-
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
183
|
-
return undefined;
|
|
184
|
-
return url.hostname === '' ? undefined : url;
|
|
185
|
-
}
|
|
186
109
|
function refuse(reason) {
|
|
187
110
|
return { ok: false, reason };
|
|
188
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reply-context.js","sourceRoot":"","sources":["../../src/webmention/reply-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"reply-context.js","sourceRoot":"","sources":["../../src/webmention/reply-context.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
{ "label": "About", "url": "/about/" },
|
|
10
10
|
{ "label": "Search", "url": "/search/" }
|
|
11
11
|
],
|
|
12
|
-
"footer": [
|
|
12
|
+
"footer": [
|
|
13
|
+
{ "label": "RSS", "url": "/feed/" },
|
|
14
|
+
{ "label": "Privacy", "url": "/privacy/" }
|
|
15
|
+
]
|
|
13
16
|
},
|
|
17
|
+
"commentEmailRetentionDays": 180,
|
|
18
|
+
"addressHashRetentionDays": 30,
|
|
19
|
+
"contactMessageRetentionDays": 365,
|
|
14
20
|
"postsPerPage": 10,
|
|
15
21
|
"feedSize": 20
|
|
16
22
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Privacy
|
|
3
|
+
permalink: /privacy/
|
|
4
|
+
description: What this site stores about the people who read it, write to it and comment on it, and for how long.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
> **To the owner of this site.** `geekity init` wrote this page from what the
|
|
8
|
+
> Geekity CMS does out of the box. It is a starting point, not legal advice,
|
|
9
|
+
> and it knows nothing about your hosting, your analytics, your mail provider
|
|
10
|
+
> or anything you added. Review it, correct it, and delete this note before
|
|
11
|
+
> you rely on it. If you change a retention period on Settings > Discussion,
|
|
12
|
+
> change the number here too. If you delete this page, delete the
|
|
13
|
+
> `Privacy | /privacy/` line from the footer menu as well, so nothing links to
|
|
14
|
+
> it.
|
|
15
|
+
|
|
16
|
+
This page says what this site stores about you, where it keeps it, how long it
|
|
17
|
+
keeps it, and who else sees it.
|
|
18
|
+
|
|
19
|
+
## Reading the site
|
|
20
|
+
|
|
21
|
+
This site sets no cookies on your browser while you read it, and it runs no
|
|
22
|
+
analytics or advertising scripts. Its fonts, styles and scripts come from this
|
|
23
|
+
site, not from another server.
|
|
24
|
+
|
|
25
|
+
Pictures of other people, such as the avatar of somebody who replied from
|
|
26
|
+
another site, are fetched and stored by this site and served from here. Your
|
|
27
|
+
browser does not fetch them from the other server, so that server does not
|
|
28
|
+
learn your IP address.
|
|
29
|
+
|
|
30
|
+
When you follow a link from this site to another one, your browser tells that
|
|
31
|
+
site only which site you came from, not which page.
|
|
32
|
+
|
|
33
|
+
To slow down abuse, the site counts requests from each IP address for a few
|
|
34
|
+
minutes. The counts are kept in memory only, and they are lost when the site
|
|
35
|
+
restarts. The web server writes a log line for each request. By default the
|
|
36
|
+
line does not contain your IP address. The owner can turn that on, and whoever
|
|
37
|
+
hosts the site can keep its own logs.
|
|
38
|
+
|
|
39
|
+
## Comments
|
|
40
|
+
|
|
41
|
+
When you leave a comment, the site stores:
|
|
42
|
+
|
|
43
|
+
- Your name, your website and the words you type. These are shown on the page
|
|
44
|
+
beside your comment, for as long as the comment is up.
|
|
45
|
+
- Your email address, if you give one. It is optional, it is never shown, and
|
|
46
|
+
it is used to decide whether your comment needs a moderator and to email you
|
|
47
|
+
about replies if you ask for that. The site deletes it after 180 days.
|
|
48
|
+
- A salted hash of your IP address, which lets the site spot a run of spam from
|
|
49
|
+
one place. The IP address itself is never stored. The site deletes the hash
|
|
50
|
+
after 30 days.
|
|
51
|
+
|
|
52
|
+
If this site can send mail, a new comment waiting for a moderator is emailed
|
|
53
|
+
to the people who moderate it.
|
|
54
|
+
|
|
55
|
+
Comments are stored as files beside the posts, and the site may keep those
|
|
56
|
+
files in version control. A copy deleted from the site can stay in that
|
|
57
|
+
history until the owner rewrites it.
|
|
58
|
+
|
|
59
|
+
**Reply emails (optional).** If this site can send mail and you tick "email me
|
|
60
|
+
when somebody replies", the site emails you when a reply to your comment is
|
|
61
|
+
approved. Each message has an unsubscribe link. If you unsubscribe, the site
|
|
62
|
+
keeps your address on a list of addresses it must not email about replies.
|
|
63
|
+
The mail provider the owner chose sees each address a message goes to.
|
|
64
|
+
|
|
65
|
+
## The contact form (optional)
|
|
66
|
+
|
|
67
|
+
If this site has a contact form and you send a message, the site stores your
|
|
68
|
+
name, your email address, the subject, the message and a salted hash of your
|
|
69
|
+
IP address, in a private file that is not published. If the site can send
|
|
70
|
+
mail, it also emails the message to the owner, through the mail provider the
|
|
71
|
+
owner chose. The site deletes the message after 365 days, and the hash after 30
|
|
72
|
+
days.
|
|
73
|
+
|
|
74
|
+
## Spam checking with Akismet (optional)
|
|
75
|
+
|
|
76
|
+
Only if the owner turns it on, the site sends each new comment, webmention and
|
|
77
|
+
contact message to Akismet, a spam-checking service run by Automattic. Akismet
|
|
78
|
+
receives your IP address, your browser's user agent, the page you came from,
|
|
79
|
+
your name, email address and website, and what you wrote. Akismet's own privacy
|
|
80
|
+
policy covers what it does with them.
|
|
81
|
+
|
|
82
|
+
## Replies from other sites
|
|
83
|
+
|
|
84
|
+
**Webmentions.** When another site links to a post here and sends a
|
|
85
|
+
webmention, this site fetches that page and stores its author's name, website
|
|
86
|
+
and avatar address and the words of the page, and may show them as a comment.
|
|
87
|
+
They are a copy of a page that is already public. When a post here links to
|
|
88
|
+
another site, this site sends that site a webmention, which tells it only the
|
|
89
|
+
address of the post.
|
|
90
|
+
|
|
91
|
+
**The fediverse.** Posts here are published over ActivityPub, so they are
|
|
92
|
+
copied to the servers of the people who follow this site, on Mastodon and
|
|
93
|
+
similar services. If you follow this site from one of those services, the site
|
|
94
|
+
stores your public profile: your handle, display name, avatar address and
|
|
95
|
+
profile address, until you unfollow. That list is kept in the site's files
|
|
96
|
+
beside the posts. When you reply to, like, boost or quote a
|
|
97
|
+
post, the site stores the activity and who sent it, and may show replies under
|
|
98
|
+
the post.
|
|
99
|
+
|
|
100
|
+
## Accounts
|
|
101
|
+
|
|
102
|
+
The people who write this site have accounts. The site stores each account's
|
|
103
|
+
username, email address, a hash of its password and its public profile. A
|
|
104
|
+
signed-in account gets one cookie, `__Host-geekity_session` (called
|
|
105
|
+
`geekity_session` on a site without https), which holds a random session id and
|
|
106
|
+
nothing else. It is deleted when the person signs out or the session expires.
|
|
107
|
+
|
|
108
|
+
## How long the site keeps things
|
|
109
|
+
|
|
110
|
+
| What | How long |
|
|
111
|
+
| ------------------------------------------------ | ------------------ |
|
|
112
|
+
| The email address on a comment | 180 days |
|
|
113
|
+
| The hash of the IP address on a comment or message | 30 days |
|
|
114
|
+
| A contact message | 365 days |
|
|
115
|
+
| Everything else above | Until it is deleted |
|
|
116
|
+
|
|
117
|
+
The site checks these periods when it starts and every six hours after that.
|
|
118
|
+
|
|
119
|
+
## Asking for your data to be removed
|
|
120
|
+
|
|
121
|
+
Write to the owner of this site. The owner can find every comment and contact
|
|
122
|
+
message that carries your email address and erase them together, on
|
|
123
|
+
Tools > Personal data: your comments stay in their threads signed "Anonymous",
|
|
124
|
+
without your email, website or address hash, your messages are deleted, and
|
|
125
|
+
your address comes off the list of addresses not to email about replies.
|
|
126
|
+
Comments left without an email address, webmentions and fediverse replies are
|
|
127
|
+
removed one at a time.
|
package/themes/default/README.md
CHANGED
|
@@ -17,6 +17,8 @@ themes/default/
|
|
|
17
17
|
author.njk one person's archive, paginated
|
|
18
18
|
search.njk the search form and what it found, paginated
|
|
19
19
|
404.njk nothing at this URL
|
|
20
|
+
500.njk the request failed on the server
|
|
21
|
+
503.njk the site is in maintenance mode
|
|
20
22
|
partials/
|
|
21
23
|
post-list.njk the h-feed a listing is made of
|
|
22
24
|
pagination.njk previous/next pager
|
|
@@ -373,6 +375,24 @@ alone.
|
|
|
373
375
|
|
|
374
376
|
`layouts/404.njk` says `Content not found.` and links home and to the search.
|
|
375
377
|
|
|
378
|
+
`layouts/500.njk` is the page a request gets when the server fails while
|
|
379
|
+
answering it (TASK-129). It says `Something went wrong.` in an
|
|
380
|
+
`article.server-error` and links home. The context is the 404's: `title`,
|
|
381
|
+
`url` and `page.url`. The response is a 500 with `Cache-Control: no-store`, and
|
|
382
|
+
the error itself goes to the server's log, never into the page. If this template
|
|
383
|
+
fails too, the CMS answers with a plain HTML page of its own instead, so a theme
|
|
384
|
+
cannot turn one failure into a crash. A request for the `.md` or `.json`
|
|
385
|
+
representation gets a 500 in that format and never reaches this template.
|
|
386
|
+
|
|
387
|
+
`layouts/503.njk` is the page a request gets while the site is in maintenance
|
|
388
|
+
mode (TASK-130). It says `Down for maintenance.` in an `article.maintenance`.
|
|
389
|
+
The context is the 404's, plus `until` (an ISO 8601 time) and `untilText` (the
|
|
390
|
+
same time as an HTTP date) when the operator named when the site should be
|
|
391
|
+
back. The response is a 503 with `Retry-After` and `Cache-Control: no-store`.
|
|
392
|
+
The theme's files under `/theme/` are still served, so the page can use the
|
|
393
|
+
stylesheet. If this template fails, the CMS answers with a plain HTML page of
|
|
394
|
+
its own.
|
|
395
|
+
|
|
376
396
|
### Search
|
|
377
397
|
|
|
378
398
|
`layouts/search.njk` is the page at `/search/` (TASK-22). It prints
|
|
@@ -1031,8 +1051,9 @@ Category archives get their own three the same way.
|
|
|
1031
1051
|
## The conversation
|
|
1032
1052
|
|
|
1033
1053
|
`conversation` is what has been said about a post: the fediverse replies, likes
|
|
1034
|
-
and boosts its inbox was sent, the
|
|
1035
|
-
the
|
|
1054
|
+
and boosts its inbox was sent, the fediverse posts that quoted it with its
|
|
1055
|
+
author's approval, the comments people left on the page itself, and the
|
|
1056
|
+
webmentions other pages sent it.
|
|
1036
1057
|
It is on the context of a rendered post **only when there is something in it**,
|
|
1037
1058
|
so a post nobody has answered renders no empty section and a layout can simply
|
|
1038
1059
|
ask:
|
|
@@ -1072,29 +1093,29 @@ the thread, because it is not an answer.
|
|
|
1072
1093
|
| `replies` | The replies to the post, oldest first, each carrying its own `replies`. See below. |
|
|
1073
1094
|
| `likes` | The likes, oldest first, in the same shape. |
|
|
1074
1095
|
| `boosts` | The boosts, oldest first, in the same shape. A webmention `repost` is one of them. |
|
|
1075
|
-
| `mentions` | The pages
|
|
1096
|
+
| `mentions` | The pages and fediverse quotes about this post, oldest first, in the same shape. |
|
|
1076
1097
|
| `counts.replies` | How many replies, counted through the whole thread rather than the top of it. |
|
|
1077
1098
|
| `counts.likes`, `counts.boosts`, `counts.mentions` | How many of each. |
|
|
1078
1099
|
| `counts.total` | All four added up. Zero never reaches a template: there would be no `conversation` at all. |
|
|
1079
1100
|
|
|
1080
1101
|
Each entry — a reply, a like or a boost — is:
|
|
1081
1102
|
|
|
1082
|
-
| Key | What it holds
|
|
1083
|
-
| ---------------- |
|
|
1084
|
-
| `id` | What it is called: a reply's own note id, which is what an answer to it names.
|
|
1085
|
-
| `source` | `"activitypub"` for a fediverse reply, `"comment"` for one left on the page, `"webmention"` for another page linking here.
|
|
1086
|
-
| `kind` | `"reply"`, `"like"`, `"boost"`, `"repost"` or `"mention"`.
|
|
1087
|
-
| `author.name` | The best name available: their display name, else their handle, else their id. For a webmention, the source's `h-card` name, else its host.
|
|
1088
|
-
| `author.handle` | `@user@host`, or `null`. Taken from the follower profile the site holds, else guessed from the actor URL. `null` for a native comment.
|
|
1089
|
-
| `author.url` | Their profile page, the website a commenter typed, or a webmention author's `u-url`. May be `null`, so guard the link.
|
|
1090
|
-
| `author.avatar` | Their avatar, or `null`. The site knows one for an actor that follows it and for a webmention whose `h-card` carried a `u-photo`.
|
|
1091
|
-
| `author.actorId` | Their id, which is what identifies them however they are named. `null` for a native comment.
|
|
1092
|
-
| `url` | Where it can be read: the remote note's `url` for a fediverse reply, the source page for a webmention, and `{permalink}#comment-{id}` — this page's own anchor — for a native comment.
|
|
1093
|
-
| `content` | What it says, **already sanitised**, so print it with `\| safe`. Empty for a like or a boost.
|
|
1094
|
-
| `published` | A `Date`: when it was published, or when it arrived if it did not say. Use the `date` filter.
|
|
1095
|
-
| `inReplyTo` | What it answers — the post's ActivityPub id, or another reply's — and `null` for a reaction.
|
|
1096
|
-
| `status` | `"published"`. On the record for the sources that moderate.
|
|
1097
|
-
| `replies` | The replies to this one, oldest first, nested as deep as the site has seen.
|
|
1103
|
+
| Key | What it holds |
|
|
1104
|
+
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1105
|
+
| `id` | What it is called: a reply's own note id, which is what an answer to it names. |
|
|
1106
|
+
| `source` | `"activitypub"` for a fediverse reply, `"comment"` for one left on the page, `"webmention"` for another page linking here. |
|
|
1107
|
+
| `kind` | `"reply"`, `"like"`, `"boost"`, `"repost"` or `"mention"`. A `repost` comes from a webmention; a `mention` is a webmention or an approved fediverse quote. |
|
|
1108
|
+
| `author.name` | The best name available: their display name, else their handle, else their id. For a webmention, the source's `h-card` name, else its host. |
|
|
1109
|
+
| `author.handle` | `@user@host`, or `null`. Taken from the follower profile the site holds, else guessed from the actor URL. `null` for a native comment. |
|
|
1110
|
+
| `author.url` | Their profile page, the website a commenter typed, or a webmention author's `u-url`. May be `null`, so guard the link. |
|
|
1111
|
+
| `author.avatar` | Their avatar, or `null`. The site knows one for an actor that follows it and for a webmention whose `h-card` carried a `u-photo`. Always a same-origin `/_geekity/avatars/…` path, never the remote URL: see [Avatars](#avatars). |
|
|
1112
|
+
| `author.actorId` | Their id, which is what identifies them however they are named. `null` for a native comment. |
|
|
1113
|
+
| `url` | Where it can be read: the remote note's `url` for a fediverse reply or quote, the source page for a webmention, and `{permalink}#comment-{id}` — this page's own anchor — for a native comment. |
|
|
1114
|
+
| `content` | What it says, **already sanitised**, so print it with `\| safe`. Empty for a like or a boost. |
|
|
1115
|
+
| `published` | A `Date`: when it was published, or when it arrived if it did not say. Use the `date` filter. |
|
|
1116
|
+
| `inReplyTo` | What it answers — the post's ActivityPub id, or another reply's — and `null` for a reaction. |
|
|
1117
|
+
| `status` | `"published"`. On the record for the sources that moderate. |
|
|
1118
|
+
| `replies` | The replies to this one, oldest first, nested as deep as the site has seen. |
|
|
1098
1119
|
|
|
1099
1120
|
Three rules decide what is in the thread, and they are the CMS's rather than a
|
|
1100
1121
|
theme's: a reply whose author deleted it is gone, and its own answers move up to
|
|
@@ -1151,6 +1172,24 @@ and then loops over exactly the keys above. The slug is what names the post's
|
|
|
1151
1172
|
comment file under `content/_data/comments/`; the format is documented in
|
|
1152
1173
|
backlog doc-6.
|
|
1153
1174
|
|
|
1175
|
+
### Avatars
|
|
1176
|
+
|
|
1177
|
+
A reader's browser never fetches a face from the server it lives on. The
|
|
1178
|
+
`author.avatar` a theme is handed is a path on this site, under
|
|
1179
|
+
`/_geekity/avatars/`, and the CMS fetches the picture, crops it to an
|
|
1180
|
+
80-pixel square WebP (twice the 40 pixels this theme shows a commenter at)
|
|
1181
|
+
and keeps it under `data/avatars/`. Print it as it is; it needs no filter.
|
|
1182
|
+
|
|
1183
|
+
The route serves only the pictures the site shows: a follower who has
|
|
1184
|
+
liked, boosted, quoted or answered a post, and the author of an approved
|
|
1185
|
+
comment or webmention. Any other URL is a 404, and nothing is fetched for it.
|
|
1186
|
+
Pictures are fetched in the background when the site starts and every six
|
|
1187
|
+
hours after, refreshed once they are a week old, and forgotten once nothing
|
|
1188
|
+
shows them. A picture that cannot be fetched, is over 2 MB, or is not a JPEG,
|
|
1189
|
+
PNG, GIF, WebP or AVIF is served as a grey placeholder, and tried again an
|
|
1190
|
+
hour later. `data/avatars/` is a cache: delete it and every picture is fetched
|
|
1191
|
+
again.
|
|
1192
|
+
|
|
1154
1193
|
## The comment form
|
|
1155
1194
|
|
|
1156
1195
|
`commentForm` is on the context of a rendered post **only when that post is
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{% extends "layouts/base.njk" %}
|
|
2
|
+
|
|
3
|
+
{% block content %}
|
|
4
|
+
<article class="server-error">
|
|
5
|
+
<h1 class="page-title">Something went wrong.</h1>
|
|
6
|
+
<p>This page could not be shown because of a problem on the server. Try again in a moment, or go <a href="{{ "/" | url }}">home</a>.</p>
|
|
7
|
+
</article>
|
|
8
|
+
{% endblock %}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{% extends "layouts/base.njk" %}
|
|
2
|
+
|
|
3
|
+
{% block content %}
|
|
4
|
+
<article class="maintenance">
|
|
5
|
+
<h1 class="page-title">Down for maintenance.</h1>
|
|
6
|
+
<p>This site is down for maintenance and will be back shortly.{% if until %} It should be back by <time datetime="{{ until }}">{{ untilText }}</time>.{% endif %} Please try again then.</p>
|
|
7
|
+
</article>
|
|
8
|
+
{% endblock %}
|