Sektor-Browser
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import pool from '../db.js';
|
||||
|
||||
/**
|
||||
* Liefert alle Sektoren für das Frontend.
|
||||
* Unterteilt in "Meine Sektoren" (wo der User schon spielt)
|
||||
* und "Alle Sektoren" (die generell verfügbar sind)
|
||||
*/
|
||||
export const getSectorsList = async(req, res) => {
|
||||
const accountId = req.user.accountId;
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
// "Meine Sektoren" abfragen
|
||||
const mySectorsResult = await client.query(`
|
||||
SELECT
|
||||
s.id, s.name, s.status, s.speed_fleet, s.speed_economy,
|
||||
las.local_username,
|
||||
las.created_at as joined_at,
|
||||
las.updated_at as last_played
|
||||
FROM sectors s
|
||||
JOIN lobby_accounts_sectors las ON s.id = las.sector_id
|
||||
WHERE las.lobby_account_id = $1
|
||||
ORDER BY las.updated_at DESC
|
||||
`, [accountId]);
|
||||
|
||||
// "Neue Sektoren"
|
||||
const newSectorsResult = await client.query(`
|
||||
SELECT
|
||||
id, name, status, speed_fleet, speed_economy
|
||||
FROM sectors
|
||||
WHERE status != 'offline'
|
||||
AND id NOT IN (
|
||||
SELECT sector_id
|
||||
FROM lobby_accounts_sectors
|
||||
WHERE lobby_account_id = $1
|
||||
)
|
||||
ORDER BY created_at ASC
|
||||
`, [accountId]);
|
||||
|
||||
// Daten platzhalter
|
||||
const mapSectorData = (sector) => ({
|
||||
...sector,
|
||||
online_players: Math.floor(Math.random() * 150) + 10,
|
||||
planets_count: 1337
|
||||
});
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
mySectors: mySectorsResult.rows.map(mapSectorData),
|
||||
newSectors: newSectorsResult.rows.map(mapSectorData)
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[SectorController] Fehler beim Abrufen der Sektoren:', error);
|
||||
return res.status(500).json({
|
||||
error: 'ERR_INTERNAL_SERVER'
|
||||
});
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
/**
|
||||
* Middleware zum Schützen privater API-Routen.
|
||||
* Prüft, ob ein gültiges Access_Token im Aithorization-Headder mitgesendet wurde.
|
||||
*/
|
||||
export const requireAuth = (req, res, next) => {
|
||||
// Der Standard für Tokens ist der Header: "Authorization: Bearer <token>"
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({
|
||||
error: 'ERR_UNAUTHORIZED_NO_TOKEN'
|
||||
});
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1];
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_ACCESS_SECRET);
|
||||
|
||||
req.user = decoded;
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(401).json({
|
||||
error: 'ERR_UNAUTHORIZED_INVALID_TOKEN'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import express from 'express';
|
||||
import * as sectorController from '../controllers/sector.controller.js';
|
||||
import { requireAuth } from '../middlewares/auth.middleware.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/list', requireAuth, sectorController.getSectorsList);
|
||||
|
||||
export default router;
|
||||
@@ -7,40 +7,72 @@ import pool from '../db.js';
|
||||
|
||||
const createTablesQuery = `
|
||||
|
||||
-- ------------------------------------------------------------------------------
|
||||
-- DROP TABLES
|
||||
-- WICHTIG: Die Reihenfolge beim Löschen ist entscheidend!
|
||||
-- Tabellen mit Fremdschlüsseln (Foreign Keys) müssen zuerst gelöscht werden.
|
||||
-- ------------------------------------------------------------------------------
|
||||
DROP TABLE IF EXISTS refresh_tokens;
|
||||
DROP TABLE IF EXISTS auth_otps;
|
||||
DROP TABLE IF EXISTS lobby_accounts;
|
||||
DROP TABLE IF EXISTS lobby_accounts_sectors; -- Zuerst die Brückentabelle löschen
|
||||
DROP TABLE IF EXISTS sectors; -- Dann die Sektoren-Tabelle
|
||||
DROP TABLE IF EXISTS lobby_accounts; -- Zuletzt die Haupt-Nutzer-Tabelle
|
||||
|
||||
-- ------------------------------------------------------------------------------
|
||||
-- TABLE: lobby_accounts
|
||||
-- Base user data for the entire Void-Genesis universe.
|
||||
-- Basis-Nutzerdaten für das gesamte Void-Genesis Universum.
|
||||
-- ------------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS lobby_accounts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
|
||||
-- Status flags and balances (English naming)
|
||||
is_email_verified BOOLEAN DEFAULT false,
|
||||
res_premium INT DEFAULT 0,
|
||||
is_admin BOOLEAN DEFAULT false,
|
||||
|
||||
-- Timestamps for record keeping
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ------------------------------------------------------------------------------
|
||||
-- TABLE: sectors
|
||||
-- Beinhaltet die globalen Metadaten zu allen verfügbaren Spiel-Sektoren.
|
||||
-- Hier definieren wir, welche Server existieren und welche Eigenschaften sie haben.
|
||||
-- ------------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS sectors (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(50) UNIQUE NOT NULL,
|
||||
status VARCHAR(20) DEFAULT 'offline',
|
||||
speed_fleet INT DEFAULT 1,
|
||||
speed_economy INT DEFAULT 1,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ------------------------------------------------------------------------------
|
||||
-- TABLE: lobby_accounts_sectors (Brückentabelle)
|
||||
-- Verknüpft einen Lobby-Account mit einem Sektor.
|
||||
-- Existiert hier ein Eintrag, bedeutet das: Der Nutzer hat in diesem Sektor einen Spielstand.
|
||||
-- ------------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS lobby_accounts_sectors (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
lobby_account_id UUID REFERENCES lobby_accounts(id) ON DELETE CASCADE,
|
||||
sector_id UUID REFERENCES sectors(id) ON DELETE CASCADE,
|
||||
local_username VARCHAR(50) UNIQUE NOT NULL,
|
||||
res_premium INT DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(lobby_account_id, sector_id)
|
||||
);
|
||||
|
||||
-- ------------------------------------------------------------------------------
|
||||
-- TABLE: auth_otps
|
||||
-- Stores temporary 6-digit codes for registration or password resets.
|
||||
-- Speichert temporäre 6-stellige Codes für Registrierung oder Passwort-Reset.
|
||||
-- ------------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS auth_otps (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
account_id UUID REFERENCES lobby_accounts(id) ON DELETE CASCADE,
|
||||
otp_code VARCHAR(6) NOT NULL,
|
||||
|
||||
-- Defines the purpose of the OTP (e.g., 'register', 'password_reset')
|
||||
type VARCHAR(20) NOT NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
@@ -48,7 +80,7 @@ CREATE TABLE IF NOT EXISTS auth_otps (
|
||||
|
||||
-- ------------------------------------------------------------------------------
|
||||
-- TABLE: refresh_tokens
|
||||
-- Stores long-lived tokens for the "Keep me logged in" and single-session feature.
|
||||
-- Speichert langlebige Tokens für die "Angemeldet bleiben"-Funktion.
|
||||
-- ------------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
@@ -60,8 +92,8 @@ CREATE TABLE IF NOT EXISTS refresh_tokens (
|
||||
|
||||
-- ------------------------------------------------------------------------------
|
||||
-- TRIGGER: Auto-update 'updated_at' timestamp
|
||||
-- Diese Funktion setzt das 'updated_at' Feld bei jeder Änderung auf die aktuelle Zeit.
|
||||
-- ------------------------------------------------------------------------------
|
||||
-- This function sets the 'updated_at' field to the current time whenever a row is modified.
|
||||
CREATE OR REPLACE FUNCTION update_modified_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
@@ -70,15 +102,32 @@ BEGIN
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- We only create the trigger if it doesn't exist to avoid errors on multiple runs.
|
||||
-- Wir wenden den Trigger nur an, wenn er noch nicht existiert.
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Trigger für lobby_accounts
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_lobby_accounts_modtime') THEN
|
||||
CREATE TRIGGER update_lobby_accounts_modtime
|
||||
BEFORE UPDATE ON lobby_accounts
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_modified_column();
|
||||
END IF;
|
||||
|
||||
-- Trigger für sectors
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_sectors_modtime') THEN
|
||||
CREATE TRIGGER update_sectors_modtime
|
||||
BEFORE UPDATE ON sectors
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_modified_column();
|
||||
END IF;
|
||||
|
||||
-- Trigger für lobby_accounts_sectors (WICHTIG für unsere "Zuletzt gespielt" Funktion)
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_lobby_accounts_sectors_modtime') THEN
|
||||
CREATE TRIGGER update_lobby_accounts_sectors_modtime
|
||||
BEFORE UPDATE ON lobby_accounts_sectors
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_modified_column();
|
||||
END IF;
|
||||
END $$;
|
||||
`;
|
||||
|
||||
@@ -90,11 +139,37 @@ async function initDatabase() {
|
||||
|
||||
// Wir holen uns eine Verbindung aus dem Pool
|
||||
const client = await pool.connect();
|
||||
|
||||
|
||||
try {
|
||||
// Wir senden den kompletten SQL-String an die Datenbank
|
||||
await client.query(createTablesQuery);
|
||||
console.log('✅ Tabellen wurden erfolgreich erstellt (oder existierten bereits).');
|
||||
|
||||
const sectorCheck = await client.query('SELECT count(*) FROM sectors');
|
||||
if (parseInt(sectorCheck.rows[0].count) === 0) {
|
||||
console.log('🌱 Füge 24 Test-Sektoren ein...');
|
||||
|
||||
const greekAlphabet = [
|
||||
'Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta',
|
||||
'Eta', 'Theta', 'Iota', 'Kappa', 'Lambda', 'Mu',
|
||||
'Nu', 'Xi', 'Omicron', 'Pi', 'Rho', 'Sigma',
|
||||
'Tau', 'Upsilon', 'Phi', 'Chi', 'Psi', 'Omega'
|
||||
];
|
||||
|
||||
// Wir generieren für jeden Namen ein dynamisches INSERT
|
||||
const insertPromises = greekAlphabet.map((name, index) => {
|
||||
// Wir variieren die Geschwindigkeiten leicht für optische Vielfalt
|
||||
const speed = (index % 4) + 1;
|
||||
return client.query(`
|
||||
INSERT INTO sectors (name, status, speed_fleet, speed_economy)
|
||||
VALUES ($1, 'online', $2, $2)
|
||||
`, [name, speed]);
|
||||
});
|
||||
|
||||
await Promise.all(insertPromises);
|
||||
console.log('✅ 24 Test-Sektoren erfolgreich eingefügt.');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Fehler beim Erstellen der Tabellen:', error);
|
||||
} finally {
|
||||
|
||||
@@ -7,6 +7,7 @@ import cors from 'cors';
|
||||
import pool from './db.js';
|
||||
|
||||
import authRoutes from './routes/auth.routes.js';
|
||||
import sectorRoutes from './routes/sector.routes.js';
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
@@ -16,6 +17,7 @@ app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/sectors', sectorRoutes);
|
||||
|
||||
// Unsere bestehende Health-Route
|
||||
app.get('/api/health', (req, res) => {
|
||||
|
||||
@@ -342,7 +342,7 @@ export const refreshAccessToken = async (refreshToken) => {
|
||||
id: account.id,
|
||||
username: account.username,
|
||||
email: account.email,
|
||||
res_premium: res_premium
|
||||
res_premium: account.res_premium
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user