diff --git a/src/dashboard.py b/src/dashboard.py index 8ed6fc0..7dbee97 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -440,7 +440,7 @@ class WireguardConfiguration: def __str__(self): return self.message - def __init__(self, name: str = None, data: dict = None): + def __init__(self, name: str = None, data: dict = None, backup: dict = None): print(f"[WGDashboard] Initialized Configuration: {name}") self.__parser: configparser.ConfigParser = configparser.ConfigParser(strict=False) @@ -463,18 +463,31 @@ class WireguardConfiguration: self.SaveConfig: bool = True self.Name = name self.__configPath = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf') - + if name is not None: + if data is not None and "Backup" in data.keys(): + db = self.__importDatabase( + os.path.join( + DashboardConfig.GetConfig("Server", "wg_conf_path")[1], + 'WGDashboard_Backup', + data["Backup"].replace(".conf", ".sql"))) + else: + self.__createDatabase() + self.__parseConfigurationFile() + self.__initPeersList() + else: self.Name = data["ConfigurationName"] self.__configPath = os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf') + for i in dir(self): if str(i) in data.keys(): if isinstance(getattr(self, i), bool): setattr(self, i, _strToBool(data[i])) else: setattr(self, i, str(data[i])) + self.__parser["Interface"] = { "PrivateKey": self.PrivateKey, "Address": self.Address, @@ -485,13 +498,15 @@ class WireguardConfiguration: "PostDown": self.PostDown, "SaveConfig": "true" } - - with open(self.__configPath, "w+") as configFile: - self.__parser.write(configFile) - - - self.__createDatabase() - self.__initPeersList() + + if "Backup" not in data.keys(): + self.__createDatabase() + with open(self.__configPath, "w+") as configFile: + self.__parser.write(configFile) + self.__initPeersList() + + + def __initPeersList(self): self.Peers: list[Peer] = [] @@ -588,6 +603,18 @@ class WireguardConfiguration: ): yield line + def __importDatabase(self, sqlFilePath) -> bool: + self.__dropDatabase() + self.__createDatabase() + if not os.path.exists(sqlFilePath): + return False + with open(sqlFilePath, 'r') as f: + for l in f.readlines(): + l = l.rstrip("\n") + if len(l) > 0: + sqlUpdate(l) + return True + def __getPublicKey(self) -> str: return _generatePublicKey(self.PrivateKey)[1] @@ -993,14 +1020,7 @@ class WireguardConfiguration: return False self.__parseConfigurationFile() self.__dropDatabase() - self.__createDatabase() - if (os.path.exists(targetSQL)): - with open(targetSQL, 'r') as sqlFile: - for l in sqlFile.readlines(): - l = l.rstrip('\n') - if len(l) > 0: - sqlUpdate(l) - + self.__importDatabase(targetSQL) self.__initPeersList() return True @@ -1414,16 +1434,13 @@ class DashboardConfig: Private Functions ''' - def _strToBool(value: str) -> bool: return value.lower() in ("yes", "true", "t", "1", 1) - def _regexMatch(regex, text): pattern = re.compile(regex) return pattern.search(text) is not None - def _getConfigurationList(): for i in os.listdir(DashboardConfig.GetConfig("Server", "wg_conf_path")[1]): if _regexMatch("^(.{1,}).(conf)$", i): @@ -1437,8 +1454,6 @@ def _getConfigurationList(): except WireguardConfiguration.InvalidConfigurationFileException as e: print(f"{i} have an invalid configuration file.") - - def _checkIPWithRange(ip): ip_patterns = ( r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|\/)){4}([0-9]{1,2})(,|$)", @@ -1455,7 +1470,6 @@ def _checkIPWithRange(ip): return result - def _checkIP(ip): ip_patterns = ( r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}", @@ -1471,7 +1485,6 @@ def _checkIP(ip): return result - def _checkDNS(dns): dns = dns.replace(' ', '').split(',') for i in dns: @@ -1479,7 +1492,6 @@ def _checkDNS(dns): return False, f"{i} does not appear to be an valid DNS address" return True, "" - def _generatePublicKey(privateKey) -> tuple[bool, str] | tuple[bool, None]: try: publicKey = subprocess.check_output(f"wg pubkey", input=privateKey.encode(), shell=True, @@ -1488,7 +1500,6 @@ def _generatePublicKey(privateKey) -> tuple[bool, str] | tuple[bool, None]: except subprocess.CalledProcessError: return False, None - def _generatePrivateKey() -> [bool, str]: try: publicKey = subprocess.check_output(f"wg genkey", shell=True, @@ -1696,23 +1707,11 @@ def API_getWireguardConfigurations(): @app.route(f'{APP_PREFIX}/api/addWireguardConfiguration', methods=["POST"]) def API_addWireguardConfiguration(): data = request.get_json() - keys = [ - "ConfigurationName", - "Address", - "ListenPort", - "PrivateKey", - "PublicKey", - "PresharedKey", - "PreUp", - "PreDown", - "PostUp", - "PostDown", - ] requiredKeys = [ "ConfigurationName", "Address", "ListenPort", "PrivateKey" ] - for i in keys: - if i not in data.keys() or (i in requiredKeys and len(str(data[i])) == 0): + for i in requiredKeys: + if i not in data.keys(): return ResponseObject(False, "Please provide all required parameters.") # Check duplicate names, ports, address @@ -1732,7 +1731,24 @@ def API_addWireguardConfiguration(): f"Already have a configuration with the address \"{data['Address']}\"", "Address") - WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data) + 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") + + 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') + ) + WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data, name=data['ConfigurationName']) + else: + WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data) return ResponseObject() diff --git a/src/static/app/src/components/configurationComponents/peerList.vue b/src/static/app/src/components/configurationComponents/peerList.vue index 419a6ff..656ed99 100644 --- a/src/static/app/src/components/configurationComponents/peerList.vue +++ b/src/static/app/src/components/configurationComponents/peerList.vue @@ -156,10 +156,10 @@ export default { modalOpen: false }, backupRestore: { - modalOpen: true + modalOpen: false }, deleteConfiguration: { - modalOpen: false + modalOpen: true } } }, @@ -685,17 +685,19 @@ export default { @close="this.selectPeers.modalOpen = false" > + + + + - - - diff --git a/src/static/app/src/components/messageCentreComponent/message.vue b/src/static/app/src/components/messageCentreComponent/message.vue index 5f864e4..ba8bc24 100644 --- a/src/static/app/src/components/messageCentreComponent/message.vue +++ b/src/static/app/src/components/messageCentreComponent/message.vue @@ -1,8 +1,10 @@ - - - - {{this.message.from}} + + + + {{this.message.from}} + + + {{dayjs().format("hh:mm A")}} + + {{this.message.content}} diff --git a/src/static/app/src/components/restoreConfigurationComponents/confirmBackup.vue b/src/static/app/src/components/restoreConfigurationComponents/confirmBackup.vue index 9d6c9a3..edc123b 100644 --- a/src/static/app/src/components/restoreConfigurationComponents/confirmBackup.vue +++ b/src/static/app/src/components/restoreConfigurationComponents/confirmBackup.vue @@ -3,13 +3,17 @@ import {computed, onMounted, reactive, ref, watch} from "vue"; import LocaleText from "@/components/text/localeText.vue"; import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js"; import {parse} from "cidr-tools"; +import {fetchPost} from "@/utilities/fetch.js"; +import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js"; +import {useRouter} from "vue-router"; const props = defineProps({ selectedConfigurationBackup: Object }) const newConfiguration = reactive({ - ConfigurationName: props.selectedConfigurationBackup.filename.split("_")[0] + ConfigurationName: props.selectedConfigurationBackup.filename.split("_")[0], + Backup: props.selectedConfigurationBackup.filename }) const lineSplit = props.selectedConfigurationBackup.content.split("\n"); @@ -76,7 +80,6 @@ const validateForm = computed(() => { && validatePrivateKey.value && validateConfigurationName.value }) - onMounted(() => { document.querySelector("main").scrollTo({ top: 0, @@ -90,7 +93,6 @@ onMounted(() => { immediate: true }) }) - const availableIPAddress = computed(() => { let p; try{ @@ -100,7 +102,6 @@ const availableIPAddress = computed(() => { } return p.end - p.start }) - const peersCount = computed(() => { if (props.selectedConfigurationBackup.database){ let l = props.selectedConfigurationBackup.databaseContent.split("\n") @@ -108,7 +109,6 @@ const peersCount = computed(() => { } return 0 }) - const restrictedPeersCount = computed(() => { if (props.selectedConfigurationBackup.database){ let l = props.selectedConfigurationBackup.databaseContent.split("\n") @@ -116,10 +116,19 @@ const restrictedPeersCount = computed(() => { } return 0 }) - - - - +const dashboardStore = DashboardConfigurationStore() +const router = useRouter(); +const submitRestore = async () => { + if (validateForm.value){ + await fetchPost("/api/addWireguardConfiguration", newConfiguration, async (res) => { + if (res.status){ + dashboardStore.newMessage("Server", "Configuration restored", "success") + await store.getConfigurations() + await router.push(`/configuration/${newConfiguration.ConfigurationName}/peers`) + } + }) + } +} @@ -137,7 +146,7 @@ const restrictedPeersCount = computed(() => { {{errorMessage}} @@ -162,16 +171,9 @@ const restrictedPeersCount = computed(() => { - - - @@ -195,7 +197,7 @@ const restrictedPeersCount = computed(() => { max="65353" v-model="newConfiguration.ListenPort" :class="[validateListenPort ? 'is-valid':'is-invalid']" - :disabled="loading" + disabled required> {{errorMessage}} @@ -225,7 +227,7 @@ const restrictedPeersCount = computed(() => { placeholder="Ex: 10.0.0.1/24" id="Address" v-model="newConfiguration.Address" :class="[validateAddress ? 'is-valid':'is-invalid']" - :disabled="loading" + disabled required> {{errorMessage}} @@ -250,25 +252,33 @@ const restrictedPeersCount = computed(() => { - + - + - + - + @@ -305,9 +315,10 @@ const restrictedPeersCount = computed(() => { - Restore + {{ !loading ? 'Restore':'Restoring...'}} diff --git a/src/static/app/src/stores/DashboardConfigurationStore.js b/src/static/app/src/stores/DashboardConfigurationStore.js index 28e6bd8..7cd3afa 100644 --- a/src/static/app/src/stores/DashboardConfigurationStore.js +++ b/src/static/app/src/stores/DashboardConfigurationStore.js @@ -32,8 +32,6 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt }else{ this.CrossServerConfiguration = JSON.parse(currentConfiguration) } - - }, syncCrossServerConfiguration(){ window.localStorage.setItem('CrossServerConfiguration', JSON.stringify(this.CrossServerConfiguration)) @@ -82,7 +80,6 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt applyLocale(key){ if (this.Locale === null) return key - const reg = Object.keys(this.Locale) const match = reg.filter(x => { return key.match(new RegExp('^' + x + '$', 'g')) !== null diff --git a/src/static/app/src/views/restoreConfiguration.vue b/src/static/app/src/views/restoreConfiguration.vue index 79a9d55..b4b55f2 100644 --- a/src/static/app/src/views/restoreConfiguration.vue +++ b/src/static/app/src/views/restoreConfiguration.vue @@ -29,7 +29,7 @@ const selectedConfiguration = ref("") - + @@ -58,21 +58,21 @@ const selectedConfiguration = ref("") + + + + + + + + + + + + - Backup of existing WireGuard Configurations - - - {selectedConfigurationBackup = b; selectedConfiguration = c; confirm = true}" - :open="selectedConfiguration === c" - :selectedConfigurationBackup="selectedConfigurationBackup" - v-for="c in Object.keys(backups.ExistingConfigurations)" - :configuration-name="c" :backups="backups.ExistingConfigurations[c]"> - - - - Backup of non-existing WireGuard Configurations - + + {selectedConfigurationBackup = b; selectedConfiguration = c; confirm = true}" @@ -80,6 +80,13 @@ const selectedConfiguration = ref("") :open="selectedConfiguration === c" v-for="c in Object.keys(backups.NonExistingConfigurations)" :configuration-name="c" :backups="backups.NonExistingConfigurations[c]"> + + + + You don't have any configuration to restore + + +
You don't have any configuration to restore