8个版本 (4个破坏性更新)

0.5.0 2023年10月28日
0.4.2 2023年9月24日
0.4.1 2023年6月19日
0.3.0 2023年6月18日
0.1.0 2023年6月15日

#741 in HTTP服务器

每月下载量 36

MIT 许可证

1.5MB
2K SLoC

包含(WOFF字体,150KB)static/webfonts/fa-solid-900.woff2,(WOFF字体,110KB)static/webfonts/fa-brands-400.woff2,(WOFF字体,25KB)static/webfonts/fa-regular-400.woff2,(WOFF字体,5KB)static/webfonts/fa-v4compatibility.woff2

Rustmark

Rustmark是用Rust编写的简单Markdown服务器。它旨在易于使用、易于分支和定制,以及易于部署。

Rustmark提供库和二进制功能,并且也被设计成可以克隆并用作可能扩展其功能的新项目的起点(有关更多信息,请参阅使用部分)。作为一个独立的二进制文件,它具有明确的价值和实用性,但在将其用作新项目的基础之前,能够运行并看到它工作也是有利的。

本README的主要部分包括

其他值得注意的文档包括

内容

读取Markdown文件的入口点是首页。如果您在这里阅读内容(例如,如果您正在使用Git系统预览Markdown,如GitHub或Gitea),请从这里开始。

提供了一个示例,展示了可用的 Markdown特性,以及一些使用指南

功能

需要注意的主要高级要点包括:

  • Markdown
    • 使用Comrak渲染Markdown文件
    • 使用Syntect进行语法高亮
    • 完全符合CommonMark 0.30
    • 支持GitHub风格的Markdown (GFM)
    • 表格、任务列表、删除线,以及自动链接(来自GFM)
    • 上标、脚注、描述列表和表情符号简码
    • 基于扩展引用的提示和细节块
  • 显示
    • 使用Bulma CSS框架的CSS基础
    • 使用Font Awesome图标
    • 使用Twemoji的Twitter表情符号
    • 使用Nerd Fonts显示代码
    • 可折叠的提示和细节块
    • 基于标题的可折叠文档部分(自动)
    • 可链接的标题
    • 导航菜单中自动的每页目录
  • 构建
    • 在构建时异步生成HTML并包含在二进制文件中
    • 高效的重建过程,仅重新生成已更改的文件
  • 自定义
    • 除了预编译的二进制文件外,还可以使用本地文件补充和覆盖Markdown内容、HTML模板和静态资产(可配置)
    • 用于自定义覆盖的自定义JS和CSS文件
    • 使用Tera模板引擎实现的模板
    • 使用Figment从配置文件和环境变量中配置
    • 简单的代码库布局
    • 易于扩展和构建
  • 安全性
    • 使用会话和基于配置的用户列表进行简单的身份验证
    • 登录页面、公开和受保护的路由、注销功能
    • 用于与Markdown内容一起使用的受保护静态内容文件
  • 性能
    • 使用Tokio Hyper的高性能异步HTTP服务器
    • 基于强大且易于使用的Axum网络框架
  • 其他
    • 与GitHub或Gitea等Git服务器浏览内容兼容
    • 静态文件处理
    • 单文件部署——所有资产都包含在内(可选且可配置)
    • 使用Tokio Tracing记录HTTP请求和事件
    • 健康检查API端点
    • 收集全面的应用程序统计信息和用于报告的API端点
    • 优雅地处理404和500 HTTP错误
    • 优雅地处理运行时应用程序错误
    • 完整的OpenAPI文档

有关支持的Markdown特性的更多详细信息,包括示例。

身份验证

Rustmark特性包含Terracotta的自定义身份验证系统,提供基于会话的基本设置。然而,强烈建议在数据库中安全地存储用户凭据。目前这超出了此项目的范围,主要原因是旨在提供一个可以扩展以使用所需任何数据库的简单系统。您可能还希望将会话存储在数据库中而不是内存中。

认证系统被配置为便于将路由配置为公开或受保护,包括登录页面、注销操作以及处理认证旅程的每个部分和可能的情况。

统计信息

