feat: field-sync-gui MVP — Wails GUI ingest SD→local+S3 (4 slots //)

This commit is contained in:
2026-04-27 22:10:15 +00:00
commit dcaf5ac020
46 changed files with 5135 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
package manifest
import (
"encoding/json"
"os"
"sync"
)
// Entry is one copied file record
type Entry struct {
Source string `json:"source"`
LocalDest string `json:"localDest"`
S3Path string `json:"s3Path,omitempty"`
SHA256 string `json:"sha256"`
Bytes int64 `json:"bytes"`
Status string `json:"status"` // "local" | "s3" | "done" | "error"
Error string `json:"error,omitempty"`
}
// Manifest holds all entries for a session
type Manifest struct {
SessionID string `json:"sessionID"`
Entries []Entry `json:"entries"`
mu sync.Mutex
}
// New creates a new manifest
func New(sessionID string) *Manifest {
return &Manifest{SessionID: sessionID}
}
// Append adds an entry atomically
func (m *Manifest) Append(e Entry) {
m.mu.Lock()
defer m.mu.Unlock()
m.Entries = append(m.Entries, e)
}
// Save writes manifest atomically via temp file + rename
func (m *Manifest) Save(path string) error {
m.mu.Lock()
defer m.mu.Unlock()
data, err := json.MarshalIndent(m, "", " ")
if err != nil {
return err
}
tmp := path + ".tmp"
if err := os.WriteFile(tmp, data, 0644); err != nil {
return err
}
return os.Rename(tmp, path)
}
// Load reads a manifest from disk
func Load(path string) (*Manifest, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var m Manifest
if err := json.Unmarshal(data, &m); err != nil {
return nil, err
}
return &m, nil
}