diff --git a/api/collections/backups.yml b/api/collections/backups.yml new file mode 100644 index 0000000..1876bef --- /dev/null +++ b/api/collections/backups.yml @@ -0,0 +1,95 @@ +name: backups +meta: + isBackupcollection: true + +permissions: + public: + methods: + get: false + post: false + put: false + delete: false + user: + methods: + get: true + post: false + put: false + delete: false + + # token als Zusatzsicherung gegen Spam, mehr siehe Hook + "token:${PUBLIC_TOKEN}": + methods: + get: false + post: true + put: false + delete: false + +hooks: + post: + create: + type: javascript + file: hooks/backups/post_create.js + +fields: + - name: collectionName + type: string + meta: + label: Collection Name + + - name: entryId + type: string + meta: + label: Entry ID + + - name: versionNr + type: number + meta: + label: Version Nr + + - name: manipulatedBy + type: string + meta: + label: Manipulated By + + - name: eventDescription + type: string + meta: + label: Event Description + widget: select + choices: + - id: create + name: Create + - id: update + name: Update + - id: delete + name: Delete + - id: recreate + name: Recreate + - id: activate + name: Activate + + - name: updateLogs + type: object[] + meta: + label: Veränderungen + + subFields: + - name: field + type: string + meta: + label: Feldname + + - name: previous + type: string + meta: + label: Vorheriger Wert + + - name: current + type: string + meta: + label: Aktueller Wert + + - name: entry + type: object + meta: + label: Entry diff --git a/api/collections/content.yml b/api/collections/content.yml index 062f9cf..0759ff7 100644 --- a/api/collections/content.yml +++ b/api/collections/content.yml @@ -4,6 +4,9 @@ uploadPath: ../media/page meta: label: Inhalt muiIcon: web + backup: + active: true + collectionName: backups views: - type: table columns: @@ -90,6 +93,7 @@ fields: type: object[] meta: label: Zeilen + widget: containerLessObjectArray folding: force: true subFields: diff --git a/api/config.yml b/api/config.yml index 0be1c7c..c3a58bd 100644 --- a/api/config.yml +++ b/api/config.yml @@ -8,9 +8,26 @@ meta: - url: https://tibi-admin-server.code.testversion.online/api/v1/_/demo description: code-server + dashboard: + majorItems: + - collection: navigation + type: reference + style: + upper: rgba(3, 50, 59, 0.7) + lower: rgba(3, 50, 59) + + - collection: page + type: reference + style: + upper: rgba(3, 50, 59, 0.7) + lower: rgba(3, 50, 59) + + minorItems: [] + collections: - !include collections/navigation.yml - !include collections/content.yml + - !include collections/backups.yml assets: - name: img diff --git a/api/hooks/backups/post_create.js b/api/hooks/backups/post_create.js new file mode 100644 index 0000000..8d5e9cd --- /dev/null +++ b/api/hooks/backups/post_create.js @@ -0,0 +1,92 @@ +;(function () { + let backup = context.data + const updateLogs = compareAndUpdateEntry(backup?.entry, backup?.collectionName, backup?.versionNr - 1) + backup.updateLogs = updateLogs + return { data: backup } + + function compareAndUpdateEntry(entry, collectionName, versionNr) { + let updateLogs + if (versionNr == 0) { + updateLogs = getUpdateLogs({}, entry) + } + delete entry.updateTime + let previousEntry = context.db.find("backups", { + filter: { entryId: context.data.entryId, versionNr, collectionName }, + })[0] + if (!previousEntry) { + console.error("No previous entry found") + updateLogs = getUpdateLogs({}, entry) + } else { + delete previousEntry.updateTime + updateLogs = getUpdateLogs(previousEntry.entry, entry) + } + console.log(context.json.stringify(previousEntry?.entry), context.json.stringify(entry)) + + return updateLogs + } + + function filterValidObjects(array) { + return array.filter((object) => { + for (let key in object) { + if (typeof object[key] !== "string" && object[key] !== null) { + return false + } + } + return true + }) + } + function getUpdateLogs(oldObj = {}, newObj = {}, path = "") { + let updateLogs = [] + const ignoredKeys = ["id", "insertTime", "updateTime"] + const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]) + + allKeys.forEach((key) => { + if (ignoredKeys.includes(key)) return + + const newPath = path ? `${path}.${key}` : key + const oldVal = oldObj.hasOwnProperty(key) ? oldObj[key] : "" + const newVal = newObj.hasOwnProperty(key) ? newObj[key] : "" + + // Handle Arrays + if (Array.isArray(oldVal) || Array.isArray(newVal)) { + const oldArr = oldVal || [] + const newArr = newVal || [] + + for (let i = 0; i < Math.max(oldArr.length, newArr.length); i++) { + const itemPath = `${newPath}[${i}]` + + if (oldArr[i] !== newArr[i]) { + if (typeof oldArr[i] === "object" || typeof newArr[i] === "object") { + const arrUpdates = getUpdateLogs(oldArr[i], newArr[i], itemPath) + updateLogs = updateLogs.concat(arrUpdates) + } else { + updateLogs.push({ + field: itemPath, + previous: oldArr[i] === undefined ? "" : oldArr[i].toString(), + current: newArr[i] === undefined ? "" : newArr[i].toString(), + }) + } + } + } + } + // Handle nested objects but not arrays + else if ( + (typeof oldVal === "object" && oldVal !== null) || + (typeof newVal === "object" && newVal !== null) + ) { + const nestedUpdates = getUpdateLogs(oldVal || {}, newVal || {}, newPath) + updateLogs = updateLogs.concat(nestedUpdates) + } + // Handle primitive types + else if (oldVal !== newVal) { + updateLogs.push({ + field: newPath, + previous: oldVal.toString(), + current: newVal.toString(), + }) + } + }) + + return filterValidObjects(updateLogs) + } +})() diff --git a/frontend/src/lib/components/Pagebuilder/Rows.svelte b/frontend/src/lib/components/Pagebuilder/Rows.svelte index c8f208e..dac5844 100644 --- a/frontend/src/lib/components/Pagebuilder/Rows.svelte +++ b/frontend/src/lib/components/Pagebuilder/Rows.svelte @@ -39,7 +39,6 @@ initPage() } } - $: console.log(page?.rows, "==rows")