本文档面向 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(系统管理面板)

安装步骤

  1. 下载程序包

    前往官网「下载中心」/category/download 获取最新版本程序包。

  2. 解压程序

    将程序包解压到任意目录即可,无需安装,绿色免部署。

  3. 检查配置

    打开 xiis.net/配置/xiis.net.yaml,确认端口与数据库连接信息(默认 MySQL root/root,连接 127.0.0.1:3306,库 cms_production)。

  4. 启动程序

    Windows 下双击 xxx.exe;Linux/macOS 下在终端执行 ./xxx。程序检测到端口被占用时会自动查杀占用进程后继续启动。

  5. 初始化数据库(首次)

    首次使用(空库/新前缀)可在系统管理面板执行「数据库初始化」,或控制台运行 initcmsdb -y,程序将按当前方言自动建表。

  6. 访问站点

    浏览器打开 http://localhost(默认端口 80),看到站点首页即安装成功。

首次启动与管理入口

安装完成后包含五个入口:

入口地址说明
网站前台http://localhost/默认端口 80,可在 yaml / 面板修改
管理后台(旧版)/admin/login.html站点内容、栏目、会员、模板管理
管理后台(新版)/admin-vueVue3 + 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、重定向等
带星标图标的节同时支持「站点级覆盖」:在 yaml 站点配置 内为某站点单独书写同名键即可覆盖全局;站点管理页会以 覆盖 / 回退全局 标签明确标识每一配置项的来源。未设置项自动回退全局值,无需重复配置。

服务器(服务器)

配置项默认值说明
端口80HTTP 启动端口;保存后自动重启程序生效
读取超时 / 写入超时 / 空闲超时30 / 60 / 120 秒HTTP 服务超时控制,防慢连接拖垮连接池
最大请求头字节1048576最大请求头大小(1MB)
最大请求体MB5单请求体上限;上传另受「安全.最大上传大小MB」控制
最大发送字节MB100响应最大发送大小
网站路径./xiis.net/网页/默认默认站点根目录(未绑定域名时使用)
运行模式releasedebug(调试)/ release(生产)
最大并发连接数100000000全局并发上限(生产可按需调低)
优雅关闭超时秒5停止服务时等待在途请求完成的时间
最小化到托盘true点击最小化收纳到系统托盘(Windows)
全局默认索引文件列表index.php…绑定域名的默认索引列表为空时使用此列表

管理面板(管理面板)

配置项默认值说明
端口8080系统管理面板端口;修改保存后自动重启程序生效
监听地址0.0.0.0127.0.0.1 仅本机,0.0.0.0 允许内网访问
管理API密钥admin面板登录密码(生产必须修改为复杂密钥)
登录失败限制5失败超限锁定该 IP(防暴力破解);0=回退默认 5 次
登录锁定时长300 秒锁定期间拒绝该 IP 登录;0=回退默认 300 秒
CSRF签名密钥cms_csrf_secret_key_2026CSRF Token 签名;空=回退源码默认值,站点级配置优先

安全(安全,可站点覆盖)

策略开关

配置项默认说明
启用CSP1内容安全策略,现代最核心 XSS 防御手段,建议开启
启用XSS保护0X-XSS-Protection,现代浏览器已弃用,兼容远古浏览器时开启
启用帧保护0X-Frame-Options,防点击劫持(CSP frame-ancestors 已覆盖,默认关)
启用内容类型选项1X-Content-Type-Options: nosniff,防 MIME 混淆,建议开启
CSP策略内置策略详见 yaml;注意本系统静态资源与模板需放行 style-src 'unsafe-inline' 等
服务器响应头xiis-v1.8.2026Server 响应头(隐藏真实服务器信息)

访问控制

配置项默认说明
启用域名校验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/…上传文件类型白名单
最大上传大小MB50全局 0=不限制(安全基线不建议);数据库 upload_max_size 配置优先

静态资源(静态资源,可站点覆盖)

