fertig bis auf Tankstellen und Graph
This commit is contained in:
26
proxy-server/Dockerfile
Normal file
26
proxy-server/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# Dockerfile für Node.js Proxy Server
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies (only production for smaller image)
|
||||
RUN npm ci --only=production
|
||||
|
||||
# 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"]
|
||||
115
proxy-server/README.md
Normal file
115
proxy-server/README.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# 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
|
||||
21
proxy-server/package.json
Normal file
21
proxy-server/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
174
proxy-server/server.js
Normal file
174
proxy-server/server.js
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user