Files
FamilyBook/.htaccess
T
laoyang 075ac6bbeb v44 第 44 批:界面 5 条 + 移动端 API v1 全量
界面(用户 2026-10-12 第 1~5 条)
1. 新建日程「日历本」并入 .cs 自绘下拉,颜色画进候选项,三字与下拉同行。
2. 新建日程「开始/结束」改「日期范围:」,删旧日期控件;
   重复相关三行仅选重复时出现;删「(含第一次)」与「这段时间算忙碌」。
3. 条目浮层加「地点」、去掉「状态」行;首页红线按分钟定位(原只取整小时);
   去掉左侧红点只留红线。
4. 任务页删掉状态说明提示(STATUS_HINT 三值清空)。
5. 新建任务:标题在前树框在后;截止时间改最新日历控件;负责人默认自己;
   删冗余提示;「上面要做什么」→「任务名称」;清理括号说明。

移动端 API(用户第 6 条:导航模块除外,其余全量)
- 新增 Bearer 令牌鉴权(api_tokens 表,Schema v32),与登录口令 / DAV 口令三套分立。
- Api.php 内核(鉴权/取参/信封/分页/分发 + _method 覆写)+
  ApiHandler.php 17 条资源 + ApiToken.php + api.php 入口。
- 路由:router.php / .htaccess / index.php 三处 /api/ 兜底(部署脚本自动追加线上规则)。
- 个人设置新增「App 接口令牌」卡;docs/API.md 接口文档;
  docs/API-QUESTION-LIST.md 自主决策与待确认缺口清单。

验证:本地 e2e 1510/0、v43 真机 33/0、v43 反向 9/0、
v44 真机 41/0、v44 API 63/0、v44 反向 19/0;全流水线 PUSH_RC=0。
2026-10-12 15:31:03 +08:00

106 lines
6.9 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
# 🔴 v31:`/.well-known/*` 是 RFC 6764 规定的 DAV 发现入口,客户端**第一句**就问这里。
# ⚠️ 它必须排在 `^dav` / `^carddav` 之前 —— 这两条是前缀通配(`^dav(/.*)?$`),
# 而 `.well-known/caldav` 并不以 dav 开头,所以严格说互不干扰;
# 但放在最前面是"发现链优先于资源访问"的意图表达,后人加规则时不易搞错。
# 交给 dav.php / carddav.php 回 301(它们在**鉴权之前**处理这条路径:
# .well-known 是免鉴权入口,在这里 401 会让客户端以为"不支持 DAV")。
RewriteRule ^\.well-known/caldav$ dav.php [L,QSA]
RewriteRule ^\.well-known/carddav$ carddav.php [L,QSA]
# 🔴 v33:站点根的 **DAV 方法**也交给 dav.php(RFC 6764 允许从域名根发现)。
# 病根:`PROPFIND /` 被交给 index.php → 302 跳登录页 → Thunderbird 判定
# "这个域名下没有日历",用户报的就是「提示没有任何联系人和日程」。
# ⚠️ 必须用 `RewriteCond` 限住 **REQUEST_METHOD**:不加的话 `GET /` 也会被吞掉、
# 站点首页直接 404。只放行 PROPFIND/REPORT/OPTIONS 三种 DAV 方法。
# ⚠️ 这条要排在下面 `^dav` / `^carddav` **之前**(空 Pattern 匹配的是根,
# 与那两条前缀互不包含,但先写更清楚)。
RewriteCond %{REQUEST_METHOD} ^(PROPFIND|REPORT|OPTIONS)$ [NC]
RewriteRule ^$ dav.php [L,QSA]
# 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]
# 🔴 v31:`/.well-known/caldav` 与 `/.well-known/carddav`(RFC 6764)。
# 标准客户端(DAVx⁵ / iOS / Evolution)添加账号时**第一句**就试这里 ——
# 原来这里 404,客户端直接判定"域名下没有 DAV 服务",连口令框都不弹。
# 症状正是用户报的「同步不出任何数据」(账号根本建不起来)。
# ⚠️ 这两条必须**排在 `^dav` / `^carddav` 之前**吗?不需要 ——
# `.well-known/...` 不以 dav/carddav 开头,两条规则互不干扰。
# 但**必须**在站点根的"真实文件"判断之前:Apache 对 .htaccess 里的
# RewriteRule 天然就是先于文件查找生效的,所以这里无需额外处理。
# 交给 dav.php / carddav.php 自己回 301(它们在鉴权**之前**处理,
# 因为 .well-known 是免鉴权入口 —— 在这里 401 会让客户端以为"不支持")。
RewriteRule ^\.well-known/caldav$ dav.php [L,QSA]
RewriteRule ^\.well-known/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]
# 🔴 v44(用户 2026-10-12 第 6 条):移动端 API —— `/api/v1/...` → api.php。
# ⚠️ `/api/v1/tasks` 不是真实文件,没有这条规则就会被交给 index.php,
# 而 index.php 不认识它 ⇒ 302 到登录页 ⇒ App 拿到一坨 HTML(JSON 解析失败)。
# ⚠️ 顺序:这条前缀(`^api`)与上面 dav/carddav/nav 互不包含,放哪都行;
# 但**只能在 `RewriteEngine On` 之后**。本地等价物在 router.php 里。
RewriteRule ^api(/.*)?$ api.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>