Browse Source

Merge pull request #7 from peterlewis/development

Merge development into production
pull/549/head
Peter Lewis 2 years ago
committed by GitHub
parent
commit
6d0949a2cd
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      .github/workflows/deploy-nightly.yml
  2. 12
      .github/workflows/deploy.yml
  3. 8
      .github/workflows/lint.yml
  4. 4
      Dockerfile
  5. 4
      package-lock.json
  6. 20
      src/lib/Server.js
  7. 4
      src/lib/Util.js
  8. 8
      src/lib/WireGuard.js
  9. 5615
      src/package-lock.json
  10. 14
      src/package.json
  11. 2
      src/server.js
  12. 10
      src/www/index.html
  13. 2
      src/www/js/api.js
  14. 32
      src/www/js/app.js

12
.github/workflows/deploy-nightly.yml

@ -10,18 +10,18 @@ jobs:
name: Build & Deploy name: Build & Deploy
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
ref: production ref: production
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
@ -31,8 +31,8 @@ jobs:
# Build & Publish # Build & Publish
- name: Build & Publish Docker Image - name: Build & Publish Docker Image
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
push: true push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: weejewel/wg-easy:nightly, weejewel/wg-easy:${{ env.RELEASE }}-nightly tags: peterlewis/wg-easy:nightly, peterlewis/wg-easy:${{ env.RELEASE }}-nightly

12
.github/workflows/deploy.yml

@ -12,18 +12,18 @@ jobs:
name: Build & Deploy name: Build & Deploy
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
ref: production ref: production
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
@ -33,8 +33,8 @@ jobs:
# Build & Publish # Build & Publish
- name: Build & Publish Docker Image - name: Build & Publish Docker Image
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
push: true push: true
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8 platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
tags: weejewel/wg-easy:latest, weejewel/wg-easy:${{ env.RELEASE }} tags: peterlewis/wg-easy:latest, peterlewis/wg-easy:${{ env.RELEASE }}

8
.github/workflows/lint.yml

@ -3,7 +3,7 @@ name: Lint
on: on:
push: push:
branches: branches:
- master - development
- production - production
pull_request: pull_request:
@ -12,10 +12,10 @@ jobs:
name: Lint name: Lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-node@v1 - uses: actions/setup-node@v3
with: with:
node-version: '14' node-version: '18'
- run: | - run: |
cd src cd src

4
Dockerfile

@ -9,7 +9,7 @@
# # # #
# # # #
FROM docker.io/library/node:14-alpine@sha256:dc92f36e7cd917816fa2df041d4e9081453366381a00f40398d99e9392e78664 AS build_node_modules FROM docker.io/library/node:18-alpine@sha256:fda98168118e5a8f4269efca4101ee51dd5c75c0fe56d8eb6fad80455c2f5827 AS build_node_modules
# Copy Web UI # Copy Web UI
COPY src/ /app/ COPY src/ /app/
@ -18,7 +18,7 @@ RUN npm ci --production
# Copy build result to a new image. # Copy build result to a new image.
# This saves a lot of disk space. # This saves a lot of disk space.
FROM docker.io/library/node:14-alpine@sha256:dc92f36e7cd917816fa2df041d4e9081453366381a00f40398d99e9392e78664 FROM docker.io/library/node:18-alpine@sha256:fda98168118e5a8f4269efca4101ee51dd5c75c0fe56d8eb6fad80455c2f5827
COPY --from=build_node_modules /app /app COPY --from=build_node_modules /app /app
# Move node_modules one directory up, so during development # Move node_modules one directory up, so during development

4
package-lock.json

@ -1,4 +0,0 @@
{
"version": "1.0.0",
"lockfileVersion": 1
}

20
src/lib/Server.js

