diff --git a/src/lib/Server.js b/src/lib/Server.js index 80e24501..08f54f91 100644 --- a/src/lib/Server.js +++ b/src/lib/Server.js @@ -102,6 +102,11 @@ module.exports = class Server { const { name, number } = req.body; return WireGuard.createClient({ name, number }); })) + .post('/api/wireguard/client/:clientId/update', Util.promisify(async req => { + const { clientId } = req.params; + const { name, number } = req.body; + return WireGuard.updateClient({ clientId, name, number }); + })) .delete('/api/wireguard/client/:clientId', Util.promisify(async req => { const { clientId } = req.params; return WireGuard.deleteClient({ clientId }); diff --git a/src/lib/WireGuard.js b/src/lib/WireGuard.js index 76c262d6..4b325bfe 100644 --- a/src/lib/WireGuard.js +++ b/src/lib/WireGuard.js @@ -65,6 +65,39 @@ module.exports = class WireGuard { return this.__configPromise; } + __composeIP(clientId, number, config) { + let address; + if (!number) { + // Calculate next unused IP + for (let i = 2; i < 255; i++) { + const client = Object.values(config.clients).find((client, j) => { + return client.address === WG_DEFAULT_ADDRESS.replace('x', i.toString()) && Object.keys(config.clients)[j] !== clientId; + }); + + if (!client) { + address = WG_DEFAULT_ADDRESS.replace('x', i.toString()); + break; + } + } + + if (!address) { + throw new Error('Maximum number of clients reached.'); + } + } else { + // Search selected number for IP + const client = Object.values(config.clients).find((client, j) => { + return client.address === WG_DEFAULT_ADDRESS.replace('x', number.toString()) && Object.keys(config.clients)[j] !== clientId; + }); + + if (client) { + throw new Error('Number in use, please select another or leave empty.'); + } + return WG_DEFAULT_ADDRESS.replace('x', number); + } + + return address; + } + async saveConfig() { const config = await this.getConfig(); await this.__saveConfig(config); @@ -202,39 +235,11 @@ Endpoint = ${WG_HOST}:${WG_PORT}`; const publicKey = await Util.exec(`echo ${privateKey} | wg pubkey`); const preSharedKey = await Util.exec('wg genpsk'); - // IP address operations - let address; - if (!number) { - // Calculate next IP - for (let i = 2; i < 255; i++) { - const client = Object.values(config.clients).find(client => { - return client.address === WG_DEFAULT_ADDRESS.replace('x', i.toString()); - }); - - if (!client) { - address = WG_DEFAULT_ADDRESS.replace('x', i.toString()); - break; - } - } - - if (!address) { - throw new Error('Maximum number of clients reached.'); - } - } else { - // Search & use selected number for IP - const client = Object.values(config.clients).find(client => { - return client.address === WG_DEFAULT_ADDRESS.replace('x', number); - }); - - if (client) { - throw new Error('Number in use, please select another or leave empty.'); - } - address = WG_DEFAULT_ADDRESS.replace('x', number); - } // Create Client const clientId = uuid.v4(); - const client = { + const address = this.__composeIP(clientId, number, config); + config.clients[clientId] = { name, address, privateKey, @@ -247,7 +252,22 @@ Endpoint = ${WG_HOST}:${WG_PORT}`; enabled: true, }; - config.clients[clientId] = client; + await this.saveConfig(); + } + + async updateClient({ clientId, name, number }) { + if (!name) { + throw new Error('Missing: Name'); + } + + const config = await this.getConfig(); + const client = config.clients[clientId]; + if (!client) { + throw new ServerError(`Client Not Found: ${clientId}`, 404); + } + client.name = name; + client.address = this.__composeIP(clientId, number, config); + client.updatedAt = new Date(); await this.saveConfig(); } diff --git a/src/www/index.html b/src/www/index.html index aefa91d7..4cd60cff 100644 --- a/src/www/index.html +++ b/src/www/index.html @@ -128,6 +128,16 @@ + + + + + + + @@ -261,6 +271,87 @@ + + + + + + + + + + + + + + + + + + + + + + Edit Client + + + + + + + + + + + + + + + + + Save + + + Save + + + Cancel + + + + + + diff --git a/src/www/js/api.js b/src/www/js/api.js index 85a614ae..d6d3939c 100644 --- a/src/www/js/api.js +++ b/src/www/js/api.js @@ -73,6 +73,14 @@ class API { }); } + async updateClient({ clientId, name, number }) { + return this.call({ + method: 'post', + path: `/wireguard/client/${clientId}/update`, + body: { name, number }, + }); + } + async deleteClient({ clientId }) { return this.call({ method: 'delete', diff --git a/src/www/js/app.js b/src/www/js/app.js index 96976951..3909d75e 100644 --- a/src/www/js/app.js +++ b/src/www/js/app.js @@ -15,6 +15,9 @@ new Vue({ clients: null, clientDelete: null, + clientEdit: null, + clientEditName: '', + clientEditNumber: '', clientCreate: null, clientCreateName: '', clientCreateNumber: '', @@ -94,6 +97,15 @@ new Vue({ .catch(err => alert(err.message || err.toString())) .finally(() => this.refresh().catch(console.error)); }, + updateClient(client) { + const name = this.clientEditName; + const number = this.clientEditNumber; + if (!name) return; + + this.api.updateClient({ clientId: client.id, name, number }) + .catch(err => alert(err.message || err.toString())) + .finally(() => this.refresh().catch(console.error)); + }, deleteClient(client) { this.api.deleteClient({ clientId: client.id }) .catch(err => alert(err.message || err.toString()))
+ +