Implement Blake3 hashing in CDokumente; update related metadata and localization files

This commit is contained in:
2026-03-11 22:20:21 +01:00
parent e15dd14cab
commit 80dc3b40d3
9 changed files with 114 additions and 41 deletions

View File

@@ -35,22 +35,24 @@ class CDokumente extends \Espo\Core\Hooks\Base
return; return;
} }
// Berechne neue Hashes // Berechne Blake3 Hash
$newMd5 = hash_file('md5', $filePath); $fileContent = file_get_contents($filePath);
$newSha256 = hash_file('sha256', $filePath); if ($fileContent === false) {
return;
}
$newBlake3 = bin2hex(blake3($fileContent));
// Setze Hashes // Setze Hash
$entity->set('md5sum', $newMd5); $entity->set('blake3hash', $newBlake3);
$entity->set('sha256', $newSha256);
// Bestimme Status // Bestimme Status
if ($entity->isNew()) { if ($entity->isNew()) {
$entity->set('fileStatus', 'new'); $entity->set('fileStatus', 'new');
} else { } else {
$oldMd5 = $entity->getFetched('md5sum'); $oldBlake3 = $entity->getFetched('blake3hash');
$oldSha256 = $entity->getFetched('sha256');
if ($oldMd5 !== $newMd5 || $oldSha256 !== $newSha256) { if ($oldBlake3 !== $newBlake3) {
$entity->set('fileStatus', 'changed'); $entity->set('fileStatus', 'changed');
} else { } else {
$entity->set('fileStatus', 'synced'); $entity->set('fileStatus', 'synced');

View File

@@ -3,8 +3,7 @@
"dokument": "Download", "dokument": "Download",
"preview": "Vorschau", "preview": "Vorschau",
"ydocumentuuid": "Y-Document-UUID", "ydocumentuuid": "Y-Document-UUID",
"md5sum": "MD5-Prüfsumme", "blake3hash": "Blake3-Hash",
"sha256": "SHA256-Prüfsumme",
"fileStatus": "Datei-Status", "fileStatus": "Datei-Status",
"contactsvmhdokumente": "Freigegebene Nutzer", "contactsvmhdokumente": "Freigegebene Nutzer",
"vmhMietverhltnisesDokumente": "Mietverhältnisse", "vmhMietverhltnisesDokumente": "Mietverhältnisse",
@@ -41,6 +40,7 @@
"Create CDokumente": "Dokument erstellen" "Create CDokumente": "Dokument erstellen"
}, },
"tooltips": { "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" "fileStatus": "Status der Datei: new = neu hochgeladen, changed = geändert, synced = synchronisiert"
}, },
"options": { "options": {

View File

@@ -8,8 +8,7 @@
"vmhErstgespraechsdokumente": "Initial Consultations", "vmhErstgespraechsdokumente": "Initial Consultations",
"vmhRumungsklagesdokumente": "Eviction Lawsuits", "vmhRumungsklagesdokumente": "Eviction Lawsuits",
"kuendigungDokumente": "Terminations", "kuendigungDokumente": "Terminations",
"md5sum": "MD5 Checksum", "blake3hash": "Blake3 Hash",
"sha256": "SHA256 Checksum",
"beteiligte2dokumente": "Parties", "beteiligte2dokumente": "Parties",
"mietobjekt2dokumente": "Properties", "mietobjekt2dokumente": "Properties",
"mietinkassosdokumente": "Rent Collection", "mietinkassosdokumente": "Rent Collection",
@@ -47,6 +46,7 @@
"listForAIKnowledge": "List for AI Knowledge" "listForAIKnowledge": "List for AI Knowledge"
}, },
"tooltips": { "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" "fileStatus": "File status: new = newly uploaded, changed = modified, synced = synchronized"
}, },
"options": { "options": {

View File

@@ -61,13 +61,9 @@
"name": "fileStatus" "name": "fileStatus"
}, },
{ {
"name": "md5sum" "name": "blake3hash"
} },
], {}
[
{
"name": "sha256"
}
] ]
], ],
"dynamicLogicVisible": null, "dynamicLogicVisible": null,

View File

@@ -59,21 +59,14 @@
"readOnlyAfterCreate": false, "readOnlyAfterCreate": false,
"isCustom": true "isCustom": true
}, },
"md5sum": { "blake3hash": {
"type": "varchar", "type": "varchar",
"maxLength": 32, "maxLength": 64,
"copyToClipboard": true, "copyToClipboard": true,
"readOnlyAfterCreate": true, "readOnlyAfterCreate": true,
"options": [], "options": [],
"isCustom": true
},
"sha256": {
"type": "varchar",
"maxLength": 64,
"readOnlyAfterCreate": true,
"options": [],
"isCustom": true, "isCustom": true,
"copyToClipboard": true "tooltip": true
}, },
"puls": { "puls": {
"type": "link", "type": "link",
@@ -298,14 +291,9 @@
"id" "id"
] ]
}, },
"md5sum": { "blake3hash": {
"columns": [ "columns": [
"md5sum" "blake3hash"
]
},
"sha256": {
"columns": [
"sha256"
] ]
} }
}, },

View File

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

View File

@@ -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');\""

View File

@@ -360,7 +360,7 @@ return [
0 => 'youtube.com', 0 => 'youtube.com',
1 => 'google.com' 1 => 'google.com'
], ],
'microtime' => 1773262547.930204, 'microtime' => 1773264000.882682,
'siteUrl' => 'https://crm.bitbylaw.com', 'siteUrl' => 'https://crm.bitbylaw.com',
'fullTextSearchMinLength' => 4, 'fullTextSearchMinLength' => 4,
'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws', 'webSocketUrl' => 'ws://api.bitbylaw.com:5000/espocrm/ws',

View File

@@ -1,7 +1,7 @@
<?php <?php
return [ return [
'cacheTimestamp' => 1773262548, 'cacheTimestamp' => 1773264001,
'microtimeState' => 1773262548.062426, 'microtimeState' => 1773264001.003279,
'currencyRates' => [ 'currencyRates' => [
'EUR' => 1.0 'EUR' => 1.0
], ],