- Go + Gin + html/template 服务端渲染 - 主页:Google 风格搜索框 + 导航卡片 - 后台:卡片 CRUD、搜索引擎配置、主页背景/标题配置 - 图片上传:支持 jpg/jpeg/png/gif,自动压缩,缩略图参数 ?thumb=1 - 安全:登录日志、修改密码、IP 自动封禁、IP 白名单 - 访问统计:主页访问/卡片点击/搜索追踪、实时流量、IP 统计 - SQLite 存储(modernc.org/sqlite,纯 Go) - 内存 Session + bcrypt 密码哈希
198 lines
4.6 KiB
Go
198 lines
4.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"simple_portal/models"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// CardsList renders the admin cards management page.
|
|
func CardsList(c *gin.Context) {
|
|
username, _ := c.Get("username")
|
|
cards, err := models.GetAllCards()
|
|
if err != nil {
|
|
c.HTML(http.StatusInternalServerError, "admin/cards.html", gin.H{
|
|
"Title": "卡片管理",
|
|
"Username": username,
|
|
"Error": "Failed to load cards",
|
|
"Cards": []models.Card{},
|
|
})
|
|
return
|
|
}
|
|
|
|
c.HTML(http.StatusOK, "admin/cards.html", gin.H{
|
|
"Title": "卡片管理",
|
|
"Username": username,
|
|
"Cards": cards,
|
|
})
|
|
}
|
|
|
|
// CardCreateGet renders the form for creating a new card.
|
|
func CardCreateGet(c *gin.Context) {
|
|
username, _ := c.Get("username")
|
|
c.HTML(http.StatusOK, "admin/card_form.html", gin.H{
|
|
"Title": "新建卡片",
|
|
"Username": username,
|
|
"Card": nil,
|
|
"IsEdit": false,
|
|
})
|
|
}
|
|
|
|
// CardCreatePost handles the form submission for creating a new card.
|
|
func CardCreatePost(c *gin.Context) {
|
|
card := &models.Card{
|
|
Icon: c.PostForm("icon"),
|
|
Title: c.PostForm("title"),
|
|
Subtitle: c.PostForm("subtitle"),
|
|
URL: c.PostForm("url"),
|
|
Enabled: c.PostForm("enabled") == "1",
|
|
}
|
|
|
|
if card.Title == "" || card.URL == "" {
|
|
username, _ := c.Get("username")
|
|
c.HTML(http.StatusOK, "admin/card_form.html", gin.H{
|
|
"Title": "新建卡片",
|
|
"Username": username,
|
|
"Card": card,
|
|
"IsEdit": false,
|
|
"Error": "标题和链接不能为空",
|
|
})
|
|
return
|
|
}
|
|
|
|
if err := models.CreateCard(card); err != nil {
|
|
username, _ := c.Get("username")
|
|
c.HTML(http.StatusOK, "admin/card_form.html", gin.H{
|
|
"Title": "新建卡片",
|
|
"Username": username,
|
|
"Card": card,
|
|
"IsEdit": false,
|
|
"Error": "创建失败: " + err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
}
|
|
|
|
// CardEditGet renders the form for editing an existing card.
|
|
func CardEditGet(c *gin.Context) {
|
|
username, _ := c.Get("username")
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
return
|
|
}
|
|
|
|
card, err := models.GetCardByID(id)
|
|
if err != nil || card == nil {
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
return
|
|
}
|
|
|
|
c.HTML(http.StatusOK, "admin/card_form.html", gin.H{
|
|
"Title": "编辑卡片",
|
|
"Username": username,
|
|
"Card": card,
|
|
"IsEdit": true,
|
|
})
|
|
}
|
|
|
|
// CardEditPost handles the form submission for updating an existing card.
|
|
func CardEditPost(c *gin.Context) {
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
return
|
|
}
|
|
|
|
card, err := models.GetCardByID(id)
|
|
if err != nil || card == nil {
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
return
|
|
}
|
|
|
|
card.Icon = c.PostForm("icon")
|
|
card.Title = c.PostForm("title")
|
|
card.Subtitle = c.PostForm("subtitle")
|
|
card.URL = c.PostForm("url")
|
|
card.Enabled = c.PostForm("enabled") == "1"
|
|
|
|
if card.Title == "" || card.URL == "" {
|
|
username, _ := c.Get("username")
|
|
c.HTML(http.StatusOK, "admin/card_form.html", gin.H{
|
|
"Title": "编辑卡片",
|
|
"Username": username,
|
|
"Card": card,
|
|
"IsEdit": true,
|
|
"Error": "标题和链接不能为空",
|
|
})
|
|
return
|
|
}
|
|
|
|
if err := models.UpdateCard(card); err != nil {
|
|
username, _ := c.Get("username")
|
|
c.HTML(http.StatusOK, "admin/card_form.html", gin.H{
|
|
"Title": "编辑卡片",
|
|
"Username": username,
|
|
"Card": card,
|
|
"IsEdit": true,
|
|
"Error": "更新失败: " + err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
}
|
|
|
|
// CardDelete handles deleting a card.
|
|
func CardDelete(c *gin.Context) {
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
return
|
|
}
|
|
|
|
_ = models.DeleteCard(id)
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
}
|
|
|
|
// CardToggle handles toggling a card's enabled status.
|
|
func CardToggle(c *gin.Context) {
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
return
|
|
}
|
|
|
|
_ = models.ToggleCard(id)
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
}
|
|
|
|
// CardMoveUp handles moving a card up in sort order.
|
|
func CardMoveUp(c *gin.Context) {
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
return
|
|
}
|
|
|
|
_ = models.MoveCardUp(id)
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
}
|
|
|
|
// CardMoveDown handles moving a card down in sort order.
|
|
func CardMoveDown(c *gin.Context) {
|
|
id, err := strconv.Atoi(c.Param("id"))
|
|
if err != nil {
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
return
|
|
}
|
|
|
|
_ = models.MoveCardDown(id)
|
|
c.Redirect(http.StatusFound, "/admin/cards")
|
|
}
|