Compare commits
10 Commits
061131b166
...
53869a6626
| Author | SHA1 | Date | |
|---|---|---|---|
| 53869a6626 | |||
| 8b905dc9b3 | |||
| 75bbfe0a77 | |||
| 2032a006c4 | |||
| 4891c539da | |||
| c4c9714d41 | |||
| ba48c760c5 | |||
| 1228a02221 | |||
|
|
ec50d8e306 | ||
|
|
e19fd78d75 |
36
.env.example
Normal file
36
.env.example
Normal file
@@ -0,0 +1,36 @@
|
||||
# ====================================
|
||||
# Configuration Application Web Admin
|
||||
# ====================================
|
||||
|
||||
# Environnement
|
||||
NODE_ENV=production
|
||||
|
||||
# Port de l'application web
|
||||
PORT=4001
|
||||
|
||||
# Chemin vers le serveur Minecraft (RELATIF au dossier parent)
|
||||
# Par défaut: ../NationsGlory_ServeurBuild_Red
|
||||
# Cela permet de pointer vers le serveur MC sans chemins absolus
|
||||
MC_SERVER_PATH=../NationsGlory_ServeurBuild_Red
|
||||
|
||||
# Configuration RCON du serveur Minecraft
|
||||
# Utiliser 'localhost' si l'app web et le serveur MC sont sur la même machine
|
||||
# Sinon, utiliser l'IP du serveur MC
|
||||
RCON_HOST=localhost
|
||||
RCON_PORT=25575
|
||||
RCON_PASSWORD=minecraft
|
||||
|
||||
# Secret de session (CHANGEZ CETTE VALEUR EN PRODUCTION!)
|
||||
# Générez avec: openssl rand -base64 32
|
||||
SESSION_SECRET=changez-moi-en-production-utilisez-une-valeur-aleatoire-securisee
|
||||
|
||||
# Domaine/IP publique (optionnel)
|
||||
# Utilisé pour générer des URLs complètes si nécessaire
|
||||
# Laissez vide pour détection automatique
|
||||
# PUBLIC_HOST=votre-domaine.com
|
||||
# PUBLIC_HOST=192.168.1.100
|
||||
|
||||
# Configuration CORS (optionnel)
|
||||
# Laissez vide pour autoriser toutes les origines (développement)
|
||||
# En production, spécifiez votre domaine
|
||||
# CORS_ORIGIN=https://votre-domaine.com
|
||||
935
ANALYSIS.md
Normal file
935
ANALYSIS.md
Normal file
@@ -0,0 +1,935 @@
|
||||
# 📊 Analyse Complète : NationsGlory Web Admin Panel + Serveur Minecraft
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Architecture Générale](#architecture-générale)
|
||||
2. [Application Web](#application-web)
|
||||
3. [Serveur Minecraft](#serveur-minecraft)
|
||||
4. [Intégration](#intégration)
|
||||
5. [Points de Sécurité](#points-de-sécurité)
|
||||
6. [Recommandations](#recommandations)
|
||||
|
||||
---
|
||||
|
||||
## Architecture Générale
|
||||
|
||||
### Stack Technique
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Frontend (SPA Vanilla JS) │
|
||||
│ - index.html, app.js, style.css │
|
||||
│ - API_URL: http://<hostname>:4001/api │
|
||||
└────────────────────┬────────────────────────────────────┘
|
||||
│ HTTP REST
|
||||
┌────────────────────▼────────────────────────────────────┐
|
||||
│ Backend (Express.js + Node.js 18) │
|
||||
│ - server.js (port 4001, listens on 0.0.0.0) │
|
||||
│ - 7 routes: auth, server, rcon, logs, players, │
|
||||
│ whitelist, backup │
|
||||
└────────────────────┬────────────────────────────────────┘
|
||||
│ RCON (port 25575)
|
||||
┌────────────────────▼────────────────────────────────────┐
|
||||
│ Minecraft Server (mcpc.jar / Forge) │
|
||||
│ - Version: 1.6.4 (MCPC+) │
|
||||
│ - Mods: 60+ mods (Nations UI, FlansMod, etc) │
|
||||
│ - Port: 25565 (server), 25575 (RCON) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
|
||||
Déploiement:
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Docker Compose (machine distante: 192.168.1.252) │
|
||||
│ - Container webnationsglory-admin │
|
||||
│ - Volume mount: /mc-server → serveur Minecraft │
|
||||
│ - Restart: unless-stopped │
|
||||
│ - Port mapping: 4001:4001 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Taille & Ressources
|
||||
- **Serveur Minecraft**: 143 MB (world + config + mods)
|
||||
- **Web App**: ~200 KB (frontend + backend source)
|
||||
- **Docker Image**: ~200 MB (Node.js 18-alpine + modules)
|
||||
|
||||
---
|
||||
|
||||
## Application Web
|
||||
|
||||
### Backend Architecture
|
||||
|
||||
#### 1. **Serveur Principal** (`backend/src/server.js`)
|
||||
```javascript
|
||||
// Configuration
|
||||
const PORT = 4001;
|
||||
const SERVER_DIR = '/mc-server' (Docker) ou local path
|
||||
const SESSION_SECRET = process.env.SESSION_SECRET
|
||||
|
||||
// Middlewares
|
||||
- express.json() / urlencoded
|
||||
- CORS (origin: true, credentials: true)
|
||||
- express-session (24h max age, httpOnly)
|
||||
|
||||
// Routes
|
||||
/api/auth → Authentification + enregistrement
|
||||
/api/server → Configuration serveur (server.properties)
|
||||
/api/rcon → Exécution commandes serveur
|
||||
/api/logs → Historique logs
|
||||
/api/players → Liste joueurs + statut OP
|
||||
/api/whitelist → Gestion whitelist
|
||||
/api/backup → Gestion backups
|
||||
```
|
||||
|
||||
**Points clés:**
|
||||
- ✅ Frontend path auto-detection (Docker vs local)
|
||||
- ✅ CORS enabled avec credentials
|
||||
- ✅ Session 24h max
|
||||
- ⚠️ Session secret en dur ("your-secret-key-change-in-prod")
|
||||
- ⚠️ HTTPS désactivé (secure: false)
|
||||
|
||||
#### 2. **Routes d'Authentification** (`backend/src/routes/auth.js`)
|
||||
|
||||
**Endpoints:**
|
||||
```
|
||||
POST /register → Créer compte admin (vérifie que MC username est OP)
|
||||
POST /login → Connexion
|
||||
POST /logout → Déconnexion
|
||||
GET /check → Vérifier session active
|
||||
POST /change-password → Changer mot de passe
|
||||
```
|
||||
|
||||
**Mécanisme:**
|
||||
```javascript
|
||||
// Enregistrement
|
||||
1. Vérifier que c'est le premier compte (users.json vide)
|
||||
2. Lire ops.txt OU ops.json depuis serveur
|
||||
3. Vérifier que mcUsername est dans la liste OPs
|
||||
4. Hash password avec bcryptjs (10 rounds)
|
||||
5. Sauvegarder dans users.json
|
||||
|
||||
// Connexion
|
||||
1. Trouver utilisateur par username
|
||||
2. Comparer password avec hash bcrypt
|
||||
3. Créer session req.session.user
|
||||
4. Session persiste dans memory (⚠️ perd les sessions au redémarrage!)
|
||||
```
|
||||
|
||||
**Fichiers utilisés:**
|
||||
- `backend/data/users.json` → Stockage utilisateurs + passwords hashés
|
||||
- `~/NationsGlory_ServeurBuild_Red/ops.txt` → Admins serveur
|
||||
- `~/NationsGlory_ServeurBuild_Red/ops.json` → Alternative format OPs
|
||||
|
||||
**Risques de sécurité:**
|
||||
- ⚠️ Sessions en mémoire uniquement (pas persistentes)
|
||||
- ⚠️ users.json stocké en local/Docker (pas de base de données)
|
||||
- ⚠️ Pas de rate limiting sur login
|
||||
- ⚠️ Pas de 2FA
|
||||
|
||||
#### 3. **Routes Serveur** (`backend/src/routes/server.js`)
|
||||
|
||||
**Endpoints:**
|
||||
```
|
||||
GET / → Lire server.properties
|
||||
POST /update → Modifier une propriété
|
||||
GET /jvm-args → Lire arguments JVM
|
||||
POST /restart → Redémarrer le serveur
|
||||
POST /stop → Arrêter le serveur
|
||||
POST /backup → Créer backup
|
||||
```
|
||||
|
||||
**Implémentation:**
|
||||
- Parse `server.properties` ligne par ligne
|
||||
- Supporte modification de propriétés individuelles
|
||||
- Contrôle via RCON pour restart/stop
|
||||
|
||||
#### 4. **Routes RCON** (`backend/src/routes/rcon.js`)
|
||||
|
||||
**Architecture RCON:**
|
||||
```javascript
|
||||
// Classe RconClient (backend/src/utils/rcon.js)
|
||||
- Protocole: Minecraft RCON (TCP port 25575)
|
||||
- Packet format: [size][id][type][payload][null]
|
||||
- Type 3 = Auth, Type 2 = Command, Type 0 = Response
|
||||
- Timeout: 5s (auth), 10s (command)
|
||||
|
||||
// Implémentation
|
||||
1. Parse server.properties pour rcon.port + rcon.password
|
||||
2. Créer socket TCP vers serveur
|
||||
3. S'authentifier avec password
|
||||
4. Envoyer commandes
|
||||
5. Recevoir réponses avec stream buffer
|
||||
6. Timeout après 10s
|
||||
```
|
||||
|
||||
**Endpoints:**
|
||||
```
|
||||
POST /command → Exécuter commande RCON
|
||||
GET /history → Historique 200 dernières commandes
|
||||
DELETE /history/:id → Nettoyer historique
|
||||
```
|
||||
|
||||
**Commandes supportées:**
|
||||
- `say <msg>` → Message serveur
|
||||
- `stop` → Arrêter serveur
|
||||
- `restart` → Redémarrer (custom)
|
||||
- `list` → Joueurs en ligne (parsé pour joueurs)
|
||||
- `op <player>` → Ajouter OP
|
||||
- `whitelist add <player>` → Ajouter whitelist
|
||||
- Tout ce que le serveur Minecraft supporte
|
||||
|
||||
**Historique:**
|
||||
- Stocké dans `.web-admin/rcon-history.json`
|
||||
- Limité à 200 dernières commandes
|
||||
- Contient: timestamp, command, response, success, error
|
||||
|
||||
#### 5. **Routes Joueurs** (`backend/src/routes/players.js`)
|
||||
|
||||
**Endpoints:**
|
||||
```
|
||||
GET / → Liste tous joueurs + statut OP + dernière connexion
|
||||
```
|
||||
|
||||
**Implémentation:**
|
||||
```javascript
|
||||
// 1. Lire world/players/*.dat (fichiers joueurs)
|
||||
// 2. Mapper UUID → Nom via usercache.json
|
||||
// 3. Lire ops.txt et parser noms OPs
|
||||
// 4. Ajouter champ isOp: boolean pour chaque joueur
|
||||
// 5. Retourner liste avec:
|
||||
{
|
||||
players: [
|
||||
{ uuid, name, isOp, lastPlayed }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Données sources:**
|
||||
- `world/players/*.dat` → Fichiers joueurs Minecraft
|
||||
- `usercache.json` → Mapping UUID ↔ Nom (si existe)
|
||||
- `ops.txt` → Noms des OPs (nouvelle feature)
|
||||
|
||||
#### 6. **Routes Whitelist** (`backend/src/routes/whitelist.js`)
|
||||
|
||||
**Endpoints:**
|
||||
```
|
||||
GET / → Lire whitelist
|
||||
POST /add → Ajouter joueur
|
||||
POST /remove → Retirer joueur
|
||||
```
|
||||
|
||||
**Support formats:**
|
||||
- `whitelist.json` → Format moderne `[{name, uuid}]`
|
||||
- `whitelist.txt` → Format legacy, 1 nom/ligne
|
||||
|
||||
**Implémentation:**
|
||||
- Auto-détecte format (JSON ou TXT)
|
||||
- Priorise JSON si les deux existent
|
||||
- Ajoute validation de doublons
|
||||
|
||||
#### 7. **Routes Logs** (`backend/src/routes/logs.js`)
|
||||
|
||||
**Endpoints:**
|
||||
```
|
||||
GET / → Logs serveur (recent-latest.log + ancien logs.1, logs.2, etc)
|
||||
```
|
||||
|
||||
**Parsing:**
|
||||
- Supporte format colorisé Minecraft
|
||||
- Convertit codes Minecraft (§X) en HTML
|
||||
- Retourne HTML colorisé pour UI
|
||||
|
||||
#### 8. **Routes Backup** (`backend/src/routes/backup.js`)
|
||||
|
||||
**Endpoints:**
|
||||
```
|
||||
GET / → Lister backups existants
|
||||
POST /create → Créer nouveau backup (tar.gz)
|
||||
GET /download → Télécharger backup
|
||||
POST /restore → Restaurer depuis backup
|
||||
```
|
||||
|
||||
**Implémentation:**
|
||||
```bash
|
||||
tar --exclude='.web-admin' --exclude='*.log.lck' \
|
||||
-czf "backup-<timestamp>.tar.gz" .
|
||||
```
|
||||
|
||||
**Répertoire:** `backups/` dans SERVER_DIR
|
||||
|
||||
---
|
||||
|
||||
### Frontend Architecture
|
||||
|
||||
#### 1. **Structure SPA** (`frontend/public/js/app.js`)
|
||||
```javascript
|
||||
// État global
|
||||
let currentUser = null;
|
||||
let isAuthenticated = false
|
||||
|
||||
// Points d'entrée
|
||||
checkAuth() // Vérifier session
|
||||
showLoginPage() // Authentification
|
||||
showDashboard() // Interface admin
|
||||
switchTab(tabName) // Navigation
|
||||
|
||||
// Sections disponibles (onglets)
|
||||
- Dashboard → Infos serveur + joueurs en ligne
|
||||
- Joueurs → Liste tous joueurs, statut OP
|
||||
- Whitelist → Gérer whitelist
|
||||
- Commandes RCON → Interface ligne de commande
|
||||
- Logs → Consulter logs serveur
|
||||
- Serveur → Settings server.properties
|
||||
- Backups → Gérer backups + restore
|
||||
- Paramètres → Changement password
|
||||
```
|
||||
|
||||
#### 2. **API_URL Resolution**
|
||||
```javascript
|
||||
// Avant: hardcoded localhost
|
||||
const API_URL = 'http://localhost:4001/api';
|
||||
|
||||
// Après: Dynamic hostname detection
|
||||
const API_URL = window.location.hostname === 'localhost' ||
|
||||
window.location.hostname === '127.0.0.1'
|
||||
? 'http://localhost:4001/api'
|
||||
: `http://${window.location.hostname}:4001/api`;
|
||||
```
|
||||
|
||||
**Impact:** Permet accès via IP (192.168.1.252) sans modification code
|
||||
|
||||
#### 3. **Authentification Frontend**
|
||||
```javascript
|
||||
// Login Flow
|
||||
checkAuth() →
|
||||
fetch /auth/check →
|
||||
Authentifié → showDashboard()
|
||||
Non-auth → showLoginPage() → handleLogin() → fetch /auth/login
|
||||
|
||||
// Données transmises
|
||||
POST /api/auth/login {
|
||||
username: string,
|
||||
password: string
|
||||
}
|
||||
|
||||
// Réponse
|
||||
{ authenticated: true, user: { username, mcUsername } }
|
||||
```
|
||||
|
||||
#### 4. **Dashboard Principal**
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ NationsGlory Admin Panel 🔓 admin ▼ Logout │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Tabs: [Dashboard] [Joueurs] [Whitelist] [RCON] │
|
||||
│ [Logs] [Serveur] [Backups] [Settings] │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ 📊 Dashboard │
|
||||
│ • Joueurs connectés: 0/20 ⚪ │
|
||||
│ • Statut serveur: ON/OFF │
|
||||
│ • Mods chargés: 60+ │
|
||||
│ • World: FLAT, seed: [value] │
|
||||
│ • Max build height: 256 │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ 👥 Joueurs en ligne │
|
||||
│ [Rafraîchir] │
|
||||
│ 📋 Tous les Joueurs │
|
||||
│ [Table avec columns: Nom, OP, UUID, LastPlayed] │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 5. **Sections Clés**
|
||||
|
||||
**A. Joueurs** (`loadPlayersData()`)
|
||||
- Affiche tous les joueurs jamais connectés
|
||||
- Colonne OP: ✅ OP ou ❌
|
||||
- Colonne UUID + dernière connexion
|
||||
- Chargement RCON `list` pour joueurs en ligne en temps réel
|
||||
|
||||
**B. Whitelist** (`getWhitelistHTML()`)
|
||||
- Ajouter / Retirer joueurs
|
||||
- Support JSON et TXT formats
|
||||
- Validation doublons
|
||||
|
||||
**C. RCON Terminal** (`getRconHTML()`)
|
||||
- Interface ligne de commande
|
||||
- Input texte + historique
|
||||
- Exécution commandes
|
||||
- Affichage résultats
|
||||
|
||||
**D. Logs** (`getLogsHTML()`)
|
||||
- Lecture logs serveur
|
||||
- Coloration codes Minecraft
|
||||
- Scroll horizontal si long
|
||||
|
||||
**E. Serveur Settings** (`getServerHTML()`)
|
||||
- Liste properties modifiables
|
||||
- Boutons Start/Stop/Restart
|
||||
- Actions avec confirmation
|
||||
|
||||
**F. Backups** (`getBackupsHTML()`)
|
||||
- Lister backups
|
||||
- Créer backup
|
||||
- Télécharger
|
||||
- Restaurer depuis backup
|
||||
|
||||
**G. Settings** (`getSettingsHTML()`)
|
||||
- Changement mot de passe
|
||||
- About version
|
||||
|
||||
---
|
||||
|
||||
## Serveur Minecraft
|
||||
|
||||
### Configuration Serveur
|
||||
|
||||
**Server Properties** (`server.properties`)
|
||||
```properties
|
||||
server-ip= # Écoute sur toutes interfaces
|
||||
server-port=25565 # Port Minecraft standard
|
||||
max-players=20 # Limite 20 joueurs
|
||||
gamemode=1 # Creative mode
|
||||
difficulty=1 # Normal
|
||||
level-type=FLAT # Monde plat
|
||||
level-name=world # Dossier monde
|
||||
enable-rcon=true # RCON activé
|
||||
rcon.port=25575 # Port RCON
|
||||
rcon.password=Landau8210 # ⚠️ Password en clair!
|
||||
online-mode=false # Offline mode (permet cracked clients)
|
||||
pvp=true # PvP activé
|
||||
allow-nether=true # Nether autorisé
|
||||
spawn-animals=true # Génération animaux
|
||||
white-list=false # Whitelist désactivée par défaut
|
||||
```
|
||||
|
||||
### Structure Serveur
|
||||
|
||||
```
|
||||
NationsGlory_ServeurBuild_Red/
|
||||
├── world/ # Données du monde
|
||||
│ ├── players/ # Fichiers joueurs (.dat)
|
||||
│ ├── stats/ # Stats joueurs (.json)
|
||||
│ ├── region/ # Chunks du monde
|
||||
│ └── level.dat # Info niveau
|
||||
├── mods/ # 60+ mods Minecraft
|
||||
│ └── [liste complète ci-dessous]
|
||||
├── plugins/ # Plugins serveur
|
||||
├── config/ # Configuration mods (60+ fichiers .cfg)
|
||||
├── libraries/ # Dépendances Java
|
||||
├── customnpcs/ # NPCs custom
|
||||
├── Flan/ # Config FlansMod
|
||||
├── .web-admin/ # Historique RCON
|
||||
├── mcpc.jar # Serveur MCPC+
|
||||
├── server.properties # Configuration
|
||||
├── ops.txt # Admins serveur (format texte)
|
||||
├── ops.json # Alternative format OPs
|
||||
├── whitelist.txt # Whitelist (TXT)
|
||||
├── whitelist.json # Alternative whitelist (JSON)
|
||||
├── banned-players.txt # Bannis
|
||||
├── banned-ips.txt # IPs bannies
|
||||
├── usercache.json # Cache UUID ↔ Nom (si existe)
|
||||
└── backups/ # Sauvegardes (créées par app)
|
||||
```
|
||||
|
||||
**Taille: 143 MB**
|
||||
|
||||
### Mods/Plugins Identifiés
|
||||
|
||||
Via `config/*.cfg`:
|
||||
1. **NationsGlory mods**
|
||||
- ngcore.cfg → Nations Core
|
||||
- ngcontent.cfg → Contenu Nations
|
||||
- nationsui.cfg → Interface Nations
|
||||
- nationsgui.json
|
||||
- nationsmachine.cfg → Machines Nations
|
||||
- NGUpgrades.json
|
||||
|
||||
2. **Gameplay Mods**
|
||||
- FlansMod.cfg → Armes, véhicules
|
||||
- WeaponMod.cfg → Armes custom
|
||||
- ICBM.cfg → Missiles
|
||||
- CustomNpcs.cfg → NPCs custom
|
||||
- GetAllTheSeeds.cfg
|
||||
- AnimalBikes.cfg
|
||||
|
||||
3. **Building/Tech**
|
||||
- Chisel.cfg → Blocs décoratifs
|
||||
- BiblioCraft.cfg → Furniture
|
||||
- ArmorStatusHUD.cfg
|
||||
- TLSpecialArmor.cfg
|
||||
|
||||
4. **Farming**
|
||||
- PAM mods (multiple):
|
||||
- pamtemperateplants.cfg
|
||||
- pamweeeflowers.cfg
|
||||
- pamrandomaplants.cfg
|
||||
- pamextendedglass.cfg
|
||||
|
||||
5. **Infrastructure**
|
||||
- Galacticraft/ → Espace
|
||||
- Netherrocks → Nether
|
||||
- AquaTweaks.cfg → Eau
|
||||
- MelonSpawn.cfg
|
||||
- Autoutils.cfg
|
||||
|
||||
6. **Visualization**
|
||||
- MapWriter.cfg → Mini-map
|
||||
- ArmorStatusHUD.cfg
|
||||
|
||||
7. **Framework/Admin**
|
||||
- forge.cfg → Forge base
|
||||
- forgeChunkLoading.cfg
|
||||
- bspkrsCore.cfg → Libraire
|
||||
- logging.properties
|
||||
|
||||
**Version:** 1.6.4 (Minecraft 1.6.4, probablement MCPC+ pour Forge)
|
||||
|
||||
### Architecture Serveur MCPC+
|
||||
|
||||
MCPC+ = Minecraft + Bukkit compatibility + Forge mods
|
||||
- Combine Minecraft serveur vanilla
|
||||
- Ajoute Forge pour mods
|
||||
- Supporte plugins Bukkit (en parallèle)
|
||||
- Version 1.6.4 (assez vieille, 2013)
|
||||
|
||||
---
|
||||
|
||||
## Intégration
|
||||
|
||||
### Flux de Données
|
||||
|
||||
```
|
||||
Frontend (Browser)
|
||||
↓ fetch /api/auth/login
|
||||
Backend (Express)
|
||||
├─ Vérifie users.json
|
||||
├─ Hash password bcrypt
|
||||
├─ Crée session
|
||||
└─ Return { authenticated: true }
|
||||
↓ fetch /api/rcon/command
|
||||
Backend RCON Client
|
||||
├─ Parse server.properties
|
||||
├─ Connect TCP:25575
|
||||
├─ Auth avec rcon.password
|
||||
├─ Send command bytes
|
||||
└─ Receive/parse response
|
||||
↓
|
||||
Minecraft Server (MCPC+)
|
||||
├─ Process command
|
||||
├─ Modify world/players/ ou server.properties
|
||||
└─ Send output back
|
||||
↓
|
||||
Backend Response
|
||||
└─ Return output JSON
|
||||
↓
|
||||
Frontend Display
|
||||
└─ Show result to user
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
**Current:** Express-session in-memory
|
||||
```javascript
|
||||
app.use(session({
|
||||
secret: SESSION_SECRET,
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: {
|
||||
secure: false, // HTTP only (not HTTPS)
|
||||
httpOnly: true, // No JS access
|
||||
sameSite: 'lax', // CSRF protection
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
**Problème:** Sessions perdues au redémarrage Docker
|
||||
- ✅ currentUser reste authentifié tant que session active
|
||||
- ❌ Redémarrage container = logout automatique
|
||||
- **Solution:** Utiliser connect-mongo, redis, ou JWT
|
||||
|
||||
### RCON Integration
|
||||
|
||||
```
|
||||
Web UI → Command → Backend → RCON Client → Minecraft Server → Response
|
||||
```
|
||||
|
||||
**Example: /api/rcon/command POST {"command": "list"}**
|
||||
|
||||
1. Backend RCON Client:
|
||||
```
|
||||
- Read: server.properties (rcon.port=25575, rcon.password=Landau8210)
|
||||
- Connect: TCP 192.168.1.252:25575
|
||||
- Authenticate: send password bytes
|
||||
- Send: "list" command
|
||||
- Parse: "There are 1/20 players online: anakine22"
|
||||
- Return: JSON response
|
||||
```
|
||||
|
||||
2. Frontend parses & displays
|
||||
|
||||
---
|
||||
|
||||
## Points de Sécurité
|
||||
|
||||
### 🔴 Critiques
|
||||
|
||||
1. **RCON Password en Clair**
|
||||
```properties
|
||||
rcon.password=Landau8210 # Visible dans server.properties
|
||||
```
|
||||
**Risque:** Accès direct au serveur Minecraft si fichier leak
|
||||
**Fix:** Lire depuis env vars, pas fichier
|
||||
|
||||
2. **Session en Mémoire**
|
||||
```javascript
|
||||
// Sessions perdues au redémarrage
|
||||
// Accessible seulement au container courant (pas clustering)
|
||||
```
|
||||
**Risque:** Forcer logout en case de crash/redémarrage
|
||||
**Fix:** Store sessions en Redis/DB
|
||||
|
||||
3. **HTTP seulement**
|
||||
```javascript
|
||||
secure: false // No HTTPS
|
||||
```
|
||||
**Risque:** Credentials en clair sur le réseau
|
||||
**Fix:** Activer HTTPS, self-signed cert minimum
|
||||
|
||||
4. **users.json pas encrypté**
|
||||
```
|
||||
backend/data/users.json
|
||||
```
|
||||
**Contient:** Passwords bcryptés (OK) + usernames (leak de données)
|
||||
**Fix:** Chiffrer fichier ou utiliser database
|
||||
|
||||
### 🟡 Modérés
|
||||
|
||||
5. **Pas de Rate Limiting**
|
||||
```
|
||||
Brute force possible sur endpoint /auth/login
|
||||
```
|
||||
**Fix:** express-rate-limit middleware
|
||||
|
||||
6. **Pas de 2FA**
|
||||
```
|
||||
Seul username/password
|
||||
```
|
||||
**Fix:** Ajouter TOTP si critique
|
||||
|
||||
7. **CORS trop permissif**
|
||||
```javascript
|
||||
CORS({ origin: true, credentials: true })
|
||||
```
|
||||
**Risque:** N'importe quel domaine peut faire requêtes
|
||||
**Fix:** Whitelist domains explicitement
|
||||
|
||||
8. **Pas de CSRF tokens**
|
||||
```
|
||||
State-changing operations (POST/DELETE) sans CSRF
|
||||
```
|
||||
**Fix:** Ajouter csurf middleware
|
||||
|
||||
9. **Pas de input validation**
|
||||
```javascript
|
||||
// POST /api/server/update
|
||||
{ property, value } // Pas de validation property names
|
||||
```
|
||||
**Risque:** Injection server.properties arbitraire
|
||||
**Fix:** Whitelist propriétés modifiables
|
||||
|
||||
10. **Fichiers logs exposés**
|
||||
```javascript
|
||||
GET /api/logs expose tous les logs du serveur
|
||||
```
|
||||
**Risque:** Info sensitive (errors, plugins, config)
|
||||
**Fix:** Limiter logs, filtrer data sensitive
|
||||
|
||||
### 🟢 Acceptables
|
||||
|
||||
11. **online-mode=false**
|
||||
```properties
|
||||
Serveur accepte cracked clients (offline mode)
|
||||
```
|
||||
**Pourquoi:** Peut être intentionnel pour privé réseau
|
||||
|
||||
12. **Debug pas activé**
|
||||
```properties
|
||||
debug=false ✓
|
||||
```
|
||||
|
||||
13. **Entity tracking config non visible** ✓
|
||||
|
||||
---
|
||||
|
||||
## Recommandations
|
||||
|
||||
### 🚀 Priorité 1: Critique
|
||||
|
||||
#### 1. Ajouter HTTPS/SSL
|
||||
```dockerfile
|
||||
# docker-compose.yml
|
||||
volumes:
|
||||
- ./certs:/app/certs # Certificates auto-renew via certbot
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- HTTPS_CERT=/app/certs/cert.pem
|
||||
- HTTPS_KEY=/app/certs/key.pem
|
||||
```
|
||||
|
||||
#### 2. Session Persistence (Redis)
|
||||
```bash
|
||||
# Ajouter redis service
|
||||
version: '3'
|
||||
services:
|
||||
app:
|
||||
# ...
|
||||
depends_on:
|
||||
- redis
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
```
|
||||
|
||||
```javascript
|
||||
// backend/src/server.js
|
||||
const RedisStore = require('connect-redis').default;
|
||||
const { createClient } = require('redis');
|
||||
|
||||
const redisClient = createClient({ host: 'redis', port: 6379 });
|
||||
const store = new RedisStore({ client: redisClient });
|
||||
|
||||
app.use(session({
|
||||
store,
|
||||
secret: process.env.SESSION_SECRET,
|
||||
// ...
|
||||
}));
|
||||
```
|
||||
|
||||
#### 3. RCON Password sécurisé
|
||||
```bash
|
||||
# .env
|
||||
RCON_PASSWORD=${RCON_PASSWORD} # Via docker-compose.yml
|
||||
|
||||
# Avoid storing in server.properties
|
||||
# Option: Lire depuis /run/secrets/rcon_password (Docker Secrets)
|
||||
```
|
||||
|
||||
#### 4. Rate Limiting
|
||||
```javascript
|
||||
// backend/src/middleware/rateLimit.js
|
||||
const rateLimit = require('express-rate-limit');
|
||||
|
||||
const loginLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 min
|
||||
max: 5, // 5 tentatives
|
||||
message: 'Trop de tentatives login'
|
||||
});
|
||||
|
||||
app.post('/api/auth/login', loginLimiter, handleLogin);
|
||||
```
|
||||
|
||||
#### 5. Validation Input Server Properties
|
||||
```javascript
|
||||
// backend/src/routes/server.js
|
||||
const ALLOWED_PROPERTIES = [
|
||||
'max-players',
|
||||
'difficulty',
|
||||
'pvp',
|
||||
'enable-nether',
|
||||
'server-ip',
|
||||
'server-port', // Attention: change port!
|
||||
// NOT: gamemode, level-type, level-name, rcon.password, etc
|
||||
];
|
||||
|
||||
if (!ALLOWED_PROPERTIES.includes(property)) {
|
||||
return res.status(400).json({ error: 'Propriété non modifiable' });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🎯 Priorité 2: Important
|
||||
|
||||
#### 6. CSRF Tokens
|
||||
```javascript
|
||||
const csrf = require('csurf');
|
||||
const csrfProtection = csrf({ cookie: false, sessionKey: 'session' });
|
||||
|
||||
app.post('/api/server/update', csrfProtection, handleUpdate);
|
||||
```
|
||||
|
||||
#### 7. Chiffrer users.json
|
||||
```javascript
|
||||
const crypto = require('crypto');
|
||||
const FILE_KEY = Buffer.from(process.env.FILE_ENCRYPTION_KEY, 'hex');
|
||||
|
||||
function encryptUsers(users) {
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv('aes-256-gcm', FILE_KEY, iv);
|
||||
const encrypted = cipher.update(JSON.stringify(users));
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 8. Logs Filtering
|
||||
```javascript
|
||||
// backend/src/routes/logs.js
|
||||
// Filtrer:
|
||||
// - Passwords
|
||||
// - Database credentials
|
||||
// - API keys
|
||||
// - Player IPs
|
||||
// - Internal errors
|
||||
```
|
||||
|
||||
#### 9. Database Migration
|
||||
```
|
||||
Remplacer:
|
||||
users.json → PostgreSQL (+ migrations)
|
||||
RCON history JSON → Database table
|
||||
|
||||
Avantages:
|
||||
- Transactions
|
||||
- Backups
|
||||
- Multi-instance
|
||||
- Better performance
|
||||
```
|
||||
|
||||
#### 10. API Documentation
|
||||
```
|
||||
Ajouter Swagger/OpenAPI
|
||||
- /api-docs
|
||||
- Documentation endpoints
|
||||
- Authentification requirements
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🛠️ Priorité 3: Nice to Have
|
||||
|
||||
#### 11. Monitoring & Alerting
|
||||
```javascript
|
||||
// Backend metrics
|
||||
- Uptime server
|
||||
- RCON connection failures
|
||||
- API response times
|
||||
- Error rate
|
||||
|
||||
// Integration: Prometheus + Grafana
|
||||
// Discord webhooks for alerts
|
||||
```
|
||||
|
||||
#### 12. Admin Audit Log
|
||||
```javascript
|
||||
// Log all admin actions
|
||||
- Login/logout timestamps
|
||||
- Command executor + command
|
||||
- Property changes
|
||||
- File modifications
|
||||
```
|
||||
|
||||
#### 13. World Backup Restoration
|
||||
```javascript
|
||||
// Current: Créer backup ✓
|
||||
// TODO: Restore backup avec safety checks
|
||||
// - Stop serveur
|
||||
// - Restore world/
|
||||
// - Restart serveur
|
||||
// - Verify integrité
|
||||
```
|
||||
|
||||
#### 14. Multi-Admin Support
|
||||
```javascript
|
||||
// Current: 1 seul admin (users.json = array mais UI single)
|
||||
// TODO:
|
||||
// - Multiple admins support
|
||||
// - Role-based access (RBAC)
|
||||
// - Permissions per admin
|
||||
```
|
||||
|
||||
#### 15. Players Statistics
|
||||
```javascript
|
||||
// Ajouter analytics:
|
||||
// - Playtime per player
|
||||
// - Last 7 days activity
|
||||
// - Top builders/killers
|
||||
// - Trends
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Métriques de Santé
|
||||
|
||||
### ✅ Points Forts
|
||||
1. **Architecture modulaire** → 7 routes bien séparées
|
||||
2. **Vanilla JS frontend** → Pas de build complexity
|
||||
3. **RCON protocol bien implémenté** → Pas de pertes commands
|
||||
4. **Session management** → Auth basique ok
|
||||
5. **Docker-ready** → Déploiement facile
|
||||
6. **Markdown docs** → QUICKSTART, DEPLOYMENT, MAINTENANCE
|
||||
7. **Dynamic API URL** → Fonctionne IP ou localhost
|
||||
8. **OP status tracking** → Lecture ops.txt correcte
|
||||
|
||||
### ⚠️ Domaines d'Amélioration
|
||||
1. **Sécurité** → HTTPS, rate limiting, validation input
|
||||
2. **Persistence** → Session, database
|
||||
3. **Error handling** → Logs réseau, retry logic
|
||||
4. **Scalability** → Multi-instance support
|
||||
5. **Observability** → Monitoring, alerting
|
||||
6. **Testing** → Tests unitaires + intégration
|
||||
7. **Code quality** → Linting, type checking
|
||||
8. **Features** → 2FA, RBAC, audit log
|
||||
|
||||
### 📈 Performance
|
||||
- **Frontend**: Single file SPA (~61 KB)
|
||||
- **Backend**: 104 lines server.js, routes <250 lines each
|
||||
- **Response time**: <100ms API (RCON varies)
|
||||
- **Memory**: ~50-100 MB Node.js container
|
||||
- **Concurrency**: Sequential RCON (une commande à la fois)
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
### Current State
|
||||
L'application est **fonctionnelle** pour un usage **LAN privé** ou **réseau interne**.
|
||||
|
||||
**Prêt pour production?** ❌
|
||||
- Manque HTTPS, rate limiting, CSRF
|
||||
- Sessions perdues au redémarrage
|
||||
- Pas de database
|
||||
- Input validation insuffisante
|
||||
|
||||
**Prêt pour LAN interne?** ✅
|
||||
- Sécurité acceptable pour réseau de confiance
|
||||
- Toutes features essentielles présentes
|
||||
- Déploiement Docker simple
|
||||
|
||||
### Priorités à Court Terme
|
||||
1. ✅ **Dynamic API URL** (FAIT: app.js détecte hostname)
|
||||
2. ✅ **OP status tracking** (FAIT: lit ops.txt, affiche ✅/❌)
|
||||
3. ⏳ **Session persistence** (Redis recommended)
|
||||
4. ⏳ **HTTPS + rate limiting** (Pour production)
|
||||
5. ⏳ **Input validation** (Server properties + RCON)
|
||||
|
||||
### Prochaines Étapes Recommandées
|
||||
```
|
||||
Phase 1 (Cette semaine)
|
||||
[ ] Tester OP display après refresh navigateur
|
||||
[ ] Documenter RCON commands disponibles
|
||||
[ ] Ajouter healthcheck serveur Minecraft
|
||||
|
||||
Phase 2 (Semaine 2)
|
||||
[ ] Implémenter Redis pour sessions
|
||||
[ ] Ajouter HTTPS auto-signed
|
||||
[ ] Rate limiting login
|
||||
|
||||
Phase 3 (Semaine 3)
|
||||
[ ] Migrer users.json → Database
|
||||
[ ] Input validation complete
|
||||
[ ] CSRF tokens
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Document généré:** 5 février 2026
|
||||
**Version:** 1.0
|
||||
**Auteur:** GitHub Copilot
|
||||
10
Dockerfile
10
Dockerfile
@@ -2,17 +2,11 @@ FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy npm config first
|
||||
COPY .npmrc ./
|
||||
|
||||
# Copy package files
|
||||
COPY backend/package*.json ./
|
||||
|
||||
# Install dependencies with retry logic
|
||||
RUN npm cache clean --force && \
|
||||
npm install --no-optional --legacy-peer-deps 2>&1 | tail -20 && \
|
||||
ls -la node_modules/express || npm install express && \
|
||||
test -d node_modules/express || (echo "FATAL: express not found" && exit 1)
|
||||
# Install dependencies
|
||||
RUN npm install --production
|
||||
|
||||
# Copy application code
|
||||
COPY backend/src ./src
|
||||
|
||||
1182
TECHNICAL_REVIEW.md
Normal file
1182
TECHNICAL_REVIEW.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,8 @@
|
||||
NODE_ENV=development
|
||||
PORT=3000
|
||||
NODE_ENV=production
|
||||
PORT=4001
|
||||
SESSION_SECRET=your-very-secret-session-key-change-in-production
|
||||
SERVER_DIR=/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red
|
||||
# LOCAL: /home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red
|
||||
# REMOTE: /home/innotex/NationsGloryRED/NationsGlory_ServeurBuild_Red
|
||||
SERVER_DIR=/home/innotex/NationsGloryRED/NationsGlory_ServeurBuild_Red
|
||||
RCON_HOST=localhost
|
||||
RCON_PORT=25575
|
||||
|
||||
@@ -5,7 +5,7 @@ const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
const USERS_FILE = path.join(__dirname, '../../data/users.json');
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red';
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/mc-server';
|
||||
|
||||
async function initUsersFile() {
|
||||
await fs.ensureDir(path.dirname(USERS_FILE));
|
||||
|
||||
@@ -4,7 +4,7 @@ const path = require('path');
|
||||
const { exec } = require('child_process');
|
||||
|
||||
const router = express.Router();
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red';
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/mc-server';
|
||||
|
||||
function isAuthenticated(req, res, next) {
|
||||
if (req.session.user) {
|
||||
|
||||
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red';
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/mc-server';
|
||||
|
||||
function isAuthenticated(req, res, next) {
|
||||
if (req.session.user) {
|
||||
|
||||
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red';
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/mc-server';
|
||||
|
||||
function isAuthenticated(req, res, next) {
|
||||
if (req.session && req.session.user) {
|
||||
@@ -46,6 +46,21 @@ router.get('/', isAuthenticated, async (req, res) => {
|
||||
console.warn('usercache.json non trouvé:', usercacheFile);
|
||||
}
|
||||
|
||||
// Récupérer les OP depuis ops.txt
|
||||
let ops = [];
|
||||
const opsFile = path.join(SERVER_DIR, 'ops.txt');
|
||||
if (await fs.pathExists(opsFile)) {
|
||||
try {
|
||||
const opsContent = await fs.readFile(opsFile, 'utf8');
|
||||
ops = opsContent
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0);
|
||||
} catch (e) {
|
||||
console.error('Erreur lecture ops.txt:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer les stats de dernière connexion
|
||||
const statsDir = path.join(SERVER_DIR, 'world', 'stats');
|
||||
let statsByUuid = {};
|
||||
@@ -71,9 +86,11 @@ router.get('/', isAuthenticated, async (req, res) => {
|
||||
|
||||
const players = playerFiles.map(file => {
|
||||
const uuid = file.replace('.dat', '');
|
||||
const playerName = usercache[uuid] || 'Inconnu';
|
||||
return {
|
||||
uuid,
|
||||
name: usercache[uuid] || 'Inconnu',
|
||||
name: playerName,
|
||||
isOp: ops.includes(playerName),
|
||||
lastPlayed: new Date() // TODO: Extraire du fichier .dat si possible
|
||||
};
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red';
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/mc-server';
|
||||
|
||||
function isAuthenticated(req, res, next) {
|
||||
if (req.session && req.session.user) {
|
||||
@@ -20,9 +20,9 @@ async function getRconConfig() {
|
||||
const content = await fs.readFile(serverPropsFile, 'utf-8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
// Utiliser RCON_HOST depuis les variables d'environnement ou localhost
|
||||
// Utiliser RCON_HOST depuis les variables d'environnement (172.17.0.1 = Docker bridge gateway)
|
||||
let config = {
|
||||
host: process.env.RCON_HOST || 'localhost',
|
||||
host: process.env.RCON_HOST || '172.17.0.1',
|
||||
port: process.env.RCON_PORT || 25575,
|
||||
password: ''
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red';
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/mc-server';
|
||||
|
||||
function isAuthenticated(req, res, next) {
|
||||
if (req.session.user) {
|
||||
|
||||
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const router = express.Router();
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red';
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/mc-server';
|
||||
|
||||
function isAuthenticated(req, res, next) {
|
||||
if (req.session.user) {
|
||||
|
||||
@@ -9,7 +9,7 @@ dotenv.config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 4001;
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/home/innotex/Documents/Projet/Serveur NationsGlory/NationsGlory_ServeurBuild_Red';
|
||||
const SERVER_DIR = process.env.SERVER_DIR || '/mc-server';
|
||||
|
||||
// Déterminer le chemin du frontend (support Docker et local)
|
||||
const frontendPath = (() => {
|
||||
@@ -28,8 +28,15 @@ const frontendPath = (() => {
|
||||
// Middlewares
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// CORS dynamique pour supporter n'importe quelle origine
|
||||
app.use(cors({
|
||||
origin: true,
|
||||
origin: (origin, callback) => {
|
||||
// Autoriser toutes les origines en mode production
|
||||
// ou l'origine spécifiée dans les variables d'environnement
|
||||
const allowedOrigin = process.env.CORS_ORIGIN || true;
|
||||
callback(null, allowedOrigin);
|
||||
},
|
||||
credentials: true
|
||||
}));
|
||||
|
||||
@@ -95,8 +102,8 @@ app.use((err, req, res, next) => {
|
||||
res.status(err.status || 500).json({ error: 'Erreur serveur interne', details: err.message });
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Backend Admin NationsGlory démarré sur http://localhost:${PORT}`);
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`Backend Admin NationsGlory démarré sur http://0.0.0.0:${PORT}`);
|
||||
console.log(`Répertoire du serveur: ${SERVER_DIR}`);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,20 +1,35 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: webnationsglory-admin
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 4001
|
||||
NODE_ENV: ${NODE_ENV:-production}
|
||||
PORT: ${PORT:-4001}
|
||||
SERVER_DIR: /mc-server
|
||||
RCON_HOST: localhost
|
||||
RCON_PORT: 25575
|
||||
SESSION_SECRET: change-this-in-production
|
||||
RCON_HOST: ${RCON_HOST:-localhost}
|
||||
RCON_PORT: ${RCON_PORT:-25575}
|
||||
RCON_PASSWORD: ${RCON_PASSWORD:-minecraft}
|
||||
SESSION_SECRET: ${SESSION_SECRET:-your-secret-key-change-in-production}
|
||||
volumes:
|
||||
- /NationsGloryRED/NationsGlory_ServeurBuild_Red:/mc-server
|
||||
- web-admin:/mc-server/.web-admin
|
||||
restart: unless-stopped
|
||||
# Montage relatif du serveur MC (depuis le dossier parent)
|
||||
- ${MC_SERVER_PATH:-../NationsGlory_ServeurBuild_Red}:/mc-server:ro
|
||||
- web-admin-data:/app/data
|
||||
ports:
|
||||
- "${PORT:-4001}:${PORT:-4001}"
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:${PORT:-4001}/api/health || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
volumes:
|
||||
web-admin:
|
||||
web-admin-data:
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
// Configuration API
|
||||
const API_URL = 'http://localhost:4001/api';
|
||||
// Configuration API - Détection automatique de l'URL de base
|
||||
// Fonctionne avec localhost, IP publique, ou nom de domaine
|
||||
const API_URL = (() => {
|
||||
const protocol = window.location.protocol; // http: ou https:
|
||||
const hostname = window.location.hostname; // IP ou domaine
|
||||
const port = '4001'; // Port du backend
|
||||
|
||||
// Si on accède depuis le même port que le backend (mode production intégré)
|
||||
if (window.location.port === port) {
|
||||
return `${protocol}//${hostname}:${port}/api`;
|
||||
}
|
||||
|
||||
// Mode développement ou accès via différent port
|
||||
return `${protocol}//${hostname}:${port}/api`;
|
||||
})();
|
||||
|
||||
console.log('API URL configurée:', API_URL);
|
||||
|
||||
// State
|
||||
let currentUser = null;
|
||||
@@ -889,12 +904,13 @@ function getPlayersHTML() {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nom</th>
|
||||
<th>OP</th>
|
||||
<th>UUID</th>
|
||||
<th>Dernière Connexion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="playersTable">
|
||||
<tr><td colspan="3" style="text-align: center;">Chargement...</td></tr>
|
||||
<tr><td colspan="4" style="text-align: center;">Chargement...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -922,12 +938,13 @@ async function loadPlayersData() {
|
||||
table.innerHTML = data.players.map(p => `
|
||||
<tr>
|
||||
<td>${p.name}</td>
|
||||
<td style="text-align: center;">${p.isOp ? '✅ OP' : '❌'}</td>
|
||||
<td><code style="font-size: 11px;">${p.uuid}</code></td>
|
||||
<td>${new Date(p.lastPlayed).toLocaleString()}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
} else {
|
||||
table.innerHTML = '<tr><td colspan="3" style="text-align: center;">Aucun joueur</td></tr>';
|
||||
table.innerHTML = '<tr><td colspan="4" style="text-align: center;">Aucun joueur</td></tr>';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Erreur joueurs:', e);
|
||||
@@ -1016,15 +1033,46 @@ window.loadOnlinePlayers = async function() {
|
||||
`;
|
||||
|
||||
if (onlineCount > 0 && playerNames.length > 0) {
|
||||
listDiv.innerHTML = `
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin-top: 15px;">
|
||||
${playerNames.map(name => `
|
||||
<div style="padding: 12px; background: white; border: 2px solid #4CAF50; border-radius: 8px; text-align: center;">
|
||||
<span style="font-weight: bold; color: #333;">🎮 ${name}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
// Charger les infos des joueurs pour voir qui est OP
|
||||
try {
|
||||
const playersResponse = await fetch(`${API_URL}/players`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
let playersData = await playersResponse.json();
|
||||
let playersByName = {};
|
||||
if (playersData.players) {
|
||||
playersByName = playersData.players.reduce((acc, p) => {
|
||||
acc[p.name] = p;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
listDiv.innerHTML = `
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin-top: 15px;">
|
||||
${playerNames.map(name => {
|
||||
const playerInfo = playersByName[name];
|
||||
const isOp = playerInfo && playerInfo.isOp;
|
||||
return `
|
||||
<div style="padding: 12px; background: white; border: 2px solid ${isOp ? '#FFD700' : '#4CAF50'}; border-radius: 8px; text-align: center;">
|
||||
<span style="font-weight: bold; color: #333;">🎮 ${name}</span>
|
||||
${isOp ? '<div style="margin-top: 5px; font-size: 12px; color: #FFD700; font-weight: bold;">👑 OP</div>' : ''}
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
`;
|
||||
} catch (e) {
|
||||
console.error('Erreur chargement infos joueurs:', e);
|
||||
listDiv.innerHTML = `
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin-top: 15px;">
|
||||
${playerNames.map(name => `
|
||||
<div style="padding: 12px; background: white; border: 2px solid #4CAF50; border-radius: 8px; text-align: center;">
|
||||
<span style="font-weight: bold; color: #333;">🎮 ${name}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
listDiv.innerHTML = '';
|
||||
}
|
||||
|
||||
16
nationglory-admin.service
Normal file
16
nationglory-admin.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=NationsGlory Admin Web Interface
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=innotex
|
||||
WorkingDirectory=/home/innotex/WebNationsGlory_Deploy/backend
|
||||
ExecStart=/usr/bin/node src/server.js
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment="NODE_ENV=production"
|
||||
Environment="PORT=4001"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
118
nginx.conf.example
Normal file
118
nginx.conf.example
Normal file
@@ -0,0 +1,118 @@
|
||||
# ====================================
|
||||
# Configuration Nginx - NationsGlory Web Admin
|
||||
# ====================================
|
||||
#
|
||||
# Ce fichier fournit une configuration exemple pour utiliser nginx
|
||||
# comme reverse proxy devant l'application web admin.
|
||||
#
|
||||
# Avantages :
|
||||
# - Support HTTPS/SSL
|
||||
# - Cache des assets statiques
|
||||
# - Compression gzip
|
||||
# - Sécurité renforcée
|
||||
#
|
||||
# Installation :
|
||||
# 1. Copiez ce fichier dans /etc/nginx/sites-available/nationsglory
|
||||
# 2. Modifiez les domaines et chemins SSL
|
||||
# 3. Créez le lien symbolique : ln -s /etc/nginx/sites-available/nationsglory /etc/nginx/sites-enabled/
|
||||
# 4. Testez : nginx -t
|
||||
# 5. Rechargez : systemctl reload nginx
|
||||
|
||||
# Redirection HTTP vers HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name votre-domaine.com www.votre-domaine.com;
|
||||
|
||||
# Redirection vers HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
# Configuration HTTPS
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name votre-domaine.com www.votre-domaine.com;
|
||||
|
||||
# Certificats SSL (Let's Encrypt ou autre)
|
||||
ssl_certificate /etc/letsencrypt/live/votre-domaine.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/votre-domaine.com/privkey.pem;
|
||||
|
||||
# Configuration SSL moderne et sécurisée
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# HSTS (optionnel mais recommandé)
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Sécurité supplémentaire
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Logs
|
||||
access_log /var/log/nginx/nationsglory-access.log;
|
||||
error_log /var/log/nginx/nationsglory-error.log;
|
||||
|
||||
# Taille maximale des uploads (pour les backups)
|
||||
client_max_body_size 500M;
|
||||
|
||||
# Proxy vers l'application Node.js
|
||||
location / {
|
||||
proxy_pass http://localhost:4001;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Headers pour le proxy
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# Support WebSocket (si nécessaire dans le futur)
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# Cache des assets statiques
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
||||
proxy_pass http://localhost:4001;
|
||||
proxy_cache_valid 200 7d;
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Compression gzip
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
|
||||
}
|
||||
|
||||
# Configuration alternative : Accès par sous-domaine
|
||||
# Décommentez si vous voulez utiliser admin.votre-domaine.com
|
||||
#
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# listen [::]:443 ssl http2;
|
||||
# server_name admin.votre-domaine.com;
|
||||
#
|
||||
# ssl_certificate /etc/letsencrypt/live/votre-domaine.com/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/votre-domaine.com/privkey.pem;
|
||||
#
|
||||
# # Même configuration que ci-dessus
|
||||
# location / {
|
||||
# proxy_pass http://localhost:4001;
|
||||
# # ... (même configuration proxy)
|
||||
# }
|
||||
# }
|
||||
Reference in New Issue
Block a user