Skip to content

Manifest 清单 ​

应用清单(manifest.json)是 ClassIntra 应用模块化架构的核心契约。前后端聚合器通过扫描 apps/*/manifest.json 自动挂载路由、注册桌面图标、加载小组件,无需手动注册。

源码位置:apps/*/manifest.json、shared/src/manifest-schema.js

Manifest Schema 完整字段表 ​

顶层字段 ​

字段类型必填缺省值说明
namestring✅—应用唯一标识(kebab-case)
labelstring✅—显示名称
iconstring❌—图标路径(如 /resources/public/icons/X.png)
colorstring❌—主题色(hex 格式)
categorystring❌desktop应用分类:desktop / system / hidden
ordernumber❌99排序权重(越小越靠前)
defaultEnabledboolean❌true默认是否启用
canDisableboolean❌true是否允许用户禁用
typestring❌app应用类型:app / system / widget
versionstring❌0.0.0语义化版本号(semver)
frontendobject❌—前端配置(见下表)
backendobject❌—后端配置(见下表)
extraBackendsarray❌—额外后端路由(见下表)
configarray❌—应用/插件配置项声明(见下方「应用配置」章节)

frontend 字段 ​

字段类型必填说明
routestring✅主路由路径(如 /countdown)
routeNamestring❌路由名称(如 Countdown)
componentstring✅组件路径(相对 manifest,如 ./frontend/Countdown.vue)
extraRoutesarray❌附加路由(如 cloud-picker)
widgetsarray❌桌面小组件定义

frontend.extraRoutes 数组项 ​

字段类型必填说明
pathstring✅路由路径
routeNamestring❌路由名称
componentstring✅组件路径
requiresAuthboolean❌是否需要登录(缺省 true)
appControlboolean❌是否受应用管控(缺省 true)

frontend.widgets 数组项 ​

字段类型必填说明
idstring✅小组件 id(应用内唯一)
namestring✅显示名称
componentstring✅组件路径
defaultSizeobject❌默认尺寸 { w, h }
minSizeobject❌最小尺寸 { w, h }
maxSizeobject❌最大尺寸 { w, h }
descriptionstring❌描述
configSchemaobject❌配置 schema(见下表)

frontend.widgets[].configSchema.fields 数组项 ​

字段类型必填说明
keystring✅配置键
labelstring✅显示标签
typestring✅字段类型:select / text / bool / number
optionsarray❌select 类型的选项列表 [{ value, label }]
defaultany❌默认值

backend 字段 ​

字段类型必填说明
mountPathstring✅挂载路径(如 /api/countdown)
entrystring✅入口文件路径(相对 manifest,如 ./backend/routes.js)
rateLimitobject❌限流配置 { max, windowMs }

extraBackends 数组项 ​

字段类型必填说明
mountPathstring✅挂载路径
entrystring✅入口文件路径

config 数组项(应用/插件配置声明) ​

字段类型必填说明
keystring✅配置键(环境变量风格,/^[A-Za-z_][A-Za-z0-9_]*$/)
labelstring✅管理端配置表单的显示标签
typestring✅字段类型:string / number / boolean / secret
requiredboolean❌是否必填;缺失时管理端安装页显示「待配置」警示
defaultstring❌默认值(三级回退的最后一级)
descriptionstring❌配置说明,展示给管理员

应用/插件后端依赖外部服务(API 地址、密钥等)时,必须在 config 中声明,让安装链路感知到需要配置什么——详见下方应用配置章节。

JSON 示例 ​

最小应用 ​

json
{
  "name": "notes",
  "label": "笔记",
  "frontend": {
    "route": "/notes",
    "component": "./frontend/Notes.vue"
  },
  "backend": {
    "mountPath": "/api/notes",
    "entry": "./backend/routes.js"
  }
}

完整应用(含 widgets + extraRoutes + extraBackends) ​

