Files
FamilyBook/.htaccess
T
laoyang a5e559b2b3 v25 第 25 批(9 条):名册分组不刷页 / 纪念日多人 / 首页等高 / 移出不回流 /
名册点击看详情拖动才排序 / 任务复选框与状态 / 财务标签多选 / 微信读书同步与统计 /
导航线上数据与图标

用户本轮 9 条需求(原话见 templates/ 与 app/ 内的逐条注释):

1. 名册:列表里给某人分组改为 AJAX 就地挪卡片(不再整页刷新)。
2. 纪念日:关联人改**多选**(复用 _person_picker,字段 person_ids[]);
   自建纪念日的按钮与其它模块统一(改=蓝 .btn、删=黄 .btn-warn);
   列表新增「关联人」列并给人名前加头像。
3. 首页:日程/任务两块**完全等高**(align-items:stretch + 固定总高 + flex 子项
   min-height:0);修「0 件待办」——数字与右侧列表同源(原来各算各的)。
4. 🔴 名册里被移出的人过一阵又回来:根因是 PersonRoster::seedFromPersons()
   **每次 Schema::ensure() 都无条件重灌**(线上实测每跑一次 +1 行)。
   修:给它加自守卫(只有在 persons 还留着 group_id/sort_order 的**老库**才跑)。
5. 名册:点击=看详情、**只有拖 ⠿ 手柄**才是排序(原来整行既能点又能拖,语义混乱)。
6. 任务:所有任务前加复选框,任何位置点勾即完成;
   状态语义照业界整理为 To Do / Doing / Done 并写出说明(用户要求参考别人的做法)。
7. 财务:类别标签改**多选非互斥**(再点一次取消该类别),上下两图 + 图例 + 表格
   全部按同一个集合联动;高亮条刻意不给「合计」(多选下各类金额不能相加)。
8. 微信读书:新增**懒同步**(访问时顺手做,6h TTL,只补空字段不覆盖用户填的)、
   封面图**拉下来存本站存储**(原来只存外链 → 看不到)、接官方 /readdata/detail 统计。
