diff --git a/CMS-IMPLEMENTATION-PLAN.md b/CMS-IMPLEMENTATION-PLAN.md new file mode 100644 index 0000000..5e0d869 --- /dev/null +++ b/CMS-IMPLEMENTATION-PLAN.md @@ -0,0 +1,566 @@ +# W-Make Portfolio CMS - Umsetzungsplan +**Erstellt:** 2026-08-22 +**Ziel:** Admin-Login + Content-Management für Seiten, Links und News + +--- + +## 🎯 Anforderungen + +### Must-Have (Phase 1): +1. **Admin-Login** - Authelia-basiert (bereits vorhanden auf auth.w-make.com) +2. **News/Updates verwalten** - erstellen, bearbeiten, löschen, publizieren +3. **Links verwalten** - externe/interne Links zur Homepage hinzufügen +4. **Seiten bearbeiten** - existierende Content-Seiten (About, Notes) editieren + +### Nice-to-Have (Phase 2): +5. Markdown-Editor mit Preview +6. Medien-Upload (Bilder für Notes) +7. Multi-Sprache (DE/EN synchron editieren) +8. Version History / Drafts + +--- + +## 🏗️ Architektur-Entscheidungen + +### ✅ Was bereits existiert: + +**Infrastructure:** +- ✅ Authelia SSO auf `auth.w-make.com` (2FA-Login) +- ✅ Traefik Reverse Proxy (schützt `/admin` Routes) +- ✅ `updates-api` Service (Node.js + SQLite) + - Admin-Panel unter `https://updates.w-make.com/admin` + - Bearer-Token Auth für API + - SQLite-Datenbank `/data/updates.sqlite` + +**Current Content Structure:** +```typescript +// Aktuell: TypeScript-Dateien (kompiliert in Bundle) +src/content/notes.ts // Blog-Posts +src/content/projects.ts // Case Studies +src/content/offers.ts // Services +src/content/person.ts // About-Content +``` + +**Problem:** Content ist im Code → Deployment nötig bei Änderungen + +--- + +## 🎨 Lösungsansatz: Hybrid CMS + +### Strategie: "Code für Struktur, DB für Content" + +**Prinzip:** +- Struktur (TypeScript-Types, Validierung) bleibt in Code +- Dynamischer Content (News, Updates, optionale Seiten) in SQLite +- Statische Inhalte (person.ts, offers.ts) bleiben vorerst im Code + +**Warum Hybrid?** +- ✅ Schnelle Time-to-Market (updates-api erweitern statt neu bauen) +- ✅ Keine komplexe CMS-Migration +- ✅ SQLite = einfaches Backup, kein Postgres nötig +- ✅ Authelia = Production-ready SSO, keine eigene User-DB + +--- + +## 📋 Umsetzungsplan: 3 Phasen + +--- + +## **Phase 1: Admin-Panel erweitern (5-7 Tage)** + +### 1.1 Updates-API erweitern für Content-Management + +**Neue Endpoints:** +```typescript +// News (bereits vorhanden, nur erweitern) +POST /v1/admin/updates // News erstellen +PUT /v1/admin/updates/:id // News bearbeiten +DELETE /v1/admin/updates/:id // News löschen +POST /v1/admin/updates/:id/publish // News publizieren + +// Links (neu) +GET /v1/links // Öffentlich: alle Links +POST /v1/admin/links // Link erstellen +PUT /v1/admin/links/:id // Link bearbeiten +DELETE /v1/admin/links/:id // Link löschen + +// Pages (neu, optional) +GET /v1/pages/:slug // Öffentlich: dynamische Seiten +POST /v1/admin/pages // Seite erstellen +PUT /v1/admin/pages/:slug // Seite bearbeiten +DELETE /v1/admin/pages/:slug // Seite löschen +``` + +**Datenbank-Schema (SQLite):** +```sql +-- Bereits vorhanden +CREATE TABLE updates ( + id INTEGER PRIMARY KEY, + product TEXT NOT NULL, + title TEXT NOT NULL, + summary TEXT, + slug TEXT UNIQUE, + published_at TEXT, + created_at TEXT NOT NULL +); + +-- Neu hinzufügen +CREATE TABLE links ( + id INTEGER PRIMARY KEY, + title TEXT NOT NULL, + url TEXT NOT NULL, + category TEXT, -- 'external' | 'internal' | 'resource' + description TEXT, + icon TEXT, -- optional: FontAwesome-Icon + sort_order INTEGER DEFAULT 0, + visible BOOLEAN DEFAULT 1, + created_at TEXT NOT NULL +); + +CREATE TABLE pages ( + id INTEGER PRIMARY KEY, + slug TEXT UNIQUE NOT NULL, + title_de TEXT NOT NULL, + title_en TEXT NOT NULL, + content_de TEXT, + content_en TEXT, + meta_description_de TEXT, + meta_description_en TEXT, + published BOOLEAN DEFAULT 0, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL +); +``` + +**Implementierung:** +```bash +services/updates-api/src/ + routes/ + admin/ + updates.js # bereits vorhanden + links.js # NEU + pages.js # NEU + db/ + schema.sql # Migration hinzufügen + migrations/ + 001-add-links.sql + 002-add-pages.sql +``` + +--- + +### 1.2 Admin-Frontend erweitern + +**Aktuell:** `https://updates.w-make.com/admin` (vermutlich einfaches HTML/JS) + +**Erweitern:** +``` +services/updates-api/src/admin/ + index.html # Dashboard + updates.html # News verwalten + links.html # NEU: Links verwalten + pages.html # NEU: Seiten verwalten + css/ + admin.css + js/ + api.js # Fetch-Wrapper mit Bearer-Token + updates.js + links.js # NEU + pages.js # NEU +``` + +**UI-Components (Plain HTML + Alpine.js oder Vanilla JS):** +```html + +
+