json
{
  "name": "resource",
  "label": "资源",
  "icon": "/resources/public/icons/Files.png",
  "color": "#5856D6",
  "category": "desktop",
  "order": 5,
  "defaultEnabled": true,
  "canDisable": true,
  "type": "app",
  "version": "1.0.0",
  "frontend": {
    "route": "/resource",
    "routeName": "Resource",
    "component": "./frontend/Resource.vue",
    "extraRoutes": [
      { "path": "/cloud", "routeName": "CloudDrive", "component": "./frontend/CloudDrive.vue" }
    ]
  },
  "backend": {
    "mountPath": "/api/resources",
    "entry": "./backend/routes.js"
  },
  "extraBackends": [
    { "mountPath": "/api/cloud", "entry": "./backend/cloud-routes.js" }
  ],
  "config": [
    { "key": "API_BASE", "label": "服务地址", "type": "string", "required": true, "description": "上游服务的完整地址" },
    { "key": "API_TOKEN", "label": "访问密钥", "type": "secret", "required": true },
    { "key": "MAX_ITEMS", "label": "列表条数上限", "type": "number", "default": "50" }
  ]
}

应用配置(config) ​

应用/插件后端依赖外部服务(API 地址、密钥等)时,此前只能写在 .env 里——安装链路感知不到,管理员装完不知道要配什么、去哪配。config 声明让配置成为 manifest 契约的一部分。

工作流 ​

manifest.config 声明
        │
        ├─→ 安装时检测:缺失必填项 → 管理端安装页显示「待配置」警示,引导填写
        ├─→ 管理端读写:/api/market/apps/:name/config(GET 返回状态+非敏感值,PUT 保存)
        │       └─→ 值统一存数据库 app_config 表,保存即时生效,无需重启服务器
        └─→ 启动扫描:服务器启动时检查所有已安装应用的必填配置,缺失记 warn 日志

读取回退顺序(三级) ​

数据库 app_config 表  →  process.env  →  manifest.config[].default

后端代码通过 server/src/utils/app-config.js 读取,值已按声明类型自动转换:

js
var appConfig = require('../utils/app-config');

// 按类型转换后的 { key: value }(应用后端自身的运行时读取)
var cfg = appConfig.getConfig('astrbot-relay');

// 配置状态(管理端用):{ values, configured, missingRequired }
// secret 类型的明文绝不出现在 status 里,只给 configured 布尔,防管理端轮询泄露
var status = appConfig.getConfigStatus('astrbot-relay');

安全约定 ​

  • secret 类型:明文只经 getConfig 提供给应用后端自身;管理端接口只返回 configured 布尔
  • key 必须匹配 /^[A-Za-z_][A-Za-z0-9_]*$/(写入数据库前强校验)
  • 卸载应用时自动清理其全部配置(clearValues)

自动聚合流程图 ​

manifest.json 由前后端两套独立的聚合器扫描,各自生成所需产物。

前端聚合流程 ​

apps/*/manifest.json
        │
        └─→ client/src/core/manifest-loader.js(import.meta.glob)
                ├─→ client/src/core/router-aggregator.js(注册 vue-router)
                │       └─→ appRoutes + ROUTE_APP_MAP
                │       └─→ 使用方:client/src/router/index.js
                ├─→ client/src/core/store-aggregator.js(注册 Vuex 模块)
                │       └─→ APP_STORE_MODULES
                │       └─→ 使用方:client/src/store/index.js
                ├─→ client/src/core/widget-aggregator.js(注册桌面小组件)
                │       └─→ WIDGET_REGISTRY
                │       └─→ 使用方:components/Desktop.vue
                └─→ client/src/core/app-registry.js(注册桌面图标)
                        └─→ APP_REGISTRY
                        └─→ 使用方:store/modules/desktop.js

后端聚合流程 ​

apps/*/manifest.json
        │
        └─→ server/src/core/manifest-loader.js(fs 扫描)
                ├─→ server/src/core/route-aggregator.js(挂载 Express 路由)
                │       └─→ mountAppRoutes(app)
                │       └─→ 主 backend + extraBackends(数组)
                │       └─→ 应用 manifest.backend.rateLimit 限流中间件
                │       └─→ 使用方:server/src/app.js
                └─→ server/src/core/default-apps-loader.js(默认应用列表)
                        └─→ getDefaultApps() / getAllApps() / getDesktopApps()
                        └─→ 使用方:init-db.js / admin 路由

manifest-loader 共享

