const express = require("express"); const { Pool } = require("pg"); const os = require("os"); const multer = require('multer'); const path = require('path'); const fs = require('fs'); const cors = require('cors'); const app = express(); const port = process.env.PORT || 3000; const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const JWT_SECRET = "supergeheimes_tg_cms_secret"; // Bodyparser für JSON aktivieren app.use(cors({ origin: ["http://localhost:8080", "http://192.168.50.65:8080"], methods: ["GET", "POST", "PUT", "DELETE"], credentials: true })); app.use(express.json()); app.use('uploads/', express.static('uploads')); // PostgreSQL Verbindung aufbauen const pool = new Pool({ user: "tgadmin", host: "db", // Docker Container Name! database: "tg-cms", password: "secretpass", port: 5432, }); // Testroute API-Check app.get("/api/hello", (req, res) => { res.send("Hello World from TG CMS API! 🚀"); }); // Testroute DB-Check app.get("/api/test-db", async (req, res) => { try { const result = await pool.query("SELECT NOW()"); res.json({ time: result.rows[0].now }); } catch (err) { console.error(err); res.status(500).send("Fehler bei DB-Verbindung"); } }); // Alle News abrufen app.get("/api/news", async (req, res) => { try { const result = await pool.query('SELECT * FROM news ORDER BY created_at DESC'); res.json(result.rows); } catch (err) { console.error(err); res.status(500).send("Fehler beim Abrufen der News"); } }); // Neue News anlegen app.post("/api/news", async (req, res) => { const { title, description, image_url, team } = req.body; if (!title || !description) { return res.status(400).send("Titel und Beschreibung sind Pflichtfelder"); } try { const result = await pool.query( 'INSERT INTO news (title, description, image_url, team) VALUES ($1, $2, $3, $4) RETURNING *', [title, description, image_url, team] ); res.status(201).json(result.rows[0]); } catch (err) { console.error(err); res.status(500).send("Fehler beim Anlegen der News"); } }); // News bearbeiten app.put("/api/news/:id", async (req, res) => { const { id } = req.params; const { title, description, image_url, team } = req.body; try { const result = await pool.query( 'UPDATE news SET title = $1, description = $2, image_url = $3, team = $4 WHERE id = $5 RETURNING *', [title, description, image_url, team, id] ); if (result.rowCount === 0) { return res.status(404).send("News-Eintrag nicht gefunden"); } res.json(result.rows[0]); } catch (err) { console.error(err); res.status(500).send("Fehler beim Aktualisieren der News"); } }); // News löschen app.delete("/api/news/:id", async (req, res) => { const { id } = req.params; try { const result = await pool.query( 'DELETE FROM news WHERE id = $1', [id] ); if (result.rowCount === 0) { return res.status(404).send("News-Eintrag nicht gefunden"); } res.send("News-Eintrag erfolgreich gelöscht"); } catch (err) { console.error(err); res.status(500).send("Fehler beim Löschen der News"); } }); // Neuen Benutzer anlegen app.post("/api/users", async (req, res) => { const { username, password, role, email } = req.body; if (!username || !password || !email) { return res.status(400).send("Username, Email & Passwort sind Pflicht"); } try { // Passwort hashen const hashedPassword = await bcrypt.hash(password, 10); // In DB speichern const result = await pool.query( 'INSERT INTO users (username, password, role, email) VALUES ($1, $2, $3, $4) RETURNING id, username, role, email, created_at', [username, hashedPassword, role ||'user', email] ); res.status(201).json(result.rows[0]); } catch (err) { console.error(err); res.status(500).send("Fehler beim Anlegen des Benutzers"); } }); // Benutzer Login app.post("/api/login", async (req, res) => { const { username, password } = req.body; if (!username || !password) { return res.status(400).send("Username und Passwort erforderlich"); } try { const result = await pool.query( 'SELECT * FROM users WHERE username = $1', [username] ); const user = result.rows[0]; if (!user) { return res.status(400).send("Benutzer existiert nicht"); } // Passwort prüfen const validPassword = await bcrypt.compare(password, user.password); if (!validPassword) { return res.status(401).send("Passwort falsch"); } await pool.query( 'UPDATE users SET last_logged = NOW() WHERE id = $1', [user.id] ); // JWT Token erzeugen const token = jwt.sign( { id: user.id, username: user.username, role: user.role }, JWT_SECRET, { expiresIn: "6h" } ); res.json({ token }); } catch (err) { console.error(err); res.status(500).send("Fehler beim Login"); } }); // Alle Benutzer abrufen app.get("/api/users", async (req, res) => { try { const result = await pool.query( 'SELECT id, username, role, email, created_at FROM users ORDER BY created_at DESC' ); res.json(result.rows); } catch (err) { console.error(err); res.status(500).send("Fehler beim Abrufen der Benutzer"); } }); // Benutzer lÃloeschen app.delete("/api/users/:id", async (req, res) => { const { id } = req.params; try { // Prüfen, ob der Benutzer existiert const checkResult = await pool.query('SELECT * FROM users WHERE id = $1', [id]); if (checkResult.rowCount === 0) { return res.status(404).send("Benutzer nicht gefunden"); } // Benutzer löschen await pool.query('DELETE FROM users WHERE id = $1', [id]); res.send("Benutzer erfolgreich gelöscht"); } catch (err) { console.error(err); res.status(500).send("Fehler beim Löschen des Benutzers"); } }); // Benutzer bearbeiten app.put("/api/users/:id", async (req, res) => { const { id } = req.params; const { username, password, role } = req.body; try { // Passwort optional neu setzen let query = 'UPDATE users SET username = $1, role = $2'; let params = [username, role, id]; if (password) { const hashedPassword = await bcrypt.hash(password, 10); query = 'UPDATE users SET username = $1, password = $2, role = $3 WHERE id = $4'; params = [username, hashedPassword, role, id]; } else { query += ' WHERE id = $3'; } const result = await pool.query(query, params); if (result.rowCount === 0) { return res.status(404).send("Benutzer nicht gefunden"); } res.send("Benutzer erfolgreich aktualisiert"); } catch (err) { console.error(err); res.status(500).send("Fehler beim Aktualisieren des Benutzers"); } }); // Speicherort definieren const storage = multer.diskStorage({ destination: (req, file, cb) => { const dir = "./uploads/news"; fs.mkdirSync(dir, { recursive: true }); cb(null, dir); }, filename: (req, file, cb) => { const ext = path.extname(file.originalname); const filename = Date.now() + ext; cb(null, filename); }, }); const upload = multer({ storage }); app.post("/api/upload-news-image", upload.single("image"), (req, res) => { if (!req.file) { return res.status(400).send("Kein Bild hochgeladen"); } const imageUrl = `/uploads/news/${req.file.filename}`; res.json({ imageUrl }); }); // Server starten app.listen(port, () => { const nets = os.networkInterfaces(); const addresses = []; for (const name of Object.keys(nets)) { for (const net of nets[name]) { if (net.family === 'IPv4' && !net.internal) { addresses.push(net.address); } } } console.log("Backend erreichbar unter:"); addresses.forEach(ip => { console.log(`http://${ip}:${port}`); }); });