mirror of https://github.com/h44z/wg-portal.git
				
				
				
			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=", | ||||
|     "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" | ||||
|     "Status": { | ||||
|       "UpdatedAt": "2025-06-27T22:20:08.734900034+02:00", | ||||
|       "IsConnected": true, | ||||
|       "IsPingable": false, | ||||
|       "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) | ||||
| 	} | ||||
|  |  | |||
|  | @ -34,9 +34,10 @@ func (d *WebhookData) Serialize() (io.Reader, error) { | |||
| type WebhookEntity = string | ||||
| 
 | ||||
| const ( | ||||
| 	WebhookEntityUser      WebhookEntity = "user" | ||||
| 	WebhookEntityPeer      WebhookEntity = "peer" | ||||
| 	WebhookEntityInterface WebhookEntity = "interface" | ||||
| 	WebhookEntityUser       WebhookEntity = "user" | ||||
| 	WebhookEntityPeer       WebhookEntity = "peer" | ||||
| 	WebhookEntityPeerMetric WebhookEntity = "peer_metric" | ||||
| 	WebhookEntityInterface  WebhookEntity = "interface" | ||||
| ) | ||||
| 
 | ||||
| type WebhookEvent = string | ||||
|  |  | |||
|  | @ -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, | ||||
| 	} | ||||
| } | ||||
|  | @ -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(), | ||||
| 	} | ||||
| } | ||||
|  | @ -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), | ||||
| 	} | ||||
| } | ||||
|  | @ -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…
	
		Reference in New Issue