@eggjs/tegg-dns-cache 3.68.0
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/LICENSE +21 -0
- package/README.md +144 -0
- package/app.d.ts +10 -0
- package/app.js +39 -0
- package/config/config.default.d.ts +26 -0
- package/config/config.default.js +30 -0
- package/lib/DnsResolver.d.ts +97 -0
- package/lib/DnsResolver.js +346 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +18 -0
- package/package.json +84 -0
- package/typings/index.d.ts +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017-present Alibaba Group Holding Limited and other contributors.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# @eggjs/tegg-dns-cache
|
|
2
|
+
|
|
3
|
+
DNS cache plugin for tegg framework. This plugin provides DNS caching capabilities to improve performance and reduce DNS lookup time.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 DNS lookup caching with LRU algorithm
|
|
8
|
+
- ⚖️ Round-robin address rotation for load balancing
|
|
9
|
+
- 🔄 Support both `dns.lookup` and `dns.resolve` modes
|
|
10
|
+
- ⏱️ Configurable cache TTL
|
|
11
|
+
- 🔌 Automatic integration with egg httpclient
|
|
12
|
+
- 🎯 Custom DNS nameservers support
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @eggjs/tegg-dns-cache --save
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Enable Plugin
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
// config/plugin.js
|
|
26
|
+
exports.dnsCache = {
|
|
27
|
+
enable: true,
|
|
28
|
+
package: '@eggjs/tegg-dns-cache',
|
|
29
|
+
};
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Configuration
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
// config/config.default.js
|
|
36
|
+
exports.dnsCache = {
|
|
37
|
+
// DNS resolution mode: 'lookup' or 'resolve' (default: 'resolve')
|
|
38
|
+
// - lookup: Use dns.lookup, respects /etc/hosts, but no TTL support
|
|
39
|
+
// - resolve: Use dns.resolve, queries DNS directly, TTL supported
|
|
40
|
+
mode: 'resolve',
|
|
41
|
+
|
|
42
|
+
// Custom DNS nameservers (only for 'resolve' mode)
|
|
43
|
+
dnsServers: ['8.8.8.8', '1.1.1.1'],
|
|
44
|
+
|
|
45
|
+
// Maximum cache entries (default: 1000)
|
|
46
|
+
maxCacheLength: 1000,
|
|
47
|
+
|
|
48
|
+
// Cache lookup interval in ms (only for 'lookup' mode, default: 10000)
|
|
49
|
+
lookupInterval: 10000,
|
|
50
|
+
|
|
51
|
+
// Enable address rotation for multiple IPs (default: true)
|
|
52
|
+
addressRotation: true,
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## DNS Resolution Modes
|
|
57
|
+
|
|
58
|
+
### Resolve Mode (Recommended)
|
|
59
|
+
|
|
60
|
+
Uses `dns.resolve4()` to query DNS servers directly. Supports TTL from DNS records.
|
|
61
|
+
|
|
62
|
+
**Advantages:**
|
|
63
|
+
- Respects DNS TTL from authoritative servers
|
|
64
|
+
- More control over DNS resolution
|
|
65
|
+
- Can specify custom nameservers
|
|
66
|
+
|
|
67
|
+
**Note:** Does not respect `/etc/hosts` file.
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
exports.dnsCache = {
|
|
71
|
+
mode: 'resolve',
|
|
72
|
+
dnsServers: ['8.8.8.8', '1.1.1.1'], // Optional custom DNS servers
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Lookup Mode
|
|
77
|
+
|
|
78
|
+
Uses `dns.lookup()` which respects system DNS configuration and `/etc/hosts`.
|
|
79
|
+
|
|
80
|
+
**Advantages:**
|
|
81
|
+
- Respects `/etc/hosts` entries
|
|
82
|
+
- Uses system DNS configuration
|
|
83
|
+
|
|
84
|
+
**Disadvantages:**
|
|
85
|
+
- Fixed cache interval (no real TTL)
|
|
86
|
+
- Less control over resolution
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
exports.dnsCache = {
|
|
90
|
+
mode: 'lookup',
|
|
91
|
+
lookupInterval: 10000, // Cache refresh interval in ms
|
|
92
|
+
};
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## API
|
|
96
|
+
|
|
97
|
+
### Access DNS Resolver
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
// In controller or service
|
|
101
|
+
const resolver = this.app.dnsResolver;
|
|
102
|
+
|
|
103
|
+
// Get cached DNS record
|
|
104
|
+
const record = resolver.getCacheRecord('example.com');
|
|
105
|
+
console.log(record); // { ip: '93.184.216.34', family: 4, ttl: 60000, ... }
|
|
106
|
+
|
|
107
|
+
// Clear DNS cache
|
|
108
|
+
resolver.resetCache();
|
|
109
|
+
|
|
110
|
+
// Get the underlying LRU cache
|
|
111
|
+
const cache = resolver.getDnsCache();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Address Rotation
|
|
115
|
+
|
|
116
|
+
When a hostname resolves to multiple IP addresses, the plugin can automatically rotate through them for load balancing:
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
exports.dnsCache = {
|
|
120
|
+
addressRotation: true, // Enable round-robin rotation
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Each request to the same hostname will use the next IP address in rotation.
|
|
125
|
+
|
|
126
|
+
## Integration with HttpClient
|
|
127
|
+
|
|
128
|
+
The plugin automatically integrates with egg's built-in httpclient. All HTTP requests will benefit from DNS caching:
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
// This request will use cached DNS
|
|
132
|
+
await this.app.httpclient.request('https://example.com/api');
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Performance Benefits
|
|
136
|
+
|
|
137
|
+
- **Reduced DNS lookup time**: Cached DNS results eliminate network round trips
|
|
138
|
+
- **Lower DNS server load**: Fewer queries to DNS servers
|
|
139
|
+
- **Improved request throughput**: Faster connection establishment
|
|
140
|
+
- **Load distribution**: Address rotation spreads load across multiple IPs
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
package/app.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Application } from 'egg';
|
|
2
|
+
export default class DnsCacheAppHook {
|
|
3
|
+
private readonly app;
|
|
4
|
+
private dnsResolver;
|
|
5
|
+
constructor(app: Application);
|
|
6
|
+
configWillLoad(): void;
|
|
7
|
+
configDidLoad(): void;
|
|
8
|
+
didLoad(): Promise<void>;
|
|
9
|
+
beforeClose(): void;
|
|
10
|
+
}
|
package/app.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const DnsResolver_1 = require("./lib/DnsResolver");
|
|
4
|
+
class DnsCacheAppHook {
|
|
5
|
+
constructor(app) {
|
|
6
|
+
this.app = app;
|
|
7
|
+
}
|
|
8
|
+
configWillLoad() {
|
|
9
|
+
if (!this.app.config.dnsCache) {
|
|
10
|
+
this.app.logger.warn('[tegg-dns-cache-plugin] DNS cache is disabled, please setup dnsCache config.');
|
|
11
|
+
}
|
|
12
|
+
const config = this.app.config.dnsCache || {};
|
|
13
|
+
// Create DNS resolver instance
|
|
14
|
+
const useDNSResolver = config.mode !== 'lookup';
|
|
15
|
+
this.dnsResolver = new DnsResolver_1.DnsResolver({
|
|
16
|
+
useResolver: useDNSResolver,
|
|
17
|
+
servers: config.dnsServers,
|
|
18
|
+
max: config.maxCacheLength || 1000,
|
|
19
|
+
dnsCacheLookupInterval: config.lookupInterval || 10000,
|
|
20
|
+
addressRotation: config.addressRotation !== false,
|
|
21
|
+
}, { logger: this.app.logger });
|
|
22
|
+
const lookupFunction = this.dnsResolver.getLookupFunction();
|
|
23
|
+
this.app.config.httpclient = this.app.config.httpclient || {};
|
|
24
|
+
this.app.config.httpclient.lookup = lookupFunction;
|
|
25
|
+
}
|
|
26
|
+
configDidLoad() {
|
|
27
|
+
// Add dnsResolver to app
|
|
28
|
+
this.app.dnsResolver = this.dnsResolver;
|
|
29
|
+
}
|
|
30
|
+
async didLoad() {
|
|
31
|
+
await this.app.moduleHandler.ready();
|
|
32
|
+
}
|
|
33
|
+
beforeClose() {
|
|
34
|
+
// Cleanup DNS cache resources
|
|
35
|
+
this.dnsResolver.resetCache();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.default = DnsCacheAppHook;
|
|
39
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYXBwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQ0EsbURBQWdEO0FBRWhELE1BQXFCLGVBQWU7SUFJbEMsWUFBWSxHQUFnQjtRQUMxQixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQztJQUNqQixDQUFDO0lBRUQsY0FBYztRQUNaLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUM5QixJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQ2xCLDhFQUE4RSxDQUMvRSxDQUFDO1FBQ0osQ0FBQztRQUNELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUM7UUFFOUMsK0JBQStCO1FBQy9CLE1BQU0sY0FBYyxHQUFHLE1BQU0sQ0FBQyxJQUFJLEtBQUssUUFBUSxDQUFDO1FBQ2hELElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSx5QkFBVyxDQUNoQztZQUNFLFdBQVcsRUFBRSxjQUFjO1lBQzNCLE9BQU8sRUFBRSxNQUFNLENBQUMsVUFBVTtZQUMxQixHQUFHLEVBQUUsTUFBTSxDQUFDLGNBQWMsSUFBSSxJQUFJO1lBQ2xDLHNCQUFzQixFQUFFLE1BQU0sQ0FBQyxjQUFjLElBQUksS0FBSztZQUN0RCxlQUFlLEVBQUUsTUFBTSxDQUFDLGVBQWUsS0FBSyxLQUFLO1NBQ2xELEVBQ0QsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FDNUIsQ0FBQztRQUNGLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztRQUM1RCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FBQztRQUM5RCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsTUFBTSxHQUFHLGNBQWMsQ0FBQztJQUNyRCxDQUFDO0lBRUQsYUFBYTtRQUNYLHlCQUF5QjtRQUN6QixJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDO0lBQzFDLENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTztRQUNYLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDdkMsQ0FBQztJQUVELFdBQVc7UUFDVCw4QkFBOEI7UUFDOUIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUNoQyxDQUFDO0NBQ0Y7QUE5Q0Qsa0NBOENDIn0=
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DNS Cache Configuration
|
|
3
|
+
*
|
|
4
|
+
* The DNS cache plugin provides DNS caching and resolution capabilities to improve performance
|
|
5
|
+
* by caching DNS lookups and supports both dns.lookup and dns.resolve modes with address rotation.
|
|
6
|
+
*
|
|
7
|
+
* @property {'lookup' | 'resolve'} mode - Use dns.lookup or dns.resolve, default is 'resolve'.
|
|
8
|
+
* - lookup: Use dns.lookup mode (old behavior), respects system DNS configuration and /etc/hosts, but does not support ttl.
|
|
9
|
+
* - resolve: Use dns.resolve mode (new feature), queries DNS servers directly, ttl supported.
|
|
10
|
+
* Note: When using resolve mode, /etc/hosts is not respected. You may need to implement custom logic
|
|
11
|
+
* if you want to include /etc/hosts resolution.
|
|
12
|
+
* @property {Number} maxCacheLength - Maximum number of DNS cache entries, default is 1000.
|
|
13
|
+
* Uses LRU (Least Recently Used) algorithm to evict old entries when cache is full.
|
|
14
|
+
* @property {Number} lookupInterval - Only works when mode is 'lookup'. DNS cache lookup interval in milliseconds, default is 10000 (10 seconds).
|
|
15
|
+
* @property {Boolean} addressRotation - Enable round-robin address rotation when multiple IP addresses
|
|
16
|
+
* are returned for a hostname, default is true. Helps distribute load across multiple servers.
|
|
17
|
+
* @property {Array<String>} dnsServers - Custom DNS nameservers for dns.resolve mode, e.g. ['8.8.8.8', '1.1.1.1'].
|
|
18
|
+
* Only effective when mode is 'resolve'. If not set, uses system default DNS servers.
|
|
19
|
+
*/
|
|
20
|
+
export declare const dnsCache: {
|
|
21
|
+
mode: "lookup" | "resolve";
|
|
22
|
+
maxCacheLength: number;
|
|
23
|
+
lookupInterval: number;
|
|
24
|
+
addressRotation: boolean;
|
|
25
|
+
dnsServers: string[] | undefined;
|
|
26
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DNS Cache Configuration
|
|
4
|
+
*
|
|
5
|
+
* The DNS cache plugin provides DNS caching and resolution capabilities to improve performance
|
|
6
|
+
* by caching DNS lookups and supports both dns.lookup and dns.resolve modes with address rotation.
|
|
7
|
+
*
|
|
8
|
+
* @property {'lookup' | 'resolve'} mode - Use dns.lookup or dns.resolve, default is 'resolve'.
|
|
9
|
+
* - lookup: Use dns.lookup mode (old behavior), respects system DNS configuration and /etc/hosts, but does not support ttl.
|
|
10
|
+
* - resolve: Use dns.resolve mode (new feature), queries DNS servers directly, ttl supported.
|
|
11
|
+
* Note: When using resolve mode, /etc/hosts is not respected. You may need to implement custom logic
|
|
12
|
+
* if you want to include /etc/hosts resolution.
|
|
13
|
+
* @property {Number} maxCacheLength - Maximum number of DNS cache entries, default is 1000.
|
|
14
|
+
* Uses LRU (Least Recently Used) algorithm to evict old entries when cache is full.
|
|
15
|
+
* @property {Number} lookupInterval - Only works when mode is 'lookup'. DNS cache lookup interval in milliseconds, default is 10000 (10 seconds).
|
|
16
|
+
* @property {Boolean} addressRotation - Enable round-robin address rotation when multiple IP addresses
|
|
17
|
+
* are returned for a hostname, default is true. Helps distribute load across multiple servers.
|
|
18
|
+
* @property {Array<String>} dnsServers - Custom DNS nameservers for dns.resolve mode, e.g. ['8.8.8.8', '1.1.1.1'].
|
|
19
|
+
* Only effective when mode is 'resolve'. If not set, uses system default DNS servers.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.dnsCache = void 0;
|
|
23
|
+
exports.dnsCache = {
|
|
24
|
+
mode: 'resolve',
|
|
25
|
+
maxCacheLength: 1000,
|
|
26
|
+
lookupInterval: 10000,
|
|
27
|
+
addressRotation: true,
|
|
28
|
+
dnsServers: undefined,
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmRlZmF1bHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb25maWcuZGVmYXVsdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRzs7O0FBRVUsUUFBQSxRQUFRLEdBQUc7SUFDdEIsSUFBSSxFQUFFLFNBQWlDO0lBQ3ZDLGNBQWMsRUFBRSxJQUFJO0lBQ3BCLGNBQWMsRUFBRSxLQUFLO0lBQ3JCLGVBQWUsRUFBRSxJQUFJO0lBQ3JCLFVBQVUsRUFBRSxTQUFpQztDQUM5QyxDQUFDIn0=
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as dns from 'node:dns';
|
|
2
|
+
import { LRU } from 'ylru';
|
|
3
|
+
import { EggLogger } from 'egg';
|
|
4
|
+
import { LookupAddress } from 'node:dns';
|
|
5
|
+
export interface DnsResolverOptions {
|
|
6
|
+
useResolver?: boolean;
|
|
7
|
+
max?: number;
|
|
8
|
+
dnsCacheLookupInterval?: number;
|
|
9
|
+
servers?: string[];
|
|
10
|
+
addressRotation?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface DnsCacheRecord {
|
|
13
|
+
ip: string;
|
|
14
|
+
family: number;
|
|
15
|
+
ttl: number;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
index: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class DnsResolver {
|
|
20
|
+
private _maxCacheSize;
|
|
21
|
+
private _dnsCache;
|
|
22
|
+
private useResolver;
|
|
23
|
+
private dnsCacheLookupInterval;
|
|
24
|
+
private enableAddressRotation;
|
|
25
|
+
private resolver?;
|
|
26
|
+
private _resolve4?;
|
|
27
|
+
private _lookup?;
|
|
28
|
+
logger: EggLogger;
|
|
29
|
+
/**
|
|
30
|
+
* Create a DNS cache resolver instance
|
|
31
|
+
* @param options - Configuration options
|
|
32
|
+
* @param options.useResolver - enable dns.resolver, otherwise use dns.lookup by default
|
|
33
|
+
* @param options.max - Maximum cache size, default is 1000
|
|
34
|
+
* @param options.dnsCacheLookupInterval - DNS cache lookup interval in milliseconds, effective when useResolver == false, default is 10000
|
|
35
|
+
* @param options.servers - Custom DNS nameservers, effective when useResolver == true, e.g. ['8.8.8.8', '1.1.1.1']
|
|
36
|
+
* @param options.addressRotation - Enable address rotation for both lookup and resolve modes, default is true
|
|
37
|
+
*/
|
|
38
|
+
constructor(options: DnsResolverOptions | undefined, args: {
|
|
39
|
+
logger: EggLogger;
|
|
40
|
+
});
|
|
41
|
+
get maxCacheSize(): number;
|
|
42
|
+
private _debugLog;
|
|
43
|
+
resetCacheSize(size: number): void;
|
|
44
|
+
/**
|
|
45
|
+
* Initialize DNS resolver with custom nameservers if provided
|
|
46
|
+
* @param defaultServers - Custom DNS nameservers
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
private _initializeResolver;
|
|
50
|
+
/**
|
|
51
|
+
* Get the lookup function compatible with dns.lookup signature
|
|
52
|
+
*/
|
|
53
|
+
getLookupFunction(): typeof dns.lookup;
|
|
54
|
+
/**
|
|
55
|
+
* This method may throw error if there is dns query in progress!
|
|
56
|
+
* @throws Error
|
|
57
|
+
* @param {string[]} servers eg. ['8.8.8.8']
|
|
58
|
+
*/
|
|
59
|
+
setServers(servers: string[]): void;
|
|
60
|
+
getDnsCache(): LRU;
|
|
61
|
+
/**
|
|
62
|
+
* Callback with record, handling rotation
|
|
63
|
+
* @param record - DNS record with rotation state
|
|
64
|
+
* @param options - Lookup options
|
|
65
|
+
* @param callback - Callback function
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
private _callbackWithRecord;
|
|
69
|
+
lookup(hostname: string): Promise<LookupAddress[]>;
|
|
70
|
+
resolve4(hostname: string): Promise<dns.RecordWithTtl[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Update DNS cache with fresh resolution
|
|
73
|
+
* Supports both dns.lookup and dns.resolve modes
|
|
74
|
+
* @param hostname - The hostname to resolve
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
private _updateDNS;
|
|
78
|
+
/**
|
|
79
|
+
* Debug DNS errors
|
|
80
|
+
* @param err - Error object
|
|
81
|
+
* @param mode - 'lookup' or 'resolve'
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
private _errorDNS;
|
|
85
|
+
/**
|
|
86
|
+
* Clear the DNS cache
|
|
87
|
+
* @param recreate - Whether to recreate the cache instance, default is false.
|
|
88
|
+
* If true, creates a new LRU instance even if cache already exists.
|
|
89
|
+
*/
|
|
90
|
+
resetCache(recreate?: boolean): void;
|
|
91
|
+
/**
|
|
92
|
+
* Get a specific hostname's single record from cache
|
|
93
|
+
* @param hostname - Hostname to query
|
|
94
|
+
* @return {DnsCacheRecord | null} cache record
|
|
95
|
+
*/
|
|
96
|
+
getCacheRecord(hostname: string): DnsCacheRecord | null;
|
|
97
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DnsResolver = void 0;
|
|
37
|
+
const util = __importStar(require("node:util"));
|
|
38
|
+
const dns = __importStar(require("node:dns"));
|
|
39
|
+
const ylru_1 = require("ylru");
|
|
40
|
+
const IP_REGEX = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
|
|
41
|
+
class DnsResolver {
|
|
42
|
+
/**
|
|
43
|
+
* Create a DNS cache resolver instance
|
|
44
|
+
* @param options - Configuration options
|
|
45
|
+
* @param options.useResolver - enable dns.resolver, otherwise use dns.lookup by default
|
|
46
|
+
* @param options.max - Maximum cache size, default is 1000
|
|
47
|
+
* @param options.dnsCacheLookupInterval - DNS cache lookup interval in milliseconds, effective when useResolver == false, default is 10000
|
|
48
|
+
* @param options.servers - Custom DNS nameservers, effective when useResolver == true, e.g. ['8.8.8.8', '1.1.1.1']
|
|
49
|
+
* @param options.addressRotation - Enable address rotation for both lookup and resolve modes, default is true
|
|
50
|
+
*/
|
|
51
|
+
constructor(options = {}, args) {
|
|
52
|
+
this._maxCacheSize = options.max || 1000;
|
|
53
|
+
this._dnsCache = new ylru_1.LRU(this._maxCacheSize);
|
|
54
|
+
this.logger = args.logger;
|
|
55
|
+
// Set useResolver before using it
|
|
56
|
+
this.useResolver = options.useResolver === true;
|
|
57
|
+
this.dnsCacheLookupInterval = options.dnsCacheLookupInterval || 10000;
|
|
58
|
+
// Address rotation is enabled by default
|
|
59
|
+
this.enableAddressRotation = options.addressRotation !== false;
|
|
60
|
+
if (this.useResolver) {
|
|
61
|
+
this._initializeResolver(options.servers);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Use dns.lookup mode (old behavior)
|
|
65
|
+
this._lookup = util.promisify(dns.lookup);
|
|
66
|
+
}
|
|
67
|
+
this.resetCache = this.resetCache.bind(this);
|
|
68
|
+
this.logger.debug(`[dns-cache] DNS Resolver initialized in ${this.useResolver ? 'resolve' : 'lookup'} mode, maxCacheSize: ${this._maxCacheSize}, addressRotation: ${this.enableAddressRotation}`);
|
|
69
|
+
}
|
|
70
|
+
get maxCacheSize() {
|
|
71
|
+
return this._maxCacheSize;
|
|
72
|
+
}
|
|
73
|
+
_debugLog(msg, ...args) {
|
|
74
|
+
this.logger.debug.apply(this.logger, [msg, ...args]);
|
|
75
|
+
}
|
|
76
|
+
resetCacheSize(size) {
|
|
77
|
+
this._maxCacheSize = size;
|
|
78
|
+
this.resetCache(true);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Initialize DNS resolver with custom nameservers if provided
|
|
82
|
+
* @param defaultServers - Custom DNS nameservers
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
_initializeResolver(defaultServers) {
|
|
86
|
+
this.resolver = new dns.Resolver({
|
|
87
|
+
timeout: 3000,
|
|
88
|
+
tries: 2,
|
|
89
|
+
});
|
|
90
|
+
const hasDefaultServers = defaultServers &&
|
|
91
|
+
Array.isArray(defaultServers) &&
|
|
92
|
+
defaultServers.length > 0;
|
|
93
|
+
if (hasDefaultServers) {
|
|
94
|
+
this.resolver.setServers(defaultServers);
|
|
95
|
+
this._debugLog(`[dns-cache] Custom DNS servers configured: ${defaultServers.join(', ')}`);
|
|
96
|
+
}
|
|
97
|
+
this._resolve4 = util.promisify(this.resolver.resolve4.bind(this.resolver));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get the lookup function compatible with dns.lookup signature
|
|
101
|
+
*/
|
|
102
|
+
getLookupFunction() {
|
|
103
|
+
return ((hostname, options, callback) => {
|
|
104
|
+
// signature handling: lookup(hostname, cb) or lookup(hostname, options, cb)
|
|
105
|
+
if (typeof options === 'function') {
|
|
106
|
+
callback = options;
|
|
107
|
+
options = {};
|
|
108
|
+
}
|
|
109
|
+
if (typeof options === 'number') {
|
|
110
|
+
options = { family: options };
|
|
111
|
+
}
|
|
112
|
+
if (typeof callback !== 'function') {
|
|
113
|
+
throw new TypeError('callback must be a function');
|
|
114
|
+
}
|
|
115
|
+
options = options || {};
|
|
116
|
+
if (!options.family) {
|
|
117
|
+
options.family = 4;
|
|
118
|
+
}
|
|
119
|
+
// keep original dns.lookup behavior for literal IPs without hitting the network
|
|
120
|
+
if (IP_REGEX.test(hostname)) {
|
|
121
|
+
// theoretically this code will never be reached because urllib will
|
|
122
|
+
// directly return for literal IPs before calling lookup function
|
|
123
|
+
const family = typeof options.family === 'number' ? options.family : 4;
|
|
124
|
+
this.logger.debug(`[dns-cache] literal IP ${hostname} lookup, bypassing cache`);
|
|
125
|
+
if (options === null || options === void 0 ? void 0 : options.all) {
|
|
126
|
+
return callback(null, [{ address: hostname, family }]);
|
|
127
|
+
}
|
|
128
|
+
return callback(null, hostname, family);
|
|
129
|
+
}
|
|
130
|
+
const record = this._dnsCache.get(hostname);
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
if (record) {
|
|
133
|
+
// Check TTL - use the first record's TTL and timestamp
|
|
134
|
+
const firstRecord = record.records[0];
|
|
135
|
+
const ttl = firstRecord.ttl || 0;
|
|
136
|
+
const timestamp = firstRecord.timestamp || now;
|
|
137
|
+
if (now - timestamp >= ttl) {
|
|
138
|
+
// refresh in background, keep serving cached value
|
|
139
|
+
this._debugLog(`[dns-cache] Cache TTL expired for ${hostname}, refreshing in background. Age: ${now - timestamp}ms, TTL: ${ttl}ms`);
|
|
140
|
+
this._updateDNS(hostname).catch(() => {
|
|
141
|
+
// do nothing, error already logged in _updateDNS
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
this._debugLog(`[dns-cache] Cache hit for ${hostname}, remaining TTL: ${ttl - (now - timestamp)}ms, records: ${record.records.length}`);
|
|
146
|
+
}
|
|
147
|
+
return this._callbackWithRecord(record, options, callback);
|
|
148
|
+
}
|
|
149
|
+
// No cached record, resolve and respond when ready
|
|
150
|
+
this._debugLog(`[dns-cache] Cache miss for ${hostname}, resolving...`);
|
|
151
|
+
this._updateDNS(hostname)
|
|
152
|
+
.then(record => {
|
|
153
|
+
this._callbackWithRecord(record, options, callback);
|
|
154
|
+
})
|
|
155
|
+
.catch(err => {
|
|
156
|
+
callback(err, '');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* This method may throw error if there is dns query in progress!
|
|
162
|
+
* @throws Error
|
|
163
|
+
* @param {string[]} servers eg. ['8.8.8.8']
|
|
164
|
+
*/
|
|
165
|
+
setServers(servers) {
|
|
166
|
+
if (this.resolver) {
|
|
167
|
+
this.resolver.setServers(servers);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
this.logger.warn('[dns-cache] Cannot set DNS servers when useResolver is false');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
getDnsCache() {
|
|
174
|
+
return this._dnsCache;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Callback with record, handling rotation
|
|
178
|
+
* @param record - DNS record with rotation state
|
|
179
|
+
* @param options - Lookup options
|
|
180
|
+
* @param callback - Callback function
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
_callbackWithRecord(record, options, callback) {
|
|
184
|
+
// All records use the unified structure with rotation
|
|
185
|
+
if (record.records && Array.isArray(record.records)) {
|
|
186
|
+
const records = record.records;
|
|
187
|
+
const currentRecord = records[record.currentIndex % records.length];
|
|
188
|
+
if (records.length > 1) {
|
|
189
|
+
this._debugLog(`[dns-cache] Address rotation: using ${currentRecord.ip} (index ${record.currentIndex % records.length}/${records.length})`);
|
|
190
|
+
}
|
|
191
|
+
// Rotate to next address for next call (if enabled)
|
|
192
|
+
if (this.enableAddressRotation) {
|
|
193
|
+
record.currentIndex = (record.currentIndex + 1) % records.length;
|
|
194
|
+
}
|
|
195
|
+
if (options.all) {
|
|
196
|
+
return callback(null, [
|
|
197
|
+
{ address: currentRecord.ip, family: currentRecord.family || 4 },
|
|
198
|
+
]);
|
|
199
|
+
}
|
|
200
|
+
return callback(null, currentRecord.ip, currentRecord.family || 4);
|
|
201
|
+
}
|
|
202
|
+
// Should not reach here, all records should use records structure
|
|
203
|
+
throw new Error('[dns_cache_error]: Invalid cache record structure');
|
|
204
|
+
}
|
|
205
|
+
async lookup(hostname) {
|
|
206
|
+
// handle localhost (some name servers may not resolve it)
|
|
207
|
+
if (hostname === 'localhost') {
|
|
208
|
+
this.logger.debug('[dns-cache] localhost lookup, bypassing cache');
|
|
209
|
+
return [{ address: '127.0.0.1', family: 4 }];
|
|
210
|
+
}
|
|
211
|
+
if (!this._lookup) {
|
|
212
|
+
throw new Error('DNS Resolver not initialized for lookup mode');
|
|
213
|
+
}
|
|
214
|
+
// Use { all: true } to get all addresses for rotation support
|
|
215
|
+
const addresses = await this._lookup(hostname, {
|
|
216
|
+
family: 4,
|
|
217
|
+
all: true,
|
|
218
|
+
});
|
|
219
|
+
return addresses;
|
|
220
|
+
}
|
|
221
|
+
async resolve4(hostname) {
|
|
222
|
+
// handle localhost (some name servers may not resolve it)
|
|
223
|
+
if (hostname === 'localhost') {
|
|
224
|
+
this.logger.debug('[dns-cache] localhost resolve, bypassing cache');
|
|
225
|
+
return [
|
|
226
|
+
{
|
|
227
|
+
address: '127.0.0.1',
|
|
228
|
+
ttl: Math.floor(Number.MAX_SAFE_INTEGER / 1000),
|
|
229
|
+
},
|
|
230
|
+
]; // provide a default TTL
|
|
231
|
+
}
|
|
232
|
+
if (!this._resolve4) {
|
|
233
|
+
throw new Error('DNS Resolver not initialized for resolve mode');
|
|
234
|
+
}
|
|
235
|
+
const addresses = await this._resolve4(hostname, {
|
|
236
|
+
ttl: true,
|
|
237
|
+
});
|
|
238
|
+
return addresses;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Update DNS cache with fresh resolution
|
|
242
|
+
* Supports both dns.lookup and dns.resolve modes
|
|
243
|
+
* @param hostname - The hostname to resolve
|
|
244
|
+
* @private
|
|
245
|
+
*/
|
|
246
|
+
async _updateDNS(hostname) {
|
|
247
|
+
// Use dns.lookup
|
|
248
|
+
if (!this.useResolver) {
|
|
249
|
+
try {
|
|
250
|
+
const addresses = await this.lookup(hostname);
|
|
251
|
+
const addressArray = Array.isArray(addresses) ? addresses : [addresses];
|
|
252
|
+
if (addressArray.length === 0) {
|
|
253
|
+
throw new Error(`empty address for ${hostname}`);
|
|
254
|
+
}
|
|
255
|
+
const records = addressArray.map((addr, index) => ({
|
|
256
|
+
ip: addr.address,
|
|
257
|
+
family: addr.family || 4,
|
|
258
|
+
ttl: this.dnsCacheLookupInterval,
|
|
259
|
+
timestamp: Date.now(),
|
|
260
|
+
index,
|
|
261
|
+
}));
|
|
262
|
+
const cacheEntry = {
|
|
263
|
+
records,
|
|
264
|
+
currentIndex: 0,
|
|
265
|
+
};
|
|
266
|
+
this._dnsCache.set(hostname, cacheEntry);
|
|
267
|
+
this._debugLog(`[dns-cache] dns.lookup succeeded for ${hostname}, resolved ${records.length} address(es): ${records.map(r => r.ip).join(', ')}, TTL: ${this.dnsCacheLookupInterval}ms`);
|
|
268
|
+
return cacheEntry;
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
this._errorDNS(err, 'lookup', hostname);
|
|
272
|
+
throw err;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Use dns.resolve
|
|
276
|
+
try {
|
|
277
|
+
const addresses = await this.resolve4(hostname);
|
|
278
|
+
const addressArray = Array.isArray(addresses) ? addresses : [addresses];
|
|
279
|
+
// Store all addresses with rotation index
|
|
280
|
+
const records = addressArray.map((addr, index) => {
|
|
281
|
+
const address = typeof addr === 'string' ? addr : addr.address;
|
|
282
|
+
const ttlSeconds = addr && Number.isInteger(addr.ttl) && addr.ttl >= 0 ? addr.ttl : 0;
|
|
283
|
+
return {
|
|
284
|
+
ip: address,
|
|
285
|
+
family: 4,
|
|
286
|
+
ttl: ttlSeconds * 1000,
|
|
287
|
+
timestamp: Date.now(),
|
|
288
|
+
index,
|
|
289
|
+
};
|
|
290
|
+
});
|
|
291
|
+
if (records.length === 0 || !records[0].ip) {
|
|
292
|
+
throw new Error(`empty address for ${hostname}`);
|
|
293
|
+
}
|
|
294
|
+
// Store all records with rotation state
|
|
295
|
+
const cacheEntry = {
|
|
296
|
+
records,
|
|
297
|
+
currentIndex: 0,
|
|
298
|
+
};
|
|
299
|
+
this._dnsCache.set(hostname, cacheEntry);
|
|
300
|
+
this._debugLog(`[dns-cache] dns.resolve4 succeeded for ${hostname}, resolved ${records.length} address(es): ${records
|
|
301
|
+
.map(r => `${r.ip} (TTL: ${r.ttl}ms)`)
|
|
302
|
+
.join(', ')}`);
|
|
303
|
+
return cacheEntry;
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
this._errorDNS(err, 'resolve', hostname);
|
|
307
|
+
throw err;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Debug DNS errors
|
|
312
|
+
* @param err - Error object
|
|
313
|
+
* @param mode - 'lookup' or 'resolve'
|
|
314
|
+
* @private
|
|
315
|
+
*/
|
|
316
|
+
_errorDNS(err, mode, hostname) {
|
|
317
|
+
this.logger.error(`error occurred when resolving ${hostname} with dns.${mode}: ${err && err.message ? err.message : err}`);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Clear the DNS cache
|
|
321
|
+
* @param recreate - Whether to recreate the cache instance, default is false.
|
|
322
|
+
* If true, creates a new LRU instance even if cache already exists.
|
|
323
|
+
*/
|
|
324
|
+
resetCache(recreate = false) {
|
|
325
|
+
this._debugLog(`[dns-cache] Resetting DNS cache (recreate: ${recreate})`);
|
|
326
|
+
if (this._dnsCache)
|
|
327
|
+
this._dnsCache.reset();
|
|
328
|
+
if (recreate) {
|
|
329
|
+
this._dnsCache = new ylru_1.LRU(this._maxCacheSize);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get a specific hostname's single record from cache
|
|
334
|
+
* @param hostname - Hostname to query
|
|
335
|
+
* @return {DnsCacheRecord | null} cache record
|
|
336
|
+
*/
|
|
337
|
+
getCacheRecord(hostname) {
|
|
338
|
+
const entry = this._dnsCache.get(hostname);
|
|
339
|
+
if (entry && entry.records && Array.isArray(entry.records)) {
|
|
340
|
+
return entry.records[entry.currentIndex % entry.records.length];
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
exports.DnsResolver = DnsResolver;
|
|
346
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRG5zUmVzb2x2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJEbnNSZXNvbHZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxnREFBa0M7QUFDbEMsOENBQWdDO0FBQ2hDLCtCQUEyQjtBQUkzQixNQUFNLFFBQVEsR0FBRyxzQ0FBc0MsQ0FBQztBQXVCeEQsTUFBYSxXQUFXO0lBV3RCOzs7Ozs7OztPQVFHO0lBQ0gsWUFBWSxVQUE4QixFQUFFLEVBQUUsSUFBMkI7UUFDdkUsSUFBSSxDQUFDLGFBQWEsR0FBRyxPQUFPLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQztRQUN6QyxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksVUFBRyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUM3QyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUM7UUFFMUIsa0NBQWtDO1FBQ2xDLElBQUksQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLFdBQVcsS0FBSyxJQUFJLENBQUM7UUFFaEQsSUFBSSxDQUFDLHNCQUFzQixHQUFHLE9BQU8sQ0FBQyxzQkFBc0IsSUFBSSxLQUFLLENBQUM7UUFFdEUseUNBQXlDO1FBQ3pDLElBQUksQ0FBQyxxQkFBcUIsR0FBRyxPQUFPLENBQUMsZUFBZSxLQUFLLEtBQUssQ0FBQztRQUUvRCxJQUFJLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNyQixJQUFJLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzVDLENBQUM7YUFBTSxDQUFDO1lBQ04scUNBQXFDO1lBQ3JDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDNUMsQ0FBQztRQUVELElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJ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
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './DnsResolver';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./DnsResolver"), exports);
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsZ0RBQThCIn0=
|
package/package.json
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eggjs/tegg-dns-cache",
|
|
3
|
+
"eggPlugin": {
|
|
4
|
+
"name": "dnsCache",
|
|
5
|
+
"dependencies": [
|
|
6
|
+
"tegg"
|
|
7
|
+
]
|
|
8
|
+
},
|
|
9
|
+
"version": "3.68.0",
|
|
10
|
+
"types": "typings/index.d.ts",
|
|
11
|
+
"description": "tegg dns cache plugin",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"egg",
|
|
14
|
+
"typescript",
|
|
15
|
+
"dns",
|
|
16
|
+
"cache",
|
|
17
|
+
"tegg"
|
|
18
|
+
],
|
|
19
|
+
"files": [
|
|
20
|
+
"app.js",
|
|
21
|
+
"app.d.ts",
|
|
22
|
+
"agent.js",
|
|
23
|
+
"agent.d.ts",
|
|
24
|
+
"index.js",
|
|
25
|
+
"index.d.ts",
|
|
26
|
+
"types.js",
|
|
27
|
+
"types.d.ts",
|
|
28
|
+
"config/**/*.js",
|
|
29
|
+
"config/**/*.d.ts",
|
|
30
|
+
"lib/**/*.js",
|
|
31
|
+
"lib/**/*.d.ts",
|
|
32
|
+
"app/**/*.js",
|
|
33
|
+
"app/**/*.d.ts",
|
|
34
|
+
"typings/*.d.ts"
|
|
35
|
+
],
|
|
36
|
+
"eggModule": {
|
|
37
|
+
"name": "teggDnsCache"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha",
|
|
41
|
+
"clean": "tsc -b --clean",
|
|
42
|
+
"tsc": "ut run clean && tsc -p ./tsconfig.json",
|
|
43
|
+
"tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json",
|
|
44
|
+
"prepublishOnly": "ut tsc:pub"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/eggjs/tegg",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/eggjs/tegg/issues"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git@github.com:eggjs/tegg.git",
|
|
53
|
+
"directory": "plugin/dns-cache"
|
|
54
|
+
},
|
|
55
|
+
"egg": {
|
|
56
|
+
"typescript": true
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=18.0.0"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"egg": ">=3.32.0"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"@eggjs/tegg": "^3.68.0",
|
|
66
|
+
"ylru": "^2.0.0"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@eggjs/tegg-config": "^3.68.0",
|
|
70
|
+
"@eggjs/tegg-plugin": "^3.68.0",
|
|
71
|
+
"@types/mocha": "^10.0.10",
|
|
72
|
+
"@types/node": "^20.2.4",
|
|
73
|
+
"cross-env": "^7.0.3",
|
|
74
|
+
"egg": "^3.32.0",
|
|
75
|
+
"egg-mock": "^5.5.0",
|
|
76
|
+
"mocha": "^10.2.0",
|
|
77
|
+
"ts-node": "^10.9.1",
|
|
78
|
+
"typescript": "^5.0.4"
|
|
79
|
+
},
|
|
80
|
+
"publishConfig": {
|
|
81
|
+
"access": "public"
|
|
82
|
+
},
|
|
83
|
+
"gitHead": "c8c089bee4aaf48ccdea3d56d142c82044223ae3"
|
|
84
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import 'egg';
|
|
2
|
+
import '@eggjs/tegg-plugin';
|
|
3
|
+
import * as dns from 'node:dns';
|
|
4
|
+
import { DnsResolver, DnsCacheRecord } from '../lib';
|
|
5
|
+
export { DnsResolver, DnsCacheRecord };
|
|
6
|
+
|
|
7
|
+
declare module 'egg' {
|
|
8
|
+
interface TeggDnsCacheApplication {
|
|
9
|
+
/**
|
|
10
|
+
* DNS resolver instance, provides DNS caching capabilities
|
|
11
|
+
*/
|
|
12
|
+
dnsResolver: DnsResolver;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Application extends TeggDnsCacheApplication {}
|
|
16
|
+
|
|
17
|
+
interface EggAppConfig {
|
|
18
|
+
/**
|
|
19
|
+
* DNS Cache Configuration
|
|
20
|
+
*/
|
|
21
|
+
dnsCache: {
|
|
22
|
+
/** Use dns.lookup or dns.resolve, default is 'resolve' */
|
|
23
|
+
mode?: 'lookup' | 'resolve';
|
|
24
|
+
/** Custom DNS nameservers for dns.resolve mode */
|
|
25
|
+
dnsServers?: string[];
|
|
26
|
+
/** Maximum number of DNS cache entries, default is 1000 */
|
|
27
|
+
maxCacheLength?: number;
|
|
28
|
+
/** DNS cache lookup interval in milliseconds, default is 10000 */
|
|
29
|
+
lookupInterval?: number;
|
|
30
|
+
/** Enable round-robin address rotation, default is true */
|
|
31
|
+
addressRotation?: boolean;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|