feat: 重构

This commit is contained in:
chenxiangtong
2026-05-28 17:37:57 +08:00
parent 88a0c45f5b
commit 3ff2454c63
26 changed files with 1248 additions and 2055 deletions

BIN
backend/data.db Normal file

Binary file not shown.

View File

@@ -1,14 +1,36 @@
import json
import os
from contextlib import asynccontextmanager
import uvicorn
from fastapi import FastAPI, HTTPException, Query
from peewee import SqliteDatabase, Model, CharField, TextField
from fastapi import FastAPI
db_path = os.path.join(os.path.dirname(__file__), "data.db")
db = SqliteDatabase(db_path)
from modules import example
from modules import voxy_import
app = FastAPI(title="ARinera Minecraft TUI Backend")
class BaseModel(Model):
class Meta:
database = db
app.include_router(example.router, prefix="/api/v1")
app.include_router(voxy_import.router, prefix="/api/v1")
class Tool(BaseModel):
code = CharField(max_length=4, unique=True)
step = TextField()
desp = TextField(default="")
@asynccontextmanager
async def lifespan(app: FastAPI):
db.connect()
db.create_tables([Tool])
yield
if not db.is_closed():
db.close()
app = FastAPI(title="ARinera Minecraft TUI Backend", lifespan=lifespan)
@app.get("/health")
@@ -16,6 +38,16 @@ async def health():
return {"status": "ok"}
@app.get("/tools")
async def get_tools(
code: str = Query(..., min_length=4, max_length=4, pattern=r"^\d{4}$"),
):
tool = Tool.get_or_none(Tool.code == code)
if tool is None:
raise HTTPException(status_code=404, detail=f"code {code} not found")
return json.loads(tool.step)
def main():
uvicorn.run("main:app", reload=True, port=3131)

165
backend/manage.py Normal file
View File

@@ -0,0 +1,165 @@
"""AMT 数据库管理脚本 - 交互式管理 Tool 表"""
import json
import os
import sys
from peewee import SqliteDatabase, Model, CharField, TextField
db_path = os.path.join(os.path.dirname(__file__), "data.db")
db = SqliteDatabase(db_path)
class BaseModel(Model):
class Meta:
database = db
class Tool(BaseModel):
code = CharField(max_length=4, unique=True)
step = TextField()
desp = TextField(default="")
def init_db():
db.connect()
db.create_tables([Tool])
def list_tools():
tools = Tool.select().order_by(Tool.code)
if not tools:
print("\n (空)")
return
print()
for t in tools:
print(f" [{t.code}] {t.desp or '(无描述)'}")
def show_tool():
code = input("输入 code: ").strip()
tool = Tool.get_or_none(Tool.code == code)
if tool is None:
print(f" code {code} 不存在")
return
print(f"\n code: {tool.code}")
print(f" desp: {tool.desp or '(无描述)'}")
print(f" step:")
print(json.dumps(json.loads(tool.step), indent=2, ensure_ascii=False))
def add_tool():
code = input("输入 code (4位数字): ").strip()
if len(code) != 4 or not code.isdigit():
print(" code 必须是4位数字")
return
if Tool.get_or_none(Tool.code == code):
print(f" code {code} 已存在,请用编辑功能修改")
return
desp = input("输入描述: ").strip()
print("输入 step JSON (输入空行结束):")
lines = []
while True:
line = input()
if line == "":
break
lines.append(line)
raw = "\n".join(lines)
try:
parsed = json.loads(raw)
except json.JSONDecodeError as e:
print(f" JSON 解析失败: {e}")
return
Tool.create(code=code, step=json.dumps(parsed, ensure_ascii=False), desp=desp)
print(f" 已添加 [{code}]")
def edit_tool():
code = input("输入要编辑的 code: ").strip()
tool = Tool.get_or_none(Tool.code == code)
if tool is None:
print(f" code {code} 不存在")
return
print(f" 当前描述: {tool.desp or '(无描述)'}")
desp = input("新描述 (回车跳过): ").strip()
if desp:
tool.desp = desp
print(f" 当前 step:")
print(json.dumps(json.loads(tool.step), indent=2, ensure_ascii=False))
choice = input("修改 step? (y/N): ").strip().lower()
if choice == "y":
print("输入新的 step JSON (输入空行结束):")
lines = []
while True:
line = input()
if line == "":
break
lines.append(line)
raw = "\n".join(lines)
try:
parsed = json.loads(raw)
except json.JSONDecodeError as e:
print(f" JSON 解析失败: {e}")
return
tool.step = json.dumps(parsed, ensure_ascii=False)
tool.save()
print(f" 已更新 [{code}]")
def delete_tool():
code = input("输入要删除的 code: ").strip()
tool = Tool.get_or_none(Tool.code == code)
if tool is None:
print(f" code {code} 不存在")
return
print(f" [{tool.code}] {tool.desp or '(无描述)'}")
confirm = input("确认删除? (y/N): ").strip().lower()
if confirm == "y":
tool.delete_instance()
print(f" 已删除 [{code}]")
else:
print(" 已取消")
MENU = {
"1": ("列出所有", list_tools),
"2": ("查看详情", show_tool),
"3": ("添加", add_tool),
"4": ("编辑", edit_tool),
"5": ("删除", delete_tool),
}
def main():
init_db()
print("=== AMT 数据库管理 ===")
while True:
print()
for k, (label, _) in MENU.items():
print(f" {k}. {label}")
print(" q. 退出")
choice = input("\n> ").strip().lower()
if choice == "q":
break
action = MENU.get(choice)
if action:
try:
action[1]()
except (KeyboardInterrupt, EOFError):
print()
else:
print(" 无效选项")
db.close()
if __name__ == "__main__":
main()

