994d93cc98
feat(gateway): 一体化统一入口——路由与 Dashy 导航同一份数据、内置登录、原自定义错误页
...
目标:一个 HomeDash 容器取代 navi + portal-nginx + portal-auth。
- Route 扩展为"反代 + 导航"一条记录:kind(proxy/portal/auth)、streaming、
insecureTls、bypassPaths、injectBeian,以及 navEnabled/navSection/navName/
navDescription/navUrl/navIcon/navAutoIcon
- 保存路由时**同时**写 gateway.yml 与 conf.yml 的导航项(gateway_nav.go):
同 host 接管而不是重复、未勾选导航的会被清理、无关手工项与系统「历史访问」分组
一律不动;两步任一步失败都回滚(避免后台显示与导航页各说各话)
- 未知域名与上游故障改用**内置自定义 404/500 页面**(portal/ 目录),
不再是一行纯文本;HTTP→HTTPS 跳转段用 RedirectPort 复刻原 3081 设计
- 内置统一登录(auth.go):HMAC 签名 cookie、恒定时间比较、登录跳转白名单校验,
portal-auth 容器可以退场
- 自动抓图标对托管导航项同样生效(history-server 识别 naviGatewayAutoIcon)
- 后台卡片补齐"加入导航/分组/名称/描述/图标/自动抓图标/服务类型"字段
- 内部入口端口 8080 → 18080:为一体化 host 模式让开宿主机 blog(:8080)
- 修掉上一轮编辑写坏的 deploy/zd.sh 一行(会让脚本直接失败)
测试:新增自定义错误页、portal 类型、导航字段校验、导航同步(接管/清理/保留)、
分组不存在必须报错等用例;admin-server 与 history-server 全绿。
2026-09-11 21:27:40 +08:00
5f6fca5c59
fix(gateway): 未知域名必须被兜底拒绝,不能落进某条路由的后端
...
实测发现:入口端口上若没有匹配 server_name 的请求,nginx 会交给该端口排序
第一的 server 块,于是一个打错的域名会访问到别的服务(若第一条恰好是开放
路由,等于把该服务暴露在错误域名下)。现在为 HTTP/HTTPS 各生成一个
default_server 返回 404,并在单测中断言其存在。
2026-09-11 20:34:20 +08:00
507632609d
fix(deploy): 历史条数守卫改为与切换前对比,而不是写死条数
...
写死 4 条会把「用户自己在后台删了几条记录」误判成部署失败(本轮实际发生),
同时又发现不了真正的丢数据。改成切换前读一次旧容器的条数,切换后要求
新容器 >= 旧值:同步接口只累加不删除,所以变小即为异常。
2026-09-11 20:30:52 +08:00
19d082e70a
feat(gateway): 统一入口路由表(后端基础层)——"哪些服务走统一认证"变成一条数据
...
目标:不再手工维护外部 nginx 配置,把"域名→内网后端、是否走统一登录认证"
变成后台可编辑的数据,由本进程生成 nginx 配置并热加载。
本提交只做基础层(纯 Go、可测、零行为变更):
- gateway.go:Route/H gatewayConfig 数据模型 + ValidateRoute + RenderGatewayConf
· 认证**按路由**决定:RequireLogin 为真才生成 auth_request 与登录跳转,
也就是"哪些服务受统一认证保护"完全由路由表决定
· 支持 websocket 透传、单域名上传上限、HTTPS 段(可同时生成旁路端口)
· 路由表单独存 gateway.yml(不混进 Dashy 的 conf.yml),写坏了也不影响导航页
- API:GET/POST /admin/api/gateway/routes(保存)、POST /admin/api/gateway/apply
· apply 的顺序是 生成 → nginx -t 校验 → 通过才 reload;**校验失败把上一份可用
配置写回去并把 nginx 原始报错回给界面**,避免"点了应用整个入口挂掉"
- nginx.conf 加 include /app/data/gateway/*.conf(生成物在容器自己的卷里,
不碰宿主机文件,天然避开"单文件 bind mount 不能用 sed -i"的坑)
- 生成物默认监听旁路端口(8081/8443):可以先与正式入口并存验证,再切 80/443
测试:受保护域名必须有 auth_request、未受保护域名必须没有;websocket/上传上限;
空域名/带协议域名/非 http 后端/域名重复/需认证却无认证地址/启用 HTTPS 却无证书
六种非法输入必须报错;缺文件时回落默认配置、保存后回读一致。
2026-09-11 20:17:57 +08:00
a6b5513f88
docs+chore: 部署脚本入库、README 补零停机部署与部署物料清单
...
仓库此前缺少部署脚本,只能靠临时文件操作,新人无法复现。补齐:
- deploy/zd.sh:零停机部署(新容器先在对侧端口验证到底,含 /search 预热
SearXNG;再同 inode 覆写门户上游 + nginx -t + reload;切后哨兵"不得为 502";
失败自动回滚,旧容器保留为回滚点)。可用环境变量覆盖主机/路径/镜像。
- deploy/verify-ui.js:部署后浏览器验收(渲染开销、后台窄屏、收边两段式、
被拒内嵌弹层、分组增删改与「历史访问」保护)。任一运行时异常即判失败。
README 新增:
- 「零停机部署」含**单文件 bind mount 不能用 sed -i** 的坑(换 inode → 容器读到
旧值 → 全站 502,本项目真实踩过)与正确顺序(同 inode 覆写 → 断言容器看到 →
nginx -t → reload)
- 「部署需要的全部自定义文件」清单表
- 「管理后台」补分组自由增删、历史分组保护、完整 API 表
- 「工作空间视图」补可内嵌性三态探测(转发 Cookie、WAF 403 误判)、被拒弹层、
悬浮控件收边与"第一次交互只滑出"
2026-09-11 20:13:44 +08:00
26a0b15014
perf(web)+fix(admin): 撤掉卡片层模糊、补隐藏动画、后台窄屏适配、收边两段式
...
1) 卡顿:撤掉"重复且参与动画"的卡片层 backdrop-filter
实测首页 37 个元素带模糊(24 张卡片 + 6 个分组容器 + 顶栏 + 悬浮件)。
模糊要对背景逐帧重采样,而卡片又参与逐张淡入与悬停位移,叠加就是卡顿。
现在只保留分组容器的玻璃感与悬浮件(搜索栏因宽/圆角/内边距一直在变也去掉)。
实测:/ 从 37 → 12,/minimal 从 15 → 4。
注意:选择器必须写成 html[data-theme] body a.item —— 主题的 customCss 由
Dashy 运行时注入,同权重 + 同为 !important 时靠文档顺序决胜,写在 navi.css
里的规则会输,必须提高特异性。
2) 隐藏/出现补动画
≤380px 让位隐藏按钮组:加上 opacity+位移过渡,并把 visibility 延迟到淡出
结束再切(否则瞬间消失、看不到过渡)。弹层关闭先加 .closing 淡出再摘节点。
降动效模式下不再用 transform:none 抹掉"收边"的位移,避免隐藏失效。
3) 工作空间收边改为"第一次交互只滑出"
收起态点一下只滑出、不触发(否则贴边的按钮会被直接点中,比如想看搜索框
结果把管理后台打开了);第二次点击才生效。判据是"元素是否真的露在视口内",
所以鼠标悬停已展开时一次点击即生效。6 秒无操作或 Esc 自动收回。
4) 后台窄屏:分组行原本是内联 flex + 固定 max-width,窄屏既不能换行也不能
收缩,输入框顶破卡片。改用类 + ≤560px 适配(竖排、整行占满、按钮右对齐、
字号 16px 防 iOS 聚焦缩放)。实测 390px:scrollWidth == innerWidth、
零横向溢出。
2026-09-11 20:13:44 +08:00
76a6a83e7d
feat(web): 不支持内嵌时明确弹层;修掉被 promise 吞掉的 ReferenceError
...
1) 点不支持在工作区显示的应用 → 弹出提示卡片
标题「该应用不支持在工作区显示」+ 域名 + 服务器返回的原因 +
「在新标签页打开 / 知道了」两个按钮;面板里同时保留同样的说明。
之前只在面板里显示一行字,用户点了之后缺少明确反馈。
2) 修 ReferenceError: openUrl is not defined(线上真实故障)
setWorkspaceState 第 4 个参数早先从 openUrl 改名为 note 时,blocked
分支漏改,于是每次被拒内嵌都在函数内部抛异常,异常沿 promise 链被吞,
导致后面的弹层根本没有执行、面板提示也只渲染到一半(URL 行和按钮缺失)。
教训:验收必须采集 Runtime.exceptionThrown,并且断言交互元素真的存在;
只断言"状态=blocked + 提示文字前缀"会恰好掩盖这类错误。
2026-09-11 19:52:19 +08:00
0d9619e446
feat(admin): 导航分组可增 / 可改名 / 可删;初始状态无分组
...
后台此前只能往已有分组里加服务,分组本身是写死在 conf.yml 里的,
用户无法增删。现在新增「导航分组」卡片:
- POST /admin/api/section 新增(重名 409、空名 400)
- POST /admin/api/section/update 改名/换图标
- DELETE /admin/api/section 删除;非空分组必须 force=1(二次确认),
避免误点一下连带删掉一组服务
「历史访问」分组由 history-server 依据浏览记录重建,改名/删除/手工新建
都会被它覆写回来,所以一律 409 并说明原因 —— 否则用户以为删掉了,
下次同步又"长"回来。
初始状态无分组:conf.example.yml 的 sections 改为空数组,后台在两个位置
给空态提示(「还没有任何分组」/「先创建分组」,并把"添加服务"置灰)。
补上原本缺失的一环:rebuildDashySections() 原先只更新已存在的
「历史访问」分组、从不创建它,新装用户永远看不到历史分组;现在缺了就补建
(完全空配置时不补,保持"初始无分组")。
测试覆盖:新增/重名/改历史分组/删历史分组/删非空(无 force 拒绝)/force 删除,
以及删除后的分组名列表断言。
2026-09-11 19:52:19 +08:00
f40e54287b
fix(web): 极窄屏备案号不再顶到按钮;mini 叶签两端改为渐变淡出
...
1) 备案号与自定义按钮只剩 1px
实测 320x640:备案号换行成两行、高 66px,占据距底 11..77;而搜索球/
按钮组固定在 bottom:76px → 两者实测只剩 1px(视觉上已贴上)。
修法:≤380px 时把按钮上移到 bottom:88px,并收紧页脚 gap/padding
(两行高度仍 66px)→ 实测净间隙 11px。≥390px 时备案号是单行 39px,
间隙本就充足,保持不变。
2) mini 顶部叶签被两侧按钮硬切
结构是 [‹按钮 34px][滚动行][›按钮 34px],滚动行 overflow-x:auto
(内容 858px / 可视 294px),卡片直接在滚动行边缘被切断,没有任何过渡。
修法:给滚动行加 mask-image 两端各 24px 渐变淡出,并用 :has() 关联
翻页按钮的 disabled 状态 —— 已经在尽头的一侧不再淡出(两端都到头则
完全不遮罩),避免"到头了还在渐隐"的错觉。
实测:最左时 mask 为「左侧实心 + 右侧渐隐」,滚到最右变为
「左侧渐隐 + 右侧实心」。
2026-09-11 19:18:00 +08:00
8ba5e5117b
feat(web): 工作空间内嵌网页 + 控件收边 + 页脚去重 + 窄屏不再重叠
...
1) 工作空间真的在中间打开网页
Dashy 的面板就是 iframe,但它的 launchApp() 只在 target 不是 newtab 时
才把 URL 灌进 iframe;而且侧栏项目大多没有 per-item target,Dashy 走的是
Vue 直接改 iframe.src 的路径 —— 根本不经过 window.open,导致判定逻辑被
绕过(实测:Gitea 仍被内嵌、空白)。现在两条路径统一进 verifyAndShow():
先探测再决定内嵌还是明确提示;被拒时还会清空 iframe 的 src。
2) 控件在工作空间收边
中间嵌的是别人的应用,常驻控件会遮挡。搜索球收左边缘、右下角两个按钮
组成 dock 整组收右边缘、备案号收下边缘,各露 14px(备案号 13px),
鼠标移入或点击、聚焦时滑出展开。只在工作空间生效。
3) 修 iframe 撑高页面 77px
Dashy 的 .web-content iframe 是 position:absolute,包含块是 #dashy 而非
.web-content,外层 overflow:hidden 拦不住它,它按 #dashy 高度撑到 814px
(164+814=977 正好等于页面 scrollHeight)。补 position:static 后归零。
4) 首页不再出现两个备案号
Vue 异步挂载,navi.js 可能先补了一份页脚,之后 Dashy 的页脚才出现;
原来的守卫只判断 .navi-footer 是否存在,不会回收自己早先注入的那份。
新增 dashyFooterNode() 区分两者,发现 Dashy 的就把我们补的移除。
5) 窄屏展开搜索框不再压住右下按钮组
手机端展开宽度是 calc(100% - 86px),而按钮组占最后 114px → 重叠 38px。
改为按按钮组真实占位预留(≥600px: min(560px, 100% - 158px);
≤599px: calc(100% - 132px));≤380px 时输入框会窄到不可用,改为
临时隐藏按钮组(用 visibility 而非 transform,因为 prefers-reduced-motion
会强制 transform:none)。
2026-09-11 19:04:38 +08:00
22c405d2a0
feat(admin): 新增可内嵌性三态探测 /admin/api/frameable
...
Dashy 的「工作空间」用 iframe 打开应用,被 X-Frame-Options /
CSP frame-ancestors 拒绝时浏览器只给一片空白,而且 JS 无法区分
「被拒绝」与「加载中」(onload 一样触发)。所以由服务端读响应头。
实测 24 个应用:19 个可内嵌;Gitea(含内网 IP) 与 docker.com 是
SAMEORIGIN,Dify 是 DENY。
三态而非布尔,因为探测本身会失真:
- conclusive=true frameable=false → 有明确拒绝证据(XFO / frame-ancestors)
- conclusive=true frameable=true → 2xx 且无拒绝头
- conclusive=false → 探测被 WAF 拦成 403、或跳到了别的主机
这类不给结论,前端照常尝试内嵌,但在工具条里说明原因并留出新标签页退路。
两个关键细节:
1) 转发浏览器自己的 Cookie。门户后面的应用对未登录请求会 302 到登录页
(登录页没有 X-Frame-Options),不转发会话就会把「重定向到登录页」
误判成「可内嵌」——这是线上真实发生过的误判。
2) 用普通浏览器 UA。自造的 "HomeDash embed check" UA 会被 WAF 当机器人拦成 403。
测试覆盖:无头/有 XFO/有 CSP frame-ancestors/WAF 403/带与不带 Cookie。
2026-09-11 19:04:38 +08:00
da196abcb5
feat(web): 隐藏原生设置、圆形视图切换、工作空间改列表、三视图标题统一
...
用户提出的四条,都基于实测数据而不是猜测:
1) 隐藏 Dashy 原生设置入口
顶栏的 .config-launcher(齿轮+铅笔+调色板)与我们的 /admin/ 重复,
连同 .options-trigger/.settings-outer 一起隐藏。
2) 视图切换图标改成圆形,紧贴自定义入口左侧
原生切换按钮是侧栏底部的方形图标(.switch-view-buttons),已隐藏;
改为右下角圆形悬浮按钮 .navi-view-switch,点开是「首页/极简/工作空间」
菜单并标出当前视图。
顺带修掉 2px 错位:管理入口是内容盒(48+2px 边框=50),视图按钮是
边框盒(48),并排时差 2px;给管理入口补 box-sizing: border-box。
3) 工作空间「点了没反应」
读 Dashy 自己的 Workspace 组件(assets/Workspace-*.js)得到结论:
右侧面板就是一个 iframe,launchApp() 只有 target 不是 newtab 才把 URL
灌进面板;我们全局 openingMethod: newtab,所以每次点击都走 window.open
分支,this.url 恒为空 → 面板永远空白。
就算灌进去也没用:Gitea 实测 X-Frame-Options: sameorigin 拒绝被嵌
(Refused to display … in a frame)。
所以改成:点左侧分组 → 右侧列出该分组应用卡片(数据取自
/admin/api/sections),不依赖目标站点是否允许 iframe,首页仍保持新标签页。
4) 三视图标题割裂
实测 /minimal 是 48px 居中 -1.92px,而 / 与 /workspace 是顶栏里 40px
左对齐、无字距。现在三个视图共用同一条规则(clamp(1.75rem,4vw,2.75rem)、
700、-0.035em、居中;移动端 clamp(1.6rem,8vw,2.2rem)),副标题也居中置灰。
另外:工作空间面板原本用 position: fixed 钉在视口中间(1364×654,上距顶栏
7px、下距搜索栏 8px),把页面切成「顶栏/大方块/底部」三段;改为正常文档流,
内容多时页面自然滚动。
2026-09-11 18:17:23 +08:00
c04d00d4de
feat(admin): 历史记录可在后台逐条删除/清空
...
此前后台**没有任何历史管理入口**,只有一条需要 admin token 的
DELETE /api/history/(清空全部),所以用户根本无法删除记录。
- history-server: 新增 DELETE /item?url=<url> 删除单条并重建分组;
CORS 允许 Authorization 头
- admin-server: 新增 /admin/api/history(GET 列表 / DELETE 清空)与
/admin/api/history/item(DELETE 单条),服务端带 token 转发给
history-server —— 令牌只在容器内部使用,浏览器侧无需保存
- admin UI: 「历史访问」卡片,列出记录 + 逐条 ✕ + 清空全部(均二次确认)
标题为空或本身是 URL 时显示简洁域名
2026-09-11 17:03:33 +08:00
80b0e5f51e
feat(web): 搜索框收起态 + admin 入口 + 修正多余滚动
...
- 搜索栏默认收起为左下角放大镜悬浮球(52/56px),点它展开成输入框,
输入框内左侧仍是同一个放大镜,点它收起;Esc 也可收起
- Dashy 页面补右下角 /admin/ 悬浮入口(原先没有任何管理入口)
- 短页面不再凭空可滚动:body 留白 104->84px,并给 #dashy 扣掉同样高度
(Dashy 的 #dashy 本已撑满 100vh,叠加留白就多出约 134px 滚动)
- 补的备案号页脚改为固定在搜索栏正上方,不再参与文档流撑高页面
- 弹层打开时搜索栏与管理入口一并让位
2026-09-11 17:03:33 +08:00
73f23927d9
fix(nginx): /admin 不再重定向,改用精确反代直接 200
...
上一版把 /admin 改成相对 301,但 301 会被浏览器**永久缓存**,用户即使
服务端已修好仍被旧的 http://<域名>:8080/admin/ 带走。现在精确匹配
location = /admin 直接反代(无 Location 头),并加 Cache-Control: no-store,
彻底消除这条跳转。其余前缀改 302(不被永久记住),/extension 直接给安装页。
2026-09-11 17:03:33 +08:00
24b2e6ab75
fix(history): 历史条目不再显示裸 URL
...
浏览器没给标题(或标题本身就是 URL)时,改为显示去掉 www. 的域名,
例如 https://www.docker.com/ -> docker.com。
2026-09-11 16:25:26 +08:00
e4967b99ee
feat(admin): 备案号可编辑,页脚模板由服务端生成
...
备案号原来只能写死在 pageInfo.footer 的 HTML 里。现在 /admin/ 站点信息
拆出「ICP 备案号」「公安备案号」两个输入框,页脚 HTML 由服务端
beianFooter() 统一按模板生成(含 html.EscapeString 转义),
自定义页脚 HTML 降级为「高级」折叠项;旧 footer 会被解析回填到字段。
conf.example.yml 里重复的 .navi-search-bar 规则已由 web/navi.css 接管,删除。
2026-09-11 16:25:26 +08:00
9eb8214fab
fix(web): 搜索栏改为底部悬浮,补极简视图页脚与整体动效
...
- 搜索栏 position:fixed 悬浮在视口下侧,任何视图/滚动位置都可见;
原先是流式跟在页脚之后,手机上要滚到底才看得到
- 极简视图(/minimal)与工作空间不渲染 Dashy 页脚,备案号整段消失;
navi.js 从只读 pageinfo 取回页脚补一份(DOMParser,不用 innerHTML)
- 空分组不再渲染成空玻璃条(:not(:has(a.item))),修掉 mini 页底部一排空框
- 图标统一 56/48px 容器 + object-fit:contain,修 24px 与 56px 混排
- 备案号页脚改为胶囊样式
- 新增动效层:视图/卡片淡入、搜索栏滑入、悬停位移,全部只动
transform/opacity 并遵循 prefers-reduced-motion
2026-09-11 16:25:26 +08:00
9a2a5ccbf5
fix(nginx): 关闭绝对重定向,修 /admin 跳到 :8080
...
容器内监听 8080,对外经门户反代走 443。nginx 默认把重定向拼成
$scheme://$host:$server_port,于是 GET /admin(无尾斜杠)被 301 到
http://<域名>:8080/admin/:既泄漏内部端口,又把 https 降级成 http。
- absolute_redirect off + port_in_redirect off,改用相对 Location
- 为 /admin /item-icons /wallpapers /extension 显式处理无尾斜杠请求
2026-09-11 16:25:26 +08:00
c4b0b8abdf
build: 用 patch-index.py 幂等注入 navi.css/navi.js
...
Dashy 的 index.html 不保证有 </body>,原来 sed 替换 </body> 会静默丢掉注入的
<script>(旧镜像里 script 与 </body> 同行,被整行删除后就没有锚点了),
结果新访客加载不到 navi.js。改为 Python 脚本:先清理旧标签,再在
</head>/</body> 前插入,缺 </body> 时在 </html> 前补回来,可重复执行。
同时 .gitignore 忽略 __pycache__。
2026-09-11 16:25:26 +08:00
6fd62d4949
docs: note that the install page shows the sync token
...
安装引导页已展示服务地址与同步令牌;README 补充说明令牌来源
(/admin/api/sync-info,位于门户登录墙内,不能挂在被放行的 /api/ 下)。
2026-09-11 11:52:17 +08:00
de795de0fb
feat(install): show the sync token on the install page
...
插件必须填 NAVI_SYNC_TOKEN,但页面上没有任何地方能看到它 —— 用户只能去翻
.env,实际就是配不起来。
- admin-server 新增 GET /admin/api/sync-info,返回 syncToken / hasToken。
放在 /admin/ 下而不是 /api/ 下是必须的:插件上报用的 /api/history/sync
被门户 auth_request 放行,若令牌挂在同一前缀下就等于公开。
实测 /api/history/sync-info 返回 401,只有 /admin/api/sync-info 能取到。
- 安装引导页把「第一步」改为填写两项(服务地址 + 同步令牌),各自可复制;
服务端未设令牌时提示留空即可;未登录时提示需先登录门户。
- README 补充对应说明。
2026-09-11 11:49:31 +08:00
e409f967a9
feat(admin): custom wallpaper for the nav background
...
- admin-server 新增 GET/POST /api/wallpaper:
multipart 上传(png/jpg/webp/gif/svg,上限 8MB)或 JSON 直接给 URL,
服务端把一段带标记的 CSS 写入 appConfig.customCss(标记块可反复替换,
不碰主题其余部分),并记录 appConfig.wallpaper / wallpaperDim。
- 上传的图片落在 data 卷 wallpapers/,nginx 以 /wallpapers/ 提供
(长缓存,文件名带时间戳)。
- 遮罩:壁纸上面铺一层可调强度的白色 linear-gradient,默认 0.45,
否则玻璃卡片上的文字在照片上会读不清。
- 安全:URL 校验只允许 / 或 http(s):// 开头且不含引号/括号/换行,
避免注入到 CSS;类型判定走正文特征而非文件名。
- admin UI 增加「外观」区块:上传/填 URL/遮罩滑块/应用/移除 + 预览。
实测:上传 → /wallpapers/wall-*.png,web 200 image/png,
body background-image 为遮罩+图片,移除后恢复默认渐变。
2026-09-11 11:46:53 +08:00
f8c4771828
fix(admin,theme): make admin actually work; glass theme targets real DOM
...
后台之前有两处让它完全不可用的问题(都属于「看着有、实际不工作」):
1. 写操作 401 —— admin UI 从未带 Authorization 头,而 admin-server 强制校验
NAVI_ADMIN_TOKEN。于是添加/删除一直静默失败。
2. JSON 键名不匹配 —— Section/NavItem 只有 yaml tag,没有 json tag,
接口返回 {"Name":..,"Items":[{"Title":..}]},而前端读 s.name / it.title,
渲染直接抛异常(菜单区永远停在「加载中…」)。
修法:
- admin-server:token 改为**默认不校验**(/admin/ 已在外层门户登录墙内,
用户确认无需叠加),仅当显式设 NAVI_ADMIN_REQUIRE_TOKEN=1 时才校验;
历史 DELETE 不受影响(那条路径被门户放行,仍必须自带 token)。
- 补 json tag;Config/Section/NavItem 加 yaml ",inline" 保留未知键,
避免保存 conf.yml 时把不认识的配置(layout/pages/item 的 target 等)丢掉。
- 新增 /api/pageinfo(标题/副标题/页脚/顶部导航可在后台编辑)与
/api/icon(图标上传,限 png/svg/jpg,512KB)。
- 重写 admin UI:站点信息 + 顶部导航编辑、图标上传(.ico 用 canvas 转 PNG,
因为 Dashy 的 isImage() 只认 svg/png/jpg)、可选令牌。
主题:旧 customCss 大部分是死代码 —— .tile / .item-group / .item-group-outer
在页面里根本不存在(querySelectorAll 计数为 0),卡片灰底其实来自 a.item,
而 .home 的不透明背景盖住了 body 渐变,所以玻璃效果一直看不出来。
现按真实类名(a.item / .item-wrapper / .collapsable / .collapsible-content /
.home / header)重写,并加入工作空间侧边栏的溢出修复。
安装页:.btn 自带 display 会盖掉浏览器默认的 [hidden]{display:none},
未配置商店时「一键安装」按钮仍然可见且 href 为空(点了没反应)。
现显式兜住 [hidden],并把浏览器识别补全(iOS/Android 单独提示为不可安装、
Edge/Brave/Vivaldi/Opera 给出各自的 extensions 地址、优先用 userAgentData)。
2026-09-11 11:43:44 +08:00
aa237868c1
feat(deploy): let data volume override SearXNG settings
...
SearXNG 的配置原本只能靠镜像内置的 /etc/searxng/settings.yml,
于是代理(outgoing.proxies)这类部署相关配置每次重建容器都会丢,
改配置也必须重建镜像。
改为:容器启动时若存在 /app/data/searxng-settings.yml 就用它,
否则回退到镜像内置配置。用 sh -c 包一层是因为 supervisord 的
environment= 不支持条件判断。
注意必须带 [ -f ] 判断:实测 SEARXNG_SETTINGS_PATH 指向不存在的
文件时 SearXNG 会直接报错退出(PermissionError: ... not exists!)。
已验证:SEARXNG_SETTINGS_PATH 指向自造配置能正确覆盖端口与实例名。
README 补充配置优先级与数据卷备份清单。
2026-09-11 11:09:33 +08:00
73396cd5d4
docs: document NAVI_FAVICON_PROXY and pass it through compose
...
新增的 favicon 抓取代理变量此前只存在于代码里:
- .env.example 补充说明与示例(默认留空,自动复用 SearXNG 的 outgoing.proxies)
- docker-compose.yml 透传该变量
- README 环境变量表补一行
2026-09-11 11:00:40 +08:00
0283985094
fix(docker): retry apt-get to tolerate transient proxy 502s
...
构建时遇到 'E: Failed to fetch ... 502 Bad Gateway [IP: 127.0.0.1 7890]':
经代理拉 Debian 包偶发上游抖动。给三处 apt-get 统一加
Acquire::Retries=5 与 http 超时,避免一次网络抖动就让整个
(含耗时较久的 SearXNG 构建阶段)前功尽弃。
2026-09-11 10:59:33 +08:00
3f1d896db8
fix(docker): create /out before zipping extension packages
...
Go 的 -o 会自动创建父目录,zip 不会,导致 extpack 阶段
'/out/navi-chrome.zip: No such file or directory' 构建失败。
(上一次构建也是失败的:后台通知里的 exit 0 来自命令末尾的 tail,
不是 docker build 的退出码,已改为显式记录 BUILD_EXIT。)
2026-09-11 10:58:20 +08:00
0a6199b240
test(history): cover favicon/ICO logic; base format detection on body, not Content-Type
...
新增 history-server/favicon_test.go(仓库此前没有任何测试),覆盖:
- Dashy 渲染白名单(只认 svg/png/jpg,jpeg 归一为 jpg)
- sniffExt 必须拒绝 SPA 的 HTML 兜底页
- ICO 内嵌 PNG 提取(取最大条目)
- DIB(BMP) 解码:尺寸、像素顺序(自下而上)、全 0 alpha 视为不透明、
不支持的位深/过短数据要报错
- 从多条目 ICO 中挑最大者
- host:port 文件名净化、中英文段落名匹配
写测试时当场抓到实现里的一个真漏洞:判定完全信任 Content-Type,
于是服务端谎报 image/svg+xml 而正文是 HTML 时会被存成 .svg。
现改为以**正文特征**为准(PNG/JPEG/ICO 魔数 + SVG 根元素校验),
Content-Type 只用于排除明显不是图片的类型,避免误存。
实测回归:google/github/docker/HN/wikipedia 及内网服务 6/6 拿到真实图标。
2026-09-11 10:57:30 +08:00
e7ea091e19
fix(history): give history entries real site favicons
...
历史条目的图标原先写死 fas fa-globe,整栏都是地球图标。
- 新增 history-server/favicon.go:后台抓取每个 host 的 favicon 并缓存到
data/item-icons/,命中后 conf.yml 写本地文件名,与手工图标同一套解析。
抓取顺序 <host>/favicon.ico → 首页 <link rel=icon>;3 个 worker,
失败 6 小时后再试;完成后合并触发一次 conf.yml 重建。
- 只缓存 Dashy 能渲染的格式:其 isImage() 白名单是 [svg,png,jpg],
写 .ico 进去完全渲染不出来。因此 .ico 先抽内嵌 PNG,若是传统 BMP/DIB
编码则解码后转 PNG(Google 这类站点只给老式 .ico)。
- 内网地址用 host:port 区分(不同端口的服务不再共用一个图标),且只直连;
外网站点直连失败时自动复用 SearXNG outgoing.proxies 的代理,
也可用 NAVI_FAVICON_PROXY 覆盖。
- 修掉一个真实缺陷:导航项都没有访问数据时,原来会提前 return 并把
「历史访问」清成只剩安装入口 —— 只浏览站外站点时真实历史会整个消失。
现在只冻结「常用服务」,历史照常生成。
- Dockerfile 去掉 # syntax=docker/dockerfile:1:该指令会让 BuildKit 去 pull
docker/dockerfile 前端镜像,无外网环境直接构建失败;本文件未用 BuildKit 语法。
实测:6/6 站点(含 google.com 的 DIB 型 ico、github.com、docker.com、
news.ycombinator.com 与两个内网服务)均得到真实图标,页面 naturalWidth>0。
2026-09-11 10:55:00 +08:00
ff0e6418f8
feat(nav): auto-detect browser extension and offer one-click install
...
导航页现在会自动检测浏览历史插件是否已安装:
- 扩展在用户配置的服务地址上动态注册 content script
(Chrome: chrome.scripting.registerContentScripts,
Firefox: browser.contentScripts.register),
于 document_start 给 <html> 打上 data-navi-ext 标记,
不在其他站点运行。
- history-server 始终把「安装浏览历史插件」放在「历史访问」第一条
(入口 url=/extension/install.html),启动时也会重建一次,
不再需要等下一次同步。
- web/navi.js 读到标记后隐藏该入口(连 .item-wrapper 一起隐藏,
避免留下空网格位)。
- 新增 /extension/install.html:自动识别浏览器,给出对应安装包与步骤;
支持在 data 卷放 extension-store.json 配置商店链接,
有商店链接时显示真正的「一键安装」。
- 段落名匹配同时兼容中英文(常用服务/Favorites、历史访问/History),
修掉了公开示例 conf.yml 用英文名时重建逻辑完全失效的问题。
- 扩展升到 1.1.0(新增 scripting 权限与 detect.js)。
Note: Chrome/Edge 不允许从网站直接安装非商店扩展,
因此自托管场景下只能下载后手动加载;发布到商店后才会出现一键安装。
2026-09-11 10:41:10 +08:00
88e23861fe
fix(search): repair SearXNG engine failures
...
- bing: www.bing.com 302-redirects to cn.bing.com and SearXNG engine
requests do not follow redirects, so it parsed the 178-byte redirect
page and returned 0 results without any error report. Point bing at
https://cn.bing.com to bypass the redirect.
- Raise outgoing request_timeout 3.0 -> 8.0 and max_request_timeout
4.0 -> 15.0. max_request_timeout overrides each engine's own timeout,
so 360search (timeout 20s) was still cut off at 4s.
- Enable duckduckgo (verified working) and disable baidu/sogou which
return 302 verification pages (baidu also hits a 1h CAPTCHA ban, and
sogou raises AttributeError on SXNG_Response.next_request).
- Document the diagnosis in the README FAQ.
2026-09-11 10:26:32 +08:00
e48fcc53cd
fix: make browser history sync reliable
2026-09-10 22:24:43 +08:00
5d4cd64b30
fix: restore working Dockerfile (SearXNG pip install + msgspec + --no-build-isolation)
2026-09-09 10:22:10 +08:00
0fe13fc90b
feat: unified nav portal - Dashy + SearXNG + history sync + admin UI + browser extensions
...
- Single Docker container with nginx reverse proxy
- SearXNG search proxy with configurable proxy support
- Browser extensions (Chrome MV3 + Firefox MV2) with SSE-style history sync
- Admin WebUI for managing navigation items
- Full deployment tutorial including auth reverse proxy and webui4frpc tunnel
- Sanitized conf.yml for public distribution
- Fixed SearXNG search trailing-slash issue via nginx rewrite
- Fixed SearXNG proxy configuration for China network access
- Fixed SearXNG engine loading (removed broken keep_only, use default_settings)
2026-09-09 09:52:33 +08:00
4f1c65a616
fix: keep manual favorites when no nav visit data; external-only visits don't trigger reorder
...
- Favorites section only rebuilds from Top-4 by visit frequency once real
navigation hosts have visit data; otherwise manual Gitea/HomeAgent/AgentMail
items are preserved untouched
- Pure external-site history (wikipedia etc.) no longer causes favorites to
borrow tiles from other sections
2026-09-08 22:53:22 +08:00
c8aa6946c1
feat: favorites section shows top-4 Dashy nav items by real visit frequency
...
- Rebuild favorites from ALL Dashy navigation items (across every section),
ranked by aggregated host visit counts from browser history
- Code-hosting (Gitea/GitLab/git.*) always pinned first regardless of visits
- Host-level dedup prevents duplicate tiles from previous rebuild cycles
- Fix code-host detection false positives (git. substring in domains like
example.com); precise gitea/github/gitlab/codeberg matching + unit test
- History section now only lists non-navigation external sites
2026-09-08 22:50:08 +08:00
33564fb8f5
docs: complete deployment tutorial + frp/webui4frpc guide, expand .env/docker-compose for tokens & base_url
...
- Rewrite quick-start for source clone & build (A: compose, B: docker run)
- Expand frp section: full frps.toml, webui4frpc WebUI workflow, native frpc.toml, tunnel planning table
- docker-compose: NAVI_PORT / SEARXNG_BASE_URL env passthrough, better docs
- .env.example: document all vars incl token generation & base_url
- Fix firefox debugging URL typo, extension token note
2026-09-08 22:39:52 +08:00
f0b318be45
feat: complete browser extensions + token auth for history/admin APIs
...
- Extensions: rewrite both Chrome MV3 & Firefox MV2 with reliable
chrome.history.search incremental pull (not onVisited), local search-engine
filtering, URL dedup, 30s alarm, retry queue, Bearer token header
- Add icons (16/48/96/128) for both browsers
- Manifest fixes: chrome action popup, firefox options_ui + browser shim
- history-server: require NAVI_SYNC_TOKEN for POST /sync & GET history,
NAVI_ADMIN_TOKEN for DELETE; fix RWMutex deadlock (unlock before
sortDashyFavorites) that stalled syncs with valid token
- admin-server: require NAVI_ADMIN_TOKEN for item add/delete writes
2026-09-08 22:35:54 +08:00
76a4265222
docs: fully sanitize README (remove host identifiers .60/.106)
2026-09-08 22:25:57 +08:00
72c947d146
chore: remove codegraph index from repo
2026-09-08 22:25:45 +08:00
7e49d2b51c
feat: HomeDash - unified self-hosted navigation portal
...
Single-container navigation service combining:
- Dashy dashboard + SearXNG metasearch + history sync API + admin WebUI
- Go services for history/admin APIs, nginx as unified entry
- Browser extensions (Chrome MV3 / Firefox MV2) with 30s batched sync
- Production guide: portal-nginx unified auth reverse proxy + webui4frpc/frp tunnel
- Fully sanitized (no internal IPs/domains/credentials)
2026-09-08 22:25:00 +08:00