volleyball-dev-backend/index.js
2025-04-29 10:04:42 +02:00

654 lines
17 KiB
JavaScript
Raw Blame History

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(path.join(__dirname, '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 newsStorage = 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 playerStorage = multer.diskStorage({
destination: (req, file, cb) => {
const dir = "./uploads/players";
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 uploadNewsImage = multer({ storage: newsStorage });
const uploadPlayerImage = multer({ storage: playerStorage });
//Neues Bild zu den News hinzufügen
app.post("/api/upload-news-image", uploadNewsImage.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 });
});
//Teams abfragen
app.get("/api/teams", async (req, res) => {
try {
const result = await pool.query(`
SELECT
t.id,
t.name,
t.liga,
t.spieler_gesucht,
t.social_media,
t.trainer,
t.carousel_images,
COUNT(pt.player_id) AS player_count
FROM teams t
LEFT JOIN player_teams pt ON pt.team_id = t.id
GROUP BY t.id, t.liga, t.spieler_gesucht, t.social_media, t.trainer, t.carousel_images
ORDER BY t.name ASC
`);
res.json(result.rows);
} catch (err) {
console.error("Fehler beim Abrufen der Teams:", err);
res.status(500).send("Fehler beim Abrufen der Teams");
}
});
//Update Team
app.put("/api/teams/:id", async (req, res) => {
const { name, liga, spieler_gesucht, social_media, trainer, carousel_images } = req.body;
const { id } = req.params;
try {
const result = await pool.query(
`UPDATE teams
SET name = $1, liga = $2, spieler_gesucht = $3, social_media = $4, trainer = $5, carousel_images = $6
WHERE id = $7 RETURNING *`,
[name, liga, spieler_gesucht, social_media, trainer, carousel_images, id]
);
if (result.rowCount === 0) return res.status(404).send("Team nicht gefunden");
res.json(result.rows[0]);
} catch (err) {
console.error("Fehler beim Aktualisieren des Teams:", err);
res.status(500).send("Fehler beim Aktualisieren des Teams");
}
});
//Teams anlegen
app.post("/api/teams", async (req, res) => {
const { name, liga, spieler_gesucht, social_media, trainer, carousel_images } = req.body;
if (!name) return res.status(400).send("Teamname ist erforderlich");
try {
const result = await pool.query(
`INSERT INTO teams (name, liga, spieler_gesucht, social_media, trainer, carousel_images)
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
[name, liga, spieler_gesucht, social_media, trainer, carousel_images]
);
res.status(201).json(result.rows[0]);
} catch (err) {
if (err.code === "23505") {
return res.status(400).send("Ein Team mit diesem Namen existiert bereits");
}
console.error("Fehler beim Anlegen des Teams:", err);
res.status(500).send("Fehler beim Anlegen des Teams");
}
});
//Team loeschen
app.delete("/api/teams/:id", async (req, res) => {
const { id } = req.params;
try {
const result = await pool.query("DELETE FROM teams WHERE id = $1", [id]);
if (result.rowCount === 0) {
return res.status(404).send("Team nicht gefunden");
}
res.send("Team gelöscht");
} catch (err) {
console.error("Fehler beim Löschen:", err);
res.status(500).send("Fehler beim Löschen");
}
});
//Spieler anlegen
app.post("/api/players", async (req, res) => {
const { name, nickname, birthdate, position, jersey_number, favorite_food, image_url, status, team_ids } = req.body;
if (!name || !position) {
return res.status(400).send("Name und Position sind Pflichtfelder");
}
const client = await pool.connect();
try {
await client.query("BEGIN");
const result = await client.query(
`INSERT INTO players (name, nickname, birthdate, position, jersey_number, favorite_food, image_url, status)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`,
[name, nickname, birthdate, position, jersey_number, favorite_food, image_url, status || "aktiv"]
);
const playerId = result.rows[0].id;
if (team_ids && team_ids.length > 0) {
const teamInsert = team_ids.map((teamId) => `(${playerId}, ${teamId})`).join(",");
await client.query(`INSERT INTO player_teams (player_id, team_id) VALUES ${teamInsert}`);
}
await client.query("COMMIT");
res.status(201).json({ id: playerId });
} catch (err) {
await client.query("ROLLBACK");
console.error("Fehler beim Anlegen des Spielers:", err);
res.status(500).send("Fehler beim Anlegen des Spielers");
} finally {
client.release();
}
});
//Einzelnes Team + Spieler laden
// Einzelnes Team + Spieler laden
app.get("/api/teams/:id", async (req, res) => {
const { id } = req.params;
try {
// Team-Daten holen
const teamResult = await pool.query(
"SELECT id, name FROM teams WHERE id = $1",
[id]
);
if (teamResult.rowCount === 0) {
return res.status(404).send("Team nicht gefunden");
}
// Spieler für dieses Team holen
const playerResult = await pool.query(`
SELECT p.id, p.name, p.nickname, p.position, p.jersey_number, p.image_url
FROM players p
INNER JOIN player_teams pt ON p.id = pt.player_id
WHERE pt.team_id = $1
ORDER BY p.name ASC
`, [id]);
res.json({
id: teamResult.rows[0].id,
name: teamResult.rows[0].name,
players: playerResult.rows,
});
} catch (err) {
console.error("Fehler beim Laden des Teams:", err);
res.status(500).send("Fehler beim Laden des Teams");
}
});
//Alle Spieler abrufen
app.get("/api/players", async (req, res) => {
try {
const result = await pool.query("SELECT * FROM players ORDER BY name ASC");
res.json(result.rows);
} catch (err) {
console.error("Fehler beim Abrufen der Spieler:", err);
res.status(500).send("Fehler beim Abrufen der Spieler");
}
});
//Upload Player Image
app.post("/api/upload-player-image", uploadPlayerImage.single("image"), (req, res) => {
if (!req.file) {
return res.status(400).send("Kein Bild hochgeladen");
}
const imageUrl = `/uploads/players/${req.file.filename}`;
res.json({ imageUrl });
});
//Spieler bearbeiten
// Spieler aktualisieren
app.put("/api/players/:id", async (req, res) => {
const { id } = req.params;
const {
name,
nickname,
birthdate,
position,
jersey_number,
favorite_food,
status,
image_url,
} = req.body;
if (!name || !position) {
return res.status(400).send("Name und Position sind Pflichtfelder");
}
try {
const result = await pool.query(
`UPDATE players
SET
name = $1,
nickname = $2,
birthdate = $3,
position = $4,
jersey_number = $5,
favorite_food = $6,
status = $7,
image_url = $8
WHERE id = $9
RETURNING *`,
[
name,
nickname || null,
birthdate || null,
position,
jersey_number ? Number(jersey_number) : null,
favorite_food || null,
status || "aktiv",
image_url || null,
id,
]
);
if (result.rowCount === 0) {
return res.status(404).send("Spieler nicht gefunden");
}
res.json(result.rows[0]);
} catch (err) {
console.error("Fehler beim Aktualisieren des Spielers:", err);
res.status(500).send("Fehler beim Aktualisieren des Spielers");
}
});
//Einzelnen Spieler abrufen
// Einzelnen Spieler abrufen
app.get("/api/players/:id", async (req, res) => {
const { id } = req.params;
try {
const result = await pool.query(
"SELECT * FROM players WHERE id = $1",
[id]
);
if (result.rowCount === 0) {
return res.status(404).send("Spieler nicht gefunden");
}
res.json(result.rows[0]);
} catch (err) {
console.error("Fehler beim Abrufen des Spielers:", err);
res.status(500).send("Fehler beim Abrufen des Spielers");
}
});
// Spieler aus einem Team entfernen
app.delete("/api/teams/:teamId/players/:playerId", async (req, res) => {
const { teamId, playerId } = req.params;
try {
const result = await pool.query(
"DELETE FROM player_teams WHERE team_id = $1 AND player_id = $2",
[teamId, playerId]
);
if (result.rowCount === 0) {
return res.status(404).send("Spieler nicht im Team gefunden");
}
res.send("Spieler erfolgreich aus dem Team entfernt");
} catch (err) {
console.error("Fehler beim Entfernen des Spielers:", err);
res.status(500).send("Fehler beim Entfernen des Spielers");
}
});
// Existierenden Spieler einem Team hinzufügen
app.post("/api/teams/:teamId/players", async (req, res) => {
const { teamId } = req.params;
const { playerId } = req.body;
if (!playerId) {
return res.status(400).send("playerId ist erforderlich");
}
try {
const result = await pool.query(
"INSERT INTO player_teams (player_id, team_id) VALUES ($1, $2)",
[playerId, teamId]
);
res.status(201).send("Spieler erfolgreich dem Team zugeordnet");
} catch (err) {
if (err.code === "23505") {
return res.status(400).send("Spieler ist bereits im Team");
}
console.error("Fehler beim Hinzufügen des Spielers zum Team:", err);
res.status(500).send("Fehler beim Hinzufügen des Spielers");
}
});
// 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}`);
});
});