Spaces:
Paused
Paused
Accept query/form/JSON params for API POSTs; panel sends form-encoded (HF gateway strips JSON quotes)
Browse files- 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 = "
|
| 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 |
-
|
| 371 |
-
|
| 372 |
if u.path == "/api/login":
|
| 373 |
-
if CONTROL_PASSWORD and
|
| 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(
|
| 386 |
-
if
|
| 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 =
|
| 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/
|
| 513 |
-
body:
|
| 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,
|
|
|
|
| 567 |
return fetch(url, {
|
| 568 |
method: 'POST',
|
| 569 |
-
headers: {'Content-Type': 'application/
|
| 570 |
-
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 |
|