Terracotta收集了关于应用程序的广泛统计数据,并提供了多个API端点来访问它们。这些端点旨在供监控系统使用,同时也提供了一种简单的方式来查看应用程序的运行情况。

尽管收集了一套全面的测量数据,但该方法性能优异,并设计为尽可能高效。它还易于扩展,可以向系统中添加新的统计数据。存在一个中心统计队列和广播系统,具有循环缓冲区用于间隔历史记录,以及一个滴答时钟以保持一切更新。

统计数据显示为汇总形式、每个测量的历史记录形式,以及实时WebSocket事件流。

数据库

Rustmark特意不包含任何类型的数据库集成。可选的数据库种类繁多,因此最好将其留作开放式。数据库交互非常直接,因此这是一个简单的附加功能。

为什么选择Rustmark?

目标是提供一个易于维护的系统,专注于开发者文档——至少,由那些熟悉使用Markdown和文本编辑器工作并在Git仓库中提交更改的人编写的文档。

有许多工具可以以非常友好的方式提供维基风格的功能,其中最领先的(可以说是)产品是NotionCoda也值得提及,但Confluence尽管因Jira的流行而广泛使用,但落后很多。

那么为什么不直接使用Notion呢?

Notion是一个出色的产品,具有一些优秀且强大的功能,并且使用起来也非常简单。然而,它没有任何审批流程,这意味着错误信息很容易悄悄进入——并且需要关注的页面越多,这个问题就越严重。虽然Notion确实有页面编辑历史记录和更改提醒,但无法要求在发布前对更改进行审批。锁定编辑访问并不是真正的解决方案,因为它会阻止最有可能需要做出更改的人进行更改,并将工作集中在少数几个特定人员身上。

因此,Rustmark的主要目的是提供一个简单易用的系统,用于发布面向开发者的文档,并具有通过标准Git pull请求管理简单审批流程。它并不打算替代Notion或任何其他维基风格系统,并且很可能最好同时使用两者,使用Notion进行通用文档和非技术人员需要编辑的页面。

此外,有些人/公司不喜欢将他们的信息存储在第三方系统中,而希望将其保留在内部。这也是为什么Rustmark是一个不错的选择,因为它允许完全控制内容、分发和访问。

如果它与Git服务器Markdown预览一起工作,为什么不能直接使用它呢?

大多数Git服务器系统都提供了Markdown预览功能,这对于有Git访问权限的人来说非常棒。但假如文档需要让那些无法访问Git服务器的人也能看到呢?虽然Rustmark面向开发者,但其特定关注点是编辑环境。它也需要让非开发者也能访问——此外,在Git服务器上浏览页面并不总是最友好的用户体验。

此外,这种方法在样式、展示、定制和托管方面提供了很大的灵活性。

那么GitHub Pages呢?

GitHub Pages 是托管静态内容的好方法,而且非常容易使用。然而,并不是每个人都使用或想要使用GitHub,而且免费账户的限制可能不适合某些人。还可以进行一定程度的定制,但由于其基于 Jekyll,所以无法实现动态内容。

为什么不使用Jekyll、Hugo或其他静态网站生成器呢?

有许多静态网站生成器可供选择,每种都有自己的优缺点。Ruby编写的 Jekyll 性能不是很好。Go编写的 Hugo 非常快,但定制起来并不容易。Gatsby 是用JavaScript编写的,非常可定制,但也很复杂,高度依赖React,并且需要很多依赖。它们只是其中一些最流行和广为人知的系统。mdBook 或许是一个接近的竞争者,基于Rust,但它仍然有重大差异。

每个系统都有一些关键决策,使其与其它系统区分开来。Rustmark的一个关键决策是使用Markdown文件作为内容的来源,同时也保持与一般Git服务器预览的兼容性。大多数常规静态网站生成器使用某种模板语言,而那些使用Markdown的则没有提供与Rustmark完全相同的关注点或功能。

使用Rustmark,可以轻松地在不同的仓库之间共享像README文件这样的东西,无需担心转换或兼容性问题,这对开发团队来说是一个优点。Rustmark也没有JavaScript依赖,实际上几乎没有JavaScript。

