clever-tools 2.9.0 → 2.10.0-beta.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.
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+
3
+ const ngApi = require('@clevercloud/client/cjs/api/v4/network-group.js');
4
+
5
+ const { sendToApi } = require('../../models/send-to-api.js');
6
+
7
+ const Logger = require('../../logger.js');
8
+ const NetworkGroup = require('../../models/networkgroup.js');
9
+ const Formatter = require('../../models/format-string.js');
10
+ const TableFormatter = require('../../models/format-ng-table.js');
11
+
12
+ async function listMembers(params) {
13
+ const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, 'natural-name': naturalName, json } = params.options;
14
+ const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
15
+ const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
16
+
17
+ Logger.info(`Listing members from Network Group '${networkGroupId}'`);
18
+ const result = await ngApi.listNetworkGroupMembers({ ownerId, networkGroupId }).then(sendToApi);
19
+
20
+ if (json) {
21
+ Logger.println(JSON.stringify(result, null, 2));
22
+ }
23
+ else {
24
+ if (result.length === 0) {
25
+ Logger.println(`No member found. You can add one with ${Formatter.formatCommand('clever networkgroups members add')}.`);
26
+ }
27
+ else {
28
+ await TableFormatter.printMembersTableHeader(naturalName);
29
+ for (const ng of result) {
30
+ Logger.println(await TableFormatter.formatMembersLine(ng, naturalName));
31
+ }
32
+ }
33
+ }
34
+ }
35
+
36
+ async function getMember(params) {
37
+ const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, 'member-id': memberId, 'natural-name': naturalName, json } = params.options;
38
+ const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
39
+ const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);;
40
+
41
+ Logger.info(`Getting details for member ${Formatter.formatString(memberId)} in Network Group ${Formatter.formatString(networkGroupId)}`);
42
+ const result = await ngApi.getNetworkGroupMember({ ownerId, networkGroupId, memberId: memberId }).then(sendToApi);
43
+
44
+ if (json) {
45
+ Logger.println(JSON.stringify(result, null, 2));
46
+ }
47
+ else {
48
+ await TableFormatter.printMembersTableHeader(naturalName);
49
+ Logger.println(await TableFormatter.formatMembersLine(result, naturalName));
50
+ }
51
+ }
52
+
53
+ async function addMember(params) {
54
+ const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, 'member-id': memberId, type, 'domain-name': domainName, label } = params.options;
55
+ const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
56
+ const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
57
+
58
+ const body = { id: memberId, label, domain_name: domainName, type };
59
+ Logger.debug('Sending body: ' + JSON.stringify(body, null, 2));
60
+ await ngApi.createNetworkGroupMember({ ownerId, networkGroupId }, body).then(sendToApi);
61
+
62
+ Logger.println(`Successfully added member ${Formatter.formatString(memberId)} to Network Group ${Formatter.formatString(networkGroupId)}.`);
63
+ }
64
+
65
+ async function removeMember(params) {
66
+ const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, 'member-id': memberId } = params.options;
67
+ const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
68
+ const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
69
+
70
+ await ngApi.deleteNetworkGroupMember({ ownerId, networkGroupId, memberId }).then(sendToApi);
71
+
72
+ Logger.println(`Successfully removed member ${Formatter.formatString(memberId)} from Network Group ${Formatter.formatString(networkGroupId)}.`);
73
+ }
74
+
75
+ module.exports = {
76
+ listMembers,
77
+ getMember,
78
+ addMember,
79
+ removeMember,
80
+ };
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const ngApi = require('@clevercloud/client/cjs/api/v4/network-group.js');
4
+
5
+ const { sendToApi } = require('../../models/send-to-api.js');
6
+
7
+ const Logger = require('../../logger.js');
8
+ const NetworkGroup = require('../../models/networkgroup.js');
9
+ const Formatter = require('../../models/format-string.js');
10
+ const TableFormatter = require('../../models/format-ng-table.js');
11
+
12
+ async function listPeers(params) {
13
+ const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, json } = params.options;
14
+ const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
15
+ const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
16
+
17
+ Logger.info(`Listing peers from Network Group ${Formatter.formatString(networkGroupId)}`);
18
+ const result = await ngApi.listNetworkGroupPeers({ ownerId, networkGroupId }).then(sendToApi);
19
+
20
+ if (json) {
21
+ Logger.println(JSON.stringify(result, null, 2));
22
+ }
23
+ else {
24
+ if (result.length === 0) {
25
+ Logger.println(`No peer found. You can add an external one with ${Formatter.formatCommand('clever networkgroups peers add-external')}.`);
26
+ }
27
+ else {
28
+ TableFormatter.printPeersTableHeader();
29
+ result.forEach((peer) => {
30
+ Logger.println(TableFormatter.formatPeersLine(peer));
31
+ });
32
+ }
33
+ }
34
+ }
35
+
36
+ async function getPeer(params) {
37
+ const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, 'peer-id': peerId, json } = params.options;
38
+ const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
39
+ const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
40
+
41
+ Logger.info(`Getting details for peer ${Formatter.formatString(peerId)} in Network Group ${Formatter.formatString(networkGroupId)}`);
42
+ const peer = await ngApi.getNetworkGroupPeer({ ownerId, networkGroupId, peerId }).then(sendToApi);
43
+
44
+ if (json) {
45
+ Logger.println(JSON.stringify(peer, null, 2));
46
+ }
47
+ else {
48
+ TableFormatter.printPeersTableHeader();
49
+ Logger.println(TableFormatter.formatPeersLine(peer));
50
+ }
51
+ }
52
+
53
+ async function addExternalPeer(params) {
54
+ const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, role, 'public-key': publicKey, label, parent, ip, port } = params.options;
55
+ const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
56
+ const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
57
+
58
+ const body = { peer_role: role, public_key: publicKey, label, parent_member: parent, ip, port };
59
+ Logger.info(`Adding external peer to Network Group ${Formatter.formatString(networkGroupId)}`);
60
+ Logger.debug('Sending body: ' + JSON.stringify(body, null, 2));
61
+ const { id: peerId } = await ngApi.createNetworkGroupExternalPeer({ ownerId, networkGroupId }, body).then(sendToApi);
62
+
63
+ Logger.println(`External peer ${Formatter.formatString(peerId)} must have been added to Network Group ${Formatter.formatString(networkGroupId)}.`);
64
+ return peerId;
65
+ }
66
+
67
+ async function removeExternalPeer(params) {
68
+ const { org: orgaIdOrName, alias, ng: networkGroupIdOrLabel, 'peer-id': peerId } = params.options;
69
+ const ownerId = await NetworkGroup.getOwnerId(orgaIdOrName, alias);
70
+ const networkGroupId = await NetworkGroup.getId(ownerId, networkGroupIdOrLabel);
71
+
72
+ Logger.info(`Removing external peer ${Formatter.formatString(peerId)} from Network Group ${Formatter.formatString(networkGroupId)}`);
73
+ await ngApi.deleteNetworkGroupExternalPeer({ ownerId, networkGroupId, peerId }).then(sendToApi);
74
+
75
+ Logger.println(`External peer ${Formatter.formatString(peerId)} must have been removed from Network Group ${Formatter.formatString(networkGroupId)}.`);
76
+ }
77
+
78
+ module.exports = {
79
+ listPeers,
80
+ getPeer,
81
+ addExternalPeer,
82
+ removeExternalPeer,
83
+ };
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const openPage = require('opn');
3
+ const openPage = require('open');
4
4
 
