feat: 上线前安全加固与发布工程化;appmall 由插件迁移为独立应用

安全(P0)
- 发布包签名:新增 scripts/package_keygen.php、scripts/package_verify.php、
  ywxapp\service\PackageVerifier;FrameworkService::apply() 解压前强制验签
  (ywxapp.framework_package_verify,默认开启)
- JWT 收紧:config/jwt.php 移除公开默认密钥,JwtService::assertStrongSecret()
  校验非空 / 长度≥32 / 已知弱值;.env.production.example 键名由 JWT_SECRET_KEY 修正为 JWT_SECRET
- /api/v1/Article:免登录白名单收窄为只读接口,写操作字段白名单 + assertOwner() 越权校验
- 开发者令牌:查找接口不再回显明文、统一失败文案 + 限流
- TemplateInstaller:文件名白名单 + realpath 归一化 + 目录内断言,修复 Zip Slip;补 use ZipArchive
- 后台登录:接入验证码、失败锁定与登录限流(config/ratelimit.php)
- 缓存 / Session 驱动可由 .env 覆盖(CACHE_DRIVER / SESSION_TYPE),集群部署改 redis
- paycenter 支付日志开关改为 PAY_LOG,不再跟随 APP_DEBUG

工程化 / 交付
- 新增 CHANGELOG.md 与 .env.production.example;composer.lock 入库锁定依赖版本
- 插件离线安装 CLI 增加 --uid,后台上传安装透传 uid,付费插件授权校验可通过
- 授权拒绝文案区分「未携带 uid」与「未找到有效授权」,便于排障
- .gitignore 放行 .env.production.example / .env.addon.example / .gitattributes / public/.htaccess

结构
- appmall 由 addon/appmall 迁移为独立应用 app/appmall(市场 / 开发者中心 / 审核 / 分润)
This commit is contained in:
yangwx committed 2026-10-22 00:53:39 +08:00
1 parent 1d49e6f5ee
commit 18101cf11a
636 files changed
+58517 -9519

No files matched your search