前端 manifest-loader.js 与后端 manifest-loader.js 是两套独立实现:

  • 前端用 import.meta.glob('../../../apps/*/manifest.json', { eager: true }) 同步加载(Vite 打包时静态分析)
  • 后端用 fs.readdirSync 扫描 apps/ 目录并 require() 加载

两者输出格式一致,但加载机制不同。详见 应用架构详解。

应用结构示例(countdown) ​

以 apps/countdown 为例,展示一个完整应用的目录结构:

apps/countdown/
├── manifest.json              # 清单(驱动聚合)
├── icon.svg                   # 应用图标
├── frontend/
│   ├── Countdown.vue          # 主页面
│   ├── widgets/
│   │   └── CountdownWidget.vue  # 桌面小组件
│   └── store.js               # Vuex 模块(可选)
└── backend/
    └── routes.js              # Express 路由

对应 manifest.json:

json
{
  "name": "countdown",
  "type": "app",
  "version": "1.0.0",
  "label": "倒数日",
  "icon": "/resources/public/icons/Countdown.png",
  "color": "#FF9500",
  "category": "desktop",
  "order": 11,
  "defaultEnabled": true,
  "canDisable": true,
  "frontend": {
    "route": "/countdown",
    "routeName": "Countdown",
    "component": "./frontend/Countdown.vue",
    "widgets": [
      {
        "id": "countdown",
        "name": "倒数日",
        "component": "./frontend/widgets/CountdownWidget.vue",
        "defaultSize": { "w": 2, "h": 1 },
        "minSize": { "w": 1, "h": 1 },
        "maxSize": { "w": 4, "h": 2 },
        "description": "显示最近的倒数日",
        "configSchema": {
          "fields": [
            {
              "key": "filter",
              "label": "显示范围",
              "type": "select",
              "options": [
                { "value": "all", "label": "全部" },
                { "value": "pinned", "label": "仅置顶" },
                { "value": "today", "label": "仅今天" }
              ],
              "default": "all"
            }
          ]
        }
      }
    ]
  },
  "backend": {
    "mountPath": "/api/countdown",
    "entry": "./backend/routes.js"
  }
}

路径基准

manifest.json 中所有 component / entry 路径都是相对 manifest.json 所在目录的相对路径(以 ./ 开头)。聚合器通过 manifest-loader.getComponent(appName, relPath) 解析为可导入的模块路径。

验证规则 ​

validateManifest(m) 返回 { valid, errors, warnings, manifest }:

级别字段行为
errors(阻断性)name / label 缺失或非字符串聚合器跳过该应用
warnings(非阻断)name 不符合 kebab-caseconsole.warn 但继续挂载
warnings(非阻断)type / category 不在枚举中降级为默认值
warnings(非阻断)version 不符合 semverconsole.warn 但继续挂载
warnings(非阻断)frontend.route / frontend.component 缺失console.warn 但继续挂载
warnings(非阻断)backend.mountPath / backend.entry 缺失console.warn 但继续挂载
warnings(非阻断)extraBackends 项缺少 mountPath / entryconsole.warn 但继续挂载

策略:聚合器加载 manifest 后调用 validateManifest,errors 阻断挂载,warnings 仅 console.warn 不阻断。

应用管控机制 ​

每个应用通过 app_control 表(app_name + enabled)控制启用/禁用:

  • 初始化:init-db.js 调用 default-apps-loader.getDefaultApps(),把所有 defaultEnabled !== false 的应用写入 app_control 表(INSERT OR IGNORE,避免覆盖管理员修改)
  • 前端路由守卫:router/index.js 的 beforeEach 通过 ROUTE_APP_MAP 检查当前路由对应的应用是否启用,未启用则跳转到桌面
  • 管理员管控:管理员可通过 /api/admin/app-control 远程启用/禁用应用,前端缓存通过 router.clearAppControlCache() 清除
  • 超能岛浏览器:例外,不通过应用管控,改为 per-user browser_enabled 字段控制

详见 应用管控。

版本演进 ​

版本变更
1.0初始规范:name / label / icon / color / category / order / defaultEnabled / canDisable / frontend / backend
1.1新增 extraBackends(阶段 0:云盘合并到资源仓库)
1.2新增 type / version 字段(阶段 3:对齐 Ditto 规范)

相关文档 ​