Links verwalten

+ + + + + + + + + + + + + + + + + + + + + + +
TitelURLKategorieSichtbarAktionen
{{ link.title }}{{ link.url }}{{ link.category }}{{ link.visible ? '✓' : '✗' }} + + +
+
+``` + +**Styling:** Design-System von w-make.com wiederverwenden (Copper/Ink/Paper Tokens) + +--- + +### 1.3 Frontend (Next.js) integrieren + +**Homepage: Links-Section hinzufügen** + +```tsx +// src/app/[lang]/page.tsx +export default async function HomePage({ params }: PageProps<"/[lang]">) { + // ... + const links = await fetchLinks(); // NEU: von updates-api + + return ( +
+ {/* ... existierende Sections ... */} + + {/* NEU: Links-Section */} +
+
+

Nützliche Links

+

+ Ressourcen und Werkzeuge +

+
+ {links.map((link) => ( + +

{link.title}

+

+ {link.description} +

+
+ ))} +
+
+
+
+ ); +} +``` + +**API-Client hinzufügen:** +```typescript +// src/lib/api-client.ts +const UPDATES_API_URL = process.env.UPDATES_API_URL || + 'http://localhost:8080'; + +export async function fetchLinks() { + const res = await fetch(`${UPDATES_API_URL}/v1/links`, { + next: { revalidate: 60 } // ISR: Cache 60s + }); + if (!res.ok) return []; + return res.json(); +} + +export async function fetchUpdates(limit = 5) { + const res = await fetch( + `${UPDATES_API_URL}/v1/updates?product=batchmaker&limit=${limit}`, + { next: { revalidate: 300 } } + ); + if (!res.ok) return []; + return res.json(); +} +``` + +--- + +### 1.4 Authelia-Integration absichern + +**Traefik-Config (auf VPS prüfen):** +```yaml +# Bereits vorhanden in /opt/traefik/config/ +http: + routers: + updates-admin: + rule: "Host(`updates.w-make.com`) && PathPrefix(`/admin`)" + middlewares: + - authelia@docker # Redirect zu auth.w-make.com + service: updates-api +``` + +**Admin-Panel: Session-Check** +```javascript +// services/updates-api/src/admin/js/auth.js +async function checkAuth() { + const token = localStorage.getItem('admin_token'); + if (!token) { + // Authelia hat Remote-User-Header gesetzt + // Oder redirect zu /login + window.location.href = '/login'; + } +} + +// Bei API-Calls +fetch('/v1/admin/updates', { + headers: { + 'Authorization': `Bearer ${token}` + } +}); +``` + +--- + +## **Phase 2: Markdown-Editor + Media-Upload (3-5 Tage)** + +### 2.1 Rich-Text-Editor einbauen + +**Library-Auswahl:** +- **TipTap** (modern, headless, gute TypeScript-Unterstützung) +- **SimpleMDE** (leichtgewichtig, Markdown-fokussiert) +- **Quill** (etabliert, aber schwerer) + +**Empfehlung:** SimpleMDE für Notes/Pages + +```html + + + + + + + + +``` + +--- + +### 2.2 Bild-Upload + +**Backend (updates-api erweitern):** +```javascript +// services/updates-api/src/routes/admin/media.js +import multer from 'multer'; +import path from 'path'; + +const storage = multer.diskStorage({ + destination: '/data/uploads/', + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); + } +}); + +const upload = multer({ + storage, + limits: { fileSize: 5 * 1024 * 1024 }, // 5MB + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only images allowed')); + } + } +}); + +router.post('/v1/admin/media', upload.single('image'), (req, res) => { + res.json({ + url: `/uploads/${req.file.filename}`, + filename: req.file.filename + }); +}); +``` + +**Frontend: Drag & Drop Upload** +```html +
+ Bild hierher ziehen oder klicken + +
+ +``` + +--- + +## **Phase 3: Dynamische Seiten + Notes aus DB (3-4 Tage)** + +### 3.1 Notes von TypeScript → SQLite migrieren + +**Migration-Script:** +```typescript +// scripts/migrate-notes-to-db.ts +import { notes } from '../src/content/notes'; +import { db } from '../services/updates-api/src/db'; + +for (const [slug, note] of Object.entries(notes)) { + db.run(` + INSERT INTO pages (slug, title_de, title_en, content_de, content_en, published) + VALUES (?, ?, ?, ?, ?, 1) + `, [ + `notizen/${slug}`, + note.de.title, + note.en.title, + note.de.body.join('\n\n'), + note.en.body.join('\n\n') + ]); +} +``` + +**Frontend: Dynamic Notes Route** +```typescript +// src/app/[lang]/notizen/[slug]/page.tsx +export default async function NotePage({ params }: PageProps) { + const locale = requireLocale((await params).lang); + const slug = (await params).slug; + + // Zuerst DB prüfen + const dbNote = await fetchPageBySlug(`notizen/${slug}`); + if (dbNote) { + return
{/* Render DB-Content */}
; + } + + // Fallback: statische notes.ts + const staticNote = getNote(slug as NoteSlug, locale); + return
{/* Render static content */}
; +} +``` + +--- + +### 3.2 Neue dynamische Seiten + +**Use-Case:** Kundenwunsch "Changelog-Seite" oder "FAQ" hinzufügen ohne Deployment + +```typescript +// src/app/[lang]/[slug]/page.tsx (Catch-All Dynamic Route) +export default async function DynamicPage({ params }: PageProps) { + const locale = requireLocale((await params).lang); + const slug = (await params).slug; + + const page = await fetchPageBySlug(slug); + + if (!page || !page.published) { + notFound(); + } + + const content = locale === 'de' ? page.content_de : page.content_en; + + return ( +
+
+

