Ntk123 commited on
Commit
cab8bfd
·
1 Parent(s): 2510bce

Accept query/form/JSON params for API POSTs; panel sends form-encoded (HF gateway strips JSON quotes)

Browse files
Files changed (1) hide show
  1. control.py +31 -25
control.py CHANGED
@@ -13,7 +13,7 @@ from collections import deque
13
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
14
  from urllib.parse import parse_qs, urlparse
15
 
16
- BUILD = "diag-1"
17
 
18
  DATA_DIR = os.environ.get("MC_DATA", "/data")
19
  JAR = os.environ.get("MC_JAR", "/server/minecraft_server.26.3.jar")
@@ -367,10 +367,10 @@ class Handler(BaseHTTPRequestHandler):
367
 
368
  def do_POST(self):
369
  u = urlparse(self.path)
370
- body, raw = self._read_post()
371
- body = body or {}
372
  if u.path == "/api/login":
373
- if CONTROL_PASSWORD and body.get("password") == CONTROL_PASSWORD:
374
  self._json(200, {"ok": True, "token": token_for(CONTROL_PASSWORD)})
375
  elif CONTROL_PASSWORD:
376
  self._json(401, {"ok": False, "error": "wrong password"})
@@ -382,26 +382,13 @@ class Handler(BaseHTTPRequestHandler):
382
  if not auth_ok(self.headers.get("X-Token", "")):
383
  self._json(401, {"ok": False, "error": "auth required"})
384
  return
385
- ok, msg = controller.send_command(body.get("cmd", ""))
386
- if not ok and not body:
387
- self._json(400, {
388
- "ok": False,
389
- "error": msg,
390
- "diag": {
391
- "cl": self.headers.get("Content-Length"),
392
- "te": self.headers.get("Transfer-Encoding"),
393
- "ct": self.headers.get("Content-Type"),
394
- "proto": self.request_version,
395
- "raw": raw[:64].decode("utf-8", "replace"),
396
- },
397
- })
398
- else:
399
- self._json(200 if ok else 400, {"ok": ok, "error": msg})
400
  elif u.path == "/api/action":
401
  if not auth_ok(self.headers.get("X-Token", "")):
402
  self._json(401, {"ok": False, "error": "auth required"})
403
  return
404
- action = body.get("action")
405
  if action == "restart":
406
  self._json(200, {"ok": True, "error": controller.restart()})
407
  elif action == "stop":
@@ -411,6 +398,24 @@ class Handler(BaseHTTPRequestHandler):
411
  else:
412
  self._json(404, {"ok": False, "error": "not found"})
413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
 
415
  PAGE = r"""<!DOCTYPE html>
416
  <html lang="en">
@@ -509,8 +514,8 @@ $('logbox').addEventListener('scroll', () => {
509
  function doLogin() {
510
  fetch('/api/login', {
511
  method: 'POST',
512
- headers: {'Content-Type': 'application/json'},
513
- body: JSON.stringify({password: $('pw').value})
514
  }).then(r => {
515
  if (r.status === 401) { $('pw_err').textContent = 'wrong password'; throw 0; }
516
  return r.json();
@@ -563,11 +568,12 @@ function renderLine(txt) {
563
  return div;
564
  }
565
 
566
- function api(url, body) {
 
567
  return fetch(url, {
568
  method: 'POST',
569
- headers: {'Content-Type': 'application/json', 'X-Token': token},
570
- body: JSON.stringify(body || {})
571
  }).then(r => r.json().then(d => ({status: r.status, d})));
572
  }
573
 
 
13
  from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
14
  from urllib.parse import parse_qs, urlparse
15
 
16
+ BUILD = "v3"
17
 
18
  DATA_DIR = os.environ.get("MC_DATA", "/data")
19
  JAR = os.environ.get("MC_JAR", "/server/minecraft_server.26.3.jar")
 
367
 
368
  def do_POST(self):
369
  u = urlparse(self.path)
370
+ raw = self._read_body() or b""
371
+ params = self._params(u, raw)
372
  if u.path == "/api/login":
373
+ if CONTROL_PASSWORD and params.get("password") == CONTROL_PASSWORD:
374
  self._json(200, {"ok": True, "token": token_for(CONTROL_PASSWORD)})
375
  elif CONTROL_PASSWORD:
376
  self._json(401, {"ok": False, "error": "wrong password"})
 
382
  if not auth_ok(self.headers.get("X-Token", "")):
383
  self._json(401, {"ok": False, "error": "auth required"})
384
  return
385
+ ok, msg = controller.send_command(params.get("cmd", ""))
386
+ self._json(200 if ok else 400, {"ok": ok, "error": msg})
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  elif u.path == "/api/action":
388
  if not auth_ok(self.headers.get("X-Token", "")):
389
  self._json(401, {"ok": False, "error": "auth required"})
390
  return
391
+ action = params.get("action")
392
  if action == "restart":
393
  self._json(200, {"ok": True, "error": controller.restart()})
394
  elif action == "stop":
 
398
  else:
399
  self._json(404, {"ok": False, "error": "not found"})
400
 
401
+ def _params(self, u, raw):
402
+ params = {}
403
+ for key, vals in parse_qs(u.query).items():
404
+ params[key] = vals[-1]
405
+ ctype = (self.headers.get("Content-Type") or "").split(";")[0].strip().lower()
406
+ if ctype == "application/x-www-form-urlencoded" and raw:
407
+ for key, vals in parse_qs(raw.decode("utf-8", "replace")).items():
408
+ params[key] = vals[-1]
409
+ if raw:
410
+ try:
411
+ obj = json.loads(raw.decode("utf-8"))
412
+ if isinstance(obj, dict):
413
+ for key, val in obj.items():
414
+ params[str(key)] = val
415
+ except (ValueError, json.JSONDecodeError):
416
+ pass
417
+ return params
418
+
419
 
420
  PAGE = r"""<!DOCTYPE html>
421
  <html lang="en">
 
514
  function doLogin() {
515
  fetch('/api/login', {
516
  method: 'POST',
517
+ headers: {'Content-Type': 'application/x-www-form-urlencoded'},
518
+ body: 'password=' + encodeURIComponent($('pw').value)
519
  }).then(r => {
520
  if (r.status === 401) { $('pw_err').textContent = 'wrong password'; throw 0; }
521
  return r.json();
 
568
  return div;
569
  }
570
 
571
+ function api(url, data) {
572
+ const body = Object.keys(data || {}).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(data[k])).join('&');
573
  return fetch(url, {
574
  method: 'POST',
575
+ headers: {'Content-Type': 'application/x-www-form-urlencoded', 'X-Token': token},
576
+ body: body
577
  }).then(r => r.json().then(d => ({status: r.status, d})));
578
  }
579