mirror of
https://github.com/h44z/wg-portal.git
synced 2025-10-06 08:26:17 +00:00
Compare commits
1 Commits
config_sty
...
improve_we
Author | SHA1 | Date | |
---|---|---|---|
|
9c1e037166 |
@@ -673,19 +673,6 @@ Without a valid `external_url`, the login process may fail due to CSRF protectio
|
|||||||
## Webhook
|
## Webhook
|
||||||
|
|
||||||
The webhook section allows you to configure a webhook that is called on certain events in WireGuard Portal.
|
The webhook section allows you to configure a webhook that is called on certain events in WireGuard Portal.
|
||||||
A JSON object is sent in a POST request to the webhook URL with the following structure:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"event": "update",
|
|
||||||
"entity": "peer",
|
|
||||||
"identifier": "the-peer-identifier",
|
|
||||||
"payload": {
|
|
||||||
// The payload of the event, e.g. peer data.
|
|
||||||
// Check the API documentation for the exact structure.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Further details can be found in the [usage documentation](../usage/webhooks.md).
|
Further details can be found in the [usage documentation](../usage/webhooks.md).
|
||||||
|
|
||||||
### `url`
|
### `url`
|
||||||
|
@@ -38,11 +38,12 @@ WireGuard Portal supports various events that can trigger webhooks. The followin
|
|||||||
- `connect`: Triggered when a user connects to the VPN.
|
- `connect`: Triggered when a user connects to the VPN.
|
||||||
- `disconnect`: Triggered when a user disconnects from the VPN.
|
- `disconnect`: Triggered when a user disconnects from the VPN.
|
||||||
|
|
||||||
The following entity types can trigger webhooks:
|
The following entity models are supported for webhook events:
|
||||||
|
|
||||||
- `user`: When a WireGuard Portal user is created, updated, or deleted.
|
- `user`: WireGuard Portal users support creation, update, or deletion events.
|
||||||
- `peer`: When a peer is created, updated, or deleted. This entity can also trigger `connect` and `disconnect` events.
|
- `peer`: Peers support creation, update, or deletion events. Via the `peer_metric` entity, you can also receive connection status updates.
|
||||||
- `interface`: When a device is created, updated, or deleted.
|
- `peer_metric`: Peer metrics support connection status updates, such as when a peer connects or disconnects.
|
||||||
|
- `interface`: WireGuard interfaces support creation, update, or deletion events.
|
||||||
|
|
||||||
## Payload Structure
|
## Payload Structure
|
||||||
|
|
||||||
@@ -51,36 +52,234 @@ A common shell structure for webhook payloads is as follows:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"event": "create",
|
"event": "create", // The event type, e.g. "create", "update", "delete", "connect", "disconnect"
|
||||||
"entity": "user",
|
"entity": "user", // The entity type, e.g. "user", "peer", "peer_metric", "interface"
|
||||||
"identifier": "the-user-identifier",
|
"identifier": "the-user-identifier", // Unique identifier of the entity, e.g. user ID or peer ID
|
||||||
"payload": {
|
"payload": {
|
||||||
// The payload of the event, e.g. peer data.
|
// The payload of the event, e.g. a Peer model.
|
||||||
// Check the API documentation for the exact structure.
|
// Detailed model descriptions are provided below.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Payload Models
|
||||||
|
|
||||||
### Example Payload
|
All payload models are encoded as JSON objects. Fields with empty values might be omitted in the payload.
|
||||||
|
|
||||||
|
#### User Payload (entity: `user`)
|
||||||
|
|
||||||
|
| JSON Field | Type | Description |
|
||||||
|
|----------------|-------------|-----------------------------------|
|
||||||
|
| CreatedBy | string | Creator identifier |
|
||||||
|
| UpdatedBy | string | Last updater identifier |
|
||||||
|
| CreatedAt | time.Time | Time of creation |
|
||||||
|
| UpdatedAt | time.Time | Time of last update |
|
||||||
|
| Identifier | string | Unique user identifier |
|
||||||
|
| Email | string | User email |
|
||||||
|
| Source | string | Authentication source |
|
||||||
|
| ProviderName | string | Name of auth provider |
|
||||||
|
| IsAdmin | bool | Whether user has admin privileges |
|
||||||
|
| Firstname | string | User's first name (optional) |
|
||||||
|
| Lastname | string | User's last name (optional) |
|
||||||
|
| Phone | string | Contact phone number (optional) |
|
||||||
|
| Department | string | User's department (optional) |
|
||||||
|
| Notes | string | Additional notes (optional) |
|
||||||
|
| Disabled | *time.Time | When user was disabled |
|
||||||
|
| DisabledReason | string | Reason for deactivation |
|
||||||
|
| Locked | *time.Time | When user account was locked |
|
||||||
|
| LockedReason | string | Reason for being locked |
|
||||||
|
|
||||||
|
|
||||||
|
#### Peer Payload (entity: `peer`)
|
||||||
|
|
||||||
|
| JSON Field | Type | Description |
|
||||||
|
|----------------------|------------|----------------------------------------|
|
||||||
|
| CreatedBy | string | Creator identifier |
|
||||||
|
| UpdatedBy | string | Last updater identifier |
|
||||||
|
| CreatedAt | time.Time | Creation timestamp |
|
||||||
|
| UpdatedAt | time.Time | Last update timestamp |
|
||||||
|
| Endpoint | string | Peer endpoint address |
|
||||||
|
| EndpointPublicKey | string | Public key of peer endpoint |
|
||||||
|
| AllowedIPsStr | string | Allowed IPs |
|
||||||
|
| ExtraAllowedIPsStr | string | Extra allowed IPs |
|
||||||
|
| PresharedKey | string | Pre-shared key for encryption |
|
||||||
|
| PersistentKeepalive | int | Keepalive interval in seconds |
|
||||||
|
| DisplayName | string | Display name of the peer |
|
||||||
|
| Identifier | string | Unique identifier |
|
||||||
|
| UserIdentifier | string | Associated user ID (optional) |
|
||||||
|
| InterfaceIdentifier | string | Interface this peer is attached to |
|
||||||
|
| Disabled | *time.Time | When the peer was disabled |
|
||||||
|
| DisabledReason | string | Reason for being disabled |
|
||||||
|
| ExpiresAt | *time.Time | Expiration date |
|
||||||
|
| Notes | string | Notes for this peer |
|
||||||
|
| AutomaticallyCreated | bool | Whether peer was auto-generated |
|
||||||
|
| PrivateKey | string | Peer private key |
|
||||||
|
| PublicKey | string | Peer public key |
|
||||||
|
| InterfaceType | string | Type of the peer interface |
|
||||||
|
| Addresses | []string | IP addresses |
|
||||||
|
| CheckAliveAddress | string | Address used for alive checks |
|
||||||
|
| DnsStr | string | DNS servers |
|
||||||
|
| DnsSearchStr | string | DNS search domains |
|
||||||
|
| Mtu | int | MTU (Maximum Transmission Unit) |
|
||||||
|
| FirewallMark | uint32 | Firewall mark (optional) |
|
||||||
|
| RoutingTable | string | Custom routing table (optional) |
|
||||||
|
| PreUp | string | Command before bringing up interface |
|
||||||
|
| PostUp | string | Command after bringing up interface |
|
||||||
|
| PreDown | string | Command before bringing down interface |
|
||||||
|
| PostDown | string | Command after bringing down interface |
|
||||||
|
|
||||||
|
|
||||||
|
#### Interface Payload (entity: `interface`)
|
||||||
|
|
||||||
|
| JSON Field | Type | Description |
|
||||||
|
|----------------------------|------------|----------------------------------------|
|
||||||
|
| CreatedBy | string | Creator identifier |
|
||||||
|
| UpdatedBy | string | Last updater identifier |
|
||||||
|
| CreatedAt | time.Time | Creation timestamp |
|
||||||
|
| UpdatedAt | time.Time | Last update timestamp |
|
||||||
|
| Identifier | string | Unique identifier |
|
||||||
|
| PrivateKey | string | Private key for the interface |
|
||||||
|
| PublicKey | string | Public key for the interface |
|
||||||
|
| ListenPort | int | Listening port |
|
||||||
|
| Addresses | []string | IP addresses |
|
||||||
|
| DnsStr | string | DNS servers |
|
||||||
|
| DnsSearchStr | string | DNS search domains |
|
||||||
|
| Mtu | int | MTU (Maximum Transmission Unit) |
|
||||||
|
| FirewallMark | uint32 | Firewall mark |
|
||||||
|
| RoutingTable | string | Custom routing table |
|
||||||
|
| PreUp | string | Command before bringing up interface |
|
||||||
|
| PostUp | string | Command after bringing up interface |
|
||||||
|
| PreDown | string | Command before bringing down interface |
|
||||||
|
| PostDown | string | Command after bringing down interface |
|
||||||
|
| SaveConfig | bool | Whether to save config to file |
|
||||||
|
| DisplayName | string | Human-readable name |
|
||||||
|
| Type | string | Type of interface |
|
||||||
|
| DriverType | string | Driver used |
|
||||||
|
| Disabled | *time.Time | When the interface was disabled |
|
||||||
|
| DisabledReason | string | Reason for being disabled |
|
||||||
|
| PeerDefNetworkStr | string | Default peer network configuration |
|
||||||
|
| PeerDefDnsStr | string | Default peer DNS servers |
|
||||||
|
| PeerDefDnsSearchStr | string | Default peer DNS search domains |
|
||||||
|
| PeerDefEndpoint | string | Default peer endpoint |
|
||||||
|
| PeerDefAllowedIPsStr | string | Default peer allowed IPs |
|
||||||
|
| PeerDefMtu | int | Default peer MTU |
|
||||||
|
| PeerDefPersistentKeepalive | int | Default keepalive value |
|
||||||
|
| PeerDefFirewallMark | uint32 | Default firewall mark for peers |
|
||||||
|
| PeerDefRoutingTable | string | Default routing table for peers |
|
||||||
|
| PeerDefPreUp | string | Default peer pre-up command |
|
||||||
|
| PeerDefPostUp | string | Default peer post-up command |
|
||||||
|
| PeerDefPreDown | string | Default peer pre-down command |
|
||||||
|
| PeerDefPostDown | string | Default peer post-down command |
|
||||||
|
|
||||||
|
|
||||||
|
#### Peer Metrics Payload (entity: `peer_metric`)
|
||||||
|
|
||||||
|
| JSON Field | Type | Description |
|
||||||
|
|------------|------------|----------------------------|
|
||||||
|
| Status | PeerStatus | Current status of the peer |
|
||||||
|
| Peer | Peer | Peer data |
|
||||||
|
|
||||||
|
`PeerStatus` sub-structure:
|
||||||
|
|
||||||
|
| JSON Field | Type | Description |
|
||||||
|
|------------------|------------|------------------------------|
|
||||||
|
| UpdatedAt | time.Time | Time of last status update |
|
||||||
|
| IsConnected | bool | Is peer currently connected |
|
||||||
|
| IsPingable | bool | Can peer be pinged |
|
||||||
|
| LastPing | *time.Time | Time of last successful ping |
|
||||||
|
| BytesReceived | uint64 | Bytes received from peer |
|
||||||
|
| BytesTransmitted | uint64 | Bytes sent to peer |
|
||||||
|
| Endpoint | string | Last known endpoint |
|
||||||
|
| LastHandshake | *time.Time | Last successful handshake |
|
||||||
|
| LastSessionStart | *time.Time | Time the last session began |
|
||||||
|
|
||||||
|
|
||||||
|
### Example Payloads
|
||||||
|
|
||||||
The following payload is an example of a webhook event when a peer connects to the VPN:
|
The following payload is an example of a webhook event when a peer connects to the VPN:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"event": "connect",
|
"event": "connect",
|
||||||
"entity": "peer",
|
"entity": "peer_metric",
|
||||||
"identifier": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
"identifier": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||||
"payload": {
|
"payload": {
|
||||||
"PeerId": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
"Status": {
|
||||||
|
"UpdatedAt": "2025-06-27T22:20:08.734900034+02:00",
|
||||||
"IsConnected": true,
|
"IsConnected": true,
|
||||||
"IsPingable": false,
|
"IsPingable": false,
|
||||||
"LastPing": null,
|
"BytesReceived": 212,
|
||||||
"BytesReceived": 1860,
|
"BytesTransmitted": 2884,
|
||||||
"BytesTransmitted": 10824,
|
"Endpoint": "10.55.66.77:58756",
|
||||||
"LastHandshake": "2025-06-26T23:04:33.325216659+02:00",
|
"LastHandshake": "2025-06-27T22:19:46.580842776+02:00",
|
||||||
"Endpoint": "10.55.66.77:33874",
|
"LastSessionStart": "2025-06-27T22:19:46.580842776+02:00"
|
||||||
"LastSessionStart": "2025-06-26T22:50:40.10221606+02:00"
|
},
|
||||||
|
"Peer": {
|
||||||
|
"CreatedBy": "admin@wgportal.local",
|
||||||
|
"UpdatedBy": "admin@wgportal.local",
|
||||||
|
"CreatedAt": "2025-06-26T21:43:49.251839574+02:00",
|
||||||
|
"UpdatedAt": "2025-06-27T22:18:39.67763985+02:00",
|
||||||
|
"Endpoint": "10.55.66.1:51820",
|
||||||
|
"EndpointPublicKey": "eiVibpi3C2PUPcx2kwA5s09OgHx7AEaKMd33k0LQ5mM=",
|
||||||
|
"AllowedIPsStr": "10.11.12.0/24,fdfd:d3ad:c0de:1234::/64",
|
||||||
|
"ExtraAllowedIPsStr": "",
|
||||||
|
"PresharedKey": "p9DDeLUSLOdQcjS8ZsBAiqUzwDIUvTyzavRZFuzhvyE=",
|
||||||
|
"PersistentKeepalive": 16,
|
||||||
|
"DisplayName": "Peer Fb5TaziA",
|
||||||
|
"Identifier": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||||
|
"UserIdentifier": "admin@wgportal.local",
|
||||||
|
"InterfaceIdentifier": "wgTesting",
|
||||||
|
"AutomaticallyCreated": false,
|
||||||
|
"PrivateKey": "QBFNBe+7J49ergH0ze2TGUJMFrL/2bOL50Z2cgluYW8=",
|
||||||
|
"PublicKey": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||||
|
"InterfaceType": "client",
|
||||||
|
"Addresses": [
|
||||||
|
"10.11.12.10/32",
|
||||||
|
"fdfd:d3ad:c0de:1234::a/128"
|
||||||
|
],
|
||||||
|
"CheckAliveAddress": "",
|
||||||
|
"DnsStr": "",
|
||||||
|
"DnsSearchStr": "",
|
||||||
|
"Mtu": 1420
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is another example of a webhook event when a peer is updated:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"event": "update",
|
||||||
|
"entity": "peer",
|
||||||
|
"identifier": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||||
|
"payload": {
|
||||||
|
"CreatedBy": "admin@wgportal.local",
|
||||||
|
"UpdatedBy": "admin@wgportal.local",
|
||||||
|
"CreatedAt": "2025-06-26T21:43:49.251839574+02:00",
|
||||||
|
"UpdatedAt": "2025-06-27T22:18:39.67763985+02:00",
|
||||||
|
"Endpoint": "10.55.66.1:51820",
|
||||||
|
"EndpointPublicKey": "eiVibpi3C2PUPcx2kwA5s09OgHx7AEaKMd33k0LQ5mM=",
|
||||||
|
"AllowedIPsStr": "10.11.12.0/24,fdfd:d3ad:c0de:1234::/64",
|
||||||
|
"ExtraAllowedIPsStr": "",
|
||||||
|
"PresharedKey": "p9DDeLUSLOdQcjS8ZsBAiqUzwDIUvTyzavRZFuzhvyE=",
|
||||||
|
"PersistentKeepalive": 16,
|
||||||
|
"DisplayName": "Peer Fb5TaziA",
|
||||||
|
"Identifier": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||||
|
"UserIdentifier": "admin@wgportal.local",
|
||||||
|
"InterfaceIdentifier": "wgTesting",
|
||||||
|
"AutomaticallyCreated": false,
|
||||||
|
"PrivateKey": "QBFNBe+7J49ergH0ze2TGUJMFrL/2bOL50Z2cgluYW8=",
|
||||||
|
"PublicKey": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||||
|
"InterfaceType": "client",
|
||||||
|
"Addresses": [
|
||||||
|
"10.11.12.10/32",
|
||||||
|
"fdfd:d3ad:c0de:1234::a/128"
|
||||||
|
],
|
||||||
|
"CheckAliveAddress": "",
|
||||||
|
"DnsStr": "",
|
||||||
|
"DnsSearchStr": "",
|
||||||
|
"Mtu": 1420
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
@@ -50,7 +50,7 @@ const selectedStats = computed(() => {
|
|||||||
|
|
||||||
if (!s) {
|
if (!s) {
|
||||||
if (!!props.peerId || props.peerId.length) {
|
if (!!props.peerId || props.peerId.length) {
|
||||||
s = profile.Statistics(props.peerId)
|
p = profile.Statistics(props.peerId)
|
||||||
} else {
|
} else {
|
||||||
s = freshStats() // dummy stats to avoid 'undefined' exceptions
|
s = freshStats() // dummy stats to avoid 'undefined' exceptions
|
||||||
}
|
}
|
||||||
@@ -79,19 +79,13 @@ const title = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const configStyle = ref("wgquick")
|
|
||||||
|
|
||||||
watch(() => props.visible, async (newValue, oldValue) => {
|
watch(() => props.visible, async (newValue, oldValue) => {
|
||||||
if (oldValue === false && newValue === true) { // if modal is shown
|
if (oldValue === false && newValue === true) { // if modal is shown
|
||||||
await peers.LoadPeerConfig(selectedPeer.value.Identifier, configStyle.value)
|
await peers.LoadPeerConfig(selectedPeer.value.Identifier)
|
||||||
configString.value = peers.configuration
|
configString.value = peers.configuration
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
watch(() => configStyle.value, async () => {
|
|
||||||
await peers.LoadPeerConfig(selectedPeer.value.Identifier, configStyle.value)
|
|
||||||
configString.value = peers.configuration
|
|
||||||
})
|
|
||||||
|
|
||||||
function download() {
|
function download() {
|
||||||
// credit: https://www.bitdegree.org/learn/javascript-download
|
// credit: https://www.bitdegree.org/learn/javascript-download
|
||||||
@@ -109,7 +103,7 @@ function download() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function email() {
|
function email() {
|
||||||
peers.MailPeerConfig(settings.Setting("MailLinkOnly"), configStyle.value, [selectedPeer.value.Identifier]).catch(e => {
|
peers.MailPeerConfig(settings.Setting("MailLinkOnly"), [selectedPeer.value.Identifier]).catch(e => {
|
||||||
notify({
|
notify({
|
||||||
title: "Failed to send mail with peer configuration!",
|
title: "Failed to send mail with peer configuration!",
|
||||||
text: e.toString(),
|
text: e.toString(),
|
||||||
@@ -120,7 +114,7 @@ function email() {
|
|||||||
|
|
||||||
function ConfigQrUrl() {
|
function ConfigQrUrl() {
|
||||||
if (props.peerId.length) {
|
if (props.peerId.length) {
|
||||||
return apiWrapper.url(`/peer/config-qr/${base64_url_encode(props.peerId)}?style=${configStyle.value}`)
|
return apiWrapper.url(`/peer/config-qr/${base64_url_encode(props.peerId)}`)
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@@ -130,15 +124,6 @@ function ConfigQrUrl() {
|
|||||||
<template>
|
<template>
|
||||||
<Modal :title="title" :visible="visible" @close="close">
|
<Modal :title="title" :visible="visible" @close="close">
|
||||||
<template #default>
|
<template #default>
|
||||||
<div class="d-flex justify-content-end align-items-center mb-1">
|
|
||||||
<span class="me-2">{{ $t('modals.peer-view.style-label') }}: </span>
|
|
||||||
<div class="btn-group btn-switch-group" role="group" aria-label="Configuration Style">
|
|
||||||
<input type="radio" class="btn-check" name="configstyle" id="raw" value="raw" autocomplete="off" checked="" v-model="configStyle">
|
|
||||||
<label class="btn btn-outline-primary btn-sm" for="raw">Raw</label>
|
|
||||||
<input type="radio" class="btn-check" name="configstyle" id="wgquick" value="wgquick" autocomplete="off" checked="" v-model="configStyle">
|
|
||||||
<label class="btn btn-outline-primary btn-sm" for="wgquick">WG-Quick</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="accordion" id="peerInformation">
|
<div class="accordion" id="peerInformation">
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header">
|
<h2 class="accordion-header">
|
||||||
@@ -228,14 +213,6 @@ function ConfigQrUrl() {
|
|||||||
</template>
|
</template>
|
||||||
</Modal></template>
|
</Modal></template>
|
||||||
|
|
||||||
<style>
|
<style>.config-qr-img {
|
||||||
.config-qr-img {
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}</style>
|
||||||
|
|
||||||
.btn-switch-group .btn {
|
|
||||||
border-width: 1px;
|
|
||||||
padding: 5px;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@@ -467,8 +467,7 @@
|
|||||||
"connected-since": "Verbunden seit",
|
"connected-since": "Verbunden seit",
|
||||||
"endpoint": "Endpunkt",
|
"endpoint": "Endpunkt",
|
||||||
"button-download": "Konfiguration herunterladen",
|
"button-download": "Konfiguration herunterladen",
|
||||||
"button-email": "Konfiguration per E-Mail senden",
|
"button-email": "Konfiguration per E-Mail senden"
|
||||||
"style-label": "Konfigurationsformat"
|
|
||||||
},
|
},
|
||||||
"peer-edit": {
|
"peer-edit": {
|
||||||
"headline-edit-peer": "Peer bearbeiten:",
|
"headline-edit-peer": "Peer bearbeiten:",
|
||||||
|
@@ -468,8 +468,7 @@
|
|||||||
"connected-since": "Connected since",
|
"connected-since": "Connected since",
|
||||||
"endpoint": "Endpoint",
|
"endpoint": "Endpoint",
|
||||||
"button-download": "Download configuration",
|
"button-download": "Download configuration",
|
||||||
"button-email": "Send configuration via E-Mail",
|
"button-email": "Send configuration via E-Mail"
|
||||||
"style-label": "Configuration Style"
|
|
||||||
},
|
},
|
||||||
"peer-edit": {
|
"peer-edit": {
|
||||||
"headline-edit-peer": "Edit peer:",
|
"headline-edit-peer": "Edit peer:",
|
||||||
|
@@ -142,8 +142,8 @@ export const peerStore = defineStore('peers', {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async MailPeerConfig(linkOnly, style, ids) {
|
async MailPeerConfig(linkOnly, ids) {
|
||||||
return apiWrapper.post(`${baseUrl}/config-mail?style=${style}`, {
|
return apiWrapper.post(`${baseUrl}/config-mail`, {
|
||||||
Identifiers: ids,
|
Identifiers: ids,
|
||||||
LinkOnly: linkOnly
|
LinkOnly: linkOnly
|
||||||
})
|
})
|
||||||
@@ -158,8 +158,8 @@ export const peerStore = defineStore('peers', {
|
|||||||
throw new Error(error)
|
throw new Error(error)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
async LoadPeerConfig(id, style) {
|
async LoadPeerConfig(id) {
|
||||||
return apiWrapper.get(`${baseUrl}/config/${base64_url_encode(id)}?style=${style}`)
|
return apiWrapper.get(`${baseUrl}/config/${base64_url_encode(id)}`)
|
||||||
.then(this.setPeerConfig)
|
.then(this.setPeerConfig)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.configuration = ""
|
this.configuration = ""
|
||||||
|
@@ -819,12 +819,6 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/model.PeerMailRequest"
|
"$ref": "#/definitions/model.PeerMailRequest"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "The configuration style",
|
|
||||||
"name": "style",
|
|
||||||
"in": "query"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -864,12 +858,6 @@
|
|||||||
"name": "id",
|
"name": "id",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "The configuration style",
|
|
||||||
"name": "style",
|
|
||||||
"in": "query"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -911,12 +899,6 @@
|
|||||||
"name": "id",
|
"name": "id",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "The configuration style",
|
|
||||||
"name": "style",
|
|
||||||
"in": "query"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
@@ -1072,10 +1072,6 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/model.PeerMailRequest'
|
$ref: '#/definitions/model.PeerMailRequest'
|
||||||
- description: The configuration style
|
|
||||||
in: query
|
|
||||||
name: style
|
|
||||||
type: string
|
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
@@ -1101,10 +1097,6 @@ paths:
|
|||||||
name: id
|
name: id
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
- description: The configuration style
|
|
||||||
in: query
|
|
||||||
name: style
|
|
||||||
type: string
|
|
||||||
produces:
|
produces:
|
||||||
- image/png
|
- image/png
|
||||||
- application/json
|
- application/json
|
||||||
@@ -1133,10 +1125,6 @@ paths:
|
|||||||
name: id
|
name: id
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
- description: The configuration style
|
|
||||||
in: query
|
|
||||||
name: style
|
|
||||||
type: string
|
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
@@ -27,12 +27,12 @@ type PeerServicePeerManager interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PeerServiceConfigFileManager interface {
|
type PeerServiceConfigFileManager interface {
|
||||||
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error)
|
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||||
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error)
|
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeerServiceMailManager interface {
|
type PeerServiceMailManager interface {
|
||||||
SendPeerEmail(ctx context.Context, linkOnly bool, style string, peers ...domain.PeerIdentifier) error
|
SendPeerEmail(ctx context.Context, linkOnly bool, peers ...domain.PeerIdentifier) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion dependencies
|
// endregion dependencies
|
||||||
@@ -95,24 +95,16 @@ func (p PeerService) DeletePeer(ctx context.Context, id domain.PeerIdentifier) e
|
|||||||
return p.peers.DeletePeer(ctx, id)
|
return p.peers.DeletePeer(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PeerService) GetPeerConfig(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error) {
|
func (p PeerService) GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error) {
|
||||||
return p.configFile.GetPeerConfig(ctx, id, style)
|
return p.configFile.GetPeerConfig(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PeerService) GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier, style string) (
|
func (p PeerService) GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error) {
|
||||||
io.Reader,
|
return p.configFile.GetPeerConfigQrCode(ctx, id)
|
||||||
error,
|
|
||||||
) {
|
|
||||||
return p.configFile.GetPeerConfigQrCode(ctx, id, style)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PeerService) SendPeerEmail(
|
func (p PeerService) SendPeerEmail(ctx context.Context, linkOnly bool, peers ...domain.PeerIdentifier) error {
|
||||||
ctx context.Context,
|
return p.mailer.SendPeerEmail(ctx, linkOnly, peers...)
|
||||||
linkOnly bool,
|
|
||||||
style string,
|
|
||||||
peers ...domain.PeerIdentifier,
|
|
||||||
) error {
|
|
||||||
return p.mailer.SendPeerEmail(ctx, linkOnly, style, peers...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PeerService) GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.PeerStatus, error) {
|
func (p PeerService) GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.PeerStatus, error) {
|
||||||
|
@@ -34,11 +34,11 @@ type PeerService interface {
|
|||||||
// DeletePeer deletes the peer with the given id.
|
// DeletePeer deletes the peer with the given id.
|
||||||
DeletePeer(ctx context.Context, id domain.PeerIdentifier) error
|
DeletePeer(ctx context.Context, id domain.PeerIdentifier) error
|
||||||
// GetPeerConfig returns the peer configuration for the given id.
|
// GetPeerConfig returns the peer configuration for the given id.
|
||||||
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error)
|
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||||
// GetPeerConfigQrCode returns the peer configuration as qr code for the given id.
|
// GetPeerConfigQrCode returns the peer configuration as qr code for the given id.
|
||||||
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error)
|
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||||
// SendPeerEmail sends the peer configuration via email.
|
// SendPeerEmail sends the peer configuration via email.
|
||||||
SendPeerEmail(ctx context.Context, linkOnly bool, style string, peers ...domain.PeerIdentifier) error
|
SendPeerEmail(ctx context.Context, linkOnly bool, peers ...domain.PeerIdentifier) error
|
||||||
// GetPeerStats returns the peer stats for the given interface.
|
// GetPeerStats returns the peer stats for the given interface.
|
||||||
GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.PeerStatus, error)
|
GetPeerStats(ctx context.Context, id domain.InterfaceIdentifier) ([]domain.PeerStatus, error)
|
||||||
}
|
}
|
||||||
@@ -355,7 +355,6 @@ func (e PeerEndpoint) handleDelete() http.HandlerFunc {
|
|||||||
// @Summary Get peer configuration as string.
|
// @Summary Get peer configuration as string.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "The peer identifier"
|
// @Param id path string true "The peer identifier"
|
||||||
// @Param style query string false "The configuration style"
|
|
||||||
// @Success 200 {object} string
|
// @Success 200 {object} string
|
||||||
// @Failure 400 {object} model.Error
|
// @Failure 400 {object} model.Error
|
||||||
// @Failure 500 {object} model.Error
|
// @Failure 500 {object} model.Error
|
||||||
@@ -370,9 +369,7 @@ func (e PeerEndpoint) handleConfigGet() http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configStyle := e.getConfigStyle(r)
|
configTxt, err := e.peerService.GetPeerConfig(r.Context(), domain.PeerIdentifier(id))
|
||||||
|
|
||||||
configTxt, err := e.peerService.GetPeerConfig(r.Context(), domain.PeerIdentifier(id), configStyle)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respond.JSON(w, http.StatusInternalServerError, model.Error{
|
respond.JSON(w, http.StatusInternalServerError, model.Error{
|
||||||
Code: http.StatusInternalServerError, Message: err.Error(),
|
Code: http.StatusInternalServerError, Message: err.Error(),
|
||||||
@@ -400,7 +397,6 @@ func (e PeerEndpoint) handleConfigGet() http.HandlerFunc {
|
|||||||
// @Produce png
|
// @Produce png
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "The peer identifier"
|
// @Param id path string true "The peer identifier"
|
||||||
// @Param style query string false "The configuration style"
|
|
||||||
// @Success 200 {file} binary
|
// @Success 200 {file} binary
|
||||||
// @Failure 400 {object} model.Error
|
// @Failure 400 {object} model.Error
|
||||||
// @Failure 500 {object} model.Error
|
// @Failure 500 {object} model.Error
|
||||||
@@ -415,9 +411,7 @@ func (e PeerEndpoint) handleQrCodeGet() http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configStyle := e.getConfigStyle(r)
|
configQr, err := e.peerService.GetPeerConfigQrCode(r.Context(), domain.PeerIdentifier(id))
|
||||||
|
|
||||||
configQr, err := e.peerService.GetPeerConfigQrCode(r.Context(), domain.PeerIdentifier(id), configStyle)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respond.JSON(w, http.StatusInternalServerError, model.Error{
|
respond.JSON(w, http.StatusInternalServerError, model.Error{
|
||||||
Code: http.StatusInternalServerError, Message: err.Error(),
|
Code: http.StatusInternalServerError, Message: err.Error(),
|
||||||
@@ -444,7 +438,6 @@ func (e PeerEndpoint) handleQrCodeGet() http.HandlerFunc {
|
|||||||
// @Summary Send peer configuration via email.
|
// @Summary Send peer configuration via email.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param request body model.PeerMailRequest true "The peer mail request data"
|
// @Param request body model.PeerMailRequest true "The peer mail request data"
|
||||||
// @Param style query string false "The configuration style"
|
|
||||||
// @Success 204 "No content if mail sending was successful"
|
// @Success 204 "No content if mail sending was successful"
|
||||||
// @Failure 400 {object} model.Error
|
// @Failure 400 {object} model.Error
|
||||||
// @Failure 500 {object} model.Error
|
// @Failure 500 {object} model.Error
|
||||||
@@ -467,13 +460,11 @@ func (e PeerEndpoint) handleEmailPost() http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configStyle := e.getConfigStyle(r)
|
|
||||||
|
|
||||||
peerIds := make([]domain.PeerIdentifier, len(req.Identifiers))
|
peerIds := make([]domain.PeerIdentifier, len(req.Identifiers))
|
||||||
for i := range req.Identifiers {
|
for i := range req.Identifiers {
|
||||||
peerIds[i] = domain.PeerIdentifier(req.Identifiers[i])
|
peerIds[i] = domain.PeerIdentifier(req.Identifiers[i])
|
||||||
}
|
}
|
||||||
if err := e.peerService.SendPeerEmail(r.Context(), req.LinkOnly, configStyle, peerIds...); err != nil {
|
if err := e.peerService.SendPeerEmail(r.Context(), req.LinkOnly, peerIds...); err != nil {
|
||||||
respond.JSON(w, http.StatusInternalServerError,
|
respond.JSON(w, http.StatusInternalServerError,
|
||||||
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
|
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
|
||||||
return
|
return
|
||||||
@@ -513,11 +504,3 @@ func (e PeerEndpoint) handleStatsGet() http.HandlerFunc {
|
|||||||
respond.JSON(w, http.StatusOK, model.NewPeerStats(e.cfg.Statistics.CollectPeerData, stats))
|
respond.JSON(w, http.StatusOK, model.NewPeerStats(e.cfg.Statistics.CollectPeerData, stats))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e PeerEndpoint) getConfigStyle(r *http.Request) string {
|
|
||||||
configStyle := request.QueryDefault(r, "style", domain.ConfigStyleWgQuick)
|
|
||||||
if configStyle != domain.ConfigStyleWgQuick && configStyle != domain.ConfigStyleRaw {
|
|
||||||
configStyle = domain.ConfigStyleWgQuick // default to wg-quick style
|
|
||||||
}
|
|
||||||
return configStyle
|
|
||||||
}
|
|
||||||
|
@@ -23,8 +23,8 @@ type ProvisioningServicePeerManagerRepo interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProvisioningServiceConfigFileManagerRepo interface {
|
type ProvisioningServiceConfigFileManagerRepo interface {
|
||||||
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error)
|
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||||
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error)
|
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProvisioningService struct {
|
type ProvisioningService struct {
|
||||||
@@ -96,7 +96,7 @@ func (p ProvisioningService) GetPeerConfig(ctx context.Context, peerId domain.Pe
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peerCfgReader, err := p.configFiles.GetPeerConfig(ctx, peer.Identifier, domain.ConfigStyleWgQuick)
|
peerCfgReader, err := p.configFiles.GetPeerConfig(ctx, peer.Identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ func (p ProvisioningService) GetPeerQrPng(ctx context.Context, peerId domain.Pee
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peerCfgQrReader, err := p.configFiles.GetPeerConfigQrCode(ctx, peer.Identifier, domain.ConfigStyleWgQuick)
|
peerCfgQrReader, err := p.configFiles.GetPeerConfigQrCode(ctx, peer.Identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,7 @@ type TemplateRenderer interface {
|
|||||||
// GetInterfaceConfig returns the configuration file for the given interface.
|
// GetInterfaceConfig returns the configuration file for the given interface.
|
||||||
GetInterfaceConfig(iface *domain.Interface, peers []domain.Peer) (io.Reader, error)
|
GetInterfaceConfig(iface *domain.Interface, peers []domain.Peer) (io.Reader, error)
|
||||||
// GetPeerConfig returns the configuration file for the given peer.
|
// GetPeerConfig returns the configuration file for the given peer.
|
||||||
GetPeerConfig(peer *domain.Peer, style string) (io.Reader, error)
|
GetPeerConfig(peer *domain.Peer) (io.Reader, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventBus interface {
|
type EventBus interface {
|
||||||
@@ -186,7 +186,7 @@ func (m Manager) GetInterfaceConfig(ctx context.Context, id domain.InterfaceIden
|
|||||||
|
|
||||||
// GetPeerConfig returns the configuration file for the given peer.
|
// GetPeerConfig returns the configuration file for the given peer.
|
||||||
// The file is structured in wg-quick format.
|
// The file is structured in wg-quick format.
|
||||||
func (m Manager) GetPeerConfig(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error) {
|
func (m Manager) GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error) {
|
||||||
peer, err := m.wg.GetPeer(ctx, id)
|
peer, err := m.wg.GetPeer(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch peer %s: %w", id, err)
|
return nil, fmt.Errorf("failed to fetch peer %s: %w", id, err)
|
||||||
@@ -196,11 +196,11 @@ func (m Manager) GetPeerConfig(ctx context.Context, id domain.PeerIdentifier, st
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.tplHandler.GetPeerConfig(peer, style)
|
return m.tplHandler.GetPeerConfig(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeerConfigQrCode returns a QR code image containing the configuration for the given peer.
|
// GetPeerConfigQrCode returns a QR code image containing the configuration for the given peer.
|
||||||
func (m Manager) GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error) {
|
func (m Manager) GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error) {
|
||||||
peer, err := m.wg.GetPeer(ctx, id)
|
peer, err := m.wg.GetPeer(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch peer %s: %w", id, err)
|
return nil, fmt.Errorf("failed to fetch peer %s: %w", id, err)
|
||||||
@@ -210,7 +210,7 @@ func (m Manager) GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgData, err := m.tplHandler.GetPeerConfig(peer, style)
|
cfgData, err := m.tplHandler.GetPeerConfig(peer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get peer config for %s: %w", id, err)
|
return nil, fmt.Errorf("failed to get peer config for %s: %w", id, err)
|
||||||
}
|
}
|
||||||
|
@@ -55,11 +55,10 @@ func (c TemplateHandler) GetInterfaceConfig(cfg *domain.Interface, peers []domai
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPeerConfig returns the rendered configuration file for a WireGuard peer.
|
// GetPeerConfig returns the rendered configuration file for a WireGuard peer.
|
||||||
func (c TemplateHandler) GetPeerConfig(peer *domain.Peer, style string) (io.Reader, error) {
|
func (c TemplateHandler) GetPeerConfig(peer *domain.Peer) (io.Reader, error) {
|
||||||
var tplBuff bytes.Buffer
|
var tplBuff bytes.Buffer
|
||||||
|
|
||||||
err := c.templates.ExecuteTemplate(&tplBuff, "wg_peer.tpl", map[string]any{
|
err := c.templates.ExecuteTemplate(&tplBuff, "wg_peer.tpl", map[string]any{
|
||||||
"Style": style,
|
|
||||||
"Peer": peer,
|
"Peer": peer,
|
||||||
"Portal": map[string]any{
|
"Portal": map[string]any{
|
||||||
"Version": "unknown",
|
"Version": "unknown",
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
# AUTOGENERATED FILE - DO NOT EDIT
|
# AUTOGENERATED FILE - DO NOT EDIT
|
||||||
# This file uses {{ .Style }} format.
|
# This file uses wg-quick format.
|
||||||
{{- if eq .Style "wgquick"}}
|
|
||||||
# See https://man7.org/linux/man-pages/man8/wg-quick.8.html#CONFIGURATION
|
# See https://man7.org/linux/man-pages/man8/wg-quick.8.html#CONFIGURATION
|
||||||
{{- end}}
|
|
||||||
# Lines starting with the -WGP- tag are used by
|
# Lines starting with the -WGP- tag are used by
|
||||||
# the WireGuard Portal configuration parser.
|
# the WireGuard Portal configuration parser.
|
||||||
|
|
||||||
@@ -23,27 +21,22 @@
|
|||||||
|
|
||||||
# Core settings
|
# Core settings
|
||||||
PrivateKey = {{ .Peer.Interface.KeyPair.PrivateKey }}
|
PrivateKey = {{ .Peer.Interface.KeyPair.PrivateKey }}
|
||||||
{{- if eq .Style "wgquick"}}
|
|
||||||
Address = {{ CidrsToString .Peer.Interface.Addresses }}
|
Address = {{ CidrsToString .Peer.Interface.Addresses }}
|
||||||
{{- end}}
|
|
||||||
|
|
||||||
# Misc. settings (optional)
|
# Misc. settings (optional)
|
||||||
{{- if eq .Style "wgquick"}}
|
|
||||||
{{- if .Peer.Interface.DnsStr.GetValue}}
|
{{- if .Peer.Interface.DnsStr.GetValue}}
|
||||||
DNS = {{ .Peer.Interface.DnsStr.GetValue }} {{- if .Peer.Interface.DnsSearchStr.GetValue}}, {{ .Peer.Interface.DnsSearchStr.GetValue }} {{- end}}
|
DNS = {{ .Peer.Interface.DnsStr.GetValue }} {{- if .Peer.Interface.DnsSearchStr.GetValue}}, {{ .Peer.Interface.DnsSearchStr.GetValue }} {{- end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- if ne .Peer.Interface.Mtu.GetValue 0}}
|
{{- if ne .Peer.Interface.Mtu.GetValue 0}}
|
||||||
MTU = {{ .Peer.Interface.Mtu.GetValue }}
|
MTU = {{ .Peer.Interface.Mtu.GetValue }}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- if ne .Peer.Interface.RoutingTable.GetValue ""}}
|
|
||||||
Table = {{ .Peer.Interface.RoutingTable.GetValue }}
|
|
||||||
{{- end}}
|
|
||||||
{{- end}}
|
|
||||||
{{- if ne .Peer.Interface.FirewallMark.GetValue 0}}
|
{{- if ne .Peer.Interface.FirewallMark.GetValue 0}}
|
||||||
FwMark = {{ .Peer.Interface.FirewallMark.GetValue }}
|
FwMark = {{ .Peer.Interface.FirewallMark.GetValue }}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
{{- if ne .Peer.Interface.RoutingTable.GetValue ""}}
|
||||||
|
Table = {{ .Peer.Interface.RoutingTable.GetValue }}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
{{- if eq .Style "wgquick"}}
|
|
||||||
# Interface hooks (optional)
|
# Interface hooks (optional)
|
||||||
{{- if .Peer.Interface.PreUp.GetValue}}
|
{{- if .Peer.Interface.PreUp.GetValue}}
|
||||||
PreUp = {{ .Peer.Interface.PreUp.GetValue }}
|
PreUp = {{ .Peer.Interface.PreUp.GetValue }}
|
||||||
@@ -57,7 +50,6 @@ PreDown = {{ .Peer.Interface.PreDown.GetValue }}
|
|||||||
{{- if .Peer.Interface.PostDown.GetValue}}
|
{{- if .Peer.Interface.PostDown.GetValue}}
|
||||||
PostDown = {{ .Peer.Interface.PostDown.GetValue }}
|
PostDown = {{ .Peer.Interface.PostDown.GetValue }}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{- end}}
|
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = {{ .Peer.EndpointPublicKey.GetValue }}
|
PublicKey = {{ .Peer.EndpointPublicKey.GetValue }}
|
||||||
|
@@ -21,9 +21,9 @@ type ConfigFileManager interface {
|
|||||||
// GetInterfaceConfig returns the configuration for the given interface.
|
// GetInterfaceConfig returns the configuration for the given interface.
|
||||||
GetInterfaceConfig(ctx context.Context, id domain.InterfaceIdentifier) (io.Reader, error)
|
GetInterfaceConfig(ctx context.Context, id domain.InterfaceIdentifier) (io.Reader, error)
|
||||||
// GetPeerConfig returns the configuration for the given peer.
|
// GetPeerConfig returns the configuration for the given peer.
|
||||||
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error)
|
GetPeerConfig(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||||
// GetPeerConfigQrCode returns the QR code for the given peer.
|
// GetPeerConfigQrCode returns the QR code for the given peer.
|
||||||
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier, style string) (io.Reader, error)
|
GetPeerConfigQrCode(ctx context.Context, id domain.PeerIdentifier) (io.Reader, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserDatabaseRepo interface {
|
type UserDatabaseRepo interface {
|
||||||
@@ -89,7 +89,7 @@ func NewMailManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SendPeerEmail sends an email to the user linked to the given peers.
|
// SendPeerEmail sends an email to the user linked to the given peers.
|
||||||
func (m Manager) SendPeerEmail(ctx context.Context, linkOnly bool, style string, peers ...domain.PeerIdentifier) error {
|
func (m Manager) SendPeerEmail(ctx context.Context, linkOnly bool, peers ...domain.PeerIdentifier) error {
|
||||||
for _, peerId := range peers {
|
for _, peerId := range peers {
|
||||||
peer, err := m.wg.GetPeer(ctx, peerId)
|
peer, err := m.wg.GetPeer(ctx, peerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,7 +123,7 @@ func (m Manager) SendPeerEmail(ctx context.Context, linkOnly bool, style string,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.sendPeerEmail(ctx, linkOnly, style, user, peer)
|
err = m.sendPeerEmail(ctx, linkOnly, user, peer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to send peer email for %s: %w", peerId, err)
|
return fmt.Errorf("failed to send peer email for %s: %w", peerId, err)
|
||||||
}
|
}
|
||||||
@@ -132,13 +132,7 @@ func (m Manager) SendPeerEmail(ctx context.Context, linkOnly bool, style string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) sendPeerEmail(
|
func (m Manager) sendPeerEmail(ctx context.Context, linkOnly bool, user *domain.User, peer *domain.Peer) error {
|
||||||
ctx context.Context,
|
|
||||||
linkOnly bool,
|
|
||||||
style string,
|
|
||||||
user *domain.User,
|
|
||||||
peer *domain.Peer,
|
|
||||||
) error {
|
|
||||||
qrName := "WireGuardQRCode.png"
|
qrName := "WireGuardQRCode.png"
|
||||||
configName := peer.GetConfigFileName()
|
configName := peer.GetConfigFileName()
|
||||||
|
|
||||||
@@ -154,12 +148,12 @@ func (m Manager) sendPeerEmail(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
peerConfig, err := m.configFiles.GetPeerConfig(ctx, peer.Identifier, style)
|
peerConfig, err := m.configFiles.GetPeerConfig(ctx, peer.Identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to fetch peer config for %s: %w", peer.Identifier, err)
|
return fmt.Errorf("failed to fetch peer config for %s: %w", peer.Identifier, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peerConfigQr, err := m.configFiles.GetPeerConfigQrCode(ctx, peer.Identifier, style)
|
peerConfigQr, err := m.configFiles.GetPeerConfigQrCode(ctx, peer.Identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to fetch peer config QR code for %s: %w", peer.Identifier, err)
|
return fmt.Errorf("failed to fetch peer config QR code for %s: %w", peer.Identifier, err)
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/h44z/wg-portal/internal/app"
|
"github.com/h44z/wg-portal/internal/app"
|
||||||
|
"github.com/h44z/wg-portal/internal/app/webhooks/models"
|
||||||
"github.com/h44z/wg-portal/internal/config"
|
"github.com/h44z/wg-portal/internal/config"
|
||||||
"github.com/h44z/wg-portal/internal/domain"
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
)
|
)
|
||||||
@@ -101,46 +102,46 @@ func (m Manager) sendWebhook(ctx context.Context, data io.Reader) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handleUserCreateEvent(user domain.User) {
|
func (m Manager) handleUserCreateEvent(user domain.User) {
|
||||||
m.handleGenericEvent(WebhookEventCreate, user)
|
m.handleGenericEvent(WebhookEventCreate, models.NewUser(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handleUserUpdateEvent(user domain.User) {
|
func (m Manager) handleUserUpdateEvent(user domain.User) {
|
||||||
m.handleGenericEvent(WebhookEventUpdate, user)
|
m.handleGenericEvent(WebhookEventUpdate, models.NewUser(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handleUserDeleteEvent(user domain.User) {
|
func (m Manager) handleUserDeleteEvent(user domain.User) {
|
||||||
m.handleGenericEvent(WebhookEventDelete, user)
|
m.handleGenericEvent(WebhookEventDelete, models.NewUser(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handlePeerCreateEvent(peer domain.Peer) {
|
func (m Manager) handlePeerCreateEvent(peer domain.Peer) {
|
||||||
m.handleGenericEvent(WebhookEventCreate, peer)
|
m.handleGenericEvent(WebhookEventCreate, models.NewPeer(peer))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handlePeerUpdateEvent(peer domain.Peer) {
|
func (m Manager) handlePeerUpdateEvent(peer domain.Peer) {
|
||||||
m.handleGenericEvent(WebhookEventUpdate, peer)
|
m.handleGenericEvent(WebhookEventUpdate, models.NewPeer(peer))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handlePeerDeleteEvent(peer domain.Peer) {
|
func (m Manager) handlePeerDeleteEvent(peer domain.Peer) {
|
||||||
m.handleGenericEvent(WebhookEventDelete, peer)
|
m.handleGenericEvent(WebhookEventDelete, models.NewPeer(peer))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handleInterfaceCreateEvent(iface domain.Interface) {
|
func (m Manager) handleInterfaceCreateEvent(iface domain.Interface) {
|
||||||
m.handleGenericEvent(WebhookEventCreate, iface)
|
m.handleGenericEvent(WebhookEventCreate, models.NewInterface(iface))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handleInterfaceUpdateEvent(iface domain.Interface) {
|
func (m Manager) handleInterfaceUpdateEvent(iface domain.Interface) {
|
||||||
m.handleGenericEvent(WebhookEventUpdate, iface)
|
m.handleGenericEvent(WebhookEventUpdate, models.NewInterface(iface))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handleInterfaceDeleteEvent(iface domain.Interface) {
|
func (m Manager) handleInterfaceDeleteEvent(iface domain.Interface) {
|
||||||
m.handleGenericEvent(WebhookEventDelete, iface)
|
m.handleGenericEvent(WebhookEventDelete, models.NewInterface(iface))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) handlePeerStateChangeEvent(peerStatus domain.PeerStatus) {
|
func (m Manager) handlePeerStateChangeEvent(peerStatus domain.PeerStatus, peer domain.Peer) {
|
||||||
if peerStatus.IsConnected {
|
if peerStatus.IsConnected {
|
||||||
m.handleGenericEvent(WebhookEventConnect, peerStatus)
|
m.handleGenericEvent(WebhookEventConnect, models.NewPeerMetrics(peerStatus, peer))
|
||||||
} else {
|
} else {
|
||||||
m.handleGenericEvent(WebhookEventDisconnect, peerStatus)
|
m.handleGenericEvent(WebhookEventDisconnect, models.NewPeerMetrics(peerStatus, peer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,18 +178,18 @@ func (m Manager) createWebhookData(action WebhookEvent, payload any) (*WebhookDa
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch v := payload.(type) {
|
switch v := payload.(type) {
|
||||||
case domain.User:
|
case models.User:
|
||||||
d.Entity = WebhookEntityUser
|
d.Entity = WebhookEntityUser
|
||||||
d.Identifier = string(v.Identifier)
|
d.Identifier = v.Identifier
|
||||||
case domain.Peer:
|
case models.Peer:
|
||||||
d.Entity = WebhookEntityPeer
|
d.Entity = WebhookEntityPeer
|
||||||
d.Identifier = string(v.Identifier)
|
d.Identifier = v.Identifier
|
||||||
case domain.Interface:
|
case models.Interface:
|
||||||
d.Entity = WebhookEntityInterface
|
d.Entity = WebhookEntityInterface
|
||||||
d.Identifier = string(v.Identifier)
|
d.Identifier = v.Identifier
|
||||||
case domain.PeerStatus:
|
case models.PeerMetrics:
|
||||||
d.Entity = WebhookEntityPeer
|
d.Entity = WebhookEntityPeerMetric
|
||||||
d.Identifier = string(v.PeerId)
|
d.Identifier = v.Peer.Identifier
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported payload type: %T", v)
|
return nil, fmt.Errorf("unsupported payload type: %T", v)
|
||||||
}
|
}
|
||||||
|
@@ -36,6 +36,7 @@ type WebhookEntity = string
|
|||||||
const (
|
const (
|
||||||
WebhookEntityUser WebhookEntity = "user"
|
WebhookEntityUser WebhookEntity = "user"
|
||||||
WebhookEntityPeer WebhookEntity = "peer"
|
WebhookEntityPeer WebhookEntity = "peer"
|
||||||
|
WebhookEntityPeerMetric WebhookEntity = "peer_metric"
|
||||||
WebhookEntityInterface WebhookEntity = "interface"
|
WebhookEntityInterface WebhookEntity = "interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
99
internal/app/webhooks/models/interface.go
Normal file
99
internal/app/webhooks/models/interface.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface represents an interface model for webhooks. For details about the fields, see the domain.Interface struct.
|
||||||
|
type Interface struct {
|
||||||
|
CreatedBy string `json:"CreatedBy"`
|
||||||
|
UpdatedBy string `json:"UpdatedBy"`
|
||||||
|
CreatedAt time.Time `json:"CreatedAt"`
|
||||||
|
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||||
|
|
||||||
|
Identifier string `json:"Identifier"`
|
||||||
|
PrivateKey string `json:"PrivateKey"`
|
||||||
|
PublicKey string `json:"PublicKey"`
|
||||||
|
ListenPort int `json:"ListenPort"`
|
||||||
|
|
||||||
|
Addresses []string `json:"Addresses"`
|
||||||
|
DnsStr string `json:"DnsStr"`
|
||||||
|
DnsSearchStr string `json:"DnsSearchStr"`
|
||||||
|
|
||||||
|
Mtu int `json:"Mtu"`
|
||||||
|
FirewallMark uint32 `json:"FirewallMark"`
|
||||||
|
RoutingTable string `json:"RoutingTable"`
|
||||||
|
|
||||||
|
PreUp string `json:"PreUp"`
|
||||||
|
PostUp string `json:"PostUp"`
|
||||||
|
PreDown string `json:"PreDown"`
|
||||||
|
PostDown string `json:"PostDown"`
|
||||||
|
|
||||||
|
SaveConfig bool `json:"SaveConfig"`
|
||||||
|
|
||||||
|
DisplayName string `json:"DisplayName"`
|
||||||
|
Type string `json:"Type"`
|
||||||
|
DriverType string `json:"DriverType"`
|
||||||
|
Disabled *time.Time `json:"Disabled,omitempty"`
|
||||||
|
DisabledReason string `json:"DisabledReason,omitempty"`
|
||||||
|
|
||||||
|
PeerDefNetworkStr string `json:"PeerDefNetworkStr,omitempty"`
|
||||||
|
PeerDefDnsStr string `json:"PeerDefDnsStr,omitempty"`
|
||||||
|
PeerDefDnsSearchStr string `json:"PeerDefDnsSearchStr,omitempty"`
|
||||||
|
PeerDefEndpoint string `json:"PeerDefEndpoint,omitempty"`
|
||||||
|
PeerDefAllowedIPsStr string `json:"PeerDefAllowedIPsStr,omitempty"`
|
||||||
|
PeerDefMtu int `json:"PeerDefMtu,omitempty"`
|
||||||
|
PeerDefPersistentKeepalive int `json:"PeerDefPersistentKeepalive,omitempty"`
|
||||||
|
PeerDefFirewallMark uint32 `json:"PeerDefFirewallMark,omitempty"`
|
||||||
|
PeerDefRoutingTable string `json:"PeerDefRoutingTable,omitempty"`
|
||||||
|
|
||||||
|
PeerDefPreUp string `json:"PeerDefPreUp,omitempty"`
|
||||||
|
PeerDefPostUp string `json:"PeerDefPostUp,omitempty"`
|
||||||
|
PeerDefPreDown string `json:"PeerDefPreDown,omitempty"`
|
||||||
|
PeerDefPostDown string `json:"PeerDefPostDown,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInterface creates a new Interface model from a domain.Interface.
|
||||||
|
func NewInterface(src domain.Interface) Interface {
|
||||||
|
return Interface{
|
||||||
|
CreatedBy: src.CreatedBy,
|
||||||
|
UpdatedBy: src.UpdatedBy,
|
||||||
|
CreatedAt: src.CreatedAt,
|
||||||
|
UpdatedAt: src.UpdatedAt,
|
||||||
|
Identifier: string(src.Identifier),
|
||||||
|
PrivateKey: src.KeyPair.PrivateKey,
|
||||||
|
PublicKey: src.KeyPair.PublicKey,
|
||||||
|
ListenPort: src.ListenPort,
|
||||||
|
Addresses: domain.CidrsToStringSlice(src.Addresses),
|
||||||
|
DnsStr: src.DnsStr,
|
||||||
|
DnsSearchStr: src.DnsSearchStr,
|
||||||
|
Mtu: src.Mtu,
|
||||||
|
FirewallMark: src.FirewallMark,
|
||||||
|
RoutingTable: src.RoutingTable,
|
||||||
|
PreUp: src.PreUp,
|
||||||
|
PostUp: src.PostUp,
|
||||||
|
PreDown: src.PreDown,
|
||||||
|
PostDown: src.PostDown,
|
||||||
|
SaveConfig: src.SaveConfig,
|
||||||
|
DisplayName: string(src.Identifier),
|
||||||
|
Type: string(src.Type),
|
||||||
|
DriverType: src.DriverType,
|
||||||
|
Disabled: src.Disabled,
|
||||||
|
DisabledReason: src.DisabledReason,
|
||||||
|
PeerDefNetworkStr: src.PeerDefNetworkStr,
|
||||||
|
PeerDefDnsStr: src.PeerDefDnsStr,
|
||||||
|
PeerDefDnsSearchStr: src.PeerDefDnsSearchStr,
|
||||||
|
PeerDefEndpoint: src.PeerDefEndpoint,
|
||||||
|
PeerDefAllowedIPsStr: src.PeerDefAllowedIPsStr,
|
||||||
|
PeerDefMtu: src.PeerDefMtu,
|
||||||
|
PeerDefPersistentKeepalive: src.PeerDefPersistentKeepalive,
|
||||||
|
PeerDefFirewallMark: src.PeerDefFirewallMark,
|
||||||
|
PeerDefRoutingTable: src.PeerDefRoutingTable,
|
||||||
|
PeerDefPreUp: src.PeerDefPreUp,
|
||||||
|
PeerDefPostUp: src.PeerDefPostUp,
|
||||||
|
PeerDefPreDown: src.PeerDefPreDown,
|
||||||
|
PeerDefPostDown: src.PeerDefPostDown,
|
||||||
|
}
|
||||||
|
}
|
89
internal/app/webhooks/models/peer.go
Normal file
89
internal/app/webhooks/models/peer.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Peer represents a peer model for webhooks. For details about the fields, see the domain.Peer struct.
|
||||||
|
type Peer struct {
|
||||||
|
CreatedBy string `json:"CreatedBy"`
|
||||||
|
UpdatedBy string `json:"UpdatedBy"`
|
||||||
|
CreatedAt time.Time `json:"CreatedAt"`
|
||||||
|
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||||
|
|
||||||
|
Endpoint string `json:"Endpoint"`
|
||||||
|
EndpointPublicKey string `json:"EndpointPublicKey"`
|
||||||
|
AllowedIPsStr string `json:"AllowedIPsStr"`
|
||||||
|
ExtraAllowedIPsStr string `json:"ExtraAllowedIPsStr"`
|
||||||
|
PresharedKey string `json:"PresharedKey"`
|
||||||
|
PersistentKeepalive int `json:"PersistentKeepalive"`
|
||||||
|
|
||||||
|
DisplayName string `json:"DisplayName"`
|
||||||
|
Identifier string `json:"Identifier"`
|
||||||
|
UserIdentifier string `json:"UserIdentifier"`
|
||||||
|
InterfaceIdentifier string `json:"InterfaceIdentifier"`
|
||||||
|
Disabled *time.Time `json:"Disabled,omitempty"`
|
||||||
|
DisabledReason string `json:"DisabledReason,omitempty"`
|
||||||
|
ExpiresAt *time.Time `json:"ExpiresAt,omitempty"`
|
||||||
|
Notes string `json:"Notes,omitempty"`
|
||||||
|
AutomaticallyCreated bool `json:"AutomaticallyCreated"`
|
||||||
|
|
||||||
|
PrivateKey string `json:"PrivateKey"`
|
||||||
|
PublicKey string `json:"PublicKey"`
|
||||||
|
|
||||||
|
InterfaceType string `json:"InterfaceType"`
|
||||||
|
|
||||||
|
Addresses []string `json:"Addresses"`
|
||||||
|
CheckAliveAddress string `json:"CheckAliveAddress"`
|
||||||
|
DnsStr string `json:"DnsStr"`
|
||||||
|
DnsSearchStr string `json:"DnsSearchStr"`
|
||||||
|
Mtu int `json:"Mtu"`
|
||||||
|
FirewallMark uint32 `json:"FirewallMark,omitempty"`
|
||||||
|
RoutingTable string `json:"RoutingTable,omitempty"`
|
||||||
|
|
||||||
|
PreUp string `json:"PreUp,omitempty"`
|
||||||
|
PostUp string `json:"PostUp,omitempty"`
|
||||||
|
PreDown string `json:"PreDown,omitempty"`
|
||||||
|
PostDown string `json:"PostDown,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeer creates a new Peer model from a domain.Peer.
|
||||||
|
func NewPeer(src domain.Peer) Peer {
|
||||||
|
return Peer{
|
||||||
|
CreatedBy: src.CreatedBy,
|
||||||
|
UpdatedBy: src.UpdatedBy,
|
||||||
|
CreatedAt: src.CreatedAt,
|
||||||
|
UpdatedAt: src.UpdatedAt,
|
||||||
|
Endpoint: src.Endpoint.GetValue(),
|
||||||
|
EndpointPublicKey: src.EndpointPublicKey.GetValue(),
|
||||||
|
AllowedIPsStr: src.AllowedIPsStr.GetValue(),
|
||||||
|
ExtraAllowedIPsStr: src.ExtraAllowedIPsStr,
|
||||||
|
PresharedKey: string(src.PresharedKey),
|
||||||
|
PersistentKeepalive: src.PersistentKeepalive.GetValue(),
|
||||||
|
DisplayName: src.DisplayName,
|
||||||
|
Identifier: string(src.Identifier),
|
||||||
|
UserIdentifier: string(src.UserIdentifier),
|
||||||
|
InterfaceIdentifier: string(src.InterfaceIdentifier),
|
||||||
|
Disabled: src.Disabled,
|
||||||
|
DisabledReason: src.DisabledReason,
|
||||||
|
ExpiresAt: src.ExpiresAt,
|
||||||
|
Notes: src.Notes,
|
||||||
|
AutomaticallyCreated: src.AutomaticallyCreated,
|
||||||
|
PrivateKey: src.Interface.KeyPair.PrivateKey,
|
||||||
|
PublicKey: src.Interface.KeyPair.PublicKey,
|
||||||
|
InterfaceType: string(src.Interface.Type),
|
||||||
|
Addresses: domain.CidrsToStringSlice(src.Interface.Addresses),
|
||||||
|
CheckAliveAddress: src.Interface.CheckAliveAddress,
|
||||||
|
DnsStr: src.Interface.DnsStr.GetValue(),
|
||||||
|
DnsSearchStr: src.Interface.DnsSearchStr.GetValue(),
|
||||||
|
Mtu: src.Interface.Mtu.GetValue(),
|
||||||
|
FirewallMark: src.Interface.FirewallMark.GetValue(),
|
||||||
|
RoutingTable: src.Interface.RoutingTable.GetValue(),
|
||||||
|
PreUp: src.Interface.PreUp.GetValue(),
|
||||||
|
PostUp: src.Interface.PostUp.GetValue(),
|
||||||
|
PreDown: src.Interface.PreDown.GetValue(),
|
||||||
|
PostDown: src.Interface.PostDown.GetValue(),
|
||||||
|
}
|
||||||
|
}
|
50
internal/app/webhooks/models/peer_metrics.go
Normal file
50
internal/app/webhooks/models/peer_metrics.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PeerMetrics represents a peer metrics model for webhooks.
|
||||||
|
// For details about the fields, see the domain.PeerStatus and domain.Peer structs.
|
||||||
|
type PeerMetrics struct {
|
||||||
|
Status PeerStatus `json:"Status"`
|
||||||
|
Peer Peer `json:"Peer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerStatus represents the status of a peer for webhooks.
|
||||||
|
// For details about the fields, see the domain.PeerStatus struct.
|
||||||
|
type PeerStatus struct {
|
||||||
|
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||||
|
|
||||||
|
IsConnected bool `json:"IsConnected"`
|
||||||
|
|
||||||
|
IsPingable bool `json:"IsPingable"`
|
||||||
|
LastPing *time.Time `json:"LastPing,omitempty"`
|
||||||
|
|
||||||
|
BytesReceived uint64 `json:"BytesReceived"`
|
||||||
|
BytesTransmitted uint64 `json:"BytesTransmitted"`
|
||||||
|
|
||||||
|
Endpoint string `json:"Endpoint"`
|
||||||
|
LastHandshake *time.Time `json:"LastHandshake,omitempty"`
|
||||||
|
LastSessionStart *time.Time `json:"LastSessionStart,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeerMetrics creates a new PeerMetrics model from the domain.PeerStatus and domain.Peer models.
|
||||||
|
func NewPeerMetrics(status domain.PeerStatus, peer domain.Peer) PeerMetrics {
|
||||||
|
return PeerMetrics{
|
||||||
|
Status: PeerStatus{
|
||||||
|
UpdatedAt: status.UpdatedAt,
|
||||||
|
IsConnected: status.IsConnected,
|
||||||
|
IsPingable: status.IsPingable,
|
||||||
|
LastPing: status.LastPing,
|
||||||
|
BytesReceived: status.BytesReceived,
|
||||||
|
BytesTransmitted: status.BytesTransmitted,
|
||||||
|
Endpoint: status.Endpoint,
|
||||||
|
LastHandshake: status.LastHandshake,
|
||||||
|
LastSessionStart: status.LastSessionStart,
|
||||||
|
},
|
||||||
|
Peer: NewPeer(peer),
|
||||||
|
}
|
||||||
|
}
|
56
internal/app/webhooks/models/user.go
Normal file
56
internal/app/webhooks/models/user.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User represents a user model for webhooks. For details about the fields, see the domain.User struct.
|
||||||
|
type User struct {
|
||||||
|
CreatedBy string `json:"CreatedBy"`
|
||||||
|
UpdatedBy string `json:"UpdatedBy"`
|
||||||
|
CreatedAt time.Time `json:"CreatedAt"`
|
||||||
|
UpdatedAt time.Time `json:"UpdatedAt"`
|
||||||
|
|
||||||
|
Identifier string `json:"Identifier"`
|
||||||
|
Email string `json:"Email"`
|
||||||
|
Source string `json:"Source"`
|
||||||
|
ProviderName string `json:"ProviderName"`
|
||||||
|
IsAdmin bool `json:"IsAdmin"`
|
||||||
|
|
||||||
|
Firstname string `json:"Firstname,omitempty"`
|
||||||
|
Lastname string `json:"Lastname,omitempty"`
|
||||||
|
Phone string `json:"Phone,omitempty"`
|
||||||
|
Department string `json:"Department,omitempty"`
|
||||||
|
Notes string `json:"Notes,omitempty"`
|
||||||
|
|
||||||
|
Disabled *time.Time `json:"Disabled,omitempty"`
|
||||||
|
DisabledReason string `json:"DisabledReason,omitempty"`
|
||||||
|
Locked *time.Time `json:"Locked,omitempty"`
|
||||||
|
LockedReason string `json:"LockedReason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUser creates a new User model from a domain.User
|
||||||
|
func NewUser(src domain.User) User {
|
||||||
|
return User{
|
||||||
|
CreatedBy: src.CreatedBy,
|
||||||
|
UpdatedBy: src.UpdatedBy,
|
||||||
|
CreatedAt: src.CreatedAt,
|
||||||
|
UpdatedAt: src.UpdatedAt,
|
||||||
|
Identifier: string(src.Identifier),
|
||||||
|
Email: src.Email,
|
||||||
|
Source: string(src.Source),
|
||||||
|
ProviderName: src.ProviderName,
|
||||||
|
IsAdmin: src.IsAdmin,
|
||||||
|
Firstname: src.Firstname,
|
||||||
|
Lastname: src.Lastname,
|
||||||
|
Phone: src.Phone,
|
||||||
|
Department: src.Department,
|
||||||
|
Notes: src.Notes,
|
||||||
|
Disabled: src.Disabled,
|
||||||
|
DisabledReason: src.DisabledReason,
|
||||||
|
Locked: src.Locked,
|
||||||
|
LockedReason: src.LockedReason,
|
||||||
|
}
|
||||||
|
}
|
@@ -213,8 +213,14 @@ func (c *StatisticsCollector) collectPeerData(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if connectionStateChanged {
|
if connectionStateChanged {
|
||||||
|
peerModel, err := c.db.GetPeer(ctx, peer.Identifier)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to fetch peer for data collection", "peer", peer.Identifier, "error",
|
||||||
|
err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
// publish event if connection state changed
|
// publish event if connection state changed
|
||||||
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus)
|
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus, *peerModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,7 +362,7 @@ func (c *StatisticsCollector) pingWorker(ctx context.Context) {
|
|||||||
|
|
||||||
if connectionStateChanged {
|
if connectionStateChanged {
|
||||||
// publish event if connection state changed
|
// publish event if connection state changed
|
||||||
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus)
|
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus, peer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -62,7 +62,4 @@ const (
|
|||||||
|
|
||||||
LockedReasonAdmin = "locked by admin"
|
LockedReasonAdmin = "locked by admin"
|
||||||
LockedReasonApi = "locked by admin"
|
LockedReasonApi = "locked by admin"
|
||||||
|
|
||||||
ConfigStyleRaw = "raw"
|
|
||||||
ConfigStyleWgQuick = "wgquick"
|
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user