From 6721cfddf1ac6520b96cd6b5cad464b0bb017845 Mon Sep 17 00:00:00 2001
From: aaron <>
Date: Fri, 15 May 2026 21:38:36 +0800
Subject: [PATCH] 1
---
app/server.py | 83 ++-----------------------------------------
app/static/styles.css | 49 +++++++++++++++++++++++++
2 files changed, 52 insertions(+), 80 deletions(-)
diff --git a/app/server.py b/app/server.py
index 9ccdd68..d08233b 100644
--- a/app/server.py
+++ b/app/server.py
@@ -149,7 +149,6 @@ class Handler(BaseHTTPRequestHandler):
"/rules/create": self.create_rule,
"/rules/update": self.update_rule,
"/rules/delete": self.delete_rule,
- "/rules/preview": self.preview_rule,
"/test/send": self.send_test,
"/account/password": self.change_password,
"/deliveries/retry": self.retry_deliveries,
@@ -421,8 +420,6 @@ class Handler(BaseHTTPRequestHandler):
title: str,
action: str,
rule: dict[str, Any] | None = None,
- preview_html: str = "",
- sample_payload: str | None = None,
) -> None:
targets = self.list_targets()
rule = rule or {
@@ -440,11 +437,6 @@ class Handler(BaseHTTPRequestHandler):
selected_targets = target_checkbox_options(targets, rule.get("target_ids", []))
hidden_id = f'' if rule.get("id") else ""
button_text = "保存修改" if rule.get("id") else "创建规则"
- sample_payload = sample_payload or json.dumps(
- {"timeframe": "5m", "symbol": "BTCUSDT", "strategy": "breakout", "action": "buy", "price": 68000},
- ensure_ascii=False,
- indent=2,
- )
body = f"""
{preview_html}"""
+
+
+"""
self.send_html(title, body)
def render_rule_new(self) -> None:
@@ -500,72 +489,6 @@ class Handler(BaseHTTPRequestHandler):
"""
self.send_html("删除路由规则", body)
- def build_rule_from_form(self, form: dict[str, list[str]]) -> dict[str, Any]:
- return {
- "id": form.get("id", [""])[-1],
- "name": form.get("name", [""])[-1].strip(),
- "timeframe": form.get("timeframe", [""])[-1].strip(),
- "symbol": form.get("symbol", [""])[-1].strip().upper(),
- "strategy": form.get("strategy", [""])[-1].strip(),
- "priority": int(form.get("priority", ["100"])[-1] or 100),
- "card_title_template": form.get("card_title_template", ["TradingView {{symbol}} {{action}}"])[-1].strip(),
- "card_body_template": form.get("card_body_template", [""])[-1].strip(),
- "target_ids": [int(value) for value in form.get("target_ids", []) if value],
- "enabled": 1 if form.get("enabled", [""])[-1] == "on" else 0,
- }
-
- def preview_rule(self) -> None:
- form = parse_form_multi(self)
- rule = self.build_rule_from_form(form)
- sample_payload = form.get("sample_payload", ["{}"])[-1]
- source_action = form.get("source_action", ["/rules/create"])[-1]
- title = "编辑路由规则" if rule.get("id") else "新增路由规则"
- try:
- alert = normalize_alert(json.loads(sample_payload))
- message = build_feishu_message(alert, rule)
- matched = self.context.dispatcher.find_matching_rule(alert)
- current_matches = self.rule_matches_alert(rule, alert)
- preview_html = self.render_preview_result(rule, message, matched, current_matches)
- except (json.JSONDecodeError, ValidationError, ValueError) as exc:
- preview_html = f"""预览失败
{html.escape(str(exc))}
"""
- self.render_rule_form(title, source_action, rule, preview_html, sample_payload)
-
- def rule_matches_alert(self, rule: dict[str, Any], alert: dict[str, Any]) -> bool:
- if not any((rule.get("timeframe"), rule.get("symbol"), rule.get("strategy"))):
- return False
- if rule.get("timeframe") and rule["timeframe"] != alert.get("timeframe"):
- return False
- if rule.get("symbol") and rule["symbol"].upper() != alert.get("symbol"):
- return False
- if rule.get("strategy") and rule["strategy"] != alert.get("strategy"):
- return False
- return True
-
- def render_preview_result(
- self,
- rule: dict[str, Any],
- message: dict[str, Any],
- matched: dict[str, Any] | None,
- current_matches: bool,
- ) -> str:
- title = message["card"]["header"]["title"]["content"]
- content = message["card"]["elements"][0]["text"]["content"]
- matched_text = f"当前已保存规则 #{matched['id']} {matched['name']}" if matched else "没有已保存规则会命中"
- current_text = "当前表单会匹配样例 Alert" if current_matches else "当前表单不会匹配样例 Alert"
- return f"""
-预览结果
-
-
当前表单{html.escape(current_text)}
-
系统实际命中{html.escape(matched_text)}
-
规则优先级{rule.get('priority')}
-
消息类型飞书卡片
-
-
-
-
{html.escape(content)}
-
-"""
-
def render_logs(self) -> None:
logs = self.list_logs()
alert_rows = ""
diff --git a/app/static/styles.css b/app/static/styles.css
index 93c7599..a2c7886 100644
--- a/app/static/styles.css
+++ b/app/static/styles.css
@@ -238,6 +238,55 @@ td textarea {
width: auto;
}
+.switch {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ margin: 4px 0 18px;
+ cursor: pointer;
+}
+
+.switch input {
+ position: absolute;
+ opacity: 0;
+ pointer-events: none;
+}
+
+.switch span {
+ position: relative;
+ width: 52px;
+ height: 30px;
+ border-radius: 999px;
+ background: #c8c2b5;
+ transition: background 0.18s ease;
+}
+
+.switch span::after {
+ content: "";
+ position: absolute;
+ top: 4px;
+ left: 4px;
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ background: #fff;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.22);
+ transition: transform 0.18s ease;
+}
+
+.switch input:checked + span {
+ background: var(--accent);
+}
+
+.switch input:checked + span::after {
+ transform: translateX(22px);
+}
+
+.switch strong {
+ color: var(--ink);
+ font: 800 14px ui-sans-serif, system-ui, sans-serif;
+}
+
.checks {
margin: 8px 0 12px;
}