Backup and restore for AWG is done

This commit is contained in:
Donald Zou 2024-12-04 17:50:16 +08:00
parent 434c236210
commit 57583b6747
7 changed files with 144 additions and 83 deletions

View File

@ -509,6 +509,7 @@ class WireguardConfiguration:
self.createDatabase() self.createDatabase()
with open(self.configPath, "w+") as configFile: with open(self.configPath, "w+") as configFile:
self.__parser.write(configFile) self.__parser.write(configFile)
print(f"[WGDashboard] Configuration file {self.configPath} created")
self.__initPeersList() self.__initPeersList()
print(f"[WGDashboard] Initialized Configuration: {name}") print(f"[WGDashboard] Initialized Configuration: {name}")
@ -2086,11 +2087,14 @@ def API_getWireguardConfigurations():
def API_addWireguardConfiguration(): def API_addWireguardConfiguration():
data = request.get_json() data = request.get_json()
requiredKeys = [ requiredKeys = [
"ConfigurationName", "Address", "ListenPort", "PrivateKey" "ConfigurationName", "Address", "ListenPort", "PrivateKey", "Protocol"
] ]
for i in requiredKeys: for i in requiredKeys:
if i not in data.keys(): if i not in data.keys():
return ResponseObject(False, "Please provide all required parameters.") 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 # Check duplicate names, ports, address
for i in WireguardConfigurations.values(): for i in WireguardConfigurations.values():
@ -2110,22 +2114,27 @@ def API_addWireguardConfiguration():
"Address") "Address")
if "Backup" in data.keys(): if "Backup" in data.keys():
if not os.path.exists(os.path.join( path = {
DashboardConfig.GetConfig("Server", "wg_conf_path")[1], "wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
'WGDashboard_Backup', "awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
data["Backup"])) or not os.path.exists(os.path.join( }
DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
'WGDashboard_Backup', if (os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"])) and
data["Backup"].replace('.conf', '.sql'))): os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
return ResponseObject(False, "Backup file does not exist") 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( shutil.copy(
os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', data["Backup"]), os.path.join(path[protocol], 'WGDashboard_Backup', data["Backup"]),
os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{data["ConfigurationName"]}.conf') 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: else:
WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data) WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data) if data.get('Protocol') == 'wg' else AmneziaWireguardConfiguration(data=data)
return ResponseObject() return ResponseObject()
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration/') @app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration/')
@ -2197,30 +2206,32 @@ def API_getAllWireguardConfigurationBackup():
b = WireguardConfigurations[i].getBackups(True) b = WireguardConfigurations[i].getBackups(True)
if len(b) > 0: if len(b) > 0:
data['ExistingConfigurations'][i] = WireguardConfigurations[i].getBackups(True) data['ExistingConfigurations'][i] = WireguardConfigurations[i].getBackups(True)
directory = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup') for protocol in ProtocolsEnabled():
files = [(file, os.path.getctime(os.path.join(directory, file))) directory = os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup')
for file in os.listdir(directory) if os.path.isfile(os.path.join(directory, file))] files = [(file, os.path.getctime(os.path.join(directory, file)))
files.sort(key=lambda x: x[1], reverse=True) 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): for f, ct in files:
s = re.search(r"^(.*)_(.*)\.(conf)$", f) if RegexMatch(r"^(.*)_(.*)\.(conf)$", f):
name = s.group(1) s = re.search(r"^(.*)_(.*)\.(conf)$", f)
if name not in existingConfiguration: name = s.group(1)
if name not in data['NonExistingConfigurations'].keys(): if name not in existingConfiguration:
data['NonExistingConfigurations'][name] = [] if name not in data['NonExistingConfigurations'].keys():
data['NonExistingConfigurations'][name] = []
date = s.group(2)
d = { date = s.group(2)
"filename": f, d = {
"backupDate": date, "protocol": protocol,
"content": open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f), 'r').read() "filename": f,
} "backupDate": date,
if f.replace(".conf", ".sql") in list(os.listdir(directory)): "content": open(os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup', f), 'r').read()
d['database'] = True }
d['databaseContent'] = open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f.replace(".conf", ".sql")), 'r').read() if f.replace(".conf", ".sql") in list(os.listdir(directory)):
data['NonExistingConfigurations'][name].append(d) 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) return ResponseObject(data=data)
@app.get(f'{APP_PREFIX}/api/createWireguardConfigurationBackup') @app.get(f'{APP_PREFIX}/api/createWireguardConfigurationBackup')
@ -2944,6 +2955,9 @@ def API_SystemStatus():
# pass # pass
return ResponseObject(data=status) return ResponseObject(data=status)
@app.get(f'{APP_PREFIX}/api/protocolsEnabled')
def API_ProtocolsEnabled():
return ResponseObject(data=ProtocolsEnabled())
@app.get(f'{APP_PREFIX}/') @app.get(f'{APP_PREFIX}/')
def index(): def index():
@ -2980,10 +2994,15 @@ def gunicornConfig():
_, app_port = DashboardConfig.GetConfig("Server", "app_port") _, app_port = DashboardConfig.GetConfig("Server", "app_port")
return app_ip, app_port return app_ip, app_port
def AmneziaWGEnabled(): def ProtocolsEnabled() -> list[str]:
from shutil import which 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): def InitWireguardConfigurationsList(startup: bool = False):
confs = os.listdir(DashboardConfig.GetConfig("Server", "wg_conf_path")[1]) confs = os.listdir(DashboardConfig.GetConfig("Server", "wg_conf_path")[1])
@ -3000,7 +3019,7 @@ def InitWireguardConfigurationsList(startup: bool = False):
except WireguardConfiguration.InvalidConfigurationFileException as e: except WireguardConfiguration.InvalidConfigurationFileException as e:
print(f"{i} have an invalid configuration file.") 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 = os.listdir(DashboardConfig.GetConfig("Server", "awg_conf_path")[1])
confs.sort() confs.sort()
for i in confs: for i in confs:

