secubox-openwrt/docs-zh/development-guidelines.md
CyberMind-FR ccfb58124c docs: Add trilingual documentation (French and Chinese translations)
Add complete French (fr) and Chinese (zh) translations for all documentation:

- Root files: README, CHANGELOG, SECURITY, BETA-RELEASE
- docs/: All 16 core documentation files
- DOCS/: All 19 deep-dive documents including embedded/ and archive/
- package/secubox/: All 123+ package READMEs
- Misc: secubox-tools/, scripts/, EXAMPLES/, config-backups/, streamlit-apps/

Total: 346 translation files created

Each file includes language switcher links for easy navigation between
English, French, and Chinese versions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-20 10:00:18 +01:00

1891 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SecuBox 与 System Hub - 开发指南
> **可用语言:** [English](../docs/development-guidelines.md) | [Français](../docs-fr/development-guidelines.md) | **中文**
**版本:** 1.0.0
**最后更新:** 2025-12-28
**状态:** 活跃
**受众:** 开发人员、AI 助手、维护人员
本文档定义了在 OpenWrt LuCI 生态系统中开发 SecuBox 和 System Hub 模块的标准、最佳实践和强制性验证要求。
---
## 目录
1. [设计系统与 UI 指南](#设计系统与-ui-指南)
2. [架构与命名约定](#架构与命名约定)
3. [RPCD 与 ubus 最佳实践](#rpcd-与-ubus-最佳实践)
4. [ACL 与权限](#acl-与权限)
5. [JavaScript 模式](#javascript-模式)
6. [CSS/样式标准](#css样式标准)
7. [常见错误与解决方案](#常见错误与解决方案)
8. [验证清单](#验证清单)
9. [部署流程](#部署流程)
10. [AI 助手上下文文件](#ai-助手上下文文件)
---
## 设计系统与 UI 指南
### 颜色调色板(基于演示版)
**重要:** 始终使用 `system-hub/common.css` 中定义的调色板
#### 深色模式(主要 - 推荐)
```css
--sh-text-primary: #fafafa;
--sh-text-secondary: #a0a0b0;
--sh-bg-primary: #0a0a0f; /* 主背景(深黑色) */
--sh-bg-secondary: #12121a; /* 卡片/区块背景 */
--sh-bg-tertiary: #1a1a24; /* 悬停/活动背景 */
--sh-bg-card: #12121a;
--sh-border: #2a2a35;
--sh-primary: #6366f1; /* 靛蓝色 */
--sh-primary-end: #8b5cf6; /* 紫色(用于渐变) */
--sh-success: #22c55e; /* 绿色 */
--sh-danger: #ef4444; /* 红色 */
--sh-warning: #f59e0b; /* 橙色 */
```
#### 浅色模式(次要)
```css
--sh-text-primary: #0f172a;
--sh-text-secondary: #475569;
--sh-bg-primary: #ffffff;
--sh-bg-secondary: #f8fafc;
--sh-bg-tertiary: #f1f5f9;
--sh-bg-card: #ffffff;
--sh-border: #e2e8f0;
```
**始终使用 CSS 变量** - 绝不硬编码颜色。
### 字体排版
#### 字体栈
```css
/* 常规文本 */
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
/* 数值、ID、代码 */
font-family: 'JetBrains Mono', 'Courier New', monospace;
```
**必需的导入**(在 common.css 中添加):
```css
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');
```
#### 字体大小
```css
/* 标题 */
--sh-title-xl: 28px; /* 页面标题 */
--sh-title-lg: 20px; /* 卡片标题 */
--sh-title-md: 16px; /* 区块标题 */
/* 文本 */
--sh-text-base: 14px; /* 正文 */
--sh-text-sm: 13px; /* 标签、元数据 */
--sh-text-xs: 11px; /* 大写标签 */
/* 数值 */
--sh-value-xl: 40px; /* 大型指标 */
--sh-value-lg: 32px; /* 统计概览 */
--sh-value-md: 28px; /* 徽章 */
```
### 组件模式
#### 组件层次结构
下图展示了标准页面结构和组件关系:
```mermaid
graph TB
PAGE[页面容器<br/>.module-dashboard] --> HEADER[sh-page-header]
PAGE --> CONTENT[sh-content]
HEADER --> TITLE_SECTION[标题区块<br/>div]
HEADER --> STATS[sh-stats-grid]
TITLE_SECTION --> TITLE[sh-page-title<br/>渐变文字]
TITLE_SECTION --> SUBTITLE[sh-page-subtitle]
STATS --> BADGE1[sh-stat-badge]
STATS --> BADGE2[sh-stat-badge]
STATS --> BADGE3[...]
BADGE1 --> VALUE1[sh-stat-value<br/>等宽字体]
BADGE1 --> LABEL1[sh-stat-label<br/>大写]
CONTENT --> TABS[sh-filter-tabs]
CONTENT --> CARD_GRID[卡片网格<br/>grid 布局]
TABS --> TAB1[sh-filter-tab<br/>活动]
TABS --> TAB2[sh-filter-tab]
CARD_GRID --> CARD1[sh-card]
CARD_GRID --> CARD2[sh-card-success]
CARD_GRID --> CARD3[sh-card-danger]
CARD1 --> CH1[sh-card-header]
CARD1 --> CB1[sh-card-body]
CH1 --> CT1[sh-card-title]
CT1 --> ICON1[sh-card-title-icon]
CB1 --> BUTTONS[按钮组]
CB1 --> INFO[信息行]
BUTTONS --> BTN1[sh-btn<br/>sh-btn-primary]
BUTTONS --> BTN2[sh-btn<br/>sh-btn-secondary]
style PAGE fill:#0a0a0f,color:#fafafa,stroke:#6366f1,stroke-width:3px
style HEADER fill:#12121a,color:#fafafa,stroke:#6366f1,stroke-width:2px
style CONTENT fill:#12121a,color:#fafafa,stroke:#6366f1,stroke-width:2px
style CARD1 fill:#12121a,color:#fafafa,stroke:#6366f1,stroke-width:2px
style CARD2 fill:#12121a,color:#fafafa,stroke:#22c55e,stroke-width:2px
style CARD3 fill:#12121a,color:#fafafa,stroke:#ef4444,stroke-width:2px
style TITLE fill:#6366f1,color:#fff
style BTN1 fill:#6366f1,color:#fff
style VALUE1 fill:#8b5cf6,color:#fff
```
**组件类别:**
1. **布局容器:** 页面包装器、标题、内容区块
2. **字体排版:** 带渐变效果的标题、副标题、标签
3. **数据展示:** 带等宽数值的统计徽章、带边框的卡片
4. **导航:** 过滤选项卡、导航选项卡(固定)
5. **交互元素:** 带渐变和悬停效果的按钮
**样式规则:**
- **卡片:** 3px 顶部边框(悬停时渐变,或根据状态着色)
- **统计徽章:** 最小宽度 130px数值使用等宽字体
- **按钮:** 渐变背景,悬停时阴影,平滑过渡
- **选项卡:** 活动状态带渐变背景和发光效果
- **网格布局:** 自适应最小值130px、240px 或 300px
---
#### 1. 页面标题(标准)
**要求:** 每个模块视图必须以这个紧凑的 `.sh-page-header` 开始。不要引入定制的大横幅或超大标题区;标题保持可预测的高度(左侧标题+副标题,右侧统计数据),确保 SecuBox 仪表板的一致性。如果不需要统计数据,保留容器但提供空的 `.sh-stats-grid` 以备将来添加指标。
**精简变体:** 当页面只需要 2-3 个指标时,使用 `.sh-page-header-lite` + `.sh-header-chip`(参见 `luci-app-vhost-manager``luci-app-secubox` 设置)。芯片包含表情符号/图标、小标签和值;颜色(`.success`、`.danger`、`.warn`)传达状态。此变体替代了旧演示版中的大型标题区块。
**版本芯片:** 始终从 RPC 后端公开包版本(从 `/usr/lib/opkg/info/<pkg>.control` 读取)并将其显示为第一个芯片(`icon: `)。这使 UI 和 `PKG_VERSION` 保持同步,无需查找硬编码的字符串。
**HTML 结构:**
```javascript
E('div', { 'class': 'sh-page-header' }, [
E('div', {}, [
E('h2', { 'class': 'sh-page-title' }, [
E('span', { 'class': 'sh-page-title-icon' }, '🎯'),
'页面标题'
]),
E('p', { 'class': 'sh-page-subtitle' }, '页面描述')
]),
E('div', { 'class': 'sh-stats-grid' }, [
// 统计徽章放这里
])
])
```
**CSS 类:**
- `.sh-page-header` - Flex 布局容器
- `.sh-page-title` - 渐变文字效果
- `.sh-page-title-icon` - 图标(无渐变)
- `.sh-page-subtitle` - 次要文本
- `.sh-stats-grid` - 徽章网格(最小 130px
#### 2. 统计徽章
**规则:** 最小 130px数值使用等宽字体
```javascript
E('div', { 'class': 'sh-stat-badge' }, [
E('div', { 'class': 'sh-stat-value' }, '92'),
E('div', { 'class': 'sh-stat-label' }, 'CPU %')
])
```
**网格布局:**
```css
.sh-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: 12px;
}
```
#### 3. 带彩色边框的卡片
**必需:** 所有卡片必须有 3px 顶部边框
```javascript
E('div', { 'class': 'sh-card sh-card-success' }, [
E('div', { 'class': 'sh-card-header' }, [
E('h3', { 'class': 'sh-card-title' }, [
E('span', { 'class': 'sh-card-title-icon' }, '⚙️'),
'卡片标题'
])
]),
E('div', { 'class': 'sh-card-body' }, [
// 内容
])
])
```
**边框变体:**
- `.sh-card` - 渐变边框(悬停时可见)
- `.sh-card-success` - 永久绿色边框
- `.sh-card-danger` - 永久红色边框
- `.sh-card-warning` - 永久橙色边框
#### 4. 按钮
**渐变按钮(首选):**
```javascript
E('button', { 'class': 'sh-btn sh-btn-primary' }, '主要操作')
E('button', { 'class': 'sh-btn sh-btn-success' }, '成功操作')
E('button', { 'class': 'sh-btn sh-btn-danger' }, '危险操作')
E('button', { 'class': 'sh-btn sh-btn-secondary' }, '次要操作')
```
**所有按钮必须有:**
- 阴影效果(已在 CSS 中)
- 悬停动画translateY(-2px)
- 平滑过渡0.3s cubic-bezier
#### 5. 过滤选项卡
```javascript
E('div', { 'class': 'sh-filter-tabs' }, [
E('div', {
'class': 'sh-filter-tab active',
'data-filter': 'all'
}, [
E('span', { 'class': 'sh-tab-icon' }, '📋'),
E('span', { 'class': 'sh-tab-label' }, '全部')
])
])
```
**活动选项卡样式:**
- 背景:靛蓝-紫色渐变
- 颜色:白色
- Box-shadow 带发光效果
### 网格系统
#### 统计概览(紧凑)
```css
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: 16px;
```
#### 指标卡片(中等)
```css
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
```
#### 信息卡片(大型)
```css
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
```
### 渐变效果
#### 渐变文字(标题)
```css
background: linear-gradient(135deg, var(--sh-primary), var(--sh-primary-end));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
```
**使用:** `.sh-gradient-text` 类或 `.sh-page-title`
#### 渐变背景(按钮、徽章)
```css
background: linear-gradient(135deg, var(--sh-primary), var(--sh-primary-end));
```
#### 渐变边框(顶部)
```css
/* 带渐变的 3px 顶部边框 */
.element::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--sh-primary), var(--sh-primary-end));
}
```
### 动画标准
#### 悬停效果
```css
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform: translateY(-3px); /* 卡片 */
transform: translateY(-2px); /* 按钮、徽章 */
```
#### 阴影渐进
```css
/* 默认 */
box-shadow: none;
/* 悬停 - 微妙 */
box-shadow: 0 8px 20px var(--sh-shadow);
/* 悬停 - 明显 */
box-shadow: 0 12px 28px var(--sh-hover-shadow);
/* 按钮悬停 */
box-shadow: 0 8px 20px rgba(99, 102, 241, 0.5);
```
---
## 架构与命名约定
### 系统架构概览
下图说明了从浏览器 JavaScript 到系统后端的完整数据流:
```mermaid
graph TB
subgraph "浏览器"
UI[JavaScript 视图<br/>view/module/overview.js]
API[API 模块<br/>module/api.js]
end
subgraph "LuCI 框架"
RPC[RPC 层<br/>L.rpc.declare]
UHTTPD[uhttpd<br/>Web 服务器]
end
subgraph "后端服务"
RPCD[RPCD 守护进程]
SCRIPT[RPCD 脚本<br/>/usr/libexec/rpcd/luci.module-name]
UBUS[ubus 消息总线]
end
subgraph "系统层"
UCI[UCI 配置]
SYS[系统服务<br/>init.d 脚本]
FS[文件系统<br/>proc, sys 等]
end
UI -->|"API.getStatus()"| API
API -->|"rpc.declare({ object: 'luci.module' })"| RPC
RPC -->|"HTTP POST /ubus"| UHTTPD
UHTTPD -->|"调用方法"| RPCD
RPCD -->|"执行脚本"| SCRIPT
SCRIPT -->|"ubus call"| UBUS
UBUS -->|"读/写"| UCI
UBUS -->|"控制"| SYS
SCRIPT -->|"读取指标"| FS
style UI fill:#6366f1,color:#fff,stroke:#4f46e5
style API fill:#8b5cf6,color:#fff,stroke:#7c3aed
style SCRIPT fill:#22c55e,color:#fff,stroke:#16a34a
style RPCD fill:#f59e0b,color:#fff,stroke:#d97706
style UCI fill:#ef4444,color:#fff,stroke:#dc2626
```
**关键组件:**
1. **浏览器层:** JavaScript 视图和 API 模块处理 UI 和数据请求
2. **LuCI 框架:** RPC 层将 JavaScript 调用转换为 ubus 协议
3. **后端服务:** RPCD 通过 ubus 消息总线执行 shell 脚本
4. **系统层:** UCI 配置、系统服务和文件系统提供数据
**关键命名规则:** RPCD 脚本名称**必须**与 JavaScript `rpc.declare()` 中的 `object` 参数匹配。
---
### 关键RPCD 脚本命名
**绝对规则:** RPCD 文件名必须与 JavaScript 中的 ubus 对象名完全匹配。
#### 正确:
**JavaScript**
```javascript
var callStatus = rpc.declare({
object: 'luci.system-hub', // ← 对象名
method: 'getHealth'
});
```
**RPCD 文件:**
```bash
root/usr/libexec/rpcd/luci.system-hub # ← 完全匹配
```
#### 错误(导致 -32000 错误):
```bash
# 错误 - 缺少前缀
root/usr/libexec/rpcd/system-hub
# 错误 - 下划线而非连字符
root/usr/libexec/rpcd/luci.system_hub
# 错误 - 名称不同
root/usr/libexec/rpcd/systemhub
```
### 菜单路径约定
**规则:** menu.d/*.json 中的路径必须与视图文件完全匹配。
#### 正确:
**菜单 JSON**
```json
{
"action": {
"type": "view",
"path": "system-hub/overview"
}
}
```
**视图文件:**
```bash
htdocs/luci-static/resources/view/system-hub/overview.js
```
#### 错误(导致 404
菜单:`"path": "system-hub/overview"` 但文件是:`view/systemhub/overview.js`
### 标准前缀
| 类型 | 前缀 | 示例 |
|------|------|------|
| ubus 对象 | `luci.` | `luci.system-hub` |
| CSS 类 | `sh-`System Hub`sb-`SecuBox | `.sh-page-header` |
| CSS 变量 | `--sh-` | `--sh-primary` |
| JavaScript 模块 | 模块名 | `system-hub/api.js` |
### 文件结构模板
```
luci-app-<module-name>/
├── Makefile
├── README.md
├── htdocs/luci-static/resources/
│ ├── view/<module-name>/
│ │ ├── overview.js # 主页面
│ │ ├── settings.js # 配置
│ │ └── *.js # 其他视图
│ └── <module-name>/
│ ├── api.js # RPC 客户端
│ ├── theme.js # 主题辅助函数(可选)
│ ├── common.css # 共享样式
│ └── *.css # 特定样式
└── root/
├── usr/
│ ├── libexec/rpcd/
│ │ └── luci.<module-name> # 必须匹配 ubus 对象
│ └── share/
│ ├── luci/menu.d/
│ │ └── luci-app-<module-name>.json
│ └── rpcd/acl.d/
│ └── luci-app-<module-name>.json
└── etc/config/<module-name>(可选)
```
---
## RPCD 与 ubus 最佳实践
### RPCD 脚本模板Shell
**文件:** `root/usr/libexec/rpcd/luci.<module-name>`
```bash
#!/bin/sh
# <module-name> 的 RPCD 后端
# ubus 对象luci.<module-name>
case "$1" in
list)
# 列出可用方法
echo '{
"getStatus": {},
"getHealth": {},
"getServices": {}
}'
;;
call)
case "$2" in
getStatus)
# 始终返回有效的 JSON
printf '{"enabled": true, "version": "1.0.0"}\n'
;;
getHealth)
# 读取系统指标
cpu_usage=$(top -bn1 | grep "CPU:" | awk '{print $2}' | sed 's/%//')
mem_total=$(free | grep Mem | awk '{print $2}')
mem_used=$(free | grep Mem | awk '{print $3}')
printf '{
"cpu": {"usage": %s},
"memory": {"total_kb": %s, "used_kb": %s}
}\n' "$cpu_usage" "$mem_total" "$mem_used"
;;
getServices)
# 服务示例
services='[]'
for service in /etc/init.d/*; do
# 构建 JSON 数组
:
done
echo "$services"
;;
*)
echo '{"error": "方法未找到"}'
exit 1
;;
esac
;;
esac
```
### RPCD 脚本验证
**必须检查清单:**
1. 文件可执行:`chmod +x root/usr/libexec/rpcd/luci.<module-name>`
2. 存在 shebang`#!/bin/sh`
3. case/esac 结构正确
4. `list` 方法返回包含所有方法的 JSON
5. `call` 方法处理所有情况
6. 始终返回有效的 JSON
7. 没有调试用的 `echo`(生产环境注释掉)
8. 对未知方法进行错误处理
### 测试 RPCD 脚本
**在路由器上:**
```bash
# 直接测试
/usr/libexec/rpcd/luci.system-hub list
# 通过 ubus
ubus list luci.system-hub
ubus call luci.system-hub getStatus
# 修改后重启 RPCD
/etc/init.d/rpcd restart
```
### 常见 RPCD 错误
#### 错误:"Object not found"-32000
**原因:** RPCD 文件名与 ubus 对象不匹配
**解决方案:**
```bash
# 检查 JS 中的名称
grep -r "object:" htdocs/luci-static/resources/view/ --include="*.js"
# 重命名 RPCD 文件以匹配
mv root/usr/libexec/rpcd/wrong-name root/usr/libexec/rpcd/luci.correct-name
```
#### 错误:"Method not found"-32601
**原因:** 方法未在 `list` 中声明或未在 `call` 中实现
**解决方案:**
```bash
# 验证方法在两个块中都存在
grep "getStatus" root/usr/libexec/rpcd/luci.*
```
#### 错误:返回无效的 JSON
**原因:** RPCD 输出不是有效的 JSON
**解决方案:**
```bash
# 测试 JSON
/usr/libexec/rpcd/luci.module-name call getStatus | jsonlint
# 使用 printf 而非 echo 来生成 JSON
printf '{"key": "%s"}\n' "$value"
```
---
## ACL 与权限
### ACL 文件模板
**文件:** `root/usr/share/rpcd/acl.d/luci-app-<module-name>.json`
```json
{
"luci-app-<module-name>": {
"description": "授予对 <Module Name> 的访问权限",
"read": {
"ubus": {
"luci.<module-name>": [
"getStatus",
"getHealth",
"getServices"
]
},
"uci": [
"<module-name>"
]
},
"write": {
"ubus": {
"luci.<module-name>": [
"setConfig",
"restartService"
]
},
"uci": [
"<module-name>"
]
}
}
}
```
### ACL 最佳实践
1. **读/写分离:** 只授予必要的权限
2. **显式列表:** 列出所有使用的 ubus 方法
3. **UCI 访问:**`read``write` 中添加 UCI 配置
4. **JSON 验证:** 始终使用 `jsonlint` 验证
### 常见 ACL 错误
#### 错误:"Access denied"
**原因:** ubus 方法不在 ACL 中
**解决方案:**
```json
{
"read": {
"ubus": {
"luci.system-hub": [
"getHealth" // ← 添加缺失的方法
]
}
}
}
```
#### 错误:"UCI config not accessible"
**原因:** UCI 配置不在 ACL 中
**解决方案:**
```json
{
"read": {
"uci": [
"system-hub" // ← 添加配置
]
}
}
```
---
## JavaScript 模式
### API 模块模板
**文件:** `htdocs/luci-static/resources/<module-name>/api.js`
```javascript
'use strict';
'require rpc';
'require uci';
return L.Class.extend({
// 声明 RPC 调用
callGetStatus: rpc.declare({
object: 'luci.<module-name>',
method: 'getStatus',
expect: { }
}),
callGetHealth: rpc.declare({
object: 'luci.<module-name>',
method: 'getHealth',
expect: { }
}),
// 带错误处理的包装方法
getStatus: function() {
return this.callGetStatus().catch(function(err) {
console.error('获取状态失败:', err);
return { enabled: false, error: err.message };
});
},
getHealth: function() {
return this.callGetHealth().catch(function(err) {
console.error('获取健康状态失败:', err);
return {
cpu: { usage: 0 },
memory: { usage: 0 },
error: err.message
};
});
},
// 工具函数
formatBytes: function(bytes) {
if (bytes === 0) return '0 B';
var k = 1024;
var sizes = ['B', 'KB', 'MB', 'GB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
});
```
### 视图模板
**文件:** `htdocs/luci-static/resources/view/<module-name>/overview.js`
```javascript
'use strict';
'require view';
'require ui';
'require dom';
'require poll';
'require <module-name>/api as API';
return view.extend({
// 状态
healthData: null,
sysInfo: null,
// 加载数据
load: function() {
return Promise.all([
API.getStatus(),
API.getHealth()
]);
},
// 渲染 UI
render: function(data) {
var self = this;
this.sysInfo = data[0] || {};
this.healthData = data[1] || {};
var container = E('div', { 'class': '<module>-dashboard' }, [
// 链接 CSS 文件
E('link', { 'rel': 'stylesheet', 'href': L.resource('<module>/common.css') }),
E('link', { 'rel': 'stylesheet', 'href': L.resource('<module>/overview.css') }),
// 标题
this.renderHeader(),
// 内容
this.renderContent()
]);
// 设置自动刷新
poll.add(L.bind(function() {
return Promise.all([
API.getStatus(),
API.getHealth()
]).then(L.bind(function(refreshData) {
this.sysInfo = refreshData[0] || {};
this.healthData = refreshData[1] || {};
this.updateDashboard();
}, this));
}, this), 30); // 每 30 秒刷新
return container;
},
renderHeader: function() {
return E('div', { 'class': 'sh-page-header' }, [
// 标题内容
]);
},
renderContent: function() {
return E('div', { 'class': 'sh-content' }, [
// 主要内容
]);
},
updateDashboard: function() {
// 更新现有 DOM 元素
var element = document.querySelector('.my-element');
if (element) {
dom.content(element, this.renderContent());
}
},
// LuCI 必需的存根
handleSaveApply: null,
handleSave: null,
handleReset: null
});
```
### 事件处理模式
```javascript
// 正确:渲染后绑定事件
render: function(data) {
var container = E('div', {}, [
E('button', {
'id': 'my-button',
'class': 'sh-btn sh-btn-primary'
}, '点击我')
]);
// 在容器创建后添加事件
container.addEventListener('click', function(ev) {
if (ev.target && ev.target.id === 'my-button') {
self.handleButtonClick();
}
});
return container;
},
handleButtonClick: function() {
ui.addNotification(null, E('p', '按钮已点击!'), 'info');
}
```
### 常见 JavaScript 错误
#### 错误:显示 "[object HTMLButtonElement]"
**原因:** 当 E() 期望简单数组时使用了嵌套数组
```javascript
// 错误
E('div', {}, [
this.renderButtons() // renderButtons 已经返回数组
])
// 正确
E('div', {},
this.renderButtons() // 不要额外的 [ ]
)
```
#### 错误:"Cannot read property of undefined"
**原因:** API 数据不可用
```javascript
// 错误
var cpuUsage = this.healthData.cpu.usage;
// 正确(使用可选链)
var cpuUsage = (this.healthData.cpu && this.healthData.cpu.usage) || 0;
// 或
var cpuUsage = this.healthData.cpu?.usage || 0; // ES2020
```
#### 错误:"poll callback failed"
**原因:** poll.add 中没有返回 Promise
```javascript
// 错误
poll.add(function() {
API.getHealth(); // 没有 return
}, 30);
// 正确
poll.add(function() {
return API.getHealth().then(function(data) {
// 更新 UI
});
}, 30);
```
---
## CSS/样式标准
### 文件组织
```
<module-name>/
├── common.css # 共享组件(标题、按钮、卡片、选项卡)
├── overview.css # 概览页面专用
├── services.css # 服务页面专用
└── *.css # 其他页面专用样式
```
### CSS 文件模板
```css
/**
* 模块名 - 页面/组件样式
* 此文件样式化的内容描述
* 版本X.Y.Z
*/
/* === 导入共享样式(如需要)=== */
/* 如果在 HTML 中加载则不需要 */
/* === 页面特定变量(如需要)=== */
:root {
--page-specific-var: value;
}
/* === 布局 === */
.module-page-container {
/* 布局样式 */
}
/* === 组件 === */
.module-component {
/* 组件样式 */
}
/* === 响应式 === */
@media (max-width: 768px) {
/* 移动端样式 */
}
/* === 深色模式覆盖 === */
[data-theme="dark"] .module-component {
/* 深色模式专用 */
}
```
### CSS 最佳实践
#### 1. 始终使用 CSS 变量
```css
/* 错误 */
.my-card {
background: #12121a;
color: #fafafa;
}
/* 正确 */
.my-card {
background: var(--sh-bg-card);
color: var(--sh-text-primary);
}
```
#### 2. 按模块添加类前缀
```css
/* System Hub */
.sh-page-header { }
.sh-card { }
.sh-btn { }
/* SecuBox */
.sb-module-grid { }
.sb-dashboard { }
/* 特定模块 */
.netdata-chart { }
.crowdsec-alert { }
```
#### 3. 一致的过渡效果
```css
/* 标准过渡 */
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
/* 快速过渡(悬停状态)*/
transition: all 0.2s ease;
/* 平滑过渡(大范围移动)*/
transition: all 0.5s ease;
```
#### 4. 响应式断点
```css
/* 移动端 */
@media (max-width: 768px) {
.sh-stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 平板 */
@media (min-width: 769px) and (max-width: 1024px) {
/* 平板专用 */
}
/* 桌面 */
@media (min-width: 1025px) {
/* 桌面专用 */
}
```
#### 5. 深色模式必需
**始终提供深色模式样式:**
```css
/* 浅色模式(默认)*/
.my-component {
background: var(--sh-bg-card);
border: 1px solid var(--sh-border);
}
/* 深色模式覆盖 */
[data-theme="dark"] .my-component {
background: var(--sh-bg-card);
border-color: var(--sh-border);
}
```
### Z-index 层级
**遵循此层级:**
```css
--z-base: 0;
--z-dropdown: 100;
--z-sticky: 200;
--z-fixed: 300;
--z-modal-backdrop: 400;
--z-modal: 500;
--z-popover: 600;
--z-tooltip: 700;
```
---
## 常见错误与解决方案
### 1. RPCD 对象未找到(-32000
**完整错误:**
```
RPC call to luci.system-hub/getHealth failed with error -32000: Object not found
```
**诊断:**
```bash
# 1. 验证 RPCD 文件存在
ls -la /usr/libexec/rpcd/luci.system-hub
# 2. 验证它是可执行的
chmod +x /usr/libexec/rpcd/luci.system-hub
# 3. 列出 ubus 对象
ubus list | grep system-hub
# 4. 如果不存在,重启 RPCD
/etc/init.d/rpcd restart
ubus list | grep system-hub
```
**解决方案:**
1. 重命名 RPCD 文件以完全匹配
2. 验证权限755 或 rwxr-xr-x
3. 重启 rpcd
### 2. 视图未找到404
**错误:**
```
HTTP error 404 while loading class file '/luci-static/resources/view/system-hub/overview.js'
```
**诊断:**
```bash
# 1. 验证文件存在
ls -la /www/luci-static/resources/view/system-hub/overview.js
# 2. 检查 menu.d 中的路径
grep "path" /usr/share/luci/menu.d/luci-app-system-hub.json
```
**解决方案:**
1. 验证菜单 JSON 中的路径与文件匹配
2. 验证文件权限644
3. 清除缓存:`rm -f /tmp/luci-indexcache /tmp/luci-modulecache/*`
### 3. CSS 未加载403 Forbidden
**错误:**
```
GET /luci-static/resources/system-hub/common.css 403 Forbidden
```
**诊断:**
```bash
# 验证权限
ls -la /www/luci-static/resources/system-hub/common.css
```
**解决方案:**
```bash
# 修正权限
chmod 644 /www/luci-static/resources/system-hub/*.css
```
### 4. RPCD 返回无效 JSON
**浏览器控制台错误:**
```
SyntaxError: Unexpected token in JSON at position X
```
**诊断:**
```bash
# 直接测试 JSON
/usr/libexec/rpcd/luci.system-hub call getHealth | jsonlint
# 或使用 jq
/usr/libexec/rpcd/luci.system-hub call getHealth | jq .
```
**常见解决方案:**
```bash
# 错误 - 未转义的单引号
echo '{"error": "can't process"}'
# 正确 - 使用 printf 和双引号
printf '{"error": "cannot process"}\n'
# 错误 - 变量未加引号
echo "{\"value\": $var}"
# 正确 - 变量加引号
printf '{"value": "%s"}\n' "$var"
```
### 5. 浏览器缓存问题
**症状:**
- CSS/JS 更改不可见
- 显示旧数据
- 代码更新但界面相同
**解决方案:**
```bash
# 1. 服务器端 - 清除 LuCI 缓存
ssh root@router "rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* && /etc/init.d/uhttpd restart"
# 2. 客户端 - 强制刷新
Ctrl + Shift + RChrome/Firefox
Ctrl + F5Windows
Cmd + Shift + RMac
# 3. 测试用隐私/无痕模式
Ctrl + Shift + NChrome
Ctrl + Shift + PFirefox
```
### 6. ACL 访问被拒绝
**错误:**
```
Access to path '/admin/secubox/system/system-hub' denied
```
**诊断:**
```bash
# 验证 ACL
cat /usr/share/rpcd/acl.d/luci-app-system-hub.json | jq .
# 验证 ubus 方法已列出
grep "getHealth" /usr/share/rpcd/acl.d/luci-app-system-hub.json
```
**解决方案:**
在 ACL 中添加缺失的方法并重启 rpcd。
---
## 验证清单
### 提交前清单
每次提交前,验证:
- [ ] **RPCD 脚本:**
- [ ] 文件名与 ubus 对象匹配
- [ ] 可执行chmod +x
- [ ] list/call 结构正确
- [ ] 返回有效的 JSON
- [ ] 所有方法已实现
- [ ] **菜单和 ACL**
- [ ] 菜单路径与视图文件匹配
- [ ] ACL 列出所有 ubus 方法
- [ ] JSON 有效jsonlint
- [ ] **JavaScript**
- [ ] 第一行是 'use strict'
- [ ] 必需的导入存在
- [ ] 生产环境无 console.log
- [ ] API 调用有错误处理
- [ ] 事件处理器正确绑定
- [ ] **CSS**
- [ ] 使用 CSS 变量(无硬编码)
- [ ] 类有前缀sh-、sb-、module-
- [ ] 支持深色模式
- [ ] 响应式max-width: 768px
- [ ] 一致的过渡效果
- [ ] **Makefile**
- [ ] PKG_VERSION 已增加
- [ ] LUCI_DEPENDS 正确
- [ ] Include 路径正确(../../luci.mk
### 部署前清单
部署到路由器前:
- [ ] **验证脚本:**
```bash
./secubox-tools/validate-modules.sh
```
- [ ] **本地测试 RPCD**
```bash
/usr/libexec/rpcd/luci.module-name list
/usr/libexec/rpcd/luci.module-name call getStatus
```
- [ ] **测试 JSON**
```bash
find . -name "*.json" -exec jsonlint {} \;
```
- [ ] **Shellcheck**
```bash
shellcheck root/usr/libexec/rpcd/*
```
- [ ] **权限:**
```bash
# RPCD 脚本
chmod 755 root/usr/libexec/rpcd/*
# CSS/JS 文件
chmod 644 htdocs/luci-static/resources/**/*
```
### 部署后清单
部署后:
- [ ] **服务:**
```bash
/etc/init.d/rpcd status
/etc/init.d/uhttpd status
```
- [ ] **ubus 对象:**
```bash
ubus list | grep luci.module-name
```
- [ ] **文件存在:**
```bash
ls -la /www/luci-static/resources/view/module-name/
ls -la /www/luci-static/resources/module-name/
```
- [ ] **权限正确:**
```bash
ls -la /usr/libexec/rpcd/luci.module-name
ls -la /www/luci-static/resources/module-name/*.css
```
- [ ] **浏览器测试:**
- [ ] 在隐私模式打开
- [ ] 检查控制台F12- 无错误
- [ ] 检查网络选项卡 - 所有文件加载200
- [ ] 测试深色/浅色模式
- [ ] 测试响应式(移动视图)
---
## 部署流程
### 部署工作流
下图说明了带验证检查点的完整部署流程:
```mermaid
flowchart TD
START([开始部署]) --> LOCAL_VAL{本地验证<br/>通过?}
LOCAL_VAL -->|否| FIX_LOCAL[本地修复问题]
FIX_LOCAL --> LOCAL_VAL
LOCAL_VAL -->|是| CHECK_DISK{磁盘空间<br/>< 90%}
CHECK_DISK -->|否| CLEAN_DISK[清理临时文件<br/>和旧备份]
CLEAN_DISK --> CHECK_DISK
CHECK_DISK -->|是| FIX_PERM_LOCAL[修复权限<br/>本地源码]
FIX_PERM_LOCAL --> COPY[复制文件到路由器<br/>scp JS/CSS/RPCD]
COPY --> FIX_PERM_REMOTE[修复权限<br/>远程文件<br/>755 RPCD / 644 CSS-JS]
FIX_PERM_REMOTE --> CLEAR[清除 LuCI 缓存<br/>/tmp/luci-*]
CLEAR --> RESTART[重启服务<br/>rpcd + uhttpd]
RESTART --> V1{ubus 对象<br/>可用?}
V1 -->|否| DEBUG1[调试 RPCD 脚本<br/>检查命名和权限]
DEBUG1 --> FIX_PERM_REMOTE
V1 -->|是| V2{文件<br/>可访问?}
V2 -->|403 错误| DEBUG2[修复文件权限<br/>chmod 644]
DEBUG2 --> FIX_PERM_REMOTE
V2 -->|是| V3{菜单路径<br/>匹配视图?}
V3 -->|404 错误| DEBUG3[修复菜单 JSON 路径]
DEBUG3 --> COPY
V3 -->|是| V4{UI 正确<br/>加载?}
V4 -->|错误| DEBUG4[检查浏览器控制台<br/>修复 JavaScript 错误]
DEBUG4 --> COPY
V4 -->|是| TEST[浏览器测试<br/>隐私模式<br/>深色/浅色模式<br/>响应式]
TEST --> SUCCESS([部署成功])
style START fill:#6366f1,color:#fff,stroke:#4f46e5
style SUCCESS fill:#22c55e,color:#fff,stroke:#16a34a
style DEBUG1 fill:#ef4444,color:#fff,stroke:#dc2626
style DEBUG2 fill:#ef4444,color:#fff,stroke:#dc2626
style DEBUG3 fill:#ef4444,color:#fff,stroke:#dc2626
style DEBUG4 fill:#ef4444,color:#fff,stroke:#dc2626
style CHECK_DISK fill:#f59e0b,color:#fff,stroke:#d97706
style LOCAL_VAL fill:#8b5cf6,color:#fff,stroke:#7c3aed
```
**部署阶段:**
1. **本地验证:** 运行 `validate-modules.sh``fix-permissions.sh --local`
2. **预检:** 磁盘空间和权限验证
3. **文件传输:** 复制 JavaScript、CSS 和 RPCD 脚本
4. **远程设置:** 修复权限并清除缓存
5. **服务重启:** 重新加载 rpcd 和 uhttpd 守护进程
6. **验证:** 多阶段验证ubus、文件、菜单、UI
7. **测试:** 隐私模式下的浏览器测试
**常见错误恢复路径:**
- **对象未找到(-32000** 检查 RPCD 脚本命名和权限
- **403 Forbidden** 将 CSS/JS 文件权限修复为 644
- **404 Not Found** 验证菜单路径与视图文件位置匹配
- **JavaScript 错误:** 检查浏览器控制台并修复代码问题
---
### 部署前检查(关键)
**在任何部署之前始终执行这些检查:**
#### 1. 磁盘空间检查
```bash
# 在目标路由器上
ssh root@192.168.8.191 "df -h | grep overlay"
# 验证使用率 < 90%
# 正常示例:
# /dev/loop0 98.8M 45.2M 53.6M 46% /overlay
# 危险示例(停止部署):
# /dev/loop0 98.8M 98.8M 0 100% /overlay ← 满了!
```
**如果 overlay 满了≥95%**
```bash
# 部署前释放空间
ssh root@192.168.8.191 << 'EOF'
# 删除临时文件
rm -rf /tmp/*.ipk /tmp/luci-* 2>/dev/null
# 删除旧备份(>7 天)
find /root -name '*.backup-*' -type f -mtime +7 -delete 2>/dev/null
# 检查未使用的包
opkg list-installed | grep -E 'netdata|unused'
# 清理后,验证释放的空间
df -h | grep overlay
EOF
```
**需要监控的典型大小:**
- Netdata Web UI~22MB如果不使用考虑删除
- LuCI 模块:每个约 1-2MB
- CSS/JS 文件:每个约 10-50KB
#### 2. 权限检查(避免 403 错误的关键)
**必需的权限:**
| 类型 | 权限 | 八进制 | 原因 |
|------|------|--------|------|
| **RPCD 脚本** | `rwxr-xr-x` | `755` | 系统可执行 |
| **CSS 文件** | `rw-r--r--` | `644` | Web 服务器可读 |
| **JS 文件** | `rw-r--r--` | `644` | Web 服务器可读 |
| **JSON 文件** | `rw-r--r--` | `644` | rpcd 可读 |
**常见错误:** 文件创建为 `600`rw-------)而非 `644`
**症状:** 加载 JS/CSS 文件时 HTTP 403 Forbidden
**错误示例:**
```
NetworkError: HTTP error 403 while loading class file
"/luci-static/resources/view/netdata-dashboard/dashboard.js"
```
**快速诊断:**
```bash
# 验证已部署文件的权限
ssh root@192.168.8.191 "ls -la /www/luci-static/resources/view/MODULE_NAME/"
# 查找权限不正确的文件600
ssh root@192.168.8.191 "find /www/luci-static/resources/view/ -type f -name '*.js' -perm 600"
# 错误(导致 403
# -rw------- 1 root root 9763 dashboard.js ← 600 = web 不可读!
# 正确:
# -rw-r--r-- 1 root root 9763 dashboard.js ← 644 = OK
```
**立即修复:**
```bash
# 修复所有 CSS/JS 文件
ssh root@192.168.8.191 << 'EOF'
find /www/luci-static/resources/ -name '*.css' -exec chmod 644 {} \;
find /www/luci-static/resources/ -name '*.js' -exec chmod 644 {} \;
find /usr/libexec/rpcd/ -name 'luci.*' -exec chmod 755 {} \;
EOF
```
**自动修复(推荐):**
使用自动脚本检查并修复所有权限:
```bash
# 修复本地权限(源代码)
./secubox-tools/fix-permissions.sh --local
# 修复路由器权限
./secubox-tools/fix-permissions.sh --remote
# 修复两者(本地 + 远程)
./secubox-tools/fix-permissions.sh
```
`fix-permissions.sh` 脚本自动执行:
- 将所有 RPCD 脚本修复为 755
- 将所有 CSS 修复为 644
- 将所有 JS 修复为 644
- 验证没有 600 文件存在
- 清除缓存并重启服务(远程模式)
- 显示更改的完整报告
**权限自动验证:**
`validate-modules.sh` 脚本现在包含 Check 7自动验证权限
```bash
./secubox-tools/validate-modules.sh
# Check 7 将验证:
# ✓ 所有 RPCD 是 755
# ✓ 所有 CSS 是 644
# ✓ 所有 JS 是 644
# ✗ 如果权限不正确将显示错误
```
**推荐工作流:**
1. 开发/修改代码
2. `./secubox-tools/fix-permissions.sh --local`(提交前)
3. `./secubox-tools/validate-modules.sh`(验证所有)
4. 提交并推送
5. 部署到路由器
6. `./secubox-tools/fix-permissions.sh --remote`(部署后)
#### 3. 部署后验证
**部署后清单:**
```bash
#!/bin/bash
ROUTER="root@192.168.8.191"
MODULE="module-name"
echo "部署后验证"
echo ""
# 1. 验证磁盘空间
echo "1. 剩余磁盘空间:"
ssh "$ROUTER" "df -h | grep overlay | awk '{print \$5}'" || echo "失败"
# 2. 验证 CSS/JS 权限
echo "2. CSS/JS 权限:"
ssh "$ROUTER" "find /www/luci-static/resources/$MODULE -type f \( -name '*.css' -o -name '*.js' \) ! -perm 644" | \
if [ -z "$(cat)" ]; then echo "OK"; else echo "失败 - 权限不正确"; fi
# 3. 验证 RPCD 权限
echo "3. RPCD 权限:"
ssh "$ROUTER" "ls -l /usr/libexec/rpcd/luci.$MODULE | grep -q rwxr-xr-x" && echo "OK" || echo "失败"
# 4. 验证 ubus 对象
echo "4. ubus 对象可用:"
ssh "$ROUTER" "ubus list | grep -q luci.$MODULE" && echo "OK" || echo "失败"
# 5. 验证文件可访问HTTP
echo "5. Web 文件可访问:"
ssh "$ROUTER" "test -r /www/luci-static/resources/$MODULE/common.css" && echo "OK" || echo "未找到 common.css"
# 6. 验证缓存已清除
echo "6. LuCI 缓存已清除:"
ssh "$ROUTER" "test ! -f /tmp/luci-indexcache" && echo "OK" || echo "缓存仍存在"
echo ""
echo "验证完成"
```
#### 4. 常见部署错误
| 错误 | 原因 | 快速解决 |
|------|------|----------|
| **HTTP 403 Forbidden** | 权限 600 而非 644 | `chmod 644 *.js *.css` |
| **No space left on device** | Overlay 满了 | 清理 /tmp删除旧备份 |
| **Object not found -32000** | RPCD 不可执行或名称错误 | `chmod 755 rpcd/luci.*` + 检查名称 |
| **模块不显示** | LuCI 缓存未清除 | `rm /tmp/luci-*` + 重启服务 |
| **更改不可见** | 浏览器缓存 | 隐私模式 + Ctrl+Shift+R |
#### 5. 紧急磁盘空间恢复
**如果部署因 "No space left on device" 失败:**
```bash
#!/bin/bash
ROUTER="root@192.168.8.191"
echo "紧急磁盘空间恢复"
echo ""
# 1. 分析使用情况
echo "前 10 大消耗者:"
ssh "$ROUTER" "du -k /overlay/upper 2>/dev/null | sort -rn | head -10"
# 2. 清理临时文件
echo ""
echo "清理临时文件..."
ssh "$ROUTER" "rm -rf /tmp/*.ipk /tmp/luci-* /root/*.ipk 2>/dev/null"
# 3. 删除旧备份
echo "删除旧备份(>7 天)..."
ssh "$ROUTER" "find /root -name '*.backup-*' -mtime +7 -delete 2>/dev/null"
# 4. 选项:删除 Netdata Web UI释放 ~22MB
echo ""
echo "选项:删除 Netdata Web UI节省 ~22MB"
read -p "继续y/N" -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
ssh "$ROUTER" "opkg remove netdata-web 2>/dev/null || rm -rf /usr/share/netdata/web/*"
fi
# 5. 验证释放的空间
echo ""
echo "清理后的空间:"
ssh "$ROUTER" "df -h | grep overlay"
```
### 标准部署脚本模板
```bash
#!/bin/bash
# 部署 <模块名>
ROUTER="root@192.168.8.191"
MODULE="<module-name>"
LOCAL_DIR="/path/to/luci-app-$MODULE/htdocs/luci-static/resources"
REMOTE_DIR="/www/luci-static/resources"
echo "部署 $MODULE"
echo ""
# 1. 部署 JS 文件
echo "1. 复制 JS 文件..."
scp "$LOCAL_DIR/view/$MODULE/"*.js "$ROUTER:$REMOTE_DIR/view/$MODULE/"
scp "$LOCAL_DIR/$MODULE/api.js" "$ROUTER:$REMOTE_DIR/$MODULE/"
# 2. 部署 CSS 文件
echo "2. 复制 CSS 文件..."
scp "$LOCAL_DIR/$MODULE/"*.css "$ROUTER:$REMOTE_DIR/$MODULE/"
# 3. 部署 RPCD 后端
echo "3. 复制 RPCD 后端..."
scp "root/usr/libexec/rpcd/luci.$MODULE" "$ROUTER:/usr/libexec/rpcd/"
# 4. 修复权限
echo "4. 修复权限..."
ssh "$ROUTER" "chmod 755 /usr/libexec/rpcd/luci.$MODULE"
ssh "$ROUTER" "chmod 644 $REMOTE_DIR/$MODULE/*.css"
ssh "$ROUTER" "chmod 644 $REMOTE_DIR/view/$MODULE/*.js"
# 5. 清除缓存
echo "5. 清除缓存..."
ssh "$ROUTER" "rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null"
# 6. 重启服务
echo "6. 重启服务..."
ssh "$ROUTER" "/etc/init.d/rpcd restart"
ssh "$ROUTER" "/etc/init.d/uhttpd restart"
# 7. 验证
echo ""
echo "7. 验证..."
ssh "$ROUTER" "ubus list | grep luci.$MODULE"
echo ""
echo "部署完成!"
echo ""
echo "测试(隐私模式):"
echo " https://192.168.8.191/cgi-bin/luci/admin/secubox/path/to/$MODULE"
```
### 回滚流程
出现问题时:
```bash
#!/bin/bash
# 回滚到之前的版本
ROUTER="root@192.168.8.191"
BACKUP_DIR="/root/luci-backups/$(date +%Y%m%d)"
# 1. 部署前创建备份
ssh "$ROUTER" "mkdir -p $BACKUP_DIR"
ssh "$ROUTER" "cp -r /www/luci-static/resources/module-name $BACKUP_DIR/"
ssh "$ROUTER" "cp /usr/libexec/rpcd/luci.module-name $BACKUP_DIR/"
# 2. 出现问题时恢复
ssh "$ROUTER" "cp -r $BACKUP_DIR/module-name /www/luci-static/resources/"
ssh "$ROUTER" "cp $BACKUP_DIR/luci.module-name /usr/libexec/rpcd/"
ssh "$ROUTER" "/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart"
```
### 版本控制
**始终增加版本:**
```makefile
# Makefile
PKG_VERSION:=0.3.0
PKG_RELEASE:=1
```
```css
/* CSS 文件 */
/**
* 模块 - 样式
* 版本0.3.0
*/
```
```javascript
// JavaScript
// 版本0.3.0
```
**语义化版本:**
- MAJOR.MINOR.PATCH1.2.3
- MAJOR破坏性更改
- MINOR新功能向后兼容
- PATCH错误修复
---
## 快速参考
### 基本命令
```bash
# 验证
./secubox-tools/validate-modules.sh
# 构建(本地)
./secubox-tools/local-build.sh build luci-app-module-name
# 部署文件
scp file.js root@router:/www/luci-static/resources/
# 修复权限
ssh root@router "chmod 644 /www/luci-static/resources/**/*.css"
ssh root@router "chmod 755 /usr/libexec/rpcd/luci.*"
# 清除缓存
ssh root@router "rm -f /tmp/luci-indexcache /tmp/luci-modulecache/*"
# 重启服务
ssh root@router "/etc/init.d/rpcd restart && /etc/init.d/uhttpd restart"
# 测试 ubus
ssh root@router "ubus list | grep luci"
ssh root@router "ubus call luci.module-name getStatus"
# 验证 JSON
jsonlint file.json
jq . file.json
```
### CSS 类快速参考
```css
/* 布局 */
.sh-page-header /* 页面标题容器 */
.sh-page-title /* 页面标题(渐变文字)*/
.sh-page-subtitle /* 页面副标题 */
/* 统计 */
.sh-stats-grid /* 统计徽章网格(最小 130px*/
.sh-stat-badge /* 统计徽章容器 */
.sh-stat-value /* 统计值(等宽)*/
.sh-stat-label /* 统计标签(大写)*/
/* 卡片 */
.sh-card /* 卡片容器(悬停时渐变边框)*/
.sh-card-success /* 绿色边框卡片 */
.sh-card-danger /* 红色边框卡片 */
.sh-card-warning /* 橙色边框卡片 */
.sh-card-header /* 卡片标题 */
.sh-card-title /* 卡片标题 */
.sh-card-body /* 卡片内容 */
/* 按钮 */
.sh-btn /* 基础按钮 */
.sh-btn-primary /* 主要按钮(渐变)*/
.sh-btn-success /* 成功按钮(绿色)*/
.sh-btn-danger /* 危险按钮(红色)*/
.sh-btn-secondary /* 次要按钮(轮廓)*/
/* 选项卡 */
.sh-filter-tabs /* 过滤选项卡容器 */
.sh-filter-tab /* 过滤选项卡 */
.sh-filter-tab.active /* 活动过滤选项卡(渐变)*/
.sh-nav-tabs /* 导航选项卡(固定)*/
.sh-nav-tab /* 导航选项卡 */
.sh-nav-tab.active /* 活动导航选项卡(下划线)*/
/* 工具 */
.sh-gradient-text /* 渐变文字效果 */
.sh-id-display /* 等宽 ID 显示 */
.sh-empty-state /* 空状态占位符 */
```
### 颜色变量快速参考
```css
/* 文字 */
var(--sh-text-primary) /* 主要文字 */
var(--sh-text-secondary) /* 次要文字 */
/* 背景 */
var(--sh-bg-primary) /* 主背景 */
var(--sh-bg-secondary) /* 次要背景 */
var(--sh-bg-tertiary) /* 第三背景 */
var(--sh-bg-card) /* 卡片背景 */
/* 边框 */
var(--sh-border) /* 边框颜色 */
/* 颜色 */
var(--sh-primary) /* 靛蓝 #6366f1 */
var(--sh-primary-end) /* 紫色 #8b5cf6 */
var(--sh-success) /* 绿色 #22c55e */
var(--sh-danger) /* 红色 #ef4444 */
var(--sh-warning) /* 橙色 #f59e0b */
/* 效果 */
var(--sh-shadow) /* 盒子阴影 */
var(--sh-hover-shadow) /* 悬停阴影 */
var(--sh-hover-bg) /* 悬停背景 */
```
---
## AI 助手上下文文件
SecuBox 工作在 Claude 和 Codex 助手之间共享。保持上下文文件夹同步,以便任何代理都能快速恢复工作:
| 目录 | 文件 | 用途 |
|------|------|------|
| `.claude/` | `HISTORY.md` | UI/主题更改和主要部署的时间日志 |
| `.claude/` | `TODO.md` | 高级待办事项UX 改进、文档、自动化想法)|
| `.claude/` | `WIP.md` | 活动任务、风险和即时下一步 |
| `.codex/` | `HISTORY.md` | Codex 会话的开发时间线镜像 |
| `.codex/` | `TODO.md` | 面向工具的任务linting、脚本、构建自动化|
| `.codex/` | `WIP.md` | 正在进行的 Codex 工作状态跟踪器 |
**维护规则**
1. **每次会话后更新:** 完成工作时,在 HISTORY 中添加简短条目并调整 WIP/TODO 以反映新状态。
2. **引用部署脚本:** 记录使用了哪个 `secubox-tools/*.sh` 脚本(仪表板 vs 完整部署),以便下一个助手知道如何重现。
3. **保持条目简洁:** 每次更新一个段落或要点即可;详细规范保留在 DOCS 中。
4. **重大更改前交叉检查:** 开始工作前阅读两个文件夹以避免冲突或重复工作。
将这些文件视为活的交接笔记——如果它们偏离,新 AI/团队成员的入职将显著变慢。
---
## 结论
本指南应在以下情况**之前**查阅:
1. 创建新模块
2. 修改现有样式
3. 添加 RPCD 方法
4. 部署到路由器
5. 调试错误
**如有疑问,始终:**
1. 查阅本指南
2. 执行 validate-modules.sh
3. 在隐私模式测试
4. 检查浏览器控制台F12
**补充资源:**
- CLAUDE.md - 架构和构建
- secubox-tools/validate-modules.sh - 自动验证
- Templates/ - 代码模板
---
**最后更新:** 2025-12-26
**维护者:** CyberMind Studio
**指南版本:** 1.0.0