mirror of
https://github.com/h44z/wg-portal.git
synced 2025-07-03 11:47:00 +00:00
warning: existing webhook receivers need to be adapted to the new models
This commit is contained in:
parent
588bbca141
commit
edb88b5768
@ -673,19 +673,6 @@ Without a valid `external_url`, the login process may fail due to CSRF protectio
|
||||
## Webhook
|
||||
|
||||
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).
|
||||
|
||||
### `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.
|
||||
- `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.
|
||||
- `peer`: When a peer is created, updated, or deleted. This entity can also trigger `connect` and `disconnect` events.
|
||||
- `interface`: When a device is created, updated, or deleted.
|
||||
- `user`: WireGuard Portal users support creation, update, or deletion events.
|
||||
- `peer`: Peers support creation, update, or deletion events. Via the `peer_metric` entity, you can also receive connection status updates.
|
||||
- `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
|
||||
|
||||
@ -51,36 +52,234 @@ A common shell structure for webhook payloads is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "create",
|
||||
"entity": "user",
|
||||
"identifier": "the-user-identifier",
|
||||
"event": "create", // The event type, e.g. "create", "update", "delete", "connect", "disconnect"
|
||||
"entity": "user", // The entity type, e.g. "user", "peer", "peer_metric", "interface"
|
||||
"identifier": "the-user-identifier", // Unique identifier of the entity, e.g. user ID or peer ID
|
||||
"payload": {
|
||||
// The payload of the event, e.g. peer data.
|
||||
// Check the API documentation for the exact structure.
|
||||
// The payload of the event, e.g. a Peer model.
|
||||
// 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:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "connect",
|
||||
"entity": "peer",
|
||||
"entity": "peer_metric",
|
||||
"identifier": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||
"payload": {
|
||||
"PeerId": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||
"Status": {
|
||||
"UpdatedAt": "2025-06-27T22:20:08.734900034+02:00",
|
||||
"IsConnected": true,
|
||||
"IsPingable": false,
|
||||
"LastPing": null,
|
||||
"BytesReceived": 1860,
|
||||
"BytesTransmitted": 10824,
|
||||
"LastHandshake": "2025-06-26T23:04:33.325216659+02:00",
|
||||
"Endpoint": "10.55.66.77:33874",
|
||||
"LastSessionStart": "2025-06-26T22:50:40.10221606+02:00"
|
||||
"BytesReceived": 212,
|
||||
"BytesTransmitted": 2884,
|
||||
"Endpoint": "10.55.66.77:58756",
|
||||
"LastHandshake": "2025-06-27T22:19:46.580842776+02:00",
|
||||
"LastSessionStart": "2025-06-27T22:19:46.580842776+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
|
||||
}
|
||||
}
|
||||
```
|
@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"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/domain"
|
||||
)
|
||||
@ -101,46 +102,46 @@ func (m Manager) sendWebhook(ctx context.Context, data io.Reader) error {
|
||||
}
|
||||
|
||||
func (m Manager) handleUserCreateEvent(user domain.User) {
|
||||
m.handleGenericEvent(WebhookEventCreate, user)
|
||||
m.handleGenericEvent(WebhookEventCreate, models.NewUser(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) {
|
||||
m.handleGenericEvent(WebhookEventDelete, user)
|
||||
m.handleGenericEvent(WebhookEventDelete, models.NewUser(user))
|
||||
}
|
||||
|
||||
func (m Manager) handlePeerCreateEvent(peer domain.Peer) {
|
||||
m.handleGenericEvent(WebhookEventCreate, peer)
|
||||
m.handleGenericEvent(WebhookEventCreate, models.NewPeer(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) {
|
||||
m.handleGenericEvent(WebhookEventDelete, peer)
|
||||
m.handleGenericEvent(WebhookEventDelete, models.NewPeer(peer))
|
||||
}
|
||||
|
||||
func (m Manager) handleInterfaceCreateEvent(iface domain.Interface) {
|
||||
m.handleGenericEvent(WebhookEventCreate, iface)
|
||||
m.handleGenericEvent(WebhookEventCreate, models.NewInterface(iface))
|
||||
}
|
||||
|
||||
func (m Manager) handleInterfaceUpdateEvent(iface domain.Interface) {
|
||||
m.handleGenericEvent(WebhookEventUpdate, iface)
|
||||
m.handleGenericEvent(WebhookEventUpdate, models.NewInterface(iface))
|
||||
}
|
||||
|
||||
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 {
|
||||
m.handleGenericEvent(WebhookEventConnect, peerStatus)
|
||||
m.handleGenericEvent(WebhookEventConnect, models.NewPeerMetrics(peerStatus, peer))
|
||||
} 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) {
|
||||
case domain.User:
|
||||
case models.User:
|
||||
d.Entity = WebhookEntityUser
|
||||
d.Identifier = string(v.Identifier)
|
||||
case domain.Peer:
|
||||
d.Identifier = v.Identifier
|
||||
case models.Peer:
|
||||
d.Entity = WebhookEntityPeer
|
||||
d.Identifier = string(v.Identifier)
|
||||
case domain.Interface:
|
||||
d.Identifier = v.Identifier
|
||||
case models.Interface:
|
||||
d.Entity = WebhookEntityInterface
|
||||
d.Identifier = string(v.Identifier)
|
||||
case domain.PeerStatus:
|
||||
d.Entity = WebhookEntityPeer
|
||||
d.Identifier = string(v.PeerId)
|
||||
d.Identifier = v.Identifier
|
||||
case models.PeerMetrics:
|
||||
d.Entity = WebhookEntityPeerMetric
|
||||
d.Identifier = v.Peer.Identifier
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported payload type: %T", v)
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ type WebhookEntity = string
|
||||
const (
|
||||
WebhookEntityUser WebhookEntity = "user"
|
||||
WebhookEntityPeer WebhookEntity = "peer"
|
||||
WebhookEntityPeerMetric WebhookEntity = "peer_metric"
|
||||
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 {
|
||||
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
|
||||
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 {
|
||||
// publish event if connection state changed
|
||||
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus)
|
||||
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus, peer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user