Skip to content

🎨 重构部分代码 #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<<<<<<< HEAD
container/.ssl/
container/bmclapi/
container/cache/
container/config/
container/__pycache__/
.vscode/
.lh/
test/
=======
config/config.properties
__pycache__
.venv
>>>>>>> 1821e9a699e53437109088d3d8cf4bb4a1bf9a50
logs
config/config.yaml
bmclapi
bmclapi_dashboard
cache
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ WORKDIR /python-openbmclapi
ADD . .

RUN pip install -r requirements.txt --no-deps
VOLUME /python-openbmclapi/cache
ENV web_port=8080
VOLUME /python-openbmclapi/bmclapi
ENV web_port=8800
EXPOSE $web_port
CMD ["python", "./container/main.py"]
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -20,6 +20,9 @@

🎉 __*新增功能!*__基于 Echart 的 OpenBMCLAPI 仪表盘(Dashboard)。

🎉 __*新增功能!*__基于 loguru 的**日志器**。


</div>

# 简介
@@ -53,7 +56,7 @@
python ./container/main.py
```

4. 在 `config.properties` 中,填写你的 `cluster.id`(即 `CLUSTER_ID`)和 `cluster.secret`(即 `CLUSTER_SECRET`)。
4. 在 `config.yaml` 中,填写你的 `cluster_id`(即 `CLUSTER_ID`)和 `cluster_secret`(即 `CLUSTER_SECRET`)。

5. 重新启动程序。

@@ -69,8 +72,8 @@

```sh
docker run -d \
-v ${/data/python-openbmclapi}:/python-openbmclapi/container/cache \
-v ${/path/to/your/config}:/python-openbmclapi/config/config.properties \
-v ${/data/python-openbmclapi}:/python-openbmclapi/bmclapi \
-v ${/path/to/your/config}:/python-openbmclapi/config/config.yaml \
-p ${web.port}:${web.port} \
--restart always \
--name python-openbmclapi \
@@ -81,9 +84,23 @@

`web.port` - 对外开放的端口。

`/path/to/your/config` - 配置文件(你需要从此仓库中下载 `config/config.properties.example` 并重命名为 `config.properties` 来进行配置)的存放路径。
`/path/to/your/config` - 配置文件(你需要从此仓库中下载 `config/config.yaml.example` 并重命名为 `config.yaml` 来进行配置)的存放路径。

`/data/python-openbmclapi` - `bmclapi` 文件夹(即缓存 `cache` 文件夹)挂载的路径。

# 配置文件

```yaml
byoc: ''
cluster_id: ''
cluster_secret: ''
download_threads: 64
timeout: 30
web_host: ''
web_port: 8800
web_publicport: 8800

`/data/python-openbmclapi` - `cache` 文件夹挂载的路径。
```

# 鸣谢

8 changes: 0 additions & 8 deletions config/config.properties.example

This file was deleted.

