bwscheddebrock_trassenplaner/server.js

327 lines
10 KiB
JavaScript

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) {
await client.query(`SET search_path TO ${process.env.DB_SCHEMA}, public;`);
}
// Routes
app.get('/api/health', (req, res) => {
res.json({ status: 'OK', time: new Date().toISOString() });
});
// 1. Get Owner Data
app.get('/api/owners', async (req, res) => {
console.log("Anfrage erhalten: /api/owners");
const client = await pool.connect();
try {
await setSchema(client);
// 1. Log actual count in DB
const countRes = await client.query('SELECT count(*) FROM bw_scheddebrock."Eigentuemerdaten"');
console.log(`Datenbank-Check: ${countRes.rows[0].count} Eigentümer in bw_scheddebrock."Eigentuemerdaten" gefunden.`);
const query = `
SELECT
*,
ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry
FROM bw_scheddebrock."Eigentuemerdaten"
`;
const result = await client.query(query);
console.log(`Abfrage erfolgreich: ${result.rowCount} Zeilen für Frontend geladen.`);
const geojson = {
type: "FeatureCollection",
features: result.rows.map(row => {
const { geometry, geom, ...properties } = row;
const geomObj = typeof geometry === 'string' ? JSON.parse(geometry) : geometry;
return {
type: "Feature",
id: row.id || row.id_0,
geometry: geomObj,
properties: properties
};
})
};
res.json(geojson);
} catch (err) {
console.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);
// 1. Log actual count in DB
const countRes = await client.query('SELECT count(*) FROM bw_scheddebrock."Nutzung"');
console.log(`Datenbank-Check: ${countRes.rows[0].count} Nutzungs-Flächen in bw_scheddebrock."Nutzung" gefunden.`);
const query = `
SELECT
*,
ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry
FROM bw_scheddebrock."Nutzung"
`;
const result = await client.query(query);
console.log(`Abfrage erfolgreich: ${result.rowCount} Zeilen für Frontend geladen.`);
const geojson = {
type: "FeatureCollection",
features: result.rows.map(row => {
const { geometry, geom, ...properties } = row;
const geomObj = typeof geometry === 'string' ? JSON.parse(geometry) : geometry;
return {
type: "Feature",
geometry: geomObj,
properties: properties
};
})
};
res.json(geojson);
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Failed to fetch usage data' });
} finally {
client.release();
}
});
// 2.5 Get WEA Data (Windturbinen)
app.get('/api/wea', async (req, res) => {
const client = await pool.connect();
try {
await setSchema(client);
let result;
try {
result = await client.query(`
SELECT
*,
ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry
FROM bw_scheddebrock."WEA"
`);
} catch (dbErr) {
console.log("WEA Tabelle fehlt oder nicht abrufbar.");
result = { rows: [], rowCount: 0 };
}
const geojson = {
type: "FeatureCollection",
features: result.rows.map(row => {
const { geometry, geom, ...properties } = row;
const geomObj = typeof geometry === 'string' ? JSON.parse(geometry) : geometry;
return {
type: "Feature",
geometry: geomObj,
properties: properties
};
})
};
res.json(geojson);
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Failed to fetch WEA data' });
} finally {
client.release();
}
});
// 2.7 Get Infrastructure Data (UW, etc)
app.get('/api/infrastructure', async (req, res) => {
const client = await pool.connect();
try {
await setSchema(client);
let result;
try {
result = await client.query(`
SELECT
*,
ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry
FROM bw_scheddebrock."Infrastruktur"
`);
} catch (dbErr) {
console.log("Infrastruktur Tabelle fehlt oder nicht abrufbar.");
result = { rows: [], rowCount: 0 };
}
const geojson = {
type: "FeatureCollection",
features: result.rows.map(row => {
const { geometry, geom, ...properties } = row;
const geomObj = typeof geometry === 'string' ? JSON.parse(geometry) : (geometry || null);
return {
type: "Feature",
geometry: geomObj,
properties: properties
};
})
};
res.json(geojson);
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Failed to fetch infrastructure data' });
} finally {
client.release();
}
});
// 3. Get Variants (Kabeltrasse)
app.get('/api/variants', async (req, res) => {
const client = await pool.connect();
try {
await setSchema(client);
// Log count
const countRes = await client.query('SELECT count(*) FROM bw_scheddebrock."Kabeltrasse"');
console.log(`Datenbank-Check: ${countRes.rows[0].count} Kabeltrassen in bw_scheddebrock."Kabeltrasse" gefunden.`);
const query = `
SELECT
id_0 as id, name, "Variante",
ST_AsGeoJSON(ST_Transform(ST_SetSRID(geom, 25832), 4326)) as geometry
FROM bw_scheddebrock."Kabeltrasse"
ORDER BY id_0 DESC
`;
const result = await client.query(query);
console.log(`Abfrage erfolgreich: ${result.rowCount} Trassen geladen.`);
const variants = result.rows.map(row => {
let routes = [];
const geomObj = typeof row.geometry === 'string' ? JSON.parse(row.geometry) : row.geometry;
if (geomObj && geomObj.coordinates) {
if (geomObj.type === 'MultiLineString') {
routes = geomObj.coordinates.map(line => line.map(c => ({ lat: c[1], lng: c[0] })));
} else if (geomObj.type === 'LineString') {
routes = geomObj.coordinates.map(c => ({ lat: c[1], lng: c[0] }));
}
}
return {
id: row.id,
name: row.name || row.Variante || 'Trasse',
active: false,
visible: true,
stats: { total: 0, drilling: 0, open: 0, muffen: 0 },
routes: routes
};
});
res.json(variants);
} catch (err) {
console.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) => {
console.log("Empfangene Daten (Payload):", JSON.stringify(req.body).substring(0, 200) + "...");
const { geometry, properties } = req.body;
const client = await pool.connect();
try {
await setSchema(client);
console.log("Starte PostgreSQL-Query...");
// Clean UPSERT by Database ID.
const routeId = req.body.id;
const routeName = properties.name || 'Neue Trasse';
const variante = properties.Variante || properties.name || 'A';
const geoJsonStr = JSON.stringify(geometry);
// Check if the passed ID is a valid database ID (small int) vs a frontend dummy Date.now() timestamp
// Or try to match it initially just in case it's a known database row.
const existing = await client.query('SELECT id_0 FROM bw_scheddebrock."Kabeltrasse" WHERE id_0 = $1', [Number(routeId) || 0]);
let result;
if (existing.rowCount > 0) {
// EXACT match found physically in DB, do an UPDATE
const updateQuery = `
UPDATE bw_scheddebrock."Kabeltrasse"
SET geom = ST_MakeValid(ST_SetSRID(ST_Transform(ST_SetSRID(ST_GeomFromGeoJSON($1), 4326), 25832), 25832)),
name = $2,
"Variante" = $3
WHERE id_0 = $4
RETURNING id_0 as id;
`;
result = await client.query(updateQuery, [geoJsonStr, routeName, variante, existing.rows[0].id_0]);
console.log(`PostgreSQL-Ergebnis: Zeile ${existing.rows[0].id_0} aktualisiert (Variante: ${routeName})!`);
} else {
// INSERT new
const insertQuery = `
INSERT INTO bw_scheddebrock."Kabeltrasse" (geom, name, "Variante")
VALUES (ST_MakeValid(ST_SetSRID(ST_Transform(ST_SetSRID(ST_GeomFromGeoJSON($1), 4326), 25832), 25832)), $2, $3)
RETURNING id_0 as id;
`;
result = await client.query(insertQuery, [geoJsonStr, routeName, variante]);
console.log(`PostgreSQL-Ergebnis: Zeile eingefügt! ID: ${result.rows[0].id}`);
}
res.json({ success: true, id: result.rows[0].id });
} catch (err) {
console.error("KRITISCHER SQL-FEHLER:", err.message);
res.status(500).json({ error: 'Failed to save variant: ' + err.message });
} 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 bw_scheddebrock."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 in database' });
}
res.json({ success: true, id: result.rows[0].id });
} catch (err) {
console.error("Owner Update 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}`);
});