require('dotenv').config(); const express = require('express'); const { Pool } = require('pg'); const cors = require('cors'); const app = express(); const port = process.env.PORT || 80; // Database Connection const pool = new Pool({ host: process.env.DB_HOST, port: process.env.DB_PORT, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }); pool.on('error', (err) => { console.error('Unexpected error on idle client', err); }); // Middleware app.use(cors()); app.use(express.json({ limit: '50mb' })); app.use(express.static(__dirname)); // Helper to run the schema isolation command async function setSchema(client) { const schema = process.env.DB_SCHEMA || 'wind_projekt_bwscheddebrock'; await client.query(`SET search_path TO "${schema}", public;`); } // Database Initial Setup / Migration Logic Removed // (Now treating existing tables as the read-only 'Source of Truth') // --- API Routes --- app.get('/api/health', (req, res) => { res.json({ status: 'OK', schema: process.env.DB_SCHEMA, time: new Date().toISOString() }); }); // 1. Get Owner Data app.get('/api/owners', async (req, res) => { const client = await pool.connect(); try { await setSchema(client); // Strict Case-Sensitivity Mapping based on server structure const query = ` SELECT id, nachname AS "Nachname", vorname AS "Vorname", ort AS "Ort", "Flur" AS "Flur", "FlSt" AS "Flurstueck", "Gema" AS "Gemarkung", "PLZ" AS "PLZ", "Land" AS "Land", "AFlaeche" AS "AFlaeche", status as "status", notiz as "notiz", ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry FROM eigentuemerdaten `; const result = await client.query(query); const geojson = { type: "FeatureCollection", features: result.rows.map(row => { const { geometry, ...properties } = row; return { type: "Feature", id: row.id, geometry: JSON.parse(geometry), properties: properties }; }) }; res.json(geojson); } catch (err) { console.error("Owner Fetch Error:", err); res.status(500).json({ error: 'Failed to fetch owner data' }); } finally { client.release(); } }); // 2. Get Usage Data (Nutzung) app.get('/api/usage', async (req, res) => { const client = await pool.connect(); try { await setSchema(client); const query = ` SELECT id, "nutzart" as "nutzart", ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry FROM nutzung `; const result = await client.query(query); const geojson = { type: "FeatureCollection", features: result.rows.map(row => { const { geometry, ...properties } = row; return { type: "Feature", geometry: JSON.parse(geometry), properties: properties }; }) }; res.json(geojson); } catch (err) { console.error("Usage Fetch Error:", err); res.status(500).json({ error: 'Failed to fetch usage data' }); } finally { client.release(); } }); // 2.5 Get WEA Data app.get('/api/wea', async (req, res) => { const client = await pool.connect(); try { await setSchema(client); const query = ` SELECT id, "Name" AS "Name", ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry FROM wea `; const result = await client.query(query); const geojson = { type: "FeatureCollection", features: result.rows.map(row => { const { geometry, ...properties } = row; return { type: "Feature", geometry: JSON.parse(geometry), properties: properties }; }) }; res.json(geojson); } catch (err) { res.json({ type: "FeatureCollection", features: [] }); } finally { client.release(); } }); // 2.7 Get Infrastructure Data app.get('/api/infrastructure', async (req, res) => { const client = await pool.connect(); try { await setSchema(client); const query = ` SELECT id, ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry FROM infrastruktur `; const result = await client.query(query); const geojson = { type: "FeatureCollection", features: result.rows.map(row => { const { geometry, ...properties } = row; return { type: "Feature", geometry: JSON.parse(geometry), properties: properties }; }) }; res.json(geojson); } catch (err) { res.json({ type: "FeatureCollection", features: [] }); } finally { client.release(); } }); // 3. Get Variants (Kabeltrasse) app.get('/api/variants', async (req, res) => { const client = await pool.connect(); try { await setSchema(client); const query = ` SELECT id, name, "Variante" AS "Variante", ST_AsGeoJSON(ST_Transform(ST_SetSRID(geom, 25832), 4326)) as geometry FROM kabeltrasse ORDER BY id DESC `; const result = await client.query(query); const featureCollection = { type: "FeatureCollection", features: result.rows.map(row => { return { type: "Feature", id: row.id, geometry: JSON.parse(row.geometry || 'null'), properties: { id: row.id, name: row.name || (row.Variante ? `Variante ${row.Variante}` : 'Neue Trasse'), Variante: row.Variante } }; }) }; res.json(featureCollection); } catch (err) { console.error("Variants Fetch Error:", err); res.status(500).json({ error: 'Failed to fetch variants' }); } finally { client.release(); } }); // 4. Save/Update Variant app.post('/api/variants', async (req, res) => { const { geometry, properties } = req.body; const client = await pool.connect(); try { await setSchema(client); const routeName = properties.name || 'Neue Trasse'; const varianteValue = properties.Variante || (properties.name ? properties.name.replace('Variante ', '') : 'A'); const geoJsonStr = JSON.stringify(geometry); const upsertQuery = ` INSERT INTO kabeltrasse (geom, name, "Variante") VALUES ( ST_MakeValid(ST_Transform(ST_SetSRID(ST_GeomFromGeoJSON($1), 4326), 25832)), $2, $3 ) ON CONFLICT ("Variante") DO UPDATE SET geom = EXCLUDED.geom, name = EXCLUDED.name RETURNING id `; const upsertRes = await client.query(upsertQuery, [geoJsonStr, routeName, varianteValue]); res.json({ success: true, id: upsertRes.rows[0].id }); } catch (err) { console.error("SQL-FEHLER (SAVE):", err.message); res.status(500).json({ error: 'Failed to save variant' }); } finally { client.release(); } }); // 5. Update Owner Note/Status app.patch('/api/owners/:id', async (req, res) => { const { id } = req.params; const { status, notiz } = req.body; const client = await pool.connect(); try { await setSchema(client); const query = ` UPDATE eigentuemerdaten SET status = $1, notiz = $2 WHERE id = $3 RETURNING id; `; const result = await client.query(query, [status, notiz, id]); if (result.rowCount === 0) return res.status(404).json({ error: 'Owner not found' }); res.json({ success: true, id: result.rows[0].id }); } catch (err) { console.error("Update Owner Error:", err); res.status(500).json({ error: 'Failed to update owner' }); } finally { client.release(); } }); app.listen(port, () => { console.log(`Server running at http://localhost:${port}`); });