6.8 KiB
应用接口、数据与后台任务
API 和 CMD
module/system/health/api/admin/status.dever 是一个可匿名访问的健康检查:
源码:健康检查 API
public get ready = app.status
API 只把请求交给 App,不写函数体。参数和返回类型直接使用 App 的声明。HTTP 支持 get/post/put/delete,不支持 patch、旧 get_ 前缀或文件内的 site 声明。
cmd greet = app.greet 生成 component.domain.greet 命令,参数是单个 JSON 对象。无需 main.dever,也无需手写命令行解析。
GET/DELETE 从 URL query 读标量参数,POST/PUT 从 JSON body 读参数。多传、重复或缺少必填字段都会报错。无需自己读 body 或构造 dever.http.Response。
成功返回 {"code":0,"message":"ok","data":...}。失败时 data 为 null,code 对应 HTTP 状态码。每个接口绑定一个能序列化的输出,输出名可自定。
dever.api.Error 中 Invalid/Unauthorized/Forbidden/NotFound/Conflict/TooManyRequests 对应 400/401/403/404/409/429;未知内部失败记录脱敏日志并返回安全的 500。
写 HTTP、CMD 和 Job 默认由入口建立事务;GET 不能写库或入队。
密码 hash/verify 除外:计算期间不能长期占用数据库事务。实际写入要放在显式的短 transaction 内,并重新检查相关状态。不要因为“默认有事务”删掉这里的短事务。
站点、身份与权限
sites.admin.path = "admin" 匹配领域内 api/admin/**,front 同理。文件名 topic 也进入 URL;上述 ready 是 /system/health/admin/status/ready。每条 HTTP 声明只能匹配一个站点,重叠或遗漏配置会失败;path 不改写 URL,也不匹配 api/admin.dever。
所有 HTTP 默认鉴权并校验精确权限。只有明确匿名操作写 public post login = app.login 等单条 public 声明;没有 public rest、public cmd、auth.require 或每个业务文件的权限注册代码。
配置中的 auth provider 指向项目自己的 verify App 方法,由它核实账号、会话、成员和租户关系。输入是 dever.auth.Claims,输出必须且只能有三个字段:
id: Text:核验后的主体标识。user_id: <UserModelId>?:用户的 Model ID。tenant_id: <TenantModelId>?:租户的 Model ID。
verify 只能读 global Model,不能写库、拿原始 Header 或返回客户端自报的权限。
受保护接口认证成功后,App 可用 dever.auth.id()、session()、user_id()、tenant_id() 和 dever.site.key() 读取身份,不需要传 context。
这些值来自已核验的上下文,不能用客户端提交的 user_id/tenant_id 替代。ID 类型必须匹配 verify 声明,可空 ID 先用分句处理。public、CMD、Job 和普通测试不会自动获得身份读取能力。
权限键由核心生成,格式为 component.domain.site.action,例如 news.article.admin.publish。请求方法记录为元数据,不拼进权限键。REST 的 action 固定为 read/create/replace/delete。
核心已经维护角色、用户角色和角色权限表。后台 App 使用 dever.auth.permissions/save_role/grant_role/revoke_role/disable_role 管理授权,参数见 签名索引;这些方法只能由受保护的写 API 调用。dever.auth.owns_user(id) 检查资源归属,不能替代接口权限。
Cookie 登录/退出用 dever.auth.issue_cookie(subject, session, tenant) 与 clear_cookie()。Header/Cookie 使用 dever.api 读取/设置,无需显式 context 参数。密钥来自部署配置;Cookie 写入和 Cookie 认证的写请求需要精确同源校验,站点配置 origin。敏感头不能当普通 Text 读取。
Model、CRUD 与业务编排
module/content/article/model.dever 的主记录是 Article,标题在 REST 写入时使用同领域纯计算规则:
源码:文章 Model
type Article {
title: Text(1, 200) create = domain.normalize(input) replace = domain.normalize(input)
body: Text
}
自动有只读 id 与 created_at。字段支持 default、private、unique、index;范围和长度在存储边界验证。关系使用目标 Model 专有 id 字段,不用 Int 代替外键。
同领域 App 直接使用 model.create({...})、model.get(id)、model.update(id,{...})、model.delete(id) 和 model.list({where=...,order=[created_at.desc,id.desc],page=1,size=20})。
这些表达式由编译器检查,不是任意字典拼 SQL;无需再写 repository。关联查询和跨表插入由 App 明确编排,不会根据关系声明自动猜测级联写入顺序。
纯粹 CRUD 可在 API 写 rest model,多 Model 时指定 rest model.<topic>。Model 中 create = expr、replace = expr、search = expr 控制 REST 字段来源/查询;引用 input 表示客户端同名输入转换,不引用 input 表示服务端提供且禁止客户端提交。不要改成 post/put/get 字段关键字。
private owner user_id: ... = dever.auth.user_id() 创建时写入当前用户,并让全部读写 SQL 都带上所有权条件;它不只是隐藏输出字段。仅需这种隔离时,不用再写 App。
有关联操作、复杂输入输出、发布状态流转或超出字段约束的业务校验时,再写 App。API 中不放这些逻辑。App 用明确的 View 返回数据,避免泄漏 Model 私有字段。
不为每个 CRUD 机械建立 App、Domain、Port 和 Adapter;Model 的数据库操作无需再套一层 Port/repository。实际新增业务合同或编排时才增加对应实现。完整反重复、反硬编码及检查边界见 项目开发。
租户、任务与部署配置
配置只有 database 租户隔离:tenant.database 选控制连接,global type 使用平台库,其余持久 Model 与 Job 使用租户库。没有 field/table/mode 配置。租户缺失、未迁移或 schema 过期明确失败,不回退平台库。
运维命令:dever tenant migrate <root> <id>;dever tenant owner <root> <id> <site> <user-id> 建立核心 Owner;dever tenant component enable|disable <root> <id> <component> 控制租户功能。可用租户组件从 Model/Job 推导,不手写 Profile 分组。
job.dever 声明持久任务并绑定 App;enqueue 与事务、重试、调度、权限重验使用核心机制,不另写后台死循环。直接从随 skill 提供的 Job 示例 复用声明、配置和隔离测试,不能把第三方 Worker 当持久 Job。
部署配置先参考随 skill 提供的 完整示例,只添加项目用到的 database、tenant、auth、sites、http、job、runtime、log。最小 CMD 项目只需模板配置。密码、token 与真实数据库连接不提交 Git。