Add curated env config UI with Chinese labels and deploy secret bootstrap

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-08 21:03:00 +08:00
parent 0b91d26f71
commit 0d2ce9c126
11 changed files with 767 additions and 23 deletions
+3 -2
View File
@@ -189,9 +189,10 @@ def settings_page_context(page: str, *, instance_base_dir: str | None = None, **
return {}
ctx: dict[str, Any] = {"instance_settings": build_instance_settings_view(**kwargs)}
if p == "env_config" and instance_base_dir:
from lib.env.env_schema import build_env_payload
from lib.env.env_ui_manifest import build_env_ui_payload
exchange_key = str(kwargs.get("exchange_key") or "")
env_path = os.path.join(instance_base_dir, ".env")
example_path = os.path.join(instance_base_dir, ".env.example")
ctx["env_config_groups"] = build_env_payload(example_path, env_path).get("groups") or []
ctx["env_config_groups"] = build_env_ui_payload(exchange_key, example_path, env_path)
return ctx
+12 -11
View File
@@ -8,12 +8,12 @@ from typing import Any, Callable
from flask import jsonify, request, session
from lib.env.env_file_lib import apply_env_updates, env_get, read_env_lines
from lib.env.env_schema import (
build_env_payload,
parse_env_example_schema,
updates_need_restart,
validate_env_updates,
from lib.env.env_ui_manifest import (
build_env_ui_payload,
filter_updates_for_ui,
validate_env_ui_updates,
)
from lib.env.env_schema import parse_env_example_schema
from lib.instance.instance_display_prefs_lib import (
display_meta_for_ui,
get_display_prefs,
@@ -85,26 +85,27 @@ def register_instance_settings_routes(
@app.route("/api/settings/env/meta", methods=["GET"])
@api_auth
def api_env_meta():
payload = build_env_payload(example_path, env_path)
return jsonify({"ok": True, **payload})
groups = build_env_ui_payload(exchange_key, example_path, env_path)
return jsonify({"ok": True, "groups": groups})
@app.route("/api/settings/env", methods=["GET", "POST"])
@api_auth
def api_settings_env():
if request.method == "GET":
payload = build_env_payload(example_path, env_path)
return jsonify({"ok": True, **payload})
groups = build_env_ui_payload(exchange_key, example_path, env_path)
return jsonify({"ok": True, "groups": groups})
body = request.get_json(silent=True) or {}
updates = body.get("values") if isinstance(body.get("values"), dict) else body
if not isinstance(updates, dict):
return jsonify({"ok": False, "msg": "无效请求体"}), 400
groups = parse_env_example_schema(example_path)
clean, errors = validate_env_updates(groups, updates)
updates = filter_updates_for_ui(exchange_key, updates)
clean, errors = validate_env_ui_updates(exchange_key, example_path, updates)
if errors:
return jsonify({"ok": False, "msg": "; ".join(errors)}), 400
if not clean:
return jsonify({"ok": True, "changed_keys": [], "restart_required": False})
changed = apply_env_updates(env_path, clean)
groups = parse_env_example_schema(example_path)
reload_info = apply_env_reload(env_path, get_db, changed, groups)
return jsonify(
{
+1 -1
View File
@@ -103,7 +103,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
<script>
window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
</script>
<script src="/static/instance_settings_prefs.js?v=5"></script>
<script src="/static/instance_settings_prefs.js?v=6"></script>
<script src="/static/instance_live.js?v=4"></script>
<script src="/static/instance_embed.js?v=19"></script>
</body>
+5 -5
View File
@@ -1,8 +1,8 @@
{# env配置:按功能分组,三列卡片布局(SSR 预渲染) #}
{# env配置:白名单分组,仅中文标签 #}
<div class="env-config-page">
<div class="env-config-head card">
<h2>env 配置</h2>
<p class="settings-env-hint">读取并编辑本实例 <code>.env</code>。标注「保存即生效」的项会立即应用;标注「需重启」的项保存后请点「保存并重启」。</p>
<p class="settings-env-hint">仅展示运营相关配置。标注「保存即生效」的项会立即应用;标注「需重启」的项保存后请点「保存并重启」。完整说明见项目文档 <code>docs/env配置说明.md</code></p>
<div class="env-config-toolbar">
<button type="button" class="btn-primary btn-sm" id="env-config-save">保存</button>
<button type="button" class="btn-secondary btn-sm" id="env-config-save-restart">保存并重启</button>
@@ -18,15 +18,15 @@
<div class="env-group-fields">
{% for field in group.fields %}
<div class="env-field-card">
<label class="env-field-label">{{ field.key }}</label>
<label class="env-field-label">{{ field.label or field.key }}</label>
{% if field.note %}
<div class="env-field-note muted">{{ field.note }}</div>
{% endif %}
{% if field.type == 'bool' %}
<select class="env-field-input" data-env-key="{{ field.key }}">
{% set cur = (field.current or field.default or 'false')|lower %}
<option value="true"{% if cur in ('true', '1', 'yes', 'on') %} selected{% endif %}>true</option>
<option value="false"{% if cur not in ('true', '1', 'yes', 'on') %} selected{% endif %}>false</option>
<option value="true"{% if cur in ('true', '1', 'yes', 'on') %} selected{% endif %}>开启</option>
<option value="false"{% if cur not in ('true', '1', 'yes', 'on') %} selected{% endif %}>关闭</option>
</select>
{% else %}
<input
+1 -1
View File
@@ -2038,6 +2038,6 @@ setInterval(refreshPriceSnapshotConditional, {{ price_refresh_seconds * 1000 }})
<script>
window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
</script>
<script src="/static/instance_settings_prefs.js?v=5"></script>
<script src="/static/instance_settings_prefs.js?v=6"></script>
</body>
</html>
@@ -1,5 +1,6 @@
{# 系统设置:2 列独立卡片 #}
<div class="settings-page settings-page--grid">
<p class="settings-env-hint muted" style="margin:0 0 10px">各区块说明见 <code>docs/系统设置说明.md</code></p>
<div class="settings-grid-2col">
<div class="settings-grid-cell">
{% include 'display_prefs_panel.html' %}