Skip to content

Commit

Permalink
0.36.15:
Browse files Browse the repository at this point in the history
    1. 实现CLI命令行工具 alist-cli,可以快速登录、列出文件
    2. 细节日志优化
    3. 全局超时时间增加到30秒
    4. 修复AlistPath.__repl__ 在相对目录时报错的问题。
    5. 在pyproject.toml中为alist-cli提供命令行入口。
    6. 实现更多命令行工具,上传、下载、删除、创建目录, 打印文本文件。
    7. 在README中添加CLI命令行工具的使用说明。
    8. 对保存在本地的配置文件进行加密存储。
    9. 添加CLI命令 - version, server-version
    10. 解决新版本的alist中 Client.service_version 返回Beta版本的问题。
    11. init_alist.sh 现在默认安装3.27.2版本。
  • Loading branch information
lee-cq committed Sep 16, 2024
1 parent 621463a commit cefa895
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 46 deletions.
7 changes: 5 additions & 2 deletions alist_sdk/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,12 +488,15 @@ async def admin_setting_list(self, group: int = None):
return locals(), await self.get("/api/admin/setting/list", params=query)

@cached_property
async def service_version(self) -> tuple:
async def service_version(self) -> tuple | str:
"""返回服务器版本元组 (int, int, int)"""
settings: list[Setting] = (await self.admin_setting_list(group=1)).data
for s in settings:
if s.key == "version":
return tuple(map(int, s.value.strip("v").split(".", 2)))
v = s.value.strip("v")
if "beta" in s.value:
return v
return tuple(map(int, v.split(".", 2)))
raise ValueError("无法获取服务端版本")


Expand Down
7 changes: 5 additions & 2 deletions alist_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,12 +505,15 @@ def admin_setting_list(self, group: int = None):
return locals(), self.get("/api/admin/setting/list", params=query)

@cached_property
def service_version(self) -> tuple:
def service_version(self) -> tuple | str:
"""返回服务器版本元组 (int, int, int)"""
settings: list[Setting] = self.admin_setting_list(group=1).data
for s in settings:
if s.key == "version":
return tuple(map(int, s.value.strip("v").split(".", 2)))
v = s.value.strip("v")
if "beta" in s.value:
return v
return tuple(map(int, v.split(".", 2)))
raise ValueError("无法获取服务端版本")


Expand Down
32 changes: 32 additions & 0 deletions alist_sdk/cmd/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""
import typer

from alist_sdk.cmd.base import cnf
from alist_sdk.cmd.fs import fs
from alist_sdk.cmd.admin import admin
from alist_sdk.cmd.auth import auth
Expand All @@ -17,3 +18,34 @@
app.add_typer(auth, name="auth")
app.add_typer(fs, name="fs")
app.add_typer(admin, name="admin")


@app.command("version")
def version():
"""
Show the version of alist-sdk.
"""
from alist_sdk import __version__

typer.echo(f"alist-sdk version: {__version__}")


@app.command("server-version")
def server_version(server: str = None):
"""
Show the version of alist-server.
"""
from alist_sdk import Client

if server and server not in cnf.auth_data:
return typer.echo("尚未登录到该服务器,请先登录 [cmd: alist-cli auth login].")

if not server:
server = cnf.auth_data.keys()
else:
server = [server]

