From ed473e996a3707f98015a40e4388aa4fa58b79ac Mon Sep 17 00:00:00 2001 From: wait4xx Date: Mon, 2 Nov 2026 19:35:28 +0800 Subject: [PATCH] =?UTF-8?q?fix(admin):=20=E4=B8=BB=E9=A2=98=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E9=9B=86=E5=90=88=E7=BB=93=E6=9E=84=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20+=20=E9=9B=86=E5=90=88=E7=BB=93=E6=9E=84=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户实测后台报错:「主题设置 集合: The file option content/site/theme.yaml is invalid. It must be the path to a JSON file」——根因是 site_theme 把 `file:` 直接写在集合层级,而 Decap/Sveltia 的文件集合必须是 `files: [{ name, label, file, format, fields }]` 结构;缺 files 包装时 Sveltia 按单文件变体解析并期待 JSON 路径,故报该错。 - site_theme 改为标准 files 集合(format: yaml 显式声明) - site_pages 三单例补显式 format: yaml(扩展名推断可用但显式更稳) - config-check.py 增加结构断言:每个集合必须 folder 或 files 二选一、files 项必须齐 file/label/fields、file 必须指向真实文件——这类"后台整体打不开" 的结构错从此在构建期拦截 验证: config-check 五文件全 OK(207 字段/64 hint)+ 结构断言通过; pnpm astro build exit 0 Spec: none (后台配置结构修复,构建校验加固) Co-authored-by: GLM-5.3 --- public/admin/config.yml | 50 +++++++++++++++++------------- scripts/build-perf/config-check.py | 21 +++++++++++++ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/public/admin/config.yml b/public/admin/config.yml index 19ed8c1..ccaabfa 100644 --- a/public/admin/config.yml +++ b/public/admin/config.yml @@ -61,28 +61,33 @@ collections: # 主题旋钮(样式即数据 · 层 4):只放白名单内的 9 组,越界值会被构建期钳制 - name: site_theme label: 主题设置 - file: content/site/theme.yaml - fields: - - { label: 强调色(亮), name: accent_light, widget: color, required: false, hint: '亮色模式强调色,如 #3454d1;构建期校验正文对比度 ≥4.5' } - - { label: 强调色(暗), name: accent_dark, widget: color, required: false, hint: '暗色模式强调色,如 #8ea2ff' } - - { label: 图片圆角, name: radius_img, widget: number, required: false, value_type: int, min: 4, max: 16, hint: '范围 4–16(建议 ≤14)' } - - { label: 一级标题字号, name: text_h1, widget: number, required: false, value_type: int, min: 22, max: 32, hint: '22–32,且必须大于 h2' } - - { label: 二级标题字号, name: text_h2, widget: number, required: false, value_type: int, min: 22, max: 32, hint: '22–32,必须小于 h1、大于 h3' } - - { label: 三级标题字号, name: text_h3, widget: number, required: false, value_type: int, min: 16, max: 32, hint: '16–32,必须小于 h2' } - - { label: 书评正文字号, name: prose_size_reading, widget: number, required: false, value_type: int, min: 16, max: 22, hint: '16–22' } - - { label: 技术文正文字号, name: prose_size_tech, widget: number, required: false, value_type: int, min: 16, max: 22, hint: '16–22' } - - { label: 书评行高, name: leading_reading, widget: number, required: false, value_type: float, min: 1.5, max: 2.0, step: 0.05, hint: '1.5–2.0' } - - { label: 技术文行高, name: leading_tech, widget: number, required: false, value_type: float, min: 1.5, max: 2.0, step: 0.05, hint: '1.5–2.0' } - - { label: 间距 3, name: space_3, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 < 间距 4' } - - { label: 间距 4, name: space_4, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 3 且 < 间距 5' } - - { label: 间距 5, name: space_5, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 4 且 < 间距 6' } - - { label: 间距 6, name: space_6, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 5' } - - { label: 段间距, name: flow_p, widget: number, required: false, value_type: float, min: 0, max: 2, step: 0.05, hint: '0–2(em)' } - - { label: 标题下间距, name: flow_h2_post, widget: number, required: false, value_type: float, min: 0, max: 2, step: 0.05, hint: '0–2(em)' } - - { label: 过渡时长, name: dur, widget: number, required: false, value_type: int, min: 150, max: 600, hint: '150–600(毫秒)' } + # 文件集合必须是 files: [{file,label,fields}] 结构;把 file 直接写在集合层级会被 Sveltia 判非法(2026-11-02 实测报 "The file option ... is invalid") + files: + - name: theme + label: 主题设置 + file: content/site/theme.yaml + format: yaml + fields: + - { label: 强调色(亮), name: accent_light, widget: color, required: false, hint: '亮色模式强调色,如 #3454d1;构建期校验正文对比度 ≥4.5' } + - { label: 强调色(暗), name: accent_dark, widget: color, required: false, hint: '暗色模式强调色,如 #8ea2ff' } + - { label: 图片圆角, name: radius_img, widget: number, required: false, value_type: int, min: 4, max: 16, hint: '范围 4–16(建议 ≤14)' } + - { label: 一级标题字号, name: text_h1, widget: number, required: false, value_type: int, min: 22, max: 32, hint: '22–32,且必须大于 h2' } + - { label: 二级标题字号, name: text_h2, widget: number, required: false, value_type: int, min: 22, max: 32, hint: '22–32,必须小于 h1、大于 h3' } + - { label: 三级标题字号, name: text_h3, widget: number, required: false, value_type: int, min: 16, max: 32, hint: '16–32,必须小于 h2' } + - { label: 书评正文字号, name: prose_size_reading, widget: number, required: false, value_type: int, min: 16, max: 22, hint: '16–22' } + - { label: 技术文正文字号, name: prose_size_tech, widget: number, required: false, value_type: int, min: 16, max: 22, hint: '16–22' } + - { label: 书评行高, name: leading_reading, widget: number, required: false, value_type: float, min: 1.5, max: 2.0, step: 0.05, hint: '1.5–2.0' } + - { label: 技术文行高, name: leading_tech, widget: number, required: false, value_type: float, min: 1.5, max: 2.0, step: 0.05, hint: '1.5–2.0' } + - { label: 间距 3, name: space_3, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 < 间距 4' } + - { label: 间距 4, name: space_4, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 3 且 < 间距 5' } + - { label: 间距 5, name: space_5, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 4 且 < 间距 6' } + - { label: 间距 6, name: space_6, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 5' } + - { label: 段间距, name: flow_p, widget: number, required: false, value_type: float, min: 0, max: 2, step: 0.05, hint: '0–2(em)' } + - { label: 标题下间距, name: flow_h2_post, widget: number, required: false, value_type: float, min: 0, max: 2, step: 0.05, hint: '0–2(em)' } + - { label: 过渡时长, name: dur, widget: number, required: false, value_type: int, min: 150, max: 600, hint: '150–600(毫秒)' } - # 瞬间:随手发(text/image 至少其一——Sveltia 无法表达条件必填,两者都设可选, - # 保存前自查;pubDate 必填)。图片走图库媒体库(content/photos 同源可复用) + # 瞬间:随手发(text/image 至少其一——Sveltia 无法表达条件必填,两者都设可选, + # 保存前自查;pubDate 必填)。图片走图库媒体库(content/photos 同源可复用) - name: moments label: 瞬间 folder: content/moments @@ -136,6 +141,7 @@ collections: - name: home label: 主页设置 file: content/site/home.yaml + format: yaml # 头像上传落 content/site/assets/,yaml 写 ./assets/xxx(Astro 图片管线优化) media_folder: content/site/assets public_folder: ./assets @@ -244,6 +250,7 @@ collections: - name: about label: 关于页 file: content/site/about.yaml + format: yaml # 块内图片上传落 content/site/assets/,yaml 写 ./assets/xxx(Astro 图片管线) media_folder: content/site/assets public_folder: ./assets @@ -521,6 +528,7 @@ collections: - name: opensource label: 开源页 file: content/site/opensource.yaml + format: yaml fields: - label: 叙述(链接前) name: blurb diff --git a/scripts/build-perf/config-check.py b/scripts/build-perf/config-check.py index 7213af6..7eb310c 100755 --- a/scripts/build-perf/config-check.py +++ b/scripts/build-perf/config-check.py @@ -69,6 +69,27 @@ def main() -> int: print(f'[config-check] FAIL {rel}\n {first}') admin = loaded.get('public/admin/config.yml') + if admin: + # 结构断言(2026-11-02 教训):每个集合必须二选一——folder 集合或 + # files 文件集合;files 项必须齐 file/label/fields,且 file 指向真实文件。 + # 把 file 直接写在集合层级会被 Sveltia 判非法(后台整体报错)。 + for coll in admin.get('collections', []): + name = coll.get('name', '') + has_folder, has_files = 'folder' in coll, 'files' in coll + if not has_folder and not has_files: + failed += 1 + print(f'[config-check] FAIL 集合 {name}: 既无 folder 也无 files(Sveltia 会拒绝加载)') + if has_files: + for entry in coll.get('files') or []: + miss = [k for k in ('file', 'label', 'fields') if k not in entry] + if miss: + failed += 1 + print(f'[config-check] FAIL 集合 {name} 的文件项缺 {miss}') + continue + if not (root / entry['file']).exists(): + failed += 1 + print(f"[config-check] FAIL 集合 {name}: 文件不存在 {entry['file']}") + if admin: fields = hints = 0