Align with engineering standards (steps 1-5)
- Rename dist/ -> deploy/ with subdirs examples/, scripts/, systemd/ per standard repository layout - Update .gitignore: gitignore all of dist/ (build output only) - Makefile: all target is now vet->lint->test->build; add vet, proto-lint, devserver targets; CGO_ENABLED=0 for builds (modernc.org/sqlite is pure-Go, no C toolchain needed); CGO_ENABLED=1 retained for tests (race detector) - Dockerfile: builder -> golang:1.26-alpine, runtime -> alpine:3.21; drop libc6 dep; add /srv/mcias/certs and /srv/mcias/backups to image - deploy/systemd/mcias.service: add RestrictSUIDSGID=true - deploy/systemd/mcias-backup.service: new oneshot backup unit - deploy/systemd/mcias-backup.timer: daily 02:00 UTC, 5m jitter - deploy/scripts/install.sh: install backup units and enable timer; create certs/ and backups/ subdirs in /srv/mcias - buf.yaml: add proto linting config for proto-lint target - internal/db: add Snapshot and SnapshotDir methods (VACUUM INTO) - cmd/mciasdb: add snapshot subcommand; no master key required
This commit is contained in:
44
cmd/mciasdb/snapshot.go
Normal file
44
cmd/mciasdb/snapshot.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcias/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcias/internal/db"
|
||||
)
|
||||
|
||||
// runSnapshot handles the "snapshot" command.
|
||||
//
|
||||
// It opens the database read-only (no master key derivation needed — VACUUM
|
||||
// INTO does not access encrypted columns) and writes a timestamped backup to
|
||||
// /srv/mcias/backups/ (or the directory adjacent to the configured DB path).
|
||||
// Backups older than --retain-days are pruned.
|
||||
func runSnapshot(configPath string, args []string) {
|
||||
fs := flag.NewFlagSet("snapshot", flag.ExitOnError)
|
||||
retainDays := fs.Int("retain-days", 30, "prune backups older than this many days (0 = keep all)")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
fatalf("snapshot: %v", err)
|
||||
}
|
||||
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
fatalf("snapshot: load config: %v", err)
|
||||
}
|
||||
|
||||
database, err := db.Open(cfg.Database.Path)
|
||||
if err != nil {
|
||||
fatalf("snapshot: open database: %v", err)
|
||||
}
|
||||
defer func() { _ = database.Close() }()
|
||||
|
||||
// Place backups in a "backups" directory adjacent to the database file.
|
||||
backupDir := filepath.Join(filepath.Dir(cfg.Database.Path), "backups")
|
||||
|
||||
dest, err := database.SnapshotDir(backupDir, *retainDays)
|
||||
if err != nil {
|
||||
fatalf("snapshot: %v", err)
|
||||
}
|
||||
fmt.Printf("snapshot written: %s\n", dest)
|
||||
}
|
||||
Reference in New Issue
Block a user