From ba74a9cce8380ae7deeaf0538ffe4d1abf1e35b1 Mon Sep 17 00:00:00 2001 From: dekun Date: Sat, 17 Oct 2026 20:59:52 +0800 Subject: [PATCH] Improve machines page: online status, formatted time, delete unbound placeholders. Co-authored-by: Cursor --- app/db.py | 18 ++++++++++++ app/routes.py | 31 +++++++++++---------- app/templates/machines.html | 55 +++++++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 26 deletions(-) diff --git a/app/db.py b/app/db.py index 0ccce36..2b7a24f 100644 --- a/app/db.py +++ b/app/db.py @@ -493,6 +493,24 @@ def delete_account(conn: sqlite3.Connection, account_id: int) -> None: conn.execute("DELETE FROM accounts WHERE id=?", (account_id,)) +def delete_machine(conn: sqlite3.Connection, machine_id: int) -> None: + """删除机器。在线中的不允许删;相关激活/成交仅解除 machine 引用。""" + m = conn.execute("SELECT * FROM machines WHERE id=?", (machine_id,)).fetchone() + if not m: + raise ValueError("机器不存在") + last = m["last_seen_at"] + online = False + try: + online = bool(last and (now() - float(last)) <= ONLINE_SECONDS) + except (TypeError, ValueError): + online = False + if online: + raise ValueError("机器当前在线,请先关闭客户端后再删") + conn.execute("UPDATE activations SET machine_id=NULL WHERE machine_id=?", (machine_id,)) + conn.execute("UPDATE trades SET machine_id=NULL WHERE machine_id=?", (machine_id,)) + conn.execute("DELETE FROM machines WHERE id=?", (machine_id,)) + + def create_user( conn: sqlite3.Connection, username: str, diff --git a/app/routes.py b/app/routes.py index 4ff9170..37047a9 100644 --- a/app/routes.py +++ b/app/routes.py @@ -321,22 +321,12 @@ def register_routes(app: Flask) -> None: @app.get("/machines") @login_required def machines_page(): - # 机器列表:管理员全看;普通用户只看与自己账户激活相关的机器 u = current_user() with database.get_db() as conn: - if u["role"] == "admin": - items = database.list_machines(conn) - else: - cur = conn.execute( - """ - SELECT DISTINCT m.* FROM machines m - JOIN activations act ON act.machine_id = m.id - WHERE act.account_id=? - ORDER BY m.id DESC - """, - (u.get("account_id"),), - ) - items = [database.row_to_dict(r) for r in cur.fetchall()] + items = database.list_machines_for_user(conn, u) + for m in items: + m["last_seen_fmt"] = _fmt_ts(m.get("last_seen_at")) + m["bound"] = bool(m.get("device_fingerprint")) return render_template("machines.html", items=items, is_admin=(u["role"] == "admin")) @app.post("/machines") @@ -349,7 +339,7 @@ def register_routes(app: Flask) -> None: VALUES(?,?,?,?,?)""", (name, "", (request.form.get("note") or "").strip(), 1, database.now()), ) - flash("机器已添加", "ok") + flash("机器已添加(预登记,激活后绑定指纹)", "ok") return redirect(url_for("machines_page")) @app.post("/machines//note") @@ -365,6 +355,17 @@ def register_routes(app: Flask) -> None: flash("机器标注已保存", "ok") return redirect(url_for("machines_page")) + @app.post("/machines//delete") + @admin_required + def machines_delete(mid: int): + try: + with database.get_db() as conn: + database.delete_machine(conn, mid) + flash("机器已删除", "ok") + except ValueError as exc: + flash(str(exc), "error") + return redirect(url_for("machines_page")) + # ---------- 下载 / 激活(管理员与用户都可发码;用户仅自己账户)---------- @app.get("/download") @login_required diff --git a/app/templates/machines.html b/app/templates/machines.html index fe2c710..c76d932 100644 --- a/app/templates/machines.html +++ b/app/templates/machines.html @@ -1,12 +1,16 @@ {% extends "base.html" %} {% block title %}机器 · {{ app_name }}{% endblock %} {% block content %} -

机器{% if is_admin %}(管理员可预登记){% else %}(与本账户相关){% endif %}

+

机器{% if is_admin %}(管理员可预登记){% else %}(与本账户相关){% endif %}

+

+ 正常用法:不必预登记,客户端激活时会自动创建机器并绑定指纹。 + 「未绑定 / 无指纹」多为早期占位或预登记未使用,管理员可删除。 +

{% if is_admin %}
-

预登记机器

-

也可不预登记:激活时自动创建机器并绑定指纹。

+

预登记机器(可选)

+

一般可跳过。仅当需要先占名、再发激活码预绑定时使用。

@@ -14,30 +18,59 @@
{% endif %} -
+

已有机器

- + + + + + + + {% if is_admin %}{% endif %} + + {% for m in items %} - - + + + + {% if is_admin %} + + {% endif %} + {% else %} + {% endfor %}
名称 / 指纹最近在线标注
名称 / 指纹状态最近心跳标注
{% if is_admin %}
+
#{{ m.id }}
-
{{ m.device_fingerprint or '未绑定' }}
- +
{{ m.device_fingerprint or '尚未绑定指纹' }}
+ {% if not m.bound %}
预登记 / 占位,激活后才会出现指纹
{% endif %} +
{% else %} -
{{ m.name }}
-
{{ m.device_fingerprint or '未绑定' }}
+
{{ m.name }} #{{ m.id }}
+
{{ m.device_fingerprint or '尚未绑定指纹' }}
{% if m.note %}
{{ m.note }}
{% endif %} {% endif %}
{{ m.last_seen_at or '—' }}#{{ m.id }} + {% if m.online %} + 在线 + {% elif m.bound %} + 离线 + {% else %} + 未绑定 + {% endif %} + {{ m.last_seen_fmt }}{{ m.note or '—' }} +
+ +
+
暂无机器。客户端首次激活后会出现在此。