chore: unify education management naming
This commit is contained in:
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
# 维护手册
|
# 维护手册
|
||||||
|
|
||||||
新时空教务管家长期维护时,目标是每次更新都能做到可检查、可部署、可回滚,并且不误改业务数据。
|
新时空教务管理系统长期维护时,目标是每次更新都能做到可检查、可部署、可回滚,并且不误改业务数据。
|
||||||
|
|
||||||
## 日常更新流程
|
## 日常更新流程
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
|
|
||||||
## Gitea 自动备份
|
## Gitea 自动备份
|
||||||
|
|
||||||
新时空教务管家推荐把 Gitea SSH 仓库配置为 `origin`,并用 `post-commit` hook 在每次提交后自动推送当前分支。
|
新时空教务管理系统推荐把 Gitea SSH 仓库配置为 `origin`,并用 `post-commit` hook 在每次提交后自动推送当前分支。
|
||||||
|
|
||||||
首次配置:
|
首次配置:
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ git push origin HEAD:<当前分支>
|
|||||||
## 课程小结迁移维护
|
## 课程小结迁移维护
|
||||||
|
|
||||||
- VPS 是 `classnotes.txt` 和 `学生课时账户.md` 的唯一正式写入方。
|
- VPS 是 `classnotes.txt` 和 `学生课时账户.md` 的唯一正式写入方。
|
||||||
- 本机 `com.xsk.records.sync` 常驻同步迁移后应停止,避免旧本地数据覆盖 VPS。
|
- 本机 `com.xsk.education-management.sync` 常驻同步迁移后应停止,避免旧本地数据覆盖 VPS。
|
||||||
- 本机课程小结采集脚本用 `XSK_INGEST_URL` 和 `XSK_INGEST_TOKEN` 推送批次;失败批次保存在本机 `推送失败队列/`。
|
- 本机课程小结采集脚本用 `XSK_INGEST_URL` 和 `XSK_INGEST_TOKEN` 推送批次;失败批次保存在本机 `推送失败队列/`。
|
||||||
- 管理后台的“课程小结审核”处理低置信或冲突小结;“操作记录”追踪接收、自动入账、重复、失败、审核批准和驳回。
|
- 管理后台的“课程小结审核”处理低置信或冲突小结;“操作记录”追踪接收、自动入账、重复、失败、审核批准和驳回。
|
||||||
- 历史小结导入使用 `scripts/import_course_summaries.py`,历史 `classnotes缺失.txt` 只生成审核任务,不自动扣课时。
|
- 历史小结导入使用 `scripts/import_course_summaries.py`,历史 `classnotes缺失.txt` 只生成审核任务,不自动扣课时。
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ build:
|
|||||||
docker compose build
|
docker compose build
|
||||||
|
|
||||||
up:
|
up:
|
||||||
docker compose up -d
|
docker compose up -d --remove-orphans
|
||||||
|
|
||||||
ps:
|
ps:
|
||||||
docker compose ps
|
docker compose ps
|
||||||
@@ -38,7 +38,7 @@ deploy: check smoke
|
|||||||
$(MAKE) --no-print-directory ps
|
$(MAKE) --no-print-directory ps
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
docker compose logs --tail=120 xsk-records-web
|
docker compose logs --tail=120 xsk-education-management
|
||||||
|
|
||||||
install-gitea-backup:
|
install-gitea-backup:
|
||||||
python3 scripts/install_gitea_backup_hook.py
|
python3 scripts/install_gitea_backup_hook.py
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 新时空教务管家
|
# 新时空教务管理系统
|
||||||
|
|
||||||
这是一个面向新时空教务业务的综合管理系统。迁移后 VPS 是正式业务数据主机,负责保存 `classnotes.txt`、`学生课时账户.md`、课程小结库、审核任务和操作记录;本机只保留微信群聊天记录采集/识别,并把课程小结批量推送到 VPS。
|
这是一个面向新时空教务业务的综合管理系统。迁移后 VPS 是正式业务数据主机,负责保存 `classnotes.txt`、`学生课时账户.md`、课程小结库、审核任务和操作记录;本机只保留微信群聊天记录采集/识别,并把课程小结批量推送到 VPS。
|
||||||
|
|
||||||
@@ -7,13 +7,13 @@
|
|||||||
- `app/`:FastAPI 后端和内置前端页面。
|
- `app/`:FastAPI 后端和内置前端页面。
|
||||||
- `MAINTENANCE.md`:日常检查、部署、数据保护和回滚流程。
|
- `MAINTENANCE.md`:日常检查、部署、数据保护和回滚流程。
|
||||||
- `Makefile`:常用维护命令入口。
|
- `Makefile`:常用维护命令入口。
|
||||||
- `scripts/deploy_to_vps.py`:部署教务管家服务到 VPS。
|
- `scripts/deploy_to_vps.py`:部署新时空教务管理系统到 VPS。
|
||||||
- `scripts/import_course_summaries.py`:一次性导入历史课程小结 Markdown。
|
- `scripts/import_course_summaries.py`:一次性导入历史课程小结 Markdown。
|
||||||
- `scripts/sync_to_vps.py`:旧版正式数据同步脚本,迁移后不要继续常驻运行。
|
- `scripts/sync_to_vps.py`:旧版正式数据同步脚本,迁移后不要继续常驻运行。
|
||||||
- `scripts/smoke_test.js`:轻量前端行为烟测。
|
- `scripts/smoke_test.js`:轻量前端行为烟测。
|
||||||
- `scripts/install_gitea_backup_hook.py`:安装提交后自动推送到 Gitea 的 Git hook。
|
- `scripts/install_gitea_backup_hook.py`:安装提交后自动推送到 Gitea 的 Git hook。
|
||||||
- `scripts/install_launch_agent.py`:安装 Mac 开机常驻同步任务。
|
- `scripts/install_launch_agent.py`:安装 Mac 开机常驻同步任务。
|
||||||
- `launchd/com.xsk.records.sync.plist.template`:LaunchAgent 模板。
|
- `launchd/com.xsk.education-management.sync.plist.template`:LaunchAgent 模板。
|
||||||
|
|
||||||
## 后端结构
|
## 后端结构
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
推荐先配置 SSH key。若临时使用密码,可通过环境变量传入,不要写入仓库文件。
|
推荐先配置 SSH key。若临时使用密码,可通过环境变量传入,不要写入仓库文件。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /Users/yangdawei/Desktop/新时空业务源数据/tools/xsk-records-web
|
cd /Users/yangdawei/Desktop/新时空业务源数据/tools/xsk-education-management
|
||||||
XSK_USE_SSHPASS=1 \
|
XSK_USE_SSHPASS=1 \
|
||||||
XSK_SSH_PASSWORD='填写SSH密码' \
|
XSK_SSH_PASSWORD='填写SSH密码' \
|
||||||
XSK_WEB_PASSWORD='填写网页访问密码' \
|
XSK_WEB_PASSWORD='填写网页访问密码' \
|
||||||
@@ -104,7 +104,7 @@ python3 /Users/yangdawei/Desktop/新时空业务源数据/新时空课程记录
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /root/新时空数据/app
|
cd /root/新时空数据/app
|
||||||
docker compose exec xsk-records-web python scripts/import_course_summaries.py \
|
docker compose exec xsk-education-management python scripts/import_course_summaries.py \
|
||||||
--source /data/import/课程小结采集 \
|
--source /data/import/课程小结采集 \
|
||||||
--target /data/course_summaries \
|
--target /data/course_summaries \
|
||||||
--state /data/course_summary_state.json \
|
--state /data/course_summary_state.json \
|
||||||
@@ -160,21 +160,21 @@ python3 scripts/install_launch_agent.py --use-sshpass
|
|||||||
日志位置:
|
日志位置:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
~/Library/Logs/xsk-records-web/sync.log
|
~/Library/Logs/xsk-education-management/sync.log
|
||||||
~/Library/Logs/xsk-records-web/sync.err.log
|
~/Library/Logs/xsk-education-management/sync.err.log
|
||||||
```
|
```
|
||||||
|
|
||||||
查看任务:
|
查看任务:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
launchctl list | grep com.xsk.records.sync
|
launchctl list | grep com.xsk.education-management.sync
|
||||||
```
|
```
|
||||||
|
|
||||||
卸载任务:
|
卸载任务:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
launchctl unload ~/Library/LaunchAgents/com.xsk.records.sync.plist
|
launchctl unload ~/Library/LaunchAgents/com.xsk.education-management.sync.plist
|
||||||
rm ~/Library/LaunchAgents/com.xsk.records.sync.plist
|
rm ~/Library/LaunchAgents/com.xsk.education-management.sync.plist
|
||||||
```
|
```
|
||||||
|
|
||||||
## 更换网页访问密码
|
## 更换网页访问密码
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ def is_records_authenticated(
|
|||||||
request,
|
request,
|
||||||
RECORDS_SESSION_COOKIE,
|
RECORDS_SESSION_COOKIE,
|
||||||
BASIC_AUTH_PASSWORD,
|
BASIC_AUTH_PASSWORD,
|
||||||
b"xsk-records-web-session-v1",
|
b"xsk-education-management-records-session-v1",
|
||||||
) or has_valid_basic_auth(credentials, BASIC_AUTH_PASSWORD)
|
) or has_valid_basic_auth(credentials, BASIC_AUTH_PASSWORD)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ ACCOUNTS_AUTH_PASSWORD = os.getenv("ACCOUNTS_AUTH_PASSWORD") or os.getenv("ACCOU
|
|||||||
ADMIN_AUTH_PASSWORD = os.getenv("ADMIN_AUTH_PASSWORD") or ACCOUNTS_AUTH_PASSWORD
|
ADMIN_AUTH_PASSWORD = os.getenv("ADMIN_AUTH_PASSWORD") or ACCOUNTS_AUTH_PASSWORD
|
||||||
INGEST_AUTH_TOKEN = os.getenv("INGEST_AUTH_TOKEN", "")
|
INGEST_AUTH_TOKEN = os.getenv("INGEST_AUTH_TOKEN", "")
|
||||||
|
|
||||||
RECORDS_SESSION_COOKIE = "xsk_records_session"
|
RECORDS_SESSION_COOKIE = "xsk_education_management_records_session"
|
||||||
ACCOUNTS_SESSION_COOKIE = "xsk_accounts_session"
|
ACCOUNTS_SESSION_COOKIE = "xsk_accounts_session"
|
||||||
ADMIN_SESSION_COOKIE = "xsk_admin_session"
|
ADMIN_SESSION_COOKIE = "xsk_admin_session"
|
||||||
SESSION_MAX_AGE = 60 * 60 * 24 * 30
|
SESSION_MAX_AGE = 60 * 60 * 24 * 30
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ from fastapi.responses import JSONResponse
|
|||||||
from .routers import accounts, admin, health, ingest, pages, records
|
from .routers import accounts, admin, health, ingest, pages, records
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(title="新时空教务管家", version="1.0.0")
|
app = FastAPI(title="新时空教务管理系统", version="1.0.0")
|
||||||
|
|
||||||
|
|
||||||
@app.exception_handler(ValueError)
|
@app.exception_handler(ValueError)
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ def login_page(
|
|||||||
return RedirectResponse(url=next_path, status_code=status.HTTP_303_SEE_OTHER)
|
return RedirectResponse(url=next_path, status_code=status.HTTP_303_SEE_OTHER)
|
||||||
return HTMLResponse(
|
return HTMLResponse(
|
||||||
render_login_page(
|
render_login_page(
|
||||||
title="新时空教务管家",
|
title="新时空教务管理系统",
|
||||||
action="/login",
|
action="/login",
|
||||||
next_path=next_path,
|
next_path=next_path,
|
||||||
has_error=has_error,
|
has_error=has_error,
|
||||||
@@ -260,7 +260,7 @@ async def login_submit(request: Request):
|
|||||||
response = RedirectResponse(url=next_path, status_code=status.HTTP_303_SEE_OTHER)
|
response = RedirectResponse(url=next_path, status_code=status.HTTP_303_SEE_OTHER)
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
RECORDS_SESSION_COOKIE,
|
RECORDS_SESSION_COOKIE,
|
||||||
session_token(records_password, b"xsk-records-web-session-v1"),
|
session_token(records_password, b"xsk-education-management-records-session-v1"),
|
||||||
max_age=SESSION_MAX_AGE,
|
max_age=SESSION_MAX_AGE,
|
||||||
httponly=True,
|
httponly=True,
|
||||||
samesite="lax",
|
samesite="lax",
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>新时空教务管家</title>
|
<title>新时空教务管理系统</title>
|
||||||
<link rel="stylesheet" href="/static/styles.css?v=20260613-date-time-sort" />
|
<link rel="stylesheet" href="/static/styles.css?v=20260613-date-time-sort" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
<div>
|
<div>
|
||||||
<h1>新时空教务管家</h1>
|
<h1>新时空教务管理系统</h1>
|
||||||
<p id="healthText">正在读取数据状态</p>
|
<p id="healthText">正在读取数据状态</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="top-actions">
|
<div class="top-actions">
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
services:
|
services:
|
||||||
xsk-records-web:
|
xsk-education-management:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
args:
|
args:
|
||||||
PYTHON_IMAGE: ${PYTHON_IMAGE:-python:3.12-slim}
|
PYTHON_IMAGE: ${PYTHON_IMAGE:-python:3.12-slim}
|
||||||
container_name: xsk-records-web
|
container_name: xsk-education-management
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
|||||||
+4
-4
@@ -3,20 +3,20 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>com.xsk.records.sync</string>
|
<string>com.xsk.education-management.sync</string>
|
||||||
<key>ProgramArguments</key>
|
<key>ProgramArguments</key>
|
||||||
<array>
|
<array>
|
||||||
<string>/usr/bin/python3</string>
|
<string>/usr/bin/python3</string>
|
||||||
<string>/Users/yangdawei/Desktop/新时空业务源数据/tools/xsk-records-web/scripts/sync_to_vps.py</string>
|
<string>/Users/yangdawei/Desktop/新时空业务源数据/tools/xsk-education-management/scripts/sync_to_vps.py</string>
|
||||||
</array>
|
</array>
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>KeepAlive</key>
|
<key>KeepAlive</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>StandardOutPath</key>
|
<key>StandardOutPath</key>
|
||||||
<string>/Users/yangdawei/Library/Logs/xsk-records-web/sync.log</string>
|
<string>/Users/yangdawei/Library/Logs/xsk-education-management/sync.log</string>
|
||||||
<key>StandardErrorPath</key>
|
<key>StandardErrorPath</key>
|
||||||
<string>/Users/yangdawei/Library/Logs/xsk-records-web/sync.err.log</string>
|
<string>/Users/yangdawei/Library/Logs/xsk-education-management/sync.err.log</string>
|
||||||
<key>EnvironmentVariables</key>
|
<key>EnvironmentVariables</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>PATH</key>
|
<key>PATH</key>
|
||||||
@@ -19,7 +19,7 @@ DEFAULT_IDENTITY_FILE = Path.home() / ".ssh" / "xsk_records_vps_ed25519"
|
|||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(description="部署新时空课程记录查询网页到 VPS")
|
parser = argparse.ArgumentParser(description="部署新时空教务管理系统到 VPS")
|
||||||
parser.add_argument("--remote-host", default=os.getenv("XSK_REMOTE_HOST", DEFAULT_REMOTE_HOST))
|
parser.add_argument("--remote-host", default=os.getenv("XSK_REMOTE_HOST", DEFAULT_REMOTE_HOST))
|
||||||
parser.add_argument("--remote-user", default=os.getenv("XSK_REMOTE_USER", DEFAULT_REMOTE_USER))
|
parser.add_argument("--remote-user", default=os.getenv("XSK_REMOTE_USER", DEFAULT_REMOTE_USER))
|
||||||
parser.add_argument("--remote-port", type=int, default=int(os.getenv("XSK_REMOTE_PORT", str(DEFAULT_REMOTE_PORT))))
|
parser.add_argument("--remote-port", type=int, default=int(os.getenv("XSK_REMOTE_PORT", str(DEFAULT_REMOTE_PORT))))
|
||||||
@@ -27,7 +27,7 @@ def parse_args() -> argparse.Namespace:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--identity-file",
|
"--identity-file",
|
||||||
default=os.getenv("XSK_IDENTITY_FILE", str(DEFAULT_IDENTITY_FILE) if DEFAULT_IDENTITY_FILE.exists() else ""),
|
default=os.getenv("XSK_IDENTITY_FILE", str(DEFAULT_IDENTITY_FILE) if DEFAULT_IDENTITY_FILE.exists() else ""),
|
||||||
help="SSH 私钥路径;默认使用 ~/.ssh/xsk_records_vps_ed25519(如果存在)",
|
help="SSH 私钥路径;默认兼容使用历史文件 ~/.ssh/xsk_records_vps_ed25519(如果存在)",
|
||||||
)
|
)
|
||||||
parser.add_argument("--app-port", default=os.getenv("XSK_APP_PORT", "18080"))
|
parser.add_argument("--app-port", default=os.getenv("XSK_APP_PORT", "18080"))
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -137,7 +137,7 @@ def main() -> int:
|
|||||||
run(ssh_command(args) + [f"mkdir -p {shlex.quote(app_dir)} {shlex.quote(data_dir)}"])
|
run(ssh_command(args) + [f"mkdir -p {shlex.quote(app_dir)} {shlex.quote(data_dir)}"])
|
||||||
run(rsync_command(args) + [f"{PROJECT_DIR}/", f"{base_remote(args)}:{app_dir}/"])
|
run(rsync_command(args) + [f"{PROJECT_DIR}/", f"{base_remote(args)}:{app_dir}/"])
|
||||||
run(rsync_command(args) + [str(env_file), f"{base_remote(args)}:{app_dir}/.env"])
|
run(rsync_command(args) + [str(env_file), f"{base_remote(args)}:{app_dir}/.env"])
|
||||||
run(ssh_command(args) + [f"cd {shlex.quote(app_dir)} && docker compose up -d --build"])
|
run(ssh_command(args) + [f"cd {shlex.quote(app_dir)} && docker compose up -d --build --remove-orphans"])
|
||||||
print(f"部署完成: http://{args.remote_host}:{args.app_port}/")
|
print(f"部署完成: http://{args.remote_host}:{args.app_port}/")
|
||||||
finally:
|
finally:
|
||||||
env_file.unlink(missing_ok=True)
|
env_file.unlink(missing_ok=True)
|
||||||
|
|||||||
@@ -9,13 +9,15 @@ import subprocess
|
|||||||
|
|
||||||
|
|
||||||
PROJECT_DIR = Path(__file__).resolve().parents[1]
|
PROJECT_DIR = Path(__file__).resolve().parents[1]
|
||||||
LABEL = "com.xsk.records.sync"
|
LABEL = "com.xsk.education-management.sync"
|
||||||
|
OLD_LABEL = "com.xsk.records.sync"
|
||||||
PLIST_PATH = Path.home() / "Library" / "LaunchAgents" / f"{LABEL}.plist"
|
PLIST_PATH = Path.home() / "Library" / "LaunchAgents" / f"{LABEL}.plist"
|
||||||
LOG_DIR = Path.home() / "Library" / "Logs" / "xsk-records-web"
|
OLD_PLIST_PATH = Path.home() / "Library" / "LaunchAgents" / f"{OLD_LABEL}.plist"
|
||||||
|
LOG_DIR = Path.home() / "Library" / "Logs" / "xsk-education-management"
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(description="安装新时空课程记录同步 LaunchAgent")
|
parser = argparse.ArgumentParser(description="安装新时空教务管理系统同步 LaunchAgent")
|
||||||
parser.add_argument("--use-sshpass", action="store_true", default=os.getenv("XSK_USE_SSHPASS", "") == "1")
|
parser.add_argument("--use-sshpass", action="store_true", default=os.getenv("XSK_USE_SSHPASS", "") == "1")
|
||||||
parser.add_argument("--ssh-password", default=os.getenv("XSK_SSH_PASSWORD"))
|
parser.add_argument("--ssh-password", default=os.getenv("XSK_SSH_PASSWORD"))
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
@@ -50,6 +52,8 @@ def main() -> int:
|
|||||||
with PLIST_PATH.open("wb") as handle:
|
with PLIST_PATH.open("wb") as handle:
|
||||||
plistlib.dump(plist, handle)
|
plistlib.dump(plist, handle)
|
||||||
|
|
||||||
|
subprocess.run(["launchctl", "unload", str(OLD_PLIST_PATH)], check=False, capture_output=True)
|
||||||
|
OLD_PLIST_PATH.unlink(missing_ok=True)
|
||||||
subprocess.run(["launchctl", "unload", str(PLIST_PATH)], check=False, capture_output=True)
|
subprocess.run(["launchctl", "unload", str(PLIST_PATH)], check=False, capture_output=True)
|
||||||
subprocess.run(["launchctl", "load", str(PLIST_PATH)], check=True)
|
subprocess.run(["launchctl", "load", str(PLIST_PATH)], check=True)
|
||||||
print(f"已安装并启动 LaunchAgent: {PLIST_PATH}")
|
print(f"已安装并启动 LaunchAgent: {PLIST_PATH}")
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def parse_args() -> argparse.Namespace:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--identity-file",
|
"--identity-file",
|
||||||
default=os.getenv("XSK_IDENTITY_FILE", str(DEFAULT_IDENTITY_FILE) if DEFAULT_IDENTITY_FILE.exists() else ""),
|
default=os.getenv("XSK_IDENTITY_FILE", str(DEFAULT_IDENTITY_FILE) if DEFAULT_IDENTITY_FILE.exists() else ""),
|
||||||
help="SSH 私钥路径;默认使用 ~/.ssh/xsk_records_vps_ed25519(如果存在)",
|
help="SSH 私钥路径;默认兼容使用历史文件 ~/.ssh/xsk_records_vps_ed25519(如果存在)",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--use-sshpass",
|
"--use-sshpass",
|
||||||
@@ -161,7 +161,7 @@ def current_signatures(local_dir: Path) -> dict[str, tuple[int, int]]:
|
|||||||
|
|
||||||
|
|
||||||
def watch(config: Config) -> None:
|
def watch(config: Config) -> None:
|
||||||
log("启动课程记录同步监听")
|
log("启动新时空教务管理系统数据同步监听")
|
||||||
signatures: dict[str, tuple[int, int]] = {}
|
signatures: dict[str, tuple[int, int]] = {}
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user