此外,还有写一些东西在Rust中的愿望!

为什么要以服务器方式运行?为什么不直接生成静态内容呢?

从Markdown文件生成静态内容是完全可能的,然后可以在静态Web服务器上托管该内容。如果这是必需的,则可以直接使用构建输出,实际上技术上不需要运行服务器。实际上,能够以更正式的方式完成这项工作可能会成为Rustmark的一个特性。

然而,有三种强有力的理由要以服务器方式运行:

  1. 它允许自我管理的身份验证,可以按照需要扩展,这是仅使用静态网站所无法做到的(而HTTP认证也并非理想)。
  2. 所有内容都打包在一个单独的二进制文件中,部署和运行都很方便。
  3. 它允许动态内容和功能,如搜索(目前尚未实现),这是静态网站无法实现的。Rustmark为构建更复杂的系统提供了一个不错的起点,如果需要的话。

此外,因为Rustmark内置了Web服务器,所以无需任何额外设置即可开始使用。只需运行二进制文件,它就会工作。当然,有些人可能希望将其运行在反向代理后面,这也是可能的。

所有内容都在一个二进制文件中,这不是一个限制因素吗?

不。如果您确实希望将所有内容编译成一个可分发文件,那么请这样做,因为它已经过测试,可以与包含超过10,000个Markdown文件的仓库兼容,这是550MB的Markdown文件,并且运行正常。很少有人会拥有如此庞大的仓库,如果他们真的有,他们可能还有更大的问题需要关注!

然而,如果您有一个大型的仓库,并且希望将二进制文件的大小保持在较低水平,那么您也可以这样做。您可以选择包含什么和动态加载什么(有关详细信息,请参阅配置 — 本地加载选项部分)。一个推荐的方法是将Markdown文件和HTML模板包含在二进制文件中,并从本地文件系统加载静态资源,如图像。

使用方法

开发者入门 开发者结构

Rustmark仓库被设计成可以分叉,并添加内容。因此,最好与现有的结构和预期用途保持一致,以便更容易合并和应用来自上游仓库的更新。这种方法提供了最大的定制潜力。

Rustmark还将其核心Markdown功能作为库提供,用于在其他项目中使用,而无需使用整个应用程序,以防您想要构建需要使用其扩展Markdown功能的某些内容。

它也可以作为一个独立的二进制文件使用,无需克隆整个仓库。这允许进行有限的定制(CSS样式、HTML模板、Markdown内容和静态资源,但不是核心逻辑),但对于许多用例来说已经足够,并且可以快速启动。如果您只想做这些,那么您将在这个文档中找到所有需要的内容,并且可以通过遵循设置部分来运行。但是,如果您想定制核心逻辑或扩展它,那么您应该参考[开发者文档][开发者入门]。

结构

Rustmark基于Terracotta,这是一个Web应用程序模板。本文档主要关注Rustmark,但如果您想了解更多关于底层应用程序结构的信息,您应该参考[开发者文档][开发者结构]。

Markdown文件应放置在content目录中,以及任何需要与渲染的Markdown页面相同认证的图像和其他文件。公共图像应放置在static/img路径,并从/img URL路径提供。

所有内容和静态资源都包含在编译的二进制文件中,使其部署非常简单。

自定义

如果需要任何自定义,它们应放置在js/custom.jscss/custom.css文件中。这些文件在默认CSS和JavaScript之后包含,因此可以用来覆盖默认行为。这些文件在从上游仓库更新时不会被修改。

请注意,custom.js文件仅包含在登录后渲染的Markdown页面中,不包括在如登录页面等通用系统页面中。而custom.css文件包含在所有页面中。

本文档主要关注如何使用和定制Rustmark,但如果您想进行更广泛的变化,您应该参考开发者文档

设置

开发者入门 [Rustmark]: https://crates.io/crates/rustmark

设置此项目的步骤简单且标准。您需要一个合理的 Rust 环境,在 Linux 机器上。目前没有特殊要求,只需构建标准 Rust 项目所需的内容。

