轻量自托管导航面板。Go 纯后端 · 零 CGO 单二进制 · 前端内嵌 · 三架构 Docker 镜像约 14MB。
- GitHub:https://github.com/Ozero-top/SolarPanel-v3
- Docker Hub:https://hub.docker.com/r/ovitor/solarpanel-v3
- DEMO:https://demo.ozero.top
- 零依赖单二进制 — Go + 纯 Go SQLite(
modernc.org/sqlite),无 CGO、无外部数据库、无 PHP;三架构镜像约 14MB,单文件即可运行 - 空库启动 — 首次启动仅建表 + 默认设置 + 管理员账号,不注入任何预置内容;「恢复初始状态」同样只清数据,不生成演示项
- 两级分组导航 — 父分组折叠、子分组缩进;实时搜索按「卡片 → 子分组 → 父分组」三级命中,排序与显隐仅在同级生效
- 十套主题 — 液态玻璃(默认)/ 柔和浮雕 / 工程蓝图 / 清透极简 / 柔和工具 / 动态极光 / IRIS 深空 / 铅笔手绘 / 复古怀旧 / 企业简洁;明暗模式、卡片样式与圆角留白可独立配置
- 热点新闻聚合 — 24 个内置源(微博 / 知乎 / B站 / GitHub Trending / IT之家 / V2EX / 少数派 / 掘金 等)+ 自定义 RSS,支持勾选、排序与独立入口
- NAS 运行状态面板 — 主机名 / 内网 IP / CPU / 内存 / 存储池 / 网络 / 温度 / 容器;四种放置方式 + 近 10 分钟曲线 + 详情弹窗;兼容群晖 DSM、绿联 UGOS、飞牛 fnOS、威联通 QTS、极空间 ZOS、iStoreOS / OpenWrt / ImmortalWrt
- 天气与壁纸 — 顶部时钟与天气组件;每日壁纸多源合并、失败重试、图库管理与上传;后台可按源随机预览 10 张再固定;内置壁纸只读不可删除,删除操作仅作用于自己上传的壁纸
- 内外网自动切换 — 卡片可同时配置公网与内网地址,页面按访问者网段或后台配置的内网主机名自动选路,顶栏按钮可手动切换
- 字体系统 — 页面字体与时钟/天气字体各 12 款免费开源设计字体,按需加载、互不干扰,清空即跟随系统
- 搜索增强 — 多搜索引擎切换、搜索历史与常用搜索(置顶收藏)、快捷键
/或Ctrl+K聚焦搜索框 - 卡片与图标 — 图标上传、域名自动抓取 favicon、标题与描述自动获取;深色模式自动适配图标轮廓;右键菜单可新标签页 / 当前页打开、快速编辑或删除
- 键盘快捷键 —
/、Ctrl+K聚焦搜索,t切深浅色,n新闻视图,b/h导航站视图,g分组目录,?帮助,Esc逐级退出 - favicon 三级缓存 — 内存 inflight 去重 → 磁盘 MD5 缓存 → 三源并发抓取(主站 / Google s2 / DuckDuckGo);
ETag+immutable协商,二次访问 304 - 安全 — Session + CSRF 双令牌、接口限流、四级角色权限、TOTP 双因子、受信设备与审计日志
- PWA — Service Worker 离线缓存,可安装为应用;资源变更自动刷新缓存版本
- 有序幂等迁移 + 旧库兼容 —
schema_versions记录已应用版本;启动时另按PRAGMA table_info检测并补齐旧库缺失字段,避免no such column直接 500 - 备份与迁移 — JSON 一键导出/导入(含上传文件 base64),分组按
parent_id排序并重建 ID 映射;支持 Chrome / Edge 书签 HTML 导入
| 层 | 技术 |
|---|---|
| 后端 | Go 1.25 · chi/v5 v5.4.0 · modernc.org/sqlite v1.34.5 |
| 前端 | 原生 JS · CSS 变量主题系统 · PWA Service Worker |
| 构建 | Node 24(生成主题 CSS + 注入 SHA1 资源戳) · Go embed |
| 部署 | Docker 三阶段构建 · Alpine 3.22 |
直接依赖仅 3 个:
github.com/go-chi/chi/v5 — HTTP 路由 + 中间件
golang.org/x/crypto — bcrypt / sha256 / TOTP
modernc.org/sqlite — 纯 Go SQLite driver(零 CGO)
镜像 ovitor/solarpanel-v3:latest,支持 linux/amd64 · linux/arm64 · linux/arm/v7。
docker run -d \
--name solarpanel-v3 \
--restart unless-stopped \
-p 10086:10086 \
-v $(pwd)/data:/app/data \
-v /:/host:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-e SERVER_PORT=10086 \
-e NAS_HOST_ROOT=/host \
-e DOCKER_SOCKET=/var/run/docker.sock \
ovitor/solarpanel-v3:latest后两个挂载仅用于 NAS 运行状态面板(读取宿主 /proc、/sys、存储池与容器数据)。不需要该功能时删掉
-v /:/host:ro、-v /var/run/docker.sock:...与两个-e即可,面板会自动隐藏。
Windows PowerShell:
docker run -d --name solarpanel-v3 --restart unless-stopped -p 10086:10086 -v ${PWD}/data:/app/data -v /:/host:ro -v /var/run/docker.sock:/var/run/docker.sock -e SERVER_PORT=10086 -e NAS_HOST_ROOT=/host -e DOCKER_SOCKET=/var/run/docker.sock ovitor/solarpanel-v3:latestgit clone https://github.com/Ozero-top/SolarPanel-v3.git
cd SolarPanel-v3
docker build -t solarpanel-v3:local .
docker run -d --name solarpanel-v3 -p 10086:10086 -v $(pwd)/data:/app/data solarpanel-v3:localservices:
solarpanel:
image: ovitor/solarpanel-v3:latest # 本地构建改成 build: .
container_name: solarpanel-v3
restart: unless-stopped
ports:
- "10086:10086"
# - "18443:18443" # HTTPS_PORT 非空时启用
volumes:
- ./data:/app/data
- /:/host:ro # NAS 监控:宿主 /proc、/sys、存储池
- /var/run/docker.sock:/var/run/docker.sock # NAS 监控:容器列表与占用
environment:
- TZ=Asia/Shanghai
- SERVER_PORT=10086
- NAS_HOST_ROOT=/host
- DOCKER_SOCKET=/var/run/docker.sock
# - HTTPS_PORT=18443 # 启用内置自签名 HTTPS
# NAS 监控需要「整机网卡」口径时改用 host 网络(ports 映射会失效,直接访问 10086):
# network_mode: hostdocker compose up -d # 启动
docker compose logs -f # 查看日志
docker compose down # 停止
docker compose up -d --build # 重新构建启动后访问 http://localhost:10086。数据目录 ./data/ 自动创建,包含 solarpanel.db 与 uploads/。
镜像内未声明
VOLUME,挂载到/app/data即持久化;不挂载则重建容器后数据随之丢失。
适用于 iStoreOS、ImmortalWrt 与带 Docker 的原生 OpenWrt(ARMv7 / ARM64),镜像会按设备架构自动匹配。先在「应用商店 / 软件包」中安装 Docker(iStoreOS 为「iStore 应用商店 → Docker」),再执行:
docker run -d --name solarpanel-v3 --restart unless-stopped \
-p 10086:10086 \
-v /volume1/docker/solarpanel/data:/app/data \
-v /:/host:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-e NAS_HOST_ROOT=/host -e DOCKER_SOCKET=/var/run/docker.sock \
ovitor/solarpanel-v3:latest- 数据目录按设备实际存储位置填写,例如
/mnt/sda1/docker/solarpanel/data或/overlay/docker/solarpanel/data - 面板会自动识别 OpenWrt 系系统名与
/overlay可用容量;不需要容器列表时可去掉docker.sock挂载与DOCKER_SOCKET - 需要整机网速口径时追加
--network host并去掉-p 10086:10086,直接访问http://设备IP:10086 - 低配设备(内存 ≤ 512MB)建议把后台「刷新间隔」调到 5~10 秒,减少采集开销
早期版本默认使用 10080,但该端口在 Chromium 的受限端口列表(net/base/port_util.cc 的 kRestrictedPorts,10080 属于 Amanda 备份服务)中,浏览器会直接以 ERR_UNSAFE_PORT 拦截,页面表现为「打不开」。因此默认端口改为 10086。
若需换端口,修改 SERVER_PORT 与端口映射即可,但需避开 Chromium 受限端口(完整列表见 Chromium 源码 net/base/port_util.cc 中的 kRestrictedPorts)。
在后台 站点设置 → 🖥️ NAS 监控 开启后,主页展示宿主机与容器状态:
| 放置方式 | 说明 |
|---|---|
| 右侧悬浮(默认) | 贴内容区右侧,可收起为竖条 |
| 左侧悬浮 | 贴内容区左侧,与左侧分组目录互斥 |
| 左右双侧 | 左主机指标 / 右容器列表,两侧同步收起 |
| 卡片下方状态条 | 单行概览 + CPU 迷你曲线,整条可点开详情 |
移动端(≤768px)统一自动使用状态条;任一形式都可通过「详情」打开完整弹窗(存储池、近 10 分钟网络曲线、容器端口 / CPU / 内存 / 网络 / 运行时长)。
数据来源与限制:
- 主机指标读取挂载的宿主
/proc、/sys、/etc:CPU、内存、负载、运行时长、存储池、温度 - 容器指标通过
/var/run/docker.sock读取(无 Docker 时只显示主机信息,不影响使用) - 系统识别:群晖 DSM、绿联 UGOS、飞牛 fnOS、威联通 QTS、极空间 ZOS、iStoreOS / OpenWrt / ImmortalWrt,其余归为 Linux
- 存储自动识别:
/volume*、/vol*、/share/CACHEDEV*_DATA、/mnt/*、/media/*、/overlay;特殊路径可在后台手填 - 网络口径:默认 bridge 网络下只能统计容器网卡,面板会标注「容器网卡」;需要整机速率请改用
network_mode: host - 温度:物理机正常读取 thermal/hwmon;虚拟机(KVM/ESXi)不暴露传感器,面板显示「虚拟机」
- 磁盘与温度依赖
/:/host:ro挂载;未挂载时相应字段为空,不影响其他功能
# 交叉编译(产物为静态链接,无运行时依赖)
CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o solarpanel .
CGO_ENABLED=0 GOOS=darwin go build -ldflags="-s -w" -o solarpanel .
CGO_ENABLED=0 GOOS=windows go build -ldflags="-s -w" -o solarpanel.exe
# 运行(当前工作目录下自动创建 data/)
./solarpanel程序以当前工作目录为数据根:
data/solarpanel.db、data/uploads/。
# 1. 生成前端产物(Node 24+)
node scripts/build-themes.js # themes.json → themes.css + 主题白名单
node scripts/build-frontend.js # 注入 SHA1 资源戳 + 生成 sw.js 版本
# 2. 后端直接跑(前端已 //go:embed all:frontend)
go run .全部可选,未设置时使用默认值:
| 变量 | 默认 | 说明 |
|---|---|---|
SERVER_PORT |
10086 |
HTTP 监听端口 |
NAS_HOST_ROOT |
/host(不存在则留空) |
NAS 监控读取的宿主机根目录;容器内由 -v /:/host:ro 提供,单二进制运行留空即读本机 |
DOCKER_SOCKET |
/var/run/docker.sock |
Docker 守护进程 socket,用于容器列表与占用 |
HTTPS_PORT |
(空,不启用) | HTTPS 端口,设置后自动生成自签名证书并同时监听 |
CERT_FILE |
data/cert.pem |
HTTPS 证书路径 |
KEY_FILE |
data/key.pem |
HTTPS 私钥路径 |
数据库路径、上传目录、Session 有效期(24h)、Cookie 名(sp_session / csrf_token)在 internal/config/config.go 中固定,不通过环境变量覆盖。
| 角色 | 权限 |
|---|---|
admin |
全部功能,含用户管理、审计清理、备份恢复 |
editor |
内容管理(分组 / 卡片 / 上传 / 导入),不可管理用户 |
viewer |
只读,可查看后台不可修改 |
guest |
访客,仅主页展示;后台开启「访客访问」后凭访客密码进入 |
- 初始管理员:
admin/admin123,首次登录后请立即修改密码 - 支持 TOTP 双因子(
auth.php?action=2fa_setup / 2fa_enable / 2fa_disable)与受信设备管理 - 系统保证至少保留一个
admin,不允许降级/删除最后一个管理员
SolarPanel/
├── main.go # 入口:DB 初始化 + 路由注册 + HTTP/HTTPS 服务 + 预置壁纸落地
├── go.mod / go.sum
├── Dockerfile # 三阶段:Node 前端 → Go builder → Alpine 运行时
├── docker-compose.yml
├── scripts/
│ ├── build-themes.js # themes.json → themes.css + 主题白名单
│ └── build-frontend.js # SHA1 资源戳注入 · sw.js 版本号
├── frontend/ # 前端源码(构建产物 embed 进二进制)
│ ├── themes.json # 主题配置唯一事实源
│ ├── index.html / admin.html / login.html / sw.js
│ ├── uploads/weather/ # 7 张预置天气壁纸(首次启动复制到 data/uploads/weather)
│ └── assets/
│ ├── css/ # common.css · index.css · themes.css · auth-admin.css
│ └── js/
│ ├── api.js # APP_VERSION + APP_CHANGELOG + 请求封装
│ ├── index.js # 主页逻辑(public.php → render)
│ ├── admin.js # 后台管理
│ └── login.js
├── internal/
│ ├── config/config.go # 环境变量 → Config
│ ├── settings/defaults.go # 设置默认值唯一事实源
│ ├── auth/ # Session · CSRF · RateLimit · Audit · TOTP · RequireLogin
│ ├── db/
│ │ ├── db.go # DSN/连接池 · DDL · 旧库补列 · 默认设置/管理员初始化 · 孤儿分组清理
│ │ └── migrations.go # schema_versions 有序幂等迁移
│ ├── handler/ # 各业务 handler(见「路由一览」)
│ └── nas/ # NAS 采集:宿主 /proc、/sys、存储池、Docker API、系统识别
├── presets/ # 示例备份数据(仅仓库提供,不参与构建与镜像)
│ └── nav-cards-backup.json # 通用导航卡片:10 个分组 / 150 张卡片
└── data/ # 运行时数据(自动创建,勿提交)
├── solarpanel.db # SQLite 数据库
└── uploads/ # icons / logos / wallpapers / favicon / weather
- 类型:SQLite(WAL 模式),
modernc.org/sqlite纯 Go driver - 连接池:
SetMaxOpenConns(15);journal_mode=WAL·synchronous=NORMAL·busy_timeout=15000·foreign_keys=ON·cache_size=-20000·temp_store=MEMORY- 这些 PRAGMA 通过 DSN 的
_pragma=参数下发,每条连接独立生效;若只在启动时DB.Exec一次,池内其余连接仍是默认值(busy_timeout=0),并发下会直接SQLITE_BUSY导致接口 500 - 启动日志会打印实际生效的
journal_mode;若显示delete而非wal,说明数据目录所在文件系统(SMB / NFS 等网络盘)不支持 WAL
- 这些 PRAGMA 通过 DSN 的
- 表:
users·settings·item_groups·items·custom_feeds·audit_logs·schema_versions·rate_limits·trusted_devices - 迁移:
schema_versions记录已应用版本;新增 DDL/DML 追加到migrations列表末尾,Version必须严格递增且不得修改历史项 - 旧库补列:
CREATE TABLE IF NOT EXISTS对已存在的表不生效,迁移也只覆盖少数几列。启动时会执行ensureColumns(),按PRAGMA table_info比对items/item_groups/users/settings/audit_logs/custom_feeds的期望列,缺失即ALTER TABLE ADD COLUMN补齐(幂等、保留原有数据);补列会打印[db] 旧库补列 表.列 ✅ - 初始化:空库只写入
internal/settings/defaults.go的默认设置与admin/admin123 - 重置:后台「恢复初始状态」= 恢复默认设置 + 清空分组/卡片 + 清空用户上传(保留
uploads/weather预置壁纸),不会重新生成任何演示内容 - 孤儿分组清理:启动时若
parent_id指向不存在的分组,自动重置为0(顶级)
主题唯一事实源为 frontend/themes.json,当前十套:liquid-glass(默认,仅浅色)、soft、blueprint、clarity、soft-utility、aurora、iris、sketch-style、retro-vintage 与 corporate-clean。
{
"theme_id": {
"display": "theme_id",
"light": { "--css-var": "value" },
"dark": { "--css-var": "value" },
"extra_css": ["额外 CSS 片段"]
}
}新增主题后执行 node scripts/build-themes.js,自动同步:
frontend/assets/css/themes.css(主题样式)api.js中的主题白名单settings_handler.go的取值白名单settingValueWhitelist(需手动追加主题名)
默认明暗模式由设置项 default_theme(light / dark)控制,主题风格由 theme_style 控制。
所有接口挂在 /api/ 下,历史原因保留 .php 后缀。
| 方法 | 路径 | 限流 | 说明 |
|---|---|---|---|
| GET | /api/public.php |
60/min | 主页数据:设置 + 分组 + 卡片 + 站点信息 |
| GET | /api/install.php |
— | 首次安装状态 |
| POST | /api/install.php |
— | 执行首次安装 |
| GET | /api/weather.php |
10/min | 天气数据 |
| GET / POST | /api/news.php |
30/min | 新闻聚合调度 |
| GET | /api/news/meta |
30/min | 新闻源元信息列表 |
| GET | /api/news/all |
— | 全部源聚合结果 |
| GET | /api/news/source |
— | 单源新闻 |
| POST | /api/wallpaper.php |
10/min | 每日壁纸调度 |
| GET / POST | /api/auth.php |
— | 登录 / 登出 / 双因子 / 受信设备 / 访客登录 |
| GET | /api/favicon.php?host=xxx |
120/min | 站点图标代理(三源抓取 · 磁盘缓存 · ETag 协商) |
| GET | /api/nas.php |
240/min | NAS 主机与容器状态(需后台开启;访客可见性由设置控制) |
| GET | /api/version.php |
30/min | 版本检查:返回当前 / 最新版本与是否有更新;?refresh=1 跳过缓存(后台「立即检查」用) |
| 方法 | 路径 | action |
说明 |
|---|---|---|---|
| GET / POST | /api/settings.php |
list / save |
设置读取与保存 |
| GET / POST | /api/groups.php |
list edit delete sort sort_batch visible |
分组 CRUD、排序、显示隐藏 |
| GET / POST | /api/items.php |
list edit delete sort sort_batch toggle_hidden favicon fetch_meta |
卡片 CRUD、排序、隐藏、抓图标、抓标题 |
| POST | /api/upload.php |
type=icon|logo|wallpaper |
图标 5MB / Logo 5MB / 壁纸 10MB |
| GET / POST | /api/gallery.php |
GET 列表 · POST delete |
壁纸图库(用户 wallpapers + 预置 weather);预置项只读,删除仅对用户上传生效 |
| GET / POST | /api/backup.php |
export / import / reset |
备份导出、导入、恢复初始状态 |
| GET / POST | /api/user.php |
list create update delete change_password set_role set_password |
用户管理 |
| GET / POST | /api/audit.php |
list / clear |
审计日志 |
| GET / POST | /api/feeds.php |
list save delete toggle |
自定义 RSS 源 |
| POST | /api/import.php |
bookmarks |
浏览器书签 HTML 导入 |
| 路径 | 说明 |
|---|---|
GET / |
主页(frontend/index.html) |
GET /login.html |
登录页;已登录非 guest 会被重定向到 /admin.html |
GET /admin.html |
后台页;未登录重定向到 /login.html,guest 角色禁止访问 |
GET /favicon.ico |
站点图标 |
/uploads/* · /frontend/uploads/* |
用户上传目录(磁盘直读) |
| 其他路径 | embed 静态资源兜底(/assets/...) |
响应统一格式:{"code": 0, "data": ..., "msg": ""},code != 0 表示业务失败。
后台 6 个主模块:
| 模块 | 能力 |
|---|---|
| 设置 | 站点信息(标题 / Logo / 公告 / 备案 / 内外网访问)、外观与卡片(主题 + 明暗 + 卡片样式 + 布局留白 + 页面字体 + 时钟天气字体 + 壁纸)、导航与搜索(顶栏开关 + 搜索栏 + 搜索引擎清单与默认引擎)、访问控制(访客密码)、热点新闻(开关 + 24 源勾选排序 + 自定义 RSS)、NAS 监控(开关 / 放置方式 / 刷新间隔 / 访客可见 / 自定义存储路径 / 弹窗不透明度) |
| 卡片 | 新增/编辑/删除、拖拽排序、显示隐藏、图标上传或自动抓取、标题自动抓取 |
| 分组 | 两级分组 CRUD、拖拽排序、显示隐藏、图标与描述 |
| 账号 | 修改本人密码、TOTP 双因子、受信设备管理;管理员可增删用户与改角色 |
| 备份 | 导出 JSON、导入 JSON、恢复初始状态;书签 HTML 导入 |
| 审计 | 操作日志分页查看、按需清空(保留策略由 auth.AuditCleanup 每小时执行) |
前端体验:后台保存后,已打开的主页通过 localStorage 的 storage 事件自动拉新数据重渲染,无需手动刷新。
GET /api/backup.php?action=export 返回:
{
"version": "1.0",
"time": "2026-10-23 19:34:00",
"settings": [{ "id": 1, "config_name": "site_title", "config_value": "SolarPanel" }],
"groups": [{ "id": 1, "parent_id": 0, "title": "...", "icon": "...", "description": "...", "sort": 1, "is_visible": 1, "user_id": 1, "created_at": "" }],
"items": [{ "id": 1, "group_id": 1, "title": "...", "url": "...", "lan_url": "", "description": "...", "icon_type": "image", "icon_value": "...", "icon_bg": "", "open_method": 2, "sort": 1, "is_hidden": 0, "user_id": 1, "created_at": "" }],
"files": { "wallpapers/xxx.jpg": "<base64>" }
}files的键是相对data/uploads的路径;weather/下的预置壁纸不导出;单个文件超过 20MB 跳过- 默认以 JSON 下载,导入同时兼容「base64 包裹的 JSON」与纯 JSON
导入规则:
version仅作记录,不做严格校验;缺失的settings/groups/items视为空集- 导入是整体替换:先清空
items/item_groups/settings,再按备份重建;用户账号与已有上传文件不受影响 - 分组先按
parent_id升序处理,保证父分组先落库,再建立旧 ID → 新 ID映射;找不到父分组映射的子分组降级为顶级 - 卡片的
group_id经映射重写;所属分组映射不存在的卡片会被直接跳过 files写入前校验路径(拒绝..、限制在uploads内),.svg会先做安全清洗,同名覆盖
presets/nav-cards-backup.json 是一份通用导航站配置备份,包含 10 个分组 / 150 张卡片(AI 工具、视频、社交、开发、云盘等常用站点),可用于快速建站或演示:
- 后台 → 系统备份 → 导入配置,选择该文件
- 导入为整体替换,会覆盖当前分组 / 卡片 / 设置,操作前请先导出自己的备份
- 文件不含账号、访客密码与上传图片,导入后用户账号不受影响
- 导入后可按需修改分组名称、卡片地址与图标
presets/仅随仓库提供,已通过.dockerignore排除,不参与 Docker 构建,也不会打入镜像。
- WAL 快照隔离:
DB.Exec归还连接池后再DB.QueryRow可能落到另一条连接,读不到刚写入的数据。写后读必须放在同一个db.Tx内(groups_handler的保存与items_handler的toggle_hidden已按此实现) - 嵌套查询死锁:SQLite 连接池较小时,
rows.Next()遍历期间再发起QueryRow会阻塞。代码统一先收集结果并rows.Close()后再查 - 列与 Scan 必须一一对应:
scanItemGroup有 9 个目标(含parent_id),SELECT 少一列会让Scan报错并被误读为「记录不存在」 schema_versions是 OTA 关键表,禁止手动删除;历史迁移条目不可修改- 新增列必须同步三处:
schemaDDL、expectedColumns(旧库补列)与migrations追加项;只改建表语句对已部署的旧库无效 - 迁移要校验结果:
ALTER TABLE ... ADD COLUMN遇到「列已存在」会报错,不要把该错误当成成功并打标,否则会出现「日志显示已迁移、实际缺列」的假象 - SQLite PRAGMA 走 DSN:
DB.Exec("PRAGMA ...")只影响连接池中的一条连接,新增连接仍用默认值;需要全局生效的参数一律通过buildDSN()的_pragma=传入 - 设置默认值只改
internal/settings/defaults.go,db.go初始化与backup_handler.go重置必须同源,避免两处漂移 - 新增主题只改
frontend/themes.json并跑build-themes.js,不要手改themes.css - 前端资源变更后必须跑
build-frontend.js,否则浏览器仍命中旧 SHA1 缓存 - Docker 重建:
docker compose down && docker compose up -d --build;若镜像未更新,加--no-cache排除 buildx 缓存干扰
完整变更明细见 updates.md。维护约定:每次升版只更新 updates.md 与 frontend/assets/js/api.js 的 APP_VERSION / APP_CHANGELOG,README 不再维护版本条目。
V3 不自动迁移 V2.1 的数据库,请按「先导出、后导入」的顺序升级:
- 在 V2.1 系列后台 → 系统备份 → 导出配置,保存导出的备份文件
- 部署 V3(建议使用新的数据目录),首次启动为空库,使用
admin/admin123登录 - 在 V3 后台 → 系统备份 → 导入配置,选择第 1 步的备份文件完成恢复
注意事项:
- 导入为整体替换:V3 现有的分组、卡片与设置会被备份内容覆盖
- 备份中的上传文件(图标 / Logo / 壁纸)随备份一起导入,无需单独拷贝
uploads - 升级后立即修改默认管理员密码;旧版浏览器缓存建议强制刷新一次
直接让 V3 复用旧数据目录时,启动会尝试补齐旧库缺失字段以兼容读取;但旧版字段语义可能不同(例如隐藏标记、图标字段命名),推荐仍按上面的「导出 → 导入」流程升级,以保证分组、卡片与设置完整还原。
MIT