跳转到主要内容

site.config.ts

构建期确定的配置值,修改后必须重新构建

site.config.ts 内定义的值通常不会频繁修改,用作数据库站点信息的默认值,开发环境和构建过程会读取其中的全部配置。

site.config.ts 内每个字段都配有 JSDoc。使用类似 VSCode 的开发工具编辑时,将鼠标悬停在字段上即可查看注释。

搭建个人站点时,请将站点信息修改为自身信息,不要继续使用示例中的内容。

预定义变量

由于部分变量会在多个位置重复使用,因此文件开头定义了一组预定义变量,具体字段可直接引用或进一步处理。这些变量并非标准字段,而是为便于引用而手动定义的辅助变量。可按下例修改这些变量,后续派生字段会自动更新;也可以删除这些变量,直接设置标准变量中的各项值。

TypeScript// 原始字面量值(修改这些即可,下方派生字段自动更新)
const _name = "ImQi1";
const _desc = "做技术的分享者、生活的摄影师、时事的评论员。";
const _displayName = "做技术的分享者 · 生活的摄影师 · 时事的评论员";
const _nickname = "棋";
const _ownerName = "Qi1";
const _avatarPath = "/imgs/avatar.webp";
const _logoPath = "/imgs/imqi1.svg";
const _url = "https://imqi1.com";
const _cdnUrl = "https://cdn.imqi1.com";
const _host = new URL(_url).hostname;

// 站长对外链接:首页图标条与关于页按钮共用同一批字面量
const _email = "mailto:imqi1@qq.com";
const _website = "https://qi1.website";
const _github = "https://github.com/imqi1-github";

配置按站点基础设置、构建、安全、SEO、页面、功能、日志七区组织,下一节会介绍。

各预定义变量在区内的引用位置如下:

变量名引用位置说明
_namesite.name、seo.pages 内多数页面的 description / keywords、pages.links.profile.siteName站点名
_descseo.description、seo.pages.home.description站点简介
_displayNamepages.homeCustomText、pages.links.profile.siteDescription首页展示的标语
_nicknamepages.links.profile.siteName昵称,拼在友链页的站点名里
_ownerNamesite.ownerName站点所有者
_avatarPathsite.avatarPath头像的裸路径
_logoPathseo.ogImage站点图标的裸路径
_urlsite.url、pages.links.profile.siteUrl、_host站点地址
_cdnUrlsite.cdnUrlCDN 根地址
_hostsite.rootDomain、security.allowedRefererDomains由 _url 推出的域名
_emailpages.homeLinks[].link、pages.aboutLinks.email站长邮箱
_websitepages.homeLinks[].link、pages.aboutLinks.website站长个人网站
_githubpages.homeLinks[].link、pages.aboutLinks.github站长 Github

说明:

  • 通常只需修改原始变量值,派生字段和标准字段会自动更新;
  • referer 校验与防镜像等防护在开发环境下均不生效,因此无需区分开发环境和生产环境的取值,直接设置为生产环境的值即可;
  • _url 应设置为生产环境的站点地址。开发环境不会发生跳转,生产环境下如实际访问地址与该值不一致,则会发生跳转;
  • 静态资源写裸路径和以 https 开头的路径均可,前缀由 utils 内的 publicAsset 自动处理。

标准变量

七个顶层对象依次是站点基础设置、构建、安全、SEO、页面、功能、日志:

站点基础设置

站点的名字、地址、CDN 根、头像与站长名:

TypeScriptdefineSiteConfig({
  site: {
    name: _name,
    url: _url,
    cdnUrl: _cdnUrl,
    rootDomain: _host,
    avatarPath: _avatarPath,
    ownerName: _ownerName,
  },
})
字段值说明
name_name = "ImQi1"站点名
url_url = "https://imqi1.com"站点地址
cdnUrl_cdnUrl = "https://cdn.imqi1.com"CDN 根地址
rootDomain_host = "imqi1.com"站点根域名
avatarPath_avatarPath = "/imgs/avatar.webp"头像地址
ownerName_ownerName = "Qi1"站点所有者

rootDomain 由 _url 推出,avatarPath 直接引用 _avatarPath,这两项会随预定义变量更新,无需单独修改。cdnUrl 填写 CDN 根地址,同时用于静态资源加速和 CSP 白名单,配置方法见 CDN 配置。

构建

构建期的压缩、体积分析与 Redis 连接:

TypeScriptdefineSiteConfig({
  build: {
    brotliCompression: true,
    statsHtml: false,
    redis: {
      enabled: true,
      host: "127.0.0.1",
      port: 6379,
      db: 0,
    },
  },
})
字段值说明
brotliCompressiontrue构建时额外产出 .br 预压缩文件
statsHtmlfalse是否产出包体积分析页 stats.html
redis.enabledtrue生产构建是否启用 Redis
redis.host"127.0.0.1"Redis 地址
redis.port6379Redis 端口
redis.db0Redis 库号

brotliCompression 开启后构建会额外产出 .br 文件,需要 Nginx 或 CDN 配合发送预压缩文件。statsHtml 平时保持关闭,只在排查包体积时打开,构建完会多出一个 stats.html。

redis 仅在生产构建中生效,开发环境始终不启用。它同时控制页面整页缓存和搜索缓存:保持默认开启时,页面缓存写入 Redis;关闭 enabled 后,两类缓存同时关闭,页面每次请求均实时渲染。本机部署保持默认配置即可,修改后需要重新构建。Docker 部署由构建参数覆盖,此处配置对 Docker 不生效,详见 Docker 部署。

安全

接口的 Referer 白名单与 CSP:

TypeScriptdefineSiteConfig({
  security: {
    allowedRefererDomains: [_host],
    enableCsp: true,
  },
})
字段值说明
allowedRefererDomains[_host] = ["imqi1.com"]允许访问 /api/* 的 Referer 白名单
enableCsptrue是否下发 CSP 响应头

allowedRefererDomains 是允许访问 /api/* 的 Referer 域名白名单,除了 _host 还有别的域名要调接口时在这里补上;/api/mini 与 /api/_nuxt_icon 豁免于这个白名单。

enableCsp 仅生产构建生效,本地预览打包产物时可以临时关掉,正式部署应保持开启。

SEO

默认文案、分享图与各页面的文案:

TypeScriptdefineSiteConfig({
  seo: {
    description: _desc,
    keywords: "棋,ImQi1,棋的小站,生活,科技,编程,学习,摄影,时事",
    ogImage: _logoPath,
    ogLocale: "zh_CN",
    twitterSite: "@imqi1_X",
    pages: {
      // ...
    },
  },
})
字段值说明
description_desc = "做技术的分享者、生活的摄影师、时事的评论员。"默认描述
keywords"棋,ImQi1,棋的小站,生活,科技,编程,学习,摄影,时事"顶层关键词(暂无消费点)
ogImage_logoPath = "/imgs/imqi1.svg"分享图与页头 logo
ogLocale"zh_CN"Open Graph 语言地区
twitterSite"@imqi1_X"twitter:site 账号
pages.*home、about、links、messages、changelogs、sitemap、archiving、subscribes、search、map、agreement、notFound静态页面的逐页文案
pages.category(name, desc) => string分类页,按名称与描述拼
pages.tag(name) => string标签页,按名称拼

description 是默认描述,在数据库没有站点描述时用作兜底;顶层 keywords 目前没有消费点,各页的关键词都取 pages 内逐页的值。pages 内逐页的 description 与 keywords 才是最终文案,其中 category 与 tag 两个动态页面写成函数,接收名称和描述后拼出文案。

ogImage 同时用于 og:image 与 twitter:image,也是页头 logo,指向 _logoPath 的那张 SVG。twitterSite 是 twitter:site 的 @用户名。

页面

过渡动画、首页文案与三个页面的专属配置:

TypeScriptdefineSiteConfig({
  pages: {
    transition: {
      fadeDuration: 150,
      translateY: 14,
    },
    homeCustomText: `<p>${_displayName}</p>`,
    homeLinks: [
      // ...
      {
        name: "小程序",
        icon: "ri:mini-program-fill",
        qrcode: "/imgs/miniprogram.jpg",
      },
    ],
    aboutLinks: {
      email: _email,
      website: _website,
      github: _github,
    },
    links: {
      blogOrganizations: [
        // ...
        { name: "十年之约", url: "https://www.foreverblog.cn/blog/5868.html", icon: "/imgs/foreverblog.png" },
      ],
      profile: {
        siteName: `${_name} / ${_nickname}`,
        siteUrl: _url,
        siteDescription: _displayName,
        siteAvatar: "https://cn.cravatar.com/avatar/2841d29eeabab633ae116c7b2c97e3bf?s=512",
      },
    },
  },
})
字段值说明
transition.fadeDuration150页面切换淡入时长(毫秒)
transition.translateY14页面切换位移幅度(像素)
homeCustomText<p>${_displayName}</p> = "<p>做技术的分享者 · 生活的摄影师 · 时事的评论员</p>"首页标语
homeLinks数组,7 项首页图标条
aboutLinksemail / website / github关于页「交个朋友」的三个按钮
links.blogOrganizations数组,5 项友链页展示的博客组织
links.profilesiteName 等 4 项友链页展示的本站信息

transition 控制页面切换的动画时长与位移幅度。homeCustomText 是首页标语,也是数据库未初始化时的兜底值。

homeLinks 是首页那排图标,每项要么给 link 直接跳转,要么给 qrcode 悬浮弹二维码,给了 qrcode 的项就不再是链接;link 项可加 target: true 让它在新窗口打开;name 需要唯一,它同时是列表的 key 和弹层的 id。aboutLinks 对应关于页「交个朋友」的三个按钮。

links.blogOrganizations 是友链页展示的博客组织(关于页的十年之约入口也取自这里),icon 写 /imgs/ 开头的路径,图片放在 public/imgs/ 下;links.profile 是友链页展示的本站信息,其中 siteAvatar 需为完整地址,因为它没被 publicAsset 函数处理。

功能

小程序、二维码入口与高德地图的开关:

TypeScriptdefineSiteConfig({
  features: {
    miniApi: true,
    miniComment: true,
    miniFakeData: false,
    mobileQr: true,
    miniQr: true,
    amap: {
      proxy: true,
      entry: true,
    },
  },
})
字段值说明
miniApitrue是否注册小程序接口路由
miniCommenttrue是否启用小程序评论与留言
miniFakeDatafalse小程序审核模式开关
mobileQrtrue文章页「本文可在【手机】上看」入口
miniQrtrue文章页「本文可在【小程序】上看」入口
amap.proxytrue生产环境是否经服务端代理取高德密钥
amap.entrytrue是否展示指向地图页的入口胶囊

miniApi 关闭后开发和生产都不再注册小程序接口路由;miniComment 是独立的小程序评论开关,关闭后小程序不展示评论区和留言入口,评论列表接口返回关闭状态,评论提交接口拒绝请求。主站后台「开启评论」只控制主站评论区,不影响这个配置。

miniFakeData 是构建期的小程序审核模式开关,开启后 /api/mini/* 只返回一篇固定占位文章,评论一并关闭,分类与归档只保留这一篇,友链、订阅、旅行、更新日志与音乐接口一律返回空,供微信提交审核时使用。审核通过后再改回 false 重新构建,配置改动需重新 build。小程序端另有自己的一份 mini/src/site.config.ts,改这里的配置对小程序无效。

mobileQr 与 miniQr 控制文章页「本文可在【手机】/【小程序】上看」两个入口,miniQr 生效还需在 .env 里配置微信小程序的 WECHAT_MINI_* 两项。

amap.proxy 决定生产环境是否经服务端代理取高德密钥,开发环境恒为直连。amap.entry 控制各处是否展示指向地图页的入口胶囊。

日志

文件日志的总开关、行格式、大小切分、保留与九类分类逐一开关:

TypeScriptdefineSiteConfig({
  logs: {
    file: true,
    dir: "./logs",
    lineFormat: "{ts} [{tag}] [{level}] {msg}{kv}",
    maxFileSizeMb: 20,
    retentionDays: 90,
    categories: {
      access: true,
      audit: true,
      auth: true,
      external: true,
      cache: true,
      ratelimit: true,
      cron: true,
      monitor: true,
      app: true,
    },
  },
})
字段值说明
filetrue文件日志总开关,false 时所有日志只打终端不落盘
dir"./logs"日志根目录,.env 的 LOGS_DIR 优先于此值
lineFormat"{ts} [{tag}] [{level}] {msg}{kv}"落盘行格式,占位符见 lib/site-config.ts 的字段注释
maxFileSizeMb20单个日志文件大小上限(MB),超出切分为 {日期}.1.log,≤ 0 不切分
retentionDays90每类日志保留天数,新的一天首次写入时清理过期文件,0 永久保留
categories.*九类各 true分类落盘开关,false 的类别只打终端不落盘

lineFormat 的占位符说明写在 lib/site-config.ts 的字段 JSDoc 里,编辑时悬停即可查看。日志的落点、分类含义与错误监控邮件见 日志系统。

类型定义

七个区的字段类型在 lib/site-config.ts 定义,由 defineSiteConfig 校验。