add docs
All checks were successful
Deploy on Master Change / deploy (push) Successful in 44s

This commit is contained in:
2026-07-01 14:58:46 +07:00
parent dd766206f7
commit 0b02cc82b5
9 changed files with 992 additions and 2 deletions

View File

@@ -67,6 +67,7 @@ func AutoMigrate(db *gorm.DB) error {
&models.StaffGitHubRepo{},
&models.SystemSetting{},
&models.GuideLink{},
&models.AppDownload{},
); err != nil {
return err
}

View File

@@ -0,0 +1,148 @@
package handlers
import (
"strconv"
"server/internal/models"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
// GET /api/public/app-downloads
func ListAppDownloadsPublicHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
var apps []models.AppDownload
if err := db.Order("created_at desc").Find(&apps).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Không thể tải danh sách ứng dụng"})
}
return c.JSON(fiber.Map{"data": apps})
}
}
// GET /api/app-downloads
func ListAppDownloadsHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
var apps []models.AppDownload
if err := db.Order("created_at desc").Find(&apps).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Không thể tải danh sách ứng dụng"})
}
return c.JSON(fiber.Map{"data": apps})
}
}
// POST /api/app-downloads
func CreateAppDownloadHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
if !isSuperAdmin(c) {
return c.Status(403).JSON(fiber.Map{"error": "Bạn không có quyền thực hiện chức năng này"})
}
type Request struct {
Name string `json:"name"`
Description string `json:"description"`
DownloadURL string `json:"downloadUrl"`
Platform string `json:"platform"`
}
var req Request
if err := c.BodyParser(&req); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
}
if req.Name == "" || req.DownloadURL == "" {
return c.Status(400).JSON(fiber.Map{"error": "Tên ứng dụng và đường dẫn tải không được để trống"})
}
app := models.AppDownload{
Name: req.Name,
Description: req.Description,
DownloadURL: req.DownloadURL,
Platform: req.Platform,
}
if err := db.Create(&app).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Lưu ứng dụng thất bại"})
}
return c.JSON(fiber.Map{"ok": true, "data": app})
}
}
// PUT /api/app-downloads/:id
func UpdateAppDownloadHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
if !isSuperAdmin(c) {
return c.Status(403).JSON(fiber.Map{"error": "Bạn không có quyền thực hiện chức năng này"})
}
idParam := c.Params("id")
id, err := strconv.ParseUint(idParam, 10, 32)
if err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid ID"})
}
var app models.AppDownload
if err := db.First(&app, uint(id)).Error; err != nil {
return c.Status(404).JSON(fiber.Map{"error": "Không tìm thấy ứng dụng"})
}
type Request struct {
Name string `json:"name"`
Description string `json:"description"`
DownloadURL string `json:"downloadUrl"`
Platform string `json:"platform"`
}
var req Request
if err := c.BodyParser(&req); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
}
if req.Name == "" || req.DownloadURL == "" {
return c.Status(400).JSON(fiber.Map{"error": "Tên ứng dụng và đường dẫn tải không được để trống"})
}
app.Name = req.Name
app.Description = req.Description
app.DownloadURL = req.DownloadURL
app.Platform = req.Platform
if err := db.Save(&app).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Cập nhật ứng dụng thất bại"})
}
return c.JSON(fiber.Map{"ok": true, "data": app})
}
}
// DELETE /api/app-downloads/:id
func DeleteAppDownloadHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
if !isSuperAdmin(c) {
return c.Status(403).JSON(fiber.Map{"error": "Bạn không có quyền thực hiện chức năng này"})
}
idParam := c.Params("id")
id, err := strconv.ParseUint(idParam, 10, 32)
if err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid ID"})
}
var app models.AppDownload
if err := db.First(&app, uint(id)).Error; err != nil {
return c.Status(404).JSON(fiber.Map{"error": "Không tìm thấy ứng dụng"})
}
if err := db.Delete(&app).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Xóa ứng dụng thất bại"})
}
return c.JSON(fiber.Map{"ok": true})
}
}
// GET /api/public/guide-links
func ListGuideLinksPublicHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
var links []models.GuideLink
if err := db.Order("created_at desc").Find(&links).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Không thể tải danh sách tài liệu"})
}
return c.JSON(fiber.Map{"data": links})
}
}

View File

@@ -199,4 +199,17 @@ type GuideLink struct {
func (GuideLink) TableName() string { return "guide_links" }
// AppDownload đại diện cho ứng dụng và link tải
type AppDownload struct {
ID uint `gorm:"primaryKey" json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Name string `gorm:"column:name;size:255;not null" json:"name"`
Description string `gorm:"column:description;size:512" json:"description"`
DownloadURL string `gorm:"column:download_url;size:1024;not null" json:"downloadUrl"`
Platform string `gorm:"column:platform;size:64" json:"platform"` // "Windows", "macOS", "Linux", "Khác"
}
func (AppDownload) TableName() string { return "app_downloads" }

View File

@@ -107,6 +107,9 @@ func main() {
return c.JSON(fiber.Map{"ok": true})
})
api.Get("/public/app-downloads", handlers.ListAppDownloadsPublicHandler(gormDB))
api.Get("/public/guide-links", handlers.ListGuideLinksPublicHandler(gormDB))
// Management — yêu cầu đăng nhập staff
staff := api.Group("", middleware.RequireStaff())
staff.Get("/auth/me", handlers.MeStaffHandler(gormDB))
@@ -207,6 +210,11 @@ func main() {
staff.Put("/guide-links/:id", handlers.UpdateGuideLinkHandler(gormDB))
staff.Delete("/guide-links/:id", handlers.DeleteGuideLinkHandler(gormDB))
staff.Get("/app-downloads", handlers.ListAppDownloadsHandler(gormDB))
staff.Post("/app-downloads", handlers.CreateAppDownloadHandler(gormDB))
staff.Put("/app-downloads/:id", handlers.UpdateAppDownloadHandler(gormDB))
staff.Delete("/app-downloads/:id", handlers.DeleteAppDownloadHandler(gormDB))
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
@@ -215,6 +223,8 @@ func main() {
}
}()
app.Static("/", "../landing_page")
port := getEnv("PORT", "8080")
log.Printf("Server starting on port %s...", port)
if err := app.Listen(":" + port); err != nil {