Documentation
¶
Overview ¶
Package vault 管理一个文档根目录,是全程序唯一直接接触文件系统的单元。
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrConflict = errors.New("文件已被外部修改")
ErrConflict 表示磁盘上的内容在调用方读到它之后被改过。 出现它意味着「按调用方以为的原文去改」这个前提已经不成立,必须交还给人决定。
var ErrNotReadable = errors.New("路径不是可读的资源")
ErrNotReadable 表示路径在根内但不是一件可读的资源 (目录、设备文件、用途不允许的扩展名,或读取时发生 I/O 错误)。
存在这个哨兵是为了兜住「既不是越界、也不是不存在」的第三类情况。 没有它,这类错误只能原样透传 *fs.PathError,而那里面带着服务端的绝对路径—— 一个 ?path= 空串或 ?path=%00 就能把文档根的真实路径回显给调用方。
var ErrOutsideRoot = errors.New("路径超出文档根目录")
ErrOutsideRoot 表示请求的路径落在文档根目录之外。 返回给 HTTP 层时必须转成 400,且不得回显任何文件系统路径。
var ErrWriteFailed = errors.New("写入失败")
ErrWriteFailed 表示路径本身没问题——存在、是文档、可读——但落盘那一步 失败了,典型原因是磁盘满或文件系统只读挂载。
单独设一个哨兵,是为了不让它被 HTTP 层的 writeVaultError 归进「路径不可读」 那一类、映射成一句无关的 400「非法路径」。那恰恰是用户最需要被准确告知 发生了什么的场景——磁盘满了不该被当成传了个坏路径。
Functions ¶
Types ¶
type Event ¶
type Event struct {
Kind EventKind `json:"kind"`
Rel string `json:"rel"` // KindDocChanged 时为变更文件的相对路径
}
Event 是一次文件系统变更通知。
type Node ¶
type Node struct {
Name string `json:"name"`
Rel string `json:"rel"` // 相对文档根目录的路径,根节点为空串
IsDir bool `json:"isDir"`
Children []*Node `json:"children,omitempty"`
}
Node 是文件树中的一个节点。目录节点的 Children 按「目录在前、文件在后」排列, 两组各自按名称升序。
type ReadWriter ¶ added in v0.4.0
ReadWriter 同时具备读写能力,是 New 实际返回的类型。 调用方只需要读时,直接把它当 Vault 用即可。
func New ¶
func New(path string) (ReadWriter, error)
New 以 path 为文档根构造 ReadWriter。 path 可以是目录,也可以是单个 Markdown 文件(此时以其所在目录为根)。
返回 ReadWriter 而不是 Vault:读写由同一个实现承载,这样 resolve() 那道 路径防线只有一份,写入链路自动继承,不存在「写侧忘了校验」的可能。 只需要读的调用方直接当 Vault 用即可。
type Vault ¶
type Vault interface {
// Root 返回消解符号链接后的绝对根路径。
Root() string
// Tree 返回当前的文件树快照。没有任何文档时返回一个空的根节点。
Tree() *Node
// ReadDoc 读取一篇 Markdown 文档,扩展名不是 .md/.markdown 时返回 ErrNotReadable。
// 其余错误语义同 ReadAsset。
ReadDoc(rel string) ([]byte, error)
// ReadAsset 读取文档中引用的资源文件。
// 路径逃逸时返回 ErrOutsideRoot,文件不存在时返回 os.ErrNotExist,
// 路径在根内但不可读(目录、设备文件、点目录内的非资源文件,或 I/O 错误)时返回 ErrNotReadable。
ReadAsset(rel string) ([]byte, error)
// Watch 持续推送文件变更事件,直到 ctx 结束时关闭返回的 channel。
Watch(ctx context.Context) <-chan Event
}
Vault 管理一个文档根目录。
type Writer ¶ added in v0.4.0
type Writer interface {
// WriteDoc 把 content 写入一篇已存在的 Markdown 文档,返回新内容的版本号。
//
// base 是调用方读到原文时的版本号:与磁盘当前版本不一致时返回 ErrConflict
// 且不写入任何内容。base 为空串表示调用方明确要求强制覆盖。
//
// 错误语义与 ReadDoc 大体一致:越界 ErrOutsideRoot、不存在 os.ErrNotExist、
// 非 Markdown 或不可写 ErrNotReadable;写侧多一种 ReadDoc 没有的失败——
// 路径本身没问题但落盘失败(磁盘满、只读挂载),返回 ErrWriteFailed,
// 不与 ErrNotReadable 混在一起。
WriteDoc(rel string, content []byte, base string) (string, error)
}
Writer 是 Vault 的写侧。
与 Vault 分成两个接口,是为了让「能不能写」可以在类型和路由层面表达出来, 而不是退化成每个 HTTP handler 里的一次运行时判断——写端点会越来越多, 漏判一个就是一个无授权写入漏洞,而且功能测试全绿、毫无症状。