Files
kevin c16a8dfbc4 feat: 门户网站初始提交
- Go + Gin + html/template 服务端渲染
- 主页:Google 风格搜索框 + 导航卡片
- 后台:卡片 CRUD、搜索引擎配置、主页背景/标题配置
- 图片上传:支持 jpg/jpeg/png/gif,自动压缩,缩略图参数 ?thumb=1
- 安全:登录日志、修改密码、IP 自动封禁、IP 白名单
- 访问统计:主页访问/卡片点击/搜索追踪、实时流量、IP 统计
- SQLite 存储(modernc.org/sqlite,纯 Go)
- 内存 Session + bcrypt 密码哈希
2026-05-28 13:54:07 +08:00

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