{page[`title_${locale}`]}

+
+
+
+ ); +} + +// Statische Pfade für Build-Zeit +export async function generateStaticParams() { + const pages = await fetchAllPublishedPages(); + return pages.map(page => ({ slug: page.slug })); +} +``` + +--- + +## 🚀 Deployment-Strategie + +### Phase 1 Deployment: +```bash +# 1. updates-api erweitern +cd services/updates-api +npm install multer marked +npm run build + +# 2. Datenbank migrieren +sqlite3 /data/updates.sqlite < src/db/migrations/001-add-links.sql + +# 3. Docker neu bauen +docker compose build updates-api +docker compose up -d updates-api + +# 4. Admin-Panel testen +open https://updates.w-make.com/admin +``` + +### Next.js ISR (Incremental Static Regeneration): +```typescript +// Automatisches Rebuild bei neuen Inhalten ohne Deployment +fetch(url, { + next: { revalidate: 60 } // Cache 60s, dann re-fetch +}); +``` + +--- + +## 📊 Aufwandsschätzung + +| Phase | Tasks | Aufwand | Priorität | +|-------|-------|---------|-----------| +| **Phase 1** | Admin-Panel + Links + API | 5-7 Tage | ⭐⭐⭐ HOCH | +| **Phase 2** | Editor + Upload | 3-5 Tage | ⭐⭐ MITTEL | +| **Phase 3** | Dynamic Pages + Migration | 3-4 Tage | ⭐ NIEDRIG | +| **Testing & Docs** | E2E-Tests, Anleitung | 2-3 Tage | ⭐⭐ MITTEL | + +**Total:** 13-19 Tage (2-3 Wochen bei Vollzeit) + +--- + +## 🔒 Sicherheits-Checkliste + +- [x] Authelia SSO (2FA bereits aktiv) +- [ ] CSRF-Protection für Admin-Endpoints +- [ ] Rate-Limiting für Uploads (max 10 MB/Stunde) +- [ ] Input-Sanitization (XSS-Schutz bei HTML-Content) +- [ ] File-Type-Validation (nur Bilder erlauben) +- [ ] SQL-Injection-Schutz (Prepared Statements) +- [ ] Backup-Strategie für SQLite-DB +- [ ] Rollback-Prozess dokumentieren + +--- + +## 📚 Alternativen (falls Hybrid-Ansatz nicht passt) + +### Option B: Headless CMS (Strapi/Payload) +**Pro:** +- ✅ Professionelles Admin-UI out-of-the-box +- ✅ Media-Library, Rollen-Management, Webhooks + +**Contra:** +- ❌ Overhead: PostgreSQL + Redis nötig +- ❌ Mehr Operational Complexity +- ❌ Höherer Server-Ressourcen-Bedarf + +### Option C: Git-basiert (Decap CMS / Tina CMS) +**Pro:** +- ✅ Content bleibt in Git (Version Control) +- ✅ Keine separate Datenbank + +**Contra:** +- ❌ Deployment bei jedem Content-Edit +- ❌ Langsamer für häufige Updates + +--- + +## 🎯 Empfehlung: Start mit Phase 1 + +**Warum:** +1. Schnellste Time-to-Market (baut auf vorhandener updates-api auf) +2. Minimaler Overhead (SQLite statt Postgres) +3. Authelia bereits Production-ready +4. Einfaches Backup/Restore + +**Nächster Schritt:** +1. Phase 1.1 umsetzen (Links-Endpoints + DB-Schema) +2. Phase 1.2 umsetzen (Admin-UI erweitern) +3. Testen auf Staging +4. Phase 1.3 umsetzen (Next.js Integration) +5. Production Deploy + +Soll ich mit **Phase 1.1** (API-Erweiterung) beginnen? diff --git a/DEPLOYMENT-SUCCESS.md b/DEPLOYMENT-SUCCESS.md new file mode 100644 index 0000000..ffe3d49 --- /dev/null +++ b/DEPLOYMENT-SUCCESS.md @@ -0,0 +1,179 @@ +# ✅ W-Make Portfolio Deployment - Erfolgreich abgeschlossen + +**Datum:** 2026-08-22 +**Deployed auf:** https://w-make.com +**VPS:** free-warez.win (100.90.92.125) + +--- + +## 🎉 Deployment Status: ERFOLGREICH + +### Validierung durchgeführt: + +✅ **Health Endpoint:** +```json +{"status":"ok","timestamp":"2026-08-22T14:39:33.760Z","service":"w-make-portfolio"} +``` + +✅ **Homepage (200 OK):** +- URL: https://w-make.com +- Status: HTTP/2 200 +- Content-Type: text/html; charset=utf-8 + +✅ **Content-Änderungen live:** +- Hero H1: ✓ "Wenn Zahlen zwischen Schichten verschwinden..." +- Demo-CTA: ✓ "W-Make Batch live erleben" +- CTA-Box: ✓ "30-Min. Erstanalyse · Kostenlos" + +✅ **Meta-Tags aktualisiert:** +- og:title: "Jan Wagner · Software für industrielle Prozesse" +- og:description: "Software für industrielle Prozesse, in denen Rezept, Material und Schichtübergabe lückenlos zusammengehören müssen..." +- og:image: 1200x630 (https://w-make.com/de/opengraph-image) +- twitter:card: "summary_large_image" + +--- + +## 📦 Deployment Details + +**Container:** +- Name: `w-make-portfolio` +- Image: `w-make-portfolio:latest` (neu gebaut) +- Status: Up 47 seconds (zum Zeitpunkt der Validierung) +- Port: 3000/tcp (hinter Traefik Reverse Proxy) + +**Build:** +- Next.js: 16.3.1 (Turbopack) +- TypeScript: ✓ kompiliert in 2.3s +- Pages: 34/34 generiert +- Routes: 12 dynamische, 2 statische +- Build-Zeit: ~8 Sekunden + +**Deployment-Methode:** +```bash +# 1. Dateien synchronisiert via rsync +rsync -avz /home/eldov-ryzen5/workspace/Coding/Web/w-make-com/ \ + free-warez.win:/opt/containers/w-make-portfolio/ + +# 2. Docker Image neu gebaut +docker compose build w-make-portfolio + +# 3. Container neu gestartet +docker compose up -d w-make-portfolio +``` + +--- + +## 📊 Git-Commits deployed + +``` +1f37edf - docs: add VPS deployment guide and script +4717404 - feat: complete SEO and mobile Quick Wins +7228fee - feat(homepage): improve hero copy and CTA conversion +c822a11 - chore: add ES module type, health endpoint, and security docs +``` + +**Dateien geändert:** +- `src/app/[lang]/page.tsx` (Demo-CTA, CTA-Box) +- `src/i18n/dictionaries.ts` (neue Copy) +- `src/lib/metadata.ts` (Twitter Card, OG Images) +- `src/app/[lang]/opengraph-image.tsx` (neuer Hero-Text) +- `src/app/globals.css` (Mobile-Optimierungen) +- `src/app/api/health/route.ts` (neu) +- `package.json` ("type": "module") + +--- + +## ✅ Validierungs-Checkliste + +### Content +- [x] Hero H1: "Wenn Zahlen zwischen Schichten verschwinden..." +- [x] Aside: "Die Software kennt die Reihenfolge nicht." +- [x] Aside erklärt Versagensmodell: "stille Korrekturen" +- [x] Demo-CTA Section nach Hero vorhanden +- [x] CTA-Box mit Benefit-Liste (3 Checkmarks) +- [x] Button Hover-Effekt funktioniert + +### Technical +- [x] Health-Endpoint antwortet: /api/health +- [x] Open Graph Tags vorhanden +- [x] Twitter Card konfiguriert +- [x] Meta Description aktualisiert +- [x] OG Image mit neuem Text +- [x] Container läuft stabil +- [x] Next.js Build erfolgreich + +### SEO Validation Tools (nächster Schritt) +- [ ] https://www.opengraph.xyz/ → w-make.com testen +- [ ] https://cards-dev.twitter.com/validator +- [ ] Google PageSpeed Insights + +--- + +## 📱 Mobile Testing (empfohlen) + +``` +Chrome DevTools → Toggle Device Toolbar (Ctrl+Shift+M) + +Zu testen: +- iPhone SE (375px) - Touch-Targets mind. 44x44px +- iPad (768px) - Typography skaliert +- Desktop (1920px) - Voller Funktionsumfang +``` + +--- + +## 📈 Erwartete Metriken (7 Tage) + +| Metrik | Baseline | Ziel | Messung | +|--------|----------|------|---------| +| Bounce Rate | ? | -20% | Google Analytics | +| Avg. Session | ? | +45s | " | +| Demo-Klicks | 0% | 8-12% | batch.w-make.com Referrer | +| Contact Form | ? | +25% | /kontakt Conversions | +| Mobile Bounce | ? | -15% | GA Mobile Segment | + +**Tracking einrichten:** +1. Google Analytics Event: Demo-CTA Click +2. Contact Form Submission +3. Scroll Depth (Hero → CTA) + +--- + +## 🔄 Rollback (falls nötig) + +```bash +ssh free-warez.win +cd /opt/containers/w-make-portfolio + +# Zu vorherigem Commit +git reset --hard 042ea88 + +# Container neu bauen +docker compose build w-make-portfolio +docker compose up -d w-make-portfolio +``` + +--- + +## 🎯 Nächste Schritte + +**Sofort:** +1. ✅ Deployment abgeschlossen +2. Meta-Tags in Validation-Tools prüfen +3. Mobile Preview testen + +**Diese Woche:** +4. Analytics-Events einrichten +5. Baseline-Metriken erfassen +6. Testimonials sammeln (anonymisiert) + +**Nächster Monat:** +7. A/B-Test der Hero-Copy +8. Video-Demo aufnehmen +9. Social Proof Section hinzufügen + +--- + +**Deployment durchgeführt von:** Jcode +**Deployment-Zeitpunkt:** 2026-08-22 14:38 UTC +**Build-Logs:** Container-Logs verfügbar via `docker logs w-make-portfolio` diff --git a/services/updates-api/data/updates.sqlite b/services/updates-api/data/updates.sqlite new file mode 100644 index 0000000..225de20 Binary files /dev/null and b/services/updates-api/data/updates.sqlite differ diff --git a/services/updates-api/migrations/001-add-links.sql b/services/updates-api/migrations/001-add-links.sql new file mode 100644 index 0000000..8f7f5e1 --- /dev/null +++ b/services/updates-api/migrations/001-add-links.sql @@ -0,0 +1,18 @@ +-- Migration: Add links table for link management +-- Created: 2026-08-22 + +CREATE TABLE IF NOT EXISTS links ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + url TEXT NOT NULL, + category TEXT NOT NULL DEFAULT 'external' CHECK (category IN ('external', 'internal', 'resource', 'tool')), + description TEXT, + icon TEXT, + sort_order INTEGER NOT NULL DEFAULT 0, + visible INTEGER NOT NULL DEFAULT 1 CHECK (visible IN (0, 1)), + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL +); + +CREATE INDEX IF NOT EXISTS idx_links_visible ON links(visible, sort_order); +CREATE INDEX IF NOT EXISTS idx_links_category ON links(category, visible, sort_order); diff --git a/services/updates-api/src/admin-links.html b/services/updates-api/src/admin-links.html new file mode 100644 index 0000000..c400576 --- /dev/null +++ b/services/updates-api/src/admin-links.html @@ -0,0 +1,284 @@ + + + + + + W-MAKE Links Admin + + + +
+
+