typer.echo("Server Version:")
for s in server:
client = cnf.get_client(s)
typer.echo(f"{s}: {client.service_version}")
4 changes: 2 additions & 2 deletions alist_sdk/cmd/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def logout(host: str):
def list_auth(token: bool = typer.Option(False, "--token", "-t", help="显示token")):
"""列出所有已登录的host"""
if token:
las = "\n".join(f"{host}: {_t}" for host, _t in cnf.auth.items())
las = "\n".join(f"{host}: {_t}" for host, _t in cnf.auth_data.items())
else:
las = "\n".join(host for host in cnf.auth.keys())
las = "\n".join(host for host in cnf.auth_data.keys())
if not las:
typer.echo("未登录到任何AlistServer")
return
Expand Down
69 changes: 34 additions & 35 deletions alist_sdk/cmd/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@Author : LeeCQ
@Date-Time : 2024/9/15 22:47
"""
import hashlib
import time
from pathlib import Path

Expand All @@ -16,6 +17,7 @@
text_types = "txt,htm,html,xml,java,properties,sql,js,md,json,conf,ini,vue,php,py,bat,gitignore,yml,go,sh,c,cpp,h,hpp,tsx,vtt,srt,ass,rs,lrc,yaml"

CMD_BASE_PATH = ""
CONFIG_FILE_PATH = Path.home().joinpath(".config", "alist_cli.json")


def beautify_size(byte_size: float):
Expand All @@ -31,25 +33,14 @@ def beautify_size(byte_size: float):
return f"{byte_size:.2f}GB"


def xor_encrypt(s: str, key: str) -> str:
def xor(s: str, key: str) -> str:
"""
字符串的亦或加密
:param s: 待加密的字符串
:param key: 加密密钥
:return: 加密后的字符串
"""
return "".join(
[chr(ord(c) ^ ord(k)) for c, k in zip(s, key * (len(s) // len(key) + 1))]
)


def xor_decrypt(s: str, key: str) -> str:
"""
字符串的亦或解密
:param s: 待解密的字符串
:param key: 加密密钥
:return: 解密后的字符串
字符串的亦或加密/解密
:param s: 待加密/解密的字符串
:param key: 加密/解密密钥
:return: 加密/解密后的字符串
"""
key = hashlib.md5(key.encode("utf-8")).hexdigest()
return "".join(
[chr(ord(c) ^ ord(k)) for c, k in zip(s, key * (len(s) // len(key) + 1))]
)
Expand All @@ -64,33 +55,41 @@ class Auth(BaseModel):


class CmdConfig(BaseModel):
auth: dict[str, Auth] = {}
auth_data: dict[str, Auth] = {}
base_path: dict[str, str] = {}

@classmethod
def load_config(cls):
"""加载配置"""

config_file = Path.home().joinpath(".config", "alist_cli.json")
if not config_file.exists():
if not CONFIG_FILE_PATH.exists():
return cls()
try:
typer.echo(f"load config from {CONFIG_FILE_PATH}")
return cls.model_validate_json(
xor_decrypt(config_file.read_text(), str(config_file))
xor(
CONFIG_FILE_PATH.read_bytes().decode("utf-8"),
CONFIG_FILE_PATH.as_posix(),
)
)
except Exception as e:
typer.echo(f"load config failed, {e}")
wait_info = f"Do you want to remove the config file[{config_file}]? (y/n)"
wait_info = (
f"Do you want to remove the config file[{CONFIG_FILE_PATH}]? (y/n)"
)
if input(wait_info).lower() == "y":
config_file.unlink()
typer.echo(f"remove config file success, {config_file}")
CONFIG_FILE_PATH.unlink()
typer.echo(f"remove config file success, {CONFIG_FILE_PATH}")
exit(1)

def save_config(self):
"""保存配置"""
Path.home().joinpath(".config").mkdir(parents=True, exist_ok=True)
config_file = Path.home().joinpath(".config", "alist_cli.json")
config_file.write_text(xor_encrypt(self.model_dump_json(), str(config_file)))
CONFIG_FILE_PATH.parent.mkdir(parents=True, exist_ok=True)

print(f"save config to {self.model_dump_json()}")
CONFIG_FILE_PATH.write_bytes(
xor(self.model_dump_json(), CONFIG_FILE_PATH.as_posix()).encode("utf-8"),
)

def add_auth(
self,
Expand All @@ -111,33 +110,33 @@ def add_auth(
typer.echo(f"login failed, {e}")
return

self.auth[host] = Auth(
self.auth_data[host] = Auth(
host=host,
token=token,
username=username,
password=password,
last_login=int(time.time()),
)
self.save_config()
typer.echo(f"login success, token: {self.auth[host].token}")
typer.echo(f"login success, token: {self.auth_data[host].token}")

def remove_auth(self, host: str):
host = host.strip("/")
if host not in self.auth:
if host not in self.auth_data:
typer.echo(f"host {host} not found in auth data")
return
del self.auth[host]
del self.auth_data[host]
self.save_config()
typer.echo(f"logout success, host: {host}")

def get_client(self, host: str):
host = host.strip("/")
if host not in self.auth:
if host not in self.auth_data:
raise ValueError(f"host {host} not found in auth data")
t_info = self.auth[host]
t_info = self.auth_data[host]
if int(time.time()) - t_info.last_login > 3600 * 24:
self.add_auth(host, t_info.username, t_info.password, t_info.token)
return login_server(host, token=self.auth[host])
self.add_auth(host, t_info.username, t_info.password)
return login_server(host, token=self.auth_data[host].token)

def set_base_path(self, base_path: str, name: str = ""):
self.base_path[name] = base_path
Expand Down
4 changes: 4 additions & 0 deletions alist_sdk/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@
5. 在pyproject.toml中为alist-cli提供命令行入口。
6. 实现更多命令行工具,上传、下载、删除、创建目录, 打印文本文件。
7. 在README中添加CLI命令行工具的使用说明。
8. 对保存在本地的配置文件进行加密存储。
9. 添加CLI命令 - version, server-version
10. 解决新版本的alist中 Client.service_version 返回Beta版本的问题。
11. init_alist.sh 现在默认安装3.27.2版本。
"""

__version__ = "0.37.15"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ authors = [{ name = "LeeCQ", email = "lcq@leecq.cn" }]
description = "Alist API 简单封装."
readme = "README.md"
requires-python = ">=3.10"
dependencies = ["httpx>=0.25.1", "pydantic>=2.5.1"]
dependencies = ["httpx>=0.25.1", "pydantic>=2.5.1", "typer>=0.12.5"]
dynamic = ["version"]

classifiers = [
Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
build
twine
pytest~=7.4.3
pytest
3 changes: 2 additions & 1 deletion tests/init_alist.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ cd "$(dirname "$0")" || exit
mkdir -p alist
cd alist || exit

alist_version=${ALIST_VERSION:-"3.30.0"}
alist_version=${ALIST_VERSION:-"3.37.2"}

if [ ! -f alist ]; then
rm -rf alist-linux-amd64.tar.gz
echo "Install Alist Version: ${alist_version}."
wget -q "https://github.com/alist-org/alist/releases/download/v${alist_version}/alist-linux-amd64.tar.gz"
tar xzvf alist-linux-amd64.tar.gz
Expand Down
6 changes: 4 additions & 2 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,8 @@ def test_admin_meta_list(self):
assert res.code == 200

def test_server_version(self):
assert self.client.service_version[0] == 3
v = self.client.service_version
assert "beta" in v or v[0] == 3

def test_login_user(self):
assert self.client.login_username == "admin"
Expand All @@ -408,7 +409,8 @@ def test_async_client(self):
assert isinstance(self.client, AsyncClient)

def test_server_version(self):
assert asyncio.run(self.client.service_version)[0] == 3
v = asyncio.run(self.client.service_version)
assert "beta" in v or v[0] == 3

def test_login_user(self):
assert asyncio.run(self.client.login_username) == "admin"

0 comments on commit cefa895

Please sign in to comment.