zgrzyt 2.2.1 → 2.2.2
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/index.js +1 -1
- package/lib/check.js +28 -30
- package/lib/cloudflare.js +22 -22
- package/lib/config.js +7 -12
- package/lib/dns.js +4 -1
- package/lib/report.js +15 -8
- package/lib/state.js +3 -9
- package/lib/zgrzyt.js +4 -1
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import rc from 'rc';
|
|
2
2
|
import prepareConfig from './lib/config.js';
|
|
3
|
-
import { zgrzyt } from './lib/zgrzyt.js';
|
|
4
3
|
import { report } from './lib/report.js';
|
|
5
4
|
import { onExit } from './lib/state.js';
|
|
5
|
+
import { zgrzyt } from './lib/zgrzyt.js';
|
|
6
6
|
|
|
7
7
|
const conf = rc('zgrzyt');
|
|
8
8
|
const apis = prepareConfig(conf);
|
package/lib/check.js
CHANGED
|
@@ -18,15 +18,17 @@ const USER_AGENT = `${name}/${version} (${homepage})`;
|
|
|
18
18
|
|
|
19
19
|
/* global URL */
|
|
20
20
|
|
|
21
|
-
export {
|
|
22
|
-
checkServices
|
|
23
|
-
};
|
|
21
|
+
export { checkServices };
|
|
24
22
|
|
|
25
23
|
async function checkService(server, api) {
|
|
26
24
|
const { ipv4 = true, ipv6 = false } = api;
|
|
27
25
|
const addresses = await resolve(server, { ipv4, ipv6 });
|
|
28
26
|
if (debug.enabled) {
|
|
29
|
-
debug(
|
|
27
|
+
debug(
|
|
28
|
+
'Resolved %s to %s',
|
|
29
|
+
server,
|
|
30
|
+
addresses.map(a => a.address).join(', ')
|
|
31
|
+
);
|
|
30
32
|
}
|
|
31
33
|
const oks = await Promise.all(addresses.map(a => checkApi(api, a)));
|
|
32
34
|
// collate results
|
|
@@ -41,13 +43,7 @@ async function checkService(server, api) {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
async function checkApi(api, { address, family }) {
|
|
44
|
-
|
|
45
|
-
const {
|
|
46
|
-
url,
|
|
47
|
-
method = 'HEAD',
|
|
48
|
-
timeout = 5000,
|
|
49
|
-
headers = {}
|
|
50
|
-
} = api;
|
|
46
|
+
const { url, method = 'HEAD', timeout = 5000, headers = {} } = api;
|
|
51
47
|
|
|
52
48
|
debug('Checking %s on %s with timeout %dms', url, address, timeout);
|
|
53
49
|
|
|
@@ -67,26 +63,28 @@ async function checkApi(api, { address, family }) {
|
|
|
67
63
|
return result;
|
|
68
64
|
|
|
69
65
|
function makeRequest() {
|
|
70
|
-
return new Promise(resolve =>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
.on('timeout', function () {
|
|
81
|
-
debug('Timeout for %s on %s', url, address);
|
|
82
|
-
this.destroy(new Error('Request timeout.'));
|
|
83
|
-
})
|
|
84
|
-
.on('response', res => resolve(isOk(res)))
|
|
85
|
-
.on('error', e => {
|
|
86
|
-
debug('request error:', e);
|
|
87
|
-
resolve(false);
|
|
66
|
+
return new Promise(resolve =>
|
|
67
|
+
request(url, {
|
|
68
|
+
method,
|
|
69
|
+
family,
|
|
70
|
+
lookup,
|
|
71
|
+
timeout,
|
|
72
|
+
headers: {
|
|
73
|
+
'User-Agent': USER_AGENT,
|
|
74
|
+
...headers
|
|
75
|
+
}
|
|
88
76
|
})
|
|
89
|
-
|
|
77
|
+
.on('timeout', function () {
|
|
78
|
+
debug('Timeout for %s on %s', url, address);
|
|
79
|
+
this.destroy(new Error('Request timeout.'));
|
|
80
|
+
})
|
|
81
|
+
.on('response', res => resolve(isOk(res)))
|
|
82
|
+
.on('error', e => {
|
|
83
|
+
debug('request error:', e);
|
|
84
|
+
resolve(false);
|
|
85
|
+
})
|
|
86
|
+
.end()
|
|
87
|
+
);
|
|
90
88
|
|
|
91
89
|
function isOk({ statusCode }) {
|
|
92
90
|
const ok = statusCode >= 200 && statusCode < 300;
|
package/lib/cloudflare.js
CHANGED
|
@@ -67,9 +67,8 @@ function client({ token, timeout = 4000, retry = 2 }) {
|
|
|
67
67
|
const record = await getRecord(zone.id, domain);
|
|
68
68
|
if (record.type === 'CNAME') {
|
|
69
69
|
return updateRecord(zone.id, record.id, record.proxied, domain, good);
|
|
70
|
-
} else {
|
|
71
|
-
console.error('Cannot only update CNAME records');
|
|
72
70
|
}
|
|
71
|
+
console.error('Cannot only update CNAME records');
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
async function listRecords(zoneName, domain) {
|
|
@@ -100,12 +99,7 @@ function client({ token, timeout = 4000, retry = 2 }) {
|
|
|
100
99
|
|
|
101
100
|
async function getPage(page) {
|
|
102
101
|
debug('Getting page %d', page);
|
|
103
|
-
const {
|
|
104
|
-
success,
|
|
105
|
-
errors,
|
|
106
|
-
result,
|
|
107
|
-
result_info: { total_count }
|
|
108
|
-
} = await cf.get('zones', {
|
|
102
|
+
const { success, errors, result, result_info } = await cf.get('zones', {
|
|
109
103
|
searchParams: {
|
|
110
104
|
status: 'active',
|
|
111
105
|
page,
|
|
@@ -118,23 +112,26 @@ function client({ token, timeout = 4000, retry = 2 }) {
|
|
|
118
112
|
}
|
|
119
113
|
|
|
120
114
|
zones.push(...result);
|
|
121
|
-
return zones.length < total_count;
|
|
115
|
+
return zones.length < result_info.total_count;
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
debug('Listing zones...');
|
|
125
|
-
for (let page = 1; await getPage(page); page++) {
|
|
119
|
+
for (let page = 1; await getPage(page); page++) {}
|
|
126
120
|
debug('Listing zones done.');
|
|
127
121
|
|
|
128
122
|
return zones;
|
|
129
123
|
}
|
|
130
124
|
|
|
131
125
|
async function getRecord(zoneId, domain) {
|
|
132
|
-
const { success, result, errors } = await cf.get(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
const { success, result, errors } = await cf.get(
|
|
127
|
+
`zones/${zoneId}/dns_records`,
|
|
128
|
+
{
|
|
129
|
+
searchParams: {
|
|
130
|
+
name: domain,
|
|
131
|
+
type: 'CNAME'
|
|
132
|
+
}
|
|
136
133
|
}
|
|
137
|
-
|
|
134
|
+
);
|
|
138
135
|
|
|
139
136
|
if (!success) {
|
|
140
137
|
throw new CloudflareError(errors);
|
|
@@ -148,14 +145,17 @@ function client({ token, timeout = 4000, retry = 2 }) {
|
|
|
148
145
|
}
|
|
149
146
|
|
|
150
147
|
async function updateRecord(zoneId, recordId, proxied, domain, good) {
|
|
151
|
-
const { success, errors, result } = await cf.put(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
const { success, errors, result } = await cf.put(
|
|
149
|
+
`zones/${zoneId}/dns_records/${recordId}`,
|
|
150
|
+
{
|
|
151
|
+
json: {
|
|
152
|
+
name: domain,
|
|
153
|
+
proxied,
|
|
154
|
+
content: good.server,
|
|
155
|
+
type: 'CNAME'
|
|
156
|
+
}
|
|
157
157
|
}
|
|
158
|
-
|
|
158
|
+
);
|
|
159
159
|
|
|
160
160
|
debug('Update record', success, errors, result);
|
|
161
161
|
|
package/lib/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fromUrl, parseDomain } from 'parse-domain';
|
|
2
2
|
import makeClient from './cloudflare.js';
|
|
3
3
|
|
|
4
4
|
export default prepareConfig;
|
|
@@ -15,21 +15,17 @@ function getDomainAndZone({ url, domain: apiDomain }) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function prepareConfig(config) {
|
|
18
|
-
const {
|
|
19
|
-
cloudflare,
|
|
20
|
-
api = {},
|
|
21
|
-
cluster = {}
|
|
22
|
-
} = config;
|
|
18
|
+
const { cloudflare, api = {}, cluster = {} } = config;
|
|
23
19
|
|
|
24
20
|
if (!cloudflare?.token) {
|
|
25
21
|
console.error('Cloudflare API token not configured');
|
|
26
22
|
return;
|
|
27
23
|
}
|
|
28
24
|
if (typeof cloudflare.timeout === 'string') {
|
|
29
|
-
cloudflare.timeout = parseInt(cloudflare.timeout);
|
|
25
|
+
cloudflare.timeout = Number.parseInt(cloudflare.timeout);
|
|
30
26
|
}
|
|
31
27
|
if (typeof cloudflare.retry === 'string') {
|
|
32
|
-
cloudflare.retry = parseInt(cloudflare.retry);
|
|
28
|
+
cloudflare.retry = Number.parseInt(cloudflare.retry);
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
// collect all APIs
|
|
@@ -49,10 +45,10 @@ function prepareConfig(config) {
|
|
|
49
45
|
}
|
|
50
46
|
|
|
51
47
|
const { domain, zone } = getDomainAndZone(api);
|
|
52
|
-
const timeout = parseInt(api.timeout || config.timeout, 10) || 250;
|
|
53
|
-
const retry = parseInt(api.retry || config.retry, 10) || 2;
|
|
48
|
+
const timeout = Number.parseInt(api.timeout || config.timeout, 10) || 250;
|
|
49
|
+
const retry = Number.parseInt(api.retry || config.retry, 10) || 2;
|
|
54
50
|
const force = 'force' in api ? api.force : config.force;
|
|
55
|
-
const repair = parseInt(api.repair || config.repair, 10) || 5;
|
|
51
|
+
const repair = Number.parseInt(api.repair || config.repair, 10) || 5;
|
|
56
52
|
const ipv6 = Boolean(api.ipv6 ?? config.ipv6);
|
|
57
53
|
const ipv4 = Boolean(api.ipv4 ?? config.ipv4 ?? true);
|
|
58
54
|
|
|
@@ -88,6 +84,5 @@ function prepareConfig(config) {
|
|
|
88
84
|
force,
|
|
89
85
|
repair
|
|
90
86
|
};
|
|
91
|
-
|
|
92
87
|
}
|
|
93
88
|
}
|
package/lib/dns.js
CHANGED
|
@@ -39,7 +39,10 @@ async function doResolve(domain) {
|
|
|
39
39
|
async function resolveWithFamily(rrtype) {
|
|
40
40
|
try {
|
|
41
41
|
const addresses = await resolver.resolve(domain, rrtype);
|
|
42
|
-
return addresses.map(address => ({
|
|
42
|
+
return addresses.map(address => ({
|
|
43
|
+
address,
|
|
44
|
+
family: RRTYPE_TO_FAMILY[rrtype]
|
|
45
|
+
}));
|
|
43
46
|
} catch (error) {
|
|
44
47
|
debug('Failed to resolve %s with type %s: %s', domain, rrtype, error);
|
|
45
48
|
return [];
|
package/lib/report.js
CHANGED
|
@@ -13,10 +13,21 @@ function collect(context, { url, domain, record, good, switched }) {
|
|
|
13
13
|
if (!good) {
|
|
14
14
|
context.missing.push(url);
|
|
15
15
|
} else if (switched) {
|
|
16
|
-
const line = sprintf(
|
|
16
|
+
const line = sprintf(
|
|
17
|
+
'%-25s %-25s => %-25s [%s]',
|
|
18
|
+
domain,
|
|
19
|
+
record.content,
|
|
20
|
+
good.server,
|
|
21
|
+
good.address
|
|
22
|
+
);
|
|
17
23
|
context.switched.push(line);
|
|
18
24
|
} else {
|
|
19
|
-
const line = sprintf(
|
|
25
|
+
const line = sprintf(
|
|
26
|
+
'%-25s %-25s [%s]',
|
|
27
|
+
record.name,
|
|
28
|
+
good.server,
|
|
29
|
+
good.address
|
|
30
|
+
);
|
|
20
31
|
context.noops.push(line);
|
|
21
32
|
}
|
|
22
33
|
return context;
|
|
@@ -28,11 +39,7 @@ function formatError(err) {
|
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
function reportAll(collected, errors) {
|
|
31
|
-
const {
|
|
32
|
-
missing,
|
|
33
|
-
noops,
|
|
34
|
-
switched
|
|
35
|
-
} = collected;
|
|
42
|
+
const { missing, noops, switched } = collected;
|
|
36
43
|
|
|
37
44
|
let exitCode = 0;
|
|
38
45
|
const lines = [];
|
|
@@ -49,7 +56,7 @@ function reportAll(collected, errors) {
|
|
|
49
56
|
lines.push('No changes:\n', ...noops, '\n');
|
|
50
57
|
}
|
|
51
58
|
if (errors.length) {
|
|
52
|
-
lines.push('Errors:\n', ...errors,
|
|
59
|
+
lines.push('Errors:\n', ...errors, '\n');
|
|
53
60
|
exitCode = -1;
|
|
54
61
|
}
|
|
55
62
|
|
package/lib/state.js
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import { readFile, writeFile
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
|
|
3
|
-
export {
|
|
4
|
-
updateHealth,
|
|
5
|
-
onExit
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const {
|
|
9
|
-
ZGRZYT_STATE = '/var/lib/zgrzyt/state.json'
|
|
10
|
-
} = process.env;
|
|
3
|
+
export { updateHealth, onExit };
|
|
11
4
|
|
|
5
|
+
const { ZGRZYT_STATE = '/var/lib/zgrzyt/state.json' } = process.env;
|
|
12
6
|
|
|
13
7
|
const state_p = read();
|
|
14
8
|
let hook;
|
package/lib/zgrzyt.js
CHANGED
|
@@ -3,7 +3,10 @@ import { checkServices } from './check.js';
|
|
|
3
3
|
export async function zgrzyt({ servers, api, client, force, repair }) {
|
|
4
4
|
const { domain, zone } = api;
|
|
5
5
|
const record = await client.listRecords(zone, domain);
|
|
6
|
-
const good = await checkServices(servers, record.content, api, {
|
|
6
|
+
const good = await checkServices(servers, record.content, api, {
|
|
7
|
+
force,
|
|
8
|
+
repair
|
|
9
|
+
});
|
|
7
10
|
const result = {
|
|
8
11
|
url: api.url,
|
|
9
12
|
domain,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zgrzyt",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"description": "Poor man's load balancing DNS switcher.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"sprintfjs": "^1.2.16"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@
|
|
35
|
+
"@biomejs/biome": "^1.9.4"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"test": "make check"
|