From 115b2937e5849686042374906ba3b4d35c11ed33 Mon Sep 17 00:00:00 2001 From: anton Date: Thu, 17 Oct 2024 17:47:50 +0200 Subject: [PATCH 1/3] Export/Import database in settings --- src/router/index.ts | 14 ++++++ src/stores/admin/database.ts | 42 ++++++++++++++++ src/stores/admin/navigation.ts | 3 ++ src/types/permissionTypes.ts | 6 ++- src/views/admin/settings/Database.vue | 70 +++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 src/stores/admin/database.ts create mode 100644 src/views/admin/settings/Database.vue diff --git a/src/router/index.ts b/src/router/index.ts index 25dbd9d..55f7da6 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -286,6 +286,20 @@ const router = createRouter({ }, ], }, + { + path: "database", + name: "admin-settings-database", + component: () => import("@/views/RouterView.vue"), + meta: { type: "read", section: "settings", module: "database" }, + beforeEnter: [abilityAndNavUpdate], + children: [ + { + path: "", + name: "admin-settings-database-status", + component: () => import("@/views/admin/settings/Database.vue"), + }, + ], + }, ], }, { diff --git a/src/stores/admin/database.ts b/src/stores/admin/database.ts new file mode 100644 index 0000000..6f84045 --- /dev/null +++ b/src/stores/admin/database.ts @@ -0,0 +1,42 @@ +import { defineStore } from "pinia"; +import { http } from "@/serverCom"; +import type { AxiosResponse } from "axios"; + +export const useDatabaseStore = defineStore("database", { + state: () => { + return { + data: {} as any, + status: "idle" as "idle" | "working" | "success" | "failed", + failureReason: '' as string, + ex: {} as Error, + }; + }, + actions: { + async import() { + this.status = "working"; + http + .post("/admin/database") + .then((result) => { + this.data = result.data; + this.status = "success"; + }) + .catch((err) => { + this.status = "failed"; + }); + }, + async export(secret: string) { + try { + this.status = "working"; + const postData = {secret: secret}; + const response: AxiosResponse = await http.post("/admin/database", postData); + this.data = response.data; + this.status = "success"; + } catch (ex) { + this.status = "failed"; + this.failureReason = (ex as Error).message; + this.ex = (ex as Error); + throw ex; + } + }, + }, +}); diff --git a/src/stores/admin/navigation.ts b/src/stores/admin/navigation.ts index a63719e..6040087 100644 --- a/src/stores/admin/navigation.ts +++ b/src/stores/admin/navigation.ts @@ -108,6 +108,9 @@ export const useNavigationStore = defineStore("navigation", { ...(abilityStore.can("read", "settings", "membership_status") ? [{ key: "membership_status", title: "Mitgliedsstatus" }] : []), + ...(abilityStore.can("read", "settings", "database") + ? [{ key: "database", title: "Datenbank" }] + : []), ], }, user: { diff --git a/src/types/permissionTypes.ts b/src/types/permissionTypes.ts index 3bd54c8..a8da5a9 100644 --- a/src/types/permissionTypes.ts +++ b/src/types/permissionTypes.ts @@ -11,7 +11,8 @@ export type PermissionModule = | "communication" | "membership_status" | "user" - | "role"; + | "role" + | "database"; export type PermissionType = "read" | "create" | "update" | "delete"; @@ -47,10 +48,11 @@ export const permissionModules: Array = [ "membership_status", "user", "role", + "database", ]; export const permissionTypes: Array = ["read", "create", "update", "delete"]; export const sectionsAndModules: SectionsAndModulesObject = { club: ["member", "calendar", "newsletter", "protocoll"], - settings: ["qualification", "award", "executive_position", "communication", "membership_status"], + settings: ["qualification", "award", "executive_position", "communication", "membership_status", "database"], user: ["user", "role"], }; diff --git a/src/views/admin/settings/Database.vue b/src/views/admin/settings/Database.vue new file mode 100644 index 0000000..220f244 --- /dev/null +++ b/src/views/admin/settings/Database.vue @@ -0,0 +1,70 @@ + + + + + From c657b36292410f176b3781d37fdbe4bd6d93018f Mon Sep 17 00:00:00 2001 From: Anton Schegg Date: Fri, 18 Oct 2024 09:37:31 +0200 Subject: [PATCH 2/3] Import started --- src/stores/admin/database.ts | 4 +- src/views/admin/settings/Database.vue | 61 ++++++++++++++++++++------- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/stores/admin/database.ts b/src/stores/admin/database.ts index 6f84045..c6a5d54 100644 --- a/src/stores/admin/database.ts +++ b/src/stores/admin/database.ts @@ -20,7 +20,7 @@ export const useDatabaseStore = defineStore("database", { this.data = result.data; this.status = "success"; }) - .catch((err) => { + .catch(() => { this.status = "failed"; }); }, @@ -29,7 +29,7 @@ export const useDatabaseStore = defineStore("database", { this.status = "working"; const postData = {secret: secret}; const response: AxiosResponse = await http.post("/admin/database", postData); - this.data = response.data; + this.data = response.data.data; this.status = "success"; } catch (ex) { this.status = "failed"; diff --git a/src/views/admin/settings/Database.vue b/src/views/admin/settings/Database.vue index 220f244..3933984 100644 --- a/src/views/admin/settings/Database.vue +++ b/src/views/admin/settings/Database.vue @@ -8,19 +8,24 @@ @@ -40,6 +45,10 @@ import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; export default defineComponent({ data() { return { + dataUrl: '' as string, + dataImport: '' as string, + file: null, + downloadFilename: '' as string, secret: '' as string, }; }, @@ -47,23 +56,43 @@ export default defineComponent({ ...mapState(useDatabaseStore, ["data", "status", "failureReason"]), disabled() : boolean { return this.status === 'working' || this.secret === ''; + }, + uploadDisabled() : boolean { + return this.status === 'working' || this.secret === '' || this.file === null; } }, mounted() { this.secret = '' as string; }, methods: { - ...mapActions(useDatabaseStore, ["export"]), + ...mapActions(useDatabaseStore, ["export", "import"]), + addFile(e) { + this.file = e.target.files[0]; + console.log(this.file); + }, async exportDatabase() { try { - const data = await this.export(this.secret); + if (this.dataUrl) { + this.dataUrl = ''; + } + await this.export(this.secret); + this.dataUrl = 'data:application/octet-stream,' + this.data; + this.downloadFilename = `${new Date().toISOString().replace(/:/g, '-')}_database_export.enc`; } catch(ex) { console.log(ex); } }, - importDatabase() { + onChangeFileUpload() { + }, + async importDatabase() { + try { + await this.import(this.secret, this.dataImport); + } + catch(ex) { + console.log(ex); + } } }, }); From 903c2f75608811583ad74656964ad731cdaefac2 Mon Sep 17 00:00:00 2001 From: Anton Schegg Date: Sat, 26 Oct 2024 20:31:07 +0200 Subject: [PATCH 3/3] Import und Export der Datenbank --- src/stores/admin/database.ts | 45 +++++++++++++++--------- src/views/admin/settings/Database.vue | 50 +++++++++++++++------------ 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/stores/admin/database.ts b/src/stores/admin/database.ts index c6a5d54..24b49b9 100644 --- a/src/stores/admin/database.ts +++ b/src/stores/admin/database.ts @@ -12,24 +12,37 @@ export const useDatabaseStore = defineStore("database", { }; }, actions: { - async import() { - this.status = "working"; - http - .post("/admin/database") - .then((result) => { - this.data = result.data; - this.status = "success"; - }) - .catch(() => { - this.status = "failed"; - }); - }, - async export(secret: string) { + async import(noEncryption: boolean, secret: string, requestData: any) { try { + this.data = {}; this.status = "working"; - const postData = {secret: secret}; - const response: AxiosResponse = await http.post("/admin/database", postData); - this.data = response.data.data; + this.failureReason = ''; + this.ex = {}; + const requestHeaders = {headers: {'Content-Type': 'application/json', 'x-decrypt-no' : noEncryption}}; + if (!noEncryption) { + requestHeaders.headers['x-decrypt-with'] = secret; + } + await http.post("/admin/database", requestData, requestHeaders); + this.status = "success"; + } catch (ex) { + this.status = "failed"; + this.failureReason = (ex as Error).message; + this.ex = (ex as Error); + throw ex; + } + }, + async export(noEncryption: boolean, secret: string) { + try { + this.data = {}; + this.status = "working"; + this.failureReason = ''; + this.ex = {}; + const requestHeaders = {headers: {'Content-Type': 'application/json', 'x-encrypt-no' : noEncryption}}; + if (!noEncryption) { + requestHeaders.headers['x-encrypt-with'] = secret; + } + const response: AxiosResponse = await http.get("/admin/database", requestHeaders); + this.data = response.data; this.status = "success"; } catch (ex) { this.status = "failed"; diff --git a/src/views/admin/settings/Database.vue b/src/views/admin/settings/Database.vue index 3933984..0a1e701 100644 --- a/src/views/admin/settings/Database.vue +++ b/src/views/admin/settings/Database.vue @@ -6,25 +6,28 @@ @@ -46,7 +49,7 @@ export default defineComponent({ data() { return { dataUrl: '' as string, - dataImport: '' as string, + noEncryption: false as boolean, file: null, downloadFilename: '' as string, secret: '' as string, @@ -55,40 +58,43 @@ export default defineComponent({ computed: { ...mapState(useDatabaseStore, ["data", "status", "failureReason"]), disabled() : boolean { - return this.status === 'working' || this.secret === ''; + return this.status === 'working' || (this.secret === '' && !this.noEncryption); }, uploadDisabled() : boolean { - return this.status === 'working' || this.secret === '' || this.file === null; + return this.status === 'working' || (this.secret === '' && !this.noEncryption) || this.file === null; } }, mounted() { this.secret = '' as string; + this.noEncryption = false; }, methods: { ...mapActions(useDatabaseStore, ["export", "import"]), addFile(e) { + this.dataUrl = ''; + + if (e.target.files?.length !== 1) { + return; + } this.file = e.target.files[0]; - console.log(this.file); }, async exportDatabase() { try { if (this.dataUrl) { this.dataUrl = ''; } - await this.export(this.secret); - this.dataUrl = 'data:application/octet-stream,' + this.data; - this.downloadFilename = `${new Date().toISOString().replace(/:/g, '-')}_database_export.enc`; + await this.export(this.noEncryption, this.secret); + this.dataUrl = 'data:application/octet-stream,' + encodeURIComponent(JSON.stringify(this.data)); + this.downloadFilename = `${new Date().toISOString().replace(/:/g, '-')}_database_export.${this.noEncryption ? 'json' : 'enc'}`; } catch(ex) { console.log(ex); } - }, - onChangeFileUpload() { - }, async importDatabase() { try { - await this.import(this.secret, this.dataImport); + const fileContent = await this.file.text(); + await this.import(this.noEncryption, this.secret, fileContent); } catch(ex) { console.log(ex);