Some checks failed
Automated Container Build / build-and-push (push) Failing after 12s
118 lines
3 KiB
Go
118 lines
3 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.elijahkuntz.com/Elijah/drive/config"
|
|
"git.elijahkuntz.com/Elijah/drive/database"
|
|
)
|
|
|
|
// CreateVersion checks if a file exists and if so, moves it to the .versions directory
|
|
// and records it in the database before it gets overwritten.
|
|
// Call this BEFORE writing the new file to disk.
|
|
func CreateVersion(db *database.DB, cfg *config.Config, fullPath string, relPath string) error {
|
|
info, err := os.Stat(fullPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil // No existing file, nothing to version
|
|
}
|
|
return err
|
|
}
|
|
|
|
if info.IsDir() {
|
|
return nil // Don't version directories
|
|
}
|
|
|
|
// Check if versioning is enabled/has a max limit
|
|
val, err := db.GetSetting("max_versions")
|
|
maxVersions := 5
|
|
if err == nil {
|
|
if v, e := strconv.Atoi(val); e == nil {
|
|
maxVersions = v
|
|
}
|
|
}
|
|
|
|
if maxVersions <= 0 {
|
|
return nil // Versioning disabled
|
|
}
|
|
|
|
// Get current file details from DB (for checksum)
|
|
var checksum string
|
|
err = db.QueryRow("SELECT checksum FROM files WHERE path = ?", filepath.ToSlash(relPath)).Scan(&checksum)
|
|
if err != nil {
|
|
checksum = "" // fallback
|
|
}
|
|
|
|
// Generate version path
|
|
timestamp := time.Now().Format("20060102150405")
|
|
versionFileName := fmt.Sprintf("%s_%s%s", strings.TrimSuffix(filepath.Base(relPath), filepath.Ext(relPath)), timestamp, filepath.Ext(relPath))
|
|
versionRelPath := filepath.Join(filepath.Dir(relPath), versionFileName)
|
|
versionFullPath := filepath.Join(cfg.VersionsDir, versionRelPath)
|
|
|
|
if err := os.MkdirAll(filepath.Dir(versionFullPath), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Move current file to versions directory
|
|
if err := os.Rename(fullPath, versionFullPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Determine version number
|
|
var currentMax int
|
|
err = db.QueryRow("SELECT COALESCE(MAX(version_number), 0) FROM file_versions WHERE file_path = ?", filepath.ToSlash(relPath)).Scan(¤tMax)
|
|
if err != nil {
|
|
currentMax = 0
|
|
}
|
|
versionNumber := currentMax + 1
|
|
|
|
// Insert version record
|
|
_, err = db.Exec(`
|
|
INSERT INTO file_versions (file_path, version_number, version_path, size, checksum)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
`, filepath.ToSlash(relPath), versionNumber, filepath.ToSlash(versionRelPath), info.Size(), checksum)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Prune old versions
|
|
pruneVersions(db, cfg, relPath, maxVersions)
|
|
|
|
return nil
|
|
}
|
|
|
|
func pruneVersions(db *database.DB, cfg *config.Config, relPath string, maxVersions int) {
|
|
rows, err := db.Query(`
|
|
SELECT id, version_path FROM file_versions
|
|
WHERE file_path = ?
|
|
ORDER BY version_number DESC
|
|
LIMIT -1 OFFSET ?
|
|
`, filepath.ToSlash(relPath), maxVersions)
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
var idsToDelete []int
|
|
for rows.Next() {
|
|
var id int
|
|
var vPath string
|
|
if err := rows.Scan(&id, &vPath); err == nil {
|
|
idsToDelete = append(idsToDelete, id)
|
|
os.Remove(filepath.Join(cfg.VersionsDir, filepath.FromSlash(vPath)))
|
|
}
|
|
}
|
|
|
|
if len(idsToDelete) > 0 {
|
|
for _, id := range idsToDelete {
|
|
db.Exec("DELETE FROM file_versions WHERE id = ?", id)
|
|
}
|
|
}
|
|
}
|