feat: publish Dever core with project scaffolding and versioned AI skills

This commit is contained in:
Antigravity committed 2026-10-04 13:37:30 +00:00
1 parent 70fa73cdc2
commit ac458ffa7f
960 files changed
+221319 -1163

No files matched your search

@@ -0,0 +1,18 @@
# 账户命令
首次部署通过显式命令建立租户、用户主体、凭据和所有者成员关系。
- 包:`user.account.api`
- 公开类型:无
- 公开方法:无
- 使用:无
## 初始化平台账户
本节定义对应的源码合同。
- 声明:`cmd bootstrap`
```dever
cmd bootstrap = app.bootstrap
```
@@ -0,0 +1,28 @@
# 后台会话接口
后台站点登录公开,登出要求认证。
- 包:`user.account.api.admin.session`
- 公开类型:无
- 公开方法:无
- 使用:无
## 登录
本节定义对应的源码合同。
- 声明:`post login`
```dever
public post login = app.login
```
## 登出
本节定义对应的源码合同。
- 声明:`post logout`
```dever
post logout = app.logout
```
@@ -0,0 +1,28 @@
# 前台会话接口
前台复用账户和会话,但由 front 站点签发独立受众令牌。
- 包:`user.account.api.front.session`
- 公开类型:无
- 公开方法:无
- 使用:无
## 登录
本节定义对应的源码合同。
- 声明:`post login`
```dever
public post login = app.login
```
## 登出
本节定义对应的源码合同。
- 声明:`post logout`
```dever
post logout = app.logout
```
@@ -0,0 +1,350 @@
# 账户应用
账户应用拥有凭据、登录、会话签发和认证身份组装。
- 包:`user.account.app`
- 公开类型:
- `AccountView`
- `BootstrapView`
- `LoginView`
- `LogoutView`
- `Identity`
- 公开方法:
- `bootstrap`
- `login`
- `establish_login`
- `logout`
- `verify`
- `active`
- `current_user`
- `require_user`
- 使用:
- `user.account.bootstrap(tenant_key, tenant_name, email, display_name, password)`
- `user.account.login(credential_id, password, tenant)`
- `user.account.establish_login(credential_id, tenant, site)`
- `user.account.logout()`
- `user.account.verify(claims)`
- `user.account.active(user_id)`
- `user.account.current_user()`
- `user.account.require_user(value)`
## 账户视图
本节定义对应的源码合同。
- 类型:`AccountView`
- 字段:
- `id: model.principal.id`:用户主体
- `uuid: Uuid`:身份 UUID
- `email: Text`:邮箱
- `display_name: Text`:显示名
```dever
type AccountView {
id: model.principal.id
uuid: Uuid
email: Text
display_name: Text
}
```
## 初始化结果
本节定义对应的源码合同。
- 类型:`BootstrapView`
- 字段:
- `tenant_id: platform.tenant.model.id`:租户
- `user_id: model.principal.id`:用户主体
- `credential_id: model.id`:凭据 ID
```dever
type BootstrapView {
tenant_id: platform.tenant.model.id
user_id: model.principal.id
credential_id: model.id
}
```
## 登录结果
本节定义对应的源码合同。
- 类型:`LoginView`
- 字段:
- `account: AccountView`:账户
- `tenant_id: platform.tenant.model.id`:租户
- `site: Text`:站点
```dever
type LoginView {
account: AccountView
tenant_id: platform.tenant.model.id
site: Text
}
```
## 登出结果
本节定义对应的源码合同。
- 类型:`LogoutView`
- 字段:
- `revoked: Bool`:是否撤销
```dever
type LogoutView {
revoked: Bool
}
```
## 认证身份
本节定义对应的源码合同。
- 类型:`Identity`
- 字段:
- `id: Text`:JWT subject
- `user_id: model.principal.id?`:用户主体
- `tenant_id: platform.tenant.model.id?`:租户
```dever
type Identity {
id: Text
user_id: model.principal.id?
tenant_id: platform.tenant.model.id?
}
```
## 初始化平台账户
本节定义对应的源码合同。
- 函数:`bootstrap`
- 输入:
- `tenant_key: Text`:租户键
- `tenant_name: Text`:租户名称
- `email: Text`:邮箱
- `display_name: Text`:显示名
- `password: Secret`:初始密码
- 输出:
- `result: BootstrapView`:初始化结果
```dever
bootstrap(
tenant_key: Text,
tenant_name: Text,
email: Text,
display_name: Text,
password: Secret
) (result: BootstrapView) {
tenant = platform.tenant.provision(tenant_key, tenant_name)
input = domain.prepare(email, display_name)
hash = blocking(dever.crypto.password_hash(password))
principal = model.principal.create(
{
display_name = input.name
}
)
stored = model.create(
{
principal_id = principal.id
email = input.address
password_hash = hash
}
)
user.membership.grant_owner(principal.id, tenant.id)
result = BootstrapView {
tenant_id = tenant.id
user_id = principal.id
credential_id = stored.id
}
}
```
## 登录
本节定义对应的源码合同。
- 函数:`login`
- 输入:
- `credential_id: user.account.model.id`:凭据 ID
- `password: Secret`:密码
- `tenant: Text`:租户键
- 输出:
- `result: LoginView`:登录结果
```dever
login(credential_id: model.id, password: Secret, tenant: Text) (result: LoginView) {
domain.require_active(
model.exists(
{
where = id == credential_id
}
)
)
stored = model.get(credential_id)
principal = model.principal.get(stored.principal_id)
selected = platform.tenant.active(tenant)
domain.require_active(stored.status == model.AccountStatus.Active)
domain.require_active(principal.status == model.principal.PrincipalStatus.Active)
domain.require_active(
blocking(dever.crypto.password_verify(password, stored.password_hash))
)
authenticated = establish_login(credential_id, selected.key, dever.site.key())
dever.auth.issue_cookie(
authenticated.subject,
uuid.to_text(authenticated.session_id),
authenticated.tenant_key
)
result = authenticated.result
}
```
## 建立登录会话
密码校验在事务外完成。本函数只负责在短事务内重新检查账户、租户和成员关系,再创建会话。
- 函数:`establish_login`
- 输入:
- `credential_id: user.account.model.id`:凭据 ID
- `tenant: Text`:租户键
- `site: Text`:站点键
- 输出:
- `result: LoginView`:登录视图
- `subject: Text`:认证主体
- `session_id: Uuid`:会话 UUID
- `tenant_key: Text`:租户键
```dever
transaction establish_login(credential_id: model.id, tenant: Text, site: Text) (
result: LoginView,
subject: Text,
session_id: Uuid,
tenant_key: Text
) {
stored = model.get(credential_id)
principal = model.principal.get(stored.principal_id)
selected = platform.tenant.active(tenant)
domain.require_active(stored.status == model.AccountStatus.Active)
domain.require_active(principal.status == model.principal.PrincipalStatus.Active)
user.membership.require_active(principal.id, selected.id)
session = user.session.open(
uuid.to_text(principal.uuid),
principal.id,
selected.id,
selected.key,
site
)
result = LoginView {
account = AccountView {
id = principal.id
uuid = principal.uuid
email = stored.email
display_name = principal.display_name
}
tenant_id = selected.id
site = site
}
subject = uuid.to_text(principal.uuid)
session_id = session.uuid
tenant_key = selected.key
}
```
## 登出
本节定义对应的源码合同。
- 函数:`logout`
- 输入:无
- 输出:
- `result: LogoutView`:登出结果
```dever
logout() (result: LogoutView) {
user.session.revoke(uuid.parse(dever.auth.session()))
dever.auth.clear_cookie()
result = LogoutView {
revoked = true
}
}
```
## 验证认证声明
本节定义对应的源码合同。
- 函数:`verify`
- 输入:
- `claims: dever.auth.Claims`:已验签声明
- 输出:
- `identity: Identity`:可信身份
```dever
verify(claims: dever.auth.Claims) (identity: Identity) {
session = user.session.authenticate(uuid.parse(claims.session), claims.site)
domain.require_active(session.subject == claims.subject)
domain.require_tenant(session.tenant_key, claims.tenant)
selected = platform.tenant.active(session.tenant_key)
domain.require_active(selected.id == session.tenant_id)
active(session.user_id)
user.membership.require_active(session.user_id, session.tenant_id)
identity = Identity {
id = claims.subject
user_id = session.user_id
tenant_id = session.tenant_id
}
}
```
## 验证用户主体
本节定义对应的源码合同。
- 函数:`active`
- 输入:
- `user_id: user.account.model.principal.id`:用户主体
- 输出:无
```dever
active(user_id: model.principal.id) () {
principal = model.principal.get(user_id)
domain.require_active(principal.status == model.principal.PrincipalStatus.Active)
}
```
## 当前用户的类型化标识
读取入口已经验证的可信用户标识,保留 Model ID 类型;不重新查询全局会话数据库,也不接受请求传入的作者 ID。
- 函数:`current_user`
- 输入:无
- 输出:
- `id: user.account.model.principal.id`:可信用户主体
```dever
current_user() (id: model.principal.id) {
id = require_user(dever.auth.user_id())
}
```
## 要求可信用户标识
有可信用户时保留其名义类型;没有用户身份时明确拒绝。
- 函数:`require_user`
- 输入:
- `value: user.account.model.principal.id?`:已验证的可空用户标识
- 输出:
- `id: user.account.model.principal.id`:可信用户主体
```dever
require_user(value: model.principal.id) (id: model.principal.id) {
id = value
}
require_user(value: null) (id: model.principal.id) {
fail(dever.api.Error.Unauthorized)
}
```
@@ -0,0 +1,141 @@
# 账户规则
账户输入和认证断言集中在纯 Domain。
- 包:`user.account.domain`
- 公开类型:无
- 公开方法:无
- 使用:无
## 账户输入
本节定义对应的源码合同。
- 类型:`AccountInput`
- 字段:
- `address: Text`:邮箱
- `name: Text`:显示名
```dever
type AccountInput {
address: Text
name: Text
}
```
## 规范邮箱
本节定义对应的源码合同。
- 函数:`normalize_email`
- 输入:
- `email: Text`:邮箱
- 输出:
- `address: Text`:规范邮箱
```dever
normalize_email(email: Text) (address: Text) pure {
address = text.lower(text.trim(email))
}
```
## 规范显示名
本节定义对应的源码合同。
- 函数:`normalize_name`
- 输入:
- `display_name: Text`:显示名
- 输出:
- `name: Text`:规范名称
```dever
normalize_name(display_name: Text) (name: Text) pure {
name = text.trim(display_name)
}
```
## 准备账户输入
本节定义对应的源码合同。
- 函数:`prepare`
- 输入:
- `email: Text`:邮箱
- `display_name: Text`:显示名
- 输出:
- `input: AccountInput`:规范输入
```dever
prepare(email: Text, display_name: Text) (input: AccountInput) pure {
address = normalize_email(email)
name = normalize_name(display_name)
require(
text.contains(address, "@") and not text.starts_with(address, "@") and not text.ends_with(
address,
"@"
),
"email must contain a local part and domain"
)
require(not text.is_empty(name), "display name cannot be blank")
input = AccountInput {
address = address
name = name
}
}
```
## 检查输入
本节定义对应的源码合同。
- 函数:`require`
- 输入:
- `valid: Bool`:是否有效
- `message: Text`:内部原因
- 输出:无
```dever
require(valid: true, message: Text) () pure {}
require(valid: false, message: Text) () pure {
fail(dever.api.Error.Invalid)
}
```
## 检查认证状态
本节定义对应的源码合同。
- 函数:`require_active`
- 输入:
- `active: Bool`:是否有效
- 输出:无
```dever
require_active(active: true) () pure {}
require_active(active: false) () pure {
fail(dever.api.Error.Unauthorized)
}
```
## 检查租户声明
本节定义对应的源码合同。
- 函数:`require_tenant`
- 输入:
- `expected: Text`:可信租户键
- `actual: Text?`:JWT 租户声明
- 输出:无
```dever
require_tenant(expected: Text, actual: Text) () pure {
require_active(expected == actual)
}
require_tenant(expected: Text, actual: null) () pure {
fail(dever.api.Error.Unauthorized)
}
```
@@ -0,0 +1,46 @@
# 账户凭据模型
控制库只在该模型保存密码哈希,公开身份由 Principal 承担。
- 包:`user.account.model`
- 公开类型:
- `AccountStatus`
- `Account`
- 公开方法:无
- 使用:无
## 凭据状态
本节定义对应的源码合同。
- 类型:`AccountStatus`
- 分支:
- `Active`:启用
- `Disabled`:禁用
```dever
type AccountStatus {
Active = "启用"
Disabled = "禁用"
}
```
## 账户凭据
本节定义对应的源码合同。
- 类型:`Account`
- 字段:
- `principal_id: user.account.model.principal.id`:用户主体
- `email: Text(254)`:登录邮箱
- `private password_hash: Text(255)`:私密密码哈希
- `status: AccountStatus`:凭据状态
```dever
global type Account {
principal_id: user.account.model.principal.id unique
email: Text(254) unique
private password_hash: Text(255)
status: AccountStatus default AccountStatus.Active index
}
```
@@ -0,0 +1,44 @@
# 用户主体模型
Principal 是会话、成员关系和认证 Identity 共用的公开用户身份。
- 包:`user.account.model.principal`
- 公开类型:
- `PrincipalStatus`
- `Principal`
- 公开方法:无
- 使用:无
## 主体状态
本节定义对应的源码合同。
- 类型:`PrincipalStatus`
- 分支:
- `Active`:启用
- `Disabled`:禁用
```dever
type PrincipalStatus {
Active = "启用"
Disabled = "禁用"
}
```
## 用户主体
本节定义对应的源码合同。
- 类型:`Principal`
- 字段:
- `uuid: Uuid`:身份标识
- `display_name: Text(1, 64)`:显示名称
- `status: PrincipalStatus`:状态
```dever
global type Principal {
uuid: Uuid generated unique
display_name: Text(1, 64)
status: PrincipalStatus default PrincipalStatus.Active index
}
```
@@ -0,0 +1,58 @@
# 后台授权接口
后台接口管理当前租户、当前站点的角色授权。
- 包:`user.authorization.api.admin.manage`
- 公开类型:无
- 公开方法:无
- 使用:无
## 权限目录
本节声明当前站点权限目录接口。
- 声明:`get permissions`
```dever
get permissions = app.permissions
```
## 保存角色
本节声明保存当前站点角色及权限集合的接口。
- 声明:`post save`
```dever
post save = app.save
```
## 授予角色
本节声明向用户授予当前站点角色的接口。
- 声明:`post grant`
```dever
post grant = app.grant
```
## 撤销角色
本节声明从用户撤销当前站点角色的接口。
- 声明:`delete revoke`
```dever
delete revoke = app.revoke
```
## 禁用角色
本节声明禁用当前站点角色的接口。
- 声明:`delete disable`
```dever
delete disable = app.disable
```
@@ -0,0 +1,58 @@
# 前台授权接口
前台接口管理当前租户、当前站点的角色授权。
- 包:`user.authorization.api.front.manage`
- 公开类型:无
- 公开方法:无
- 使用:无
## 权限目录
本节声明当前站点权限目录接口。
- 声明:`get permissions`
```dever
get permissions = app.permissions
```
## 保存角色
本节声明保存当前站点角色及权限集合的接口。
- 声明:`post save`
```dever
post save = app.save
```
## 授予角色
本节声明向用户授予当前站点角色的接口。
- 声明:`post grant`
```dever
post grant = app.grant
```
## 撤销角色
本节声明从用户撤销当前站点角色的接口。
- 声明:`delete revoke`
```dever
delete revoke = app.revoke
```
## 禁用角色
本节声明禁用当前站点角色的接口。
- 声明:`delete disable`
```dever
delete disable = app.disable
```
@@ -0,0 +1,108 @@
# 授权管理应用
授权管理应用只适配 Dever 核心角色存储,不保存业务权限副本。
- 包:`user.authorization.app`
- 公开类型:无
- 公开方法:
- `permissions`
- `save`
- `grant`
- `revoke`
- `disable`
- 使用:
- `user.authorization.permissions()`
- `user.authorization.save(id, name, all_permissions, permission_keys)`
- `user.authorization.grant(user_id, role_id)`
- `user.authorization.revoke(user_id, role_id)`
- `user.authorization.disable(role_id)`
## 权限目录
本节定义当前站点权限目录的读取入口。
- 函数:`permissions`
- 输入:无
- 输出:
- `values: List<dever.auth.Permission>`:当前站点权限目录
```dever
permissions() (values: List<dever.auth.Permission>) {
values = dever.auth.permissions()
}
```
## 保存角色
本节定义当前站点角色及其权限集合的保存入口。
- 函数:`save`
- 输入:
- `id: Text`:角色 ID
- `name: Text`:角色名称
- `all_permissions: Bool`:是否拥有当前站点全部权限
- `permission_keys: List<Text>`:权限键
- 输出:
- `ok: Bool`:保存成功
```dever
save(id: Text, name: Text, all_permissions: Bool, permission_keys: List<Text>) (
ok: Bool
) {
dever.auth.save_role(id, name, all_permissions, permission_keys)
ok = true
}
```
## 授予角色
本节定义向用户授予当前站点角色的入口。
- 函数:`grant`
- 输入:
- `user_id: Int`:用户 ID
- `role_id: Text`:角色 ID
- 输出:
- `ok: Bool`:授予成功
```dever
grant(user_id: Int, role_id: Text) (ok: Bool) {
dever.auth.grant_role(user_id, role_id)
ok = true
}
```
## 撤销角色
本节定义从用户撤销当前站点角色的入口。
- 函数:`revoke`
- 输入:
- `user_id: Int`:用户 ID
- `role_id: Text`:角色 ID
- 输出:
- `ok: Bool`:撤销成功
```dever
revoke(user_id: Int, role_id: Text) (ok: Bool) {
dever.auth.revoke_role(user_id, role_id)
ok = true
}
```
## 禁用角色
本节定义禁用当前站点角色的入口。
- 函数:`disable`
- 输入:
- `role_id: Text`:角色 ID
- 输出:
- `ok: Bool`:禁用成功
```dever
disable(role_id: Text) (ok: Bool) {
dever.auth.disable_role(role_id)
ok = true
}
```
@@ -0,0 +1,81 @@
# 成员应用
成员应用建立租户关系并验证有效成员。
- 包:`user.membership.app`
- 公开类型:无
- 公开方法:
- `grant_owner`
- `require_active`
- `active`
- 使用:
- `user.membership.grant_owner(account_id, selected_tenant_id)`
- `user.membership.require_active(account_id, selected_tenant_id)`
- `user.membership.active(stored)`
## 授予所有者
本节定义对应的源码合同。
- 函数:`grant_owner`
- 输入:
- `account_id: user.account.model.principal.id`:用户主体
- `selected_tenant_id: platform.tenant.model.id`:租户
- 输出:无
```dever
grant_owner(
account_id: user.account.model.principal.id,
selected_tenant_id: platform.tenant.model.id
) () {
stored = model.create(
{
user_id = account_id
tenant_id = selected_tenant_id
role = model.MembershipRole.Owner
}
)
discarded = stored
}
```
## 验证有效成员
本节定义对应的源码合同。
- 函数:`require_active`
- 输入:
- `account_id: user.account.model.principal.id`:用户主体
- `selected_tenant_id: platform.tenant.model.id`:租户
- 输出:无
```dever
require_active(
account_id: user.account.model.principal.id,
selected_tenant_id: platform.tenant.model.id
) () {
stored = model.first(
{
where = user_id == account_id and tenant_id == selected_tenant_id and status == model.MembershipStatus.Active
}
)
active(stored)
}
```
## 检查查询结果
本节定义对应的源码合同。
- 函数:`active`
- 输入:
- `stored: user.membership.model.Membership?`:成员查询结果
- 输出:无
```dever
active(stored: model.Membership) () {}
active(stored: null) () {
fail(dever.api.Error.Unauthorized)
}
```
@@ -0,0 +1,75 @@
# 租户成员模型
控制库保存用户主体在租户中的角色。
- 包:`user.membership.model`
- 公开类型:
- `MembershipRole`
- `MembershipStatus`
- `Membership`
- 公开方法:无
- 使用:无
## 成员角色
本节定义对应的源码合同。
- 类型:`MembershipRole`
- 分支:
- `Owner`:所有者
- `Editor`:编辑
- `Reader`:读者
```dever
type MembershipRole {
Owner = "所有者"
Editor = "编辑"
Reader = "读者"
}
```
## 成员状态
本节定义对应的源码合同。
- 类型:`MembershipStatus`
- 分支:
- `Active`:启用
- `Disabled`:禁用
```dever
type MembershipStatus {
Active = "启用"
Disabled = "禁用"
}
```
## 成员记录
本节定义对应的源码合同。
- 类型:`Membership`
- 字段:
- `user_id: user.account.model.principal.id`:用户主体
- `tenant_id: platform.tenant.model.id`:租户
- `role: MembershipRole`:角色
- `status: MembershipStatus`:状态
```dever
global type Membership {
user_id: user.account.model.principal.id
tenant_id: platform.tenant.model.id
role: MembershipRole index
status: MembershipStatus default MembershipStatus.Active index
}
```
## 成员唯一约束
本节定义对应的源码合同。
- 声明:`unique(user_id, tenant_id)`
```dever
unique(user_id, tenant_id)
```
@@ -0,0 +1,213 @@
# 会话应用
会话应用创建、验证和撤销站点绑定的数据库会话。
- 包:`user.session.app`
- 公开类型:
- `SessionView`
- `AuthSession`
- 公开方法:
- `open`
- `authenticate`
- `revoke`
- `auth_session`
- `revoke_found`
- `cleanup`
- 使用:
- `user.session.open(subject, account_id, selected_tenant_id, selected_tenant_key, site_key)`
- `user.session.authenticate(session_id, site_key)`
- `user.session.revoke(session_id)`
- `user.session.auth_session(stored)`
- `user.session.revoke_found(stored)`
- `user.session.cleanup()`
## 会话视图
本节定义对应的源码合同。
- 类型:`SessionView`
- 字段:
- `uuid: Uuid`:会话 UUID
```dever
type SessionView {
uuid: Uuid
}
```
## 认证会话
本节定义对应的源码合同。
- 类型:`AuthSession`
- 字段:
- `subject: Text`:subject
- `user_id: user.account.model.principal.id`:用户主体
- `tenant_id: platform.tenant.model.id`:租户
- `tenant_key: Text`:租户声明
```dever
type AuthSession {
subject: Text
user_id: user.account.model.principal.id
tenant_id: platform.tenant.model.id
tenant_key: Text
}
```
## 创建会话
本节定义对应的源码合同。
- 函数:`open`
- 输入:
- `subject: Text`:subject
- `account_id: user.account.model.principal.id`:用户主体
- `selected_tenant_id: platform.tenant.model.id`:租户
- `selected_tenant_key: Text`:租户键
- `site_key: Text`:站点
- 输出:
- `session: SessionView`:会话
```dever
open(
subject: Text,
account_id: user.account.model.principal.id,
selected_tenant_id: platform.tenant.model.id,
selected_tenant_key: Text,
site_key: Text
) (session: SessionView) {
stored = model.create(
{
subject = subject
user_id = account_id
tenant_id = selected_tenant_id
tenant_key = selected_tenant_key
site = site_key
expires_at = dever.time.add(dever.time.now(), dever.time.duration(3600000))
}
)
session = SessionView {
uuid = stored.uuid
}
}
```
## 验证会话
本节定义对应的源码合同。
- 函数:`authenticate`
- 输入:
- `session_id: Uuid?`:会话 UUID
- `site_key: Text`:站点
- 输出:
- `result: AuthSession`:认证会话
```dever
authenticate(session_id: Uuid, site_key: Text) (result: AuthSession) {
stored = model.first(
{
where = uuid == session_id and site == site_key and revoked == false and expires_at > dever.time.now(
)
}
)
result = auth_session(stored)
}
authenticate(session_id: null, site_key: Text) (result: AuthSession) {
fail(dever.api.Error.Unauthorized)
}
```
## 撤销会话
本节定义对应的源码合同。
- 函数:`revoke`
- 输入:
- `session_id: Uuid?`:会话 UUID
- 输出:无
```dever
revoke(session_id: Uuid) () {
stored = model.first(
{
where = uuid == session_id and revoked == false
}
)
revoke_found(stored)
}
revoke(session_id: null) () {
fail(dever.api.Error.Unauthorized)
}
```
## 映射认证会话
本节定义对应的源码合同。
- 函数:`auth_session`
- 输入:
- `stored: user.session.model.Session?`:查询结果
- 输出:
- `result: AuthSession`:认证会话
```dever
auth_session(stored: model.Session) (result: AuthSession) {
result = AuthSession {
subject = stored.subject
user_id = stored.user_id
tenant_id = stored.tenant_id
tenant_key = stored.tenant_key
}
}
auth_session(stored: null) (result: AuthSession) {
fail(dever.api.Error.Unauthorized)
}
```
## 更新撤销状态
本节定义对应的源码合同。
- 函数:`revoke_found`
- 输入:
- `stored: user.session.model.Session?`:查询结果
- 输出:无
```dever
revoke_found(stored: model.Session) () {
changed = model.update(
stored.id,
{
revoked = true
}
)
discarded = changed
}
revoke_found(stored: null) () {
fail(dever.api.Error.Unauthorized)
}
```
## 清理过期会话
清理已撤销或已过期的会话记录。
- 函数:`cleanup`
- 输入:无
- 输出:无
```dever
cleanup() () {
removed = model.delete(
{
where = revoked == true or expires_at <= dever.time.now()
}
)
}
```
@@ -0,0 +1,42 @@
# 会话清理任务
周期任务清理撤销或过期会话。
- 包:`user.session.job`
- 公开类型:无
- 公开方法:无
- 使用:无
## 队列连接
使用默认数据库连接。
- 声明:`database default`
```dever
database default
```
## 清理任务
任务委托给会话应用。
- 函数:`cleanup`
- 输入:无
- 输出:无
```dever
job cleanup() () retry(3) timeout(30000) {
app.cleanup()
}
```
## 每小时执行
按 UTC 每小时清理。
- 声明:`schedule cleanup`
```dever
schedule cleanup = "0 * * * *"
```
@@ -0,0 +1,47 @@
# 会话模型
控制库保存站点绑定、租户绑定且可撤销的登录会话。
- 包:`user.session.model`
- 公开类型:
- `Session`
- 公开方法:无
- 使用:无
## 会话记录
本节定义对应的源码合同。
- 类型:`Session`
- 字段:
- `uuid: Uuid`:会话标识
- `subject: Text(1, 128)`:JWT subject
- `user_id: user.account.model.principal.id`:用户主体
- `tenant_id: platform.tenant.model.id`:租户
- `tenant_key: Text(1, 64)`:JWT 租户声明
- `site: Text(1, 32)`:站点
- `expires_at: DateTime`:过期时间
- `revoked: Bool`:撤销状态
```dever
global type Session {
uuid: Uuid generated unique
subject: Text(1, 128)
user_id: user.account.model.principal.id
tenant_id: platform.tenant.model.id
tenant_key: Text(1, 64)
site: Text(1, 32)
expires_at: DateTime index
revoked: Bool default false index
}
```
## 会话查询索引
本节定义对应的源码合同。
- 声明:`index(user_id, tenant_id, site)`
```dever
index(user_id, tenant_id, site)
```