View File

@@ -1,18 +0,0 @@
from fastapi import APIRouter
router = APIRouter(prefix="/example", tags=["example"])
@router.get("/")
async def list_items():
return {"items": ["a", "b", "c"]}
@router.get("/{item_id}")
async def get_item(item_id: int):
return {"id": item_id, "name": f"item_{item_id}"}
@router.post("/")
async def create_item():
return {"status": "created"}

View File

@@ -1,12 +0,0 @@
from fastapi import APIRouter
router = APIRouter(prefix="/voxy_import", tags=["voxy_import"])
@router.get("/")
async def list_urls():
return {
"default": "https://dav.arinera.fun/voxy.zip",
"cf": "https://oss.arinera.space/voxy.zip",
"test": "http://local.arinera.fun:9000/oss/voxy.zip",
}

View File

@@ -6,6 +6,6 @@ readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.136.3",
"httpx>=0.28.1",
"peewee>=4.0.6",
"uvicorn>=0.48.0",
]

52
backend/uv.lock generated
View File

@@ -1,5 +1,5 @@
version = 1
revision = 3
revision = 2
requires-python = ">=3.12"
[[package]]
@@ -8,14 +8,14 @@ version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "fastapi" },
{ name = "httpx" },
{ name = "peewee" },
{ name = "uvicorn" },
]
[package.metadata]
requires-dist = [
{ name = "fastapi", specifier = ">=0.136.3" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "peewee", specifier = ">=4.0.6" },
{ name = "uvicorn", specifier = ">=0.48.0" },
]
@@ -50,15 +50,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
]
[[package]]
name = "certifi"
version = "2026.5.20"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
]
[[package]]
name = "click"
version = "8.4.1"
@@ -105,34 +96,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
[[package]]
name = "httpcore"
version = "1.0.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[[package]]
name = "idna"
version = "3.16"
@@ -142,6 +105,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" },
]
[[package]]
name = "peewee"
version = "4.0.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9f/09/a3b2a32ce498f405dce4320267e99b1b076c1ea39ad01151a353bc7f81d7/peewee-4.0.6.tar.gz", hash = "sha256:ea2f78f24ff9e3660281dc5b0be8bc00d9a9514bdc40c98e416fcd042b66ac6a", size = 724591, upload-time = "2026-05-20T13:18:17.26Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/6a/e1455b94ee48f5666f2e7831b6247098794bfe9747da457111be4d0bea10/peewee-4.0.6-py3-none-any.whl", hash = "sha256:5fa665913c410f0b5faef1469ed0aa9eceb9fef262665ebbb6f29408f826eeeb", size = 146222, upload-time = "2026-05-20T13:18:15.694Z" },
]
[[package]]
name = "pydantic"
version = "2.13.4"