block-proxy 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(git commit:*)",
5
+ "Bash(git add:*)",
6
+ "Bash(tshark:*)",
7
+ "Bash(python3:*)",
8
+ "Bash(gh issue create:*)"
9
+ ]
10
+ }
11
+ }
@@ -0,0 +1,32 @@
1
+ # Skill: Smart Git Commit
2
+
3
+ Use this skill when the user runs `/commit` or asks to commit changes.
4
+
5
+ ## Instructions
6
+
7
+ 1. **Analyze Context**:
8
+ - Run `git status` to identify modified, added, or deleted files.
9
+ - Run `git diff` to inspect the actual code changes in unstaged files.
10
+ - Run `git diff --cached` to inspect changes in staged files.
11
+
12
+ 2. **Stage Changes**:
13
+ - If there are unstaged changes, run `git add -A` to stage all changes (unless the user specifically asked to partial commit).
14
+
15
+ 3. **Generate Commit Message**:
16
+ - Analyze the diffs to understand the *intent* of the changes.
17
+ - Draft a commit message following the **Conventional Commits** specification:
18
+ ```
19
+ <type>(<scope>): <description>
20
+
21
+ [optional body]
22
+ ```
23
+ - **Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`.
24
+ - **Scope**: (Optional) The module or file affected (e.g., `proxy`, `ui`, `deps`).
25
+ - **Description**: Concise summary in imperative mood (e.g., "add support for...", not "added").
26
+
27
+ 4. **Execute Commit**:
28
+ - Run `git commit -m "generated_message"`.
29
+ - If the message has a body, use multiple `-m` flags or a heredoc.
30
+
31
+ 5. **Report**:
32
+ - Inform the user of the commit message used and the result.
@@ -0,0 +1,100 @@
1
+ ---
2
+ model: default
3
+ ---
4
+
5
+ # Pcap Analyse Skill
6
+
7
+ ## Description
8
+ 分析 pcap/pcapng 网络抓包文件,专注于代理服务器(Socks5 over TLS/HTTP)场景下的连接诊断、性能评估和异常检测。
9
+
10
+ ## Usage
11
+ 当用户请求分析 pcap 文件,或者提供一个抓包文件并询问网络连接质量、异常或性能问题时使用此 skill。
12
+
13
+ ## Instructions
14
+ 执行此 skill 时,请遵循以下步骤进行分析。如果用户没有提供 pcap 文件路径,请先询问。
15
+
16
+ **重要**:你不能直接读取二进制 pcap 文件。你必须编写并执行 Python 脚本(推荐使用 `scapy` 库,如果环境没有则尝试使用 `tshark` 命令行工具)来提取信息。
17
+
18
+ ### 1. 编写分析脚本
19
+ 编写一个 Python 脚本来解析 pcap 文件,脚本需要提取并计算以下指标:
20
+
21
+ #### A. 基础信息与抓包点推断
22
+ - 统计源 IP 和目的 IP 的出现频率。
23
+ - 分析 TCP SYN 包的 TTL (Time To Live) 和 MSS (Max Segment Size)。通常客户端发出的包 TTL 较大(如 64, 128),经过路由后会减少。如果抓到的入站包 TTL 接近默认值(未减少太多),可能是服务端抓包;如果出站包 TTL 是默认值,则是本地发出的。
24
+ - 结合端口号(如 8001, 8002)判断流量方向。
25
+
26
+ #### B. 连接异常分析
27
+ - **重传率 (Retransmission Rate)**: 重传包数 / 总包数。
28
+ - **复位连接 (RST)**: 统计 RST 包的数量及出现阶段(握手期、数据传输期、结束期)。
29
+ - **握手失败**: 有 SYN 但无 SYN/ACK 的比例。
30
+ - **TLS 错误**: 检查是否有 TLS Alert 消息。
31
+
32
+ #### C. 性能分析
33
+ - **TCP 握手延迟 (RTT)**: SYN 到 SYN/ACK 的时间差。
34
+ - **TLS 握手延迟**: Client Hello 到 Handshake Finished 的时间。
35
+ - **数据传输 RTT**: 数据包与对应 ACK 之间的时间差统计(平均值、最大值、最小值、抖动)。
36
+
37
+ #### D. 连接复用情况
38
+ - 统计每个 TCP 流 (Stream) 的持续时间。
39
+ - 统计每个流的数据传输量。
40
+ - 判断长连接 (Keep-Alive) 的使用情况:是否有长时间空闲但未断开的连接,或者单连接承载多次数据交互。
41
+
42
+ #### E. 阻塞与拥塞分析
43
+ - **Zero Window**: 统计 TCP Zero Window 通告次数(接收端缓冲区满,通知发送端暂停发送)。
44
+ - **Window Full**: 统计发送端未收到 ACK 而导致发送窗口耗尽的情况。
45
+ - **吞吐量波动**: 检查是否有明显的吞吐量骤降。
46
+
47
+ #### F. 高并发与吞吐量评估
48
+ - **并发连接数**: 统计每一秒内的活跃 TCP 连接数峰值。
49
+ - **每秒请求数 (RPS)**: 如果是 HTTP,估算请求速率。
50
+ - **峰值带宽**: 计算每秒传输的最大字节数。
51
+
52
+ ### 2. 执行分析与报告
53
+ 运行脚本获取输出,并生成一份详细的分析报告。报告必须包含以下章节:
54
+
55
+ 1. **抓包环境推断**:
56
+ - 结论:是客户端抓包还是代理服务器端抓包?
57
+ - 依据:IP 分布、TTL 特征、端口方向。
58
+
59
+ 2. **连接健康度与异常**:
60
+ - 异常连接比例。
61
+ - 具体的错误类型(重传、RST、超时等)及其对用户体验的潜在影响。
62
+
63
+ 3. **性能指标**:
64
+ - 平均/P95 延迟数据。
65
+ - 握手耗时分析。
66
+
67
+ 4. **连接复用分析**:
68
+ - 连接是否被有效复用(Socks5/HTTP Keep-Alive)。
69
+ - 是否存在频繁新建连接导致的开销。
70
+
71
+ 5. **阻塞情况**:
72
+ - 是否发现 Zero Window 或流控阻塞。
73
+ - 是否有队头阻塞迹象。
74
+
75
+ 6. **负载能力评估**:
76
+ - 当前并发峰值和吞吐峰值。
77
+ - 基于当前性能指标(如 RTT 抖动、丢包率随流量变化),预估系统在高并发下的抗压能力。
78
+
79
+ ### 3. 示例 Python 代码结构 (供参考)
80
+ ```python
81
+ from scapy.all import *
82
+ import collections
83
+
84
+ # 读取 pcap
85
+ packets = rdpcap("path/to/file.pcap")
86
+
87
+ # 初始化统计变量
88
+ # ...
89
+
90
+ for pkt in packets:
91
+ if TCP in pkt:
92
+ # 统计逻辑
93
+ # ...
94
+
95
+ # 输出结果
96
+ ```
97
+
98
+ **注意**: 如果环境不支持 Python scapy,请尝试使用 `tshark` 命令:
99
+ `tshark -r file.pcap -q -z io,stat,1 -z conv,tcp`
100
+ 以及 `tshark -r file.pcap -Y "tcp.analysis.flags"` 来分析异常。
package/CLAUDE.md ADDED
@@ -0,0 +1,114 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Common Commands
6
+
7
+ ### Development
8
+ - `npm run dev` – Start development mode with BLOCK_PROXY_DEV=1 (starts all services)
9
+ - `npm run craco` – Start React development server with CRACO (port 3000)
10
+ - `npm run start` / `npm run express` – Start backend + proxy server for production
11
+ - `npm run proxy` – Start proxy only (no admin interface)
12
+ - `npm run socks5` – Start SOCKS5 server only
13
+
14
+ ### Build & Deployment
15
+ - `npm run build` – Build React frontend
16
+ - `npm run docker:build` – Build Docker image for current architecture
17
+ - `npm run docker:build_arm` – Build ARM64 Docker image
18
+ - `npm test` – Run React tests
19
+ - `npm run eject` – Eject from Create React App (irreversible)
20
+
21
+ ### Global CLI
22
+ - `block-proxy` – Start the proxy system (auto-restart on failure)
23
+ - `block-proxy -c rule.js` – Start with external MITM rule configuration
24
+
25
+ ## Architecture Overview
26
+
27
+ Block-Proxy is a MITM-based proxy filtering tool designed for parental control and ad blocking, built with Node.js, React, and a custom AnyProxy fork. It runs on OpenWRT routers or Docker containers.
28
+
29
+ ### Core Components
30
+
31
+ 1. **Proxy Engine** (`/proxy/`)
32
+ - `proxy.js` – Main AnyProxy integration with MITM logic
33
+ - `mitm/` – MITM rule implementations (YouTube ads, dictionary, etc.)
34
+ - `scan.js` – Network scanning for device discovery (every 2 hours)
35
+ - `fs.js` – Configuration file management
36
+
37
+ 2. **SOCKS5 Proxy** (`/socks5/`)
38
+ - `server.js` – SOCKS5 over TLS implementation (port 8002)
39
+ - `start.js` – SOCKS5 server entry point
40
+
41
+ 3. **Backend Server** (`/server/`)
42
+ - `express.js` – Express.js API server for admin interface (port 8004)
43
+ - `start.js` – Main server entry point (decides whether to start admin UI)
44
+
45
+ 4. **React Frontend** (`/src/`)
46
+ - `App.js` – Admin interface for managing blocking rules
47
+ - Built with Create React App, configured via CRACO
48
+
49
+ 5. **CLI Interface** (`/bin/`)
50
+ - `start.js` – Global CLI entry point with auto-restart capabilities and config cleanup
51
+
52
+ 6. **Configuration** (`config.json`)
53
+ - Runtime configuration: ports, blocked hosts, authentication
54
+ - Auto-saved from admin interface
55
+
56
+ ### Port Configuration
57
+ - `8001` – HTTP proxy port (mandatory)
58
+ - `8002` – SOCKS5 over TLS port (optional)
59
+ - `8003` – AnyProxy monitoring interface (optional)
60
+ - `8004` – Admin configuration interface (optional)
61
+ - `3000` – React development server (dev only)
62
+
63
+ ### Entry Points
64
+ - **Primary**: `bin/start.js` (CLI) → `server/start.js` → decides between proxy-only or full stack
65
+ - **Proxy-only**: `proxy/start.js` → `proxy/proxy.js`
66
+ - **Development**: `npm run dev` → starts everything with dev flag
67
+
68
+ ## Key Patterns
69
+
70
+ ### MITM Rule System
71
+ - Host-based blocking with regex pattern matching
72
+ - Time-based restrictions (start/end times, weekdays)
73
+ - MAC address targeting for device-specific rules
74
+ - YouTube ad blocking with predefined regex patterns
75
+ - Custom rule injection via external `rule.js` configuration
76
+
77
+ ### Configuration Management
78
+ - Configuration stored in `config.json` at runtime
79
+ - Supports external rule files via `-c` flag
80
+ - Network device scanning every 2 hours (stored in `config.json`)
81
+ - Auto-clears global config file on exit/restart
82
+
83
+ ### Deployment Patterns
84
+ - Designed for OpenWRT router deployment with host networking (`--network=host`)
85
+ - Docker container with volume mounting for configuration
86
+ - Multi-architecture support (ARM/X86)
87
+ - Auto-restart on failure with config cleanup
88
+ - Production vs. development modes controlled by `BLOCK_PROXY_DEV` env var
89
+
90
+ ### Development Workflow
91
+ 1. **Development**: `npm run dev` starts proxy + admin UI + SOCKS5 (if enabled)
92
+ 2. **Testing**: Proxy-only mode with `npm run proxy`
93
+ 3. **Building**: `npm run build` compiles React frontend to `/build/`
94
+ 4. **Docker**: Separate commands for x86 and ARM architectures
95
+
96
+ ### Dependencies
97
+ - `@bachi/anyproxy` – Modified AnyProxy fork for MITM
98
+ - `express` – Backend API server
99
+ - `react`, `react-dom` – Frontend framework
100
+ - `commander` – CLI argument parsing
101
+ - `axios` – HTTP client for API calls
102
+ - `qrcode` – Certificate QR code generation for MITM setup
103
+
104
+ ## Important Notes
105
+ - SOCKS5 proxy does not support MAC address targeting (only HTTP proxy does)
106
+ - Clients must install AnyProxy certificate for HTTPS MITM inspection
107
+ - Service needs network scanning permissions (best deployed on OpenWRT gateway)
108
+ - Admin interface allows real-time rule management with proxy restart
109
+ - Docker builds use Chinese npm registry (registry.npmmirror.com) by default
110
+
111
+ # Project Rules & Skills
112
+
113
+ - **Import Skill**: 实时遵循 `.claude/skills/*/skill.md` 中的指令。
114
+
package/Dockerfile CHANGED
@@ -49,3 +49,4 @@ EXPOSE 8001 8002 8003
49
49
 
50
50
  # 使用 node 启动脚本作为 CMD
51
51
  CMD ["npm", "run", "start"]
52
+
package/README.md CHANGED
@@ -7,11 +7,12 @@
7
7
 
8
8
  > **Block-Proxy**
9
9
 
10
- 基于 MITM 的代理和上网过滤工具
10
+ 基于 MITM 的代理和过滤工具,Socks5 套 TLS,用于公网
11
11
 
12
12
  主要是限制小朋友上网,依赖 anyproxy,用在 openwrt 里。特性:
13
13
 
14
- - HTTP/Socks5 代理
14
+ - HTTP 代理
15
+ - Socks5 over TLS 代理
15
16
  - 域名拦截
16
17
  - url 正则拦截
17
18
  - 指定拦截Mac地址
package/bin/start.js CHANGED
@@ -122,4 +122,3 @@ process.on('SIGTERM', () => {
122
122
  // 启动
123
123
  startApp();
124
124
  })();
125
-
package/build/index.html CHANGED
@@ -1 +1,2 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><script defer="defer" src="/static/js/main.2247fb80.js"></script><link href="/static/css/main.8bfa3d5f.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><script defer="defer" src="/static/js/main.2247fb80.js"></script><link href="/static/css/main.8bfa3d5f.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
2
+
@@ -0,0 +1,2 @@
1
+ body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;margin:0}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.server-info p{margin:5px 0}.ip-list{list-style:none;margin:10px 0;padding:0}.ip-item{border-bottom:1px solid #eee;display:flex;padding:8px 0}.ip-item:last-child{border-bottom:none}.interface-name{color:#555;font-weight:700}.ip-address{background-color:#f8f9fa;border-radius:3px;flex:1 1;font-family:Courier New,monospace;padding:2px 6px}.toast{align-items:center;animation:toastSlideIn .3s ease-out;border-radius:4px;box-shadow:0 4px 12px #00000026;color:#fff;display:flex;font-weight:500;min-width:250px;padding:16px 20px;position:fixed;right:20px;top:20px;z-index:1000}@keyframes toastSlideIn{0%{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}}.toast.success{background-color:#28a745;border-left:4px solid #1e7e34}.toast.error{background-color:#dc3545;border-left:4px solid #bd2130}.toast.info{background-color:#17a2b8;border-left:4px solid #117a8b}.toast-close{align-items:center;background:none;border:none;color:#fff;cursor:pointer;display:flex;font-size:20px;font-weight:700;height:20px;justify-content:center;margin-left:15px;padding:0;width:20px}.toast-close:hover{opacity:.7}.App{background-color:#f5f5f5;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;min-height:100vh;padding:20px;text-align:center}.config-container{background:#fff;border-radius:8px;box-shadow:0 2px 10px #0000001a;margin:0 auto;max-width:1220px;padding:30px;position:relative;text-align:left}.config-container h1{color:#333;margin-top:0;text-align:center}.config-section{background-color:#fafafa;border:1px solid #eee;border-radius:5px;margin-bottom:30px;padding:20px}.config-section h2{border-bottom:1px solid #eee;color:#555;margin-top:0;padding-bottom:10px}.host-input{display:flex;flex-direction:row;gap:10px;margin-bottom:15px}.host-input input[type=text]{border:1px solid #ddd;border-radius:4px;flex:1 1;font-size:14px;padding:10px}.host-input button{align-self:flex-start;background-color:#007bff;border:none;border-radius:4px;color:#fff;cursor:pointer;font-size:14px}.host-input button:hover{background-color:#0069d9}.time-inputs{align-items:center;display:flex;gap:10px}.time-inputs label{flex-direction:column;font-size:14px}hr.simple-line{background-color:#e3e3e3;border-width:0;height:1px}input[type=time]{background-color:#fff;border:1px solid #ddd;border-radius:3px;padding:4px}input[type=time]:focus{border-color:#007bff;box-shadow:0 0 0 2px #007bff40;outline:none}.host-list{list-style:none;margin:0;padding:0}.host-item{align-items:flex-start;border-bottom:1px solid #eee;display:flex;justify-content:space-between;padding:12px 0}.host-item:last-child{border-bottom:none}.host-info{align-items:center;display:flex;flex-grow:1;font-size:15px}span.host-text{align-self:center;flex-grow:1;font-size:14px}.time-controls{align-items:center;display:flex;gap:8px}.time-controls label{display:flex;flex-direction:column;font-size:12px}.remove-btn{align-self:center;background-color:#dc3545;border:none;border-radius:3px;color:#fff;cursor:pointer;font-size:12px;height:-webkit-fit-content;height:fit-content;margin-left:10px;min-width:23px;padding:5px 7px}.remove-btn:hover{background-color:#c82333}.setting-row{align-items:center;display:flex;gap:15px;margin-bottom:15px}.setting-row label{color:#555;font-weight:700}.setting-row input{flex:1 1}.setting-row input,.setting-row input[type=number]{border:1px solid #ddd;border-radius:4px;padding:8px}.setting-row input[type=number]{font-size:14px}.setting-row input[type=number]:focus{border-color:#007bff;box-shadow:0 0 0 2px #007bff40;outline:none}.actions{display:flex;justify-content:space-between}.restart-btn,.save-btn{border:none;border-radius:4px;cursor:pointer;flex:1 1;font-size:16px;font-weight:700;padding:12px 20px}.save-btn{background-color:#28a745;color:#fff}.save-btn:hover:not(:disabled){background-color:#218838}.restart-btn{background-color:#ffc107;color:#212529}.restart-btn:hover:not(:disabled){background-color:#e0a800}.restart-btn:disabled,.save-btn:disabled{background-color:#6c757d;cursor:not-allowed}button{border:none;border-radius:3px;cursor:pointer;padding:8px 12px;transition:background-color .2s}button:hover{opacity:.9}button:disabled{cursor:not-allowed;opacity:.6}.docker-info{color:#007bff;font-size:.9em}.host-ip-info{border-top:1px solid #eee;margin-top:15px;padding-top:10px}.host-ip-list{list-style:none;margin:10px 0;padding:0}.host-ip-item{display:flex;padding:5px 0}.method-name{color:#555;font-weight:700;width:150px}.host-ip-address{background-color:#e9ecef;border-radius:3px;flex:1 1;font-family:Courier New,monospace;padding:2px 6px}.weekday-btn,.weekday-controls{align-items:center;display:flex}.weekday-btn{background-color:#e5e5e5;border-radius:0;color:#515b63;cursor:pointer;font-size:12px;height:24px;justify-content:center;padding:0;width:24px}.weekday-btn:hover{background-color:#e9ecef;border-color:#adb5bd}.weekday-btn.active{background-color:#007bff;color:#fff}.weekday-btn:focus{box-shadow:0 0 0 2px #007bff40;outline:none}.mac-input{align-items:center;display:flex;gap:5px;margin:0 10px}.mac-input label{color:#555;font-size:12px}.mac-input input[type=text]{border:1px solid #ddd;border-radius:3px;font-size:12px;padding:4px;width:106px}.mac-input input[type=text]:focus{border-color:#007bff;box-shadow:0 0 0 2px #007bff40;outline:none}.table-right-blank{min-width:30px}.title-mac-input{font-size:15px;min-width:131px;text-align:center}.title-time-controls{font-size:15px;min-width:183px;text-align:center}.title-weedkey-controls{font-size:15px;min-width:106px}@media (max-width:500px){.config-container{margin:10px;padding:20px}.time-controls{align-items:flex-start;flex-direction:column;gap:5px}.host-item{gap:10px}.remove-btn{align-self:flex-start;margin-left:0;margin-top:10px}.mac-input{margin:5px 0}.mac-input input[type=text]{width:100px}}
2
+ /*# sourceMappingURL=main.098e0e65.css.map*/
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static/css/main.098e0e65.css","mappings":"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","sources":["index.css","App.css"],"sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n","/* 服务器信息样式 */\n.server-info {\n /*padding: 10px 0;*/\n}\n\n.server-info p {\n margin: 5px 0;\n}\n\n.ip-list {\n list-style: none;\n padding: 0;\n margin: 10px 0;\n}\n\n.ip-item {\n display: flex;\n padding: 8px 0;\n border-bottom: 1px solid #eee;\n}\n\n.ip-item:last-child {\n border-bottom: none;\n}\n\n.interface-name {\n font-weight: bold;\n color: #555;\n}\n\n.ip-address {\n flex: 1;\n font-family: 'Courier New', monospace;\n background-color: #f8f9fa;\n padding: 2px 6px;\n border-radius: 3px;\n}\n\n/* Toast 样式 */\n.toast {\n position: fixed;\n top: 20px;\n right: 20px;\n padding: 16px 20px;\n border-radius: 4px;\n color: white;\n font-weight: 500;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n z-index: 1000;\n display: flex;\n align-items: center;\n animation: toastSlideIn 0.3s ease-out;\n min-width: 250px;\n}\n\n@keyframes toastSlideIn {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n.toast.success {\n background-color: #28a745;\n border-left: 4px solid #1e7e34;\n}\n\n.toast.error {\n background-color: #dc3545;\n border-left: 4px solid #bd2130;\n}\n\n.toast.info {\n background-color: #17a2b8;\n border-left: 4px solid #117a8b;\n}\n\n.toast-close {\n background: none;\n border: none;\n color: white;\n font-size: 20px;\n font-weight: bold;\n margin-left: 15px;\n cursor: pointer;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.toast-close:hover {\n opacity: 0.7;\n}\n\n/* 其他原有样式保持不变 */\n.App {\n text-align: center;\n padding: 20px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n background-color: #f5f5f5;\n min-height: 100vh;\n}\n\n.config-container {\n max-width: 1220px;\n margin: 0 auto;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n padding: 30px;\n text-align: left;\n position: relative;\n}\n\n.config-container h1 {\n text-align: center;\n color: #333;\n margin-top: 0;\n}\n\n.config-section {\n margin-bottom: 30px;\n padding: 20px;\n border: 1px solid #eee;\n border-radius: 5px;\n background-color: #fafafa;\n}\n\n.config-section h2 {\n margin-top: 0;\n color: #555;\n border-bottom: 1px solid #eee;\n padding-bottom: 10px;\n}\n\n.host-input {\n display: flex;\n margin-bottom: 15px;\n gap: 10px;\n flex-direction: row;\n}\n\n.host-input input[type=\"text\"] {\n flex: 1;\n padding: 10px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n}\n\n.host-input button {\n background-color: #007bff;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n align-self: flex-start;\n}\n\n.host-input button:hover {\n background-color: #0069d9;\n}\n\n.time-inputs {\n display: flex;\n gap: 10px;\n align-items: center;\n}\n\n.time-inputs label {\n flex-direction: column;\n font-size: 14px;\n}\n\nhr.simple-line {\n height:1px;\n border-width:0px;\n background-color:#e3e3e3\n}\n\n/* 时间控件样式 */\ninput[type=\"time\"] {\n padding: 4px;\n border: 1px solid #ddd;\n border-radius: 3px;\n background-color: white;\n}\n\ninput[type=\"time\"]:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.host-list {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n\n.host-item {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 12px 0px;\n border-bottom: 1px solid #eee;\n}\n\n.host-item:last-child {\n border-bottom: none;\n}\n\n.host-info {\n display: flex;\n flex-grow: 1;\n align-items:center;\n font-size:15px;\n}\n\nspan.host-text {\n font-size: 14px;\n flex-grow: 1;\n align-self: center; /* 垂直居中 */\n}\n\n.time-controls {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.time-controls label {\n display: flex;\n flex-direction: column;\n font-size: 12px;\n}\n\n.remove-btn {\n background-color: #dc3545;\n color: white;\n border: none;\n padding: 5px 7px;\n min-width: 23px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n height: fit-content;\n align-self: center;\n margin-left: 10px;\n}\n\n.remove-btn:hover {\n background-color: #c82333;\n}\n\n.setting-row {\n display: flex;\n align-items: center;\n margin-bottom: 15px;\n gap: 15px;\n}\n\n.setting-row label {\n /*width: 120px;*/\n font-weight: bold;\n color: #555;\n}\n\n.setting-row input {\n flex: 1;\n padding: 8px;\n border: 1px solid #ddd;\n border-radius: 4px;\n}\n\n.setting-row input[type=\"number\"] {\n padding: 8px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n}\n\n.setting-row input[type=\"number\"]:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.actions {\n display: flex;\n justify-content: space-between;\n}\n\n.save-btn, .restart-btn {\n flex: 1;\n padding: 12px 20px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 16px;\n font-weight: bold;\n}\n\n.save-btn {\n background-color: #28a745;\n color: white;\n}\n\n.save-btn:hover:not(:disabled) {\n background-color: #218838;\n}\n\n.restart-btn {\n background-color: #ffc107;\n color: #212529;\n}\n\n.restart-btn:hover:not(:disabled) {\n background-color: #e0a800;\n}\n\n.save-btn:disabled, .restart-btn:disabled {\n background-color: #6c757d;\n cursor: not-allowed;\n}\n\nbutton {\n padding: 8px 12px;\n border: none;\n border-radius: 3px;\n cursor: pointer;\n transition: background-color 0.2s;\n}\n\nbutton:hover {\n opacity: 0.9;\n}\n\nbutton:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* Docker信息样式 */\n.docker-info {\n color: #007bff;\n font-size: 0.9em;\n}\n\n.host-ip-info {\n margin-top: 15px;\n padding-top: 10px;\n border-top: 1px solid #eee;\n}\n\n.host-ip-list {\n list-style: none;\n padding: 0;\n margin: 10px 0;\n}\n\n.host-ip-item {\n display: flex;\n padding: 5px 0;\n}\n\n.method-name {\n font-weight: bold;\n width: 150px;\n color: #555;\n}\n\n.host-ip-address {\n flex: 1;\n font-family: 'Courier New', monospace;\n background-color: #e9ecef;\n padding: 2px 6px;\n border-radius: 3px;\n}\n\n/* 星期几控件样式 */\n.weekday-controls {\n display: flex;\n /*flex-wrap: wrap;*/\n align-items: center;\n}\n\n.weekday-btn {\n width: 24px;\n height: 24px;\n padding: 0;\n /*border: 1px solid #ddd;*/\n background-color: #e5e5e5;\n color: #515b63;\n font-size: 12px;\n border-radius: 0px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.weekday-btn:hover {\n background-color: #e9ecef;\n border-color: #adb5bd;\n}\n\n.weekday-btn.active {\n background-color: #007bff;\n color: white;\n}\n\n.weekday-btn:focus {\n outline: none;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n/* MAC地址输入框样式 */\n.mac-input {\n display: flex;\n align-items: center;\n margin: 0 10px;\n gap: 5px;\n}\n\n.mac-input label {\n font-size: 12px;\n color: #555;\n}\n\n.mac-input input[type=\"text\"] {\n width: 106px;\n padding: 4px;\n border: 1px solid #ddd;\n border-radius: 3px;\n font-size: 12px;\n}\n\n.mac-input input[type=\"text\"]:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.table-right-blank {\n min-width:30px;\n}\n\n.title-mac-input {\n font-size:15px;\n text-align:center;\n min-width:131px;\n}\n.title-time-controls {\n font-size:15px;\n text-align:center;\n min-width:183px;\n}\n.title-weedkey-controls {\n font-size:15px;\n min-width:106px;\n}\n\n/* 响应式设计 */\n@media (max-width: 500px) {\n .config-container {\n margin: 10px;\n padding: 20px;\n }\n \n .time-controls {\n flex-direction: column;\n align-items: flex-start;\n gap: 5px;\n }\n\n .host-item {\n gap: 10px;\n }\n \n .remove-btn {\n align-self: flex-start;\n margin-left: 0;\n margin-top: 10px;\n }\n\n .mac-input {\n margin: 5px 0;\n }\n \n .mac-input input[type=\"text\"] {\n width: 100px;\n }\n}\n\n"],"names":[],"sourceRoot":""}