9. 导航:线上数据只搬了一半(5 板块 29 站 → 7 板块 67 站 + 67 图标)、
   展示页头像不显示 / 搜索按钮错位 / 点登录 404。三个真根因:
   · 相对地址在 /nav/ 下全 404 → 新增 Nav::abs();
   · 搜索容器丢了 id="search"(CSS 是 #search button.submit 绝对定位);
   · Nav::iconKey() 与 SimpleNav 的 icon_key() **算法不一致**(少一次 md5 归一化)
     → 搬来的图标文件名对不上;且 uploads/ 整目录 Require all denied。
   新增两个**免登录公开出口** nav_icon.php / nav_avatar.php。

顺带修掉一个「数据损坏级」的真 bug(做第 4 条线上反证时发现的,之前一直没人注意):
app/Region.php 三处用中文标点当 trim/ltrim 的 charlist,而 PHP 的 charlist 是
**按字节**匹配的 —— 「。」= E3 80 **82** → 字节 82 进了可剥集合 →
「层」= E5 B1 **82** 被切掉尾字节 → 非法 UTF-8 → backfillAddress 的 UPDATE 报
`1366 Incorrect string value` → **地址拆分升级从上线起一直在失败被跳过**(每请求报一次)。
改成字符级 preg_replace('/…/u');线上实测 ensure() 现在无报错、待拆行数真的在下降。

验证战绩:
- e2e 1289/0(新增 Region 字节级用例等)
- 真机探针:选人 19/0 · 首页/导航 24/0 · 导航模块 34/0 · b18 暂存+确定 10/0 ·
  第23批 52/0
- 桌面几何 428/0 · 移动端几何 680/0 · 交互 92/0 · 窄屏 24/0
- 老库升级 153/0(含 anniversary_people 迁移 + 「再跑 ensure() 名册行数不变」)
- 部署静态断言 180 OK / 0 FAIL · 反向验证 3/3 + Region 三变体各被两条断言同时抓住
- 线上 26 个文件与本地**逐字节一致**;展示页 200、图标 66/66 200、登录 200、
  越权/不存在均 404、签发短语后 ensure() 不再报错。
2026-10-07 11:35:31 +08:00

67 lines
4.0 KiB
ApacheConf
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.
# 家书 FamilyBook · Apache 重写规则
#
# ⚠️ 这个文件**不随包上线**(familybook_push.sh 的打包白名单里没有它)。
# 原因:线上站点根那份 .htaccess 是**宝塔面板生成的**(PHP 版本段、防盗链、
# 缓存段、伪静态…),整份覆盖会把面板的东西清空,而且面板下次重写还会改回来,
# 变成"时好时坏"。
# /dav/ 与 /carddav/ 的重写规则由 `familybook_deploy.sh` 第 5c 段**追加**到线上那份里
# (带 `# >>> FamilyBook CalDAV` 标记,幂等)。
#
# 那这份文件是干什么用的?
# · 新建站点(没有宝塔那层)时,可直接把它作为站点根的 .htaccess;
# · 它是那段规则的**唯一书面来源**,push 脚本会断言部署脚本里的规则串
# 与它完全一致(防两处漂移)。
#
# 只做一件事:把 /dav/ 与 /carddav/ 开头的 URL 分别交给 dav.php / carddav.php。
# 其余一律不动 —— index.php 那套 ?r=xxx 路由不依赖重写,
# 少写规则就少一处"上线才发现 404"的地方。
<IfModule mod_rewrite.c>
RewriteEngine On
# CalDAV:/dav/、/dav/3.ics/、/dav/3.ics/12.ics 全部交给 dav.php
RewriteRule ^dav(/.*)?$ dav.php [L,QSA]
# CardDAV(v24 第 22 批):/carddav/、/carddav/1.vcf/、/carddav/1.vcf/12.vcf 交给 carddav.php
# ⚠️ 两条规则**顺序无关但都必须有**:它们的前缀(dav vs carddav)互不包含,
# 不会互相抢;写成 `^dav` 在前是因为历史顺序,真要调也不能写成 `^dav(/|$)` 之外的
# 宽松形式 —— 那会把 /dav2/ 之类也吞进来。
RewriteRule ^carddav(/.*)?$ carddav.php [L,QSA]
# 导航展示页(v24 第 24 批 用户第 4 条):/nav/?<短语> —— 全站唯一免登录页面。
# ⚠️ suggest 那条**必须排在前面**:`^nav(/.*)?$` 会把 /nav/suggest.php 也吞进去,
# 顺序反了就变成"每次联想都去渲染展示页"(返回一页 HTML、前端拿到非 JSON)。
# 这也是本批新踩的一条:之前只有 dav/carddav 两条互不包含的前缀,
# 到这里第一次出现"一个前缀包含另一个"的情况,顺序**变成有语义的**了。
RewriteRule ^nav/suggest\.php$ nav_suggest.php [L,QSA]
RewriteRule ^nav/?$ nav_show.php [L,QSA]
</IfModule>
# ⚠️ 导航图标与展示页头像**不需要重写规则**(v24 第 25 批):
# nav_icon.php / nav_avatar.php 是站点根下的**真实文件**,Apache 直接就能执行。
# 展示页里的地址由 Nav::abs() 拼成 `<base_url>/nav_icon.php?f=…`,落在站点根,
# 不经过 /nav/ 这个重写前缀 —— 所以上面那两条规则一个字都不用动。
# (这正是选"站点根下的两个独立入口"而不是"塞进 /nav/ 子路径"的原因:
# 塞进去就得再加两条重写规则,而重写规则的顺序语义已经踩过一次坑了。)
# ⚠️⚠️ DAV 的 Basic 鉴权**必须**靠这一行才能在 Apache 上工作:
# Apache 默认**不把 `Authorization` 头交给 PHP-FPM** → `$_SERVER['PHP_AUTH_USER']`
# 和 `HTTP_AUTHORIZATION` 都是 null → /dav/ 与 /carddav/ 无论口令填得多正确都 401。
# 线上实测(Apache 2.4.53,直接监听 80/443):
# 没有这行 : {"PHP_AUTH_USER":null,"HTTP_AUTHORIZATION":null}
# 加上这行 : {"PHP_AUTH_USER":"abc","HTTP_AUTHORIZATION":"(有)"}
# `CGIPassAuth` 是 Apache **2.4.13+ 的核心指令**,不需要额外模块。
# (本地 `php -S` 会自己填好 PHP_AUTH_USER,所以 e2e 全绿而线上全 401 ——
# 典型的"本地好好的、线上不行"。部署脚本也会把这一段追加到线上那份里。)
<IfModule mod_authz_core.c>
CGIPassAuth On
</IfModule>
# 备份/临时文件永远不给出去(这条线上很要紧:*.php.bak 曾经可被直接下载)
<FilesMatch "\.(bak|old|orig|save|swp|swo|tmp)$">
Require all denied
</FilesMatch>
<FilesMatch "^\.|~$">
Require all denied
</FilesMatch>