面向读者的网站
apps/web-storefront读者访问的网站,以出版社自己的品牌对外发布。包括浏览、搜索、排行榜、关注、登录,以及翻页阅读的阅读器。
Publira 是一套多租户 SaaS,让 IT 资源有限的出版社也能以自有品牌运营漫画数字发行平台。编辑录入从作者那里收到的作品信息,读者在网页或手机上阅读。
仍在开发中
Publira 仍在积极开发中,最新版本 v0.3.2 也尚不适合用于生产环境。本页介绍的功能目前都能运行,也可以用每个版本发布的镜像和一个 Compose 文件完成部署;但要真正供出版社使用,仍需阅读代码、根据自己的环境进行调整,并跟进每个 0.x 次版本可能带来的变更。请把它视为可供评估、二次开发和参与贡献的项目,而不是成品。

Publira 本身是一个平台。开发过程中做出的部分组件已拆分为独立的库并公开发布,无需引入平台的其余部分即可使用。
出版社可以获得面向读者的网站和应用,以及供编辑使用的控制台;负责运营部署的人员还有一个统管全局的平台控制台。
每家出版社都以自己的品牌和域名运营。平台控制台 (不运行它的部署则使用 publiractl) 在此之上统一管理各租户、租户的读者,以及整个部署共享的设置。
作品、页面和公告都在同一个控制台中编辑,所有待发布的内容汇总在一个队列里。读者的咨询也在这里回复,每位工作人员只能看到其角色有权使用的操作。
颜色、字体、标志和图标可以一边编辑一边在实时预览中确认,预览与公开网站使用同一套组件构建。保存之前,网站会继续使用当前主题。
租户在网页端通过 Stripe 或 PAY.JP 销售章节,在应用内通过 App Store 和 Google Play 销售。购买不会在页面跳转时记录,而是在收到支付服务的签名通知后才记录,读者随后会收到购买凭证邮件。工作人员可以在控制台为网页端的购买办理退款;全额退款或发生争议时,该章节会恢复为未购买状态。
付费章节可以在免费阅读期内向所有读者开放。免费阅读期可以设置在单个章节上,也可以为一个系列的多个章节批量设置。系列还可以提供“等待即可免费”:已登录的读者使用等待一段时间后恢复的免费券,每次免费阅读一话付费章节。
读者可以用邮箱地址、网站和应用通用的通行密钥,或各租户登记的 Apple 和 Google 凭据登录网站和应用。租户的工作人员和平台运营人员可以开启两步验证 (工作人员也可以用通行密钥完成验证),平台还可以要求所有租户管理员和运营人员必须开启。
系列可以设为只在网站或只在应用中显示,章节还可以进一步限定;服务器会根据发起请求的一端返回相应的作品数据。章节的销售位置也以同样的方式设置,从租户默认值一直细化到单个章节。
每位署名作者都按一定比例获得章节销售额的分成。每个月可以手动结算,也可以在租户选定的日期自动结算并生成版税明细,已结算的明细可以下载为 CSV。关联了读者账户的作者无需购买即可阅读自己的章节,但不能为其评分。
R15 和 R18 作品在所有列表中都会标示,在读者确认之前保持隐藏,并根据账户登记的出生日期、按各租户选择的验证规则限制访问。这些作品另有独立的排行榜,只有有权查看的读者才能打开。
三个 Next.js 应用、一个 Flutter 客户端以及它们发送的邮件,各自面向不同的用户。
读者访问的网站,以出版社自己的品牌对外发布。包括浏览、搜索、排行榜、关注、登录,以及翻页阅读的阅读器。
编辑的工作台:作品目录和发布队列、支付和登录服务、公开网站的主题和页面、读者的咨询、读者的购买与退款、应付给每位作者的款项,以及对上述所有内容的变更记录。
位于各租户之上,供运营部署的人员使用:部署尚需完成的设置、租户状态、跨租户事件、所有租户的读者,以及从对象存储到安全策略等所有租户共享的设置。
在手机上浏览同一份作品目录,用一套 Flutter 代码同时支持 Android 和 iOS,并使用租户自己的名称和主题。应用直接调用公开 API,不经过网站;读者可以用邮箱地址、通行密钥、Apple 或 Google 登录,通过应用商店或网站结账购买章节,打开过或保存过的章节会保留在设备上。
账户相关邮件和工作人员收到的通知,由独立的渲染器根据 React 组件生成 HTML,并附带纯文本部分一起发送。发给租户读者和工作人员的邮件使用租户名称而非 Publira,并以租户的语言撰写;日语、韩语和中文里不同文字之间的间距,与网站一样交由邮件客户端的文字渲染处理。
前端是 Next.js,后端是通过 Connect RPC 提供服务的 Go 服务器及与之配套的 worker,再往下全是你现在就能运行的常见组件。
一个 publira server 进程承载公开、管理和平台三个命名空间,并为读者端和控制台两个源分发图片。也可以用 --role storefront 再启动一个进程,只让它承接租户的域名,并且只持有面向读者的数据库登录凭据。同一个二进制文件提供的 publira worker 负责投递邮件和推送通知,并运行所有定时任务:发布到期的章节、处理免费期的开始和结束、为每个租户切换日历日、重建排行榜、结算版税月份,以及清除超过保留期限的数据。部署所需的调度只靠它就够了;在单台主机上,也可以用 publira server --with-worker 将两者合并为一个进程运行。可选的 email-renderer 负责生成每封邮件的 HTML 部分,publiractl 负责数据库迁移、创建数据库角色和部署的初始设置,也可以手动运行 worker 的任意任务。
每一层都接入了 OpenTelemetry,一次浏览器请求就是一条完整的链路:Next.js 的根 span、SSR 期间发出的 Connect RPC、Go 端同名的 span,以及其下的 db.query 子 span。可以接入你现有的任何 collector,也可以不启用追踪 (默认关闭)。两个 Go 进程还会导出 OpenTelemetry 指标,同样需要启用后才会导出。
| 依赖项 | 用途 |
|---|---|
| 反向代理必需 | 将各租户的主机名路由到对应的网站和控制台,并将公开 API 和图片请求路由到服务器。仓库提供了 Traefik、nginx 和 Caddy 的配置。 |
| PostgreSQL必需 | 作品目录、租户、用户、购买记录和审计日志。每个服务器以各自的角色连接。 |
| Redis 兼容缓存必需 | Next.js 的共享缓存,使同一部署可以在负载均衡器后运行多个实例。 |
| S3 兼容对象存储必需 | 章节页面、封面图和其他上传内容。存储桶作为部署设置保存,而不是写在各进程的环境变量中。 |
| SMTP 中继必需 | 账户相关邮件、通知以及工作人员收到的通知,可分别按平台和租户配置。 |
| OpenSearch 或 Elasticsearch可选 | 供需要按相关度排序作品搜索结果的部署使用,每个节点都需要安装 analysis-kuromoji 和 analysis-icu 插件。不使用时,搜索由 PostgreSQL 完成。 |
| 支付服务可选 | 仅销售付费章节时需要,每个租户一个账户:网页端使用 Stripe 或 PAY.JP,应用使用 App Store 和 Google Play。购买以支付服务发来的签名通知为准。 |
| 邮件接收服务可选 | 仅当租户希望把读者对回复的回信记录到控制台中对应的咨询下方时需要。每个租户可使用 SendGrid、Resend、Mailgun、Amazon SES,或能通过 HTTPS 转发所收邮件的邮件服务器。 |
| Firebase 项目可选 | 每个租户应用一个。发往移动应用的推送通知通过 Firebase Cloud Messaging 发送,iOS 的通知再由其转发到 APNs。 |
| OpenTelemetry collector可选 | 接收 Next.js 应用和 Go 服务器的链路追踪,以及 Go 服务器的指标;默认关闭。 |
部署由容器镜像和一个 Compose 文件运行,publiractl 负责完成数据库和第一个租户的初始化。主机只需安装 Docker。
PUBLIRA_VERSION=0.3.2
curl -fsSLO \
https://github.com/publira/publira/releases/download/v$PUBLIRA_VERSION/publira-deploy-$PUBLIRA_VERSION.tar.gz
tar -xzf publira-deploy-$PUBLIRA_VERSION.tar.gz
cd publira-deploy-$PUBLIRA_VERSION
# Write .env from .env.example, with every secret generated
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD":/work -w /work \
ghcr.io/publira/publiractl:$PUBLIRA_VERSION env generate归档中包含该版本的 Compose 文件、代理用的 Caddyfile 和 .env.example。publiractl env generate 会据此生成 .env,并自动生成所有密钥。
每个版本都会以版本号为标签,将 linux/amd64 和 linux/arm64 镜像发布到 GitHub Container Registry,例如 ghcr.io/publira/publiractl:0.3.2。拉取镜像无需登录,因此主机上无需构建任何内容。
.env 中的 PUBLIRA_IMAGE_TAG 已设为该版本号,请勿修改。镜像必须与 .env 所在目录中的文件来自同一版本:publiractl 包含该版本的数据库迁移,各进程依赖该版本的数据库结构,代理则按归档中 Caddyfile 的路由运行。
如果修改了代码或在其他平台上运行,请从仓库检出代码自行构建镜像,并改为指定这些镜像。