diff --git a/.env b/.env index 0d0ce7e..df68717 100644 --- a/.env +++ b/.env @@ -1,3 +1,13 @@ +# FITRA environment variables + +# Port to run the server on PORT=8080 + +# Directory for uploaded files UPLOAD_DIR=./uploads -BASE_URL=http://localhost:8080 \ No newline at end of file + +# Base URL for file downloads +BASE_URL=http://localhost:8080 + +# How long to keep uploaded files before deletion (examples: 1d, 12h, 30m) +DELETE_FILES_AFTER=1d \ No newline at end of file diff --git a/.env.example b/.env.example index 4758c73..df68717 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,13 @@ +# FITRA environment variables + +# Port to run the server on PORT=8080 + +# Directory for uploaded files UPLOAD_DIR=./uploads -MAX_FILE_SIZE=10MB -BASE_URL=http://localhost:8080 \ No newline at end of file + +# Base URL for file downloads +BASE_URL=http://localhost:8080 + +# How long to keep uploaded files before deletion (examples: 1d, 12h, 30m) +DELETE_FILES_AFTER=1d \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6ff45a7..b743b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -205,4 +205,5 @@ $RECYCLE.BIN/ # Application -uploads/** \ No newline at end of file +uploads/** +.env \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 84e1123..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,4 +0,0 @@ -This project is FITRA's backend, FITRA stands for file transfer. -GoLang is used for the backend. - -This app is made for developers who needs to upload files via HTTP requests in CLI. diff --git a/application/clean.go b/application/clean.go new file mode 100644 index 0000000..fb3c548 --- /dev/null +++ b/application/clean.go @@ -0,0 +1,80 @@ +package application + +import ( + "fmt" + "log" + "os" + "time" +) + +func ParseDuration(s string) (time.Duration, error) { + if s == "" { + return 24 * time.Hour, nil // Default to 1 day + } + return time.ParseDuration(s) +} + +func StartCleanupRoutine() { + deleteAfterStr := os.Getenv("DELETE_FILES_AFTER") + deleteAfter, err := ParseDuration(deleteAfterStr) + if err != nil { + log.Printf("Invalid DELETE_FILES_AFTER value '%s', using default 1 day: %v", deleteAfterStr, err) + deleteAfter = 24 * time.Hour + } + + log.Printf("File cleanup routine started, deleting files older than %v", deleteAfter) + + ticker := time.NewTicker(time.Hour) // Check every hour + defer ticker.Stop() + + for { + select { + case <-ticker.C: + CleanupOldFiles(deleteAfter) + } + } +} + +func CleanupOldFiles(maxAge time.Duration) { + uploadsDir := os.Getenv("UPLOAD_DIR") + if uploadsDir == "" { + uploadsDir = "./uploads" + } + + entries, err := os.ReadDir(uploadsDir) + if err != nil { + if !os.IsNotExist(err) { + log.Printf("Error reading uploads directory: %v", err) + } + return + } + + cutoffTime := time.Now().Add(-maxAge) + deletedCount := 0 + + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + dirPath := fmt.Sprintf("%s/%s", uploadsDir, entry.Name()) + info, err := entry.Info() + if err != nil { + log.Printf("Error getting info for directory %s: %v", dirPath, err) + continue + } + + if info.ModTime().Before(cutoffTime) { + if err := os.RemoveAll(dirPath); err != nil { + log.Printf("Error deleting directory %s: %v", dirPath, err) + } else { + deletedCount++ + log.Printf("Deleted old directory: %s", dirPath) + } + } + } + + if deletedCount > 0 { + log.Printf("Cleanup completed: deleted %d old directories", deletedCount) + } +} \ No newline at end of file diff --git a/endpoints/download.go b/controllers/download.go similarity index 97% rename from endpoints/download.go rename to controllers/download.go index 630f0b9..8ab7b30 100644 --- a/endpoints/download.go +++ b/controllers/download.go @@ -1,4 +1,4 @@ -package endpoints +package controllers import ( "net/http" diff --git a/endpoints/index.go b/controllers/index.go similarity index 91% rename from endpoints/index.go rename to controllers/index.go index dfe473e..01cd29e 100644 --- a/endpoints/index.go +++ b/controllers/index.go @@ -1,4 +1,4 @@ -package endpoints +package controllers import ( "net/http" @@ -13,4 +13,4 @@ func HandleIndex(c *gin.Context) { baseURL = "http://localhost:8080" } c.HTML(http.StatusOK, "index.html", gin.H{"BaseURL": baseURL}) -} \ No newline at end of file +} diff --git a/endpoints/upload.go b/controllers/upload.go similarity index 98% rename from endpoints/upload.go rename to controllers/upload.go index 145eec8..8976d5e 100644 --- a/endpoints/upload.go +++ b/controllers/upload.go @@ -1,4 +1,4 @@ -package endpoints +package controllers import ( "crypto/rand" diff --git a/main.go b/main.go index 6713ade..20cc970 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,8 @@ package main import ( - "fitra-backend/endpoints" + "fitra-backend/application" + "fitra-backend/controllers" "fmt" "os" @@ -9,12 +10,14 @@ import ( ) func main() { + go application.StartCleanupRoutine() + r := gin.Default() r.LoadHTMLGlob("views/*") - r.GET("/", endpoints.HandleIndex) - r.POST("/upload", endpoints.HandleFileUpload) - r.GET("/uploads/:fileID/:filename", endpoints.HandleFileDownload) + r.GET("/", controllers.HandleIndex) + r.POST("/upload", controllers.HandleFileUpload) + r.GET("/uploads/:fileID/:filename", controllers.HandleFileDownload) r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) if port := os.Getenv("PORT"); port != "" {