Bookshelf – 基于对象存储的自托管电子书库
Bookshelf – Self-hosted eBook library that runs on object storage

原始链接: https://github.com/murerkinn/bookshelf

Bookshelf 是一个自托管、轻量级的 EPUB 和 PDF 文件图书馆管理系统。它允许你通过单一的服务器渲染界面,直接在浏览器中整理、搜索、下载并阅读电子书。 该项目支持两种主要的部署方式: * **Docker/本地文件系统:** 作为 Node 服务器运行在类 Unix 系统上,非常适合家庭服务器或 VPS 设置。 * **Cloudflare Workers & R2:** 提供无服务器部署选项,实现全球访问。 **主要功能:** * **浏览器内阅读:** 内置专用的 EPUB 和 PDF 阅读器。 * **同步工具:** 自动生成可搜索的书架,处理封面图像生成,并管理图书馆状态。 * **可扩展性:** 采用模块化架构构建,支持自定义存储提供商。 * **无需身份验证:** 拥有 URL 的人皆可访问图书馆;个人资料仅用于追踪个人的阅读进度,而非限制访问。 入门指南:克隆仓库,将你的书籍放入 `books/` 目录,并按照提供的 npm 或 Docker 命令进行操作。系统提供演示模式,可填充公版书供测试使用。所有用户数据均包含在一个卷中,简化了备份流程。

Hacker News 上的讨论围绕 **Bookshelf** 的发布展开。这是一个专为极简架构设计的自托管电子书库,通过利用对象存储(如 Cloudflare R2)运行。作者强调,其目标是为个人用户提供一种低维护、高性价比的解决方案,而非托管服务。 社区讨论的主要亮点包括: * **自托管替代方案:** 用户讨论了多种管理个人媒体的成熟工具,包括 **BookOrbit**、**Kavita**、**Audiobookshelf**、**Grimmory** 和 **Copyparty**。 * **架构辩论:** 讨论探讨了“简单”对象存储实现与传统 SQLite/基于文件的托管之间的权衡。参与者还讨论了在自托管项目中使用专有云服务的利弊。 * **功能优先级:** 社区对实现电子阅读器集成的 OPDS 支持、跨设备阅读进度同步以及沉浸式“朗读”功能有着浓厚的兴趣。 * **安全顾虑:** 关于自托管库的客户端端到端加密问题引发了热议,一些用户主张通过加密来降低云存储和版权法规相关的法律风险。 Bookshelf 的作者表示有兴趣整合加密功能,并寻求社区反馈以指导未来的开发方向。
相关文章

原文

CI

A self-hosted library for the ebooks you already own. One server-rendered page lists them, filters them with a search box, serves downloads, and reads them in the browser — EPUB and PDF, each with its own reader — as a Cloudflare Worker over R2, or as a Node server over a directory on disk.

The shelf: a searchable list of books with covers, a profile switcher, and a Continue button on the book being read

Everything above is npm run demo — a generated shelf of public-domain titles, so the screenshots can be reproduced without finding books first.

Node 24 or newer, and a Unix-like system: the sync tool finds its image tools with which, so Windows is not supported. Covers come out better with cwebp and pdftoppm installed — see publishing, or use Docker, which ships both.

git clone https://github.com/murerkinn/bookshelf.git
cd bookshelf
npm install

Put some books — EPUB or PDF — in books/, then choose where the library should live.

The shortest path, and the image brings its own cwebp and pdftoppm so covers come out right without installing anything on the host.

mkdir books && cp ~/Downloads/*.epub books/
docker compose run --rm sync --create
docker compose up -d

The shelf is then on http://localhost:3000. Flags pass through, so docker compose run --rm sync --force and --dry-run behave as they do locally.

One named volume, library, holds the published books and everything the app writes to them — profiles and reading positions — so it is the only thing to back up.

The image is built for the filesystem provider: it is a Node server, and Cloudflare needs no container. It runs as a non-root user, and creates /data owned by that user so a named volume inherits an ownership the app can write to. If you would rather bind-mount a host directory, chown it first:

chown -R 1000:1000 /srv/bookshelf

On a machine you own, without Docker

No account anywhere. Point bookshelf.config.json at a directory, publish into it, and run the app:

npm run sync -- --create   # builds library/, then publishes it to shelf-data/
npm run build
npm start -w @bookshelf/app

library/ is the tree the sync tool builds; directory is where it publishes to, and it holds the books being served. Keep it out of the repository — shelf-data/ already is.

Needs a Cloudflare account and npx wrangler login. Two checked-in files carry this project's own bucket and Worker name and are meant to be edited — see the R2 provider.

npm run sync -- --create   # creates the bucket, then uploads to it
npm run deploy

The two must agree about which bucket holds the library, or the app will serve an empty shelf. They are checked against each other before anything uploads, and a mismatch is reported rather than published through.

No books to hand, or want something worth screenshotting? Nine generated public-domain titles — eight EPUBs and a PDF, so both readers are one click from the shelf — downloading nothing:

npm run demo                 # writes them into books/

Then publish and serve by whichever route above. The titles and authors are real works long out of copyright so a shelf of them looks like a shelf; the prose inside is placeholder. Note that the config in this repository points at R2, so npm run sync goes there unless you change it.

Anything reachable by strangers should refuse to be changed:

Storage keeps serving and stops accepting. Profiles cannot be added, renamed or deleted, and reading positions go back to living in the browser — the same degradation as a provider that cannot write, because to the app it is the same situation. Switching between existing profiles still works; that is a cookie, not a change.

It is enforced where the writing happens, not by hiding the forms, so posting the actions directly gets the same refusal.

There is no authentication. Anyone who can reach the app can read and download the whole library, so put it on a network you trust or behind something that asks who is calling. See Not done yet.

All of these run from the repository root; Turborepo builds whatever the task depends on first.

npm run dev          # local dev server, against the local R2 bucket
npm run sync         # build the library and upload it to the bucket
npm run build        # build every workspace
npm run check-types  # typecheck every workspace
npm run preview      # build + run the Worker locally
npm run deploy       # build + deploy to Cloudflare Workers
npm test             # the test suite
npm run lint         # biome, across the repo

npm run cf-typegen -w @bookshelf/app regenerates cloudflare-env.d.ts after editing wrangler.jsonc.

Publishing a library the sync tool, its flags, and covers
The library format what ends up in the bucket, and why it is regenerable
Storage providers the contract, and what the two shipped ones can each do
Cloudflare R2 configuration, deploying, publishing locally
Filesystem running it on your own machine or a VPS
Profiles who is reading, and where they got to
Reading in the browser how a chapter reaches the page
Architecture ports, adapters, and the composition root
The demo library how the public shelf is built, and how to rebuild it
  • There is no authentication. Anyone with the URL can read and download the whole library — and pick any profile while doing it. Profiles are a way to keep housemates' bookmarks apart, not a way to keep anyone out.
  • Two devices reading as one profile at the same time is last-write-wins.

See CONTRIBUTING.md. The short version: Node 24, npm install, and npm run lint, npm run check-types and npm test before you push. The tests reach the packages and the app's service layer but not its pages, so say what you ran as well.

Storage providers are the extension point and do not have to live here: a package published by anyone can be installed and named in the config.

MIT — see LICENSE.

That covers the code. It says nothing about the books you put in a library built with it, whose copyright is between you and their publishers.

联系我们 contact @ memedata.com