Files
tamabox/tamabox.go
T
tamakyi 1e9ffc0718 feat(ops): 新增 TamaBox backup 命令,同时备份数据库与 R2 图片
此前没有任何备份手段,而站点的图片全在 Cloudflare R2、数据库只存 key/URL ——
只备份 DB 的话,恢复后 key 指向的图片若已不在 R2(bucket 误删 / 账号丢失 /
图片被清理),页面就全是裂图。

新增 TamaBox backup --output <dir>:
- 数据库:pg_dump 全量导出(schema + data,标准恢复格式),密码经 PGPASSWORD
  环境变量传递避免出现在 ps 进程参数里
- 图片:从 R2 下载 tamabox 站全部图片,保持原 key 路径,并写 images-manifest.json
  (key → size → md5)用于恢复后完整性校验
- --db-only / --images-only 分开关

R2 访问方式的坑:storage.GetS3Client 的 endpoint 带 /<bucket> 后缀,对
GetObject/PutObject 恰好可用,但 ListObjectsV2 会把 URL 拼成 /<bucket>/<bucket>
而 404 NoSuchKey。备份脚本独立构造客户端(剥离 endpoint 的 bucket 后缀 +
UsePathStyle),List prefix="tamabox/" 后用完整 key 下载 —— 这是 R2 上 List
唯一稳定可用的姿势。

实测:db-only 导出 46KB(21 表全含 schema_migrations);images-only 下载的文件
大小与 R2 List 返回完全一致,路径保持 tamabox/ 结构。
2026-10-29 19:27:13 +08:00

38 lines
693 B
Go

// Copyright 2022 E99p1ant. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main
import (
"os"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/tamakyi/TamaBox/internal/cmd"
)
func main() {
app := cli.NewApp()
app.Name = "TamaBox"
app.Description = "Anonymous question box"
app.Commands = []*cli.Command{
cmd.Web,
cmd.Censor,
cmd.Uid,
cmd.Ban,
cmd.Restore,
cmd.Reports,
cmd.Restrict,
cmd.Unrestrict,
cmd.HidePublic,
cmd.Honeypot,
cmd.Backup,
}
if err := app.Run(os.Args); err != nil {
logrus.WithError(err).Fatal("Failed to start application")
}
}