zgrzyt 2.2.0 → 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 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('Resolved %s to %s', server, addresses.map(a => a.address).join(', '));
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 => request(url, {
71
- method,
72
- family,
73
- lookup,
74
- timeout,
75
- headers: {
76
- 'User-Agent': USER_AGENT,
77
- ...headers
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
- .end());
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
@@ -1,38 +1,74 @@
1
- import got from 'got';
2
1
  import makeDebug from 'debug';
3
2
 
4
3
  const debug = makeDebug('zgrzyt:cloudflare');
5
4
 
6
5
  export default client;
7
6
 
7
+ /* global AbortController, fetch, URLSearchParams */
8
+
8
9
  class CloudflareError extends Error {
9
10
  constructor(errors) {
10
11
  super(`Cloudflare API error: ${errors[0].message}`);
11
12
  }
12
13
  }
13
14
 
14
- function client({ token, timeout = 4000, retry = 2 }) {
15
- const cf = got.extend({
16
- prefixUrl: 'https://api.cloudflare.com/client/v4',
17
- resolveBodyOnly: true,
18
- responseType: 'json',
19
- timeout: { request: timeout },
20
- retry: { limit: retry },
21
- headers: {
22
- 'Authorization': `Bearer ${token}`
15
+ function makeFetch({ token, timeout, retry }) {
16
+ const authorization = `Bearer ${token}`;
17
+ return {
18
+ put: (...args) => retryFetch('PUT', ...args),
19
+ get: (...args) => retryFetch('GET', ...args)
20
+ };
21
+
22
+ async function retryFetch(...args) {
23
+ while (true) {
24
+ try {
25
+ return await doFetch(...args);
26
+ } catch (err) {
27
+ if (--retry <= 0) {
28
+ throw err;
29
+ }
30
+ }
23
31
  }
24
- });
32
+ }
33
+
34
+ async function doFetch(method, command, { searchParams, json }) {
35
+ const controller = new AbortController();
36
+ const id = setTimeout(() => controller.abort(), timeout);
37
+ const url = new URL(command, 'https://api.cloudflare.com/client/v4/');
38
+ if (searchParams) {
39
+ url.search = new URLSearchParams(searchParams);
40
+ }
41
+ const options = {
42
+ method,
43
+ headers: { authorization },
44
+ signal: controller.signal
45
+ };
46
+ if (json) {
47
+ options.headers['Content-Type'] = 'application/json';
48
+ options.body = JSON.stringify(json);
49
+ }
50
+ const res = await fetch(url, options);
51
+ clearTimeout(id);
52
+ return res.json();
53
+ }
54
+ }
55
+
56
+ function client({ token, timeout = 4000, retry = 2 }) {
57
+ const cf = makeFetch({ token, timeout, retry });
25
58
  const cacheZones = Object.create(null);
26
59
  let pZones;
60
+ return {
61
+ switchToService,
62
+ listRecords
63
+ };
27
64
 
28
65
  async function switchToService(zoneName, domain, good) {
29
66
  const zone = await getZone(zoneName);
30
67
  const record = await getRecord(zone.id, domain);
31
68
  if (record.type === 'CNAME') {
32
69
  return updateRecord(zone.id, record.id, record.proxied, domain, good);
33
- } else {
34
- console.error('Cannot only update CNAME records');
35
70
  }
71
+ console.error('Cannot only update CNAME records');
36
72
  }
37
73
 
38
74
  async function listRecords(zoneName, domain) {
@@ -63,12 +99,7 @@ function client({ token, timeout = 4000, retry = 2 }) {
63
99
 
64
100
  async function getPage(page) {
65
101
  debug('Getting page %d', page);
66
- const {
67
- success,
68
- errors,
69
- result,
70
- result_info: { total_count }
71
- } = await cf.get('zones', {
102
+ const { success, errors, result, result_info } = await cf.get('zones', {
72
103
  searchParams: {
73
104
  status: 'active',
74
105
  page,
@@ -81,23 +112,26 @@ function client({ token, timeout = 4000, retry = 2 }) {
81
112
  }
82
113
 
83
114
  zones.push(...result);
84
- return zones.length < total_count;
115
+ return zones.length < result_info.total_count;
85
116
  }
86
117
 
87
118
  debug('Listing zones...');
88
- for (let page = 1; await getPage(page); page++) { }
119
+ for (let page = 1; await getPage(page); page++) {}
89
120
  debug('Listing zones done.');
90
121
 
91
122
  return zones;
92
123
  }
93
124
 
94
125
  async function getRecord(zoneId, domain) {
95
- const { success, result, errors } = await cf.get(`zones/${zoneId}/dns_records`, {
96
- searchParams: {
97
- name: domain,
98
- type: 'CNAME'
126
+ const { success, result, errors } = await cf.get(
127
+ `zones/${zoneId}/dns_records`,
128
+ {
129
+ searchParams: {
130
+ name: domain,
131
+ type: 'CNAME'
132
+ }
99
133
  }
100
- });
134
+ );
101
135
 
102
136
  if (!success) {
103
137
  throw new CloudflareError(errors);
@@ -111,14 +145,17 @@ function client({ token, timeout = 4000, retry = 2 }) {
111
145
  }
112
146
 
113
147
  async function updateRecord(zoneId, recordId, proxied, domain, good) {
114
- const { success, errors, result } = await cf.put(`zones/${zoneId}/dns_records/${recordId}`, {
115
- json: {
116
- name: domain,
117
- proxied,
118
- content: good.server,
119
- type: 'CNAME'
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
+ }
120
157
  }
121
- });
158
+ );
122
159
 
123
160
  debug('Update record', success, errors, result);
124
161
 
@@ -128,9 +165,4 @@ function client({ token, timeout = 4000, retry = 2 }) {
128
165
 
129
166
  return success;
130
167
  }
131
-
132
- return {
133
- switchToService,
134
- listRecords
135
- };
136
168
  }
package/lib/config.js CHANGED
@@ -1,4 +1,4 @@
1
- import { parseDomain, fromUrl } from 'parse-domain';
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 => ({ address, family: RRTYPE_TO_FAMILY[rrtype] }));
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('%-25s %-25s => %-25s [%s]', domain, record.content, good.server, good.address);
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('%-25s %-25s [%s]', record.name, good.server, good.address);
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, `\n`);
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, } from 'node:fs/promises';
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, { force, repair });
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.0",
3
+ "version": "2.2.2",
4
4
  "description": "Poor man's load balancing DNS switcher.",
5
5
  "type": "module",
6
6
  "author": {
@@ -27,13 +27,12 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "debug": "~4",
30
- "got": "~14",
31
30
  "parse-domain": "~8",
32
31
  "rc": "^1.2.8",
33
32
  "sprintfjs": "^1.2.16"
34
33
  },
35
34
  "devDependencies": {
36
- "@pirxpilot/jshint": "^3.0.0"
35
+ "@biomejs/biome": "^1.9.4"
37
36
  },
38
37
  "scripts": {
39
38
  "test": "make check"
@@ -43,4 +42,4 @@
43
42
  "lib",
44
43
  "bin"
45
44
  ]
46
- }
45
+ }