roster-server 2.3.4 → 2.3.8
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 +5 -1
- package/docs/decisions/local-subdomain-localhost-url.md +12 -0
- package/docs/generated/git-greenlock-notify-fallback-v2-3-4.md +7 -0
- package/docs/generated/greenlock-notify-domain-context.md +12 -0
- package/index.js +46 -1
- package/package.json +1 -1
- package/test/roster-server.test.js +8 -1
package/README.md
CHANGED
|
@@ -309,12 +309,13 @@ await roster.start();
|
|
|
309
309
|
const url = roster.getUrl('example.com');
|
|
310
310
|
console.log(url);
|
|
311
311
|
// Local mode: http://localhost:9465
|
|
312
|
+
// Local subdomain: http://api.localhost:9465
|
|
312
313
|
// Production mode: https://example.com
|
|
313
314
|
```
|
|
314
315
|
|
|
315
316
|
This method:
|
|
316
317
|
- Returns the correct URL based on your environment (`local: true/false`)
|
|
317
|
-
- In **local mode**: Returns `http://localhost:{port}`
|
|
318
|
+
- In **local mode**: Returns `http://localhost:{port}` for apex domains and `http://{subdomain}.localhost:{port}` for subdomains
|
|
318
319
|
- In **production mode**: Returns `https://{domain}` (or with custom port if configured)
|
|
319
320
|
- Handles `www.` prefix automatically (returns same URL)
|
|
320
321
|
- Returns `null` for domains that aren't registered
|
|
@@ -327,9 +328,12 @@ import Roster from 'roster-server';
|
|
|
327
328
|
// Local development
|
|
328
329
|
const localRoster = new Roster({ local: true });
|
|
329
330
|
localRoster.register('example.com', handler);
|
|
331
|
+
localRoster.register('api.example.com', handler);
|
|
330
332
|
await localRoster.start();
|
|
331
333
|
console.log(localRoster.getUrl('example.com'));
|
|
332
334
|
// → http://localhost:9465
|
|
335
|
+
console.log(localRoster.getUrl('api.example.com'));
|
|
336
|
+
// → http://api.localhost:7342
|
|
333
337
|
|
|
334
338
|
// Production
|
|
335
339
|
const prodRoster = new Roster({ local: false });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: wrxv04lnq9
|
|
3
|
+
type: decision
|
|
4
|
+
title: 'Decision: Local subdomain URL format'
|
|
5
|
+
created: '2026-03-16 17:15:01'
|
|
6
|
+
---
|
|
7
|
+
# Decision: Local subdomain URL format
|
|
8
|
+
|
|
9
|
+
**What**: In local mode, `getUrl(domain)` now returns `http://<subdomain>.localhost:<port>` for subdomains, while apex domains keep `http://localhost:<port>`.
|
|
10
|
+
**Where**: Implemented in `index.js` (`getUrl` local branch), validated in `test/roster-server.test.js`, and documented in `README.md`.
|
|
11
|
+
**Why**: Local URLs should preserve subdomain semantics so multi-tenant/subdomain routes are explicit and easier to test.
|
|
12
|
+
**Alternatives rejected**: Keeping all local URLs as `http://localhost:<port>` was rejected because it hides host intent for subdomain-based apps.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: oyzcq7vmk6
|
|
3
|
+
type: work-log
|
|
4
|
+
title: Committed and pushed greenlock notify fix
|
|
5
|
+
created: '2026-03-16 15:55:18'
|
|
6
|
+
---
|
|
7
|
+
Created commit bfdb456 on master and pushed to origin/master. Changes: improved notify() detail-to-message normalization in index.js to handle Error objects, undefined/non-serializable inputs, and fallback to `[event] (no details)`; bumped package version to 2.3.4; added docs/generated/greenlock-notify-empty-details.md.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: blpic0aj8a
|
|
3
|
+
type: bugfix
|
|
4
|
+
title: 'Bugfix: Include domain in Greenlock notify logs'
|
|
5
|
+
created: '2026-03-16 16:25:57'
|
|
6
|
+
---
|
|
7
|
+
# Bug: Greenlock notify errors lacked domain context
|
|
8
|
+
|
|
9
|
+
**Symptom**: Timeout/certificate errors in `notify` logs did not clearly indicate which domain triggered the failure.
|
|
10
|
+
**Root cause**: `notify` logged only the normalized message and omitted available domain fields from Greenlock/ACME details.
|
|
11
|
+
**Solution**: Added domain extraction in `notify` from `subject`, `servername`, `domain`, `hostname`, `host`, `altnames`, `domains`, and `identifier.value`; prepends `[domain:<value>]` to log messages.
|
|
12
|
+
**Location**: index.js Greenlock `notify` callback.
|
package/index.js
CHANGED
|
@@ -606,7 +606,22 @@ class Roster {
|
|
|
606
606
|
if (this.local) {
|
|
607
607
|
const pattern = resolved.siteKey.split(':')[0];
|
|
608
608
|
if (this.domainPorts && this.domainPorts[pattern] !== undefined) {
|
|
609
|
-
|
|
609
|
+
const labels = cleanDomain.split('.').filter(Boolean);
|
|
610
|
+
let localHost = 'localhost';
|
|
611
|
+
|
|
612
|
+
// For wildcard matches, preserve the full left-side subdomain chain.
|
|
613
|
+
if (pattern.startsWith('*.')) {
|
|
614
|
+
const root = wildcardRoot(pattern);
|
|
615
|
+
if (root && hostMatchesWildcard(cleanDomain, pattern)) {
|
|
616
|
+
const subdomain = cleanDomain.slice(0, -(root.length + 1));
|
|
617
|
+
if (subdomain) localHost = `${subdomain}.localhost`;
|
|
618
|
+
}
|
|
619
|
+
} else if (labels.length > 2) {
|
|
620
|
+
// For exact subdomains (e.g. api.example.com), map to api.localhost.
|
|
621
|
+
localHost = `${labels.slice(0, -2).join('.')}.localhost`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return `http://${localHost}:${this.domainPorts[pattern]}`;
|
|
610
625
|
}
|
|
611
626
|
return null;
|
|
612
627
|
}
|
|
@@ -736,6 +751,33 @@ class Roster {
|
|
|
736
751
|
cluster: this.cluster,
|
|
737
752
|
staging: this.staging,
|
|
738
753
|
notify: (event, details) => {
|
|
754
|
+
const eventDomain = (() => {
|
|
755
|
+
if (!details || typeof details !== 'object') return null;
|
|
756
|
+
|
|
757
|
+
const directKeys = ['subject', 'servername', 'domain', 'hostname', 'host'];
|
|
758
|
+
for (const key of directKeys) {
|
|
759
|
+
if (typeof details[key] === 'string' && details[key].trim()) {
|
|
760
|
+
return details[key].trim().toLowerCase();
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (Array.isArray(details.altnames) && details.altnames.length > 0) {
|
|
765
|
+
const alt = details.altnames.find(name => typeof name === 'string' && name.trim());
|
|
766
|
+
if (alt) return alt.trim().toLowerCase();
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (Array.isArray(details.domains) && details.domains.length > 0) {
|
|
770
|
+
const domain = details.domains.find(name => typeof name === 'string' && name.trim());
|
|
771
|
+
if (domain) return domain.trim().toLowerCase();
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (details.identifier && typeof details.identifier.value === 'string' && details.identifier.value.trim()) {
|
|
775
|
+
return details.identifier.value.trim().toLowerCase();
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return null;
|
|
779
|
+
})();
|
|
780
|
+
|
|
739
781
|
let msg;
|
|
740
782
|
if (typeof details === 'string') {
|
|
741
783
|
msg = details;
|
|
@@ -751,6 +793,9 @@ class Roster {
|
|
|
751
793
|
}
|
|
752
794
|
}
|
|
753
795
|
if (!msg || msg === 'undefined') msg = `[${event}] (no details)`;
|
|
796
|
+
if (eventDomain && !msg.includes(`[${eventDomain}]`)) {
|
|
797
|
+
msg = `[domain:${eventDomain}] ${msg}`;
|
|
798
|
+
}
|
|
754
799
|
// Suppress known benign warnings from ACME when using acme-dns-01-cli
|
|
755
800
|
if (event === 'warning' && typeof msg === 'string') {
|
|
756
801
|
if (/acme-dns-01-cli.*(incorrect function signatures|deprecated use of callbacks)/i.test(msg)) return;
|
package/package.json
CHANGED
|
@@ -218,7 +218,7 @@ describe('Roster', () => {
|
|
|
218
218
|
roster.register('*.example.com', () => {});
|
|
219
219
|
roster.domainPorts = { '*.example.com': 9999 };
|
|
220
220
|
roster.local = true;
|
|
221
|
-
assert.strictEqual(roster.getUrl('api.example.com'), 'http://localhost:9999');
|
|
221
|
+
assert.strictEqual(roster.getUrl('api.example.com'), 'http://api.localhost:9999');
|
|
222
222
|
});
|
|
223
223
|
it('returns https URL for wildcard-matched host in production', () => {
|
|
224
224
|
const roster = new Roster({ local: false });
|
|
@@ -359,6 +359,13 @@ describe('Roster', () => {
|
|
|
359
359
|
roster.local = true;
|
|
360
360
|
assert.strictEqual(roster.getUrl('exact.local'), 'http://localhost:4567');
|
|
361
361
|
});
|
|
362
|
+
it('returns http://subdomain.localhost:PORT in local mode for exact subdomain', () => {
|
|
363
|
+
const roster = new Roster({ local: true });
|
|
364
|
+
roster.register('api.example.com', () => {});
|
|
365
|
+
roster.domainPorts = { 'api.example.com': 5678 };
|
|
366
|
+
roster.local = true;
|
|
367
|
+
assert.strictEqual(roster.getUrl('api.example.com'), 'http://api.localhost:5678');
|
|
368
|
+
});
|
|
362
369
|
it('returns https URL in production for registered domain', () => {
|
|
363
370
|
const roster = new Roster({ local: false });
|
|
364
371
|
roster.register('example.com', () => {});
|