This commit is contained in:
parent
fa2be029a2
commit
724d70e58b
3339 changed files with 1075535 additions and 0 deletions
118
backend/handlers/versioning.go
Normal file
118
backend/handlers/versioning.go
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in a new issue