Hearth / 炉火:自托管的家庭照片与文件库

根据 Hearth 公开仓库,介绍这个用 Go API 与嵌入式 React 做成的家庭照片、视频与文件库:合集、回收站、收藏、分组与分享、主题,以及 FTS5 检索和 MCP。

- 次浏览

概述

Hearth(炉火)是一个自托管的家庭资料库。仓库主页上的一句话是:

Your family’s photos, videos, and files — warm, private, self-hosted.
家用照片、视频与文件归档:私密、自托管、开箱即用。

它把上传、浏览、家庭分组和整理放在同一套 HTTP API 上。服务端是 Go,Web 界面是 React,构建后嵌进同一个二进制。本仓库以服务端和 Web 为主;若另有 Android 客户端,README 说明它可以对接同一套 HTTP API,而不是放在这个仓库里。

下文依据 GitHub 上 main 分支的 README、许可证和公开代码,不补充仓库里没有写明的部署规模或使用人数。

程序怎么拼在一起

部分仓库中的位置作用
主进程cmd/hearthHTTP 服务,默认监听 :8080
MCPcmd/hearth-mcpstdio 服务,供 Cursor 等客户端调用同一套 API
Webweb/React 19 + Vite;web/ui.go 用 embed 装入 web/dist
存储internal/storeSQLite(modernc.org/sqlite),迁移用 goose
媒体internal/media按文件名和文件头分类,并包含 EXIF、缩略图相关代码

go.mod 写明模块名 hearth,Go 1.27。Makefile 里的 build / mcp 都使用 CGO_ENABLED=0。Docker 构建分两段:Node 24 打前端,Go 1.27 打二进制,最终镜像基于 Alpine 3.22,并安装 ffmpeg 与 vips-tools。docker-compose.yml 把数据放在命名卷 hearth-data,并用 GET /healthz 做健康检查。

GitHub Actions(.github/workflows/ci.yml)在推送和 pull request 上执行:安装 Go 1.27 与 Node 24、构建前端、go test ./...,再以 CGO_ENABLED=0 编译主程序。

文件、合集与权限

文件的 media_kind 只允许四类:image、video、audio、other。分类函数会看扩展名和文件头;只有音轨的 mp4 / m4a / webm 会归到音频,避免被当成视频打开。图片扩展名覆盖 jpeg、png、gif、webp、heic / heif。

可见性字段只有两种:private 与 shared。README 把 shared 解释为登录用户可见。除此之外,合集还可以授权给指定用户或用户组;管理员在检索等接口中可以越过普通成员限制。

合集最初有单一的 media_kind。后续迁移增加了 media_kinds,因此一个合集可以同时容纳多种类型。合集、用户组成员、合集成员和收藏都在同一组表里:collections、groups、group_members、collection_users、collection_groups、favorites。

误删走回收站:files.deleted_at 非空表示已删除,检索默认不返回这些行。收藏是用户与文件的一对记录,按用户和时间索引。

分享链接单独存放。shares 表记录文件、创建者和 expires_at。MCP 创建分享时,过期可选 never(默认)、7d 或 30d,对应 POST /api/files/{id}/shares。README 写明这是无需登录的预览链接。

示例配置里,单次上传上限为 2147483648 字节(2 GiB),会话默认 168 小时。真实的 hearth.toml 在 .gitignore 中,仓库只提供 config.example.toml。

主题

界面主题写在 web/src/styles.css。除默认配色外,还有五种具名主题:pine、dusk、sand、sea、sakura。sakura 使用浅粉背景和 #E891A8 强调色。App.jsx 按当前用户的 theme 字段设置 document.documentElement.dataset.theme。

检索

检索走 SQLite FTS5,分词器是 unicode61 remove_diacritics 2。

  • files_fts 索引文件名,以及该文件所属合集名
  • collections_fts 索引合集名
  • 插入、删除、改名时由触发器维护索引

GET /api/search?q= 由 Store.Search 实现。查询会先截断到 200 个 Unicode 字符,再按空白拆成词。每个词变成带前缀的 FTS 查询("词"*),词与词之间是 AND。代码注释说明,这样英文词干和中文前缀都能配合 unicode61 使用。

未指定排序时,文件和合集都按同一优先级:

  1. 名称精确匹配(不区分大小写)
  2. 名称前缀
  3. 名称中包含
  4. 其余命中

同级再按 bm25() 从低到高,然后才是创建时间或名称。已进回收站的文件不会出现。权限与列表一致:管理员、所有者、shared,或该用户能访问的合集。默认最多返回 200 个文件和 50 个合集。

测试 TestSearchFTSRankingAndPermissions 覆盖了精确名先于前缀、前缀先于包含,以及私有文件不会被其他普通用户搜到等情况。

MCP

make mcp 生成 bin/hearth-mcp。进程名是 hearth,实现版本字符串为 0.1.0。它通过环境变量连接已经在跑的 Hearth:

变量是否必需默认
HEARTH_BASE_URL否http://127.0.0.1:8080
HEARTH_USERNAME是—
HEARTH_PASSWORD是—

示例描述符在 mcp/hearth.mcp.json,其中的用户名和密码是占位符。当前注册的工具有:

  • hearth_whoami
  • hearth_list_files
  • hearth_search
  • hearth_list_collections
  • hearth_create_collection
  • hearth_list_groups
  • hearth_rename_group
  • hearth_file_metadata
  • hearth_set_favorite
  • hearth_create_share

在本机跑起来

README 要求 Go 1.27+;只有构建前端时才需要 Node.js 24。首次启动若还没有可用管理员,会用环境变量创建,之后再次启动不会改密码。

1
2
3
4
5
6
cp config.example.toml hearth.toml
export HEARTH_ADMIN_USER=admin
export HEARTH_ADMIN_PASSWORD='change-me-now'
make web
go test ./...
make run

make run 监听 :8080,浏览器打开 /login。前端开发时另开一个终端,在 web/ 里执行 npm run dev,把 /api 代理到 127.0.0.1:8080。也可以直接 docker compose up --build。

示例 systemd 单元在 deploy/hearth.service:从 /etc/hearth/hearth.toml 读配置,工作目录 /var/lib/hearth。

许可证与贡献

仓库使用 MIT 许可证,版权声明为 Copyright (c) 2026 Junxiao and contributors。README 为中英对照。贡献说明要求改动保持聚焦,用 go test ./... 验证,涉及检索时覆盖中英文文件名,并且不要提交 hearth.toml、真实密码、本机绝对路径、上传日志或 data/。

代码与说明见 github.com/rovinax/hearth。

评论

留言经审核后显示,欢迎留下想法。