portability

package
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package portability writes and reads the full-site archive behind `hatamoto export` and `hatamoto import` (format described in docs/EXPORT.md).

Index

Constants

View Source
const (
	FormatName    = "hatamoto-export"
	FormatVersion = 1
)

FormatName and FormatVersion identify the archive. A reader refuses a newer version than it knows; the version only changes when the layout does.

View Source
const DefaultMaxImportBytes = 64 << 30

DefaultMaxImportBytes is the decompressed-size cap when none is set.

Variables

View Source
var ErrNotEmpty = errors.New("database is not empty")

ErrNotEmpty is returned when the target database already has content.

Functions

This section is empty.

Types

type ExportOptions

type ExportOptions struct {
	// MediaDir is the upload directory the media rows' paths are relative to.
	MediaDir string
	// NoSubmissions leaves out form submissions (visitor data).
	NoSubmissions bool
	// OnMissing, when set, is called with the relative path of each referenced
	// media file that is not on disk.
	OnMissing func(rel string)
	// Now is the archive's timestamp (tests); zero means time.Now.
	Now time.Time
}

ExportOptions control Export.

type Header struct {
	Format            string    `json:"format"`
	Version           int       `json:"version"`
	CreatedAt         time.Time `json:"created_at"`
	SchemaVersion     int64     `json:"schema_version"` // newest applied database migration
	IncludeSubmission bool      `json:"include_form_submissions"`
	Tables            []string  `json:"tables"` // order the data files appear in
}

Header is the first entry of the archive.

type ImportOptions

type ImportOptions struct {
	// MediaDir is the upload directory media files are written to.
	MediaDir string
	// MaxBytes caps the decompressed archive (a gzip-bomb guard). Zero means
	// DefaultMaxImportBytes.
	MaxBytes int64
}

ImportOptions control Import.

type ImportResult

type ImportResult struct {
	Header     Header
	Rows       map[string]int64
	MediaFiles int
	MediaBytes int64
	Users      int64
	// Warnings lists media files the import skipped (per-file, not fatal).
	Warnings []string
	// People is how many person entities the post-import backfill made for an
	// archive that predates people (zero for an archive that has them).
	People int
	// contains filtered or unexported fields
}

ImportResult says what was imported.

func Import

func Import(ctx context.Context, pool *pgxpool.Pool, r io.Reader, opts ImportOptions) (*ImportResult, error)

Import reads an archive from r into an empty, migrated database, keeping ids and timestamps. It runs in one transaction: any error, including a failed manifest check, leaves the database as it was. Media files are staged in a temp directory under MediaDir and linked into place just before the commit; a failure removes only the files this import put there, never one that was already in MediaDir (an import refuses to overwrite). Users come back without passwords.

An archive from before people existed has authors and no person entities: once it is committed, person.Backfill makes the people (it is idempotent, so running it again after a failure is safe). No FormatVersion bump: the archive layout is unchanged, hatamoto_user_people is just one more table file, and archives without it import as before.

type Manifest

type Manifest struct {
	Rows       map[string]int64 `json:"rows"`
	MediaFiles int              `json:"media_files"`
	MediaBytes int64            `json:"media_bytes"`
	// MediaMissing counts referenced files the export did not find on disk. It
	// is informational; an import ignores it.
	MediaMissing int `json:"media_missing,omitempty"`
}

Manifest is the last entry: what the archive must contain. An import checks it, so a truncated or tampered archive fails instead of importing half a site.

func Export

func Export(ctx context.Context, pool *pgxpool.Pool, w io.Writer, opts ExportOptions) (*Manifest, error)

Export streams the site as a tar.gz to w. The database is read in one repeatable-read transaction, so the tables agree with each other; each table is spooled to a temp file (a tar entry needs its size first), so memory use does not grow with the site.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL