Files

109 lines
3.0 KiB
Go

package docrevision
import (
"context"
"strings"
"time"
)
// ImportFreeformLegacy imports legacy freeform checkpoint as sealed migration versions + open head.
func (s *Service) ImportFreeformLegacy(ctx context.Context, docID string, ownerID int64, projectID int64, checkpoints []struct {
VersionNo int
Markdown string
SHA string
}, currentMarkdown string) (*RevisionState, error) {
if err := requireDocID(docID); err != nil {
return nil, err
}
_ = ctx
g := s.lockDoc(docID)
defer g.Release()
s.mu.Lock()
defer s.mu.Unlock()
if root, ok := s.roots[docID]; ok && root.LedgerVersionID != nil {
return s.mustStateLocked(docID), nil
}
actor := ActorSnapshot{DisplayName: "migration", Role: "system"}
var lastSnap *Snapshot
startNo := 1
for _, cp := range checkpoints {
canon := CanonicalizeMarkdown(cp.Markdown)
sha := cp.SHA
if sha == "" {
sha = SHA256Hex(canon)
}
var parent *int64
if lastSnap != nil {
id := lastSnap.ID
parent = &id
}
snap := s.putSnapshotLocked(docID, canon, sha, parent, SourceMigration, &ownerID, "legacy", actor, "", nil)
ver := s.openVersionLocked(docID, OriginMigration, snap.ID, snap.ID, "")
// force version number and seal
ver.VersionNo = cp.VersionNo
now := time.Now().UTC()
ver.Status = VersionSealed
ver.SealedAt = &now
lastSnap = snap
if cp.VersionNo >= startNo {
startNo = cp.VersionNo + 1
}
}
canon := CanonicalizeMarkdown(currentMarkdown)
sha := SHA256Hex(canon)
var parent *int64
if lastSnap != nil {
id := lastSnap.ID
parent = &id
}
snap := s.putSnapshotLocked(docID, canon, sha, parent, SourceMigration, &ownerID, "legacy_head", actor, "", nil)
ver := &Version{
ID: 0, DocID: docID, VersionNo: startNo, Status: VersionOpen, OriginKind: OriginMigration,
BaselineSnapshotID: snap.ID, HeadSnapshotID: snap.ID, OpenedAt: time.Now().UTC(),
}
ver.ID = nextFreeID(&s.nextVerID, func(id int64) bool { return s.versions[id] != nil })
s.versions[ver.ID] = ver
root := &DocRootPointers{
DocID: docID, Kind: "freeform", ProjectID: projectID,
LedgerVersionID: &ver.ID, HeadSnapshotID: &snap.ID, HeadRevisionNo: 0,
ContentSHA256: sha, LifecycleState: "drafting", RevisionSchemaVer: SchemaVersion,
LegacyVersionID: startNo - 1,
}
s.roots[docID] = root
return s.mustStateLocked(docID), nil
}
// SetDocWorkDir updates the in-memory ledger root workDir used by ProjectOutputMarkdown.
func (s *Service) SetDocWorkDir(ctx context.Context, docID, workDir string) error {
if err := requireDocID(docID); err != nil {
return err
}
if strings.TrimSpace(workDir) == "" {
return nil
}
g := s.lockDoc(docID)
defer g.Release()
s.mu.Lock()
defer s.mu.Unlock()
if err := s.hydrateDocLocked(ctx, docID); err != nil {
return err
}
root := s.roots[docID]
if root == nil {
return ErrNotFound
}
root.WorkDir = workDir
return nil
}
// SetLifecycle updates lifecycle gate used by write paths.
func (s *Service) SetLifecycle(docID, state string) {
g := s.lockDoc(docID)
defer g.Release()
s.mu.Lock()
defer s.mu.Unlock()
if r := s.roots[docID]; r != nil {
r.LifecycleState = state
}
}