Browse Source

add china developer proxy

add IpSegmentSelect function, support select ip-segment behind al gateway
pull/936/head^2
ub 2 years ago
committed by lblk
parent
commit
7212bf1fb8
  1. 4
      Dockerfile
  2. 9
      src/lib/Server.js
  3. 45
      src/lib/WireGuard.js
  4. 51
      src/www/css/app.css
  5. 1
      src/www/css/vue-multiselect.min.css
  6. 9
      src/www/index.html
  7. 8
      src/www/js/api.js
  8. 21
      src/www/js/app.js
  9. 1
      src/www/js/vendor/vue-multiselect.min.js
  10. 19
      src/www/src/css/app.css

4
Dockerfile

@ -6,6 +6,7 @@ FROM docker.io/library/node:18-alpine AS build_node_modules
# Copy Web UI # Copy Web UI
COPY src/ /app/ COPY src/ /app/
WORKDIR /app WORKDIR /app
RUN npm ci --omit=dev &&\ RUN npm ci --omit=dev &&\
mv node_modules /node_modules mv node_modules /node_modules
@ -23,6 +24,9 @@ COPY --from=build_node_modules /app /app
# than what runs inside of docker. # than what runs inside of docker.
COPY --from=build_node_modules /node_modules /node_modules COPY --from=build_node_modules /node_modules /node_modules
# only for china developer
RUN npm config set registry https://registry.npmmirror.com && sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN \ RUN \
# Enable this to run `npm run serve` # Enable this to run `npm run serve`
npm i -g nodemon &&\ npm i -g nodemon &&\

9
src/lib/Server.js

@ -210,6 +210,15 @@ module.exports = class Server {
const { address } = await readBody(event); const { address } = await readBody(event);
await WireGuard.updateClientAddress({ clientId, address }); await WireGuard.updateClientAddress({ clientId, address });
return { success: true }; return { success: true };
}))
.put('/api/wireguard/client/:clientId/ipSegSelected', defineEventHandler(async (event) => {
const clientId = getRouterParam(event, 'clientId');
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
throw createError({ status: 403 });
}
const { ipSegSelected } = await readBody(event);
await WireGuard.updateClientIpSegSelected({ clientId, ipSegSelected });
return { success: true };
})); }));
// Static assets // Static assets

45
src/lib/WireGuard.js

