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()
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,12 +2087,15 @@ 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():
if i.Name == data['ConfigurationName']:
@ -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/')
@ -2198,29 +2207,31 @@ def API_getAllWireguardConfigurationBackup():
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 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] = []
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)
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:

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

View File

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

View File

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

View File

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

View File

@ -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">