domma-cms 0.25.10 → 0.25.12
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/bin/cli.js +3 -0
- package/bin/update.js +12 -9
- package/package.json +6 -1
- package/plugins/analytics/plugin.js +30 -4
- package/plugins/analytics/plugin.json +11 -3
- package/plugins/analytics/daily.json +0 -8
- package/plugins/analytics/journeys.json +0 -18
- package/plugins/analytics/lifetime.json +0 -25
- package/plugins/analytics/stats.json +0 -24
package/bin/cli.js
CHANGED
package/bin/update.js
CHANGED
|
@@ -467,18 +467,21 @@ export default async function update(_positional, flags) {
|
|
|
467
467
|
done();
|
|
468
468
|
|
|
469
469
|
// -------------------------------------------------------------------------
|
|
470
|
-
// 11. Ensure
|
|
470
|
+
// 11. Ensure runtime state is in .gitignore (self-heal existing installs)
|
|
471
|
+
// .domma-backups/ + analytics counters — tracking the latter resets
|
|
472
|
+
// live analytics on every checkout/pull/update (recurring bug).
|
|
471
473
|
// -------------------------------------------------------------------------
|
|
472
474
|
|
|
473
475
|
const gitignorePath = path.join(cwd, '.gitignore');
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
476
|
+
const requiredIgnores = [
|
|
477
|
+
'.domma-backups/',
|
|
478
|
+
'plugins/analytics/data/',
|
|
479
|
+
];
|
|
480
|
+
const current = existsSync(gitignorePath) ? readFileSync(gitignorePath, 'utf8') : '';
|
|
481
|
+
const missing = requiredIgnores.filter(entry => !current.split(/\r?\n/).includes(entry));
|
|
482
|
+
if (missing.length) {
|
|
483
|
+
const prefix = current ? current.trimEnd() + '\n' : '';
|
|
484
|
+
writeFileSync(gitignorePath, prefix + missing.join('\n') + '\n', 'utf8');
|
|
482
485
|
}
|
|
483
486
|
|
|
484
487
|
// -------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domma-cms",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.12",
|
|
4
4
|
"description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/server.js",
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
"config/",
|
|
21
21
|
"!config/connections.json",
|
|
22
22
|
"plugins/",
|
|
23
|
+
"!plugins/analytics/data",
|
|
24
|
+
"!plugins/analytics/stats.json",
|
|
25
|
+
"!plugins/analytics/daily.json",
|
|
26
|
+
"!plugins/analytics/lifetime.json",
|
|
27
|
+
"!plugins/analytics/journeys.json",
|
|
23
28
|
"scripts/",
|
|
24
29
|
"docs/",
|
|
25
30
|
"CHANGELOG.md",
|
|
@@ -19,10 +19,17 @@ import path from 'path';
|
|
|
19
19
|
import {fileURLToPath} from 'url';
|
|
20
20
|
|
|
21
21
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
|
|
23
|
+
// Runtime counters live under data/ — the carve-out that BOTH update paths
|
|
24
|
+
// preserve (siteCmsUpdater's copyPlugin backs up data/; `domma-cms update`
|
|
25
|
+
// keeps 'data' in its preserve set). Pre-2.1 they sat in the plugin root and
|
|
26
|
+
// were overwritten by the manager's copy on every plugin push. On first boot
|
|
27
|
+
// migrateToDataDir() moves any pre-2.1 root files across.
|
|
28
|
+
const DATA_DIR = path.join(__dirname, 'data');
|
|
29
|
+
const LIFETIME_FILE = path.join(DATA_DIR, 'lifetime.json');
|
|
30
|
+
const DAILY_FILE = path.join(DATA_DIR, 'daily.json');
|
|
31
|
+
const LEGACY_FILE = path.join(DATA_DIR, 'stats.json');
|
|
32
|
+
const JOURNEYS_FILE = path.join(DATA_DIR, 'journeys.json');
|
|
26
33
|
const JOURNEY_DAILY_CAP = 5000;
|
|
27
34
|
const REALTIME_WINDOW_MS = 5 * 60 * 1000;
|
|
28
35
|
|
|
@@ -35,11 +42,29 @@ async function readJson(file, fallback) {
|
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
async function writeJson(file, data) {
|
|
45
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
38
46
|
const tmp = file + '.tmp';
|
|
39
47
|
await fs.writeFile(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
40
48
|
await fs.rename(tmp, file);
|
|
41
49
|
}
|
|
42
50
|
|
|
51
|
+
/**
|
|
52
|
+
* One-shot relocation of pre-2.1 counters from the plugin root into data/.
|
|
53
|
+
* Runs before migrateLegacy() so the rest of the plugin only ever sees data/.
|
|
54
|
+
*/
|
|
55
|
+
async function migrateToDataDir() {
|
|
56
|
+
await fs.mkdir(DATA_DIR, { recursive: true });
|
|
57
|
+
for (const name of ['lifetime.json', 'daily.json', 'journeys.json', 'stats.json']) {
|
|
58
|
+
const dest = path.join(DATA_DIR, name);
|
|
59
|
+
try { await fs.access(dest); continue; } catch { /* dest absent — maybe move */ }
|
|
60
|
+
const oldPath = path.join(__dirname, name);
|
|
61
|
+
try {
|
|
62
|
+
await fs.access(oldPath);
|
|
63
|
+
await fs.rename(oldPath, dest);
|
|
64
|
+
} catch { /* no pre-2.1 file to move */ }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
43
68
|
/**
|
|
44
69
|
* One-shot migration from the legacy flat stats.json.
|
|
45
70
|
* Preserves existing totals as lifetime numbers; daily history starts empty.
|
|
@@ -253,6 +278,7 @@ export default async function analyticsPlugin(fastify, options) {
|
|
|
253
278
|
const { authenticate, requireAdmin } = options.auth;
|
|
254
279
|
const settings = options.settings || {};
|
|
255
280
|
|
|
281
|
+
await migrateToDataDir();
|
|
256
282
|
await migrateLegacy();
|
|
257
283
|
|
|
258
284
|
fastify.decorate('analytics', {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "analytics",
|
|
3
3
|
"displayName": "Analytics",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Page view analytics with daily trends, time-range filtering, top-pages table and CSV export. Honours Do-Not-Track and dedups per session.",
|
|
6
6
|
"author": "Darryl Waterhouse",
|
|
7
7
|
"date": "2026-05-04",
|
|
@@ -37,11 +37,19 @@
|
|
|
37
37
|
"scaffold": {
|
|
38
38
|
"reset": [
|
|
39
39
|
{
|
|
40
|
-
"path": "lifetime.json",
|
|
40
|
+
"path": "data/lifetime.json",
|
|
41
41
|
"content": "{}"
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
|
-
"path": "daily.json",
|
|
44
|
+
"path": "data/daily.json",
|
|
45
|
+
"content": "{}"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"path": "data/journeys.json",
|
|
49
|
+
"content": "{}"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"path": "data/stats.json",
|
|
45
53
|
"content": "{}"
|
|
46
54
|
}
|
|
47
55
|
]
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"2026-05-06": [
|
|
3
|
-
{
|
|
4
|
-
"sid": "test-controller",
|
|
5
|
-
"t": 1778084386121,
|
|
6
|
-
"url": "/test-from-controller",
|
|
7
|
-
"ref": "/"
|
|
8
|
-
}
|
|
9
|
-
],
|
|
10
|
-
"2026-05-22": [
|
|
11
|
-
{
|
|
12
|
-
"sid": "test-sid-123",
|
|
13
|
-
"t": 1779450391054,
|
|
14
|
-
"url": "/about",
|
|
15
|
-
"ref": ""
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"/": 162,
|
|
3
|
-
"/about": 89,
|
|
4
|
-
"/blog": 42,
|
|
5
|
-
"/contact": 31,
|
|
6
|
-
"/resources/typography": 4,
|
|
7
|
-
"/resources": 13,
|
|
8
|
-
"/resources/shortcodes": 14,
|
|
9
|
-
"/resources/cards": 19,
|
|
10
|
-
"/resources/interactive": 13,
|
|
11
|
-
"/resources/grid": 6,
|
|
12
|
-
"/forms": 14,
|
|
13
|
-
"/resources/effects": 6,
|
|
14
|
-
"/blog/hello-world": 28,
|
|
15
|
-
"/feedback": 42,
|
|
16
|
-
"/resources/dependencies": 2,
|
|
17
|
-
"/resources/components": 6,
|
|
18
|
-
"/gdpr": 3,
|
|
19
|
-
"/scratch": 84,
|
|
20
|
-
"/getting-started": 3,
|
|
21
|
-
"/resources/pro": 1,
|
|
22
|
-
"/todo": 23,
|
|
23
|
-
"/thank-you": 1,
|
|
24
|
-
"/test-from-controller": 1
|
|
25
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"/": 162,
|
|
3
|
-
"/about": 88,
|
|
4
|
-
"/blog": 42,
|
|
5
|
-
"/contact": 31,
|
|
6
|
-
"/resources/typography": 4,
|
|
7
|
-
"/resources": 13,
|
|
8
|
-
"/resources/shortcodes": 14,
|
|
9
|
-
"/resources/cards": 19,
|
|
10
|
-
"/resources/interactive": 13,
|
|
11
|
-
"/resources/grid": 6,
|
|
12
|
-
"/forms": 14,
|
|
13
|
-
"/resources/effects": 6,
|
|
14
|
-
"/blog/hello-world": 28,
|
|
15
|
-
"/feedback": 42,
|
|
16
|
-
"/resources/dependencies": 2,
|
|
17
|
-
"/resources/components": 6,
|
|
18
|
-
"/gdpr": 3,
|
|
19
|
-
"/scratch": 84,
|
|
20
|
-
"/getting-started": 3,
|
|
21
|
-
"/resources/pro": 1,
|
|
22
|
-
"/todo": 23,
|
|
23
|
-
"/thank-you": 1
|
|
24
|
-
}
|