Authelia · updates.w-make.com

+

Links-Verwaltung

+
+ +
+ + + +
+ + +
+ + + + + + +
+ + + +
+ + + + + + + +
+ + + +
+ +

+
+
+ + + + diff --git a/services/updates-api/src/admin.html b/services/updates-api/src/admin.html index c5e250d..343b238 100644 --- a/services/updates-api/src/admin.html +++ b/services/updates-api/src/admin.html @@ -23,6 +23,9 @@ header, main { max-width: 1200px; margin: 0 auto; padding: 1.25rem 1.5rem; } header { display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; border-bottom: 1px solid var(--line); } h1 { font-size: 1.4rem; font-weight: 550; margin: 0; } + nav { display: flex; gap: 1.5rem; margin-top: 1rem; border-bottom: 1px solid var(--line); } + nav a { color: var(--mute); text-decoration: none; padding: 0.5rem 0; border-bottom: 2px solid transparent; } + nav a:hover, nav a.active { color: var(--paper); border-bottom-color: var(--copper); } .kicker { color: var(--copper); font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase; } .layout { display: grid; grid-template-columns: 280px 1fr; gap: 1.5rem; } @media (max-width: 860px) { .layout { grid-template-columns: 1fr; } } @@ -64,6 +67,12 @@ + + +