Fix: Backend-Bugs, fehlende Galerie, Watchtower statt Docker-Socket
- express.urlencoded() hinzugefuegt (Login war komplett kaputt) - Oeffentliche Galerie zeigt jetzt alle Bilder (statt leere Galerie) - loadGallery() Aufruf in main.js eingefuegt (fehlte komplett) - main.min.js neu gebaut mit gulp (Galerie-Code fehlte in minified Version) - Watchtower Container in docker-compose.yml (auto-updates alle 5 Min) - Docker-Socket und docker-cli aus Photography-Container entfernt - runs.json Muell-Datei geloescht - Brain.md und Feature-Request.md aktualisiert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
111
Brain.md
111
Brain.md
@@ -20,12 +20,15 @@ Keine 1:1 Kopie, sondern eine individuell angepasste Version mit eigenen Ansprue
|
|||||||
| Komponente | Technologie |
|
| Komponente | Technologie |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Static Site Generator | **Jekyll** (Ruby-basiert) |
|
| Static Site Generator | **Jekyll** (Ruby-basiert) |
|
||||||
|
| Backend | **Node.js / Express** (Auth, API, Static Serving) |
|
||||||
|
| Datenbank | **SQLite** (Nutzerverwaltung, Bildzuweisung) |
|
||||||
| Templating | Liquid Templates |
|
| Templating | Liquid Templates |
|
||||||
| Styling | SCSS -> kompiliertes CSS |
|
| Styling | SCSS -> kompiliertes CSS |
|
||||||
| JS Libraries | jQuery, Poptrox (Lightbox), EXIF.js |
|
| JS Libraries | jQuery, Poptrox (Lightbox), EXIF.js |
|
||||||
| Build-Tool | Gulp (SCSS kompilieren, JS minifizieren, Bilder resizen) |
|
| Build-Tool | Gulp (SCSS kompilieren, JS minifizieren, Bilder resizen) |
|
||||||
| Hosting | **UGREEN NAS (Docker)** via nginx |
|
| Hosting | **UGREEN NAS (Docker)** via Node.js Express |
|
||||||
| Containerisierung | Docker Multi-Stage (Jekyll Build + nginx) |
|
| Containerisierung | Docker Multi-Stage (Jekyll Build + Node.js Backend) |
|
||||||
|
| Auto-Update | **Watchtower** (prueft alle 5 Min auf neue Images) |
|
||||||
| Fonts | FontAwesome, Google Fonts (Source Sans Pro) |
|
| Fonts | FontAwesome, Google Fonts (Source Sans Pro) |
|
||||||
| Paketmanager | npm (Node), Bundler (Ruby) |
|
| Paketmanager | npm (Node), Bundler (Ruby) |
|
||||||
|
|
||||||
@@ -37,9 +40,13 @@ Photography/
|
|||||||
├── _layouts/
|
├── _layouts/
|
||||||
│ └── default.html # Basis-Layout (Head + Body + Scripts)
|
│ └── default.html # Basis-Layout (Head + Body + Scripts)
|
||||||
├── _includes/
|
├── _includes/
|
||||||
│ ├── header.html # <head> Bereich: CSS, Fonts, Google Analytics
|
│ ├── header.html # <head> Bereich: CSS, Fonts
|
||||||
│ └── footer.html # Script-Einbindungen (jQuery, Poptrox, EXIF, Main)
|
│ └── footer.html # Script-Einbindungen (jQuery, Poptrox, EXIF, Main)
|
||||||
├── index.html # Hauptseite: Header, Galerie-Grid, Footer/About
|
├── index.html # Hauptseite: Header, Galerie-Grid, Footer/About, Login-Panel
|
||||||
|
├── admin.html # Admin-Dashboard (Nutzerverwaltung, Bildzuweisung)
|
||||||
|
├── backend/
|
||||||
|
│ ├── server.js # Express Backend (Auth, API, Static Files, Port 8090)
|
||||||
|
│ └── database.js # SQLite Datenbank-Setup (Users, User-Images)
|
||||||
├── assets/
|
├── assets/
|
||||||
│ ├── css/ # Kompilierte CSS-Dateien (.min.css)
|
│ ├── css/ # Kompilierte CSS-Dateien (.min.css)
|
||||||
│ ├── sass/ # SCSS Quelldateien
|
│ ├── sass/ # SCSS Quelldateien
|
||||||
@@ -47,50 +54,82 @@ Photography/
|
|||||||
│ │ ├── components/ # Buttons, Forms, Icons, Panels, Poptrox
|
│ │ ├── components/ # Buttons, Forms, Icons, Panels, Poptrox
|
||||||
│ │ ├── layout/ # Header, Footer, Main, Wrapper
|
│ │ ├── layout/ # Header, Footer, Main, Wrapper
|
||||||
│ │ └── libs/ # Breakpoints, Mixins, Vars, Functions
|
│ │ └── libs/ # Breakpoints, Mixins, Vars, Functions
|
||||||
│ ├── js/ # JavaScript (jQuery, Poptrox, EXIF, Main)
|
│ ├── js/ # JavaScript (jQuery, Poptrox, EXIF, Main, Admin)
|
||||||
│ ├── fonts/ # FontAwesome Fonts
|
│ ├── fonts/ # FontAwesome Fonts
|
||||||
│ └── webfonts/ # FA5 Webfonts
|
│ └── webfonts/ # FA5 Webfonts
|
||||||
├── images/
|
├── images/
|
||||||
│ ├── fulls/ # Vollaufloesung Bilder
|
│ ├── fulls/ # Vollaufloesung Bilder
|
||||||
│ └── thumbs/ # Thumbnails (512px)
|
│ └── thumbs/ # Thumbnails (512px)
|
||||||
├── gulpfile.mjs # Build: SCSS->CSS, JS minify, Image resize
|
├── gulpfile.mjs # Build: SCSS->CSS, JS minify, Image resize
|
||||||
├── package.json # Node Dependencies
|
├── package.json # Node Dependencies (Express, SQLite, JWT, bcrypt)
|
||||||
├── Gemfile # Ruby Dependencies (jekyll)
|
├── Gemfile # Ruby Dependencies (jekyll)
|
||||||
└── CNAME # Custom Domain Config
|
├── Dockerfile # Multi-Stage: Jekyll Build + Node.js Runtime
|
||||||
|
├── docker-compose.yml # Container + Watchtower
|
||||||
|
├── entrypoint.sh # Thumbnail-Generierung + Node.js Start
|
||||||
|
└── nginx.conf # (Legacy, nicht mehr aktiv - Express dient statische Dateien)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Wie die Galerie funktioniert (Dynamisch)
|
## Wie die Galerie funktioniert (Dynamisch)
|
||||||
|
|
||||||
1. **Bilder hochladen**: Fotos in Nextcloud-Ordner `Websitebilder` ablegen
|
1. **Bilder hochladen**: Fotos in Nextcloud-Ordner `Websitebilder` ablegen
|
||||||
2. **Volume-Mount**: Docker mountet den Nextcloud-Ordner als `/images/fulls/` (read-only)
|
2. **Volume-Mount**: Docker mountet den Nextcloud-Ordner als `/app/public/images/fulls/` (read-only)
|
||||||
3. **Thumbnails**: `entrypoint.sh` generiert automatisch 512px Thumbnails via ImageMagick
|
3. **Thumbnails**: `entrypoint.sh` generiert automatisch 512px Thumbnails via ImageMagick
|
||||||
4. **Thumbnail-Check**: Alle 60 Sekunden prueft der Container auf neue Bilder
|
4. **Thumbnail-Check**: Alle 60 Sekunden prueft der Container auf neue Bilder
|
||||||
5. **nginx autoindex**: Liefert JSON-Liste aller Dateien in `/images/fulls/`
|
5. **Express API**: `/images/fulls/` liefert JSON-Liste der Bilddateien
|
||||||
6. **JavaScript (main.js)**: Fetcht die JSON-Liste und baut dynamisch `<article class="thumb">` Elemente
|
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)
|
7. **Poptrox-Lightbox**: Klick oeffnet Vollbild + EXIF-Daten (clientseitig via exif.js)
|
||||||
|
8. **Download-Button**: In der Lightbox kann jedes Bild heruntergeladen werden
|
||||||
|
|
||||||
|
### Zugriffslogik
|
||||||
|
- **Nicht eingeloggt / Admin**: Alle Bilder sichtbar (oeffentliche Galerie)
|
||||||
|
- **Eingeloggter Nutzer**: Nur zugewiesene Bilder sichtbar + Download
|
||||||
|
|
||||||
### Nextcloud-Integration
|
### Nextcloud-Integration
|
||||||
- **Nextcloud-Pfad**: `/volume1/nextcloud/data/Tom/files/Websitebilder`
|
- **Nextcloud-Pfad**: `/volume1/nextcloud/data/Tom/files/Websitebilder`
|
||||||
- **Mount im Container**: `/usr/share/nginx/html/images/fulls:ro`
|
- **Mount im Container**: `/app/public/images/fulls:ro`
|
||||||
- **Thumbnails-Volume**: Docker-Volume `thumbs` (persistent)
|
- **Thumbnails-Volume**: Docker-Volume `thumbs` (persistent)
|
||||||
|
- **Datenbank-Volume**: Docker-Volume `database` (persistent, SQLite)
|
||||||
- **Workflow**: Bild in Nextcloud hochladen -> max. 60s warten -> Seite neu laden -> fertig
|
- **Workflow**: Bild in Nextcloud hochladen -> max. 60s warten -> Seite neu laden -> fertig
|
||||||
|
|
||||||
|
## Login-System & Nutzerverwaltung
|
||||||
|
|
||||||
|
### Architektur
|
||||||
|
- **Backend**: Node.js/Express auf Port 8090
|
||||||
|
- **Auth**: JWT-Token als httpOnly Cookie (7 Tage gueltig)
|
||||||
|
- **Datenbank**: SQLite unter `/app/data/database.sqlite`
|
||||||
|
- **Passwort-Hashing**: bcryptjs (10 Rounds)
|
||||||
|
|
||||||
|
### Standard-Admin
|
||||||
|
- **Username**: `admin`
|
||||||
|
- **Passwort**: `admin123` (SOFORT AENDERN nach erstem Login!)
|
||||||
|
- **Rolle**: admin
|
||||||
|
|
||||||
|
### API-Endpunkte
|
||||||
|
| Endpunkt | Methode | Beschreibung |
|
||||||
|
|---|---|---|
|
||||||
|
| `/api/auth/login` | POST | Login (username, password) |
|
||||||
|
| `/api/auth/logout` | POST | Logout (Cookie loeschen) |
|
||||||
|
| `/api/auth/me` | GET | Aktueller User |
|
||||||
|
| `/api/admin/users` | GET/POST | Nutzer auflisten / erstellen |
|
||||||
|
| `/api/admin/users/:id` | DELETE | Nutzer loeschen |
|
||||||
|
| `/api/admin/assign` | GET/POST | Bildzuweisungen anzeigen / setzen |
|
||||||
|
| `/images/fulls/` | GET | Bilderliste (gefiltert nach User-Rolle) |
|
||||||
|
|
||||||
## Wichtige Konfiguration (_config.yml)
|
## Wichtige Konfiguration (_config.yml)
|
||||||
|
|
||||||
- `baseurl`: URL der Website (leer fuer lokal)
|
- `baseurl`: URL der Website
|
||||||
- `title`, `subtitle`, `author`: Seitentitel und Autor
|
- `title`, `subtitle`, `author`: Seitentitel und Autor
|
||||||
- `header.title/subtitle`: Header-Anzeige
|
- `header.title/subtitle`: Header-Anzeige
|
||||||
- `footer.name/bio/github`: Footer-Infos
|
- `footer.name/bio`: Footer-Infos
|
||||||
- `social_urls`: Links zu Social Media
|
- `social_urls`: Links zu Social Media
|
||||||
- `exif`: Welche EXIF-Tags angezeigt werden (Model, FNumber, ExposureTime, ISO)
|
- `exif`: Welche EXIF-Tags angezeigt werden (Model, FNumber, ExposureTime, ISO)
|
||||||
- `google_analytics`: Tracking-ID
|
|
||||||
|
|
||||||
## Build-Befehle
|
## Build-Befehle
|
||||||
|
|
||||||
| Befehl | Aktion |
|
| Befehl | Aktion |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `bundle install` | Ruby Dependencies installieren |
|
| `bundle install` | Ruby Dependencies installieren |
|
||||||
| `bundle exec jekyll serve` | Lokaler Dev-Server |
|
| `bundle exec jekyll serve` | Lokaler Dev-Server (nur Frontend) |
|
||||||
| `npm install` | Node Dependencies installieren |
|
| `npm install` | Node Dependencies installieren |
|
||||||
| `gulp build` | SCSS kompilieren + JS minifizieren |
|
| `gulp build` | SCSS kompilieren + JS minifizieren |
|
||||||
| `gulp resize` | Bilder zu fulls (1024px) + thumbs (512px) |
|
| `gulp resize` | Bilder zu fulls (1024px) + thumbs (512px) |
|
||||||
@@ -99,45 +138,51 @@ Photography/
|
|||||||
## Docker / NAS Hosting
|
## Docker / NAS Hosting
|
||||||
|
|
||||||
- **NAS**: UGREEN mit UGOS PRO
|
- **NAS**: UGREEN mit UGOS PRO
|
||||||
- **Port**: 8090 (Port 80 ist belegt)
|
- **Port**: 8090 (intern und extern)
|
||||||
- **Zugriff**: `http://NAS-IP:8090` -> spaeter `https://tom.mischlabs.de`
|
- **Domain**: tom.mischlabs.de (via Cloudflare Tunnel)
|
||||||
- **Domain**: tom.mischlabs.de (Reverse Proxy auf NAS einrichten)
|
|
||||||
|
|
||||||
### Docker-Dateien
|
### Docker-Dateien
|
||||||
- `Dockerfile` - Multi-Stage: Ruby/Jekyll baut Site -> nginx serviert statische Dateien
|
- `Dockerfile` - Multi-Stage: Ruby/Jekyll baut Site -> Node.js Express serviert alles
|
||||||
- `docker-compose.yml` - Container-Definition, Port 8090:80, Nextcloud-Volume, auto-restart
|
- `docker-compose.yml` - Photography Container + Watchtower, Volumes fuer Bilder/Thumbs/DB
|
||||||
- `entrypoint.sh` - Thumbnail-Generierung + periodischer Check (alle 60s)
|
- `entrypoint.sh` - Thumbnail-Generierung + Node.js Server-Start
|
||||||
- `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.)
|
||||||
|
|
||||||
### Docker-Befehle
|
### Docker-Befehle (auf NAS via SSH)
|
||||||
| Befehl | Aktion |
|
| Befehl | Aktion |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `docker compose up -d --build` | Container bauen und starten |
|
| `sudo docker compose up -d` | Container starten |
|
||||||
| `docker compose down` | Container stoppen |
|
| `sudo docker compose down` | Container stoppen |
|
||||||
| `docker compose logs -f` | Logs anzeigen |
|
| `sudo docker compose logs -f photography` | Logs anzeigen |
|
||||||
| `docker compose up -d --build --force-recreate` | Neu bauen nach Aenderungen |
|
| `sudo docker compose pull && sudo docker compose up -d` | Manuelles Update |
|
||||||
|
|
||||||
### Deployment-Workflow
|
### Deployment-Workflow (automatisch!)
|
||||||
1. Aenderungen lokal machen (Bilder, Config, Design)
|
1. Aenderungen lokal machen (Code, Design)
|
||||||
2. Git commit & push
|
2. `gulp build` ausfuehren (JS + CSS minifizieren)
|
||||||
3. Auf NAS: `git pull && docker compose up -d --build`
|
3. Git commit & push nach master
|
||||||
4. Fertig - Website aktualisiert
|
4. GitHub Actions baut automatisch Docker Image -> GHCR
|
||||||
|
5. Watchtower auf NAS erkennt neues Image (alle 5 Min)
|
||||||
|
6. Watchtower aktualisiert Container automatisch
|
||||||
|
7. Fertig - Website aktualisiert ohne SSH!
|
||||||
|
|
||||||
## Anpassungs-Roadmap (TODO - mit Besitzer besprechen)
|
## Anpassungs-Roadmap
|
||||||
|
|
||||||
- [x] Titel, Subtitle, Autor anpassen (Mischkomposition / Tom Misch)
|
- [x] Titel, Subtitle, Autor anpassen (Mischkomposition / Tom Misch)
|
||||||
- [x] Social Media Links aktualisieren (Instagram: @Mischkomposition)
|
- [x] Social Media Links aktualisieren (Instagram: @Mischkomposition)
|
||||||
- [x] Sponsor-Bereich entfernen
|
- [x] Sponsor-Bereich entfernen
|
||||||
- [x] Kontaktformular auf tom@mischlabs.de konfiguriert
|
- [x] Kontaktformular auf tom@mischlabs.de konfiguriert (Formsubmit.co)
|
||||||
- [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
|
||||||
- [x] Nextcloud-Integration (Bilder aus Websitebilder-Ordner)
|
- [x] Nextcloud-Integration (Bilder aus Websitebilder-Ordner)
|
||||||
- [x] Dynamische Galerie (JS statt Jekyll-Loop)
|
- [x] Dynamische Galerie (JS statt Jekyll-Loop)
|
||||||
- [x] Automatische Thumbnail-Generierung
|
- [x] Automatische Thumbnail-Generierung
|
||||||
|
- [x] Login-System mit JWT + SQLite
|
||||||
|
- [x] Admin-Dashboard (Nutzerverwaltung, Bildzuweisung)
|
||||||
|
- [x] Kunden-Portal (zugewiesene Bilder + Download)
|
||||||
|
- [x] Watchtower fuer automatische Updates
|
||||||
- [ ] 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)
|
||||||
|
- [ ] Admin-Passwort aendern!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ RUN bundle exec jekyll build
|
|||||||
# === Serve-Phase: node.js backend + ImageMagick ===
|
# === Serve-Phase: node.js backend + ImageMagick ===
|
||||||
FROM node:20-alpine
|
FROM node:20-alpine
|
||||||
|
|
||||||
# Install build dependencies for sqlite3 native bindings on Alpine
|
# ImageMagick fuer Thumbnails, Build-Tools fuer sqlite3 native bindings
|
||||||
RUN apk add --no-cache imagemagick docker-cli python3 make g++ build-base sqlite-dev
|
RUN apk add --no-cache imagemagick python3 make g++ build-base sqlite-dev
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
## Nutzerverwaltung & Login-System
|
## Nutzerverwaltung & Login-System
|
||||||
|
|
||||||
### Beschreibung
|
### Beschreibung
|
||||||
Die Website soll um ein Login-System mit Nutzerverwaltung erweitert werden.
|
Die Website wurde um ein Login-System mit Nutzerverwaltung erweitert.
|
||||||
|
|
||||||
### Anforderungen
|
### Anforderungen
|
||||||
|
|
||||||
@@ -17,23 +17,22 @@ Die Website soll um ein Login-System mit Nutzerverwaltung erweitert werden.
|
|||||||
- Nutzer koennen ihre zugewiesenen Bilder anschauen (Galerie + Lightbox)
|
- Nutzer koennen ihre zugewiesenen Bilder anschauen (Galerie + Lightbox)
|
||||||
- Nutzer koennen ihre zugewiesenen Bilder herunterladen
|
- Nutzer koennen ihre zugewiesenen Bilder herunterladen
|
||||||
|
|
||||||
### Status: Geplant (noch nicht umgesetzt)
|
### Status: Umgesetzt
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Admin-Dashboard: One-Click Update
|
## Auto-Update via Watchtower
|
||||||
|
|
||||||
### Beschreibung
|
### Beschreibung
|
||||||
Im Admin-Bereich soll ein Button vorhanden sein, der per Klick die neueste Version aus dem Git-Repository zieht und den Container automatisch aktualisiert. Kein manuelles SSH oder Terminal noetig.
|
Watchtower laeuft als separater Docker-Container und prueft alle 5 Minuten auf neue Images.
|
||||||
|
Nach einem `git push` baut GitHub Actions das neue Image und Watchtower aktualisiert den Container automatisch.
|
||||||
|
|
||||||
### Anforderungen
|
### Anforderungen
|
||||||
- Button im Admin-Dashboard: "Website aktualisieren"
|
- Automatisches Update ohne SSH oder manuellen Eingriff
|
||||||
- Zieht automatisch die neueste Version aus GitHub (git pull oder neues Image)
|
- Alte Images werden automatisch aufgeraeumt (WATCHTOWER_CLEANUP)
|
||||||
- Baut den Container neu / startet ihn neu
|
|
||||||
- Zeigt Status-Feedback (Erfolg/Fehler)
|
|
||||||
|
|
||||||
### Status: Geplant (noch nicht umgesetzt)
|
### Status: Umgesetzt
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Erstellt: 2026-04-12*
|
*Aktualisiert: 2026-04-12*
|
||||||
|
|||||||
@@ -161,7 +161,7 @@
|
|||||||
cache: false,
|
cache: false,
|
||||||
success: function(files) {
|
success: function(files) {
|
||||||
if(files.length === 0) {
|
if(files.length === 0) {
|
||||||
$main.html('<p style="text-align:center;color:#fff;padding:2em;width:100%">Bitte logge dich über den Login-Button unten links ein, um deine zugewiesenen Bilder zu sehen.</p>');
|
$main.html('<p style="text-align:center;color:#fff;padding:2em;width:100%">Noch keine Bilder vorhanden.</p>');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +279,9 @@
|
|||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Galerie laden
|
||||||
|
loadGallery();
|
||||||
|
|
||||||
// Login Handling
|
// Login Handling
|
||||||
$.get('/api/auth/me').done(function(u) {
|
$.get('/api/auth/me').done(function(u) {
|
||||||
$('#login-username, #login-password, #login-submit').hide();
|
$('#login-username, #login-password, #login-submit').hide();
|
||||||
|
|||||||
2
assets/js/main.min.js
vendored
2
assets/js/main.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -11,6 +11,7 @@ const app = express();
|
|||||||
const JWT_SECRET = process.env.JWT_SECRET || 'mischkomposition-super-secret-key-change-me';
|
const JWT_SECRET = process.env.JWT_SECRET || 'mischkomposition-super-secret-key-change-me';
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
|
|
||||||
// Middleware to verify JWT cookie
|
// Middleware to verify JWT cookie
|
||||||
@@ -63,37 +64,38 @@ app.get('/api/auth/me', (req, res) => {
|
|||||||
|
|
||||||
// ==================== IMAGE PROXY ====================
|
// ==================== IMAGE PROXY ====================
|
||||||
|
|
||||||
// We must override the nginx `/images/fulls/` JSON behavior here:
|
// Bilder-Listing: Oeffentlich = alle Bilder, eingeloggt = zugewiesene Bilder
|
||||||
app.get('/images/fulls/', (req, res) => {
|
app.get('/images/fulls/', (req, res) => {
|
||||||
if (!req.user) return res.json([]); // Not logged in -> empty gallery
|
|
||||||
|
|
||||||
const fullsDir = path.join(__dirname, '../public/images/fulls');
|
const fullsDir = path.join(__dirname, '../public/images/fulls');
|
||||||
|
|
||||||
fs.readdir(fullsDir, (err, files) => {
|
fs.readdir(fullsDir, (err, files) => {
|
||||||
if (err) return res.json([]);
|
if (err) return res.json([]);
|
||||||
|
|
||||||
let imageFiles = files.filter(f => f.match(/\.(jpg|jpeg|png|gif|webp)$/i));
|
const imageFiles = files.filter(f => f.match(/\.(jpg|jpeg|png|gif|webp)$/i));
|
||||||
|
|
||||||
if (req.user.role === 'admin') {
|
function buildResult(names) {
|
||||||
// Admin sees all
|
return names.map(name => {
|
||||||
const result = imageFiles.map(name => {
|
try {
|
||||||
const stats = fs.statSync(path.join(fullsDir, name));
|
const stats = fs.statSync(path.join(fullsDir, name));
|
||||||
return { name, type: 'file', mtime: stats.mtime.toUTCString(), size: stats.size };
|
return { name, type: 'file', mtime: stats.mtime.toUTCString(), size: stats.size };
|
||||||
|
} catch (e) {
|
||||||
|
return { name, type: 'file' };
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return res.json(result);
|
}
|
||||||
} else {
|
|
||||||
// Normal user sees only assigned
|
// Nicht eingeloggt oder Admin: alle Bilder anzeigen
|
||||||
|
if (!req.user || req.user.role === 'admin') {
|
||||||
|
return res.json(buildResult(imageFiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normaler User: nur zugewiesene Bilder
|
||||||
db.all('SELECT image_name FROM user_images WHERE user_id = ?', [req.user.id], (err, rows) => {
|
db.all('SELECT image_name FROM user_images WHERE user_id = ?', [req.user.id], (err, rows) => {
|
||||||
if (err) return res.json([]);
|
if (err) return res.json([]);
|
||||||
const assignedNames = rows.map(r => r.image_name);
|
const assignedNames = rows.map(r => r.image_name);
|
||||||
const assignedFiles = imageFiles.filter(name => assignedNames.includes(name));
|
const assignedFiles = imageFiles.filter(name => assignedNames.includes(name));
|
||||||
const result = assignedFiles.map(name => {
|
return res.json(buildResult(assignedFiles));
|
||||||
const stats = fs.statSync(path.join(fullsDir, name));
|
|
||||||
return { name, type: 'file', mtime: stats.mtime.toUTCString(), size: stats.size };
|
|
||||||
});
|
});
|
||||||
return res.json(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -150,19 +152,18 @@ app.post('/api/admin/assign', requireAdmin, (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/admin/update', requireAdmin, (req, res) => {
|
app.post('/api/admin/update', requireAdmin, (req, res) => {
|
||||||
console.log("Triggering Docker Compose Update...");
|
console.log("Triggering Docker Update...");
|
||||||
|
|
||||||
// Wir fuehren den Docker compose pull/down/up aus.
|
// Neues Image pullen und Container neu starten (ohne docker-compose)
|
||||||
// ACHTUNG: Der Server stuertzt danach ohnehin im Container ab (da er neu gestartet wird).
|
const image = 'ghcr.io/kroonk/photography:latest';
|
||||||
const cmd = "docker compose pull && docker compose down && docker compose up -d";
|
const cmd = `docker pull ${image} && docker stop photography-website && docker rm photography-website`;
|
||||||
|
|
||||||
// Return early to ensure client gets success BEFORE container dies
|
res.json({ success: true, message: 'Update gestartet. Container wird neu gestartet... Seite laedt in 30 Sekunden neu.' });
|
||||||
res.json({ success: true, message: 'Update gestartet. Container wird neu gestartet...' });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
exec(cmd, { cwd: '/app' }, (err, stdout, stderr) => {
|
exec(cmd, (err, stdout, stderr) => {
|
||||||
if (err) console.error("Update failed:", err);
|
if (err) console.error("Update failed:", err);
|
||||||
else console.log("Update success:", stdout);
|
else console.log("Update triggered:", stdout);
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,10 +9,20 @@ services:
|
|||||||
- /volume1/nextcloud/data/Tom/files/Websitebilder:/app/public/images/fulls:ro
|
- /volume1/nextcloud/data/Tom/files/Websitebilder:/app/public/images/fulls:ro
|
||||||
# Thumbnails werden automatisch generiert und hier gespeichert
|
# Thumbnails werden automatisch generiert und hier gespeichert
|
||||||
- thumbs:/app/public/images/thumbs
|
- thumbs:/app/public/images/thumbs
|
||||||
# SQLite Database Volume
|
# SQLite Database (persistent)
|
||||||
- database:/app/data
|
- database:/app/data
|
||||||
# Docker Socket fuer One-Click Update
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Watchtower: Prueft automatisch auf neue Docker-Images und aktualisiert den Container
|
||||||
|
watchtower:
|
||||||
|
image: containrrr/watchtower
|
||||||
|
container_name: watchtower
|
||||||
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
environment:
|
||||||
|
- WATCHTOWER_CLEANUP=true
|
||||||
|
- WATCHTOWER_POLL_INTERVAL=300
|
||||||
|
- WATCHTOWER_LABEL_ENABLE=false
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
Reference in New Issue
Block a user