Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,8 @@
|
||||
# 集合处理
|
||||
|
||||
筛选已发布文章,整理标题并计算总长度。
|
||||
|
||||
- [文章汇总命令](doc/api.md)
|
||||
- [业务操作](doc/app.md)
|
||||
- [内部规则](doc/domain.md)
|
||||
- [调用流程](link.md)
|
||||
@@ -0,0 +1 @@
|
||||
cmd summarize = app.summarize
|
||||
@@ -0,0 +1,18 @@
|
||||
type Article {
|
||||
title: Text
|
||||
published: Bool
|
||||
}
|
||||
|
||||
type Summary {
|
||||
titles: List<Text>
|
||||
title_length: Int
|
||||
}
|
||||
|
||||
summarize(articles: List<Article>) (summary: Summary) {
|
||||
published = filter(domain.isPublished, articles)
|
||||
titles = each(domain.publishedTitle, published)
|
||||
summary = Summary {
|
||||
titles = titles
|
||||
title_length = reduce(domain.addTitleLength, titles, 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# 文章汇总命令
|
||||
|
||||
## content.article.api.summarize
|
||||
|
||||
筛选已发布文章并统计标题。
|
||||
@@ -0,0 +1,13 @@
|
||||
# 业务操作
|
||||
|
||||
## content.article.Article
|
||||
|
||||
用于集合处理示例的文章标题和发布标记。
|
||||
|
||||
## content.article.Summary
|
||||
|
||||
筛选后的标题列表及标题总长度。
|
||||
|
||||
## content.article.summarize
|
||||
|
||||
选出已发布且标题非空白的文章,去掉标题两端空白,并累计标题长度。
|
||||
@@ -0,0 +1,13 @@
|
||||
# 内部规则
|
||||
|
||||
## content.article.domain.isPublished
|
||||
|
||||
检查文章已发布且标题不是空白。
|
||||
|
||||
## content.article.domain.publishedTitle
|
||||
|
||||
去掉文章标题两端空白。
|
||||
|
||||
## content.article.domain.addTitleLength
|
||||
|
||||
把当前标题的字符数加入累计值。
|
||||
@@ -0,0 +1,11 @@
|
||||
isPublished(article: app.Article) (selected: Bool) pure {
|
||||
selected = article.published and text.trim(article.title) != ""
|
||||
}
|
||||
|
||||
publishedTitle(article: app.Article) (title: Text) pure {
|
||||
title = text.trim(article.title)
|
||||
}
|
||||
|
||||
addTitleLength(title: Text, total: Int) (next: Int) pure {
|
||||
next = total + length(title)
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
<!-- Generated by dever inspect --links. Do not edit. -->
|
||||
<!-- source-sha256: 34a9b8f6199b0fb7fa26e50e29915dd0df8c9c5e142fa23a7f2a5fdd24572ef7 -->
|
||||
|
||||
# content.article
|
||||
|
||||
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
|
||||
|
||||
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
|
||||
|
||||
## 入口
|
||||
|
||||
- CMD [content.article.api.summarize](api.dever#L1) → [content.article.summarize](app.dever#L11)
|
||||
|
||||
## 外部调用方
|
||||
|
||||
当前加载的源码中没有已收录的外部调用。
|
||||
|
||||
## 能力与内部调用
|
||||
|
||||
### content.article.Article
|
||||
|
||||
定义:[app.dever:1:1](app.dever#L1,1)
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### content.article.Summary
|
||||
|
||||
定义:[app.dever:6:1](app.dever#L6,1)
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### content.article.api.summarize
|
||||
|
||||
定义:[api.dever:1:1](api.dever#L1,1)
|
||||
|
||||
<a id="flow-3c3fded4eed8bf7d-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["content.article.api.summarize"])
|
||||
click n0 href "api.dever#L1,17" "打开源码"
|
||||
n1[["content.article.summarize"]]
|
||||
click n1 href "api.dever#L1,17" "打开源码"
|
||||
n2(["返回命令结果;未捕获错误终止命令"])
|
||||
n0 --> n1
|
||||
n1 --> n2
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| content.article.summarize | [api.dever:1:17](api.dever#L1,17) | [content.article.summarize](link.md#flow-61067b9f346a3099-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [content.article.summarize](app.dever#L11) | 领域内 | CMD 绑定 | [api.dever:1:17](api.dever#L1,17) |
|
||||
|
||||
### content.article.domain.addTitleLength
|
||||
|
||||
定义:[domain.dever:9:1](domain.dever#L9,1)
|
||||
|
||||
<a id="flow-ae94676d0264c9fd-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["addTitleLength"])
|
||||
click n0 href "domain.dever#L9,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3["赋值 next"]
|
||||
click n3 href "domain.dever#L10,10" "打开源码"
|
||||
n0 --> n3
|
||||
n3 --> n1
|
||||
```
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### content.article.domain.isPublished
|
||||
|
||||
定义:[domain.dever:1:1](domain.dever#L1,1)
|
||||
|
||||
<a id="flow-424569accb37593c-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["isPublished"])
|
||||
click n0 href "domain.dever#L1,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3{"article.published"}
|
||||
click n3 href "domain.dever#L2,14" "打开源码"
|
||||
n4["求右侧值"]
|
||||
click n4 href "domain.dever#L2,36" "打开源码"
|
||||
n5["短路表达式结果"]
|
||||
click n5 href "domain.dever#L2,14" "打开源码"
|
||||
n6[["text.trim"]]
|
||||
click n6 href "domain.dever#L2,36" "打开源码"
|
||||
n7["赋值 selected"]
|
||||
click n7 href "domain.dever#L2,14" "打开源码"
|
||||
n0 --> n3
|
||||
n3 -->|"true"| n4
|
||||
n3 -->|"false:跳过右侧"| n5
|
||||
n4 --> n6
|
||||
n6 --> n5
|
||||
n5 --> n7
|
||||
n7 --> n1
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| text.trim | [domain.dever:2:36](domain.dever#L2,36) | 依赖或生成的分派(本页不展开) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| `text.trim` | 依赖 | 调用 | [domain.dever:2:36](domain.dever#L2,36) |
|
||||
|
||||
### content.article.domain.publishedTitle
|
||||
|
||||
定义:[domain.dever:5:1](domain.dever#L5,1)
|
||||
|
||||
<a id="flow-e1432830e4785e97-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["publishedTitle"])
|
||||
click n0 href "domain.dever#L5,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3[["text.trim"]]
|
||||
click n3 href "domain.dever#L6,11" "打开源码"
|
||||
n4["赋值 title"]
|
||||
click n4 href "domain.dever#L6,11" "打开源码"
|
||||
n0 --> n3
|
||||
n3 --> n4
|
||||
n4 --> n1
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| text.trim | [domain.dever:6:11](domain.dever#L6,11) | 依赖或生成的分派(本页不展开) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| `text.trim` | 依赖 | 调用 | [domain.dever:6:11](domain.dever#L6,11) |
|
||||
|
||||
### content.article.summarize
|
||||
|
||||
定义:[app.dever:11:1](app.dever#L11,1)
|
||||
|
||||
<a id="flow-61067b9f346a3099-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["summarize"])
|
||||
click n0 href "app.dever#L11,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3{"还有元素?"}
|
||||
click n3 href "app.dever#L12,42" "打开源码"
|
||||
n4["集合操作结果"]
|
||||
click n4 href "app.dever#L12,15" "打开源码"
|
||||
n5["取下一元素"]
|
||||
click n5 href "app.dever#L12,15" "打开源码"
|
||||
n6[["content.article.domain.isPublished"]]
|
||||
click n6 href "app.dever#L12,15" "打开源码"
|
||||
n7["赋值 published"]
|
||||
click n7 href "app.dever#L12,15" "打开源码"
|
||||
n8{"还有元素?"}
|
||||
click n8 href "app.dever#L13,40" "打开源码"
|
||||
n9["集合操作结果"]
|
||||
click n9 href "app.dever#L13,12" "打开源码"
|
||||
n10["取下一元素"]
|
||||
click n10 href "app.dever#L13,12" "打开源码"
|
||||
n11[["content.article.domain.publishedTitle"]]
|
||||
click n11 href "app.dever#L13,12" "打开源码"
|
||||
n12["赋值 titles"]
|
||||
click n12 href "app.dever#L13,12" "打开源码"
|
||||
n13{"还有元素?"}
|
||||
click n13 href "app.dever#L16,50" "打开源码"
|
||||
n14["集合操作结果"]
|
||||
click n14 href "app.dever#L16,20" "打开源码"
|
||||
n15["取下一元素"]
|
||||
click n15 href "app.dever#L16,20" "打开源码"
|
||||
n16[["content.article.domain.addTitleLength"]]
|
||||
click n16 href "app.dever#L16,20" "打开源码"
|
||||
n17["赋值 summary"]
|
||||
click n17 href "app.dever#L14,13" "打开源码"
|
||||
n0 -->|"开始遍历"| n3
|
||||
n3 -->|"否(包括空集合)"| n4
|
||||
n3 -->|"是"| n5
|
||||
n5 --> n6
|
||||
n6 -->|"下一元素"| n3
|
||||
n4 --> n7
|
||||
n7 -->|"开始遍历"| n8
|
||||
n8 -->|"否(包括空集合)"| n9
|
||||
n8 -->|"是"| n10
|
||||
n10 --> n11
|
||||
n11 -->|"下一元素"| n8
|
||||
n9 --> n12
|
||||
n12 -->|"开始遍历"| n13
|
||||
n13 -->|"否(包括空集合)"| n14
|
||||
n13 -->|"是"| n15
|
||||
n15 --> n16
|
||||
n16 -->|"下一元素"| n13
|
||||
n14 --> n17
|
||||
n17 --> n1
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| content.article.domain.isPublished | [app.dever:12:15](app.dever#L12,15) | [content.article.domain.isPublished](link.md#flow-424569accb37593c-1) |
|
||||
| content.article.domain.publishedTitle | [app.dever:13:12](app.dever#L13,12) | [content.article.domain.publishedTitle](link.md#flow-e1432830e4785e97-1) |
|
||||
| content.article.domain.addTitleLength | [app.dever:16:20](app.dever#L16,20) | [content.article.domain.addTitleLength](link.md#flow-ae94676d0264c9fd-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [content.article.domain.isPublished](domain.dever#L1) | 领域内 | 集合回调 | [app.dever:12:22](app.dever#L12,22) |
|
||||
| [content.article.domain.publishedTitle](domain.dever#L5) | 领域内 | 集合回调 | [app.dever:13:17](app.dever#L13,17) |
|
||||
| [content.article.domain.addTitleLength](domain.dever#L9) | 领域内 | 集合回调 | [app.dever:16:27](app.dever#L16,27) |
|
||||
@@ -0,0 +1,43 @@
|
||||
type State {
|
||||
Draft
|
||||
Published
|
||||
Rejected(reason: Text)
|
||||
}
|
||||
|
||||
clauses() () {
|
||||
assertEq(label(State.Draft), "draft")
|
||||
assertEq(label(State.Published), "published")
|
||||
assertEq(label(State.Rejected("needs revision")), "needs revision")
|
||||
assertEq(display(null), "missing")
|
||||
assertEq(display("present"), "present")
|
||||
assertEq(grade(59), "fail")
|
||||
assertEq(grade(60), "pass")
|
||||
}
|
||||
|
||||
label(state: State.Published) (text: Text) {
|
||||
text = "published"
|
||||
}
|
||||
|
||||
label(state: State.Rejected(reason)) (text: Text) {
|
||||
text = reason
|
||||
}
|
||||
|
||||
label(state: other) (text: Text) {
|
||||
text = "draft"
|
||||
}
|
||||
|
||||
display(value: Text) (text: Text) {
|
||||
text = value
|
||||
}
|
||||
|
||||
display(value: null) (text: Text) {
|
||||
text = "missing"
|
||||
}
|
||||
|
||||
grade(score: Int<60) (text: Text) {
|
||||
text = "fail"
|
||||
}
|
||||
|
||||
grade(score: Int >= 60) (text: Text) {
|
||||
text = "pass"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
summary() () {
|
||||
articles = [
|
||||
app.Article {
|
||||
title = " First "
|
||||
published = true
|
||||
},
|
||||
app.Article {
|
||||
title = "Hidden"
|
||||
published = false
|
||||
},
|
||||
app.Article {
|
||||
title = " "
|
||||
published = true
|
||||
},
|
||||
app.Article {
|
||||
title = "Second"
|
||||
published = true
|
||||
}
|
||||
]
|
||||
actual = app.summarize(articles)
|
||||
assertEq(actual.titles, ["First", "Second"])
|
||||
assertEq(actual.title_length, 11)
|
||||
each(assertCleanTitle, actual.titles)
|
||||
empty = app.summarize([])
|
||||
assertEq(empty.titles, [])
|
||||
assertEq(empty.title_length, 0)
|
||||
}
|
||||
|
||||
assertCleanTitle(title: Text) () {
|
||||
assert(title != "")
|
||||
assertEq(title, text.trim(title))
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 外部命令
|
||||
|
||||
通过 Port 调用工具并读取版本。
|
||||
|
||||
- [工具适配器](doc/adapter.md)
|
||||
- [工具版本命令](doc/api.md)
|
||||
- [业务操作](doc/app.md)
|
||||
- [外部接口](doc/port.md)
|
||||
- [调用流程](link.md)
|
||||
@@ -0,0 +1 @@
|
||||
external command "bin/tool" {}
|
||||
@@ -0,0 +1 @@
|
||||
cmd version = app.version
|
||||
@@ -0,0 +1,14 @@
|
||||
type Version {
|
||||
code: Int
|
||||
stdout: Text
|
||||
stderr: Text
|
||||
}
|
||||
|
||||
version() (version: Version) {
|
||||
output = port.execute(["--version"], null)
|
||||
version = Version {
|
||||
code = output.code
|
||||
stdout = dever.bytes.toText(output.stdout)
|
||||
stderr = dever.bytes.toText(output.stderr)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# 工具适配器
|
||||
|
||||
调用受管资源中的 `bin/tool`。程序参数由 Port 传入,不通过 shell 执行。
|
||||
@@ -0,0 +1,5 @@
|
||||
# 工具版本命令
|
||||
|
||||
## document.tool.api.version
|
||||
|
||||
运行工具的 --version 并返回退出码和输出。
|
||||
@@ -0,0 +1,9 @@
|
||||
# 业务操作
|
||||
|
||||
## document.tool.Version
|
||||
|
||||
外部命令的退出码及解码后的标准输出、错误输出。
|
||||
|
||||
## document.tool.version
|
||||
|
||||
执行工具的 --version,将输出按 UTF-8 解码;命令非零退出码仍作为结果返回。
|
||||
@@ -0,0 +1,5 @@
|
||||
# 外部接口
|
||||
|
||||
## document.tool.port.execute
|
||||
|
||||
执行指定工具并返回退出码和原始输出,stdin 可为空。
|
||||
@@ -0,0 +1,127 @@
|
||||
<!-- Generated by dever inspect --links. Do not edit. -->
|
||||
<!-- source-sha256: c3d45ae2a42beb7d0a9cdab16d131b25e36234ae4dce85b2b66c0086454ba10a -->
|
||||
|
||||
# document.tool
|
||||
|
||||
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
|
||||
|
||||
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
|
||||
|
||||
## 入口
|
||||
|
||||
- CMD [document.tool.api.version](api.dever#L1) → [document.tool.version](app.dever#L7)
|
||||
|
||||
## 外部调用方
|
||||
|
||||
当前加载的源码中没有已收录的外部调用。
|
||||
|
||||
## 能力与内部调用
|
||||
|
||||
### document.tool.Version
|
||||
|
||||
定义:[app.dever:1:1](app.dever#L1,1)
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### document.tool.adapter.execute
|
||||
|
||||
定义:[adapter.dever:1:1](adapter.dever#L1,1)
|
||||
|
||||
<a id="flow-82fa6bebec059055-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["execute"])
|
||||
click n0 href "adapter.dever#L1,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3[["外部命令边界:execute(受检参数、退出状态与输出)"]]
|
||||
click n3 href "adapter.dever#L1,1" "打开源码"
|
||||
n4(["声明的业务错误或 Worker 故障向调用方传播"])
|
||||
n0 --> n3
|
||||
n3 -->|"成功"| n1
|
||||
n3 -->|"失败"| n4
|
||||
```
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### document.tool.api.version
|
||||
|
||||
定义:[api.dever:1:1](api.dever#L1,1)
|
||||
|
||||
<a id="flow-437acd584ec674f9-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["document.tool.api.version"])
|
||||
click n0 href "api.dever#L1,15" "打开源码"
|
||||
n1[["document.tool.version"]]
|
||||
click n1 href "api.dever#L1,15" "打开源码"
|
||||
n2(["返回命令结果;未捕获错误终止命令"])
|
||||
n0 --> n1
|
||||
n1 --> n2
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| document.tool.version | [api.dever:1:15](api.dever#L1,15) | [document.tool.version](link.md#flow-d23463cc55069c99-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [document.tool.version](app.dever#L7) | 领域内 | CMD 绑定 | [api.dever:1:15](api.dever#L1,15) |
|
||||
|
||||
### document.tool.port.execute
|
||||
|
||||
定义:[port.dever:1:1](port.dever#L1,1)
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### document.tool.version
|
||||
|
||||
定义:[app.dever:7:1](app.dever#L7,1)
|
||||
|
||||
<a id="flow-d23463cc55069c99-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["version"])
|
||||
click n0 href "app.dever#L7,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3[["document.tool.adapter.execute"]]
|
||||
click n3 href "app.dever#L8,12" "打开源码"
|
||||
n4["赋值 output"]
|
||||
click n4 href "app.dever#L8,12" "打开源码"
|
||||
n5[["dever.bytes.toText"]]
|
||||
click n5 href "app.dever#L11,14" "打开源码"
|
||||
n6[["dever.bytes.toText"]]
|
||||
click n6 href "app.dever#L12,14" "打开源码"
|
||||
n7["赋值 version"]
|
||||
click n7 href "app.dever#L9,13" "打开源码"
|
||||
n0 --> n3
|
||||
n3 --> n4
|
||||
n4 --> n5
|
||||
n5 --> n6
|
||||
n6 --> n7
|
||||
n7 --> n1
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| document.tool.adapter.execute | [app.dever:8:12](app.dever#L8,12) | [document.tool.adapter.execute](link.md#flow-82fa6bebec059055-1) |
|
||||
| dever.bytes.toText | [app.dever:11:14](app.dever#L11,14) | 依赖或生成的分派(本页不展开) |
|
||||
| dever.bytes.toText | [app.dever:12:14](app.dever#L12,14) | 依赖或生成的分派(本页不展开) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [document.tool.adapter.execute](adapter.dever#L1) | 领域内 | 调用 | [app.dever:8:12](app.dever#L8,12) |
|
||||
| `dever.bytes.toText` | 依赖 | 调用 | [app.dever:11:14](app.dever#L11,14) |
|
||||
| `dever.bytes.toText` | 依赖 | 调用 | [app.dever:12:14](app.dever#L12,14) |
|
||||
@@ -0,0 +1 @@
|
||||
execute(args: List<Text>, stdin: Bytes?) (output: dever.process.Output) fails dever.process.Error
|
||||
@@ -0,0 +1,16 @@
|
||||
version() () {
|
||||
output = app.version()
|
||||
assertEq(output.code, 0)
|
||||
assertEq(output.stdout, "tool 1.0\n")
|
||||
assertEq(output.stderr, "")
|
||||
}
|
||||
|
||||
port.execute(args: List<Text>, stdin: Bytes?) (output: dever.process.Output) {
|
||||
assertEq(args, ["--version"])
|
||||
assertEq(stdin, null)
|
||||
output = dever.process.Output {
|
||||
code = 0
|
||||
stdout = dever.bytes.fromText("tool 1.0\n")
|
||||
stderr = dever.bytes.fromText("")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"lib": ["pip:markdown@3.8.2"],
|
||||
"log": { "level": "info" }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Markdown 渲染
|
||||
|
||||
用外部 Markdown 库实现渲染,通过 fake 测试成功与拒绝。
|
||||
|
||||
- [Markdown 适配器](doc/adapter.md)
|
||||
- [Markdown 命令](doc/api.md)
|
||||
- [业务操作](doc/app.md)
|
||||
- [外部接口](doc/port.md)
|
||||
- [调用流程](link.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
external pip "python/render.py" {
|
||||
lib "markdown@3.8.2"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
cmd render = app.render
|
||||
@@ -0,0 +1,8 @@
|
||||
type RenderResult {
|
||||
Rendered(html: Text)
|
||||
error Rejected(message: Text)
|
||||
}
|
||||
|
||||
render(source: Text) (html: Text) {
|
||||
html = port.render(source)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Markdown 适配器
|
||||
|
||||
使用 `python/render.py` 和锁定的 Markdown 库实现渲染。第三方库与 Worker 资源须先准备,测试中的 fake 不依赖它们。
|
||||
@@ -0,0 +1,5 @@
|
||||
# Markdown 命令
|
||||
|
||||
## document.markup.api.render
|
||||
|
||||
调用外部组件把 Markdown 转为 HTML。
|
||||
@@ -0,0 +1,9 @@
|
||||
# 业务操作
|
||||
|
||||
## document.markup.RenderResult
|
||||
|
||||
Markdown 渲染成功或业务拒绝;拒绝包含原因。
|
||||
|
||||
## document.markup.render
|
||||
|
||||
调用外部渲染器把 Markdown 转为 HTML;渲染器可拒绝输入。
|
||||
@@ -0,0 +1,5 @@
|
||||
# 外部接口
|
||||
|
||||
## document.markup.port.render
|
||||
|
||||
外部 Markdown 渲染操作,失败类型为 RenderResult。
|
||||
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
)
|
||||
|
||||
func Render(ctx context.Context, payload any, setting any) (any, error) {
|
||||
var output bytes.Buffer
|
||||
source := payload.(map[string]any)["source"].(string)
|
||||
if err := goldmark.Convert([]byte(source), &output); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"html": output.String()}, nil
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { marked } from 'marked';
|
||||
|
||||
export async function render(payload, setting, signal) {
|
||||
return { html: await marked.parse(payload.source) };
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<!-- Generated by dever inspect --links. Do not edit. -->
|
||||
<!-- source-sha256: 43bec2fc499db07b0488aa4e6d1775a75b4d28f1a575072696eba0c215ae34a6 -->
|
||||
|
||||
# document.markup
|
||||
|
||||
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
|
||||
|
||||
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
|
||||
|
||||
## 入口
|
||||
|
||||
- CMD [document.markup.api.render](api.dever#L1) → [document.markup.render](app.dever#L6)
|
||||
|
||||
## 外部调用方
|
||||
|
||||
当前加载的源码中没有已收录的外部调用。
|
||||
|
||||
## 能力与内部调用
|
||||
|
||||
### document.markup.RenderResult
|
||||
|
||||
定义:[app.dever:1:1](app.dever#L1,1)
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### document.markup.adapter.render
|
||||
|
||||
定义:[adapter.dever:1:1](adapter.dever#L1,1)
|
||||
|
||||
<a id="flow-97bf2a6af81a831b-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["render"])
|
||||
click n0 href "adapter.dever#L1,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3[["外部 pip Worker 边界:render(受检协议与结果)"]]
|
||||
click n3 href "adapter.dever#L1,1" "打开源码"
|
||||
n4(["声明的业务错误或 Worker 故障向调用方传播"])
|
||||
n0 --> n3
|
||||
n3 -->|"成功"| n1
|
||||
n3 -->|"失败"| n4
|
||||
```
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### document.markup.api.render
|
||||
|
||||
定义:[api.dever:1:1](api.dever#L1,1)
|
||||
|
||||
<a id="flow-b479961e7bdb93bd-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["document.markup.api.render"])
|
||||
click n0 href "api.dever#L1,14" "打开源码"
|
||||
n1[["document.markup.render"]]
|
||||
click n1 href "api.dever#L1,14" "打开源码"
|
||||
n2(["返回命令结果;未捕获错误终止命令"])
|
||||
n0 --> n1
|
||||
n1 --> n2
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| document.markup.render | [api.dever:1:14](api.dever#L1,14) | [document.markup.render](link.md#flow-1e0e90ddee897072-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [document.markup.render](app.dever#L6) | 领域内 | CMD 绑定 | [api.dever:1:14](api.dever#L1,14) |
|
||||
|
||||
### document.markup.port.render
|
||||
|
||||
定义:[port.dever:1:1](port.dever#L1,1)
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### document.markup.render
|
||||
|
||||
定义:[app.dever:6:1](app.dever#L6,1)
|
||||
|
||||
<a id="flow-1e0e90ddee897072-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["render"])
|
||||
click n0 href "app.dever#L6,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3[["document.markup.adapter.render"]]
|
||||
click n3 href "app.dever#L7,10" "打开源码"
|
||||
n4["赋值 html"]
|
||||
click n4 href "app.dever#L7,10" "打开源码"
|
||||
n0 --> n3
|
||||
n3 --> n4
|
||||
n4 --> n1
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| document.markup.adapter.render | [app.dever:7:10](app.dever#L7,10) | [document.markup.adapter.render](link.md#flow-97bf2a6af81a831b-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [document.markup.adapter.render](adapter.dever#L1) | 领域内 | 调用 | [app.dever:7:10](app.dever#L7,10) |
|
||||
@@ -0,0 +1 @@
|
||||
render(source: Text) (html: Text) fails app.RenderResult
|
||||
@@ -0,0 +1,5 @@
|
||||
import markdown
|
||||
|
||||
|
||||
async def render(payload, setting):
|
||||
return {"html": markdown.markdown(payload["source"])}
|
||||
@@ -0,0 +1,16 @@
|
||||
rejected() () {
|
||||
verify(result(app.render("rejected input")))
|
||||
}
|
||||
|
||||
port.render(source: Text) (html: Text) {
|
||||
assertEq(source, "rejected input")
|
||||
fail(app.RenderResult.Rejected("invalid document"))
|
||||
}
|
||||
|
||||
verify(value: app.RenderResult.Rejected(message)) () recover("test verifies declared rejection") {
|
||||
assertEq(message, "invalid document")
|
||||
}
|
||||
|
||||
verify(value: app.RenderResult.Rendered(html)) () recover("test verifies declared rejection") {
|
||||
assert(false)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
rendered() () {
|
||||
assertEq(app.render("# Report"), "<h1>Report</h1>")
|
||||
}
|
||||
|
||||
port.render(source: Text) (html: Text) {
|
||||
assertEq(source, "# Report")
|
||||
html = "<h1>Report</h1>"
|
||||
}
|
||||
@@ -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 @@
|
||||
rest model
|
||||
@@ -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")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"database": { "default": { "type": "sqlite", "path": "data/jobs.db" } },
|
||||
"runtime": { "mode": "worker", "shutdown_ms": 30000 },
|
||||
"job": {
|
||||
"workers": 1,
|
||||
"poll_ms": 1000,
|
||||
"lease_ms": 60000,
|
||||
"retry_base_ms": 1000,
|
||||
"retry_max_ms": 300000
|
||||
},
|
||||
"log": { "level": "info" }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 归档任务
|
||||
|
||||
按请求键入队任务并保存归档记录。
|
||||
|
||||
- [归档命令](doc/api.md)
|
||||
- [业务操作](doc/app.md)
|
||||
- [任务](doc/job.md)
|
||||
- [数据结构](doc/model.md)
|
||||
- [调用流程](link.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
cmd submit = app.submit
|
||||
|
||||
cmd total = app.total
|
||||
@@ -0,0 +1,21 @@
|
||||
type Request {
|
||||
key: Text
|
||||
title: Text
|
||||
}
|
||||
|
||||
submit(input: Request) (id: Id) {
|
||||
id = dever.job.enqueue(job.store, input, input.key)
|
||||
}
|
||||
|
||||
store(input: Request) () {
|
||||
stored = model.create(
|
||||
{
|
||||
request_key = input.key
|
||||
title = input.title
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
total() (count: Int) {
|
||||
count = model.count()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 归档命令
|
||||
|
||||
## report.archive.api.submit
|
||||
|
||||
使用请求键入队归档任务。
|
||||
|
||||
## report.archive.api.total
|
||||
|
||||
读取归档记录数。
|
||||
@@ -0,0 +1,17 @@
|
||||
# 业务操作
|
||||
|
||||
## report.archive.Request
|
||||
|
||||
归档请求的幂等键和标题。
|
||||
|
||||
## report.archive.submit
|
||||
|
||||
使用 input.key 入队归档任务,返回任务编号;相同键不会重复入队。
|
||||
|
||||
## report.archive.store
|
||||
|
||||
保存请求键和标题,供归档 Job 调用。
|
||||
|
||||
## report.archive.total
|
||||
|
||||
返回已保存的归档记录数。
|
||||
@@ -0,0 +1,5 @@
|
||||
# 任务
|
||||
|
||||
## report.archive.job.store
|
||||
|
||||
执行归档,最多执行 3 次(含首次),单次期限 30 秒。
|
||||
@@ -0,0 +1,5 @@
|
||||
# 数据结构
|
||||
|
||||
## report.archive.model.Archive
|
||||
|
||||
归档记录,请求键唯一。
|
||||
@@ -0,0 +1,5 @@
|
||||
database default
|
||||
|
||||
job store(input: app.Request) () retry(3) timeout(30000) {
|
||||
app.store(input)
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<!-- Generated by dever inspect --links. Do not edit. -->
|
||||
<!-- source-sha256: eb439b9c77c2bf6e0d55e26f5ad1b2a024bc2783baebddb3756d892621724e0c -->
|
||||
|
||||
# report.archive
|
||||
|
||||
按求值顺序展示调用、分支和任务调度,包含 API 与自动 REST 入口。点击节点打开源码;双框为调用,虚线为调度或错误路径。外部库和驱动不展开。
|
||||
|
||||
图不是某次运行的日志,分句和 Adapter 按输入或配置选择。修改源码后运行 `dever inspect . --links`。
|
||||
|
||||
## 入口
|
||||
|
||||
- CMD [report.archive.api.submit](api.dever#L1) → [report.archive.submit](app.dever#L6)
|
||||
- CMD [report.archive.api.total](api.dever#L3) → [report.archive.total](app.dever#L19)
|
||||
- Job [report.archive.job.store](job.dever#L3)
|
||||
|
||||
## 外部调用方
|
||||
|
||||
当前加载的源码中没有已收录的外部调用。
|
||||
|
||||
## 能力与内部调用
|
||||
|
||||
### report.archive.Request
|
||||
|
||||
定义:[app.dever:1:1](app.dever#L1,1)
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### report.archive.api.submit
|
||||
|
||||
定义:[api.dever:1:1](api.dever#L1,1)
|
||||
|
||||
<a id="flow-e67830c6d0f7d77d-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["report.archive.api.submit"])
|
||||
click n0 href "api.dever#L1,14" "打开源码"
|
||||
n1[["report.archive.submit"]]
|
||||
click n1 href "api.dever#L1,14" "打开源码"
|
||||
n2(["返回命令结果;未捕获错误终止命令"])
|
||||
n0 --> n1
|
||||
n1 --> n2
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| report.archive.submit | [api.dever:1:14](api.dever#L1,14) | [report.archive.submit](link.md#flow-efc65eed6c9089ab-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [report.archive.submit](app.dever#L6) | 领域内 | CMD 绑定 | [api.dever:1:14](api.dever#L1,14) |
|
||||
|
||||
### report.archive.api.total
|
||||
|
||||
定义:[api.dever:3:1](api.dever#L3,1)
|
||||
|
||||
<a id="flow-72cad6bf19cb8c40-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["report.archive.api.total"])
|
||||
click n0 href "api.dever#L3,13" "打开源码"
|
||||
n1[["report.archive.total"]]
|
||||
click n1 href "api.dever#L3,13" "打开源码"
|
||||
n2(["返回命令结果;未捕获错误终止命令"])
|
||||
n0 --> n1
|
||||
n1 --> n2
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| report.archive.total | [api.dever:3:13](api.dever#L3,13) | [report.archive.total](link.md#flow-b00d0e9bba4b635b-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [report.archive.total](app.dever#L19) | 领域内 | CMD 绑定 | [api.dever:3:13](api.dever#L3,13) |
|
||||
|
||||
### report.archive.job.store
|
||||
|
||||
定义:[job.dever:3:1](job.dever#L3,1)
|
||||
|
||||
<a id="flow-3208caa5c17a4976-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["store"])
|
||||
click n0 href "job.dever#L3,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3[["report.archive.store"]]
|
||||
click n3 href "job.dever#L4,3" "打开源码"
|
||||
n0 --> n3
|
||||
n3 --> n1
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| report.archive.store | [job.dever:4:3](job.dever#L4,3) | [report.archive.store](link.md#flow-73a6995d7ee43f96-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [report.archive.store](app.dever#L10) | 领域内 | 调用 | [job.dever:4:3](job.dever#L4,3) |
|
||||
|
||||
### report.archive.model.Archive
|
||||
|
||||
定义:[model.dever:1:1](model.dever#L1,1)
|
||||
|
||||
没有已收录的下游调用。
|
||||
|
||||
### report.archive.store
|
||||
|
||||
定义:[app.dever:10:1](app.dever#L10,1)
|
||||
|
||||
<a id="flow-73a6995d7ee43f96-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["store"])
|
||||
click n0 href "app.dever#L10,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3["report.archive.model.create"]
|
||||
click n3 href "app.dever#L11,12" "打开源码"
|
||||
n4["赋值 stored"]
|
||||
click n4 href "app.dever#L11,12" "打开源码"
|
||||
n0 --> n3
|
||||
n3 --> n4
|
||||
n4 --> n1
|
||||
```
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [report.archive.model.create](model.dever#L1) | 领域内 | 内建操作 | [app.dever:11:12](app.dever#L11,12) |
|
||||
|
||||
### report.archive.submit
|
||||
|
||||
定义:[app.dever:6:1](app.dever#L6,1)
|
||||
|
||||
<a id="flow-efc65eed6c9089ab-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["submit"])
|
||||
click n0 href "app.dever#L6,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3["持久化任务入队"]
|
||||
click n3 href "app.dever#L7,8" "打开源码"
|
||||
n4[["Job:report.archive.job.store"]]
|
||||
click n4 href "app.dever#L7,8" "打开源码"
|
||||
n5["赋值 id"]
|
||||
click n5 href "app.dever#L7,8" "打开源码"
|
||||
n0 --> n3
|
||||
n3 -.->|"由 Worker 后续调度,不等待执行"| n4
|
||||
n3 --> n5
|
||||
n5 --> n1
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>源码与子流程</summary>
|
||||
|
||||
| 节点 | 源码 | 子流程 |
|
||||
| --- | --- | --- |
|
||||
| Job:report.archive.job.store | [app.dever:7:8](app.dever#L7,8) | [report.archive.job.store](link.md#flow-3208caa5c17a4976-1) |
|
||||
|
||||
</details>
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [report.archive.job.store](job.dever#L3) | 领域内 | 入队 | [app.dever:7:26](app.dever#L7,26) |
|
||||
|
||||
### report.archive.total
|
||||
|
||||
定义:[app.dever:19:1](app.dever#L19,1)
|
||||
|
||||
<a id="flow-b00d0e9bba4b635b-1"></a>
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
n0(["total"])
|
||||
click n0 href "app.dever#L19,1" "打开源码"
|
||||
n1(["返回"])
|
||||
n3["report.archive.model.count"]
|
||||
click n3 href "app.dever#L20,11" "打开源码"
|
||||
n4["赋值 count"]
|
||||
click n4 href "app.dever#L20,11" "打开源码"
|
||||
n0 --> n3
|
||||
n3 --> n4
|
||||
n4 --> n1
|
||||
```
|
||||
|
||||
| 目标 | 归属 | 方式 | 调用位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| [report.archive.model.count](model.dever#L1) | 领域内 | 内建操作 | [app.dever:20:11](app.dever#L20,11) |
|
||||
@@ -0,0 +1,4 @@
|
||||
type Archive {
|
||||
request_key: Text unique
|
||||
title: Text
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
queued() () {
|
||||
request = app.Request {
|
||||
key = "test-report"
|
||||
title = "Report"
|
||||
}
|
||||
first_id = app.submit(request)
|
||||
assertEq(app.submit(request), first_id)
|
||||
assertEq(dever.test.drainJobs(5), 1)
|
||||
assertEq(app.total(), 1)
|
||||
dever.test.advanceClock(1000)
|
||||
assertEq(dever.test.drainJobs(5), 0)
|
||||
}
|
||||
Reference in new issue
Block a user