配置项默认说明
启用自动处理true自动处理静态资源请求
启用请求统计false静态资源请求统计(性能影响极小,按需开启)
是否启用静态文件缓存falseLRU 文件缓存开关(站点级未配置时回退此值)
缓存大小上限MB / 单文件最大缓存KB100 / 512缓存容量与单文件上限
缓存数量上限10000缓存条目上限,0=不限制
缓存同步间隔分钟3定期检查磁盘文件变化并增量更新缓存
允许缓存扩展名.html/.css/.js/…空=缓存所有文件
启用压缩true静态文件、模板、API 响应压缩
压缩类型11=gzip(默认),2=zstd
压缩阈值字节10240小于 10KB 不压缩
可压缩扩展名html/css/js/json/…仅列出的类型参与压缩

HTTPS / SSL(HTTPS/SSL,可站点覆盖)

配置项默认说明
启用HTTPSfalse站点级常独立开启(见「站点配置」HTTPS 子节)
强制HTTPS重定向falseHTTP 自动 301 到 HTTPS
HTTPSPort443HTTPS 监听端口
启用自动续签false到期前自动续签证书
检查间隔秒 / 提前续签天数86400 / 8证书过期检查周期与提前续签天数
证书文件路径 / 私钥文件路径证书/server.crt|key全局默认证书;站点级证书见「站点配置」
ACME.启用 / 邮箱 / 使用测试环境true / — / trueLet's Encrypt 自动申请;测试环境仅供联调,生产置 false
证书与 ACME 域名列表在「站点管理」每个站点内独立配置;修改 ACME 域名列表后系统自动检测变化并重新申请 SAN 证书(含全部域名),无需手动干预。

认证(认证,可站点覆盖)

配置项默认说明
Cookie签名密钥 / Session签名密钥change-me会话与 Cookie 签名(生产必须修改非默认值)
Session超时秒 / 清理间隔秒1800 / 300会话生命周期与过期清理周期
JWT签名密钥change-meAPI 令牌签名密钥(生产必须修改)
JWT过期时间秒 / 刷新时间秒259200 / 86400令牌有效期与可刷新窗口
Cookie启用Securefalse仅 HTTPS 时开启,防止明文传输
Cookie启用HttpOnlytrue禁止 JS 读取,防 XSS 窃取会话
Cookie启用SameSite / CookieSameSite值true / Lax防 CSRF;值可选 Strict/Lax/None
验证码.滑块图目录 / 背景图目录空留空用内嵌资源;配置目录后可自定义滑块/背景图(不重新编译)

资源保护(静态资源保护,可站点覆盖)

将后台等敏感 HTML/JS 纳入登录保护:未登录访问受保护路径时,按配置重定向登录页或返回 401。

