From ea15be0368fcadc7f87d803e096f3b939e58394f Mon Sep 17 00:00:00 2001 From: Kroonk Date: Thu, 16 Apr 2026 19:40:18 +0200 Subject: [PATCH] Fix Thumbnail-Generierung (magick statt convert) und DB-Migration fuer role-Spalte - entrypoint.sh: ImageMagick 7 nutzt 'magick' statt 'convert', Auto-Detection - database.js: Migration fuer role-Spalte bei bestehender DB (ALTER TABLE) - main.js: Fallback auf Vollbild wenn Thumbnail fehlt (error handler) Co-Authored-By: Claude Opus 4.6 --- assets/js/main.js | 8 ++++++++ assets/js/main.min.js | 2 +- backend/database.js | 10 +++++++++- entrypoint.sh | 16 +++++++++++++++- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/assets/js/main.js b/assets/js/main.js index b9f2ba1..f1952a9 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -214,6 +214,14 @@ if ((x = $image_img.data("position"))) $image.css("background-position", x); $image_img.hide(); + // Fallback: Wenn Thumbnail fehlt, Vollbild als Vorschau nutzen + $image_img[0].addEventListener("error", function() { + var fullUrl = $image.attr("href"); + if (fullUrl) { + $image.css("background-image", "url('" + fullUrl + "')"); + } + }); + $image_img[0].addEventListener("load", function() { EXIF.getData($image_img[0], function () { exifDatas[$image_img.data('name')] = getExifDataMarkup(this); diff --git a/assets/js/main.min.js b/assets/js/main.min.js index a46089f..db67895 100644 --- a/assets/js/main.min.js +++ b/assets/js/main.min.js @@ -1 +1 @@ -!function(e){var n,o=e(window),a=e("body");e("#wrapper");(breakpoints({xlarge:["1281px","1680px"],large:["981px","1280px"],medium:["737px","980px"],small:["481px","736px"],xsmall:[null,"480px"]}),"ie"==browser.name&&a.addClass("ie"),browser.mobile&&a.addClass("touch"),browser.canUse("transition"))&&(o.on("load",function(){window.setTimeout(function(){a.removeClass("is-preload")},100)}),o.on("resize",function(){window.clearTimeout(n),a.addClass("is-resizing"),n=window.setTimeout(function(){a.removeClass("is-resizing")},100)}));o.scrollTop(0);var t=e(".panel");t.each(function(){var n=e(this),o=e('[href="#'+n.attr("id")+'"]');e('
').appendTo(n).on("click",function(e){n.trigger("---hide")}),n.on("click",function(e){e.stopPropagation()}).on("---toggle",function(){n.hasClass("active")?n.triggerHandler("---hide"):n.triggerHandler("---show")}).on("---show",function(){a.hasClass("content-active")&&t.trigger("---hide"),n.addClass("active"),o.addClass("active"),a.addClass("content-active")}).on("---hide",function(){n.removeClass("active"),o.removeClass("active"),a.removeClass("content-active")}),o.removeAttr("href").css("cursor","pointer").on("click",function(e){e.preventDefault(),e.stopPropagation(),n.trigger("---toggle")})}),a.on("click",function(e){a.hasClass("content-active")&&(e.preventDefault(),e.stopPropagation(),t.trigger("---hide"))}),o.on("keyup",function(e){27==e.keyCode&&a.hasClass("content-active")&&(e.preventDefault(),e.stopPropagation(),t.trigger("---hide"))}),e("#header").find("a").each(function(){var n=e(this),o=n.attr("href");o&&"#"!=o.charAt(0)&&n.removeAttr("href").css("cursor","pointer").on("click",function(e){e.preventDefault(),e.stopPropagation(),window.location.href=o})}),e("#footer").find(".copyright").each(function(){var n=e(this),o=n.parent(),a=o.parent().children().last();breakpoints.on("<=medium",function(){n.appendTo(a)}),breakpoints.on(">medium",function(){n.appendTo(o)})});var i=e("#main"),r={},s=["jpg","jpeg","png","gif","webp"];function l(n){var o=e("#main").data("exif"),a="";for(var t in o){var i=o[t],r=EXIF.getTag(n,i.tag);void 0!==r&&(a+=' '+r+"  ")}return a}e.ajax({url:"/images/fulls/",dataType:"json",cache:!1,success:function(n){if(0!==n.length){var o=n.filter(function(e){if("file"!==e.type)return!1;var n=e.name.split(".").pop().toLowerCase();return-1!==s.indexOf(n)});0!==o.length?(o.sort(function(e,n){return e.name.localeCompare(n.name)}),o.forEach(function(n){var o=e('
'),a=n.folder?encodeURIComponent(n.folder)+"/":"",t="/images/fulls/"+a+encodeURIComponent(n.name),r="/images/thumbs/"+a+encodeURIComponent(n.name),s=e(''),l=e('');s.append(l),o.append(s),i.append(o)}),i.children(".thumb").each(function(){var n,o=e(this),a=o.find(".image"),t=a.children("img");0!=a.length&&(a.css("background-image","url('"+t.attr("src")+"')"),(n=t.data("position"))&&a.css("background-position",n),t.hide(),t[0].addEventListener("load",function(){EXIF.getData(t[0],function(){r[t.data("name")]=l(this)})}))}),i.poptrox({baseZIndex:2e4,caption:function(e){var n=e.children("img"),o=r[n.data("name")];void 0===o&&EXIF.getData(n[0],function(){o=r[n.data("name")]=l(this)});var a='
Bild Speichern ';return void 0!==o?"

