109 lines
3.0 KiB
Go
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
|
|
}
|
|
}
|