roster-server 2.3.0 → 2.3.4
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.
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: zskjlbshg2
|
|
3
|
+
type: bugfix
|
|
4
|
+
title: 'Bugfix: Harden Greenlock notify logging fallback'
|
|
5
|
+
created: '2026-03-16 15:54:09'
|
|
6
|
+
---
|
|
7
|
+
# Bug: Greenlock notify emitted unhelpful error logs
|
|
8
|
+
|
|
9
|
+
**Symptom**: Log lines such as `roster:error ----` appeared, indicating error events with missing/invalid details.
|
|
10
|
+
**Root cause**: `notify` used `details?.message ?? JSON.stringify(details)`, which could produce `undefined` (or throw on non-serializable data), resulting in low-signal logger output.
|
|
11
|
+
**Solution**: Added robust message normalization in `notify` to handle strings, `Error` instances, plain objects, and serialization failures, then fallback to `[{event}] (no details)` when empty.
|
|
12
|
+
**Location**: index.js notify callback inside Greenlock options.
|
package/index.js
CHANGED
|
@@ -736,7 +736,21 @@ class Roster {
|
|
|
736
736
|
cluster: this.cluster,
|
|
737
737
|
staging: this.staging,
|
|
738
738
|
notify: (event, details) => {
|
|
739
|
-
|
|
739
|
+
let msg;
|
|
740
|
+
if (typeof details === 'string') {
|
|
741
|
+
msg = details;
|
|
742
|
+
} else if (details instanceof Error) {
|
|
743
|
+
msg = details.stack || details.message;
|
|
744
|
+
} else if (details && typeof details === 'object' && typeof details.message === 'string') {
|
|
745
|
+
msg = details.message;
|
|
746
|
+
} else {
|
|
747
|
+
try {
|
|
748
|
+
msg = JSON.stringify(details);
|
|
749
|
+
} catch {
|
|
750
|
+
msg = String(details);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (!msg || msg === 'undefined') msg = `[${event}] (no details)`;
|
|
740
754
|
// Suppress known benign warnings from ACME when using acme-dns-01-cli
|
|
741
755
|
if (event === 'warning' && typeof msg === 'string') {
|
|
742
756
|
if (/acme-dns-01-cli.*(incorrect function signatures|deprecated use of callbacks)/i.test(msg)) return;
|
|
@@ -97,14 +97,24 @@ function createStaticHandler(rootPath) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const stat = fs.statSync(filePath);
|
|
100
|
+
let servePath = filePath;
|
|
100
101
|
if (!stat.isFile()) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
if (!stat.isDirectory()) {
|
|
103
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
104
|
+
res.end('Not Found');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const indexInDir = path.join(filePath, 'index.html');
|
|
108
|
+
if (!fs.existsSync(indexInDir) || !fs.statSync(indexInDir).isFile()) {
|
|
109
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
110
|
+
res.end('Not Found');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
servePath = indexInDir;
|
|
104
114
|
}
|
|
105
115
|
|
|
106
|
-
const contentType = getContentType(
|
|
107
|
-
const content = fs.readFileSync(
|
|
116
|
+
const contentType = getContentType(servePath);
|
|
117
|
+
const content = fs.readFileSync(servePath);
|
|
108
118
|
|
|
109
119
|
res.writeHead(200, {
|
|
110
120
|
'Content-Type': contentType,
|
package/package.json
CHANGED
|
@@ -626,6 +626,33 @@ describe('Roster loadSites', () => {
|
|
|
626
626
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
627
627
|
}
|
|
628
628
|
});
|
|
629
|
+
|
|
630
|
+
it('static site serves index.html for subpath directory (/en/)', async () => {
|
|
631
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'roster-test-'));
|
|
632
|
+
const wwwPath = path.join(tmpDir, 'www');
|
|
633
|
+
const siteDir = path.join(wwwPath, 'subpath.example');
|
|
634
|
+
fs.mkdirSync(siteDir, { recursive: true });
|
|
635
|
+
fs.writeFileSync(path.join(siteDir, 'index.html'), '<html>root</html>', 'utf8');
|
|
636
|
+
const enDir = path.join(siteDir, 'en');
|
|
637
|
+
fs.mkdirSync(enDir, { recursive: true });
|
|
638
|
+
fs.writeFileSync(path.join(enDir, 'index.html'), '<html><body>en page</body></html>', 'utf8');
|
|
639
|
+
const roster = new Roster({ wwwPath, local: true, minLocalPort: 19220, maxLocalPort: 19229 });
|
|
640
|
+
try {
|
|
641
|
+
await roster.start();
|
|
642
|
+
const port = roster.domainPorts['subpath.example'];
|
|
643
|
+
assert.ok(typeof port === 'number');
|
|
644
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
645
|
+
const resultSlash = await httpGet('localhost', port, '/en/');
|
|
646
|
+
assert.strictEqual(resultSlash.statusCode, 200);
|
|
647
|
+
assert.ok(resultSlash.body.includes('en page'));
|
|
648
|
+
const resultNoSlash = await httpGet('localhost', port, '/en');
|
|
649
|
+
assert.strictEqual(resultNoSlash.statusCode, 200);
|
|
650
|
+
assert.ok(resultNoSlash.body.includes('en page'));
|
|
651
|
+
} finally {
|
|
652
|
+
closePortServers(roster);
|
|
653
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
654
|
+
}
|
|
655
|
+
});
|
|
629
656
|
});
|
|
630
657
|
|
|
631
658
|
describe('Roster generateConfigJson', () => {
|