@ -35,7 +35,7 @@ module.exports = class Server {
}))) })))
// Authentication // Authentication
.get('/api/session', Util.promisify(async req => { .get('/api/session', Util.promisify(async (req) => {
const requiresPassword = !!process.env.PASSWORD; const requiresPassword = !!process.env.PASSWORD;
const authenticated = requiresPassword const authenticated = requiresPassword
? !!(req.session && req.session.authenticated) ? !!(req.session && req.session.authenticated)
@ -46,7 +46,7 @@ module.exports = class Server {
authenticated, authenticated,
}; };
})) }))
.post('/api/session', Util.promisify(async req => { .post('/api/session', Util.promisify(async (req) => {
const { const {
password, password,
} = req.body; } = req.body;
@ -79,14 +79,14 @@ module.exports = class Server {
error: 'Not Logged In', error: 'Not Logged In',
}); });
}) })
.delete('/api/session', Util.promisify(async req => { .delete('/api/session', Util.promisify(async (req) => {
const sessionId = req.session.id; const sessionId = req.session.id;
req.session.destroy(); req.session.destroy();
debug(`Deleted Session: ${sessionId}`); debug(`Deleted Session: ${sessionId}`);
})) }))
.get('/api/wireguard/client', Util.promisify(async req => { .get('/api/wireguard/client', Util.promisify(async (req) => {
return WireGuard.getClients(); return WireGuard.getClients();
})) }))
.get('/api/wireguard/client/:clientId/qrcode.svg', Util.promisify(async (req, res) => { .get('/api/wireguard/client/:clientId/qrcode.svg', Util.promisify(async (req, res) => {
@ -108,28 +108,28 @@ module.exports = class Server {
res.header('Content-Type', 'text/plain'); res.header('Content-Type', 'text/plain');
res.send(config); res.send(config);
})) }))
.post('/api/wireguard/client', Util.promisify(async req => { .post('/api/wireguard/client', Util.promisify(async (req) => {
const { name } = req.body; const { name } = req.body;
return WireGuard.createClient({ name }); return WireGuard.createClient({ name });
})) }))
.delete('/api/wireguard/client/:clientId', Util.promisify(async req => { .delete('/api/wireguard/client/:clientId', Util.promisify(async (req) => {
const { clientId } = req.params; const { clientId } = req.params;
return WireGuard.deleteClient({ clientId }); return WireGuard.deleteClient({ clientId });
})) }))
.post('/api/wireguard/client/:clientId/enable', Util.promisify(async req => { .post('/api/wireguard/client/:clientId/enable', Util.promisify(async (req) => {
const { clientId } = req.params; const { clientId } = req.params;
return WireGuard.enableClient({ clientId }); return WireGuard.enableClient({ clientId });
})) }))
.post('/api/wireguard/client/:clientId/disable', Util.promisify(async req => { .post('/api/wireguard/client/:clientId/disable', Util.promisify(async (req) => {
const { clientId } = req.params; const { clientId } = req.params;
return WireGuard.disableClient({ clientId }); return WireGuard.disableClient({ clientId });
})) }))
.put('/api/wireguard/client/:clientId/name', Util.promisify(async req => { .put('/api/wireguard/client/:clientId/name', Util.promisify(async (req) => {
const { clientId } = req.params; const { clientId } = req.params;
const { name } = req.body; const { name } = req.body;
return WireGuard.updateClientName({ clientId, name }); return WireGuard.updateClientName({ clientId, name });
})) }))
.put('/api/wireguard/client/:clientId/address', Util.promisify(async req => { .put('/api/wireguard/client/:clientId/address', Util.promisify(async (req) => {
const { clientId } = req.params; const { clientId } = req.params;
const { address } = req.body; const { address } = req.body;
return WireGuard.updateClientAddress({ clientId, address }); return WireGuard.updateClientAddress({ clientId, address });

4
src/lib/Util.js

@ -21,7 +21,7 @@ module.exports = class Util {
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
return function(req, res) { return function(req, res) {
Promise.resolve().then(async () => fn(req, res)) Promise.resolve().then(async () => fn(req, res))
.then(result => { .then((result) => {
if (res.headersSent) return; if (res.headersSent) return;
if (typeof result === 'undefined') { if (typeof result === 'undefined') {
@ -34,7 +34,7 @@ module.exports = class Util {
.status(200) .status(200)
.json(result); .json(result);
}) })
.catch(error => { .catch((error) => {
if (typeof error === 'string') { if (typeof error === 'string') {
error = new Error(error); error = new Error(error);
} }

8
src/lib/WireGuard.js

@ -60,7 +60,7 @@ module.exports = class WireGuard {
await this.__saveConfig(config); await this.__saveConfig(config);
await Util.exec('wg-quick down wg0').catch(() => { }); await Util.exec('wg-quick down wg0').catch(() => { });
await Util.exec('wg-quick up wg0').catch(err => { await Util.exec('wg-quick up wg0').catch((err) => {
if (err && err.message && err.message.includes('Cannot find device "wg0"')) { if (err && err.message && err.message.includes('Cannot find device "wg0"')) {
throw new Error('WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!'); throw new Error('WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!');
} }
@ -156,7 +156,7 @@ AllowedIPs = ${client.address}/32`;
.trim() .trim()
.split('\n') .split('\n')
.slice(1) .slice(1)
.forEach(line => { .forEach((line) => {
const [ const [
publicKey, publicKey,
preSharedKey, // eslint-disable-line no-unused-vars preSharedKey, // eslint-disable-line no-unused-vars
@ -168,7 +168,7 @@ AllowedIPs = ${client.address}/32`;
persistentKeepalive, persistentKeepalive,
] = line.split('\t'); ] = line.split('\t');
const client = clients.find(client => client.publicKey === publicKey); const client = clients.find((client) => client.publicKey === publicKey);
if (!client) return; if (!client) return;
client.latestHandshakeAt = latestHandshakeAt === '0' client.latestHandshakeAt = latestHandshakeAt === '0'
@ -233,7 +233,7 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
// Calculate next IP // Calculate next IP
let address; let address;
for (let i = 2; i < 255; i++) { for (let i = 2; i < 255; i++) {
const client = Object.values(config.clients).find(client => { const client = Object.values(config.clients).find((client) => {
return client.address === WG_DEFAULT_ADDRESS.replace('x', i); return client.address === WG_DEFAULT_ADDRESS.replace('x', i);
}); });

5615
src/package-lock.json

File diff suppressed because it is too large

14
src/package.json

@ -12,15 +12,15 @@
"author": "Emile Nijssen", "author": "Emile Nijssen",
"license": "GPL", "license": "GPL",
"dependencies": { "dependencies": {
"debug": "^4.3.1", "debug": "^4.3.4",
"express": "^4.17.1", "express": "^4.18.1",
"express-session": "^1.17.1", "express-session": "^1.17.3",
"qrcode": "^1.4.4", "qrcode": "^1.5.1",
"uuid": "^8.3.2" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^7.27.0", "eslint": "^8.24.0",
"eslint-config-athom": "^2.1.0" "eslint-config-athom": "^3.1.1"
}, },
"nodemonConfig": { "nodemonConfig": {
"ignore": [ "ignore": [

2
src/server.js

@ -5,7 +5,7 @@ require('./services/Server');
const WireGuard = require('./services/WireGuard'); const WireGuard = require('./services/WireGuard');
WireGuard.getConfig() WireGuard.getConfig()
.catch(err => { .catch((err) => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(err); console.error(err);

10
src/www/index.html

@ -121,7 +121,7 @@
</div> </div>
</div> </div>
<div class="relative p-5 z-10 flex flex-row"> <div class="relative p-5 z-10 flex flex-row flex-wrap justify-start">
<div class="h-10 w-10 mr-5 rounded-full bg-gray-50 relative"> <div class="h-10 w-10 mr-5 rounded-full bg-gray-50 relative">
<svg class="w-6 m-2 text-gray-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" <svg class="w-6 m-2 text-gray-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor"> fill="currentColor">
@ -137,7 +137,7 @@
</div> </div>
</div> </div>
<div class="flex-grow"> <div class="sm:flex-grow">
<!-- Name --> <!-- Name -->
<div class="text-gray-700 group" :title="'Created on ' + dateTime(new Date(client.createdAt))"> <div class="text-gray-700 group" :title="'Created on ' + dateTime(new Date(client.createdAt))">
@ -148,8 +148,8 @@
v-on:keyup.escape="clientEditName = null; clientEditNameId = null;" v-on:keyup.escape="clientEditName = null; clientEditNameId = null;"
:ref="'client-' + client.id + '-name'" :ref="'client-' + client.id + '-name'"
class="rounded px-1 border-2 border-gray-100 focus:border-gray-200 outline-none w-30" /> class="rounded px-1 border-2 border-gray-100 focus:border-gray-200 outline-none w-30" />
<span v-show="clientEditNameId !== client.id" <span v-show="clientEditNameId !== client.id" style="max-width: 28ch;"
class="inline-block border-t-2 border-b-2 border-transparent">{{client.name}}</span> class="inline-block border-t-2 border-b-2 border-transparent align-top sm:w-auto overflow-hidden sm:overflow-visible overflow-ellipsis">{{client.name}}</span>
<!-- Edit --> <!-- Edit -->
<span v-show="clientEditNameId !== client.id" <span v-show="clientEditNameId !== client.id"
@ -230,7 +230,7 @@
</div> </div>
</div> </div>
<div class="text-right"> <div class="text-right w-full sm:w-auto mt-3 sm:mt-0">
<div class="text-gray-400"> <div class="text-gray-400">
<!-- Enable/Disable --> <!-- Enable/Disable -->

2
src/www/js/api.js

@ -62,7 +62,7 @@ class API {
return this.call({ return this.call({
method: 'get', method: 'get',
path: '/wireguard/client', path: '/wireguard/client',
}).then(clients => clients.map(client => ({ }).then((clients) => clients.map((client) => ({
...client, ...client,
createdAt: new Date(client.createdAt), createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt), updatedAt: new Date(client.updatedAt),

32
src/www/js/app.js

@ -112,7 +112,7 @@ new Vue({
}, },
}, },
methods: { methods: {
dateTime: value => { dateTime: (value) => {
return new Intl.DateTimeFormat(undefined, { return new Intl.DateTimeFormat(undefined, {
year: 'numeric', year: 'numeric',
month: 'short', month: 'short',
@ -127,7 +127,7 @@ new Vue({
if (!this.authenticated) return; if (!this.authenticated) return;
const clients = await this.api.getClients(); const clients = await this.api.getClients();
this.clients = clients.map(client => { this.clients = clients.map((client) => {
if (client.name.includes('@') && client.name.includes('.')) { if (client.name.includes('@') && client.name.includes('.')) {
client.avatar = `https://www.gravatar.com/avatar/${md5(client.name)}?d=blank`; client.avatar = `https://www.gravatar.com/avatar/${md5(client.name)}?d=blank`;
} }
@ -186,7 +186,7 @@ new Vue({
this.requiresPassword = session.requiresPassword; this.requiresPassword = session.requiresPassword;
return this.refresh(); return this.refresh();
}) })
.catch(err => { .catch((err) => {
alert(err.message || err.toString()); alert(err.message || err.toString());
}) })
.finally(() => { .finally(() => {
@ -202,7 +202,7 @@ new Vue({
this.authenticated = false; this.authenticated = false;
this.clients = null; this.clients = null;
}) })
.catch(err => { .catch((err) => {
alert(err.message || err.toString()); alert(err.message || err.toString());
}); });
}, },
@ -211,54 +211,54 @@ new Vue({
if (!name) return; if (!name) return;
this.api.createClient({ name }) this.api.createClient({ name })
.catch(err => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error)); .finally(() => this.refresh().catch(console.error));
}, },
deleteClient(client) { deleteClient(client) {
this.api.deleteClient({ clientId: client.id }) this.api.deleteClient({ clientId: client.id })
.catch(err => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error)); .finally(() => this.refresh().catch(console.error));
}, },
enableClient(client) { enableClient(client) {
this.api.enableClient({ clientId: client.id }) this.api.enableClient({ clientId: client.id })
.catch(err => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error)); .finally(() => this.refresh().catch(console.error));
}, },
disableClient(client) { disableClient(client) {
this.api.disableClient({ clientId: client.id }) this.api.disableClient({ clientId: client.id })
.catch(err => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error)); .finally(() => this.refresh().catch(console.error));
}, },
updateClientName(client, name) { updateClientName(client, name) {
this.api.updateClientName({ clientId: client.id, name }) this.api.updateClientName({ clientId: client.id, name })
.catch(err => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error)); .finally(() => this.refresh().catch(console.error));
}, },
updateClientAddress(client, address) { updateClientAddress(client, address) {
this.api.updateClientAddress({ clientId: client.id, address }) this.api.updateClientAddress({ clientId: client.id, address })
.catch(err => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error)); .finally(() => this.refresh().catch(console.error));
}, },
}, },
filters: { filters: {
bytes, bytes,
timeago: value => { timeago: (value) => {
return timeago().format(value); return timeago().format(value);
}, },
}, },
mounted() { mounted() {
this.api = new API(); this.api = new API();
this.api.getSession() this.api.getSession()
.then(session => { .then((session) => {
this.authenticated = session.authenticated; this.authenticated = session.authenticated;
this.requiresPassword = session.requiresPassword; this.requiresPassword = session.requiresPassword;
this.refresh({ this.refresh({
updateCharts: true, updateCharts: true,
}).catch(err => { }).catch((err) => {
alert(err.message || err.toString()); alert(err.message || err.toString());
}); });
}) })
.catch(err => { .catch((err) => {
alert(err.message || err.toString()); alert(err.message || err.toString());
}); });
@ -271,8 +271,8 @@ new Vue({
Promise.resolve().then(async () => { Promise.resolve().then(async () => {
const currentRelease = await this.api.getRelease(); const currentRelease = await this.api.getRelease();
const latestRelease = await fetch('https://weejewel.github.io/wg-easy/changelog.json') const latestRelease = await fetch('https://weejewel.github.io/wg-easy/changelog.json')
.then(res => res.json()) .then((res) => res.json())
.then(releases => { .then((releases) => {
const releasesArray = Object.entries(releases).map(([version, changelog]) => ({ const releasesArray = Object.entries(releases).map(([version, changelog]) => ({
version: parseInt(version, 10), version: parseInt(version, 10),
changelog, changelog,

Loading…
Cancel
Save