请注意,这些说明是关于您自己构建应用程序的,通常是在创建了一个 [新的 Rustmark 项目][开发者入门] 后,通过克隆、分叉或可能使用 Rustmark 仓库作为模板。在这种情况下,这些步骤将适用于您的新项目。您也可以使用以下命令下载包:cargo install rustmark,这将从 [crates.io][Rustmark] 安装最新的 Rustmark 版本作为独立二进制文件。这是最快的方法,如果您只想快速启动并运行,而不需要自定义核心逻辑,则这是最佳选择。

环境

关于您选择的环境,有一些关键点需要注意

  • Debian 和 Ubuntu 是首选的 Linux 发行版,尽管其他发行版也应该运行良好,因为没有特殊要求。
  • 在 Windows 上本地运行不受目标或测试,也没有计划支持它,因此尽管它可能工作,也可能不工作。在 WSL 上运行是可行的,并且是 Windows 上的推荐运行方式。
  • 在 MacOS 上本地运行未经过测试,尽管没有已知的技術原因说明它不能工作。

通常,您可以使用 rustup 来设置 Rust,这是推荐安装 Rust 的方法。目标是 stable 工具链,因为重点是稳定性和正确性,而不是前沿功能。

安装 Rust 后,您可以使用 cargo build 命令来构建项目。这将下载并编译所有依赖项,并构建项目。然后,您可以使用 cargo run 命令运行项目。

配置

Rustmark 使用 TOML 文件进行配置。默认配置文件是 Config.toml,它应该放置在二进制文件相同的目录中。配置设置(和文件)是可选的,如果未提供,Rustmark 将使用所有配置选项的默认值。

还可以通过命令行参数以环境变量的形式传递配置参数。环境变量优先于配置文件选项。

一般选项

以下选项应指定无标题

  • host - 监听的宿主。默认为 127.0.0.1
  • port - 监听的端口。默认为 8000
  • logdir - 存储日志文件的目录。默认为 log
  • title - 应用程序标题。默认为 Rustmark

如上图所示

host   = "127.0.0.1"
port   = 8000
logdir = "log"
title  = "Rustmark"

本地加载选项

默认情况下,所有资源都嵌入到二进制文件中,并从那里提供。这是运行应用程序最有效的方式,但也可以从本地文件系统加载资源,这对于开发和测试以及有大型内容文件的情况非常有用。

可以补充或覆盖 Markdown 内容文件、HTML 模板和静态资源。静态资源分为受保护和公开两种。

出于性能原因,建议将 Markdown 文件烘焙到二进制文件中,因为如果它们在本地加载则不会被缓存,所以除非烘焙,否则每次请求都会解析。这在生产环境中比开发环境更重要,因为在开发环境中可能希望每次都重新解析。

以下选项应在 [local_loading] 标题下指定

  • html - HTML 模板的加载行为。
  • markdown - Markdown 内容的加载行为。
  • protected_assets - 受保护静态资产的加载行为。
  • public_assets - 公共静态资产的加载行为。

这些选项可以是以下值之一

  • Deny - 从本地文件系统拒绝加载。这是所有选项的默认值。
  • Supplement - 如果烘焙的资源不存在,则从本地文件系统加载。
  • Override - 如果本地文件系统中有,则从本地文件系统加载,否则从烘焙的资源加载。

如上图所示

[local_loading]
html             = "Deny"
markdown         = "Supplement" # default is "Deny"
protected_assets = "Override"   # default is "Deny"
public_assets    = "Override"   # default is "Deny"

对于允许从本地文件系统加载的选项,以下选项应在 [local_paths] 标题下指定

  • html - HTML 模板的路径。默认为 html
  • markdown - Markdown 内容的路径。默认为 content
  • protected_assets - 受保护静态资产的路径。默认为 content
  • public_assets - 公共静态资产的路径。默认为 static

如上图所示

[local_paths]
html             = "html"
markdown         = "content"
protected_assets = "content"
public_assets    = "static"

静态文件选项

当请求静态文件时,它们的服务方式取决于其来源和大小。所有烘焙到二进制中的文件都直接从内存中提供服务,因此这些选项不适用于它们。如果文件足够小,则从本地文件系统加载的文件将加载到内存中并一次性提供服务,但超过一定(可配置)大小后,它们将流式传输到客户端。

流缓冲区和读取缓冲区的大小对性能有很大影响,较小的缓冲区会极大地影响下载速度。默认值已在广泛的测试基础上仔细选择,通常无需更改。然而,在用户众多且大文件非常少的情况下,可能值得减小缓冲区大小以减少在请求这些文件时的内存使用,在用户很少且大文件很多的情况下,可能值得增加缓冲区大小以提高吞吐量。但是,所选的值已经非常接近最佳速度的 5-10%,因此任何增加都应谨慎进行。更有可能的情况是在非常繁忙且大文件众多的系统中,内存使用可能成为问题,而原始速度变得不那么重要。

以下选项应在 [static_files] 标题下指定

  • stream_threshold - 文件大小超过多少 KB 时才会流式传输到客户端。默认为 1000 (1MiB)。
  • stream_buffer - 用于流式传输文件时使用的流缓冲区大小,以 KB 为单位。默认为 256 (256KB)。
  • read_buffer - 用于流式传输文件时使用的读取缓冲区大小,以 KB 为单位。默认为 128 (128KB)。

每个这些选项都接受一个整数值。

如上图所示

[static_files]
stream_threshold = 1000 # 1MiB — files above this size will be streamed
stream_buffer    = 256  # 256KB
read_buffer      = 128  # 128KB

用户列表

可以在[users]标题下指定用户凭据列表

  • username: password - 以用户名为键,密码为值。

如上图所示

[users]
joe = "1a2b3c"

这是一个简单的用户名/密码对列表,其中用户名是键,密码是值。密码以纯文本形式存储,因此请留意其安全性(理想情况下,您应该实现与您首选数据库的集成)。用户名和密码都区分大小写。

运行

可以使用cargo run命令运行Rustmark,或者直接运行编译后的二进制文件。服务器默认监听8000端口,并从markdownstatic目录提供内容。markdown目录包含要渲染的Markdown文件,而static目录包含要提供的服务器静态文件。

请注意,如果您已使用cargo install rustmark安装了独立二进制文件,则需要使用rustmark而不是cargo run来运行它。

测试

可以使用cargo test运行测试套件。这将运行所有单元和集成测试。

请注意,目前为该项目编写的特定测试非常少,因为它主要是Rust生态系统中的其他crate的组合。现有的测试旨在作为方法的示例,并不详尽。在项目更加成熟且已明确识别出合理测试点时,可能会添加更多测试。

文档

您可以使用cargo doc构建开发者文档。这将生成HTML文件并将它们放置在target/doc中。然后您可以通过在浏览器中打开target/doc/rustmark/index.html来打开文档。

为本地开发构建文档还会为您提供源代码的链接。

部署

构建

您可以使用cargo build --release在发布模式下构建项目。所有部署所需的内容都将包含在生成的单个二进制文件中。建议在部署前对可执行文件运行upx,以减小文件大小。

您可以选择性地使用上面描述的本地加载选项部分中的文件从本地文件系统补充编译后的系统。

然后可以将生成的二进制文件复制到部署环境并直接运行。这通常是在Docker或Kubernetes容器中进行的。

示例

典型的构建脚本可能看起来像这样

cargo build --release
upx --best target/release/rustmark
scp target/release/rustmark you@yourserver:/path/to/deployment/directory

Docker

常见的部署方案是使用Docker。Rustmark 仓库包含一个Dockerfile,可用于构建 Docker 镜像。这个镜像基于Alpine,因此非常小巧。它还使用多阶段构建,因此最终镜像更小。

值得注意的是,Alpine 构建使用的是musl C 库,这与大多数其他 Linux 发行版和 Docker 镜像使用的glibc C 库不兼容。使用 Alpine 的优点是生成的镜像非常小巧,且所有内容都是静态编译的。如果您遇到任何兼容性问题,则可能需要使用基于glibcdistroless构建。

可以使用以下命令构建 Docker 镜像:

docker build -t rustmark .

默认情况下,这将构建一个发布镜像,并使用upx压缩二进制文件。设置针对可执行文件速度、构建速度和镜像大小进行了优化。

配置文件

您可以通过将--build-arg profile=dev选项传递给docker build命令来指定开发配置文件。这将构建一个未压缩的镜像,并针对构建速度进行了优化,但不是针对镜像大小。

构建参数

此外,还可以传递其他两个构建参数:

  • upx - 是否使用upx压缩二进制文件。默认为1。指定0以禁用压缩。
  • cargo-opts - 传递给cargo build的附加选项,例如--build-arg cargo_opts="--config opt-level=z"

运行

请注意,需要将主机 IP 设置为0.0.0.0以允许外部流量连接。换句话说,在 Docker 设置中,将Config.toml文件中的host条目设置为"0.0.0.0"

host   = "0.0.0.0"
port   = 8000

默认情况下,Rustmark 将在端口8000上运行,Dockerfile 预期如此。因此,建议在Config.toml文件中(或省略)保持此配置,并使用端口映射将容器端口映射到主机端口。这可以通过调用docker run命令时指定-p选项来实现,例如

docker run -p 8000:8000 rustmark

这将使 Rustmark 服务器在主机机器上的端口8000上可用,因此,您可以在该机器上通过浏览器访问https://127.0.0.1:8000http://127.0.0.1:8000

如果您在 Rustmark 上运行不同的端口,则需要在该 Dockerfile 中指定该端口。

可以将卷挂载到 Docker 容器中,以提供对本地文件的访问。这对于开发很有用,也可以用于提供额外内容和静态资源。以下卷可用:

  • /usr/src/html - HTML 模板。
  • /usr/src/content - Markdown 内容和受保护的静态资产。
  • /usr/src/static - 公共静态资产。

这些路径以及控制它们的选项可以通过上述描述的本地加载选项进行覆盖。

要挂载卷,请在调用 docker run 命令时使用 -v 选项,例如

docker run -v /path/to/markdown:/usr/src/content:ro rustmark

建议指定 ro(只读)选项,如上所示,因为没有理由 Rustmark 需要写入内容文件。

示例

默认构建,生成压缩的发布镜像

docker build -t rustmark .

默认构建,生成未压缩的发布镜像

docker build -t rustmark --build-arg upx=0 .

开发构建,生成未压缩的开发镜像

docker build -t rustmark --build-arg profile=dev .

调整发布构建的 opt-level

docker build -t rustmark --build-arg cargo_opts="--config opt-level=z" .

运行镜像

docker run rustmark

运行镜像并公开默认端口

docker run -p 8000:8000 rustmark

挂载卷

docker run \
  -v /path/to/markdown:/usr/src/content:ro \
  -v /path/to/templates:/usr/src/html:ro \
  -v /path/to/assets:/usr/src/static:ro \
  rustmark

免责声明

“Rustmark” 一词是“Rust”和“Markdown”的组合。它与 Rust 项目或 Rust 基金会没有关联,也没有暗示任何意图。

致谢

由于本项目是用 Rust 编写的,因此使用 Rust 标志 作为默认标志。该标志在 此条件下免费使用,根据 CC-BY (Creative Commons Attribution) 许可

使用螃蟹费里斯(Rust 的吉祥物)的图片来展示 Markdown 内容示例。此图片来源于 rustacean.net,属于 公共领域,因此可以自由使用。

本项目使用 Bulma CSS 框架,该框架在 此处发布,根据 MIT 许可,且无限制地免费使用。

Font Awesome 图标根据 此许可发布,根据 CC-BY (Creative Commons Attribution) 许可,以及网络字体根据 SIL OFL (Open Font License) 发布。它们可以自由使用,包括用于显示它们的 CSS 代码,该代码根据 MIT 许可发布。

用于美化 Unicode 表情符号的 Twemoji 图形由 Twitter 发布,根据 CC-BY (Creative Commons Attribution) 许可发布,并且可以自由使用,包括用于转换它们的 Twitter JavaScript 代码,该代码根据 MIT 许可发布。

依赖项

~38–53MB
~1M SLoC