8 changes: 8 additions & 0 deletions config/config.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
byoc: false
cluster_id: ''
cluster_secret: ''
download_threads: 64
timeout: 30
web_host: ''
web_port: 8800
web_publicport: 8800
2 changes: 1 addition & 1 deletion container/bmclapi_dashboard/index.html
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8"/>
<title>OPENBMCLAPI - TTB Network</title>
<title>Python OpenBMCLAPI Dashboard</title>
</head>
<body>
</body>
6 changes: 2 additions & 4 deletions container/bmclapi_dashboard/static/js/index.js
Original file line number Diff line number Diff line change
@@ -77,6 +77,7 @@ const calc_more_bytes = (...values) => {
".container .vcmp-status-bar:hover":"color: #8dcffc;",
".container .vcmp-status-online":"width: 12px;height: 12px;border-radius: 50%;background-color: #0FC6C2;margin: 8px;",
".container .vcmp-status-offline":"width: 12px;height: 12px;border-radius: 50%;background-color: rgba(0, 0, 0, 0.5);margin: 8px;margin-right: 0;",
"h1, h2, h3":"font-family: Microsoft Yahei UI"
}
set_styles(default_styles)
Extendi18nSets("zh_cn", {
@@ -266,10 +267,7 @@ const calc_more_bytes = (...values) => {
ExtendFlex().append(
ExtendFlex().css("domain", "extend-flex-auto").append(
ExtendElement("span").append(
ExtendElement("h2").text("Python OpenBMCLAPI Dashboard").valueOf(),
ExtendElement("span").text("Built by ").append(
ExtendElement("a").text("TTB Network").setAttr("href", "https://github.com/TTB-Network/python-openbmclapi/").valueOf()
).id("dashboard-geo").valueOf()
ExtendElement("h3").text("Python OpenBMCLAPI Dashboard").valueOf(),
).valueOf(),
).valueOf(),
).css("header-bar").valueOf(),
35 changes: 19 additions & 16 deletions container/cluster.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import asyncio
from dataclasses import dataclass
import glob
import hashlib
import hmac
import io
@@ -18,6 +17,8 @@
import utils
import stats
import web
from logger import logger
from tqdm import tqdm

PY_VERSION = "1.0.0"
VERSION = "1.9.7"
@@ -60,7 +61,7 @@ async def fetchToken(self):
Timer.delay(self.fetchToken, delay=float(content['ttl']) / 1000.0 - 600)

except aiohttp.ClientError as e:
print(f"Error fetching token: {e}")
logger.error(f"Error fetching token: {e}.")
async def getToken(self) -> str:
if not self.token:
await self.fetchToken()
@@ -120,29 +121,30 @@ async def download(self, session: aiohttp.ClientSession):
self.download_bytes.add(-size)
await self.files.put(file)
async def check_file(self):
print("Requesting files...")
logger.info("Requesting filelist...")
filelist = await self.get_file_list()
filesize = sum((file.size for file in filelist))
total = len(filelist)
byte = 0
miss = []
pbar = tqdm(total=total, unit=' file(s)', unit_scale=True)
pbar.set_description("Checking files")
for i, file in enumerate(filelist):
filepath = str(self.dir) + f"/{file.hash[:2]}/{file.hash}"
if not os.path.exists(filepath) or os.path.getsize(filepath) != file.size:
miss.append(file)
...
await asyncio.sleep(0)
byte += file.size
b = utils.calc_more_bytes(byte, filesize)
print(f"<<<flush>>>Check file {i}/{total} ({b[0]}/{b[1]})")
pbar.update(1)
if not miss:
print(f"<<<flush>>>Checked all files!")
logger.info("Checked all files!")
await self.start_service()
return
filelist = miss
filesize = sum((file.size for file in filelist))
total = len(filelist)
print(f"<<<flush>>>Missing files: {total}({utils.calc_bytes(filesize)})")
logger.info(f"Missing files: {total}({utils.calc_bytes(filesize)}).")
for file in filelist:
await self.files.put(file)
self.download_bytes = utils.Progress(5, filesize)
@@ -154,16 +156,17 @@ async def check_file(self):
"User-Agent": UA,
"Authorization": f"Bearer {await token.getToken()}"
}), )))
pbar = tqdm(total=total, unit=' file(s)', unit_scale=True)
pre = 0
while any([not timer.called for timer in timers]):
b = utils.calc_more_bytes(self.download_bytes.get_cur(), filesize)
bits = self.download_bytes.get_cur_speeds() or [0]
minbit = min(bits)
bit = utils.calc_more_bit(minbit, bits[-1], max(bits))
eta = self.download_bytes.get_eta()
print(f"<<<flush>>>Downloading files... {self.download_files.get_cur()}/{total} {b[0]}/{b[1]}, eta: {utils.format_time(eta if eta != -1 else None)}, total: {utils.format_time(self.download_bytes.get_total())}, Min: {bit[0]}, Cur: {bit[2]}, Max: {bit[1]}, Files: {self.download_files.get_cur_speed()}/s")
pbar.set_description(f"Downloading files | Curent speed: {bit[2]}")
await asyncio.sleep(1)
for timer in timers:
del timer
pbar.update(self.download_files.get_cur() - pre)
pre = self.download_files.get_cur()
await self.start_service()
async def start_service(self):
tokens = await token.getToken()
@@ -185,11 +188,11 @@ async def enable(self):
if not web.get_ssl() and not (Path(".ssl/cert.pem").exists() and Path(".ssl/key.pem").exists()):
await self.emit("request-cert")
self.cur_counter = stats.Counters()
print("Connected Main Server.")
logger.info("Connected to the Main Server.")
async def message(self, type, data):
if type == "request-cert":
cert = data[1]
print("Requested cert!")
logger.info("Requested cert!")
cert_file = Path(".ssl/cert.pem")
key_file = Path(".ssl/key.pem")
for file in (cert_file, key_file):
@@ -206,9 +209,9 @@ async def message(self, type, data):
self.keepalive.block()
self.keepalive = Timer.delay(self.keepaliveTimer, (), 5)
if len(data) == 2 and data[1] == True:
print("Checked! Can service")
logger.info("Checked! Starting the service")
return
print("Error:" + data[0]['message'])
logger.error("Error:" + data[0]['message'])
Timer.delay(self.enable)
elif type == "keep-alive":
COUNTER.hit -= self.cur_counter.hit
@@ -234,7 +237,7 @@ async def get_file_list(self):
"cache": ""
}) as req:
req.raise_for_status()
print("Requested files")
logger.info("Requested filelist.")

