Files
blog/write-go/internal/recycle/recycle.go
T
2026-06-01 17:57:22 +08:00

169 lines
3.9 KiB
Go

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 ""
}