Add PWA support for MischLabs
All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 11s
All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 11s
This commit is contained in:
32
Brain.md
32
Brain.md
@@ -8,6 +8,7 @@ Eine ansprechende Landing Page fuer mischlabs.de, die alle Dienste als Kacheln m
|
|||||||
- [x] Dienste-Liste vom Screenshot erfasst (9 Services inkl. MiChess)
|
- [x] Dienste-Liste vom Screenshot erfasst (9 Services inkl. MiChess)
|
||||||
- [x] Landing Page Design und Implementierung (index.html + style.css)
|
- [x] Landing Page Design und Implementierung (index.html + style.css)
|
||||||
- [x] Docker-Setup (Dockerfile, docker-compose.yml, nginx.conf)
|
- [x] Docker-Setup (Dockerfile, docker-compose.yml, nginx.conf)
|
||||||
|
- [x] PWA-Grundlage fuer iOS/Home-Screen-Installation
|
||||||
- [x] Push zum Remote
|
- [x] Push zum Remote
|
||||||
- [x] MiChess-Kachel hinzugefuegt (Commit lokal, Push steht aus wg. Auth-Fehler)
|
- [x] MiChess-Kachel hinzugefuegt (Commit lokal, Push steht aus wg. Auth-Fehler)
|
||||||
- [ ] Push des MiChess-Commits (Git-Auth-Token erneuern)
|
- [ ] Push des MiChess-Commits (Git-Auth-Token erneuern)
|
||||||
@@ -31,13 +32,14 @@ Eine ansprechende Landing Page fuer mischlabs.de, die alle Dienste als Kacheln m
|
|||||||
| 8 | tom.mischlabs.de | Persoenliche Seite | #ec4899 (Pink) |
|
| 8 | tom.mischlabs.de | Persoenliche Seite | #ec4899 (Pink) |
|
||||||
|
|
||||||
## Technische Entscheidungen
|
## Technische Entscheidungen
|
||||||
- **Reines HTML + CSS** (kein Framework, kein JS) - maximale Performance, minimales Docker-Image
|
- **Reines HTML + CSS mit minimalem PWA-JS** (kein Framework) - maximale Performance, minimales Docker-Image
|
||||||
- **nginx:alpine** als Base-Image - extrem leichtgewichtig (~5MB)
|
- **nginx:alpine** als Base-Image - extrem leichtgewichtig (~5MB)
|
||||||
- **Google Fonts (Inter)** fuer moderne Typografie
|
- **Google Fonts (Inter)** fuer moderne Typografie
|
||||||
- **Inline SVG Icons** - keine externen Dependencies
|
- **Inline SVG Icons** - keine externen Dependencies
|
||||||
- **CSS Grid** mit `auto-fill` + `minmax(240px, 1fr)` fuer responsives Layout
|
- **CSS Grid** mit `auto-fill` + `minmax(240px, 1fr)` fuer responsives Layout
|
||||||
- **Dark Theme** mit subtilen Glow-Effekten
|
- **Dark Theme** mit subtilen Glow-Effekten
|
||||||
- **Staggered Animations**: Kacheln faden nacheinander ein (9 Stueck)
|
- **Staggered Animations**: Kacheln faden nacheinander ein (9 Stueck)
|
||||||
|
- **PWA**: Manifest, Apple-Touch-Icon, App-Icons und Service Worker fuer installierbare iOS/Web-App
|
||||||
|
|
||||||
## Docker-Setup
|
## Docker-Setup
|
||||||
- **Container-Name**: `mischlabs`
|
- **Container-Name**: `mischlabs`
|
||||||
@@ -51,6 +53,9 @@ Eine ansprechende Landing Page fuer mischlabs.de, die alle Dienste als Kacheln m
|
|||||||
mischlabs/
|
mischlabs/
|
||||||
index.html - Haupt-HTML mit allen 9 Service-Kacheln
|
index.html - Haupt-HTML mit allen 9 Service-Kacheln
|
||||||
style.css - Komplettes Styling (Dark Theme, Grid, Animationen)
|
style.css - Komplettes Styling (Dark Theme, Grid, Animationen)
|
||||||
|
manifest.webmanifest - PWA-Metadaten fuer installierbare App
|
||||||
|
sw.js - Schlanker Service Worker mit App-Shell-Cache
|
||||||
|
icons/ - App-Icons fuer iOS/PWA
|
||||||
Dockerfile - nginx:alpine mit kopierten Static Files
|
Dockerfile - nginx:alpine mit kopierten Static Files
|
||||||
docker-compose.yml - Container-Definition (Name: mischlabs, Port 8085)
|
docker-compose.yml - Container-Definition (Name: mischlabs, Port 8085)
|
||||||
nginx.conf - Server-Konfiguration, Caching, Security-Headers
|
nginx.conf - Server-Konfiguration, Caching, Security-Headers
|
||||||
@@ -72,3 +77,28 @@ mischlabs/
|
|||||||
- Git-Push-Fehler aufgetreten (Authentication failed) - Token muss erneuert werden
|
- Git-Push-Fehler aufgetreten (Authentication failed) - Token muss erneuert werden
|
||||||
- DNS: Alte A-Records (185.181.104.242) von INWX wurden geloescht, nur noch Tunnel-Eintraege
|
- DNS: Alte A-Records (185.181.104.242) von INWX wurden geloescht, nur noch Tunnel-Eintraege
|
||||||
- NAS hat kein git installiert - entweder Git Server Paket installieren oder wget nutzen
|
- NAS hat kein git installiert - entweder Git Server Paket installieren oder wget nutzen
|
||||||
|
|
||||||
|
## PWA-Stand 2026-05-21
|
||||||
|
- MischLabs wurde als PWA vorbereitet.
|
||||||
|
- iOS-relevante Meta-Tags in `index.html`:
|
||||||
|
- `apple-mobile-web-app-capable`
|
||||||
|
- `apple-mobile-web-app-status-bar-style`
|
||||||
|
- `apple-mobile-web-app-title`
|
||||||
|
- `apple-touch-icon`
|
||||||
|
- Manifest:
|
||||||
|
- `name`: MischLabs
|
||||||
|
- `display`: standalone
|
||||||
|
- `theme_color`: #10b981
|
||||||
|
- `background_color`: #0a0a0f
|
||||||
|
- Icons:
|
||||||
|
- `icons/apple-touch-icon.png` (180x180)
|
||||||
|
- `icons/icon-192.png` (192x192)
|
||||||
|
- `icons/icon-512.png` (512x512)
|
||||||
|
- `icons/maskable-512.png` (512x512, maskable)
|
||||||
|
- `icons/app-icon.svg` als editierbare Quelle
|
||||||
|
- Service Worker:
|
||||||
|
- Cache-Name: `mischlabs-pwa-v1`
|
||||||
|
- Cached die App-Shell (`/`, `index.html`, `style.css?v=8`, Manifest und Icons)
|
||||||
|
- Navigationen fallen offline auf `index.html` zurueck
|
||||||
|
- Dockerfile kopiert Manifest, Service Worker und Icons in das Nginx-Image.
|
||||||
|
- `nginx.conf` liefert `manifest.webmanifest` mit `application/manifest+json` und `sw.js` mit `no-cache` aus.
|
||||||
|
|||||||
@@ -3,5 +3,8 @@ FROM nginx:alpine
|
|||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY index.html /usr/share/nginx/html/
|
COPY index.html /usr/share/nginx/html/
|
||||||
COPY style.css /usr/share/nginx/html/
|
COPY style.css /usr/share/nginx/html/
|
||||||
|
COPY manifest.webmanifest /usr/share/nginx/html/
|
||||||
|
COPY sw.js /usr/share/nginx/html/
|
||||||
|
COPY icons/ /usr/share/nginx/html/icons/
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|||||||
27
icons/app-icon.svg
Normal file
27
icons/app-icon.svg
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="176" y1="116" x2="848" y2="908" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#14f1c6"/>
|
||||||
|
<stop offset="0.52" stop-color="#10b981"/>
|
||||||
|
<stop offset="1" stop-color="#0f766e"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="glow" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(320 236) rotate(52) scale(690)">
|
||||||
|
<stop stop-color="#ecfeff" stop-opacity="0.9"/>
|
||||||
|
<stop offset="0.42" stop-color="#99f6e4" stop-opacity="0.2"/>
|
||||||
|
<stop offset="1" stop-color="#042f2e" stop-opacity="0"/>
|
||||||
|
</radialGradient>
|
||||||
|
<filter id="shadow" x="120" y="132" width="784" height="784" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feDropShadow dx="0" dy="28" stdDeviation="38" flood-color="#042f2e" flood-opacity="0.34"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<rect width="1024" height="1024" rx="230" fill="#071412"/>
|
||||||
|
<rect x="72" y="72" width="880" height="880" rx="196" fill="url(#bg)"/>
|
||||||
|
<rect x="72" y="72" width="880" height="880" rx="196" fill="url(#glow)"/>
|
||||||
|
<g filter="url(#shadow)">
|
||||||
|
<path d="M294 690V334h88l130 205 130-205h88v356h-86V481L538 648h-52L380 482v208h-86Z" fill="#f8fffc"/>
|
||||||
|
<path d="M283 752h458" stroke="#d9fff4" stroke-width="42" stroke-linecap="round" opacity="0.72"/>
|
||||||
|
</g>
|
||||||
|
<circle cx="746" cy="276" r="34" fill="#ecfeff" opacity="0.86"/>
|
||||||
|
<circle cx="812" cy="350" r="17" fill="#ecfeff" opacity="0.64"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
BIN
icons/apple-touch-icon.png
Normal file
BIN
icons/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
icons/icon-192.png
Normal file
BIN
icons/icon-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
icons/icon-512.png
Normal file
BIN
icons/icon-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
BIN
icons/maskable-512.png
Normal file
BIN
icons/maskable-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
18
index.html
18
index.html
@@ -4,7 +4,16 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>MischLabs - Services</title>
|
<title>MischLabs - Services</title>
|
||||||
<link rel="stylesheet" href="style.css?v=7">
|
<meta name="description" content="MischLabs service hub for self-hosted apps.">
|
||||||
|
<meta name="theme-color" content="#10b981">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="MischLabs">
|
||||||
|
<link rel="manifest" href="/manifest.webmanifest">
|
||||||
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="512x512" href="/icons/icon-512.png">
|
||||||
|
<link rel="stylesheet" href="style.css?v=8">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="bg-glow bg-glow-1"></div>
|
<div class="bg-glow bg-glow-1"></div>
|
||||||
@@ -160,5 +169,12 @@
|
|||||||
<footer>
|
<footer>
|
||||||
<p>© 2026 MischLabs — Self-Hosted with care</p>
|
<p>© 2026 MischLabs — Self-Hosted with care</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
<script>
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register('/sw.js').catch(() => {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
34
manifest.webmanifest
Normal file
34
manifest.webmanifest
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "MischLabs",
|
||||||
|
"short_name": "MischLabs",
|
||||||
|
"description": "Self-hosted service hub for MischLabs.",
|
||||||
|
"lang": "de",
|
||||||
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"background_color": "#0a0a0f",
|
||||||
|
"theme_color": "#10b981",
|
||||||
|
"categories": ["productivity", "utilities"],
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/maskable-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
10
nginx.conf
10
nginx.conf
@@ -9,6 +9,16 @@ server {
|
|||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location = /manifest.webmanifest {
|
||||||
|
default_type application/manifest+json;
|
||||||
|
add_header Cache-Control "public, max-age=3600" always;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /sw.js {
|
||||||
|
default_type application/javascript;
|
||||||
|
add_header Cache-Control "no-cache" always;
|
||||||
|
}
|
||||||
|
|
||||||
# Cache static assets
|
# Cache static assets
|
||||||
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
|
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
|
||||||
expires 7d;
|
expires 7d;
|
||||||
|
|||||||
56
sw.js
Normal file
56
sw.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const CACHE_NAME = 'mischlabs-pwa-v1';
|
||||||
|
const APP_SHELL = [
|
||||||
|
'/',
|
||||||
|
'/index.html',
|
||||||
|
'/style.css?v=8',
|
||||||
|
'/manifest.webmanifest',
|
||||||
|
'/icons/icon-192.png',
|
||||||
|
'/icons/icon-512.png',
|
||||||
|
'/icons/apple-touch-icon.png'
|
||||||
|
];
|
||||||
|
|
||||||
|
self.addEventListener('install', (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL))
|
||||||
|
);
|
||||||
|
self.skipWaiting();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.keys().then((keys) => (
|
||||||
|
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
self.clients.claim();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('fetch', (event) => {
|
||||||
|
const request = event.request;
|
||||||
|
|
||||||
|
if (request.method !== 'GET') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.mode === 'navigate') {
|
||||||
|
event.respondWith(
|
||||||
|
fetch(request).catch(() => caches.match('/index.html'))
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(request).then((cached) => (
|
||||||
|
cached || fetch(request).then((response) => {
|
||||||
|
if (!response || response.status !== 200 || response.type !== 'basic') {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseToCache = response.clone();
|
||||||
|
caches.open(CACHE_NAME).then((cache) => cache.put(request, responseToCache));
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
Reference in New Issue
Block a user