#!/usr/bin/env python3 from http.server import BaseHTTPRequestHandler, HTTPServer import urllib.request, urllib.error, os PANEL_FILE="/opt/gostpanel/panel.html" SERVER_IP="127.0.0.1" try: import urllib.request as _u for _src in ("http://members.3322.org/dyndns/getip","http://ip.3322.net","https://api.ipify.org"): try: v=_u.urlopen(_src,timeout=5).read().decode().strip() if v: SERVER_IP=v; break except Exception: continue except Exception: pass if not SERVER_IP or SERVER_IP=="127.0.0.1": try: import socket as _s _sk=_s.socket(_s.AF_INET,_s.SOCK_DGRAM) _sk.connect(("8.8.8.8",80)) SERVER_IP=_sk.getsockname()[0]; _sk.close() except Exception: pass # 动态读取 gost API/SAVE 端口(启动时和每次代理时兜底) import re as _re def _read_api_ports(): api, save = "18080", "18081" try: with open("/etc/gost/gost.yaml") as f: txt = f.read() m = _re.search(r"^api:\s*\n\s*addr:\s*:?(\d+)", txt, _re.M) if m: api = m.group(1) # save端口: gost-save-server.py监听(SAVE_PORT写在文件里)或默认18081 m2 = _re.search(r"HTTPServer\(\(\"0\.0\.0\.0\",(\d+)\)", open("/usr/local/bin/gost-save-server.py").read()) if m2: save = m2.group(1) except Exception: pass return api, save GOST_API_PORT, GOST_SAVE_PORT = _read_api_ports() class H(BaseHTTPRequestHandler): def _apiconfig(self): import json as J, os CFG="/opt/gostpanel/apiconfig.json" # 认证: Basic 或 同源(GET可以同源不认证? 配置含密码 — 必须认证) # GET免认证(同源面板场景); POST仍需认证(写操作保护) if self.command=="POST": auth=self.headers.get("Authorization","") USER="admin"; PASS="oyh159159oiw.@" ok=False if auth.startswith("Basic "): import base64 try: u,pw=base64.b64decode(auth[6:]).decode().split(":",1) ok=(u==USER and pw==PASS) except Exception: ok=False if not ok: self.send_response(401); self.end_headers(); return if self.command=="GET": try: with open(CFG) as f: body=f.read().encode() except Exception: body=b"[]" self.send_response(200) self.send_header("Content-Type","application/json") self.send_header("Access-Control-Allow-Origin","*") self.send_header("Content-Length",str(len(body))) self.end_headers(); self.wfile.write(body); return if self.command=="POST": n=int(self.headers.get("Content-Length","0") or 0) data=self.rfile.read(n) try: arr=J.loads(data) assert isinstance(arr,list) os.makedirs(os.path.dirname(CFG),exist_ok=True) with open(CFG,"w") as f: f.write(J.dumps(arr,ensure_ascii=False,indent=1)) r=J.dumps({"ok":True}).encode() self.send_response(200) except Exception as e: r=J.dumps({"ok":False,"err":str(e)}).encode() self.send_response(400) self.send_header("Content-Type","application/json") self.send_header("Content-Length",str(len(r))) self.end_headers(); self.wfile.write(r); return self.send_response(405); self.end_headers(); return def _status(self): import subprocess, time, json as J, base64 auth=self.headers.get("Authorization","") USER="admin"; PASS="oyh159159oiw.@" ok=False if auth.startswith("Basic "): try: u,pw=base64.b64decode(auth[6:]).decode().split(":",1) ok=(u==USER and pw==PASS) except Exception: ok=False if not ok: self.send_response(401); self.end_headers(); return try: active=subprocess.run(["systemctl","is-active","gost"],capture_output=True,text=True).stdout.strip() ver=subprocess.run(["/usr/local/bin/gost","-V"],capture_output=True,text=True).stdout.strip() since=subprocess.run(["systemctl","show","gost","-p","ActiveEnterTimestamp","--value"],capture_output=True,text=True).stdout.strip() except Exception as e: active="unknown"; ver=""; since=str(e) ver=subprocess.run(["/usr/local/bin/gost","-V"],capture_output=True,text=True).stdout.strip() since=subprocess.run(["systemctl","show","gost","-p","ActiveEnterTimestamp","--value"],capture_output=True,text=True).stdout.strip() uptime="" try: out=subprocess.run(["systemctl","show","gost","-p","ActiveEnterTimestampMonotonic","--value"],capture_output=True,text=True).stdout.strip() except Exception: pass r=J.dumps({"active":active,"version":ver,"since":since}).encode() self.send_response(200) self.send_header("Content-Type","application/json") self.send_header("Access-Control-Allow-Origin","*") self.end_headers(); self.wfile.write(r) def _checkport(self): import socket, base64 auth=self.headers.get("Authorization","") USER="admin"; PASS="oyh159159oiw.@" ok=False if auth.startswith("Basic "): try: u,pw=base64.b64decode(auth[6:]).decode().split(":",1) ok=(u==USER and pw==PASS) except Exception: ok=False if not ok: self.send_response(401); self.end_headers(); return import json as J n=int(self.headers.get("Content-Length","0") or 0) try: data=J.loads(self.rfile.read(n)); port=int(data.get("port",0)) except Exception: port=0 occupied=False if port>0: s=socket.socket(); s.settimeout(1) try: s.bind(("0.0.0.0",port)); s.close() except Exception: occupied=True out=J.dumps({"port":port,"occupied":occupied}).encode() self.send_response(200) self.send_header("Content-Type","application/json") self.send_header("Access-Control-Allow-Origin","*") self.end_headers(); self.wfile.write(out) def _pingtest(self): import socket, base64, json as J, time auth=self.headers.get("Authorization","") USER="admin"; PASS="oyh159159oiw.@" ok=False if auth.startswith("Basic "): try: u,pw=base64.b64decode(auth[6:]).decode().split(":",1) ok=(u==USER and pw==PASS) except Exception: ok=False if not ok: self.send_response(401); self.end_headers(); return n=int(self.headers.get("Content-Length","0") or 0) target="" try: data=J.loads(self.rfile.read(n)); target=data.get("target","") except Exception: pass result={"target":target,"ok":False,"ms":0,"err":""} try: host,port=target.rsplit(":",1); port=int(port) s=socket.socket(); s.settimeout(3) t0=time.time(); s.connect((host,port)); result["ms"]=round((time.time()-t0)*1000) s.close(); result["ok"]=True except Exception as e: result["err"]=str(e)[:100] r=J.dumps(result).encode() self.send_response(200) self.send_header("Content-Type","application/json") self.send_header("Access-Control-Allow-Origin","*") self.end_headers(); self.wfile.write(r) def _logop(self): import base64, json as J, datetime auth=self.headers.get("Authorization","") USER="admin"; PASS="oyh159159oiw.@" ok=False if auth.startswith("Basic "): try: u,pw=base64.b64decode(auth[6:]).decode().split(":",1) ok=(u==USER and pw==PASS) except Exception: ok=False if not ok: self.send_response(401); self.end_headers(); return n=int(self.headers.get("Content-Length","0") or 0) try: data=J.loads(self.rfile.read(n)) line=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")+" "+data.get("action","")+" "+data.get("detail","")+"\n" with open("/opt/gostpanel/oplog.log","a") as f: f.write(line) except Exception: pass self.send_response(200); self.end_headers() def _getlog(self): import base64, json as J auth=self.headers.get("Authorization","") USER="admin"; PASS="oyh159159oiw.@" ok=False if auth.startswith("Basic "): try: u,pw=base64.b64decode(auth[6:]).decode().split(":",1) ok=(u==USER and pw==PASS) except Exception: ok=False if not ok: self.send_response(401); self.end_headers(); return try: with open("/opt/gostpanel/oplog.log") as f: lines=f.readlines()[-100:] r=J.dumps({"log":" vas ".join([]) or "".join(lines[-100:])}).encode() except FileNotFoundError: r=b'{"log":""}' except Exception as e: r=J.dumps({"log":"读取失败: "+str(e)}).encode() self.send_response(200) self.send_header("Content-Type","application/json") self.send_header("Access-Control-Allow-Origin","*") self.end_headers(); self.wfile.write(r) def _proxy(self): p=self.path if p=="/status": self._status(); return True if p=="/apiconfig": self._apiconfig(); return True if p=="/checkport": self._checkport(); return True if p=="/pingtest": self._pingtest(); return True if p=="/logop": self._logop(); return True if p=="/getlog": self._getlog(); return True if p.startswith("/gostapi"): t="http://127.0.0.1:"+GOST_API_PORT+p elif p.startswith("/save"): t="http://127.0.0.1:"+GOST_SAVE_PORT+p elif p.startswith("/yaml"): t="http://127.0.0.1:"+GOST_SAVE_PORT+p elif p.startswith("/rawyaml"): t="http://127.0.0.1:"+GOST_SAVE_PORT+p elif p=="/restart": t=None else: return False auth=self.headers.get("Authorization","") if t is None: import base64, subprocess ok=False if auth.startswith("Basic "): try: u,pw=base64.b64decode(auth[6:]).decode().split(":",1) ok=(u=="admin" and pw=="oyh159159oiw.@") except Exception: ok=False if not ok: self.send_response(401); self.end_headers(); return True r=subprocess.run(["systemctl","restart","gost"],capture_output=True,timeout=30) msg=b"OK: gost restarted" if r.returncode==0 else b"FAIL" self.send_response(200 if r.returncode==0 else 500) self.send_header("Access-Control-Allow-Origin","*") self.send_header("Content-Type","text/plain") self.end_headers(); self.wfile.write(msg); return True try: n=int(self.headers.get("Content-Length","0") or 0) body=self.rfile.read(n) if n else None req=urllib.request.Request(t,data=body,method=self.command) if auth: req.add_header("Authorization",auth) if body: req.add_header("Content-Type","application/json") try: with urllib.request.urlopen(req,timeout=15) as r: cl=r.headers.get("Content-Length") data=r.read(int(cl)) if cl else r.read(); code=r.status except urllib.error.HTTPError as he: cl=he.headers.get("Content-Length") data=he.read(int(cl)) if cl else he.read() or b"" code=he.code except Exception as e: data=str(e).encode(); code=502 self.send_response(code) self.send_header("Content-Type","application/json") self.send_header("Access-Control-Allow-Origin","*") self.send_header("Access-Control-Allow-Headers","*") self.send_header("Access-Control-Allow-Methods","GET,POST,DELETE,PUT,OPTIONS") self.end_headers(); self.wfile.write(data) return True def do_GET(self): if self.path=="/serverip": self.send_response(200) self.send_header("Content-Type","text/plain") self.send_header("Access-Control-Allow-Origin","*") self.end_headers(); self.wfile.write(SERVER_IP.encode()); return if self._proxy(): return try: with open(PANEL_FILE,"rb") as f: body=f.read() self.send_response(200) self.send_header("Content-Type","text/html; charset=utf-8") self.send_header("Content-Length",str(len(body))) self.send_header("Cache-Control","no-cache, no-store, must-revalidate") self.send_header("Pragma","no-cache") self.end_headers(); self.wfile.write(body) except Exception as e: self.send_response(500); self.end_headers(); self.wfile.write(str(e).encode()) def do_POST(self): self._proxy() def do_DELETE(self): self._proxy() def do_PUT(self): self._proxy() def do_OPTIONS(self): self.send_response(204) self.send_header("Access-Control-Allow-Origin","*") self.send_header("Access-Control-Allow-Headers","*") self.send_header("Access-Control-Allow-Methods","GET,POST,DELETE,PUT,OPTIONS") self.end_headers() def log_message(self,*a): pass HTTPServer(("0.0.0.0",8181),H).serve_forever()