@ -105,13 +105,19 @@ PostDown = ${WG_POST_DOWN}
for (const [clientId, client] of Object.entries(config.clients)) { for (const [clientId, client] of Object.entries(config.clients)) {
if (!client.enabled) continue; if (!client.enabled) continue;
let allowedIPs = `${client.address}/32`;
if (client.ipSegSelected && client.ipSegSelected.length > 0) {
const joinedIpSeg = client.ipSegSelected.join(',');
allowedIPs += `,${joinedIpSeg}`;
}
debug(`allowedIPs:${allowedIPs}`);
result += ` result += `
# Client: ${client.name} (${clientId}) # Client: ${client.name} (${clientId})
[Peer] [Peer]
PublicKey = ${client.publicKey} PublicKey = ${client.publicKey}
${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : '' ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
}AllowedIPs = ${client.address}/32`; }AllowedIPs = ${allowedIPs}`;
} }
debug('Config saving...'); debug('Config saving...');
@ -137,6 +143,8 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
name: client.name, name: client.name,
enabled: client.enabled, enabled: client.enabled,
address: client.address, address: client.address,
ipSegSelected: client.ipSegSelected,
ipSegSelecting: client.ipSegSelecting,
publicKey: client.publicKey, publicKey: client.publicKey,
createdAt: new Date(client.createdAt), createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt), updatedAt: new Date(client.updatedAt),
@ -192,9 +200,25 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
return client; return client;
} }
async getAllCIDR() {
const config = await this.getConfig();
const clients = await this.getClients();
let allCIDRs = [];
clients.forEach(client => {
allCIDRs = allCIDRs.concat(client.ipSegSelected);
});
return allCIDRs.join(',');
}
async getClientConfiguration({ clientId }) { async getClientConfiguration({ clientId }) {
const config = await this.getConfig(); const config = await this.getConfig();
const client = await this.getClient({ clientId }); const client = await this.getClient({ clientId });
let allowedIPs = await this.getAllCIDR();
if (allowedIPs === "") {
allowedIPs = WG_ALLOWED_IPS;
} else {
allowedIPs = `${WG_ALLOWED_IPS},${allowedIPs}`;
}
return ` return `
[Interface] [Interface]
@ -206,7 +230,7 @@ ${WG_MTU ? `MTU = ${WG_MTU}\n` : ''}\
[Peer] [Peer]
PublicKey = ${config.server.publicKey} PublicKey = ${config.server.publicKey}
${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : '' ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
}AllowedIPs = ${WG_ALLOWED_IPS} }AllowedIPs = ${allowedIPs}
PersistentKeepalive = ${WG_PERSISTENT_KEEPALIVE} PersistentKeepalive = ${WG_PERSISTENT_KEEPALIVE}
Endpoint = ${WG_HOST}:${WG_PORT}`; Endpoint = ${WG_HOST}:${WG_PORT}`;
} }
@ -253,6 +277,8 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
id, id,
name, name,
address, address,
ipSegSelected: [],
ipSegSelecting: [],
privateKey, privateKey,
publicKey, publicKey,
preSharedKey, preSharedKey,
@ -319,6 +345,21 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
await this.saveConfig(); await this.saveConfig();
} }
async updateClientIpSegSelected({ clientId, ipSegSelected }) {
const client = await this.getClient({ clientId });
const ipSegRegex = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/;
for (const address of ipSegSelected) {
if (!ipSegRegex.test(address)) {
throw new ServerError(`Invalid Address: ${address}`, 400);
}
}
client.ipSegSelected = ipSegSelected;
client.updatedAt = new Date();
await this.saveConfig();
}
// Shutdown wireguard // Shutdown wireguard
async Shutdown() { async Shutdown() {
await Util.exec('wg-quick down wg0').catch(() => { }); await Util.exec('wg-quick down wg0').catch(() => { });

51
src/www/css/app.css

@ -590,6 +590,53 @@ video {
} }
} }
.multiselect--active:where(.dark, .dark *),
.multiselect__content-wrapper:where(.dark, .dark *),
.multiselect:where(.dark, .dark *),
.multiselect__input:where(.dark, .dark *),
.multiselect__tags:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(82 82 82 / var(--tw-bg-opacity));
--tw-text-opacity: 1;
color: rgb(212 212 212 / var(--tw-text-opacity));
}
.multiselect--active:where(.dark, .dark *)::-moz-placeholder, .multiselect__content-wrapper:where(.dark, .dark *)::-moz-placeholder, .multiselect:where(.dark, .dark *)::-moz-placeholder, .multiselect__input:where(.dark, .dark *)::-moz-placeholder, .multiselect__tags:where(.dark, .dark *)::-moz-placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.multiselect--active:where(.dark, .dark *)::placeholder,
.multiselect__content-wrapper:where(.dark, .dark *)::placeholder,
.multiselect:where(.dark, .dark *)::placeholder,
.multiselect__input:where(.dark, .dark *)::placeholder,
.multiselect__tags:where(.dark, .dark *)::placeholder {
--tw-text-opacity: 1;
color: rgb(163 163 163 / var(--tw-text-opacity));
}
.multiselect__tag {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
}
.multiselect__tag-icon {
--tw-bg-opacity: 1;
background-color: rgb(153 27 27 / var(--tw-bg-opacity));
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.multiselect__tag-icon:hover {
--tw-bg-opacity: 1;
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
}
.multiselect__content-wrapper,
.multiselect__content {
display: none;
}
.sr-only { .sr-only {
position: absolute; position: absolute;
width: 1px; width: 1px;
@ -622,6 +669,10 @@ video {
position: relative; position: relative;
} }
.sticky {
position: sticky;
}
.inset-0 { .inset-0 {
inset: 0px; inset: 0px;
} }

1
src/www/css/vue-multiselect.min.css

File diff suppressed because one or more lines are too long

9
src/www/index.html

@ -4,6 +4,7 @@
<head> <head>
<title>WireGuard</title> <title>WireGuard</title>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<link href="./css/vue-multiselect.min.css" rel="stylesheet">
<link href="./css/app.css" rel="stylesheet"> <link href="./css/app.css" rel="stylesheet">
<link rel="manifest" href="./manifest.json"> <link rel="manifest" href="./manifest.json">
<link rel="icon" type="image/png" href="./img/favicon.png"> <link rel="icon" type="image/png" href="./img/favicon.png">
@ -284,8 +285,13 @@
<div class="rounded-full w-4 h-4 m-1 bg-white"></div> <div class="rounded-full w-4 h-4 m-1 bg-white"></div>
</div> </div>
<!-- Show QR--> <!-- Show Route Ip Segment -->
<multiselect v-model="client.ipSegSelected" :options="client.ipSegSelecting" :multiple="true" placeholder="input router"
id="ip-segment-selector" :taggable="true" @tag="addNewTag(client, $event)" @select="handleSelect(client, $event)"
@remove="handleRemove(client, $event)">
</multiselect>
<!-- Show QR-->
<button :disabled="!client.downloadableConfig" <button :disabled="!client.downloadableConfig"
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition" class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition"
:class="{ :class="{
@ -586,6 +592,7 @@
<script src="./js/vendor/vue-i18n.min.js"></script> <script src="./js/vendor/vue-i18n.min.js"></script>
<script src="./js/vendor/apexcharts.min.js"></script> <script src="./js/vendor/apexcharts.min.js"></script>
<script src="./js/vendor/vue-apexcharts.min.js"></script> <script src="./js/vendor/vue-apexcharts.min.js"></script>
<script src="./js/vendor/vue-multiselect.min.js"></script>
<script src="./js/vendor/sha256.min.js"></script> <script src="./js/vendor/sha256.min.js"></script>
<script src="./js/vendor/timeago.full.min.js"></script> <script src="./js/vendor/timeago.full.min.js"></script>
<script src="./js/api.js"></script> <script src="./js/api.js"></script>

8
src/www/js/api.js

@ -138,4 +138,12 @@ class API {
}); });
} }
async updateClientIpSegSelected({ clientId, ipSegSelected }) {
return this.call({
method: 'put',
path: `/wireguard/client/${clientId}/ipSegSelected/`,
body: { ipSegSelected },
});
}
} }

