core/backup/manifest.go
Alex Dunmow d7c1abbfb6 style(backup): satisfy errcheck on deferred Close calls
Wrap deferred rc.Close() in closures to explicitly discard the error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 17:41:07 +08:00

75 lines
2.3 KiB
Go

package backup
import (
"archive/zip"
"encoding/json"
"fmt"
"io"
"time"
)
const (
// FormatVersion / FormatName identify the backup archive format. They are
// byte-identical to the cms constants (backupFormatVersion / backupFormatName)
// so archives interoperate with the existing cms restore path.
FormatVersion = 3
FormatName = "blockninja-pgdump-backup"
)
// Source identifies where a backup came from. Field-for-field identical to the
// cms BackupSource JSON shape.
type Source struct {
SiteTitle string `json:"site_title"`
Domain string `json:"domain,omitempty"`
}
// Manifest is the metadata stored in manifest.json. It is a field-for-field copy
// of the cms BackupManifest so archives built here restore through the existing
// cms path unchanged.
type Manifest struct {
Version int `json:"version"`
Format string `json:"format"`
ExportedAt time.Time `json:"exported_at"`
Source Source `json:"source"`
DatabaseSize int64 `json:"database_size"`
MediaCount int `json:"media_count"`
BrandCount int `json:"brand_count,omitempty"`
IconsCount int `json:"icons_count,omitempty"`
StylesCount int `json:"styles_count,omitempty"`
PluginsCount int `json:"plugins_count,omitempty"`
Encryption *EncryptionParams `json:"encryption,omitempty"`
Checksum string `json:"checksum"` // SHA-256 of unencrypted database dump
}
// ReadManifest opens a backup zip archive and parses its manifest.json entry.
func ReadManifest(r io.ReaderAt, size int64) (*Manifest, error) {
zr, err := zip.NewReader(r, size)
if err != nil {
return nil, fmt.Errorf("failed to open backup archive: %w", err)
}
for _, f := range zr.File {
if f.Name != "manifest.json" {
continue
}
rc, err := f.Open()
if err != nil {
return nil, fmt.Errorf("failed to open manifest.json: %w", err)
}
defer func() { _ = rc.Close() }()
data, err := io.ReadAll(rc)
if err != nil {
return nil, fmt.Errorf("failed to read manifest.json: %w", err)
}
var m Manifest
if err := json.Unmarshal(data, &m); err != nil {
return nil, fmt.Errorf("failed to parse manifest.json: %w", err)
}
return &m, nil
}
return nil, fmt.Errorf("manifest.json not found in backup archive")
}