diff --git a/docs/documentation/rest-api/swagger.yaml b/docs/documentation/rest-api/swagger.yaml index 31aef5c..46c00ae 100644 --- a/docs/documentation/rest-api/swagger.yaml +++ b/docs/documentation/rest-api/swagger.yaml @@ -82,6 +82,14 @@ definitions: description: EnabledPeers is the number of enabled peers for this interface. Only enabled peers are able to connect. readOnly: true 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: description: FirewallMark is an optional firewall mark which is used to handle interface traffic. type: integer @@ -277,6 +285,14 @@ definitions: items: type: string 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: allOf: - $ref: '#/definitions/models.ConfigOption-uint32' diff --git a/frontend/src/components/PeerViewModal.vue b/frontend/src/components/PeerViewModal.vue index 18aa60f..686ee8d 100644 --- a/frontend/src/components/PeerViewModal.vue +++ b/frontend/src/components/PeerViewModal.vue @@ -89,19 +89,11 @@ watch(() => props.visible, async (newValue, oldValue) => { function 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 element = document.createElement('a') 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' document.body.appendChild(element) diff --git a/frontend/src/helpers/models.js b/frontend/src/helpers/models.js index b2d52cf..8f8683e 100644 --- a/frontend/src/helpers/models.js +++ b/frontend/src/helpers/models.js @@ -42,7 +42,8 @@ export function freshInterface() { PeerDefPostDown: "", TotalPeers: 0, - EnabledPeers: 0 + EnabledPeers: 0, + Filename: "" } } @@ -120,6 +121,8 @@ export function freshPeer() { Overridable: true, }, + Filename: "", + // Internal values IgnoreGlobalSettings: false, IsSelected: false diff --git a/frontend/src/views/InterfaceView.vue b/frontend/src/views/InterfaceView.vue index 4322d9c..82912b5 100644 --- a/frontend/src/views/InterfaceView.vue +++ b/frontend/src/views/InterfaceView.vue @@ -49,12 +49,11 @@ async function download() { await interfaces.LoadInterfaceConfig(interfaces.GetSelected.Identifier) // credit: https://www.bitdegree.org/learn/javascript-download - let filename = interfaces.GetSelected.Identifier + ".conf" let text = interfaces.configuration let element = document.createElement('a') 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' document.body.appendChild(element) diff --git a/internal/app/api/core/assets/doc/v0_swagger.json b/internal/app/api/core/assets/doc/v0_swagger.json index 7095a02..3ca06a1 100644 --- a/internal/app/api/core/assets/doc/v0_swagger.json +++ b/internal/app/api/core/assets/doc/v0_swagger.json @@ -1646,6 +1646,10 @@ "EnabledPeers": { "type": "integer" }, + "Filename": { + "description": "the filename of the config file, for example: wg0.conf", + "type": "string" + }, "FirewallMark": { "description": "a firewall mark", "type": "integer" @@ -1887,6 +1891,10 @@ "type": "string" } }, + "Filename": { + "description": "the filename of the config file, for example: wg_peer_x.conf", + "type": "string" + }, "FirewallMark": { "description": "a firewall mark", "allOf": [ diff --git a/internal/app/api/core/assets/doc/v0_swagger.yaml b/internal/app/api/core/assets/doc/v0_swagger.yaml index 374fd82..ab42b60 100644 --- a/internal/app/api/core/assets/doc/v0_swagger.yaml +++ b/internal/app/api/core/assets/doc/v0_swagger.yaml @@ -88,6 +88,9 @@ definitions: type: array EnabledPeers: type: integer + Filename: + description: 'the filename of the config file, for example: wg0.conf' + type: string FirewallMark: description: a firewall mark type: integer @@ -256,6 +259,9 @@ definitions: items: type: string type: array + Filename: + description: 'the filename of the config file, for example: wg_peer_x.conf' + type: string FirewallMark: allOf: - $ref: '#/definitions/model.ConfigOption-uint32' diff --git a/internal/app/api/core/assets/doc/v1_swagger.json b/internal/app/api/core/assets/doc/v1_swagger.json index de6480c..56f3ffb 100644 --- a/internal/app/api/core/assets/doc/v1_swagger.json +++ b/internal/app/api/core/assets/doc/v1_swagger.json @@ -1531,6 +1531,13 @@ "type": "integer", "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": { "description": "FirewallMark is an optional firewall mark which is used to handle interface traffic.", "type": "integer" @@ -1799,6 +1806,13 @@ "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": { "description": "FirewallMark is an optional firewall mark which is used to handle peer traffic.", "allOf": [ diff --git a/internal/app/api/core/assets/doc/v1_swagger.yaml b/internal/app/api/core/assets/doc/v1_swagger.yaml index 9fea2f3..01b8d19 100644 --- a/internal/app/api/core/assets/doc/v1_swagger.yaml +++ b/internal/app/api/core/assets/doc/v1_swagger.yaml @@ -87,6 +87,14 @@ definitions: Only enabled peers are able to connect. readOnly: true 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: description: FirewallMark is an optional firewall mark which is used to handle interface traffic. @@ -310,6 +318,14 @@ definitions: items: type: string 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: allOf: - $ref: '#/definitions/models.ConfigOption-uint32' diff --git a/internal/app/api/v0/model/models_interface.go b/internal/app/api/v0/model/models_interface.go index f339d65..5684178 100644 --- a/internal/app/api/v0/model/models_interface.go +++ b/internal/app/api/v0/model/models_interface.go @@ -47,8 +47,9 @@ type Interface struct { // Calculated values - EnabledPeers int `json:"EnabledPeers"` - TotalPeers int `json:"TotalPeers"` + EnabledPeers int `json:"EnabledPeers"` + 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 { @@ -88,6 +89,7 @@ func NewInterface(src *domain.Interface, peers []domain.Peer) *Interface { EnabledPeers: 0, TotalPeers: 0, + Filename: src.GetConfigFileName(), } if len(peers) > 0 { diff --git a/internal/app/api/v0/model/models_peer.go b/internal/app/api/v0/model/models_peer.go index aca304d..9612dc6 100644 --- a/internal/app/api/v0/model/models_peer.go +++ b/internal/app/api/v0/model/models_peer.go @@ -73,6 +73,10 @@ type Peer struct { 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 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 { @@ -105,6 +109,7 @@ func NewPeer(src *domain.Peer) *Peer { PostUp: ConfigOptionFromDomain(src.Interface.PostUp), PreDown: ConfigOptionFromDomain(src.Interface.PreDown), PostDown: ConfigOptionFromDomain(src.Interface.PostDown), + Filename: src.GetConfigFileName(), } } diff --git a/internal/app/api/v1/models/models_interface.go b/internal/app/api/v1/models/models_interface.go index dd6653d..ed14c2c 100644 --- a/internal/app/api/v1/models/models_interface.go +++ b/internal/app/api/v1/models/models_interface.go @@ -84,6 +84,9 @@ type Interface struct { EnabledPeers int `json:"EnabledPeers" readonly:"true"` // TotalPeers is the total number of peers for this interface. 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 { @@ -123,6 +126,7 @@ func NewInterface(src *domain.Interface, peers []domain.Peer) *Interface { EnabledPeers: 0, TotalPeers: 0, + Filename: src.GetConfigFileName(), } if len(peers) > 0 { diff --git a/internal/app/api/v1/models/models_peer.go b/internal/app/api/v1/models/models_peer.go index 1bee282..99679f9 100644 --- a/internal/app/api/v1/models/models_peer.go +++ b/internal/app/api/v1/models/models_peer.go @@ -72,6 +72,10 @@ type Peer struct { PreDown ConfigOption[string] `json:"PreDown"` // PostDown is an optional action that is executed after the device is down. 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 { @@ -109,6 +113,7 @@ func NewPeer(src *domain.Peer) *Peer { PostUp: ConfigOptionFromDomain(src.Interface.PostUp), PreDown: ConfigOptionFromDomain(src.Interface.PreDown), PostDown: ConfigOptionFromDomain(src.Interface.PostDown), + Filename: src.GetConfigFileName(), } } diff --git a/internal/domain/interface.go b/internal/domain/interface.go index 0007c9f..977f7d3 100644 --- a/internal/domain/interface.go +++ b/internal/domain/interface.go @@ -121,8 +121,8 @@ func (i *Interface) CopyCalculatedAttributes(src *Interface) { } func (i *Interface) GetConfigFileName() string { - filename := internal.TruncateString(string(i.Identifier), 8) - filename = allowedFileNameRegex.ReplaceAllString(filename, "") + filename := allowedFileNameRegex.ReplaceAllString(string(i.Identifier), "") + filename = internal.TruncateString(filename, 16) filename += ".conf" return filename diff --git a/internal/domain/peer.go b/internal/domain/peer.go index 78cf2d7..22cb5ae 100644 --- a/internal/domain/peer.go +++ b/internal/domain/peer.go @@ -3,7 +3,6 @@ package domain import ( "fmt" "net" - "regexp" "strings" "time" @@ -87,17 +86,19 @@ func (p *Peer) CopyCalculatedAttributes(src *Peer) { func (p *Peer) GetConfigFileName() string { filename := "" - reg := regexp.MustCompile("[^a-zA-Z0-9-_]+") if p.DisplayName != "" { filename = p.DisplayName 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 += ".conf" } else { filename = fmt.Sprintf("wg_%s", internal.TruncateString(string(p.Identifier), 8)) - filename = reg.ReplaceAllString(filename, "") + filename = allowedFileNameRegex.ReplaceAllString(filename, "") filename += ".conf" }