21
src/www/js/app.js

@ -46,6 +46,7 @@ new Vue({
el: '#app', el: '#app',
components: { components: {
apexchart: VueApexCharts, apexchart: VueApexCharts,
Multiselect: window.VueMultiselect.default,
}, },
i18n, i18n,
data: { data: {
@ -230,6 +231,21 @@ new Vue({
return client; return client;
}); });
}, },
addNewTag(client, newTag) {
const tag = newTag;
client.ipSegSelected.push(tag);
client.ipSegSelecting.push(tag);
const list = client.ipSegSelected;
this.updateClientIpSegSelected(client, list);
},
handleSelect(client, event) {
const list = client.ipSegSelected;
this.updateClientIpSegSelected(client, list);
},
handleRemove(client, event) {
const list = client.ipSegSelected;
this.updateClientIpSegSelected(client, list);
},
login(e) { login(e) {
e.preventDefault(); e.preventDefault();
@ -299,6 +315,11 @@ new Vue({
.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));
}, },
updateClientIpSegSelected(client, ipSegSelected) {
this.api.updateClientIpSegSelected({ clientId: client.id, ipSegSelected })
.catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error));
},
toggleTheme() { toggleTheme() {
const themes = ['light', 'dark', 'auto']; const themes = ['light', 'dark', 'auto'];
const currentIndex = themes.indexOf(this.uiTheme); const currentIndex = themes.indexOf(this.uiTheme);

1
src/www/js/vendor/vue-multiselect.min.js

File diff suppressed because one or more lines are too long

19
src/www/src/css/app.css

@ -1,3 +1,22 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@layer components {
.multiselect--active,
.multiselect__content-wrapper,
.multiselect,
.multiselect__input,
.multiselect__tags {
@apply dark:bg-neutral-600 dark:text-neutral-300 dark:placeholder:text-neutral-400;
}
.multiselect__tag {
@apply bg-red-800;
}
.multiselect__tag-icon {
@apply bg-red-800 hover:bg-red-700;
}
.multiselect__content-wrapper,
.multiselect__content {
@apply hidden;
}
}
Loading…
Cancel
Save