+342
View File
@@ -0,0 +1,342 @@
# haonav 网址导航 — 使用文档
> 面向站长/管理员。开发者请看 [haonav_dev_guide.md](haonav_dev_guide.md)。
- 插件标识:`haonav`
- 版本:1.0.9
- 访问入口:`https://你的域名/haonav`(前台)/ `https://你的域名/haonav/dashboard`(后台)
- 依赖:YwxApp 框架 ≥ PHP 8.0、MySQL 5.7+
---
## 1. 功能总览
| 模块 | 说明 |
|---|---|
| 网址导航首页 | 顶级分类聚合 + 子分类 + 每分类精选链接 + 热门/推荐 |
| 站内搜索 | 关键词/拼音搜索 + 搜索建议(suggest)+ 多引擎跳转 |
| 热门排行 | 按点击量 Top 50 |
| 网址详情页 | 快照预览、评分、JSON-LD 结构化数据(SEO) |
| 分类浏览 | 一级/二级分类,URL 规则化(`/haonav/category/{id}.html`) |
| 申请收录 | 前台投稿 → 后台审核 |
| 广告位 | 首页/详情页广告位管理 + 点击统计 |
| 我的收藏 | 本地收藏(localStorage)+ 云端同步(登录)+ 收藏夹分享页 |
| 实时小组件 | 实时热搜 / 天气(服务端代理 + 缓存) |
| 主题个性化 | 浅色/暗色/跟随系统 + 5 组主色 + 10 组背景渐变 + 自定义背景图 + 随机换色 |
| 节日/节气换装 | **36 项自动检测**(24 节气 + 传统节日 + 公历节日),后台可关,访客可关 |
| PWA 离线 | manifest + Service Worker 缓存(支持添加到主屏幕、离线访问) |
| SEO | sitemap.xml/html、canonical、JSON-LD、拼音搜索 |
| 死链巡检 | 计划任务全量检测 + 前台惰性巡检兜底,可自动下线/恢复 |
---
## 2. 安装与启用
1. 将插件目录放入 `addon/haonav/`
2. 后台 → 应用管理 → 找到「网址导航」→ 安装/启用
3. 首次访问后台配置页时,数据库会自动补种默认配置行(含表主键自愈),无需手工执行 SQL
4. 前台访问 `/haonav` 即可看到空站点,进入后台开始录入
---
## 3. 快速上手(5 分钟建站)
1. **建分类**:后台 → 网址导航 → 分类管理 → 新增一级分类(如「常用工具」),可再挂二级分类
2. **录链接**:链接管理 → 新增 → 选分类、填名称/URL/描述、可选设「热门」「推荐」
3. **看前台**:`/haonav` 首页即展示;点击量自动累计进排行榜
4. **做 SEO**:后台 → 网址导航配置 → 填 SEO 标题/关键词/描述
5. **(可选)开启外观**:同一配置页「外观」分组设置背景图/随机换色/节日换装
---
## 4. 后台管理
### 4.1 数据概览(仪表盘)
`/haonav/dashboard` — 链接/分类/收藏/点击量汇总统计 + 点击热力图。访问时自动为存量链接回填拼音搜索串(每次 300 条,幂等)。
### 4.2 分类管理
- 一级/二级分类、排序(sort 越大越靠前)、启用/禁用
- 删除分类不影响已挂链接(链接保留待重新归类)
### 4.3 链接管理
- 字段:名称、URL、分类、描述、关键词、热门/推荐标记、排序、状态
- 自动抓取 favicon(`/haonav/favicon/...` 代理输出)与网站快照
- **回收站**:删除先进回收站,可恢复/彻底删除
- **死链巡检**:见 §8 计划任务
### 4.4 申请审核(Apply)
前台 `/haonav/apply` 投稿后进入待审列表,可一键通过(转为正式链接)/驳回。
### 4.5 广告位(Ad)
- 按 position 分组投放(如 `home_top`、`detail_side`)
- 支持图片/文字广告、投放时段、排序
- 点击经 `/haonav/ad/click` 跳转并计数
### 4.6 配置(Configs)
分组管理所有配置项,保存即时生效。见下节明细。
---
## 5. 配置项详解
后台 → 网址导航配置。首次访问自动补齐缺失项。
| 分组 | 配置项 | 默认 | 说明 |
|---|---|---|---|
| 基础 | `default_engine` | baidu | 搜索框默认引擎 |
| 基础 | `enable_submit` | 1 | 前台投稿开关 |
| 基础 | `enable_hot_api` | 0 | 实时热搜组件开关 |
| SEO | `seo_title` / `seo_keywords` / `seo_description` | — | 全站 TDK |
| 返利 | `enable_rebate` | 0 | 命中域名自动拼接返利 PID |
| 返利 | `rebate_param` | pid | 返利参数名 |
| 返利 | `rebate_domains` | taobao.com,... | 返利适用域名列表 |
| task | `deadlink_auto` | 0 | 死链连续失败达阈值自动下线 |
| task | `deadlink_threshold` | 2 | 连续失败次数阈值 |
| task | `deadlink_recover` | 0 | 恢复可达后自动重新上线 |
| **外观** | `bg_image` | 空 | 全站默认背景图 URL(空=渐变) |
| **外观** | `bg_mask` | 35 | 背景图遮罩不透明度 0-90(保证文字可读) |
| **外观** | `bg_random` | 0 | 游客随机渐变背景(每次刷新随机) |
| **外观** | `festival_theme` | 1 | 节日/节气自动换装总开关 |
---
## 6. 前台功能说明
| 页面 | URL | 说明 |
|---|---|---|
| 首页 | `/haonav`(`/haonav/index.html`) | 分类聚合 + 热门 18 + 推荐 10 |
| 搜索 | `/haonav/search?q=关键词` | 站内 + 引擎外跳 |
| 排行 | `/haonav/rank` | 点击量 Top 50 |
| 详情 | `/haonav/site/{id}.html` | 快照、评分、JSON-LD |
| 分类 | `/haonav/category/{id}.html` | 单分类列表 |
| 申请 | `/haonav/apply` | 收录/广告合作投稿 |
| 分享页 | `/haonav/favorite/shared/{token}.html` | 只读收藏夹(noindex) |
| 站点地图 | `/haonav/sitemap.xml` | XML + HTML 双格式 |
| Service Worker | `/haonav/sw` | PWA 离线支撑 |
**会员**:`/haonav/account/login|register|logout|profile`。登录后「我的导航」自动云端同步收藏;未登录自动回退本地 localStorage。
---
## 7. 主题与个性化
前台导航栏 🎨 按钮打开主题面板:
| 功能 | 说明 | 记忆 |
|---|---|---|
| 模式 | ☀️ 浅色 / 🌙 暗色 / 🌗 跟随系统 | localStorage |
| 主色 | 5 组色板(紫/绿/蓝/粉/灰) | localStorage |
| 背景 | 10 组渐变色点 + 🎲 随机换色 + 自定义图片 URL | localStorage |
| 恢复默认 | 一键清除以上所有个人偏好 | — |
**自定义背景图**:面板中「粘贴图片地址设为背景」→ 应用(或回车)。图片以 cover 全屏铺满,自动叠加白色遮罩(暗色模式自动改压暗),遮罩强度由后台 `bg_mask` 控制。
**优先级链**(高 → 低):
```
访客自选背景/主色 > 节日/节气主题 > 站点背景图(bg_image) > 随机换色 > 默认渐变
```
---
## 8. 节日 / 节气自动换装
### 8.1 内置条目(共 36 项)
- **24 节气**:立春/雨水/惊蛰/春分/清明/谷雨/立夏/小满/芒种/夏至/小暑/大暑/立秋/处暑/白露/秋分/寒露/霜降/立冬/小雪/大雪/冬至/小寒/大寒 — 公历固定日期,当天自动换季相配色 + 应季粒子(🌱☀️🍂❄️)
- **传统节日(农历)**:除夕 / 春节(初一至初三)/ 元宵 / 小年 / 端午 / 七夕 / 中秋 / 重阳 — 内置 2026-2036 公历换算表
- **公历节日**:元旦 / 情人节 / 国庆(10/1-10/7)/ 万圣节 / 圣诞(12/24-12/26)
### 8.2 效果
命中当天自动:换节日背景渐变 → 换全站主色 → 飘落装饰粒子(emoji 随机大小/速度)→ 右上角徽章(如「🏮 春节快乐」「⚡ 惊蛰」)。
- **时间判定**:浏览器本地时间(`new Date()`)。跨时区访客可能偏移 1 天,国内用户无感
- **访客关闭**:点徽章右上 × 永久关闭(localStorage)
- **站长关闭**:后台 → 外观 → 节日主题自动换装 → 0
### 8.3 「节日不生效」排查
依次检查(Console 执行):
```js
localStorage.getItem('haonav_festival_off') // '1' = 曾点过关闭 → removeItem 后刷新
localStorage.getItem('haonav_bg') // 有值 = 自选背景覆盖了节日背景
window.NavAppearance.festival // 0 = 后台总开关关闭
```
Service Worker 旧缓存干扰时:F12 → Application → Clear site data。
---
## 9. SEO 能力
- `sitemap.xml` / `sitemap.html` 双格式自动生成
- 详情页输出 JSON-LD(WebSite + SearchAction 搜索直达框)
- 全站 canonical 链接
- 链接支持自定义 keywords/description
- 拼音搜索:中文名自动转拼音串,支持拼音首字母检索
---
## 10. 死链检测(计划任务)
两种模式可同时启用:
### 10.1 定时全量(推荐 cron)
```cron
0 3 * * * curl -s "https://你的域名/haonav/task/checklinks?token=你的TOKEN" > /dev/null
```
每日凌晨全量检测所有启用链接。
### 10.2 惰性巡检(无 cron 兜底)
前台页面访问时异步触发 `/haonav/task/autocheck`,带缓存锁限频,自动分批检测。
### 10.3 自动下线/恢复
开启「死链自动下线」后:连续失败 ≥ 阈值 → 自动禁用;开启「恢复自动上线」后:重新可达 → 自动启用(仅对检测器下线的链接生效,手动禁用不受影响)。
---
## 11. 常见问题 FAQ
**Q1:页面 500?**
确认没有模板 include 传参(`{include file="..." var="x" /}` 在 think-template 跨模板作用域不可靠,page-*.css 由各页面手动 `<link>` 引入)。
**Q2:改了背景图后台配置前台不生效?**
浏览器/SW 缓存。F12 → Application → Clear site data;或等 sw 版本升级自动刷新。
**Q3:游客反馈"背景一直没变过"?**
该游客曾点过节日徽章 × 或自选过背景。让其点击面板「↺ 恢复默认」。
**Q4:拼音搜索搜不到?**
旧数据拼音串未回填。访问一次后台仪表盘即自动补(每批 300 条),或多次刷新。
**Q5:收藏在不同设备不同步?**
收藏分本地(未登录)/云端(登录)两套。登录后自动 merge 并以云端为准。
---
## 12. 绑定域名访问
希望独立域名(如 `hao.example.com`)直接访问网址导航,而不是 `example.com/haonav`?YwxApp 通过 `config/app.php` 的 `domain_bind` 支持。
### 12.1 三种使用场景
| 场景 | 配置方式 | URL 形态 |
|---|---|---|
| 默认(路径访问) | 不配 | `example.com/haonav` |
| 二级域名绑定 | `'hao' => 'haonav'` | `hao.example.com`(自动剥 `hao.`) |
| 完整域名绑定 | `'hao.example.com' => 'haonav'` | 直接访问 `hao.example.com` |
### 12.2 启用步骤(5 步)
#### ① 修改 `config/app.php`
```php
'domain_bind' => [
'hao' => 'haonav', // 二级域名 → 插件名(推荐)
// 'hao.example.com' => 'haonav', // 完整域名 → 插件名
],
```
#### ② Web 服务器指向同一项目目录
绑定域名访问本质上是同一份代码,让 DNS 把 `hao.example.com` 解析到与 `example.com` 相同的服务器 / 同一 Web 根。
**Nginx**(在主 server 块外新增或合并):
```nginx
server {
listen 80;
server_name example.com hao.example.com; # 多 server_name 即可
root /www/ywxapp_dev/public;
index index.php;
# 其余 fastcgi / rewrite 规则同主站
location ~ \.php$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
```
**Apache**:`ServerName example.com` → `ServerAlias hao.example.com`。
#### ③ DNS 解析
到域名服务商把 `hao.example.com` 用 A/CNAME 记录指向主站 IP,TTL 选短值便于调试。
#### ④ 清框架运行时缓存
```bash
rm -rf runtime/cache/*
```
#### ⑤ 测试
浏览器访问 `http://hao.example.com` → 应直接呈现 haonav 首页(不再带 `/haonav` 前缀)。
### 12.3 「绑定后无法访问」逐项排查
按下面顺序逐项确认(90% 的问题在第 1/2 步):
| 序号 | 排查点 | 自检命令 / 现象 |
|---|---|---|
| 1 | **DNS 是否解析到服务器** | `ping hao.example.com` 应返回主站 IP;不通就是 DNS 没配 |
| 2 | **Web Server 是否监听该域名** | `curl -H "Host: hao.example.com" http://服务器IP/` 不应返回 404/连接拒绝;Nginx 看 `server_name` 包含子域名,Apache 看 `ServerAlias` |
| 3 | **`config/app.php` 是否正确** | `php -r "print_r(config('app.domain_bind'));"` 确认配置生效(务必重启 php-fpm) |
| 4 | **缓存清理** | 改完配置删 `runtime/cache/*`;TP 路由缓存若有也清 |
| 5 | **插件名与 bind 值一致** | `domain_bind['hao']='haonav'` 中 `haonav` 必须与 `addon/haonav/info.php` 的 `name` 完全一致(区分大小写) |
| 6 | **`addon/haonav/` 存在且有 route** | 验证 `addon/haonav/route/app.php` 可被框架 include |
| 7 | **完整域名是否带协议** | `'hao.example.com'` 不要写成 `'http://hao.example.com'` |
| 8 | **HTTPS** | 绑定后若浏览器强制 HTTPS,请确保子域名也有证书(Let's Encrypt 用 `certbot --expand`) |
| 9 | **SSL 泛域名** | 多子域名绑同一站点,用通配证书 `*.example.com` 最省事 |
| 10 | **框架缓存类** | 部分主机有 OPcache,改完 `config/app.php` 重启 `php-fpm` 才生效 |
**最常见两大坑**:
```
坑 ①:DNS 已解析但 Web Server 没加 server_name → 访问显示「无法访问此网站」/ 默认页
坑 ②:Web Server 已加但 domain_bind 没写 / 拼写错 → 显示「路由不存在 404」
```
### 12.4 多域名绑定同一插件
```php
'domain_bind' => [
'hao' => 'haonav', // hao.example.com
'nav' => 'haonav', // nav.example.com
'hao.dev.example.com' => 'haonav', // 测试子域
],
```
不同域名可分别承担同一插件的「访问入口」与「SEO 镜像」,但**避免一站多域名导致 SEO 重复**——通过 `seo_title` 区分或加 `<link rel="canonical">` 指向主域。
### 12.5 绑定域名后的相对路径影响
| 场景 | 行为 |
|---|---|
| 模板用 `url('haonav/index')` | 生成的 URL 已自动带域名上下文 → `https://hao.example.com/haonav/index.html` |
| 模板用绝对路径 `/static/...` | 不变(推荐) |
| 模板用相对路径 `static/...` | ⚠️ 子域名访问时可能找不到资源 → **统一用绝对路径** |
| 前端 JS 中 `location.href = '/xxx'` | 浏览器会替换为当前域名下的绝对路径,安全 |
| Service Worker | 缓存清单使用绝对路径(已配置),绑定域名后仍正常工作 |
---
## 13. 相关链接
- 开发文档:[haonav_dev_guide.md](haonav_dev_guide.md)
- 框架插件机制:[../插件开发指南.md](../插件开发指南.md)
- 部署指南:`docs/deploy/`