pending-dns 1.1.1 → 1.2.1
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/.github/workflows/deploy.yml +37 -0
- package/README.md +1 -1
- package/bin/pending-dns.js +49 -0
- package/config/default.toml +104 -104
- package/entitlements.mac.plist +18 -0
- package/help.txt +23 -0
- package/lib/api-server.js +16 -18
- package/lib/cached-resolver.js +1 -1
- package/lib/certs.js +4 -6
- package/lib/dns-handler.js +39 -13
- package/lib/dns-server.js +2 -2
- package/lib/dns-tcp-server.js +1 -3
- package/lib/health-worker.js +4 -6
- package/lib/public-server.js +7 -15
- package/lib/tools.js +1 -1
- package/lib/zone-store.js +3 -4
- package/package.json +47 -23
- package/server.js +43 -6
- package/workers/api.js +19 -6
- package/workers/dns.js +43 -6
- package/workers/health.js +44 -6
- package/workers/public.js +43 -6
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
on:
|
|
2
|
+
push:
|
|
3
|
+
branches:
|
|
4
|
+
- master
|
|
5
|
+
|
|
6
|
+
name: Deploy instance
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
deploy:
|
|
10
|
+
name: Deploy
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v2
|
|
16
|
+
|
|
17
|
+
- name: Install SSH key
|
|
18
|
+
uses: shimataro/ssh-key-action@v2
|
|
19
|
+
with:
|
|
20
|
+
key: ${{ secrets.SSH_KEY }}
|
|
21
|
+
known_hosts: ${{ secrets.KNOWN_HOSTS }}
|
|
22
|
+
|
|
23
|
+
- name: Deploy to server
|
|
24
|
+
env:
|
|
25
|
+
TARGET_HOST_01: dns-01.emailengine.app
|
|
26
|
+
TARGET_HOST_02: dns-02.emailengine.app
|
|
27
|
+
NODE_ENV: production
|
|
28
|
+
SERVICE_NAME: pending-dns
|
|
29
|
+
id: deploy
|
|
30
|
+
run: |
|
|
31
|
+
echo $GITHUB_SHA > commit.txt
|
|
32
|
+
npm install --production
|
|
33
|
+
tar czf /tmp/${SERVICE_NAME}.tar.gz --exclude .git .
|
|
34
|
+
scp /tmp/${SERVICE_NAME}.tar.gz deploy@${TARGET_HOST_01}:
|
|
35
|
+
scp /tmp/${SERVICE_NAME}.tar.gz deploy@${TARGET_HOST_02}:
|
|
36
|
+
ssh deploy@$TARGET_HOST_01 "/opt/deploy.sh ${SERVICE_NAME}"
|
|
37
|
+
ssh deploy@$TARGET_HOST_02 "/opt/deploy.sh ${SERVICE_NAME}"
|
package/README.md
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint global-require: 0 */
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const packageData = require('../package.json');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const pathlib = require('path');
|
|
8
|
+
const argv = require('minimist')(process.argv.slice(2));
|
|
9
|
+
|
|
10
|
+
function run() {
|
|
11
|
+
let cmd = ((argv._ && argv._[0]) || '').toLowerCase();
|
|
12
|
+
if (!cmd) {
|
|
13
|
+
if (argv.version || argv.v) {
|
|
14
|
+
cmd = 'version';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (argv.help || argv.h) {
|
|
18
|
+
cmd = 'help';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
switch (cmd) {
|
|
23
|
+
case 'help':
|
|
24
|
+
// Show version
|
|
25
|
+
fs.readFile(pathlib.join(__dirname, '..', 'help.txt'), (err, helpText) => {
|
|
26
|
+
if (err) {
|
|
27
|
+
console.error('Failed to load help information');
|
|
28
|
+
console.error(err);
|
|
29
|
+
return process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
console.error(helpText.toString().trim());
|
|
32
|
+
console.error('');
|
|
33
|
+
process.exit();
|
|
34
|
+
});
|
|
35
|
+
break;
|
|
36
|
+
|
|
37
|
+
case 'version':
|
|
38
|
+
// Show version
|
|
39
|
+
console.log(`EmailEngine v${packageData.version} (${packageData.license})`);
|
|
40
|
+
return process.exit();
|
|
41
|
+
|
|
42
|
+
default:
|
|
43
|
+
// run normally
|
|
44
|
+
require('../server');
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
run();
|
package/config/default.toml
CHANGED
|
@@ -1,143 +1,143 @@
|
|
|
1
1
|
|
|
2
2
|
[log]
|
|
3
|
-
|
|
3
|
+
level = "trace"
|
|
4
4
|
|
|
5
5
|
[dbs]
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
# By default all redis commands are sent against the same instance
|
|
8
|
+
redis = "redis://127.0.0.1:6379/2"
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
# Alternatively you can separate write and read tasks
|
|
11
|
+
# by writing to master and reading from a closer replica
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
# Redis master
|
|
14
|
+
#redisRead = "redis://127.0.0.1:6379/2"
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
# Redis replica, preferrably localhost for fastest responses
|
|
17
|
+
#redisWrite = "redis://127.0.0.1:6379/2"
|
|
18
18
|
|
|
19
19
|
[api]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
# If enabled=false then API server is not started and configuration is not used
|
|
21
|
+
enabled = true
|
|
22
|
+
workers = 1
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
# You want to keep this local or firewalled otherwise anyone would be able to change DNS records
|
|
25
|
+
# Do not use ports 80 or 443 as these are needed for the redirect interface
|
|
26
|
+
port = 5080
|
|
27
|
+
host = "127.0.0.1"
|
|
28
28
|
|
|
29
29
|
[dns]
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
# If enabled=false then DNS server is not started and configuration is not used
|
|
31
|
+
enabled = true
|
|
32
|
+
workers = 2
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
# Default TTL value for all records
|
|
35
|
+
ttl = 300 # 5 min
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
# Use 53 on production.
|
|
38
|
+
# This port is used both for TCP and UDP so make sure both are allowed by the firewall
|
|
39
|
+
# ufw allow 53/tcp
|
|
40
|
+
# ufw allow 53/udp
|
|
41
|
+
port = 5053
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
# In most cases you have to set actual interface IP address here
|
|
44
|
+
# instead of using 0.0.0.0 as there might be already some other DNS
|
|
45
|
+
# handlers running (eg. SystemD stub resolver) on some local address
|
|
46
|
+
host = "127.0.0.1"
|
|
47
47
|
|
|
48
48
|
# List of Name Servers running this system
|
|
49
49
|
# 1) ACME certificates are created only for domains that have NS records set to these values
|
|
50
50
|
# 2) These values are reported as NS records for all domains
|
|
51
51
|
[[ns]]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
# First NS in the list is also reported as the master in SOA record
|
|
53
|
+
domain = "testdns01.pendingdns.com"
|
|
54
|
+
ip = "188.165.168.22"
|
|
55
55
|
[[ns]]
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
domain = "testdns02.pendingdns.com"
|
|
57
|
+
ip = "51.38.177.242"
|
|
58
58
|
|
|
59
59
|
# SOA info
|
|
60
60
|
# 1) This is reported as the SOA record for all domains
|
|
61
61
|
[soa]
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
admin = "hostmaster.pendingdns.com"
|
|
63
|
+
serial = 2020050501
|
|
64
|
+
refresh = 3600
|
|
65
|
+
retry = 600
|
|
66
|
+
expiration = 604800
|
|
67
|
+
minimum = 60
|
|
68
68
|
|
|
69
69
|
# Resolver for external DNS queries, set ns=false to use system default
|
|
70
70
|
# Mostly used for ANAME resolving
|
|
71
71
|
[resolver]
|
|
72
|
-
|
|
72
|
+
ns = ["8.8.8.8", "1.1.1.1"]
|
|
73
73
|
|
|
74
74
|
# Settings for Let's Encrypt certificate generation
|
|
75
75
|
[acme]
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
# Local identifier for the ACME account
|
|
77
|
+
key = "staging"
|
|
78
|
+
# Defaults to Let's Encrypt staging environment
|
|
79
|
+
directoryUrl = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
|
80
|
+
|
|
81
|
+
#key = "production"
|
|
82
|
+
#directoryUrl = "https://acme-v02.api.letsencrypt.org/directory"
|
|
83
|
+
|
|
84
|
+
# Email address to recive account related notifications
|
|
85
|
+
# This value must be set as this is the person who agrees to LE TOS
|
|
86
|
+
#email = "hostmaster@example.com"
|
|
87
|
+
email = ""
|
|
88
88
|
|
|
89
89
|
[public]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
90
|
+
# If enabled=false then URL redirect server is not started and configuration is not used
|
|
91
|
+
enabled = true
|
|
92
|
+
server = "PendingDNS/1.0"
|
|
93
|
+
workers = 2
|
|
94
|
+
|
|
95
|
+
# path to error files, you can use your own error pages instead of the default ones
|
|
96
|
+
# make sure though that the service user is able to read these
|
|
97
|
+
# 1) All paths are relative to working directory, eg. /opt/pending-dns
|
|
98
|
+
[public.errors]
|
|
99
|
+
error404 = "./views/errors/404.hbs"
|
|
100
|
+
error500 = "./views/errors/500.hbs"
|
|
101
|
+
|
|
102
|
+
[public.http]
|
|
103
|
+
# URL record handling over HTTP
|
|
104
|
+
# Set to 80 in production
|
|
105
|
+
port = 6080
|
|
106
|
+
host = "0.0.0.0"
|
|
107
|
+
|
|
108
|
+
[public.https]
|
|
109
|
+
# URL record handling over HTTPS/HTTP2
|
|
110
|
+
# Set to 443 in production
|
|
111
|
+
port = 6443
|
|
112
|
+
host = "0.0.0.0"
|
|
113
|
+
|
|
114
|
+
# Path to default certificate files
|
|
115
|
+
# These are used only for unknown domains, so can leave as is to use self-signed certs
|
|
116
|
+
#key = "/path/to/default-privkey.pem"
|
|
117
|
+
#cert = "/path/to/default-cert.pem"
|
|
118
|
+
|
|
119
|
+
# Path to dhparam file
|
|
120
|
+
# Generate using: openssl dhparam -out /path/to/dhparam.pem 4096
|
|
121
|
+
#dhParam = "/path/to/dhparam.pem"
|
|
122
|
+
|
|
123
|
+
# Allowed ciphers list. Leave empty for Node.js default set of ciphers
|
|
124
|
+
ciphers = "ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384"
|
|
125
|
+
|
|
126
|
+
# List redirect server IP addresses here
|
|
127
|
+
# These are returned as A/AAAA query responses for the URL domain
|
|
128
|
+
# It should include the IP addresses for all servers where you have PendingDNS installed and "public.enabled=true"
|
|
129
|
+
[public.hosts]
|
|
130
|
+
A = ["127.0.0.1", "127.0.0.2"]
|
|
131
|
+
AAAA = []
|
|
132
132
|
|
|
133
133
|
[process]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
# Change user for child processes once privileged ports have been bound
|
|
135
|
+
#user="www-data"
|
|
136
|
+
#group="www-data"
|
|
137
137
|
|
|
138
138
|
[health]
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
enabled = true # If enabled=false then health checks are not performed
|
|
140
|
+
workers = 1
|
|
141
|
+
handlers = 1 # How many health checks to run in parallel
|
|
142
|
+
ttl = 30000 # Time in milliseconds until pending health check is considered failing
|
|
143
|
+
delay = 60000 # Time in milliseconds between health checks against same target
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>com.apple.security.cs.allow-jit</key>
|
|
6
|
+
<true/>
|
|
7
|
+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
|
8
|
+
<true/>
|
|
9
|
+
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
|
10
|
+
<true/>
|
|
11
|
+
<key>com.apple.security.cs.disable-library-validation</key>
|
|
12
|
+
<true/>
|
|
13
|
+
<key>com.apple.security.network.client</key>
|
|
14
|
+
<true/>
|
|
15
|
+
<key>com.apple.security.network.server</key>
|
|
16
|
+
<true/>
|
|
17
|
+
</dict>
|
|
18
|
+
</plist>
|
package/help.txt
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
pending-dns [command] [options]
|
|
2
|
+
|
|
3
|
+
Lightweight API driven Authoritative DNS server.
|
|
4
|
+
|
|
5
|
+
Commands:
|
|
6
|
+
pending-dns Run the application
|
|
7
|
+
pending-dns help Show this information
|
|
8
|
+
|
|
9
|
+
Options:
|
|
10
|
+
-h, --help Show help
|
|
11
|
+
|
|
12
|
+
General options:
|
|
13
|
+
--dbs.redis Database connection URL [string]
|
|
14
|
+
--log.level Log level [string] [default: "trace"]
|
|
15
|
+
|
|
16
|
+
API options:
|
|
17
|
+
--api.host Host to bind to [string] [default: "127.0.0.1"]
|
|
18
|
+
--api.port Port to bind to [number] [default: 5080]
|
|
19
|
+
|
|
20
|
+
DNS options:
|
|
21
|
+
--dns.host Host to bind to [string] [default: "127.0.0.1"]
|
|
22
|
+
--dns.port Port to bind to [number] [default: 5053]
|
|
23
|
+
|
package/lib/api-server.js
CHANGED
|
@@ -18,28 +18,26 @@ const hostnameSchema = Joi.string().hostname({
|
|
|
18
18
|
minDomainSegments: 1
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
const subdomainValidator = opts => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
21
|
+
const subdomainValidator = opts => (value, helpers) => {
|
|
22
|
+
if (!value) {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
let valueToCheck = value;
|
|
27
|
+
if (opts.allowUnderscore) {
|
|
28
|
+
valueToCheck = valueToCheck.replace(/\b_/g, 'x');
|
|
29
|
+
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
if (opts.allowWildcard) {
|
|
32
|
+
valueToCheck = valueToCheck.replace(/^\*\./, 'x.');
|
|
33
|
+
}
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
let result = hostnameSchema.validate(valueToCheck);
|
|
36
|
+
if (result.error) {
|
|
37
|
+
return helpers.error('any.invalid');
|
|
38
|
+
}
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
};
|
|
40
|
+
return value;
|
|
43
41
|
};
|
|
44
42
|
|
|
45
43
|
const recordScheme = Joi.object({
|
package/lib/cached-resolver.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const config = require('wild-config');
|
|
4
4
|
const db = require('./db');
|
|
5
|
-
const punycode = require('punycode');
|
|
5
|
+
const punycode = require('punycode/');
|
|
6
6
|
const { Resolver } = require('dns').promises;
|
|
7
7
|
const resolver = new Resolver();
|
|
8
8
|
const logger = require('./logger').child({ component: 'cached-resolver' });
|
package/lib/certs.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const config = require('wild-config');
|
|
4
4
|
const pkg = require('../package.json');
|
|
5
5
|
const db = require('./db');
|
|
6
|
-
const punycode = require('punycode');
|
|
6
|
+
const punycode = require('punycode/');
|
|
7
7
|
const { zoneStore } = require('./zone-store');
|
|
8
8
|
const crypto = require('crypto');
|
|
9
9
|
const { checkNSStatus, normalizeDomain } = require('./tools');
|
|
@@ -34,11 +34,9 @@ const acme = ACME.create({
|
|
|
34
34
|
},
|
|
35
35
|
dns01(query) {
|
|
36
36
|
return localResolver.resolveTxt(query.dnsHost).then(records => ({
|
|
37
|
-
answer: records.map(rr => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
41
|
-
})
|
|
37
|
+
answer: records.map(rr => ({
|
|
38
|
+
data: rr
|
|
39
|
+
}))
|
|
42
40
|
}));
|
|
43
41
|
}
|
|
44
42
|
});
|
package/lib/dns-handler.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const config = require('wild-config');
|
|
4
4
|
const dns2 = require('dns2');
|
|
5
|
-
const punycode = require('punycode');
|
|
5
|
+
const punycode = require('punycode/');
|
|
6
6
|
const { zoneStore } = require('./zone-store');
|
|
7
7
|
const cachedResolver = require('./cached-resolver');
|
|
8
8
|
const ipaddr = require('ipaddr.js');
|
|
9
9
|
const logger = require('./logger').child({ component: 'dns-handler' });
|
|
10
10
|
const { normalizeDomain } = require('./tools');
|
|
11
|
+
const { v4: uuidv4 } = require('uuid');
|
|
11
12
|
|
|
12
13
|
// Split long string values into character chunks
|
|
13
14
|
const formatTXTData = data => {
|
|
@@ -19,11 +20,7 @@ const formatTXTData = data => {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
// Helps to convert DNS type integer into a string (0x01 -> 'A')
|
|
22
|
-
const reversedTypes = new Map(
|
|
23
|
-
Object.keys(dns2.Packet.TYPE).map(key => {
|
|
24
|
-
return [dns2.Packet.TYPE[key], key];
|
|
25
|
-
})
|
|
26
|
-
);
|
|
23
|
+
const reversedTypes = new Map(Object.keys(dns2.Packet.TYPE).map(key => [dns2.Packet.TYPE[key], key]));
|
|
27
24
|
|
|
28
25
|
const shuffle = array => {
|
|
29
26
|
let currentIndex = array.length,
|
|
@@ -89,7 +86,7 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
89
86
|
let dnsEntries = (
|
|
90
87
|
await Promise.all(
|
|
91
88
|
Array.from(types).map(async type => {
|
|
92
|
-
let records = await zoneStore.resolve(domain, type,
|
|
89
|
+
let records = await zoneStore.resolve(domain, type, false);
|
|
93
90
|
if (records && records.length > 1) {
|
|
94
91
|
switch (type) {
|
|
95
92
|
case 'A':
|
|
@@ -187,7 +184,13 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
187
184
|
break;
|
|
188
185
|
}
|
|
189
186
|
} catch (err) {
|
|
190
|
-
logger.error({
|
|
187
|
+
logger.error({
|
|
188
|
+
msg: 'Failed resolving ANAME',
|
|
189
|
+
id: response._id,
|
|
190
|
+
domain: dnsEntry.domain,
|
|
191
|
+
type: questionTypeStr,
|
|
192
|
+
err
|
|
193
|
+
});
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
[].concat(value || []).forEach(value => {
|
|
@@ -258,7 +261,12 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
258
261
|
try {
|
|
259
262
|
entry.domain = punycode.toASCII(entry.domain);
|
|
260
263
|
} catch (err) {
|
|
261
|
-
logger.error({
|
|
264
|
+
logger.error({
|
|
265
|
+
msg: 'Failed to punycode',
|
|
266
|
+
id: response._id,
|
|
267
|
+
domain: entry.domain,
|
|
268
|
+
err
|
|
269
|
+
});
|
|
262
270
|
}
|
|
263
271
|
break;
|
|
264
272
|
|
|
@@ -267,7 +275,12 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
267
275
|
try {
|
|
268
276
|
entry.ns = punycode.toASCII(entry.ns);
|
|
269
277
|
} catch (err) {
|
|
270
|
-
logger.error({
|
|
278
|
+
logger.error({
|
|
279
|
+
msg: 'Failed to punycode',
|
|
280
|
+
id: response._id,
|
|
281
|
+
domain: entry.ns,
|
|
282
|
+
err
|
|
283
|
+
});
|
|
271
284
|
}
|
|
272
285
|
break;
|
|
273
286
|
|
|
@@ -284,7 +297,12 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
284
297
|
try {
|
|
285
298
|
entry.exchange = punycode.toASCII(entry.exchange);
|
|
286
299
|
} catch (err) {
|
|
287
|
-
logger.error({
|
|
300
|
+
logger.error({
|
|
301
|
+
msg: 'Failed to punycode',
|
|
302
|
+
id: response._id,
|
|
303
|
+
domain: entry.exchange,
|
|
304
|
+
err
|
|
305
|
+
});
|
|
288
306
|
}
|
|
289
307
|
break;
|
|
290
308
|
|
|
@@ -316,6 +334,7 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
316
334
|
const dnsHandler = async request => {
|
|
317
335
|
let startTime = Date.now();
|
|
318
336
|
const response = new dns2.Packet(request);
|
|
337
|
+
request._id = response._id = uuidv4();
|
|
319
338
|
|
|
320
339
|
response.header.qr = 1;
|
|
321
340
|
response.header.aa = 1;
|
|
@@ -323,18 +342,25 @@ const dnsHandler = async request => {
|
|
|
323
342
|
await Promise.all(
|
|
324
343
|
request.questions.map(question => {
|
|
325
344
|
logger.info({
|
|
345
|
+
id: request._id,
|
|
326
346
|
msg: 'DNS query',
|
|
327
347
|
type: request.source.type,
|
|
328
348
|
port: request.source.port,
|
|
329
349
|
address: request.source.address,
|
|
330
|
-
|
|
350
|
+
question: question.name,
|
|
331
351
|
rr: reversedTypes.get(question.type) || question.type
|
|
332
352
|
});
|
|
333
353
|
return processQuestion(response, question);
|
|
334
354
|
})
|
|
335
355
|
);
|
|
336
356
|
|
|
337
|
-
logger.info({
|
|
357
|
+
logger.info({
|
|
358
|
+
msg: 'DNS response',
|
|
359
|
+
id: request._id,
|
|
360
|
+
dnsTime: Date.now() - startTime,
|
|
361
|
+
questions: request.questions,
|
|
362
|
+
answers: response.answers
|
|
363
|
+
});
|
|
338
364
|
|
|
339
365
|
// normalize answers for the DNS library
|
|
340
366
|
response.answers.forEach(answer => {
|
package/lib/dns-server.js
CHANGED
|
@@ -15,7 +15,7 @@ const SUPPORTED_TYPES = new Set(
|
|
|
15
15
|
|
|
16
16
|
const init = async () => {
|
|
17
17
|
// create UDP server
|
|
18
|
-
createDNSUdpServer(
|
|
18
|
+
createDNSUdpServer((request, send) => {
|
|
19
19
|
// filter out unsupported requests (eg. EDNS)
|
|
20
20
|
if (request.additionals && request.additionals.length) {
|
|
21
21
|
request.additionals = request.additionals.filter(additional => SUPPORTED_TYPES.has(additional.type));
|
|
@@ -43,7 +43,7 @@ const init = async () => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
// create TCP server
|
|
46
|
-
createDNSTcpServer(
|
|
46
|
+
createDNSTcpServer((request, send) => {
|
|
47
47
|
// filter out unsupported requests (eg. EDNS)
|
|
48
48
|
if (request.additionals && request.additionals.length) {
|
|
49
49
|
request.additionals = request.additionals.filter(additional => SUPPORTED_TYPES.has(additional.type));
|
package/lib/dns-tcp-server.js
CHANGED
|
@@ -52,9 +52,7 @@ class DNSTcpServer extends EventEmitter {
|
|
|
52
52
|
port: socket.remotePort,
|
|
53
53
|
address: socket.remoteAddress
|
|
54
54
|
};
|
|
55
|
-
this.emit('request', request, message =>
|
|
56
|
-
return this.send(socket, message);
|
|
57
|
-
});
|
|
55
|
+
this.emit('request', request, message => this.send(socket, message));
|
|
58
56
|
};
|
|
59
57
|
|
|
60
58
|
socket.on('readable', () => {
|
package/lib/health-worker.js
CHANGED
|
@@ -10,8 +10,8 @@ const tls = require('tls');
|
|
|
10
10
|
const http = require('http');
|
|
11
11
|
const https = require('https');
|
|
12
12
|
|
|
13
|
-
const tcpHealthCheck = async url =>
|
|
14
|
-
|
|
13
|
+
const tcpHealthCheck = async url =>
|
|
14
|
+
new Promise((resolve, reject) => {
|
|
15
15
|
let connectTimeout;
|
|
16
16
|
|
|
17
17
|
let conn;
|
|
@@ -57,10 +57,9 @@ const tcpHealthCheck = async url => {
|
|
|
57
57
|
client.on('timeout', onTimeout);
|
|
58
58
|
client.setTimeout(config.health.ttl);
|
|
59
59
|
});
|
|
60
|
-
};
|
|
61
60
|
|
|
62
|
-
const httpHealthCheck = async url =>
|
|
63
|
-
|
|
61
|
+
const httpHealthCheck = async url =>
|
|
62
|
+
new Promise((resolve, reject) => {
|
|
64
63
|
let connectTimeout;
|
|
65
64
|
|
|
66
65
|
let conn;
|
|
@@ -111,7 +110,6 @@ const httpHealthCheck = async url => {
|
|
|
111
110
|
client.on('timeout', onTimeout);
|
|
112
111
|
client.setTimeout(config.health.ttl);
|
|
113
112
|
});
|
|
114
|
-
};
|
|
115
113
|
|
|
116
114
|
const healthCheck = async target => {
|
|
117
115
|
try {
|
package/lib/public-server.js
CHANGED
|
@@ -17,7 +17,7 @@ const httpProxy = require('http-proxy');
|
|
|
17
17
|
|
|
18
18
|
const proxyServer = httpProxy.createProxyServer({});
|
|
19
19
|
|
|
20
|
-
proxyServer.on('proxyReq',
|
|
20
|
+
proxyServer.on('proxyReq', (proxyReq, req /*, res, options*/) => {
|
|
21
21
|
proxyReq.setHeader('X-Forwarded-Proto', req.proto);
|
|
22
22
|
proxyReq.setHeader('X-Connecting-IP', req.ip);
|
|
23
23
|
proxyReq.setHeader('X-CDN-Loop', 'PendingDNS');
|
|
@@ -181,11 +181,7 @@ const handler = async (req, res) => {
|
|
|
181
181
|
const url = new URL(req.url, `${req.proto}://${domain}/`);
|
|
182
182
|
const route = url.pathname;
|
|
183
183
|
|
|
184
|
-
const target =
|
|
185
|
-
records &&
|
|
186
|
-
records.find(rr => {
|
|
187
|
-
return rr && rr.type === 'URL' && rr.value && rr.value.length;
|
|
188
|
-
});
|
|
184
|
+
const target = records && records.find(rr => rr && rr.type === 'URL' && rr.value && rr.value.length);
|
|
189
185
|
|
|
190
186
|
if (target) {
|
|
191
187
|
// redirect to target URL
|
|
@@ -238,8 +234,8 @@ const handler = async (req, res) => {
|
|
|
238
234
|
);
|
|
239
235
|
};
|
|
240
236
|
|
|
241
|
-
const setupHttps = () =>
|
|
242
|
-
|
|
237
|
+
const setupHttps = () =>
|
|
238
|
+
new Promise((resolve, reject) => {
|
|
243
239
|
const server = https.createServer(
|
|
244
240
|
{
|
|
245
241
|
key: defaultKey,
|
|
@@ -249,9 +245,7 @@ const setupHttps = () => {
|
|
|
249
245
|
allowHTTP1: true,
|
|
250
246
|
SNICallback(servername, cb) {
|
|
251
247
|
getSNIContext(servername)
|
|
252
|
-
.then(ctx =>
|
|
253
|
-
return cb(null, ctx || defaultCtx);
|
|
254
|
-
})
|
|
248
|
+
.then(ctx => cb(null, ctx || defaultCtx))
|
|
255
249
|
.catch(err => {
|
|
256
250
|
logger.error({ msg: 'SNI failed', servername, err });
|
|
257
251
|
return cb(null, defaultCtx);
|
|
@@ -325,10 +319,9 @@ const setupHttps = () => {
|
|
|
325
319
|
reject(err);
|
|
326
320
|
});
|
|
327
321
|
});
|
|
328
|
-
};
|
|
329
322
|
|
|
330
|
-
const setupHttp = () =>
|
|
331
|
-
|
|
323
|
+
const setupHttp = () =>
|
|
324
|
+
new Promise((resolve, reject) => {
|
|
332
325
|
const server = http.createServer((req, res) => {
|
|
333
326
|
req.proto = 'http';
|
|
334
327
|
middleware(req, res);
|
|
@@ -362,7 +355,6 @@ const setupHttp = () => {
|
|
|
362
355
|
reject(err);
|
|
363
356
|
});
|
|
364
357
|
});
|
|
365
|
-
};
|
|
366
358
|
|
|
367
359
|
const init = async () => {
|
|
368
360
|
await Promise.all([setupHttps(), setupHttp()]);
|
package/lib/tools.js
CHANGED
package/lib/zone-store.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const punycode = require('punycode');
|
|
3
|
+
const punycode = require('punycode/');
|
|
4
4
|
const shortid = require('shortid');
|
|
5
5
|
const db = require('./db');
|
|
6
6
|
const logger = require('./logger').child({ component: 'zone-store' });
|
|
@@ -196,6 +196,7 @@ class ZoneStore {
|
|
|
196
196
|
if (short) {
|
|
197
197
|
return {
|
|
198
198
|
id: this.getFullId(name, type, hid),
|
|
199
|
+
zone: zoneDomain,
|
|
199
200
|
domain,
|
|
200
201
|
type,
|
|
201
202
|
value: JSON.parse(value)
|
|
@@ -225,9 +226,7 @@ class ZoneStore {
|
|
|
225
226
|
|
|
226
227
|
return Object.keys(record)
|
|
227
228
|
.map(key => [key, record[key]])
|
|
228
|
-
.sort((a, b) =>
|
|
229
|
-
return a[1].localeCompare(b[1]);
|
|
230
|
-
})
|
|
229
|
+
.sort((a, b) => a[1].localeCompare(b[1]))
|
|
231
230
|
.map(entry => {
|
|
232
231
|
let hid = entry[0];
|
|
233
232
|
let value = entry[1];
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pending-dns",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Lightweight API driven DNS server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"start": "node server.js",
|
|
8
8
|
"test": "grunt",
|
|
9
|
-
"
|
|
9
|
+
"build-source": "rm -rf node_modules package-lock.json && npm install && npm run licenses && rm -rf node_modules package-lock.json && npm install --production && rm -rf package-lock.json",
|
|
10
|
+
"build-dist-fast": "npx pkg --debug package.json && rm -rf package-lock.json && npm install",
|
|
11
|
+
"build-dist": "npx pkg --compress Brotli package.json && rm -rf package-lock.json && npm install",
|
|
12
|
+
"licenses": "license-report --only=prod --output=table --config license-report-config.json > licenses.txt"
|
|
10
13
|
},
|
|
11
14
|
"repository": {
|
|
12
15
|
"type": "git",
|
|
@@ -25,34 +28,55 @@
|
|
|
25
28
|
},
|
|
26
29
|
"homepage": "https://github.com/postalsys/pending-dns#readme",
|
|
27
30
|
"devDependencies": {
|
|
28
|
-
"eslint": "
|
|
31
|
+
"eslint": "8.45.0",
|
|
29
32
|
"eslint-config-nodemailer": "1.2.0",
|
|
30
|
-
"eslint-config-prettier": "
|
|
31
|
-
"grunt": "1.1
|
|
32
|
-
"grunt-cli": "1.3
|
|
33
|
-
"grunt-eslint": "
|
|
34
|
-
"
|
|
33
|
+
"eslint-config-prettier": "8.8.0",
|
|
34
|
+
"grunt": "1.6.1",
|
|
35
|
+
"grunt-cli": "1.4.3",
|
|
36
|
+
"grunt-eslint": "24.3.0",
|
|
37
|
+
"license-report": "6.4.0"
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
40
|
+
"@bugsnag/js": "^7.20.2",
|
|
37
41
|
"@fidm/x509": "1.2.1",
|
|
38
|
-
"@hapi/boom": "
|
|
39
|
-
"@hapi/hapi": "
|
|
40
|
-
"@hapi/inert": "
|
|
42
|
+
"@hapi/boom": "10.0.1",
|
|
43
|
+
"@hapi/hapi": "21.3.2",
|
|
44
|
+
"@hapi/inert": "7.1.0",
|
|
41
45
|
"@hapi/joi": "17.1.1",
|
|
42
|
-
"@hapi/vision": "
|
|
43
|
-
"@root/acme": "3.0
|
|
46
|
+
"@hapi/vision": "7.0.2",
|
|
47
|
+
"@root/acme": "3.1.0",
|
|
44
48
|
"@root/csr": "0.8.1",
|
|
45
|
-
"dns2": "1.
|
|
46
|
-
"
|
|
47
|
-
"hapi-
|
|
49
|
+
"dns2": "2.1.0",
|
|
50
|
+
"handlebars": "4.7.7",
|
|
51
|
+
"hapi-pino": "12.1.0",
|
|
52
|
+
"hapi-swagger": "17.1.0",
|
|
48
53
|
"http-proxy": "1.18.1",
|
|
49
|
-
"ioredfour": "1.0
|
|
50
|
-
"ioredis": "
|
|
51
|
-
"ipaddr.js": "1.
|
|
52
|
-
"
|
|
54
|
+
"ioredfour": "1.2.0-ioredis-07",
|
|
55
|
+
"ioredis": "5.3.2",
|
|
56
|
+
"ipaddr.js": "2.1.0",
|
|
57
|
+
"minimist": "1.2.8",
|
|
58
|
+
"node-rsa": "1.1.1",
|
|
53
59
|
"pem-jwk": "2.0.0",
|
|
54
|
-
"pino": "
|
|
55
|
-
"
|
|
56
|
-
"
|
|
60
|
+
"pino": "8.14.1",
|
|
61
|
+
"punycode": "2.3.0",
|
|
62
|
+
"shortid": "2.2.16",
|
|
63
|
+
"uuid": "9.0.0",
|
|
64
|
+
"wild-config": "1.7.0"
|
|
65
|
+
},
|
|
66
|
+
"bin": {
|
|
67
|
+
"pending-dns": "bin/pending-dns.js"
|
|
68
|
+
},
|
|
69
|
+
"pkg": {
|
|
70
|
+
"assets": [
|
|
71
|
+
"licenses.txt",
|
|
72
|
+
"LICENSE.txt",
|
|
73
|
+
"help.txt"
|
|
74
|
+
],
|
|
75
|
+
"targets": [
|
|
76
|
+
"node16-linux-x64",
|
|
77
|
+
"node16-macos-x64",
|
|
78
|
+
"node16-win-x64"
|
|
79
|
+
],
|
|
80
|
+
"outputPath": "ee-dist"
|
|
57
81
|
}
|
|
58
82
|
}
|
package/server.js
CHANGED
|
@@ -7,6 +7,7 @@ const argv = process.argv.slice(2);
|
|
|
7
7
|
const config = require('wild-config');
|
|
8
8
|
const logger = require('./lib/logger').child({ component: 'server' });
|
|
9
9
|
const pathlib = require('path');
|
|
10
|
+
const packageData = require('./package.json');
|
|
10
11
|
const { Worker, SHARE_ENV } = require('worker_threads');
|
|
11
12
|
const { isemail } = require('./lib/tools');
|
|
12
13
|
|
|
@@ -15,6 +16,29 @@ if (!config.acme || !isemail(config.acme.email)) {
|
|
|
15
16
|
process.exit(51);
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
const Bugsnag = require('@bugsnag/js');
|
|
20
|
+
if (process.env.BUGSNAG_API_KEY) {
|
|
21
|
+
Bugsnag.start({
|
|
22
|
+
apiKey: process.env.BUGSNAG_API_KEY,
|
|
23
|
+
appVersion: packageData.version,
|
|
24
|
+
logger: {
|
|
25
|
+
debug(...args) {
|
|
26
|
+
logger.debug({ msg: args.shift(), worker: 'main', source: 'bugsnag', args: args.length ? args : undefined });
|
|
27
|
+
},
|
|
28
|
+
info(...args) {
|
|
29
|
+
logger.debug({ msg: args.shift(), worker: 'main', source: 'bugsnag', args: args.length ? args : undefined });
|
|
30
|
+
},
|
|
31
|
+
warn(...args) {
|
|
32
|
+
logger.warn({ msg: args.shift(), worker: 'main', source: 'bugsnag', args: args.length ? args : undefined });
|
|
33
|
+
},
|
|
34
|
+
error(...args) {
|
|
35
|
+
logger.error({ msg: args.shift(), worker: 'main', source: 'bugsnag', args: args.length ? args : undefined });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
logger.notifyError = Bugsnag.notify.bind(Bugsnag);
|
|
40
|
+
}
|
|
41
|
+
|
|
18
42
|
let closing = false;
|
|
19
43
|
|
|
20
44
|
let workers = new Map();
|
|
@@ -67,17 +91,30 @@ if (config.health.enabled) {
|
|
|
67
91
|
spawnWorker('health');
|
|
68
92
|
}
|
|
69
93
|
|
|
70
|
-
const closeProcess = code => {
|
|
94
|
+
const closeProcess = (code, errType, err) => {
|
|
71
95
|
if (closing) {
|
|
72
96
|
return;
|
|
73
97
|
}
|
|
74
98
|
closing = true;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
99
|
+
|
|
100
|
+
if (!code) {
|
|
101
|
+
return setTimeout(() => {
|
|
102
|
+
process.exit(code);
|
|
103
|
+
}, 10);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
logger.fatal({
|
|
107
|
+
msg: errType,
|
|
108
|
+
_msg: errType,
|
|
109
|
+
err
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (!logger.notifyError) {
|
|
113
|
+
setTimeout(() => process.exit(code), 10);
|
|
114
|
+
}
|
|
78
115
|
};
|
|
79
116
|
|
|
80
|
-
process.on('uncaughtException',
|
|
81
|
-
process.on('unhandledRejection',
|
|
117
|
+
process.on('uncaughtException', err => closeProcess(1, 'uncaughtException', err));
|
|
118
|
+
process.on('unhandledRejection', err => closeProcess(2, 'unhandledRejection', err));
|
|
82
119
|
process.on('SIGTERM', () => closeProcess(0));
|
|
83
120
|
process.on('SIGINT', () => closeProcess(0));
|
package/workers/api.js
CHANGED
|
@@ -9,18 +9,31 @@ const config = require('wild-config');
|
|
|
9
9
|
const workerName = 'api';
|
|
10
10
|
|
|
11
11
|
let closing = false;
|
|
12
|
-
const closeProcess = code => {
|
|
12
|
+
const closeProcess = (code, errType, err) => {
|
|
13
13
|
if (closing) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
closing = true;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
|
|
18
|
+
if (!code) {
|
|
19
|
+
return setTimeout(() => {
|
|
20
|
+
process.exit(code);
|
|
21
|
+
}, 10);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
logger.fatal({
|
|
25
|
+
msg: errType,
|
|
26
|
+
_msg: errType,
|
|
27
|
+
err
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!logger.notifyError) {
|
|
31
|
+
setTimeout(() => process.exit(code), 10);
|
|
32
|
+
}
|
|
20
33
|
};
|
|
21
34
|
|
|
22
|
-
process.on('uncaughtException',
|
|
23
|
-
process.on('unhandledRejection',
|
|
35
|
+
process.on('uncaughtException', err => closeProcess(1, 'uncaughtException', err));
|
|
36
|
+
process.on('unhandledRejection', err => closeProcess(2, 'unhandledRejection', err));
|
|
24
37
|
process.on('SIGTERM', () => closeProcess(0));
|
|
25
38
|
process.on('SIGINT', () => closeProcess(0));
|
|
26
39
|
|
package/workers/dns.js
CHANGED
|
@@ -9,21 +9,58 @@ const config = require('wild-config');
|
|
|
9
9
|
const workerName = 'dns';
|
|
10
10
|
|
|
11
11
|
let closing = false;
|
|
12
|
-
const closeProcess = code => {
|
|
12
|
+
const closeProcess = (code, errType, err) => {
|
|
13
13
|
if (closing) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
closing = true;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
|
|
18
|
+
if (!code) {
|
|
19
|
+
return setTimeout(() => {
|
|
20
|
+
process.exit(code);
|
|
21
|
+
}, 10);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
logger.fatal({
|
|
25
|
+
msg: errType,
|
|
26
|
+
_msg: errType,
|
|
27
|
+
err
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!logger.notifyError) {
|
|
31
|
+
setTimeout(() => process.exit(code), 10);
|
|
32
|
+
}
|
|
20
33
|
};
|
|
21
34
|
|
|
22
|
-
process.on('uncaughtException',
|
|
23
|
-
process.on('unhandledRejection',
|
|
35
|
+
process.on('uncaughtException', err => closeProcess(1, 'uncaughtException', err));
|
|
36
|
+
process.on('unhandledRejection', err => closeProcess(2, 'unhandledRejection', err));
|
|
24
37
|
process.on('SIGTERM', () => closeProcess(0));
|
|
25
38
|
process.on('SIGINT', () => closeProcess(0));
|
|
26
39
|
|
|
40
|
+
const packageData = require('../package.json');
|
|
41
|
+
const Bugsnag = require('@bugsnag/js');
|
|
42
|
+
if (process.env.BUGSNAG_API_KEY) {
|
|
43
|
+
Bugsnag.start({
|
|
44
|
+
apiKey: process.env.BUGSNAG_API_KEY,
|
|
45
|
+
appVersion: packageData.version,
|
|
46
|
+
logger: {
|
|
47
|
+
debug(...args) {
|
|
48
|
+
logger.debug({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
49
|
+
},
|
|
50
|
+
info(...args) {
|
|
51
|
+
logger.debug({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
52
|
+
},
|
|
53
|
+
warn(...args) {
|
|
54
|
+
logger.warn({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
55
|
+
},
|
|
56
|
+
error(...args) {
|
|
57
|
+
logger.error({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
logger.notifyError = Bugsnag.notify.bind(Bugsnag);
|
|
62
|
+
}
|
|
63
|
+
|
|
27
64
|
const run = () => {
|
|
28
65
|
require(`../lib/${workerName}-server.js`)()
|
|
29
66
|
.then(() => {
|
package/workers/health.js
CHANGED
|
@@ -9,21 +9,59 @@ const config = require('wild-config');
|
|
|
9
9
|
const workerName = 'health';
|
|
10
10
|
|
|
11
11
|
let closing = false;
|
|
12
|
-
const closeProcess = code => {
|
|
12
|
+
const closeProcess = (code, errType, err) => {
|
|
13
13
|
if (closing) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
closing = true;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
|
|
18
|
+
if (!code) {
|
|
19
|
+
return setTimeout(() => {
|
|
20
|
+
process.exit(code);
|
|
21
|
+
}, 10);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
logger.fatal({
|
|
25
|
+
msg: errType,
|
|
26
|
+
_msg: errType,
|
|
27
|
+
err
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!logger.notifyError) {
|
|
31
|
+
setTimeout(() => process.exit(code), 10);
|
|
32
|
+
}
|
|
20
33
|
};
|
|
21
34
|
|
|
22
|
-
process.on('uncaughtException',
|
|
23
|
-
process.on('unhandledRejection',
|
|
35
|
+
process.on('uncaughtException', err => closeProcess(1, 'uncaughtException', err));
|
|
36
|
+
process.on('unhandledRejection', err => closeProcess(2, 'unhandledRejection', err));
|
|
24
37
|
process.on('SIGTERM', () => closeProcess(0));
|
|
25
38
|
process.on('SIGINT', () => closeProcess(0));
|
|
26
39
|
|
|
40
|
+
const packageData = require('../package.json');
|
|
41
|
+
const Bugsnag = require('@bugsnag/js');
|
|
42
|
+
|
|
43
|
+
if (process.env.BUGSNAG_API_KEY) {
|
|
44
|
+
Bugsnag.start({
|
|
45
|
+
apiKey: process.env.BUGSNAG_API_KEY,
|
|
46
|
+
appVersion: packageData.version,
|
|
47
|
+
logger: {
|
|
48
|
+
debug(...args) {
|
|
49
|
+
logger.debug({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
50
|
+
},
|
|
51
|
+
info(...args) {
|
|
52
|
+
logger.debug({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
53
|
+
},
|
|
54
|
+
warn(...args) {
|
|
55
|
+
logger.warn({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
56
|
+
},
|
|
57
|
+
error(...args) {
|
|
58
|
+
logger.error({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
logger.notifyError = Bugsnag.notify.bind(Bugsnag);
|
|
63
|
+
}
|
|
64
|
+
|
|
27
65
|
const run = () => {
|
|
28
66
|
require(`../lib/${workerName}-worker.js`)()
|
|
29
67
|
.then(() => {
|
package/workers/public.js
CHANGED
|
@@ -9,21 +9,58 @@ const config = require('wild-config');
|
|
|
9
9
|
const workerName = 'public';
|
|
10
10
|
|
|
11
11
|
let closing = false;
|
|
12
|
-
const closeProcess = code => {
|
|
12
|
+
const closeProcess = (code, errType, err) => {
|
|
13
13
|
if (closing) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
closing = true;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
|
|
18
|
+
if (!code) {
|
|
19
|
+
return setTimeout(() => {
|
|
20
|
+
process.exit(code);
|
|
21
|
+
}, 10);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
logger.fatal({
|
|
25
|
+
msg: errType,
|
|
26
|
+
_msg: errType,
|
|
27
|
+
err
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!logger.notifyError) {
|
|
31
|
+
setTimeout(() => process.exit(code), 10);
|
|
32
|
+
}
|
|
20
33
|
};
|
|
21
34
|
|
|
22
|
-
process.on('uncaughtException',
|
|
23
|
-
process.on('unhandledRejection',
|
|
35
|
+
process.on('uncaughtException', err => closeProcess(1, 'uncaughtException', err));
|
|
36
|
+
process.on('unhandledRejection', err => closeProcess(2, 'unhandledRejection', err));
|
|
24
37
|
process.on('SIGTERM', () => closeProcess(0));
|
|
25
38
|
process.on('SIGINT', () => closeProcess(0));
|
|
26
39
|
|
|
40
|
+
const packageData = require('../package.json');
|
|
41
|
+
const Bugsnag = require('@bugsnag/js');
|
|
42
|
+
if (process.env.BUGSNAG_API_KEY) {
|
|
43
|
+
Bugsnag.start({
|
|
44
|
+
apiKey: process.env.BUGSNAG_API_KEY,
|
|
45
|
+
appVersion: packageData.version,
|
|
46
|
+
logger: {
|
|
47
|
+
debug(...args) {
|
|
48
|
+
logger.debug({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
49
|
+
},
|
|
50
|
+
info(...args) {
|
|
51
|
+
logger.debug({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
52
|
+
},
|
|
53
|
+
warn(...args) {
|
|
54
|
+
logger.warn({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
55
|
+
},
|
|
56
|
+
error(...args) {
|
|
57
|
+
logger.error({ msg: args.shift(), worker: workerName, source: 'bugsnag', args: args.length ? args : undefined });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
logger.notifyError = Bugsnag.notify.bind(Bugsnag);
|
|
62
|
+
}
|
|
63
|
+
|
|
27
64
|
const run = () => {
|
|
28
65
|
require(`../lib/${workerName}-server.js`)()
|
|
29
66
|
.then(() => {
|