View 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>

View File

@ -2,12 +2,14 @@
import {onMounted, ref} from "vue"; import {onMounted, ref} from "vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import LocaleText from "@/components/text/localeText.vue"; import LocaleText from "@/components/text/localeText.vue";
import ProtocolBadge from "@/components/protocolBadge.vue";
const props = defineProps({ const props = defineProps({
configurationName: String, configurationName: String,
backups: Array, backups: Array,
open: false, open: false,
selectedConfigurationBackup: Object selectedConfigurationBackup: Object,
protocol: Array
}) })
const emit = defineEmits(["select"]) const emit = defineEmits(["select"])
@ -28,18 +30,20 @@ onMounted(() => {
<template> <template>
<div class="card rounded-3 shadow-sm"> <div class="card rounded-3 shadow-sm">
<a role="button" class="card-body d-flex align-items-center text-decoration-none" @click="showBackups = !showBackups"> <a role="button" class="card-body d-flex align-items-center text-decoration-none d-flex gap-3" @click="showBackups = !showBackups">
<div class="d-flex gap-3 align-items-center"> <h6 class="mb-0 d-flex align-items-center gap-3">
<h6 class="mb-0"> <samp>
<samp> {{configurationName}}
{{configurationName}} </samp>
</samp>
</h6> <ProtocolBadge
<small class="text-muted"> v-for="p in protocol"
<LocaleText :t="backups.length + (backups.length > 1 ? ' Backups':' Backup')"></LocaleText> :protocol="p"></ProtocolBadge>
</small> </h6>
</div> <small class="text-muted ms-auto d-block">
<h5 class="ms-auto mb-0 dropdownIcon text-muted" :class="{active: showBackups}"> <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> <i class="bi bi-chevron-down"></i>
</h5> </h5>
</a> </a>

View File

@ -6,6 +6,7 @@ import {parse} from "cidr-tools";
import {fetchPost} from "@/utilities/fetch.js"; import {fetchPost} from "@/utilities/fetch.js";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js"; import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import ProtocolBadge from "@/components/protocolBadge.vue";
const props = defineProps({ const props = defineProps({
@ -14,7 +15,8 @@ const props = defineProps({
const newConfiguration = reactive({ const newConfiguration = reactive({
ConfigurationName: props.selectedConfigurationBackup.filename.split("_")[0], 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"); const lineSplit = props.selectedConfigurationBackup.content.split("\n");
@ -136,6 +138,16 @@ const submitRestore = async () => {
<LocaleText t="Configuration"></LocaleText> <LocaleText t="Configuration"></LocaleText>
</h4> </h4>
</div> </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> <div>
<label class="text-muted mb-1" for="ConfigurationName"><small> <label class="text-muted mb-1" for="ConfigurationName"><small>
<LocaleText t="Configuration Name"></LocaleText> <LocaleText t="Configuration Name"></LocaleText>

View File

@ -180,11 +180,7 @@ router.beforeEach(async (to, from, next) => {
next() next()
} }
}else { }else {
if (to.path === "/signin" && await checkAuth()){ next()
next("/")
}else{
next()
}
} }
}); });

View File

@ -2,15 +2,22 @@
import {parse} from "cidr-tools"; import {parse} from "cidr-tools";
import '@/utilities/wireguard.js' import '@/utilities/wireguard.js'
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.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 LocaleText from "@/components/text/localeText.vue";
import {ref} from "vue";
export default { export default {
name: "newConfiguration", name: "newConfiguration",
components: {LocaleText}, components: {LocaleText},
setup(){ async setup(){
const store = WireguardConfigurationsStore() const store = WireguardConfigurationsStore()
return {store} const protocols = ref([])
await fetchGet("/api/protocolsEnabled", {}, (res) => {
protocols.value = res.data
})
return {store, protocols}
}, },
data(){ data(){
return { return {
@ -279,30 +286,31 @@ export default {
<div id="newConfigurationOptionalAccordionCollapse" <div id="newConfigurationOptionalAccordionCollapse"
class="accordion-collapse collapse" data-bs-parent="#newConfigurationOptionalAccordion"> class="accordion-collapse collapse" data-bs-parent="#newConfigurationOptionalAccordion">
<div class="accordion-body d-flex flex-column gap-3"> <div class="accordion-body d-flex flex-column gap-3">
<div class="card rounded-3"> <div class="card rounded-3" v-for="key in ['PreUp', 'PreDown', 'PostUp', 'PostDown']">
<div class="card-header">PreUp</div> <div class="card-header">{{ key }}</div>
<div class="card-body"> <div class="card-body">
<input type="text" class="form-control" id="preUp" v-model="this.newConfiguration.PreUp"> <input type="text"
</div> class="form-control font-monospace" :id="key" v-model="this.newConfiguration[key]">
</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 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> </div>
</div> </div>

View File

@ -67,6 +67,7 @@ const selectedConfiguration = ref("")
@select="(b) => {selectedConfigurationBackup = b; selectedConfiguration = c; confirm = true}" @select="(b) => {selectedConfigurationBackup = b; selectedConfiguration = c; confirm = true}"
:selectedConfigurationBackup="selectedConfigurationBackup" :selectedConfigurationBackup="selectedConfigurationBackup"
:open="selectedConfiguration === c" :open="selectedConfiguration === c"
:protocol="[...new Set(backups.NonExistingConfigurations[c].map(x => x.protocol))]"
v-for="c in Object.keys(backups.NonExistingConfigurations)" v-for="c in Object.keys(backups.NonExistingConfigurations)"
:configuration-name="c" :backups="backups.NonExistingConfigurations[c]"></BackupGroup> :configuration-name="c" :backups="backups.NonExistingConfigurations[c]"></BackupGroup>
<div v-if="Object.keys(backups.NonExistingConfigurations).length === 0"> <div v-if="Object.keys(backups.NonExistingConfigurations).length === 0">