160 lines
3.6 KiB
Go
160 lines
3.6 KiB
Go
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")
|
|
}
|
|
}
|