BG5 commited on
Commit
b215301
·
verified ·
1 Parent(s): 4e7fad0

Update api.txt

Browse files
Files changed (1) hide show
  1. api.txt +113 -11
api.txt CHANGED
@@ -13,9 +13,65 @@ from typing import Dict
13
  from loguru import logger
14
  from starlette.responses import StreamingResponse
15
  import socket
 
 
 
 
16
 
17
  app = FastAPI()
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  # 浏览器实例管理
20
  BROWSERS: Dict[str, dict] = {} # {browser_id: {"process": Popen, "ws": str, "port": int, "status": str, "info": dict}}
21
  # 浏览器实例状态: open, closed
@@ -198,10 +254,12 @@ async def system_usage():
198
  """
199
  try:
200
  usage_info = get_system_usage()
201
- return {"success": True, "msg": "success", "data": usage_info}
 
202
  except Exception as e:
203
  logger.error(f"获取系统资源使用情况失败: {e}")
204
- return {"success": False, "msg": "failed", "error": str(e)}
 
205
 
206
 
207
  # BitBrowser风格API
@@ -214,12 +272,13 @@ async def open_browser(request: Request):
214
  if browser_id in BROWSERS:
215
  logger.info(f"Browser ID {browser_id}: {BROWSERS[browser_id]}")
216
  if BROWSERS[browser_id]["status"] == "open":
217
- return {
218
  "success": True,
219
  "msg": "already opened",
220
  "data": {"id": browser_id, "ws": BROWSERS[browser_id]["ws"]},
221
  "info": BROWSERS[browser_id]["info"]
222
  }
 
223
  else:
224
  port = get_free_port()
225
  BROWSERS[browser_id]["port"] = port
@@ -253,7 +312,8 @@ async def open_browser(request: Request):
253
  proc = subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
254
  if not wait_port("127.0.0.1", port, timeout=60):
255
  proc.terminate()
256
- return {"success": False, "msg": f"chrome端口{port}未就绪", "data": {}}
 
257
  async with httpx.AsyncClient() as client:
258
  resp = await client.get(f"http://127.0.0.1:{port}/json/version")
259
  if resp.status_code == 200:
@@ -263,7 +323,8 @@ async def open_browser(request: Request):
263
  ws_url = re.sub(r":\d+", "", ws_url)
264
  logger.info(f"Browser opened with ID: {browser_id}, Info: {browser_info}")
265
  BROWSERS[browser_id] = {"process": proc, "ws": ws_url, "port": port, "status": "open", "info": browser_info}
266
- return {"success": True, "msg": "success", "data": {"id": browser_id, "ws": ws_url}, "info": browser_info}
 
267
 
268
 
269
  @app.post("/browser/close")
@@ -272,19 +333,23 @@ async def close_browser(request: Request):
272
  browser_id = data.get("id")
273
  b = BROWSERS.get(browser_id)
274
  if not b:
275
- return {"success": False, "msg": "not found"}
 
276
  b["process"].terminate()
277
  b["status"] = "closed"
278
- return {"success": True, "msg": "closed", "data": {"id": browser_id}}
 
279
 
280
 
 
281
  @app.post("/browser/delete")
282
  async def delete_browser(request: Request):
283
  data = await request.json()
284
  browser_id = data.get("id")
285
  b = BROWSERS.pop(browser_id, None)
286
  if not b:
287
- return {"success": False, "msg": "not found"}
 
288
  try:
289
  b["process"].terminate()
290
  except Exception:
@@ -294,7 +359,8 @@ async def delete_browser(request: Request):
294
  if os.path.exists(profile_dir):
295
  import shutil
296
  shutil.rmtree(profile_dir, ignore_errors=True)
297
- return {"success": True, "msg": "deleted", "data": {"id": browser_id}}
 
298
 
299
 
300
  # @app.post("/browser/update")
@@ -316,12 +382,48 @@ async def delete_browser(request: Request):
316
  async def browser_ports():
317
  # 返回 {id: port} 格式
318
  data = {k: str(v["ws"]) for k, v in BROWSERS.items() if v["status"] == "open"}
319
- return {"success": True, "data": data}
 
320
 
321
 
322
  @app.post("/health")
323
  async def health():
324
- return {"success": True, "msg": "ok"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
326
 
327
  CDP_PATHS = [
 
13
  from loguru import logger
14
  from starlette.responses import StreamingResponse
15
  import socket
16
+ import base64
17
+ from cryptography.fernet import Fernet
18
+ from cryptography.hazmat.primitives import hashes
19
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
20
 
21
  app = FastAPI()
22
 
23
+ # 加密配置
24
+ ENCRYPTION_KEY = os.getenv('ENCRYPTION_KEY', 'default-encryption-key-change-in-production') # 生产环境中应该使用环境变量
25
+ SALT = b'stable_salt_123456' # 固定salt,生产环境中应该更安全
26
+
27
+ def get_encryption_key():
28
+ """生成加密密钥"""
29
+ kdf = PBKDF2HMAC(
30
+ algorithm=hashes.SHA256(),
31
+ length=32,
32
+ salt=SALT,
33
+ iterations=100000,
34
+ )
35
+ key = base64.urlsafe_b64encode(kdf.derive(ENCRYPTION_KEY.encode()))
36
+ return key
37
+
38
+ def encrypt_data(data: str) -> str:
39
+ """加密数据"""
40
+ try:
41
+ key = get_encryption_key()
42
+ f = Fernet(key)
43
+ encrypted_data = f.encrypt(data.encode())
44
+ return base64.urlsafe_b64encode(encrypted_data).decode()
45
+ except Exception as e:
46
+ logger.error(f"加密失败: {e}")
47
+ return data
48
+
49
+ def decrypt_data(encrypted_data: str) -> str:
50
+ """解密数据"""
51
+ try:
52
+ key = get_encryption_key()
53
+ f = Fernet(key)
54
+ encrypted_bytes = base64.urlsafe_b64decode(encrypted_data.encode())
55
+ decrypted_data = f.decrypt(encrypted_bytes)
56
+ return decrypted_data.decode()
57
+ except Exception as e:
58
+ logger.error(f"解密失败: {e}")
59
+ return encrypted_data
60
+
61
+ def encrypt_json_response(data: dict) -> dict:
62
+ """加密JSON响应数据"""
63
+ try:
64
+ json_str = json.dumps(data, ensure_ascii=False)
65
+ encrypted_str = encrypt_data(json_str)
66
+ return {
67
+ "encrypted": True,
68
+ "data": encrypted_str,
69
+ "timestamp": int(time.time())
70
+ }
71
+ except Exception as e:
72
+ logger.error(f"加密JSON响应失败: {e}")
73
+ return data
74
+
75
  # 浏览器实例管理
76
  BROWSERS: Dict[str, dict] = {} # {browser_id: {"process": Popen, "ws": str, "port": int, "status": str, "info": dict}}
77
  # 浏览器实例状态: open, closed
 
254
  """
