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