mirror of
https://github.com/h44z/wg-portal.git
synced 2025-08-09 23:12:24 +00:00
peer config and qr code
This commit is contained in:
parent
4a53a5207a
commit
9fdb8d8633
@ -36,7 +36,7 @@ const title = computed(() => {
|
||||
watch(() => props.visible, async (newValue, oldValue) => {
|
||||
if (oldValue === false && newValue === true) { // if modal is shown
|
||||
console.log(selectedInterface.value)
|
||||
await interfaces.InterfaceConfig(selectedInterface.value.Identifier)
|
||||
await interfaces.LoadInterfaceConfig(selectedInterface.value.Identifier)
|
||||
configString.value = interfaces.configuration
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import Vue3TagsInput from "vue3-tags-input";
|
||||
import { validateCIDR, validateIP, validateDomain } from '@/helpers/validators';
|
||||
import isCidr from "is-cidr";
|
||||
import {isIP} from 'is-ip';
|
||||
import { freshPeer } from '@/helpers/models';
|
||||
import { freshPeer, freshInterface } from '@/helpers/models';
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -31,10 +31,7 @@ const selectedInterface = computed(() => {
|
||||
let i = interfaces.GetSelected;
|
||||
|
||||
if (!i) {
|
||||
i = { // dummy interface to avoid 'undefined' exceptions
|
||||
Identifier: "none",
|
||||
Mode: "server"
|
||||
}
|
||||
i = freshInterface() // dummy interface to avoid 'undefined' exceptions
|
||||
}
|
||||
|
||||
return i
|
||||
|
@ -2,7 +2,12 @@
|
||||
import Modal from "./Modal.vue";
|
||||
import {peerStore} from "@/stores/peers";
|
||||
import {interfaceStore} from "@/stores/interfaces";
|
||||
import {computed} from "vue";
|
||||
import {computed, ref, watch} from "vue";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import { freshInterface, freshPeer } from '@/helpers/models';
|
||||
import Prism from "vue-prism-component";
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const peers = peerStore()
|
||||
const interfaces = interfaceStore()
|
||||
@ -18,30 +23,49 @@ function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const configString = ref("")
|
||||
|
||||
const selectedPeer = computed(() => {
|
||||
return peers.Find(props.peerId)
|
||||
let p = peers.Find(props.peerId)
|
||||
|
||||
if (!p) {
|
||||
p = freshPeer() // dummy peer to avoid 'undefined' exceptions
|
||||
}
|
||||
|
||||
return p
|
||||
})
|
||||
|
||||
const selectedInterface = computed(() => {
|
||||
let i = interfaces.GetSelected;
|
||||
|
||||
if (!i) {
|
||||
i = { // dummy interface to avoid 'undefined' exceptions
|
||||
Identifier: "none",
|
||||
Mode: "server"
|
||||
}
|
||||
i = freshInterface() // dummy interface to avoid 'undefined' exceptions
|
||||
}
|
||||
|
||||
return i
|
||||
})
|
||||
|
||||
const title = computed(() => {
|
||||
if (selectedPeer.value) {
|
||||
return "Peer: " + selectedPeer.value.Name
|
||||
if (!props.visible) {
|
||||
return "" // otherwise interfaces.GetSelected will die...
|
||||
}
|
||||
if (selectedInterface.value.Mode === "server") {
|
||||
return t("interfaces.peer.view") + ": " + selectedPeer.value.DisplayName
|
||||
} else {
|
||||
return t("interfaces.endpoint.view") + ": " + selectedPeer.value.DisplayName
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
watch(() => props.visible, async (newValue, oldValue) => {
|
||||
if (oldValue === false && newValue === true) { // if modal is shown
|
||||
console.log(selectedInterface.value)
|
||||
console.log(selectedPeer.value)
|
||||
await peers.LoadPeerConfig(selectedPeer.value.Identifier)
|
||||
configString.value = peers.configuration
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -60,16 +84,16 @@ const title = computed(() => {
|
||||
<div class="col-md-8">
|
||||
<h4>Details</h4>
|
||||
<ul>
|
||||
<li>Firstname: Some</li>
|
||||
<li>Lastname: Username</li>
|
||||
<li>Phone: 123456789</li>
|
||||
<li>Mail: x@y.de</li>
|
||||
<li>Identifier: {{ selectedPeer.PublicKey }}</li>
|
||||
<li>IP Addresses: <span v-for="ip in selectedPeer.Addresses" :key="ip" class="badge rounded-pill bg-light">{{ ip }}</span></li>
|
||||
<li>Linked User: {{ selectedPeer.UserIdentifier }}</li>
|
||||
<li>Notes: {{ selectedPeer.Notes }}</li>
|
||||
</ul>
|
||||
<h4>Traffic</h4>
|
||||
<p><i class="fas fa-long-arrow-alt-down"></i> 1.5 MB / <i class="fas fa-long-arrow-alt-up"></i> 3.9 MB</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<img class="config-qr-img" src="https://hexdocs.pm/qr_code/docs/qrcode.svg">
|
||||
<img class="config-qr-img" :src="peers.ConfigQrUrl(props.peerId)" loading="lazy" alt="Configuration QR Code">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -83,28 +107,7 @@ const title = computed(() => {
|
||||
</h2>
|
||||
<div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#accordionExample" style="">
|
||||
<div class="accordion-body">
|
||||
<pre>
|
||||
# AUTOGENERATED FILE - PROVIDED BY WIREGUARD PORTAL
|
||||
# WireGuard configuration: Some username (Home)
|
||||
# -WGP- PublicKey: xyz123
|
||||
|
||||
[Interface]
|
||||
|
||||
# Core settings
|
||||
PrivateKey = abcd2131234
|
||||
Address = 10.6.6.3/32, fd9f:6666::3/128
|
||||
|
||||
# Misc. settings (optional)
|
||||
DNS = 10.10.1.20, fd9f:6666::10:6:6:1
|
||||
MTU = 1380
|
||||
|
||||
[Peer]
|
||||
PublicKey = oidjsfgsp9oih23
|
||||
Endpoint = vpn.server.de:51820
|
||||
AllowedIPs = 10.6.6.0/24, 10.10.0.0/16, 10.12.0.0/16, fd9f:6666::/64
|
||||
PresharedKey = +1FPHPdsfjkln23
|
||||
PersistentKeepalive = 16
|
||||
</pre>
|
||||
<Prism language="ini" :code="configString"></Prism>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
7
frontend/src/helpers/encoding.js
Normal file
7
frontend/src/helpers/encoding.js
Normal file
@ -0,0 +1,7 @@
|
||||
export function base64_url_encode(input) {
|
||||
let output = btoa(input)
|
||||
output = output.replace('+', '.')
|
||||
output = output.replace('/', '_')
|
||||
output = output.replace('=', '-')
|
||||
return output
|
||||
}
|
@ -2,6 +2,7 @@ import { authStore } from '../stores/auth';
|
||||
import { securityStore } from '../stores/security';
|
||||
|
||||
export const fetchWrapper = {
|
||||
url: apiUrl(),
|
||||
get: request('GET'),
|
||||
post: request('POST'),
|
||||
put: request('PUT'),
|
||||
@ -9,6 +10,7 @@ export const fetchWrapper = {
|
||||
};
|
||||
|
||||
export const apiWrapper = {
|
||||
url: apiUrl(),
|
||||
get: apiRequest('GET'),
|
||||
post: apiRequest('POST'),
|
||||
put: apiRequest('PUT'),
|
||||
@ -46,6 +48,13 @@ function apiRequest(method) {
|
||||
}
|
||||
}
|
||||
|
||||
// apiUrl uses WGPORTAL_BACKEND_BASE_URL as base URL
|
||||
function apiUrl() {
|
||||
return (path) => {
|
||||
return WGPORTAL_BACKEND_BASE_URL + path
|
||||
}
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
function getHeaders(method, url) {
|
||||
|
@ -54,9 +54,8 @@
|
||||
"h2-client": "Aktuelle Endpunkte",
|
||||
"tableHeadings": [
|
||||
"Name",
|
||||
"Kennung",
|
||||
"Benutzer",
|
||||
"IPs",
|
||||
"IP's",
|
||||
"Endpunkt",
|
||||
"Handschlag"
|
||||
],
|
||||
|
@ -51,9 +51,8 @@
|
||||
"h2-client": "Current Endpoints",
|
||||
"tableHeadings": [
|
||||
"Name",
|
||||
"Identifier",
|
||||
"User",
|
||||
"IPs",
|
||||
"IP's",
|
||||
"Endpoint",
|
||||
"Handshake"
|
||||
],
|
||||
|
@ -3,6 +3,7 @@ import { defineStore } from 'pinia'
|
||||
import {apiWrapper} from '@/helpers/fetch-wrapper'
|
||||
import {notify} from "@kyvg/vue3-notification";
|
||||
import { freshInterface } from '@/helpers/models';
|
||||
import { base64_url_encode } from '@/helpers/encoding';
|
||||
|
||||
const baseUrl = `/interface`
|
||||
|
||||
@ -66,21 +67,21 @@ export const interfaceStore = defineStore({
|
||||
})
|
||||
})
|
||||
},
|
||||
async InterfaceConfig(id) {
|
||||
return apiWrapper.get(`${baseUrl}/config/${encodeURIComponent(id)}`)
|
||||
.then(this.setInterfaceConfig)
|
||||
.catch(error => {
|
||||
this.prepared = {}
|
||||
console.log("Failed to load interface configuration: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load interface configuration!",
|
||||
})
|
||||
async LoadInterfaceConfig(id) {
|
||||
return apiWrapper.get(`${baseUrl}/config/${base64_url_encode(id)}`)
|
||||
.then(this.setInterfaceConfig)
|
||||
.catch(error => {
|
||||
this.configuration = ""
|
||||
console.log("Failed to load interface configuration: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load interface configuration!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async DeleteInterface(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.delete(`${baseUrl}/${encodeURIComponent(id)}`)
|
||||
return apiWrapper.delete(`${baseUrl}/${base64_url_encode(id)}`)
|
||||
.then(() => {
|
||||
this.interfaces = this.interfaces.filter(i => i.Identifier !== id)
|
||||
if (this.interfaces.length > 0) {
|
||||
@ -98,7 +99,7 @@ export const interfaceStore = defineStore({
|
||||
},
|
||||
async UpdateInterface(id, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.put(`${baseUrl}/${encodeURIComponent(id)}`, formData)
|
||||
return apiWrapper.put(`${baseUrl}/${base64_url_encode(id)}`, formData)
|
||||
.then(iface => {
|
||||
let idx = this.interfaces.findIndex((i) => i.Identifier === id)
|
||||
this.interfaces[idx] = iface
|
||||
|
@ -3,6 +3,7 @@ import {apiWrapper} from "../helpers/fetch-wrapper";
|
||||
import {notify} from "@kyvg/vue3-notification";
|
||||
import {interfaceStore} from "./interfaces";
|
||||
import { freshPeer } from '@/helpers/models';
|
||||
import { base64_url_encode } from '@/helpers/encoding';
|
||||
|
||||
const baseUrl = `/peer`
|
||||
|
||||
@ -12,6 +13,7 @@ export const peerStore = defineStore({
|
||||
peers: [],
|
||||
peer: freshPeer(),
|
||||
prepared: freshPeer(),
|
||||
configuration: "",
|
||||
filter: "",
|
||||
pageSize: 10,
|
||||
pageOffset: 0,
|
||||
@ -37,6 +39,9 @@ export const peerStore = defineStore({
|
||||
FilteredAndPaged: (state) => {
|
||||
return state.Filtered.slice(state.pageOffset, state.pageOffset + state.pageSize)
|
||||
},
|
||||
ConfigQrUrl: (state) => {
|
||||
return (id) => apiWrapper.url(`${baseUrl}/config-qr/${base64_url_encode(id)}`)
|
||||
},
|
||||
isFetching: (state) => state.fetching,
|
||||
hasNextPage: (state) => state.pageOffset < (state.FilteredCount - state.pageSize),
|
||||
hasPrevPage: (state) => state.pageOffset > 0,
|
||||
@ -82,8 +87,11 @@ export const peerStore = defineStore({
|
||||
setPreparedPeer(peer) {
|
||||
this.prepared = peer;
|
||||
},
|
||||
setPeerConfig(config) {
|
||||
this.configuration = config;
|
||||
},
|
||||
async PreparePeer(interfaceId) {
|
||||
return apiWrapper.get(`${baseUrl}/iface/${encodeURIComponent(interfaceId)}/prepare`)
|
||||
return apiWrapper.get(`${baseUrl}/iface/${base64_url_encode(interfaceId)}/prepare`)
|
||||
.then(this.setPreparedPeer)
|
||||
.catch(error => {
|
||||
this.prepared = freshPeer()
|
||||
@ -94,9 +102,21 @@ export const peerStore = defineStore({
|
||||
})
|
||||
})
|
||||
},
|
||||
async LoadPeerConfig(id) {
|
||||
return apiWrapper.get(`${baseUrl}/config/${base64_url_encode(id)}`)
|
||||
.then(this.setPeerConfig)
|
||||
.catch(error => {
|
||||
this.configuration = ""
|
||||
console.log("Failed to load peer configuration: ", error)
|
||||
notify({
|
||||
title: "Backend Connection Failure",
|
||||
text: "Failed to load peer configuration!",
|
||||
})
|
||||
})
|
||||
},
|
||||
async LoadPeer(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.get(`${baseUrl}/${encodeURIComponent(id)}`)
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(id)}`)
|
||||
.then(this.setPeer)
|
||||
.catch(error => {
|
||||
this.setPeers([])
|
||||
@ -109,7 +129,7 @@ export const peerStore = defineStore({
|
||||
},
|
||||
async DeletePeer(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.delete(`${baseUrl}/${encodeURIComponent(id)}`)
|
||||
return apiWrapper.delete(`${baseUrl}/${base64_url_encode(id)}`)
|
||||
.then(() => {
|
||||
this.peers = this.peers.filter(p => p.Identifier !== id)
|
||||
this.fetching = false
|
||||
@ -122,7 +142,7 @@ export const peerStore = defineStore({
|
||||
},
|
||||
async UpdatePeer(id, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.put(`${baseUrl}/${encodeURIComponent(id)}`, formData)
|
||||
return apiWrapper.put(`${baseUrl}/${base64_url_encode(id)}`, formData)
|
||||
.then(peer => {
|
||||
let idx = this.peers.findIndex((p) => p.Identifier === id)
|
||||
this.peers[idx] = peer
|
||||
@ -136,7 +156,7 @@ export const peerStore = defineStore({
|
||||
},
|
||||
async CreatePeer(interfaceId, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.post(`${baseUrl}/iface/${encodeURIComponent(interfaceId)}/new`, formData)
|
||||
return apiWrapper.post(`${baseUrl}/iface/${base64_url_encode(interfaceId)}/new`, formData)
|
||||
.then(peer => {
|
||||
this.peers.push(peer)
|
||||
this.fetching = false
|
||||
@ -157,7 +177,7 @@ export const peerStore = defineStore({
|
||||
}
|
||||
this.fetching = true
|
||||
|
||||
return apiWrapper.get(`${baseUrl}/iface/${encodeURIComponent(interfaceId)}/all`)
|
||||
return apiWrapper.get(`${baseUrl}/iface/${base64_url_encode(interfaceId)}/all`)
|
||||
.then(this.setPeers)
|
||||
.catch(error => {
|
||||
this.setPeers([])
|
||||
|
@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
|
||||
import {apiWrapper} from "@/helpers/fetch-wrapper";
|
||||
import {notify} from "@kyvg/vue3-notification";
|
||||
import {authStore} from "@/stores/auth";
|
||||
import { base64_url_encode } from '@/helpers/encoding';
|
||||
|
||||
|
||||
const baseUrl = `/user`
|
||||
@ -79,7 +80,7 @@ export const profileStore = defineStore({
|
||||
async LoadPeers() {
|
||||
this.fetching = true
|
||||
let currentUser = authStore().user.Identifier
|
||||
return apiWrapper.get(`${baseUrl}/${encodeURIComponent(currentUser)}/peers`)
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(currentUser)}/peers`)
|
||||
.then(this.setPeers)
|
||||
.catch(error => {
|
||||
this.setPeers([])
|
||||
@ -93,7 +94,7 @@ export const profileStore = defineStore({
|
||||
async LoadUser() {
|
||||
this.fetching = true
|
||||
let currentUser = authStore().user.Identifier
|
||||
return apiWrapper.get(`${baseUrl}/${encodeURIComponent(currentUser)}`)
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(currentUser)}`)
|
||||
.then(this.setUser)
|
||||
.catch(error => {
|
||||
this.setUser({})
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import {apiWrapper} from "@/helpers/fetch-wrapper";
|
||||
import {notify} from "@kyvg/vue3-notification";
|
||||
import { base64_url_encode } from '@/helpers/encoding';
|
||||
|
||||
const baseUrl = `/user`
|
||||
|
||||
@ -91,7 +92,7 @@ export const userStore = defineStore({
|
||||
},
|
||||
async DeleteUser(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.delete(`${baseUrl}/${encodeURIComponent(id)}`)
|
||||
return apiWrapper.delete(`${baseUrl}/${base64_url_encode(id)}`)
|
||||
.then(() => {
|
||||
this.users = this.users.filter(u => u.Identifier !== id)
|
||||
this.fetching = false
|
||||
@ -104,7 +105,7 @@ export const userStore = defineStore({
|
||||
},
|
||||
async UpdateUser(id, formData) {
|
||||
this.fetching = true
|
||||
return apiWrapper.put(`${baseUrl}/${encodeURIComponent(id)}`, formData)
|
||||
return apiWrapper.put(`${baseUrl}/${base64_url_encode(id)}`, formData)
|
||||
.then(user => {
|
||||
let idx = this.users.findIndex((u) => u.Identifier === id)
|
||||
this.users[idx] = user
|
||||
@ -131,7 +132,7 @@ export const userStore = defineStore({
|
||||
},
|
||||
async LoadUserPeers(id) {
|
||||
this.fetching = true
|
||||
return apiWrapper.get(`${baseUrl}/${encodeURIComponent(id)}/peers`)
|
||||
return apiWrapper.get(`${baseUrl}/${base64_url_encode(id)}/peers`)
|
||||
.then(this.setUserPeers)
|
||||
.catch(error => {
|
||||
this.setUserPeers([])
|
||||
|
@ -272,9 +272,8 @@ onMounted(async () => {
|
||||
<th scope="col">{{ $t('interfaces.tableHeadings[0]') }}</th>
|
||||
<th scope="col">{{ $t('interfaces.tableHeadings[1]') }}</th>
|
||||
<th scope="col">{{ $t('interfaces.tableHeadings[2]') }}</th>
|
||||
<th scope="col">{{ $t('interfaces.tableHeadings[3]') }}</th>
|
||||
<th v-if="interfaces.GetSelected.Mode==='client'" scope="col">{{ $t('interfaces.tableHeadings[4]') }}</th>
|
||||
<th scope="col">{{ $t('interfaces.tableHeadings[5]') }}</th>
|
||||
<th v-if="interfaces.GetSelected.Mode==='client'" scope="col">{{ $t('interfaces.tableHeadings[3]') }}</th>
|
||||
<th scope="col">{{ $t('interfaces.tableHeadings[4]') }}</th>
|
||||
<th scope="col"></th><!-- Actions -->
|
||||
</tr>
|
||||
</thead>
|
||||
@ -284,10 +283,9 @@ onMounted(async () => {
|
||||
<input id="flexCheckDefault" class="form-check-input" type="checkbox" value="">
|
||||
</th>
|
||||
<td>{{peer.DisplayName}}</td>
|
||||
<td>{{peer.Identifier}}</td>
|
||||
<td>{{peer.UserIdentifier}}</td>
|
||||
<td>
|
||||
<span v-for="ip in peer.Addresses" :key="ip" class="badge rounded-pill bg-light">{{ ip }}</span>
|
||||
<span v-for="ip in peer.Addresses" :key="ip" class="badge bg-light me-1">{{ ip }}</span>
|
||||
</td>
|
||||
<td v-if="interfaces.GetSelected.Mode==='client'">{{peer.Endpoint.Value}}</td>
|
||||
<td>{{peer.LastConnected}}</td>
|
||||
|
7
go.mod
7
go.mod
@ -19,6 +19,8 @@ require (
|
||||
github.com/vardius/message-bus v1.1.5
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
github.com/xhit/go-simple-mail/v2 v2.13.0
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.2.1
|
||||
golang.org/x/crypto v0.10.0
|
||||
golang.org/x/oauth2 v0.9.0
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
@ -37,6 +39,7 @@ require (
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dchest/uniuri v1.2.0 // indirect
|
||||
github.com/fogleman/gg v1.3.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
@ -53,6 +56,7 @@ require (
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
@ -79,6 +83,7 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
@ -86,7 +91,9 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/yeqown/reedsolomon v1.0.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
|
||||
golang.org/x/net v0.11.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.9.0 // indirect
|
||||
|
14
go.sum
14
go.sum
@ -28,6 +28,8 @@ github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g=
|
||||
github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
@ -89,6 +91,8 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@ -190,6 +194,8 @@ github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZ
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus-community/pro-bing v0.2.0 h1:hyK7yPFndU3LCDwEQJwPQUCjNkp1DGP/VxyzrWfXZUU=
|
||||
@ -242,6 +248,12 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/xhit/go-simple-mail/v2 v2.13.0 h1:OANWU9jHZrVfBkNkvLf8Ww0fexwpQVF/v/5f96fFTLI=
|
||||
github.com/xhit/go-simple-mail/v2 v2.13.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1 h1:Jc1Q916fwC05R8C7mpWDbrT9tyLPaLLKDABoC5XBCe8=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1/go.mod h1:2Qsk2APUCPne0TsRo40DIkI5MYnbzYKCnKGEFWrxd24=
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.2.1 h1:FMRZiur5yApUIe4fqtqmcdl/XQTZAZWt2DhkPx4VIW0=
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.2.1/go.mod h1:ZelyDFiVymrauRjUn454iF7bjsabmB1vixkDA5kq2bw=
|
||||
github.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr4B0=
|
||||
github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
@ -255,6 +267,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
|
||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
|
@ -380,7 +380,7 @@ func (r *SqlRepo) GetPeer(ctx context.Context, id domain.PeerIdentifier) (*domai
|
||||
func (r *SqlRepo) GetInterfacePeers(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.Peer, error) {
|
||||
var peers []domain.Peer
|
||||
|
||||
err := r.db.WithContext(ctx).Where("interface_identifier = ?", id).Find(&peers).Error
|
||||
err := r.db.WithContext(ctx).Preload("Addresses").Where("interface_identifier = ?", id).Find(&peers).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -11,7 +11,7 @@
|
||||
let WGPORTAL_BACKEND_BASE_URL="http://localhost:5000/api/v0";
|
||||
</script>
|
||||
<script src="/api/v0/config/frontend.js"></script>
|
||||
<script type="module" crossorigin src="/app/assets/index-2321088d.js"></script>
|
||||
<script type="module" crossorigin src="/app/assets/index-8f53e6dd.js"></script>
|
||||
<link rel="stylesheet" href="/app/assets/index-a233ff7e.css">
|
||||
</head>
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
|
15
internal/app/api/v0/handlers/encoding.go
Normal file
15
internal/app/api/v0/handlers/encoding.go
Normal file
@ -0,0 +1,15 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Base64UrlDecode(in string) string {
|
||||
in = strings.ReplaceAll(in, "-", "=")
|
||||
in = strings.ReplaceAll(in, "_", "/")
|
||||
in = strings.ReplaceAll(in, ".", "+")
|
||||
|
||||
output, _ := base64.StdEncoding.DecodeString(in)
|
||||
return string(output)
|
||||
}
|
@ -90,7 +90,7 @@ func (e interfaceEndpoint) handleAllGet() gin.HandlerFunc {
|
||||
// @Router /interface/get/{id} [get]
|
||||
func (e interfaceEndpoint) handleSingleGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: "missing id parameter",
|
||||
@ -122,7 +122,7 @@ func (e interfaceEndpoint) handleSingleGet() gin.HandlerFunc {
|
||||
// @Router /interface/config/{id} [get]
|
||||
func (e interfaceEndpoint) handleConfigGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: "missing id parameter",
|
||||
@ -166,7 +166,7 @@ func (e interfaceEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
return
|
||||
@ -241,7 +241,9 @@ func (e interfaceEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
// @Router /interface/peers/{id} [get]
|
||||
func (e interfaceEndpoint) handlePeersGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: "missing id parameter",
|
||||
@ -249,7 +251,7 @@ func (e interfaceEndpoint) handlePeersGet() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
_, peers, err := e.app.GetInterfaceAndPeers(c.Request.Context(), domain.InterfaceIdentifier(id))
|
||||
_, peers, err := e.app.GetInterfaceAndPeers(ctx, domain.InterfaceIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: err.Error(),
|
||||
@ -276,7 +278,7 @@ func (e interfaceEndpoint) handleDelete() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
return
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/h44z/wg-portal/internal/app"
|
||||
"github.com/h44z/wg-portal/internal/app/api/v0/model"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@ -23,6 +24,8 @@ func (e peerEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenti
|
||||
apiGroup.GET("/iface/:iface/all", e.handleAllGet())
|
||||
apiGroup.GET("/iface/:iface/prepare", e.handlePrepareGet())
|
||||
apiGroup.POST("/iface/:iface/new", e.handleCreatePost())
|
||||
apiGroup.GET("/config-qr/:id", e.handleQrCodeGet())
|
||||
apiGroup.GET("/config/:id", e.handleConfigGet())
|
||||
apiGroup.GET("/:id", e.handleSingleGet())
|
||||
apiGroup.PUT("/:id", e.handleUpdatePut())
|
||||
apiGroup.DELETE("/:id", e.handleDelete())
|
||||
@ -43,7 +46,7 @@ func (e peerEndpoint) handleAllGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
interfaceId := c.Param("iface")
|
||||
interfaceId := Base64UrlDecode(c.Param("iface"))
|
||||
if interfaceId == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing iface parameter"})
|
||||
return
|
||||
@ -74,7 +77,7 @@ func (e peerEndpoint) handleSingleGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
peerId := c.Param("id")
|
||||
peerId := Base64UrlDecode(c.Param("id"))
|
||||
if peerId == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing id parameter"})
|
||||
return
|
||||
@ -105,7 +108,7 @@ func (e peerEndpoint) handlePrepareGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
interfaceId := c.Param("iface")
|
||||
interfaceId := Base64UrlDecode(c.Param("iface"))
|
||||
if interfaceId == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing iface parameter"})
|
||||
return
|
||||
@ -137,7 +140,7 @@ func (e peerEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
interfaceId := c.Param("iface")
|
||||
interfaceId := Base64UrlDecode(c.Param("iface"))
|
||||
if interfaceId == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing iface parameter"})
|
||||
return
|
||||
@ -176,12 +179,12 @@ func (e peerEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
// @Success 200 {object} model.Peer
|
||||
// @Failure 400 {object} model.Error
|
||||
// @Failure 500 {object} model.Error
|
||||
// @Router /peer/{id} [post]
|
||||
// @Router /peer/{id} [put]
|
||||
func (e peerEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
peerId := c.Param("id")
|
||||
peerId := Base64UrlDecode(c.Param("id"))
|
||||
if peerId == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing id parameter"})
|
||||
return
|
||||
@ -224,7 +227,7 @@ func (e peerEndpoint) handleDelete() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
return
|
||||
@ -239,3 +242,83 @@ func (e peerEndpoint) handleDelete() gin.HandlerFunc {
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// handleConfigGet returns a gorm handler function.
|
||||
//
|
||||
// @ID peers_handleConfigGet
|
||||
// @Tags Peer
|
||||
// @Summary Get peer configuration as string.
|
||||
// @Produce json
|
||||
// @Success 200 {object} string
|
||||
// @Failure 400 {object} model.Error
|
||||
// @Failure 500 {object} model.Error
|
||||
// @Router /peer/config/{id} [get]
|
||||
func (e peerEndpoint) handleConfigGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: "missing id parameter",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
config, err := e.app.GetPeerConfig(c.Request.Context(), domain.PeerIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
configString, err := io.ReadAll(config)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, string(configString))
|
||||
}
|
||||
}
|
||||
|
||||
// handleQrCodeGet returns a gorm handler function.
|
||||
//
|
||||
// @ID peers_handleQrCodeGet
|
||||
// @Tags Peer
|
||||
// @Summary Get peer configuration as qr code.
|
||||
// @Produce json
|
||||
// @Success 200 {object} string
|
||||
// @Failure 400 {object} model.Error
|
||||
// @Failure 500 {object} model.Error
|
||||
// @Router /peer/config-qr/{id} [get]
|
||||
func (e peerEndpoint) handleQrCodeGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: "missing id parameter",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
config, err := e.app.GetPeerConfigQrCode(c.Request.Context(), domain.PeerIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
configData, err := io.ReadAll(config)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error{
|
||||
Code: http.StatusInternalServerError, Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, "image/png", configData)
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,9 @@ func (e userEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenti
|
||||
// @Router /user/all [get]
|
||||
func (e userEndpoint) handleAllGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
users, err := e.app.GetAllUsers(c.Request.Context())
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
users, err := e.app.GetAllUsers(ctx)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
|
||||
return
|
||||
@ -63,7 +65,7 @@ func (e userEndpoint) handleSingleGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
return
|
||||
@ -95,7 +97,7 @@ func (e userEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
return
|
||||
@ -166,13 +168,15 @@ func (e userEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
// @Router /user/{id}/peers [get]
|
||||
func (e userEndpoint) handlePeersGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
interfaceId := c.Param("id")
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
interfaceId := Base64UrlDecode(c.Param("id"))
|
||||
if interfaceId == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusInternalServerError, Message: "missing id parameter"})
|
||||
return
|
||||
}
|
||||
|
||||
peers, err := e.app.GetUserPeers(c.Request.Context(), domain.UserIdentifier(interfaceId))
|
||||
peers, err := e.app.GetUserPeers(ctx, domain.UserIdentifier(interfaceId))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
|
||||
return
|
||||
@ -197,7 +201,7 @@ func (e userEndpoint) handleDelete() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
id := Base64UrlDecode(c.Param("id"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
return
|
||||
|
@ -1,10 +1,13 @@
|
||||
package filetemplate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/h44z/wg-portal/internal/config"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
"github.com/yeqown/go-qrcode/v2"
|
||||
"github.com/yeqown/go-qrcode/writer/standard"
|
||||
"io"
|
||||
)
|
||||
|
||||
@ -50,3 +53,41 @@ func (m Manager) GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (i
|
||||
|
||||
return m.tplHandler.GetPeerConfig(peer)
|
||||
}
|
||||
|
||||
func (m Manager) GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error) {
|
||||
peer, err := m.wg.GetPeer(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch peer %s: %w", id, err)
|
||||
}
|
||||
|
||||
cfgData, err := m.tplHandler.GetPeerConfig(peer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get peer config for %s: %w", id, err)
|
||||
}
|
||||
|
||||
configBytes, err := io.ReadAll(cfgData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read peer config for %s: %w", id, err)
|
||||
}
|
||||
|
||||
code, err := qrcode.New(string(configBytes))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initializeqr code for %s: %w", id, err)
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
wr := nopCloser{Writer: buf}
|
||||
qrWriter := standard.NewWithWriter(wr, standard.WithQRWidth(40), standard.WithBuiltinImageEncoder(standard.PNG_FORMAT))
|
||||
err = code.Save(qrWriter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write code for %s: %w", id, err)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
type nopCloser struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
|
@ -1,11 +1,16 @@
|
||||
# AUTOGENERATED FILE - DO NOT EDIT
|
||||
# This file uses wg-quick format. See https://man7.org/linux/man-pages/man8/wg-quick.8.html#CONFIGURATION
|
||||
# This file uses wg-quick format.
|
||||
# See https://man7.org/linux/man-pages/man8/wg-quick.8.html#CONFIGURATION
|
||||
# Lines starting with the -WGP- tag are used by
|
||||
# the WireGuard Portal configuration parser.
|
||||
|
||||
# -WGP- WIREGUARD PORTAL CONFIGURATION FILE, version {{ .Portal.Version }}
|
||||
# Lines starting with the -WGP- tag are used by the WireGuard Portal configuration parser.
|
||||
# -WGP- WIREGUARD PORTAL CONFIGURATION FILE
|
||||
# -WGP- version {{ .Portal.Version }}
|
||||
|
||||
[Interface]
|
||||
# -WGP- Interface: {{ .Interface.Identifier }} | Updated: {{ .Interface.UpdatedAt }} | Created: {{ .Interface.CreatedAt }}
|
||||
# -WGP- Interface: {{ .Interface.Identifier }}
|
||||
# -WGP- Created: {{ .Interface.CreatedAt }}
|
||||
# -WGP- Updated: {{ .Interface.UpdatedAt }}
|
||||
# -WGP- Display name: {{ .Interface.DisplayName }}
|
||||
# -WGP- Interface mode: {{ .Interface.Type }}
|
||||
# -WGP- PublicKey = {{ .Interface.KeyPair.PublicKey }}
|
||||
@ -53,30 +58,32 @@ PostDown = {{ .Interface.PostDown }}
|
||||
#
|
||||
|
||||
{{range .Peers}}
|
||||
{{- if not .DisabledAt}}
|
||||
{{- if not .IsDisabled}}
|
||||
[Peer]
|
||||
# -WGP- Peer: {{.Uid}} | Updated: {{.UpdatedAt}} | Created: {{.CreatedAt}}
|
||||
# -WGP- Display name: {{ .Identifier }}
|
||||
{{- if .KeyPair.PrivateKey}}
|
||||
# -WGP- PrivateKey: {{.KeyPair.PrivateKey}}
|
||||
# -WGP- Peer: {{.Identifier}}
|
||||
# -WGP- Created: {{.CreatedAt}}
|
||||
# -WGP- Updated: {{.UpdatedAt}}
|
||||
# -WGP- Display name: {{ .DisplayName }}
|
||||
{{- if .Interface.KeyPair.PrivateKey}}
|
||||
# -WGP- PrivateKey: {{.Interface.KeyPair.PrivateKey}}
|
||||
{{- end}}
|
||||
PublicKey = {{ .KeyPair.PublicKey }}
|
||||
PublicKey = {{ .Interface.KeyPair.PublicKey }}
|
||||
{{- if .PresharedKey}}
|
||||
PresharedKey = {{ .PresharedKey }}
|
||||
{{- end}}
|
||||
{{- if eq $.Interface.Type "server"}}
|
||||
AllowedIPs = {{ .AddressStr }}{{if ne .ExtraAllowedIPsStr ""}}, {{ .ExtraAllowedIPsStr }}{{end}}
|
||||
AllowedIPs = {{ CidrsToString .Interface.Addresses }}{{if ne .ExtraAllowedIPsStr ""}}, {{ .ExtraAllowedIPsStr }}{{end}}
|
||||
{{- end}}
|
||||
{{- if eq $.Interface.Type "client"}}
|
||||
{{- if .AllowedIPsStr}}
|
||||
AllowedIPs = {{ .AllowedIPsStr }}
|
||||
{{- if .AllowedIPsStr.GetValue}}
|
||||
AllowedIPs = {{ .AllowedIPsStr.GetValue }}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
{{- if and (ne .Endpoint "") (eq $.Interface.Type "client")}}
|
||||
Endpoint = {{ .Endpoint }}
|
||||
{{- if and (ne .Endpoint.GetValue "") (eq $.Interface.Type "client")}}
|
||||
Endpoint = {{ .Endpoint.GetValue }}
|
||||
{{- end}}
|
||||
{{- if ne .PersistentKeepalive 0}}
|
||||
PersistentKeepalive = {{ .PersistentKeepalive }}
|
||||
{{- if and (ne .PersistentKeepalive.GetValue 0) (eq $.Interface.Type "client")}}
|
||||
PersistentKeepalive = {{ .PersistentKeepalive.GetValue }}
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
{{end}}
|
@ -1,22 +1,27 @@
|
||||
# AUTOGENERATED FILE - DO NOT EDIT
|
||||
# This file uses wg-quick format. See https://man7.org/linux/man-pages/man8/wg-quick.8.html#CONFIGURATION
|
||||
# This file uses wg-quick format.
|
||||
# See https://man7.org/linux/man-pages/man8/wg-quick.8.html#CONFIGURATION
|
||||
# Lines starting with the -WGP- tag are used by
|
||||
# the WireGuard Portal configuration parser.
|
||||
|
||||
# -WGP- WIREGUARD PORTAL CONFIGURATION FILE, version {{ .Portal.Version }}
|
||||
# Lines starting with the -WGP- tag are used by the WireGuard Portal configuration parser.
|
||||
# -WGP- WIREGUARD PORTAL CONFIGURATION FILE
|
||||
# -WGP- version {{ .Portal.Version }}
|
||||
|
||||
[Interface]
|
||||
# -WGP- Peer: {{.Peer.Identifier}} | Updated: {{.Peer.UpdatedAt}} | Created: {{.Peer.CreatedAt}}
|
||||
# -WGP- Peer: {{.Peer.Identifier}}
|
||||
# -WGP- Created: {{.Peer.CreatedAt}}
|
||||
# -WGP- Updated: {{.Peer.UpdatedAt}}
|
||||
# -WGP- Display name: {{ .Peer.DisplayName }}
|
||||
# -WGP- PublicKey: {{ .Peer.KeyPair.PublicKey }}
|
||||
# -WGP- PublicKey: {{ .Peer.Interface.KeyPair.PublicKey }}
|
||||
{{- if eq .Peer.Interface.Type "server"}}
|
||||
# -WGP- Peer type: client
|
||||
{{else}}
|
||||
# -WGP- Peer type: server
|
||||
{{else}}
|
||||
# -WGP- Peer type: client
|
||||
{{- end}}
|
||||
|
||||
# Core settings
|
||||
PrivateKey = {{ .Peer.KeyPair.PrivateKey }}
|
||||
Address = {{ .Peer.Interface.AddressStr }}
|
||||
PrivateKey = {{ .Peer.Interface.KeyPair.PrivateKey }}
|
||||
Address = {{ CidrsToString .Peer.Interface.Addresses }}
|
||||
|
||||
# Misc. settings (optional)
|
||||
{{- if .Peer.Interface.DnsStr.GetValue}}
|
||||
@ -47,7 +52,7 @@ PostDown = {{ .Peer.Interface.PostDown.GetValue }}
|
||||
{{- end}}
|
||||
|
||||
[Peer]
|
||||
PublicKey = {{ .Peer.Interface.PublicKey }}
|
||||
PublicKey = {{ .Peer.EndpointPublicKey.GetValue }}
|
||||
Endpoint = {{ .Peer.Endpoint.GetValue }}
|
||||
{{- if .Peer.AllowedIPsStr.GetValue}}
|
||||
AllowedIPs = {{ .Peer.AllowedIPsStr.GetValue }}
|
||||
@ -55,6 +60,6 @@ AllowedIPs = {{ .Peer.AllowedIPsStr.GetValue }}
|
||||
{{- if .Peer.PresharedKey}}
|
||||
PresharedKey = {{ .Peer.PresharedKey }}
|
||||
{{- end}}
|
||||
{{- if ne .Peer.PersistentKeepalive.GetValue 0}}
|
||||
{{- if and (ne .Peer.PersistentKeepalive.GetValue 0) (eq .Peer.Interface.Type "client")}}
|
||||
PersistentKeepalive = {{ .Peer.PersistentKeepalive.GetValue }}
|
||||
{{- end}}
|
@ -51,4 +51,5 @@ type StatisticsCollector interface {
|
||||
type TemplateManager interface {
|
||||
GetInterfaceConfig(ctx context.Context, id domain.InterfaceIdentifier) (io.Reader, error)
|
||||
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user