generate interface and peer configuration filenames in backend only (#395)

This commit is contained in:
Christoph 2025-04-19 13:12:31 +02:00
parent a60feb7fc9
commit 6681dfa96f
14 changed files with 91 additions and 20 deletions

View File

@ -82,6 +82,14 @@ definitions:
description: EnabledPeers is the number of enabled peers for this interface. Only enabled peers are able to connect. description: EnabledPeers is the number of enabled peers for this interface. Only enabled peers are able to connect.
readOnly: true readOnly: true
type: integer type: integer
Filename:
description: |-
Filename is the name of the config file for this interface.
This value is read only and is not settable by the user.
example: wg0.conf
maxLength: 21
readOnly: true
type: string
FirewallMark: FirewallMark:
description: FirewallMark is an optional firewall mark which is used to handle interface traffic. description: FirewallMark is an optional firewall mark which is used to handle interface traffic.
type: integer type: integer
@ -277,6 +285,14 @@ definitions:
items: items:
type: string type: string
type: array type: array
Filename:
description: |-
Filename is the name of the config file for this peer.
This value is read only and is not settable by the user.
example: wg_peer_x.conf
maxLength: 21
readOnly: true
type: string
FirewallMark: FirewallMark:
allOf: allOf:
- $ref: '#/definitions/models.ConfigOption-uint32' - $ref: '#/definitions/models.ConfigOption-uint32'

View File

@ -89,19 +89,11 @@ watch(() => props.visible, async (newValue, oldValue) => {
function download() { function download() {
// credit: https://www.bitdegree.org/learn/javascript-download // credit: https://www.bitdegree.org/learn/javascript-download
let filename = 'WireGuard-Tunnel.conf'
if (selectedPeer.value.DisplayName) {
filename = selectedPeer.value.DisplayName
.replace(/ /g, "_")
.replace(/[^a-zA-Z0-9-_]/g, "")
.substring(0, 16)
+ ".conf"
}
let text = configString.value let text = configString.value
let element = document.createElement('a') let element = document.createElement('a')
element.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(text)) element.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(text))
element.setAttribute('download', filename) element.setAttribute('download', selectedPeer.value.Filename)
element.style.display = 'none' element.style.display = 'none'
document.body.appendChild(element) document.body.appendChild(element)

View File

@ -42,7 +42,8 @@ export function freshInterface() {
PeerDefPostDown: "", PeerDefPostDown: "",
TotalPeers: 0, TotalPeers: 0,
EnabledPeers: 0 EnabledPeers: 0,
Filename: ""
} }
} }
@ -120,6 +121,8 @@ export function freshPeer() {
Overridable: true, Overridable: true,
}, },
Filename: "",
// Internal values // Internal values
IgnoreGlobalSettings: false, IgnoreGlobalSettings: false,
IsSelected: false IsSelected: false

View File

@ -49,12 +49,11 @@ async function download() {
await interfaces.LoadInterfaceConfig(interfaces.GetSelected.Identifier) await interfaces.LoadInterfaceConfig(interfaces.GetSelected.Identifier)
// credit: https://www.bitdegree.org/learn/javascript-download // credit: https://www.bitdegree.org/learn/javascript-download
let filename = interfaces.GetSelected.Identifier + ".conf"
let text = interfaces.configuration let text = interfaces.configuration
let element = document.createElement('a') let element = document.createElement('a')
element.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(text)) element.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(text))
element.setAttribute('download', filename) element.setAttribute('download', interfaces.GetSelected.Filename)
element.style.display = 'none' element.style.display = 'none'
document.body.appendChild(element) document.body.appendChild(element)

View File