"+o+a+"

":"

"+a+"

"},fadeSpeed:300,onPopupClose:function(){a.removeClass("modal-active")},onPopupOpen:function(){a.addClass("modal-active")},overlayOpacity:0,popupCloserText:"",popupHeight:150,popupLoaderText:"",popupSpeed:300,popupWidth:150,selector:".thumb > a.image",usePopupCaption:!0,usePopupCloser:!0,usePopupDefaultStyling:!1,usePopupForceClose:!0,usePopupLoader:!0,usePopupNav:!0,windowMargin:50}),breakpoints.on("<=xsmall",function(){i[0]._poptrox.windowMargin=0}),breakpoints.on(">xsmall",function(){i[0]._poptrox.windowMargin=50})):i.html('

Es wurden noch keine passenden Bilder für diesen Account hochgeladen oder zugewiesen.

')}else i.html('

Noch keine Bilder vorhanden.

')},error:function(){i.html('

Keine Bilder gefunden. Lade Bilder in deinen Nextcloud-Ordner hoch.

')}});var c=localStorage.getItem("mischkomposition_theme")||"main";function p(n){"light"===n?e("#theme-toggle i").removeClass("fa-sun").addClass("fa-moon"):e("#theme-toggle i").removeClass("fa-moon").addClass("fa-sun")}p(c),e("#theme-toggle").click(function(n){n.preventDefault(),c="main"===c?"light":"main",localStorage.setItem("mischkomposition_theme",c),e("#theme-style").attr("href","assets/css/"+c+".min.css"),p(c)}),e.get("/api/auth/me").done(function(n){e("#login-username, #login-password, #login-submit").hide(),e("#logout-btn").show().css("display","inline-block"),e("#pw-change-section").show(),"admin"===n.role&&e("#admin-link").html('Admin-Dashboard'),e("#login-nav").html('Account ')}),e("#login-form").submit(function(n){n.preventDefault(),e.post("/api/auth/login",{username:e("#login-username").val(),password:e("#login-password").val()}).done(function(){window.location.reload()}).fail(function(e){var n="Login fehlgeschlagen.";e.responseJSON&&"Account abgelaufen"===e.responseJSON.error&&(n="Dein Account ist abgelaufen. Bitte kontaktiere den Administrator."),alert(n)})}),e("#logout-btn").click(function(n){n.preventDefault(),e.post("/api/auth/logout").done(function(){window.location.reload()})}),e("#pw-change-form").submit(function(n){n.preventDefault();var o=e("#pw-new").val();o===e("#pw-confirm").val()?e.post("/api/auth/change-password",{old_password:e("#pw-old").val(),new_password:o}).done(function(){alert("Passwort geaendert!"),e("#pw-old, #pw-new, #pw-confirm").val("")}).fail(function(e){alert(e.responseJSON?e.responseJSON.error:"Fehler")}):alert("Passwoerter stimmen nicht ueberein.")})}(jQuery); \ No newline at end of file +!function(e){var n,o=e(window),a=e("body");e("#wrapper");(breakpoints({xlarge:["1281px","1680px"],large:["981px","1280px"],medium:["737px","980px"],small:["481px","736px"],xsmall:[null,"480px"]}),"ie"==browser.name&&a.addClass("ie"),browser.mobile&&a.addClass("touch"),browser.canUse("transition"))&&(o.on("load",function(){window.setTimeout(function(){a.removeClass("is-preload")},100)}),o.on("resize",function(){window.clearTimeout(n),a.addClass("is-resizing"),n=window.setTimeout(function(){a.removeClass("is-resizing")},100)}));o.scrollTop(0);var t=e(".panel");t.each(function(){var n=e(this),o=e('[href="#'+n.attr("id")+'"]');e('
').appendTo(n).on("click",function(e){n.trigger("---hide")}),n.on("click",function(e){e.stopPropagation()}).on("---toggle",function(){n.hasClass("active")?n.triggerHandler("---hide"):n.triggerHandler("---show")}).on("---show",function(){a.hasClass("content-active")&&t.trigger("---hide"),n.addClass("active"),o.addClass("active"),a.addClass("content-active")}).on("---hide",function(){n.removeClass("active"),o.removeClass("active"),a.removeClass("content-active")}),o.removeAttr("href").css("cursor","pointer").on("click",function(e){e.preventDefault(),e.stopPropagation(),n.trigger("---toggle")})}),a.on("click",function(e){a.hasClass("content-active")&&(e.preventDefault(),e.stopPropagation(),t.trigger("---hide"))}),o.on("keyup",function(e){27==e.keyCode&&a.hasClass("content-active")&&(e.preventDefault(),e.stopPropagation(),t.trigger("---hide"))}),e("#header").find("a").each(function(){var n=e(this),o=n.attr("href");o&&"#"!=o.charAt(0)&&n.removeAttr("href").css("cursor","pointer").on("click",function(e){e.preventDefault(),e.stopPropagation(),window.location.href=o})}),e("#footer").find(".copyright").each(function(){var n=e(this),o=n.parent(),a=o.parent().children().last();breakpoints.on("<=medium",function(){n.appendTo(a)}),breakpoints.on(">medium",function(){n.appendTo(o)})});var i=e("#main"),r={},s=["jpg","jpeg","png","gif","webp"];function l(n){var o=e("#main").data("exif"),a="";for(var t in o){var i=o[t],r=EXIF.getTag(n,i.tag);void 0!==r&&(a+=' '+r+"  ")}return a}e.ajax({url:"/images/fulls/",dataType:"json",cache:!1,success:function(n){if(0!==n.length){var o=n.filter(function(e){if("file"!==e.type)return!1;var n=e.name.split(".").pop().toLowerCase();return-1!==s.indexOf(n)});0!==o.length?(o.sort(function(e,n){return e.name.localeCompare(n.name)}),o.forEach(function(n){var o=e('
'),a=n.folder?encodeURIComponent(n.folder)+"/":"",t="/images/fulls/"+a+encodeURIComponent(n.name),r="/images/thumbs/"+a+encodeURIComponent(n.name),s=e(''),l=e('');s.append(l),o.append(s),i.append(o)}),i.children(".thumb").each(function(){var n,o=e(this),a=o.find(".image"),t=a.children("img");0!=a.length&&(a.css("background-image","url('"+t.attr("src")+"')"),(n=t.data("position"))&&a.css("background-position",n),t.hide(),t[0].addEventListener("error",function(){var e=a.attr("href");e&&a.css("background-image","url('"+e+"')")}),t[0].addEventListener("load",function(){EXIF.getData(t[0],function(){r[t.data("name")]=l(this)})}))}),i.poptrox({baseZIndex:2e4,caption:function(e){var n=e.children("img"),o=r[n.data("name")];void 0===o&&EXIF.getData(n[0],function(){o=r[n.data("name")]=l(this)});var a='
Bild Speichern ';return void 0!==o?"

