package api import ( "net/http" "regexp" "slices" "github.com/lgcosta/notas-online/internal/diffx" "github.com/lgcosta/notas-online/internal/store" ) var hashRe = regexp.MustCompile(`^[0-9a-f]{64}$`) // revisionHash valida o hash da rota; em hash inválido responde 404 e retorna // ("", false) — o chamador deve apenas retornar. func revisionHash(w http.ResponseWriter, r *http.Request) (string, bool) { hash := r.PathValue("hash") if !hashRe.MatchString(hash) { writeError(w, http.StatusNotFound, "revisão não encontrada") return "", false } return hash, true } func (s *Server) handleListRevisions(w http.ResponseWriter, r *http.Request) { revs, err := s.st.History(r.PathValue("id")) if err != nil { mapStoreError(w, err) return } // Mais recente primeiro: ordem natural para um painel de histórico. slices.Reverse(revs) if revs == nil { revs = []store.Revision{} } writeJSON(w, http.StatusOK, revs) } func (s *Server) handleGetRevision(w http.ResponseWriter, r *http.Request) { hash, ok := revisionHash(w, r) if !ok { return } rc, err := s.st.RevisionWithContent(r.PathValue("id"), hash) if err != nil { mapStoreError(w, err) return } writeJSON(w, http.StatusOK, rc) } type diffView struct { From string `json:"from"` To string `json:"to"` Author string `json:"author"` When string `json:"when"` Message string `json:"message"` Diff string `json:"diff"` Added int `json:"added"` Removed int `json:"removed"` } func (s *Server) handleRevisionDiff(w http.ResponseWriter, r *http.Request) { hash, ok := revisionHash(w, r) if !ok { return } before, after, rev, err := s.st.DiffBetween(r.PathValue("id"), hash) if err != nil { mapStoreError(w, err) return } from := "criação" if rev.Parent != "" { from = short(rev.Parent) } patch := diffx.Unified(from, short(hash), before, after) added, removed := diffx.Stats(patch) writeJSON(w, http.StatusOK, diffView{ From: from, To: short(hash), Author: rev.Author, When: rev.When.Format("2006-01-02 15:04:05 MST"), Message: rev.Message, Diff: patch, Added: added, Removed: removed, }) } type restoreInput struct { Author string `json:"author"` Session string `json:"session"` } func (s *Server) handleRestore(w http.ResponseWriter, r *http.Request) { hash, ok := revisionHash(w, r) if !ok { return } var in restoreInput if r.ContentLength > 0 { if err := decodeJSON(w, r, &in, 1<<16); err != nil { return } } n, err := s.st.Restore(r.PathValue("id"), hash, in.Author) if err != nil { mapStoreError(w, err) return } content, _, err := s.st.Content(n.ID) if err != nil { mapStoreError(w, err) return } s.publishSaved(n, in.Author, in.Session, false) writeJSON(w, http.StatusOK, newNoteView(n, content)) } func short(hash string) string { if len(hash) > 8 { return hash[:8] } return hash }