255
  try:
256
  usage_info = get_system_usage()
257
+ response_data = {"success": True, "msg": "success", "data": usage_info}
258
+ return encrypt_json_response(response_data)
259
  except Exception as e:
260
  logger.error(f"获取系统资源使用情况失败: {e}")
261
+ error_data = {"success": False, "msg": "failed", "error": str(e)}
262
+ return encrypt_json_response(error_data)
263
 
264
 
265
  # BitBrowser风格API
 
272
  if browser_id in BROWSERS:
273
  logger.info(f"Browser ID {browser_id}: {BROWSERS[browser_id]}")
274
  if BROWSERS[browser_id]["status"] == "open":
275
+ response_data = {
276
  "success": True,
277
  "msg": "already opened",
278
  "data": {"id": browser_id, "ws": BROWSERS[browser_id]["ws"]},
279
  "info": BROWSERS[browser_id]["info"]
280
  }
281
+ return encrypt_json_response(response_data)
282
  else:
283
  port = get_free_port()
284
  BROWSERS[browser_id]["port"] = port
 
312
  proc = subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
313
  if not wait_port("127.0.0.1", port, timeout=60):
314
  proc.terminate()
315
+ error_data = {"success": False, "msg": f"chrome端口{port}未就绪", "data": {}}
316
+ return encrypt_json_response(error_data)
317
  async with httpx.AsyncClient() as client:
