mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-07-16 18:16:59 +00:00
Backup and restore for AWG is done
This commit is contained in:
parent
434c236210
commit
57583b6747
@ -509,6 +509,7 @@ class WireguardConfiguration:
|
||||
self.createDatabase()
|
||||
with open(self.configPath, "w+") as configFile:
|
||||
self.__parser.write(configFile)
|
||||
print(f"[WGDashboard] Configuration file {self.configPath} created")
|
||||
self.__initPeersList()
|
||||
|
||||
print(f"[WGDashboard] Initialized Configuration: {name}")
|
||||
@ -2086,11 +2087,14 @@ def API_getWireguardConfigurations():
|
||||
def API_addWireguardConfiguration():
|
||||
data = request.get_json()
|
||||
requiredKeys = [
|
||||
"ConfigurationName", "Address", "ListenPort", "PrivateKey"
|
||||
"ConfigurationName", "Address", "ListenPort", "PrivateKey", "Protocol"
|
||||
]
|
||||
for i in requiredKeys:
|
||||
if i not in data.keys():
|
||||
return ResponseObject(False, "Please provide all required parameters.")
|
||||
|
||||
if data.get("Protocol") not in ProtocolsEnabled():
|
||||
return ResponseObject(False, "Please provide a valid protocol: wg / awg.")
|
||||
|
||||
# Check duplicate names, ports, address
|
||||
for i in WireguardConfigurations.values():
|
||||
@ -2110,22 +2114,27 @@ def API_addWireguardConfiguration():
|
||||
"Address")
|
||||
|
||||
if "Backup" in data.keys():
|
||||
if not os.path.exists(os.path.join(
|
||||
DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||
'WGDashboard_Backup',
|
||||
data["Backup"])) or not os.path.exists(os.path.join(
|
||||
DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||
'WGDashboard_Backup',
|
||||
data["Backup"].replace('.conf', '.sql'))):
|
||||
return ResponseObject(False, "Backup file does not exist")
|
||||
path = {
|
||||
"wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||
"awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
|
||||
}
|
||||
|
||||
if (os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"])) and
|
||||
os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
||||
protocol = "wg"
|
||||
elif (os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"])) and
|
||||
os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
||||
protocol = "awg"
|
||||
else:
|
||||
return ResponseObject(False, "Backup does not exist")
|
||||
|
||||
shutil.copy(
|
||||
os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', data["Backup"]),
|
||||
os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{data["ConfigurationName"]}.conf')
|
||||
os.path.join(path[protocol], 'WGDashboard_Backup', data["Backup"]),
|
||||
os.path.join(path[protocol], f'{data["ConfigurationName"]}.conf')
|
||||
)
|
||||
WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data, name=data['ConfigurationName'])
|
||||
WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data, name=data['ConfigurationName']) if protocol == 'wg' else AmneziaWireguardConfiguration(data=data, name=data['ConfigurationName'])
|
||||
else:
|
||||
WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data)
|
||||
WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data) if data.get('Protocol') == 'wg' else AmneziaWireguardConfiguration(data=data)
|
||||
return ResponseObject()
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration/')
|
||||
@ -2197,30 +2206,32 @@ def API_getAllWireguardConfigurationBackup():
|
||||
b = WireguardConfigurations[i].getBackups(True)
|
||||
if len(b) > 0:
|
||||
data['ExistingConfigurations'][i] = WireguardConfigurations[i].getBackups(True)
|
||||
|
||||
directory = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup')
|
||||
files = [(file, os.path.getctime(os.path.join(directory, file)))
|
||||
for file in os.listdir(directory) if os.path.isfile(os.path.join(directory, file))]
|
||||
files.sort(key=lambda x: x[1], reverse=True)
|
||||
|
||||
for f, ct in files:
|
||||
if RegexMatch(r"^(.*)_(.*)\.(conf)$", f):
|
||||
s = re.search(r"^(.*)_(.*)\.(conf)$", f)
|
||||
name = s.group(1)
|
||||
if name not in existingConfiguration:
|
||||
if name not in data['NonExistingConfigurations'].keys():
|
||||
data['NonExistingConfigurations'][name] = []
|
||||
|
||||
date = s.group(2)
|
||||
d = {
|
||||
"filename": f,
|
||||
"backupDate": date,
|
||||
"content": open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f), 'r').read()
|
||||
}
|
||||
if f.replace(".conf", ".sql") in list(os.listdir(directory)):
|
||||
d['database'] = True
|
||||
d['databaseContent'] = open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f.replace(".conf", ".sql")), 'r').read()
|
||||
data['NonExistingConfigurations'][name].append(d)
|
||||
|
||||
for protocol in ProtocolsEnabled():
|
||||
directory = os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup')
|
||||
files = [(file, os.path.getctime(os.path.join(directory, file)))
|
||||
for file in os.listdir(directory) if os.path.isfile(os.path.join(directory, file))]
|
||||
files.sort(key=lambda x: x[1], reverse=True)
|
||||
|
||||
for f, ct in files:
|
||||
if RegexMatch(r"^(.*)_(.*)\.(conf)$", f):
|
||||
s = re.search(r"^(.*)_(.*)\.(conf)$", f)
|
||||
name = s.group(1)
|
||||
if name not in existingConfiguration:
|
||||
if name not in data['NonExistingConfigurations'].keys():
|
||||
data['NonExistingConfigurations'][name] = []
|
||||
|
||||
date = s.group(2)
|
||||
d = {
|
||||
"protocol": protocol,
|
||||
"filename": f,
|
||||
"backupDate": date,
|
||||
"content": open(os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup', f), 'r').read()
|
||||
}
|
||||
if f.replace(".conf", ".sql") in list(os.listdir(directory)):
|
||||
d['database'] = True
|
||||
d['databaseContent'] = open(os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup', f.replace(".conf", ".sql")), 'r').read()
|
||||
data['NonExistingConfigurations'][name].append(d)
|
||||
return ResponseObject(data=data)
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/createWireguardConfigurationBackup')
|
||||
@ -2944,6 +2955,9 @@ def API_SystemStatus():
|
||||
# pass
|
||||
return ResponseObject(data=status)
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/protocolsEnabled')
|
||||
def API_ProtocolsEnabled():
|
||||
return ResponseObject(data=ProtocolsEnabled())
|
||||
|
||||
@app.get(f'{APP_PREFIX}/')
|
||||
def index():
|
||||
@ -2980,10 +2994,15 @@ def gunicornConfig():
|
||||
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
|
||||
return app_ip, app_port
|
||||
|
||||
def AmneziaWGEnabled():
|
||||
def ProtocolsEnabled() -> list[str]:
|
||||
from shutil import which
|
||||
protocols = []
|
||||
if which('awg') is not None and which('awg-quick') is not None:
|
||||
protocols.append("awg")
|
||||
if which('wg') is not None and which('wg-quick') is not None:
|
||||
protocols.append("wg")
|
||||
return protocols
|
||||
|
||||
return which('awg') is not None and which('awg-quick') is not None
|
||||
|
||||
def InitWireguardConfigurationsList(startup: bool = False):
|
||||
confs = os.listdir(DashboardConfig.GetConfig("Server", "wg_conf_path")[1])
|
||||
@ -3000,7 +3019,7 @@ def InitWireguardConfigurationsList(startup: bool = False):
|
||||
except WireguardConfiguration.InvalidConfigurationFileException as e:
|
||||
print(f"{i} have an invalid configuration file.")
|
||||
|
||||
if AmneziaWGEnabled():
|
||||
if "awg" in ProtocolsEnabled():
|
||||
confs = os.listdir(DashboardConfig.GetConfig("Server", "awg_conf_path")[1])
|
||||
confs.sort()
|
||||
for i in confs:
|
||||
|
21
src/static/app/src/components/protocolBadge.vue
Normal file
21
src/static/app/src/components/protocolBadge.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script setup>
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
const props = defineProps({
|
||||
protocol: String,
|
||||
mini: false
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span class="badge wireguardBg rounded-3 shadow" v-if="protocol === 'wg'">
|
||||
WireGuard <LocaleText t="Configuration" v-if="!mini"></LocaleText>
|
||||
</span>
|
||||
<span class="badge amneziawgBg rounded-3 shadow" v-else-if="protocol === 'awg'">
|
||||
AmneziaWG <LocaleText t="Configuration" v-if="!mini"></LocaleText>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -2,12 +2,14 @@
|
||||
import {onMounted, ref} from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import ProtocolBadge from "@/components/protocolBadge.vue";
|
||||
|
||||
const props = defineProps({
|
||||
configurationName: String,
|
||||
backups: Array,
|
||||
open: false,
|
||||
selectedConfigurationBackup: Object
|
||||
selectedConfigurationBackup: Object,
|
||||
protocol: Array
|
||||
})
|
||||
|
||||
const emit = defineEmits(["select"])
|
||||
@ -28,18 +30,20 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="card rounded-3 shadow-sm">
|
||||
<a role="button" class="card-body d-flex align-items-center text-decoration-none" @click="showBackups = !showBackups">
|
||||
<div class="d-flex gap-3 align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<samp>
|
||||
{{configurationName}}
|
||||
</samp>
|
||||
</h6>
|
||||
<small class="text-muted">
|
||||
<LocaleText :t="backups.length + (backups.length > 1 ? ' Backups':' Backup')"></LocaleText>
|
||||
</small>
|
||||
</div>
|
||||
<h5 class="ms-auto mb-0 dropdownIcon text-muted" :class="{active: showBackups}">
|
||||
<a role="button" class="card-body d-flex align-items-center text-decoration-none d-flex gap-3" @click="showBackups = !showBackups">
|
||||
<h6 class="mb-0 d-flex align-items-center gap-3">
|
||||
<samp>
|
||||
{{configurationName}}
|
||||
</samp>
|
||||
|
||||
<ProtocolBadge
|
||||
v-for="p in protocol"
|
||||
:protocol="p"></ProtocolBadge>
|
||||
</h6>
|
||||
<small class="text-muted ms-auto d-block">
|
||||
<LocaleText :t="backups.length + (backups.length > 1 ? ' Backups':' Backup')"></LocaleText>
|
||||
</small>
|
||||
<h5 class="mb-0 dropdownIcon text-muted" :class="{active: showBackups}">
|
||||
<i class="bi bi-chevron-down"></i>
|
||||
</h5>
|
||||
</a>
|
||||
|
@ -6,6 +6,7 @@ import {parse} from "cidr-tools";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {useRouter} from "vue-router";
|
||||
import ProtocolBadge from "@/components/protocolBadge.vue";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
@ -14,7 +15,8 @@ const props = defineProps({
|
||||
|
||||
const newConfiguration = reactive({
|
||||
ConfigurationName: props.selectedConfigurationBackup.filename.split("_")[0],
|
||||
Backup: props.selectedConfigurationBackup.filename
|
||||
Backup: props.selectedConfigurationBackup.filename,
|
||||
Protocol: props.selectedConfigurationBackup.protocol
|
||||
})
|
||||
|
||||
const lineSplit = props.selectedConfigurationBackup.content.split("\n");
|
||||
@ -136,6 +138,16 @@ const submitRestore = async () => {
|
||||
<LocaleText t="Configuration"></LocaleText>
|
||||
</h4>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-muted mb-1">
|
||||
<small>
|
||||
<LocaleText t="Protocol"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<h5 class="mb-0">
|
||||
<ProtocolBadge :protocol="selectedConfigurationBackup.protocol" :mini="true"></ProtocolBadge>
|
||||
</h5>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-muted mb-1" for="ConfigurationName"><small>
|
||||
<LocaleText t="Configuration Name"></LocaleText>
|
||||
|
@ -180,11 +180,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
next()
|
||||
}
|
||||
}else {
|
||||
if (to.path === "/signin" && await checkAuth()){
|
||||
next("/")
|
||||
}else{
|
||||
next()
|
||||
}
|
||||
next()
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2,15 +2,22 @@
|
||||
import {parse} from "cidr-tools";
|
||||
import '@/utilities/wireguard.js'
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import {ref} from "vue";
|
||||
|
||||
export default {
|
||||
name: "newConfiguration",
|
||||
components: {LocaleText},
|
||||
setup(){
|
||||
async setup(){
|
||||
const store = WireguardConfigurationsStore()
|
||||
return {store}
|
||||
const protocols = ref([])
|
||||
await fetchGet("/api/protocolsEnabled", {}, (res) => {
|
||||
protocols.value = res.data
|
||||
})
|
||||
|
||||
|
||||
return {store, protocols}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
@ -279,30 +286,31 @@ export default {
|
||||
<div id="newConfigurationOptionalAccordionCollapse"
|
||||
class="accordion-collapse collapse" data-bs-parent="#newConfigurationOptionalAccordion">
|
||||
<div class="accordion-body d-flex flex-column gap-3">
|
||||
<div class="card rounded-3">
|
||||
<div class="card-header">PreUp</div>
|
||||
<div class="card rounded-3" v-for="key in ['PreUp', 'PreDown', 'PostUp', 'PostDown']">
|
||||
<div class="card-header">{{ key }}</div>
|
||||
<div class="card-body">
|
||||
<input type="text" class="form-control" id="preUp" v-model="this.newConfiguration.PreUp">
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-3">
|
||||
<div class="card-header">PreDown</div>
|
||||
<div class="card-body">
|
||||
<input type="text" class="form-control" id="preDown" v-model="this.newConfiguration.PreDown">
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-3">
|
||||
<div class="card-header">PostUp</div>
|
||||
<div class="card-body">
|
||||
<input type="text" class="form-control" id="postUp" v-model="this.newConfiguration.PostUp">
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-3">
|
||||
<div class="card-header">PostDown</div>
|
||||
<div class="card-body">
|
||||
<input type="text" class="form-control" id="postDown" v-model="this.newConfiguration.PostDown">
|
||||
<input type="text"
|
||||
class="form-control font-monospace" :id="key" v-model="this.newConfiguration[key]">
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="card rounded-3">-->
|
||||
<!-- <div class="card-header">PreDown</div>-->
|
||||
<!-- <div class="card-body">-->
|
||||
<!-- <input type="text" class="form-control" id="preDown" v-model="this.newConfiguration.PreDown">-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="card rounded-3">-->
|
||||
<!-- <div class="card-header">PostUp</div>-->
|
||||
<!-- <div class="card-body">-->
|
||||
<!-- <input type="text" class="form-control" id="postUp" v-model="this.newConfiguration.PostUp">-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="card rounded-3">-->
|
||||
<!-- <div class="card-header">PostDown</div>-->
|
||||
<!-- <div class="card-body">-->
|
||||
<!-- <input type="text" class="form-control" id="postDown" v-model="this.newConfiguration.PostDown">-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -67,6 +67,7 @@ const selectedConfiguration = ref("")
|
||||
@select="(b) => {selectedConfigurationBackup = b; selectedConfiguration = c; confirm = true}"
|
||||
:selectedConfigurationBackup="selectedConfigurationBackup"
|
||||
:open="selectedConfiguration === c"
|
||||
:protocol="[...new Set(backups.NonExistingConfigurations[c].map(x => x.protocol))]"
|
||||
v-for="c in Object.keys(backups.NonExistingConfigurations)"
|
||||
:configuration-name="c" :backups="backups.NonExistingConfigurations[c]"></BackupGroup>
|
||||
<div v-if="Object.keys(backups.NonExistingConfigurations).length === 0">
|
||||
|
Loading…
x
Reference in New Issue
Block a user