added support for multiple routes multiple ports
This commit is contained in:
parent
7399cf83f9
commit
7fd2531501
4 changed files with 445 additions and 151 deletions
112
panel-api.py
112
panel-api.py
|
|
@ -214,11 +214,23 @@ class Handler(BaseHTTPRequestHandler):
|
|||
fields = line.split()
|
||||
if len(fields) < 4:
|
||||
continue
|
||||
# New format: name domain|upstream routes=N auth=bool [repo_url]
|
||||
first_route = fields[1]
|
||||
route_parts = first_route.split("|")
|
||||
domain = route_parts[0].split(",")[0] if route_parts else first_route
|
||||
upstream = route_parts[1] if len(route_parts) > 1 else ""
|
||||
route_count_str = fields[2].replace("routes=", "")
|
||||
# Backward compat: fields[2] may be upstream if old format
|
||||
if not route_count_str.isdigit():
|
||||
upstream = fields[2]
|
||||
route_count_str = "1"
|
||||
apps.append({
|
||||
"name": fields[0],
|
||||
"domain": fields[1].split(",")[0],
|
||||
"domains": fields[1],
|
||||
"upstream": fields[2],
|
||||
"domain": domain,
|
||||
"domains": domain,
|
||||
"upstream": upstream,
|
||||
"first_route": first_route,
|
||||
"route_count": route_count_str,
|
||||
"auth": fields[3].replace("auth=", ""),
|
||||
"repo_url": fields[4] if len(fields) >= 5 else "",
|
||||
})
|
||||
|
|
@ -312,6 +324,41 @@ class Handler(BaseHTTPRequestHandler):
|
|||
self.wfile.write(chunk)
|
||||
return
|
||||
|
||||
# /apps/<name>/routes — get parsed routes
|
||||
if len(parts) == 3 and parts[0] == "apps" and parts[2] == "routes":
|
||||
name = parts[1]
|
||||
if not is_safe_name(name):
|
||||
self._json(400, {"ok": False, "error": "invalid app name"})
|
||||
return
|
||||
result = run_panelctl(["show", name])
|
||||
if not result["ok"]:
|
||||
self._json(404, result)
|
||||
return
|
||||
env = parse_env_blob(result["stdout"])
|
||||
routes_raw = env.get("APP_ROUTES", "")
|
||||
# Backward compat: build from old APP_DOMAIN/APP_PORT/APP_UPSTREAM
|
||||
if not routes_raw and "APP_DOMAIN" in env:
|
||||
upstream = env.get("APP_UPSTREAM", f"127.0.0.1:{env.get('APP_PORT', '18080')}")
|
||||
domains_str = env.get("APP_DOMAINS", env["APP_DOMAIN"])
|
||||
routes_parts = []
|
||||
for d in domains_str.split(","):
|
||||
d = d.strip()
|
||||
if d:
|
||||
routes_parts.append(f"{d}|{upstream}")
|
||||
routes_raw = ",".join(routes_parts)
|
||||
routes = []
|
||||
for entry in routes_raw.split(","):
|
||||
entry = entry.strip()
|
||||
if not entry:
|
||||
continue
|
||||
if "|" in entry:
|
||||
domain, upstream = entry.split("|", 1)
|
||||
routes.append({"domain": domain.strip(), "upstream": upstream.strip()})
|
||||
else:
|
||||
routes.append({"domain": entry.strip(), "upstream": ""})
|
||||
self._json(200, {"ok": True, "name": name, "routes": routes})
|
||||
return
|
||||
|
||||
# /apps/<name> — show single app
|
||||
if len(parts) == 2 and parts[0] == "apps":
|
||||
name = parts[1]
|
||||
|
|
@ -533,14 +580,32 @@ class Handler(BaseHTTPRequestHandler):
|
|||
try:
|
||||
payload = self._read_json()
|
||||
name = payload["name"]
|
||||
# Support both "domain" (string, possibly comma-separated) and "domains" (array)
|
||||
if "domains" in payload and isinstance(payload["domains"], list):
|
||||
domain = ",".join(payload["domains"])
|
||||
else:
|
||||
domain = str(payload.get("domain", ""))
|
||||
port = str(payload["port"])
|
||||
auth = str(payload.get("auth", True)).lower()
|
||||
source_type = payload.get("source_type", "default")
|
||||
|
||||
# Build routes string: "domain|upstream,domain|upstream,..."
|
||||
routes_parts = []
|
||||
if "routes" in payload and isinstance(payload["routes"], list):
|
||||
for r in payload["routes"]:
|
||||
d = r.get("domain", "").strip()
|
||||
u = r.get("upstream", "").strip()
|
||||
if d and u:
|
||||
routes_parts.append(f"{d}|{u}")
|
||||
elif "domain" in payload and "port" in payload:
|
||||
# Backward compat: single domain + port
|
||||
domain_str = payload.get("domain", "")
|
||||
if "domains" in payload and isinstance(payload["domains"], list):
|
||||
domain_str = ",".join(payload["domains"])
|
||||
port = str(payload["port"])
|
||||
for d in domain_str.split(","):
|
||||
d = d.strip()
|
||||
if d:
|
||||
routes_parts.append(f"{d}|127.0.0.1:{port}")
|
||||
else:
|
||||
self._json(400, {"ok": False, "error": "missing 'routes' array or 'domain'+'port' fields"})
|
||||
return
|
||||
|
||||
routes_str = ",".join(routes_parts)
|
||||
except Exception as exc:
|
||||
self._json(400, {"ok": False, "error": f"invalid payload: {exc}"})
|
||||
return
|
||||
|
|
@ -550,7 +615,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||
return
|
||||
|
||||
try:
|
||||
result = run_panelctl(["init", name, domain, port, auth])
|
||||
result = run_panelctl(["init", name, routes_str, auth])
|
||||
if not result["ok"]:
|
||||
self._json(400, result)
|
||||
return
|
||||
|
|
@ -730,6 +795,33 @@ class Handler(BaseHTTPRequestHandler):
|
|||
self._json(200 if result["ok"] else 400, result)
|
||||
return
|
||||
|
||||
# POST /apps/<name>/routes — hot update routes
|
||||
if action == "routes":
|
||||
if not is_safe_name(name):
|
||||
self._json(400, {"ok": False, "error": "invalid app name"})
|
||||
return
|
||||
try:
|
||||
payload = self._read_json()
|
||||
except Exception as exc:
|
||||
self._json(400, {"ok": False, "error": f"invalid payload: {exc}"})
|
||||
return
|
||||
route_list = payload.get("routes", [])
|
||||
if not isinstance(route_list, list) or not route_list:
|
||||
self._json(400, {"ok": False, "error": "routes must be a non-empty array"})
|
||||
return
|
||||
routes_parts = []
|
||||
for r in route_list:
|
||||
d = r.get("domain", "").strip()
|
||||
u = r.get("upstream", "").strip()
|
||||
if not d or not u:
|
||||
self._json(400, {"ok": False, "error": "each route needs 'domain' and 'upstream'"})
|
||||
return
|
||||
routes_parts.append(f"{d}|{u}")
|
||||
routes_str = ",".join(routes_parts)
|
||||
result = run_panelctl(["set-routes", name, routes_str])
|
||||
self._json(200 if result["ok"] else 400, result)
|
||||
return
|
||||
|
||||
# Simple panelctl pass-through actions
|
||||
if action in {"deploy", "stop", "restart", "render-route", "volume-clear"}:
|
||||
if not is_safe_name(name):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue