srinetwork commited on
Commit
2e5d75b
Β·
verified Β·
1 Parent(s): e0f3430

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +354 -58
Dockerfile CHANGED
@@ -3,13 +3,17 @@ FROM openjdk:21-jdk-slim
3
  # Install required packages
4
  RUN apt-get update && apt-get install -y \
5
  python3 \
 
6
  curl \
7
  git \
8
  && rm -rf /var/lib/apt/lists/*
9
 
 
 
 
10
  WORKDIR /app
11
 
12
- # Create startup script with health check server (REQUIRED for HF Spaces)
13
  RUN cat > start.sh << 'EOF'
14
  #!/bin/bash
15
  set -e
@@ -17,14 +21,14 @@ set -e
17
  cd /app
18
 
19
  echo "=========================================="
20
- echo "πŸš€ Starting Minecraft Server on HF Spaces"
21
  echo "⏰ $(date)"
22
  echo "=========================================="
23
 
24
  # Clone private repo (requires GITHUB_TOKEN)
25
  if [ -n "$GITHUB_TOKEN" ]; then
26
  echo "πŸ“₯ Cloning Minecraft server from GitHub..."
27
- git clone https://$GITHUB_TOKEN@github.com/Walukapah/mcserver.git mcserver
28
  cd mcserver
29
  echo "βœ… Repository cloned successfully"
30
  else
@@ -39,6 +43,16 @@ if [ ! -f eula.txt ]; then
39
  echo "βœ… EULA accepted"
40
  fi
41
 
 
 
 
 
 
 
 
 
 
 
42
  # Create backup script
43
  cat > /app/backup.sh << 'BACKUPSCRIPT'
44
  #!/bin/bash
@@ -60,7 +74,7 @@ BACKUPSCRIPT
60
 
61
  chmod +x /app/backup.sh
62
 
63
- # Start backup loop in background (every 5 minutes to avoid rate limits)
64
  (
65
  while true; do
66
  sleep 300
@@ -75,81 +89,363 @@ echo "βœ… Auto-backup enabled (every 5 minutes)"
75
  echo ""
76
 
77
  # ==========================================
78
- # HF Spaces Health Check Server (REQUIRED)
79
- # This keeps the Space in "Running" state
80
  # ==========================================
81
- cat > /app/health_server.py << 'PYTHON_SCRIPT'
82
- import http.server
83
- import socketserver
84
- import subprocess
85
  import os
 
 
86
  import json
87
  import threading
88
  import time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
- class Handler(http.server.BaseHTTPRequestHandler):
91
- def do_GET(self):
92
- self.send_response(200)
93
- self.send_header('Content-type', 'application/json')
94
- self.send_header('Access-Control-Allow-Origin', '*')
95
- self.end_headers()
96
 
97
- # Check if Minecraft is running
 
 
 
 
98
  try:
99
- # Check if Java process is running
100
- result = subprocess.run(['pgrep', '-f', 'server.jar'], capture_output=True)
101
- mc_running = result.returncode == 0
102
  except:
103
- mc_running = False
104
-
105
- # Get server stats
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  try:
107
- import psutil
108
- cpu = psutil.cpu_percent(interval=0.1)
109
- memory = psutil.virtual_memory().percent
 
110
  except:
111
- cpu = 0
112
- memory = 0
113
-
114
- status = {
115
- 'status': 'running',
116
- 'minecraft': 'online' if mc_running else 'offline',
117
- 'cpu_percent': cpu,
118
- 'memory_percent': memory,
119
- 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
120
- }
121
- self.wfile.write(json.dumps(status).encode())
122
 
123
- def log_message(self, format, *args):
124
- # Suppress health check logs to avoid spam
125
- pass
126
-
127
- # Start health check server on port 7860 (HF Spaces requirement)
128
- PORT = 7860
129
- with socketserver.TCPServer(('0.0.0.0', PORT), Handler) as httpd:
130
- print(f'πŸ₯ Health server running on port {PORT}')
131
- httpd.serve_forever()
 
132
  PYTHON_SCRIPT
133
 
134
- # Start health check server in background
135
- echo "πŸ₯ Starting health check server on port 7860..."
136
- python3 /app/health_server.py &
137
-
138
- # Wait a moment for health server to start
139
- sleep 2
140
 
141
  echo ""
142
- echo "⛏️ Starting Minecraft Server..."
143
- echo "πŸ’‘ Logs will appear below:"
144
  echo "=========================================="
145
 
146
- # Start Minecraft server in foreground
147
- exec java -Xms1G -Xmx14G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -jar server.jar nogui
 
 
 
 
 
 
 
148
  EOF
149
 
150
  RUN chmod +x start.sh
151
 
152
- # HF Spaces requires port 7860 for health checks
153
- EXPOSE 25565 25575 7860
154
 
155
  CMD ["./start.sh"]
 
3
  # Install required packages
4
  RUN apt-get update && apt-get install -y \
5
  python3 \
6
+ python3-pip \
7
  curl \
8
  git \
9
  && rm -rf /var/lib/apt/lists/*
10
 
11
+ # Install Python WebSocket and Flask
12
+ RUN pip3 install flask flask-sock psutil --break-system-packages
13
+
14
  WORKDIR /app
15
 
16
+ # Create startup script with web terminal
17
  RUN cat > start.sh << 'EOF'
18
  #!/bin/bash
19
  set -e
 
21
  cd /app
22
 
23
  echo "=========================================="
24
+ echo "πŸš€ Starting Minecraft Server with Web Terminal"
25
  echo "⏰ $(date)"
26
  echo "=========================================="
27
 
28
  # Clone private repo (requires GITHUB_TOKEN)
29
  if [ -n "$GITHUB_TOKEN" ]; then
30
  echo "πŸ“₯ Cloning Minecraft server from GitHub..."
31
+ git clone https://$GITHUB_TOKEN@github.com/Walukapah/bungemcserver.git mcserver
32
  cd mcserver
33
  echo "βœ… Repository cloned successfully"
34
  else
 
43
  echo "βœ… EULA accepted"
44
  fi
45
 
46
+ # Enable RCON for web terminal control
47
+ if ! grep -q "enable-rcon=true" server.properties 2>/dev/null; then
48
+ echo "" >> server.properties
49
+ echo "enable-rcon=true" >> server.properties
50
+ echo "rcon.port=25575" >> server.properties
51
+ echo "rcon.password=mcadmin123" >> server.properties
52
+ echo "broadcast-rcon-to-ops=true" >> server.properties
53
+ echo "βœ… RCON enabled for web terminal"
54
+ fi
55
+
56
  # Create backup script
57
  cat > /app/backup.sh << 'BACKUPSCRIPT'
58
  #!/bin/bash
 
74
 
75
  chmod +x /app/backup.sh
76
 
77
+ # Start backup loop in background (every 5 minutes)
78
  (
79
  while true; do
80
  sleep 300
 
89
  echo ""
90
 
91
  # ==========================================
92
+ # WEB TERMINAL SERVER (Flask + WebSocket)
 
93
  # ==========================================
94
+ cat > /app/web_terminal.py << 'PYTHON_SCRIPT'
 
 
 
95
  import os
96
+ import sys
97
+ import subprocess
98
  import json
99
  import threading
100
  import time
101
+ import queue
102
+ from flask import Flask, render_template_string
103
+ from flask_sock import Sock
104
+ import psutil
105
+
106
+ app = Flask(__name__)
107
+ sock = Sock(app)
108
+
109
+ # Global variables for process management
110
+ mc_process = None
111
+ output_queue = queue.Queue()
112
+ command_queue = queue.Queue()
113
+
114
+ def start_minecraft():
115
+ global mc_process
116
+ mc_dir = "/app/mcserver"
117
+
118
+ # Start Minecraft server with stdin/stdout pipes
119
+ mc_process = subprocess.Popen(
120
+ ["java", "-Xms1G", "-Xmx14G", "-XX:+UseG1GC",
121
+ "-XX:+ParallelRefProcEnabled", "-XX:MaxGCPauseMillis=200",
122
+ "-jar", "server.jar", "nogui"],
123
+ cwd=mc_dir,
124
+ stdin=subprocess.PIPE,
125
+ stdout=subprocess.PIPE,
126
+ stderr=subprocess.STDOUT,
127
+ text=True,
128
+ bufsize=1
129
+ )
130
+
131
+ # Read output and put in queue
132
+ def read_output():
133
+ for line in mc_process.stdout:
134
+ output_queue.put(line)
135
+
136
+ threading.Thread(target=read_output, daemon=True).start()
137
+
138
+ # Process commands from queue
139
+ def process_commands():
140
+ while True:
141
+ try:
142
+ cmd = command_queue.get(timeout=0.1)
143
+ if mc_process and mc_process.poll() is None:
144
+ mc_process.stdin.write(cmd + "\n")
145
+ mc_process.stdin.flush()
146
+ except queue.Empty:
147
+ continue
148
+ except:
149
+ break
150
+
151
+ threading.Thread(target=process_commands, daemon=True).start()
152
+
153
+ # HTML Template with xterm.js
154
+ HTML_TEMPLATE = '''
155
+ <!DOCTYPE html>
156
+ <html>
157
+ <head>
158
+ <title>Minecraft Server - Web Terminal</title>
159
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
160
+ <style>
161
+ body {
162
+ margin: 0;
163
+ padding: 0;
164
+ background: #1e1e1e;
165
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
166
+ }
167
+ .header {
168
+ background: #2d2d2d;
169
+ color: #fff;
170
+ padding: 15px 20px;
171
+ display: flex;
172
+ justify-content: space-between;
173
+ align-items: center;
174
+ border-bottom: 3px solid #4CAF50;
175
+ }
176
+ .header h1 {
177
+ margin: 0;
178
+ font-size: 20px;
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 10px;
182
+ }
183
+ .status {
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 15px;
187
+ }
188
+ .status-badge {
189
+ background: #4CAF50;
190
+ color: white;
191
+ padding: 5px 15px;
192
+ border-radius: 20px;
193
+ font-size: 12px;
194
+ font-weight: bold;
195
+ }
196
+ .status-badge.offline {
197
+ background: #f44336;
198
+ }
199
+ .terminal-container {
200
+ padding: 20px;
201
+ height: calc(100vh - 80px);
202
+ }
203
+ #terminal {
204
+ width: 100%;
205
+ height: 100%;
206
+ background: #000;
207
+ border-radius: 8px;
208
+ padding: 10px;
209
+ }
210
+ .quick-cmds {
211
+ position: fixed;
212
+ bottom: 20px;
213
+ right: 20px;
214
+ display: flex;
215
+ gap: 10px;
216
+ flex-wrap: wrap;
217
+ max-width: 400px;
218
+ justify-content: flex-end;
219
+ }
220
+ .quick-cmds button {
221
+ background: #4CAF50;
222
+ color: white;
223
+ border: none;
224
+ padding: 8px 16px;
225
+ border-radius: 5px;
226
+ cursor: pointer;
227
+ font-size: 12px;
228
+ transition: all 0.3s;
229
+ }
230
+ .quick-cmds button:hover {
231
+ background: #45a049;
232
+ transform: scale(1.05);
233
+ }
234
+ .quick-cmds button.danger {
235
+ background: #f44336;
236
+ }
237
+ .quick-cmds button.danger:hover {
238
+ background: #da190b;
239
+ }
240
+ </style>
241
+ </head>
242
+ <body>
243
+ <div class="header">
244
+ <h1>
245
+ <span>⛏️</span>
246
+ Minecraft Server Console
247
+ </h1>
248
+ <div class="status">
249
+ <span id="cpu">CPU: --%</span>
250
+ <span id="memory">RAM: --%</span>
251
+ <span class="status-badge" id="status">ONLINE</span>
252
+ </div>
253
+ </div>
254
+
255
+ <div class="terminal-container">
256
+ <div id="terminal"></div>
257
+ </div>
258
+
259
+ <div class="quick-cmds">
260
+ <button onclick="sendCmd('help')">Help</button>
261
+ <button onclick="sendCmd('list')">Players</button>
262
+ <button onclick="sendCmd('save-all')">Save World</button>
263
+ <button onclick="sendCmd('say Server restart in 30s!')">Broadcast</button>
264
+ <button class="danger" onclick="sendCmd('stop')">Stop Server</button>
265
+ </div>
266
+
267
+ <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
268
+ <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
269
+ <script>
270
+ const term = new Terminal({
271
+ fontSize: 14,
272
+ fontFamily: 'Consolas, "Courier New", monospace',
273
+ cursorBlink: true,
274
+ theme: {
275
+ background: '#000000',
276
+ foreground: '#00ff00',
277
+ cursor: '#00ff00',
278
+ selection: '#4CAF50'
279
+ },
280
+ scrollback: 10000,
281
+ cols: 120,
282
+ rows: 40
283
+ });
284
+
285
+ const fitAddon = new FitAddon.FitAddon();
286
+ term.loadAddon(fitAddon);
287
+
288
+ term.open(document.getElementById('terminal'));
289
+ fitAddon.fit();
290
+
291
+ // Connect to WebSocket
292
+ const ws = new WebSocket('ws://' + window.location.host + '/ws');
293
+ let inputBuffer = '';
294
+
295
+ ws.onopen = () => {
296
+ term.writeln('\\r\\n');
297
+ term.writeln('\\x1b[32m╔════════════════════════════════════════╗\\x1b[0m');
298
+ term.writeln('\\x1b[32mβ•‘ Minecraft Server Web Terminal β•‘\\x1b[0m');
299
+ term.writeln('\\x1b[32mβ•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\\x1b[0m');
300
+ term.writeln('');
301
+ term.writeln('\\x1b[33mConnected to server console...\\x1b[0m');
302
+ term.writeln('');
303
+ };
304
+
305
+ ws.onmessage = (event) => {
306
+ const data = JSON.parse(event.data);
307
+ if (data.type === 'output') {
308
+ term.write(data.data);
309
+ } else if (data.type === 'stats') {
310
+ document.getElementById('cpu').textContent = 'CPU: ' + data.cpu + '%';
311
+ document.getElementById('memory').textContent = 'RAM: ' + data.memory + '%';
312
+ if (!data.running) {
313
+ document.getElementById('status').textContent = 'OFFLINE';
314
+ document.getElementById('status').classList.add('offline');
315
+ }
316
+ }
317
+ };
318
+
319
+ ws.onclose = () => {
320
+ term.writeln('\\r\\n\\x1b[31m[Disconnected from server]\\x1b[0m');
321
+ };
322
+
323
+ // Handle input
324
+ term.onData((data) => {
325
+ const code = data.charCodeAt(0);
326
+
327
+ if (code === 13) { // Enter
328
+ ws.send(JSON.stringify({type: 'command', data: inputBuffer}));
329
+ inputBuffer = '';
330
+ term.write('\\r\\n');
331
+ } else if (code === 127) { // Backspace
332
+ if (inputBuffer.length > 0) {
333
+ inputBuffer = inputBuffer.slice(0, -1);
334
+ term.write('\\b \\b');
335
+ }
336
+ } else if (code >= 32 && code <= 126) { // Printable chars
337
+ inputBuffer += data;
338
+ term.write(data);
339
+ }
340
+ });
341
+
342
+ // Quick command sender
343
+ function sendCmd(cmd) {
344
+ ws.send(JSON.stringify({type: 'command', data: cmd}));
345
+ term.writeln('');
346
+ term.writeln('\\x1b[36m[Quick Command] ' + cmd + '\\x1b[0m');
347
+ }
348
+
349
+ // Resize handling
350
+ window.addEventListener('resize', () => {
351
+ fitAddon.fit();
352
+ });
353
+ </script>
354
+ </body>
355
+ </html>
356
+ '''
357
+
358
+ @app.route('/')
359
+ def index():
360
+ return render_template_string(HTML_TEMPLATE)
361
 
362
+ @app.route('/health')
363
+ def health():
364
+ return {'status': 'running', 'minecraft': mc_process is not None and mc_process.poll() is None}
 
 
 
365
 
366
+ @sock.route('/ws')
367
+ def websocket(ws):
368
+ # Send initial output history
369
+ buffer = []
370
+ while not output_queue.empty() and len(buffer) < 100:
371
  try:
372
+ buffer.append(output_queue.get_nowait())
 
 
373
  except:
374
+ break
375
+
376
+ for line in buffer:
377
+ ws.send(json.dumps({'type': 'output', 'data': line}))
378
+
379
+ def send_output():
380
+ while True:
381
+ try:
382
+ line = output_queue.get(timeout=0.1)
383
+ ws.send(json.dumps({'type': 'output', 'data': line}))
384
+ except queue.Empty:
385
+ # Send stats periodically
386
+ try:
387
+ cpu = psutil.cpu_percent(interval=0.1)
388
+ memory = psutil.virtual_memory().percent
389
+ running = mc_process is not None and mc_process.poll() is None
390
+ ws.send(json.dumps({
391
+ 'type': 'stats',
392
+ 'cpu': cpu,
393
+ 'memory': memory,
394
+ 'running': running
395
+ }))
396
+ except:
397
+ pass
398
+ time.sleep(1)
399
+ except:
400
+ break
401
+
402
+ output_thread = threading.Thread(target=send_output, daemon=True)
403
+ output_thread.start()
404
+
405
+ # Receive commands
406
+ while True:
407
  try:
408
+ data = ws.receive()
409
+ msg = json.loads(data)
410
+ if msg.get('type') == 'command':
411
+ command_queue.put(msg.get('data', ''))
412
  except:
413
+ break
 
 
 
 
 
 
 
 
 
 
414
 
415
+ if __name__ == '__main__':
416
+ # Start Minecraft in background thread
417
+ mc_thread = threading.Thread(target=start_minecraft, daemon=True)
418
+ mc_thread.start()
419
+
420
+ # Wait for Minecraft to start
421
+ time.sleep(5)
422
+
423
+ # Start Flask server
424
+ app.run(host='0.0.0.0', port=7860, threaded=True)
425
  PYTHON_SCRIPT
426
 
427
+ echo "🌐 Starting Web Terminal Server on port 7860..."
428
+ python3 /app/web_terminal.py &
 
 
 
 
429
 
430
  echo ""
431
+ echo "βœ… Web Terminal ready at: http://your-domain:7860"
432
+ echo "πŸ’‘ Minecraft logs will stream to the web terminal"
433
  echo "=========================================="
434
 
435
+ # Keep script running
436
+ while true; do
437
+ sleep 10
438
+ # Check if processes are running
439
+ if ! pgrep -f "web_terminal.py" > /dev/null; then
440
+ echo "❌ Web terminal crashed, restarting..."
441
+ python3 /app/web_terminal.py &
442
+ fi
443
+ done
444
  EOF
445
 
446
  RUN chmod +x start.sh
447
 
448
+ # HF Spaces required port
449
+ EXPOSE 7860 25565 25575
450
 
451
  CMD ["./start.sh"]