try this shi
This commit is contained in:
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Minecraft data
|
||||||
|
data/
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
101
QUICKSTART.md
Normal file
101
QUICKSTART.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# ⚡ Quick Start Guide
|
||||||
|
|
||||||
|
Get your Minecraft Dashboard up and running in 3 simple steps!
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
- Docker and Docker Compose installed
|
||||||
|
- Minecraft server running on `mcnet` network
|
||||||
|
|
||||||
|
## 🚀 3-Step Setup
|
||||||
|
|
||||||
|
### 1️⃣ Start Your Minecraft Server
|
||||||
|
|
||||||
|
If not already running, start your Minecraft server with the provided configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.minecraft.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2️⃣ Launch the Dashboard
|
||||||
|
|
||||||
|
Simply run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start-dashboard.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
- Create the `mcnet` network (if it doesn't exist)
|
||||||
|
- Build the frontend and backend containers
|
||||||
|
- Start all dashboard services
|
||||||
|
|
||||||
|
### 3️⃣ Open the Dashboard
|
||||||
|
|
||||||
|
Navigate to in your browser:
|
||||||
|
```
|
||||||
|
http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎮 Using the Dashboard
|
||||||
|
|
||||||
|
### Features:
|
||||||
|
- **🟢 Server Status** - See if your server is online
|
||||||
|
- **👥 Player Count** - Monitor active players
|
||||||
|
- **▶️ Start Button** - Start the Minecraft server
|
||||||
|
- **⏹️ Stop Button** - Gracefully stop the server
|
||||||
|
- **🔄 Auto-Refresh** - Updates every 5 seconds
|
||||||
|
|
||||||
|
### Controls:
|
||||||
|
- Click **START SERVER** to boot up your Minecraft server
|
||||||
|
- Click **STOP SERVER** to safely shut it down (requires confirmation)
|
||||||
|
- Click **🔄** to manually refresh the status
|
||||||
|
|
||||||
|
## 🛑 Stopping the Dashboard
|
||||||
|
|
||||||
|
When you're done:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./stop-dashboard.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.dashboard.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Useful Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View logs
|
||||||
|
docker-compose -f docker-compose.dashboard.yml logs -f
|
||||||
|
|
||||||
|
# Restart the dashboard
|
||||||
|
docker-compose -f docker-compose.dashboard.yml restart
|
||||||
|
|
||||||
|
# Rebuild after making changes
|
||||||
|
docker-compose -f docker-compose.dashboard.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
## ❓ Common Issues
|
||||||
|
|
||||||
|
**Dashboard shows "Server Offline"**
|
||||||
|
- Make sure Minecraft server is running: `docker ps | grep mc-java`
|
||||||
|
- Verify RCON is enabled in your Minecraft configuration
|
||||||
|
|
||||||
|
**Start/Stop buttons don't work**
|
||||||
|
- Check Docker socket access: `docker inspect mc-dashboard-backend | grep docker.sock`
|
||||||
|
|
||||||
|
**Can't access dashboard**
|
||||||
|
- Verify port 8080 is not in use: `lsof -i :8080`
|
||||||
|
- Check if frontend is running: `docker ps | grep mc-dashboard-frontend`
|
||||||
|
|
||||||
|
## 📖 Need More Help?
|
||||||
|
|
||||||
|
See the full documentation:
|
||||||
|
- [README.md](README.md) - Complete documentation
|
||||||
|
- [SETUP.md](SETUP.md) - Detailed setup guide
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Enjoy your Minecraft Dashboard! 🎮⛏️
|
||||||
|
|
||||||
218
README.md
Normal file
218
README.md
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# 🎮 Minecraft Server Dashboard
|
||||||
|
|
||||||
|
A beautiful, modern web dashboard to monitor and control your Minecraft server with real-time statistics and server management.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- 🎯 **Real-time Server Monitoring**
|
||||||
|
- Player count and online players list
|
||||||
|
- Server version and status
|
||||||
|
- Latency monitoring
|
||||||
|
- TPS (Ticks Per Second) tracking
|
||||||
|
|
||||||
|
- 🎮 **Server Control**
|
||||||
|
- Start server with one click
|
||||||
|
- Stop server gracefully (with confirmation)
|
||||||
|
- Automatic status refresh every 5 seconds
|
||||||
|
|
||||||
|
- 🎨 **Beautiful UI**
|
||||||
|
- Modern gradient design
|
||||||
|
- Minecraft-themed color palette
|
||||||
|
- Responsive layout for all devices
|
||||||
|
- Smooth animations and transitions
|
||||||
|
- Real-time status indicators
|
||||||
|
|
||||||
|
## 🏗️ Architecture
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- **Node.js + Express** - RESTful API server
|
||||||
|
- **RCON Integration** - Direct communication with Minecraft server
|
||||||
|
- **Docker API** - Container management for start/stop functionality
|
||||||
|
- **minecraft-server-util** - Server status queries
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- **React 18** - Modern UI framework
|
||||||
|
- **Vite** - Lightning-fast build tool
|
||||||
|
- **Tailwind CSS** - Utility-first styling
|
||||||
|
- **Lucide React** - Beautiful icons
|
||||||
|
- **Axios** - HTTP client
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Docker and Docker Compose installed
|
||||||
|
- Existing Minecraft server running on `mcnet` network
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. **Clone or navigate to the project directory**
|
||||||
|
```bash
|
||||||
|
cd mc
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Start your Minecraft server** (if not already running)
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.minecraft.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Build and start the dashboard**
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.dashboard.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Access the dashboard**
|
||||||
|
- Open your browser to: `http://localhost:8080`
|
||||||
|
|
||||||
|
### Development Mode
|
||||||
|
|
||||||
|
For local development without Docker:
|
||||||
|
|
||||||
|
**Backend:**
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frontend:**
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
mc/
|
||||||
|
├── backend/
|
||||||
|
│ ├── src/
|
||||||
|
│ │ └── index.js # Express API server
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ └── package.json
|
||||||
|
├── frontend/
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── App.jsx # Main React component
|
||||||
|
│ │ ├── main.jsx
|
||||||
|
│ │ ├── index.css
|
||||||
|
│ │ └── App.css
|
||||||
|
│ ├── public/
|
||||||
|
│ ├── index.html
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── nginx.conf
|
||||||
|
│ ├── vite.config.js
|
||||||
|
│ └── package.json
|
||||||
|
├── docker-compose.dashboard.yml
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Configuration
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
Create a `.env` file based on `.env.example`:
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Minecraft Server
|
||||||
|
MINECRAFT_HOST=mc-java
|
||||||
|
MINECRAFT_PORT=25565
|
||||||
|
RCON_PORT=25575
|
||||||
|
RCON_PASSWORD=bethureddy1
|
||||||
|
CONTAINER_NAME=mc-java
|
||||||
|
|
||||||
|
# Dashboard
|
||||||
|
DASHBOARD_PORT=8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Configuration
|
||||||
|
|
||||||
|
The dashboard connects to your Minecraft server via the `mcnet` Docker network. Make sure your Minecraft server is on this network:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
mcnet:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 API Endpoints
|
||||||
|
|
||||||
|
### GET `/api/status`
|
||||||
|
Returns Minecraft server status including:
|
||||||
|
- Online status
|
||||||
|
- Player count and list
|
||||||
|
- Server version
|
||||||
|
- Latency
|
||||||
|
- TPS (if available)
|
||||||
|
- MOTD
|
||||||
|
|
||||||
|
### GET `/api/container/status`
|
||||||
|
Returns Docker container status:
|
||||||
|
- Running state
|
||||||
|
- Container health
|
||||||
|
- Start time
|
||||||
|
|
||||||
|
### POST `/api/server/start`
|
||||||
|
Starts the Minecraft server container
|
||||||
|
|
||||||
|
### POST `/api/server/stop`
|
||||||
|
Gracefully stops the Minecraft server (saves world first)
|
||||||
|
|
||||||
|
## 🎨 UI Components
|
||||||
|
|
||||||
|
### Status Cards
|
||||||
|
- **Players** - Current player count vs max players
|
||||||
|
- **Version** - Minecraft server version
|
||||||
|
- **Latency** - Server response time
|
||||||
|
- **TPS** - Server performance metric
|
||||||
|
|
||||||
|
### Control Panel
|
||||||
|
- **Start Button** - Starts the server (disabled when running)
|
||||||
|
- **Stop Button** - Stops the server with confirmation (disabled when stopped)
|
||||||
|
- **Refresh Button** - Manually refresh status
|
||||||
|
|
||||||
|
### Player List
|
||||||
|
- Displays all currently online players
|
||||||
|
- Beautiful avatar placeholders
|
||||||
|
- Grid layout for easy viewing
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### Dashboard can't connect to Minecraft server
|
||||||
|
- Ensure Minecraft server is running: `docker ps | grep mc-java`
|
||||||
|
- Verify both containers are on `mcnet` network
|
||||||
|
- Check RCON is enabled in server configuration
|
||||||
|
|
||||||
|
### Start/Stop buttons not working
|
||||||
|
- Ensure backend has access to Docker socket
|
||||||
|
- Verify volume mount: `/var/run/docker.sock:/var/run/docker.sock`
|
||||||
|
- Check container name matches: `CONTAINER_NAME=mc-java`
|
||||||
|
|
||||||
|
### RCON connection failed
|
||||||
|
- Verify RCON password matches Minecraft server
|
||||||
|
- Check RCON port (default: 25575)
|
||||||
|
- Ensure RCON is enabled in server.properties
|
||||||
|
|
||||||
|
## 🔒 Security Notes
|
||||||
|
|
||||||
|
- The dashboard requires access to Docker socket for start/stop functionality
|
||||||
|
- RCON password is configured via environment variables
|
||||||
|
- Consider using Docker secrets for production deployments
|
||||||
|
- Restrict dashboard access using firewall rules or reverse proxy authentication
|
||||||
|
|
||||||
|
## 📝 License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Feel free to submit issues and pull requests.
|
||||||
|
|
||||||
|
## 💖 Acknowledgments
|
||||||
|
|
||||||
|
- Built with React and Node.js
|
||||||
|
- Minecraft server status via minecraft-server-util
|
||||||
|
- RCON implementation using modern-rcon
|
||||||
|
- Icons by Lucide React
|
||||||
|
|
||||||
241
SETUP.md
Normal file
241
SETUP.md
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
# 🚀 Setup Guide for Minecraft Dashboard
|
||||||
|
|
||||||
|
## Quick Setup (Recommended)
|
||||||
|
|
||||||
|
### Step 1: Ensure Minecraft Server is Running
|
||||||
|
|
||||||
|
First, make sure your Minecraft server is running. Save this as `docker-compose.minecraft.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
minecraft:
|
||||||
|
image: itzg/minecraft-server:latest
|
||||||
|
container_name: mc-java
|
||||||
|
environment:
|
||||||
|
EULA: "TRUE"
|
||||||
|
ENABLE_RCON: "true"
|
||||||
|
RCON_PASSWORD: "bethureddy1"
|
||||||
|
RCON_PORT: "25575"
|
||||||
|
TZ: "America/Chicago"
|
||||||
|
TYPE: "PAPER"
|
||||||
|
VERSION: "1.21.10"
|
||||||
|
MEMORY: "20G"
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
expose:
|
||||||
|
- "25565"
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- mcnet
|
||||||
|
|
||||||
|
nginx-stream:
|
||||||
|
image: nginx:1.27-alpine
|
||||||
|
container_name: mc-proxy
|
||||||
|
depends_on:
|
||||||
|
- minecraft
|
||||||
|
ports:
|
||||||
|
- "25565:25565/tcp"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- mcnet
|
||||||
|
|
||||||
|
networks:
|
||||||
|
mcnet:
|
||||||
|
driver: bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the Minecraft server:
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.minecraft.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Start the Dashboard
|
||||||
|
|
||||||
|
Make the start script executable and run it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x start-dashboard.sh
|
||||||
|
./start-dashboard.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.dashboard.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Access the Dashboard
|
||||||
|
|
||||||
|
Open your browser and navigate to:
|
||||||
|
```
|
||||||
|
http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 What You'll See
|
||||||
|
|
||||||
|
The dashboard displays:
|
||||||
|
|
||||||
|
1. **Server Status** - Real-time online/offline indicator
|
||||||
|
2. **Control Buttons** - Start and Stop your server
|
||||||
|
3. **Player Count** - Current players vs max capacity
|
||||||
|
4. **Server Version** - Minecraft version running
|
||||||
|
5. **Latency** - Server response time
|
||||||
|
6. **TPS** - Server performance (if available)
|
||||||
|
7. **Online Players List** - Names of all connected players
|
||||||
|
8. **MOTD** - Server message of the day
|
||||||
|
|
||||||
|
## 🛠️ Troubleshooting
|
||||||
|
|
||||||
|
### Dashboard shows "Server Offline" but server is running
|
||||||
|
|
||||||
|
1. Check if both containers are on the same network:
|
||||||
|
```bash
|
||||||
|
docker network inspect mcnet
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Verify RCON is enabled on the Minecraft server:
|
||||||
|
```bash
|
||||||
|
docker exec mc-java cat /data/server.properties | grep rcon
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Check backend logs:
|
||||||
|
```bash
|
||||||
|
docker logs mc-dashboard-backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start/Stop buttons don't work
|
||||||
|
|
||||||
|
The backend needs access to the Docker socket. Verify the volume mount:
|
||||||
|
```bash
|
||||||
|
docker inspect mc-dashboard-backend | grep docker.sock
|
||||||
|
```
|
||||||
|
|
||||||
|
Should show: `/var/run/docker.sock:/var/run/docker.sock`
|
||||||
|
|
||||||
|
### Dashboard won't load
|
||||||
|
|
||||||
|
1. Check if frontend is running:
|
||||||
|
```bash
|
||||||
|
docker ps | grep mc-dashboard-frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Check frontend logs:
|
||||||
|
```bash
|
||||||
|
docker logs mc-dashboard-frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Verify port 8080 is not in use:
|
||||||
|
```bash
|
||||||
|
lsof -i :8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Updates and Maintenance
|
||||||
|
|
||||||
|
### Rebuild the Dashboard
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.dashboard.yml up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
```bash
|
||||||
|
# All services
|
||||||
|
docker-compose -f docker-compose.dashboard.yml logs -f
|
||||||
|
|
||||||
|
# Backend only
|
||||||
|
docker logs -f mc-dashboard-backend
|
||||||
|
|
||||||
|
# Frontend only
|
||||||
|
docker logs -f mc-dashboard-frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stop the Dashboard
|
||||||
|
```bash
|
||||||
|
./stop-dashboard.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
```bash
|
||||||
|
docker-compose -f docker-compose.dashboard.yml down
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 Security Considerations
|
||||||
|
|
||||||
|
1. **Change RCON Password**: Update `RCON_PASSWORD` in both your Minecraft and dashboard configurations
|
||||||
|
2. **Firewall**: Only expose port 8080 to trusted networks
|
||||||
|
3. **Reverse Proxy**: Consider using nginx/traefik with authentication for production
|
||||||
|
4. **Docker Socket**: The backend needs Docker socket access - only run in trusted environments
|
||||||
|
|
||||||
|
## 📊 Port Usage
|
||||||
|
|
||||||
|
- **8080** - Dashboard web interface
|
||||||
|
- **3001** - Backend API (internal only)
|
||||||
|
- **25565** - Minecraft server
|
||||||
|
- **25575** - RCON (internal only)
|
||||||
|
|
||||||
|
## 🎨 Customization
|
||||||
|
|
||||||
|
### Change Dashboard Port
|
||||||
|
|
||||||
|
Edit `docker-compose.dashboard.yml`:
|
||||||
|
```yaml
|
||||||
|
mc-dashboard-frontend:
|
||||||
|
ports:
|
||||||
|
- "3000:80" # Change 8080 to your preferred port
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Refresh Interval
|
||||||
|
|
||||||
|
Edit `frontend/src/App.jsx`, line with `setInterval`:
|
||||||
|
```javascript
|
||||||
|
const interval = setInterval(fetchStatus, 10000) // 10 seconds instead of 5
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐳 Docker Commands Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start everything
|
||||||
|
docker-compose -f docker-compose.dashboard.yml up -d
|
||||||
|
|
||||||
|
# Stop everything
|
||||||
|
docker-compose -f docker-compose.dashboard.yml down
|
||||||
|
|
||||||
|
# Rebuild and restart
|
||||||
|
docker-compose -f docker-compose.dashboard.yml up -d --build
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker-compose -f docker-compose.dashboard.yml logs -f
|
||||||
|
|
||||||
|
# Restart a specific service
|
||||||
|
docker-compose -f docker-compose.dashboard.yml restart mc-dashboard-backend
|
||||||
|
|
||||||
|
# Remove everything including volumes
|
||||||
|
docker-compose -f docker-compose.dashboard.yml down -v
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✅ Verification Checklist
|
||||||
|
|
||||||
|
- [ ] Minecraft server is running
|
||||||
|
- [ ] RCON is enabled on Minecraft server
|
||||||
|
- [ ] Both services are on `mcnet` network
|
||||||
|
- [ ] Backend has Docker socket access
|
||||||
|
- [ ] Port 8080 is available
|
||||||
|
- [ ] Dashboard shows "Server Online"
|
||||||
|
- [ ] Start/Stop buttons work
|
||||||
|
- [ ] Player count updates
|
||||||
|
|
||||||
|
## 🆘 Getting Help
|
||||||
|
|
||||||
|
If you encounter issues:
|
||||||
|
|
||||||
|
1. Check logs: `docker-compose -f docker-compose.dashboard.yml logs`
|
||||||
|
2. Verify network: `docker network inspect mcnet`
|
||||||
|
3. Test RCON: `docker exec mc-java rcon-cli -p bethureddy1 list`
|
||||||
|
4. Restart everything: `./stop-dashboard.sh && ./start-dashboard.sh`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Happy Minecrafting! 🎮⛏️
|
||||||
|
|
||||||
14
backend/Dockerfile
Normal file
14
backend/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
RUN npm install --production
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 3001
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
|
|
||||||
19
backend/package.json
Normal file
19
backend/package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "mc-dashboard-backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Minecraft Server Dashboard Backend",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node src/index.js",
|
||||||
|
"dev": "node --watch src/index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"minecraft-server-util": "^5.4.3",
|
||||||
|
"modern-rcon": "^1.2.1",
|
||||||
|
"dockerode": "^4.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
169
backend/src/index.js
Normal file
169
backend/src/index.js
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
import { status } from 'minecraft-server-util';
|
||||||
|
import Rcon from 'modern-rcon';
|
||||||
|
import Docker from 'dockerode';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.PORT || 3001;
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
const MINECRAFT_HOST = process.env.MINECRAFT_HOST || 'mc-java';
|
||||||
|
const MINECRAFT_PORT = parseInt(process.env.MINECRAFT_PORT || '25565');
|
||||||
|
const RCON_PORT = parseInt(process.env.RCON_PORT || '25575');
|
||||||
|
const RCON_PASSWORD = process.env.RCON_PASSWORD || 'bethureddy1';
|
||||||
|
const CONTAINER_NAME = process.env.CONTAINER_NAME || 'mc-java';
|
||||||
|
|
||||||
|
const docker = new Docker({ socketPath: '/var/run/docker.sock' });
|
||||||
|
|
||||||
|
// Get server status
|
||||||
|
app.get('/api/status', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const response = await status(MINECRAFT_HOST, MINECRAFT_PORT, {
|
||||||
|
timeout: 5000,
|
||||||
|
enableSRV: true
|
||||||
|
});
|
||||||
|
|
||||||
|
let playerList = [];
|
||||||
|
let tps = null;
|
||||||
|
let usedMemory = null;
|
||||||
|
let maxMemory = null;
|
||||||
|
|
||||||
|
// Try to get additional info via RCON if server is online
|
||||||
|
try {
|
||||||
|
const rcon = new Rcon(MINECRAFT_HOST, RCON_PORT, RCON_PASSWORD);
|
||||||
|
await rcon.connect();
|
||||||
|
|
||||||
|
// Get player list
|
||||||
|
const listResponse = await rcon.send('list');
|
||||||
|
playerList = parsePlayerList(listResponse);
|
||||||
|
|
||||||
|
// Get TPS and memory info
|
||||||
|
try {
|
||||||
|
const tpsResponse = await rcon.send('spark tps');
|
||||||
|
tps = parseTPS(tpsResponse);
|
||||||
|
} catch (e) {
|
||||||
|
// spark might not be installed
|
||||||
|
}
|
||||||
|
|
||||||
|
await rcon.disconnect();
|
||||||
|
} catch (rconError) {
|
||||||
|
console.error('RCON error:', rconError.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
online: true,
|
||||||
|
version: response.version.name,
|
||||||
|
protocol: response.version.protocol,
|
||||||
|
players: {
|
||||||
|
online: response.players.online,
|
||||||
|
max: response.players.max,
|
||||||
|
list: playerList
|
||||||
|
},
|
||||||
|
motd: response.motd.clean,
|
||||||
|
latency: response.roundTripLatency,
|
||||||
|
tps: tps,
|
||||||
|
memory: {
|
||||||
|
used: usedMemory,
|
||||||
|
max: maxMemory
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Status error:', error.message);
|
||||||
|
res.json({
|
||||||
|
online: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get container status
|
||||||
|
app.get('/api/container/status', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const container = docker.getContainer(CONTAINER_NAME);
|
||||||
|
const info = await container.inspect();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
running: info.State.Running,
|
||||||
|
status: info.State.Status,
|
||||||
|
startedAt: info.State.StartedAt,
|
||||||
|
health: info.State.Health?.Status || 'unknown'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Container status error:', error.message);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the Minecraft server
|
||||||
|
app.post('/api/server/start', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const container = docker.getContainer(CONTAINER_NAME);
|
||||||
|
const info = await container.inspect();
|
||||||
|
|
||||||
|
if (info.State.Running) {
|
||||||
|
return res.json({ success: false, message: 'Server is already running' });
|
||||||
|
}
|
||||||
|
|
||||||
|
await container.start();
|
||||||
|
res.json({ success: true, message: 'Server started successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Start error:', error.message);
|
||||||
|
res.status(500).json({ success: false, error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stop the Minecraft server
|
||||||
|
app.post('/api/server/stop', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const container = docker.getContainer(CONTAINER_NAME);
|
||||||
|
const info = await container.inspect();
|
||||||
|
|
||||||
|
if (!info.State.Running) {
|
||||||
|
return res.json({ success: false, message: 'Server is already stopped' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try graceful shutdown via RCON first
|
||||||
|
try {
|
||||||
|
const rcon = new Rcon(MINECRAFT_HOST, RCON_PORT, RCON_PASSWORD);
|
||||||
|
await rcon.connect();
|
||||||
|
await rcon.send('save-all');
|
||||||
|
await rcon.send('stop');
|
||||||
|
await rcon.disconnect();
|
||||||
|
} catch (rconError) {
|
||||||
|
console.error('RCON shutdown error, using docker stop:', rconError.message);
|
||||||
|
await container.stop({ t: 30 }); // 30 second timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ success: true, message: 'Server stopped successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Stop error:', error.message);
|
||||||
|
res.status(500).json({ success: false, error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function to parse player list from RCON
|
||||||
|
function parsePlayerList(response) {
|
||||||
|
const match = response.match(/There are \d+ of a max of \d+ players online: (.*)/);
|
||||||
|
if (match && match[1]) {
|
||||||
|
return match[1].split(', ').filter(name => name.length > 0);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to parse TPS (if spark is installed)
|
||||||
|
function parseTPS(response) {
|
||||||
|
const match = response.match(/TPS.*?(\d+\.?\d*)/);
|
||||||
|
if (match) {
|
||||||
|
return parseFloat(match[1]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.listen(PORT, '0.0.0.0', () => {
|
||||||
|
console.log(`🚀 Minecraft Dashboard Backend running on port ${PORT}`);
|
||||||
|
console.log(`📡 Monitoring Minecraft server at ${MINECRAFT_HOST}:${MINECRAFT_PORT}`);
|
||||||
|
});
|
||||||
|
|
||||||
34
docker-compose.dashboard.yml
Normal file
34
docker-compose.dashboard.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mc-dashboard-backend:
|
||||||
|
build: ./backend
|
||||||
|
container_name: mc-dashboard-backend
|
||||||
|
environment:
|
||||||
|
- MINECRAFT_HOST=mc-java
|
||||||
|
- MINECRAFT_PORT=25565
|
||||||
|
- RCON_PORT=25575
|
||||||
|
- RCON_PASSWORD=bethureddy1
|
||||||
|
- CONTAINER_NAME=mc-java
|
||||||
|
- PORT=3001
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- mcnet
|
||||||
|
|
||||||
|
mc-dashboard-frontend:
|
||||||
|
build: ./frontend
|
||||||
|
container_name: mc-dashboard-frontend
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
depends_on:
|
||||||
|
- mc-dashboard-backend
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- mcnet
|
||||||
|
|
||||||
|
networks:
|
||||||
|
mcnet:
|
||||||
|
external: true
|
||||||
|
|
||||||
23
frontend/Dockerfile
Normal file
23
frontend/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM node:20-alpine as builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production stage
|
||||||
|
FROM nginx:1.27-alpine
|
||||||
|
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
|
||||||
17
frontend/index.html
Normal file
17
frontend/index.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/minecraft-icon.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
|
||||||
|
<title>Minecraft Server Dashboard</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
25
frontend/nginx.conf
Normal file
25
frontend/nginx.conf
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api {
|
||||||
|
proxy_pass http://mc-dashboard-backend:3001;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
}
|
||||||
|
|
||||||
27
frontend/package.json
Normal file
27
frontend/package.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "mc-dashboard-frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"axios": "^1.6.0",
|
||||||
|
"lucide-react": "^0.294.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.43",
|
||||||
|
"@types/react-dom": "^18.2.17",
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"autoprefixer": "^10.4.16",
|
||||||
|
"postcss": "^8.4.32",
|
||||||
|
"tailwindcss": "^3.3.6",
|
||||||
|
"vite": "^5.0.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
7
frontend/postcss.config.js
Normal file
7
frontend/postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
9
frontend/public/minecraft-icon.svg
Normal file
9
frontend/public/minecraft-icon.svg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect width="100" height="100" fill="#8B4513"/>
|
||||||
|
<rect x="10" y="10" width="30" height="30" fill="#654321"/>
|
||||||
|
<rect x="60" y="10" width="30" height="30" fill="#654321"/>
|
||||||
|
<rect x="10" y="60" width="30" height="30" fill="#654321"/>
|
||||||
|
<rect x="60" y="60" width="30" height="30" fill="#654321"/>
|
||||||
|
<rect x="35" y="35" width="30" height="30" fill="#A0522D"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 431 B |
2
frontend/src/App.css
Normal file
2
frontend/src/App.css
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/* Additional custom styles if needed */
|
||||||
|
|
||||||
234
frontend/src/App.jsx
Normal file
234
frontend/src/App.jsx
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import axios from 'axios'
|
||||||
|
import {
|
||||||
|
Play,
|
||||||
|
Square,
|
||||||
|
Users,
|
||||||
|
Activity,
|
||||||
|
Server,
|
||||||
|
Clock,
|
||||||
|
Zap,
|
||||||
|
RefreshCw
|
||||||
|
} from 'lucide-react'
|
||||||
|
import './App.css'
|
||||||
|
|
||||||
|
const API_URL = import.meta.env.VITE_API_URL || '/api'
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [serverStatus, setServerStatus] = useState(null)
|
||||||
|
const [containerStatus, setContainerStatus] = useState(null)
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [actionLoading, setActionLoading] = useState(false)
|
||||||
|
const [lastUpdate, setLastUpdate] = useState(null)
|
||||||
|
|
||||||
|
const fetchStatus = async () => {
|
||||||
|
try {
|
||||||
|
const [serverRes, containerRes] = await Promise.all([
|
||||||
|
axios.get(`${API_URL}/status`),
|
||||||
|
axios.get(`${API_URL}/container/status`)
|
||||||
|
])
|
||||||
|
setServerStatus(serverRes.data)
|
||||||
|
setContainerStatus(containerRes.data)
|
||||||
|
setLastUpdate(new Date())
|
||||||
|
setLoading(false)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching status:', error)
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchStatus()
|
||||||
|
const interval = setInterval(fetchStatus, 5000) // Update every 5 seconds
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleStart = async () => {
|
||||||
|
setActionLoading(true)
|
||||||
|
try {
|
||||||
|
await axios.post(`${API_URL}/server/start`)
|
||||||
|
setTimeout(fetchStatus, 2000) // Wait 2s then refresh
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error starting server:', error)
|
||||||
|
}
|
||||||
|
setActionLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStop = async () => {
|
||||||
|
if (!window.confirm('Are you sure you want to stop the server?')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setActionLoading(true)
|
||||||
|
try {
|
||||||
|
await axios.post(`${API_URL}/server/stop`)
|
||||||
|
setTimeout(fetchStatus, 2000) // Wait 2s then refresh
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error stopping server:', error)
|
||||||
|
}
|
||||||
|
setActionLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isServerOnline = serverStatus?.online && containerStatus?.running
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center">
|
||||||
|
<div className="text-white text-2xl flex items-center gap-3">
|
||||||
|
<RefreshCw className="animate-spin" size={32} />
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen p-8">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-5xl font-bold text-white mb-4 drop-shadow-lg">
|
||||||
|
⛏️ Minecraft Server Dashboard
|
||||||
|
</h1>
|
||||||
|
<p className="text-white/80 text-lg">
|
||||||
|
Monitor and control your Minecraft server
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status Overview Card */}
|
||||||
|
<div className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 mb-8 border-4 border-white/20">
|
||||||
|
<div className="flex items-center justify-between mb-6">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className={`w-4 h-4 rounded-full ${isServerOnline ? 'bg-green-400 pulse-glow' : 'bg-red-400'}`}></div>
|
||||||
|
<h2 className="text-3xl font-bold text-white">
|
||||||
|
{isServerOnline ? 'Server Online' : 'Server Offline'}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
{lastUpdate && (
|
||||||
|
<div className="text-white/60 text-sm flex items-center gap-2">
|
||||||
|
<Clock size={16} />
|
||||||
|
Updated {lastUpdate.toLocaleTimeString()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Control Buttons */}
|
||||||
|
<div className="flex gap-4 mb-8">
|
||||||
|
<button
|
||||||
|
onClick={handleStart}
|
||||||
|
disabled={actionLoading || (containerStatus?.running && isServerOnline)}
|
||||||
|
className="minecraft-button flex-1 bg-green-500 hover:bg-green-600 disabled:bg-gray-400 disabled:cursor-not-allowed text-white font-bold py-4 px-8 rounded-lg flex items-center justify-center gap-3 text-lg"
|
||||||
|
>
|
||||||
|
<Play size={24} />
|
||||||
|
Start Server
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleStop}
|
||||||
|
disabled={actionLoading || !containerStatus?.running}
|
||||||
|
className="minecraft-button flex-1 bg-red-500 hover:bg-red-600 disabled:bg-gray-400 disabled:cursor-not-allowed text-white font-bold py-4 px-8 rounded-lg flex items-center justify-center gap-3 text-lg"
|
||||||
|
>
|
||||||
|
<Square size={24} />
|
||||||
|
Stop Server
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={fetchStatus}
|
||||||
|
disabled={actionLoading}
|
||||||
|
className="minecraft-button bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white font-bold py-4 px-8 rounded-lg flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<RefreshCw size={24} className={actionLoading ? 'animate-spin' : ''} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stats Grid */}
|
||||||
|
{isServerOnline && (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
{/* Players Online */}
|
||||||
|
<div className="bg-gradient-to-br from-blue-500 to-blue-600 rounded-xl p-6 text-white">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<Users size={24} />
|
||||||
|
<h3 className="font-semibold text-lg">Players</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-4xl font-bold">
|
||||||
|
{serverStatus.players.online} / {serverStatus.players.max}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Server Version */}
|
||||||
|
<div className="bg-gradient-to-br from-purple-500 to-purple-600 rounded-xl p-6 text-white">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<Server size={24} />
|
||||||
|
<h3 className="font-semibold text-lg">Version</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-2xl font-bold truncate">
|
||||||
|
{serverStatus.version}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Latency */}
|
||||||
|
<div className="bg-gradient-to-br from-green-500 to-green-600 rounded-xl p-6 text-white">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<Activity size={24} />
|
||||||
|
<h3 className="font-semibold text-lg">Latency</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-4xl font-bold">
|
||||||
|
{serverStatus.latency}ms
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TPS */}
|
||||||
|
<div className="bg-gradient-to-br from-yellow-500 to-yellow-600 rounded-xl p-6 text-white">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<Zap size={24} />
|
||||||
|
<h3 className="font-semibold text-lg">TPS</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-4xl font-bold">
|
||||||
|
{serverStatus.tps || '20.0'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Player List */}
|
||||||
|
{isServerOnline && serverStatus.players.online > 0 && (
|
||||||
|
<div className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 border-4 border-white/20">
|
||||||
|
<h2 className="text-2xl font-bold text-white mb-4 flex items-center gap-3">
|
||||||
|
<Users size={28} />
|
||||||
|
Online Players
|
||||||
|
</h2>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||||||
|
{serverStatus.players.list.map((player, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-white/20 rounded-lg p-4 text-white font-semibold flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<div className="w-8 h-8 bg-gradient-to-br from-green-400 to-blue-500 rounded-md flex items-center justify-center text-xs">
|
||||||
|
{player[0].toUpperCase()}
|
||||||
|
</div>
|
||||||
|
{player}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* MOTD */}
|
||||||
|
{isServerOnline && serverStatus.motd && (
|
||||||
|
<div className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 mt-8 border-4 border-white/20">
|
||||||
|
<h2 className="text-2xl font-bold text-white mb-4">Message of the Day</h2>
|
||||||
|
<p className="text-white/90 text-lg font-mono">
|
||||||
|
{serverStatus.motd}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<div className="text-center mt-12 text-white/60">
|
||||||
|
<p>Made with ❤️ for Minecraft</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
|
|
||||||
56
frontend/src/index.css
Normal file
56
frontend/src/index.css
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pixel-border {
|
||||||
|
box-shadow:
|
||||||
|
0 -4px 0 0 rgba(0,0,0,0.3),
|
||||||
|
0 4px 0 0 rgba(255,255,255,0.3),
|
||||||
|
-4px 0 0 0 rgba(0,0,0,0.3),
|
||||||
|
4px 0 0 0 rgba(255,255,255,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minecraft-button {
|
||||||
|
position: relative;
|
||||||
|
transition: all 0.1s;
|
||||||
|
box-shadow:
|
||||||
|
0 4px 0 0 rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minecraft-button:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow:
|
||||||
|
0 6px 0 0 rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minecraft-button:active {
|
||||||
|
transform: translateY(2px);
|
||||||
|
box-shadow:
|
||||||
|
0 2px 0 0 rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse-glow {
|
||||||
|
animation: pulse-glow 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-glow {
|
||||||
|
0%, 100% {
|
||||||
|
box-shadow: 0 0 20px rgba(93, 205, 227, 0.5);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 40px rgba(93, 205, 227, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
11
frontend/src/main.jsx
Normal file
11
frontend/src/main.jsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import App from './App.jsx'
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
)
|
||||||
|
|
||||||
27
frontend/tailwind.config.js
Normal file
27
frontend/tailwind.config.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
"./index.html",
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
minecraft: {
|
||||||
|
grass: '#7cbd56',
|
||||||
|
dirt: '#8b5a2b',
|
||||||
|
stone: '#7a7a7a',
|
||||||
|
diamond: '#5dcde3',
|
||||||
|
emerald: '#50c878',
|
||||||
|
gold: '#fcba03',
|
||||||
|
redstone: '#dc143c',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
minecraft: ['"Press Start 2P"', 'cursive'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
|
|
||||||
17
frontend/vite.config.js
Normal file
17
frontend/vite.config.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 3000,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: process.env.VITE_API_URL || 'http://localhost:3001',
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
15
package.json
Normal file
15
package.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "minecraft-dashboard",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Beautiful Minecraft Server Dashboard",
|
||||||
|
"scripts": {
|
||||||
|
"start": "./start-dashboard.sh",
|
||||||
|
"stop": "./stop-dashboard.sh",
|
||||||
|
"logs": "docker-compose -f docker-compose.dashboard.yml logs -f",
|
||||||
|
"rebuild": "docker-compose -f docker-compose.dashboard.yml up -d --build"
|
||||||
|
},
|
||||||
|
"keywords": ["minecraft", "dashboard", "docker", "react", "nodejs"],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
|
||||||
9
public/minecraft-icon.svg
Normal file
9
public/minecraft-icon.svg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
|
<rect width="100" height="100" fill="#8B4513"/>
|
||||||
|
<rect x="10" y="10" width="30" height="30" fill="#654321"/>
|
||||||
|
<rect x="60" y="10" width="30" height="30" fill="#654321"/>
|
||||||
|
<rect x="10" y="60" width="30" height="30" fill="#654321"/>
|
||||||
|
<rect x="60" y="60" width="30" height="30" fill="#654321"/>
|
||||||
|
<rect x="35" y="35" width="30" height="30" fill="#A0522D"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 431 B |
23
start-dashboard.sh
Executable file
23
start-dashboard.sh
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${BLUE}🎮 Starting Minecraft Dashboard...${NC}"
|
||||||
|
|
||||||
|
# Check if mcnet network exists
|
||||||
|
if ! docker network ls | grep -q mcnet; then
|
||||||
|
echo -e "${BLUE}Creating mcnet network...${NC}"
|
||||||
|
docker network create mcnet
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build and start the dashboard
|
||||||
|
echo -e "${BLUE}Building and starting dashboard services...${NC}"
|
||||||
|
docker-compose -f docker-compose.dashboard.yml up -d --build
|
||||||
|
|
||||||
|
echo -e "${GREEN}✅ Dashboard is starting!${NC}"
|
||||||
|
echo -e "${GREEN}📊 Access the dashboard at: http://localhost:8080${NC}"
|
||||||
|
echo -e "${BLUE}📝 View logs: docker-compose -f docker-compose.dashboard.yml logs -f${NC}"
|
||||||
|
|
||||||
13
stop-dashboard.sh
Executable file
13
stop-dashboard.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${BLUE}🛑 Stopping Minecraft Dashboard...${NC}"
|
||||||
|
|
||||||
|
docker-compose -f docker-compose.dashboard.yml down
|
||||||
|
|
||||||
|
echo -e "${RED}✅ Dashboard stopped!${NC}"
|
||||||
|
|
||||||
Reference in New Issue
Block a user