舍弃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:
from concurrent.futures import ThreadPoolExecutor
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(
max_workers=max_workers,
thread_name_prefix="dev-add"

View File

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

View File

@@ -59,7 +59,7 @@ class FlaskSubprocessManager:
self._log("info", "FlaskSubprocessManager 初始化完成")
# ========= 日志工具 =========
def _log(self, level: str, msg: str, udid="system"):
def _log(self, level: str, msg: str, udid="flask"):
"""同时写 LogManager + 控制台"""
try:
if level == "info":
@@ -146,6 +146,12 @@ class FlaskSubprocessManager:
threading.Thread(target=self._flush_stdout, daemon=True).start()
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):
self._log("error", "[FlaskMgr] 启动失败,端口未监听")
self.stop()
@@ -292,4 +298,18 @@ class FlaskSubprocessManager:
@classmethod
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 asgiref.wsgi import WsgiToAsgi
from sympy import false
from Utils.AiUtils import AiUtils
from Utils.LogManager import LogManager
@@ -40,9 +41,6 @@ def _run_flask_role():
AiUtils.flask_port_free(flaskPort)
bootstrap_server_side_effects()
# 把 WSGI Flask app 包成 ASGI app
asgi_app = WsgiToAsgi(app)
# ==== 关键:统一获取 resources 目录 ====
if "__compiled__" in globals():
# 被 Nuitka 编译后的 exe 运行时
@@ -54,20 +52,16 @@ def _run_flask_role():
resource_dir = os.path.join(base_dir, "resources")
# Hypercorn 配置
config = Config()
config.bind = [f"0.0.0.0:{flaskPort}"]
config.alpn_protocols = ["h2"] # 开 HTTP/2
config.certfile = os.path.join(resource_dir, "cert.pem")
config.keyfile = os.path.join(resource_dir, "key.pem")
config.certfile = os.path.join(resource_dir, "server.crt")
config.keyfile = os.path.join(resource_dir, "server.key")
config.alpn_protocols = ["h2"] # 👈 这一行
config.workers = 6 # 你机器 4GB → 推荐 34 个 worker
print(f"Starting Hypercorn on port {flaskPort} (HTTP/2 enabled)")
# 开启 HTTP/2
config.alpn_protocols = ["h2"]
print(f"Starting Hypercorn on https://localhost:{flaskPort} (HTTP/2 enabled)")
asyncio.run(serve(asgi_app, config))
# 直接跑 QuartASGI 原生,不再用 WsgiToAsgi
asyncio.run(serve(app, config))
if "--role=flask" in sys.argv:
_run_flask_role()
@@ -84,7 +78,7 @@ if __name__ == "__main__":
# 清空日志
LogManager.clearLogs()
main(sys.argv)
# main(sys.argv)
# 添加iOS开发包到电脑上
deployer = DevDiskImageDeployer(verbose=True)

View File

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

View File

@@ -9,7 +9,7 @@ python -m nuitka Module\Main.py ^
--windows-console-mode=disable ^
--output-filename=IOSAI ^
--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=SupportFiles=SupportFiles ^
--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-----