Knowledge base of ~80+ markdown files across 14 domains (00-13), Logseq graph, hardware design files (KiCAD), infrastructure configs, and talas-wiki static site. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
112 lines
2.5 KiB
Go
112 lines
2.5 KiB
Go
package wiki
|
|
|
|
import (
|
|
"archive/tar"
|
|
"compress/gzip"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type BackupManager struct {
|
|
docsRoot string
|
|
backupDir string
|
|
maxKeep int
|
|
}
|
|
|
|
func NewBackupManager(docsRoot string, maxKeep int) *BackupManager {
|
|
dir := filepath.Join(docsRoot, ".wiki-data", "backups")
|
|
os.MkdirAll(dir, 0755)
|
|
return &BackupManager{docsRoot: docsRoot, backupDir: dir, maxKeep: maxKeep}
|
|
}
|
|
|
|
// CreateBackup creates a tar.gz backup of all markdown files
|
|
func (bm *BackupManager) CreateBackup() (string, error) {
|
|
filename := fmt.Sprintf("backup-%s.tar.gz", time.Now().Format("2006-01-02-150405"))
|
|
path := filepath.Join(bm.backupDir, filename)
|
|
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer f.Close()
|
|
|
|
gzw := gzip.NewWriter(f)
|
|
defer gzw.Close()
|
|
tw := tar.NewWriter(gzw)
|
|
defer tw.Close()
|
|
|
|
filepath.Walk(bm.docsRoot, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil || info.IsDir() {
|
|
return nil
|
|
}
|
|
relPath, _ := filepath.Rel(bm.docsRoot, path)
|
|
if strings.HasPrefix(relPath, ".wiki-data") || strings.HasPrefix(relPath, ".") ||
|
|
strings.HasPrefix(relPath, "talas-wiki") || strings.HasPrefix(relPath, "13_ARCHIVES") {
|
|
return nil
|
|
}
|
|
if !strings.HasSuffix(relPath, ".md") {
|
|
return nil
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
tw.WriteHeader(&tar.Header{
|
|
Name: relPath,
|
|
Size: int64(len(data)),
|
|
Mode: 0644,
|
|
ModTime: info.ModTime(),
|
|
})
|
|
tw.Write(data)
|
|
return nil
|
|
})
|
|
|
|
bm.rotate()
|
|
return filename, nil
|
|
}
|
|
|
|
// ListBackups returns available backups sorted by date
|
|
func (bm *BackupManager) ListBackups() []string {
|
|
entries, err := os.ReadDir(bm.backupDir)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var backups []string
|
|
for _, e := range entries {
|
|
if strings.HasSuffix(e.Name(), ".tar.gz") {
|
|
backups = append(backups, e.Name())
|
|
}
|
|
}
|
|
sort.Sort(sort.Reverse(sort.StringSlice(backups)))
|
|
return backups
|
|
}
|
|
|
|
// GetBackupPath returns the absolute path of a backup file
|
|
func (bm *BackupManager) GetBackupPath(filename string) string {
|
|
return filepath.Join(bm.backupDir, filepath.Base(filename))
|
|
}
|
|
|
|
func (bm *BackupManager) rotate() {
|
|
backups := bm.ListBackups()
|
|
if len(backups) <= bm.maxKeep {
|
|
return
|
|
}
|
|
for _, name := range backups[bm.maxKeep:] {
|
|
os.Remove(filepath.Join(bm.backupDir, name))
|
|
}
|
|
}
|
|
|
|
// StartAutoBackup runs a backup every interval, keeping maxKeep copies
|
|
func (bm *BackupManager) StartAutoBackup(interval time.Duration) {
|
|
go func() {
|
|
for {
|
|
time.Sleep(interval)
|
|
bm.CreateBackup()
|
|
}
|
|
}()
|
|
}
|