Nextcloud-Integration: Dynamische Galerie + Auto-Thumbnails
- Galerie laedt Bilder dynamisch per JavaScript statt Jekyll-Loop - nginx autoindex liefert JSON-Bilderliste - Nextcloud-Ordner (Websitebilder) wird als Volume gemountet - ImageMagick generiert automatisch 512px Thumbnails - Hintergrund-Check alle 60s fuer neue Bilder - Neuer entrypoint.sh fuer Thumbnail-Generierung + nginx Start Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
27
Brain.md
27
Brain.md
@@ -59,13 +59,21 @@ Photography/
|
|||||||
└── CNAME # Custom Domain Config
|
└── CNAME # Custom Domain Config
|
||||||
```
|
```
|
||||||
|
|
||||||
## Wie die Galerie funktioniert
|
## Wie die Galerie funktioniert (Dynamisch)
|
||||||
|
|
||||||
1. Jekyll scannt `images/fulls/` nach statischen Dateien
|
1. **Bilder hochladen**: Fotos in Nextcloud-Ordner `Websitebilder` ablegen
|
||||||
2. `index.html` iteriert mit `{% for image in site.static_files %}` ueber alle Bilder
|
2. **Volume-Mount**: Docker mountet den Nextcloud-Ordner als `/images/fulls/` (read-only)
|
||||||
3. Fuer jedes Bild wird ein `<article class="thumb">` erstellt
|
3. **Thumbnails**: `entrypoint.sh` generiert automatisch 512px Thumbnails via ImageMagick
|
||||||
4. Klick oeffnet Poptrox-Lightbox mit Vollbild + EXIF-Daten
|
4. **Thumbnail-Check**: Alle 60 Sekunden prueft der Container auf neue Bilder
|
||||||
5. EXIF-Daten werden clientseitig via `exif.js` aus dem Bild gelesen
|
5. **nginx autoindex**: Liefert JSON-Liste aller Dateien in `/images/fulls/`
|
||||||
|
6. **JavaScript (main.js)**: Fetcht die JSON-Liste und baut dynamisch `<article class="thumb">` Elemente
|
||||||
|
7. **Poptrox-Lightbox**: Klick oeffnet Vollbild + EXIF-Daten (clientseitig via exif.js)
|
||||||
|
|
||||||
|
### Nextcloud-Integration
|
||||||
|
- **Nextcloud-Pfad**: `/volume1/nextcloud/data/Tom/files/Websitebilder`
|
||||||
|
- **Mount im Container**: `/usr/share/nginx/html/images/fulls:ro`
|
||||||
|
- **Thumbnails-Volume**: Docker-Volume `thumbs` (persistent)
|
||||||
|
- **Workflow**: Bild in Nextcloud hochladen -> max. 60s warten -> Seite neu laden -> fertig
|
||||||
|
|
||||||
## Wichtige Konfiguration (_config.yml)
|
## Wichtige Konfiguration (_config.yml)
|
||||||
|
|
||||||
@@ -97,7 +105,8 @@ Photography/
|
|||||||
|
|
||||||
### Docker-Dateien
|
### Docker-Dateien
|
||||||
- `Dockerfile` - Multi-Stage: Ruby/Jekyll baut Site -> nginx serviert statische Dateien
|
- `Dockerfile` - Multi-Stage: Ruby/Jekyll baut Site -> nginx serviert statische Dateien
|
||||||
- `docker-compose.yml` - Container-Definition, Port 8080:80, auto-restart
|
- `docker-compose.yml` - Container-Definition, Port 8080:80, Nextcloud-Volume, auto-restart
|
||||||
|
- `entrypoint.sh` - Thumbnail-Generierung + periodischer Check (alle 60s)
|
||||||
- `nginx.conf` - Webserver-Config mit Caching und Sicherheit
|
- `nginx.conf` - Webserver-Config mit Caching und Sicherheit
|
||||||
- `.dockerignore` - Haelt Build-Context klein (kein .git, node_modules etc.)
|
- `.dockerignore` - Haelt Build-Context klein (kein .git, node_modules etc.)
|
||||||
|
|
||||||
@@ -123,7 +132,9 @@ Photography/
|
|||||||
- [x] Kontaktformular auf tom@mischlabs.de konfiguriert
|
- [x] Kontaktformular auf tom@mischlabs.de konfiguriert
|
||||||
- [x] Google Analytics vom Originalautor entfernt
|
- [x] Google Analytics vom Originalautor entfernt
|
||||||
- [x] Custom Domain: tom.mischlabs.de eingetragen
|
- [x] Custom Domain: tom.mischlabs.de eingetragen
|
||||||
- [ ] Eigene Bilder einpflegen
|
- [x] Nextcloud-Integration (Bilder aus Websitebilder-Ordner)
|
||||||
|
- [x] Dynamische Galerie (JS statt Jekyll-Loop)
|
||||||
|
- [x] Automatische Thumbnail-Generierung
|
||||||
- [ ] Footer Bio-Text schreiben (optional)
|
- [ ] Footer Bio-Text schreiben (optional)
|
||||||
- [ ] Farbschema/Design anpassen
|
- [ ] Farbschema/Design anpassen
|
||||||
- [ ] Eigene Kategorien/Alben erstellen (Feature-Erweiterung)
|
- [ ] Eigene Kategorien/Alben erstellen (Feature-Erweiterung)
|
||||||
|
|||||||
12
Dockerfile
12
Dockerfile
@@ -12,10 +12,20 @@ COPY . .
|
|||||||
|
|
||||||
RUN bundle exec jekyll build
|
RUN bundle exec jekyll build
|
||||||
|
|
||||||
# === Serve-Phase: nginx liefert nur die fertigen Dateien aus ===
|
# === Serve-Phase: nginx + ImageMagick fuer Thumbnails ===
|
||||||
FROM nginx:alpine
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache imagemagick
|
||||||
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY --from=builder /site/_site /usr/share/nginx/html
|
COPY --from=builder /site/_site /usr/share/nginx/html
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
# Leere Bildverzeichnisse anlegen (werden durch Volumes ueberschrieben)
|
||||||
|
RUN mkdir -p /usr/share/nginx/html/images/fulls \
|
||||||
|
/usr/share/nginx/html/images/thumbs
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
html5up.net | @ajlkn
|
html5up.net | @ajlkn
|
||||||
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
|
||||||
|
|
||||||
Added EXIF data and enhanced for Jekyll by Ram Patra
|
Enhanced for dynamic gallery loading - Mischkomposition by Tom Misch
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function ($) {
|
(function ($) {
|
||||||
@@ -75,22 +75,14 @@
|
|||||||
else $this.triggerHandler("---show");
|
else $this.triggerHandler("---show");
|
||||||
})
|
})
|
||||||
.on("---show", function () {
|
.on("---show", function () {
|
||||||
// Hide other content.
|
|
||||||
if ($body.hasClass("content-active")) $panels.trigger("---hide");
|
if ($body.hasClass("content-active")) $panels.trigger("---hide");
|
||||||
|
|
||||||
// Activate content, toggles.
|
|
||||||
$this.addClass("active");
|
$this.addClass("active");
|
||||||
$toggles.addClass("active");
|
$toggles.addClass("active");
|
||||||
|
|
||||||
// Activate body.
|
|
||||||
$body.addClass("content-active");
|
$body.addClass("content-active");
|
||||||
})
|
})
|
||||||
.on("---hide", function () {
|
.on("---hide", function () {
|
||||||
// Deactivate content, toggles.
|
|
||||||
$this.removeClass("active");
|
$this.removeClass("active");
|
||||||
$toggles.removeClass("active");
|
$toggles.removeClass("active");
|
||||||
|
|
||||||
// Deactivate body.
|
|
||||||
$body.removeClass("content-active");
|
$body.removeClass("content-active");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -101,7 +93,6 @@
|
|||||||
.on("click", function (event) {
|
.on("click", function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
$this.trigger("---toggle");
|
$this.trigger("---toggle");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -111,7 +102,6 @@
|
|||||||
if ($body.hasClass("content-active")) {
|
if ($body.hasClass("content-active")) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
$panels.trigger("---hide");
|
$panels.trigger("---hide");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -120,7 +110,6 @@
|
|||||||
if (event.keyCode == 27 && $body.hasClass("content-active")) {
|
if (event.keyCode == 27 && $body.hasClass("content-active")) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
$panels.trigger("---hide");
|
$panels.trigger("---hide");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -128,22 +117,16 @@
|
|||||||
// Header.
|
// Header.
|
||||||
var $header = $("#header");
|
var $header = $("#header");
|
||||||
|
|
||||||
// Links.
|
|
||||||
$header.find("a").each(function () {
|
$header.find("a").each(function () {
|
||||||
var $this = $(this),
|
var $this = $(this),
|
||||||
href = $this.attr("href");
|
href = $this.attr("href");
|
||||||
|
|
||||||
// Internal link? Skip.
|
|
||||||
if (!href || href.charAt(0) == "#") return;
|
if (!href || href.charAt(0) == "#") return;
|
||||||
|
|
||||||
// Redirect on click.
|
|
||||||
$this
|
$this
|
||||||
.removeAttr("href")
|
.removeAttr("href")
|
||||||
.css("cursor", "pointer")
|
.css("cursor", "pointer")
|
||||||
.on("click", function (event) {
|
.on("click", function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
window.location.href = href;
|
window.location.href = href;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -151,9 +134,6 @@
|
|||||||
// Footer.
|
// Footer.
|
||||||
var $footer = $("#footer");
|
var $footer = $("#footer");
|
||||||
|
|
||||||
// Copyright.
|
|
||||||
// This basically just moves the copyright line to the end of the *last* sibling of its current parent
|
|
||||||
// when the "medium" breakpoint activates, and moves it back when it deactivates.
|
|
||||||
$footer.find(".copyright").each(function () {
|
$footer.find(".copyright").each(function () {
|
||||||
var $this = $(this),
|
var $this = $(this),
|
||||||
$parent = $this.parent(),
|
$parent = $this.parent(),
|
||||||
@@ -168,86 +148,110 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Main.
|
// Main - Dynamische Galerie
|
||||||
var $main = $("#main"),
|
var $main = $("#main"),
|
||||||
exifDatas = {};
|
exifDatas = {};
|
||||||
|
|
||||||
// Thumbs.
|
var imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||||
$main.children(".thumb").each(function () {
|
|
||||||
var $this = $(this),
|
|
||||||
$image = $this.find(".image"),
|
|
||||||
$image_img = $image.children("img"),
|
|
||||||
x;
|
|
||||||
|
|
||||||
// No image? Bail.
|
function loadGallery() {
|
||||||
if ($image.length == 0) return;
|
$.ajax({
|
||||||
|
url: '/images/fulls/',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(files) {
|
||||||
|
var images = files.filter(function(f) {
|
||||||
|
if (f.type !== 'file') return false;
|
||||||
|
var ext = f.name.split('.').pop().toLowerCase();
|
||||||
|
return imageExtensions.indexOf(ext) !== -1;
|
||||||
|
});
|
||||||
|
|
||||||
// Image.
|
images.sort(function(a, b) {
|
||||||
// This sets the background of the "image" <span> to the image pointed to by its child
|
return a.name.localeCompare(b.name);
|
||||||
// <img> (which is then hidden). Gives us way more flexibility.
|
});
|
||||||
|
|
||||||
// Set background.
|
images.forEach(function(file) {
|
||||||
$image.css("background-image", "url(" + $image_img.attr("src") + ")");
|
var article = $('<article class="thumb"></article>');
|
||||||
|
var link = $('<a class="image" href="/images/fulls/' + encodeURIComponent(file.name) + '"></a>');
|
||||||
|
var img = $('<img src="/images/thumbs/' + encodeURIComponent(file.name) + '" alt="" data-name="' + file.name.replace(/"/g, '"') + '" />');
|
||||||
|
link.append(img);
|
||||||
|
article.append(link);
|
||||||
|
$main.append(article);
|
||||||
|
});
|
||||||
|
|
||||||
// Set background position.
|
initThumbs();
|
||||||
if ((x = $image_img.data("position"))) $image.css("background-position", x);
|
initPoptrox();
|
||||||
|
},
|
||||||
// Hide original img.
|
error: function() {
|
||||||
$image_img.hide();
|
$main.html('<p style="text-align:center;color:#fff;padding:2em;">Keine Bilder gefunden. Lade Bilder in deinen Nextcloud-Ordner hoch.</p>');
|
||||||
|
}
|
||||||
// EXIF data
|
|
||||||
$image_img[0].addEventListener("load", function() {
|
|
||||||
EXIF.getData($image_img[0], function () {
|
|
||||||
exifDatas[$image_img.data('name')] = getExifDataMarkup(this);
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Poptrox.
|
function initThumbs() {
|
||||||
$main.poptrox({
|
$main.children(".thumb").each(function () {
|
||||||
baseZIndex: 20000,
|
var $this = $(this),
|
||||||
caption: function ($a) {
|
$image = $this.find(".image"),
|
||||||
var $image_img = $a.children('img');
|
$image_img = $image.children("img"),
|
||||||
var data = exifDatas[$image_img.data('name')];
|
x;
|
||||||
if (data === undefined) {
|
|
||||||
// EXIF data
|
if ($image.length == 0) return;
|
||||||
EXIF.getData($image_img[0], function () {
|
|
||||||
data = exifDatas[$image_img.data('name')] = getExifDataMarkup(this);
|
$image.css("background-image", "url(" + $image_img.attr("src") + ")");
|
||||||
|
if ((x = $image_img.data("position"))) $image.css("background-position", x);
|
||||||
|
$image_img.hide();
|
||||||
|
|
||||||
|
$image_img[0].addEventListener("load", function() {
|
||||||
|
EXIF.getData($image_img[0], function () {
|
||||||
|
exifDatas[$image_img.data('name')] = getExifDataMarkup(this);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
return data !== undefined ? '<p>' + data + '</p>' : ' ';
|
}
|
||||||
},
|
|
||||||
fadeSpeed: 300,
|
|
||||||
onPopupClose: function () {
|
|
||||||
$body.removeClass("modal-active");
|
|
||||||
},
|
|
||||||
onPopupOpen: function () {
|
|
||||||
$body.addClass("modal-active");
|
|
||||||
},
|
|
||||||
overlayOpacity: 0,
|
|
||||||
popupCloserText: "",
|
|
||||||
popupHeight: 150,
|
|
||||||
popupLoaderText: "",
|
|
||||||
popupSpeed: 300,
|
|
||||||
popupWidth: 150,
|
|
||||||
selector: ".thumb > a.image",
|
|
||||||
usePopupCaption: true,
|
|
||||||
usePopupCloser: true,
|
|
||||||
usePopupDefaultStyling: false,
|
|
||||||
usePopupForceClose: true,
|
|
||||||
usePopupLoader: true,
|
|
||||||
usePopupNav: true,
|
|
||||||
windowMargin: 50,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Hack: Set margins to 0 when 'xsmall' activates.
|
function initPoptrox() {
|
||||||
breakpoints.on("<=xsmall", function () {
|
$main.poptrox({
|
||||||
$main[0]._poptrox.windowMargin = 0;
|
baseZIndex: 20000,
|
||||||
});
|
caption: function ($a) {
|
||||||
|
var $image_img = $a.children('img');
|
||||||
|
var data = exifDatas[$image_img.data('name')];
|
||||||
|
if (data === undefined) {
|
||||||
|
EXIF.getData($image_img[0], function () {
|
||||||
|
data = exifDatas[$image_img.data('name')] = getExifDataMarkup(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data !== undefined ? '<p>' + data + '</p>' : ' ';
|
||||||
|
},
|
||||||
|
fadeSpeed: 300,
|
||||||
|
onPopupClose: function () {
|
||||||
|
$body.removeClass("modal-active");
|
||||||
|
},
|
||||||
|
onPopupOpen: function () {
|
||||||
|
$body.addClass("modal-active");
|
||||||
|
},
|
||||||
|
overlayOpacity: 0,
|
||||||
|
popupCloserText: "",
|
||||||
|
popupHeight: 150,
|
||||||
|
popupLoaderText: "",
|
||||||
|
popupSpeed: 300,
|
||||||
|
popupWidth: 150,
|
||||||
|
selector: ".thumb > a.image",
|
||||||
|
usePopupCaption: true,
|
||||||
|
usePopupCloser: true,
|
||||||
|
usePopupDefaultStyling: false,
|
||||||
|
usePopupForceClose: true,
|
||||||
|
usePopupLoader: true,
|
||||||
|
usePopupNav: true,
|
||||||
|
windowMargin: 50,
|
||||||
|
});
|
||||||
|
|
||||||
breakpoints.on(">xsmall", function () {
|
breakpoints.on("<=xsmall", function () {
|
||||||
$main[0]._poptrox.windowMargin = 50;
|
$main[0]._poptrox.windowMargin = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
breakpoints.on(">xsmall", function () {
|
||||||
|
$main[0]._poptrox.windowMargin = 50;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getExifDataMarkup(img) {
|
function getExifDataMarkup(img) {
|
||||||
var exif = $('#main').data('exif');
|
var exif = $('#main').data('exif');
|
||||||
@@ -263,4 +267,7 @@
|
|||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Galerie laden
|
||||||
|
loadGallery();
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|||||||
@@ -4,4 +4,12 @@ services:
|
|||||||
container_name: photography-website
|
container_name: photography-website
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
# Nextcloud-Ordner mit Originalbildern (read-only)
|
||||||
|
- /volume1/nextcloud/data/Tom/files/Websitebilder:/usr/share/nginx/html/images/fulls:ro
|
||||||
|
# Thumbnails werden automatisch generiert und hier gespeichert
|
||||||
|
- thumbs:/usr/share/nginx/html/images/thumbs
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
thumbs:
|
||||||
|
|||||||
39
entrypoint.sh
Normal file
39
entrypoint.sh
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
FULLS_DIR="/usr/share/nginx/html/images/fulls"
|
||||||
|
THUMBS_DIR="/usr/share/nginx/html/images/thumbs"
|
||||||
|
|
||||||
|
generate_thumbs() {
|
||||||
|
mkdir -p "$THUMBS_DIR"
|
||||||
|
for img in "$FULLS_DIR"/*; do
|
||||||
|
[ -f "$img" ] || continue
|
||||||
|
|
||||||
|
filename=$(basename "$img")
|
||||||
|
ext=$(echo "${filename##*.}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
# Nur Bilddateien verarbeiten
|
||||||
|
case "$ext" in
|
||||||
|
jpg|jpeg|png|gif|webp) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Thumbnail nur erstellen wenn noch nicht vorhanden oder aelter als Original
|
||||||
|
if [ ! -f "$THUMBS_DIR/$filename" ] || [ "$img" -nt "$THUMBS_DIR/$filename" ]; then
|
||||||
|
echo "Thumbnail erstellen: $filename"
|
||||||
|
convert "$img" -resize 512x512 -quality 80 "$THUMBS_DIR/$filename" 2>/dev/null
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "=== Mischkomposition - Starte Thumbnail-Generierung ==="
|
||||||
|
generate_thumbs
|
||||||
|
echo "=== Thumbnail-Generierung abgeschlossen ==="
|
||||||
|
|
||||||
|
# Hintergrund-Prozess: Alle 60 Sekunden nach neuen Bildern suchen
|
||||||
|
(while true; do
|
||||||
|
sleep 60
|
||||||
|
generate_thumbs
|
||||||
|
done) &
|
||||||
|
|
||||||
|
# nginx starten
|
||||||
|
nginx -g 'daemon off;'
|
||||||
12
index.html
12
index.html
@@ -17,17 +17,7 @@ layout: default
|
|||||||
|
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<div id="main" data-exif='{{ site.exif }}'>
|
<div id="main" data-exif='{{ site.exif }}'>
|
||||||
{% for image in site.static_files %}
|
<!-- Galerie wird dynamisch per JavaScript geladen -->
|
||||||
{% if image.path contains 'fulls' %}
|
|
||||||
<article class="thumb">
|
|
||||||
<a href="{{ site.baseurl }}{{ site.image_fulls_loc }}/{{ image.name }}" class="image">
|
|
||||||
<img src="{{ site.baseurl }}{{ site.image_thumbs_loc }}/{{ image.name }}" alt=""
|
|
||||||
data-name="{{ image.name }}"/></a>
|
|
||||||
<!--<h2></h2>
|
|
||||||
<p></p>-->
|
|
||||||
</article>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
|
|||||||
13
nginx.conf
13
nginx.conf
@@ -4,8 +4,19 @@ server {
|
|||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
|
# Bilder-Verzeichnis: JSON-Listing fuer dynamische Galerie
|
||||||
|
location /images/fulls/ {
|
||||||
|
autoindex on;
|
||||||
|
autoindex_format json;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /images/thumbs/ {
|
||||||
|
autoindex on;
|
||||||
|
autoindex_format json;
|
||||||
|
}
|
||||||
|
|
||||||
# Caching fuer Bilder und Assets
|
# Caching fuer Bilder und Assets
|
||||||
location ~* \.(jpg|jpeg|png|gif|svg|ico|css|js|woff|woff2|ttf|eot|otf)$ {
|
location ~* \.(jpg|jpeg|png|gif|svg|ico|css|js|woff|woff2|ttf|eot|otf|webp)$ {
|
||||||
expires 30d;
|
expires 30d;
|
||||||
add_header Cache-Control "public, immutable";
|
add_header Cache-Control "public, immutable";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user