294 lines
7.9 KiB
JavaScript
294 lines
7.9 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) {
|
|
const schema = process.env.DB_SCHEMA || 'wind_projekt_bwscheddebrock';
|
|
await client.query(`SET search_path TO "${schema}", public;`);
|
|
}
|
|
|
|
// --- 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);
|
|
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,
|
|
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",
|
|
bez AS "bez",
|
|
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 Error:", err);
|
|
res.json({ type: "FeatureCollection", features: [] });
|
|
} 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,
|
|
"WEA" AS "Name",
|
|
"GH" AS "GH",
|
|
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) {
|
|
console.error("WEA Error:", 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",
|
|
ST_AsGeoJSON(ST_Transform(geom, 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) {
|
|
res.json({ type: "FeatureCollection", features: [] });
|
|
} 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);
|
|
|
|
// Safety Check: DON'T delete/save if geometry is empty or invalid
|
|
if (!geometry || !geometry.coordinates || geometry.coordinates.length < 2) {
|
|
console.log(`Aborting save for ${varianteValue}: Geometry is too short or empty.`);
|
|
return res.json({ success: true, message: 'Skipped empty geometry' });
|
|
}
|
|
|
|
try {
|
|
await client.query('BEGIN');
|
|
|
|
// Delete existing variant if it exists
|
|
await client.query('DELETE FROM kabeltrasse WHERE "Variante" = $1 OR name = $2', [varianteValue, routeName]);
|
|
|
|
const insertQuery = `
|
|
INSERT INTO kabeltrasse (geom, name, "Variante")
|
|
VALUES (
|
|
ST_Multi(ST_MakeValid(ST_Transform(ST_SetSRID(ST_GeomFromGeoJSON($1), 4326), 25832))),
|
|
$2,
|
|
$3
|
|
)
|
|
RETURNING id
|
|
`;
|
|
|
|
const insertRes = await client.query(insertQuery, [geoJsonStr, routeName, varianteValue]);
|
|
await client.query('COMMIT');
|
|
|
|
res.json({ success: true, id: insertRes.rows[0].id });
|
|
} catch (sqlErr) {
|
|
await client.query('ROLLBACK');
|
|
throw sqlErr;
|
|
}
|
|
} 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]);
|
|
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(3000, () => {
|
|
console.log(`Server running at http://localhost:3000`);
|
|
});
|