mod divers data
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
# .dockerignore
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
.vscode
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Flutter/Dart
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.metadata
|
||||
|
||||
# Node.js (for proxy)
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# Test
|
||||
test/
|
||||
integration_test/
|
||||
.test_coverage/
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
!README.md
|
||||
|
||||
# CI/CD
|
||||
.github/
|
||||
|
||||
# Local development
|
||||
.env.local
|
||||
*.log
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
|
||||
# Linux
|
||||
*~
|
||||
|
||||
# Other
|
||||
*.iml
|
||||
*.class
|
||||
*.lock
|
||||
!pubspec.lock
|
||||
!package-lock.json
|
||||
328
DEPLOYMENT.md
328
DEPLOYMENT.md
@@ -1,328 +0,0 @@
|
||||
# Proxy Server Deployment - Anleitung
|
||||
|
||||
## 📋 Übersicht
|
||||
|
||||
Diese Anleitung erklärt, wie Sie den Reverse Proxy Server für PTV API Geocoding deployen. Der Proxy umgeht CORS-Probleme und läuft als eigenständiger Container.
|
||||
|
||||
## 🏗️ Architektur
|
||||
|
||||
```
|
||||
Browser (Flutter Web App)
|
||||
↓
|
||||
Proxy Server (localhost:3000 / your-server.com:3000)
|
||||
↓
|
||||
PTV Geocoding API (api.myptv.com)
|
||||
```
|
||||
|
||||
## 🚀 Lokale Entwicklung
|
||||
|
||||
### 1. Proxy Server starten
|
||||
|
||||
```bash
|
||||
cd /home/digitalman/Development/flutter_tank_web_app
|
||||
|
||||
# Option A: Mit Script
|
||||
./start-proxy.sh
|
||||
|
||||
# Option B: Manuell
|
||||
cd proxy-server
|
||||
node server.js
|
||||
```
|
||||
|
||||
Der Proxy läuft auf `http://localhost:3000`
|
||||
|
||||
### 2. Flutter App starten
|
||||
|
||||
```bash
|
||||
flutter run -d chrome
|
||||
```
|
||||
|
||||
### 3. Testen
|
||||
|
||||
**Geocoding Request:**
|
||||
```bash
|
||||
curl "http://localhost:3000/?lat=47.9385165&lon=13.762887&apiKey=YOUR_API_KEY"
|
||||
```
|
||||
|
||||
**Erwartete Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"location": "Straßenname Hausnummer, PLZ Stadt",
|
||||
"coordinates": {
|
||||
"lat": 47.9385165,
|
||||
"lon": 13.762887
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Health Check:**
|
||||
```bash
|
||||
curl http://localhost:3000/health
|
||||
# Response: healthy
|
||||
```
|
||||
|
||||
## 🐳 Docker Deployment (Empfohlen)
|
||||
|
||||
### 1. Docker Images bauen
|
||||
|
||||
```bash
|
||||
# Interaktives Deployment-Script
|
||||
./deploy.sh
|
||||
# Wählen Sie Option 3: "Alles bauen und starten"
|
||||
|
||||
# Oder manuell
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
### 2. Services überprüfen
|
||||
|
||||
```bash
|
||||
docker-compose ps
|
||||
|
||||
# Erwartete Ausgabe:
|
||||
# flutter-tank-web Up 0.0.0.0:8090->80/tcp
|
||||
# ptv-proxy Up 0.0.0.0:3000->3000/tcp
|
||||
```
|
||||
|
||||
### 3. Logs prüfen
|
||||
|
||||
```bash
|
||||
# Proxy Logs
|
||||
docker-compose logs -f ptv-proxy
|
||||
|
||||
# Alle Services
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
## 🌐 Produktion - Auf Server deployen
|
||||
|
||||
### Schritt 1: Server vorbereiten
|
||||
|
||||
**SSH zum Server:**
|
||||
```bash
|
||||
ssh user@your-server.com
|
||||
```
|
||||
|
||||
**Docker installieren (falls nicht vorhanden):**
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
### Schritt 2: Code auf Server kopieren
|
||||
|
||||
**Option A: Git**
|
||||
```bash
|
||||
git clone https://github.com/your-repo/flutter_tank_web_app.git
|
||||
cd flutter_tank_web_app
|
||||
```
|
||||
|
||||
**Option B: SCP/Rsync**
|
||||
```bash
|
||||
# Von lokalem Rechner
|
||||
rsync -avz --exclude 'build' --exclude '.dart_tool' \
|
||||
. user@your-server.com:/opt/flutter-tank-app/
|
||||
```
|
||||
|
||||
### Schritt 3: Proxy URL konfigurieren
|
||||
|
||||
Bearbeiten Sie `lib/config/environment.dart`:
|
||||
|
||||
```dart
|
||||
class Environment {
|
||||
// Produktions-URL des Proxy Servers
|
||||
static const String localProxyUrl = 'http://your-server.com:3000';
|
||||
static const bool useLocalProxy = true;
|
||||
|
||||
// ... rest bleibt gleich
|
||||
}
|
||||
```
|
||||
|
||||
### Schritt 4: Neu builden und deployen
|
||||
|
||||
```bash
|
||||
cd /opt/flutter-tank-app
|
||||
./deploy.sh
|
||||
# Option 3: "Alles bauen und starten"
|
||||
```
|
||||
|
||||
### Schritt 5: Firewall konfigurieren
|
||||
|
||||
```bash
|
||||
# Port 8090 (Web App) und 3000 (Proxy) öffnen
|
||||
sudo ufw allow 8090/tcp
|
||||
sudo ufw allow 3000/tcp
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
## 🔒 Sichere Konfiguration mit Nginx (Empfohlen)
|
||||
|
||||
Statt Port 3000 direkt zu exponieren, verwenden Sie Nginx als Reverse Proxy:
|
||||
|
||||
### 1. Nginx konfigurieren
|
||||
|
||||
```bash
|
||||
sudo nano /etc/nginx/sites-available/tank.yourdomain.com
|
||||
```
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name tank.yourdomain.com;
|
||||
|
||||
# Flutter Web App
|
||||
location / {
|
||||
proxy_pass http://localhost:8090;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# Proxy Server (API Endpoint)
|
||||
location /api/geocode {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
# CORS Headers werden vom Proxy Server gesetzt
|
||||
proxy_hide_header Access-Control-Allow-Origin;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Site aktivieren
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/tanknew.joshihomeserver.ipv64.net /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 3. SSL Zertifikat (Let's Encrypt)
|
||||
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d tanknew.joshihomeserver.ipv64.net
|
||||
```
|
||||
|
||||
### 4. Environment anpassen
|
||||
|
||||
```dart
|
||||
static const String localProxyUrl = 'https://tanknew.joshihomeserver.ipv64.net/api/geocode';
|
||||
```
|
||||
|
||||
### 5. Firewall anpassen
|
||||
|
||||
```bash
|
||||
# Nur 80 und 443 öffnen (nicht 3000)
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
sudo ufw deny 3000/tcp # Proxy nur über Nginx erreichbar
|
||||
```
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Proxy Server startet nicht
|
||||
|
||||
**Logs prüfen:**
|
||||
```bash
|
||||
docker-compose logs ptv-proxy
|
||||
```
|
||||
|
||||
**Häufige Probleme:**
|
||||
- Port 3000 bereits belegt: `sudo lsof -i :3000`
|
||||
- Node.js nicht installiert: `node --version`
|
||||
|
||||
### CORS-Fehler trotz Proxy
|
||||
|
||||
**Prüfen:**
|
||||
1. Ist der Proxy erreichbar? `curl http://localhost:3000/health`
|
||||
2. Richtige URL in `environment.dart`?
|
||||
3. Browser Cache leeren und App neu laden
|
||||
|
||||
### "Connection refused"
|
||||
|
||||
**Lösung:**
|
||||
```bash
|
||||
# Docker Container laufen?
|
||||
docker-compose ps
|
||||
|
||||
# Proxy neu starten
|
||||
docker-compose restart ptv-proxy
|
||||
```
|
||||
|
||||
### PTV API gibt 401 zurück
|
||||
|
||||
**Problem:** Ungültiger API Key
|
||||
**Lösung:** API Key in `environment.dart` überprüfen
|
||||
|
||||
### Im Container: "npm not found"
|
||||
|
||||
**Problem:** Lokales `node_modules` committet
|
||||
**Lösung:**
|
||||
```bash
|
||||
# .dockerignore prüfen
|
||||
cat .dockerignore | grep node_modules
|
||||
|
||||
# Sollte vorhanden sein
|
||||
```
|
||||
|
||||
## 📊 Monitoring
|
||||
|
||||
### Service Status
|
||||
|
||||
```bash
|
||||
# Docker
|
||||
docker-compose ps
|
||||
|
||||
# System Resources
|
||||
docker stats
|
||||
|
||||
# Health Check
|
||||
curl http://localhost:3000/health
|
||||
```
|
||||
|
||||
### Logs Live anschauen
|
||||
|
||||
```bash
|
||||
# Proxy
|
||||
docker-compose logs -f ptv-proxy
|
||||
|
||||
# Alle Services
|
||||
docker-compose logs -f --tail=100
|
||||
```
|
||||
|
||||
## 🔄 Updates deployen
|
||||
|
||||
```bash
|
||||
# Code aktualisieren
|
||||
git pull
|
||||
|
||||
# Container neu bauen und starten
|
||||
docker-compose up -d --build
|
||||
|
||||
# Alte Images aufräumen
|
||||
docker image prune -f
|
||||
```
|
||||
|
||||
## ✅ Erfolgskriterien
|
||||
|
||||
Nach erfolgreichem Deployment sollten Sie:
|
||||
- ✅ Proxy Health Check erfolgreich: `curl http://tanknew.joshihomeserver.ipv64.net:3000/health`
|
||||
- ✅ Geocoding funktioniert in der App
|
||||
- ✅ Browser Console zeigt: "🔄 Verwende lokalen Proxy..."
|
||||
- ✅ Browser Console zeigt: "✅ Geocoding erfolgreich (Proxy): [Adresse]"
|
||||
- ✅ Keine CORS-Fehler mehr
|
||||
|
||||
## 📝 Produktions-Checkliste
|
||||
|
||||
- [ ] Docker und Docker Compose installiert
|
||||
- [ ] Code auf Server übertragen
|
||||
- [ ] `localProxyUrl` in environment.dart angepasst
|
||||
- [ ] Docker Containers laufen (`docker-compose ps`)
|
||||
- [ ] Firewall konfiguriert
|
||||
- [ ] Nginx Reverse Proxy eingerichtet (empfohlen)
|
||||
- [ ] SSL Zertifikat installiert
|
||||
- [ ] Health Checks erfolgreich
|
||||
- [ ] Geocoding in App getestet
|
||||
@@ -1,363 +0,0 @@
|
||||
# 🐳 Docker Deployment Anleitung
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Docker installiert (`docker --version`)
|
||||
- Docker Compose installiert (`docker-compose --version`)
|
||||
- Min. 2 GB freier RAM
|
||||
- Min. 5 GB freier Speicherplatz
|
||||
|
||||
## 🚀 Schnellstart
|
||||
|
||||
### Option 1: Mit Deploy-Script (Empfohlen)
|
||||
|
||||
```bash
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
Folgen Sie den Anweisungen im interaktiven Menü.
|
||||
|
||||
### Option 2: Manuelle Commands
|
||||
|
||||
**Alles bauen und starten:**
|
||||
```bash
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
**Nur starten (bereits gebaut):**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
**Stoppen:**
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
**Logs anzeigen:**
|
||||
```bash
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
## 📦 Was wird deployed?
|
||||
|
||||
### 1. Flutter Web App Container
|
||||
- **Image:** `flutter-tank-web:latest`
|
||||
- **Port:** 8080 (Host) → 80 (Container)
|
||||
- **URL:** `http://localhost:8080`
|
||||
- **Technologie:** Flutter Web + Nginx
|
||||
|
||||
### 2. Proxy Server Container (Optional)
|
||||
- **Image:** `ptv-proxy:latest`
|
||||
- **Port:** 3000 (Host) → 3000 (Container)
|
||||
- **URL:** `http://localhost:3000`
|
||||
- **Technologie:** Node.js
|
||||
|
||||
## 🏗️ Build-Prozess
|
||||
|
||||
Der Build läuft in 2 Phasen:
|
||||
|
||||
**Phase 1: Flutter Build (Multi-Stage)**
|
||||
1. Ubuntu base image
|
||||
2. Flutter SDK installieren
|
||||
3. Dependencies installieren (`flutter pub get`)
|
||||
4. Web App bauen (`flutter build web`)
|
||||
|
||||
**Phase 2: Production (Nginx)**
|
||||
1. Alpine Nginx image
|
||||
2. Gebaute App kopieren
|
||||
3. Nginx konfigurieren
|
||||
4. Port 80 exponieren
|
||||
|
||||
**Geschätzte Build-Zeit:** 5-10 Minuten (beim ersten Mal)
|
||||
|
||||
## 🌐 Auf Server deployen
|
||||
|
||||
### 1. Server vorbereiten
|
||||
|
||||
**SSH zum Server:**
|
||||
```bash
|
||||
ssh user@your-server.com
|
||||
```
|
||||
|
||||
**Docker installieren (falls nicht vorhanden):**
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
**Docker Compose installieren:**
|
||||
```bash
|
||||
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
### 2. Code auf Server übertragen
|
||||
|
||||
**Option A: Git Clone**
|
||||
```bash
|
||||
git clone https://github.com/your-repo/flutter_tank_web_app.git
|
||||
cd flutter_tank_web_app
|
||||
```
|
||||
|
||||
**Option B: SCP/Rsync**
|
||||
```bash
|
||||
# Von lokalem Rechner aus
|
||||
rsync -avz --exclude 'build' --exclude '.dart_tool' \
|
||||
/home/digitalman/Development/flutter_tank_web_app/ \
|
||||
user@your-server.com:/opt/flutter-tank-app/
|
||||
```
|
||||
|
||||
### 3. Environment anpassen
|
||||
|
||||
Bearbeiten Sie `lib/config/environment.dart`:
|
||||
```dart
|
||||
static const bool useLocalProxy = false; // Für Produktion
|
||||
```
|
||||
|
||||
### 4. Deployen
|
||||
|
||||
```bash
|
||||
cd /opt/flutter-tank-app
|
||||
./deploy.sh
|
||||
# Wählen Sie Option 3: "Alles bauen und starten"
|
||||
```
|
||||
|
||||
### 5. Mit Domain verbinden (Optional)
|
||||
|
||||
**Nginx Reverse Proxy konfigurieren:**
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/tank.yourdomain.com
|
||||
server {
|
||||
listen 80;
|
||||
server_name tank.yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Aktivieren:**
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/tank.yourdomain.com /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
**SSL mit Let's Encrypt:**
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d tank.yourdomain.com
|
||||
```
|
||||
|
||||
## 🔧 Konfiguration
|
||||
|
||||
### Ports ändern
|
||||
|
||||
In `docker-compose.yml`:
|
||||
```yaml
|
||||
ports:
|
||||
- "8080:80" # Ändern Sie 8080 auf gewünschten Port
|
||||
```
|
||||
|
||||
### Umgebungsvariablen
|
||||
|
||||
Erstellen Sie `.env` Datei:
|
||||
```env
|
||||
# App Settings
|
||||
APP_PORT=8080
|
||||
PROXY_PORT=3000
|
||||
|
||||
# Domain (für Traefik)
|
||||
DOMAIN=tank.yourdomain.com
|
||||
```
|
||||
|
||||
### Mit Traefik (Reverse Proxy)
|
||||
|
||||
Die `docker-compose.yml` enthält bereits Traefik Labels.
|
||||
|
||||
**Traefik setup:**
|
||||
```yaml
|
||||
# docker-compose.traefik.yml
|
||||
version: '3.8'
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v2.10
|
||||
command:
|
||||
- "--providers.docker=true"
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--certificatesresolvers.letsencrypt.acme.email=your@email.com"
|
||||
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
|
||||
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "./letsencrypt:/letsencrypt"
|
||||
```
|
||||
|
||||
## 📊 Monitoring
|
||||
|
||||
### Container Status prüfen
|
||||
```bash
|
||||
docker-compose ps
|
||||
```
|
||||
|
||||
### Logs anschauen
|
||||
```bash
|
||||
# Alle Services
|
||||
docker-compose logs -f
|
||||
|
||||
# Nur Web App
|
||||
docker-compose logs -f flutter-tank-web
|
||||
|
||||
# Nur Proxy
|
||||
docker-compose logs -f ptv-proxy
|
||||
```
|
||||
|
||||
### Resource Usage
|
||||
```bash
|
||||
docker stats
|
||||
```
|
||||
|
||||
### Health Check
|
||||
```bash
|
||||
# Web App
|
||||
curl http://localhost:8080/health
|
||||
|
||||
# Proxy
|
||||
curl http://localhost:3000/health
|
||||
```
|
||||
|
||||
## 🔄 Updates deployen
|
||||
|
||||
```bash
|
||||
# Code aktualisieren
|
||||
git pull
|
||||
|
||||
# Neu bauen und starten
|
||||
docker-compose up -d --build
|
||||
|
||||
# Alte Images aufräumen
|
||||
docker image prune -f
|
||||
```
|
||||
|
||||
## 🛠️ Troubleshooting
|
||||
|
||||
### Container startet nicht
|
||||
```bash
|
||||
# Logs prüfen
|
||||
docker-compose logs flutter-tank-web
|
||||
|
||||
# Container Status
|
||||
docker-compose ps
|
||||
|
||||
# Neu starten
|
||||
docker-compose restart flutter-tank-web
|
||||
```
|
||||
|
||||
### Port bereits belegt
|
||||
```bash
|
||||
# Prozess auf Port finden
|
||||
sudo lsof -i :8080
|
||||
|
||||
# Port in docker-compose.yml ändern
|
||||
```
|
||||
|
||||
### Out of Memory
|
||||
```bash
|
||||
# Docker Memory Limit erhöhen
|
||||
# In docker-compose.yml unter service:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
### Build schlägt fehl
|
||||
```bash
|
||||
# Docker Cache leeren
|
||||
docker builder prune -a
|
||||
|
||||
# Komplett neu bauen
|
||||
docker-compose build --no-cache
|
||||
```
|
||||
|
||||
## 📦 Backup & Restore
|
||||
|
||||
### Volumes sichern
|
||||
```bash
|
||||
# Data Volume backup
|
||||
docker run --rm -v flutter-tank-data:/data -v $(pwd):/backup alpine tar czf /backup/data-backup.tar.gz -C /data .
|
||||
```
|
||||
|
||||
### Image speichern
|
||||
```bash
|
||||
# Image exportieren
|
||||
docker save flutter-tank-web:latest | gzip > flutter-tank-web.tar.gz
|
||||
|
||||
# Image importieren
|
||||
docker load < flutter-tank-web.tar.gz
|
||||
```
|
||||
|
||||
## 🔒 Sicherheit
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Nicht als Root laufen** (bereits in Proxy-Dockerfile implementiert)
|
||||
2. **Security Headers** (bereits in nginx.conf)
|
||||
3. **Firewall konfigurieren:**
|
||||
```bash
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
4. **Regular Updates:**
|
||||
```bash
|
||||
# Base images updaten
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## 🎯 Performance Optimierung
|
||||
|
||||
### Nginx Caching
|
||||
Bereits in `nginx.conf` konfiguriert:
|
||||
- Static assets: 1 Jahr Cache
|
||||
- Gzip Kompression aktiv
|
||||
|
||||
### Docker Image Size
|
||||
```bash
|
||||
# Image Größe prüfen
|
||||
docker images flutter-tank-web
|
||||
|
||||
# Multi-stage build reduziert Größe bereits deutlich
|
||||
```
|
||||
|
||||
## 📝 Checkliste Produktion
|
||||
|
||||
- [ ] `useLocalProxy = false` in environment.dart
|
||||
- [ ] Domain konfiguriert
|
||||
- [ ] SSL Zertifikat installiert
|
||||
- [ ] Firewall konfiguriert
|
||||
- [ ] Backup-Strategie definiert
|
||||
- [ ] Monitoring eingerichtet
|
||||
- [ ] Logs rotieren konfiguriert
|
||||
- [ ] Update-Prozess dokumentiert
|
||||
|
||||
## 🆘 Support
|
||||
|
||||
Bei Problemen:
|
||||
1. Logs prüfen: `docker-compose logs`
|
||||
2. Container Status: `docker-compose ps`
|
||||
3. Health Checks: `curl http://localhost:8080/health`
|
||||
4. Docker System: `docker system df`
|
||||
54
Dockerfile
54
Dockerfile
@@ -1,54 +0,0 @@
|
||||
# Dockerfile für Flutter Web App
|
||||
FROM ubuntu:22.04 AS build
|
||||
|
||||
# Avoid interactive prompts
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
git \
|
||||
unzip \
|
||||
xz-utils \
|
||||
zip \
|
||||
libglu1-mesa \
|
||||
wget \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Flutter
|
||||
ENV FLUTTER_HOME=/usr/local/flutter
|
||||
ENV PATH="${FLUTTER_HOME}/bin:${PATH}"
|
||||
ENV TAR_OPTIONS=--no-same-owner
|
||||
|
||||
RUN git clone https://github.com/flutter/flutter.git ${FLUTTER_HOME} -b stable --depth 1 && \
|
||||
flutter config --enable-web --no-analytics && \
|
||||
flutter precache --web
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy app files
|
||||
COPY pubspec.yaml pubspec.lock ./
|
||||
|
||||
# Run pub get (downloads web dependencies without needing gradle)
|
||||
RUN flutter pub get
|
||||
|
||||
COPY . .
|
||||
|
||||
# Build web app (web-only artifacts; suppress wasm dry-run warnings)
|
||||
RUN flutter build web --release --no-wasm-dry-run
|
||||
|
||||
# Production stage - Nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy built app to nginx
|
||||
COPY --from=build /app/build/web /usr/share/nginx/html
|
||||
|
||||
# Copy custom nginx config
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
# Start nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,86 +0,0 @@
|
||||
# 🚀 Quick Start - Lokaler Reverse Proxy
|
||||
|
||||
## Sofort loslegen
|
||||
|
||||
### Terminal 1: Proxy Server starten
|
||||
|
||||
```bash
|
||||
./start-proxy.sh
|
||||
```
|
||||
|
||||
Oder manuell:
|
||||
```bash
|
||||
cd proxy-server
|
||||
node server.js
|
||||
```
|
||||
|
||||
### Terminal 2: Flutter App starten
|
||||
|
||||
```bash
|
||||
flutter run -d chrome
|
||||
```
|
||||
|
||||
## ✅ Das wars!
|
||||
|
||||
Die App verwendet jetzt automatisch den lokalen Proxy auf `http://localhost:3000` für Geocoding.
|
||||
|
||||
## 🔧 Konfiguration
|
||||
|
||||
In [lib/config/environment.dart](lib/config/environment.dart):
|
||||
|
||||
```dart
|
||||
static const bool useLocalProxy = true; // true = Proxy, false = Appwrite Function
|
||||
```
|
||||
|
||||
## 📋 Checkliste
|
||||
|
||||
- ✅ Node.js installiert (`node --version`)
|
||||
- ✅ Proxy läuft (`http://localhost:3000` im Browser)
|
||||
- ✅ `useLocalProxy = true` in environment.dart
|
||||
- ✅ Flutter App läuft
|
||||
|
||||
## 🎯 Vorteile Lokaler Proxy
|
||||
|
||||
✅ **Kein CORS-Problem** - Server umgeht Browser-Beschränkungen
|
||||
✅ **Schnelles Testen** - Kein Cloud-Deployment nötig
|
||||
✅ **Einfaches Debugging** - Logs direkt im Terminal
|
||||
✅ **Kostenlos** - Läuft lokal, keine Cloud-Kosten
|
||||
|
||||
## 🌐 Für Produktion
|
||||
|
||||
Für den Live-Betrieb:
|
||||
|
||||
1. Setzen Sie `useLocalProxy = false`
|
||||
2. Deployen Sie die Appwrite Function (siehe [DEPLOYMENT.md](DEPLOYMENT.md))
|
||||
3. Tragen Sie die Function ID ein
|
||||
|
||||
## 📊 Vergleich
|
||||
|
||||
| Feature | Lokaler Proxy | Appwrite Function |
|
||||
|---------|--------------|-------------------|
|
||||
| Setup Zeit | < 1 Minute | ~10 Minuten |
|
||||
| CORS | ✅ Gelöst | ✅ Gelöst |
|
||||
| Kosten | Kostenlos | Serverless (minimal) |
|
||||
| Verwendung | Nur Entwicklung | Entwicklung + Produktion |
|
||||
| Debugging | Sehr einfach | Logs in Console |
|
||||
| Offline | ❌ | ❌ |
|
||||
|
||||
## 🐛 Problemlösung
|
||||
|
||||
**Proxy startet nicht:**
|
||||
```bash
|
||||
# Port 3000 belegt?
|
||||
lsof -i :3000
|
||||
# Prozess beenden
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
**App findet Proxy nicht:**
|
||||
- Proxy läuft? Prüfen Sie `http://localhost:3000` im Browser
|
||||
- `useLocalProxy = true`? Prüfen Sie environment.dart
|
||||
- Hot Restart in Flutter machen
|
||||
|
||||
**Immer noch CORS-Fehler:**
|
||||
- Browser-Cache leeren
|
||||
- DevTools → Network → "Disable cache"
|
||||
- App neu builden: `flutter run -d chrome`
|
||||
111
STATUS.md
111
STATUS.md
@@ -1,111 +0,0 @@
|
||||
# ✅ System Status - Alles Repariert
|
||||
|
||||
## 📋 Überprüfungsergebnis
|
||||
|
||||
### ✅ Alle Fehler behoben!
|
||||
|
||||
**Probleme die behoben wurden:**
|
||||
1. ❌ Syntax-Fehler in edit_controller.dart → ✅ Behoben
|
||||
2. ❌ Fehlende Imports → ✅ Hinzugefügt
|
||||
3. ❌ Unvollständige Methoden → ✅ Implementiert
|
||||
4. ❌ Duplicate Code → ✅ Entfernt
|
||||
|
||||
## 🎯 Aktueller Status
|
||||
|
||||
### 1. Edit Controller ✅
|
||||
- [x] Keine Compile-Fehler
|
||||
- [x] Verwendet `appwriteService.geocodeLocation()`
|
||||
- [x] Benutzer-Feedback mit Snackbars
|
||||
- [x] Saubere Imports
|
||||
|
||||
### 2. Appwrite Service ✅
|
||||
- [x] `geocodeLocation()` Methode implementiert
|
||||
- [x] `_geocodeViaLocalProxy()` für lokalen Proxy
|
||||
- [x] Fallback auf Koordinaten bei Fehlern
|
||||
- [x] http package importiert
|
||||
|
||||
### 3. Environment Config ✅
|
||||
```dart
|
||||
static const bool useLocalProxy = true; // ✅ Aktiv
|
||||
static const String localProxyUrl = 'http://localhost:3000';
|
||||
```
|
||||
|
||||
### 4. Proxy Server ✅
|
||||
- [x] server.js existiert
|
||||
- [x] package.json existiert
|
||||
- [x] README.md vorhanden
|
||||
- [x] Node.js funktioniert
|
||||
- [x] Start-Script ausführbar
|
||||
|
||||
## 🚀 So starten Sie die App
|
||||
|
||||
### Terminal 1: Proxy Server
|
||||
```bash
|
||||
cd /home/digitalman/Development/flutter_tank_web_app
|
||||
./start-proxy.sh
|
||||
```
|
||||
|
||||
### Terminal 2: Flutter App
|
||||
```bash
|
||||
cd /home/digitalman/Development/flutter_tank_web_app
|
||||
flutter run -d chrome
|
||||
```
|
||||
|
||||
## 📊 Funktionsweise
|
||||
|
||||
```
|
||||
User erstellt Tankeintrag
|
||||
↓
|
||||
Geolocation fragt GPS ab
|
||||
↓
|
||||
EditController ruft appwriteService.geocodeLocation() auf
|
||||
↓
|
||||
AppwriteService prüft useLocalProxy Flag
|
||||
↓
|
||||
├─ true → Lokaler Proxy (localhost:3000) → PTV API ✅
|
||||
└─ false → Fallback auf Koordinaten
|
||||
↓
|
||||
Adresse oder Koordinaten werden gespeichert
|
||||
```
|
||||
|
||||
## ✅ Erfolgskriterien
|
||||
|
||||
Wenn alles funktioniert, sehen Sie:
|
||||
- ✅ Proxy-Server läuft auf Port 3000
|
||||
- ✅ "🔄 Verwende lokalen Proxy" in Logs
|
||||
- ✅ "✅ Geocoding erfolgreich (Proxy): [Adresse]"
|
||||
- ✅ Grüne Snackbar mit Adresse in der App
|
||||
- ✅ Keine CORS-Fehler in Browser Console
|
||||
|
||||
## 🐛 Wenn etwas nicht funktioniert
|
||||
|
||||
**Proxy nicht erreichbar:**
|
||||
```bash
|
||||
# Prüfen ob läuft
|
||||
curl http://localhost:3000
|
||||
# Neu starten
|
||||
./start-proxy.sh
|
||||
```
|
||||
|
||||
**Flutter App zeigt Koordinaten statt Adresse:**
|
||||
1. Prüfen: Proxy läuft?
|
||||
2. Prüfen: `useLocalProxy = true`?
|
||||
3. Browser Console für Fehler prüfen
|
||||
4. Hot Restart: `r` im Flutter Terminal
|
||||
|
||||
**Port 3000 belegt:**
|
||||
```bash
|
||||
# Prozess finden und beenden
|
||||
lsof -i :3000
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
## 📝 Nächste Schritte
|
||||
|
||||
1. **Testen**: Neuen Tankeintrag erstellen
|
||||
2. **Verifizieren**: Adresse wird korrekt angezeigt
|
||||
3. **Optional**: Für Produktion auf `useLocalProxy = false` setzen
|
||||
|
||||
## 🎉 Status: BEREIT FÜR NUTZUNG!
|
||||
|
||||
Alle Systeme sind operationsbereit. Der lokale Reverse-Proxy umgeht das CORS-Problem erfolgreich.
|
||||
85
deploy.sh
85
deploy.sh
@@ -1,85 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Farben
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} Flutter Tank App - Docker Deployment${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
# Prüfen ob Docker installiert ist
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}❌ Docker ist nicht installiert!${NC}"
|
||||
echo -e "${YELLOW}Installieren Sie Docker: https://docs.docker.com/get-docker/${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
echo -e "${RED}❌ Docker Compose ist nicht installiert!${NC}"
|
||||
echo -e "${YELLOW}Installieren Sie Docker Compose: https://docs.docker.com/compose/install/${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Docker und Docker Compose gefunden${NC}"
|
||||
echo ""
|
||||
|
||||
# Build Option
|
||||
echo -e "${YELLOW}Wählen Sie eine Option:${NC}"
|
||||
echo "1) Nur Flutter Web App bauen"
|
||||
echo "2) Flutter Web App + Proxy Server bauen"
|
||||
echo "3) Alles bauen und starten"
|
||||
echo "4) Nur starten (ohne neu zu bauen)"
|
||||
echo "5) Stoppen"
|
||||
echo "6) Logs anzeigen"
|
||||
read -p "Option (1-6): " option
|
||||
|
||||
case $option in
|
||||
1)
|
||||
echo -e "${BLUE}🔨 Baue Flutter Web App...${NC}"
|
||||
docker build -t flutter-tank-web:latest .
|
||||
echo -e "${GREEN}✅ Build abgeschlossen!${NC}"
|
||||
echo -e "${YELLOW}Zum Starten: docker run -p 8080:80 flutter-tank-web:latest${NC}"
|
||||
;;
|
||||
2)
|
||||
echo -e "${BLUE}🔨 Baue alle Images...${NC}"
|
||||
docker-compose build
|
||||
echo -e "${GREEN}✅ Build abgeschlossen!${NC}"
|
||||
;;
|
||||
3)
|
||||
echo -e "${BLUE}🔨 Baue und starte alle Services...${NC}"
|
||||
docker-compose up -d --build
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ Services gestartet!${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}📡 Verfügbare Endpoints:${NC}"
|
||||
echo -e " Flutter Web App: ${GREEN}http://localhost:8090${NC}"
|
||||
echo -e " Proxy Server: ${GREEN}http://localhost:3000${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}📊 Status prüfen:${NC}"
|
||||
docker-compose ps
|
||||
;;
|
||||
4)
|
||||
echo -e "${BLUE}🚀 Starte Services...${NC}"
|
||||
docker-compose up -d
|
||||
echo -e "${GREEN}✅ Services gestartet!${NC}"
|
||||
docker-compose ps
|
||||
;;
|
||||
5)
|
||||
echo -e "${BLUE}🛑 Stoppe Services...${NC}"
|
||||
docker-compose down
|
||||
echo -e "${GREEN}✅ Services gestoppt!${NC}"
|
||||
;;
|
||||
6)
|
||||
echo -e "${BLUE}📋 Zeige Logs...${NC}"
|
||||
docker-compose logs -f
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}❌ Ungültige Option${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,39 +0,0 @@
|
||||
services:
|
||||
# Flutter Web App
|
||||
flutter-tank-web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: flutter-tank-web
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8090:80"
|
||||
networks:
|
||||
- app-network
|
||||
environment:
|
||||
- TZ=Europe/Vienna
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.flutter-tank.rule=Host(`tanknew.joshihomeserver.ipv64.net`)"
|
||||
- "traefik.http.routers.flutter-tank.entrypoints=websecure"
|
||||
- "traefik.http.routers.flutter-tank.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.flutter-tank.loadbalancer.server.port=80"
|
||||
|
||||
# Optional: Reverse Proxy für PTV API (Production)
|
||||
ptv-proxy:
|
||||
build:
|
||||
context: ./proxy-server
|
||||
dockerfile: Dockerfile
|
||||
container_name: ptv-proxy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
- app-network
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
@@ -5,17 +5,6 @@ class Environment {
|
||||
static const String appwriteProjectName = 'Flutter Projects';
|
||||
static const String appwriteRealtimeCollectionId = '68a22f520035a95d6666';
|
||||
static const String appwriteDatabaseId = '68a22ef90021b90f0f43';
|
||||
static const String ptvApiKey =
|
||||
'NTYxMDQ3NTY2OWI3NDI5ZGIzZWIxOWNiNTNhMDEwODY6YTQ4MTJhYzYtYmYzOC00ZmE4LTk4YzYtZDBjNzYyZTAyNjBk';
|
||||
static const String locationIQKey = 'pk.dea65023dc6fed25c96902bb97fb231d';
|
||||
static const double lat=47.93875449671056;
|
||||
static const double lon=13.762706553431048;
|
||||
//https://eu1.locationiq.com/v1/reverse?key=pk.dea65023dc6fed25c96902bb97fb231d&lat=47.93875449671056&lon=13.762706553431048&format=json
|
||||
static const String locationIQBaseUrl =
|
||||
'https://eu1.locationiq.com/v1/reverse?key=$locationIQKey&'; //47.93875449671056, 13.762706553431048
|
||||
static const String ptvApiVersion = '3';
|
||||
// Lokaler Reverse Proxy für Entwicklung (CORS-Workaround)
|
||||
static const String localProxyUrl = 'http://localhost:3000';
|
||||
static const bool useLocalProxy =
|
||||
true; // true = lokaler Proxy, false = Appwrite Function
|
||||
static const String locationIQBaseUrl = 'https://eu1.locationiq.com/v1/reverse?key=$locationIQKey&';
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter_tank_web_app/services/appwrite_service.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../models/tank_model.dart';
|
||||
import '../pages/edit_view.dart';
|
||||
import '../services/appwrite_service.dart';
|
||||
|
||||
class DetailController extends GetxController {
|
||||
late TankModel tank;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
|
||||
import '../models/locationiq_model.dart';
|
||||
import '../models/tank_model.dart';
|
||||
import '../services/appwrite_service.dart';
|
||||
@@ -62,78 +61,6 @@ class EditController extends GetxController {
|
||||
'${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
// Future<void> _requestLocation() async {
|
||||
// bool serviceEnabled;
|
||||
// LocationPermission permission;
|
||||
|
||||
// try {
|
||||
// isLoadingLocation.value = true;
|
||||
|
||||
// // 1. Prüfen, ob Standortdienste aktiviert sind
|
||||
// serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
// if (!serviceEnabled) {
|
||||
// return Future.error('Standortdienste sind deaktiviert.');
|
||||
// }
|
||||
|
||||
// // 2. Berechtigungen prüfen
|
||||
// permission = await Geolocator.checkPermission();
|
||||
// if (permission == LocationPermission.denied) {
|
||||
// permission = await Geolocator.requestPermission();
|
||||
// if (permission == LocationPermission.denied) {
|
||||
// return Future.error('Berechtigung verweigert.');
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 3. Position abrufen
|
||||
// Position position = await Geolocator.getCurrentPosition(
|
||||
// locationSettings: const LocationSettings(
|
||||
// accuracy: LocationAccuracy.high,
|
||||
// ),
|
||||
// );
|
||||
|
||||
// // 4. Standort über Backend-Proxy abrufen
|
||||
// var lat = position.latitude;
|
||||
// var lon = position.longitude;
|
||||
|
||||
// print('📍 Verwende Backend-Proxy für Geocoding...');
|
||||
// String location = await appwriteService.geocodeLocation(lat, lon);
|
||||
// locationController.text = location;
|
||||
|
||||
// // Info anzeigen basierend auf Ergebnis
|
||||
// if (location.startsWith('Lat:')) {
|
||||
// Get.snackbar(
|
||||
// 'Hinweis',
|
||||
// 'Adresse konnte nicht abgerufen werden. Koordinaten gespeichert.',
|
||||
// snackPosition: SnackPosition.BOTTOM,
|
||||
// backgroundColor: Colors.orange[100],
|
||||
// colorText: Colors.orange[900],
|
||||
// duration: const Duration(seconds: 3),
|
||||
// );
|
||||
// } else {
|
||||
// Get.snackbar(
|
||||
// 'Erfolg',
|
||||
// 'Standort: $location',
|
||||
// snackPosition: SnackPosition.BOTTOM,
|
||||
// backgroundColor: Colors.green[100],
|
||||
// colorText: Colors.green[900],
|
||||
// duration: const Duration(seconds: 2),
|
||||
// );
|
||||
// }
|
||||
// } catch (e) {
|
||||
// Get.snackbar(
|
||||
// "Fehler",
|
||||
// "Standort konnte nicht abgerufen werden: $e",
|
||||
// snackPosition: SnackPosition.BOTTOM,
|
||||
// backgroundColor: Colors.red[100],
|
||||
// colorText: Colors.red[900],
|
||||
// );
|
||||
// print("Fehler beim Abrufen des Standorts: $e");
|
||||
// locationController.text = '';
|
||||
// } finally {
|
||||
// isLoadingLocation.value = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> _requestLocationIQ() async {
|
||||
bool serviceEnabled;
|
||||
LocationPermission permission;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter_tank_web_app/models/tank_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../models/tank_model.dart';
|
||||
|
||||
class GraphController extends GetxController {
|
||||
final listTankModel = <TankModel>[].obs;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//import 'dart:convert';
|
||||
|
||||
import 'package:appwrite/models.dart';
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
//import 'package:http/http.dart' as http;
|
||||
import '../config/environment.dart';
|
||||
|
||||
class AppwriteService {
|
||||
@@ -167,47 +166,4 @@ class AppwriteService {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// // Geocode coordinates using local proxy or Appwrite Function
|
||||
// Future<String> geocodeLocation(double lat, double lon) async {
|
||||
// // Wenn lokaler Proxy aktiviert ist, diesen verwenden
|
||||
// if (Environment.useLocalProxy) {
|
||||
// return _geocodeViaLocalProxy(lat, lon);
|
||||
// }
|
||||
|
||||
// // Fallback: Koordinaten zurückgeben
|
||||
// return 'Lat: ${lat.toStringAsFixed(6)}, Lon: ${lon.toStringAsFixed(6)}';
|
||||
// }
|
||||
|
||||
// // Geocoding über lokalen Reverse Proxy
|
||||
// Future<String> _geocodeViaLocalProxy(double lat, double lon) async {
|
||||
// try {
|
||||
// final proxyUrl = '${Environment.localProxyUrl}/?lat=$lat&lon=$lon&apiKey=${Environment.ptvApiKey}';
|
||||
|
||||
// print('🔄 Verwende lokalen Proxy: ${Environment.localProxyUrl}');
|
||||
|
||||
// final response = await http.get(Uri.parse(proxyUrl));
|
||||
|
||||
// if (response.statusCode == 200) {
|
||||
// final data = jsonDecode(response.body);
|
||||
|
||||
// if (data['success'] == true) {
|
||||
// final location = data['location'] as String;
|
||||
// print('✅ Geocoding erfolgreich (Proxy): $location');
|
||||
// return location;
|
||||
// } else {
|
||||
// print('❌ Geocoding fehlgeschlagen (Proxy): ${data['error']}');
|
||||
// return data['fallbackLocation'] ??
|
||||
// 'Lat: ${lat.toStringAsFixed(6)}, Lon: ${lon.toStringAsFixed(6)}';
|
||||
// }
|
||||
// } else {
|
||||
// print('⚠️ Proxy Response Status: ${response.statusCode}');
|
||||
// return 'Lat: ${lat.toStringAsFixed(6)}, Lon: ${lon.toStringAsFixed(6)}';
|
||||
// }
|
||||
// } catch (e) {
|
||||
// print('❌ Lokaler Proxy nicht erreichbar: $e');
|
||||
// print('💡 Tipp: Starten Sie den Proxy mit: cd proxy-server && node server.js');
|
||||
// return 'Lat: ${lat.toStringAsFixed(6)}, Lon: ${lon.toStringAsFixed(6)}';
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
import '../config/environment.dart';
|
||||
import '../models/locationiq_model.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class LocationIQService {
|
||||
static final String baseUrl = Environment.locationIQBaseUrl;
|
||||
late LocationIQ locationIQ;
|
||||
|
||||
Future<void> fetchLocationIQ(double lat, double lon) async {
|
||||
// Hier würde die eigentliche API-Anfrage an LocationIQ erfolgen
|
||||
// Zum Beispiel mit http.get() und der URL aus Environment.locationIQUrl
|
||||
// Die Antwort würde dann in das LocationIQ-Modell umgewandelt werden
|
||||
// https://eu1.locationiq.com/v1/reverse?key=$locationIQKey&lat=47.93875449671056&lon=13.762706553431048&format=json
|
||||
// locationIQ = LocationIQ.fromJson(responseData);
|
||||
// Simulierte Antwort (für Testzwecke)
|
||||
locationIQ = LocationIQ(
|
||||
placeId: '12345',
|
||||
|
||||
35
nginx.conf
35
nginx.conf
@@ -1,35 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Flutter web routing
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
# Dockerfile für Node.js Proxy Server
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies (omit dev; no lockfile required)
|
||||
RUN npm install --omit=dev
|
||||
|
||||
# Copy application code
|
||||
COPY server.js ./
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||||
|
||||
# Run as non-root user
|
||||
USER node
|
||||
|
||||
# Start server
|
||||
CMD ["node", "server.js"]
|
||||
@@ -1,115 +0,0 @@
|
||||
# Lokaler Reverse Proxy Server
|
||||
|
||||
Dieser Proxy-Server umgeht CORS-Probleme während der Entwicklung, indem er Anfragen an die PTV Geocoding API weiterleitet.
|
||||
|
||||
## 🚀 Installation & Start
|
||||
|
||||
### 1. Dependencies installieren (nur Node.js Standard-Module, keine Installation nötig)
|
||||
|
||||
Der Server verwendet nur Node.js Built-in Module (`http`, `https`, `url`), daher ist kein `npm install` erforderlich.
|
||||
|
||||
### 2. Server starten
|
||||
|
||||
```bash
|
||||
cd proxy-server
|
||||
node server.js
|
||||
```
|
||||
|
||||
Oder mit npm:
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
Der Server läuft dann auf `http://localhost:3000`
|
||||
|
||||
## 📡 API Verwendung
|
||||
|
||||
**Endpoint:**
|
||||
```
|
||||
GET http://localhost:3000/?lat={latitude}&lon={longitude}&apiKey={ptv_api_key}
|
||||
```
|
||||
|
||||
**Beispiel:**
|
||||
```
|
||||
http://localhost:3000/?lat=47.9385165&lon=13.762887&apiKey=YOUR_API_KEY
|
||||
```
|
||||
|
||||
**Response (Erfolg):**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"location": "Hauptstraße 123, 5020 Salzburg",
|
||||
"coordinates": {
|
||||
"lat": 47.9385165,
|
||||
"lon": 13.762887
|
||||
},
|
||||
"rawData": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Fehler):**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Error message",
|
||||
"fallbackLocation": "Lat: 47.938517, Lon: 13.762887"
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 In Flutter App verwenden
|
||||
|
||||
Der Flutter Code wurde bereits angepasst, um den lokalen Proxy zu verwenden.
|
||||
|
||||
1. Starten Sie den Proxy-Server:
|
||||
```bash
|
||||
cd proxy-server
|
||||
node server.js
|
||||
```
|
||||
|
||||
2. Starten Sie die Flutter App:
|
||||
```bash
|
||||
flutter run -d chrome
|
||||
```
|
||||
|
||||
3. Die App verwendet automatisch `http://localhost:3000` wenn verfügbar.
|
||||
|
||||
## ⚠️ Wichtige Hinweise
|
||||
|
||||
- **Nur für Entwicklung!** Dieser Server ist nicht für Produktionsumgebungen geeignet.
|
||||
- Der Server muss laufen, während Sie die Flutter App im Development-Modus verwenden.
|
||||
- Für Produktion: Verwenden Sie die Appwrite Function (siehe [DEPLOYMENT.md](../DEPLOYMENT.md))
|
||||
|
||||
## 🛑 Server stoppen
|
||||
|
||||
Drücken Sie `Strg+C` im Terminal, wo der Server läuft.
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Port bereits belegt
|
||||
**Problem:** `Error: listen EADDRINUSE: address already in use :::3000`
|
||||
|
||||
**Lösung:**
|
||||
1. Anderen Port verwenden: Ändern Sie `PORT = 3000` in `server.js`
|
||||
2. Oder den bestehenden Prozess beenden:
|
||||
```bash
|
||||
# Port finden
|
||||
lsof -i :3000
|
||||
# Prozess beenden
|
||||
kill -9 PID
|
||||
```
|
||||
|
||||
### Flutter App findet Proxy nicht
|
||||
**Problem:** "Failed to fetch" oder Connection Error
|
||||
|
||||
**Lösung:**
|
||||
1. Prüfen Sie ob der Proxy läuft (`http://localhost:3000` im Browser öffnen)
|
||||
2. Stellen Sie sicher, dass Port 3000 korrekt ist
|
||||
3. Prüfen Sie die Browser Console für Fehler
|
||||
|
||||
### CORS immer noch ein Problem
|
||||
**Problem:** CORS-Fehler trotz Proxy
|
||||
|
||||
**Lösung:**
|
||||
1. Stellen Sie sicher, dass die Flutter App `localhost:3000` verwendet
|
||||
2. Browser-Cache leeren
|
||||
3. Entwickler-Tools → Network → "Disable cache" aktivieren
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "ptv-proxy-server",
|
||||
"version": "1.0.0",
|
||||
"description": "Local reverse proxy for PTV Geocoding API to bypass CORS",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "node server.js"
|
||||
},
|
||||
"keywords": [
|
||||
"proxy",
|
||||
"cors",
|
||||
"ptv",
|
||||
"geocoding"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/**
|
||||
* Lokaler Reverse Proxy für PTV Geocoding API
|
||||
* Umgeht CORS-Probleme bei der Entwicklung
|
||||
*/
|
||||
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const url = require('url');
|
||||
|
||||
const PORT = 3000;
|
||||
const PTV_API_BASE = 'https://api.myptv.com';
|
||||
|
||||
// CORS Headers für alle Responses
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
console.log(`📨 ${req.method} ${req.url}`);
|
||||
|
||||
// Health check endpoint
|
||||
if (req.url === '/health') {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end('healthy');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle preflight requests
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(200, corsHeaders);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Nur GET Requests erlauben
|
||||
if (req.method !== 'GET') {
|
||||
res.writeHead(405, corsHeaders);
|
||||
res.end(JSON.stringify({ error: 'Method not allowed' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse URL
|
||||
const parsedUrl = url.parse(req.url, true);
|
||||
const { lat, lon, apiKey } = parsedUrl.query;
|
||||
|
||||
// Validierung
|
||||
if (!lat || !lon || !apiKey) {
|
||||
res.writeHead(400, corsHeaders);
|
||||
res.end(JSON.stringify({
|
||||
error: 'Missing parameters: lat, lon, apiKey are required',
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// PTV API URL erstellen
|
||||
const ptvUrl = `${PTV_API_BASE}/geocoding/v1/locations/by-position/${lat}/${lon}?language=de&apiKey=${apiKey}`;
|
||||
|
||||
console.log(`🔄 Weiterleitung an: ${ptvUrl.replace(apiKey, 'API_KEY_HIDDEN')}`);
|
||||
|
||||
// Request an PTV API
|
||||
https.get(ptvUrl, (ptvRes) => {
|
||||
let data = '';
|
||||
|
||||
ptvRes.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
ptvRes.on('end', () => {
|
||||
try {
|
||||
const jsonData = JSON.parse(data);
|
||||
|
||||
// Erfolgreiche Response
|
||||
if (ptvRes.statusCode === 200) {
|
||||
console.log('✅ PTV API Response erfolgreich');
|
||||
|
||||
// Adresse extrahieren
|
||||
if (jsonData.locations && jsonData.locations.length > 0) {
|
||||
const location = jsonData.locations[0];
|
||||
if (location.address) {
|
||||
const address = location.address;
|
||||
const street = address.street || '';
|
||||
const houseNumber = address.houseNumber || '';
|
||||
const postalCode = address.postalCode || '';
|
||||
const city = address.city || '';
|
||||
|
||||
const formattedAddress = `${street} ${houseNumber}, ${postalCode} ${city}`.trim();
|
||||
|
||||
const response = {
|
||||
success: true,
|
||||
location: formattedAddress,
|
||||
coordinates: {
|
||||
lat: parseFloat(lat),
|
||||
lon: parseFloat(lon),
|
||||
},
|
||||
rawData: location,
|
||||
};
|
||||
|
||||
console.log(`📍 Adresse: ${formattedAddress}`);
|
||||
res.writeHead(200, corsHeaders);
|
||||
res.end(JSON.stringify(response));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback auf Koordinaten
|
||||
const response = {
|
||||
success: true,
|
||||
location: `Lat: ${parseFloat(lat).toFixed(6)}, Lon: ${parseFloat(lon).toFixed(6)}`,
|
||||
coordinates: {
|
||||
lat: parseFloat(lat),
|
||||
lon: parseFloat(lon),
|
||||
},
|
||||
};
|
||||
|
||||
res.writeHead(200, corsHeaders);
|
||||
res.end(JSON.stringify(response));
|
||||
} else {
|
||||
// Fehler von PTV API
|
||||
console.log(`❌ PTV API Fehler: ${ptvRes.statusCode}`);
|
||||
res.writeHead(ptvRes.statusCode, corsHeaders);
|
||||
res.end(JSON.stringify({
|
||||
success: false,
|
||||
error: `PTV API Error: ${ptvRes.statusCode}`,
|
||||
fallbackLocation: `Lat: ${parseFloat(lat).toFixed(6)}, Lon: ${parseFloat(lon).toFixed(6)}`,
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ JSON Parse Fehler:', error.message);
|
||||
res.writeHead(500, corsHeaders);
|
||||
res.end(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Failed to parse PTV API response',
|
||||
fallbackLocation: `Lat: ${parseFloat(lat).toFixed(6)}, Lon: ${parseFloat(lon).toFixed(6)}`,
|
||||
}));
|
||||
}
|
||||
});
|
||||
}).on('error', (error) => {
|
||||
console.error('❌ Request Fehler:', error.message);
|
||||
res.writeHead(500, corsHeaders);
|
||||
res.end(JSON.stringify({
|
||||
success: false,
|
||||
error: error.message,
|
||||
fallbackLocation: `Lat: ${parseFloat(lat).toFixed(6)}, Lon: ${parseFloat(lon).toFixed(6)}`,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log('═══════════════════════════════════════════════════════');
|
||||
console.log('🚀 Lokaler Reverse Proxy Server gestartet!');
|
||||
console.log(`📡 Läuft auf: http://localhost:${PORT}`);
|
||||
console.log('');
|
||||
console.log('📌 Verwendung:');
|
||||
console.log(` http://localhost:${PORT}/?lat=47.9385&lon=13.7629&apiKey=YOUR_KEY`);
|
||||
console.log('');
|
||||
console.log('⚠️ Hinweis: Nur für Entwicklung verwenden!');
|
||||
console.log(' Für Produktion: Appwrite Function deployen');
|
||||
console.log('═══════════════════════════════════════════════════════');
|
||||
console.log('');
|
||||
console.log('💡 Zum Beenden: Strg+C drücken');
|
||||
console.log('');
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n\n👋 Server wird beendet...');
|
||||
server.close(() => {
|
||||
console.log('✅ Server erfolgreich gestoppt');
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Farben für Output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} Flutter Tank App - Lokaler Proxy Starter${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
# Proxy Server starten
|
||||
echo -e "${YELLOW}🚀 Starte lokalen Reverse Proxy Server...${NC}"
|
||||
cd "$(dirname "$0")/proxy-server"
|
||||
|
||||
if [ ! -f "server.js" ]; then
|
||||
echo -e "${YELLOW}❌ server.js nicht gefunden!${NC}"
|
||||
echo -e "${YELLOW}Bitte führen Sie dieses Script aus dem Projekt-Root-Verzeichnis aus.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Proxy Server wird gestartet auf http://localhost:3000${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}📌 Nächste Schritte:${NC}"
|
||||
echo -e " 1. In neuem Terminal: ${GREEN}flutter run -d chrome${NC}"
|
||||
echo -e " 2. Neuen Tankeintrag erstellen"
|
||||
echo -e " 3. Standort wird automatisch über Proxy abgerufen"
|
||||
echo ""
|
||||
echo -e "${YELLOW}⚠️ Zum Beenden: Strg+C drücken${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
node server.js
|
||||
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"3452d735bd38224ef2db85ca763d862d6326b1
|
||||
|
||||
_flutter.loader.load({
|
||||
serviceWorkerSettings: {
|
||||
serviceWorkerVersion: "138245282" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
||||
serviceWorkerVersion: "38490492" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user