Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

@@ -0,0 +1,22 @@
{
"database": {
"default": {
"type": "sqlite",
"path": "data/db/platform.db",
"tenant_directory": "data/tenants"
}
},
"tenant": { "database": "default" },
"auth": {
"providers": {
"session": {
"verify": "access.session.verify",
"jwtSecret": "replace-this-example-secret-before-deployment",
"ttlSeconds": 3600
}
}
},
"sites": { "admin": { "path": "admin", "auth": "session" } },
"http": { "host": "127.0.0.1", "port": 8080 },
"log": { "level": "info" }
}
@@ -0,0 +1,8 @@
# 会话认证
验证站点会话、用户、租户和成员关系。
- [业务操作](doc/app.md)
- [成员关系](doc/model-membership.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,60 @@
type Identity {
id: Text
user_id: access.user.model.id?
tenant_id: access.tenant.model.id?
}
verify(claims: dever.auth.Claims) (identity: Identity) {
identity = findSession(uuid.parse(claims.session), claims.tenant, claims)
}
findSession(session_id: Uuid, selected_key: Text, claims: dever.auth.Claims) (
identity: Identity
) {
stored = model.first(
{
where = uuid == session_id and subject == claims.subject and site == claims.site and tenant_key == selected_key and revoked == false and expires_at > dever.time.now(
)
}
)
identity = verified(stored)
}
findSession(session_id: null, selected_key: Text?, claims: dever.auth.Claims) (
identity: Identity
) {
fail(dever.api.Error.Unauthorized)
}
findSession(session_id: Uuid, selected_key: null, claims: dever.auth.Claims) (
identity: Identity
) {
fail(dever.api.Error.Unauthorized)
}
verified(stored: model.Session) (identity: Identity) {
access.user.requireActive(stored.user_id)
access.tenant.requireActive(stored.tenant_id, stored.tenant_key)
requireMembership(
model.membership.exists(
{
where = user_id == stored.user_id and tenant_id == stored.tenant_id and active == true
}
)
)
identity = Identity {
id = stored.subject
user_id = stored.user_id
tenant_id = stored.tenant_id
}
}
verified(stored: null) (identity: Identity) {
fail(dever.api.Error.Unauthorized)
}
requireMembership(active: true) () {}
requireMembership(active: false) () {
fail(dever.api.Error.Unauthorized)
}
@@ -0,0 +1,21 @@
# 业务操作
## access.session.Identity
认证后的主体、用户编号和租户编号。
## access.session.verify
检查会话声明以及用户、租户和成员状态,返回可信身份。
## access.session.findSession
按 UUID、主体、站点和租户查找未撤销且未过期的会话;缺失或无效时返回未授权。
## access.session.verified
验证已找到会话的用户、租户和成员状态;空记录返回未授权。
## access.session.requireMembership
成员关系启用时通过,否则返回未授权。
@@ -0,0 +1,5 @@
# 成员关系
## access.session.model.membership.Membership
平台库中的用户与租户成员关系,每对用户和租户唯一。
@@ -0,0 +1,5 @@
# 数据结构
## access.session.model.Session
平台库中的站点会话,保存用户、租户、有效期和撤销状态。
@@ -0,0 +1,231 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 1e9b28fb1ca00f29b41bebdf0b358f4e1a1b787fb2a524aea6377dd8b98ab87b -->
# access.session
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
没有已收录的入口。
## 外部调用方
| 能力 | 调用方 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.verify](app.dever#L7) | [content.article.api.admin.entries.rest](../../content/article/api/admin/entries.dever#L1) | 身份验证 | [../../content/article/api/admin/entries.dever:1:1](../../content/article/api/admin/entries.dever#L1,1) |
| [access.session.verify](app.dever#L7) | [system.health.api.admin.status.ready](../../system/health/api/admin/status.dever#L1) | 身份验证 | [../../system/health/api/admin/status.dever:1:20](../../system/health/api/admin/status.dever#L1,20) |
## 能力与内部调用
### access.session.Identity
定义:[app.dever:1:1](app.dever#L1,1)
没有已收录的下游调用。
### access.session.findSession
定义:[app.dever:11:1](app.dever#L11,1)、[app.dever:23:1](app.dever#L23,1)、[app.dever:29:1](app.dever#L29,1)
<a id="flow-390194c3c1297291-1"></a>
```mermaid
flowchart TD
n0(["findSession"])
click n0 href "app.dever#L11,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["session_id: Uuid 且 selected_key: Text"]
click n4 href "app.dever#L11,1" "打开源码"
n5[["dever.time.now"]]
click n5 href "app.dever#L16,157" "打开源码"
n6["access.session.model.first"]
click n6 href "app.dever#L14,12" "打开源码"
n7["赋值 stored"]
click n7 href "app.dever#L14,12" "打开源码"
n8[["access.session.verified"]]
click n8 href "app.dever#L20,14" "打开源码"
n9["赋值 identity"]
click n9 href "app.dever#L20,14" "打开源码"
n10["session_id: null"]
click n10 href "app.dever#L23,1" "打开源码"
n11["fail dever.api.Error.Unauthorized"]
click n11 href "app.dever#L26,8" "打开源码"
n12["session_id: Uuid 且 selected_key: null"]
click n12 href "app.dever#L29,1" "打开源码"
n13["fail dever.api.Error.Unauthorized"]
click n13 href "app.dever#L32,8" "打开源码"
n0 --> n3
n3 -->|"session_id: Uuid 且 selected_key: Text"| n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n1
n3 -->|"session_id: null"| n10
n10 --> n11
n11 --> n2
n3 -->|"session_id: Uuid 且 selected_key: null"| n12
n12 --> n13
n13 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| dever.time.now | [app.dever:16:157](app.dever#L16,157) | 依赖或生成的分派(本页不展开) |
| access.session.verified | [app.dever:20:14](app.dever#L20,14) | [access.session.verified](link.md#flow-d703972fda2ee383-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.model.first](model.dever#L1) | 领域内 | 内建操作 | [app.dever:14:12](app.dever#L14,12) |
| `dever.time.now` | 依赖 | 调用 | [app.dever:16:157](app.dever#L16,157) |
| [access.session.verified](app.dever#L35) | 领域内 | 调用 | [app.dever:20:14](app.dever#L20,14) |
### access.session.model.Session
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
### access.session.model.membership.Membership
定义:[model/membership.dever:1:1](model/membership.dever#L1,1)
没有已收录的下游调用。
### access.session.requireMembership
定义:[app.dever:56:1](app.dever#L56,1)、[app.dever:58:1](app.dever#L58,1)
<a id="flow-322031245e905aab-1"></a>
```mermaid
flowchart TD
n0(["requireMembership"])
click n0 href "app.dever#L56,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["active: true"]
click n4 href "app.dever#L56,1" "打开源码"
n5["active: false"]
click n5 href "app.dever#L58,1" "打开源码"
n6["fail dever.api.Error.Unauthorized"]
click n6 href "app.dever#L59,8" "打开源码"
n0 --> n3
n3 -->|"active: true"| n4
n4 --> n1
n3 -->|"active: false"| n5
n5 --> n6
n6 --> n2
```
没有已收录的下游调用。
### access.session.verified
定义:[app.dever:35:1](app.dever#L35,1)、[app.dever:52:1](app.dever#L52,1)
<a id="flow-d703972fda2ee383-1"></a>
```mermaid
flowchart TD
n0(["verified"])
click n0 href "app.dever#L35,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["stored: model.Session"]
click n4 href "app.dever#L35,1" "打开源码"
n5[["access.user.requireActive"]]
click n5 href "app.dever#L36,3" "打开源码"
n6[["access.tenant.requireActive"]]
click n6 href "app.dever#L37,3" "打开源码"
n7["access.session.model.membership.exists"]
click n7 href "app.dever#L39,5" "打开源码"
n8[["access.session.requireMembership"]]
click n8 href "app.dever#L38,3" "打开源码"
n9["赋值 identity"]
click n9 href "app.dever#L45,14" "打开源码"
n10["stored: null"]
click n10 href "app.dever#L52,1" "打开源码"
n11["fail dever.api.Error.Unauthorized"]
click n11 href "app.dever#L53,8" "打开源码"
n0 --> n3
n3 -->|"stored: model.Session"| n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n1
n3 -->|"stored: null"| n10
n10 --> n11
n11 --> n2
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.user.requireActive | [app.dever:36:3](app.dever#L36,3) | [access.user.requireActive](../user/link.md#flow-f368d51d0e19702c-1) |
| access.tenant.requireActive | [app.dever:37:3](app.dever#L37,3) | [access.tenant.requireActive](../tenant/link.md#flow-8fb2bcc151c4208d-1) |
| access.session.requireMembership | [app.dever:38:3](app.dever#L38,3) | [access.session.requireMembership](link.md#flow-322031245e905aab-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.user.requireActive](../user/app.dever#L1) | 跨领域 [access.user](../user/link.md) | 调用 | [app.dever:36:3](app.dever#L36,3) |
| [access.tenant.requireActive](../tenant/app.dever#L1) | 跨领域 [access.tenant](../tenant/link.md) | 调用 | [app.dever:37:3](app.dever#L37,3) |
| [access.session.requireMembership](app.dever#L56) | 领域内 | 调用 | [app.dever:38:3](app.dever#L38,3) |
| [access.session.model.membership.exists](model/membership.dever#L1) | 领域内 | 内建操作 | [app.dever:39:5](app.dever#L39,5) |
### access.session.verify
定义:[app.dever:7:1](app.dever#L7,1)
<a id="flow-01943b8d6356f307-1"></a>
```mermaid
flowchart TD
n0(["verify"])
click n0 href "app.dever#L7,1" "打开源码"
n1(["返回"])
n3[["uuid.parse"]]
click n3 href "app.dever#L8,26" "打开源码"
n4[["access.session.findSession"]]
click n4 href "app.dever#L8,14" "打开源码"
n5["赋值 identity"]
click n5 href "app.dever#L8,14" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n5
n5 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| uuid.parse | [app.dever:8:26](app.dever#L8,26) | 依赖或生成的分派(本页不展开) |
| access.session.findSession | [app.dever:8:14](app.dever#L8,14) | [access.session.findSession](link.md#flow-390194c3c1297291-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.findSession](app.dever#L11) | 领域内 | 调用 | [app.dever:8:14](app.dever#L8,14) |
| `uuid.parse` | 依赖 | 调用 | [app.dever:8:26](app.dever#L8,26) |
@@ -0,0 +1,10 @@
global type Session {
uuid: Uuid generated unique
subject: Text
user_id: access.user.model.id
tenant_id: access.tenant.model.id
tenant_key: Text
site: Text
expires_at: DateTime
revoked: Bool default false
}
@@ -0,0 +1,7 @@
global type Membership {
user_id: access.user.model.id
tenant_id: access.tenant.model.id
active: Bool default true
}
unique(user_id, tenant_id)
@@ -0,0 +1,8 @@
# 租户状态
检查租户编号、唯一键和启用状态。
- [业务操作](doc/app.md)
- [内部规则](doc/domain.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,9 @@
requireActive(tenant_id: model.id, tenant_key: Text) () {
domain.requireActive(
model.exists(
{
where = id == tenant_id and key == tenant_key and active == true
}
)
)
}
@@ -0,0 +1,5 @@
# 业务操作
## access.tenant.requireActive
要求租户编号、键和启用状态同时匹配。
@@ -0,0 +1,5 @@
# 内部规则
## access.tenant.domain.requireActive
租户已启用时通过,否则返回未授权。
@@ -0,0 +1,5 @@
# 数据结构
## access.tenant.model.Tenant
平台租户的唯一键和启用状态。
@@ -0,0 +1,5 @@
requireActive(active: true) () {}
requireActive(active: false) () {
fail(dever.api.Error.Unauthorized)
}
@@ -0,0 +1,89 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 6ab5ca6ad85fdbfe6452f6aaf1909ecf5a0ea5b89b5e50d80d0325d23097bd03 -->
# access.tenant
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
没有已收录的入口。
## 外部调用方
| 能力 | 调用方 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.tenant.requireActive](app.dever#L1) | [access.session.verified](../session/app.dever#L35) | 调用 | [../session/app.dever:37:3](../session/app.dever#L37,3) |
## 能力与内部调用
### access.tenant.domain.requireActive
定义:[domain.dever:1:1](domain.dever#L1,1)、[domain.dever:3:1](domain.dever#L3,1)
<a id="flow-4bf621ec21992ef9-1"></a>
```mermaid
flowchart TD
n0(["requireActive"])
click n0 href "domain.dever#L1,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["active: true"]
click n4 href "domain.dever#L1,1" "打开源码"
n5["active: false"]
click n5 href "domain.dever#L3,1" "打开源码"
n6["fail dever.api.Error.Unauthorized"]
click n6 href "domain.dever#L4,8" "打开源码"
n0 --> n3
n3 -->|"active: true"| n4
n4 --> n1
n3 -->|"active: false"| n5
n5 --> n6
n6 --> n2
```
没有已收录的下游调用。
### access.tenant.model.Tenant
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
### access.tenant.requireActive
定义:[app.dever:1:1](app.dever#L1,1)
<a id="flow-8fb2bcc151c4208d-1"></a>
```mermaid
flowchart TD
n0(["requireActive"])
click n0 href "app.dever#L1,1" "打开源码"
n1(["返回"])
n3["access.tenant.model.exists"]
click n3 href "app.dever#L3,5" "打开源码"
n4[["access.tenant.domain.requireActive"]]
click n4 href "app.dever#L2,3" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.tenant.domain.requireActive | [app.dever:2:3](app.dever#L2,3) | [access.tenant.domain.requireActive](link.md#flow-4bf621ec21992ef9-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.tenant.domain.requireActive](domain.dever#L1) | 领域内 | 调用 | [app.dever:2:3](app.dever#L2,3) |
| [access.tenant.model.exists](model.dever#L1) | 领域内 | 内建操作 | [app.dever:3:5](app.dever#L3,5) |
@@ -0,0 +1,4 @@
global type Tenant {
key: Text unique
active: Bool default true
}
@@ -0,0 +1,8 @@
# 用户状态
检查用户是否存在且已启用。
- [业务操作](doc/app.md)
- [内部规则](doc/domain.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,9 @@
requireActive(user_id: model.id) () {
domain.requireActive(
model.exists(
{
where = id == user_id and active == true
}
)
)
}
@@ -0,0 +1,5 @@
# 业务操作
## access.user.requireActive
要求指定用户存在且已启用。
@@ -0,0 +1,5 @@
# 内部规则
## access.user.domain.requireActive
用户已启用时通过,否则返回未授权。
@@ -0,0 +1,5 @@
# 数据结构
## access.user.model.User
平台用户的 UUID 和启用状态。
@@ -0,0 +1,5 @@
requireActive(active: true) () {}
requireActive(active: false) () {
fail(dever.api.Error.Unauthorized)
}
@@ -0,0 +1,89 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 8c879374d859fd5ed0f03587252b2d7be479f6a1198251f23907f88f8d0c2b39 -->
# access.user
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
没有已收录的入口。
## 外部调用方
| 能力 | 调用方 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.user.requireActive](app.dever#L1) | [access.session.verified](../session/app.dever#L35) | 调用 | [../session/app.dever:36:3](../session/app.dever#L36,3) |
## 能力与内部调用
### access.user.domain.requireActive
定义:[domain.dever:1:1](domain.dever#L1,1)、[domain.dever:3:1](domain.dever#L3,1)
<a id="flow-fa5889ca969e0acf-1"></a>
```mermaid
flowchart TD
n0(["requireActive"])
click n0 href "domain.dever#L1,1" "打开源码"
n1(["返回"])
n2(["传播错误"])
n3{"按实参匹配分句(互斥)"}
n4["active: true"]
click n4 href "domain.dever#L1,1" "打开源码"
n5["active: false"]
click n5 href "domain.dever#L3,1" "打开源码"
n6["fail dever.api.Error.Unauthorized"]
click n6 href "domain.dever#L4,8" "打开源码"
n0 --> n3
n3 -->|"active: true"| n4
n4 --> n1
n3 -->|"active: false"| n5
n5 --> n6
n6 --> n2
```
没有已收录的下游调用。
### access.user.model.User
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
### access.user.requireActive
定义:[app.dever:1:1](app.dever#L1,1)
<a id="flow-f368d51d0e19702c-1"></a>
```mermaid
flowchart TD
n0(["requireActive"])
click n0 href "app.dever#L1,1" "打开源码"
n1(["返回"])
n3["access.user.model.exists"]
click n3 href "app.dever#L3,5" "打开源码"
n4[["access.user.domain.requireActive"]]
click n4 href "app.dever#L2,3" "打开源码"
n0 --> n3
n3 --> n4
n4 --> n1
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.user.domain.requireActive | [app.dever:2:3](app.dever#L2,3) | [access.user.domain.requireActive](link.md#flow-fa5889ca969e0acf-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.user.domain.requireActive](domain.dever#L1) | 领域内 | 调用 | [app.dever:2:3](app.dever#L2,3) |
| [access.user.model.exists](model.dever#L1) | 领域内 | 内建操作 | [app.dever:3:5](app.dever#L3,5) |
@@ -0,0 +1,4 @@
global type User {
uuid: Uuid generated unique
active: Bool default true
}
@@ -0,0 +1,7 @@
# 文章 REST
用 Model 和 REST 提供文章增删改查,正文保留原文。
- [文章 REST](doc/api-admin-entries.md)
- [数据结构](doc/model.md)
- [调用流程](link.md)
@@ -0,0 +1,5 @@
# 文章 REST
## content.article.api.admin.entries.rest
按 Article Model 提供增删改查,由站点认证和角色权限保护。
@@ -0,0 +1,5 @@
# 数据结构
## content.article.model.Article
文章标题和正文;标题自动去首尾空白,正文使用 raw 保留原文。
@@ -0,0 +1,254 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: 09e55892630b724d8c2e599fc3b266e774d5c3576ea2741220017ee9246fa910 -->
# content.article
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
- REST [content.article.api.admin.entries.rest](api/admin/entries.dever#L1)
## 外部调用方
当前加载的源码中没有已收录的外部调用。
## 能力与内部调用
### content.article.api.admin.entries.rest
定义:[api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1)
<a id="flow-96f9bcd93be27ba3-1"></a>
```mermaid
flowchart TD
n0(["content.article.api.admin.entries.rest"])
click n0 href "api/admin/entries.dever#L1,1" "打开源码"
n1{"自动 REST /content/article/admin/entries 或其正整数 id 路径;topic #34;admin/entries#34;"}
n2(["返回 HTTP 404"])
n3{"集合允许 GET / POST;详情允许 GET / PUT / DELETE"}
n4(["返回 HTTP 405 + Allow"])
n5["取得认证时间"]
n6(["返回 HTTP 500"])
n7["按 topic #34;admin/entries#34; 选择站点与 Provider"]
n8(["返回 HTTP 403"])
n9{"读取 Bearer 或站点 Cookie;两者互斥"}
n10(["返回 HTTP 401"])
n11["认证上下文就绪"]
n12(["返回 HTTP 401"])
n13["校验 Token 的签名、有效期及站点"]
n14{"Cookie 且非 GET / HEAD / OPTIONS?"}
n15["检查站点 Origin"]
n16(["返回 HTTP 403"])
n17(["返回 HTTP 401"])
n18{"按 Provider.verify 选择验证函数"}
n19[["access.session.verify"]]
click n19 href "api/admin/entries.dever#L1,1" "打开源码"
n20(["返回 HTTP 业务错误映射状态;其他故障 500"])
n21["用验证结果建立身份"]
n22(["返回 HTTP 认证错误状态"])
n23(["返回 HTTP 500"])
n24["建立请求数据库上下文"]
n25{"有已验证租户?"}
n26["数据库上下文就绪"]
n27{"部署启用了租户存储?"}
n28(["返回 HTTP 403"])
n29["按已验证租户选择数据库"]
n30(["返回 HTTP 503"])
n31{"检查租户组件启用状态:content"}
n32(["返回 HTTP 403"])
n33(["返回 HTTP 503"])
n34{"写请求(非 GET)?"}
n35["进入写入口事务"]
n36["开始 REST 操作"]
n37["失败:保留 HTTP 错误状态"]
n38{"此请求拥有写入口事务?"}
n39["回滚入口拥有的事务"]
n40(["返回 HTTP 500"])
n41["事务边界结束"]
n42(["返回错误响应"])
n43{"权限检查:content.article.{site}.{read/create/replace/delete}"}
n44["权限通过"]
n45["取得目标 Model 的数据库执行器"]
n46{"按方法与 id 选择一个操作"}
n47["REST 响应已编码"]
n48["解码分页和可选搜索参数"]
click n48 href "api/admin/entries.dever#L1,1" "打开源码"
n49[["content.article.model.list:COUNT + 分页 SELECT"]]
click n49 href "model.dever#L1,1" "打开源码"
n50["按 REST 读字段编码结果(不含私有字段)"]
n51["检查空参数合同"]
click n51 href "api/admin/entries.dever#L1,1" "打开源码"
n52[["content.article.model.get:SELECT 单条"]]
click n52 href "model.dever#L1,1" "打开源码"
n53["按 REST 读字段编码结果(不含私有字段)"]
n54["解码创建字段"]
click n54 href "api/admin/entries.dever#L1,1" "打开源码"
n55["取得已解码字段 title"]
n56["规范化字段 title:去首尾空白;null 保持不变"]
click n56 href "model.dever#L2,3" "打开源码"
n57["检查字段 title 的长度 / 有限数合同"]
click n57 href "model.dever#L2,3" "打开源码"
n58["取得已解码字段 body"]
n59["填入 created_at"]
n60[["content.article.model.create:INSERT RETURNING"]]
click n60 href "model.dever#L1,1" "打开源码"
n61["按 REST 读字段编码结果(不含私有字段)"]
n62["解码替换字段"]
click n62 href "api/admin/entries.dever#L1,1" "打开源码"
n63["取得已解码字段 title"]
n64["规范化字段 title:去首尾空白;null 保持不变"]
click n64 href "model.dever#L2,3" "打开源码"
n65["检查字段 title 的长度 / 有限数合同"]
click n65 href "model.dever#L2,3" "打开源码"
n66["取得已解码字段 body"]
n67[["content.article.model.update:UPDATE RETURNING"]]
click n67 href "model.dever#L1,1" "打开源码"
n68["按 REST 读字段编码结果(不含私有字段)"]
n69["检查空参数合同"]
click n69 href "api/admin/entries.dever#L1,1" "打开源码"
n70[["content.article.model.delete:DELETE"]]
click n70 href "model.dever#L1,1" "打开源码"
n71["编码空结果"]
n72{"此请求拥有写入口事务?"}
n73["提交入口拥有的事务"]
n74(["返回 HTTP 500"])
n75["事务边界结束"]
n76["发布暂存的认证 Header / Cookie"]
n77(["返回成功响应"])
n0 --> n1
n1 -->|"站点未匹配或 id 无效"| n2
n1 --> n3
n3 -->|"不允许的方法"| n4
n3 --> n5
n5 -->|"失败"| n6
n5 --> n7
n7 -->|"无站点绑定"| n8
n7 --> n9
n9 -->|"凭据格式错误或冲突"| n10
n9 -->|"无凭据"| n12
n9 -->|"有凭据"| n14
n14 -->|"否"| n13
n14 -->|"是"| n15
n15 -->|"不符合"| n16
n15 -->|"通过"| n13
n13 -->|"无 Provider 或 Token 无效"| n17
n13 --> n18
n18 -->|"access.session.verify"| n19
n19 -->|"验证函数失败"| n20
n19 --> n21
n21 -->|"身份不合法"| n22
n21 -->|"通过"| n11
n18 -->|"无匹配验证函数"| n23
n11 --> n24
n24 --> n25
n25 -->|"否"| n27
n27 -->|"是:缺少租户身份"| n28
n27 -->|"否"| n26
n25 -->|"是"| n29
n29 -->|"选择失败"| n30
n29 --> n31
n31 -->|"未启用"| n32
n31 -->|"查询失败"| n33
n31 -->|"允许"| n26
n26 --> n34
n34 -->|"是"| n35
n34 -->|"否"| n36
n35 --> n36
n37 --> n38
n38 -->|"是"| n39
n39 -->|"事务结束失败"| n40
n38 -->|"否"| n41
n39 -->|"完成"| n41
n41 --> n42
n36 --> n43
n43 -->|"拒绝:Access 状态;查询故障:500"| n37
n43 -->|"允许"| n44
n44 --> n45
n45 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n45 --> n46
n46 -->|"GET 集合"| n48
n48 -->|"输入或解码错误:400 等对应状态"| n37
n48 --> n49
n49 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n49 --> n50
n50 -->|"编码失败:500"| n37
n50 --> n47
n46 -->|"GET 详情"| n51
n51 -->|"输入或解码错误:400 等对应状态"| n37
n51 --> n52
n52 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n52 -->|"无记录或删除影响 0 行:404"| n37
n52 --> n53
n53 -->|"编码失败:500"| n37
n53 --> n47
n46 -->|"POST 集合"| n54
n54 -->|"输入或解码错误:400 等对应状态"| n37
n54 --> n55
n55 --> n56
n56 --> n57
n57 -->|"长度不符合:400;非有限数:500"| n37
n57 --> n58
n58 --> n59
n59 -->|"值生成失败:数据库错误映射"| n37
n59 --> n60
n60 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n60 --> n61
n61 -->|"编码失败:500"| n37
n61 --> n47
n46 -->|"PUT 详情"| n62
n62 -->|"输入或解码错误:400 等对应状态"| n37
n62 --> n63
n63 --> n64
n64 --> n65
n65 -->|"长度不符合:400;非有限数:500"| n37
n65 --> n66
n66 --> n67
n67 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n67 -->|"无记录或删除影响 0 行:404"| n37
n67 --> n68
n68 -->|"编码失败:500"| n37
n68 --> n47
n46 -->|"DELETE 详情"| n69
n69 -->|"输入或解码错误:400 等对应状态"| n37
n69 --> n70
n70 -->|"数据库错误:404 / 400 / 409 / 500"| n37
n70 -->|"无记录或删除影响 0 行:404"| n37
n70 --> n71
n71 -->|"编码失败:500"| n37
n71 --> n47
n47 --> n72
n72 -->|"是"| n73
n73 -->|"事务结束失败"| n74
n72 -->|"否"| n75
n73 -->|"完成"| n75
n75 --> n76
n76 --> n77
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.session.verify | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) | [access.session.verify](../../access/session/link.md#flow-01943b8d6356f307-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.verify](../../access/session/app.dever#L7) | 跨领域 [access.session](../../access/session/link.md) | 身份验证 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.create](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.delete](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.get](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.list](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
| [content.article.model.update](model.dever#L1) | 领域内 | REST Model 操作 | [api/admin/entries.dever:1:1](api/admin/entries.dever#L1,1) |
### content.article.model.Article
定义:[model.dever:1:1](model.dever#L1,1)
没有已收录的下游调用。
@@ -0,0 +1,4 @@
type Article {
title: Text(1, 200)
body: Text raw
}
@@ -0,0 +1,7 @@
# 就绪检查
提供公开 HTTP 就绪接口。
- [就绪接口](doc/api-admin-status.md)
- [业务操作](doc/app.md)
- [调用流程](link.md)
@@ -0,0 +1 @@
public get ready = app.status
@@ -0,0 +1,3 @@
status() (healthy: Bool) {
healthy = true
}
@@ -0,0 +1,5 @@
# 就绪接口
## system.health.api.admin.status.ready
公开就绪检查,返回 true。
@@ -0,0 +1,5 @@
# 业务操作
## system.health.status
返回 true,供就绪检查使用。
@@ -0,0 +1,122 @@
<!-- Generated by dever inspect --links. Do not edit. -->
<!-- source-sha256: bda5697651e3fc58a69923938754c00177ae31286d9b8d63f2bce96eb2a9620c -->
# system.health
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
## 入口
- API [system.health.api.admin.status.ready](api/admin/status.dever#L1) → [system.health.status](app.dever#L1)
## 外部调用方
当前加载的源码中没有已收录的外部调用。
## 能力与内部调用
### system.health.api.admin.status.ready
定义:[api/admin/status.dever:1:1](api/admin/status.dever#L1,1)
<a id="flow-b6734ecb2e8b0bd8-1"></a>
```mermaid
flowchart TD
n0(["system.health.api.admin.status.ready"])
click n0 href "api/admin/status.dever#L1,20" "打开源码"
n1{"路由 GET /system/health/admin/status/ready,topic #34;admin/status#34; 接受请求?"}
n2(["返回 HTTP 404"])
n3["进入 API"]
n4["取得认证时间"]
n5(["返回 HTTP 500"])
n6["按 topic #34;admin/status#34; 选择站点与 Provider"]
n7(["返回 HTTP 403"])
n8{"读取显式 Authorization;public 忽略自动 Cookie"}
n9(["返回 HTTP 401"])
n10["认证上下文就绪"]
n11["校验 Token 的签名、有效期及站点"]
n12(["返回 HTTP 401"])
n13{"按 Provider.verify 选择验证函数"}
n14[["access.session.verify"]]
click n14 href "api/admin/status.dever#L1,20" "打开源码"
n15(["返回 HTTP 业务错误映射状态;其他故障 500"])
n16["用验证结果建立身份"]
n17(["返回 HTTP 认证错误状态"])
n18(["返回 HTTP 500"])
n19["失败:保留 HTTP 错误状态"]
n20(["返回错误响应"])
n21["读取 API 输入"]
n22["解码具体参数并检查字段合同"]
n23[["system.health.status"]]
click n23 href "api/admin/status.dever#L1,20" "打开源码"
n24["按输出类型检查可序列化值并编码响应"]
n25["发布暂存的认证 Header / Cookie"]
n26(["返回成功响应"])
n0 --> n1
n1 -->|"未匹配此入口:继续路由;均未匹配"| n2
n1 --> n3
n3 --> n4
n4 -->|"失败"| n5
n4 --> n6
n6 -->|"无站点绑定"| n7
n6 --> n8
n8 -->|"凭据格式错误或冲突"| n9
n8 -->|"没有显式凭据:匿名"| n10
n8 -->|"有 Bearer"| n11
n11 -->|"无 Provider 或 Token 无效"| n12
n11 --> n13
n13 -->|"access.session.verify"| n14
n14 -->|"验证函数失败"| n15
n14 --> n16
n16 -->|"身份不合法"| n17
n16 -->|"通过"| n10
n13 -->|"无匹配验证函数"| n18
n19 --> n20
n10 --> n21
n21 -->|"失败:输入错误状态"| n19
n21 --> n22
n22 -->|"失败:400"| n19
n22 --> n23
n23 -->|"业务错误映射状态;其他故障:500"| n19
n23 --> n24
n24 -->|"编码失败:500"| n19
n24 --> n25
n25 --> n26
```
<details>
<summary>源码与子流程</summary>
| 节点 | 源码 | 子流程 |
| --- | --- | --- |
| access.session.verify | [api/admin/status.dever:1:20](api/admin/status.dever#L1,20) | [access.session.verify](../../access/session/link.md#flow-01943b8d6356f307-1) |
| system.health.status | [api/admin/status.dever:1:20](api/admin/status.dever#L1,20) | [system.health.status](link.md#flow-3f8f51687b91cbe2-1) |
</details>
| 目标 | 归属 | 方式 | 调用位置 |
| --- | --- | --- | --- |
| [access.session.verify](../../access/session/app.dever#L7) | 跨领域 [access.session](../../access/session/link.md) | 身份验证 | [api/admin/status.dever:1:20](api/admin/status.dever#L1,20) |
| [system.health.status](app.dever#L1) | 领域内 | API 绑定 | [api/admin/status.dever:1:20](api/admin/status.dever#L1,20) |
### system.health.status
定义:[app.dever:1:1](app.dever#L1,1)
<a id="flow-3f8f51687b91cbe2-1"></a>
```mermaid
flowchart TD
n0(["status"])
click n0 href "app.dever#L1,1" "打开源码"
n1(["返回"])
n3["赋值 healthy"]
click n3 href "app.dever#L2,13" "打开源码"
n0 --> n3
n3 --> n1
```
没有已收录的下游调用。
@@ -0,0 +1,3 @@
title() () {
assertEq(domain.normalize(" Draft "), "Draft")
}