舍弃flask。请求增加H2协议。

This commit is contained in:
2025-11-18 22:09:19 +08:00
parent 37f91c4b8c
commit e90dbf14e9
10 changed files with 170 additions and 130 deletions

View File

@@ -169,7 +169,7 @@ class DeviceInfo:
if getattr(self, "_add_executor", None) is None: if getattr(self, "_add_executor", None) is None:
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
import os import os
max_workers = max(2, min(6, (os.cpu_count() or 4) // 2)) max_workers = int(os.getenv("DEVICE_ADD_WORKERS", "6"))
self._add_executor = ThreadPoolExecutor( self._add_executor = ThreadPoolExecutor(
max_workers=max_workers, max_workers=max_workers,
thread_name_prefix="dev-add" thread_name_prefix="dev-add"

View File

@@ -7,13 +7,16 @@ import time
from pathlib import Path from pathlib import Path
from queue import Queue from queue import Queue
from typing import Any, Dict, List from typing import Any, Dict, List
import anyio
from quart import Quart
from quart_cors import cors
from Entity import Variables from Entity import Variables
from Utils.AiUtils import AiUtils from Utils.AiUtils import AiUtils
from Utils.IOSAIStorage import IOSAIStorage from Utils.IOSAIStorage import IOSAIStorage
from Utils.LogManager import LogManager from Utils.LogManager import LogManager
import wda import wda
from flask import Flask, request from quart import Quart, request, g
from flask_cors import CORS
from Entity.ResultData import ResultData from Entity.ResultData import ResultData
from Utils.ControlUtils import ControlUtils from Utils.ControlUtils import ControlUtils
from Utils.ThreadManager import ThreadManager from Utils.ThreadManager import ThreadManager
@@ -29,8 +32,9 @@ for name in ('werkzeug', 'werkzeug.serving'):
log.propagate = False log.propagate = False
log.handlers.clear() log.handlers.clear()
app = Flask(__name__) app = Quart(__name__) # ⭐ 这里用 Quart而不是 Flask
CORS(app) app = cors(app, allow_origin="*") # 允许所有来源跨域
app.config['JSON_AS_ASCII'] = False # Flask jsonify 不转义中文/emoji app.config['JSON_AS_ASCII'] = False # Flask jsonify 不转义中文/emoji
app.config['JSONIFY_MIMETYPE'] = "application/json; charset=utf-8" app.config['JSONIFY_MIMETYPE'] = "application/json; charset=utf-8"
@@ -277,9 +281,27 @@ def bootstrap_server_side_effects():
def get_app(): def get_app():
return app return app
@app.before_request
def _log_request_start():
g._start_ts = time.time()
LogManager.info(
text=f"[HTTP] START {request.method} {request.path}",
udid="flask"
)
@app.after_request
def _log_request_end(response):
cost = time.time() - getattr(g, "_start_ts", time.time())
LogManager.info(
text=f"[HTTP] END {request.method} {request.path} {response.status_code} in {cost:.3f}s",
udid="flask"
)
return response
# ============ API 路由 ============ # ============ API 路由 ============
@app.route('/deviceList', methods=['GET']) @app.route('/deviceList', methods=['GET'])
def deviceList(): async def deviceList():
global _last_device_count, change_version global _last_device_count, change_version
global _last_nonempty_snapshot, _last_snapshot_ts, _STICKY_TTL_SEC global _last_nonempty_snapshot, _last_snapshot_ts, _STICKY_TTL_SEC
global _empty_logged, _recovered_logged global _empty_logged, _recovered_logged
@@ -315,23 +337,23 @@ def deviceList():
return ResultData(data=[]).toJson() return ResultData(data=[]).toJson()
@app.route('/passToken', methods=['POST']) @app.route('/passToken', methods=['POST'])
def passToken(): async def passToken():
data = request.get_json() data = await request.get_json()
print(json.dumps(data)) print(json.dumps(data))
return ResultData(data="").toJson() return ResultData(data="").toJson()
# 获取设备应用列表 # 获取设备应用列表
@app.route('/deviceAppList', methods=['POST']) @app.route('/deviceAppList', methods=['POST'])
def deviceAppList(): async def deviceAppList():
param = request.get_json() param = await request.get_json()
udid = param["udid"] udid = param["udid"]
apps = ControlUtils.getDeviceAppList(udid) apps = ControlUtils.getDeviceAppList(udid)
return ResultData(data=apps).toJson() return ResultData(data=apps).toJson()
# 打开指定app # 打开指定app
@app.route('/launchApp', methods=['POST']) @app.route('/launchApp', methods=['POST'])
def launchApp(): async def launchApp():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
bundleId = body.get("bundleId") bundleId = body.get("bundleId")
t = wda.USBClient(udid, wdaFunctionPort) t = wda.USBClient(udid, wdaFunctionPort)
@@ -340,8 +362,8 @@ def launchApp():
# 回到首页 # 回到首页
@app.route('/toHome', methods=['POST']) @app.route('/toHome', methods=['POST'])
def toHome(): async def toHome():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
client = wda.USBClient(udid, wdaFunctionPort) client = wda.USBClient(udid, wdaFunctionPort)
client.home() client.home()
@@ -349,8 +371,8 @@ def toHome():
# 点击事件 # 点击事件
@app.route('/tapAction', methods=['POST']) @app.route('/tapAction', methods=['POST'])
def tapAction(): async def tapAction():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
client = wda.USBClient(udid, wdaFunctionPort) client = wda.USBClient(udid, wdaFunctionPort)
print("-----------------------") print("-----------------------")
@@ -365,8 +387,8 @@ def tapAction():
# 拖拽事件 # 拖拽事件
@app.route('/swipeAction', methods=['POST']) @app.route('/swipeAction', methods=['POST'])
def swipeAction(): async def swipeAction():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
duration = body.get("duration") # 时长 duration = body.get("duration") # 时长
sx = body.get("sx") # 起始X点 sx = body.get("sx") # 起始X点
@@ -383,8 +405,8 @@ def swipeAction():
# 长按事件 # 长按事件
@app.route('/longPressAction', methods=['POST']) @app.route('/longPressAction', methods=['POST'])
def longPressAction(): async def longPressAction():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
x = body.get("x") x = body.get("x")
y = body.get("y") y = body.get("y")
@@ -396,8 +418,8 @@ def longPressAction():
# 养号 # 养号
@app.route('/growAccount', methods=['POST']) @app.route('/growAccount', methods=['POST'])
def growAccount(): async def growAccount():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
Variables.commentList = body.get("comment") Variables.commentList = body.get("comment")
isComment = body.get("isComment") isComment = body.get("isComment")
@@ -412,8 +434,8 @@ def growAccount():
# 观看直播 # 观看直播
@app.route("/watchLiveForGrowth", methods=['POST']) @app.route("/watchLiveForGrowth", methods=['POST'])
def watchLiveForGrowth(): async def watchLiveForGrowth():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
manager = ScriptManager() manager = ScriptManager()
event = threading.Event() event = threading.Event()
@@ -424,8 +446,8 @@ def watchLiveForGrowth():
# 停止脚本 # 停止脚本
@app.route("/stopScript", methods=['POST']) @app.route("/stopScript", methods=['POST'])
def stopScript(): async def stopScript():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
LogManager.method_info(f"接口收到 /stopScript udid={udid}", method="task") LogManager.method_info(f"接口收到 /stopScript udid={udid}", method="task")
code, msg = ThreadManager.stop(udid) code, msg = ThreadManager.stop(udid)
@@ -433,10 +455,10 @@ def stopScript():
# 关注打招呼 # 关注打招呼
@app.route('/passAnchorData', methods=['POST']) @app.route('/passAnchorData', methods=['POST'])
def passAnchorData(): async def passAnchorData():
try: try:
LogManager.method_info("关注打招呼", "关注打招呼") LogManager.method_info("关注打招呼", "关注打招呼")
data: Dict[str, Any] = request.get_json() data: Dict[str, Any] = await request.get_json()
# 设备列表 # 设备列表
idList = data.get("deviceList", []) idList = data.get("deviceList", [])
# 主播列表 # 主播列表
@@ -469,10 +491,10 @@ def passAnchorData():
return ResultData(data="", code=1001).toJson() return ResultData(data="", code=1001).toJson()
@app.route('/followAndGreetUnion', methods=['POST']) @app.route('/followAndGreetUnion', methods=['POST'])
def followAndGreetUnion(): async def followAndGreetUnion():
try: try:
LogManager.method_info("关注打招呼", "关注打招呼(联盟号)") LogManager.method_info("关注打招呼", "关注打招呼(联盟号)")
data: Dict[str, Any] = request.get_json() data: Dict[str, Any] = await request.get_json()
# 设备列表 # 设备列表
idList = data.get("deviceList", []) idList = data.get("deviceList", [])
# 主播列表 # 主播列表
@@ -510,20 +532,20 @@ def followAndGreetUnion():
# 获取私信数据 # 获取私信数据
@app.route("/getPrologueList", methods=['GET']) @app.route("/getPrologueList", methods=['GET'])
def getPrologueList(): async def getPrologueList():
import Entity.Variables as Variables import Entity.Variables as Variables
return ResultData(data=Variables.prologueList).toJson() return ResultData(data=Variables.prologueList).toJson()
# 添加临时数据 # 添加临时数据
# 批量追加主播到 JSON 文件 # 批量追加主播到 JSON 文件
@app.route("/addTempAnchorData", methods=['POST']) @app.route("/addTempAnchorData", methods=['POST'])
def addTempAnchorData(): async def addTempAnchorData():
""" """
请求体支持: 请求体支持:
- 单个对象:{"anchorId": "xxx", "country": "CN"} - 单个对象:{"anchorId": "xxx", "country": "CN"}
- 对象数组:[{"anchorId": "xxx", "country": "CN"}, {"anchorId": "yyy", "country": "US"}] - 对象数组:[{"anchorId": "xxx", "country": "CN"}, {"anchorId": "yyy", "country": "US"}]
""" """
data = request.get_json() data = await request.get_json()
if not data: if not data:
return ResultData(code=400, message="请求数据为空").toJson() return ResultData(code=400, message="请求数据为空").toJson()
# 追加到 JSON 文件 # 追加到 JSON 文件
@@ -532,8 +554,8 @@ def addTempAnchorData():
# 获取当前屏幕上的聊天信息 # 获取当前屏幕上的聊天信息
@app.route("/getChatTextInfo", methods=['POST']) @app.route("/getChatTextInfo", methods=['POST'])
def getChatTextInfo(): async def getChatTextInfo():
data = request.get_json() data = await request.get_json()
udid = data.get("udid") udid = data.get("udid")
client = wda.USBClient(udid,wdaFunctionPort) client = wda.USBClient(udid,wdaFunctionPort)
session = client.session() session = client.session()
@@ -578,9 +600,9 @@ def getChatTextInfo():
# 监控消息 # 监控消息
@app.route("/replyMessages", methods=['POST']) @app.route("/replyMessages", methods=['POST'])
def monitorMessages(): async def monitorMessages():
LogManager.method_info("开始监控消息,监控消息脚本启动", "监控消息") LogManager.method_info("开始监控消息,监控消息脚本启动", "监控消息")
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
# Variables.commentList = body.get("comment") # Variables.commentList = body.get("comment")
@@ -594,8 +616,8 @@ def monitorMessages():
# 上传日志 # 上传日志
@app.route("/setLoginInfo", methods=['POST']) @app.route("/setLoginInfo", methods=['POST'])
def upLoadLogLogs(): async def upLoadLogLogs():
data = request.get_json() # 解析 JSON data = await request.get_json() # 解析 JSON
token = data.get("token") token = data.get("token")
userId = data.get("userId") userId = data.get("userId")
tenantId = data.get("tenantId") tenantId = data.get("tenantId")
@@ -607,7 +629,7 @@ def upLoadLogLogs():
# 获取当前的主播列表数据 # 获取当前的主播列表数据
@app.route("/anchorList", methods=['POST']) @app.route("/anchorList", methods=['POST'])
def queryAnchorList(): async def queryAnchorList():
# 项目根目录(当前文件在 infos 下,回退两层到根目录) # 项目根目录(当前文件在 infos 下,回退两层到根目录)
root_dir = Path(__file__).resolve().parent.parent root_dir = Path(__file__).resolve().parent.parent
file_path = root_dir / "data" / "acList.json" file_path = root_dir / "data" / "acList.json"
@@ -624,12 +646,12 @@ def queryAnchorList():
# 修改当前的主播列表数据 # 修改当前的主播列表数据
@app.route("/updateAnchorList", methods=['POST']) @app.route("/updateAnchorList", methods=['POST'])
def updateAnchorList(): async def updateAnchorList():
""" """
invitationType: 1 普票 2 金票 invitationType: 1 普票 2 金票
state: 1 通行(True) / 0 不通行(False) state: 1 通行(True) / 0 不通行(False)
""" """
data = request.get_json(force=True, silent=True) or {} data = await request.get_json(force=True, silent=True) or {}
invitationType = data.get("invitationType") invitationType = data.get("invitationType")
state = bool(data.get("state")) # 转成布尔 state = bool(data.get("state")) # 转成布尔
@@ -679,16 +701,16 @@ def updateAnchorList():
# 删除主播 # 删除主播
@app.route("/deleteAnchorWithIds", methods=['POST']) @app.route("/deleteAnchorWithIds", methods=['POST'])
def deleteAnchorWithIds(): async def deleteAnchorWithIds():
ls: list[dict] = request.get_json() # [{"anchorId": "xxx"}, ...] ls: list[dict] = await request.get_json() # [{"anchorId": "xxx"}, ...]
ids = [d.get("anchorId") for d in ls if d.get("anchorId")] ids = [d.get("anchorId") for d in ls if d.get("anchorId")]
deleted = AiUtils.delete_anchors_by_ids(ids) deleted = AiUtils.delete_anchors_by_ids(ids)
return ResultData(data={"deleted": deleted}).toJson() return ResultData(data={"deleted": deleted}).toJson()
# 配置ai人设 # 配置ai人设
@app.route("/aiConfig", methods=['POST']) @app.route("/aiConfig", methods=['POST'])
def aiConfig(): async def aiConfig():
data = request.get_json() data = await request.get_json()
agentName = data.get("agentName") agentName = data.get("agentName")
guildName = data.get("guildName") guildName = data.get("guildName")
contactTool = data.get("contactTool") contactTool = data.get("contactTool")
@@ -725,14 +747,14 @@ def aiConfig():
# 查询主播聊天发送的最后一条信息 # 查询主播聊天发送的最后一条信息
@app.route("/select_last_message", methods=['GET']) @app.route("/select_last_message", methods=['GET'])
def select_last_message(): async def select_last_message():
data = JsonUtils.query_all_json_items() data = JsonUtils.query_all_json_items()
return ResultData(data=data).toJson() return ResultData(data=data).toJson()
# 修改消息(已读改成未读) # 修改消息(已读改成未读)
@app.route("/update_last_message", methods=['POST']) @app.route("/update_last_message", methods=['POST'])
def update_last_message(): async def update_last_message():
data = request.get_json() # 解析 JSON data = await request.get_json() # 解析 JSON
sender = data.get("sender") sender = data.get("sender")
udid = data.get("device") udid = data.get("device")
text = data.get("text") text = data.get("text")
@@ -749,8 +771,8 @@ def update_last_message():
# 删除已读消息 # 删除已读消息
@app.route("/delete_last_message", methods=['POST']) @app.route("/delete_last_message", methods=['POST'])
def delete_last_message(): async def delete_last_message():
data = request.get_json() # 解析 JSON data = await request.get_json() # 解析 JSON
sender = data.get("sender") sender = data.get("sender")
udid = data.get("device") udid = data.get("device")
text = data.get("text") text = data.get("text")
@@ -767,15 +789,15 @@ def delete_last_message():
# 停止所有任务 # 停止所有任务
@app.route("/stopAllTask", methods=['POST']) @app.route("/stopAllTask", methods=['POST'])
def stopAllTask(): async def stopAllTask():
idList = request.get_json() idList = await request.get_json()
code, msg, data = ThreadManager.batch_stop(idList) code, msg, data = ThreadManager.batch_stop(idList)
return ResultData(code, data, msg).toJson() return ResultData(code, data, msg).toJson()
# 切换账号 # 切换账号
@app.route('/changeAccount', methods=['POST']) @app.route('/changeAccount', methods=['POST'])
def changeAccount(): async def changeAccount():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
if not udid: if not udid:
return ResultData(data="", code=400, message="缺少 udid").toJson() return ResultData(data="", code=400, message="缺少 udid").toJson()
@@ -792,12 +814,16 @@ def changeAccount():
# 查看设备网络状态 # 查看设备网络状态
@app.route('/getDeviceNetStatus', methods=['POST']) @app.route('/getDeviceNetStatus', methods=['POST'])
def getDeviceNetStatus(): async def getDeviceNetStatus():
body = request.get_json() body = await request.get_json()
udid = body.get("udid") udid = body.get("udid")
client = wda.USBClient(udid, wdaFunctionPort) # 同步且超级慢的逻辑 → 丢到线程池,不阻塞事件循环
r = client.getNetWorkStatus() def _work():
value = r.get("value") client = wda.USBClient(udid, wdaFunctionPort)
r = client.getNetWorkStatus()
return r.get("value")
value = await anyio.to_thread.run_sync(_work)
return ResultData(data=value, code=200).toJson() return ResultData(data=value, code=200).toJson()
@app.route('/test', methods=['POST']) @app.route('/test', methods=['POST'])
@@ -866,8 +892,8 @@ def getAiConfig():
# 重新开启tiktok # 重新开启tiktok
@app.route("/restartTikTok", methods=['POST']) @app.route("/restartTikTok", methods=['POST'])
def restartTikTok(): async def restartTikTok():
json = request.get_json() json = await request.get_json()
udid = json.get("udid") udid = json.get("udid")
client = wda.USBClient(udid, wdaFunctionPort) client = wda.USBClient(udid, wdaFunctionPort)
session = client.session() session = client.session()

View File

@@ -59,7 +59,7 @@ class FlaskSubprocessManager:
self._log("info", "FlaskSubprocessManager 初始化完成") self._log("info", "FlaskSubprocessManager 初始化完成")
# ========= 日志工具 ========= # ========= 日志工具 =========
def _log(self, level: str, msg: str, udid="system"): def _log(self, level: str, msg: str, udid="flask"):
"""同时写 LogManager + 控制台""" """同时写 LogManager + 控制台"""
try: try:
if level == "info": if level == "info":
@@ -146,6 +146,12 @@ class FlaskSubprocessManager:
threading.Thread(target=self._flush_stdout, daemon=True).start() threading.Thread(target=self._flush_stdout, daemon=True).start()
self._log("info", f"[FlaskMgr] Flask 子进程已启动PID={self.process.pid}") self._log("info", f"[FlaskMgr] Flask 子进程已启动PID={self.process.pid}")
# 启动看门狗线程
self._watchdog_thread = threading.Thread(target=self.watchdog_loop, daemon=True)
self._watchdog_thread.start()
LogManager.info("[FlaskWD] 看门狗线程已启动", udid="flask")
if not self._wait_port_open(timeout=10): if not self._wait_port_open(timeout=10):
self._log("error", "[FlaskMgr] 启动失败,端口未监听") self._log("error", "[FlaskMgr] 启动失败,端口未监听")
self.stop() self.stop()
@@ -293,3 +299,17 @@ class FlaskSubprocessManager:
@classmethod @classmethod
def get_instance(cls) -> 'FlaskSubprocessManager': def get_instance(cls) -> 'FlaskSubprocessManager':
return cls() return cls()
def watchdog_loop(self):
while True:
if self.process is not None:
code = self.process.poll()
if code is not None:
LogManager.error(
text=f"[FlaskWD] Flask 子进程退出exit code={code}",
udid="flask",
)
# 可以顺便触发一下 _stop_event看你愿不愿意
# self._stop_event.set()
break
time.sleep(1)

View File

@@ -5,6 +5,7 @@ import sys
from pathlib import Path from pathlib import Path
from asgiref.wsgi import WsgiToAsgi from asgiref.wsgi import WsgiToAsgi
from sympy import false
from Utils.AiUtils import AiUtils from Utils.AiUtils import AiUtils
from Utils.LogManager import LogManager from Utils.LogManager import LogManager
@@ -40,9 +41,6 @@ def _run_flask_role():
AiUtils.flask_port_free(flaskPort) AiUtils.flask_port_free(flaskPort)
bootstrap_server_side_effects() bootstrap_server_side_effects()
# 把 WSGI Flask app 包成 ASGI app
asgi_app = WsgiToAsgi(app)
# ==== 关键:统一获取 resources 目录 ==== # ==== 关键:统一获取 resources 目录 ====
if "__compiled__" in globals(): if "__compiled__" in globals():
# 被 Nuitka 编译后的 exe 运行时 # 被 Nuitka 编译后的 exe 运行时
@@ -54,20 +52,16 @@ def _run_flask_role():
resource_dir = os.path.join(base_dir, "resources") resource_dir = os.path.join(base_dir, "resources")
# Hypercorn 配置
config = Config() config = Config()
config.bind = [f"0.0.0.0:{flaskPort}"] config.bind = [f"0.0.0.0:{flaskPort}"]
config.alpn_protocols = ["h2"] # 开 HTTP/2 config.certfile = os.path.join(resource_dir, "server.crt")
config.certfile = os.path.join(resource_dir, "cert.pem") config.keyfile = os.path.join(resource_dir, "server.key")
config.keyfile = os.path.join(resource_dir, "key.pem") config.alpn_protocols = ["h2"] # 👈 这一行
config.workers = 6 # 你机器 4GB → 推荐 34 个 worker
print(f"Starting Hypercorn on port {flaskPort} (HTTP/2 enabled)") # 直接跑 QuartASGI 原生,不再用 WsgiToAsgi
asyncio.run(serve(app, config))
# 开启 HTTP/2
config.alpn_protocols = ["h2"]
print(f"Starting Hypercorn on https://localhost:{flaskPort} (HTTP/2 enabled)")
asyncio.run(serve(asgi_app, config))
if "--role=flask" in sys.argv: if "--role=flask" in sys.argv:
_run_flask_role() _run_flask_role()
@@ -84,7 +78,7 @@ if __name__ == "__main__":
# 清空日志 # 清空日志
LogManager.clearLogs() LogManager.clearLogs()
main(sys.argv) # main(sys.argv)
# 添加iOS开发包到电脑上 # 添加iOS开发包到电脑上
deployer = DevDiskImageDeployer(verbose=True) deployer = DevDiskImageDeployer(verbose=True)

View File

@@ -26,7 +26,7 @@ def _force_utf8_everywhere():
except Exception: except Exception:
pass pass
_force_utf8_everywhere() # _force_utf8_everywhere()
class LogManager: class LogManager:
""" """

View File

@@ -9,7 +9,7 @@ python -m nuitka Module\Main.py ^
--windows-console-mode=disable ^ --windows-console-mode=disable ^
--output-filename=IOSAI ^ --output-filename=IOSAI ^
--include-package=Module,Utils,Entity,script ^ --include-package=Module,Utils,Entity,script ^
--include-module=flask,wda,psutil,portalocker,flask_cors,cv2,lxml.etree,requests,urllib3,certifi,idna,setuptools,asgiref,hypercorn,h2,hpack,wsproto,priority,anyio,sniffio ^ --include-module=quart,quart_cors,wda,psutil,portalocker,cv2,lxml.etree,requests,urllib3,certifi,idna,setuptools,asgiref,hypercorn,h2,hpack,wsproto,priority,anyio,sniffio ^
--include-data-dir=resources=resources ^ --include-data-dir=resources=resources ^
--include-data-dir=SupportFiles=SupportFiles ^ --include-data-dir=SupportFiles=SupportFiles ^
--include-data-files="resources/iproxy/*=resources/iproxy/" ^ --include-data-files="resources/iproxy/*=resources/iproxy/" ^

View File

@@ -1,21 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDizCCAnOgAwIBAgIUXpRsBS0IBEvAw22Ii/wtu/gdooUwDQYJKoZIhvcNAQEL
BQAwXTELMAkGA1UEBhMCQ04xDjAMBgNVBAgMBUxvY2FsMQ4wDAYDVQQHDAVMb2Nh
bDEMMAoGA1UECgwDRGV2MQwwCgYDVQQLDANEZXYxEjAQBgNVBAMMCWxvY2FsaG9z
dDAeFw0yNTExMTcwODI2MzdaFw0yNjExMTcwODI2MzdaMF0xCzAJBgNVBAYTAkNO
MQ4wDAYDVQQIDAVMb2NhbDEOMAwGA1UEBwwFTG9jYWwxDDAKBgNVBAoMA0RldjEM
MAoGA1UECwwDRGV2MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCixGQJ0gdzcQoqdOv4lLvfH5Kz4C/t4/WCZfheF3Z7
cYiog0Ql8URyn6bF8ux97X4TtpJ621jM/lfxc2hKrqXpbsyO2EKgT3OEpuv/lyqC
YQqbHUUIAiXI9OF/iAANY5rBIeEDEUhcH4Ngt6EJ1FZdU+tfgV8V9zNUSeSJ+VtF
C+0LsTyWy7eqnXkXnPZvitVeZU85Zy5lWdC1mp9cOoyElmYuGxIQkW6ZtEzMjVp4
Eim3RsZgk6ZYRAdMGfdaa6YrmDDqhEZVEEL55dstOqfIWKUppazC9HSs7FnGsaUn
xkdqOArACdhU9y3f+yyeLC93Xllx9kgFfvLueysUqSgdAgMBAAGjQzBBMCAGA1Ud
EQQZMBeCCWxvY2FsaG9zdIcEfwAAAYcEwKgB2jAdBgNVHQ4EFgQURY1BDcpSTUNO
D0olM9H84Gu/QQ0wDQYJKoZIhvcNAQELBQADggEBAFrCAqIlpzncH6owBImN8Ub5
8ZwtTm+C3nQZF5FkCdsXHfqtPTEk4bX7IFHaj7saqroCYXfgopzvk2QX16wlPwk2
SKA/pF6I2bNNozlcVN9QAf9ue6xa8g8AxwPT46gbKTKFyG5lg1umYXhCGKVIJ/1l
B4Bh8KmPfzNWxiKOzflGNx5j1BHPZ9S7jt9wtiEwENceGZXVE8ANMiNR44+suuM7
6/syQUetPN+VWW+/14OrDeYQDLZTbUVigY75KIuLF41PxNWG745Qlcu/5nmvBnHv
5NNm2Zs8GYKLqLIlUQNU8x0R03FBLCYjDKRJsNpsZPqjI5cDSPj5vHZrzD3Jh0s=
-----END CERTIFICATE-----

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCixGQJ0gdzcQoq
dOv4lLvfH5Kz4C/t4/WCZfheF3Z7cYiog0Ql8URyn6bF8ux97X4TtpJ621jM/lfx
c2hKrqXpbsyO2EKgT3OEpuv/lyqCYQqbHUUIAiXI9OF/iAANY5rBIeEDEUhcH4Ng
t6EJ1FZdU+tfgV8V9zNUSeSJ+VtFC+0LsTyWy7eqnXkXnPZvitVeZU85Zy5lWdC1
mp9cOoyElmYuGxIQkW6ZtEzMjVp4Eim3RsZgk6ZYRAdMGfdaa6YrmDDqhEZVEEL5
5dstOqfIWKUppazC9HSs7FnGsaUnxkdqOArACdhU9y3f+yyeLC93Xllx9kgFfvLu
eysUqSgdAgMBAAECggEAGY29qvEHbG9Vyj6bAWbQbAI39PeAbte4JqW9rX//gPfd
HZ+mJlLPjTNVaoRt7oNHpO6n5pPjSCOySNz2hasPrytPACoho6t1lmDicjkYWmnD
0YBx4wT7S6ZudKg0YeW+WQ3plqKy+ouUA64woStt968CJ/dWp0stCtGjCKpWUuuB
TXvAUM91qrQlpIuqQI0QYRcKaoKPV6IckWPGeLvKi8EhVOFCNdya8jTP/ayxT06r
/cNucO+Tqe0PUe6jet6Ecx7Av4h8QXg9FMoF40RKxD6Q+sK/bM1c51KvUZVt9v4F
epAHjQyrzPjUozjtKzQvibexBvSwz/XaFCDgr7OwPQKBgQDOJtILcxjJDZfk8LUG
GNF8XVtgDu2FFVw79MSuQgERC+w2b+P2feffwdI4vu2+8jkWCHnttXNgdrS3r41C
gak3Xb2Jzx8M06eNC9QCtjicrWNTmJtcp5YZm1vS+fAPQG4DPLK+J4EmLNNyQ9nC
0vMfYRwKr3TX5FU6MyJbAsVnkwKBgQDKH/zUhMPdN6+0upEVoUJrI3BOhVdN8eEp
eqNWYU2Hhmg5mHsnoxz8Rjxw5ZKx108BT1JNea/rrNgOhe8TTpiAIVl5ZMN0OcO2
INlfteCtXY5nzU8HilfFBPwroR8Msx662GpM8TRA9RfTGJ7nKmiXhC1jnmkPecwX
+f+LaLCfjwKBgQC11H3dxXYuF8RLFZjFuOxFIl7vOht8D9wbsggsn2ErdPWzCjvq
9SCpNt7CWH2At0tsyKsq5KnQgsNhZQFWkOD9SbxdKgf8G0+k07L7dVg3saNzX55h
OhvlmCeEzhlUioK+bjJGELgUQON73Kbc9Y2lttSyBBIuPmKCBAogdjBB6wKBgGnM
VIro85zXiSEQhuDLh/iMlDyFjy09bp5HkzejtvE5aVS8e7pDpuhl2z087YwpJzGI
U4w6Jds2neD8Oifg+/IVgsAH/kbX9ZlfmGiAyxnz3pZ24OcRgt+dvGEZ9Sawm2Ux
4nJjzvYxVEcqnAJkMFse1KNQR63SEwJ52Ukfg1QBAoGBAKByisJiMTi/gmSlPFf6
3ZfxCd5ReMS/Ak4JIBN4S+GoAZp6yMZy1jYFHqV2k3slhmcswy+V9DGxtldHlDu9
+90zV1btfaB3nq5ydkmj32SU9MRqpzP4axHYTi8cvmOBlOEpL5e7KWTg072rGHAC
+ih8VPn9LTKk2GCtCCp4r2jI
-----END PRIVATE KEY-----

21
resources/server.crt Normal file
View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDaTCCAlGgAwIBAgIUAQMZUx/qRQIv49P6EIWt+mjYlKYwDQYJKoZIhvcNAQEL
BQAwXTELMAkGA1UEBhMCQ04xDjAMBgNVBAgMBUxvY2FsMQ4wDAYDVQQHDAVMb2Nh
bDEMMAoGA1UECgwDRGV2MQwwCgYDVQQLDANEZXYxEjAQBgNVBAMMCWxvY2FsaG9z
dDAeFw0yNTExMTgwNjMyMjlaFw0zNTExMTYwNjMyMjlaMF0xCzAJBgNVBAYTAkNO
MQ4wDAYDVQQIDAVMb2NhbDEOMAwGA1UEBwwFTG9jYWwxDDAKBgNVBAoMA0RldjEM
MAoGA1UECwwDRGV2MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDxY0znIdwlVW+EVcQww7Xg/16iN3JpX0svpEOIbo1u
40S3bRn057cS//1AG+c+55iisfOaoUX59ZUQhihcAje7IfTKO1/dCzDy4d/PT2hl
UOO9Zo8GQzflFM0U6fIi2Ifly09JTGJEyxr+SrJHcJPENualiR6zwNLlqupE9bDP
40ydznYWZRvw3N0QmrkOg+eY6FwaYtWspvf/KiJWucscc31zGyA0MhF552k6sIVg
9Vskr9Bd3g52Umv/1yPZmESkuM905ImCwSCK0VPAY+rooUeTYw3ktE7q/iy5+l71
s6hN9YHVo3m4pIJz4G0YT039TnjtZxxHt8IIVSv6Ymr3AgMBAAGjITAfMB0GA1Ud
DgQWBBQ6Q3V0wyXXyNm3jvmSKc2KoAMvAzANBgkqhkiG9w0BAQsFAAOCAQEAacsO
ja4qpX/vWUTelhdvzg5alD5WDrP8iSIXmGF+HSHgJbbjxbDm4vlMZjzwh8iqODQR
yJ9iuRiFFXGCktEqFx2NTCIUBmyoBg/LFeLtOn0Ncqs11ypoSoRxqE0IaeDjirBH
hNUIXzJ+3pOqgyHU+3WqgEzEjW63pNmjX1esVZqA0SQJejsv4hJOvBzGoFFgSRcC
Zp7NrusZ8IDkdLbUgD9pgZHPI8YNH/MVocV3wd45o9Y3nkMPhIqkp/1GOWIdN6qj
co66o0hYsJduQC9fEBceWpRNWUirEKd231SeaW9vZMPMrfmOZanDY6pXdvhsZR3L
9ZGsJk6ktoTagz2AYw==
-----END CERTIFICATE-----

28
resources/server.key Normal file
View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQDxY0znIdwlVW+E
VcQww7Xg/16iN3JpX0svpEOIbo1u40S3bRn057cS//1AG+c+55iisfOaoUX59ZUQ
hihcAje7IfTKO1/dCzDy4d/PT2hlUOO9Zo8GQzflFM0U6fIi2Ifly09JTGJEyxr+
SrJHcJPENualiR6zwNLlqupE9bDP40ydznYWZRvw3N0QmrkOg+eY6FwaYtWspvf/
KiJWucscc31zGyA0MhF552k6sIVg9Vskr9Bd3g52Umv/1yPZmESkuM905ImCwSCK
0VPAY+rooUeTYw3ktE7q/iy5+l71s6hN9YHVo3m4pIJz4G0YT039TnjtZxxHt8II
VSv6Ymr3AgMBAAECgf9Wf6myg+iV5TRN77TAGbAP6udHacgYn+7Ad5eQC+ZlobuU
Y3Tnh325tXqvPRFSGFwiAlMQVDECt1u/PDm6vJ077t/UHQ5zjr3sYPU1oONuptJ7
Jo0WsoFKLkH/oMeBi7h643+Oo9/GH04/nTdnMS9kqL1lYy/aUOVGW5JXIZUWhwMc
gTEXuyuaajqdRbQex6vSQANpauYMewj9jlU9RVfjB6LGUHLDJwWbi61dTOYakxkn
ssfzEDPMFuGQ8qjG5Li6ceUgNHlmY73WZBXNHKt6FcwNqldy5FB4PiIxoWx0ZHmq
z/i89BQlurp20RyFD8f+iMTIbU/z0gHM86JkziECgYEA+qO5KVSg0XQlQmsyXT7Z
nKI+DakGxn4ipJWuUDLdi0QoymNIF3diUi1o9P4Q8QzfNFCTVdUsQft24ekXAkJX
QtGL61BXBJX5vZvihbBuCEGatE+31LRQZOxWLaeWE6y8XbFfez22SRpa+IrTma+h
BxBRyKSqO03xyojKTPPMbDkCgYEA9ozsY02MUJKDXxtE2BRgwGQumoHi7HPRUISk
ij0MWw/AYdYoCsrALP4IKB65tK0M7lJsK+g5M2Kyh8XD/Z4hfT1oNnJW8SOtPBwd
heuPUzjnhIhnnxoLFeKMo6bGNqVkIYAOKbcX9s2J7U9NwUlYxMxmjNrV2/Nb2MFE
Raun8K8CgYBbX1ydaLDIKyN6N0JBJCyJIcylhj1mF43hmn/V1PVXVB3ayp75jxhV
BSECT601c4/brpRH8lMUKuyIJ0WwGSdewK3Vt5BBp7tIGJBYVJ2IfQI9QeKutJ2q
bU5tjm7z9UEmlwdMEo9lzyni+hlyKcj2nkhycTVuMVg4ke3OaALaYQKBgDD3XBt8
01lNP/ormEiyA2UygG7/TOpZNkEflu49oa6UOkk0F0/NZM2KxmPxdkCD/gV3KTSv
Mm0aNQryJDLCrTQKdiAaJVpPE6DUlKh8WELXEmQoEyxuJ7V5ASWfgc5omrJslGOE
kaXavIH9NhwlTRQI5HUlIURF2P/7omuT5A7RAoGBAJhtThrzxExrzmf4bTIXe+EQ
M5lAPqIn46ThlujcNCLroxWz32Ekzs1ywn2NKwK6F1gMJEsFXqQDXtumJYTJ94tY
L1fv0knHgRnURsq6xZ4IQgq3YzV0OXQwN4PBzqcRR6sMgFAXfCzXMy/eE7iz8eZT
FsrkWh/9tgVvITaUrqr5
-----END PRIVATE KEY-----