@cloud-cli/dns 1.0.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/README.md +32 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +45 -0
- package/dist/index.spec.d.ts +1 -0
- package/dist/index.spec.js +43 -0
- package/package.json +25 -0
- package/src/index.spec.ts +55 -0
- package/src/index.ts +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Local DNS resolution
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
Install and add to [Cloudy](cloud-cli/cli):
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm i @cloud-cli/dns
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import dns from '@cloud-cli/dns';
|
|
13
|
+
|
|
14
|
+
export default { dns }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Available Commands
|
|
18
|
+
|
|
19
|
+
### Add
|
|
20
|
+
|
|
21
|
+
Target is optional. Defaults to `127.0.0.1`
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
cy dns.add --domain foo --target 1.2.3.4
|
|
25
|
+
cy dns.add --domain bar{ domain: test, target: '1.2.3.4' },
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Remove
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
cy dns.remove --domain foo
|
|
32
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare class DNS {
|
|
2
|
+
static parse(line: any): {
|
|
3
|
+
domain: any;
|
|
4
|
+
target: any;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
interface DomainAndTarget {
|
|
8
|
+
domain: string;
|
|
9
|
+
target?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function add(input: DomainAndTarget): boolean;
|
|
12
|
+
declare function remove(input: DomainAndTarget): boolean;
|
|
13
|
+
declare function show(): DomainAndTarget[];
|
|
14
|
+
declare function reload(): Promise<true>;
|
|
15
|
+
declare const _default: {
|
|
16
|
+
add: typeof add;
|
|
17
|
+
remove: typeof remove;
|
|
18
|
+
show: typeof show;
|
|
19
|
+
reload: typeof reload;
|
|
20
|
+
};
|
|
21
|
+
export default _default;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { exec } from '@cloud-cli/exec';
|
|
4
|
+
const lineParse = /^address=\/(.+)\/(.+)$/;
|
|
5
|
+
const filePath = join(process.cwd(), 'configuration/apps.conf');
|
|
6
|
+
export class DNS {
|
|
7
|
+
static parse(line) {
|
|
8
|
+
const parts = line.match(lineParse);
|
|
9
|
+
return { domain: parts[1], target: parts[2] };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function add(input) {
|
|
13
|
+
let list = show();
|
|
14
|
+
if (!input.target) {
|
|
15
|
+
input.target = '127.0.0.1';
|
|
16
|
+
}
|
|
17
|
+
list = list.filter(item => item.domain !== input.domain);
|
|
18
|
+
list.push(input);
|
|
19
|
+
save(list);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
function remove(input) {
|
|
23
|
+
const list = show();
|
|
24
|
+
const newList = list.filter(item => item.domain !== input.domain);
|
|
25
|
+
save(newList);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
function save(list) {
|
|
29
|
+
const txt = list.map(line => 'address=/' + line.domain + '/' + line.target);
|
|
30
|
+
fs.writeFileSync(filePath, txt.join('\n'));
|
|
31
|
+
}
|
|
32
|
+
function show() {
|
|
33
|
+
const input = fs.readFileSync(filePath, 'utf8');
|
|
34
|
+
const entries = input
|
|
35
|
+
.trim()
|
|
36
|
+
.split('\n')
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.map(line => DNS.parse(line));
|
|
39
|
+
return entries;
|
|
40
|
+
}
|
|
41
|
+
async function reload() {
|
|
42
|
+
const cmd = await exec('systemctl', ['restart', 'dnsmasq']);
|
|
43
|
+
return cmd.ok || Promise.reject(new Error('Failed to reload'));
|
|
44
|
+
}
|
|
45
|
+
export default { add, remove, show, reload };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DNS } from './index';
|
|
2
|
+
import dns from './index';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import * as exec from '@cloud-cli/exec';
|
|
5
|
+
describe('dns', () => {
|
|
6
|
+
it('should parse a DNS configuration line', () => {
|
|
7
|
+
const line = 'address=/foo/1.2.3.4';
|
|
8
|
+
const output = DNS.parse(line);
|
|
9
|
+
expect(output).toEqual({ target: '1.2.3.4', domain: 'foo' });
|
|
10
|
+
});
|
|
11
|
+
it('should load entries from file', () => {
|
|
12
|
+
const buffer = `address=/test/1.2.3.4\naddress=/foo/5.6.7.8`;
|
|
13
|
+
jest.spyOn(fs, 'readFileSync').mockImplementation(() => buffer);
|
|
14
|
+
const list = dns.show();
|
|
15
|
+
expect(list).toEqual([
|
|
16
|
+
{ domain: 'test', target: '1.2.3.4' },
|
|
17
|
+
{ domain: 'foo', target: '5.6.7.8' },
|
|
18
|
+
]);
|
|
19
|
+
});
|
|
20
|
+
it('should add and remove local DNS entries', () => {
|
|
21
|
+
let text = '';
|
|
22
|
+
jest.spyOn(fs, 'writeFileSync').mockImplementation((_file, value) => { text = value; });
|
|
23
|
+
jest.spyOn(fs, 'readFileSync').mockImplementation(() => text);
|
|
24
|
+
dns.add({ domain: 'foo', target: '2.3.4.5' });
|
|
25
|
+
dns.add({ domain: 'bar' });
|
|
26
|
+
const lines = ['address=/foo/2.3.4.5', 'address=/bar/127.0.0.1'];
|
|
27
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), lines[0]);
|
|
28
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), lines.join('\n'));
|
|
29
|
+
dns.remove({ domain: 'bar' });
|
|
30
|
+
expect(text).toBe(lines[0]);
|
|
31
|
+
});
|
|
32
|
+
describe('reload', () => {
|
|
33
|
+
it('should reload the DNS service', async () => {
|
|
34
|
+
jest.spyOn(exec, 'exec').mockResolvedValue({ ok: true });
|
|
35
|
+
await expect(dns.reload()).resolves.toBe(true);
|
|
36
|
+
expect(exec.exec).toHaveBeenCalledWith('systemctl', ['restart', 'dnsmasq']);
|
|
37
|
+
});
|
|
38
|
+
it('should show error on reload', async () => {
|
|
39
|
+
jest.spyOn(exec, 'exec').mockResolvedValue({ ok: false, stderr: 'error' });
|
|
40
|
+
await expect(dns.reload()).rejects.toEqual(new Error('Failed to reload'));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cloud-cli/dns",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"build": "tsc",
|
|
6
|
+
"test": "jest",
|
|
7
|
+
"tdd": "jest --watchAll"
|
|
8
|
+
},
|
|
9
|
+
"prettier": "@cloud-cli/prettier-config",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/cloud-cli/dns.git"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@cloud-cli/jest-config": "^1.0.0",
|
|
19
|
+
"@cloud-cli/prettier-config": "^1.0.0",
|
|
20
|
+
"@cloud-cli/typescript-config": "^1.0.0"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@cloud-cli/exec": "^1.0.4"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { DNS } from './index';
|
|
2
|
+
import dns from './index';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import * as exec from '@cloud-cli/exec';
|
|
5
|
+
|
|
6
|
+
describe('dns', () => {
|
|
7
|
+
it('should parse a DNS configuration line', () => {
|
|
8
|
+
const line = 'address=/foo/1.2.3.4';
|
|
9
|
+
const output = DNS.parse(line);
|
|
10
|
+
expect(output).toEqual({ target: '1.2.3.4', domain: 'foo' });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should load entries from file', () => {
|
|
14
|
+
const buffer = `address=/test/1.2.3.4\naddress=/foo/5.6.7.8`;
|
|
15
|
+
jest.spyOn(fs, 'readFileSync').mockImplementation(() => buffer);
|
|
16
|
+
|
|
17
|
+
const list = dns.show();
|
|
18
|
+
expect(list).toEqual([
|
|
19
|
+
{ domain: 'test', target: '1.2.3.4' },
|
|
20
|
+
{ domain: 'foo', target: '5.6.7.8' },
|
|
21
|
+
])
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should add and remove local DNS entries', () => {
|
|
25
|
+
let text = '';
|
|
26
|
+
jest.spyOn(fs, 'writeFileSync').mockImplementation((_file, value: string) => { text = value; });
|
|
27
|
+
jest.spyOn(fs, 'readFileSync').mockImplementation(() => text);
|
|
28
|
+
|
|
29
|
+
dns.add({ domain: 'foo', target: '2.3.4.5' });
|
|
30
|
+
dns.add({ domain: 'bar' });
|
|
31
|
+
|
|
32
|
+
const lines = ['address=/foo/2.3.4.5', 'address=/bar/127.0.0.1']
|
|
33
|
+
|
|
34
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), lines[0]);
|
|
35
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.any(String), lines.join('\n'));
|
|
36
|
+
|
|
37
|
+
dns.remove({ domain: 'bar' });
|
|
38
|
+
expect(text).toBe(lines[0]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('reload', () => {
|
|
42
|
+
it('should reload the DNS service', async () => {
|
|
43
|
+
jest.spyOn(exec, 'exec').mockResolvedValue({ ok: true } as any);
|
|
44
|
+
|
|
45
|
+
await expect(dns.reload()).resolves.toBe(true);
|
|
46
|
+
expect(exec.exec).toHaveBeenCalledWith('systemctl', ['restart', 'dnsmasq']);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should show error on reload', async () => {
|
|
50
|
+
jest.spyOn(exec, 'exec').mockResolvedValue({ ok: false, stderr: 'error' } as any);
|
|
51
|
+
|
|
52
|
+
await expect(dns.reload()).rejects.toEqual(new Error('Failed to reload'));
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { exec } from '@cloud-cli/exec';
|
|
4
|
+
|
|
5
|
+
const lineParse = /^address=\/(.+)\/(.+)$/;
|
|
6
|
+
const filePath = join(process.cwd(), 'configuration/apps.conf');
|
|
7
|
+
|
|
8
|
+
export class DNS {
|
|
9
|
+
static parse(line) {
|
|
10
|
+
const parts = line.match(lineParse);
|
|
11
|
+
return { domain: parts[1], target: parts[2] }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
interface DomainAndTarget {
|
|
15
|
+
domain: string;
|
|
16
|
+
target?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function add(input: DomainAndTarget) {
|
|
20
|
+
let list = show();
|
|
21
|
+
|
|
22
|
+
if (!input.target) {
|
|
23
|
+
input.target = '127.0.0.1';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
list = list.filter(item => item.domain !== input.domain);
|
|
27
|
+
list.push(input);
|
|
28
|
+
save(list);
|
|
29
|
+
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function remove(input: DomainAndTarget) {
|
|
34
|
+
const list = show();
|
|
35
|
+
const newList = list.filter(item => item.domain !== input.domain)
|
|
36
|
+
save(newList);
|
|
37
|
+
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function save(list: DomainAndTarget[]) {
|
|
42
|
+
const txt = list.map(line => 'address=/' + line.domain + '/' + line.target);
|
|
43
|
+
fs.writeFileSync(filePath, txt.join('\n'));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function show(): DomainAndTarget[] {
|
|
47
|
+
const input = fs.readFileSync(filePath, 'utf8');
|
|
48
|
+
const entries = input
|
|
49
|
+
.trim()
|
|
50
|
+
.split('\n')
|
|
51
|
+
.filter(Boolean)
|
|
52
|
+
.map(line => DNS.parse(line))
|
|
53
|
+
|
|
54
|
+
return entries;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function reload() {
|
|
58
|
+
const cmd = await exec('systemctl', ['restart', 'dnsmasq']);
|
|
59
|
+
return cmd.ok || Promise.reject(new Error('Failed to reload'));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default { add, remove, show, reload }
|