package main import ( "fmt" "html/template" "log" "os" "path/filepath" "strconv" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" "gorm.io/driver/sqlite" "gorm.io/gorm" ) var db *gorm.DB func main() { _ = os.MkdirAll("uploads", 0755) _ = os.MkdirAll("data", 0755) var err error db, err = gorm.Open(sqlite.Open("data/files.db"), &gorm.Config{}) if err != nil { log.Fatal("DB Connection failed:", err) } db.AutoMigrate(&User{}, &FileRecord{}) ensureAdmin() go cleanupWorker() 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 }, }) router.LoadHTMLGlob("templates/*") var staticPath = gin.Dir("./static", false) fmt.Printf("Static path: %v\n", staticPath) router.StaticFS("/static", staticPath) // Public Routes router.GET("/", func(c *gin.Context) { c.HTML(200, "index.html", nil) }) router.GET("/f/:id", downloadHandler) router.GET("/api/file/delete/:del_id", deleteHandler) router.POST("/api/upload", uploadHandler) 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 } } 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, }) }) admin.POST("/delete/:id", func(c *gin.Context) { var record FileRecord if err := db.First(&record, "id = ?", c.Param("id")).Error; err == nil { performDeletion(&record) } c.Redirect(303, "/admin") }) admin.GET("/download/:id", func(c *gin.Context) { var record FileRecord fmt.Printf("Getting file id: %v\n", c.Param("id")) if err := db.First(&record, "id = ?", c.Param("id")).Error; err != nil { fmt.Println("Admin download failed:", err) c.Header("Content-Type", "text/plain") c.String(418, "File not found") return } fmt.Printf("Path: %s, Filename: %s\n", record.Path, record.Filename) absPath, err := filepath.Abs(record.Path) if err != nil { c.String(500, "Path error") return } fmt.Println("Serving:", absPath) c.Header("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, record.Filename)) c.File(record.Path) }) admin.GET("/files", func(c *gin.Context) { var files []FileRecord db.Order("created_at desc").Find(&files) c.JSON(200, files) }) router.POST("/login", loginHandler) router.GET("/login", func(c *gin.Context) { c.HTML(200, "login.html", nil) }) router.GET("/logout", func(c *gin.Context) { c.SetCookie("auth", "", -1, "/", "", false, true) c.Redirect(302, "/") }) log.Println("Server starting at http://localhost:8080") router.Run("0.0.0.0:8080") } func ensureAdmin() { var count int64 db.Model(&User{}).Where("username = ?", "admin").Count(&count) password := os.Getenv("ADMIN_PASSWORD") if password == "" { log.Println("WARNING: ADMIN_PASSWORD not set, using default password") log.Println("WARNING: MAKING NO ADMIN ACCOUNT") return } if count == 0 { hash, _ := bcrypt.GenerateFromPassword( []byte(password), bcrypt.DefaultCost, ) db.Create(&User{ Username: "admin", Password: string(hash), }) log.Println("Admin user created") } }