From 80dc3b40d3e9f96edd4c881fe3bbb437684cc452 Mon Sep 17 00:00:00 2001 From: bsiggel Date: Wed, 11 Mar 2026 22:20:21 +0100 Subject: [PATCH] Implement Blake3 hashing in CDokumente; update related metadata and localization files --- .../Custom/Hooks/CDokumente/CDokumente.php | 20 +++-- .../Resources/i18n/de_DE/CDokumente.json | 4 +- .../Resources/i18n/en_US/CDokumente.json | 4 +- .../Resources/layouts/CDokumente/detail.json | 10 +-- .../metadata/entityDefs/CDokumente.json | 22 ++--- .../metadata/formula/CDokumente.json | 2 +- custom/scripts/install_blake3.sh | 87 +++++++++++++++++++ data/config.php | 2 +- data/state.php | 4 +- 9 files changed, 114 insertions(+), 41 deletions(-) create mode 100755 custom/scripts/install_blake3.sh diff --git a/custom/Espo/Custom/Hooks/CDokumente/CDokumente.php b/custom/Espo/Custom/Hooks/CDokumente/CDokumente.php index 66463fc2..cf32dea9 100644 --- a/custom/Espo/Custom/Hooks/CDokumente/CDokumente.php +++ b/custom/Espo/Custom/Hooks/CDokumente/CDokumente.php @@ -35,22 +35,24 @@ class CDokumente extends \Espo\Core\Hooks\Base return; } - // Berechne neue Hashes - $newMd5 = hash_file('md5', $filePath); - $newSha256 = hash_file('sha256', $filePath); + // Berechne Blake3 Hash + $fileContent = file_get_contents($filePath); + if ($fileContent === false) { + return; + } + + $newBlake3 = bin2hex(blake3($fileContent)); - // Setze Hashes - $entity->set('md5sum', $newMd5); - $entity->set('sha256', $newSha256); + // Setze Hash + $entity->set('blake3hash', $newBlake3); // Bestimme Status if ($entity->isNew()) { $entity->set('fileStatus', 'new'); } else { - $oldMd5 = $entity->getFetched('md5sum'); - $oldSha256 = $entity->getFetched('sha256'); + $oldBlake3 = $entity->getFetched('blake3hash'); - if ($oldMd5 !== $newMd5 || $oldSha256 !== $newSha256) { + if ($oldBlake3 !== $newBlake3) { $entity->set('fileStatus', 'changed'); } else { $entity->set('fileStatus', 'synced'); diff --git a/custom/Espo/Custom/Resources/i18n/de_DE/CDokumente.json b/custom/Espo/Custom/Resources/i18n/de_DE/CDokumente.json index 4ac7806f..55703cc5 100644 --- a/custom/Espo/Custom/Resources/i18n/de_DE/CDokumente.json +++ b/custom/Espo/Custom/Resources/i18n/de_DE/CDokumente.json @@ -3,8 +3,7 @@ "dokument": "Download", "preview": "Vorschau", "ydocumentuuid": "Y-Document-UUID", - "md5sum": "MD5-Prüfsumme", - "sha256": "SHA256-Prüfsumme", + "blake3hash": "Blake3-Hash", "fileStatus": "Datei-Status", "contactsvmhdokumente": "Freigegebene Nutzer", "vmhMietverhltnisesDokumente": "Mietverhältnisse", @@ -41,6 +40,7 @@ "Create CDokumente": "Dokument erstellen" }, "tooltips": { + "blake3hash": "Kryptografischer Blake3-Hash der Datei (schneller und sicherer als MD5/SHA256)", "fileStatus": "Status der Datei: new = neu hochgeladen, changed = geändert, synced = synchronisiert" }, "options": { diff --git a/custom/Espo/Custom/Resources/i18n/en_US/CDokumente.json b/custom/Espo/Custom/Resources/i18n/en_US/CDokumente.json index 3fb02c7b..c1b6116a 100644 --- a/custom/Espo/Custom/Resources/i18n/en_US/CDokumente.json +++ b/custom/Espo/Custom/Resources/i18n/en_US/CDokumente.json @@ -8,8 +8,7 @@ "vmhErstgespraechsdokumente": "Initial Consultations", "vmhRumungsklagesdokumente": "Eviction Lawsuits", "kuendigungDokumente": "Terminations", - "md5sum": "MD5 Checksum", - "sha256": "SHA256 Checksum", + "blake3hash": "Blake3 Hash", "beteiligte2dokumente": "Parties", "mietobjekt2dokumente": "Properties", "mietinkassosdokumente": "Rent Collection", @@ -47,6 +46,7 @@ "listForAIKnowledge": "List for AI Knowledge" }, "tooltips": { + "blake3hash": "Cryptographic Blake3 hash of the file (faster and more secure than MD5/SHA256)", "fileStatus": "File status: new = newly uploaded, changed = modified, synced = synchronized" }, "options": { diff --git a/custom/Espo/Custom/Resources/layouts/CDokumente/detail.json b/custom/Espo/Custom/Resources/layouts/CDokumente/detail.json index 690d7135..eb0ca04f 100644 --- a/custom/Espo/Custom/Resources/layouts/CDokumente/detail.json +++ b/custom/Espo/Custom/Resources/layouts/CDokumente/detail.json @@ -61,13 +61,9 @@ "name": "fileStatus" }, { - "name": "md5sum" - } - ], - [ - { - "name": "sha256" - } + "name": "blake3hash" + }, + {} ] ], "dynamicLogicVisible": null, diff --git a/custom/Espo/Custom/Resources/metadata/entityDefs/CDokumente.json b/custom/Espo/Custom/Resources/metadata/entityDefs/CDokumente.json index f358f3ba..44840627 100644 --- a/custom/Espo/Custom/Resources/metadata/entityDefs/CDokumente.json +++ b/custom/Espo/Custom/Resources/metadata/entityDefs/CDokumente.json @@ -59,21 +59,14 @@ "readOnlyAfterCreate": false, "isCustom": true }, - "md5sum": { + "blake3hash": { "type": "varchar", - "maxLength": 32, + "maxLength": 64, "copyToClipboard": true, "readOnlyAfterCreate": true, "options": [], - "isCustom": true - }, - "sha256": { - "type": "varchar", - "maxLength": 64, - "readOnlyAfterCreate": true, - "options": [], "isCustom": true, - "copyToClipboard": true + "tooltip": true }, "puls": { "type": "link", @@ -298,14 +291,9 @@ "id" ] }, - "md5sum": { + "blake3hash": { "columns": [ - "md5sum" - ] - }, - "sha256": { - "columns": [ - "sha256" + "blake3hash" ] } }, diff --git a/custom/Espo/Custom/Resources/metadata/formula/CDokumente.json b/custom/Espo/Custom/Resources/metadata/formula/CDokumente.json index 74e7525c..556e5a13 100644 --- a/custom/Espo/Custom/Resources/metadata/formula/CDokumente.json +++ b/custom/Espo/Custom/Resources/metadata/formula/CDokumente.json @@ -1,3 +1,3 @@ { - "beforeSaveScript": "// Automatische x.AI Sync-Status Verwaltung\n\n// Fall 1: xaiId wurde gelöscht (war vorher vorhanden, jetzt leer)\nif (\n attribute\\fetched('xaiId') != null &&\n xaiId == null\n) {\n xaiSyncStatus = 'no_sync';\n}\n// Fall 2: xaiId wird neu gesetzt (war vorher leer, jetzt gefüllt)\nelse if (\n attribute\\fetched('xaiId') == null &&\n xaiId != null\n) {\n xaiSyncStatus = 'pending_sync';\n}\n// Fall 3: Dokument hat xaiId und relevante Felder haben sich geändert\nelse if (\n xaiId != null &&\n xaiSyncStatus != 'no_sync' &&\n (\n attribute\\isChanged('name') ||\n attribute\\isChanged('description') ||\n attribute\\isChanged('dokumentId') ||\n attribute\\isChanged('md5sum') ||\n attribute\\isChanged('sha256')\n )\n) {\n xaiSyncStatus = 'unclean';\n}\n// Fall 4: Bei neuem Dokument MIT xaiId → pending_sync\nelse if (\n entity\\isNew() &&\n xaiId != null\n) {\n xaiSyncStatus = 'pending_sync';\n}\n// Fall 5: Bei neuem Dokument OHNE xaiId → no_sync\nelse if (\n entity\\isNew() &&\n xaiId == null\n) {\n xaiSyncStatus = 'no_sync';\n}\n\n// Automatische Advoware Sync-Status Verwaltung\n\n// Fall 1: aktennr wurde gelöscht (war vorher vorhanden, jetzt leer)\nif (\n attribute\\fetched('aktennr') != null &&\n aktennr == null\n) {\n syncStatus = 'no_sync';\n}\n// Fall 2: aktennr wird neu gesetzt (war vorher leer, jetzt gefüllt)\nelse if (\n attribute\\fetched('aktennr') == null &&\n aktennr != null\n) {\n syncStatus = 'pending_sync';\n}\n// Fall 3: Dokument hat aktennr und relevante Felder haben sich geändert\nelse if (\n aktennr != null &&\n syncStatus != 'no_sync' &&\n (\n attribute\\isChanged('name') ||\n attribute\\isChanged('description') ||\n attribute\\isChanged('dokumentId') ||\n attribute\\isChanged('md5sum') ||\n attribute\\isChanged('sha256')\n )\n) {\n syncStatus = 'unclean';\n}\n// Fall 4: Bei neuem Dokument MIT aktennr → pending_sync\nelse if (\n entity\\isNew() &&\n aktennr != null\n) {\n syncStatus = 'pending_sync';\n}\n// Fall 5: Bei neuem Dokument OHNE aktennr → no_sync\nelse if (\n entity\\isNew() &&\n aktennr == null\n) {\n syncStatus = 'no_sync';\n}" + "beforeSaveScript": "// Automatische x.AI Sync-Status Verwaltung\n\n// Fall 1: xaiId wurde gelöscht (war vorher vorhanden, jetzt leer)\nif (\n attribute\\fetched('xaiId') != null &&\n xaiId == null\n) {\n xaiSyncStatus = 'no_sync';\n}\n// Fall 2: xaiId wird neu gesetzt (war vorher leer, jetzt gefüllt)\nelse if (\n attribute\\fetched('xaiId') == null &&\n xaiId != null\n) {\n xaiSyncStatus = 'pending_sync';\n}\n// Fall 3: Dokument hat xaiId und relevante Felder haben sich geändert\nelse if (\n xaiId != null &&\n xaiSyncStatus != 'no_sync' &&\n (\n attribute\\isChanged('name') ||\n attribute\\isChanged('description') ||\n attribute\\isChanged('dokumentId') ||\n attribute\\isChanged('blake3hash')\n )\n) {\n xaiSyncStatus = 'unclean';\n}\n// Fall 4: Bei neuem Dokument MIT xaiId → pending_sync\nelse if (\n entity\\isNew() &&\n xaiId != null\n) {\n xaiSyncStatus = 'pending_sync';\n}\n// Fall 5: Bei neuem Dokument OHNE xaiId → no_sync\nelse if (\n entity\\isNew() &&\n xaiId == null\n) {\n xaiSyncStatus = 'no_sync';\n}\n\n// Automatische Advoware Sync-Status Verwaltung\n\n// Fall 1: aktennr wurde gelöscht (war vorher vorhanden, jetzt leer)\nif (\n attribute\\fetched('aktennr') != null &&\n aktennr == null\n) {\n syncStatus = 'no_sync';\n}\n// Fall 2: aktennr wird neu gesetzt (war vorher leer, jetzt gefüllt)\nelse if (\n attribute\\fetched('aktennr') == null &&\n aktennr != null\n) {\n syncStatus = 'pending_sync';\n}\n// Fall 3: Dokument hat aktennr und relevante Felder haben sich geändert\nelse if (\n aktennr != null &&\n syncStatus != 'no_sync' &&\n (\n attribute\\isChanged('name') ||\n attribute\\isChanged('description') ||\n attribute\\isChanged('dokumentId') ||\n attribute\\isChanged('blake3hash')\n )\n) {\n syncStatus = 'unclean';\n}\n// Fall 4: Bei neuem Dokument MIT aktennr → pending_sync\nelse if (\n entity\\isNew() &&\n aktennr != null\n) {\n syncStatus = 'pending_sync';\n}\n// Fall 5: Bei neuem Dokument OHNE aktennr → no_sync\nelse if (\n entity\\isNew() &&\n aktennr == null\n) {\n syncStatus = 'no_sync';\n}" } diff --git a/custom/scripts/install_blake3.sh b/custom/scripts/install_blake3.sh new file mode 100755 index 00000000..2e4a5e41 --- /dev/null +++ b/custom/scripts/install_blake3.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# Blake3 PHP Extension Installation Script +# Für EspoCRM Docker Container + +set -e # Beende bei Fehler + +echo "=========================================" +echo "Blake3 PHP Extension Installation" +echo "=========================================" + +# Schritt 1: Build-Tools installieren +echo "" +echo "Schritt 1: Installiere Build-Tools..." +apt-get update +apt-get install -y \ + git \ + build-essential \ + autoconf \ + automake \ + libtool \ + pkg-config \ + curl \ + libcurl4-openssl-dev + +# PHP-Dev ist bereits im Image vorhanden +echo "PHP Development headers: $(php-config --version)" + +# Schritt 2: Blake3 C-Bibliothek klonen +echo "" +echo "Schritt 2: Lade Blake3 C-Bibliothek..." +cd /tmp +rm -rf BLAKE3 php-blake3 +git clone https://github.com/BLAKE3-team/BLAKE3.git +cd BLAKE3/c +gcc -shared -O3 -o libblake3.so blake3.c blake3_dispatch.c blake3_portable.c blake3_sse2_x86-64_unix.S blake3_sse41_x86-64_unix.S blake3_avx2_x86-64_unix.S blake3_avx512_x86-64_unix.S -fPIC +cp libblake3.so /usr/local/lib/ +ldconfig + +# Schritt 3: PHP Blake3 Extension klonen und kompilieren +echo "" +echo "Schritt 3: Kompiliere PHP Blake3 Extension..." +cd /tmp +git clone https://github.com/cypherbits/php-blake3.git +cd php-blake3 + +phpize +./configure +make +make install + +# Schritt 4: Extension aktivieren +echo "" +echo "Schritt 4: Aktiviere Blake3 Extension..." +PHP_INI_DIR=$(php -i | grep "Scan this dir for additional .ini files" | cut -d'>' -f2 | xargs) +if [ -z "$PHP_INI_DIR" ]; then + PHP_INI_DIR="/usr/local/etc/php/conf.d" +fi + +echo "extension=blake3.so" > ${PHP_INI_DIR}/99-blake3.ini + +# Schritt 5: Verifizierung +echo "" +echo "Schritt 5: Verifiziere Installation..." +php -m | grep -i blake3 +if [ $? -eq 0 ]; then + echo "✅ Blake3 Extension erfolgreich installiert!" + php -r "echo 'Test Hash: ' . hash('blake3', 'test') . PHP_EOL;" +else + echo "❌ Blake3 Extension nicht geladen!" + exit 1 +fi + +# Cleanup +echo "" +echo "Schritt 6: Aufräumen..." +cd / +rm -rf /tmp/BLAKE3 /tmp/php-blake3 + +echo "" +echo "=========================================" +echo "✅ Installation abgeschlossen!" +echo "=========================================" +echo "" +echo "Nächste Schritte:" +echo "1. Starte PHP-FPM neu: service php8.4-fpm restart || pkill -USR2 php-fpm" +echo "2. Überprüfe: php -m | grep blake3" +echo "3. Teste: php -r \"echo hash('blake3', 'test');\"" diff --git a/data/config.php b/data/config.php index 3ec8ad72..0fd3a8cc 100644 --- a/data/config.php +++ b/data/config.php @@ -360,7 +360,7 @@ return [ 0 => 'youtube.com', 1 => 'google.com' ], - 'microtime' => 1773262547.930204, + 'microtime' => 1773264000.882682, 'siteUrl' => 'https://crm.bitbylaw.com', 'fullTextSearchMinLength' => 4, 'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws', diff --git a/data/state.php b/data/state.php index 55f522e2..be12685f 100644 --- a/data/state.php +++ b/data/state.php @@ -1,7 +1,7 @@ 1773262548, - 'microtimeState' => 1773262548.062426, + 'cacheTimestamp' => 1773264001, + 'microtimeState' => 1773264001.003279, 'currencyRates' => [ 'EUR' => 1.0 ],