"+o+a+"

":"

"+a+"

"},fadeSpeed:300,onPopupClose:function(){a.removeClass("modal-active")},onPopupOpen:function(){a.addClass("modal-active")},overlayOpacity:0,popupCloserText:"",popupHeight:150,popupLoaderText:"",popupSpeed:300,popupWidth:150,selector:".thumb > a.image",usePopupCaption:!0,usePopupCloser:!0,usePopupDefaultStyling:!1,usePopupForceClose:!0,usePopupLoader:!0,usePopupNav:!0,windowMargin:50}),breakpoints.on("<=xsmall",function(){i[0]._poptrox.windowMargin=0}),breakpoints.on(">xsmall",function(){i[0]._poptrox.windowMargin=50})):i.html('

Es wurden noch keine passenden Bilder für diesen Account hochgeladen oder zugewiesen.

')}else i.html('

Noch keine Bilder vorhanden.

')},error:function(){i.html('

Keine Bilder gefunden. Lade Bilder in deinen Nextcloud-Ordner hoch.

')}});var c=localStorage.getItem("mischkomposition_theme")||"main";function p(n){"light"===n?e("#theme-toggle i").removeClass("fa-sun").addClass("fa-moon"):e("#theme-toggle i").removeClass("fa-moon").addClass("fa-sun")}p(c),e("#theme-toggle").click(function(n){n.preventDefault(),c="main"===c?"light":"main",localStorage.setItem("mischkomposition_theme",c),e("#theme-style").attr("href","assets/css/"+c+".min.css"),p(c)}),e.get("/api/auth/me").done(function(n){e("#login-username, #login-password, #login-submit").hide(),e("#logout-btn").show().css("display","inline-block"),e("#pw-change-section").show(),"admin"===n.role&&e("#admin-link").html('Admin-Dashboard'),e("#login-nav").html('Account ')}),e("#login-form").submit(function(n){n.preventDefault(),e.post("/api/auth/login",{username:e("#login-username").val(),password:e("#login-password").val()}).done(function(){window.location.reload()}).fail(function(e){var n="Login fehlgeschlagen.";e.responseJSON&&"Account abgelaufen"===e.responseJSON.error&&(n="Dein Account ist abgelaufen. Bitte kontaktiere den Administrator."),alert(n)})}),e("#logout-btn").click(function(n){n.preventDefault(),e.post("/api/auth/logout").done(function(){window.location.reload()})}),e("#pw-change-form").submit(function(n){n.preventDefault();var o=e("#pw-new").val();o===e("#pw-confirm").val()?e.post("/api/auth/change-password",{old_password:e("#pw-old").val(),new_password:o}).done(function(){alert("Passwort geaendert!"),e("#pw-old, #pw-new, #pw-confirm").val("")}).fail(function(e){alert(e.responseJSON?e.responseJSON.error:"Fehler")}):alert("Passwoerter stimmen nicht ueberein.")})}(jQuery); \ No newline at end of file diff --git a/backend/database.js b/backend/database.js index f25e366..597e934 100644 --- a/backend/database.js +++ b/backend/database.js @@ -17,7 +17,15 @@ db.serialize(() => { created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`); - // Migration: created_at hinzufuegen falls Tabelle schon existiert + // Migration: Spalten hinzufuegen falls Tabelle schon existiert (alte DB ohne role/created_at) + db.run(`ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT 'client'`, (err) => { + // Ignoriere Fehler wenn Spalte schon existiert + if (!err) { + // Alte Admins (die einzigen User vor dem Update) als admin markieren + db.run(`UPDATE users SET role = 'admin' WHERE id = 1`); + console.log('Migration: role-Spalte hinzugefuegt, User ID 1 als admin gesetzt'); + } + }); db.run(`ALTER TABLE users ADD COLUMN created_at DATETIME DEFAULT CURRENT_TIMESTAMP`, (err) => { // Ignoriere Fehler wenn Spalte schon existiert }); diff --git a/entrypoint.sh b/entrypoint.sh index f1f5621..c0dcf50 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,10 +3,21 @@ FULLS_DIR="/app/public/images/fulls" THUMBS_DIR="/app/public/images/thumbs" +# ImageMagick 7 (Alpine) nutzt "magick" statt "convert" +if command -v magick >/dev/null 2>&1; then + IM_CMD="magick" +elif command -v convert >/dev/null 2>&1; then + IM_CMD="convert" +else + echo "FEHLER: Weder magick noch convert gefunden! Thumbnails koennen nicht erzeugt werden." + IM_CMD="" +fi + # Thumbnails fuer einen Quell- und Zielordner generieren generate_thumbs_for_dir() { src_dir="$1" dst_dir="$2" + [ -z "$IM_CMD" ] && return mkdir -p "$dst_dir" for img in "$src_dir"/*; do [ -f "$img" ] || continue @@ -21,7 +32,10 @@ generate_thumbs_for_dir() { if [ ! -f "$dst_dir/$filename" ] || [ "$img" -nt "$dst_dir/$filename" ]; then echo "Thumbnail erstellen: $dst_dir/$filename" - convert "$img" -resize 512x512 -quality 80 "$dst_dir/$filename" 2>/dev/null + $IM_CMD "$img" -resize 512x512 -quality 80 "$dst_dir/$filename" + if [ $? -ne 0 ]; then + echo "WARNUNG: Thumbnail fehlgeschlagen fuer $filename" + fi fi done }