snapshot before merge
This commit is contained in:
1 parent
06a5f3da4d
commit
671871e0b2
145 files changed
+9786
-59
No files matched your search
@@ -0,0 +1,168 @@
|
||||
package recycle
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"write-server/internal/config"
|
||||
)
|
||||
|
||||
var RECYCLE_POSTS_DIR string
|
||||
var RECYCLE_META_DIR string
|
||||
|
||||
type RecycleItem struct {
|
||||
Slug string `json:"slug"`
|
||||
Title string `json:"title"`
|
||||
DeletedAt string `json:"deletedAt"`
|
||||
OriginalPath string `json:"originalPath"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
DaysRemaining int `json:"daysRemaining"`
|
||||
}
|
||||
|
||||
func InitRecycleBin() {
|
||||
base := config.RECYCLE_DIR
|
||||
RECYCLE_POSTS_DIR = filepath.Join(base, "posts")
|
||||
RECYCLE_META_DIR = filepath.Join(base, "meta")
|
||||
if err := os.MkdirAll(RECYCLE_POSTS_DIR, 0755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := os.MkdirAll(RECYCLE_META_DIR, 0755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func MoveToRecycle(slug, sourceDir, title string) error {
|
||||
InitRecycleBin()
|
||||
|
||||
now := time.Now()
|
||||
expires := now.AddDate(0, 0, config.RECYCLE_DAYS)
|
||||
|
||||
meta := map[string]interface{}{
|
||||
"slug": slug,
|
||||
"title": title,
|
||||
"deletedAt": now.Format(time.RFC3339),
|
||||
"originalPath": sourceDir,
|
||||
"expiresAt": expires.Format(time.RFC3339),
|
||||
}
|
||||
|
||||
targetDir := filepath.Join(RECYCLE_POSTS_DIR, slug)
|
||||
os.RemoveAll(targetDir)
|
||||
if err := os.Rename(sourceDir, targetDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metaPath := filepath.Join(RECYCLE_META_DIR, slug+".json")
|
||||
data, _ := json.MarshalIndent(meta, "", " ")
|
||||
return os.WriteFile(metaPath, data, 0644)
|
||||
}
|
||||
|
||||
func ListRecycle() []RecycleItem {
|
||||
if _, err := os.Stat(RECYCLE_META_DIR); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
entries, err := os.ReadDir(RECYCLE_META_DIR)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var items []RecycleItem
|
||||
now := time.Now()
|
||||
for _, entry := range entries {
|
||||
if !strings.HasSuffix(entry.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(RECYCLE_META_DIR, entry.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var meta map[string]interface{}
|
||||
if err := json.Unmarshal(data, &meta); err != nil {
|
||||
continue
|
||||
}
|
||||
expiresAt, _ := time.Parse(time.RFC3339, getString(meta, "expiresAt"))
|
||||
daysRemaining := int(math.Ceil(expiresAt.Sub(now).Hours() / 24))
|
||||
if daysRemaining < 0 {
|
||||
daysRemaining = 0
|
||||
}
|
||||
items = append(items, RecycleItem{
|
||||
Slug: getString(meta, "slug"),
|
||||
Title: getString(meta, "title"),
|
||||
DeletedAt: getString(meta, "deletedAt"),
|
||||
OriginalPath: getString(meta, "originalPath"),
|
||||
ExpiresAt: getString(meta, "expiresAt"),
|
||||
DaysRemaining: daysRemaining,
|
||||
})
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
ti, _ := time.Parse(time.RFC3339, items[i].DeletedAt)
|
||||
tj, _ := time.Parse(time.RFC3339, items[j].DeletedAt)
|
||||
return ti.After(tj)
|
||||
})
|
||||
return items
|
||||
}
|
||||
|
||||
func RestoreFromRecycle(slug string) bool {
|
||||
metaPath := filepath.Join(RECYCLE_META_DIR, slug+".json")
|
||||
data, err := os.ReadFile(metaPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var meta map[string]interface{}
|
||||
json.Unmarshal(data, &meta)
|
||||
|
||||
recyclePostPath := filepath.Join(RECYCLE_POSTS_DIR, slug)
|
||||
if _, err := os.Stat(recyclePostPath); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
originalPath := getString(meta, "originalPath")
|
||||
os.MkdirAll(filepath.Dir(originalPath), 0755)
|
||||
os.RemoveAll(originalPath)
|
||||
|
||||
if err := os.Rename(recyclePostPath, originalPath); err != nil {
|
||||
return false
|
||||
}
|
||||
os.Remove(metaPath)
|
||||
return true
|
||||
}
|
||||
|
||||
func PermanentDelete(slug string) bool {
|
||||
deleted := false
|
||||
recyclePostPath := filepath.Join(RECYCLE_POSTS_DIR, slug)
|
||||
if _, err := os.Stat(recyclePostPath); err == nil {
|
||||
os.RemoveAll(recyclePostPath)
|
||||
deleted = true
|
||||
}
|
||||
metaPath := filepath.Join(RECYCLE_META_DIR, slug+".json")
|
||||
if _, err := os.Stat(metaPath); err == nil {
|
||||
os.Remove(metaPath)
|
||||
deleted = true
|
||||
}
|
||||
return deleted
|
||||
}
|
||||
|
||||
func CleanExpired() int {
|
||||
items := ListRecycle()
|
||||
count := 0
|
||||
for _, item := range items {
|
||||
if item.DaysRemaining <= 0 {
|
||||
PermanentDelete(item.Slug)
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func getString(m map[string]interface{}, key string) string {
|
||||
if v, ok := m[key]; ok {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user