318
  resp = await client.get(f"http://127.0.0.1:{port}/json/version")
319
  if resp.status_code == 200:
 
323
  ws_url = re.sub(r":\d+", "", ws_url)
324
  logger.info(f"Browser opened with ID: {browser_id}, Info: {browser_info}")
325
  BROWSERS[browser_id] = {"process": proc, "ws": ws_url, "port": port, "status": "open", "info": browser_info}
326
+ response_data = {"success": True, "msg": "success", "data": {"id": browser_id, "ws": ws_url}, "info": browser_info}
327
+ return encrypt_json_response(response_data)
328
 
329
 
330
  @app.post("/browser/close")
 
333
  browser_id = data.get("id")
334
  b = BROWSERS.get(browser_id)
335
  if not b:
336
+ error_data = {"success": False, "msg": "not found"}
337
+ return encrypt_json_response(error_data)
338
  b["process"].terminate()
339
  b["status"] = "closed"
340
+ response_data = {"success": True, "msg": "closed", "data": {"id": browser_id}}
341
+ return encrypt_json_response(response_data)
342
 
343
 
344
+ @app.post("/browser/delete")
345
  @app.post("/browser/delete")
346
  async def delete_browser(request: Request):
347
  data = await request.json()
348
  browser_id = data.get("id")
349
  b = BROWSERS.pop(browser_id, None)
350
  if not b:
351
+ error_data = {"success": False, "msg": "not found"}
352
+ return encrypt_json_response(error_data)
353
  try:
354
  b["process"].terminate()
355
  except Exception:
 
359
  if os.path.exists(profile_dir):
360
  import shutil
361
  shutil.rmtree(profile_dir, ignore_errors=True)
362
+ response_data = {"success": True, "msg": "deleted", "data": {"id": browser_id}}
363
+ return encrypt_json_response(response_data)
364
 
365
 
366
  # @app.post("/browser/update")
 
382
  async def browser_ports():
383
  # 返回 {id: port} 格式
384
  data = {k: str(v["ws"]) for k, v in BROWSERS.items() if v["status"] == "open"}
385
+ response_data = {"success": True, "data": data}
386
+ return encrypt_json_response(response_data)
387
 
388
 
389
  @app.post("/health")
390
  async def health():
391
+ response_data = {"success": True, "msg": "ok"}
392
+ return encrypt_json_response(response_data)
393
+
394
+
395
+ @app.post("/decrypt")
396
+ async def decrypt_response(request: Request):
397
+ """
398
+ 解密接口,用于客户端解密响应数据
399
+ """
400
+ try:
401
+ data = await request.json()
402
+ encrypted_data = data.get("data")
403
+ if not encrypted_data:
404
+ return {"success": False, "msg": "missing encrypted data"}
405
+
406
+ decrypted_str = decrypt_data(encrypted_data)
407
+ decrypted_json = json.loads(decrypted_str)
408
+ return {"success": True, "msg": "decrypted", "data": decrypted_json}
409
+ except Exception as e:
410
+ logger.error(f"解密失败: {e}")
411
+ return {"success": False, "msg": "decrypt failed", "error": str(e)}
412
+
413
+
414
+ # 添加获取加密配置的接口(仅用于开发调试)
415
+ @app.get("/encryption/info")
416
+ async def encryption_info():
417
+ """
418
+ 返回加密相关信息(用于开发调试)
419
+ """
420
+ return {
421
+ "encrypted": True,
422
+ "algorithm": "Fernet (AES 128)",
423
+ "key_derivation": "PBKDF2HMAC with SHA256",
424
+ "iterations": 100000,
425
+ "info": "All JSON responses are encrypted. Use /decrypt endpoint to decrypt."
426
+ }
427
 
428
 
429
  CDP_PATHS = [