roster-server 2.2.4 → 2.2.5
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/package.json
CHANGED
|
@@ -28,16 +28,16 @@ describe('acme-dns-01-cli-wrapper automatic Linode DNS', () => {
|
|
|
28
28
|
const calls = [];
|
|
29
29
|
global.fetch = async (url, options = {}) => {
|
|
30
30
|
calls.push({ url, options });
|
|
31
|
-
if (url.endsWith('/domains
|
|
32
|
-
return { ok: true, status: 200, json: async () => ({}) };
|
|
31
|
+
if (url.endsWith('/domains?page_size=500')) {
|
|
32
|
+
return { ok: true, status: 200, json: async () => ({ data: [{ id: 42, domain: 'example.com' }] }) };
|
|
33
33
|
}
|
|
34
|
-
if (url.endsWith('/domains/
|
|
34
|
+
if (url.endsWith('/domains/42/records?page_size=500')) {
|
|
35
35
|
return { ok: true, status: 200, json: async () => ({ data: [] }) };
|
|
36
36
|
}
|
|
37
|
-
if (url.endsWith('/domains/
|
|
37
|
+
if (url.endsWith('/domains/42/records') && options.method === 'POST') {
|
|
38
38
|
return { ok: true, status: 200, json: async () => ({ id: 321 }) };
|
|
39
39
|
}
|
|
40
|
-
if (url.endsWith('/domains/
|
|
40
|
+
if (url.endsWith('/domains/42/records/321') && options.method === 'DELETE') {
|
|
41
41
|
return { ok: true, status: 204, json: async () => ({}) };
|
|
42
42
|
}
|
|
43
43
|
return { ok: false, status: 404, statusText: 'not mocked', text: async () => 'not mocked' };
|
|
@@ -55,8 +55,8 @@ describe('acme-dns-01-cli-wrapper automatic Linode DNS', () => {
|
|
|
55
55
|
await challenger.set(opts);
|
|
56
56
|
await challenger.remove(opts);
|
|
57
57
|
|
|
58
|
-
assert.ok(calls.some((c) => c.url.endsWith('/domains/
|
|
59
|
-
assert.ok(calls.some((c) => c.url.endsWith('/domains/
|
|
58
|
+
assert.ok(calls.some((c) => c.url.endsWith('/domains/42/records') && c.options.method === 'POST'));
|
|
59
|
+
assert.ok(calls.some((c) => c.url.endsWith('/domains/42/records/321') && c.options.method === 'DELETE'));
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
it('uses LINODE_API_KEY from environment', async () => {
|
|
@@ -64,10 +64,10 @@ describe('acme-dns-01-cli-wrapper automatic Linode DNS', () => {
|
|
|
64
64
|
const calls = [];
|
|
65
65
|
global.fetch = async (url, options = {}) => {
|
|
66
66
|
calls.push({ url, options });
|
|
67
|
-
if (url.endsWith('/domains
|
|
68
|
-
return { ok: true, status: 200, json: async () => ({}) };
|
|
67
|
+
if (url.endsWith('/domains?page_size=500')) {
|
|
68
|
+
return { ok: true, status: 200, json: async () => ({ data: [{ id: 42, domain: 'example.com' }] }) };
|
|
69
69
|
}
|
|
70
|
-
if (url.endsWith('/domains/
|
|
70
|
+
if (url.endsWith('/domains/42/records?page_size=500')) {
|
|
71
71
|
return { ok: true, status: 200, json: async () => ({ data: [{ id: 111, type: 'TXT', name: '_acme-challenge', target: 'test-token' }] }) };
|
|
72
72
|
}
|
|
73
73
|
return { ok: true, status: 204, json: async () => ({}) };
|
|
@@ -90,19 +90,16 @@ describe('acme-dns-01-cli-wrapper automatic Linode DNS', () => {
|
|
|
90
90
|
const calls = [];
|
|
91
91
|
global.fetch = async (url, options = {}) => {
|
|
92
92
|
calls.push({ url, options });
|
|
93
|
-
if (url.endsWith('/domains
|
|
94
|
-
return { ok:
|
|
93
|
+
if (url.endsWith('/domains?page_size=500')) {
|
|
94
|
+
return { ok: true, status: 200, json: async () => ({ data: [{ id: 99, domain: 'example.com' }] }) };
|
|
95
95
|
}
|
|
96
|
-
if (url.endsWith('/domains/
|
|
97
|
-
return { ok: true, status: 200, json: async () => ({}) };
|
|
98
|
-
}
|
|
99
|
-
if (url.endsWith('/domains/example.com/records?page_size=500')) {
|
|
96
|
+
if (url.endsWith('/domains/99/records?page_size=500')) {
|
|
100
97
|
return { ok: true, status: 200, json: async () => ({ data: [] }) };
|
|
101
98
|
}
|
|
102
|
-
if (url.endsWith('/domains/
|
|
99
|
+
if (url.endsWith('/domains/99/records') && options.method === 'POST') {
|
|
103
100
|
return { ok: true, status: 200, json: async () => ({ id: 654 }) };
|
|
104
101
|
}
|
|
105
|
-
if (url.endsWith('/domains/
|
|
102
|
+
if (url.endsWith('/domains/99/records/654') && options.method === 'DELETE') {
|
|
106
103
|
return { ok: true, status: 204, json: async () => ({}) };
|
|
107
104
|
}
|
|
108
105
|
return { ok: false, status: 404, statusText: 'not mocked', text: async () => 'not mocked' };
|
|
@@ -127,7 +124,7 @@ describe('acme-dns-01-cli-wrapper automatic Linode DNS', () => {
|
|
|
127
124
|
await challenger.set(opts);
|
|
128
125
|
await challenger.remove(opts);
|
|
129
126
|
|
|
130
|
-
const postCall = calls.find((c) => c.url.endsWith('/domains/
|
|
127
|
+
const postCall = calls.find((c) => c.url.endsWith('/domains/99/records') && c.options.method === 'POST');
|
|
131
128
|
assert.ok(postCall, 'expected TXT create call on parent zone');
|
|
132
129
|
const payload = JSON.parse(postCall.options.body);
|
|
133
130
|
assert.strictEqual(payload.name, '_acme-challenge.sub');
|
|
@@ -179,13 +176,13 @@ describe('acme-dns-01-cli-wrapper automatic Linode DNS', () => {
|
|
|
179
176
|
const calls = [];
|
|
180
177
|
global.fetch = async (url, options = {}) => {
|
|
181
178
|
calls.push({ url, options });
|
|
182
|
-
if (url.endsWith('/domains
|
|
183
|
-
return { ok: true, status: 200, json: async () => ({}) };
|
|
179
|
+
if (url.endsWith('/domains?page_size=500')) {
|
|
180
|
+
return { ok: true, status: 200, json: async () => ({ data: [{ id: 42, domain: 'example.com' }] }) };
|
|
184
181
|
}
|
|
185
|
-
if (url.endsWith('/domains/
|
|
182
|
+
if (url.endsWith('/domains/42/records?page_size=500')) {
|
|
186
183
|
return { ok: true, status: 200, json: async () => ({ data: [] }) };
|
|
187
184
|
}
|
|
188
|
-
if (url.endsWith('/domains/
|
|
185
|
+
if (url.endsWith('/domains/42/records') && options.method === 'POST') {
|
|
189
186
|
return { ok: true, status: 200, json: async () => ({ id: 222 }) };
|
|
190
187
|
}
|
|
191
188
|
return { ok: true, status: 204, json: async () => ({}) };
|
|
@@ -201,7 +198,7 @@ describe('acme-dns-01-cli-wrapper automatic Linode DNS', () => {
|
|
|
201
198
|
});
|
|
202
199
|
|
|
203
200
|
await challenger.set(buildChallengeOpts());
|
|
204
|
-
const postCall = calls.find((c) => c.url.endsWith('/domains/
|
|
201
|
+
const postCall = calls.find((c) => c.url.endsWith('/domains/42/records') && c.options.method === 'POST');
|
|
205
202
|
assert.ok(postCall, 'expected TXT create call');
|
|
206
203
|
const payload = JSON.parse(postCall.options.body);
|
|
207
204
|
assert.strictEqual(payload.ttl_sec, 300);
|
|
@@ -212,10 +209,10 @@ describe('acme-dns-01-cli-wrapper automatic Linode DNS', () => {
|
|
|
212
209
|
const calls = [];
|
|
213
210
|
global.fetch = async (url, options = {}) => {
|
|
214
211
|
calls.push({ url, options });
|
|
215
|
-
if (url.endsWith('/domains
|
|
216
|
-
return { ok: true, status: 200, json: async () => ({}) };
|
|
212
|
+
if (url.endsWith('/domains?page_size=500')) {
|
|
213
|
+
return { ok: true, status: 200, json: async () => ({ data: [{ id: 42, domain: 'example.com' }] }) };
|
|
217
214
|
}
|
|
218
|
-
if (url.endsWith('/domains/
|
|
215
|
+
if (url.endsWith('/domains/42/records?page_size=500')) {
|
|
219
216
|
return { ok: true, status: 200, json: async () => ({ data: [{ id: 111, type: 'TXT', name: '_acme-challenge', target: 'test-token' }] }) };
|
|
220
217
|
}
|
|
221
218
|
return { ok: true, status: 204, json: async () => ({}) };
|
|
@@ -199,6 +199,7 @@ module.exports.create = function create(config = {}) {
|
|
|
199
199
|
const presentedByHost = new Map();
|
|
200
200
|
const presentedByAltname = new Map();
|
|
201
201
|
const linodeTxtRecordsByHost = new Map();
|
|
202
|
+
const linodeZoneCache = new Map();
|
|
202
203
|
|
|
203
204
|
function buildZoneCandidates({ dnsHost, altname }) {
|
|
204
205
|
const candidates = new Set();
|
|
@@ -260,22 +261,40 @@ module.exports.create = function create(config = {}) {
|
|
|
260
261
|
return response.json();
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
async function resolveLinodeZone(zone) {
|
|
265
|
+
const normalizedZone = String(zone || '').trim().toLowerCase();
|
|
266
|
+
if (!normalizedZone) return null;
|
|
267
|
+
if (linodeZoneCache.has(normalizedZone)) return linodeZoneCache.get(normalizedZone);
|
|
268
|
+
|
|
269
|
+
const domainsResult = await linodeRequest('/domains?page_size=500', 'GET');
|
|
270
|
+
const domains = Array.isArray(domainsResult?.data) ? domainsResult.data : [];
|
|
271
|
+
const matched = domains.find((entry) => String(entry?.domain || '').trim().toLowerCase() === normalizedZone);
|
|
272
|
+
if (!matched?.id) return null;
|
|
273
|
+
|
|
274
|
+
const zoneInfo = { id: matched.id, domain: String(matched.domain || normalizedZone) };
|
|
275
|
+
linodeZoneCache.set(normalizedZone, zoneInfo);
|
|
276
|
+
return zoneInfo;
|
|
277
|
+
}
|
|
278
|
+
|
|
263
279
|
async function linodeUpsertTxtRecord(dnsHost, dnsAuthorization, altname) {
|
|
264
280
|
const zoneCandidates = buildZoneCandidates({ dnsHost, altname });
|
|
265
281
|
let lastError = null;
|
|
266
282
|
|
|
267
283
|
for (const zone of zoneCandidates) {
|
|
284
|
+
let zoneInfo = null;
|
|
268
285
|
try {
|
|
269
|
-
await
|
|
286
|
+
zoneInfo = await resolveLinodeZone(zone);
|
|
270
287
|
} catch (error) {
|
|
271
288
|
lastError = error;
|
|
272
289
|
continue;
|
|
273
290
|
}
|
|
291
|
+
if (!zoneInfo?.id) continue;
|
|
274
292
|
|
|
275
293
|
const recordName = linodeRecordNameForHost(dnsHost, zone);
|
|
276
294
|
if (!recordName && dnsHost !== zone) continue;
|
|
277
295
|
|
|
278
|
-
const
|
|
296
|
+
const zoneId = zoneInfo.id;
|
|
297
|
+
const recordsResult = await linodeRequest(`/domains/${zoneId}/records?page_size=500`, 'GET');
|
|
279
298
|
const existing = Array.isArray(recordsResult?.data) ? recordsResult.data : [];
|
|
280
299
|
const sameRecord = existing.find((record) =>
|
|
281
300
|
record?.type === 'TXT'
|
|
@@ -284,11 +303,11 @@ module.exports.create = function create(config = {}) {
|
|
|
284
303
|
);
|
|
285
304
|
|
|
286
305
|
if (sameRecord && sameRecord.id) {
|
|
287
|
-
linodeTxtRecordsByHost.set(dnsHost, { zone, id: sameRecord.id });
|
|
288
|
-
return { zone, id: sameRecord.id, reused: true };
|
|
306
|
+
linodeTxtRecordsByHost.set(dnsHost, { zone, zoneId, id: sameRecord.id });
|
|
307
|
+
return { zone, zoneId, id: sameRecord.id, reused: true };
|
|
289
308
|
}
|
|
290
309
|
|
|
291
|
-
const created = await linodeRequest(`/domains/${
|
|
310
|
+
const created = await linodeRequest(`/domains/${zoneId}/records`, 'POST', {
|
|
292
311
|
type: 'TXT',
|
|
293
312
|
name: recordName,
|
|
294
313
|
target: dnsAuthorization,
|
|
@@ -296,8 +315,8 @@ module.exports.create = function create(config = {}) {
|
|
|
296
315
|
});
|
|
297
316
|
|
|
298
317
|
if (created?.id) {
|
|
299
|
-
linodeTxtRecordsByHost.set(dnsHost, { zone, id: created.id });
|
|
300
|
-
return { zone, id: created.id, reused: false };
|
|
318
|
+
linodeTxtRecordsByHost.set(dnsHost, { zone, zoneId, id: created.id });
|
|
319
|
+
return { zone, zoneId, id: created.id, reused: false };
|
|
301
320
|
}
|
|
302
321
|
}
|
|
303
322
|
|
|
@@ -307,8 +326,8 @@ module.exports.create = function create(config = {}) {
|
|
|
307
326
|
|
|
308
327
|
async function linodeRemoveTxtRecord(dnsHost) {
|
|
309
328
|
const stored = linodeTxtRecordsByHost.get(dnsHost);
|
|
310
|
-
if (!stored?.
|
|
311
|
-
await linodeRequest(`/domains/${
|
|
329
|
+
if (!stored?.zoneId || !stored?.id) return false;
|
|
330
|
+
await linodeRequest(`/domains/${stored.zoneId}/records/${stored.id}`, 'DELETE');
|
|
312
331
|
linodeTxtRecordsByHost.delete(dnsHost);
|
|
313
332
|
return true;
|
|
314
333
|
}
|