server.js aktualisiert
Deploy-TrassenPlaner / deploy (push) Successful in 12s
Details
Deploy-TrassenPlaner / deploy (push) Successful in 12s
Details
This commit is contained in:
parent
041568edcb
commit
b4c7333fa1
343
server.js
343
server.js
|
|
@ -1,6 +1,341 @@
|
|||
// Schema-Helper
|
||||
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
|
||||
// This ensures all subsequent queries in this client session use the specified schema
|
||||
async function setSchema(client) {
|
||||
await client.query(`SET search_path TO wind_projekt_bwscheddebrock, public;`);
|
||||
const schema = process.env.DB_SCHEMA || 'wind_projekt_bwscheddebrock';
|
||||
await client.query(`SET search_path TO "${schema}", public;`);
|
||||
}
|
||||
// Beispiel-Query:
|
||||
await client.query('ALTER TABLE wind_projekt_bwscheddebrock."Kabeltrasse" ALTER COLUMN id_0 TYPE BIGINT');
|
||||
|
||||
// Database Initial Setup / Migration
|
||||
const initDBData = async () => {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await setSchema(client);
|
||||
console.log(`Prüfe Datenbank-Schema (Schema: ${process.env.DB_SCHEMA})...`);
|
||||
|
||||
// Ensure id_0 is BIGINT
|
||||
await client.query('ALTER TABLE "Kabeltrasse" ALTER COLUMN id_0 TYPE BIGINT');
|
||||
|
||||
// Enforce SRID 25832 and LineString type
|
||||
try {
|
||||
await client.query('ALTER TABLE "Kabeltrasse" ALTER COLUMN geom TYPE geometry(LineString, 25832) USING ST_Transform(ST_GeomFromGeoJSON(ST_AsGeoJSON(geom)), 25832)');
|
||||
} catch(e) {
|
||||
try {
|
||||
await client.query('ALTER TABLE "Kabeltrasse" ALTER COLUMN geom TYPE geometry(LineString, 25832) USING ST_SetSRID(geom, 25832)');
|
||||
} catch(e2) {
|
||||
console.warn("SRID Enforcement skipped:", e2.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-clean duplicates
|
||||
try {
|
||||
await client.query(`
|
||||
DELETE FROM "Kabeltrasse" a
|
||||
USING "Kabeltrasse" b
|
||||
WHERE a.id_0 < b.id_0 AND a."Variante" = b."Variante"
|
||||
`);
|
||||
} catch(e) { console.warn("Cleanup warning:", e.message); }
|
||||
|
||||
// Ensure Variante is unique
|
||||
try {
|
||||
await client.query('ALTER TABLE "Kabeltrasse" ADD CONSTRAINT variant_unique UNIQUE ("Variante")');
|
||||
} catch(e) { /* ignore if already exists */ }
|
||||
|
||||
console.log("Migration abgeschlossen.");
|
||||
} catch (err) {
|
||||
console.error("Migration fehlgeschlagen:", err.message);
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
initDBData();
|
||||
|
||||
// --- 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 countRes = await client.query('SELECT count(*) FROM "Eigentuemerdaten"');
|
||||
console.log(`Datenbank-Check: ${countRes.rows[0].count} Einträge in "Eigentuemerdaten".`);
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
*,
|
||||
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, 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);
|
||||
const countRes = await client.query('SELECT count(*) FROM "Nutzung"');
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
*,
|
||||
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, 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
|
||||
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 "WEA"
|
||||
`);
|
||||
} catch (dbErr) {
|
||||
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
|
||||
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 "Infrastruktur"
|
||||
`);
|
||||
} catch (dbErr) {
|
||||
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);
|
||||
const countRes = await client.query('SELECT count(*) FROM "Kabeltrasse"');
|
||||
|
||||
const query = `
|
||||
SELECT
|
||||
id_0 as id, name, "Variante",
|
||||
ST_AsGeoJSON(ST_Transform(ST_SetSRID(geom, 25832), 4326)) as geometry
|
||||
FROM "Kabeltrasse"
|
||||
ORDER BY id_0 DESC
|
||||
`;
|
||||
const result = await client.query(query);
|
||||
|
||||
const featureCollection = {
|
||||
type: "FeatureCollection",
|
||||
features: result.rows.map(row => {
|
||||
const geomObj = JSON.parse(row.geometry || 'null');
|
||||
return {
|
||||
type: "Feature",
|
||||
id: row.id,
|
||||
geometry: geomObj,
|
||||
properties: {
|
||||
id: row.id,
|
||||
name: row.name || (row.Variante ? `Variante ${row.Variante}` : 'Neue Trasse'),
|
||||
Variante: row.Variante
|
||||
}
|
||||
};
|
||||
})
|
||||
};
|
||||
res.json(featureCollection);
|
||||
} 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) => {
|
||||
const { geometry, properties } = req.body;
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await setSchema(client);
|
||||
|
||||
const routeName = properties.name || 'Neue Trasse';
|
||||
const variante = 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_0
|
||||
`;
|
||||
|
||||
const upsertRes = await client.query(upsertQuery, [geoJsonStr, routeName, variante]);
|
||||
const finalId = upsertRes.rows[0].id_0;
|
||||
|
||||
res.json({ success: true, id: finalId });
|
||||
} 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(err);
|
||||
res.status(500).json({ error: 'Failed to update owner' });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server running at http://localhost:${port}`);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue