cover

使用容器部署 fava 实现跨平台记账

本文介绍了如何使用 fava 实现跨平台记帐。首先,文章介绍了如何配置和运行 fava,包括直接部署和容器化部署。接着,文章介绍了如何在 Nginx 中设置反向代理和基本身份验证,以确保安全。

2023/04/13

beancount 作为一个命令行记账工具,本身并不具备跨平台使用的能力。但是得益于其纯文本记账的特性,通过以下几种方式实现跨平台记账:

  1. 在多处记账然后定期汇总到一处进行处理;
  2. 将账本文件保存到 iCloud 等工具中;
  3. 将 fava 部署到公网上,直接在 fava 客户端中进行记账。

本文将介绍如何将 fava 部署到公网以实现跨平台记账的功能。

fava 是一个 beancount 的 web 客户端。相比于在命令行下使用 beancount 进行记账,fava 更为方便,满足了大多数人记账的需求。fava 具有以下功能特性:

  • 提供简单易用的 web 页面;
  • 支持按账户、标签和币种进行过滤和搜索交易记录;
  • 支持多种财务报表及图表可视化财务数据;
  • 支持导入导出 Beancount 文件;
  • 支持 beancount 的插件体系,并提供了其他的插件(如预算功能);
  • 多语言支持(包括中文)。

对于那些不善于使用命令行操作的用户,fava 的图形化操作界面大大提高了操作的简便性。此外,通过 fava,用户可以更清晰直观地查看记账报表。

fava 的基本用法如下:

pip install fava
fava <path/to/main.book>
# 使用浏览器访问 localhost:5000 

接下来介绍两种在服务器上部署 fava 服务的方法:

  1. 直接在服务器中使用 python 启动 fava 服务,并利用 systemd 管理 fava 应用服务。
  2. 容器化部署。

Python + Systemd 部署

如果服务器只是用来部署 fava 应用,那么使用 python 和 Systemd 部署是最简单的部署方式。

请使用以下的脚本进行部署:

# 确保系统安装了 Python
pip install fava

# 创建服务文件使 fava 服务能够开机自启动
sudo touch /etc/systemd/system/fava.service
sudo echo '[Unit]
Description=fava

[Service]
Type=simple
ExecStart=/usr/local/bin/fava <path/to/main.book>
RestartSec=10
StartLimitInterval=300
StartLimitBurst=10

[Install]
WantedBy=multi-user.target' > /etc/systemd/system/fava.service

sudo systemctl daemon-reload
sudo systemctl enable fava.service
sudo systemctl start fava.service

容器化部署

如果当前服务器上部署了多个应用,考虑到服务的可移植性以及简化服务管理流程,使用容器化方式部署 fava 会更好。

由于 fava 官方没有提供 Docker 镜像,因此需要手动构建,具体方法请参考下面的脚本。

# 下载最新源码
TAG=`wget --no-check-certificate -qO- -t1 -T2 "https://api.github.com/repos/beancount/fava/tags" | jq '.[0].name' | tr -d '"'`
wget https://ghproxy.com/https://github.com/beancount/fava/archive/refs/tags/${TAG}.tar.gz
tar Cxzvf ${TAG}.tar.gz && rm ${TAG}.tar.gz
# 构建 Docker 镜像
pushd fava-${TAG:1}/contrib/docker
  docker build -t fava .
popd
rm -rf fava-${TAG:1}

构建完成后,即可使用以下脚本启动容器:

docker run \
  --name fava \
  -p 5000:5000 \                             # 端口映射
  -v <path/to/books>:/data/books/ \          # 存放账本的路径挂载到容器中
  -e BEANCOUNT_FILE=/data/books/main.book \  # 通过环境变量设置主账本的路径
  fava 

反向代理和身份认证配置

不论使用哪种方法进行部署,在部署完成后都可以使用下面的脚本验证部署是否成功:

if `curl -s localhost:5000 | grep -q "/beancount/income_statement/"`; then echo "ok"; fi

如果输出为 "ok",则表示部署成功。

为了能够在公网进行访问,首先应该在服务器上开放 5000 端口。接着,可以在浏览器中通过 <ip>:5000 访问 fava 的 Web 客户端。

如果有自己的域名,则可以在 DNS 控制台中添加一条 A 记录,将其指向服务器的 IP 地址。然后添加如下 Nginx 配置:

server {
    server_name <domain>;
    listen      80;
    # 如果需要配置 SSL,则添加证书路径
    # listen              443 default ssl;
    # ssl_certificate       <path/to/crt>;
    # ssl_certificate_key <path/to/key>;

    add_header Strict-Transport-Security max-age=2592000;

    location / {
        # 添加基本身份认证
        # auth_basic            "Restricted Content";
        # auth_basic_user_file  /etc/nginx/.htpasswd;
        # 配置反向代理
        proxy_pass            http://127.0.0.1:5000;
        proxy_set_header      Host $host;
        proxy_set_header      X-Real-IP $remote_addr;
        proxy_set_header      X-Scheme $scheme;
        proxy_connect_timeout 1;
        proxy_send_timeout    30;
        proxy_read_timeout    30;
    }
}

由于 fava 没有内置身份认证的功能,因此需要额外配置身份认证功能,虽然 fava 官方文档提到了可以使用 oauth-proxy 实现认证,但是 oauth-proxy 内置的 Provider 在国内都无法正常访问。 如果只是个人使用的话,可以通过 Nginx 提供的 auth_basic 指令设置基础身份认证。 首先在 Nginx 配置文件所在的目录中执行以下命令用于生成访问密码:

sudo htpasswd -c /etc/nginx/.htpasswd <username>

注意:输入命令会提示你输入一个密码,Nginx 会使用 bcrypt 加密改密码并将其保存在 /etc/nginx/.htpasswd 文件中。如果想添加另一个用户,则可以省略 -c 标志。 然后在 nginx 的配置文件中移除 auth_basic 的注释。

一个完整的例子可以参考该 Repo