fix(admin): 主题设置集合结构修复 + 集合结构构建校验
用户实测后台报错:「主题设置 集合: 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
This commit is contained in:
1 parent
c772236d1a
commit
ed473e996a
2 files changed
+50
-21
No files matched your search
+29
-21
@@ -61,28 +61,33 @@ collections:
|
|||||||
# 主题旋钮(样式即数据 · 层 4):只放白名单内的 9 组,越界值会被构建期钳制
|
# 主题旋钮(样式即数据 · 层 4):只放白名单内的 9 组,越界值会被构建期钳制
|
||||||
- name: site_theme
|
- name: site_theme
|
||||||
label: 主题设置
|
label: 主题设置
|
||||||
file: content/site/theme.yaml
|
# 文件集合必须是 files: [{file,label,fields}] 结构;把 file 直接写在集合层级会被 Sveltia 判非法(2026-11-02 实测报 "The file option ... is invalid")
|
||||||
fields:
|
files:
|
||||||
- { label: 强调色(亮), name: accent_light, widget: color, required: false, hint: '亮色模式强调色,如 #3454d1;构建期校验正文对比度 ≥4.5' }
|
- name: theme
|
||||||
- { label: 强调色(暗), name: accent_dark, widget: color, required: false, hint: '暗色模式强调色,如 #8ea2ff' }
|
label: 主题设置
|
||||||
- { label: 图片圆角, name: radius_img, widget: number, required: false, value_type: int, min: 4, max: 16, hint: '范围 4–16(建议 ≤14)' }
|
file: content/site/theme.yaml
|
||||||
- { label: 一级标题字号, name: text_h1, widget: number, required: false, value_type: int, min: 22, max: 32, hint: '22–32,且必须大于 h2' }
|
format: yaml
|
||||||
- { label: 二级标题字号, name: text_h2, widget: number, required: false, value_type: int, min: 22, max: 32, hint: '22–32,必须小于 h1、大于 h3' }
|
fields:
|
||||||
- { label: 三级标题字号, name: text_h3, widget: number, required: false, value_type: int, min: 16, max: 32, hint: '16–32,必须小于 h2' }
|
- { label: 强调色(亮), name: accent_light, widget: color, required: false, hint: '亮色模式强调色,如 #3454d1;构建期校验正文对比度 ≥4.5' }
|
||||||
- { label: 书评正文字号, name: prose_size_reading, widget: number, required: false, value_type: int, min: 16, max: 22, hint: '16–22' }
|
- { label: 强调色(暗), name: accent_dark, widget: color, required: false, hint: '暗色模式强调色,如 #8ea2ff' }
|
||||||
- { label: 技术文正文字号, name: prose_size_tech, widget: number, required: false, value_type: int, min: 16, max: 22, hint: '16–22' }
|
- { label: 图片圆角, name: radius_img, widget: number, required: false, value_type: int, min: 4, max: 16, hint: '范围 4–16(建议 ≤14)' }
|
||||||
- { 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: text_h1, widget: number, required: false, value_type: int, min: 22, max: 32, hint: '22–32,且必须大于 h2' }
|
||||||
- { 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: 二级标题字号, name: text_h2, widget: number, required: false, value_type: int, min: 22, max: 32, hint: '22–32,必须小于 h1、大于 h3' }
|
||||||
- { label: 间距 3, name: space_3, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 < 间距 4' }
|
- { label: 三级标题字号, name: text_h3, widget: number, required: false, value_type: int, min: 16, max: 32, hint: '16–32,必须小于 h2' }
|
||||||
- { label: 间距 4, name: space_4, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 3 且 < 间距 5' }
|
- { label: 书评正文字号, name: prose_size_reading, widget: number, required: false, value_type: int, min: 16, max: 22, hint: '16–22' }
|
||||||
- { label: 间距 5, name: space_5, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 4 且 < 间距 6' }
|
- { label: 技术文正文字号, name: prose_size_tech, widget: number, required: false, value_type: int, min: 16, max: 22, hint: '16–22' }
|
||||||
- { label: 间距 6, name: space_6, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 > 间距 5' }
|
- { 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: flow_p, widget: number, required: false, value_type: float, min: 0, max: 2, step: 0.05, hint: '0–2(em)' }
|
- { 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: 标题下间距, name: flow_h2_post, widget: number, required: false, value_type: float, min: 0, max: 2, step: 0.05, hint: '0–2(em)' }
|
- { label: 间距 3, name: space_3, widget: number, required: false, value_type: int, min: 8, max: 80, hint: '8–80,必须 < 间距 4' }
|
||||||
- { label: 过渡时长, name: dur, widget: number, required: false, value_type: int, min: 150, max: 600, hint: '150–600(毫秒)' }
|
- { 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 无法表达条件必填,两者都设可选,
|
# 瞬间:随手发(text/image 至少其一——Sveltia 无法表达条件必填,两者都设可选,
|
||||||
# 保存前自查;pubDate 必填)。图片走图库媒体库(content/photos 同源可复用)
|
# 保存前自查;pubDate 必填)。图片走图库媒体库(content/photos 同源可复用)
|
||||||
- name: moments
|
- name: moments
|
||||||
label: 瞬间
|
label: 瞬间
|
||||||
folder: content/moments
|
folder: content/moments
|
||||||
@@ -136,6 +141,7 @@ collections:
|
|||||||
- name: home
|
- name: home
|
||||||
label: 主页设置
|
label: 主页设置
|
||||||
file: content/site/home.yaml
|
file: content/site/home.yaml
|
||||||
|
format: yaml
|
||||||
# 头像上传落 content/site/assets/,yaml 写 ./assets/xxx(Astro 图片管线优化)
|
# 头像上传落 content/site/assets/,yaml 写 ./assets/xxx(Astro 图片管线优化)
|
||||||
media_folder: content/site/assets
|
media_folder: content/site/assets
|
||||||
public_folder: ./assets
|
public_folder: ./assets
|
||||||
@@ -244,6 +250,7 @@ collections:
|
|||||||
- name: about
|
- name: about
|
||||||
label: 关于页
|
label: 关于页
|
||||||
file: content/site/about.yaml
|
file: content/site/about.yaml
|
||||||
|
format: yaml
|
||||||
# 块内图片上传落 content/site/assets/,yaml 写 ./assets/xxx(Astro 图片管线)
|
# 块内图片上传落 content/site/assets/,yaml 写 ./assets/xxx(Astro 图片管线)
|
||||||
media_folder: content/site/assets
|
media_folder: content/site/assets
|
||||||
public_folder: ./assets
|
public_folder: ./assets
|
||||||
@@ -521,6 +528,7 @@ collections:
|
|||||||
- name: opensource
|
- name: opensource
|
||||||
label: 开源页
|
label: 开源页
|
||||||
file: content/site/opensource.yaml
|
file: content/site/opensource.yaml
|
||||||
|
format: yaml
|
||||||
fields:
|
fields:
|
||||||
- label: 叙述(链接前)
|
- label: 叙述(链接前)
|
||||||
name: blurb
|
name: blurb
|
||||||
|
|||||||
@@ -69,6 +69,27 @@ def main() -> int:
|
|||||||
print(f'[config-check] FAIL {rel}\n {first}')
|
print(f'[config-check] FAIL {rel}\n {first}')
|
||||||
|
|
||||||
admin = loaded.get('public/admin/config.yml')
|
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', '<unnamed>')
|
||||||
|
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:
|
if admin:
|
||||||
fields = hints = 0
|
fields = hints = 0
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user