diff --git a/src/handlers.go b/src/handlers.go index 4352730..4245545 100644 --- a/src/handlers.go +++ b/src/handlers.go @@ -30,6 +30,9 @@ func uploadHandler(c *gin.Context) { id := uuid.New().String() delID := uuid.New().String() cleanName := filepath.Base(header.Filename) + if len(cleanName) > 255 { + cleanName = cleanName[:255] + } folderPath := filepath.Join("uploads", id) os.MkdirAll(folderPath, 0755) @@ -65,6 +68,7 @@ func uploadHandler(c *gin.Context) { Filename: cleanName, Path: storagePath, ExpiresAt: expiry, + Size: written, DeleteAfterDownload: c.PostForm("once") == "true", } @@ -79,14 +83,21 @@ func uploadHandler(c *gin.Context) { func downloadHandler(c *gin.Context) { var record FileRecord - if err := db.First(&record, "id = ? AND deleted = ?", c.Param("id"), false).Error; err != nil { - c.String(404, "File not found or expired") + var err = db.First(&record, "id = ? AND deleted = ?", c.Param("id"), false).Error + if err != nil { + c.HTML(200, "fileNotFound.html", gin.H{ + "message": "File not found", + }) return } if time.Now().After(record.ExpiresAt) { performDeletion(&record) - c.String(410, "File has expired") + + //c.String(410, "File has expired") + c.HTML(404, "fileNotFound.html", gin.H{ + "message": "File not found", + }) return } @@ -107,7 +118,8 @@ func deleteHandler(c *gin.Context) { return } performDeletion(&record) - c.JSON(200, gin.H{"message": "Deleted successfully"}) + //c.JSON(200, gin.H{"message": "Deleted successfully"}) + c.HTML(200, "deleted.html", nil) } func loginHandler(c *gin.Context) { @@ -145,3 +157,30 @@ func loginHandler(c *gin.Context) { c.Redirect(302, "/admin") } + +func adminIndexHandler(c *gin.Context) { + var files []FileRecord + + // Pagination parameters + perPage := 20 + page := 1 + if p := c.Query("page"); p != "" { + if v, err := strconv.Atoi(p); err == nil && v > 0 { + page = v + } + } + + var total int64 + db.Model(&FileRecord{}).Count(&total) + + totalPages := int((total + int64(perPage) - 1) / int64(perPage)) // ceiling division + + offset := (page - 1) * perPage + db.Order("created_at desc").Limit(perPage).Offset(offset).Find(&files) + + c.HTML(200, "admin.html", gin.H{ + "Files": files, + "Page": page, + "TotalPages": totalPages, + }) +} diff --git a/src/main.go b/src/main.go index 29689a7..9d2e0d0 100644 --- a/src/main.go +++ b/src/main.go @@ -6,7 +6,6 @@ import ( "log" "os" "path/filepath" - "strconv" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" @@ -34,8 +33,9 @@ func main() { router := gin.Default() router.MaxMultipartMemory = 10 << 30 router.SetFuncMap(template.FuncMap{ - "add": func(a, b int) int { return a + b }, - "sub": func(a, b int) int { return a - b }, + "add": func(a, b int) int { return a + b }, + "sub": func(a, b int) int { return a - b }, + "humanSize": humanSize, }) router.LoadHTMLGlob("templates/*") var staticPath = gin.Dir("./static", false) @@ -43,6 +43,9 @@ func main() { router.StaticFS("/static", staticPath) + router.NoRoute(func(c *gin.Context) { + c.HTML(404, "error.html", nil) + }) // Public Routes router.GET("/", func(c *gin.Context) { c.HTML(200, "index.html", nil) }) router.GET("/f/:id", downloadHandler) @@ -53,34 +56,16 @@ func main() { admin := router.Group("/admin") admin.Use(authMiddleware()) - admin.GET("/", func(c *gin.Context) { - var files []FileRecord - - // Pagination parameters - perPage := 20 - page := 1 - if p := c.Query("page"); p != "" { - if v, err := strconv.Atoi(p); err == nil && v > 0 { - page = v - } + admin.GET("/", adminIndexHandler) + admin.GET("/delete/fr/:id", func(c *gin.Context) { + var record FileRecord + if err := db.First(&record, "id = ?", c.Param("id")).Error; err == nil { + performActualDeletion(&record) } - - var total int64 - db.Model(&FileRecord{}).Count(&total) - - totalPages := int((total + int64(perPage) - 1) / int64(perPage)) // ceiling division - - offset := (page - 1) * perPage - db.Order("created_at desc").Limit(perPage).Offset(offset).Find(&files) - - c.HTML(200, "admin.html", gin.H{ - "Files": files, - "Page": page, - "TotalPages": totalPages, - }) + c.Redirect(301, "/admin") }) - admin.POST("/delete/:id", func(c *gin.Context) { + admin.GET("/delete/:id", func(c *gin.Context) { var record FileRecord if err := db.First(&record, "id = ?", c.Param("id")).Error; err == nil { performDeletion(&record) diff --git a/src/models.go b/src/models.go index 74ef47b..e5631a2 100644 --- a/src/models.go +++ b/src/models.go @@ -9,6 +9,7 @@ type FileRecord struct { Path string `json:"-"` ExpiresAt time.Time `json:"expires_at"` DeleteAfterDownload bool `json:"delete_after_download"` + Size int64 `json:"size"` DownloadCount int `json:"download_count"` Deleted bool `json:"deleted"` CreatedAt time.Time `json:"created_at"` diff --git a/src/utils.go b/src/utils.go index 9b70f89..cebc322 100644 --- a/src/utils.go +++ b/src/utils.go @@ -1,6 +1,9 @@ package main import ( + "fmt" + "os" + "path/filepath" "time" "github.com/gin-gonic/gin" @@ -12,6 +15,19 @@ func performDeletion(r *FileRecord) { db.Save(r) } +func performActualDeletion(r *FileRecord) { + + folderPath := filepath.Join("uploads", r.ID) + err := os.RemoveAll(folderPath) + + if err != nil { + fmt.Println("Error deleting file:", err) + return + } + db.Delete(r) + fmt.Println("Deleted file:", r.Filename) +} + func cleanupWorker() { for { time.Sleep(10 * time.Minute) @@ -38,7 +54,7 @@ func authMiddleware() gin.HandlerFunc { tokenStr, err := c.Cookie("auth") if err != nil { - c.Redirect(302, "/") + c.Redirect(302, "/login") c.Abort() return } @@ -48,7 +64,7 @@ func authMiddleware() gin.HandlerFunc { }) if err != nil || !token.Valid { - c.Redirect(302, "/") + c.Redirect(302, "/login") c.Abort() return } @@ -56,3 +72,19 @@ func authMiddleware() gin.HandlerFunc { c.Next() } } + +func humanSize(size int64) string { + const unit = 1024 + if size < unit { + return fmt.Sprintf("%d B", size) + } + div, exp := int64(unit), 0 + for n := size / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %cB", + float64(size)/float64(div), + "KMGTPE"[exp], + ) +} diff --git a/templates/admin.html b/templates/admin.html index 441b9ce..9f28a4e 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -39,6 +39,7 @@ Filename + Size Created Expires Hits @@ -53,6 +54,7 @@ {{.Filename}} + {{humanSize .Size}} {{.CreatedAt.Format "Jan 02, 2006 15:04"}} {{.ExpiresAt.Format "Jan 02, 2006 15:04"}} {{.DownloadCount}} @@ -72,10 +74,13 @@ {{if not .Deleted}} -
+
{{end}} +
+ +
{{end}} diff --git a/templates/deleted.html b/templates/deleted.html new file mode 100644 index 0000000..55e8775 --- /dev/null +++ b/templates/deleted.html @@ -0,0 +1,92 @@ + + + + + + File Deleted sucessfull + + + + + +
+ +
+ +
+ FILE DELETED SUCESSFULL +
+ +
+ The file has been absolutely obliterated. +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + \ No newline at end of file diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 0000000..891a5cd --- /dev/null +++ b/templates/error.html @@ -0,0 +1,97 @@ + + + + + + Nothing to see here + + + + + +
+ +
+ +
+ NOTHING TO SEE HERE +
+ +
+ MOVE ALONG +
+ +
+ This page is empty,
+ unavailable, private,
+ or intentionally left blank. +
+ +
+ GO BACK +
+ +
+ +
+ + + \ No newline at end of file diff --git a/templates/fileNotFound.html b/templates/fileNotFound.html new file mode 100644 index 0000000..c9d1134 --- /dev/null +++ b/templates/fileNotFound.html @@ -0,0 +1,88 @@ + + + + + + 404 — File Not Found + + + + + +
+ +
+ +
404
+ +
+ FILE NOT FOUND 💀 +
+ +
+ The requested file does not exist,
+ has expired, or was destroyed,
or my db is fucked. + We'll never know :D +
+ + + +
+ + +
+ + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 485a86a..ea31325 100644 --- a/templates/index.html +++ b/templates/index.html @@ -335,6 +335,6 @@ document.execCommand('copy'); } -SUDO +SUDO \ No newline at end of file