@ -1646,6 +1646,10 @@
"EnabledPeers": { "EnabledPeers": {
"type": "integer" "type": "integer"
}, },
"Filename": {
"description": "the filename of the config file, for example: wg0.conf",
"type": "string"
},
"FirewallMark": { "FirewallMark": {
"description": "a firewall mark", "description": "a firewall mark",
"type": "integer" "type": "integer"
@ -1887,6 +1891,10 @@
"type": "string" "type": "string"
} }
}, },
"Filename": {
"description": "the filename of the config file, for example: wg_peer_x.conf",
"type": "string"
},
"FirewallMark": { "FirewallMark": {
"description": "a firewall mark", "description": "a firewall mark",
"allOf": [ "allOf": [

View File

@ -88,6 +88,9 @@ definitions:
type: array type: array
EnabledPeers: EnabledPeers:
type: integer type: integer
Filename:
description: 'the filename of the config file, for example: wg0.conf'
type: string
FirewallMark: FirewallMark:
description: a firewall mark description: a firewall mark
type: integer type: integer
@ -256,6 +259,9 @@ definitions:
items: items:
type: string type: string
type: array type: array
Filename:
description: 'the filename of the config file, for example: wg_peer_x.conf'
type: string
FirewallMark: FirewallMark:
allOf: allOf:
- $ref: '#/definitions/model.ConfigOption-uint32' - $ref: '#/definitions/model.ConfigOption-uint32'

View File

@ -1531,6 +1531,13 @@
"type": "integer", "type": "integer",
"readOnly": true "readOnly": true
}, },
"Filename": {
"description": "Filename is the name of the config file for this interface.\nThis value is read only and is not settable by the user.",
"type": "string",
"maxLength": 21,
"readOnly": true,
"example": "wg0.conf"
},
"FirewallMark": { "FirewallMark": {
"description": "FirewallMark is an optional firewall mark which is used to handle interface traffic.", "description": "FirewallMark is an optional firewall mark which is used to handle interface traffic.",
"type": "integer" "type": "integer"
@ -1799,6 +1806,13 @@
"type": "string" "type": "string"
} }
}, },
"Filename": {
"description": "Filename is the name of the config file for this peer.\nThis value is read only and is not settable by the user.",
"type": "string",
"maxLength": 21,
"readOnly": true,
"example": "wg_peer_x.conf"
},
"FirewallMark": { "FirewallMark": {
"description": "FirewallMark is an optional firewall mark which is used to handle peer traffic.", "description": "FirewallMark is an optional firewall mark which is used to handle peer traffic.",
"allOf": [ "allOf": [

View File

@ -87,6 +87,14 @@ definitions:
Only enabled peers are able to connect. Only enabled peers are able to connect.
readOnly: true readOnly: true
type: integer type: integer
Filename:
description: |-
Filename is the name of the config file for this interface.
This value is read only and is not settable by the user.
example: wg0.conf
maxLength: 21
readOnly: true
type: string
FirewallMark: FirewallMark:
description: FirewallMark is an optional firewall mark which is used to handle description: FirewallMark is an optional firewall mark which is used to handle
interface traffic. interface traffic.
@ -310,6 +318,14 @@ definitions:
items: items:
type: string type: string
type: array type: array
Filename:
description: |-
Filename is the name of the config file for this peer.
This value is read only and is not settable by the user.
example: wg_peer_x.conf
maxLength: 21
readOnly: true
type: string
FirewallMark: FirewallMark:
allOf: allOf:
- $ref: '#/definitions/models.ConfigOption-uint32' - $ref: '#/definitions/models.ConfigOption-uint32'

View File

@ -47,8 +47,9 @@ type Interface struct {
// Calculated values // Calculated values
EnabledPeers int `json:"EnabledPeers"` EnabledPeers int `json:"EnabledPeers"`
TotalPeers int `json:"TotalPeers"` TotalPeers int `json:"TotalPeers"`
Filename string `json:"Filename"` // the filename of the config file, for example: wg0.conf
} }
func NewInterface(src *domain.Interface, peers []domain.Peer) *Interface { func NewInterface(src *domain.Interface, peers []domain.Peer) *Interface {
@ -88,6 +89,7 @@ func NewInterface(src *domain.Interface, peers []domain.Peer) *Interface {
EnabledPeers: 0, EnabledPeers: 0,
TotalPeers: 0, TotalPeers: 0,
Filename: src.GetConfigFileName(),
} }
if len(peers) > 0 { if len(peers) > 0 {

View File

@ -73,6 +73,10 @@ type Peer struct {
PostUp ConfigOption[string] `json:"PostUp"` // action that is executed after the device is up PostUp ConfigOption[string] `json:"PostUp"` // action that is executed after the device is up
PreDown ConfigOption[string] `json:"PreDown"` // action that is executed before the device is down PreDown ConfigOption[string] `json:"PreDown"` // action that is executed before the device is down
PostDown ConfigOption[string] `json:"PostDown"` // action that is executed after the device is down PostDown ConfigOption[string] `json:"PostDown"` // action that is executed after the device is down
// Calculated values
Filename string `json:"Filename"` // the filename of the config file, for example: wg_peer_x.conf
} }
func NewPeer(src *domain.Peer) *Peer { func NewPeer(src *domain.Peer) *Peer {
@ -105,6 +109,7 @@ func NewPeer(src *domain.Peer) *Peer {
PostUp: ConfigOptionFromDomain(src.Interface.PostUp), PostUp: ConfigOptionFromDomain(src.Interface.PostUp),
PreDown: ConfigOptionFromDomain(src.Interface.PreDown), PreDown: ConfigOptionFromDomain(src.Interface.PreDown),
PostDown: ConfigOptionFromDomain(src.Interface.PostDown), PostDown: ConfigOptionFromDomain(src.Interface.PostDown),
Filename: src.GetConfigFileName(),
} }
} }

View File

@ -84,6 +84,9 @@ type Interface struct {
EnabledPeers int `json:"EnabledPeers" readonly:"true"` EnabledPeers int `json:"EnabledPeers" readonly:"true"`
// TotalPeers is the total number of peers for this interface. // TotalPeers is the total number of peers for this interface.
TotalPeers int `json:"TotalPeers" readonly:"true"` TotalPeers int `json:"TotalPeers" readonly:"true"`
// Filename is the name of the config file for this interface.
// This value is read only and is not settable by the user.
Filename string `json:"Filename" example:"wg0.conf" binding:"omitempty,max=21" readonly:"true"`
} }
func NewInterface(src *domain.Interface, peers []domain.Peer) *Interface { func NewInterface(src *domain.Interface, peers []domain.Peer) *Interface {
@ -123,6 +126,7 @@ func NewInterface(src *domain.Interface, peers []domain.Peer) *Interface {
EnabledPeers: 0, EnabledPeers: 0,
TotalPeers: 0, TotalPeers: 0,
Filename: src.GetConfigFileName(),
} }
if len(peers) > 0 { if len(peers) > 0 {

View File

@ -72,6 +72,10 @@ type Peer struct {
PreDown ConfigOption[string] `json:"PreDown"` PreDown ConfigOption[string] `json:"PreDown"`
// PostDown is an optional action that is executed after the device is down. // PostDown is an optional action that is executed after the device is down.
PostDown ConfigOption[string] `json:"PostDown"` PostDown ConfigOption[string] `json:"PostDown"`
// Filename is the name of the config file for this peer.
// This value is read only and is not settable by the user.
Filename string `json:"Filename" example:"wg_peer_x.conf" binding:"omitempty,max=21" readonly:"true"`
} }
func NewPeer(src *domain.Peer) *Peer { func NewPeer(src *domain.Peer) *Peer {
@ -109,6 +113,7 @@ func NewPeer(src *domain.Peer) *Peer {
PostUp: ConfigOptionFromDomain(src.Interface.PostUp), PostUp: ConfigOptionFromDomain(src.Interface.PostUp),
PreDown: ConfigOptionFromDomain(src.Interface.PreDown), PreDown: ConfigOptionFromDomain(src.Interface.PreDown),
PostDown: ConfigOptionFromDomain(src.Interface.PostDown), PostDown: ConfigOptionFromDomain(src.Interface.PostDown),
Filename: src.GetConfigFileName(),
} }
} }

View File

@ -121,8 +121,8 @@ func (i *Interface) CopyCalculatedAttributes(src *Interface) {
} }
func (i *Interface) GetConfigFileName() string { func (i *Interface) GetConfigFileName() string {
filename := internal.TruncateString(string(i.Identifier), 8) filename := allowedFileNameRegex.ReplaceAllString(string(i.Identifier), "")
filename = allowedFileNameRegex.ReplaceAllString(filename, "") filename = internal.TruncateString(filename, 16)
filename += ".conf" filename += ".conf"
return filename return filename

View File

@ -3,7 +3,6 @@ package domain
import ( import (
"fmt" "fmt"
"net" "net"
"regexp"
"strings" "strings"
"time" "time"
@ -87,17 +86,19 @@ func (p *Peer) CopyCalculatedAttributes(src *Peer) {
func (p *Peer) GetConfigFileName() string { func (p *Peer) GetConfigFileName() string {
filename := "" filename := ""
reg := regexp.MustCompile("[^a-zA-Z0-9-_]+")
if p.DisplayName != "" { if p.DisplayName != "" {
filename = p.DisplayName filename = p.DisplayName
filename = strings.ReplaceAll(filename, " ", "_") filename = strings.ReplaceAll(filename, " ", "_")
filename = reg.ReplaceAllString(filename, "") // Eliminate the automatically detected peer part,
// as it makes the filename indistinguishable among multiple auto-detected peers.
filename = strings.ReplaceAll(filename, "Autodetected_", "")
filename = allowedFileNameRegex.ReplaceAllString(filename, "")
filename = internal.TruncateString(filename, 16) filename = internal.TruncateString(filename, 16)
filename += ".conf" filename += ".conf"
} else { } else {
filename = fmt.Sprintf("wg_%s", internal.TruncateString(string(p.Identifier), 8)) filename = fmt.Sprintf("wg_%s", internal.TruncateString(string(p.Identifier), 8))
filename = reg.ReplaceAllString(filename, "") filename = allowedFileNameRegex.ReplaceAllString(filename, "")
filename += ".conf" filename += ".conf"
} }