Proof of concept: Container management with FastAPI backend
- FastAPI backend with REST API endpoints - SQLite database for container metadata - Docker/Podman SDK integration with label filtering - Frontend: Server creation form and management page - Container operations: create, list, start, stop, delete - Single container deployment (nginx + Python + supervisor) - Support for Docker and Podman (rootless) - Volume management for persistent data
This commit is contained in:
commit
c31e48e2b1
25 changed files with 3382 additions and 0 deletions
7
.dockerignore
Normal file
7
.dockerignore
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
references
|
||||||
|
docs
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
data/
|
||||||
|
*.db
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.env
|
||||||
|
|
||||||
71
Dockerfile
Normal file
71
Dockerfile
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Install nginx
|
||||||
|
RUN apt-get update && apt-get install -y nginx supervisor && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Create data directory
|
||||||
|
RUN mkdir -p /app/data && chmod 777 /app/data
|
||||||
|
|
||||||
|
# Copy backend requirements and install
|
||||||
|
COPY backend/requirements.txt /app/backend/
|
||||||
|
RUN pip install --no-cache-dir -r backend/requirements.txt
|
||||||
|
|
||||||
|
# Copy backend code
|
||||||
|
COPY backend/ /app/backend/
|
||||||
|
|
||||||
|
# Copy frontend files
|
||||||
|
COPY index.html manage.html /usr/share/nginx/html/
|
||||||
|
|
||||||
|
# Configure nginx
|
||||||
|
RUN echo 'server {\n\
|
||||||
|
listen 80;\n\
|
||||||
|
server_name localhost;\n\
|
||||||
|
root /usr/share/nginx/html;\n\
|
||||||
|
index index.html;\n\
|
||||||
|
\n\
|
||||||
|
location / {\n\
|
||||||
|
try_files $uri $uri/ /index.html;\n\
|
||||||
|
}\n\
|
||||||
|
\n\
|
||||||
|
location /api {\n\
|
||||||
|
proxy_pass http://127.0.0.1:8000;\n\
|
||||||
|
proxy_set_header Host $host;\n\
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;\n\
|
||||||
|
}\n\
|
||||||
|
}' > /etc/nginx/sites-available/default
|
||||||
|
|
||||||
|
# Configure supervisor
|
||||||
|
RUN echo '[supervisord]\n\
|
||||||
|
nodaemon=true\n\
|
||||||
|
logfile=/dev/stdout\n\
|
||||||
|
logfile_maxbytes=0\n\
|
||||||
|
pidfile=/var/run/supervisord.pid\n\
|
||||||
|
\n\
|
||||||
|
[program:nginx]\n\
|
||||||
|
command=nginx -g "daemon off;"\n\
|
||||||
|
autostart=true\n\
|
||||||
|
autorestart=true\n\
|
||||||
|
stdout_logfile=/dev/stdout\n\
|
||||||
|
stdout_logfile_maxbytes=0\n\
|
||||||
|
stderr_logfile=/dev/stderr\n\
|
||||||
|
stderr_logfile_maxbytes=0\n\
|
||||||
|
\n\
|
||||||
|
[program:fastapi]\n\
|
||||||
|
command=uvicorn backend.main:app --host 127.0.0.1 --port 8000\n\
|
||||||
|
directory=/app\n\
|
||||||
|
environment=PYTHONPATH=/app\n\
|
||||||
|
autostart=true\n\
|
||||||
|
autorestart=true\n\
|
||||||
|
stdout_logfile=/dev/stdout\n\
|
||||||
|
stdout_logfile_maxbytes=0\n\
|
||||||
|
stderr_logfile=/dev/stderr\n\
|
||||||
|
stderr_logfile_maxbytes=0\n\
|
||||||
|
' > /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||||
57
README.md
Normal file
57
README.md
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Stronghold
|
||||||
|
|
||||||
|
A web ui to manage minecraft server using the itzg docker container
|
||||||
|
https://github.com/itzg/docker-minecraft-server
|
||||||
|
|
||||||
|
Also manage proxy servers like velocity, bungeecord
|
||||||
|
|
||||||
|
## Development
|
||||||
|
See design.md for design notes
|
||||||
|
|
||||||
|
## Quick Start (Proof of Concept)
|
||||||
|
|
||||||
|
Stronghold is a web UI for managing Minecraft servers using Docker/Podman containers.
|
||||||
|
|
||||||
|
### Running the POC
|
||||||
|
|
||||||
|
**Using Docker Compose (Docker users)**
|
||||||
|
```bash
|
||||||
|
docker-compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using Podman Compose (Podman users)**
|
||||||
|
```bash
|
||||||
|
podman-compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The container needs access to your Docker/Podman socket to manage containers. The compose file mounts:
|
||||||
|
- `/var/run/docker.sock` (Docker)
|
||||||
|
- `/run/podman/podman.sock` (Podman)
|
||||||
|
|
||||||
|
Then open http://localhost:8080 in your browser.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
**Current POC Features:**
|
||||||
|
- ✅ Generate docker-compose.yml files
|
||||||
|
- ✅ Create Minecraft server containers directly
|
||||||
|
- ✅ List and manage containers
|
||||||
|
- ✅ Start/Stop/Delete containers
|
||||||
|
- ✅ Support for Docker and Podman
|
||||||
|
- ✅ SQLite database for container metadata
|
||||||
|
- ✅ Multiple server types (Vanilla, Paper, Forge, Fabric, etc.)
|
||||||
|
- ✅ Modpack support (CurseForge, Modrinth, URL, Local)
|
||||||
|
- ✅ Advanced configuration options
|
||||||
|
|
||||||
|
**Pages:**
|
||||||
|
- `/` (index.html) - Create new servers
|
||||||
|
- `/manage.html` - Manage existing servers
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
- **Frontend**: Static HTML/CSS/JavaScript
|
||||||
|
- **Backend**: Python FastAPI
|
||||||
|
- **Database**: SQLite (stored in `./data/stronghold.db`)
|
||||||
|
- **Container Engine**: Docker SDK (works with both Docker and Podman)
|
||||||
|
|
||||||
|
All containers created by Stronghold are labeled with `stronghold.managed=true` to isolate them from other containers.
|
||||||
37
backend/database.py
Normal file
37
backend/database.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from sqlalchemy import create_engine, Column, String, Integer, Text, Boolean, DateTime
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
class Container(Base):
|
||||||
|
__tablename__ = "containers"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
container_id = Column(String, unique=True, index=True) # Docker container ID
|
||||||
|
name = Column(String, unique=True, index=True)
|
||||||
|
image = Column(String)
|
||||||
|
status = Column(String) # running, stopped, etc.
|
||||||
|
config_json = Column(Text) # Full config as JSON string
|
||||||
|
created_at = Column(DateTime, default=datetime.utcnow)
|
||||||
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Create engine at module level
|
||||||
|
# Use /app/data for database storage (mounted volume)
|
||||||
|
data_dir = '/app/data'
|
||||||
|
os.makedirs(data_dir, exist_ok=True)
|
||||||
|
db_path = os.path.join(data_dir, 'stronghold.db')
|
||||||
|
engine = create_engine(f"sqlite:///{db_path}")
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
116
backend/docker_manager.py
Normal file
116
backend/docker_manager.py
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
import docker
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
|
LABEL_MANAGED = "stronghold.managed"
|
||||||
|
LABEL_VALUE = "true"
|
||||||
|
|
||||||
|
def get_docker_client():
|
||||||
|
"""Get Docker client, automatically detecting Docker or Podman"""
|
||||||
|
try:
|
||||||
|
# Try Docker socket first
|
||||||
|
if os.path.exists('/var/run/docker.sock'):
|
||||||
|
return docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||||
|
|
||||||
|
# Try Podman rootful
|
||||||
|
if os.path.exists('/run/podman/podman.sock'):
|
||||||
|
return docker.DockerClient(base_url='unix://run/podman/podman.sock')
|
||||||
|
|
||||||
|
# Try Podman rootless
|
||||||
|
xdg_runtime = os.environ.get('XDG_RUNTIME_DIR', '')
|
||||||
|
podman_sock = f'{xdg_runtime}/podman/podman.sock'
|
||||||
|
if xdg_runtime and os.path.exists(podman_sock):
|
||||||
|
return docker.DockerClient(base_url=f'unix://{podman_sock}')
|
||||||
|
|
||||||
|
# Fallback to default
|
||||||
|
return docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Failed to connect to Docker/Podman: {e}")
|
||||||
|
|
||||||
|
def list_containers(client):
|
||||||
|
"""List all Stronghold-managed containers"""
|
||||||
|
filters = {"label": f"{LABEL_MANAGED}={LABEL_VALUE}"}
|
||||||
|
return client.containers.list(all=True, filters=filters)
|
||||||
|
|
||||||
|
def create_container(client, name, config):
|
||||||
|
"""Create a new container with Stronghold label"""
|
||||||
|
# Build environment variables from config
|
||||||
|
environment = {
|
||||||
|
"EULA": "TRUE" if config.get("acceptEULA") else "FALSE",
|
||||||
|
"TYPE": config.get("serverType", "PAPER"),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add optional environment variables
|
||||||
|
if config.get("version") and config.get("version") != "LATEST":
|
||||||
|
environment["VERSION"] = config["version"]
|
||||||
|
if config.get("memory"):
|
||||||
|
environment["MEMORY"] = config["memory"]
|
||||||
|
if config.get("difficulty"):
|
||||||
|
environment["DIFFICULTY"] = config["difficulty"]
|
||||||
|
if config.get("gamemode"):
|
||||||
|
environment["MODE"] = config["gamemode"]
|
||||||
|
if config.get("levelType"):
|
||||||
|
environment["LEVEL_TYPE"] = config["levelType"]
|
||||||
|
if config.get("motd"):
|
||||||
|
environment["MOTD"] = config["motd"]
|
||||||
|
if config.get("maxPlayers"):
|
||||||
|
environment["MAX_PLAYERS"] = str(config["maxPlayers"])
|
||||||
|
if config.get("viewDistance"):
|
||||||
|
environment["VIEW_DISTANCE"] = str(config["viewDistance"])
|
||||||
|
if not config.get("pvpEnabled", True):
|
||||||
|
environment["PVP"] = "false"
|
||||||
|
if config.get("allowFlight"):
|
||||||
|
environment["ALLOW_FLIGHT"] = "true"
|
||||||
|
if config.get("enableRCON"):
|
||||||
|
environment["ENABLE_RCON"] = "true"
|
||||||
|
if config.get("rconPassword"):
|
||||||
|
environment["RCON_PASSWORD"] = config["rconPassword"]
|
||||||
|
|
||||||
|
# Port bindings (host_port:container_port)
|
||||||
|
port = config.get("port", "25565")
|
||||||
|
port_bindings = {f"{25565}/tcp": int(port)}
|
||||||
|
if config.get("enableRCON"):
|
||||||
|
rcon_port = config.get("rconPort", "25575")
|
||||||
|
port_bindings[f"{25575}/tcp"] = int(rcon_port)
|
||||||
|
|
||||||
|
# Create or get volume
|
||||||
|
volume_name = f"{name}_data"
|
||||||
|
try:
|
||||||
|
client.volumes.get(volume_name)
|
||||||
|
except:
|
||||||
|
client.volumes.create(name=volume_name, labels={LABEL_MANAGED: LABEL_VALUE})
|
||||||
|
|
||||||
|
# Create container with proper volume mount
|
||||||
|
restart_policy = config.get("restartPolicy", "unless-stopped")
|
||||||
|
|
||||||
|
container = client.containers.create(
|
||||||
|
image="itzg/minecraft-server",
|
||||||
|
name=name,
|
||||||
|
environment=environment,
|
||||||
|
ports=port_bindings,
|
||||||
|
volumes=[f"{volume_name}:/data"],
|
||||||
|
labels={LABEL_MANAGED: LABEL_VALUE},
|
||||||
|
restart_policy={"Name": restart_policy} if restart_policy != "no" else None,
|
||||||
|
detach=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return container
|
||||||
|
|
||||||
|
def start_container(client, container_id):
|
||||||
|
"""Start a container"""
|
||||||
|
container = client.containers.get(container_id)
|
||||||
|
container.start()
|
||||||
|
return container
|
||||||
|
|
||||||
|
def stop_container(client, container_id):
|
||||||
|
"""Stop a container"""
|
||||||
|
container = client.containers.get(container_id)
|
||||||
|
container.stop()
|
||||||
|
return container
|
||||||
|
|
||||||
|
def remove_container(client, container_id):
|
||||||
|
"""Remove a container"""
|
||||||
|
container = client.containers.get(container_id)
|
||||||
|
container.remove(force=True)
|
||||||
|
return container
|
||||||
|
|
||||||
195
backend/main.py
Normal file
195
backend/main.py
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
from fastapi import FastAPI, HTTPException, Depends
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import List, Optional
|
||||||
|
import json
|
||||||
|
|
||||||
|
from backend.database import get_db, Container, Base, engine
|
||||||
|
from backend.docker_manager import (
|
||||||
|
get_docker_client, list_containers, create_container,
|
||||||
|
start_container, stop_container, remove_container
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create tables
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
app = FastAPI(title="Stronghold API")
|
||||||
|
|
||||||
|
# CORS middleware
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mount static files (frontend will be served by nginx in production)
|
||||||
|
# app.mount("/", StaticFiles(directory="../frontend", html=True), name="static")
|
||||||
|
|
||||||
|
# Pydantic models
|
||||||
|
class ContainerConfig(BaseModel):
|
||||||
|
serverName: str
|
||||||
|
serverType: str
|
||||||
|
version: Optional[str] = "LATEST"
|
||||||
|
memory: Optional[str] = "2G"
|
||||||
|
port: Optional[str] = "25565"
|
||||||
|
difficulty: Optional[str] = None
|
||||||
|
gamemode: Optional[str] = None
|
||||||
|
levelType: Optional[str] = None
|
||||||
|
motd: Optional[str] = None
|
||||||
|
maxPlayers: Optional[int] = 20
|
||||||
|
viewDistance: Optional[int] = 10
|
||||||
|
acceptEULA: bool = True
|
||||||
|
enableRCON: bool = False
|
||||||
|
rconPort: Optional[str] = "25575"
|
||||||
|
rconPassword: Optional[str] = None
|
||||||
|
pvpEnabled: bool = True
|
||||||
|
allowFlight: bool = False
|
||||||
|
restartPolicy: str = "unless-stopped"
|
||||||
|
|
||||||
|
class ContainerResponse(BaseModel):
|
||||||
|
id: int
|
||||||
|
container_id: str
|
||||||
|
name: str
|
||||||
|
image: str
|
||||||
|
status: str
|
||||||
|
created_at: str
|
||||||
|
|
||||||
|
# API Routes
|
||||||
|
@app.get("/api/containers")
|
||||||
|
def get_containers(db: Session = Depends(get_db)):
|
||||||
|
"""List all Stronghold-managed containers"""
|
||||||
|
try:
|
||||||
|
client = get_docker_client()
|
||||||
|
docker_containers = list_containers(client)
|
||||||
|
|
||||||
|
# Get containers from database
|
||||||
|
db_containers = db.query(Container).all()
|
||||||
|
|
||||||
|
# Merge data
|
||||||
|
result = []
|
||||||
|
for db_container in db_containers:
|
||||||
|
# Find matching docker container
|
||||||
|
docker_container = next(
|
||||||
|
(dc for dc in docker_containers if dc.id == db_container.container_id),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
status = docker_container.status if docker_container else "unknown"
|
||||||
|
|
||||||
|
result.append({
|
||||||
|
"id": db_container.id,
|
||||||
|
"container_id": db_container.container_id,
|
||||||
|
"name": db_container.name,
|
||||||
|
"image": db_container.image,
|
||||||
|
"status": status,
|
||||||
|
"created_at": db_container.created_at.isoformat() if db_container.created_at else None
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.post("/api/containers")
|
||||||
|
def create_container_endpoint(config: ContainerConfig, db: Session = Depends(get_db)):
|
||||||
|
"""Create a new Minecraft server container"""
|
||||||
|
try:
|
||||||
|
client = get_docker_client()
|
||||||
|
|
||||||
|
# Check if name already exists
|
||||||
|
existing = db.query(Container).filter(Container.name == config.serverName).first()
|
||||||
|
if existing:
|
||||||
|
raise HTTPException(status_code=400, detail="Container name already exists")
|
||||||
|
|
||||||
|
# Create container
|
||||||
|
docker_container = create_container(client, config.serverName, config.dict())
|
||||||
|
|
||||||
|
# Save to database
|
||||||
|
db_container = Container(
|
||||||
|
container_id=docker_container.id,
|
||||||
|
name=config.serverName,
|
||||||
|
image="itzg/minecraft-server",
|
||||||
|
status="created",
|
||||||
|
config_json=json.dumps(config.dict())
|
||||||
|
)
|
||||||
|
db.add(db_container)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_container)
|
||||||
|
|
||||||
|
# Start container
|
||||||
|
docker_container.start()
|
||||||
|
|
||||||
|
# Update status
|
||||||
|
db_container.status = "running"
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": db_container.id,
|
||||||
|
"container_id": docker_container.id,
|
||||||
|
"name": config.serverName,
|
||||||
|
"status": "running"
|
||||||
|
}
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.post("/api/containers/{container_id}/start")
|
||||||
|
def start_container_endpoint(container_id: str, db: Session = Depends(get_db)):
|
||||||
|
"""Start a container"""
|
||||||
|
try:
|
||||||
|
client = get_docker_client()
|
||||||
|
container = start_container(client, container_id)
|
||||||
|
|
||||||
|
# Update database
|
||||||
|
db_container = db.query(Container).filter(Container.container_id == container_id).first()
|
||||||
|
if db_container:
|
||||||
|
db_container.status = "running"
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {"status": "started", "container_id": container_id}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.post("/api/containers/{container_id}/stop")
|
||||||
|
def stop_container_endpoint(container_id: str, db: Session = Depends(get_db)):
|
||||||
|
"""Stop a container"""
|
||||||
|
try:
|
||||||
|
client = get_docker_client()
|
||||||
|
container = stop_container(client, container_id)
|
||||||
|
|
||||||
|
# Update database
|
||||||
|
db_container = db.query(Container).filter(Container.container_id == container_id).first()
|
||||||
|
if db_container:
|
||||||
|
db_container.status = "stopped"
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {"status": "stopped", "container_id": container_id}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.delete("/api/containers/{container_id}")
|
||||||
|
def delete_container_endpoint(container_id: str, db: Session = Depends(get_db)):
|
||||||
|
"""Delete a container"""
|
||||||
|
try:
|
||||||
|
client = get_docker_client()
|
||||||
|
remove_container(client, container_id)
|
||||||
|
|
||||||
|
# Remove from database
|
||||||
|
db_container = db.query(Container).filter(Container.container_id == container_id).first()
|
||||||
|
if db_container:
|
||||||
|
db.delete(db_container)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {"status": "deleted", "container_id": container_id}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.get("/api/health")
|
||||||
|
def health_check():
|
||||||
|
"""Health check endpoint"""
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
5
backend/requirements.txt
Normal file
5
backend/requirements.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
fastapi==0.104.1
|
||||||
|
uvicorn[standard]==0.24.0
|
||||||
|
docker==7.0.0
|
||||||
|
sqlalchemy==2.0.23
|
||||||
|
|
||||||
38
design.md
Normal file
38
design.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Design
|
||||||
|
|
||||||
|
Take a lot of inspiration from crafty.
|
||||||
|
Make creating a modded server as simple as it is using ATLauncher
|
||||||
|
|
||||||
|
# Philosophy
|
||||||
|
KISS = Keep It Simple Stupid
|
||||||
|
dont make things over complicated
|
||||||
|
|
||||||
|
Keep code clean, readable, and concise.
|
||||||
|
|
||||||
|
Modular
|
||||||
|
|
||||||
|
# Plans
|
||||||
|
This web ui app should be ran in a docker container.
|
||||||
|
User authentication, permissions
|
||||||
|
simple status page for players to see if the server is up
|
||||||
|
|
||||||
|
# References
|
||||||
|
[itzg docker-minecraft-server](https://github.com/itzg/docker-minecraft-server)
|
||||||
|
[itzg docs](https://docker-minecraft-server.readthedocs.io/en/latest/) these docs exist in the main itzg github repo
|
||||||
|
[itzg minecraft proxy](https://github.com/itzg/docker-mc-proxy) used to setup proxy servers like velocity and bungee cord
|
||||||
|
[itzeg mc-router](https://github.com/itzg/mc-router) used to direct client to the right minecraft proxy or server based on requested server address
|
||||||
|
[crafty](https://craftycontrol.com)
|
||||||
|
[docker-rcon-web-admin](https://github.com/itzg/docker-rcon-web-admin)
|
||||||
|
[setupmc.com](https://setupmc.com/java-server/) Not open source but it's a web ui to generate yml files for docker using the itzg container image
|
||||||
|
|
||||||
|
|
||||||
|
# Maybe useful references
|
||||||
|
These are the github repos to other minecraft project that may be useful
|
||||||
|
|
||||||
|
[pterodactyl](https://github.com/pterodactyl/panel)
|
||||||
|
[fork](https://github.com/ForkGG/Fork) This is a Windows based server manager that may have useful ideas.
|
||||||
|
[MCSManager](https://github.com/MCSManager/MCSManager)
|
||||||
|
|
||||||
|
Minecraft launchers for clients. they have tools to download packs and server packs
|
||||||
|
[ATLauncher](https://github.com/ATLauncher/ATLauncher)
|
||||||
|
[Prism Launcher](https://github.com/PrismLauncher/PrismLauncher)
|
||||||
0
docker-compose
Normal file
0
docker-compose
Normal file
24
docker-compose.yml
Normal file
24
docker-compose.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
stronghold:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
# Socket access for Podman (rootless)
|
||||||
|
# For Podman rootless, mount the socket to /var/run/docker.sock inside container
|
||||||
|
# This way the Docker SDK can find it at the standard location
|
||||||
|
- ${XDG_RUNTIME_DIR}/podman/podman.sock:/var/run/docker.sock:ro
|
||||||
|
# Database persistence
|
||||||
|
- ./data:/app/data
|
||||||
|
# Hot reload frontend (optional, for development)
|
||||||
|
- ./index.html:/usr/share/nginx/html/index.html:ro,z
|
||||||
|
- ./manage.html:/usr/share/nginx/html/manage.html:ro,z
|
||||||
|
environment:
|
||||||
|
# Set working directory for database
|
||||||
|
- PWD=/app/data
|
||||||
|
# Pass XDG_RUNTIME_DIR for Podman rootless
|
||||||
|
- XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-/run/user/1000}
|
||||||
|
working_dir: /app/data
|
||||||
|
restart: unless-stopped
|
||||||
852
index.html
Normal file
852
index.html
Normal file
|
|
@ -0,0 +1,852 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Stronghold - Minecraft Server Generator</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 2.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 2px solid #e0e0e0;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
padding: 12px 24px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:hover {
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active {
|
||||||
|
color: #667eea;
|
||||||
|
border-bottom-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, select, textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 1em;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus, select:focus, textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row-3 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
width: auto;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview h3 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #2d2d2d;
|
||||||
|
color: #f8f8f2;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.85em;
|
||||||
|
line-height: 1.5;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
background: #28a745;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 15px;
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn:hover {
|
||||||
|
background: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-btn:disabled {
|
||||||
|
background: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-text {
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modpack-type-section {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modpack-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modpack-input.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>🛡️ Stronghold</h1>
|
||||||
|
<p class="subtitle">Minecraft Server Docker Compose Generator</p>
|
||||||
|
|
||||||
|
<div class="tabs">
|
||||||
|
<button class="tab active" data-tab="basic">Basic Configuration</button>
|
||||||
|
<button class="tab" data-tab="modpack">Modpack</button>
|
||||||
|
<button class="tab" data-tab="advanced">Advanced</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="serverForm">
|
||||||
|
<!-- Basic Configuration Tab -->
|
||||||
|
<div class="tab-content active" id="basic-tab">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="serverName">Server Name</label>
|
||||||
|
<input type="text" id="serverName" placeholder="my-minecraft-server" value="minecraft">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="serverType">Server Type</label>
|
||||||
|
<select id="serverType">
|
||||||
|
<option value="VANILLA">Vanilla</option>
|
||||||
|
<option value="PAPER" selected>Paper (Recommended)</option>
|
||||||
|
<option value="FORGE">Forge (Modded)</option>
|
||||||
|
<option value="FABRIC">Fabric (Modded)</option>
|
||||||
|
<option value="SPIGOT">Spigot</option>
|
||||||
|
<option value="BUKKIT">Bukkit</option>
|
||||||
|
<option value="MAGMA">Magma (Mods + Plugins)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="version">Minecraft Version</label>
|
||||||
|
<input type="text" id="version" placeholder="LATEST or 1.20.1" value="LATEST">
|
||||||
|
<span class="info-text">Use LATEST for newest version, or specify version like 1.20.1</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="memory">Memory Allocation</label>
|
||||||
|
<select id="memory">
|
||||||
|
<option value="1G">1GB (Small)</option>
|
||||||
|
<option value="2G" selected>2GB (Recommended for most)</option>
|
||||||
|
<option value="4G">4GB (Medium)</option>
|
||||||
|
<option value="8G">8GB (Large/Modded)</option>
|
||||||
|
<option value="16G">16GB (Very Large)</option>
|
||||||
|
<option value="custom">Custom</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section" id="customMemorySection" style="display: none;">
|
||||||
|
<label for="customMemory">Custom Memory</label>
|
||||||
|
<input type="text" id="customMemory" placeholder="32G">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="port">Server Port</label>
|
||||||
|
<input type="number" id="port" value="25565" min="1024" max="65535">
|
||||||
|
<span class="info-text">Default Minecraft port is 25565</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="difficulty">Difficulty</label>
|
||||||
|
<select id="difficulty">
|
||||||
|
<option value="">Default</option>
|
||||||
|
<option value="peaceful">Peaceful</option>
|
||||||
|
<option value="easy" selected>Easy</option>
|
||||||
|
<option value="normal">Normal</option>
|
||||||
|
<option value="hard">Hard</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="gamemode">Default Game Mode</label>
|
||||||
|
<select id="gamemode">
|
||||||
|
<option value="">Default</option>
|
||||||
|
<option value="survival" selected>Survival</option>
|
||||||
|
<option value="creative">Creative</option>
|
||||||
|
<option value="adventure">Adventure</option>
|
||||||
|
<option value="spectator">Spectator</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="levelType">World Type</label>
|
||||||
|
<select id="levelType">
|
||||||
|
<option value="">Default</option>
|
||||||
|
<option value="DEFAULT">Default</option>
|
||||||
|
<option value="FLAT">Flat</option>
|
||||||
|
<option value="LARGEBIOMES">Large Biomes</option>
|
||||||
|
<option value="AMPLIFIED">Amplified</option>
|
||||||
|
<option value="CUSTOMIZED">Customized</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="motd">Server MOTD (Message of the Day)</label>
|
||||||
|
<input type="text" id="motd" placeholder="A Minecraft Server" maxlength="59">
|
||||||
|
<span class="info-text">Message shown in server list (max 59 characters)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="maxPlayers">Max Players</label>
|
||||||
|
<input type="number" id="maxPlayers" value="20" min="1" max="999">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="viewDistance">View Distance (chunks)</label>
|
||||||
|
<input type="number" id="viewDistance" value="10" min="3" max="32">
|
||||||
|
<span class="info-text">Higher = better visibility but more CPU usage</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="acceptEULA" checked>
|
||||||
|
Accept Minecraft EULA
|
||||||
|
</label>
|
||||||
|
<span class="info-text">Required to run a Minecraft server</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modpack Tab -->
|
||||||
|
<div class="tab-content" id="modpack-tab">
|
||||||
|
<div class="section-title">Modpack Installation</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="modpackType">Modpack Source</label>
|
||||||
|
<select id="modpackType">
|
||||||
|
<option value="none">No Modpack (Vanilla/Plugins only)</option>
|
||||||
|
<option value="curseforge">CurseForge Modpack</option>
|
||||||
|
<option value="modrinth">Modrinth Modpack</option>
|
||||||
|
<option value="url">Download from URL</option>
|
||||||
|
<option value="local">Local File Path</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modpack-input" id="curseforge-input">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="curseforgeFile">CurseForge Server Pack Path</label>
|
||||||
|
<input type="text" id="curseforgeFile" placeholder="/modpacks/modpack-server.zip">
|
||||||
|
<span class="info-text">Path to CurseForge server pack zip file inside container</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="useCurseforgeType">Use CurseForge TYPE</label>
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="useCurseforgeType" checked>
|
||||||
|
Automatically set TYPE to CURSEFORGE
|
||||||
|
</label>
|
||||||
|
<span class="info-text">Uncheck if you want to manually set server type</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modpack-input" id="modrinth-input">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="modrinthProject">Modrinth Project ID or Slug</label>
|
||||||
|
<input type="text" id="modrinthProject" placeholder="allthemods">
|
||||||
|
<span class="info-text">Modrinth project identifier</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="modrinthVersion">Version ID (optional)</label>
|
||||||
|
<input type="text" id="modrinthVersion" placeholder="Leave empty for latest">
|
||||||
|
<span class="info-text">Specific version ID, or leave empty for latest</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modpack-input" id="url-input">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="modpackUrl">Modpack URL</label>
|
||||||
|
<input type="url" id="modpackUrl" placeholder="https://example.com/modpack.zip">
|
||||||
|
<span class="info-text">Direct download URL for modpack zip file</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modpack-input" id="local-input">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="modpackPath">Local Modpack Path</label>
|
||||||
|
<input type="text" id="modpackPath" placeholder="/modpacks/my-modpack.zip">
|
||||||
|
<span class="info-text">Path to modpack file inside container (use volume mount)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="removeOldMods">
|
||||||
|
Remove Old Mods/Plugins Before Installing
|
||||||
|
</label>
|
||||||
|
<span class="info-text">Deletes existing mods/plugins before installing modpack</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Advanced Tab -->
|
||||||
|
<div class="tab-content" id="advanced-tab">
|
||||||
|
<div class="section-title">RCON Configuration</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="enableRCON">
|
||||||
|
Enable RCON (Remote Console)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="rconConfig" style="display: none;">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="rconPort">RCON Port</label>
|
||||||
|
<input type="number" id="rconPort" value="25575" min="1024" max="65535">
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="rconPassword">RCON Password</label>
|
||||||
|
<input type="password" id="rconPassword" placeholder="changeit">
|
||||||
|
<span class="info-text">Choose a strong password</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-title">Server Properties</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-section">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="pvpEnabled" checked>
|
||||||
|
Enable PVP
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="allowFlight">
|
||||||
|
Allow Flight
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="maxTickTime">Max Tick Time (ms)</label>
|
||||||
|
<input type="number" id="maxTickTime" placeholder="-1 (no limit)" value="-1">
|
||||||
|
<span class="info-text">Maximum time a single tick can take (-1 = no limit)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="ops">Server Operators (comma-separated Minecraft usernames)</label>
|
||||||
|
<input type="text" id="ops" placeholder="Player1,Player2">
|
||||||
|
<span class="info-text">Players who will have operator permissions</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-title">Container Options</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="restartPolicy">Restart Policy</label>
|
||||||
|
<select id="restartPolicy">
|
||||||
|
<option value="no">No</option>
|
||||||
|
<option value="always">Always</option>
|
||||||
|
<option value="on-failure">On Failure</option>
|
||||||
|
<option value="unless-stopped" selected>Unless Stopped</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<label for="timezone">Timezone</label>
|
||||||
|
<input type="text" id="timezone" placeholder="America/New_York" value="UTC">
|
||||||
|
<span class="info-text">Server timezone (e.g., America/New_York, Europe/London)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="rollingLogs">
|
||||||
|
Enable Rolling Logs
|
||||||
|
</label>
|
||||||
|
<span class="info-text">Automatically delete old log files</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="overrideServerProperties">
|
||||||
|
Override Server Properties
|
||||||
|
</label>
|
||||||
|
<span class="info-text">Allow environment variables to override server.properties</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="generate-btn" id="submitBtn">Create Server</button>
|
||||||
|
<button type="button" class="generate-btn" id="generateYamlBtn" style="margin-top: 10px; background: #6c757d;">Generate YAML Only</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="preview" class="preview" style="display: none;">
|
||||||
|
<h3>Generated docker-compose.yml</h3>
|
||||||
|
<pre id="yamlPreview"></pre>
|
||||||
|
<button class="download-btn" id="downloadBtn">Download docker-compose.yml</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Tab switching
|
||||||
|
document.querySelectorAll('.tab').forEach(tab => {
|
||||||
|
tab.addEventListener('click', () => {
|
||||||
|
const targetTab = tab.dataset.tab;
|
||||||
|
|
||||||
|
// Update tabs
|
||||||
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||||
|
tab.classList.add('active');
|
||||||
|
|
||||||
|
// Update tab content
|
||||||
|
document.querySelectorAll('.tab-content').forEach(content => {
|
||||||
|
content.classList.remove('active');
|
||||||
|
});
|
||||||
|
document.getElementById(`${targetTab}-tab`).classList.add('active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Modpack type switching
|
||||||
|
const modpackType = document.getElementById('modpackType');
|
||||||
|
modpackType.addEventListener('change', () => {
|
||||||
|
document.querySelectorAll('.modpack-input').forEach(input => {
|
||||||
|
input.classList.remove('active');
|
||||||
|
});
|
||||||
|
if (modpackType.value !== 'none') {
|
||||||
|
const activeInput = document.getElementById(`${modpackType.value}-input`);
|
||||||
|
if (activeInput) {
|
||||||
|
activeInput.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// RCON config toggle
|
||||||
|
document.getElementById('enableRCON').addEventListener('change', (e) => {
|
||||||
|
document.getElementById('rconConfig').style.display = e.target.checked ? 'block' : 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Custom memory toggle
|
||||||
|
const memorySelect = document.getElementById('memory');
|
||||||
|
const customMemorySection = document.getElementById('customMemorySection');
|
||||||
|
memorySelect.addEventListener('change', () => {
|
||||||
|
customMemorySection.style.display = memorySelect.value === 'custom' ? 'block' : 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Form submission
|
||||||
|
const form = document.getElementById('serverForm');
|
||||||
|
const preview = document.getElementById('preview');
|
||||||
|
const yamlPreview = document.getElementById('yamlPreview');
|
||||||
|
const downloadBtn = document.getElementById('downloadBtn');
|
||||||
|
|
||||||
|
const API_BASE = window.location.origin;
|
||||||
|
|
||||||
|
// Generate YAML only (existing functionality)
|
||||||
|
document.getElementById('generateYamlBtn').addEventListener('click', () => {
|
||||||
|
const config = gatherFormData();
|
||||||
|
const yaml = generateDockerCompose(config);
|
||||||
|
|
||||||
|
yamlPreview.textContent = yaml;
|
||||||
|
preview.style.display = 'block';
|
||||||
|
downloadBtn.disabled = false;
|
||||||
|
|
||||||
|
downloadBtn.onclick = () => {
|
||||||
|
const blob = new Blob([yaml], { type: 'text/yaml' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'docker-compose.yml';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create server (new functionality)
|
||||||
|
form.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const submitBtn = document.getElementById('submitBtn');
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.textContent = 'Creating Server...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const config = gatherFormData();
|
||||||
|
|
||||||
|
// Submit to API
|
||||||
|
const response = await fetch(`${API_BASE}/api/containers`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(config),
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
// Show success message and redirect
|
||||||
|
preview.style.display = 'block';
|
||||||
|
preview.innerHTML = `
|
||||||
|
<h3 style="color: #28a745;">✓ Server Created Successfully!</h3>
|
||||||
|
<p><strong>Name:</strong> ${result.name}</p>
|
||||||
|
<p><strong>Status:</strong> ${result.status}</p>
|
||||||
|
<br>
|
||||||
|
<a href="manage.html" class="btn btn-primary" style="text-decoration: none; display: inline-block; padding: 10px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 6px;">Go to Manage Page</a>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.detail || 'Failed to create server');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
preview.style.display = 'block';
|
||||||
|
preview.innerHTML = `
|
||||||
|
<h3 style="color: #dc3545;">Error Creating Server</h3>
|
||||||
|
<p>${error.message}</p>
|
||||||
|
`;
|
||||||
|
} finally {
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.textContent = 'Create Server';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function gatherFormData() {
|
||||||
|
return {
|
||||||
|
serverName: document.getElementById('serverName').value || 'minecraft',
|
||||||
|
serverType: document.getElementById('serverType').value,
|
||||||
|
version: document.getElementById('version').value || 'LATEST',
|
||||||
|
memory: memorySelect.value === 'custom'
|
||||||
|
? document.getElementById('customMemory').value || '2G'
|
||||||
|
: memorySelect.value,
|
||||||
|
port: document.getElementById('port').value || '25565',
|
||||||
|
difficulty: document.getElementById('difficulty').value,
|
||||||
|
gamemode: document.getElementById('gamemode').value,
|
||||||
|
levelType: document.getElementById('levelType').value,
|
||||||
|
motd: document.getElementById('motd').value,
|
||||||
|
maxPlayers: document.getElementById('maxPlayers').value,
|
||||||
|
viewDistance: document.getElementById('viewDistance').value,
|
||||||
|
acceptEULA: document.getElementById('acceptEULA').checked,
|
||||||
|
|
||||||
|
// Modpack
|
||||||
|
modpackType: document.getElementById('modpackType').value,
|
||||||
|
curseforgeFile: document.getElementById('curseforgeFile').value,
|
||||||
|
useCurseforgeType: document.getElementById('useCurseforgeType').checked,
|
||||||
|
modrinthProject: document.getElementById('modrinthProject').value,
|
||||||
|
modrinthVersion: document.getElementById('modrinthVersion').value,
|
||||||
|
modpackUrl: document.getElementById('modpackUrl').value,
|
||||||
|
modpackPath: document.getElementById('modpackPath').value,
|
||||||
|
removeOldMods: document.getElementById('removeOldMods').checked,
|
||||||
|
|
||||||
|
// RCON
|
||||||
|
enableRCON: document.getElementById('enableRCON').checked,
|
||||||
|
rconPort: document.getElementById('rconPort').value,
|
||||||
|
rconPassword: document.getElementById('rconPassword').value,
|
||||||
|
|
||||||
|
// Advanced
|
||||||
|
pvpEnabled: document.getElementById('pvpEnabled').checked,
|
||||||
|
allowFlight: document.getElementById('allowFlight').checked,
|
||||||
|
maxTickTime: document.getElementById('maxTickTime').value,
|
||||||
|
ops: document.getElementById('ops').value,
|
||||||
|
restartPolicy: document.getElementById('restartPolicy').value,
|
||||||
|
timezone: document.getElementById('timezone').value,
|
||||||
|
rollingLogs: document.getElementById('rollingLogs').checked,
|
||||||
|
overrideServerProperties: document.getElementById('overrideServerProperties').checked
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateDockerCompose(config) {
|
||||||
|
let yaml = `services:
|
||||||
|
${config.serverName}:
|
||||||
|
image: itzg/minecraft-server
|
||||||
|
container_name: ${config.serverName}
|
||||||
|
ports:
|
||||||
|
- "${config.port}:25565"`;
|
||||||
|
|
||||||
|
// RCON port if enabled
|
||||||
|
if (config.enableRCON) {
|
||||||
|
yaml += `\n - "${config.rconPort}:25575"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml += `\n volumes:
|
||||||
|
- ${config.serverName}_data:/data`;
|
||||||
|
|
||||||
|
// Modpack volume mount if using local file
|
||||||
|
if (config.modpackType === 'curseforge' && config.curseforgeFile) {
|
||||||
|
yaml += `\n - ./modpacks:/modpacks:ro`;
|
||||||
|
} else if (config.modpackType === 'local' && config.modpackPath) {
|
||||||
|
const modpackDir = config.modpackPath.substring(0, config.modpackPath.lastIndexOf('/'));
|
||||||
|
if (modpackDir) {
|
||||||
|
yaml += `\n - ./modpacks:${modpackDir}:ro`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml += `\n environment:`;
|
||||||
|
|
||||||
|
// EULA
|
||||||
|
if (config.acceptEULA) {
|
||||||
|
yaml += `\n EULA: "TRUE"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server Type
|
||||||
|
let serverType = config.serverType;
|
||||||
|
if (config.modpackType === 'curseforge' && config.useCurseforgeType) {
|
||||||
|
serverType = 'CURSEFORGE';
|
||||||
|
} else if (config.modpackType === 'modrinth') {
|
||||||
|
serverType = config.serverType; // Keep original or could be auto-detected
|
||||||
|
}
|
||||||
|
yaml += `\n TYPE: ${serverType}`;
|
||||||
|
|
||||||
|
// Version
|
||||||
|
if (config.version && config.version !== 'LATEST') {
|
||||||
|
yaml += `\n VERSION: ${config.version}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory
|
||||||
|
if (config.memory) {
|
||||||
|
yaml += `\n MEMORY: ${config.memory}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modpack configuration
|
||||||
|
if (config.modpackType === 'curseforge' && config.curseforgeFile) {
|
||||||
|
yaml += `\n CF_SERVER_MOD: ${config.curseforgeFile}`;
|
||||||
|
} else if (config.modpackType === 'modrinth' && config.modrinthProject) {
|
||||||
|
yaml += `\n MODPACK: modrinth:${config.modrinthProject}`;
|
||||||
|
if (config.modrinthVersion) {
|
||||||
|
yaml += `:${config.modrinthVersion}`;
|
||||||
|
}
|
||||||
|
} else if (config.modpackType === 'url' && config.modpackUrl) {
|
||||||
|
yaml += `\n MODPACK: ${config.modpackUrl}`;
|
||||||
|
} else if (config.modpackType === 'local' && config.modpackPath) {
|
||||||
|
yaml += `\n MODPACK: ${config.modpackPath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.removeOldMods) {
|
||||||
|
yaml += `\n REMOVE_OLD_MODS: "TRUE"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RCON
|
||||||
|
if (config.enableRCON) {
|
||||||
|
yaml += `\n ENABLE_RCON: "true"`;
|
||||||
|
if (config.rconPassword) {
|
||||||
|
yaml += `\n RCON_PASSWORD: ${config.rconPassword}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Difficulty
|
||||||
|
if (config.difficulty) {
|
||||||
|
yaml += `\n DIFFICULTY: ${config.difficulty}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game Mode
|
||||||
|
if (config.gamemode) {
|
||||||
|
yaml += `\n MODE: ${config.gamemode}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Level Type
|
||||||
|
if (config.levelType) {
|
||||||
|
yaml += `\n LEVEL_TYPE: ${config.levelType}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MOTD
|
||||||
|
if (config.motd) {
|
||||||
|
yaml += `\n MOTD: "${config.motd}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max Players
|
||||||
|
if (config.maxPlayers) {
|
||||||
|
yaml += `\n MAX_PLAYERS: ${config.maxPlayers}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// View Distance
|
||||||
|
if (config.viewDistance) {
|
||||||
|
yaml += `\n VIEW_DISTANCE: ${config.viewDistance}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PVP
|
||||||
|
if (!config.pvpEnabled) {
|
||||||
|
yaml += `\n PVP: "false"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow Flight
|
||||||
|
if (config.allowFlight) {
|
||||||
|
yaml += `\n ALLOW_FLIGHT: "true"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max Tick Time
|
||||||
|
if (config.maxTickTime && config.maxTickTime !== '-1') {
|
||||||
|
yaml += `\n MAX_TICK_TIME: ${config.maxTickTime}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ops
|
||||||
|
if (config.ops) {
|
||||||
|
yaml += `\n OPS: "${config.ops}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timezone
|
||||||
|
if (config.timezone && config.timezone !== 'UTC') {
|
||||||
|
yaml += `\n TZ: ${config.timezone}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rolling Logs
|
||||||
|
if (config.rollingLogs) {
|
||||||
|
yaml += `\n ENABLE_ROLLING_LOGS: "true"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override Server Properties
|
||||||
|
if (config.overrideServerProperties) {
|
||||||
|
yaml += `\n OVERRIDE_SERVER_PROPERTIES: "true"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart Policy
|
||||||
|
yaml += `\n restart: ${config.restartPolicy}`;
|
||||||
|
|
||||||
|
yaml += `\n\nvolumes:
|
||||||
|
${config.serverName}_data:`;
|
||||||
|
|
||||||
|
return yaml;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
292
manage.html
Normal file
292
manage.html
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Manage Servers - Stronghold</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 2.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background: #28a745;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
background: #dc3545;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-warning {
|
||||||
|
background: #ffc107;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.servers-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-card {
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-info h3 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-info p {
|
||||||
|
color: #666;
|
||||||
|
margin: 4px 0;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-running {
|
||||||
|
background: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-stopped {
|
||||||
|
background: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-unknown {
|
||||||
|
background: #e2e3e5;
|
||||||
|
color: #383d41;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 60px 20px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state h3 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<div>
|
||||||
|
<h1>🛡️ Stronghold</h1>
|
||||||
|
<p style="color: #666;">Manage Your Minecraft Servers</p>
|
||||||
|
</div>
|
||||||
|
<a href="index.html" class="btn btn-primary">Create New Server</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="serversContainer">
|
||||||
|
<div class="loading">Loading servers...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const API_BASE = window.location.origin;
|
||||||
|
|
||||||
|
async function loadServers() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/containers`);
|
||||||
|
const servers = await response.json();
|
||||||
|
|
||||||
|
const container = document.getElementById('serversContainer');
|
||||||
|
|
||||||
|
if (servers.length === 0) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="empty-state">
|
||||||
|
<h3>No servers found</h3>
|
||||||
|
<p>Create your first Minecraft server to get started!</p>
|
||||||
|
<br>
|
||||||
|
<a href="index.html" class="btn btn-primary">Create Server</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = '<div class="servers-grid"></div>';
|
||||||
|
const grid = container.querySelector('.servers-grid');
|
||||||
|
|
||||||
|
servers.forEach(server => {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'server-card';
|
||||||
|
|
||||||
|
const statusClass = server.status === 'running' ? 'status-running' :
|
||||||
|
server.status === 'stopped' ? 'status-stopped' : 'status-unknown';
|
||||||
|
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="server-info">
|
||||||
|
<h3>${server.name}</h3>
|
||||||
|
<p><strong>Image:</strong> ${server.image}</p>
|
||||||
|
<p><strong>Status:</strong> <span class="status ${statusClass}">${server.status}</span></p>
|
||||||
|
<p><strong>Created:</strong> ${new Date(server.created_at).toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
<div class="server-actions">
|
||||||
|
${server.status === 'running'
|
||||||
|
? `<button class="btn btn-warning btn-sm" onclick="stopServer('${server.container_id}', '${server.name}')">Stop</button>`
|
||||||
|
: `<button class="btn btn-success btn-sm" onclick="startServer('${server.container_id}', '${server.name}')">Start</button>`
|
||||||
|
}
|
||||||
|
<button class="btn btn-danger btn-sm" onclick="deleteServer('${server.container_id}', '${server.name}')">Delete</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
grid.appendChild(card);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById('serversContainer').innerHTML =
|
||||||
|
`<div class="empty-state"><h3>Error loading servers</h3><p>${error.message}</p></div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startServer(containerId, name) {
|
||||||
|
if (!confirm(`Start server "${name}"?`)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/containers/${containerId}/start`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
alert('Server started successfully!');
|
||||||
|
loadServers();
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (result.detail || 'Unknown error'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('Error: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopServer(containerId, name) {
|
||||||
|
if (!confirm(`Stop server "${name}"?`)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/containers/${containerId}/stop`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
alert('Server stopped successfully!');
|
||||||
|
loadServers();
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (result.detail || 'Unknown error'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('Error: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteServer(containerId, name) {
|
||||||
|
if (!confirm(`Delete server "${name}"? This will remove the container and cannot be undone.`)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE}/api/containers/${containerId}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
alert('Server deleted successfully!');
|
||||||
|
loadServers();
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (result.detail || 'Unknown error'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('Error: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load servers on page load
|
||||||
|
loadServers();
|
||||||
|
|
||||||
|
// Refresh every 5 seconds
|
||||||
|
setInterval(loadServers, 5000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
103
references/SUMMARY.md
Normal file
103
references/SUMMARY.md
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
# Reference Projects Summary
|
||||||
|
|
||||||
|
This directory contains detailed analysis of reference projects for the Stronghold Minecraft server management tool.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
1. **itzg-projects.md** - Core Docker projects by itzg that Stronghold will integrate with:
|
||||||
|
- docker-minecraft-server
|
||||||
|
- docker-mc-proxy
|
||||||
|
- mc-router
|
||||||
|
- docker-rcon-web-admin
|
||||||
|
|
||||||
|
2. **server-managers.md** - Existing web-based server management tools:
|
||||||
|
- Crafty (CraftyControl)
|
||||||
|
- Pterodactyl Panel
|
||||||
|
- Fork (ForkGG)
|
||||||
|
- MCSManager
|
||||||
|
- setupmc.com
|
||||||
|
|
||||||
|
3. **launchers.md** - Client-side launchers with excellent modpack workflows:
|
||||||
|
- ATLauncher
|
||||||
|
- Prism Launcher
|
||||||
|
|
||||||
|
4. **examples/** - Docker Compose example files from itzg repository:
|
||||||
|
- docker-compose-big.yml (Large Biomes world type)
|
||||||
|
- docker-compose-curseforge-atm7.yaml (All The Mods 7 modpack)
|
||||||
|
- docker-compose-curseforge.yml (Basic CurseForge example)
|
||||||
|
- docker-compose-magma.yml (Hybrid mods + plugins)
|
||||||
|
- docker-compose-rconcmd.yml (Advanced RCON commands)
|
||||||
|
- EXAMPLES-ANALYSIS.md (Detailed analysis of each example)
|
||||||
|
|
||||||
|
## Key Insights
|
||||||
|
|
||||||
|
### Architecture Recommendations
|
||||||
|
- **Keep it simple** (KISS): Docker API directly, no agents/daemons needed initially
|
||||||
|
- **Start without database**: Use file-based configuration, add database later if needed
|
||||||
|
- **Node.js stack**: Could work well (like MCSManager but simpler)
|
||||||
|
- **Single-host focus**: Start with single Docker host, expand later
|
||||||
|
|
||||||
|
### Feature Priorities
|
||||||
|
1. **Server CRUD**: Create, read, update, delete servers
|
||||||
|
2. **Configuration UI**: User-friendly interface for itzg container environment variables
|
||||||
|
3. **Console Access**: RCON integration for server management
|
||||||
|
4. **File Browser**: Access server files (worlds, configs, mods)
|
||||||
|
5. **Modpack Support**: ATLauncher-like experience for server creation
|
||||||
|
|
||||||
|
### Technical Stack Suggestions
|
||||||
|
- **Backend**: Node.js/Express or Python/FastAPI
|
||||||
|
- **Frontend**: React, Vue, or SvelteKit
|
||||||
|
- **Docker**: Direct Docker API integration
|
||||||
|
- **Storage**: File-based configs initially, optional database later
|
||||||
|
- **APIs**: Modrinth, CurseForge for modpack integration
|
||||||
|
|
||||||
|
### UI/UX Principles
|
||||||
|
- Take inspiration from Crafty and Fork for clean, intuitive interfaces
|
||||||
|
- Follow ATLauncher's model for modpack installation simplicity
|
||||||
|
- Use setupmc.com as reference for configuration UI patterns
|
||||||
|
- Prioritize common workflows over advanced features
|
||||||
|
|
||||||
|
### Implementation Phases
|
||||||
|
|
||||||
|
**MVP (Phase 1)**
|
||||||
|
- Basic Docker integration
|
||||||
|
- Simple server creation/management
|
||||||
|
- Basic web UI
|
||||||
|
- Console access
|
||||||
|
|
||||||
|
**V1.0 (Phase 2)**
|
||||||
|
- Full configuration UI
|
||||||
|
- File browser
|
||||||
|
- Modpack installation (basic)
|
||||||
|
- Proxy server support
|
||||||
|
|
||||||
|
**V1.5+ (Phase 3)**
|
||||||
|
- Modpack browser
|
||||||
|
- Advanced features
|
||||||
|
- Templates/presets
|
||||||
|
- Backup system
|
||||||
|
- mc-router integration
|
||||||
|
|
||||||
|
## Notes on Reference Projects
|
||||||
|
|
||||||
|
### Most Relevant
|
||||||
|
1. **itzg/docker-minecraft-server**: Core technology Stronghold will use
|
||||||
|
2. **itzg examples**: Real-world docker-compose configurations showing patterns and edge cases
|
||||||
|
3. **setupmc.com**: Shows how to simplify Docker config generation
|
||||||
|
4. **ATLauncher**: Target UX for modpack installation
|
||||||
|
5. **Crafty**: Good UI/UX patterns to follow
|
||||||
|
|
||||||
|
### Study but Don't Copy
|
||||||
|
1. **Pterodactyl**: Too complex, but has good separation of concerns
|
||||||
|
2. **MCSManager**: Good architecture ideas, but agent model overkill for Docker
|
||||||
|
3. **Fork**: Great UX, but Windows-focused
|
||||||
|
|
||||||
|
## Questions to Resolve
|
||||||
|
|
||||||
|
Based on these references, Stronghold needs to decide:
|
||||||
|
1. **Tech Stack**: Node.js vs Python vs Go?
|
||||||
|
2. **Database**: Start with files or include database from start?
|
||||||
|
3. **Multi-user**: Single-user MVP or multi-user from start?
|
||||||
|
4. **Deployment**: Single container, docker-compose, or microservices?
|
||||||
|
5. **Modpack APIs**: Which to support first (Modrinth, CurseForge, both)?
|
||||||
|
|
||||||
207
references/examples/ANALYSIS.md
Normal file
207
references/examples/ANALYSIS.md
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
# Docker Compose Examples Analysis
|
||||||
|
|
||||||
|
This document analyzes various docker-compose.yml examples to identify patterns, edge cases, and implementation requirements for Stronghold.
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Basic Server Setup
|
||||||
|
|
||||||
|
**Pattern Elements**:
|
||||||
|
- Image: `itzg/minecraft-server`
|
||||||
|
- Required: `EULA=TRUE`
|
||||||
|
- Port mapping: `25565:25565`
|
||||||
|
- Volume: Persistent data storage
|
||||||
|
- Environment variables for configuration
|
||||||
|
|
||||||
|
### Modded Server Setup
|
||||||
|
|
||||||
|
**Pattern Elements**:
|
||||||
|
- `TYPE`: FORGE, FABRIC, or other mod loader
|
||||||
|
- `MODPACK` or `MODS`: Modpack/mod installation
|
||||||
|
- Additional memory allocation
|
||||||
|
- Mod volume mounts (`/mods`)
|
||||||
|
- Config volume mounts (`/config`)
|
||||||
|
|
||||||
|
### Plugin Server Setup
|
||||||
|
|
||||||
|
**Pattern Elements**:
|
||||||
|
- `TYPE`: PAPER, SPIGOT, BUKKIT
|
||||||
|
- Plugin volume mounts (`/plugins`)
|
||||||
|
- `PLUGINS` environment variable or volume
|
||||||
|
|
||||||
|
### Proxy Server Setup
|
||||||
|
|
||||||
|
**Pattern Elements**:
|
||||||
|
- Image: `itzg/mc-proxy`
|
||||||
|
- `PROXY_TYPE`: BUNGEE, VELOCITY, WATERFALL
|
||||||
|
- Backend server configuration
|
||||||
|
- Network linking
|
||||||
|
|
||||||
|
## Edge Cases to Handle
|
||||||
|
|
||||||
|
### 1. Multiple Mod Sources
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
COPY_MODS_SRC: /mods-common,/mods-local
|
||||||
|
volumes:
|
||||||
|
- ../shared-mods:/mods-common:ro
|
||||||
|
- ./local-mods:/mods-local:ro
|
||||||
|
```
|
||||||
|
**Stronghold needs**: UI to manage multiple mod source directories
|
||||||
|
|
||||||
|
### 2. Generic Pack with Custom Configuration
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
GENERIC_PACK: https://example.com/server-pack.zip
|
||||||
|
GENERIC_PACKS_DISABLE_MODS: "conflicting-mod.jar"
|
||||||
|
APPLY_EXTRA_FILES: "config/server.properties<https://example.com/custom.properties"
|
||||||
|
```
|
||||||
|
**Stronghold needs**: Support for generic packs and file patching
|
||||||
|
|
||||||
|
### 3. Environment Variable Substitution in Configs
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
REPLACE_ENV_SUFFIXES: "yml,yaml,properties,json"
|
||||||
|
REPLACE_ENV_VARIABLES_EXCLUDES: "secrets.yml"
|
||||||
|
volumes:
|
||||||
|
- ./config:/config
|
||||||
|
```
|
||||||
|
**Stronghold needs**: UI for managing env var substitution rules
|
||||||
|
|
||||||
|
### 4. Auto-removal of Mods/Plugins
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
REMOVE_OLD_MODS: "TRUE"
|
||||||
|
REMOVE_OLD_MODS_INCLUDE: "*.jar"
|
||||||
|
REMOVE_OLD_MODS_EXCLUDE: "core-mod.jar"
|
||||||
|
REMOVE_OLD_MODS_DEPTH: 1
|
||||||
|
```
|
||||||
|
**Stronghold needs**: Options for mod/plugin cleanup behavior
|
||||||
|
|
||||||
|
### 5. Multiple Generic Packs with Prefix/Suffix
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
GENERIC_PACKS: configs-v9.0.1,mods-v4.3.6
|
||||||
|
GENERIC_PACKS_PREFIX: "https://cdn.example.org/"
|
||||||
|
GENERIC_PACKS_SUFFIX: ".zip"
|
||||||
|
```
|
||||||
|
**Stronghold needs**: Support for batch generic pack installation
|
||||||
|
|
||||||
|
### 6. Mod/Plugin URL Lists via File
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
MODS_FILE: /extras/mods.txt
|
||||||
|
volumes:
|
||||||
|
- ./mods-list.txt:/extras/mods.txt:ro
|
||||||
|
```
|
||||||
|
**Stronghold needs**: File-based mod/plugin list management
|
||||||
|
|
||||||
|
### 7. Custom Copy Destinations
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
COPY_PLUGINS_SRC: /plugins-custom
|
||||||
|
COPY_PLUGINS_DEST: /data/plugins
|
||||||
|
COPY_MODS_SRC: /mods-custom
|
||||||
|
COPY_MODS_DEST: /data/mods
|
||||||
|
COPY_CONFIG_SRC: /config-custom
|
||||||
|
COPY_CONFIG_DEST: /data/config
|
||||||
|
```
|
||||||
|
**Stronghold needs**: Advanced volume mount configuration options
|
||||||
|
|
||||||
|
### 8. Multi-Server Network with Proxy
|
||||||
|
**Complex Setup**:
|
||||||
|
- Multiple backend servers
|
||||||
|
- Proxy server
|
||||||
|
- Internal Docker network
|
||||||
|
- Port mapping coordination
|
||||||
|
- Service dependencies
|
||||||
|
|
||||||
|
**Stronghold needs**: Multi-container orchestration UI
|
||||||
|
|
||||||
|
### 9. Modpack Updates with Checksum Control
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
MODPACK: modrinth:abc123
|
||||||
|
SKIP_GENERIC_PACK_CHECKSUM: "false"
|
||||||
|
SKIP_GENERIC_PACK_UPDATE_CHECK: "false"
|
||||||
|
FORCE_GENERIC_PACK_UPDATE: "true"
|
||||||
|
```
|
||||||
|
**Stronghold needs**: Modpack update management UI
|
||||||
|
|
||||||
|
### 10. Hybrid Server (Mods + Plugins)
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
TYPE: MAGMA # or other hybrid type
|
||||||
|
USES_PLUGINS: "true"
|
||||||
|
volumes:
|
||||||
|
- ./mods:/mods
|
||||||
|
- ./plugins:/plugins
|
||||||
|
```
|
||||||
|
**Stronghold needs**: Detection and configuration of hybrid server types
|
||||||
|
|
||||||
|
## Configuration Complexity Levels
|
||||||
|
|
||||||
|
### Level 1: Simple
|
||||||
|
- Basic vanilla server
|
||||||
|
- Minimal configuration
|
||||||
|
- Default settings
|
||||||
|
|
||||||
|
**Stronghold UI**: Simple wizard, minimal options
|
||||||
|
|
||||||
|
### Level 2: Standard
|
||||||
|
- Common server types (Paper, Forge, Fabric)
|
||||||
|
- Standard modpack installation
|
||||||
|
- Basic resource allocation
|
||||||
|
|
||||||
|
**Stronghold UI**: Guided setup with sensible defaults
|
||||||
|
|
||||||
|
### Level 3: Advanced
|
||||||
|
- Custom configurations
|
||||||
|
- Multiple mod sources
|
||||||
|
- Environment variable substitution
|
||||||
|
- Custom volumes
|
||||||
|
|
||||||
|
**Stronghold UI**: Advanced options panel, expert mode
|
||||||
|
|
||||||
|
### Level 4: Expert
|
||||||
|
- Edge cases
|
||||||
|
- Manual docker-compose editing
|
||||||
|
- Complex network setups
|
||||||
|
- Custom scripts
|
||||||
|
|
||||||
|
**Stronghold UI**: YAML editor with validation, export/import
|
||||||
|
|
||||||
|
## Stronghold Implementation Priorities
|
||||||
|
|
||||||
|
### Phase 1 (MVP)
|
||||||
|
- Level 1 configurations (simple servers)
|
||||||
|
- Basic modpack installation (MODPACK env var)
|
||||||
|
- Standard volume mounts
|
||||||
|
|
||||||
|
### Phase 2 (V1.0)
|
||||||
|
- Level 2 configurations
|
||||||
|
- Individual mod/plugin management
|
||||||
|
- Multiple mod sources
|
||||||
|
- Basic generic pack support
|
||||||
|
|
||||||
|
### Phase 3 (V1.5+)
|
||||||
|
- Level 3 configurations
|
||||||
|
- Advanced volume mount options
|
||||||
|
- Environment variable substitution UI
|
||||||
|
- Multi-server orchestration
|
||||||
|
|
||||||
|
### Phase 4 (Future)
|
||||||
|
- Level 4 expert mode
|
||||||
|
- YAML editor/validator
|
||||||
|
- Import existing docker-compose files
|
||||||
|
- Template marketplace
|
||||||
|
|
||||||
|
## Key Takeaways
|
||||||
|
|
||||||
|
1. **Most users need Level 1-2**: Focus UI on these first
|
||||||
|
2. **Edge cases are powerful**: Support them but don't make them required
|
||||||
|
3. **Flexibility is key**: Allow export to docker-compose.yml for advanced users
|
||||||
|
4. **Validate configurations**: Check for conflicts and errors before applying
|
||||||
|
5. **Presets help**: Provide templates for common scenarios
|
||||||
|
6. **Documentation**: Link to itzg docs for advanced features
|
||||||
|
|
||||||
370
references/examples/EXAMPLES-ANALYSIS.md
Normal file
370
references/examples/EXAMPLES-ANALYSIS.md
Normal file
|
|
@ -0,0 +1,370 @@
|
||||||
|
# Detailed Example Analysis
|
||||||
|
|
||||||
|
This document provides detailed analysis of each example docker-compose file from the itzg repository.
|
||||||
|
|
||||||
|
## 1. docker-compose-big.yml - Large Biomes World Type
|
||||||
|
|
||||||
|
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-big.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-big.yml)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Demonstrates configuration for a vanilla Minecraft server with:
|
||||||
|
- Large Biomes world generation
|
||||||
|
- High memory allocation (32G)
|
||||||
|
- RCON web admin integration
|
||||||
|
- Performance tuning settings
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
|
||||||
|
#### World Configuration
|
||||||
|
- `LEVEL_TYPE: LARGEBIOMES` - Generates worlds with larger biome sizes
|
||||||
|
|
||||||
|
#### Performance Settings
|
||||||
|
- `MAX_MEMORY: 32G` - Very high memory allocation (for large servers)
|
||||||
|
- `MAX_BUILD_HEIGHT: 256` - Standard build height
|
||||||
|
- `VIEW_DISTANCE: 15` - Moderate view distance
|
||||||
|
- `MAX_PLAYERS: 100` - High player capacity
|
||||||
|
- `CONSOLE: "false"` - Disables console (likely uses RCON instead)
|
||||||
|
|
||||||
|
#### Multi-Service Setup
|
||||||
|
- **minecraft**: Main server container
|
||||||
|
- **rcon**: Separate RCON web admin service (itzg/rcon)
|
||||||
|
- Exposes ports 4326 and 4327 for web admin access
|
||||||
|
- Uses separate volume for RCON database
|
||||||
|
|
||||||
|
### Stronghold Implementation Notes
|
||||||
|
|
||||||
|
1. **World Type Selection**: UI needs dropdown/selector for world types:
|
||||||
|
- DEFAULT
|
||||||
|
- FLAT
|
||||||
|
- LARGEBIOMES
|
||||||
|
- AMPLIFIED
|
||||||
|
- And modded world types (e.g., "biomesoplenty")
|
||||||
|
|
||||||
|
2. **Memory Presets**: Provide memory presets:
|
||||||
|
- Small (1-2G): Single player / small groups
|
||||||
|
- Medium (4-8G): Small servers
|
||||||
|
- Large (16G+): Large servers
|
||||||
|
- Custom: User-specified
|
||||||
|
|
||||||
|
3. **RCON Integration**: Show option to include RCON web admin:
|
||||||
|
- Separate service
|
||||||
|
- Automatic port configuration
|
||||||
|
- Link to web admin interface
|
||||||
|
|
||||||
|
4. **Performance Tuning**: Advanced options panel:
|
||||||
|
- View distance
|
||||||
|
- Max players
|
||||||
|
- Build height
|
||||||
|
- Console enable/disable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. docker-compose-curseforge-atm7.yaml - All The Mods 7
|
||||||
|
|
||||||
|
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-curseforge-atm7.yaml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-curseforge-atm7.yaml)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Complete example for installing "All The Mods 7" modpack from CurseForge. This is a **comprehensive, well-documented example** that serves as an excellent template.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
|
||||||
|
#### CurseForge Modpack Installation
|
||||||
|
- `TYPE: CURSEFORGE` - CurseForge modpack type
|
||||||
|
- `CF_SERVER_MOD: /modpacks/ATM7-0.4.32-server.zip` - Path to modpack server files
|
||||||
|
- Modpack zip file mounted from local `./modpacks` directory
|
||||||
|
|
||||||
|
#### Extensive Configuration
|
||||||
|
- **Memory**: `8G` - Appropriate for large modpacks
|
||||||
|
- **Timezone**: `America/New_York` - Server timezone configuration
|
||||||
|
- **Logging**: `ENABLE_ROLLING_LOGS: "true"` - Automatic log rotation
|
||||||
|
- **Game Options**: Extensive server properties override
|
||||||
|
|
||||||
|
#### Server Properties Override
|
||||||
|
- `OVERRIDE_SERVER_PROPERTIES: "true"` - Allows environment variable overrides
|
||||||
|
- Custom difficulty, view distance, max players
|
||||||
|
- PVP settings
|
||||||
|
- Level type (biomesoplenty for ATM7)
|
||||||
|
- MOTD customization
|
||||||
|
- Flight allowed
|
||||||
|
- Max tick time set to -1 (no timeout)
|
||||||
|
|
||||||
|
### Edge Cases Demonstrated
|
||||||
|
|
||||||
|
1. **EULA in Modpack**: Important note that EULA file in modpack.zip will overwrite EULA flag
|
||||||
|
2. **Local Modpack Files**: Shows mounting local modpacks directory
|
||||||
|
3. **Custom Container Name**: `container_name: mc_atm7`
|
||||||
|
4. **Restart Policy**: `unless-stopped` for persistence
|
||||||
|
|
||||||
|
### Stronghold Implementation Notes
|
||||||
|
|
||||||
|
1. **Modpack Installation Wizard**:
|
||||||
|
- Step 1: Select modpack source (CurseForge, Modrinth, local file, URL)
|
||||||
|
- Step 2: If local file, prompt for file path or upload
|
||||||
|
- Step 3: Auto-detect recommended memory based on modpack size/complexity
|
||||||
|
- Step 4: Configuration presets (can customize later)
|
||||||
|
|
||||||
|
2. **Configuration UI Sections**:
|
||||||
|
- **Basic**: EULA, memory, restart policy
|
||||||
|
- **Modpack**: Type, source, file path
|
||||||
|
- **Performance**: View distance, max players, tick time
|
||||||
|
- **Gameplay**: Difficulty, PVP, flight, MOTD
|
||||||
|
- **Advanced**: Override flags, timezone, logging
|
||||||
|
|
||||||
|
3. **Modpack Detection**:
|
||||||
|
- Parse modpack manifest to suggest:
|
||||||
|
- Server type (FORGE/FABRIC)
|
||||||
|
- Recommended memory
|
||||||
|
- Minecraft version
|
||||||
|
- Required mods/dependencies
|
||||||
|
|
||||||
|
4. **File Management**:
|
||||||
|
- UI for managing modpack files
|
||||||
|
- Upload/download modpack files
|
||||||
|
- Version selection
|
||||||
|
- Update notifications
|
||||||
|
|
||||||
|
5. **Template Generation**:
|
||||||
|
- Use this as a template for modpack installations
|
||||||
|
- Generate similar structure with user's selections
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. docker-compose-curseforge.yml - Basic CurseForge Example
|
||||||
|
|
||||||
|
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-curseforge.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-curseforge.yml)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Minimal example showing CurseForge modpack installation. Demonstrates simplicity for basic use cases.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
|
||||||
|
#### Minimal Configuration
|
||||||
|
- Only essential variables
|
||||||
|
- `TYPE: CURSEFORGE`
|
||||||
|
- `CF_SERVER_MOD` pointing to modpack zip
|
||||||
|
- Uses environment variable substitution: `${IMAGE_TAG:-java8}`
|
||||||
|
|
||||||
|
#### Multiple Modpack Examples (Commented)
|
||||||
|
Shows flexibility with commented examples:
|
||||||
|
- Different modpack names
|
||||||
|
- URL-based modpack (direct download)
|
||||||
|
- Environment variable substitution for modpack selection
|
||||||
|
|
||||||
|
### Edge Cases Demonstrated
|
||||||
|
|
||||||
|
1. **Java Version Selection**: Uses `${IMAGE_TAG:-java8}` to select Java version
|
||||||
|
2. **URL Modpacks**: Shows that modpack can be a URL (direct download)
|
||||||
|
3. **Environment Variable Modpack Selection**: `${MODPACK:-default.zip}`
|
||||||
|
4. **Read-only Modpack Mount**: `./modpacks:/modpacks:ro`
|
||||||
|
|
||||||
|
### Stronghold Implementation Notes
|
||||||
|
|
||||||
|
1. **Simple Modpack Installation**:
|
||||||
|
- Minimal UI for quick setup
|
||||||
|
- Three input methods:
|
||||||
|
- Local file (browse/upload)
|
||||||
|
- URL (direct download)
|
||||||
|
- Modpack ID (if we integrate with CurseForge API)
|
||||||
|
|
||||||
|
2. **Java Version Selection**:
|
||||||
|
- Dropdown for Java version
|
||||||
|
- Auto-detect based on Minecraft version
|
||||||
|
- Allow manual override
|
||||||
|
|
||||||
|
3. **Quick Setup Mode**:
|
||||||
|
- Use this as "Simple" mode template
|
||||||
|
- Advanced options hidden by default
|
||||||
|
- "Show Advanced" toggle for power users
|
||||||
|
|
||||||
|
4. **Presets/Saved Configurations**:
|
||||||
|
- Save common modpack configurations
|
||||||
|
- Quick-switch between different modpacks
|
||||||
|
- Template library
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. docker-compose-magma.yml - Hybrid Mods + Plugins
|
||||||
|
|
||||||
|
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-magma.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-magma.yml)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Demonstrates Magma server type - a hybrid that supports both Forge mods AND Bukkit/Spigot plugins simultaneously.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
|
||||||
|
#### Hybrid Server Type
|
||||||
|
- `TYPE: MAGMA` - Enables both mods and plugins
|
||||||
|
- `VERSION: 1.16.5` - Specific Minecraft version
|
||||||
|
- `image: itzg/minecraft-server:java8` - Java 8 required for this version
|
||||||
|
|
||||||
|
#### Interactive Container
|
||||||
|
- `tty: true` - Allocates a pseudo-TTY
|
||||||
|
- `stdin_open: true` - Keeps STDIN open
|
||||||
|
- Allows interactive console access
|
||||||
|
|
||||||
|
### Edge Cases Demonstrated
|
||||||
|
|
||||||
|
1. **Hybrid Server Type**: Unique server type requiring special handling
|
||||||
|
2. **Version-Specific Java**: Older versions require Java 8
|
||||||
|
3. **Interactive Mode**: Console access configuration
|
||||||
|
|
||||||
|
### Stronghold Implementation Notes
|
||||||
|
|
||||||
|
1. **Server Type Detection**:
|
||||||
|
- When user selects Magma (or other hybrid types):
|
||||||
|
- Show both "Mods" and "Plugins" tabs/sections
|
||||||
|
- Support both `/mods` and `/plugins` volume mounts
|
||||||
|
- Set `USES_PLUGINS=true` if needed
|
||||||
|
|
||||||
|
2. **Java Version Compatibility**:
|
||||||
|
- Map Minecraft versions to required Java versions
|
||||||
|
- Warn user if incompatible Java version selected
|
||||||
|
- Auto-select appropriate Java version
|
||||||
|
|
||||||
|
3. **Version-Specific Handling**:
|
||||||
|
- Some server types only work with specific Minecraft versions
|
||||||
|
- Validate compatibility
|
||||||
|
- Show warnings/errors
|
||||||
|
|
||||||
|
4. **Interactive Console**:
|
||||||
|
- Option to enable interactive console
|
||||||
|
- Useful for debugging
|
||||||
|
- May not be needed for most users
|
||||||
|
|
||||||
|
5. **Hybrid Server UI**:
|
||||||
|
- Special UI mode for hybrid servers
|
||||||
|
- Clear separation of mods vs plugins
|
||||||
|
- Different management for each type
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. docker-compose-rconcmd.yml - Advanced RCON Commands
|
||||||
|
|
||||||
|
**Source**: [itzg/docker-minecraft-server/examples/docker-compose-rconcmd.yml](https://github.com/itzg/docker-minecraft-server/blob/master/examples/docker-compose-rconcmd.yml)
|
||||||
|
|
||||||
|
### Purpose
|
||||||
|
Demonstrates advanced RCON usage with:
|
||||||
|
- Automated command execution at different lifecycle events
|
||||||
|
- CurseForge API integration for mod installation
|
||||||
|
- YAML heredoc syntax for multi-line commands
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
|
||||||
|
#### RCON Lifecycle Commands
|
||||||
|
- `RCON_CMDS_STARTUP` - Commands executed when server starts
|
||||||
|
- `RCON_CMDS_ON_CONNECT` - Commands executed when any player connects
|
||||||
|
- `RCON_CMDS_FIRST_CONNECT` - Commands executed on first player connection
|
||||||
|
- `RCON_CMDS_LAST_DISCONNECT` - Commands executed when last player disconnects
|
||||||
|
|
||||||
|
#### CurseForge API Integration
|
||||||
|
- `CURSEFORGE_FILES` - List of CurseForge mod names to auto-install
|
||||||
|
- `CF_API_KEY` - CurseForge API key from environment variable
|
||||||
|
- Requires API key from https://console.curseforge.com/
|
||||||
|
|
||||||
|
#### Advanced YAML Syntax
|
||||||
|
- Uses `|-` heredoc to remove leading/trailing newlines
|
||||||
|
- Multi-line command lists
|
||||||
|
- Proper formatting for readability
|
||||||
|
|
||||||
|
### Example Use Case
|
||||||
|
The example shows:
|
||||||
|
- Setting game rules on startup
|
||||||
|
- Creating teams for players
|
||||||
|
- Using Chunky (world pregeneration) mod
|
||||||
|
- Giving items to new players
|
||||||
|
- Managing entities when players disconnect
|
||||||
|
|
||||||
|
### Edge Cases Demonstrated
|
||||||
|
|
||||||
|
1. **Environment Variable Security**: API keys from `.env` file (not in compose file)
|
||||||
|
2. **YAML Heredoc Syntax**: Proper multi-line string handling
|
||||||
|
3. **CurseForge API Key**: Requires external API key management
|
||||||
|
4. **Lifecycle Hooks**: Automated server management via RCON
|
||||||
|
|
||||||
|
### Stronghold Implementation Notes
|
||||||
|
|
||||||
|
1. **RCON Command Management UI**:
|
||||||
|
- Tabs/sections for each lifecycle event
|
||||||
|
- Multi-line text editor for commands
|
||||||
|
- Command validation
|
||||||
|
- Preview of commands that will run
|
||||||
|
- Test/simulate commands
|
||||||
|
|
||||||
|
2. **CurseForge API Integration**:
|
||||||
|
- UI for managing API key (stored securely)
|
||||||
|
- Search/browse CurseForge mods by name
|
||||||
|
- Auto-install mods via API
|
||||||
|
- Dependency resolution
|
||||||
|
|
||||||
|
3. **Lifecycle Events**:
|
||||||
|
- **Startup**: Server initialization commands
|
||||||
|
- **On Connect**: Welcome messages, permissions, etc.
|
||||||
|
- **First Connect**: One-time setup (world generation, etc.)
|
||||||
|
- **Last Disconnect**: Cleanup, save, shutdown tasks
|
||||||
|
|
||||||
|
4. **Command Editor**:
|
||||||
|
- Syntax highlighting (Minecraft commands)
|
||||||
|
- Auto-completion
|
||||||
|
- Validation
|
||||||
|
- Help/cheat sheet
|
||||||
|
|
||||||
|
5. **Security**:
|
||||||
|
- Secure storage of API keys
|
||||||
|
- Environment variable management
|
||||||
|
- Never expose secrets in generated files
|
||||||
|
|
||||||
|
6. **Advanced Features**:
|
||||||
|
- Schedule commands (cron-like)
|
||||||
|
- Conditional commands
|
||||||
|
- Command templates
|
||||||
|
- Import/export command sets
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Patterns Across Examples
|
||||||
|
|
||||||
|
### 1. Volume Management
|
||||||
|
- All use named volumes or bind mounts for `/data`
|
||||||
|
- Some use read-only mounts for modpacks
|
||||||
|
- Consistent pattern: `volume_name:/data`
|
||||||
|
|
||||||
|
### 2. Environment Variables
|
||||||
|
- `EULA: "TRUE"` - Always required
|
||||||
|
- `TYPE` - Server type
|
||||||
|
- `MEMORY` - Memory allocation
|
||||||
|
- Port mapping: `25565:25565`
|
||||||
|
|
||||||
|
### 3. Restart Policies
|
||||||
|
- `always` - Production servers
|
||||||
|
- `unless-stopped` - Common for game servers
|
||||||
|
- `on-failure` - Development/testing
|
||||||
|
|
||||||
|
### 4. Image Tags
|
||||||
|
- Default: `itzg/minecraft-server`
|
||||||
|
- Java version: `:java8`, `:java17`, etc.
|
||||||
|
- Architecture: `:arm64`, `:amd64`, etc.
|
||||||
|
- Environment variable substitution common
|
||||||
|
|
||||||
|
## Implementation Priorities for Stronghold
|
||||||
|
|
||||||
|
### Phase 1 (MVP)
|
||||||
|
- Basic server creation (like curseforge.yml)
|
||||||
|
- Simple modpack installation
|
||||||
|
- Essential environment variables
|
||||||
|
|
||||||
|
### Phase 2 (V1.0)
|
||||||
|
- Advanced modpack configuration (like atm7.yaml)
|
||||||
|
- World type selection (like big.yml)
|
||||||
|
- RCON basic integration
|
||||||
|
|
||||||
|
### Phase 3 (V1.5)
|
||||||
|
- Hybrid server support (like magma.yml)
|
||||||
|
- RCON lifecycle commands (like rconcmd.yml)
|
||||||
|
- CurseForge API integration
|
||||||
|
|
||||||
|
### Phase 4 (Future)
|
||||||
|
- Multi-service orchestration
|
||||||
|
- Advanced RCON features
|
||||||
|
- Template library
|
||||||
|
- Import/export configurations
|
||||||
|
|
||||||
48
references/examples/README.md
Normal file
48
references/examples/README.md
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Docker Compose Examples Reference
|
||||||
|
|
||||||
|
This directory contains example Docker Compose configurations for various Minecraft server setups using itzg containers.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
These examples serve as:
|
||||||
|
- **Reference implementations** for common server configurations
|
||||||
|
- **Edge case demonstrations** for complex scenarios
|
||||||
|
- **Learning materials** for understanding itzg container capabilities
|
||||||
|
- **Templates** for Stronghold to generate similar configurations
|
||||||
|
|
||||||
|
## Categories
|
||||||
|
|
||||||
|
### Basic Examples
|
||||||
|
- Simple vanilla server
|
||||||
|
- Basic modded server (Forge/Fabric)
|
||||||
|
- Basic plugin server (Paper/Spigot)
|
||||||
|
|
||||||
|
### Advanced Examples
|
||||||
|
- Multi-server setups
|
||||||
|
- Proxy server configurations
|
||||||
|
- Modpack installations
|
||||||
|
- Custom configurations
|
||||||
|
|
||||||
|
### Edge Cases
|
||||||
|
- Complex volume mounts
|
||||||
|
- Multiple mod sources
|
||||||
|
- Environment variable substitution
|
||||||
|
- Generic pack installations
|
||||||
|
- Custom network configurations
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Examples are typically sourced from:
|
||||||
|
- [itzg/docker-minecraft-server GitHub examples](https://github.com/itzg/docker-minecraft-server)
|
||||||
|
- Community contributions
|
||||||
|
- Documentation examples
|
||||||
|
- Real-world use cases
|
||||||
|
|
||||||
|
## Usage in Stronghold
|
||||||
|
|
||||||
|
Stronghold should:
|
||||||
|
1. Analyze these examples to understand configuration patterns
|
||||||
|
2. Generate similar configurations dynamically based on user input
|
||||||
|
3. Provide presets/templates based on common patterns
|
||||||
|
4. Handle edge cases gracefully in the UI
|
||||||
|
|
||||||
28
references/examples/docker-compose-big.yml
Normal file
28
references/examples/docker-compose-big.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
services:
|
||||||
|
minecraft:
|
||||||
|
ports:
|
||||||
|
- "25565:25565"
|
||||||
|
volumes:
|
||||||
|
- "mcbig:/data"
|
||||||
|
environment:
|
||||||
|
EULA: "TRUE"
|
||||||
|
MAX_MEMORY: 32G
|
||||||
|
MAX_BUILD_HEIGHT: 256
|
||||||
|
VIEW_DISTANCE: 15
|
||||||
|
LEVEL_TYPE: LARGEBIOMES
|
||||||
|
MAX_PLAYERS: 100
|
||||||
|
CONSOLE: "false"
|
||||||
|
image: itzg/minecraft-server
|
||||||
|
restart: always
|
||||||
|
rcon:
|
||||||
|
image: itzg/rcon
|
||||||
|
ports:
|
||||||
|
- "4326:4326"
|
||||||
|
- "4327:4327"
|
||||||
|
volumes:
|
||||||
|
- "rcon:/opt/rcon-web-admin/db"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mcbig:
|
||||||
|
rcon:
|
||||||
|
|
||||||
149
references/examples/docker-compose-curseforge-atm7.yaml
Normal file
149
references/examples/docker-compose-curseforge-atm7.yaml
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
####################################################################
|
||||||
|
# CURSEFORGE #
|
||||||
|
# #
|
||||||
|
# Date: 20221005 #
|
||||||
|
# #
|
||||||
|
# Mod: All The Mods 7 0.4.32 #
|
||||||
|
# #
|
||||||
|
# Notes: Verify that there is no EULA file in the modpack.zip #
|
||||||
|
# if you do not delete it the EULA flag below will be #
|
||||||
|
# overwritten when the modpack is copied. #
|
||||||
|
# #
|
||||||
|
####################################################################
|
||||||
|
services:
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Service Name #
|
||||||
|
# #
|
||||||
|
# Define Service Name here. If using RCON this name will be #
|
||||||
|
# referenced again as RWA_RCON_HOST below. #
|
||||||
|
# #
|
||||||
|
# Example: 'name:' or 'mc_atm6:' #
|
||||||
|
####################################################################
|
||||||
|
mc_atm7:
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Image & Container Name #
|
||||||
|
# #
|
||||||
|
# Specify Image Name and Java Version. The 'image' will always be #
|
||||||
|
# 'itzg/minecraft-server' however the tag added to the end is #
|
||||||
|
# where you can specify the java version or container architecture.#
|
||||||
|
# See readme.md for a full list. #
|
||||||
|
# #
|
||||||
|
# 'container_name:' This can be anything you like. This is the name#
|
||||||
|
# that will show when you run 'docker ps' commands. #
|
||||||
|
####################################################################
|
||||||
|
image: itzg/minecraft-server
|
||||||
|
container_name: mc_atm7
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Server Ports #
|
||||||
|
# #
|
||||||
|
# Specify external port. #
|
||||||
|
####################################################################
|
||||||
|
ports:
|
||||||
|
- 25565:25565
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Automatic Server Restart #
|
||||||
|
# #
|
||||||
|
# Define a restart policy here. #
|
||||||
|
# - 'no' = Do not restart. #
|
||||||
|
# - 'on-failure' = Restart if container exits because an error. #
|
||||||
|
# - 'always' = Regardless of stop reason. #
|
||||||
|
# - 'unless-stopped' = Similar to always except if stopped. #
|
||||||
|
####################################################################
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Volume and Folder Access #
|
||||||
|
# #
|
||||||
|
# This section defines what folders and volumes you want to give #
|
||||||
|
# this container access to. It is recommended to leaves these set #
|
||||||
|
# to the default values unless you know what you are doing. #
|
||||||
|
# #
|
||||||
|
# Place your mod zip file in a folder called 'modpacks' in the #
|
||||||
|
# same directory you place this docker-compose file. #
|
||||||
|
# #
|
||||||
|
# Specify the data volume name or directory here as well. #
|
||||||
|
# In this example the volume name is 'data'. When docker creates #
|
||||||
|
# the volume it will add what ever name you give it here to the #
|
||||||
|
# end of the container name specified above. In this example it #
|
||||||
|
# would be named 'mc_atm6_data'. If you change this be sure to #
|
||||||
|
# update the volume name at the bottom of this config. #
|
||||||
|
####################################################################
|
||||||
|
volumes:
|
||||||
|
- ./modpacks:/modpacks:ro
|
||||||
|
- data:/data
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# EULA #
|
||||||
|
# #
|
||||||
|
# Accept EULA by setting to "true" #
|
||||||
|
####################################################################
|
||||||
|
environment:
|
||||||
|
EULA: "true"
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# CURSEFORGE INSTALL #
|
||||||
|
# #
|
||||||
|
# Sets install type to FORGE and specifys the zip folder name #
|
||||||
|
# and location of your mod pack. #
|
||||||
|
# #
|
||||||
|
# TYPE: Defines the install type as CURSEFORGE #
|
||||||
|
# #
|
||||||
|
# CF_SERVER_MOD: Define where the modpack.zip is located. #
|
||||||
|
# #
|
||||||
|
# Place your mod zip file in a folder called 'modpacks' in the #
|
||||||
|
# same directory you place this docker-compose file. #
|
||||||
|
####################################################################
|
||||||
|
TYPE: CURSEFORGE
|
||||||
|
CF_SERVER_MOD: /modpacks/ATM7-0.4.32-server.zip
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Server Memory #
|
||||||
|
# #
|
||||||
|
# Set Maximum amount of memory allowed for your server. #
|
||||||
|
####################################################################
|
||||||
|
MEMORY: "8G"
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Logging Options #
|
||||||
|
# #
|
||||||
|
# Set to "true" to delete old logs #
|
||||||
|
################################################################ ####
|
||||||
|
ENABLE_ROLLING_LOGS: "true"
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Server Timezone #
|
||||||
|
# #
|
||||||
|
# Specify server Timezone #
|
||||||
|
####################################################################
|
||||||
|
TZ: "America/New_York"
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Minecraft Game Options #
|
||||||
|
# #
|
||||||
|
# List any game options you want to define here. A full list can #
|
||||||
|
# be found on the readme.md page on github. #
|
||||||
|
####################################################################
|
||||||
|
OVERRIDE_SERVER_PROPERTIES: "true"
|
||||||
|
DIFFICULTY: "easy"
|
||||||
|
MAX_TICK_TIME: "-1"
|
||||||
|
ALLOW_FLIGHT: "true"
|
||||||
|
OPS: ""
|
||||||
|
VIEW_DISTANCE: 10
|
||||||
|
MAX_PLAYERS: 10
|
||||||
|
PVP: "false"
|
||||||
|
LEVEL_TYPE: "biomesoplenty"
|
||||||
|
MOTD: "Welcome Home"
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
# Volumes #
|
||||||
|
# #
|
||||||
|
# Define data volume name here. You should leave this set to the #
|
||||||
|
# default. #
|
||||||
|
####################################################################
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
|
|
||||||
20
references/examples/docker-compose-curseforge.yml
Normal file
20
references/examples/docker-compose-curseforge.yml
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
services:
|
||||||
|
mc:
|
||||||
|
image: itzg/minecraft-server:${IMAGE_TAG:-java8}
|
||||||
|
volumes:
|
||||||
|
- ./modpacks:/modpacks:ro
|
||||||
|
- data:/data
|
||||||
|
environment:
|
||||||
|
EULA: "true"
|
||||||
|
TYPE: CURSEFORGE
|
||||||
|
CF_SERVER_MOD: /modpacks/SIMPLE-SERVER-FILES-0.3.20.zip
|
||||||
|
# CF_SERVER_MOD: /modpacks/createlive3serverfiles+1.4.2.zip
|
||||||
|
# CF_SERVER_MOD: /modpacks/Valhelsia+3-3.5.1-SERVER.zip
|
||||||
|
# CF_SERVER_MOD: https://media.forgecdn.net/files/3012/800/SkyFactory-4_Server_4.2.2.zip
|
||||||
|
# CF_SERVER_MOD: /modpacks/${MODPACK:-SkyFactory_4_Server_4.1.0.zip}
|
||||||
|
ports:
|
||||||
|
- "25565:25565"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
data: {}
|
||||||
|
|
||||||
16
references/examples/docker-compose-magma.yml
Normal file
16
references/examples/docker-compose-magma.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
services:
|
||||||
|
mc:
|
||||||
|
image: itzg/minecraft-server:java8
|
||||||
|
tty: true
|
||||||
|
stdin_open: true
|
||||||
|
ports:
|
||||||
|
- "25565:25565"
|
||||||
|
environment:
|
||||||
|
EULA: "TRUE"
|
||||||
|
TYPE: MAGMA
|
||||||
|
VERSION: 1.16.5
|
||||||
|
volumes:
|
||||||
|
- data:/data
|
||||||
|
volumes:
|
||||||
|
data: {}
|
||||||
|
|
||||||
40
references/examples/docker-compose-rconcmd.yml
Normal file
40
references/examples/docker-compose-rconcmd.yml
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
services:
|
||||||
|
minecraft:
|
||||||
|
image: ${IMAGE_TO_TEST:-itzg/minecraft-server}
|
||||||
|
ports:
|
||||||
|
- "25565:25565"
|
||||||
|
volumes:
|
||||||
|
- "mc:/data"
|
||||||
|
environment:
|
||||||
|
EULA: "TRUE"
|
||||||
|
TYPE: FABRIC
|
||||||
|
MEMORY: "2G"
|
||||||
|
CURSEFORGE_FILES: |
|
||||||
|
fabric-api
|
||||||
|
chunky-pregenerator
|
||||||
|
# Allocate API key from https://console.curseforge.com/
|
||||||
|
# and set in .env file making sure to double up dollar signs, such as
|
||||||
|
# CF_API_KEY=$$2a$$10$$....
|
||||||
|
# Refer to https://docker-minecraft-server.readthedocs.io/en/latest/types-and-platforms/mod-platforms/auto-curseforge/#api-key
|
||||||
|
CF_API_KEY: ${CF_API_KEY}
|
||||||
|
# YAML Heredoc, be sure to use '|-' this will remove the first newline and final new line.
|
||||||
|
# This is versus '|' that will leaving with two empty strings at top and bottom.
|
||||||
|
RCON_CMDS_STARTUP: |-
|
||||||
|
/gamerule doFireTick false
|
||||||
|
/team add New
|
||||||
|
/team add Old
|
||||||
|
/chunky radius 1000
|
||||||
|
/chunky start
|
||||||
|
RCON_CMDS_ON_CONNECT: |-
|
||||||
|
/team join New @a[team=]
|
||||||
|
/give @a[team=New] birch_boat
|
||||||
|
/team join Old @a[team=New]
|
||||||
|
RCON_CMDS_FIRST_CONNECT: |-
|
||||||
|
/chunky pause
|
||||||
|
RCON_CMDS_LAST_DISCONNECT: |-
|
||||||
|
/kill @e[type=minecraft:boat]
|
||||||
|
/chunky continue
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
mc: {}
|
||||||
|
|
||||||
283
references/itzg-projects.md
Normal file
283
references/itzg-projects.md
Normal file
|
|
@ -0,0 +1,283 @@
|
||||||
|
# itzg Projects Reference
|
||||||
|
|
||||||
|
These are the core Docker projects by itzg that Stronghold will integrate with.
|
||||||
|
|
||||||
|
## itzg/docker-minecraft-server
|
||||||
|
**GitHub**: https://github.com/itzg/docker-minecraft-server
|
||||||
|
**Docs**: https://docker-minecraft-server.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Docker image for running Minecraft Java Edition servers. Automatically downloads the specified version at startup.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Automatic Version Management**: Downloads and sets up the specified Minecraft version at startup
|
||||||
|
- **Server Type Support**: Supports multiple server types via `TYPE` environment variable:
|
||||||
|
- VANILLA
|
||||||
|
- PAPER (optimized, recommended)
|
||||||
|
- FORGE (modded servers)
|
||||||
|
- FABRIC (modded servers)
|
||||||
|
- SPIGOT
|
||||||
|
- BUKKIT
|
||||||
|
- And more
|
||||||
|
- **Configuration via Environment Variables**: Extensive configuration through environment variables for:
|
||||||
|
- Server properties (gamemode, difficulty, seed, etc.)
|
||||||
|
- Memory allocation (`MEMORY`)
|
||||||
|
- Java version selection
|
||||||
|
- Modpack installation
|
||||||
|
- Plugin management
|
||||||
|
- **Modpack Integration**:
|
||||||
|
- Supports Modrinth and CurseForge modpack installation
|
||||||
|
- Can download server packs automatically
|
||||||
|
- Handles mod installation and updates
|
||||||
|
- Feed the Beast (FTB) support
|
||||||
|
- Packwiz modpack support
|
||||||
|
- **Data Persistence**: Uses `/data` volume for:
|
||||||
|
- World data
|
||||||
|
- Server configurations
|
||||||
|
- Mods and plugins
|
||||||
|
- Logs
|
||||||
|
- **Health Monitoring**: Built-in health checks
|
||||||
|
- **RCON Support**: Built-in RCON support for remote console access
|
||||||
|
- **Resource Management**: Memory and CPU allocation via Docker
|
||||||
|
|
||||||
|
### Important Environment Variables
|
||||||
|
- `TYPE`: Server type (VANILLA, PAPER, FORGE, FABRIC, etc.)
|
||||||
|
- `VERSION`: Minecraft version (LATEST, specific version, or SNAPSHOT)
|
||||||
|
- `MEMORY`: JVM memory allocation
|
||||||
|
- `EULA`: Must be set to `TRUE` to accept Minecraft EULA
|
||||||
|
- `MOTD`: Server message of the day
|
||||||
|
- `DIFFICULTY`: Game difficulty
|
||||||
|
- `MODE`: Game mode (survival, creative, adventure, spectator)
|
||||||
|
- `MODPACK`: Modpack URL or ID for automatic installation
|
||||||
|
|
||||||
|
### Mods and Plugins Management
|
||||||
|
**Reference**: [itzg Mods and Plugins Documentation](https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/)
|
||||||
|
|
||||||
|
#### Modpack Platforms Supported
|
||||||
|
- **Modrinth**: Auto-download modpacks from Modrinth
|
||||||
|
- **CurseForge**: Auto-download modpacks from CurseForge
|
||||||
|
- **Feed the Beast (FTB)**: FTB modpack support
|
||||||
|
- **Packwiz**: Packwiz format modpacks
|
||||||
|
|
||||||
|
#### Modpack Installation Methods
|
||||||
|
|
||||||
|
1. **MODPACK Environment Variable**:
|
||||||
|
- Set `MODPACK` to a zip file URL or container path
|
||||||
|
- Automatically downloads and installs modpack
|
||||||
|
- Works with zip files containing jar files at top level
|
||||||
|
|
||||||
|
2. **GENERIC_PACK / GENERIC_PACKS**:
|
||||||
|
- Install all server content (jars, mods, plugins, configs) from zip/tgz
|
||||||
|
- Can apply CurseForge modpacks missing server start script
|
||||||
|
- Supports multiple packs: `GENERIC_PACKS=pack1,pack2`
|
||||||
|
- Prefix/suffix support: `GENERIC_PACKS_PREFIX` and `GENERIC_PACKS_SUFFIX`
|
||||||
|
- Update checksum control: `SKIP_GENERIC_PACK_CHECKSUM`, `SKIP_GENERIC_PACK_UPDATE_CHECK`, `FORCE_GENERIC_PACK_UPDATE`
|
||||||
|
- Can disable specific mods: `GENERIC_PACKS_DISABLE_MODS`
|
||||||
|
|
||||||
|
#### Individual Mod/Plugin Installation
|
||||||
|
|
||||||
|
1. **MODS / PLUGINS Environment Variables**:
|
||||||
|
- Comma or newline delimited list of:
|
||||||
|
- URLs of jar files
|
||||||
|
- Container paths to jar files
|
||||||
|
- Container paths to directories containing jars
|
||||||
|
- Example:
|
||||||
|
```yaml
|
||||||
|
MODS: |
|
||||||
|
https://example.com/mod1.jar
|
||||||
|
/plugins/common/mod2.jar
|
||||||
|
```
|
||||||
|
- **Auto-removal**: Entries removed from list automatically remove files from server
|
||||||
|
- Empty list removes all mods/plugins
|
||||||
|
|
||||||
|
2. **MODS_FILE / PLUGINS_FILE**:
|
||||||
|
- Path or URL to text file listing mod/plugin URLs (one per line)
|
||||||
|
- Blank lines and lines starting with `#` are ignored
|
||||||
|
- Useful for managing large mod lists
|
||||||
|
|
||||||
|
#### Volume Mount Points for Mods/Plugins
|
||||||
|
|
||||||
|
1. **`/plugins` Volume**:
|
||||||
|
- Syncs into `/data/plugins` for plugin-based server types
|
||||||
|
- Customizable: `COPY_PLUGINS_SRC`, `COPY_PLUGINS_DEST`
|
||||||
|
- Set `USES_PLUGINS=true` for hybrid mod-based loaders using plugins
|
||||||
|
|
||||||
|
2. **`/mods` Volume**:
|
||||||
|
- Syncs into `/data/mods` for mod-based server types
|
||||||
|
- Customizable: `COPY_MODS_SRC`, `COPY_MODS_DEST`
|
||||||
|
|
||||||
|
3. **`/config` Volume**:
|
||||||
|
- Syncs into `/data/config` by default
|
||||||
|
- Customizable: `COPY_CONFIG_SRC`, `COPY_CONFIG_DEST`
|
||||||
|
- Environment variable processing on config files
|
||||||
|
- `SYNC_SKIP_NEWER_IN_DESTINATION=false` to force overwrite
|
||||||
|
|
||||||
|
4. **Multiple Source Directories**:
|
||||||
|
- `COPY_PLUGINS_SRC`, `COPY_MODS_SRC`, `COPY_CONFIG_SRC` can be comma/newline delimited lists
|
||||||
|
- Allows combining shared and project-specific mods/plugins
|
||||||
|
|
||||||
|
#### Environment Variable Processing
|
||||||
|
- Config files matching suffixes in `REPLACE_ENV_SUFFIXES` (default: "yml,yaml,txt,cfg,conf,properties,hjson,json,tml,toml")
|
||||||
|
- Exclusions: `REPLACE_ENV_VARIABLES_EXCLUDES`, `REPLACE_ENV_VARIABLES_EXCLUDE_PATHS`
|
||||||
|
- Disable with: `REPLACE_ENV_DURING_SYNC=false`
|
||||||
|
|
||||||
|
#### Removing Old Mods/Plugins
|
||||||
|
|
||||||
|
- **REMOVE_OLD_MODS=TRUE**: Delete old mods/plugins before installing new ones
|
||||||
|
- Fine-tuning options:
|
||||||
|
- `REMOVE_OLD_MODS_INCLUDE`: Comma-separated glob patterns to include
|
||||||
|
- `REMOVE_OLD_MODS_EXCLUDE`: Comma-separated glob patterns to exclude
|
||||||
|
- `REMOVE_OLD_MODS_DEPTH`: Maximum directory depth (default: 16)
|
||||||
|
- Default: Only jar files are removed
|
||||||
|
|
||||||
|
#### Additional Configuration Files
|
||||||
|
|
||||||
|
- **APPLY_EXTRA_FILES**: Download/copy additional config files before server starts
|
||||||
|
- Format: `destination<source_url` or `destination<source_path`
|
||||||
|
- Multiple entries with comma/newline separation
|
||||||
|
- Useful for baseline configs to be patched at runtime
|
||||||
|
- Processed before patch processing
|
||||||
|
|
||||||
|
#### Mods vs Plugins
|
||||||
|
|
||||||
|
- **Mods**: Client-side modifications for Forge/Fabric servers
|
||||||
|
- Modify rendering, add blocks, add behaviors
|
||||||
|
- Usually need to be installed on both client and server
|
||||||
|
- **Plugins**: Server-only modifications for Bukkit/Spigot/Paper
|
||||||
|
- Add behaviors, commands, features
|
||||||
|
- Only installed on server
|
||||||
|
- **Hybrid**: Some types like Magma use both mods and plugins
|
||||||
|
|
||||||
|
### Usage Pattern
|
||||||
|
Stronghold will need to:
|
||||||
|
1. Generate docker-compose.yml files or use Docker API to create containers
|
||||||
|
2. Set appropriate environment variables based on user configuration
|
||||||
|
3. Mount volumes for persistent data storage
|
||||||
|
4. Expose appropriate ports
|
||||||
|
5. Handle container lifecycle (start, stop, restart, delete)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## itzg/docker-mc-proxy
|
||||||
|
**GitHub**: https://github.com/itzg/docker-mc-proxy
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Docker image for running Minecraft proxy servers (BungeeCord, Waterfall, Velocity). Enables managing multiple Minecraft servers under a single proxy network.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Proxy Type Support**:
|
||||||
|
- BungeeCord
|
||||||
|
- Waterfall (BungeeCord fork)
|
||||||
|
- Velocity
|
||||||
|
- **Configuration Management**:
|
||||||
|
- Configuration files via volume mounts
|
||||||
|
- Environment variable substitution in config files
|
||||||
|
- Automatic config synchronization
|
||||||
|
- **Plugin Support**:
|
||||||
|
- Plugins mounted via volumes
|
||||||
|
- Supports proxy-specific plugins
|
||||||
|
- **Port Configuration**:
|
||||||
|
- Default proxy port (25577) can be mapped
|
||||||
|
- Custom port configuration
|
||||||
|
- **Health Checks**: Built-in health monitoring
|
||||||
|
- **Java Version Support**: Multiple Java version tags available
|
||||||
|
- **Multi-Architecture**: Supports multiple CPU architectures
|
||||||
|
|
||||||
|
### Usage Pattern
|
||||||
|
Stronghold will need to:
|
||||||
|
1. Create proxy containers with appropriate type
|
||||||
|
2. Configure proxy config files (config.yml, servers.yml, etc.)
|
||||||
|
3. Link backend servers to the proxy
|
||||||
|
4. Manage plugin installation
|
||||||
|
5. Handle port mapping and routing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## itzg/mc-router
|
||||||
|
**GitHub**: https://github.com/itzg/mc-router
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Lightweight TCP reverse proxy/multiplexer for Minecraft Java servers. Routes client connections to backend servers based on the requested server address (SRV records or subdomain).
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Dynamic Routing**: Routes based on server address requested by client
|
||||||
|
- **Docker Auto-Discovery**: Automatically discovers running Minecraft containers
|
||||||
|
- **Health Checks**: Monitors backend server health
|
||||||
|
- **Load Balancing**: Can distribute connections across multiple servers
|
||||||
|
- **REST API**: Provides REST API for managing routes
|
||||||
|
- **High Availability**: Redirects to operational servers if one fails
|
||||||
|
- **Configuration**:
|
||||||
|
- Can be configured via environment variables
|
||||||
|
- Supports static route configuration
|
||||||
|
- Docker labels for dynamic discovery
|
||||||
|
|
||||||
|
### Usage Pattern
|
||||||
|
Stronghold will need to:
|
||||||
|
1. Deploy mc-router container alongside servers
|
||||||
|
2. Configure routing rules (static or dynamic)
|
||||||
|
3. Use Docker labels for auto-discovery
|
||||||
|
4. Manage backend server registrations via REST API
|
||||||
|
5. Monitor route health and availability
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## itzg/docker-rcon-web-admin
|
||||||
|
**GitHub**: https://github.com/itzg/docker-rcon-web-admin
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Web-based admin interface for Minecraft servers via RCON (Remote Console).
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **RCON Integration**: Connects to Minecraft servers via RCON protocol
|
||||||
|
- **Web Interface**: Browser-based console access
|
||||||
|
- **Command Execution**: Execute server commands remotely
|
||||||
|
- **Log Viewing**: View server logs in real-time
|
||||||
|
- **Docker Integration**: Runs as a side-car container
|
||||||
|
|
||||||
|
### Usage Pattern
|
||||||
|
Stronghold may want to:
|
||||||
|
1. Integrate RCON functionality directly rather than using separate container
|
||||||
|
2. Use this as reference for RCON implementation
|
||||||
|
3. Potentially use as a fallback/admin interface
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Takeaways for Stronghold
|
||||||
|
|
||||||
|
1. **Environment Variable Configuration**: All itzg containers use extensive environment variable configuration. Stronghold needs a robust system for:
|
||||||
|
- UI to configure these variables
|
||||||
|
- Validating configurations
|
||||||
|
- Generating docker-compose.yml files or Docker API calls
|
||||||
|
|
||||||
|
2. **Volume Management**: Need to manage Docker volumes for:
|
||||||
|
- Server data persistence
|
||||||
|
- Configuration files
|
||||||
|
- Mods/plugins
|
||||||
|
- Backups
|
||||||
|
|
||||||
|
3. **Network Management**: Need to handle:
|
||||||
|
- Port allocation and mapping
|
||||||
|
- Internal Docker networking for proxy/server communication
|
||||||
|
- External port exposure
|
||||||
|
|
||||||
|
4. **Lifecycle Management**: Complete container lifecycle:
|
||||||
|
- Create (with proper configuration)
|
||||||
|
- Start/Stop/Restart
|
||||||
|
- Update/Upgrade
|
||||||
|
- Delete (with data handling)
|
||||||
|
|
||||||
|
5. **Integration Points**:
|
||||||
|
- Docker API for container management
|
||||||
|
- RCON for server console access
|
||||||
|
- Health checks for monitoring
|
||||||
|
- REST APIs (mc-router) for advanced routing
|
||||||
|
|
||||||
|
6. **Mod/Plugin Management Complexity**: The itzg container provides extensive mod/plugin management options:
|
||||||
|
- Multiple modpack platforms (Modrinth, CurseForge, FTB, Packwiz)
|
||||||
|
- Multiple installation methods (MODPACK, GENERIC_PACK, MODS/PLUGINS vars, file lists)
|
||||||
|
- Volume mounts for mods/plugins/configs
|
||||||
|
- Auto-removal of mods/plugins
|
||||||
|
- Environment variable substitution in configs
|
||||||
|
- Stronghold UI should simplify these options while exposing flexibility when needed
|
||||||
|
- Consider wizard-based flows for common scenarios (modpack installation) vs advanced configuration for power users
|
||||||
|
|
||||||
210
references/launchers.md
Normal file
210
references/launchers.md
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
# Minecraft Launcher Reference
|
||||||
|
|
||||||
|
These are client-side Minecraft launchers that have excellent modpack installation workflows. Stronghold should aim to make server modpack installation as easy as these launchers make client installation.
|
||||||
|
|
||||||
|
## ATLauncher
|
||||||
|
**GitHub**: https://github.com/ATLauncher/ATLauncher
|
||||||
|
**Website**: https://atlauncher.com
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Minecraft launcher focused on modpack installation and management. Known for making modpack installation simple and user-friendly.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Modpack Browser**: Browse and search modpacks from various sources
|
||||||
|
- **One-Click Installation**: Install modpacks with minimal configuration
|
||||||
|
- **Instance Management**: Create multiple Minecraft instances with different modpacks
|
||||||
|
- **Modpack Updates**: Easy updating of installed modpacks
|
||||||
|
- **Server Pack Generation**: Can generate server packs from modpacks
|
||||||
|
- **Integration with Modpack Platforms**:
|
||||||
|
- CurseForge
|
||||||
|
- Modrinth
|
||||||
|
- FTB (Feed The Beast)
|
||||||
|
- **Version Management**: Manage Minecraft and mod versions
|
||||||
|
- **Mod Management**: View, enable, disable individual mods
|
||||||
|
- **Configuration**: Edit modpack configurations easily
|
||||||
|
|
||||||
|
### User Experience Highlights
|
||||||
|
- Simple, clean interface
|
||||||
|
- Clear categorization of modpacks
|
||||||
|
- Detailed modpack information before installation
|
||||||
|
- Progress indicators during installation
|
||||||
|
- Pre-configured settings that "just work"
|
||||||
|
- Easy troubleshooting
|
||||||
|
|
||||||
|
### Lessons for Stronghold
|
||||||
|
**This is a KEY reference** - the goal is to make modded server creation as simple as ATLauncher makes modpack installation.
|
||||||
|
|
||||||
|
Stronghold should:
|
||||||
|
1. **Modpack Browser**: Provide UI to browse/search modpacks
|
||||||
|
2. **One-Click Server Creation**: Create modded server from modpack in one click
|
||||||
|
3. **Server Pack Handling**: Automatically detect and install server packs
|
||||||
|
4. **Configuration Simplification**: Hide complexity, provide good defaults
|
||||||
|
5. **Mod Management**: Allow viewing/managing mods in installed servers
|
||||||
|
6. **Update Management**: Easy way to update modpacks
|
||||||
|
|
||||||
|
**Key Insight**: The magic of ATLauncher is that it handles all the complexity (downloading, dependency resolution, configuration, version matching) behind the scenes. Stronghold needs to do the same for servers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prism Launcher
|
||||||
|
**GitHub**: https://github.com/PrismLauncher/PrismLauncher
|
||||||
|
**Website**: https://prismlauncher.org
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Fork of MultiMC, focused on modpack management. Open source and community-driven.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Multi-Platform**: Windows, macOS, Linux
|
||||||
|
- **Modpack Support**:
|
||||||
|
- Modrinth integration
|
||||||
|
- CurseForge integration
|
||||||
|
- FTB integration
|
||||||
|
- Technic Platform support
|
||||||
|
- **Instance Management**: Multiple instances with isolated configurations
|
||||||
|
- **Mod Management**: Individual mod installation, removal, updates
|
||||||
|
- **Version Control**: Git integration for instance management
|
||||||
|
- **Metadata Management**: Rich metadata for modpacks and mods
|
||||||
|
- **Customization**: Highly customizable interface and workflows
|
||||||
|
|
||||||
|
### Technology Stack
|
||||||
|
- C++/Qt for desktop application
|
||||||
|
- Various APIs for modpack platforms
|
||||||
|
|
||||||
|
### Lessons for Stronghold
|
||||||
|
- **Modrinth/CurseForge APIs**: Need to integrate with these platforms
|
||||||
|
- **Metadata Handling**: Rich metadata helps users make informed choices
|
||||||
|
- **Version Management**: Proper version handling is critical
|
||||||
|
- **Instance Isolation**: Each server should be isolated (Docker helps here)
|
||||||
|
|
||||||
|
### API Integration Notes
|
||||||
|
Both ATLauncher and Prism Launcher integrate with:
|
||||||
|
- **Modrinth API**: Modern modpack/mod platform
|
||||||
|
- **CurseForge API**: Legacy but still popular platform
|
||||||
|
- **FTB API**: Feed The Beast modpacks
|
||||||
|
|
||||||
|
Stronghold will need to:
|
||||||
|
1. Integrate with these APIs for modpack discovery
|
||||||
|
2. Handle server pack downloads from these platforms
|
||||||
|
3. Parse modpack metadata (version, dependencies, server pack info)
|
||||||
|
4. Manage modpack installation in Docker containers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Takeaways for Stronghold
|
||||||
|
|
||||||
|
### The ATLauncher Experience, But for Servers
|
||||||
|
|
||||||
|
When a user wants to create a modded server, the flow should be:
|
||||||
|
|
||||||
|
1. **Browse Modpacks**: Search/browse available modpacks (like ATLauncher)
|
||||||
|
2. **Select Modpack**: Click on a modpack to see details
|
||||||
|
3. **Create Server**: One click to create server from modpack
|
||||||
|
4. **Automatic Setup**: Stronghold handles:
|
||||||
|
- Downloading server pack
|
||||||
|
- Setting up Docker container with correct server type (FORGE/FABRIC)
|
||||||
|
- Installing mods
|
||||||
|
- Configuring environment variables
|
||||||
|
- Setting up volumes
|
||||||
|
- Starting server
|
||||||
|
5. **Ready to Play**: Server is ready, user can customize further if needed
|
||||||
|
|
||||||
|
### Technical Requirements
|
||||||
|
|
||||||
|
1. **Modpack Platform Integration**:
|
||||||
|
- Modrinth API client
|
||||||
|
- CurseForge API client
|
||||||
|
- FTB API (if needed)
|
||||||
|
|
||||||
|
2. **Modpack Processing**:
|
||||||
|
- Parse modpack manifest files
|
||||||
|
- Download modpack files
|
||||||
|
- Extract server packs
|
||||||
|
- Handle mod dependencies
|
||||||
|
- Version resolution
|
||||||
|
|
||||||
|
3. **Docker Integration**:
|
||||||
|
- Detect server type (FORGE/FABRIC) from modpack
|
||||||
|
- Set appropriate `TYPE` environment variable
|
||||||
|
- Configure modpack installation
|
||||||
|
- Set up proper Java versions
|
||||||
|
- Allocate appropriate memory
|
||||||
|
|
||||||
|
4. **UI Components**:
|
||||||
|
- Modpack browser/search
|
||||||
|
- Modpack detail view
|
||||||
|
- Installation progress
|
||||||
|
- Mod management UI
|
||||||
|
- Server configuration tied to modpack
|
||||||
|
|
||||||
|
### Simplification Goals
|
||||||
|
|
||||||
|
**From User Perspective:**
|
||||||
|
- Before: User needs to know about modpack formats, server packs, FORGE vs FABRIC, Java versions, memory allocation, Docker, etc.
|
||||||
|
- After: User finds modpack, clicks "Create Server", server is ready
|
||||||
|
|
||||||
|
**Implementation Complexity:**
|
||||||
|
- Handle all the technical details internally
|
||||||
|
- Provide smart defaults
|
||||||
|
- Allow customization but don't require it
|
||||||
|
- Good error messages if something goes wrong
|
||||||
|
|
||||||
|
### Implementation Phases
|
||||||
|
|
||||||
|
**Phase 1: Basic Modpack Support**
|
||||||
|
- Manual modpack URL/ID input
|
||||||
|
- Support for Modrinth server packs
|
||||||
|
- Basic installation
|
||||||
|
|
||||||
|
**Phase 2: Modpack Browser**
|
||||||
|
- Integrate Modrinth API
|
||||||
|
- Browse/search modpacks
|
||||||
|
- Server pack detection and installation
|
||||||
|
|
||||||
|
**Phase 3: Enhanced Features**
|
||||||
|
- CurseForge integration
|
||||||
|
- Modpack updates
|
||||||
|
- Mod management UI
|
||||||
|
- Advanced configuration options
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related Tools
|
||||||
|
|
||||||
|
### Server Pack Generators
|
||||||
|
Some modpacks come with server pack generators. Stronghold should:
|
||||||
|
- Detect if server pack is available
|
||||||
|
- Download server pack automatically
|
||||||
|
- Use itzg container's built-in modpack support when possible
|
||||||
|
- Handle cases where manual setup is needed
|
||||||
|
|
||||||
|
### itzg Container Modpack Support
|
||||||
|
The itzg/docker-minecraft-server container has extensive built-in modpack support ([Documentation](https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/)):
|
||||||
|
|
||||||
|
**Modpack Platforms**:
|
||||||
|
- Modrinth (auto-download)
|
||||||
|
- CurseForge (auto-download)
|
||||||
|
- Feed the Beast (FTB)
|
||||||
|
- Packwiz format
|
||||||
|
|
||||||
|
**Installation Methods**:
|
||||||
|
- `MODPACK` environment variable: Simple zip file or modpack ID
|
||||||
|
- `GENERIC_PACK` / `GENERIC_PACKS`: Install complete server content from archives
|
||||||
|
- Individual mods via `MODS` / `PLUGINS` environment variables
|
||||||
|
- File-based lists via `MODS_FILE` / `PLUGINS_FILE`
|
||||||
|
|
||||||
|
**Advanced Features**:
|
||||||
|
- Volume mounts for mods/plugins/configs (`/mods`, `/plugins`, `/config`)
|
||||||
|
- Auto-removal of mods/plugins
|
||||||
|
- Environment variable substitution in configs
|
||||||
|
- Multiple source directories
|
||||||
|
- Disable specific mods
|
||||||
|
|
||||||
|
**Key Insight**: The container handles much of the complexity internally. Stronghold's role is to:
|
||||||
|
1. Provide UI for modpack selection/browsing
|
||||||
|
2. Detect modpack platform and server type automatically
|
||||||
|
3. Generate appropriate environment variables
|
||||||
|
4. Handle the container creation with proper configuration
|
||||||
|
5. Manage modpack updates and mod additions/removals
|
||||||
|
|
||||||
|
Stronghold should leverage this built-in support heavily to simplify implementation - we don't need to reimplement modpack processing, just orchestrate it via the container's environment variables.
|
||||||
|
|
||||||
207
references/server-managers.md
Normal file
207
references/server-managers.md
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
# Server Management Web UIs Reference
|
||||||
|
|
||||||
|
These are existing web-based Minecraft server management tools that can provide inspiration for Stronghold's UI/UX and feature set.
|
||||||
|
|
||||||
|
## Crafty (CraftyControl)
|
||||||
|
**Website**: https://craftycontrol.com
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Web-based Minecraft server management tool. Stronghold takes inspiration from Crafty's approach to server management.
|
||||||
|
|
||||||
|
### Notable Features (based on common patterns)
|
||||||
|
- Web-based interface for managing servers
|
||||||
|
- Docker-based server hosting
|
||||||
|
- Server creation and configuration UI
|
||||||
|
- Console access
|
||||||
|
- File browser/editor
|
||||||
|
- Backup management
|
||||||
|
- Plugin/mod management
|
||||||
|
- User management (multi-user support)
|
||||||
|
- Server templates/presets
|
||||||
|
|
||||||
|
### Design Philosophy
|
||||||
|
- User-friendly interface
|
||||||
|
- Simplified server management
|
||||||
|
- Docker containerization
|
||||||
|
- Self-hosted solution
|
||||||
|
|
||||||
|
### Lessons for Stronghold
|
||||||
|
- Keep the UI clean and intuitive (KISS principle)
|
||||||
|
- Make common tasks easy to accomplish
|
||||||
|
- Provide good defaults with customization options
|
||||||
|
- Focus on Docker-based deployment
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pterodactyl Panel
|
||||||
|
**GitHub**: https://github.com/pterodactyl/panel
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Open-source game server management panel. Supports multiple game types including Minecraft. Uses a client-server architecture with Wings (daemon) and Panel (web UI).
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
- **Panel**: Web UI (Laravel/PHP)
|
||||||
|
- **Wings**: Daemon running on game servers (Go)
|
||||||
|
- **API**: RESTful API between Panel and Wings
|
||||||
|
- **Database**: MySQL/PostgreSQL
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- Multi-server management
|
||||||
|
- User management and permissions
|
||||||
|
- Resource allocation (CPU, memory, disk)
|
||||||
|
- File manager with code editor
|
||||||
|
- Console access
|
||||||
|
- Backup system
|
||||||
|
- Server templates
|
||||||
|
- Plugin/mod installation
|
||||||
|
- Resource monitoring
|
||||||
|
- SFTP access
|
||||||
|
- Multi-node support (Wings daemons on multiple hosts)
|
||||||
|
|
||||||
|
### Technology Stack
|
||||||
|
- Backend: Laravel (PHP)
|
||||||
|
- Daemon: Go
|
||||||
|
- Frontend: React/Vue components
|
||||||
|
- Database: MySQL/PostgreSQL
|
||||||
|
|
||||||
|
### Lessons for Stronghold
|
||||||
|
- **Pros**:
|
||||||
|
- Comprehensive feature set
|
||||||
|
- Multi-user with permissions
|
||||||
|
- Good separation of concerns (Panel/Daemon)
|
||||||
|
- Resource management
|
||||||
|
- **Cons/Considerations**:
|
||||||
|
- Complex architecture (may violate KISS)
|
||||||
|
- Requires database
|
||||||
|
- More overhead than needed for single-host Docker setup
|
||||||
|
- Can be over-engineered for simple use cases
|
||||||
|
|
||||||
|
**Takeaway**: Consider simplified version - Pterodactyl is powerful but complex. Stronghold can be simpler since it's focused on Docker containers rather than multi-node deployments.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fork (ForkGG)
|
||||||
|
**GitHub**: https://github.com/ForkGG/Fork
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Windows-based Minecraft server manager (not web-based). Built with .NET.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- Simple, intuitive UI
|
||||||
|
- Server creation wizard
|
||||||
|
- Console management
|
||||||
|
- File browser
|
||||||
|
- Plugin/mod installation
|
||||||
|
- Server templates
|
||||||
|
- Backup management
|
||||||
|
- Resource monitoring
|
||||||
|
|
||||||
|
### Design Philosophy
|
||||||
|
- Windows-focused
|
||||||
|
- User-friendly
|
||||||
|
- Simplified workflows
|
||||||
|
|
||||||
|
### Lessons for Stronghold
|
||||||
|
- Good UX patterns for server management
|
||||||
|
- Simple workflows for common tasks
|
||||||
|
- Focus on ease of use
|
||||||
|
- Wizard-based server creation (good for beginners)
|
||||||
|
|
||||||
|
**Note**: Being Windows-focused limits direct applicability, but UX patterns are valuable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCSManager
|
||||||
|
**GitHub**: https://github.com/MCSManager/MCSManager
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Open-source Minecraft server management panel. Built with Node.js.
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
- **Web UI**: React-based frontend
|
||||||
|
- **Backend**: Node.js/Express API
|
||||||
|
- **Agent**: Daemon running on servers (also Node.js)
|
||||||
|
- **Database**: MongoDB (or can use file-based)
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- Web-based interface
|
||||||
|
- Multi-server management
|
||||||
|
- Console access
|
||||||
|
- File manager
|
||||||
|
- Task scheduling
|
||||||
|
- Backup system
|
||||||
|
- User management
|
||||||
|
- Server templates
|
||||||
|
- Remote server management (via agents)
|
||||||
|
- REST API
|
||||||
|
|
||||||
|
### Technology Stack
|
||||||
|
- Backend: Node.js/Express
|
||||||
|
- Frontend: React
|
||||||
|
- Database: MongoDB (optional, can use files)
|
||||||
|
- Agents: Node.js daemons
|
||||||
|
|
||||||
|
### Lessons for Stronghold
|
||||||
|
- **Pros**:
|
||||||
|
- Node.js stack (good for web-based tools)
|
||||||
|
- Can work without database (file-based)
|
||||||
|
- REST API architecture
|
||||||
|
- Good separation (UI/API/Agent)
|
||||||
|
- **Considerations**:
|
||||||
|
- Agent-based architecture (may not be needed for Docker-only)
|
||||||
|
- Can be complex
|
||||||
|
- MongoDB dependency (unless using file mode)
|
||||||
|
|
||||||
|
**Takeaway**: Node.js stack could work well for Stronghold. The agent model might be overkill if we're just managing Docker containers directly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## setupmc.com
|
||||||
|
**Website**: https://setupmc.com/java-server/
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
Web UI for generating docker-compose.yml files for itzg docker-minecraft-server. Not open source but provides a good example of a configuration generator.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- Interactive web form for server configuration
|
||||||
|
- Generates docker-compose.yml files
|
||||||
|
- Handles various server types
|
||||||
|
- Configuration presets
|
||||||
|
- Downloadable configuration files
|
||||||
|
|
||||||
|
### Lessons for Stronghold
|
||||||
|
- **Inspiration**: This is essentially a subset of what Stronghold should do
|
||||||
|
- Shows how to make Docker configuration user-friendly
|
||||||
|
- Good example of configuration UI patterns
|
||||||
|
- Proves the value of simplifying Docker setup
|
||||||
|
|
||||||
|
**Takeaway**: Stronghold should go beyond just generating configs - should also manage the actual containers, but this is a good reference for the configuration UI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comparison Summary
|
||||||
|
|
||||||
|
| Feature | Crafty | Pterodactyl | Fork | MCSManager | setupmc.com |
|
||||||
|
|---------|--------|-------------|------|------------|-------------|
|
||||||
|
| Web UI | ✅ | ✅ | ❌ | ✅ | ✅ |
|
||||||
|
| Docker-based | ✅ | ❌ (Wings) | ❌ | ❌ (Agents) | ✅ |
|
||||||
|
| Multi-user | ✅ | ✅ | ❌ | ✅ | ❌ |
|
||||||
|
| Complexity | Medium | High | Low | Medium | Low |
|
||||||
|
| Database | Yes | Yes | No | Optional | No |
|
||||||
|
| Architecture | Simple | Panel/Daemon | Desktop | UI/API/Agent | Static site |
|
||||||
|
|
||||||
|
### Recommendations for Stronghold
|
||||||
|
|
||||||
|
1. **KISS Principle**: Keep architecture simple - Docker API directly, no agents/daemons needed
|
||||||
|
2. **Database**: Start without database (file-based config), add later if needed
|
||||||
|
3. **Stack**: Node.js/Express + React could be good (like MCSManager but simpler)
|
||||||
|
4. **Features**: Focus on essential features first:
|
||||||
|
- Server CRUD operations
|
||||||
|
- Configuration UI
|
||||||
|
- Console access
|
||||||
|
- File browser
|
||||||
|
- Basic backups
|
||||||
|
5. **Multi-user**: Can add later, not essential for MVP
|
||||||
|
6. **UI Patterns**: Study Crafty and Fork for UX inspiration
|
||||||
|
7. **Configuration**: Use setupmc.com as reference for config UI patterns
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue