跳转到内容

开发问题与踩坑记录

面向在 cikio 生态里写代码的人。每条按 现象 → 原因 → 处理 写,只收已经定论、可复现的问题。

这份清单是活的:排查出一个新坑,直接在本页对应小节补一条,走一次 PR 即可。 不写进来:密钥、Token、账号 ID、数据库 ID、内网地址 —— 本页是公开的,一律不落敏感信息。

  • 现象:unable to verify the first certificate … try running Node.js with --use-system-ca,Astro 构建期脚本、任何 fetch 都失败。
  • 原因:本机在企业 TLS 拦截代理后面,Node 不信任代理 CA(浏览器和 JDK 各自有信任库,表现不一样)。
  • 处理:NODE_OPTIONS=--use-system-ca pnpm build(Node ≥ 22.15)。JDK 侧另用 -Djavax.net.ssl.trustStoreType=Windows-ROOT -Djavax.net.ssl.trustStore=NUL。

经代理下载的二进制被静默截断

Section titled “经代理下载的二进制被静默截断”
  • 现象:构建时报 RangeError: Offset is outside the bounds of the DataView,只影响部分产物,极难定位(真实案例:satori 渲染动态 OG 图时字体文件解析越界)。
  • 原因:代理把下载截断了却不报错,文件体积小于正常值(10.5 MB 的字体只下到 8.2 MB)。
  • 处理:下载后核对文件体积;用 curl 时加 -H "Accept-Encoding: identity"。
  • 现象:git push 报证书/SSL 相关错误。
  • 处理:git -c http.sslBackend=schannel push origin main(走 Windows 系统证书)。
  • 现象:只提交子目录,ci.yml 两个 job 都失败。
  • 原因:CI 用 frozen-lockfile,而根 pnpm-lock.yaml 没跟着提交。
  • 处理:子包改依赖时,一并 git add pnpm-lock.yaml(仓库根)。
  • 现象:pnpm --filter docs deploy 什么都没部署。
  • 原因:deploy 与 pnpm 内置命令同名。
  • 处理:必须写 pnpm --filter docs run deploy。
  • 现象:Unterminated string literal,但代码里引号是配对的。
  • 原因:属性值跨行书写会让 esbuild 误判。
  • 处理:把跨行的属性值改成单行,或换成对象/变量形式。
  • 原因:该域名在国内被 DNS 污染/屏蔽。
  • 处理:一律走 cikio.cn 子域;本地验证、给别人的链接都别用 workers.dev。
  • 现象:访问不存在的路径,浏览器显示「找不到此 xxx 页 / HTTP ERROR 404」,站点样式和入口都没有。
  • 原因:Workers Static Assets 的 not_found_handling 默认是 none,未命中就返回空 body 的 404。
  • 处理:wrangler.toml 里写 [assets] not_found_handling = "404-page",回落到产物里的 404.html。
  • 原因:Pages 优先读仓库根的 wrangler.toml,只在控制台设置绑定不够。
  • 处理:根 wrangler.toml 里声明 [[d1_databases]]、[[r2_buckets]] 等;注意 migrations_dir 要改成相对仓库根的路径。密钥用 wrangler pages secret put,不要依赖 .env 或构建变量。
  • 现象:wrangler deploy 动了控制台手动绑定的域名。
  • 处理:手动绑定的域不要在 wrangler.toml 里声明 routes。
  • 规则:定位名填 wrangler.toml 顶层 [[d1_databases]] 的 database_name,再追加 --preview 由 preview_database_id 切到预览库;直接填预览库名会报 Please define a preview_database_id。
  • 附带的坑:含空格的 SQL 用 --file=xxx.sql 传参,--command="..." 会被 shell 拆开。
  • 原因:CREATE TRIGGER 的 SQL 体内含分号,wrangler d1 migrations apply 会把它拆成多条语句,触发器实际从未部署。
  • 处理:别依赖触发器做自增/默认值,改为在写入前显式算出值(如插入前 SELECT MAX(id)+1)再传进 INSERT;已出错的数据另写回填迁移。
  • CLI 包名:官方 CLI 的 npm 包名是 auth(npx auth),@better-auth/cli 已废弃,别再用。
  • OIDC Provider 插件:旧的 oidcProvider 插件已废弃(有安全公告),必须用独立包 @better-auth/oauth-provider 的 oauthProvider;只有当客户端请求 openid scope 时才表现为 OIDC Provider,JWKS 由 jwt() 插件提供。
  • 数据库迁移路线:D1 只在 Workers 运行时可达,Kysely 适配器要内省实时数据库,本地必然失败 —— 走 drizzle 适配器:auth generate --adapter drizzle --dialect sqlite → drizzle-kit generate → wrangler d1 migrations apply --remote。
  • 端点路径:basePath 为 /api/auth 时,OIDC 发现端点是 /api/auth/.well-known/openid-configuration(path-appending),不是根路径。
  • 本地 Origin 被改写:wrangler.toml 声明了 routes 后,wrangler dev 会把本地请求的 Origin 重写成自定义域,必须把它加进 trustedOrigins,否则回调被拒。
  • OAuth 客户端凭据:client_secret 在库里是哈希存储、PKCE 默认强制 —— 手写 INSERT 造客户端时不能塞明文,必须存哈希。
  • URL 由文件路径决定:Starlight 0.42 不支持 frontmatter slug 覆盖,content/ccu/assets.md 就是 /ccu/assets/。挪文件=换 URL,要同步处理旧地址。
  • 侧栏显式 slug 写错会整站构建失败(Starlight 直接抛错并点名是哪个 slug)——这是刻意保留的严格性,别改成宽松校验。
  • 「编辑此页」的链接:Starlight 用 filePath 拼,而 filePath 落在 src/content/docs/(构建期拷贝的生成物、已 gitignore),拼出来的地址在仓库里不存在。本站用 src/routeMiddleware.ts 把前缀换成真源目录。别用 import.meta.url 或 import.meta.glob 去解这个问题——中间件被打进 dist/.prerender/,前者拿错根目录、后者返回空。
  • 草稿机制:没写完的页面 frontmatter 标 draft: true,生产构建不输出、本地可见;定稿删掉这一行。不要让已发布页面链接到草稿页。
  • 新增一篇文档要同步三处:文件、astro.config.mjs 的 sidebar、scripts/fetch-content.mjs 的 PUBLISHED_SLUGS(policy/ 目录下的由 autogenerate 自动跟进,不用管)。
  • 删掉内容文件后编辑器报「找不到文件」:tsconfig.json 的 include 是 **/*,若不额外排除 content/,TypeScript 语言服务会把正文文件也纳入程序;删掉或改名后它仍拿着旧文件列表,报 找不到文件 …/content/xxx.mdx。把 content、.content-cache 加进 exclude(构建产物 src/content/docs 本来就排除)即可,astro check 不受影响;改完若编辑器仍报,重启一次 TS 服务。
  • 表结构别凭记忆:同一个库里不同表的字段并不一致(例:posts 只有 createdAt / updatedAt,没有 publishedAt;announcements、featured_issues 才有)。Drizzle 不做运行时列校验,写错列只会在运行到那行时崩。改查询前先看 schema。
  • IP 属地不要实时查第三方:Cloudflare 边缘会免费注入 cf 对象(cf.regionCode / cf.region / cf-ipcountry),绝大多数 IPv4 访客就能拿到精确省份,在写入时固化进 DB 即可,之后直接读字段。
    • ipinfo 的免费档(Lite)只返回国家/大洲,没有省份 region 字段,补省要付费套餐;拿 lite token 去打标准端点会被静默吞掉。
    • ip-api.com 免费版只有 HTTP,而 Workers 的 fetch 只支持 HTTPS(及 localhost),直接失败 —— 不要用。
  • 站内信 ≠ 邮件:后台审核类动作只写站内信;只有走 notify() 的通知才会按用户偏好(email_enabled)决定要不要发邮件,且互动类(评论回复、点赞、新关注)默认不发。评估「用户有没有收到」时要先看是哪条路径。
  • 共享包是源码直出:main / exports 直接指向 src/index.ts,没有构建步骤,子项目用 workspace:* 引用。改动即时生效,但也意味着没有编译期保护。
### 标题:一句话现象
- 现象:<报错原文或表现>
- 原因:<为什么>
- 处理:<可直接照做的步骤>

只写已定论、可复现的;不写讨论过程与推测;涉及敏感信息的一律不写。拿不准是否该公开的,先按上面的「不写进来」清单自查,再问项目负责人。