mirror of
https://github.com/h44z/wg-portal.git
synced 2025-10-06 16:36:18 +00:00
Compare commits
2 Commits
improve_we
...
peer_state
Author | SHA1 | Date | |
---|---|---|---|
|
1d7dd26290 | ||
|
3c72a26e91 |
@@ -50,7 +50,7 @@ COPY --from=builder /build/dist/wg-portal /
|
||||
######
|
||||
# Final image
|
||||
######
|
||||
FROM alpine:3.22
|
||||
FROM alpine:3.19
|
||||
# Install OS-level dependencies
|
||||
RUN apk add --no-cache bash curl iptables nftables openresolv wireguard-tools
|
||||
# Setup timezone
|
||||
|
@@ -76,7 +76,6 @@ auth:
|
||||
webauthn:
|
||||
enabled: true
|
||||
min_password_length: 16
|
||||
hide_login_form: false
|
||||
|
||||
web:
|
||||
listening_address: :8888
|
||||
@@ -355,12 +354,6 @@ Some core authentication options are shared across all providers, while others a
|
||||
The default admin password strength is also enforced by this setting.
|
||||
- **Important:** The password should be strong and secure. It is recommended to use a password with at least 16 characters, including uppercase and lowercase letters, numbers, and special characters.
|
||||
|
||||
### `hide_login_form`
|
||||
- **Default:** `false`
|
||||
- **Description:** If `true`, the login form is hidden and only the OIDC, OAuth, LDAP, or WebAuthn providers are shown. This is useful if you want to enforce a specific authentication method.
|
||||
If no social login providers are configured, the login form is always shown, regardless of this setting.
|
||||
- **Important:** You can still access the login form by adding the `?all` query parameter to the login URL (e.g. https://wg.portal/#/login?all).
|
||||
|
||||
---
|
||||
|
||||
### OIDC
|
||||
@@ -673,6 +666,19 @@ 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,12 +38,11 @@ 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 models are supported for webhook events:
|
||||
The following entity types can trigger webhooks:
|
||||
|
||||
- `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.
|
||||
- `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.
|
||||
|
||||
## Payload Structure
|
||||
|
||||
@@ -52,234 +51,36 @@ A common shell structure for webhook payloads is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
"event": "create",
|
||||
"entity": "user",
|
||||
"identifier": "the-user-identifier",
|
||||
"payload": {
|
||||
// The payload of the event, e.g. a Peer model.
|
||||
// Detailed model descriptions are provided below.
|
||||
// The payload of the event, e.g. peer data.
|
||||
// Check the API documentation for the exact structure.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Payload Models
|
||||
|
||||
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
|
||||
### Example Payload
|
||||
|
||||
The following payload is an example of a webhook event when a peer connects to the VPN:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "connect",
|
||||
"entity": "peer_metric",
|
||||
"identifier": "Fb5TaziAs1WrPBjC/MFbWsIelVXvi0hDKZ3YQM9wmU8=",
|
||||
"payload": {
|
||||
"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
|
||||
"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"
|
||||
}
|
||||
}
|
||||
```
|
@@ -16,10 +16,7 @@ const password = ref("")
|
||||
const usernameInvalid = computed(() => username.value === "")
|
||||
const passwordInvalid = computed(() => password.value === "")
|
||||
const disableLoginBtn = computed(() => username.value === "" || password.value === "" || loggingIn.value)
|
||||
const showLoginForm = computed(() => {
|
||||
console.log(router.currentRoute.value.query)
|
||||
return settings.Setting('LoginFormVisible') || router.currentRoute.value.query.hasOwnProperty('all');
|
||||
});
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await settings.LoadSettings()
|
||||
@@ -101,7 +98,7 @@ const externalLogin = function (provider) {
|
||||
</div></div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<fieldset v-if="showLoginForm">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="inputUsername">{{ $t('login.username.label') }}</label>
|
||||
<div class="input-group mb-3">
|
||||
@@ -121,40 +118,19 @@ const externalLogin = function (provider) {
|
||||
</div>
|
||||
|
||||
<div class="row mt-5 mb-2">
|
||||
<div class="col-sm-4 col-xs-12">
|
||||
<button :disabled="disableLoginBtn" class="btn btn-primary mb-2" type="submit" @click.prevent="login">
|
||||
<div class="col-lg-4">
|
||||
<button :disabled="disableLoginBtn" class="btn btn-primary" type="submit" @click.prevent="login">
|
||||
{{ $t('login.button') }} <div v-if="loggingIn" class="d-inline"><i class="ms-2 fa-solid fa-circle-notch fa-spin"></i></div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-sm-8 col-xs-12 text-sm-end">
|
||||
<div class="col-lg-8 mb-2 text-end">
|
||||
<button v-if="settings.Setting('WebAuthnEnabled')" class="btn btn-primary" type="submit" @click.prevent="loginWebAuthn">
|
||||
{{ $t('login.button-webauthn') }} <div v-if="loggingIn" class="d-inline"><i class="ms-2 fa-solid fa-circle-notch fa-spin"></i></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4 d-flex">
|
||||
<div class="col-lg-12 d-flex mb-2">
|
||||
<!-- OpenIdConnect / OAUTH providers -->
|
||||
<button v-for="(provider, idx) in auth.LoginProviders" :key="provider.Identifier" :class="{'ms-1':idx > 0}"
|
||||
:disabled="loggingIn" :title="provider.Name" class="btn btn-outline-primary flex-fill"
|
||||
v-html="provider.Name" @click.prevent="externalLogin(provider)"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset v-else>
|
||||
<div class="row mt-1 mb-2" v-if="settings.Setting('WebAuthnEnabled')">
|
||||
<div class="col-lg-12 d-flex mb-2">
|
||||
<button class="btn btn-outline-primary flex-fill" type="submit" @click.prevent="loginWebAuthn">
|
||||
{{ $t('login.button-webauthn') }} <div v-if="loggingIn" class="d-inline"><i class="ms-2 fa-solid fa-circle-notch fa-spin"></i></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-1 d-flex">
|
||||
<div class="row mt-5 d-flex">
|
||||
<div class="col-lg-12 d-flex mb-2">
|
||||
<!-- OpenIdConnect / OAUTH providers -->
|
||||
<button v-for="(provider, idx) in auth.LoginProviders" :key="provider.Identifier" :class="{'ms-1':idx > 0}"
|
||||
@@ -168,6 +144,7 @@ const externalLogin = function (provider) {
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2231,9 +2231,6 @@
|
||||
"ApiAdminOnly": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"LoginFormVisible": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"MailLinkOnly": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@@ -381,8 +381,6 @@ definitions:
|
||||
properties:
|
||||
ApiAdminOnly:
|
||||
type: boolean
|
||||
LoginFormVisible:
|
||||
type: boolean
|
||||
MailLinkOnly:
|
||||
type: boolean
|
||||
MinPasswordLength:
|
||||
|
@@ -96,13 +96,10 @@ func (e ConfigEndpoint) handleSettingsGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
sessionUser := domain.GetUserInfo(r.Context())
|
||||
|
||||
hasSocialLogin := len(e.cfg.Auth.OAuth) > 0 || len(e.cfg.Auth.OpenIDConnect) > 0 || e.cfg.Auth.WebAuthn.Enabled
|
||||
|
||||
// For anonymous users, we return the settings object with minimal information
|
||||
if sessionUser.Id == domain.CtxUnknownUserId || sessionUser.Id == "" {
|
||||
respond.JSON(w, http.StatusOK, model.Settings{
|
||||
WebAuthnEnabled: e.cfg.Auth.WebAuthn.Enabled,
|
||||
LoginFormVisible: !e.cfg.Auth.HideLoginForm || !hasSocialLogin,
|
||||
WebAuthnEnabled: e.cfg.Auth.WebAuthn.Enabled,
|
||||
})
|
||||
} else {
|
||||
respond.JSON(w, http.StatusOK, model.Settings{
|
||||
@@ -112,7 +109,6 @@ func (e ConfigEndpoint) handleSettingsGet() http.HandlerFunc {
|
||||
ApiAdminOnly: e.cfg.Advanced.ApiAdminOnly,
|
||||
WebAuthnEnabled: e.cfg.Auth.WebAuthn.Enabled,
|
||||
MinPasswordLength: e.cfg.Auth.MinPasswordLength,
|
||||
LoginFormVisible: !e.cfg.Auth.HideLoginForm || !hasSocialLogin,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -12,5 +12,4 @@ type Settings struct {
|
||||
ApiAdminOnly bool `json:"ApiAdminOnly"`
|
||||
WebAuthnEnabled bool `json:"WebAuthnEnabled"`
|
||||
MinPasswordLength int `json:"MinPasswordLength"`
|
||||
LoginFormVisible bool `json:"LoginFormVisible"`
|
||||
}
|
||||
|
@@ -71,7 +71,7 @@ func NewMailManager(
|
||||
users UserDatabaseRepo,
|
||||
wg WireguardDatabaseRepo,
|
||||
) (*Manager, error) {
|
||||
tplHandler, err := newTemplateHandler(cfg.Web.ExternalUrl, cfg.Web.SiteTitle)
|
||||
tplHandler, err := newTemplateHandler(cfg.Web.ExternalUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize template handler: %w", err)
|
||||
}
|
||||
|
@@ -17,12 +17,11 @@ var TemplateFiles embed.FS
|
||||
// TemplateHandler is a struct that holds the html and text templates.
|
||||
type TemplateHandler struct {
|
||||
portalUrl string
|
||||
portalName string
|
||||
htmlTemplates *htmlTemplate.Template
|
||||
textTemplates *template.Template
|
||||
}
|
||||
|
||||
func newTemplateHandler(portalUrl, portalName string) (*TemplateHandler, error) {
|
||||
func newTemplateHandler(portalUrl string) (*TemplateHandler, error) {
|
||||
htmlTemplateCache, err := htmlTemplate.New("Html").ParseFS(TemplateFiles, "tpl_files/*.gohtml")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse html template files: %w", err)
|
||||
@@ -35,7 +34,6 @@ func newTemplateHandler(portalUrl, portalName string) (*TemplateHandler, error)
|
||||
|
||||
handler := &TemplateHandler{
|
||||
portalUrl: portalUrl,
|
||||
portalName: portalName,
|
||||
htmlTemplates: htmlTemplateCache,
|
||||
textTemplates: txtTemplateCache,
|
||||
}
|
||||
@@ -83,7 +81,6 @@ func (c TemplateHandler) GetConfigMailWithAttachment(user *domain.User, cfgName,
|
||||
"ConfigFileName": cfgName,
|
||||
"QrcodePngName": qrName,
|
||||
"PortalUrl": c.portalUrl,
|
||||
"PortalName": c.portalName,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to execute template mail_with_attachment.gotpl: %w", err)
|
||||
@@ -94,7 +91,6 @@ func (c TemplateHandler) GetConfigMailWithAttachment(user *domain.User, cfgName,
|
||||
"ConfigFileName": cfgName,
|
||||
"QrcodePngName": qrName,
|
||||
"PortalUrl": c.portalUrl,
|
||||
"PortalName": c.portalName,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to execute template mail_with_attachment.gohtml: %w", err)
|
||||
|
@@ -19,7 +19,7 @@
|
||||
<!--[if !mso]><!-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Muli:400,400i,700,700i" rel="stylesheet" />
|
||||
<!--<![endif]-->
|
||||
<title>{{$.PortalName}}</title>
|
||||
<title>Email Template</title>
|
||||
<!--[if gte mso 9]>
|
||||
<style type="text/css" media="all">
|
||||
sup { font-size: 100% !important; }
|
||||
@@ -143,7 +143,7 @@
|
||||
<td align="left">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="blue-button text-button" style="background:#000000; color:#ffffff; font-family:'Muli', Arial,sans-serif; font-size:14px; line-height:18px; padding:12px 30px; text-align:center; border-radius:0px 22px 22px 22px; font-weight:bold;"><a href="https://www.wireguard.com/install" target="_blank" class="link-white" style="color:#ffffff; text-decoration:none;"><span class="link-white" style="color:#ffffff; text-decoration:none;">Download WireGuard VPN Client</span></a></td>
|
||||
<td class="blue-button text-button" style="background:#000000; color:#c1cddc; font-family:'Muli', Arial,sans-serif; font-size:14px; line-height:18px; padding:12px 30px; text-align:center; border-radius:0px 22px 22px 22px; font-weight:bold;"><a href="https://www.wireguard.com/install" target="_blank" class="link-white" style="color:#ffffff; text-decoration:none;"><span class="link-white" style="color:#ffffff; text-decoration:none;">Download WireGuard VPN Client</span></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
@@ -167,10 +167,10 @@
|
||||
<td class="p30-15 bbrr" style="padding: 50px 30px; border-radius:0px 0px 26px 26px;" bgcolor="#ffffff">
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="text-footer1 pb10" style="color:#000000; font-family:'Muli', Arial,sans-serif; font-size:16px; line-height:20px; text-align:center; padding-bottom:10px;">This mail was generated by {{$.PortalName}}.</td>
|
||||
<td class="text-footer1 pb10" style="color:#000000; font-family:'Muli', Arial,sans-serif; font-size:16px; line-height:20px; text-align:center; padding-bottom:10px;">This mail was generated using WireGuard Portal.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-footer2" style="color:#000000; font-family:'Muli', Arial,sans-serif; font-size:12px; line-height:26px; text-align:center;"><a href="{{$.PortalUrl}}" target="_blank" rel="noopener noreferrer" class="link" style="color:#000000; text-decoration:none;"><span class="link" style="color:#000000; text-decoration:none;">Visit {{$.PortalName}}</span></a></td>
|
||||
<td class="text-footer2" style="color:#000000; font-family:'Muli', Arial,sans-serif; font-size:12px; line-height:26px; text-align:center;"><a href="{{$.PortalUrl}}" target="_blank" rel="noopener noreferrer" class="link" style="color:#000000; text-decoration:none;"><span class="link" style="color:#000000; text-decoration:none;">Visit WireGuard Portal</span></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
|
@@ -20,5 +20,5 @@ You can download and install the WireGuard VPN client from:
|
||||
https://www.wireguard.com/install/
|
||||
|
||||
|
||||
This mail was generated by {{$.PortalName}}.
|
||||
This mail was generated using WireGuard Portal.
|
||||
{{$.PortalUrl}}
|
@@ -19,7 +19,7 @@
|
||||
<!--[if !mso]><!-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Muli:400,400i,700,700i" rel="stylesheet" />
|
||||
<!--<![endif]-->
|
||||
<title>{{$.PortalName}}</title>
|
||||
<title>Email Template</title>
|
||||
<!--[if gte mso 9]>
|
||||
<style type="text/css" media="all">
|
||||
sup { font-size: 100% !important; }
|
||||
@@ -143,7 +143,7 @@
|
||||
<td align="left">
|
||||
<table border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="blue-button text-button" style="background:#000000; color:#ffffff; font-family:'Muli', Arial,sans-serif; font-size:14px; line-height:18px; padding:12px 30px; text-align:center; border-radius:0px 22px 22px 22px; font-weight:bold;"><a href="https://www.wireguard.com/install" target="_blank" class="link-white" style="color:#ffffff; text-decoration:none;"><span class="link-white" style="color:#ffffff; text-decoration:none;">Download WireGuard VPN Client</span></a></td>
|
||||
<td class="blue-button text-button" style="background:#000000; color:#c1cddc; font-family:'Muli', Arial,sans-serif; font-size:14px; line-height:18px; padding:12px 30px; text-align:center; border-radius:0px 22px 22px 22px; font-weight:bold;"><a href="https://www.wireguard.com/install" target="_blank" class="link-white" style="color:#ffffff; text-decoration:none;"><span class="link-white" style="color:#ffffff; text-decoration:none;">Download WireGuard VPN Client</span></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
@@ -167,10 +167,10 @@
|
||||
<td class="p30-15 bbrr" style="padding: 50px 30px; border-radius:0px 0px 26px 26px;" bgcolor="#ffffff">
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td class="text-footer1 pb10" style="color:#000000; font-family:'Muli', Arial,sans-serif; font-size:16px; line-height:20px; text-align:center; padding-bottom:10px;">This mail was generated by {{$.PortalName}}.</td>
|
||||
<td class="text-footer1 pb10" style="color:#000000; font-family:'Muli', Arial,sans-serif; font-size:16px; line-height:20px; text-align:center; padding-bottom:10px;">This mail was generated using WireGuard Portal.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-footer2" style="color:#000000; font-family:'Muli', Arial,sans-serif; font-size:12px; line-height:26px; text-align:center;"><a href="{{$.PortalUrl}}" target="_blank" rel="noopener noreferrer" class="link" style="color:#000000; text-decoration:none;"><span class="link" style="color:#000000; text-decoration:none;">Visit {{$.PortalName}}</span></a></td>
|
||||
<td class="text-footer2" style="color:#000000; font-family:'Muli', Arial,sans-serif; font-size:12px; line-height:26px; text-align:center;"><a href="{{$.PortalUrl}}" target="_blank" rel="noopener noreferrer" class="link" style="color:#000000; text-decoration:none;"><span class="link" style="color:#000000; text-decoration:none;">Visit WireGuard Portal</span></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
|
@@ -20,5 +20,5 @@ You can download and install the WireGuard VPN client from:
|
||||
https://www.wireguard.com/install/
|
||||
|
||||
|
||||
This mail was generated by {{$.PortalName}}.
|
||||
This mail was generated using WireGuard Portal.
|
||||
{{$.PortalUrl}}
|
@@ -8,7 +8,6 @@ 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"
|
||||
)
|
||||
@@ -102,46 +101,46 @@ func (m Manager) sendWebhook(ctx context.Context, data io.Reader) error {
|
||||
}
|
||||
|
||||
func (m Manager) handleUserCreateEvent(user domain.User) {
|
||||
m.handleGenericEvent(WebhookEventCreate, models.NewUser(user))
|
||||
m.handleGenericEvent(WebhookEventCreate, user)
|
||||
}
|
||||
|
||||
func (m Manager) handleUserUpdateEvent(user domain.User) {
|
||||
m.handleGenericEvent(WebhookEventUpdate, models.NewUser(user))
|
||||
m.handleGenericEvent(WebhookEventUpdate, user)
|
||||
}
|
||||
|
||||
func (m Manager) handleUserDeleteEvent(user domain.User) {
|
||||
m.handleGenericEvent(WebhookEventDelete, models.NewUser(user))
|
||||
m.handleGenericEvent(WebhookEventDelete, user)
|
||||
}
|
||||
|
||||
func (m Manager) handlePeerCreateEvent(peer domain.Peer) {
|
||||
m.handleGenericEvent(WebhookEventCreate, models.NewPeer(peer))
|
||||
m.handleGenericEvent(WebhookEventCreate, peer)
|
||||
}
|
||||
|
||||
func (m Manager) handlePeerUpdateEvent(peer domain.Peer) {
|
||||
m.handleGenericEvent(WebhookEventUpdate, models.NewPeer(peer))
|
||||
m.handleGenericEvent(WebhookEventUpdate, peer)
|
||||
}
|
||||
|
||||
func (m Manager) handlePeerDeleteEvent(peer domain.Peer) {
|
||||
m.handleGenericEvent(WebhookEventDelete, models.NewPeer(peer))
|
||||
m.handleGenericEvent(WebhookEventDelete, peer)
|
||||
}
|
||||
|
||||
func (m Manager) handleInterfaceCreateEvent(iface domain.Interface) {
|
||||
m.handleGenericEvent(WebhookEventCreate, models.NewInterface(iface))
|
||||
m.handleGenericEvent(WebhookEventCreate, iface)
|
||||
}
|
||||
|
||||
func (m Manager) handleInterfaceUpdateEvent(iface domain.Interface) {
|
||||
m.handleGenericEvent(WebhookEventUpdate, models.NewInterface(iface))
|
||||
m.handleGenericEvent(WebhookEventUpdate, iface)
|
||||
}
|
||||
|
||||
func (m Manager) handleInterfaceDeleteEvent(iface domain.Interface) {
|
||||
m.handleGenericEvent(WebhookEventDelete, models.NewInterface(iface))
|
||||
m.handleGenericEvent(WebhookEventDelete, iface)
|
||||
}
|
||||
|
||||
func (m Manager) handlePeerStateChangeEvent(peerStatus domain.PeerStatus, peer domain.Peer) {
|
||||
func (m Manager) handlePeerStateChangeEvent(peerStatus domain.PeerStatus) {
|
||||
if peerStatus.IsConnected {
|
||||
m.handleGenericEvent(WebhookEventConnect, models.NewPeerMetrics(peerStatus, peer))
|
||||
m.handleGenericEvent(WebhookEventConnect, peerStatus)
|
||||
} else {
|
||||
m.handleGenericEvent(WebhookEventDisconnect, models.NewPeerMetrics(peerStatus, peer))
|
||||
m.handleGenericEvent(WebhookEventDisconnect, peerStatus)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,18 +177,18 @@ func (m Manager) createWebhookData(action WebhookEvent, payload any) (*WebhookDa
|
||||
}
|
||||
|
||||
switch v := payload.(type) {
|
||||
case models.User:
|
||||
case domain.User:
|
||||
d.Entity = WebhookEntityUser
|
||||
d.Identifier = v.Identifier
|
||||
case models.Peer:
|
||||
d.Identifier = string(v.Identifier)
|
||||
case domain.Peer:
|
||||
d.Entity = WebhookEntityPeer
|
||||
d.Identifier = v.Identifier
|
||||
case models.Interface:
|
||||
d.Identifier = string(v.Identifier)
|
||||
case domain.Interface:
|
||||
d.Entity = WebhookEntityInterface
|
||||
d.Identifier = v.Identifier
|
||||
case models.PeerMetrics:
|
||||
d.Entity = WebhookEntityPeerMetric
|
||||
d.Identifier = v.Peer.Identifier
|
||||
d.Identifier = string(v.Identifier)
|
||||
case domain.PeerStatus:
|
||||
d.Entity = WebhookEntityPeer
|
||||
d.Identifier = string(v.PeerId)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported payload type: %T", v)
|
||||
}
|
||||
|
@@ -34,10 +34,9 @@ func (d *WebhookData) Serialize() (io.Reader, error) {
|
||||
type WebhookEntity = string
|
||||
|
||||
const (
|
||||
WebhookEntityUser WebhookEntity = "user"
|
||||
WebhookEntityPeer WebhookEntity = "peer"
|
||||
WebhookEntityPeerMetric WebhookEntity = "peer_metric"
|
||||
WebhookEntityInterface WebhookEntity = "interface"
|
||||
WebhookEntityUser WebhookEntity = "user"
|
||||
WebhookEntityPeer WebhookEntity = "peer"
|
||||
WebhookEntityInterface WebhookEntity = "interface"
|
||||
)
|
||||
|
||||
type WebhookEvent = string
|
||||
|
@@ -1,99 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
@@ -1,89 +0,0 @@
|
||||
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(),
|
||||
}
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
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),
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
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,14 +213,8 @@ 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, *peerModel)
|
||||
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,7 +356,7 @@ func (c *StatisticsCollector) pingWorker(ctx context.Context) {
|
||||
|
||||
if connectionStateChanged {
|
||||
// publish event if connection state changed
|
||||
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus, peer)
|
||||
c.bus.Publish(app.TopicPeerStateChanged, newPeerStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,9 +21,6 @@ type Auth struct {
|
||||
// MinPasswordLength is the minimum password length for user accounts. This also applies to the admin user.
|
||||
// It is encouraged to set this value to at least 16 characters.
|
||||
MinPasswordLength int `yaml:"min_password_length"`
|
||||
// HideLoginForm specifies whether the login form should be hidden. If no social login providers are configured,
|
||||
// the login form will be shown regardless of this setting.
|
||||
HideLoginForm bool `yaml:"hide_login_form"`
|
||||
}
|
||||
|
||||
// BaseFields contains the basic fields that are used to map user information from the authentication providers.
|
||||
|
@@ -95,9 +95,6 @@ func (c *Config) LogStartupValues() {
|
||||
"oidcProviders", len(c.Auth.OpenIDConnect),
|
||||
"oauthProviders", len(c.Auth.OAuth),
|
||||
"ldapProviders", len(c.Auth.Ldap),
|
||||
"webauthnEnabled", c.Auth.WebAuthn.Enabled,
|
||||
"minPasswordLength", c.Auth.MinPasswordLength,
|
||||
"hideLoginForm", c.Auth.HideLoginForm,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -172,7 +169,6 @@ func defaultConfig() *Config {
|
||||
|
||||
cfg.Auth.WebAuthn.Enabled = true
|
||||
cfg.Auth.MinPasswordLength = 16
|
||||
cfg.Auth.HideLoginForm = false
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
Reference in New Issue
Block a user