parser = avro_io.DatumReader(schema.parse(
'''
81 changes: 29 additions & 52 deletions container/config.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,39 @@
from pathlib import Path
import yaml
from typing import Union

class CFG:
def __init__(self, path: str) -> None:
self.file = Path(path)
self.cfg = {}
if self.file.exists():
self.load()

class Property:
def __init__(self, config: str, exists_ok: bool = True) -> None:
self.file = Path(config)
self.properties = {}
if self.file.exists():
self.parse_file()
self.exists_ok = exists_ok

def parse_file(self):
def load(self):
with open(self.file, "r", encoding="utf-8") as f:
for line in f.readlines():
line = line.strip()
if not line or line.startswith('#'):
continue
key, value = line.split('=', 1)
value = value.strip('"')
self.properties[key] = value
def set(self, key, value):
self.properties[key] = str(value)
self.cfg = yaml.load(f.read(), Loader=yaml.FullLoader)

def get(self, key, default=None):
val = self.properties.get(key, None)
if not self.exists_ok and val is None:
self.set(key, default or "")
self.save()
return val or str(default)
def getBoolean(self, key, def_: bool = False):
val = self.get(key, def_)
return val.lower() in ("true", "1", "t", "yes", "y")
def getInteger(self, key, def_: int = 0):
val = self.get(key, def_)
return int(val) if val.isnumeric() else def_
def __getitem__(self, key):
return self.properties.get(key)
def __setitem__(self, key, value):
self.set(key, value)

def save(self):
sorted_dict = sorted(self.properties.items())
self.file.parent.mkdir(exist_ok=True, parents=True)
with open(self.file, 'w', encoding="utf-8") as file:
for key, value in sorted_dict:
if "\n" in value:
value = f'"{value}"'
file.write(f"{key}={value}\n")
def get(self, key, default_):
value = self.cfg.get(key, default_)
if value is None:
self.write(key, default_)
return value

Config: Property = Property("./config/config.properties", False)
def write(self, key, value):
self.cfg[key] = value
with open(self.file, "w", encoding="utf-8") as f:
yaml.dump(data=self.cfg, stream=f, allow_unicode=True)

CLUSTER_ID = Config.get("cluster.id", None) or ""
CLUSTER_SECRET = Config.get("cluster.secret", None) or ""
MAX_DOWNLOAD = Config.getInteger("download.threads", 64)
PORT = Config.getInteger("web.port", 8800)
PUBLICPORT = Config.getInteger("web.publicport", 8800)
PUBLICHOST = Config.get("web.host", "")
BYOC = False
TIMEOUT = 30
Config: CFG = CFG("./config/config.yaml")

CLUSTER_ID: str = Config.get("cluster_id", "")
CLUSTER_SECRET: str = Config.get("cluster_secret", "")
MAX_DOWNLOAD: int = Config.get("download_threads", 64)
PORT: int = Config.get("web_port", 8800)
PUBLICPORT: int = Config.get("web_publicport", 8800)
PUBLICHOST: int = Config.get("web_host", "")
BYOC: bool = Config.get("byoc", False)
TIMEOUT: int = Config.get("timeout", 30)
MIN_RATE_TIMESTAMP = 1000
MIN_RATE = 500
REQUEST_BUFFER = 1024 * 8
4 changes: 4 additions & 0 deletions container/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from loguru import logger
from pathlib import Path

logger.add(Path("./logs/{time}.log"), rotation="3 hours")
5 changes: 3 additions & 2 deletions container/timer.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
import inspect
import time
import traceback
from logger import logger


class Task:
@@ -44,15 +45,15 @@ def block(self):
self.blocked = True
async def callback_error(self):
if not self.error:
print(traceback.format_exc())
logger.debug(traceback.format_exc())
return
try:
if inspect.iscoroutinefunction(self.error):
await self.error()
else:
self.error()
except:
print(traceback.format_exc())
logger.debug(traceback.format_exc())
class TimerManager:
def delay(self, target, args = (), delay: float = 0, callback = None):
task = Task(target=target, args=args, delay=delay, back=callback)
13 changes: 7 additions & 6 deletions container/web.py
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
import config
import filetype
import urllib.parse as urlparse
from logger import logger


class Route:
@@ -195,7 +196,7 @@ async def handle(self, request: 'Request'):
}))
def mount(self, router: Router):
self._routes.append(router)
print(f"Serve router at: {router.prefix}")
logger.info(f"Serve router at: {router.prefix}")
def mount_resource(self, resource: Resource):
self._resources.append(resource)

@@ -492,7 +493,7 @@ async def handle(data, client: Client):
async for resp in app.handle(request):
await resp(request, client)
await request.skip()
print(request.get_request_time(), "|", request.method.ljust(6), request.get_status_code(), "|", request.get_ip().ljust(16), "|", request.url, request.get_user_agent())
logger.info(request.get_request_time(), "|", request.method.ljust(6), request.get_status_code(), "|", request.get_ip().ljust(16), "|", request.url, request.get_user_agent())
except TimeoutError:
...
except:
@@ -542,19 +543,19 @@ async def _handle(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
traceback.print_exc()
async def main():
global cert, server
print(f"Loading...")
logger.info(f"Loading...")
load_cert()
import cluster
await cluster.init()
while 1:
while True:
try:
server = await asyncio.start_server(_handle, host='0.0.0.0', port=config.PORT, ssl=cert)
print(f"Server listen on {config.PORT}{' with ssl' if cert else ''}!")
logger.info(f"Server listening on {config.PORT}{' with ssl' if cert else ''}!")
await server.serve_forever()
except:
if server:
server.close()
traceback.print_exc()
logger.error(traceback.format_exc())

@app.get("/favicon.ico")
async def _():
Binary file modified requirements.txt
Binary file not shown.