本文档面向 xiis(悉思)的使用者与模板开发者,覆盖安装部署、系统配置、目录结构与模板标签参考。首次部署按《快速开始》顺序操作即可;站点已上线、要调整参数或排查问题,直接看《系统配置》;编写模板、栏目与标签的开发者,重点看《模板开发》与《模板标签参考》;对接系统能力的开发者,可查阅《API 接口参考》。
产品介绍
产品概述
xiis(悉思)是一款轻量·高性能·多站点 Web 系统,基于 Go 语言 + Gin v1.9+ 高性能 Web 框架构建,原生 CMS 内核,支持静态网站与自定义 PHP 扩展,灵活适配多元业务场景。系统编译为单一可执行文件交付,绿色免安装、跨平台运行,内置轻量 Web 服务器,无需配置 Nginx/Apache 等外部环境;数据层支持 MySQL / PostgreSQL / SQLite 三种数据库方言,一行配置即可切换。
系统采用多站点(域名)架构:一个程序实例可同时托管多个站点,按 site_id 隔离数据,每个站点拥有独立的域名绑定、模板、栏目、内容与业务配置,模板标签数据同样按站点隔离;标签名与参数名支持中英文双语。
核心特性
| 特性 | 说明 |
|---|---|
| 单文件交付 | Go 编译为单一可执行文件,绿色免安装,跨平台支持 Windows / Linux / macOS |
| 高性能 | 压测目标 QPS≥10万/单机、P99 延迟 <10ms、常驻内存 ≤100MB、10万+ 并发、7×24 小时无崩溃 |
| 三数据库方言 | MySQL(默认)/ PostgreSQL / SQLite 无缝切换,生产环境建议 MySQL 8.0+ 或 PostgreSQL,轻量场景可用 SQLite 零部署 |
| 多站点架构 | 域名绑定多站点、site_id 数据隔离、站点独立模板/栏目/内容/业务配置 |
| 静态网站与 PHP 扩展 | 统一支持 CMS 站点、PHP 站点、纯静态站点;站点级 PHP 开关 + 进程池隔离,不用 PHP 的站点零攻击面;兼容 DedeCMS / PHPCMS / 帝国CMS 等传统程序平滑迁移 |
| HTTPS 证书零运维 | 内置 ACME 协议,自动申请 SAN 多域名证书、到期前自动续签,无需 certbot、无需 cron、无需手写 443 配置,告别手动证书运维 |
| SEO 自定义路由 | 自定义路由 + SEO URL,全局 + 栏目级双重配置、占位符灵活,支持伪静态与多 URL 规则 |
| 双语模板标签 | 全局标签、循环标签、点号标签三套体系,标签名与参数名均支持中英文双语,支持输出缓存 |
| 安全防护 + 多层缓存 | OWASP Top 10 防护、IP 黑名单(IPv4/IPv6)、滑块验证码、CSP 安全头、SQL 注入参数化防护;静态文件 + 模板 + 查询三层缓存,gzip/zstd 压缩 |
| 定时任务 + 静态生成 | robfig/cron/v3 秒级精度定时任务,动态增删;按栏目/内容/全站静态生成,并发生成 + 原子写入 |
| AI 创作 | 接入大模型自动生成文章(标题/摘要/正文/关键词/标签),内置 AI 关键词库与主题模板,支持定时批量创作 |
| 双后台管理 | 新版后台(Vue3 + Element Plus)+ 老框架后台(Layui)并存,另有管理控制面板可视化配置 yaml 参数 |
功能模块
| 模块 | 包含功能 |
|---|---|
| 内容管理 | 文章/产品/视频/应用四种内容类型、栏目分类、标签、评论审核、单页管理、静态生成 |
| 用户中心 | 用户管理、角色权限、会员等级、积分记录、关注管理、用户元数据 |
| 财务中心 | 充值套餐、内容付费、附件付费、产品授权码、内容/产品购买记录、消费记录 |
| 运营推广 | 广告位/广告列表、友情链接、通知消息、系统消息、点赞收藏、推广记录 |
| 扩展功能 | 媒体管理、表单管理、模板变量 |
| 系统管理 | 站点管理、站点配置、模板切换、文件管理、定时任务、缓存管理、AI 管理(关键词库/主题模板/AI 配置/API 密钥) |
| 日志审计 | API 日志、操作日志、错误日志、积分日志、任务日志 |
技术架构
- 后端:Go + Gin v1.9+,GORM 数据层(MySQL/PostgreSQL/SQLite 三方言统一),原生 SQL 参数化防注入
- 前台渲染:服务端模板引擎 + 自研标签系统,支持页面静态化与输出缓存,标签按站点隔离
- 后台管理:新版后台基于 Vue3 + Element Plus(vue-pure-admin),老框架基于 Pear Admin + Layui
- 管理面板:独立管理端口可视化配置 yaml 参数,支持热更新
- 安全中间件:认证 JWT、IP 黑名单、滑块验证码、CSP、请求限流分层防护
快速开始
环境要求
xiis 采用 Go 编译为单一可执行文件,绿色免安装、跨平台运行,内置轻量 Web 服务器,无需配置 Nginx/Apache 等外部环境。数据库三选一(默认 MySQL),生产环境建议 MySQL 8.0+ 或 PostgreSQL,轻量场景可直接使用 SQLite 零部署。
| 项目 | 要求 |
|---|---|
| 操作系统 | Windows / Linux / macOS(均为 64 位) |
| 运行依赖 | 无外部依赖,单一可执行文件(xxx.exe / xxx) |
| 数据库 | MySQL 8.0+ / PostgreSQL / SQLite(三选一,默认 MySQL,可随时切换) |
| 内存占用 | 约 20MB 起步 |
| 默认端口 | 80(HTTP)、443(HTTPS)、8080(系统管理面板) |
安装步骤
-
下载程序包
前往官网「下载中心」/category/download 获取最新版本程序包。
-
解压程序
将程序包解压到任意目录即可,无需安装,绿色免部署。
-
检查配置
打开
xiis.net/配置/xiis.net.yaml,确认端口与数据库连接信息(默认 MySQLroot/root,连接 127.0.0.1:3306,库cms_production)。 -
启动程序
Windows 下双击
xxx.exe;Linux/macOS 下在终端执行./xxx。程序检测到端口被占用时会自动查杀占用进程后继续启动。 -
初始化数据库(首次)
首次使用(空库/新前缀)可在系统管理面板执行「数据库初始化」,或控制台运行
initcmsdb -y,程序将按当前方言自动建表。 -
访问站点
浏览器打开
http://localhost(默认端口 80),看到站点首页即安装成功。
首次启动与管理入口
安装完成后包含五个入口:
| 入口 | 地址 | 说明 |
|---|---|---|
| 网站前台 | http://localhost/ | 默认端口 80,可在 yaml / 面板修改 |
| 管理后台(旧版) | /admin/login.html | 站点内容、栏目、会员、模板管理 |
| 管理后台(新版) | /admin-vue | Vue3 + Element Plus 重构后台 |
| 系统管理面板 | http://127.0.0.1:8080/ | 服务器级配置与运维(域名、数据库、安全、日志等) |
| 标签生成器 | /tag-tool | 模板标签速查 / 组合生成 / 一键复制 |
admin / 123456、管理面板密钥 admin)仅用于首次登录,生产环境请立即修改。管理面板登录密码即 yaml 管理面板.管理API密钥。系统配置
配置文件 xiis.net.yaml
全部核心配置集中在 xiis.net/配置/xiis.net.yaml,按功能分节组织。文件修改后约 5 秒自动热更新,无需重启程序(端口、监听地址、数据库类型等底层项除外,见文末 FAQ);也可通过系统管理面板(端口 8080)可视化维护,两种方式写入同一配置文件。系统管理面板左侧导航即按「全局配置分组」组织,另有站点管理页可对每个站点单独覆盖全局项。
配置文件自 2026 年 08 月起将「服务器」「安全」中的管理面板相关项集中迁移到独立 管理面板 顶级节;静态资源、HTTPS、认证、日志、数据库等亦均为独立顶级节。新增配置项后,管理面板「配置同步检查」会提示同步到面板页面。
配置分组与面板导航
yaml 顶级节与系统管理面板导航页的对应关系如下(带星标图标为可站点覆盖的全局节,可在「站点管理」中覆盖):
| YAML 顶级节 | 面板导航页 | 覆盖范围 | 主要作用 |
|---|---|---|---|
服务器 | 服务器 | 全局 | HTTP 端口、超时、并发、默认站点路径、索引文件 |
管理面板 | 管理面板 | 全局 | 面板端口、监听地址、登录失败锁定、API 密钥、CSRF 密钥 |
安全 | 安全 | 全局 + 站点 | CSP/XSS/帧保护、请求频率限制、域名白名单、IP/UA 黑名单、上传限制 |
静态资源 | 静态资源 | 全局 + 站点 | 静态文件缓存、gzip/zstd 压缩、可压缩/可缓存扩展名 |
HTTPS | HTTPS/SSL | 全局 + 站点 | HTTPS 开关、证书路径、ACME 自动续签/签发 |
认证 | 认证 | 全局 + 站点 | Cookie/Session/JWT 密钥与过期、Cookie 安全属性、验证码资源 |
静态资源保护 | 资源保护 | 全局 + 站点 | 未登录访问控制、受保护/公开路径、失败行为 |
性能 | 性能 | 全局 | 协程池、内存池、GC 百分比 |
日志 | 日志 | 全局 + 站点 | 日志开关/级别/格式/轮换/采样/压缩归档 |
归属地 | 日志 → IP归属地 | 全局 | IP 离线库引擎(ip2region/geolite2/qqwry)与显示格式 |
蜘蛛特征 | 蜘蛛特征 | 全局 | 蜘蛛 UA 关键词 → 显示名映射(供日志分析统计) |
可疑请求特征 | 可疑请求 | 全局 | 可疑请求路径特征表(供日志分析「请求正规性」) |
验证码 | 认证 → 验证码资源 | 全局 | 滑块验证码背景图 / 滑块图磁盘目录 |
数据库 | 数据库 | 全局 | 三方言连接、连接池、查询缓存、方言专属参数 |
插件 | 插件 | 全局 | 插件系统总开关、插件目录、程序形态默认值、上传治理、实例与熔断限额 |
站点配置 | 站点管理 | 按站点 | 绑定域名、站点类型(CMS/PHP/静态)、模板路径、PHP、重定向等 |
站点配置 内为某站点单独书写同名键即可覆盖全局;站点管理页会以 覆盖 / 回退全局 标签明确标识每一配置项的来源。未设置项自动回退全局值,无需重复配置。服务器(服务器)
| 配置项 | 默认值 | 说明 |
|---|---|---|
端口 | 80 | HTTP 启动端口;保存后自动重启程序生效 |
读取超时 / 写入超时 / 空闲超时 | 30 / 60 / 120 秒 | HTTP 服务超时控制,防慢连接拖垮连接池 |
最大请求头字节 | 1048576 | 最大请求头大小(1MB) |
最大请求体MB | 5 | 单请求体上限;上传另受「安全.最大上传大小MB」控制 |
最大发送字节MB | 100 | 响应最大发送大小 |
网站路径 | ./xiis.net/网页/默认 | 默认站点根目录(未绑定域名时使用) |
运行模式 | release | debug(调试)/ release(生产) |
最大并发连接数 | 100000000 | 全局并发上限(生产可按需调低) |
优雅关闭超时秒 | 5 | 停止服务时等待在途请求完成的时间 |
最小化到托盘 | true | 点击最小化收纳到系统托盘(Windows) |
全局默认索引文件列表 | index.php… | 绑定域名的默认索引列表为空时使用此列表 |
管理面板(管理面板)
| 配置项 | 默认值 | 说明 |
|---|---|---|
端口 | 8080 | 系统管理面板端口;修改保存后自动重启程序生效 |
监听地址 | 0.0.0.0 | 127.0.0.1 仅本机,0.0.0.0 允许内网访问 |
管理API密钥 | admin | 面板登录密码(生产必须修改为复杂密钥) |
登录失败限制 | 5 | 失败超限锁定该 IP(防暴力破解);0=回退默认 5 次 |
登录锁定时长 | 300 秒 | 锁定期间拒绝该 IP 登录;0=回退默认 300 秒 |
CSRF签名密钥 | cms_csrf_secret_key_2026 | CSRF Token 签名;空=回退源码默认值,站点级配置优先 |
安全(安全,可站点覆盖)
策略开关
| 配置项 | 默认 | 说明 |
|---|---|---|
启用CSP | 1 | 内容安全策略,现代最核心 XSS 防御手段,建议开启 |
启用XSS保护 | 0 | X-XSS-Protection,现代浏览器已弃用,兼容远古浏览器时开启 |
启用帧保护 | 0 | X-Frame-Options,防点击劫持(CSP frame-ancestors 已覆盖,默认关) |
启用内容类型选项 | 1 | X-Content-Type-Options: nosniff,防 MIME 混淆,建议开启 |
CSP策略 | 内置策略 | 详见 yaml;注意本系统静态资源与模板需放行 style-src 'unsafe-inline' 等 |
服务器响应头 | xiis-v1.8.2026 | Server 响应头(隐藏真实服务器信息) |
访问控制
| 配置项 | 默认 | 说明 |
|---|---|---|
启用域名校验 | true | 开启后仅允许白名单域名访问 |
允许域名列表 | 如 localhost、*.example.com 等 | 域名白名单,支持通配符 *.example.com;未命中直接拒绝 |
IP黑名单 | — | 支持单个 IP 与 CIDR 网段(IPv4/IPv6) |
UA黑名单 | 内置 4 条 | 支持正则,匹配 UA 返回 403(如扫描器、旧版浏览器) |
可信代理列表 | 空 | 仅当请求直连 IP 命中时信任 X-Forwarded-For,防伪造代理头绕过黑名单 |
禁止访问扩展名 | go/mod/sum/env/… | 源码与配置文件禁止 HTTP 直访(.go .yaml .env .bat 等) |
频率限制与上传
| 配置项 | 默认 | 说明 |
|---|---|---|
启用请求频率限制 | false | 按 IP 限流(防 CC/暴力破解) |
默认最大请求数 / 默认时间窗口秒 | — / 60 | 窗口内最大请求数,超出返回 429 |
允许上传扩展名 | jpg/png/zip/… | 上传文件类型白名单 |
最大上传大小MB | 50 | 全局 0=不限制(安全基线不建议);数据库 upload_max_size 配置优先 |
静态资源(静态资源,可站点覆盖)
| 配置项 | 默认 | 说明 |
|---|---|---|
启用自动处理 | true | 自动处理静态资源请求 |
启用请求统计 | false | 静态资源请求统计(性能影响极小,按需开启) |
是否启用静态文件缓存 | false | LRU 文件缓存开关(站点级未配置时回退此值) |
缓存大小上限MB / 单文件最大缓存KB | 100 / 512 | 缓存容量与单文件上限 |
缓存数量上限 | 10000 | 缓存条目上限,0=不限制 |
缓存同步间隔分钟 | 3 | 定期检查磁盘文件变化并增量更新缓存 |
允许缓存扩展名 | .html/.css/.js/… | 空=缓存所有文件 |
启用压缩 | true | 静态文件、模板、API 响应压缩 |
压缩类型 | 1 | 1=gzip(默认),2=zstd |
压缩阈值字节 | 10240 | 小于 10KB 不压缩 |
可压缩扩展名 | html/css/js/json/… | 仅列出的类型参与压缩 |
HTTPS / SSL(HTTPS/SSL,可站点覆盖)
| 配置项 | 默认 | 说明 |
|---|---|---|
启用HTTPS | false | 站点级常独立开启(见「站点配置」HTTPS 子节) |
强制HTTPS重定向 | false | HTTP 自动 301 到 HTTPS |
HTTPSPort | 443 | HTTPS 监听端口 |
启用自动续签 | false | 到期前自动续签证书 |
检查间隔秒 / 提前续签天数 | 86400 / 8 | 证书过期检查周期与提前续签天数 |
证书文件路径 / 私钥文件路径 | 证书/server.crt|key | 全局默认证书;站点级证书见「站点配置」 |
ACME.启用 / 邮箱 / 使用测试环境 | true / — / true | Let's Encrypt 自动申请;测试环境仅供联调,生产置 false |
认证(认证,可站点覆盖)
| 配置项 | 默认 | 说明 |
|---|---|---|
Cookie签名密钥 / Session签名密钥 | change-me | 会话与 Cookie 签名(生产必须修改非默认值) |
Session超时秒 / 清理间隔秒 | 1800 / 300 | 会话生命周期与过期清理周期 |
JWT签名密钥 | change-me | API 令牌签名密钥(生产必须修改) |
JWT过期时间秒 / 刷新时间秒 | 259200 / 86400 | 令牌有效期与可刷新窗口 |
Cookie启用Secure | false | 仅 HTTPS 时开启,防止明文传输 |
Cookie启用HttpOnly | true | 禁止 JS 读取,防 XSS 窃取会话 |
Cookie启用SameSite / CookieSameSite值 | true / Lax | 防 CSRF;值可选 Strict/Lax/None |
验证码.滑块图目录 / 背景图目录 | 空 | 留空用内嵌资源;配置目录后可自定义滑块/背景图(不重新编译) |
资源保护(静态资源保护,可站点覆盖)
将后台等敏感 HTML/JS 纳入登录保护:未登录访问受保护路径时,按配置重定向登录页或返回 401。
| 配置项 | 默认 | 说明 |
|---|---|---|
启用静态资源保护 | true(站点1) | 是否启用登录访问控制 |
受保护路径 | /admin/*.html、/admin/*.js | 支持通配符 * 与 ? |
公开路径 | /admin/login.html 等 | 白名单优先于受保护路径(登录页、公共静态资源放行) |
认证失败行为 | redirect | redirect 重定向登录页 / 401 返回 401 |
登录页面路径 / 认证Cookie名称 | /admin/login.html / cms_auth_token | 跳转目标与会话 Cookie 名 |
性能(性能)
| 配置项 | 默认 | 说明 |
|---|---|---|
GC百分比 | 100 | 值越大 GC 频率越低、内存占用越高(高 QPS 场景可上调) |
协程池任务队列容量 | 10000 | 任务队列上限,超出时降级直跑防堆积 |
启用内存池 | 1 | 复用对象减少 GC 压力 |
异步 / 日志 / 统计协程池大小 | 8 / 4 / 4 | 各领域 worker 数量,一般无需调整 |
日志与归属地(日志,可站点覆盖)
基础与高级
| 配置项 | 默认 | 说明 |
|---|---|---|
启用日志 / 日志路径 | true / ./xiis.net/日志 | 站点级可独立到 日志/站点N |
日志级别 | info | debug / info / warn / error / silent |
输出格式 | json | text 或 json(兼容 ELK/Loki) |
单文件最大行数 / 最大大小MB | 100000 / 50 | 达到上限自动轮换新文件 |
刷新阈值 / 刷新间隔秒 | 1000 / 5 | 异步写盘缓冲策略(降低 IO) |
记录调用位置 | false | 记录 文件名:行号,便于排查(略增开销) |
压缩归档 / 压缩算法 | true / zstd | 轮换旧日志压缩归档(可省 60%+ 磁盘) |
保留天数 | 30 | 超过自动清理 |
最大单条日志大小KB | 1024 | 超长自动截断 |
输出到控制台 | false | 请求日志是否同步输出到控制台 |
采样配置.请求 / 调试 / 警告 | 1 / 10 / 100 | 高频日志每 N 条记 1 条(1=不采样) |
IP 归属地(顶级节 归属地)
| 配置项 | 默认 | 说明 |
|---|---|---|
启用IP归属地库 | true | false 时回退简单 IP 段判断 |
引擎类型 | ip2region | ip2region(v4.xdb)/ geolite2 / qqwry(纯真,GBK) |
ip2region库路径 / GeoLite2库路径 / 纯真库路径 | 配置/下 | 离线库文件路径(相对程序根目录) |
显示格式 | 国家-省份-城市-运营商 | geolite2 无运营商字段自动降级 |
蜘蛛与可疑请求特征(蜘蛛特征 / 可疑请求)
两组「关键词 → 说明」映射表,分别供日志分析页的 Top 蜘蛛 与 请求正规性 识别展示;保存后热更新即时生效,命中仅统计展示、不拦截请求。
| 特征表 | 匹配方式 | 内置规模 | 典型条目 |
|---|---|---|---|
蜘蛛特征 | UA 小写关键词 → 显示名 | 约 50 条 | baiduspider=百度蜘蛛、googlebot=谷歌蜘蛛、bingbot=必应蜘蛛、bytespider=字节跳动蜘蛛、gptbot=OpenAI 训练爬虫等 |
可疑请求特征 | 路径小写子串 → 特征说明 | 约 150 条 | .php=PHP 脚本直访、/.env=环境变量泄露探测、/.git=源码泄露、union%20select=SQL 注入、webshell 后门文件名等 |
数据库(数据库)
xiis 同时支持 MySQL、PostgreSQL、SQLite 三种数据库方言,通过 数据库.数据库类型 切换;切换后需重启程序生效。数据库相关逻辑全链路兼容三方言,建表/查询/分页/事务均由方言层自动适配。
| 数据库类型 | 适用场景 | 关键说明 |
|---|---|---|
mysql(默认) | 生产环境,高并发 | 端口 3306,编码 utf8mb4;支持 TLS 加密连接(云库强制 TLS 时开启) |
postgresql | 生产环境,强一致 | 端口 5432;库名不存在时自动创建;schema 默认 public,SSL 模式可配 |
sqlite | 单机、内网、轻量 | 单文件库(WAL 模式);空库启动自动建表;忽略地址/账号/密码/编码 |
连接设置
| 配置项 | 默认 | 说明 |
|---|---|---|
启用数据库 | true | 三方言通用总开关 |
数据库类型 | mysql | mysql / postgresql / sqlite;切换后重启生效 |
地址 / 端口 | 127.0.0.1 / 3306 | MySQL/PG 有效;PG 默认 5432;SQLite 忽略 |
数据库名称 | cms_production | MySQL/PG 为库名(PG 不存在自动创建);SQLite 为文件名主体(自动拼 .db) |
表前缀 | 按需自定义,如 x9k_ | 表前缀规则:仅允许字母/数字/下划线、长度 ≤20、自动补下划线结尾(前缀+短名组成表名,如 x9k_users);自定义前缀(如 x9k_)可提高批量自动化攻击门槛,修改后需同步 RENAME 数据库已有表。 |
账号 / 密码 | root / root | SQLite 忽略 |
编码 | utf8mb4 | 仅 MySQL 生效;PG/SQLite 固定 UTF-8 |
方言专属参数
| 配置项 | 默认 | 说明 |
|---|---|---|
MySQLTLS | false | MySQL TLS 加密连接(TiDB Cloud Serverless 等强制 TLS 必须开启,证书严格校验;本机自建 MySQL 未开 SSL 保持 false) |
PostgreSQL模式 | public | PG schema 名 |
PostgreSQLSSL模式 | disable | disable / require / verify-ca / verify-full |
SQLite文件目录 | ./xiis.net/数据/数据库 | SQLite 库文件目录(自动创建) |
SQLite日志模式 | WAL | WAL(推荐,读写不互斥)/ DELETE |
SQLite忙等待毫秒 | 5000 | busy_timeout,写锁等待上限 |
SQLite最大打开连接数 | 10 | WAL 单写者,多连接无收益反增锁等待;连接池/空闲数自动截断至此 |
连接池与运行参数
| 配置项 | 默认 | 说明 |
|---|---|---|
最大连接池大小 | 10 | 语义为 app 池与 CMS 池合计值,两池按 30% : 70% 自动拆分(2026-09-02 修复前两池各取全额,实际为设定值 2 倍);须小于数据库 max_connections |
最大空闲连接数 | 10 | 同为两池合计值,按 30:70 拆分(app≈3、CMS≈7);SQLite 侧同位截断 |
连接最大生命周期秒 | 3600 | 连接到期回收;建议远大于数据库 wait_timeout(见 FAQ) |
心跳间隔秒 | 60 | SQLite 方言下改为 WAL 日志合并(防长期增长占盘) |
启动等待秒 | 120 | MySQL/PG 与程序并行启动时等待其就绪再初始化连接;SQLite 即时可用自动视为 0 |
查询缓存过期秒 | 300 | SQL 查询结果缓存时间(最长 5 分钟生效滞后来源之一) |
cms初始化数据库_{mysql|postgresql|sqlite}.sql;会删除同前缀全部旧表,必须二次确认。修改表前缀后需同步 RENAME 已存在的表。站点(域名)配置(站点配置 → 站点管理)
站点配置 是列表型顶级节,以站点ID 为主 key。站点 ID 分配规则:
- 1 – 9999:CMS 站点,ID 与数据库
{表前缀}sites.id一致(当前 站点1…站点5) - ≥ 10000:非 CMS 站点(独立 ID 空间,不建 {表前缀}sites 记录),可为启用 PHP 的站点或纯静态站点(当前 10000、10001、10002)
- 绑定域名完全平等(无主次之分),启动时构建 域名→站点ID 的 O(1) 反向查找表;同域多站点互不干扰
每个站点可独立配置:
| 配置分组 | 关键配置项 | 说明 |
|---|---|---|
| 基础 | 绑定域名、是否CMS站点、网站开关、网站路径、模板路径、启用模板缓存、默认索引文件列表 | 站点类型决定模板引擎/静态/PHP 处理链路;网站开关=false 时站点下线 |
| PHP | 启用PHP、PHP环境路径、最小/最大/当前进程数量、伸缩检查间隔秒、缩容冷却时间秒、PHP缓存开关/时间/最大条目数/预热URL列表 | 进程池自适应伸缩;环境路径指向解压后的 PHP(如 Extensions/php/…) |
| HTTPS/ACME | 同 HTTPS 节全部项 + ACME.域名列表 | 各站点独立证书;ACME 域名列表显式配置优先,为空自动收集绑定域名(过滤 IP) |
| 静态资源 / 安全 / 认证 / 日志 / 资源保护 | 与对应全局节一致 | 未配置项回退全局(面板「覆盖 / 回退全局」标签明示) |
| 重定向 | 重定向来源、类型、目标URL、状态码、保留路径、启用 | 域名类型或路径类型 301/302 跳转,SEO 收口主域名常用 |
站点配置:
- 站点ID: 1 # CMS站点 ID 与 {表前缀}sites.id 一致
绑定域名: [xiis.net, www.xiis.net, www.example.com] # 域名完全平等
是否CMS站点: true
网站路径: "./xiis.net/网页/站点1"
模板路径: "./xiis.net/模板/站点1/front"
安全:
CSP策略: "default-src 'self'; ..." # 站点级覆盖全局
重定向: # 301 收口主域名
- 重定向来源: [xiis.net, example.com]
目标URL: "https://www.xiis.net"
状态码: 301
保留路径: true
启用: true
类型: 域名
站点业务配置(数据库 {表前缀}system_configs 表)
本文档中的 {表前缀} 表示 CMS 数据表前缀,实际值由 xiis.net.yaml 的 数据库.表前缀 配置决定;{表前缀}system_configs 即该配置值拼接 system_configs 后的完整表名。
除 yaml 服务器级配置外,CMS 站点级业务配置统一存于数据库 {表前缀}system_configs 表,覆盖站点名称/Logo、SEO、路由前缀、上传存储、邮件、支付、验证码、评论、缓存等 CMS 业务参数。后台通过「系统管理 → 站点配置」页面可视化维护(路由分组 route),改动写入该表后即时/按缓存生效。
| 字段 | 含义 |
|---|---|
site_id | 站点 ID,0 = 全局配置(所有站点回退基准),其他值 = 该站点私有配置 |
config_group | 配置分组标识:basic 基础 / seo SEO / route 路由 / upload 上传 / email 邮件 / payment 支付 / security 安全 / comment 评论 / cache 缓存 / ai AI / other 其他 |
config_key | 配置键名(如 slug_link_enabled、link_suffix、upload_max_size) |
config_value | 配置值(文本,按 config_type 解析) |
config_type | 值类型:string / int / boolean / json / text |
description / sort_order | 配置项说明 / 后台显示排序 |
读取回退顺序:站点私有记录(site_id=N) > 全局记录(site_id=0) > 代码内置默认值。三方言(MySQL/PostgreSQL/SQLite)下该表结构一致,切换方言后配置自动迁移。
常用配置键详解(以下分组与键覆盖 CMS 常用业务配置;示例值/建议值供参考,实际以站点自身配置为准。涉及密钥、Token 的键位仅说明获取方式,不在公开文档展示实际值)
站点基础 basic
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
site_name | 站点名称,如「悉思」 | 站点名称;是 {xiis:网站标题} 等站点信息标签的数据源 |
site_logo | /uploads/sites/{id}/logo.png | 站点 Logo 图片地址({xiis:网站标志}) |
site_favicon | /uploads/sites/{id}/favicon.ico | 浏览器标签页图标({xiis:网站图标}) |
site_icp | 京ICP备XXXXXXXX号 | ICP 备案号({xiis:备案号}) |
site_description | 一句话站点介绍(≤120字) | 站点描述,SEO 与分享兜底描述 |
site_copyright | © 2026 悉思 版权所有 | 版权声明({xiis:版权信息}) |
site_statistics_code | 统计平台生成的 JS 代码 | 全站统计代码(百度统计/CNZZ 等),输出于页脚模板 |
site_status | true | 站点开关:false 时前台按关闭原因提示并停止访问 |
site_close_reason | 站点维护中,请稍后访问 | 站点关闭时的提示文案 |
site_watermark | false | 图片水印开关(功能规划中,暂不启用) |
SEO 与链接 seo
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
seo_title | 站名 · 副标题,如「悉思 · 轻量高性能多站点Web系统」 | 站点级 SEO 标题({xiis:SEO标题}),页面无独立标题时兜底 |
seo_keywords | 3~8 个关键词,英文逗号分隔 | SEO 关键词({xiis:网站关键词}) |
seo_description | ≤150 字站点描述 | SEO 描述({xiis:网站描述}) |
slug_link_enabled | false(ID 链接)/ true(slug 链接) | 栏目/内容智能链接是否用 slug 格式:/category/news、/content/xxx;false=ID 格式 /category/1 |
tag_slug_link_enabled | true | 标签智能链接 slug 开关,独立于全局,仅控制 /tag/{slug};true 时访问端启用严格模式(裸数字 ID 返回 404,slug 为空的标签降级允许 ID 访问) |
link_suffix | .html 或留空 | 所有动态链接附加的后缀;空=不附加。已含后缀的静态/伪静态链接不重复附加 |
route_prefix_link_suffix_enabled | false | 栏目 route_prefix 链接是否附加 link_suffix |
tag_link_suffix_enabled | false / true | 标签链接是否附加 link_suffix |
search_keyword_param | q | 搜索关键词 URL 参数名,如 /search.html?q=SEO(兼容旧版 keyword 参数) |
baidu_api_enabled | false | 百度站长 API 主动推送总开关(AI 文章发布自动收录) |
baidu_api_url | http://data.zz.baidu.com/urls?site=…&token=… | 百度站长推送完整地址(含站点与 16 位 token;token 请自行保管,勿公开) |
路由前缀 route
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
route_category | category | 栏目列表页路由前缀:/category/{slug|id};栏目自身 route_prefix 存在时优先使用栏目级前缀;改后重启生效 |
route_content | content | 内容详情页路由前缀:/content/{slug|id};有栏目 route_prefix 时优先栏目级;改后重启生效 |
route_tag | tag | 标签路由前缀:/tag/{slug|id},标签首页与标签详情共用;改后重启生效 |
route_search | search.html | 搜索页路由前缀 |
route_user | user | 用户中心路由前缀:/user/login、/user/profile 等 |
route_tag_pagination_style | path | 标签分页 URL 格式:path=路径式(/tag/seo/page/2.html,SEO 友好);query=查询串(/tag/seo.html?page=2,兼容旧行为);改后重启生效 |
上传与存储 upload
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
upload_max_size | 10485760(10MB,字节) | 单文件上传大小上限(字节);建议与「安全.最大上传大小MB」一致 |
upload_allowed_types | jpg,jpeg,png,gif,webp,pdf,zip,… | 允许上传的文件扩展名白名单(英文逗号分隔) |
upload_storage_type | local / oss / qiniu | 存储后端:local 本地 / oss 阿里云 / qiniu 七牛 |
local_storage_dir | uploads | 本地存储目录名(可改为 files 等) |
file_prefix | 留空 / img | 上传文件路径前缀,加在日期目录前(如 img/2026/09/…) |
oss_access_key_id / oss_access_key_secret | 阿里云控制台获取(Secret 勿公开) | 阿里云 OSS 密钥对 |
oss_bucket / oss_region / oss_endpoint | 如 your-bucket / cn-hangzhou / https://oss-cn-hangzhou.aliyuncs.com | OSS Bucket 名称、区域与 Endpoint |
oss_custom_domain | 留空或绑定 CDN 域名 | OSS 自定义访问域名(可选) |
qiniu_access_key / qiniu_secret_key | 七牛控制台获取(Secret 勿公开) | 七牛云密钥对 |
qiniu_bucket / qiniu_region | 如 your-space / z0 | 七牛空间名与区域:z0华东 z1华北 z2华南 na0北美 as0东南亚 |
qiniu_custom_domain | https://cdn.example.com/ | 七牛自定义访问域名(建议 HTTPS) |
邮件(SMTP)email
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
smtp_host | smtp.example.com | SMTP 服务器地址(可用企业邮箱/邮件服务商) |
smtp_port | 465(SSL)/ 587(STARTTLS) | SMTP 端口,与 smtp_ssl 配套 |
smtp_user / smtp_pass | 发信账号/授权码(勿公开) | SMTP 登录账号与密码(常为邮箱授权码) |
smtp_ssl | true | 是否启用 SSL 加密连接 |
mail_from / mail_from_name | noreply@example.com / 站点名称 | 发件人地址与显示名 |
email_register_enabled | false(可选开启) | 注册是否需要邮箱验证码;false 时注册无需邮箱验证 |
email_template_register / email_template_reset_password / email_template_welcome | 留空=内置模板 | 各场景邮件 HTML 模板;可用变量:{站点名称}/{验证码}/{有效期分钟}/{当前年份}/{版权信息}/{用户名}/{站点URL},留空回退代码内置模板 |
支付 payment
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
payment_alipay_enabled | false(接入后置 true) | 支付宝支付开关,控制前台支付方式显隐 |
alipay_app_id | 支付宝开放平台获取 | 支付宝应用 APPID |
alipay_merchant_private_key / alipay_public_key | 密钥自行填入(勿公开) | 商户应用私钥与支付宝公钥(RSA2) |
alipay_sandbox | true(联调)/ false(生产) | 是否使用沙箱环境 |
alipay_notify_url | https://yourdomain.com/…(公网可访问) | 支付宝异步回调通知地址 |
payment_wechat_enabled | false | 微信支付开关(当前版本未接入,保持关闭) |
wechat_appid / wechat_mchid / wechat_mch_private_key / wechat_mch_cert_serial_no / wechat_api_v3_key | 微信商户平台获取(密钥勿公开) | 微信支付商户参数:AppID、商户号、商户 API 私钥、证书序列号、APIv3 密钥 |
wechat_notify_url | https://yourdomain.com/…(必须 HTTPS) | 微信支付回调通知地址 |
payment_balance_enabled | false | 余额支付开关(会员套餐可用,充值不支持余额支付) |
payment_callback_signature_enabled | true | 支付回调 HMAC 签名校验开关,建议保持开启 |
payment_callback_secret | 留空=回退 JWT 签名密钥 | 回调 HMAC 共享密钥(可自定义随机长串) |
payment_callback_ip_whitelist | 留空=允许所有 IP | 回调 IP 白名单(逗号分隔),建议按支付平台回调出口 IP 填写 |
安全与验证码 security
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
captcha_enabled | true | 验证码总开关:false 时所有场景验证码均失效(含接口) |
captcha_login_enabled | true | 前台登录滑块验证码开关 |
captcha_register_enabled | true | 前台注册滑块验证码开关 |
captcha_comment_enabled | false(已登录可关) | 发表评论验证码开关(已登录用户可免验证) |
captcha_admin_login_enabled | true | 后台登录滑块验证码开关 |
captcha_form_submit_enabled | true | 通用表单提交验证码开关(已登录可免) |
captcha_slide_padding | 5(1–20) | 滑块验证码容差像素:越小越严格,建议 3–8 |
illegal_char_filter_enabled | true | 非法字符过滤总开关;false 时下列规则不生效 |
illegal_char_filter | 每行一条,支持 regex:… 正则 | 非法字符/敏感词过滤规则(表单提交校验),如敏感词A、regex:\d{4}-\d{4} |
ip_blacklist | 每行一条:单 IP 或 CIDR | IP 黑名单(192.168.1.1 或 10.0.0.0/8),与全局 yaml 黑名单合并生效 |
ua_blacklist | 每行一条正则,如 .*bot.* | UA 黑名单(正则),与全局 yaml UA 黑名单合并生效 |
评论 comment
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
comment_enabled | true | 评论功能总开关:false 时前台禁止发表评论 |
comment_audit_enabled | false(社区可开审核) | 评论审核开关:true=新评论待审核;false=直接通过 |
comment_closed_hide_list | false | 关闭评论后是否隐藏整个评论区:true=隐藏;false=保留已有评论+禁用输入框 |
缓存 cache
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
system_cache_ttl | 0(禁用)或 60–300 | 系统统一缓存时长(秒);0=禁用缓存直接查库(database/验证码模块除外);未配置同视为 0 |
general_cache_enabled | true | 通用内存缓存开关(站点信息/统计等高频小对象) |
general_cache_max_entries | 10000 | 通用内存缓存最大条目数 |
template_cache_max_entries | 500 | 模板文件解析缓存条目数 |
html_cache_max_entries | 10000(100–100000) | HTML 输出缓存每站点条目上限,超限 LRU 淘汰;约 62KB/条,10000 条约 620MB 内存,请按机器内存调整 |
db_cache_enabled | true | 数据库查询缓存开关 |
db_cache_expire_time | 300 | 查询缓存过期时间(秒),越小数据越新鲜 |
db_cache_max_entries | 10000 | 查询缓存最大条目数 |
db_cache_clean_interval | 2 | 查询缓存过期清理间隔(秒) |
db_operation_timeout | 30 | 数据库单次操作超时(秒) |
static_cache_max_size_mb / static_cache_single_file_max_kb / static_cache_max_entries | 100MB / 512KB / 1000 | 静态资源缓存容量、单文件上限与条目数(与 yaml 静态资源节一致) |
captcha_ttl | 300(1–86400,禁 0) | 验证码有效期(秒),滑块/邮箱/图形验证码及邮件验证码统一使用;不受 system_cache_ttl=0 影响 |
AI 内容生成 ai
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
ai_enabled | false(接入后置 true) | AI 功能总开关:控制后台 AI 文章任务入口 |
ai_provider | deepseek 或兼容服务商名 | AI 服务商标识(默认 DeepSeek 系) |
ai_api_key | 服务商控制台获取(勿公开) | AI 服务 API Key |
ai_base_url | 服务商 OpenAI 兼容地址 | AI 接口地址(OpenAI 兼容 /v1/chat/completions) |
ai_model | 服务商模型名,如 deepseek-chat / deepseek-reasoner | AI 模型名称:通用/推理模型按需选 |
ai_max_tokens | 8192 | 单次生成最大 Token 数(越长成本越高) |
ai_temperature | 1.0(0–2) | 创造性温度:0 精确 / 2 发散 |
ai_system_prompt | 留空=内置提示词 | 系统提示词,可用 {栏目}/{栏目描述补充}/{当前年份} 等占位;留空回退内置模板 |
ai_request_timeout | 120(60–300) | AI 请求超时秒数 |
ai_default_author | 如「AI文章助手」 | 任务未指定作者时使用的默认作者名 |
ai_default_max_use_count | 1(0=不限) | 生成结果默认最大使用次数(0=不限,1=一次性) |
ai_max_articles_per_run | 9(建议 ≤10) | 单次任务最大生成文章数(防 API 超额调用) |
其他 other
| 配置键 | 建议值 / 示例 | 作用说明 |
|---|---|---|
content_version_enabled | false | 内容版本控制开关:启用后新增/修改文章才写入版本快照表 |
sql_tag_enabled | false(默认关闭) | SQL标签开关:启用后模板中 {xiis:sql} 才执行只读 SQL。仅允许 SELECT/WITH,并拦截写操作、敏感表、系统库与信息探测函数(详见「模板标签参考 → SQL标签」) |
route 前缀、缓存类、上传存储类键在后台保存后即时应用;其中 route_* 变更会自动触发路由热重建(见「路由热重建」)。直接修改数据库不会自动触发,需手动执行重建。改库时注意 config_type 与值格式,且全局记录会回退共享给未单独配置的站点。路由热重建
CMS 前台路由支持运行期重建:栏目变更或 route_* 配置变更后,路由表在后台重新构建并原子切换,进程无需重启。
触发条件
| 操作 | 触发方式 | 生效内容 |
|---|---|---|
| 新增栏目 | 自动 | 新栏目 route_prefix 对应的静态路由立即可访问 |
编辑栏目(slug / route_prefix) | 自动 | 旧前缀路由失效,新前缀路由生效 |
| 删除栏目(单条) | 自动 | 该栏目前缀路由立即返回 404 |
| 批量删除栏目 | 自动 | 被删栏目的前缀路由逐条失效 |
新增 / 编辑 / 删除 route_* 配置 | 自动 | route_category、route_content、route_tag、route_search、route_user 对应的路由前缀切换 |
| 栏目排序 | 不触发 | 仅调整 sort_order,不改变路由集合 |
手动触发
以下场景不经过后台控制器,需手动执行重建:
- 直接修改数据库中的
{表前缀}categories.route_prefix或{表前缀}system_configs的route_*值; - 通过外部脚本或运维工具修改上述配置;
- 自动重建失败后重试。
| 入口 | 说明 |
|---|---|
| 管理面板 → 系统管理 → 快捷操作 → 重建CMS路由 | 提交后回查版本号,版本号递增表示重建完成 |
POST /admin/api/routes/rebuild | 管理端口(默认 8080,绑定 127.0.0.1),需 JWT 认证;不接受参数,仅触发全量重建 |
GET /admin/api/routes/status | 返回 版本号、上次重建、最小间隔秒、重建就绪 |
执行规则
| 规则 | 说明 |
|---|---|
| 串行执行 | 重建由独立协程串行处理,同一时刻仅一次重建 |
| 最小间隔 | 两次重建间隔不少于 2 秒,间隔内的多次触发合并为一次 |
| 构建与切换分离 | 在新建的 Gin 引擎上完成全部路由注册后才切换处理器指针;构建失败不切换,旧路由继续服务 |
| 在途请求 | 持有旧处理器的引用,执行至结束;后续请求由新处理器处理 |
| 长连接 | 已升级的 WebSocket / TCP 连接由旧处理器持有,重建不中断 |
| 插件路由 | 插件路由由独立的原子快照匹配,不在 Gin 路由树内,不受重建影响 |
| 请求开销 | 稳态请求仅增加一次原子指针读取(atomic.Pointer.Load) |
实现约束
维护内核代码时需遵守以下约束,否则会产生资源泄漏或状态不一致:
| 约束 | 规则 | 违反后果 |
|---|---|---|
| 初始化隔离 | H_注册CMS路由 中的 4 项一次性初始化(停用词缓存、后台模块、标签系统、非 CMS 站点缓存清理)由 Q_CMS模块已初始化 守卫,重建时跳过 | 每次重建新增 2 个常驻 goroutine,并重复查库与全表缓存扫描 |
| 路由表指针 | app.Q_主Gin引擎 在 H_初始化路由 末尾赋值(全部路由注册完成后) | 插件在重建期间读取到不完整路由表,冲突判定失效 |
| 全局状态重置 | 重建前清空 Q_全局正则路由分组管理器 与路由匹配缓存(追加语义);Q_全局路由表 不清理(覆盖语义) | 正则匹配路由数随重建次数线性增长;清空路由表会产生匹配空窗期 |
目录结构
程序解压后的目录结构如下(网站路径/模板路径 等均可在 yaml 或面板调整):
xiis.net/
├─ 网页/ # 站点根目录目录(多站点按目录隔离)
│ ├─ 站点1/ # 站点1 根:static/v3(CSS/JS/图标)、admin、uploads…
│ ├─ 站点2/ … 站点N/ # 其余站点(含默认目录)
│ └─ 默认/ # 未绑定域名时的默认站点
├─ 模板/
│ ├─ 站点1/front/ # 站点1 前台模板(HTML + 中文标签)
│ ├─ 站点2/front/ … # 各站点模板目录(可后台切换/上传)
│ └─ 默认/ # 默认模板
├─ 插件/
│ └─ 站点1/ # 站点级插件目录(按 站点{ID}/{插件标识}/ 组织,物理隔离互不感知)
│ └─ apitest/ # 插件标识(须与清单 标识 一致)
│ ├─ 插件.yaml # 插件清单 + 配置值(作者声明定义、使用者填「值」)
│ ├─ 程序/ # 可选:插件代码或可执行文件(含依赖)
│ ├─ 静态资源/ # 可选:经 /_插件/{标识}/… 托管(支持 Range/ETag)
│ └─ 数据/ # 可选:插件私有可写区(内核自动创建)
├─ 配置/
│ ├─ xiis.net.yaml # 系统核心配置(约5秒热更新)
│ ├─ 悉思管理控制面板/ # 系统管理面板(8080)静态页面
│ └─ GeoLite2-City.mmdb 等 # IP 归属地离线库(可选)
├─ 数据/
│ ├─ 数据库/ # SQLite 库文件目录(SQLite 模式)
│ ├─ 验证码/ # 滑块/背景图自定义目录(留空用内嵌)
│ └─ 站点1/ # 站点级数据(保护附件等)
├─ 日志/
│ ├─ 全局/ # 全局日志
│ └─ 站点1/ … # 站点级日志(站点配置可独立路径)
├─ 证书/
│ ├─ server.crt|key # 全局默认证书
│ └─ 站点1/ … # 站点级证书
├─ 工具/ # 开发/迁移辅助脚本(非运行必需)
└─ 上级目录:
xxx.exe # 程序主文件(单一可执行文件)
Extensions/ # PHP/MySQL 等运行时(面板「软件管理」安装)
备份/ · docs/ … # 项目资料
插件开发
插件系统概览
xiis 插件系统是Web 服务器层的能力,与站点类型无关——CMS 站点、PHP 站点、纯静态站点装了插件都能用。用户按规范写出插件 → 复制进插件目录 → 填好插件配置 → 程序自动发现并加载:没有安装流程、不写数据库、不需要重新编译 xxx.exe。
插件是站点级作用域:每个站点加载自己的一套插件,目录物理隔离,互不干扰、互不感知;插件挂载在 URL 上(插件 apitest 对应 站点域名/apitest),请求进来由插件处理。
| 形态 | 零代码 / 需编程 | 能力边界 | 适用场景 |
|---|---|---|---|
声明式 | 零代码,纯 插件.yaml 配置 | 返回固定/插值文本、HTML、JSON、重定向、静态文件 | 健康探针、验证文件、固定跳转、robots.txt 类接口 |
程序 | 需编程(任意语言) | 完整文件 / 数据库 / 网络 / 系统命令能力,由操作系统权限模型提供;进程级崩溃隔离 | 动态接口、增删改查、文件上传下载、业务逻辑 |
{表前缀}permissions)挂在 Gin 引擎的 CMS 路由组上,插件不经引擎故不自动生效。插件路由需要登录才能访问时,把插件路径配进站点级「静态资源保护 → 受保护路径」即可套用现成鉴权;或由插件进程自行校验(请求头已全量转发,含 Cookie,插件可直连数据库)。插件目录规范
插件存放在 xiis.net/插件/站点{ID}/{插件标识}/。站点隔离是物理隔离:站点1 与 站点2 下可放同名插件,完全独立;站点作用域由目录决定,不需要在清单里声明。
xiis.net/插件/
└── 站点1/ ← 站点ID,对应 站点配置 的 站点ID
└── apitest/ ← 插件标识,必须与清单里 标识 一致
├── 插件.yaml ← 必需:插件清单(作者写定义、使用者填配置值)
├── 程序/ ← 可选:插件的代码或可执行文件及其依赖
│ └── 入口.py
├── 模板/ ← 可选:模板片段(消费机制属后置项,当前内核不读取)
├── 静态资源/ ← 可选:css/js/img,经 /_插件/ 前缀托管
└── 数据/ ← 可选:内核自动创建,插件私有可写区
扫描规则:内核只处理 插件/站点{数字}/{插件标识}/插件.yaml 这一层。站点{数字} 不是合法站点 ID 的目录忽略;没有 插件.yaml 的忽略;标识与清单不一致的忽略并记日志。
插件清单 插件.yaml
插件只有一个配置文件 插件.yaml(原 配置.yaml 已废弃并合并进本文件):插件作者写 标识/名称/版本/协议版本/程序/路由 以及每个配置项的 键/类型/默认/描述;插件使用者只改 配置项[].值。改配置 = 用记事本改「值」,≤5 秒热生效,不需要登录后台;管理面板的「插件」页也可可视化编辑同一个文件。
顶层字段
| 字段 | 类型 | 必填 | 默认 | 校验规则 |
|---|---|---|---|---|
标识 | 字符串 | 是 | — | 正则 ^[a-z][a-z0-9_]{1,31}$(首字符为字母,只含小写字母/数字/下划线,长 2~32),必须等于插件目录名 |
名称 | 字符串 | 是 | — | 非空,长度 ≤ 50 字符 |
版本 | 字符串 | 是 | — | 严格 x.y.z |
协议版本 | 整数 | 是 | — | 当前内核支持 1;不匹配则跳过该插件并记日志(明确拒绝优于模糊兼容) |
启用 | 布尔 | 否 | true | false 则跳过加载(不删目录即可临时停用) |
描述 / 作者 | 字符串 | 否 | 空 | 仅作展示 |
配置项 | 列表 | 否 | — | 见「插件配置项」 |
路由 | 列表 | 否 | — | 条数 ≤ 256;见下方「路由段」 |
程序 | 映射 | 条件必需 | — | 存在 处理: 程序 的路由时必需 |
路由段(路由 列表的每个元素)
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
路径 | 字符串 | 是 | — | 以 / 开头;静态段只允许小写字母/数字/ . / -(不含下划线);不含 ..、空格、反斜杠、//;支持 :参数名(单段捕获)与 *参数名(尾部通配),详见「RESTful 路径参数与尾部通配」 |
方法 | 字符串列表 | 是 | — | GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS,自动转大写 |
处理 | 字符串 | 是 | — | 声明式 或 程序;含参数/通配的路径只允许 程序 |
网络 | 字符串 | 否 | 空 | 空=普通 HTTP 路由;websocket=WS 长连接路由,必须同时满足 处理: 程序 + 方法 含 GET + 不声明 上传 段 + 不支持路径参数,详见「形态三:WebSocket 长连接」 |
空闲超时秒 | 整数 | 否 | 60 | 仅 网络: websocket 有效;上界 300;会话在该秒数内既无上行也无下行动作即关闭 |
响应 | 映射 | 声明式 必需 | — | 见「形态一:声明式」 |
超时秒 | 整数 | 否 | 全局默认 5 | 上限 300;从「请求正文读取完成之后」起算,含排队等待,不含客户端上传耗时 |
上传 | 映射 | 否 | — | 仅 处理: 程序 有效;见「上传治理」 |
传输 | 字符串 | 否 | 自动 | 仅 处理: 程序 有效;取值 自动 / 内存 / 落盘,见「正文三通道」 |
描述 | 字符串 | 否 | 空 | 仅供管理面板展示,内核路由匹配不使用 |
程序段(程序 映射)
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
命令 | 字符串 | 与 入口 二选一 | — | 启动命令,任意语言/任意可执行体;以 ./、../ 开头时按插件目录解析为绝对路径 |
入口 | 字符串 | 与 命令 二选一 | — | 便捷写法:未写 命令 时内核按扩展名推断解释器(见下表) |
参数 | 字符串列表 | 否 | — | 传给 命令 的参数;条数 ≤ 64 |
工作目录 | 字符串 | 否 | 插件目录 | 相对插件目录解析 |
环境 | 键值映射 | 否 | — | 额外环境变量,按键名排序追加(保证行为可复现) |
实例数 | 整数 | 否 | 全局 程序默认实例数(2) | 上限 64;单实例串行,并发靠多实例 |
排队上限 | 整数 | 否 | 全局 程序排队上限(64) | 上限 4096;无空闲实例时排队,超限返回 503 |
关闭超时秒 | 整数 | 否 | 5 | 上限 60;关闭时先关 stdin(EOF),超时未退出则强制终止 |
最大重启次数 | 整数 | 否 | 全局 程序最大重启次数(5) | 上限 100;熔断窗口内的失败上限 |
弹性实例上限 | 整数 | 否 | 0 = 不启用 | 填 > 实例数 即启用:按负载在 [实例数, 上限] 间自动伸缩,上界钳到 64 |
解释器推断表(未写 命令 时按 入口 扩展名推断):.py→python、.js→node、.php→php、.ps1→powershell -NoProfile -ExecutionPolicy Bypass -File、.bat/.cmd→cmd /c、.jar→java -jar、.dll→dotnet、.exe/.sh/无扩展名→直接执行。解释器不存在时内核硬失败并给出明确错误日志(写明推断出的命令是什么),不静默回退。显式 命令 是主路径。
插件配置项
配置项 声明「键/类型/默认/值/描述」,内核据此把值注入插件。使用者改「值」即可定制插件行为,≤5 秒热生效。
| 字段 | 必填 | 说明 |
|---|---|---|
键 | 是 | 配置键名,同一清单内不得重复 |
类型 | 是 | 6 种之一:string / int / float / boolean / json / text |
默认 | 否 | 作者声明的默认值(未填「值」时生效,也是「值」类型不符时的回退目标) |
值 | 否 | 使用者填写的当前值;留空 = 用「默认」 |
描述 | 否 | 面向使用者的说明(面板展示) |
规则:值 的类型必须与 类型 一致,不一致则回退到「默认」并记日志,不中断插件;值 未写(缺该键)或写成纯空白串 → 用「默认」(因此 string/text 类型不能被显式设为空串)。配置最终通过环境变量 插件配置(JSON 字符串,形如 {"问候语":"你好,欢迎访问"})传给程序形态插件;声明式路由用 {配置:键} 插值读取。
插件.yaml,使用者填的「值」会被作者声明值覆盖。升级请只替换「程序文件」,或在替换后重新填「值」。原 配置.yaml 已废弃,内核不再读取;若目录下仍有该文件,请把其中的值搬到 插件.yaml 的 配置项[].值 后删除。形态一:声明式(零代码)
响应.类型 共 5 种:
| 类型 | 必需字段 | 说明 |
|---|---|---|
text/plain | 内容 | 纯文本 |
text/html | 内容 | HTML;插值出的值会 HTML 转义 |
application/json | 内容 | 须为合法 JSON,不合法则丢弃该路由 |
重定向 | 目标 + 状态码 | 状态码限 301/302/307/308;目标支持插值 |
静态文件 | 文件 | 相对插件目录;越界则丢弃该路由;声明状态码为 200 时支持 Range/ETag/304 分段下载 |
可选字段 头:自定义响应头,值支持插值(如 X-Robots-Tag: noindex)。
插值变量(只做字面替换,不支持条件/运算/函数调用,因此不存在代码注入面):
| 变量 | 含义 | 变量 | 含义 |
|---|---|---|---|
{配置:键名} | 该配置项的当前值 | {请求路径} | 请求路径 |
{站点ID} | 当前站点 ID | {请求方法} | 请求方法 |
{站点名称} | 当前站点名称 | {查询串} | 原始查询串(未解码) |
{域名} | 当前请求的 Host | {客户端IP} | 已按可信代理列表解析的真实客户端 IP |
重定向目标校验:非空;不得含反斜杠 \(防 WHATWG 把 \ 当 / 绕过 // 拦截造成开放重定向);不得以 //(协议相对 URL)开头;以 / 开头视为站内,否则必须以 http:// / https:// 开头且能解析出 Host。不合法即丢弃该路由。
零代码最小示例:
# xiis.net/插件/站点1/apitest/插件.yaml
标识: apitest
名称: 接口测试
版本: 1.0.0
协议版本: 1
配置项:
- 键: 问候语
类型: string
默认: "你好"
值: "你好" # 使用者改这里
描述: 接口返回的问候内容
路由:
- 路径: /apitest # 路径只允许小写字母/数字与 . / -,不含下划线
方法: [GET, POST]
处理: 声明式
响应:
类型: text/plain
状态码: 200
内容: "{配置:问候语}"
头:
X-Robots-Tag: noindex
访问 http://{该站点任意绑定域名}/apitest → 你好。CMS / PHP / 纯静态站点通用。
形态二:程序(stdio 协议)
内核按 程序.命令 + 程序.参数 启动插件进程,并建立 stdin / stdout 两根管道。任意语言的判据不是「语言」,而是四项能力:能被 命令 启动起来、能读 stdin(一行 JSON 请求)、能写 stdout(一行 JSON 响应)、日志走 stderr 不污染 stdout。
三条传参通道不要混:
| 内容 | 通道 | 时机 | 谁写 |
|---|---|---|---|
| 跑哪个脚本、解释器选项 | 命令行 参数 | 启动时传一次,之后永不变 | 插件作者(写在 插件.yaml) |
| 站点上下文与插件配置 | 环境变量 | 启动时注入一次 | 内核 |
| HTTP 请求 | stdin(一行 JSON) | 每次请求 | 内核 |
| HTTP 响应 | stdout(一行 JSON) | 每次请求 | 插件 |
| 日志与调试输出 | stderr | 随时 | 插件 |
环境变量全集(启动时注入,与主程序环境合并):
| 环境变量 | 内容 |
|---|---|
插件标识 | 插件标识 |
插件站点ID / 插件站点名称 / 插件域名 | 站点上下文 |
插件网站路径 | 站点物理根目录绝对路径 |
插件数据目录 | 插件私有可写目录绝对路径(内核保证已创建) |
插件配置 | 全部配置项序列化后的 JSON |
插件实例序号 | 第几个实例(从 1 开始),便于插件区分 |
插件内核能力 | 能力位清单(逗号分隔):正文文件响应(响应落盘通道)、websocket事件、tcp事件、路径参数(RESTful 路径参数)。插件按「是否包含某能力位」判断并决定是否启用;旧内核无本变量时插件应自动降级(如大响应退回 正文base64、自行从 路径 解析参数)。协议版本号保持 1 不变——能力协商走环境变量而非版本号 |
进程生命周期:
【启动时,只一次】
内核: exec.Command(命令, 参数...) ← 参数只在这里用一次
同时注入环境变量,建立 stdin / stdout 两根管道
等待插件输出「就绪握手行」,就绪超时秒内未收到 → 启动失败
【每次请求,进程常驻、不重启】
内核 → 插件 stdin : {"方法":"GET","路径":"/apitest",...}\n
插件(循环体) : 读一行 → 解析 JSON → 处理逻辑 → 组织响应
插件 → 内核 stdout: {"状态码":200,"头":{...},"正文":"你好"}\n
内核 : 解析这一行 → 取出状态码/头/正文 → 原样透传给客户端
【关闭时】
内核关闭插件 stdin → 插件读到 EOF → 自行清理并退出(超时未退出则强制终止)
三条硬性约定:① 启动后第一行输出必须是握手行;② stdout 只输出协议 JSON,任何调试输出/日志一律走 stderr;③ 读到 stdin EOF = 内核要求退出,应自行清理后退出。优雅关闭用 stdin EOF 而不是信号,因为它在 Windows 与 Linux 上行为一致,插件无需写平台相关的信号处理代码。
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 与 $OutputEncoding,清单加 -NoProfile;cmd/.bat 默认代码页 936(GBK)——需先 chcp 65001,或改用 PowerShell / 编译型语言。Windows Python 别名陷阱:裸命令
python 可能命中 Microsoft Store 的 App 别名(假 python.exe,静默失败或弹商店);应先用 where python 确认指向真实解释器,必要时写绝对路径或改用 py 启动器。协议字段规格
就绪握手行(启动时一次,方向:插件 → 内核):插件进程启动并完成自身初始化后,必须向 stdout 输出第一行握手,然后才进入请求循环:
{"就绪":true,"协议版本":1}
内核在「就绪超时秒」(默认 10)内等待每个实例的握手行,收到才把该实例加入可用池;超时未收到 / 进程提前退出 → 该实例启动失败,记日志并重试拉起;握手行 协议版本 与清单 协议版本 不一致 → 跳过该插件并记日志。握手之后再往 stdout 写的每一行都必须且只能是响应。
请求(内核 → 插件,stdin,一行一个 JSON 对象,UTF-8):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
协议版本 | 整数 | 是 | 当前为 1 |
方法 | 字符串 | 是 | GET/POST/PUT/DELETE/PATCH;HEAD 已由内核映射为 GET 转发(内核剥响应正文);OPTIONS 不转发(内核统一应答 204 + Allow) |
路径 | 字符串 | 是 | 请求路径 |
查询 | 字符串 | 否 | 原始查询串(未解码,如 a=1&b=2) |
协议 | 字符串 | 是 | "http" 或 "https" |
头 | 对象:键→字符串数组 | 是 | 请求头(多值头用数组);已剥离 hop-by-hop 头 |
正文 | 字符串 | 否 | UTF-8 文本正文;与 正文字节base64、正文文件 三选一 |
正文字节base64 | 字符串 | 否 | 二进制或非 UTF-8 内容(也用于「JSON 转义后长度超单行上限」的 UTF-8 正文) |
正文文件 | 字符串 | 否 | 落盘中转通道:内核把正文写入插件数据目录 数据/上传/up_随机.tmp,本字段给出该文件绝对路径;此时 正文 与 正文字节base64 均为空。插件应流式读取(内存 O(64KB)),内核在本请求响应写回后自动删除该临时文件——插件若要留存必须在响应前完成复制/移动 |
站点ID / 站点名称 / 域名 | 整数 / 字符串 | 是 | 站点上下文;域名 为当前请求的 Host |
网站路径 | 字符串 | 是 | 站点物理根目录绝对路径 |
客户端IP | 字符串 | 是 | 已按项目可信代理列表逻辑解析的真实客户端 IP |
插件数据目录 | 字符串 | 是 | 插件私有可写目录绝对路径 |
超时秒 | 数值 | 是 | 本请求剩余超时(已扣除排队等待);超时从「请求正文读取完成之后」起算,因此客户端上传耗时不会占用本预算 |
路径参数 | 对象:参数名→字符串 | 否 | RESTful 路径参数:路由声明 :参数名 / *参数名 时下发已捕获的参数;静态路由省略该字段(旧插件完全不受影响)。值为 路径 中对应段的原文(已百分号解码);通配参数为剩余全部段以 / 连接。参数属不可信输入,插件输出到 HTML/JSON/SQL 前必须自行转义 |
响应(插件 → 内核,stdout,一行一个 JSON 对象):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
状态码 | 整数 | 是 | HTTP 状态码(合法区间 100~599,否则内核按 502 处理) |
头 | 对象:键→字符串数组 | 否 | 多值头用数组(如 "Set-Cookie": ["a=1", "b=2"]);内核剥离 hop-by-hop 头,Content-Length 由内核重算(插件设置无效),Date/Server 由内核覆盖 |
正文 | 字符串 | 否 | UTF-8 文本;与 正文base64、正文文件 三选一 |
正文base64 | 字符串 | 否 | 二进制响应 |
正文文件 | 字符串 | 否 | 大响应落盘通道:插件把响应写入 {插件数据目录}/下载/{随机名}.tmp 后,本字段只给出该文件的绝对路径(正文 与 正文base64 留空)。内核以流式方式(内存 O(32KB))回写客户端并在写回后自动删除——与请求侧完全对称,响应大小不受单行上限/正文上限约束。路径必须位于本插件数据目录之内(内核做包含性硬校验,越界一律 502 并记安全日志);文件不存在或指向目录同样 502 |
hop-by-hop 头剥离清单(双向):Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、TE、Trailer、Transfer-Encoding、Upgrade。站点安全响应头(CSP/X-Frame-Options 等,按站点级配置)由中间件链自动附加,插件仍可在 头 里声明额外自定义头。
完整示例(Python,无任何依赖):
# 插件.yaml
标识: apitest
名称: 接口测试
版本: 1.0.0
协议版本: 1
配置项:
- 键: 问候语
类型: string
默认: "你好"
程序:
命令: python
参数: ["./程序/入口.py"]
工作目录: ./程序
实例数: 2
弹性实例上限: 4 # 可选,0/缺省=不启用
路由:
- 路径: /apitest
方法: [GET, POST]
处理: 程序
- 路径: /apitest/download
方法: [GET]
处理: 程序
超时秒: 30
import sys, os, json, base64
数据目录 = os.environ.get("插件数据目录", ".")
配置 = json.loads(os.environ.get("插件配置") or "{}")
# 能力协商:拆成集合后做【成员判断】,避免未来能力位命名有前缀重叠时误判
内核能力位集合 = set((os.environ.get("插件内核能力") or "").split(","))
# 就绪握手行(启动时输出一次)
sys.stdout.write(json.dumps({"就绪": True, "协议版本": 1}, ensure_ascii=False) + "\n")
sys.stdout.flush()
def 处理(请求):
if 请求["路径"] == "/apitest/download":
# 大响应落盘通道:写入 {插件数据目录}/下载/ 后只回传路径,
# 内核流式回写客户端并在写回后【自动删除】该文件。
if "正文文件响应" not in 内核能力位集合: # 旧内核自动降级
return {"状态码": 200, "头": {}, "正文base64": base64.b64encode(b"fallback").decode()}
下载目录 = os.path.join(数据目录, "下载")
os.makedirs(下载目录, exist_ok=True)
文件路径 = os.path.join(下载目录, "结果.txt")
with open(文件路径, "wb") as f:
f.write((配置.get("问候语", "你好") + ",下载通道演示\n").encode("utf-8"))
return {"状态码": 200, "头": {"Content-Type": ["application/octet-stream"]},
"正文文件": os.path.abspath(文件路径)}
return {"状态码": 200, "头": {"Content-Type": ["text/plain; charset=utf-8"]},
"正文": 配置.get("问候语", "你好") + ",当前站点:" + 请求["站点名称"]}
for 行 in sys.stdin: # 读到 EOF 即退出
行 = 行.strip()
if not 行:
continue
try:
响应 = 处理(json.loads(行))
except Exception as 错误:
print("处理失败: %s" % 错误, file=sys.stderr) # 日志必须走 stderr
响应 = {"状态码": 500, "头": {}, "正文": "插件内部错误"}
sys.stdout.write(json.dumps(响应, ensure_ascii=False) + "\n")
sys.stdout.flush()
单独调试(不需要内核、不需要启动服务):
echo '{"方法":"GET","路径":"/apitest","站点名称":"悉思"}' | python 入口.py
注意单独调试时首行会多输出一个握手行,属正常现象。插件与数据的关系:插件自己连,内核不中介——插件进程直接用你自己的账号连你自己的数据库、自己读写文件,内核不提供任何数据访问接口、不需要令牌、不做数据代理;内核唯一提供的是站点上下文(站点 ID、站点名称、域名、网站路径、插件数据目录),目的是让插件不必自己解析 Host 判断当前是哪个站点。多站点场景下插件需按站点隔离数据时,自己用 插件站点ID 区分即可。
上传治理与正文三通道
上传治理认「站点级 + 全局」yaml,不读数据库。本项目的上传校验有两条通道,二者共享 yaml 口径,但只有 CMS 通道额外读数据库:
| 通道 | 服务对象 | 大小上限来源 | 扩展名来源 |
|---|---|---|---|
| CMS 常规上传 | 后台媒体库、前台会员上传、UEditor | ① 数据库 cms_system_configs.upload_max_size → ② yaml 安全.最大上传大小MB → ③ 默认 50MB | ① 站点级 upload_allowed_types → ② yaml 安全.允许上传扩展名 |
| 插件上传 | 声明了「上传」段的程序路由 | min 三层:① yaml 服务器.最大请求体MB ② yaml 安全.最大上传大小MB(站点级优先+全局回退)③ 清单 上传.最大MB | yaml 安全.允许上传扩展名 ∩ 清单 上传.允许扩展名;yaml「禁止访问扩展名」为硬黑名单 |
三条核心规则:① yaml 站点级优先,站点级未配置则回退全局(改 yaml 一处,插件与 CMS 同时生效);② 取交集、只收紧——三层大小取最小值,任何一层配大都不会放宽其余层;③ 黑名单优先于白名单——命中 yaml「禁止访问扩展名」立即拒绝,即使该扩展名也在白名单内。
| 上传段字段 | 必填 | 说明 |
|---|---|---|
最大MB | 否 | 插件自定义的接口边界;0/缺省 = 不额外收紧,完全沿用 yaml 上限。只能收紧不能放宽 |
文件名参数 | 否 | 内核从查询串哪个键取文件名(默认即 路径);属插件自身接口约定,yaml 无从得知 |
允许类型 | 否 | Content-Type 白名单,支持 image/* 通配;已由 yaml「安全」节点统一治理,通常无需重复声明,仅当插件确实只接受某类文件时才额外收紧 |
允许扩展名 | 否 | 与 yaml 白名单取交集;同上,通常无需重复声明 |
「上传」段本身是 opt-in 标记:写了内核才对该路由施加上传治理;不写则纯按普通请求处理。未声明「上传」段的路由不受影响——普通 POST/GET 不会被套用文件名与扩展名校验,避免对非上传路由的误伤。
内核在转发前依次校验,任一不符即拒绝且不转发插件:
| 顺序 | 校验 | 失败返回 | 说明 |
|---|---|---|---|
| 1 | 大小 ≤ min(请求体上限, 站点级 yaml 上限, 最大MB) | 413 | 有 Content-Length 时在读正文前拦截;chunked 由读取上限兜底 |
| 2 | 扩展名 不命中 yaml「禁止访问扩展名」 | 415 | 硬黑名单,优先于白名单 |
| 3 | 扩展名 命中 yaml「允许上传扩展名」 | 415 | 白名单;文件名取自 文件名参数 |
| 4 | 扩展名 命中清单 允许扩展名 | 415 | 与第 3 项取交集;清单未声明则跳过 |
| 5 | Content-Type ∈ 清单 允许类型 | 415 | 清单未声明则跳过(yaml 无此维度) |
| 6 | 正文文件头魔数与 Content-Type 匹配 | 415 | 挡「把 .exe 改名成 .png 并声明 image/png」;不依赖清单声明;未收录类型放行 |
正文三通道——编码通道由内核自动选择,插件必须三种都支持:
| 条件 | 通道 | 膨胀 | 说明 |
|---|---|---|---|
路由声明 传输: 落盘;或声明了「上传」段且正文 > 插件.上传落盘阈值KB(默认 256KB) | 正文文件 | 0(落盘) | 端到端 O(1):内核内存 O(32KB),插件流式读 O(64KB) |
| 正文为合法 UTF-8 且「JSON 转义后长度」未超单行上限 | 正文 | 1.00x | 纯文本,最小开销 |
| 其余情况(非 UTF-8,或转义后超限) | 正文字节base64 | 1.33x | 二进制与「高转义字符」正文 |
路由级 传输 声明(仅 处理: 程序 有效):自动(缺省)= 旧行为,仅当路由声明了「上传」段且正文预计超过落盘阈值时才落盘;落盘 = 强制落盘(有正文即落盘,即使小于阈值),普通程序路由无需声明「上传」段即可启用;内存 = 强制走内存通道,即使超过阈值也不落盘,是「插件未实现 正文文件 通道」时的兼容退路。
<、>、& 与控制字符各转义为 6 字节,实测 8MB 全 & 的合法 UTF-8 正文序列化后协议行达 48MB(6.00 倍),会导致插件读行失败 → 内核判失败 → 强杀实例 → 累计达阈值即熔断。内核现已在序列化前单遍扫描估算转义后长度,超限即改走 base64(恒定 1.33 倍)。「内存」不等于「快」——base64 的瓶颈是 CPU/分配,不是磁盘;大正文一律落盘更快更省内存。插件侧处理三种通道的标准写法:
中转文件 = 请求.get("正文文件") or ""
if 中转文件:
# 通道 1:落盘中转 —— 流式搬运,内存 O(64KB)
with open(中转文件, "rb") as 源, open(目标, "wb") as 目标文件:
while True:
块 = 源.read(65536)
if not 块:
break
目标文件.write(块)
# 不要删除中转文件:内核在响应写回后自动清理
elif 请求.get("正文字节base64"):
内容 = base64.b64decode(请求["正文字节base64"]) # 通道 2:二进制正文
else:
内容 = (请求.get("正文") or "").encode("utf-8") # 通道 3:UTF-8 文本正文
必读提示:内核不解析 multipart/form-data——需要表单上传时请由插件自行解析,或改用「正文即文件字节 + 查询串传文件名」的约定(本项目以 ?路径= 为准);含正文(≥64KB)或声明了「上传」段的请求,内核会记一条审计日志(站点/插件/路径/客户端IP/字节数/通道/中转文件/耗时/结果),日志中不含正文与请求头(不写 Cookie,避免凭据泄漏);大文件下载有两条通道——① 响应落盘通道(响应 正文文件 字段)适合「内容由插件动态生成」,不受正文上限约束;② 静态资源/ 托管(/_插件/{标识}/...)适合「文件已存在磁盘上」,支持 ETag/Last-Modified/Range 与 304,可断点续传与视频拖动。
RESTful 路径参数与尾部通配
插件路由支持 RESTful 风格路径参数:语法与主链路 Gin 完全一致,不引入新写法。静态路径仍是 O(1) 精确匹配,参数路由只在静态未命中时才参与匹配——既有插件与未装插件的站点读路径开销不变。
| 写法 | 含义 | 示例 | 捕获结果 |
|---|---|---|---|
:参数名 | 捕获一个非空段 | /apitest/users/:id | 请求 /apitest/users/42 → {"id":"42"} |
*参数名 | 捕获剩余全部段,只能放末段 | /apitest/files/*rest | 请求 /apitest/files/a/b.txt → {"rest":"a/b.txt"} |
匹配优先级:静态段 > 参数段 > 通配段。/apitest/users 与 /apitest/users/:id 并存时,请求 /apitest/users 命中静态路由,不会被参数路由抢走。
参数怎么拿到:内核把捕获结果放在协议请求的 路径参数 对象里(键=声明中的参数名,值=已解码的请求段),插件直接读即可,无需自己解析 URL。静态路由不携带该字段。
路由:
- 路径: /apitest/users
方法: [GET, POST] # 集合:GET 列表 / POST 创建
处理: 程序
- 路径: /apitest/users/:id
方法: [GET, PUT, DELETE] # 单体:GET 详情 / PUT 更新 / DELETE 删除
处理: 程序
- 路径: /apitest/files/*rest
方法: [GET]
处理: 程序
# 插件侧:直接读 路径参数,不需要 rsplit 解析路径
请求 = json.loads(行)
参数 = 请求.get("路径参数") or {}
if "id" in 参数:
用户ID = 参数["id"] # 例如 "42"
elif "rest" in 参数:
剩余路径 = 参数["rest"] # 例如 "a/b.txt"
硬约束(加载时校验,违反则丢弃该条路由并记日志):
| 约束 | 原因 |
|---|---|
含参数/通配的路径只允许 处理: 程序 | 声明式响应不读取请求上下文,参数捕获后无处可用 |
首段必须是静态段(禁止 /:id) | 根级参数会抢占 /favicon.ico、/robots.txt 等根级静态资源与后台路径 |
| 通配段必须是最后一段 | 中间通配会让「剩余段」语义产生歧义 |
参数名须匹配 ^[a-z][a-z0-9_]{0,31}$ 且同一路由内不重复 | 参数名会成为协议 JSON 键与日志字段,必须限定字符集 |
网络: websocket 的路由不支持参数/通配 | WS 事件通道只下发模板路径,捕获了也无处可用 |
| 静态前缀不得落入 CMS 路径空间 | 防止 /news/:id 吃掉 /news/123 这类真实内容页(见下方 CMS 优先) |
/news/:id 会吃掉 /news/ 下所有两段路径。内核在加载期检查参数路由的静态前缀(第一个参数段之前的部分)是否落入 CMS 路径空间:① 真实已注册路由表(CMS 前台、CMS 前后端接口、后台、静态资源——从 Gin 引擎实际路由模板推导,不是硬编码清单);② CMS 实体数据(栏目 slug、单页 slug、栏目自定义 route_prefix)。只要存在段边界上的前缀重叠,该路由直接丢弃,面板「未生效原因」写明冲突对象。插件参数路由请使用自己的独立前缀(如 /apitest/users/:id)。自动复检:栏目新增/编辑/删除成功后,CMS 会自动触发插件重扫,参数路由立即按新路径空间复检(新增栏目即拦、删除栏目即恢复),无需手动操作。
插件静态资源托管
插件目录里的 静态资源/ 通过统一 URL 前缀对外提供:
URL :/_插件/{插件标识}/{相对路径}
映射 :{插件目录}/静态资源/{相对路径}
例:/_插件/apitest/style.css
→ xiis.net/插件/站点1/apitest/静态资源/style.css
| 项 | 说明 |
|---|---|
| 方法 | 仅 GET / HEAD(HEAD 按 GET 处理、内核剥正文),其他方法落回原链路 |
| MIME | 按扩展名映射(复用项目静态服务的扩展名逻辑) |
| 路径穿越防护 | 拒绝 ..、盘符、UNC,越界返回 404 |
| 缓存协商 | 支持 ETag(If-None-Match)与 Last-Modified(If-Modified-Since)→ 304 |
| 分段下载 | 支持 Range:bytes=start-end、bytes=start-、bytes=-N 三种单段形式 → 206 + Content-Range;起始越界 → 416。多段 Range 按整文件 200 返回 |
| 单文件上限 | 32MB,超限按未找到处理并记日志 |
| 站点隔离 | 仅当该站点目录下存在此插件时生效 |
| 中间件链 | 走精简链(服务暂停 + 安全头 + 限流),与项目静态资源处理先例一致 |
/_插件 同时列入内核保留路径,插件路由不得声明以 /_插件/ 开头的路径。
形态三:WebSocket 长连接
插件可以声明 WebSocket 路由:内核完成握手升级,把客户端 WS 帧经 stdio 以「事件行」转发给插件,并以「动作行」接收插件的回发/广播/关闭指令。协议版本仍为 1(纯新增可选通道,未声明 WS 的插件完全不受影响)。
声明三要素(缺一不可):
路由:
- 路径: /wss # 必需;WS 路由通常是一个「专用前缀」
方法: [GET] # 必需且必须含 GET(WS 握手本质是 HTTP GET + Upgrade)
网络: websocket # 必需,声明本路由为 WS 长连接路由
处理: 程序 # 必需,必须为 程序(声明式无法承载长连接)
空闲超时秒: 60 # 可选,缺省 60,上界 300
程序:
命令: python
参数: ["./程序/入口.py"]
WS实例数: 8 # 可选,WS 路由【专用】实例数,缺省 8,上界 64
独占实例模型:一个 WS 会话独占一个实例。内核为 WS 路由单独拉起 WS实例数 个 WS 专属实例(与 HTTP 的 实例数 池完全隔离):会话期间该实例只服务本会话,会话结束才归还。因此单实例内仍是「一行事件进、动作行出」的串行模型,插件不需要自己实现会话并发调度。代价是最大并发 WS 连接数 = WS实例数,超出即拒绝新连接(503)。
上行事件(内核 → 插件 stdin,一行一 JSON):
| 字段 | 类型 | 说明 |
|---|---|---|
协议版本 | 整数 | 当前为 1 |
类型 | 字符串 | 连接建立 / 收到消息 / 连接关闭,插件按此分派 |
会话ID | 字符串 | ws_ + 32 位十六进制随机串,全事件/动作共用 |
路径 / 查询 / 协议 / 头 / 子协议 | — | 仅 连接建立:路由模板路径、原始查询串、http/https、请求头(已剥 hop-by-hop)、客户端 Sec-WebSocket-Protocol |
空闲超时秒 | 整数 | 仅 连接建立:本会话空闲超时 |
站点ID / 站点名称 / 域名 / 网站路径 / 客户端IP / 插件数据目录 | — | 仅 连接建立:站点上下文(与 HTTP 协议请求口径一致) |
数据类型 / 数据 | 字符串 | 仅 收到消息:文本 或 二进制(二进制为 base64) |
代码 / 原因 | 整数 / 字符串 | 仅 连接关闭:关闭码与原因 |
下行动作(插件 → 内核 stdout,一行一 JSON,可多次异步输出):
动作 | 会话ID | 说明 |
|---|---|---|
发送 | 必填 | 发给指定会话(会话不存在则内核忽略并记日志,不杀实例) |
广播 | 忽略 | 发给本插件本站点的全部 WS 会话(内核持有全部会话,故由内核执行) |
关闭 | 必填 | 关闭该会话(只能关闭本插件的会话,防跨插件越权) |
{"协议版本":1,"类型":"连接建立","会话ID":"ws_3f8a...","路径":"/wss","子协议":["chat"],"空闲超时秒":60}
{"协议版本":1,"类型":"收到消息","会话ID":"ws_3f8a...","数据类型":"文本","数据":"你好"}
{"会话ID":"ws_3f8a...","动作":"发送","数据类型":"文本","数据":"echo: 你好"}
{"动作":"广播","数据类型":"文本","数据":"系统公告"}
插件内核能力 含 websocket事件 才走本通道。WS 路由不支持路径参数/通配、不得声明 上传 段;违反任一条会在加载期丢弃该路由并记日志。形态四:TCP 监听服务
插件可以直接监听 TCP 端口(推送服务、私有协议、游戏/物联网网关等):内核接受连接,把字节流经 stdio 以「事件行」转发给插件,并以「动作行」接收发送/关闭指令。与 WS 事件通道同构(独占实例 + 事件行 + 动作行),协议版本仍为 1。
声明两处:
TCP服务:
- 标识: main # 必需,服务标识(插件内唯一),^[a-z][a-z0-9_]{1,31}$
端口: 0 # 【0 = 内核随机分配】;也可写死端口(如 9001)
绑定地址: 0.0.0.0 # 可选,默认 0.0.0.0;仅本机可用 127.0.0.1
处理: 程序 # 必需,必须为 程序(声明式无法承载字节流)
空闲超时秒: 300 # 可选;无数据 300 秒断连;上界 86400
最大连接数: 0 # 可选;0=不额外限制(受 TCP实例数 天然约束)
程序:
命令: python
参数: ["./程序/入口.py"]
TCP实例数: 8 # 可选,TCP 服务【专用】实例数,缺省 8,上界 64
| 端口写法 | 行为 | 适用场景 | 注意事项 |
|---|---|---|---|
端口: 0 | 内核随机分配空闲端口,写入日志、管理面板与实际监听 | 多站点/多插件共存,默认推荐 | 端口不固定,客户端需先查管理面板或 GET /admin/api/plugins/tcp/stats 获知实际端口 |
端口: 9001 | 按声明值监听(内核不分配) | 对接既有系统、要求固定端口 | 被占用即跳过该服务并记日志;多站点需自行错开端口 |
独占实例模型(与 WS 一致):一个 TCP 连接独占一个实例。最大并发连接数 = TCP实例数(超出即关闭新连接);与 HTTP 池、WS 池三者隔离。
上行事件(内核 → 插件 stdin):类型 为 TCP连接建立 / TCP收到数据 / TCP连接关闭;连接ID 为 tcp_ + 32 位十六进制;建立事件带 服务标识、站点上下文、客户端IP/客户端端口;数据事件带 数据类型(文本/二进制)与 数据(base64,单块 ≤64KB)。
下行动作:发送数据(向本连接写数据,数据类型 为 文本 或 二进制)、关闭连接(内核关闭本连接,实例归还)。无「广播」动作——TCP 是点对点,多播由插件自行遍历连接发送。
{"协议版本":1,"类型":"TCP连接建立","连接ID":"tcp_3f8a...","服务标识":"main","客户端IP":"1.2.3.4","客户端端口":52341}
{"协议版本":1,"类型":"TCP收到数据","连接ID":"tcp_3f8a...","数据类型":"二进制","数据":"5L2g5aW9Cg=="}
{"连接ID":"tcp_3f8a...","动作":"发送数据","数据类型":"文本","数据":"echo: 你好\n"}
{"连接ID":"tcp_3f8a...","动作":"关闭连接","原因":"服务端主动关闭"}
TCP收到数据 是内核按读到的块转发的(每块 ≤64KB),不是一条完整的业务消息。插件必须自行处理粘包/拆包(按长度前缀、分隔符等),或约定「一行一请求」。能力协商:环境变量
插件内核能力 含 tcp事件 才走本通道;控制面(连接统计、定向发送)统一收敛在管理端口 /admin/api/plugins/tcp/*,不暴露在主站点公开路径。热更新与生命周期
插件目录扫描复用 xiis.net.yaml 的 5 秒热更新 tick(同节拍、同停止通道、零新增配置项)。tick 内只做廉价指纹比对(插件根目录与各 站点{ID} 目录 mtime + 各插件目录 mtime + 插件.yaml mtime + 程序文件指纹),没变立即返回;发现变化后把实际的加载/卸载/重启投递到独立处理协程执行,本 tick 立即返回,不阻塞 yaml 配置重载。
| 触发 | 行为 |
|---|---|
| 程序启动 | 全量扫描所有站点的插件目录并加载 |
| 热更新 tick(5 秒) | 目录新增 → 加载;目录消失或 启用: false → 卸载;插件.yaml(含配置值)mtime 变化 → 重载;程序文件 mtime 变化 → 重载并重启该插件进程 |
| yaml 配置变更 | 总开关等变更触发插件侧重建;站点从 yaml 消失 → 卸载该站点全部插件进程 |
| 站点路由配置变更 | 站点配置缓存清除时触发一次冲突复检 |
用户操作闭环:复制插件目录进 xiis.net/插件/站点1/,最多约 5 秒后接口可访问;删掉目录,最多约 5 秒后路由消失;改插件程序源码(如 程序/入口.py)保存后最多约 5 秒自动重载并重启该插件进程,连 xxx.exe 都不用重启。全程不重启、不点按钮、不登录后台。
为什么必须把程序文件纳入变化检测:进程常驻的插件在自己进程内存里跑的是启动那一刻读入的源码,改脚本文件不会自动重载;而改动脚本文件不会改变插件目录自身的 mtime(变的只是脚本所在子目录)。实现上只对清单声明过的程序文件做 os.Stat,绝不递归目录(递归会走进 node_modules/vendor 这类大目录,把 5 秒 tick 拖慢)——代价是「入口脚本 import 的其他文件」不在监听范围内,改动那类文件时需同时改动入口脚本本身或动一下 插件.yaml 来触发重载。
生命周期全景:
加载:创建数据目录 → 拉起 实例数 个进程 → 等待每实例就绪握手
→ 全部就绪后路由入快照;任一实例就绪失败 → 重试拉起并计一次失败
转发:从空闲实例池取一个 → 写一行请求 → 读一行响应(剩余超时)→ 归还实例
超时:立即向客户端返回 504 → ★强制终止该实例(其管道内可能残留半行响应,绝不回收)
→ 重启补足实例数 → 计一次失败
崩溃:进程退出 → 该实例上所有在途请求立即返回 502(不等超时)
→ 记日志 + 计一次失败 → 未熔断则重启补足
熔断:「熔断窗口分钟」(默认5)滑动窗口内失败累计 ≥「最大重启次数」(默认5)
→ 停用该插件(路由摘除 + 进程全部停止 + 记日志)
恢复:插件.yaml(含配置值)或【程序文件】任一 mtime 变化 → 自动重新加载并复位失败计数
卸载:关闭该实例的 stdin(EOF)→ 等 关闭超时秒 → 未退出则强制终止
主程序退出:并行向全部插件实例发 EOF,总预算 = 关闭超时秒,超时强杀(无残留进程)
站点移除:yaml 热更新后站点消失 → 卸载该站点全部插件(进程 + 路由)
可用性保护参数(均可在 yaml 插件 节或管理面板调整):
| 配置文件键 | 默认 | 作用 |
|---|---|---|
启用插件系统 | false | 总开关;false 时零开销(不扫描插件目录,读路径首行判空即返回) |
插件目录 | ./xiis.net/插件 | 插件包根目录(按 站点{ID}/{插件标识}/ 组织) |
程序默认实例数 | 2 | 单个程序插件默认拉起的进程数(可被清单 程序.实例数 覆盖,上限 64) |
程序默认超时秒 | 5 | 单请求默认超时,从「请求正文读取完成后」起算、含排队(可被路由 超时秒 覆盖,上限 300) |
程序就绪超时秒 | 10 | 启动握手等待上限,超时未收到握手该实例启动失败并重试 |
程序排队上限 | 64 | 单插件默认排队上限,超限返回 503(上限 4096) |
程序最大重启次数 | 5 | 熔断窗口内的失败上限,超限停用该插件(上限 100) |
熔断窗口分钟 | 5 | 熔断滑动窗口 |
上传并发内存预算MB | 64 | 插件请求正文「在途资源」的全局总预算(跨插件共享,含内存路径与落盘路径),读正文前按字节占用,不足返回 503。这是「并发数 × 单请求正文」的硬上界(上限 1024) |
上传落盘阈值KB | 256 | 声明「上传」段的路由,正文超过该阈值即落盘中转;普通程序路由可用路由级 传输: 落盘 主动启用(上限 1048576) |
全局实例数上限 | 32 | 所有程序插件实例总数上限,超出则削减实际实例数并记日志(上限 512) |
正文上限不在此节——唯一权威来源是同一 yaml 的 服务器.最大请求体MB(默认 5)。协议单行上限 = 正文上限 × 2,且双向强制:内核 → 插件侧超限返回 413;插件 → 内核侧超限返回 502、记日志、计入失败计数。
弹性实例伸缩(清单 程序.弹性实例上限):内核每 2 秒检查一次——「在飞请求数 ≥ 当前实例数」连续 2 次成立且未达上限则扩容,扩容后 10 秒内不再动作;在飞请求数为 0 且持续 30 秒则缩容(一次只关一个,只动空闲实例,绝不打断在途请求),缩容后 10 秒内不再动作。扩容不是免费的——拉起进程要付启动与握手成本,因此只对「持续排队」的突发流量有意义;纯短脉冲流量建议直接调大 实例数。
stderr 采集:插件 stderr 由内核限速采集后写入站点日志——单行截断 4KB、每实例每分钟最多 1000 行,超限丢弃并补记一条汇总警告(防插件狂打日志撑爆日志与内存)。
保留路径与冲突检测
保留路径(内核自保,不是权限管控):
/api/ /admin /cms/v1 /.well-known/ /ws /_插件
占了 /admin → 管理面板打不开;占了 /api/ → 健康检查、控制台命令、ACME 挑战全失效,HTTPS 证书续签会失败;占了 /_插件 → 破坏所有插件的静态资源托管。
冲突检测在加载时执行,冲突则丢弃该路由并记日志(写明冲突对象),共六类来源:
| 类别 | 检测对象 | 规则 |
|---|---|---|
| 一、CMS 路由保留字 | 18 个保留字:content、tag、search、user、category、list、admin、cms、api、static、well-known、article、uploads、assets、favicon.ico、sitemap、robots.txt、page | 插件路径的首段不得命中 |
| 二、真实已注册路由表 | Q_主Gin引擎.Routes() 的全部路由模板(CMS 前台、CMS 前后端接口、后台、静态资源、健康检查等),取其参数段之前的静态前缀 | 参数/通配路由的静态前缀与该前缀存在段边界重叠即冲突(不依赖硬编码清单,CMS 新增路由形态自动纳入) |
| 三、数据库路由配置 | {表前缀}system_configs 的 route 分组(route_category、route_content、route_tag、route_search、route_user)与 seo 分组(slug_link_enabled、link_suffix 等) | 该前缀下整段是 CMS 的,插件不可占(静态路由精确比对;参数路由按前缀空间比对) |
| 四、CMS 站点内容实体 | 已有的单页 slug、栏目 slug、栏目自定义 route_prefix | 静态路由精确相等即冲突;参数路由与 slug 存在段边界前缀重叠即冲突(如 /articles/:id 撞栏目 articles) |
| 五、文件系统 | PHP 站点:网站目录下同名的 .php 文件或同名目录;纯静态站点:网站目录下同名的文件或目录 | 同名即冲突 |
| 六、插件之间 | 同一站点内两个插件声明了相同的「方法 + 路径」;参数路由按归一模板判重(/users/:id 与 /users/:name 视为同一模板) | 按插件标识字典序,小者保留、大者丢弃并记日志。规则确定、可预测、不依赖扫描顺序 |
| 七、反向抢占(运行期) | CMS 单页/栏目 创建与编辑保存时查一次插件路由快照 | 命中则拒绝保存并提示冲突对象(防「先装了插件 /tool,事后新建 slug=tool 的单页被插件遮蔽 404 且无提示」) |
优先级(2026年10月18日 修订):CMS 路径空间优先于插件参数路由。静态插件路由按「精确占位」与 CMS 实体逐条比对;参数/通配路由按「路径空间归属」判定——静态前缀(第一个参数段之前的部分)只要与任一 CMS 路径前缀存在段边界重叠(相同,或一方是另一方的父路径),该路由即被丢弃,面板「未生效原因」写明冲突对象。这样 /news/:id 不会吃掉 /news/123 这类真实内容页。冲突在加载期消灭,而不是靠运行期比先后;检测是低频动作(不在请求路径上),可以放心直接查库、扫目录;其中第三、四类配置来自数据库,而 {表前缀}system_configs 的站点配置缓存是固定 5 分钟 TTL,因此冲突检测必须直接查库取原值,不能走 5 分钟缓存。所有数据库查询均走 app/cms/model 与 service 层既有查询设施并复用 SQL 方言适配器,MySQL / PostgreSQL / SQLite 三方言全兼容。
自动复检:栏目新增/编辑/删除成功后,CMS 会调用内核的「请求插件重扫」,参数路由立即按最新 CMS 路径空间复检(新增栏目即拦、删除栏目即恢复);该调用是非阻塞信号,不影响 CMS 请求响应。
CMS 路由热重建与插件路由互不影响:插件路由由独立原子快照匹配,不在 Gin 路由树内。详见「路由热重建」。
插件.yaml(清单+配置)、数据/(插件私有可写区)、数据/上传/(请求落盘中转)、数据/下载/(响应落盘中转)、静态资源/(对外托管);运行态在原子路由快照与内存熔断记录中。目录存在即已安装,卸载就是删目录。管理接口:插件状态、重扫、启停、熔断复位、详情、清单读写、日志查询与调试请求等接口统一注册在管理端口(默认 8080,绑定 127.0.0.1)的
/admin/api/plugins/* 路由组下,由管理验证中间件统一做认证,不暴露在主站点的公开请求路径上。开发检查清单
清单与目录
- 目录名与
插件.yaml的标识完全一致;标识只含小写字母/数字/下划线,首字符为字母 名称、版本、协议版本等必需字段齐全,协议版本为 1- 每条路由的路径首段都避开了 CMS 保留字(18 个)
- 路由路径不与站点已配置的
route_*前缀冲突(栏目/内容/标签/搜索/用户中心) - 路由路径不以保留路径开头(
/api/、/admin、/cms/v1、/.well-known/、/ws、/_插件) - 参数路由(
:参数/*通配):首段是静态段、只用于处理: 程序、静态前缀不在 CMS 路径空间内(如不要写/articles/:id撞栏目articles) - WS 路由三要素齐全:
网络: websocket+处理: 程序+方法含GET,且不声明上传段、不含路径参数 - TCP 服务:
处理: 程序、端口0(随机,推荐)或固定端口且未被占用、TCP实例数覆盖预期并发 处理: 程序时命令与入口至少有一个- 用到的每个配置项都在
配置项里声明过
程序形态(stdio 协议)
- stdin/stdout 都用 UTF-8——PowerShell 要设
[Console]::OutputEncoding与$OutputEncoding;.bat要chcp 65001;否则中文会乱码 - 启动后第一行输出握手行
{"就绪":true,"协议版本":1},之后才进入请求循环 - stdout 只输出协议 JSON,所有调试输出、日志、
print/echo一律走 stderr - 每次请求输出单行合法 JSON,行内没有裸换行
- 读到 stdin EOF 时自行清理并退出(这是内核的关闭信号)
- 请求与响应在同一实例内严格一一对应,插件内部不要在单实例内并发处理
- 已确认解释器/可执行文件在目标机器上存在(如
python还是python3、powershell还是pwsh);Windows 上用where python确认没命中 Microsoft Store 假别名 - 能单独调试:
echo '{"方法":"GET","路径":"/apitest"}' | <你的启动方式>能正常返回握手行 + 一行 JSON - 崩溃或异常退出后能被重启并再次正常工作(内核会重启你,熔断窗口内最多
最大重启次数次) - 插件应实现三种通道入参(
正文/正文字节base64/正文文件)——凡声明了「上传」段或传输: 落盘的路由,正文超阈值时会走正文文件;插件若不处理该通道会拿到空正文 - 上传类路由按需声明「上传」段(大小/类型/扩展名由内核统一校验);需要落盘但不需要校验时,声明
传输: 落盘 - 上传类路由必须显式设置
超时秒(建议 ≥ 30);注意超时只衡量「排队 + 插件处理」,不含客户端上传耗时 - 落盘中转文件(
正文文件)不要在插件里删除——内核在响应写回后自动清理;要留存必须在响应前复制/移动 - 大文件不要走 stdio 正文返回(有单行上限)——动态生成的内容用响应
正文文件落盘通道,已存在的文件放静态资源/托管 - 参数路由:直接读协议请求的
路径参数对象,不要自己 rsplit 解析路径;参数属不可信输入,输出前必须转义 - WS 插件:按
类型(连接建立/收到消息/连接关闭)分派,用会话ID做会话上下文键;回发用动作: 发送,群发用动作: 广播;一个会话独占一个实例,单实例内串行 - TCP 插件:必须自行处理粘包/拆包(内核按 ≤64KB 块转发,不是完整业务消息);发送用
动作: 发送数据,关闭用动作: 关闭连接;无广播语义
数据与文件
- 需要持久化的东西写在自己的
数据/目录(路径来自插件数据目录环境变量) - 需要连数据库时自己连(账号密码由你在插件里配置),不要指望内核提供数据接口
HTML 与安全
- 站点安全响应头(CSP/X-Frame-Options 等)由内核经中间件链自动附加(站点级配置),无需插件处理;插件若需额外自定义头,在响应
头里声明 - 插件路由需要登录才能访问时,把插件路径配进站点级「静态资源保护-受保护路径」即可(现成鉴权复用),不必自己在插件里实现登录
发布
- 配置值写在
插件.yaml的配置项[].值(单一文件,无配置.yaml) - 升级时若整体替换
插件.yaml,需重新填写使用者自定义的配置「值」
插件.启用插件系统: false 即全量禁用且零开销——无需删除任何插件目录或数据库记录。性能保障:一个插件都没装时,快照指针为 nil,首行一次原子加载判空即返回;读路径无锁无分配(原子快照 + map 精确匹配);扫描、校验、冲突检测等重活全部在加载路径且已异步化,不阻塞热更新 tick。上线前建议用 hey 压测对比引入插件前后的 QPS/P99 确认零回退。模板开发
模板目录与页面模板
前台模板存放于 模板/站点{ID}/front/ 目录,支持多模板切换(后台「模板管理」上传/切换,模板路径可在站点管理中指定)。一个模板由一系列页面模板 HTML 文件组成,每个文件是一个完整页面骨架:
| 模板文件 | 对应页面 |
|---|---|
index.html | 站点首页 |
category.html | 栏目列表页 |
article.html | 内容详情页 |
about.html / docs.html / tag-tool.html 等 | 单页模板(以单页 code 命名,如 code=docs → docs.html) |
tag.html / tags.html | 标签详情页 / 标签汇总页 |
search.html | 站内搜索结果页 |
not_found.html 等 | 错误页(404 等) |
导航.html / 页脚.html / 头部css.html / 用户导航.html | 公共部件,通过载入标签复用 |
单页模板选择优先级
单页(如「关于我们」「产品文档」)渲染时按以下优先级选择模板:
页面 template 字段(后台可指定) > {code}.html > page.html > about.html
将模板文件命名为单页的 code(如 code=docs 对应 docs.html)即可自动匹配,无需额外配置。
模板复用与输出缓存
公共部件通过载入标签复用,页面渲染后进入 LRU 输出缓存并做 gzip/zstd 预压缩,静态资源同样支持文件缓存与压缩,保证高并发下极低延迟。头部 CSS 与脚本统一由部件引入并带 ?v= 时间戳版本参数防缓存。
<!-- 复用公共部件(CSS/导航/页脚,main.js 由 页脚.html 引入) -->
{xiis:载入 头部css.html}
{xiis:载入 导航.html}
{xiis:载入 页脚.html}
<!-- 模板内禁止行内样式/行内事件,样式统一进 static/v3/css/style.css,逻辑统一进 static/v3/js/main.js -->
URL 生成规则
动态链接(栏目/内容/标签/导航)的形态由数据库站点配置({表前缀}system_configs 表,见「站点业务配置」)驱动,需与前端实际渲染保持一致:
| 配置键({表前缀}system_configs) | 作用 |
|---|---|
slug_link_enabled | true 时栏目/内容智能链接返回 slug 格式(/category/news、/content/xxx);false 时保持 ID 格式。slug 含路径遍历/XSS 字符自动降级 ID 链接 |
tag_slug_link_enabled | 标签专属 slug 开关,独立于全局 slug_link_enabled,仅控制 /tag/{slug} 格式;true 时访问端启用严格模式(裸数字 ID 返回 404,slug 为空的标签降级允许 ID 访问) |
link_suffix | 设置如 .html 时所有动态链接自动附加后缀;静态栏目链接与已含后缀的伪静态链接不重复附加;非法值自动忽略 |
route_prefix_link_suffix_enabled / tag_link_suffix_enabled | 栏目 route_prefix 链接 / 标签链接是否附加 link_suffix 后缀(独立开关) |
search_keyword_param | 搜索关键词 URL 参数名(默认 q,如 /search.html?q=SEO),兼容旧版 keyword 参数自动兑底 |
模板中一律使用 [内容:智能链接]、[栏目:智能链接]、[标签:智能链接] 等「智能链接」字段输出 URL,即可自动适配上述开关,无需在模板内拼接。
模板标签参考
xiis 模板语言的标签名与参数名均支持中英文双语。标签系统源码位于 app/cms/service/标签系统.go,采用注册表模式,新增标签只需添加一行注册项。
全部标签按页面功能分类速查 · 点选组合生成代码 · 一键复制 · 含完整示例预设。本文仅列标签清单,详细参数与示例请使用标签生成器。
标签语法
标签分四类:全局标签(单值替换)、循环标签(成对包裹循环渲染)、点号标签(页面上下文取字段)、字段标签(循环内输出数据字段)。
<!-- 全局标签:单值替换,输出站点配置/统计/当前页面上下文 -->
<title>{xiis:网站标题}</title>
<meta name="keywords" content="{xiis:网站关键词}">
<footer>{xiis:备案号} · {xiis:公安备案}</footer>
<!-- 循环标签:成对使用,参数值为双引号包裹 -->
{xiis:内容列表 栏目="1" 数量="10"}
<a href="[内容:智能链接]">[内容:标题]</a>
<time>[内容:发布时间 Y-m-d]</time>
<p>[内容:摘要]</p>
{/xiis:内容列表}
<!-- 点号标签:页面上下文取字段,无需循环包裹(仅对应类型页面生效) -->
<h1>{xiis:内容.标题}</h1>
<p>{xiis:栏目.名称} · {xiis:标签.使用次数} · {xiis:单页.标题}</p>
<!-- 中英文双语写法等价,参数可混用 -->
{xiis:list num="10" category="1" order="desc"}
{xiis:内容列表 数量="10" 栏目="1" 排序="desc"}
<!-- 搜索页分类筛选:按钮 data-filter 与卡片 data-type 必须同基准 -->
{xiis:栏目列表 数量="10"}
<button data-filter="[栏目:编号]">[栏目:名称]</button>
{/xiis:栏目列表}
{xiis:内容列表 数量="10" paging="true"}
<a href="[内容:智能链接]" data-type="[内容:顶级分类ID]">[内容:标题]</a>
{/xiis:内容列表}
{xiis:内容列表 num="10" 栏目="1"}。{xiis:前缀.字段名} 依赖当前页面已注入的上下文数据,仅在对应类型页面输出——内容. 仅内容详情页、栏目. 仅栏目列表/详情页、单页. 仅单页详情页、标签. 仅标签详情页;其它页面输出为空字符串。4 套点号标签的字段可在 标签生成器「构建模式 → 标签类型 → 点号标签」中下拉选取并一键生成。字段标签
字段标签在循环标签内部使用,前缀必须与循环标签匹配(如 {xiis:内容列表} 内使用 [内容:xxx])。支持三种形态:
| 形态 | 语法 | 示例 |
|---|---|---|
| 基础字段 | [前缀:字段名] | [内容:标题] |
| 格式参数 | [前缀:字段名 格式参数] | [内容:发布时间 Y-m-d H:i:s] |
| 三元条件 | [前缀:字段名?真值:假值] | [内容:封面图?有封面:无封面] |
常用字段前缀与字段示例:
| 前缀 | 配合循环标签 | 常用字段示例 |
|---|---|---|
内容 | {xiis:内容列表} | [内容:标题] [内容:智能链接] [内容:摘要] [内容:封面图] / [内容:cover_image] [内容:封面图片] / [内容:cover_image_url] [内容:发布时间 Y-m-d] [内容:分类ID] / [内容:category_id] [内容:分类名称] / [内容:category_name] [内容:顶级分类ID] / [内容:top_category_id] |
栏目 | {xiis:栏目列表} | [栏目:名称] [栏目:编号] / [栏目:id] [栏目:别名] [栏目:智能链接] [栏目:内容数量] |
单页 | {xiis:单页列表} / {xiis:单页详情} | [单页:名称] [单页:链接] [单页:打开方式属性] [单页:标题] |
标签 | {xiis:标签列表} | [标签:名称] [标签:智能链接] |
导航 导航子分类 | {xiis:导航列表} | [导航:名称] [导航:智能链接] [导航子分类:名称] |
搜索 | {xiis:热门搜索} | [搜索:关键词] [搜索:链接] |
sql SQL | {xiis:sql} | [sql:列名] [sql:自增id] [sql:总数] [sql:created_at Y-m-d] |
内容/栏目/单页/标签四类还有 点号标签系列(如 {xiis:内容.标题}),可直接在对应详情页输出任意字段,无需包循环;字段全集与类型专属字段(article/product/video/page/app)请在标签生成器中查阅。
[内容:分类ID]([内容:category_id])返回内容实际所属栏目,可能是任意层级子栏目;[内容:顶级分类ID]([内容:top_category_id])则上溯到顶级栏目。当筛选按钮由 {xiis:栏目列表}(默认只输出顶级栏目)生成时,卡片必须用 [内容:顶级分类ID] 与该按钮的 [栏目:编号] 对齐——若误用 slug 类字段([栏目:别名] / [内容:分类别名]),两者永不相等,点击筛选后结果恒为空。全局标签速查(47 个)
| 标签名 | 英文别名 | 说明 |
|---|---|---|
{xiis:网站标题} | sitetitle | 网站标题 |
{xiis:网站关键词} | sitekeywords | 网站关键词 |
{xiis:网站描述} | sitedescription | 网站描述 |
{xiis:网站地址} | siteurl | 网站地址 |
{xiis:网站标志} | sitelogo | 网站 Logo |
{xiis:网站邮箱} | siteemail | 网站邮箱 |
{xiis:网站电话} | sitephone | 网站电话 |
{xiis:网站图标} | sitefavicon / favicon | 网站图标 |
{xiis:OG图片} | ogimage / siteogimage | OG 图片 |
{xiis:备案号} | icp / siteicp | 备案号 |
{xiis:公安备案} | policeicp / sitepoliceicp | 公安备案 |
{xiis:版权信息} | copyright / sitecopyright | 版权信息 |
{xiis:联系地址} | siteaddress / address | 联系地址 |
{xiis:统计代码} | analytics / siteanalytics | 统计代码 |
{xiis:SEO标题} | seotitle / siteseotitle | SEO 标题 |
{xiis:错误提示} | errormsg | 错误提示 |
{xiis:错误类型} | errortype | 错误类型 |
{xiis:状态码} | statuscode / code | HTTP 状态码 |
{xiis:内容总数} | contentcount | 内容总数 |
{xiis:栏目总数} | categorycount | 栏目总数 |
{xiis:标签总数} | tagcount | 标签总数 |
{xiis:用户总数} | usercount | 用户总数 |
{xiis:单页总数} | pagecount | 单页总数 |
{xiis:附件下载总数} | attachmentdownloadtotal / downloadtotal / 附件下载量 / 下载总数 | 本篇内容全部附件下载数之和(全局标签,2026-10-11 新增) |
{xiis:模板头部} | header | 模板头部 |
{xiis:模板底部} | footer | 模板底部 |
{xiis:搜索关键词} | keyword | 搜索关键词 |
{xiis:内容ID} | contentid | 内容 ID |
{xiis:搜索总数} | searchtotal | 搜索总数 |
{xiis:当前页码} | currentpage | 当前页码 |
{xiis:总页数} | totalpages | 总页数 |
{xiis:每页数量} | pagesize | 每页数量 |
{xiis:搜索分页} | searchpagination / pagination | 搜索分页 |
{xiis:栏目分页} | categorypagination / catpagination | 栏目分页 |
{xiis:页面内容总数} | pagecontentcount | 页面内容总数 |
{xiis:当前位置} | breadcrumb / position | 面包屑导航 |
{xiis:时间} | time / now / 当前时间 | 当前时间(支持格式/偏移) |
{xiis:时间戳} | timestamp / unixtime | Unix 时间戳(秒/毫秒/微秒/纳秒) |
{xiis:随机字符串} | random / rand | 随机字符串(crypto/rand 安全) |
{xiis:页面分类顶层名称} | pagecategorytopname | 页面分类顶层名称 |
{xiis:页面分类顶层链接} | pagecategorytopurl | 页面分类顶层链接 |
{xiis:页面分类父层名称} | pagecategoryparentname | 页面分类父层名称 |
{xiis:页面分类父层链接} | pagecategoryparenturl | 页面分类父层链接 |
{xiis:页面分类顶层激活} | pagecategorytopactive | 页面分类顶层激活(「全部」筛选按钮高亮) |
{xiis:页面分类归属链接} | pagecategoryownerurl | 页面分类归属链接(筛选列表归属栏目,「全部」按钮链接) |
{xiis:页面分类归属激活} | pagecategoryowneractive | 页面分类归属激活(「全部」按钮高亮,三元条件) |
{xiis:route_tag} | route_tag(配置键) | 系统配置项输出(按站点取值,如路由前缀;未配置输出空) |
{xiis:site_slogan} | site_slogan(变量名) | 模板变量输出(配置表未命中时按模板变量名取值) |
{xiis:页面激活} | pageactive | 页面激活状态(三元条件) |
循环标签速查(26 个)
| 标签名 | 英文别名 | 说明 |
|---|---|---|
{xiis:内容列表} | list / contentlist | 内容列表 |
{xiis:栏目列表} | category / categorylist | 栏目列表 |
{xiis:标签列表} | taglist | 标签列表 |
{xiis:评论列表} | commentlist | 评论列表 |
{xiis:内容详情} | contentdetail | 内容详情 |
{xiis:分类信息} | categoryinfo | 分类信息 |
{xiis:标签信息} | taginfo | 标签信息 |
{xiis:相关内容} | related | 相关内容 |
{xiis:页面内容列表} | pagecontentlist | 页面内容列表 |
{xiis:页面评论列表} | pagecommentlist | 页面评论列表 |
{xiis:页面标签列表} | pagetaglist | 页面标签列表 |
{xiis:页面分类列表} | pagecategorylist | 页面分类列表 |
{xiis:子分类列表} | subcategory | 子分类列表(嵌套标签) |
{xiis:内容标签列表} | contenttaglist | 内容标签列表 |
{xiis:激活状态} | active | 激活状态(块标签) |
{xiis:条件判断} | if / 条件 / 如果 | 条件判断 |
{xiis:导航列表} | nav | 导航列表 |
{xiis:导航子分类} | navchildren | 导航子分类 |
{xiis:友情链接} | friendlylink / friendlink / flink | 友情链接 |
{xiis:广告列表} | ad / ads / adlist | 广告列表 |
{xiis:单页列表} | pagelist | 单页列表 |
{xiis:单页详情} | pagedetail / pageinfo | 单页详情块(旧名「页面详情」已废弃) |
{xiis:热门搜索} | hotsearch / hotkeyword / 热门搜索词 | 热门搜索 |
{xiis:收费附件列表} | paidattachments / paidattach / 付费附件列表 | 收费附件列表(字段含 [附件:下载次数] 单个附件累计下载数) |
{xiis:数据列表} | datalist / data / jsonlist / list / 文件列表 / files / filelist / fileslist | 数据列表(通用JSON列表) |
{xiis:sql} | SQL / SQL查询 / 查询 / 数据查询 | SQL标签(模板直写只读SQL,需开启站点开关,详见下方「SQL标签」) |
SQL标签
2026年10月08日 新增。在模板中直接书写只读 SQL(SELECT / WITH),查询结果按循环标签方式渲染,字段前缀 [sql:列名]。适用于标准标签无法覆盖的复杂查询(多表关联统计、自定义聚合、跨表取数等)。
sql_tag_enabled 改为 true(按站点隔离,site_id=0 为全局兜底)。未开启时标签整体输出空字符串,页面其余内容正常渲染。配置有 5 分钟缓存,修改后可到「缓存管理」清理「系统配置统一缓存」立即生效。| 项 | 说明 |
|---|---|
| 标签名 | {xiis:sql} / {xiis:SQL} / {xiis:SQL查询} / {xiis:查询} / {xiis:数据查询} |
| 字段前缀 | [sql:列名] / [SQL:列名],列名由 SQL 的 AS 别名决定 |
| 参数 | sql(别名 语句 / 查询语句 / query,必填);num(别名 数量,行数上限,默认 200);timeformat(别名 时间格式) |
| 通用字段 | [sql:自增id](从1开始)、[sql:索引0](从0开始)、[sql:总数] |
| 能力 | 聚合统计、多表关联、分组统计(GROUP BY)、与 {xiis:if} 嵌套、自嵌套(最多 3 层)、字段级时间格式;不支持 WITH(CTE) |
| 结果上限 | 最多 200 行,超出按上限截断;num 参数仅可收窄 |
| 结果缓存 | 固定 60 秒进程内缓存(键 = sqltag:站点ID:SQL原文),需立即生效可到「缓存管理」清理 |
| 多数据库 | 语句经 GORM Raw/Scan 执行,MySQL / PostgreSQL / SQLite 三库通用;表名需写完整前缀(如 {表前缀}contents) |
| 不支持占位符 | 模板不支持传参,SQL 中不能含 ? 占位符(将因缺少参数执行失败) |
| 站点隔离 | 服务端自动注入当前站点 site_id,无需也不允许手写;写其他站点 id 或 IN 含其他站点 → 拒绝执行;JOIN 的关联表同样自动隔离(注入到 ON 内,LEFT JOIN 语义不变);WITH(CTE)整体拒绝(别名可伪装成不含 site_id 的表绕过隔离);子查询无法自动注入 → 拒绝(需显式写当前站点) |
| 全局共享表 | 主表不含 site_id 列(如 {表前缀}content_types)→ 不注入,数据本就全站点共享 |
sql,按 SQL 子句顺序可视化配置:数据表(FROM) → 关联表(JOIN) → 筛选(WHERE) → 分组(GROUP BY) → 聚合函数 → 分组筛选(HAVING) → 输出字段(SELECT) → 去重(DISTINCT) → 排序(ORDER BY) → 条数(LIMIT),SQL 实时生成并填入参数框(表与字段均带中文注释,敏感表不出现)。支持多表 JOIN(自动猜测关联字段)、聚合统计、按聚合别名排序、字段别名。构建器无需登录:表/字段清单来自项目自带的 sql-tag-meta.json(默认库结构,可自行编辑增删表或修正注释,改后在后台「缓存管理 → SQL标签」清理一次即生效);站点未开启 sql_tag_enabled 时不展示任何表结构。<!-- 最新文章 5 条(含时间格式与自增序号) -->
{xiis:sql sql="SELECT id, title, view_count, created_at FROM {表前缀}contents WHERE status=1 ORDER BY id DESC LIMIT 5" timeformat="Y-m-d"}
<a href="/article/[sql:id]">[sql:自增id]. [sql:title]</a>
<span>浏览 [sql:view_count]</span> <time>[sql:created_at]</time>
{/xiis:sql}
<!-- 聚合统计(单行) -->
{xiis:sql sql="SELECT COUNT(*) AS cnt FROM {表前缀}contents WHERE status=1"}
<p>共 [sql:cnt] 篇</p>
{/xiis:sql}
<!-- 多表关联 + 与条件判断嵌套 -->
{xiis:sql sql="SELECT c.id, c.title, cat.name AS category_name FROM {表前缀}contents c LEFT JOIN {表前缀}categories cat ON c.category_id=cat.id WHERE c.status=1 LIMIT 5"}
{xiis:if 条件="category_name"}
<li>[sql:category_name]:[sql:title]</li>
{/xiis:if}
{/xiis:sql}
<!-- 每个栏目的文章数(JOIN + 分组聚合 + HAVING + 按别名排序) -->
{xiis:sql sql="SELECT cat.name AS category_name, COUNT(*) AS cnt FROM {表前缀}contents c JOIN {表前缀}categories cat ON c.category_id=cat.id WHERE c.status=1 GROUP BY cat.name HAVING COUNT(*) > 1 ORDER BY cnt DESC LIMIT 10"}
<li>[sql:category_name]:[sql:cnt] 篇</li>
{/xiis:sql}
安全模型:SQL标签执行模板中书写的任意 SQL 存在极高风险,故设置多重校验(任一不通过即拒绝执行、写入服务日志,模板输出空字符串):
| # | 校验项 | 说明 |
|---|---|---|
| 1 | 站点开关 | sql_tag_enabled 必须为 true;站点ID为 0 时强制拒绝 |
| 2 | 语句长度 | 上限 8192 字节 |
| 3 | 多语句拦截 | 禁止分号(防 SQL 堆叠注入),允许结尾单个分号 |
| 4 | 注释拦截 | 禁止 --、/*、#(防注释绕过关键字检测) |
| 5 | 只读白名单 | 首关键字必须为 SELECT 或 WITH |
| 6 | 禁用关键字 | 写操作/DDL/DCL(INSERT、UPDATE、DELETE、DROP、CREATE、ALTER、TRUNCATE、GRANT、REVOKE、SET、CALL、EXEC 等);危险函数(OUTFILE、DUMPFILE、LOAD_FILE、SLEEP、BENCHMARK、PG_SLEEP 等);FOR UPDATE |
| 7 | 敏感表黑名单 | users、user_meta、user_roles、permissions、system_configs、api_keys、sites、member_info、member_levels、points_log、各类订单/充值/授权码表、日志表、定时任务与消息表等 |
| 8 | 系统库拦截 | mysql、information_schema、performance_schema、sys、pg_catalog、pg_toast、sqlite_master 等 |
| 9 | 系统信息函数拦截 | @@version、@@datadir、DATABASE()、SCHEMA()、USER()、CURRENT_USER()、VERSION() 等 |
| 10 | 构建器元数据接口 | /cms/v1/sql-tag/tables(公开只读)仅返回项目自带的默认库结构清单中的非敏感表;表名归一化后与清单精确比对,未命中即 404(无表结构探测面);站点未开启 sql_tag_enabled 时返回空清单/403,与标签可用性保持一致 |
<!-- … --> 内的 SQL 标签不执行;结果集按站点隔离缓存。详细示例与参数生成见 标签生成器 /tag-tool。载入模板标签(6 个别名)
| 标签名 | 说明 |
|---|---|
{xiis:载入} | 载入模板 |
{xiis:导入} | 导入模板 |
{xiis:引入} | 引入模板 |
{xiis:include} | Include template |
{xiis:zr} | 载入模板(缩写) |
{xiis:zairu} | 载入模板(拼音) |
使用注意事项
- 参数引号:参数值必须使用双引号包裹
- 中英文混用:中英文参数可混用,如
{xiis:内容列表 num="10" 栏目="1"} - 字段标签前缀:字段标签前缀必须与循环标签匹配(内容列表内用
[内容:…]) - 时间格式优先级:字段级格式 > 列表级格式 > 默认格式
- 排序字段白名单:仅允许白名单内字段排序,防止 SQL 注入
- 多站点隔离:所有数据库查询均强制携带
site_id条件 - 嵌套深度限制:全局标签 10 层,循环标签 3 层,数据列表 3 层,SQL标签 3 层
- SQL标签:
{xiis:sql}默认关闭,需站点配置开启sql_tag_enabled;仅允许 SELECT/WITH 只读查询,敏感表/系统库/写操作一律拦截;站点隔离由服务端自动完成(含 JOIN 关联表),不要手写site_id(写其他站点会被拒绝);支持 JOIN / 分组聚合 / HAVING / DISTINCT,不熟悉 SQL 可用标签生成器的可视化构建器按 SQL 子句顺序配置 - 智能链接:URL 一律用「智能链接」字段输出,自动适配 slug/link_suffix 配置
- 详细参考:参数映射、排序白名单、时间格式、文字属性、完整示例见 标签生成器 /tag-tool
API 接口参考
本章系统梳理了系统对外提供的全部 API 接口,覆盖前台公开接口、前台认证接口、后台登录与验证码、后台管理接口四大类,共约 404 个接口。所有列表接口统一支持 page(页码)与 limit/page_size(每页条数,上限 100)分页参数,下表不再重复列出。
通用约定
所有 API 走 JSON 协议,统一响应格式见下表。后台管理 API 需同时携带 Authorization: Bearer <token>、X-Site-ID: <站点ID>、X-CSRF-Token: <csrfToken> 三个请求头;登录与验证码接口免 CSRF。X-Site-ID 为 0 表示全部站点(仅超级管理员)。
1.1 基础路径前缀
| 域 | 前缀 | 示例 |
|---|---|---|
| 前台公开 API | /cms/v1/ |
https://www.example.com/cms/v1/contents |
| 前台认证 API | /cms/v1/(需 Bearer Token) |
https://www.example.com/cms/v1/user/info |
| 后台管理 API | /cms/v1/admin/(需 Bearer Token + 管理员) |
https://www.example.com/cms/v1/admin/users |
| 后台登录/验证码 | /admin/(公开) |
https://www.example.com/admin/api/login |
| 后台页面路由 | /admin/(需 Cookie 认证) |
https://www.example.com/admin/content |
| 管理控制面板 | /admin/auth/ |
http://127.0.0.1:8080/admin/auth/refresh(本机管理端口) |
1.2 认证方式
| 场景 | 认证头 | 说明 |
|---|---|---|
| 前台用户接口 | Authorization: Bearer <token> |
登录后获取 JWT Token |
| 后台管理接口 | Authorization: Bearer <token> + X-Site-ID: <站点ID> + X-CSRF-Token: <csrfToken> |
三头联合认证 |
| 后台页面路由 | Cookie(xiis-admin-token / xiis-admin-refresh-token) |
管理面板 Cookie 认证 |
1.3 统一响应格式
{
"code": 200,
"message": "操作成功",
"data": { ... },
"timestamp": 1697000000
}字段说明:code 取 200/0 表示成功,401 未认证,403 无权限;列表接口 data 含 list(数组)与 total(总数);登录返回 token、refreshToken、csrfToken 与用户信息。
前台公开 API
前缀 /cms/v1/,无需认证即可访问。包含认证注册、验证码、内容栏目、标签搜索、评论、支付回调、二维码、系统消息、附件、产品、扩展模块、SQL 标签构建器等子模块。
2.1 认证与注册
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/auth/register |
用户注册 | body: {username, password, email, captcha_id, captcha_x, captcha_y, email_code}(其中 captcha_id、captcha_x、captcha_y、email_code 为可选) |
| POST | /cms/v1/auth/login |
用户登录 | body: {username, password, rememberMe, captcha_id, captcha_x, captcha_y}(其中 rememberMe、captcha_id、captcha_x、captcha_y 为可选) |
| POST | /cms/v1/auth/logout |
用户登出 | 无(需已登录 Token) |
| POST | /cms/v1/auth/refresh |
刷新 Token | body: {refresh_token} 或从 Cookie 读取 |
| POST | /cms/v1/auth/forgot-password |
忘记密码 | body: {email} |
| POST | /cms/v1/auth/reset-password |
重置密码 | body: {email, code, new_password} |
| POST | /cms/v1/auth/send-register-code |
发送注册验证码 | body: {email} |
2.2 验证码
| 方法 | 路径 | 用途 | 请求参数 | 响应 |
|---|---|---|---|---|
| GET | /cms/v1/captcha |
获取滑块验证码 | 无 | {captcha_id, master_base64, tile_base64, master_width, master_height, tile_width, tile_height, tile_y} |
| POST | /cms/v1/captcha/verify |
校验滑块 | body: {captcha_id, x, y} |
{success: bool} |
| GET | /cms/v1/captcha/config |
验证码开关配置 | 无 | {captcha_enabled, captcha_login_enabled, captcha_register_enabled, ...} |
2.3 内容与栏目
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/contents |
内容列表 | query: {page, limit, category_id, tag_id, keyword, sort, order}(其中 category_id、tag_id、keyword、sort、order 为可选) |
| GET | /cms/v1/contents/:id |
内容详情 | 路径参数: id |
| GET | /cms/v1/contents/:id/pay-info |
内容付费信息 | 路径参数: id |
| GET | /cms/v1/categories |
栏目列表 | query: {page, limit} |
2.4 标签与搜索
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/tags |
标签列表(JSON) | query: {page, size} |
| GET | /cms/v1/search |
内容搜索 | query: {keyword, page, limit, category_id}(其中 category_id 为可选) |
2.5 评论
| 方法 | 路径 | 用途 | 请求参数 | 认证 |
|---|---|---|---|---|
| GET | /cms/v1/comments/config |
评论配置查询 | 无 | 公开 |
| GET | /cms/v1/comments |
评论列表 | query: {content_id, page, limit} |
可选认证(已登录可看待审核评论) |
2.6 支付回调
| 方法 | 路径 | 用途 | 请求参数 | 安全 |
|---|---|---|---|---|
| POST | /cms/v1/recharge/callback |
充值回调 | 第三方支付回调数据 | IP白名单 + HMAC签名 |
| POST | /cms/v1/payment/alipay/notify |
支付宝异步回调 | 支付宝 form 表单 | RSA2 验签 |
| POST | /cms/v1/payment/wechat/notify |
微信支付回调 | 微信 JSON 通知 | RSA-SHA256 + AES-256-GCM |
| GET | /cms/v1/payment/methods |
支付方式列表 | 无 | 公开 |
2.7 二维码
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/qrcode |
生成二维码 PNG | query: {content, size}(其中 size 为可选) |
2.8 系统消息
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/system-messages |
系统消息列表 |
2.9 附件
| 方法 | 路径 | 用途 | 认证 |
|---|---|---|---|
| GET | /cms/v1/attachments |
附件列表 | 公开 |
| GET | /cms/v1/attachments/:id |
附件详情 | 公开 |
| GET | /cms/v1/attachments/:id/preview |
附件预览 | 公开 |
| POST | /cms/v1/attachment/access |
附件访问统一入口(auto_pay 控制自动扣费) | 可选认证 |
| GET | /cms/v1/attachment/download/signed |
签名下载附件 | 可选认证 |
2.10 产品
| 方法 | 路径 | 用途 | 认证 |
|---|---|---|---|
| GET | /cms/v1/products/:id/purchase-info |
产品购买信息 | 可选认证 |
2.11 扩展模块
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/forms |
表单列表 |
| GET | /cms/v1/pages |
页面列表 |
| GET | /cms/v1/pages/:slug |
页面详情(JSON) |
| GET | /cms/v1/template-vars |
模板变量列表 |
2.12 SQL 标签构建器
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/sql-tag/tables |
可选表清单 |
| GET | /cms/v1/sql-tag/tables/:name/columns |
表字段清单 |
前台认证 API
前缀 /cms/v1/,请求头需携带 Authorization: Bearer <token>。包含用户信息、评论操作、表单提交、会员、充值、会员购买、支付预下单、购买与下载、内容互动、媒体、互动、通知、附件付费、产品购买、产品授权码、用户地址簿等子模块。
3.1 用户信息
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/user/info |
获取用户信息(可选认证,未登录返回 HTTP 200 + {状态码:401, 消息:"未登录"},避免浏览器自动 console.error;已登录返回 {状态码:0, 数据:{...}}) |
无 |
| PUT | /cms/v1/user/info |
更新用户信息 | body: {nickname, avatar, email, phone}(其中 nickname、avatar、email、phone 为可选) |
| PUT | /cms/v1/user/password |
修改密码 | body: {old_password, new_password} |
| GET | /cms/v1/user/collections |
我的收藏 | query: {page, limit} |
| GET | /cms/v1/user/comments |
我的评论 | query: {page, limit} |
| GET | /cms/v1/user/login-logs |
登录日志 | query: {page, limit} |
| GET | /cms/v1/user/followings |
我的关注列表 | query: {page, limit} |
| GET | /cms/v1/user/followers |
我的粉丝列表 | query: {page, limit} |
3.2 评论操作
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/comments |
发表评论 | body: {content_id, content, parent_id}(其中 parent_id 为可选) |
| DELETE | /cms/v1/comments/:id |
删除评论 | 路径参数: id |
| POST | /cms/v1/comments/dislike |
评论反对 | body: {comment_id} |
| DELETE | /cms/v1/comments/dislike |
取消反对 | body: {comment_id} |
3.3 表单提交
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/forms/:id/submit |
提交表单 | body: 表单字段键值对 |
3.4 会员
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/member/signin |
每日签到 | 无 |
| GET | /cms/v1/member/info |
会员信息 | 无 |
| GET | /cms/v1/member/points-log |
积分记录 | query: {page, limit} |
3.5 充值
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/recharge/packages |
充值套餐列表 | 无 |
| POST | /cms/v1/recharge/create |
创建充值订单 | body: {package_id, payment_method} |
| GET | /cms/v1/recharge/records |
充值记录 | query: {page, limit} |
| GET | /cms/v1/recharge/status |
订单支付状态查询 | query: {order_no} |
3.6 会员购买
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/membership/purchase |
购买会员套餐 | body: {package_id} |
3.7 支付预下单
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/payment/alipay/precreate |
支付宝预下单 | body: {order_no} |
| POST | /cms/v1/payment/wechat/precreate |
微信支付预下单 | body: {order_no} |
3.8 购买与下载
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/purchase/records |
购买记录列表 | query: {page, limit} |
| POST | /cms/v1/purchase/create |
创建购买订单 | body: {content_id} |
| GET | /cms/v1/download-records |
下载记录 | query: {page, limit} |
| GET | /cms/v1/contents/:id/purchased |
检查购买状态 | 路径参数: id |
| POST | /cms/v1/contents/purchase |
购买内容 | body: {content_id} |
| GET | /cms/v1/purchases |
我的购买记录 | query: {page, limit} |
3.9 内容互动
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/contents/:id/interaction-status |
互动状态(点赞/收藏) | 路径参数: id |
| POST | /cms/v1/contents/:id/share |
分享计数 | 路径参数: id |
3.10 媒体
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/media |
媒体列表 | query: {page, limit} |
| POST | /cms/v1/media/upload |
上传媒体 | FormData: {file} |
3.11 互动(点赞/收藏/关注)
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/like |
点赞 | body: {target_type, target_id} |
| DELETE | /cms/v1/like |
取消点赞 | body: {target_type, target_id} |
| POST | /cms/v1/collect |
收藏 | body: {target_type, target_id} |
| DELETE | /cms/v1/collect |
取消收藏 | body: {target_type, target_id} |
| POST | /cms/v1/follow |
关注 | body: {user_id} |
| DELETE | /cms/v1/follow |
取消关注 | body: {user_id} |
3.12 通知
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/notifications |
通知列表 | query: {page, limit} |
| GET | /cms/v1/notifications/unread-count |
未读数量 | 无 |
| PUT | /cms/v1/notifications/:id/read |
标记已读 | 路径参数: id |
| PUT | /cms/v1/notifications/read-all |
全部已读 | 无 |
| DELETE | /cms/v1/notifications/:id |
删除通知 | 路径参数: id |
3.13 附件付费
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/attachment/purchase/status/:id |
检查附件购买状态 | 路径参数: id |
| GET | /cms/v1/attachment/purchases |
附件购买记录 | query: {page, limit} |
3.14 产品购买
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/products/purchase |
产品购买下单 | body: {product_id, quantity, address_id}(其中 quantity、address_id 为可选) |
| GET | /cms/v1/products/orders |
产品订单列表 | query: {page, limit} |
| GET | /cms/v1/products/orders/:order_no |
订单详情 | 路径参数: order_no |
| GET | /cms/v1/products/:id/purchased |
购买状态检查 | 路径参数: id |
3.15 产品授权码
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/product-codes/my |
我的授权码 | query: {page, limit} |
| POST | /cms/v1/product-codes/activate |
激活授权码 | body: {code, machine_code}(其中 machine_code 为可选) |
| GET | /cms/v1/product-codes/:id/detail |
授权码详情 | 路径参数: id |
3.16 用户地址簿
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/addresses |
地址列表 | 无 |
| GET | /cms/v1/addresses/:id |
地址详情 | 路径参数: id |
| POST | /cms/v1/addresses |
创建地址 | body: {name, phone, province, city, district, detail, is_default}(其中 is_default 为可选) |
| PUT | /cms/v1/addresses/:id |
更新地址 | body: 同上 |
| DELETE | /cms/v1/addresses/:id |
删除地址 | 路径参数: id |
| PUT | /cms/v1/addresses/:id/default |
设置默认地址 | 路径参数: id |
后台登录与验证码
前缀 /admin/,公开访问。登录返回 token、refreshToken、csrfToken、user_id、username、nickname、avatar、roles、user_level、sites、current_site_id、is_super_admin。
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /admin/api/login |
管理员登录 | body: {username, password, captcha_id, captcha_x, captcha_y}(其中 captcha_id、captcha_x、captcha_y 为可选) |
| POST | /admin/login |
管理员登录(兼容) | 同上 |
| GET | /admin/captcha |
获取后台滑块验证码 | 无 |
| GET | /admin/captcha/config |
后台验证码配置 | 无 |
| POST | /admin/captcha/verify |
校验后台滑块 | body: {captcha_id, x, y} |
后台管理 API
前缀 /cms/v1/admin/,需 Bearer Token + 管理员权限,并携带 X-Site-ID 与 X-CSRF-Token 请求头。按业务模块分组如下,可点击模块标题展开/收起。
控制面板
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/dashboard |
工作台统计卡片 |
| GET | /cms/v1/admin/monitor |
系统监控 |
| GET | /cms/v1/admin/disks |
磁盘信息 |
| GET | /cms/v1/admin/system-info |
系统环境 |
| GET | /cms/v1/admin/server-status |
服务器资源(CPU/内存/磁盘) |
| GET | /cms/v1/admin/content-trend |
内容发布趋势 |
| GET | /cms/v1/admin/user-trend |
用户趋势 |
| GET | /cms/v1/admin/latest-contents |
最新内容 |
| GET | /cms/v1/admin/latest-comments |
最新评论 |
| GET | /cms/v1/admin/operation-logs |
操作日志列表 |
用户管理
5.2 用户管理
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/admin/users |
用户列表 | query: {page, limit, keyword, status}(其中 keyword、status 为可选) |
| POST | /cms/v1/admin/users |
新增用户 | body: {username, password, email, phone, role_id, status}(其中 email、phone、role_id、status 为可选) |
| GET | /cms/v1/admin/users/:id |
用户详情 | 路径参数: id |
| PUT | /cms/v1/admin/users/:id |
编辑用户 | body: 用户字段 |
| DELETE | /cms/v1/admin/users/:id |
删除用户 | 路径参数: id |
| DELETE | /cms/v1/admin/users/batch |
批量删除 | body: {ids} |
| PUT | /cms/v1/admin/users/:id/status |
修改用户状态 | body: {status} |
| PUT | /cms/v1/admin/users/:id/reset-password |
重置密码 | body: {new_password} |
| POST | /cms/v1/admin/users/batch-status |
批量启用/禁用 | body: {ids, status} |
5.4 用户元数据
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/user-metas |
用户元数据列表 |
| POST | /cms/v1/admin/user-metas |
新增元数据 |
| PUT | /cms/v1/admin/user-metas/:id |
编辑元数据 |
| DELETE | /cms/v1/admin/user-metas/:id |
删除元数据 |
| DELETE | /cms/v1/admin/user-meta/batch |
批量删除(注意单数路径) |
5.33 用户关注
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/user-follows |
关注列表 |
| POST | /cms/v1/admin/user-follows |
新增关注 |
| PUT | /cms/v1/admin/user-follows/:id |
编辑关注 |
| DELETE | /cms/v1/admin/user-follows/:id |
删除关注 |
| DELETE | /cms/v1/admin/user-follows/batch |
批量删除 |
角色权限
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/admin/roles |
角色列表 | query: {page, limit} |
| POST | /cms/v1/admin/roles |
新增角色 | body: {name, description, status}(其中 description、status 为可选) |
| GET | /cms/v1/admin/roles/:id |
角色详情 | 路径参数: id |
| PUT | /cms/v1/admin/roles/:id |
编辑角色 | body: 角色字段 |
| DELETE | /cms/v1/admin/roles/:id |
删除角色 | 路径参数: id |
| DELETE | /cms/v1/admin/roles/batch |
批量删除 | body: {ids} |
| POST | /cms/v1/admin/roles/batch-status |
批量启禁用 | body: {ids, status} |
| PUT | /cms/v1/admin/roles/:id/permissions |
分配角色权限 | body: {permission_ids} |
| GET | /cms/v1/admin/roles/:id/permissions |
获取角色权限 | 路径参数: id |
| GET | /cms/v1/admin/roles/all |
所有角色 | 无 |
| GET | /cms/v1/admin/permissions |
权限列表 | query: {page, limit} |
| POST | /cms/v1/admin/permissions |
新增权限 | body: 权限字段 |
| GET | /cms/v1/admin/permissions/:id |
权限详情 | 路径参数: id |
| PUT | /cms/v1/admin/permissions/:id |
编辑权限 | body: 权限字段 |
| DELETE | /cms/v1/admin/permissions/:id |
删除权限 | 路径参数: id |
| DELETE | /cms/v1/admin/permissions/batch |
批量删除 | body: {ids} |
| GET | /cms/v1/admin/permissions/:id/roles |
权限关联角色 | 路径参数: id |
| GET | /cms/v1/admin/permission-modules |
所有模块 | 无 |
| GET | /cms/v1/admin/permission-tree |
权限树 | 无 |
栏目管理
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/admin/categories |
栏目列表 | query: {page, limit, format}(format 为可选;format=list 返回下拉列表) |
| POST | /cms/v1/admin/categories |
新增栏目 | body: {name, parent_id, slug, sort, is_static, ...}(其中 parent_id、slug、sort、is_static 为可选) |
| PUT | /cms/v1/admin/categories/:id |
编辑栏目 | body: 栏目字段 |
| DELETE | /cms/v1/admin/categories/:id |
删除栏目 | 路径参数: id |
| DELETE | /cms/v1/admin/categories/batch |
批量删除 | body: {ids} |
| GET | /cms/v1/admin/categories/all |
所有栏目 | 无 |
| PUT | /cms/v1/admin/categories/sort |
栏目排序 | body: {items: [{id, sort}]} |
| GET | /cms/v1/admin/categories/:id/type |
栏目类型查询 | 路径参数: id |
内容管理
5.6 内容类型
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/content-types |
内容类型列表 |
| GET | /cms/v1/admin/content-types/all |
全部内容类型 |
| GET | /cms/v1/admin/content-types/:id |
内容类型详情 |
| POST | /cms/v1/admin/content-types |
新增内容类型 |
| PUT | /cms/v1/admin/content-types/:id |
编辑内容类型 |
| DELETE | /cms/v1/admin/content-types/:id |
删除内容类型 |
5.8 内容管理
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/admin/contents |
内容列表 | query: {page, limit, category_id, status, keyword}(其中 category_id、status、keyword 为可选) |
| POST | /cms/v1/admin/contents |
新增内容 | body: 内容字段 |
| GET | /cms/v1/admin/contents/gen-slug |
生成 slug | query: {title, ai}(其中 ai 为可选) |
| GET | /cms/v1/admin/contents/:id |
内容详情 | 路径参数: id |
| PUT | /cms/v1/admin/contents/:id |
编辑内容 | body: 内容字段 |
| DELETE | /cms/v1/admin/contents/:id |
删除内容 | 路径参数: id |
| POST | /cms/v1/admin/contents/batch |
批量删除 | body: {ids} |
| DELETE | /cms/v1/admin/contents/batch |
批量删除 | body: {ids} |
| PUT | /cms/v1/admin/contents/batch/status |
批量改状态 | body: {ids, status}(0草稿/1发布/2待审/3拒绝/4下架) |
| PUT | /cms/v1/admin/contents/:id/audit |
审核内容 | body: {status, reason}(其中 reason 为可选) |
| PUT | /cms/v1/admin/contents/:id/status |
修改状态 | body: {status} |
5.10 停用词与 Slug
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/stopwords |
停用词列表 |
| POST | /cms/v1/admin/stopwords |
新增停用词 |
| PUT | /cms/v1/admin/stopwords/:id |
编辑停用词 |
| DELETE | /cms/v1/admin/stopwords/:id |
删除停用词 |
| POST | /cms/v1/admin/stopwords/regenerate-slugs |
批量重生成 slug |
5.11 静态生成
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| POST | /cms/v1/admin/static/action |
静态化统一入口 | body: {action, ...}(action 值见下方说明) |
5.12 单页管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/pages |
页面列表 |
| POST | /cms/v1/admin/pages |
新增页面 |
| PUT | /cms/v1/admin/pages/:id |
编辑页面 |
| DELETE | /cms/v1/admin/pages/:id |
删除页面 |
| DELETE | /cms/v1/admin/pages/batch |
批量删除 |
| POST | /cms/v1/admin/pages/batch-status |
批量改状态 |
5.13 内容版本
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/content-versions |
版本列表 |
| GET | /cms/v1/admin/content-versions/diff |
对比版本 |
| GET | /cms/v1/admin/content-versions/:id |
版本详情 |
| DELETE | /cms/v1/admin/content-versions/:id |
删除版本 |
| DELETE | /cms/v1/admin/content-versions/batch |
批量删除 |
| GET | /cms/v1/admin/contents/:id/versions |
内容版本历史 |
| POST | /cms/v1/admin/contents/:id/versions/:vid/rollback |
回滚版本 |
5.17 UEditor 富文本
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/ueditor |
UEditor 配置/列表 |
| POST | /cms/v1/admin/ueditor |
UEditor 上传(图片/视频/附件) |
回收站
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/contents/trashed |
回收站列表 |
| DELETE | /cms/v1/admin/contents/trashed/purge-all |
清空回收站 |
| PUT | /cms/v1/admin/contents/:id/restore |
恢复内容 |
| DELETE | /cms/v1/admin/contents/:id/purge |
彻底删除 |
标签管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/tags |
标签列表 |
| POST | /cms/v1/admin/tags |
新增标签 |
| PUT | /cms/v1/admin/tags/:id |
编辑标签 |
| DELETE | /cms/v1/admin/tags/:id |
删除标签 |
| DELETE | /cms/v1/admin/tags/batch |
批量删除 |
| POST | /cms/v1/admin/tags/rebuild-use-count |
重建使用次数 |
搜索词
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/search-logs |
搜索词列表 |
| POST | /cms/v1/admin/search-logs |
新增搜索词 |
| PUT | /cms/v1/admin/search-logs/:id |
编辑搜索词 |
| DELETE | /cms/v1/admin/search-logs/:id |
删除搜索词 |
| POST | /cms/v1/admin/search-logs/batch-pin |
批量置顶 |
| POST | /cms/v1/admin/search-logs/batch-block |
批量屏蔽 |
| POST | /cms/v1/admin/search-logs/reset-count |
清空计数 |
| DELETE | /cms/v1/admin/search-logs/batch |
批量删除 |
媒体库
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/admin/media |
媒体列表 | query: {page, limit} |
| POST | /cms/v1/admin/media/upload |
上传媒体 | FormData: {file} |
| PUT | /cms/v1/admin/media/:id |
编辑媒体 | body: 媒体字段 |
| DELETE | /cms/v1/admin/media/:id |
删除媒体 | 路径参数: id |
| DELETE | /cms/v1/admin/media/batch |
批量删除 | body: {ids} |
| GET | /cms/v1/admin/media/:id/meta |
媒体元数据列表 | 路径参数: id |
| POST | /cms/v1/admin/media/:id/meta |
新增元数据 | body: 元数据字段 |
| PUT | /cms/v1/admin/media/:id/meta/:metaId |
编辑元数据 | body: 元数据字段 |
| DELETE | /cms/v1/admin/media/:id/meta/:metaId |
删除元数据 | 路径参数 |
评论管理
5.18 评论管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/comments |
评论列表 |
| PUT | /cms/v1/admin/comments/:id |
编辑评论 |
| PUT | /cms/v1/admin/comments/:id/audit |
审核评论 |
| DELETE | /cms/v1/admin/comments/:id |
删除评论 |
| POST | /cms/v1/admin/comments/batch-delete |
批量删除 |
| DELETE | /cms/v1/admin/comments/batch |
批量删除 |
| POST | /cms/v1/admin/comments/batch-status |
批量改状态 |
5.44 点赞与收藏
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/likes |
点赞记录列表 |
| GET | /cms/v1/admin/likes/:id |
点赞详情 |
| POST | /cms/v1/admin/likes |
新增点赞 |
| PUT | /cms/v1/admin/likes/:id |
编辑点赞 |
| DELETE | /cms/v1/admin/likes/:id |
删除点赞 |
| POST | /cms/v1/admin/likes/batch-delete |
批量删除 |
| DELETE | /cms/v1/admin/likes/batch |
批量删除 |
| GET | /cms/v1/admin/likes/stats |
点赞统计 |
| GET | /cms/v1/admin/collects |
收藏记录列表 |
| GET | /cms/v1/admin/collects/:id |
收藏详情 |
| POST | /cms/v1/admin/collects |
新增收藏 |
| PUT | /cms/v1/admin/collects/:id |
编辑收藏 |
| DELETE | /cms/v1/admin/collects/:id |
删除收藏 |
| POST | /cms/v1/admin/collects/batch-delete |
批量删除 |
| DELETE | /cms/v1/admin/collects/batch |
批量删除 |
| GET | /cms/v1/admin/collects/stats |
收藏统计 |
会员管理
5.19 会员管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/members |
会员列表 |
| POST | /cms/v1/admin/members |
新增会员 |
| PUT | /cms/v1/admin/members/:id |
编辑会员 |
| DELETE | /cms/v1/admin/members/:id |
删除会员 |
| DELETE | /cms/v1/admin/members/batch |
批量删除 |
| PUT | /cms/v1/admin/members/:id/points |
积分调整 |
| PUT | /cms/v1/admin/members/:id/balance |
余额调整 |
5.20 会员等级
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/member-levels |
等级列表 |
| POST | /cms/v1/admin/member-levels |
新增等级 |
| PUT | /cms/v1/admin/member-levels/:id |
编辑等级 |
| DELETE | /cms/v1/admin/member-levels/:id |
删除等级 |
| DELETE | /cms/v1/admin/member-levels/batch |
批量删除 |
充值套餐
5.21 充值套餐
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/recharge-packages |
套餐列表 |
| POST | /cms/v1/admin/recharge-packages |
新增套餐 |
| PUT | /cms/v1/admin/recharge-packages/:id |
编辑套餐 |
| DELETE | /cms/v1/admin/recharge-packages/:id |
删除套餐 |
| DELETE | /cms/v1/admin/recharge-packages/batch |
批量删除 |
| POST | /cms/v1/admin/recharge-packages/batch-status |
批量启禁用 |
5.22 充值记录
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/recharge-records |
充值记录列表 |
| DELETE | /cms/v1/admin/recharge-records/batch |
批量删除 |
财务记录
5.23 财务记录
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/purchase-records |
购买记录列表 |
| GET | /cms/v1/admin/consume-records |
消费记录列表 |
| POST | /cms/v1/admin/consume-records |
新增消费记录 |
| PUT | /cms/v1/admin/consume-records/:id |
编辑消费记录 |
| DELETE | /cms/v1/admin/consume-records/:id |
删除消费记录 |
| DELETE | /cms/v1/admin/consume-records/batch |
批量删除 |
| GET | /cms/v1/admin/download-records |
下载记录列表 |
| DELETE | /cms/v1/admin/download-records/batch |
批量删除 |
5.28 推广记录
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/promotions |
推广记录列表 |
| GET | /cms/v1/admin/promotions/:id |
推广详情 |
| PUT | /cms/v1/admin/promotions/:id/reward |
推广奖励状态 |
| DELETE | /cms/v1/admin/promotions/:id |
删除推广 |
| DELETE | /cms/v1/admin/promotions/batch |
批量删除 |
| DELETE | /cms/v1/admin/promotion-records/batch |
批量删除 |
内容付费
5.7 付费类型字典
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/pay-type-dict |
付费类型列表 |
| GET | /cms/v1/admin/pay-type-dict/all |
全部付费类型 |
| GET | /cms/v1/admin/pay-type-dict/:id |
付费类型详情 |
| POST | /cms/v1/admin/pay-type-dict |
新增付费类型 |
| PUT | /cms/v1/admin/pay-type-dict/:id |
编辑付费类型 |
| DELETE | /cms/v1/admin/pay-type-dict/:id |
删除付费类型 |
| POST | /cms/v1/admin/pay-type-dict/batch-status |
批量启禁用 |
5.24 内容付费设置
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/content-pay |
内容付费设置 |
| PUT | /cms/v1/admin/content-pay/:id |
更新设置 |
| DELETE | /cms/v1/admin/content-pay/batch |
批量删除 |
| POST | /cms/v1/admin/content-pay/batch-status |
批量改状态 |
附件付费
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/attachment-pay |
附件付费设置列表 |
| POST | /cms/v1/admin/attachment-pay |
新增设置 |
| POST | /cms/v1/admin/attachment-pay/upload |
上传收费附件(FormData) |
| POST | /cms/v1/admin/attachment-pay/url |
添加 URL 附件 |
| PUT | /cms/v1/admin/attachment-pay/:id |
更新设置 |
| DELETE | /cms/v1/admin/attachment-pay/:id |
删除设置 |
| DELETE | /cms/v1/admin/attachment-pay/batch |
批量删除 |
| POST | /cms/v1/admin/attachment-pay/batch-status |
批量改状态 |
购买记录
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/content-purchases |
内容购买记录 |
| DELETE | /cms/v1/admin/content-purchases/:id |
删除记录 |
| DELETE | /cms/v1/admin/content-purchases/batch |
批量删除 |
| GET | /cms/v1/admin/attachment-purchases |
附件购买记录 |
| DELETE | /cms/v1/admin/attachment-purchases/:id |
删除记录 |
| DELETE | /cms/v1/admin/attachment-purchases/batch |
批量删除 |
产品授权码
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /cms/v1/admin/products/:id/codes/generate |
批量生成授权码 |
| GET | /cms/v1/admin/products/:id/codes |
授权码列表 |
| GET | /cms/v1/admin/products/:id/codes/stats |
库存统计 |
| POST | /cms/v1/admin/products/codes/revoke |
作废授权码 |
| GET | /cms/v1/admin/product-codes |
全局查询 |
| GET | /cms/v1/admin/product-purchases |
产品购买列表 |
| POST | /cms/v1/admin/products/codes/:id/unbind-machine |
解绑机器码 |
| POST | /cms/v1/admin/products/codes/:id/extend |
延期授权码 |
| GET | /cms/v1/admin/products/codes/:id/detail |
授权码详情 |
广告管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/ad-positions |
广告位列表 |
| POST | /cms/v1/admin/ad-positions |
新增广告位 |
| PUT | /cms/v1/admin/ad-positions/:id |
编辑广告位 |
| DELETE | /cms/v1/admin/ad-positions/:id |
删除广告位 |
| DELETE | /cms/v1/admin/ad-positions/batch |
批量删除 |
| POST | /cms/v1/admin/ad-positions/batch-status |
批量启禁用 |
| GET | /cms/v1/admin/ads |
广告列表 |
| POST | /cms/v1/admin/ads |
新增广告 |
| PUT | /cms/v1/admin/ads/:id |
编辑广告 |
| DELETE | /cms/v1/admin/ads/:id |
删除广告 |
| DELETE | /cms/v1/admin/ads/batch |
批量删除 |
| POST | /cms/v1/admin/ads/batch-status |
批量启禁用 |
| GET | /cms/v1/admin/ads/:id/stats |
广告统计 |
友情链接
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/friendly-links |
友链列表 |
| POST | /cms/v1/admin/friendly-links |
新增友链 |
| PUT | /cms/v1/admin/friendly-links/:id |
编辑友链 |
| DELETE | /cms/v1/admin/friendly-links/:id |
删除友链 |
| DELETE | /cms/v1/admin/friendly-links/batch |
批量删除 |
| PUT | /cms/v1/admin/friendly-links/:id/audit |
审核友链 |
| POST | /cms/v1/admin/friendly-links/batch-status |
批量改状态 |
API密钥
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/api-keys |
密钥列表 |
| POST | /cms/v1/admin/api-keys |
生成密钥 |
| PUT | /cms/v1/admin/api-keys/:id |
编辑密钥 |
| DELETE | /cms/v1/admin/api-keys/:id |
删除密钥 |
| DELETE | /cms/v1/admin/api-keys/batch |
批量删除 |
日志管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/api-logs |
API 日志列表 |
| GET | /cms/v1/admin/api-stats |
API 调用统计 |
| GET | /cms/v1/admin/operation-logs |
操作日志列表 |
| DELETE | /cms/v1/admin/operation-logs/:id |
删除操作日志 |
| DELETE | /cms/v1/admin/operation-logs/batch |
批量删除 |
| GET | /cms/v1/admin/error-logs |
错误日志列表 |
| DELETE | /cms/v1/admin/error-logs/:id |
删除错误日志 |
| DELETE | /cms/v1/admin/error-logs/batch |
批量删除 |
| DELETE | /cms/v1/admin/logs/clean |
日志清理 |
| GET | /cms/v1/admin/points-logs |
积分日志列表 |
| POST | /cms/v1/admin/points-logs |
新增积分日志 |
| PUT | /cms/v1/admin/points-logs/:id |
编辑积分日志 |
| DELETE | /cms/v1/admin/points-logs/:id |
删除积分日志 |
| DELETE | /cms/v1/admin/points-logs/batch |
批量删除 |
| GET | /cms/v1/admin/task-logs |
任务日志列表 |
| DELETE | /cms/v1/admin/task-logs/:id |
删除任务日志 |
| DELETE | /cms/v1/admin/task-logs/batch |
批量删除 |
系统配置
5.34 系统配置
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/config |
配置列表 |
| POST | /cms/v1/admin/config |
新增配置 |
| PUT | /cms/v1/admin/config/:id |
编辑配置 |
| DELETE | /cms/v1/admin/config/:id |
删除配置 |
| DELETE | /cms/v1/admin/config/batch |
批量删除 |
5.39 邮件测试
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /cms/v1/admin/email/test |
邮件发送测试 |
百度推送
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/baidu/config |
百度配置读取 |
| PUT | /cms/v1/admin/baidu/config |
百度配置保存 |
| POST | /cms/v1/admin/baidu/submit |
提交内容 |
| POST | /cms/v1/admin/baidu/submit-all |
一键全部提交 |
| GET | /cms/v1/admin/baidu/sitemap |
生成 Sitemap |
模板管理
5.36 模板管理
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/admin/templates |
模板列表 | 无 |
| PUT | /cms/v1/admin/templates/switch |
切换模板 | body: {template} |
| POST | /cms/v1/admin/templates/upload/archive |
上传压缩包模板 | FormData |
| POST | /cms/v1/admin/templates/upload/folder |
上传文件夹模板 | FormData |
| POST | /cms/v1/admin/templates/upload/files |
上传多文件模板 | FormData |
| DELETE | /cms/v1/admin/templates |
删除模板 | query: {template} |
5.47 模板变量
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/template-vars |
变量列表 |
| POST | /cms/v1/admin/template-vars |
新增变量 |
| PUT | /cms/v1/admin/template-vars/:id |
编辑变量 |
| DELETE | /cms/v1/admin/template-vars/:id |
删除变量 |
| DELETE | /cms/v1/admin/template-vars/batch |
批量删除 |
文件管理
| 方法 | 路径 | 用途 | 请求参数 |
|---|---|---|---|
| GET | /cms/v1/admin/files/list |
文件列表 | query: {path} |
| GET | /cms/v1/admin/files/content |
读取文件内容 | query: {path} |
| PUT | /cms/v1/admin/files/content |
保存文件内容 | body: {path, content} |
| POST | /cms/v1/admin/files/folder |
新建文件夹 | body: {path} |
| POST | /cms/v1/admin/files/file |
新建文件 | body: {path} |
| PUT | /cms/v1/admin/files/rename |
重命名 | body: {old_path, new_path} |
| DELETE | /cms/v1/admin/files |
删除文件 | query: {path} |
| GET | /cms/v1/admin/files/download |
下载文件 | query: {path} |
| POST | /cms/v1/admin/files/upload |
上传文件 | FormData |
| PUT | /cms/v1/admin/files/move |
移动文件 | body: {old_path, new_path} |
| POST | /cms/v1/admin/files/copy |
复制文件 | body: {src, dst} |
| POST | /cms/v1/admin/files/batch-delete |
批量删除 | body: {paths} |
| POST | /cms/v1/admin/files/extract |
解压 | body: {path, dest}(其中 dest 为可选) |
| POST | /cms/v1/admin/files/compress |
压缩 | body: {path, dest} |
缓存管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/cache/overview |
缓存概览 |
| GET | /cms/v1/admin/cache/entries |
条目列表 |
| POST | /cms/v1/admin/cache/clear |
清理指定模块 |
| POST | /cms/v1/admin/cache/clear-all |
清空所有缓存 |
| PUT | /cms/v1/admin/cache/template-toggle |
模板缓存开关 |
| PUT | /cms/v1/admin/cache/static-resource-toggle |
静态资源缓存开关 |
| GET | /cms/v1/admin/cache/config |
缓存配置列表 |
| POST | /cms/v1/admin/cache/config |
新增缓存配置 |
| PUT | /cms/v1/admin/cache/config/:id |
编辑缓存配置 |
| DELETE | /cms/v1/admin/cache/config/:id |
删除缓存配置 |
站点管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/sites |
站点列表 |
| POST | /cms/v1/admin/sites |
新增站点 |
| GET | /cms/v1/admin/sites/:id |
站点详情 |
| PUT | /cms/v1/admin/sites/:id |
编辑站点 |
| DELETE | /cms/v1/admin/sites/:id |
删除站点 |
| DELETE | /cms/v1/admin/sites/batch |
批量删除 |
| PUT | /cms/v1/admin/sites/:id/toggle |
切换站点状态 |
| POST | /cms/v1/admin/sites/batch-status |
批量启禁用 |
定时任务
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/tasks |
任务列表 |
| POST | /cms/v1/admin/tasks |
新增任务 |
| GET | /cms/v1/admin/tasks/:id |
任务详情 |
| PUT | /cms/v1/admin/tasks/:id |
编辑任务 |
| PUT | /cms/v1/admin/tasks/:id/toggle |
切换任务状态 |
| POST | /cms/v1/admin/tasks/:id/run |
执行任务 |
| DELETE | /cms/v1/admin/tasks/:id |
删除任务 |
| DELETE | /cms/v1/admin/tasks/batch |
批量删除 |
| POST | /cms/v1/admin/tasks/batch-status |
批量切换状态 |
| GET | /cms/v1/admin/tasks/:id/logs |
任务日志 |
消息通知
5.42 消息通知
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/message-center |
消息中心数据 |
| GET | /cms/v1/admin/notifications |
通知列表 |
| POST | /cms/v1/admin/notifications |
发送通知 |
| GET | /cms/v1/admin/notifications/:id |
通知详情 |
| PUT | /cms/v1/admin/notifications/:id/read |
标记已读 |
| PUT | /cms/v1/admin/notifications/:id |
编辑通知 |
| DELETE | /cms/v1/admin/notifications/:id |
删除通知 |
| POST | /cms/v1/admin/notifications/batch-delete |
批量删除 |
| DELETE | /cms/v1/admin/notifications/batch |
批量删除 |
| POST | /cms/v1/admin/notifications/batch-read |
批量标记已读 |
5.43 系统消息
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/system-messages |
系统消息列表 |
| POST | /cms/v1/admin/system-messages |
发送系统消息 |
| GET | /cms/v1/admin/system-messages/:id |
消息详情 |
| PUT | /cms/v1/admin/system-messages/:id/revoke |
撤回消息 |
| PUT | /cms/v1/admin/system-messages/:id |
编辑消息 |
| DELETE | /cms/v1/admin/system-messages/:id |
删除消息 |
| POST | /cms/v1/admin/system-messages/batch-delete |
批量删除 |
| DELETE | /cms/v1/admin/system-messages/batch |
批量删除 |
表单管理
5.45 表单管理
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/forms |
表单列表 |
| POST | /cms/v1/admin/forms |
新增表单 |
| GET | /cms/v1/admin/forms/:id |
表单详情 |
| PUT | /cms/v1/admin/forms/:id |
编辑表单 |
| DELETE | /cms/v1/admin/forms/:id |
删除表单 |
| DELETE | /cms/v1/admin/forms/batch |
批量删除 |
| POST | /cms/v1/admin/forms/batch-status |
批量改状态 |
5.46 表单提交记录
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/form-submissions |
提交列表 |
| GET | /cms/v1/admin/form-submissions/:id |
提交详情 |
| PUT | /cms/v1/admin/form-submissions/:id |
处理提交 |
| DELETE | /cms/v1/admin/form-submissions/:id |
删除提交 |
| POST | /cms/v1/admin/form-submissions/batch-delete |
批量删除 |
| DELETE | /cms/v1/admin/form-submissions/batch |
批量删除 |
| GET | /cms/v1/admin/form-submissions/stats |
提交统计 |
AI功能
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /cms/v1/admin/ai/keywords |
AI 关键词列表 |
| POST | /cms/v1/admin/ai/keywords |
新增关键词 |
| PUT | /cms/v1/admin/ai/keywords/:id |
编辑关键词 |
| DELETE | /cms/v1/admin/ai/keywords/:id |
删除关键词 |
| POST | /cms/v1/admin/ai/keywords/batch |
批量导入 |
| POST | /cms/v1/admin/ai/keywords/batch-status |
批量状态 |
| POST | /cms/v1/admin/ai/keywords/:id/reset |
重置使用次数 |
| GET | /cms/v1/admin/ai/topics |
AI 主题列表 |
| POST | /cms/v1/admin/ai/topics |
新增主题 |
| PUT | /cms/v1/admin/ai/topics/:id |
编辑主题 |
| DELETE | /cms/v1/admin/ai/topics/:id |
删除主题 |
| POST | /cms/v1/admin/ai/topics/:id/test |
测试主题生成 |
| GET | /cms/v1/admin/ai/config |
AI 配置读取 |
| PUT | /cms/v1/admin/ai/config |
AI 配置更新 |
| POST | /cms/v1/admin/ai/test |
AI 测试调用 |
| POST | /cms/v1/admin/ai/editor |
富文本 AI 助手 |
已知问题与注意事项
- 未注册路由:前端调用的
POST /cms/v1/admin/logout与POST /admin/api/refresh-token后端未注册,登出请走POST /cms/v1/auth/logout,刷新走管理面板POST /admin/auth/refresh。 - 单复数路径差异:用户元数据明细为
/cms/v1/admin/user-metas(复数),批量删除为/cms/v1/admin/user-meta/batch(单数),以后端实际注册路径为准。 - 统一动作入口:
POST /cms/v1/admin/static/action是静态生成与缩略图统一入口,通过 body 中的action字段区分不同操作,前端不应拆分为多个独立接口。支持的action值包括stats、category_generate、all_generate、category_only、homepage_status、homepage_toggle、tags_generate、pages_generate、page_generate、page_delete、thumbnail_config_get/save、thumbnail_status/generate/progress/generate_one。 - 前台兼容旧版路径:
register、login、logout、refresh、forgot-password、reset-password同时注册了/cms/v1/auth/*与/cms/v1/*两个版本,功能完全相同。 - 站点头:后台管理 API 需携带
X-Site-ID,值从localStorage.cms_current_site_id读取,0表示全部站点。 - CSRF 保护:后台管理 API 需携带
X-CSRF-Token,值从登录响应获取;登录与验证码接口免 CSRF。 - 接口统计:前台公开 API 约 42 个、前台认证 API 约 59 个、后台登录/验证码 5 个、后台管理 API 约 280 个、后台页面路由 18 个,合计约 404 个。
常见问题
启动时提示端口被占用怎么办?
服务器.端口(前台)或 管理面板.端口(面板 8080)中修改,保存后自动重启生效。如何切换数据库类型(MySQL / PostgreSQL / SQLite)?
数据库.数据库类型 为 mysql / postgresql / sqlite 后重启程序生效。MySQL/PostgreSQL 需自行准备数据库服务(PG 库名不存在自动创建);SQLite 为单文件库(WAL 模式),空库启动自动建表。建表/查询/分页全链路三方言兼容,可在面板「数据库」页一键初始化(会清空同前缀旧表)。为什么实际数据库连接数是我配置的 2 倍?
最大连接池大小/最大空闲连接数 的语义是「每池全额」,而系统实际有 app 池与 CMS 池两套,导致连接数为配置值的 2 倍。现已修复为「两池合计值」并按 app:CMS = 30:70 自动拆分(配置 10 → app 3 / CMS 7)。请升级后再按合计值配置,并确保小于数据库 max_connections。日志报 invalid connection 与数据库 wait_timeout 有关吗?
wait_timeout 必须大于 Go 侧空闲超时(连接最大生命周期 / 4,如 3600/4=900 秒),否则空闲连接被数据库先行回收后会报 invalid connection。生产建议 wait_timeout ≥ 1800 秒(如 MySQL 8.4 用 authentication_policy='*,,' 3 元素写法并正确配置 my.ini)。修改 xiis.net.yaml 后多久生效?
模板或样式修改后未生效?
?v= 时间戳版本参数,浏览器可能缓存旧文件。强制刷新(Ctrl+F5)或更新引用的版本参数后即可看到最新效果;若启用模板缓存,可在站点管理中关闭「启用模板缓存」。新建的单页如何指定模板?
template 字段 > {code}.html > page.html > about.html。将模板文件命名为单页 code(如 code=docs 对应 docs.html)即可自动匹配。标签不输出数据怎么排查?
site_id 隔离);条件判断参数写法是否符合文档;可在 标签生成器 复制标准写法对照。如何添加新站点/新域名?
HTTPS 证书如何申请与续期?
使用测试环境 置 false;证书域名变化时系统自动重新申请 SAN 证书。也可在 HTTPS/SSL 页为默认站点配置已有证书路径。为什么某些后台页面需要登录才能访问?
管理面板登录被锁定怎么办?
管理面板.登录失败限制(默认 5 次)会锁定该 IP 登录锁定时长(默认 300 秒)。等待解锁后重试,或修改 yaml 增大限制/时长(5 秒热更新)。密钥丢失时直接修改 管理面板.管理API密钥 即可重置面板密码。安装插件需要重启程序或改数据库吗?
xiis.net/插件/站点{ID}/,最多约 5 秒后接口即可访问;删掉目录最多约 5 秒后路由消失。全程不重启 xxx.exe、不点按钮、不登录后台、不写数据库——插件系统自身零数据库表,目录存在即已安装。确需立刻生效时,可在管理面板「插件」页点「立即重新扫描」。改了插件程序源码(如 程序/入口.py)为什么没生效?
xxx.exe 不重启)。若未生效,请检查:① 改的是否是「入口脚本 import 的其他文件」——这类文件不在监听范围内(只对清单声明过的程序文件做 stat、绝不递归目录),需同时改动入口脚本本身或动一下 插件.yaml 触发重载;② 是否改在插件目录之外。插件崩溃会不会带走主程序?性能会不会受影响?
程序最大重启次数 即熔断停用该插件——主程序始终保持存活。性能方面:一个插件都没装时读路径首行一次原子判空即返回,零开销;装了插件的请求走的是 PHP 每请求都在走的现成中间件链,成本为微秒级,远小于 stdio 传输本身。插件路由为什么声明了却不生效(被丢弃)?
content/tag/search/user/category/list/admin/cms/api/static/page 等 18 个);② 路径以保留路径开头(/api/、/admin、/cms/v1、/.well-known/、/ws、/_插件);③ 路径含下划线或大写字母(只允许小写字母/数字与 . / -);④ 与站点已配置的 route_* 前缀或已有单页/栏目 slug 冲突;⑤ 与同站点另一插件声明了相同的「方法+路径」(按标识字典序,大者被丢弃)。请查站点日志确认具体冲突对象。插件能访问文件系统和数据库吗?内核提供数据接口吗?
数据/ 目录(路径来自 插件数据目录 环境变量);多站点场景下按 插件站点ID 自行区分即可。插件返回大文件(如下载、视频)怎么做?
{插件数据目录}/下载/ 后,响应里只回传 正文文件 绝对路径,内核流式回写客户端并在写回后自动删除该文件,不受正文上限约束,适合「内容由插件动态生成」;② 静态资源/ 托管——把文件放入插件目录的 静态资源/,通过 /_插件/{标识}/... 访问,该通道支持 ETag/Last-Modified/Range(206 分段、416 越界)与 304 条件请求,可断点续传与视频拖动,适合「文件已存在磁盘上」。注意协议内存通道(正文base64)仍受正文上限约束,不适合大文件。插件路由需要登录才能访问怎么配置?
{表前缀}permissions)挂在 Gin 引擎的 CMS 路由组上,插件不经引擎故不自动生效;若插件在后台类场景需要后台级鉴权,请在插件进程内自行校验(请求头已全量转发,含 Cookie,插件可直连数据库)。插件上传大文件返回 413 或 415 怎么办?
服务器.最大请求体MB、yaml 安全.最大上传大小MB、清单 上传.最大MB)——任何一层配大都不会放宽其余层,因此最常见的 413 原因是 服务器.最大请求体MB(默认 5MB)卡住了。415 则是扩展名命中 yaml「禁止访问扩展名」硬黑名单(优先于白名单)、未通过 yaml/清单白名单交集、Content-Type 不在清单 允许类型 内,或正文文件头魔数与 Content-Type 不匹配(挡「改名伪装」)。改 yaml 安全 节一处,插件与 CMS 上传同时生效。插件请求超时(504)或提示插件繁忙(503)怎么排查?
超时秒(路由级 → 全局 程序默认超时秒,默认 5)。注意超时从「请求正文读取完成之后」起算,因此客户端上传耗时不会占用该预算;普通路由若无耗时需求应保持小值。上传类路由务必显式设置 超时秒(建议 ≥ 30)。503 有两种:一是「插件繁忙」——并发请求超过「实例数 + 排队上限」(默认 2 + 64),应调大 程序.实例数(或启用 弹性实例上限)与 排队上限;二是「插件上传并发已达上限」——全局在途正文资源预算 上传并发内存预算MB(默认 64MB)被占满,这是「以 503 换不 OOM」的取舍。