@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 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
+ ```
@@ -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 }