配置项默认说明
启用静态资源保护true(站点1)是否启用登录访问控制
受保护路径/admin/*.html、/admin/*.js支持通配符 * 与 ?
公开路径/admin/login.html 等白名单优先于受保护路径(登录页、公共静态资源放行)
认证失败行为redirectredirect 重定向登录页 / 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
日志级别infodebug / info / warn / error / silent
输出格式jsontext 或 json(兼容 ELK/Loki)
单文件最大行数 / 最大大小MB100000 / 50达到上限自动轮换新文件
刷新阈值 / 刷新间隔秒1000 / 5异步写盘缓冲策略(降低 IO)
记录调用位置false记录 文件名:行号,便于排查(略增开销)
压缩归档 / 压缩算法true / zstd轮换旧日志压缩归档(可省 60%+ 磁盘)
保留天数30超过自动清理
最大单条日志大小KB1024超长自动截断
输出到控制台false请求日志是否同步输出到控制台
采样配置.请求 / 调试 / 警告1 / 10 / 100高频日志每 N 条记 1 条(1=不采样)

IP 归属地(顶级节 归属地)

配置项默认说明
启用IP归属地库truefalse 时回退简单 IP 段判断
引擎类型ip2regionip2region(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三方言通用总开关
数据库类型mysqlmysql / postgresql / sqlite;切换后重启生效
地址 / 端口127.0.0.1 / 3306MySQL/PG 有效;PG 默认 5432;SQLite 忽略
数据库名称cms_productionMySQL/PG 为库名(PG 不存在自动创建);SQLite 为文件名主体(自动拼 .db)
表前缀按需自定义,如 x9k_表前缀规则:仅允许字母/数字/下划线、长度 ≤20、自动补下划线结尾(前缀+短名组成表名,如 x9k_users);自定义前缀(如 x9k_)可提高批量自动化攻击门槛,修改后需同步 RENAME 数据库已有表。
账号 / 密码root / rootSQLite 忽略
编码utf8mb4仅 MySQL 生效;PG/SQLite 固定 UTF-8

方言专属参数

配置项默认说明
MySQLTLSfalseMySQL TLS 加密连接(TiDB Cloud Serverless 等强制 TLS 必须开启,证书严格校验;本机自建 MySQL 未开 SSL 保持 false)
PostgreSQL模式publicPG schema 名
PostgreSQLSSL模式disabledisable / require / verify-ca / verify-full
SQLite文件目录./xiis.net/数据/数据库SQLite 库文件目录(自动创建)
SQLite日志模式WALWAL(推荐,读写不互斥)/ DELETE
SQLite忙等待毫秒5000busy_timeout,写锁等待上限
SQLite最大打开连接数10WAL 单写者,多连接无收益反增锁等待;连接池/空闲数自动截断至此

连接池与运行参数

配置项默认说明
最大连接池大小10语义为 app 池与 CMS 池合计值,两池按 30% : 70% 自动拆分(2026-09-02 修复前两池各取全额,实际为设定值 2 倍);须小于数据库 max_connections
最大空闲连接数10同为两池合计值,按 30:70 拆分(app≈3、CMS≈7);SQLite 侧同位截断
连接最大生命周期秒3600连接到期回收;建议远大于数据库 wait_timeout(见 FAQ)
心跳间隔秒60SQLite 方言下改为 WAL 日志合并(防长期增长占盘)
启动等待秒120MySQL/PG 与程序并行启动时等待其就绪再初始化连接;SQLite 即时可用自动视为 0
查询缓存过期秒300SQL 查询结果缓存时间(最长 5 分钟生效滞后来源之一)
面板「数据库」页支持一键初始化数据库(initcmsdb),按当前方言自动选择 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_statustrue站点开关:false 时前台按关闭原因提示并停止访问
site_close_reason站点维护中,请稍后访问站点关闭时的提示文案
site_watermarkfalse图片水印开关(功能规划中,暂不启用)

SEO 与链接 seo

配置键建议值 / 示例作用说明
seo_title站名 · 副标题,如「悉思 · 轻量高性能多站点Web系统」站点级 SEO 标题({xiis:SEO标题}),页面无独立标题时兜底
seo_keywords3~8 个关键词,英文逗号分隔SEO 关键词({xiis:网站关键词})
seo_description≤150 字站点描述SEO 描述({xiis:网站描述})
slug_link_enabledfalse(ID 链接)/ true(slug 链接)栏目/内容智能链接是否用 slug 格式:/category/news、/content/xxx;false=ID 格式 /category/1
tag_slug_link_enabledtrue标签智能链接 slug 开关,独立于全局,仅控制 /tag/{slug};true 时访问端启用严格模式(裸数字 ID 返回 404,slug 为空的标签降级允许 ID 访问)
link_suffix.html 或留空所有动态链接附加的后缀;空=不附加。已含后缀的静态/伪静态链接不重复附加
route_prefix_link_suffix_enabledfalse栏目 route_prefix 链接是否附加 link_suffix
tag_link_suffix_enabledfalse / true标签链接是否附加 link_suffix
search_keyword_paramq搜索关键词 URL 参数名,如 /search.html?q=SEO(兼容旧版 keyword 参数)
baidu_api_enabledfalse百度站长 API 主动推送总开关(AI 文章发布自动收录)
baidu_api_urlhttp://data.zz.baidu.com/urls?site=…&token=…百度站长推送完整地址(含站点与 16 位 token;token 请自行保管,勿公开)

路由前缀 route

配置键建议值 / 示例作用说明
route_categorycategory栏目列表页路由前缀:/category/{slug|id};栏目自身 route_prefix 存在时优先使用栏目级前缀;改后重启生效
route_contentcontent内容详情页路由前缀:/content/{slug|id};有栏目 route_prefix 时优先栏目级;改后重启生效
route_tagtag标签路由前缀:/tag/{slug|id},标签首页与标签详情共用;改后重启生效
route_searchsearch.html搜索页路由前缀
route_useruser用户中心路由前缀:/user/login、/user/profile 等
route_tag_pagination_stylepath标签分页 URL 格式:path=路径式(/tag/seo/page/2.html,SEO 友好);query=查询串(/tag/seo.html?page=2,兼容旧行为);改后重启生效

上传与存储 upload

配置键建议值 / 示例作用说明
upload_max_size10485760(10MB,字节)单文件上传大小上限(字节);建议与「安全.最大上传大小MB」一致
upload_allowed_typesjpg,jpeg,png,gif,webp,pdf,zip,…允许上传的文件扩展名白名单(英文逗号分隔)
upload_storage_typelocal / oss / qiniu存储后端:local 本地 / oss 阿里云 / qiniu 七牛
local_storage_diruploads本地存储目录名(可改为 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.comOSS 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_domainhttps://cdn.example.com/七牛自定义访问域名(建议 HTTPS)

邮件(SMTP)email

配置键建议值 / 示例作用说明
smtp_hostsmtp.example.comSMTP 服务器地址(可用企业邮箱/邮件服务商)
smtp_port465(SSL)/ 587(STARTTLS)SMTP 端口,与 smtp_ssl 配套
smtp_user / smtp_pass发信账号/授权码(勿公开)SMTP 登录账号与密码(常为邮箱授权码)
smtp_ssltrue是否启用 SSL 加密连接
mail_from / mail_from_namenoreply@example.com / 站点名称发件人地址与显示名
email_register_enabledfalse(可选开启)注册是否需要邮箱验证码;false 时注册无需邮箱验证
email_template_register / email_template_reset_password / email_template_welcome留空=内置模板各场景邮件 HTML 模板;可用变量:{站点名称}/{验证码}/{有效期分钟}/{当前年份}/{版权信息}/{用户名}/{站点URL},留空回退代码内置模板

支付 payment

配置键建议值 / 示例作用说明
payment_alipay_enabledfalse(接入后置 true)支付宝支付开关,控制前台支付方式显隐
alipay_app_id支付宝开放平台获取支付宝应用 APPID
alipay_merchant_private_key / alipay_public_key密钥自行填入(勿公开)商户应用私钥与支付宝公钥(RSA2)
alipay_sandboxtrue(联调)/ false(生产)是否使用沙箱环境
alipay_notify_urlhttps://yourdomain.com/…(公网可访问)支付宝异步回调通知地址
payment_wechat_enabledfalse微信支付开关(当前版本未接入,保持关闭)
wechat_appid / wechat_mchid / wechat_mch_private_key / wechat_mch_cert_serial_no / wechat_api_v3_key微信商户平台获取(密钥勿公开)微信支付商户参数:AppID、商户号、商户 API 私钥、证书序列号、APIv3 密钥
wechat_notify_urlhttps://yourdomain.com/…(必须 HTTPS)微信支付回调通知地址
payment_balance_enabledfalse余额支付开关(会员套餐可用,充值不支持余额支付)
payment_callback_signature_enabledtrue支付回调 HMAC 签名校验开关,建议保持开启
payment_callback_secret留空=回退 JWT 签名密钥回调 HMAC 共享密钥(可自定义随机长串)
payment_callback_ip_whitelist留空=允许所有 IP回调 IP 白名单(逗号分隔),建议按支付平台回调出口 IP 填写

安全与验证码 security

配置键建议值 / 示例作用说明
captcha_enabledtrue验证码总开关:false 时所有场景验证码均失效(含接口)
captcha_login_enabledtrue前台登录滑块验证码开关
captcha_register_enabledtrue前台注册滑块验证码开关
captcha_comment_enabledfalse(已登录可关)发表评论验证码开关(已登录用户可免验证)
captcha_admin_login_enabledtrue后台登录滑块验证码开关
captcha_form_submit_enabledtrue通用表单提交验证码开关(已登录可免)
captcha_slide_padding5(1–20)滑块验证码容差像素:越小越严格,建议 3–8
illegal_char_filter_enabledtrue非法字符过滤总开关;false 时下列规则不生效
illegal_char_filter每行一条,支持 regex:… 正则非法字符/敏感词过滤规则(表单提交校验),如敏感词A、regex:\d{4}-\d{4}
ip_blacklist每行一条:单 IP 或 CIDRIP 黑名单(192.168.1.1 或 10.0.0.0/8),与全局 yaml 黑名单合并生效
ua_blacklist每行一条正则,如 .*bot.*UA 黑名单(正则),与全局 yaml UA 黑名单合并生效

评论 comment

配置键建议值 / 示例作用说明
comment_enabledtrue评论功能总开关:false 时前台禁止发表评论
comment_audit_enabledfalse(社区可开审核)评论审核开关:true=新评论待审核;false=直接通过
comment_closed_hide_listfalse关闭评论后是否隐藏整个评论区:true=隐藏;false=保留已有评论+禁用输入框

缓存 cache

配置键建议值 / 示例作用说明
system_cache_ttl0(禁用)或 60–300系统统一缓存时长(秒);0=禁用缓存直接查库(database/验证码模块除外);未配置同视为 0
general_cache_enabledtrue通用内存缓存开关(站点信息/统计等高频小对象)
general_cache_max_entries10000通用内存缓存最大条目数
template_cache_max_entries500模板文件解析缓存条目数
html_cache_max_entries10000(100–100000)HTML 输出缓存每站点条目上限,超限 LRU 淘汰;约 62KB/条,10000 条约 620MB 内存,请按机器内存调整
db_cache_enabledtrue数据库查询缓存开关
db_cache_expire_time300查询缓存过期时间(秒),越小数据越新鲜
db_cache_max_entries10000查询缓存最大条目数
db_cache_clean_interval2查询缓存过期清理间隔(秒)
db_operation_timeout30数据库单次操作超时(秒)
static_cache_max_size_mb / static_cache_single_file_max_kb / static_cache_max_entries100MB / 512KB / 1000静态资源缓存容量、单文件上限与条目数(与 yaml 静态资源节一致)
captcha_ttl300(1–86400,禁 0)验证码有效期(秒),滑块/邮箱/图形验证码及邮件验证码统一使用;不受 system_cache_ttl=0 影响

AI 内容生成 ai

配置键建议值 / 示例作用说明
ai_enabledfalse(接入后置 true)AI 功能总开关:控制后台 AI 文章任务入口
ai_providerdeepseek 或兼容服务商名AI 服务商标识(默认 DeepSeek 系)
ai_api_key服务商控制台获取(勿公开)AI 服务 API Key
ai_base_url服务商 OpenAI 兼容地址AI 接口地址(OpenAI 兼容 /v1/chat/completions)
ai_model服务商模型名,如 deepseek-chat / deepseek-reasonerAI 模型名称:通用/推理模型按需选
ai_max_tokens8192单次生成最大 Token 数(越长成本越高)
ai_temperature1.0(0–2)创造性温度:0 精确 / 2 发散
ai_system_prompt留空=内置提示词系统提示词,可用 {栏目}/{栏目描述补充}/{当前年份} 等占位;留空回退内置模板
ai_request_timeout120(60–300)AI 请求超时秒数
ai_default_author如「AI文章助手」任务未指定作者时使用的默认作者名
ai_default_max_use_count1(0=不限)生成结果默认最大使用次数(0=不限,1=一次性)
ai_max_articles_per_run9(建议 ≤10)单次任务最大生成文章数(防 API 超额调用)

其他 other

配置键建议值 / 示例作用说明
content_version_enabledfalse内容版本控制开关:启用后新增/修改文章才写入版本快照表
sql_tag_enabledfalse(默认关闭)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),请求进来由插件处理。

2
插件形态
5
声明式响应类型
7
插值变量
5
秒热更新
形态零代码 / 需编程能力边界适用场景
声明式零代码,纯 插件.yaml 配置返回固定/插值文本、HTML、JSON、重定向、静态文件健康探针、验证文件、固定跳转、robots.txt 类接口
程序需编程(任意语言)完整文件 / 数据库 / 网络 / 系统命令能力,由操作系统权限模型提供;进程级崩溃隔离动态接口、增删改查、文件上传下载、业务逻辑
插件不经过 Gin 引擎,而是在请求分发主链路的「第 0.55 步」命中(位于 PHP、CMS 伪静态、Gin 路由之前,静态资源保护之后),因此主程序现有防护零开发全量复用:认证(静态资源保护)、网站开关、IP/UA 黑名单、域名校验、CSP 与安全响应头、限流、请求日志、请求 ID、真实 IP 解析、请求体大小限制、panic 恢复、服务暂停检查、请求统计。未命中就原样落回原链路,装了插件的站点才付该成本,一个插件都没装时读路径首行判空即返回,零开销。
不复用的只有一类:CMS 后台权限体系({表前缀}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;不匹配则跳过该插件并记日志(明确拒绝优于模糊兼容)
启用布尔否truefalse 则跳过加载(不删目录即可临时停用)
描述 / 作者字符串否空仅作展示
配置项列表否—见「插件配置项」
路由列表否—条数 ≤ 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 上行为一致,插件无需写平台相关的信号处理代码。

编码坑(两个常见环境默认不是 UTF-8):PowerShell 默认输出编码非 UTF-8(中文乱码或带 BOM)——脚本开头需设 [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(站点级优先+全局回退)③ 清单 上传.最大MByaml 安全.允许上传扩展名 ∩ 清单 上传.允许扩展名;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 项取交集;清单未声明则跳过
5Content-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,或转义后超限)正文字节base641.33x二进制与「高转义字符」正文

路由级 传输 声明(仅 处理: 程序 有效):自动(缺省)= 旧行为,仅当路由声明了「上传」段且正文预计超过落盘阈值时才落盘;落盘 = 强制落盘(有正文即落盘,即使小于阈值),普通程序路由无需声明「上传」段即可启用;内存 = 强制走内存通道,即使超过阈值也不落盘,是「插件未实现 正文文件 通道」时的兼容退路。

为什么必须做「转义后长度」判定:JSON 默认把 <、>、& 与控制字符各转义为 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 优先)
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 热更新后站点消失 → 卸载该站点全部插件(进程 + 路由)
超时后实例为什么必须强制终止:这是防「响应串台」的关键——超时实例的 stdin 里可能还压着上一个请求,若归还池中,下一个请求的响应会与残留行错位,导致 A 用户收到 B 用户的响应。同理,插件进程内必须请求与响应严格一一对应、串行处理,不要在单个实例内并发。

可用性保护参数(均可在 yaml 插件 节或管理面板调整):

配置文件键默认作用
启用插件系统false总开关;false 时零开销(不扫描插件目录,读路径首行判空即返回)
插件目录./xiis.net/插件插件包根目录(按 站点{ID}/{插件标识}/ 组织)
程序默认实例数2单个程序插件默认拉起的进程数(可被清单 程序.实例数 覆盖,上限 64)
程序默认超时秒5单请求默认超时,从「请求正文读取完成后」起算、含排队(可被路由 超时秒 覆盖,上限 300)
程序就绪超时秒10启动握手等待上限,超时未收到握手该实例启动失败并重试
程序排队上限64单插件默认排队上限,超限返回 503(上限 4096)
程序最大重启次数5熔断窗口内的失败上限,超限停用该插件(上限 100)
熔断窗口分钟5熔断滑动窗口
上传并发内存预算MB64插件请求正文「在途资源」的全局总预算(跨插件共享,含内存路径与落盘路径),读正文前按字节占用,不足返回 503。这是「并发数 × 单请求正文」的硬上界(上限 1024)
上传落盘阈值KB256声明「上传」段的路由,正文超过该阈值即落盘中转;普通程序路由可用路由级 传输: 落盘 主动启用(上限 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_enabledtrue 时栏目/内容智能链接返回 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,即可自动适配上述开关,无需在模板内拼接。

模板标签参考

47
全局标签
26
循环标签
6
载入模板别名
4
点号标签系列

xiis 模板语言的标签名与参数名均支持中英文双语。标签系统源码位于 app/cms/service/标签系统.go,采用注册表模式,新增标签只需添加一行注册项。

标签生成器 /tag-tool

全部标签按页面功能分类速查 · 点选组合生成代码 · 一键复制 · 含完整示例预设。本文仅列标签清单,详细参数与示例请使用标签生成器。

标签语法

标签分四类:全局标签(单值替换)、循环标签(成对包裹循环渲染)、点号标签(页面上下文取字段)、字段标签(循环内输出数据字段)。

<!-- 全局标签:单值替换,输出站点配置/统计/当前页面上下文 -->
<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 / siteogimageOG 图片
{xiis:备案号}icp / siteicp备案号
{xiis:公安备案}policeicp / sitepoliceicp公安备案
{xiis:版权信息}copyright / sitecopyright版权信息
{xiis:联系地址}siteaddress / address联系地址
{xiis:统计代码}analytics / siteanalytics统计代码
{xiis:SEO标题}seotitle / siteseotitleSEO 标题
{xiis:错误提示}errormsg错误提示
{xiis:错误类型}errortype错误类型
{xiis:状态码}statuscode / codeHTTP 状态码
{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 / unixtimeUnix 时间戳(秒/毫秒/微秒/纳秒)
{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,按 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,与标签可用性保持一致
字段值统一经 HTML 转义输出(防存储型 XSS);HTML 注释 <!-- … --> 内的 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 倍?
2026-09-02 前 最大连接池大小/最大空闲连接数 的语义是「每池全额」,而系统实际有 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 后多久生效?
配置文件约 5 秒热更新自动应用,无需重启。例外:端口、监听地址、数据库类型等底层项保存后需自动重启程序;通过后台界面保存的站点配置立即生效;直接改数据库的配置最长需等待 5 分钟查询缓存过期。
模板或样式修改后未生效?
页面与静态资源引用带有 ?v= 时间戳版本参数,浏览器可能缓存旧文件。强制刷新(Ctrl+F5)或更新引用的版本参数后即可看到最新效果;若启用模板缓存,可在站点管理中关闭「启用模板缓存」。
新建的单页如何指定模板?
单页模板选择优先级为:页面 template 字段 > {code}.html > page.html > about.html。将模板文件命名为单页 code(如 code=docs 对应 docs.html)即可自动匹配。
标签不输出数据怎么排查?
依次检查:字段标签前缀是否与循环标签匹配;参数值是否使用双引号包裹;栏目 ID / 站点 ID 是否存在于当前站点(所有查询按 site_id 隔离);条件判断参数写法是否符合文档;可在 标签生成器 复制标准写法对照。
如何添加新站点/新域名?
系统管理面板(8080)→「站点管理」→「添加站点」:填写绑定域名、选择站点类型(CMS 站点 ID<10000;非 CMS 站点 ID≥10000,可启用 PHP 或纯静态)、网站路径后确认。CMS 站点需在数据库 {表前缀}sites 中存在对应记录;站点级配置未设置项自动回退全局。
HTTPS 证书如何申请与续期?
「站点管理」选中站点 → HTTPS 配置:开启 HTTPS 与 ACME,填写邮箱,ACME 域名列表留空则自动收集绑定域名(过滤 IP)。正式环境将 使用测试环境 置 false;证书域名变化时系统自动重新申请 SAN 证书。也可在 HTTPS/SSL 页为默认站点配置已有证书路径。
为什么某些后台页面需要登录才能访问?
这是「静态资源保护」在生效:受保护路径(如 /admin/*.html)未登录访问会重定向登录页或返回 401。如误伤公共资源,将路径加入「公开路径」白名单即可(白名单优先于受保护路径)。
管理面板登录被锁定怎么办?
连续失败超过 管理面板.登录失败限制(默认 5 次)会锁定该 IP 登录锁定时长(默认 300 秒)。等待解锁后重试,或修改 yaml 增大限制/时长(5 秒热更新)。密钥丢失时直接修改 管理面板.管理API密钥 即可重置面板密码。
安装插件需要重启程序或改数据库吗?
都不需要。把插件目录复制进 xiis.net/插件/站点{ID}/,最多约 5 秒后接口即可访问;删掉目录最多约 5 秒后路由消失。全程不重启 xxx.exe、不点按钮、不登录后台、不写数据库——插件系统自身零数据库表,目录存在即已安装。确需立刻生效时,可在管理面板「插件」页点「立即重新扫描」。
改了插件程序源码(如 程序/入口.py)为什么没生效?
正常情况下应会自动重载:内核把程序文件 mtime 也纳入了指纹检测,改脚本保存后最多约 5 秒自动重载并重启该插件进程(xxx.exe 不重启)。若未生效,请检查:① 改的是否是「入口脚本 import 的其他文件」——这类文件不在监听范围内(只对清单声明过的程序文件做 stat、绝不递归目录),需同时改动入口脚本本身或动一下 插件.yaml 触发重载;② 是否改在插件目录之外。
插件崩溃会不会带走主程序?性能会不会受影响?
不会。程序形态下插件跑在独立子进程里,崩溃隔离由操作系统进程边界提供:插件崩溃时该实例上的在途请求立即返回 502,内核自动重启补足实例数;窗口内失败累计达 程序最大重启次数 即熔断停用该插件——主程序始终保持存活。性能方面:一个插件都没装时读路径首行一次原子判空即返回,零开销;装了插件的请求走的是 PHP 每请求都在走的现成中间件链,成本为微秒级,远小于 stdio 传输本身。
插件路由为什么声明了却不生效(被丢弃)?
多半命中冲突检测被丢弃并记了日志。常见原因:① 路径首段命中 CMS 保留字(content/tag/search/user/category/list/admin/cms/api/static/page 等 18 个);② 路径以保留路径开头(/api/、/admin、/cms/v1、/.well-known/、/ws、/_插件);③ 路径含下划线或大写字母(只允许小写字母/数字与 . / -);④ 与站点已配置的 route_* 前缀或已有单页/栏目 slug 冲突;⑤ 与同站点另一插件声明了相同的「方法+路径」(按标识字典序,大者被丢弃)。请查站点日志确认具体冲突对象。
插件能访问文件系统和数据库吗?内核提供数据接口吗?
能,但内核不提供任何数据访问接口——插件进程直接用你自己的账号连你自己的数据库、自己读写文件,和你在别的程序里连这个库没有任何区别。内核唯一提供的是站点上下文(站点 ID、站点名称、域名、网站路径、插件数据目录),通过环境变量注入,目的是让插件不必自己解析 Host 判断当前是哪个站点。插件需要持久化的东西写在自己的 数据/ 目录(路径来自 插件数据目录 环境变量);多站点场景下按 插件站点ID 自行区分即可。
插件返回大文件(如下载、视频)怎么做?
两条通道,按「文件是否已存在于磁盘」选择:① 响应落盘通道——插件把响应写入 {插件数据目录}/下载/ 后,响应里只回传 正文文件 绝对路径,内核流式回写客户端并在写回后自动删除该文件,不受正文上限约束,适合「内容由插件动态生成」;② 静态资源/ 托管——把文件放入插件目录的 静态资源/,通过 /_插件/{标识}/... 访问,该通道支持 ETag/Last-Modified/Range(206 分段、416 越界)与 304 条件请求,可断点续传与视频拖动,适合「文件已存在磁盘上」。注意协议内存通道(正文base64)仍受正文上限约束,不适合大文件。
插件路由需要登录才能访问怎么配置?
把插件路径配进站点级「静态资源保护 → 受保护路径」即可套用现成鉴权(Cookie/JWT 认证、登录跳转、认证失败行为),无需在插件里实现登录。注意 CMS 后台权限体系({表前缀}permissions)挂在 Gin 引擎的 CMS 路由组上,插件不经引擎故不自动生效;若插件在后台类场景需要后台级鉴权,请在插件进程内自行校验(请求头已全量转发,含 Cookie,插件可直连数据库)。
插件上传大文件返回 413 或 415 怎么办?
上传治理采用「取交集、只收紧」的三条规则:正文上限取 min(yaml 服务器.最大请求体MB、yaml 安全.最大上传大小MB、清单 上传.最大MB)——任何一层配大都不会放宽其余层,因此最常见的 413 原因是 服务器.最大请求体MB(默认 5MB)卡住了。415 则是扩展名命中 yaml「禁止访问扩展名」硬黑名单(优先于白名单)、未通过 yaml/清单白名单交集、Content-Type 不在清单 允许类型 内,或正文文件头魔数与 Content-Type 不匹配(挡「改名伪装」)。改 yaml 安全 节一处,插件与 CMS 上传同时生效。
插件请求超时(504)或提示插件繁忙(503)怎么排查?
504 表示排队等待 + 插件处理超过了 超时秒(路由级 → 全局 程序默认超时秒,默认 5)。注意超时从「请求正文读取完成之后」起算,因此客户端上传耗时不会占用该预算;普通路由若无耗时需求应保持小值。上传类路由务必显式设置 超时秒(建议 ≥ 30)。503 有两种:一是「插件繁忙」——并发请求超过「实例数 + 排队上限」(默认 2 + 64),应调大 程序.实例数(或启用 弹性实例上限)与 排队上限;二是「插件上传并发已达上限」——全局在途正文资源预算 上传并发内存预算MB(默认 64MB)被占满,这是「以 503 换不 OOM」的取舍。