5
5
  const AppConfig = require('../models/app_configuration.js');
6
6
  const Domain = require('../models/domain.js');
@@ -79,6 +79,14 @@ function findApp (config, alias) {
79
79
  return appByAlias;
80
80
  }
81
81
 
82
+ return findDefaultApp(config);
83
+ }
84
+
85
+ function findDefaultApp (config) {
86
+ if (_.isEmpty(config.apps)) {
87
+ throw new Error('There are no applications linked. You can add one with `clever link`');
88
+ }
89
+
82
90
  if (config.default != null) {
83
91
  const defaultApp = _.find(config.apps, { app_id: config.default });
84
92
  if (defaultApp == null) {
@@ -95,6 +103,25 @@ function findApp (config, alias) {
95
103
  throw new Error(`Several applications are linked. You can specify one with the "--alias" option. Run "clever applications" to list linked applications. Available aliases: ${aliases}`);
96
104
  }
97
105
 
106
+ async function getAppDetailsForId (appId) {
107
+ const config = await loadApplicationConf();
108
+
109
+ if (_.isEmpty(config.apps)) {
110
+ throw new Error('There are no applications linked. You can add one with `clever link`');
111
+ }
112
+
113
+ const [appById, secondAppById] = _.filter(config.apps, { app_id: appId });
114
+ if (appById == null) {
115
+ throw new Error(`There are no applications matching id '${appId}'`);
116
+ }
117
+ if (secondAppById != null) {
118
+ throw new Error(`There are several applications matching id '${appId}'.`
119
+ + 'This should not happen, your `.clever.json` should be fixed.');
120
+ }
121
+
122
+ return appById;
123
+ }
124
+
98
125
  async function getAppDetails ({ alias }) {
99
126
  const config = await loadApplicationConf();
100
127
  const app = findApp(config, alias);
@@ -110,6 +137,16 @@ async function getAppDetails ({ alias }) {
110
137
  };
111
138
  };
112
139
 
140
+ async function getMostNaturalName (appId) {
141
+ try {
142
+ const details = await getAppDetailsForId(appId);
143
+ return details.alias || details.name || appId;
144
+ }
145
+ catch {
146
+ return appId;
147
+ }
148
+ }
149
+
113
150
  function persistConfig (modifiedConfig) {
114
151
  const jsonContents = JSON.stringify(modifiedConfig, null, 2);
115
152
  return fs.writeFile(conf.APP_CONFIGURATION_FILE, jsonContents);
@@ -128,5 +165,6 @@ module.exports = {
128
165
  removeLinkedApplication,
129
166
  findApp,
130
167
  getAppDetails,
168
+ getMostNaturalName,
131
169
  setDefault,
132
170
  };
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ const formatNgTable = require('../format-table.js');
4
+ const colors = require('colors/safe');
5
+
6
+ const AppConfig = require('./app_configuration.js');
7
+ const Logger = require('../logger.js');
8
+ const Formatter = require('./format-string.js');
9
+
10
+ function printSeparator (columnLengths) {
11
+ Logger.println('─'.repeat(columnLengths.reduce((a, b) => a + b + 2)));
12
+ }
13
+
14
+ // We use examples of maximum width text to have a clean display
15
+ const networkGroupsTableColumnLengths = [
16
+ 39, /* id length */
17
+ 48, /* label length */
18
+ 7, /* members length */
19
+ 5, /* peers length */
20
+ 48, /* description */
21
+ ];
22
+ const formatNetworkGroupsTable = formatNgTable(networkGroupsTableColumnLengths);
23
+ function formatNetworkGroupsLine (ng) {
24
+ return formatNetworkGroupsTable([
25
+ [
26
+ Formatter.formatId(ng.id),
27
+ Formatter.formatString(ng.label, false),
28
+ Formatter.formatNumber(ng.members.length),
29
+ Formatter.formatNumber(ng.peers.length),
30
+ Formatter.formatString(ng.description || ' ', false),
31
+ ],
32
+ ]);
33
+ };
34
+ function printNetworkGroupsTableHeader () {
35
+ Logger.println(colors.bold(formatNetworkGroupsTable([
36
+ ['Network Group ID', 'Label', 'Members', 'Peers', 'Description'],
37
+ ])));
38
+ printSeparator(networkGroupsTableColumnLengths);
39
+ }
40
+
41
+ const membersTableColumnLengths = [
42
+ 48, /* id length */
43
+ 12, /* type length */
44
+ 48, /* label length */
45
+ 24, /* domain-name length */
46
+ ];
47
+ const formatMembersTable = formatNgTable(membersTableColumnLengths);
48
+ async function formatMembersLine (member, showAliases = false) {
49
+ return formatMembersTable([
50
+ [
51
+ showAliases
52
+ ? Formatter.formatString(await AppConfig.getMostNaturalName(member.id), false)
53
+ : Formatter.formatId(member.id),
54
+ Formatter.formatString(member.type, false),
55
+ Formatter.formatString(member.label, false),
56
+ Formatter.formatString(member.domain_name || ' ', false),
57
+ ],
58
+ ]);
59
+ };
60
+ async function printMembersTableHeader (naturalName = false) {
61
+ Logger.println(colors.bold(formatMembersTable([
62
+ [
63
+ naturalName ? 'Member' : 'Member ID',
64
+ 'Member Type',
65
+ 'Label',
66
+ 'Domain Name',
67
+ ],
68
+ ])));
69
+ printSeparator(membersTableColumnLengths);
70
+ }
71
+
72
+ const peersTableColumnLengths = [
73
+ 45, /* id length */
74
+ 12, /* type length */
75
+ 14, /* endpoint type length */
76
+ 48, /* label length */
77
+ 24, /* hostname */
78
+ 15, /* ip */
79
+ ];
80
+ const formatPeersTable = formatNgTable(peersTableColumnLengths);
81
+ function formatPeersLine (peer) {
82
+ const ip = (peer.endpoint.type === 'ServerEndpoint') ? peer.endpoint.ng_term.ip : peer.endpoint.ng_ip;
83
+ return formatPeersTable([
84
+ [
85
+ Formatter.formatId(peer.id),
86
+ Formatter.formatString(peer.type, false),
87
+ Formatter.formatString(peer.endpoint.type, false),
88
+ Formatter.formatString(peer.label, false),
89
+ Formatter.formatString(peer.hostname, false),
90
+ Formatter.formatIp(ip),
91
+ ],
92
+ ]);
93
+ };
94
+ function printPeersTableHeader () {
95
+ Logger.println(colors.bold(formatPeersTable([
96
+ [
97
+ 'Peer ID',
98
+ 'Peer Type',
99
+ 'Endpoint Type',
100
+ 'Label',
101
+ 'Hostname',
102
+ 'IP Address',
103
+ ],
104
+ ])));
105
+ printSeparator(peersTableColumnLengths);
106
+ }
107
+
108
+ module.exports = {
109
+ formatNetworkGroupsLine,
110
+ printNetworkGroupsTableHeader,
111
+ formatMembersLine,
112
+ printMembersTableHeader,
113
+ formatPeersLine,
114
+ printPeersTableHeader,
115
+ };
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const colors = require('colors/safe');
4
+
5
+ function formatId (id) {
6
+ return colors.dim(id);
7
+ }
8
+
9
+ function formatString (str, decorated = true) {
10
+ const string = decorated ? `'${str}'` : str;
11
+ return colors.green(string);
12
+ }
13
+
14
+ function formatNumber (number) {
15
+ return colors.yellow(number);
16
+ }
17
+
18
+ function formatIp (ip) {
19
+ return colors.cyan(ip);
20
+ }
21
+
22
+ function formatUrl (url, decorated = true) {
23
+ const string = decorated ? `<${url}>` : url;
24
+ return colors.cyan(string);
25
+ }
26
+
27
+ function formatCommand (command, decorated = true) {
28
+ const string = decorated ? `\`${command}\`` : command;
29
+ return colors.magenta(string);
30
+ }
31
+
32
+ function formatCode (code, decorated = true) {
33
+ return formatCommand(code, decorated);
34
+ }
35
+
36
+ module.exports = {
37
+ formatId,
38
+ formatString,
39
+ formatNumber,
40
+ formatIp,
41
+ formatUrl,
42
+ formatCommand,
43
+ formatCode,
44
+ };
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ const autocomplete = require('cliparse').autocomplete;
4
+ const Organisation = require('../models/organisation.js');
5
+ const User = require('../models/user.js');
6
+ const AppConfig = require('./app_configuration.js');
7
+ const ngApi = require('@clevercloud/client/cjs/api/v4/network-group.js');
8
+ const { sendToApi } = require('./send-to-api.js');
9
+
10
+ async function getOwnerId(orgaIdOrName, alias) {
11
+ if (orgaIdOrName == null) {
12
+ try {
13
+ return (await AppConfig.getAppDetails({alias})).ownerId;
14
+ } catch (error) {
15
+ return (await User.getCurrentId())
16
+ }
17
+ } else {
18
+ return (await Organisation.getId(orgaIdOrName));
19
+ }
20
+ }
21
+
22
+ async function getId(ownerId, ngIdOrLabel) {
23
+ if (ngIdOrLabel == null) {
24
+ return null;
25
+ }
26
+
27
+ if (ngIdOrLabel.ng_id != null) {
28
+ return ngIdOrLabel.ng_id;
29
+ }
30
+
31
+ return getByLabel(ownerId, ngIdOrLabel.ng_label)
32
+ .then((ng) => ng.id);
33
+ }
34
+
35
+ async function getByLabel(owner_id, label) {
36
+ const networkGroups = await ngApi.get({ owner_id }).then(sendToApi);
37
+ const filteredNgs = networkGroups.filter((ng) => ng.label === label);
38
+
39
+ if (filteredNgs.length === 0) {
40
+ throw new Error('Network Group not found');
41
+ }
42
+ if (filteredNgs.length > 1) {
43
+ throw new Error('Ambiguous Network Group label');
44
+ }
45
+
46
+ return filteredNgs[0];
47
+ }
48
+
49
+ function listAvailablePeerRoles() {
50
+ return autocomplete.words(['client', 'server']);
51
+ }
52
+
53
+ function listAvailableMemberTypes() {
54
+ return autocomplete.words(['application', 'addon', 'external']);
55
+ }
56
+
57
+ module.exports = {
58
+ getOwnerId,
59
+ getId,
60
+ listAvailablePeerRoles,
61
+ listAvailableMemberTypes,
62
+ };
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const { promises: fs, existsSync } = require('fs');
5
+ const path = require('path');
6
+
7
+ const Logger = require('../logger.js');
8
+ const Formatter = require('./format-string.js');
9
+
10
+ function getWgConfFolder () {
11
+ // TODO: See if we can use runtime dirs
12
+ return path.join(os.tmpdir(), 'com.clever-cloud.networkgroups');
13
+ }
14
+
15
+ async function createWgConfFolderIfNeeded () {
16
+ const confFolder = getWgConfFolder();
17
+ if (!existsSync(confFolder)) {
18
+ await fs.mkdir(confFolder);
19
+ }
20
+ }
21
+
22
+ function getWgConfInformation (ngId) {
23
+ const confName = `wgcc${ngId.slice(-8)}`;
24
+ const confPath = path.join(getWgConfFolder(), `${confName}.conf`);
25
+
26
+ return { confName, confPath };
27
+ }
28
+
29
+ function getPeerIdPath (confName) {
30
+ return path.join(getWgConfFolder(), `${confName}.id`);
31
+ }
32
+
33
+ async function storePeerId (peerId, confName) {
34
+ const filePath = getPeerIdPath(confName);
35
+
36
+ try {
37
+ await fs.writeFile(filePath, peerId, { mode: 0o600, flag: 'wx' });
38
+ Logger.info(`Saved peer ID file to ${Formatter.formatUrl(filePath)}`);
39
+ }
40
+ catch (error) {
41
+ throw new Error(`Error saving peer ID: ${error}`);
42
+ }
43
+ }
44
+
45
+ async function getPeerId (ngId) {
46
+ const { confName } = getWgConfInformation(ngId);
47
+ const filePath = getPeerIdPath(confName);
48
+ if (existsSync(filePath)) {
49
+ Logger.debug(`Reading peer ID from ${Formatter.formatUrl(filePath)}`);
50
+ return (await fs.readFile(filePath, { encoding: 'utf-8' })).trim();
51
+ }
52
+ else {
53
+ Logger.debug(`No file found at ${Formatter.formatUrl(filePath)}`);
54
+ return null;
55
+ }
56
+ }
57
+
58
+ async function deletePeerIdFile (ngId) {
59
+ const { confName } = getWgConfInformation(ngId);
60
+ const filePath = getPeerIdPath(confName);
61
+ // We need `force: true` to avoid errors if file doesn't exist
62
+ await fs.rm(filePath, { force: true });
63
+ Logger.info(`Deleted peer ID from ${Formatter.formatUrl(filePath)}`);
64
+ }
65
+
66
+ async function getInterfaceName (confName) {
67
+ // This file is created by WireGuard®, hence the file path (`/var/run/…`)
68
+ // TODO: Handle Windows (not yet supported by `wg-quick` anyway)
69
+ const interfaceNameFile = path.join('/var', 'run', 'wireguard', `${confName}.name`);
70
+
71
+ Logger.debug(`Reading WireGuard® interface name in ${Formatter.formatUrl(interfaceNameFile)}…`);
72
+ const interfaceName = (await fs.readFile(interfaceNameFile, { encoding: 'utf-8' })).trim();
73
+ Logger.debug(`Found WireGuard® interface name ${Formatter.formatString(interfaceName)} for ${Formatter.formatString(confName)}`);
74
+ return interfaceName;
75
+ }
76
+
77
+ function confWithoutPlaceholders (conf, { privateKey }) {
78
+ conf = conf.replace('<%PrivateKey%>', privateKey);
79
+
80
+ // TODO: This just removes leading and trailing new lines in the configuration file
81
+ // It should be better formatted on the API's side
82
+ conf = conf.trim();
83
+
84
+ return conf;
85
+ }
86
+
87
+ module.exports = {
88
+ createWgConfFolderIfNeeded,
89
+ getWgConfInformation,
90
+ storePeerId,
91
+ getPeerId,
92
+ deletePeerIdFile,
93
+ getInterfaceName,
94
+ confWithoutPlaceholders,
95
+ };
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+
3
+ const { execSync, spawnSync } = require('child_process');
4
+ const Logger = require('../logger.js');
5
+
6
+ function privateKey () {
7
+ return execSync('wg genkey', { encoding: 'utf-8' }).trim();
8
+ }
9
+
10
+ function publicKey (privateKey) {
11
+ return execSync(`echo '${privateKey}' | wg pubkey`, { encoding: 'utf-8' }).trim();
12
+ }
13
+
14
+ function up (confPath) {
15
+ // We must use `spawn` with `detached: true` instead of `exec`
16
+ // because `wg-quick up` starts a `wireguard-go` used by `wg-quick down`
17
+ const { stdout, stderr } = spawnSync('wg-quick', ['up', confPath], { detached: true, encoding: 'utf-8' });
18
+ if (stdout.length > 0) {
19
+ Logger.debug(stdout.trim());
20
+ }
21
+ if (stderr.length > 0) {
22
+ Logger.debug(stderr.trim());
23
+ }
24
+ Logger.println('Activated WireGuard® tunnel');
25
+ }
26
+
27
+ function update (confPath, interfaceName) {
28
+ try {
29
+ // Update WireGuard® configuration
30
+ execSync(`wg-quick strip ${confPath} | wg syncconf ${interfaceName} /dev/stdin`);
31
+ Logger.info('Updated WireGuard® tunnel configuration');
32
+ }
33
+ catch (error) {
34
+ throw new Error(`Error updating WireGuard® tunnel configuration: ${error}`);
35
+ }
36
+ }
37
+
38
+ function down (confPath) {
39
+ const { stdout, stderr } = spawnSync('wg-quick', ['down', confPath], { encoding: 'utf-8' });
40
+ if (stdout.length > 0) {
41
+ Logger.debug(stdout.trim());
42
+ }
43
+ if (stderr.length > 0) {
44
+ Logger.debug(stderr.trim());
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Check that `wg` and `wg-quick` are installed
50
+ */
51
+ function checkAvailable () {
52
+ try {
53
+ // The redirect to `/dev/null` ensures that your program does not produce the output of these commands.
54
+ execSync('which wg > /dev/null 2>&1');
55
+ execSync('which wg-quick > /dev/null 2>&1');
56
+
57
+ // TODO: Handle Windows
58
+ // - Those checks won't work on Windows, and wg-quick doesn't exist anyway.
59
+ // - We need to wait for a Windows version of wg-quick to support the rest of the operations
60
+ // - Or we could use vanilla wg on Windows, and wg-quick on other OSs
61
+ return true;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+
68
+ module.exports = {
69
+ privateKey,
70
+ publicKey,
71
+ up,
72
+ update,
73
+ down,
74
+ checkAvailable,
75
+ };