up
This commit is contained in:
@@ -3,10 +3,12 @@ package main
|
||||
import (
|
||||
"html/template"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"simple_portal/config"
|
||||
"simple_portal/database"
|
||||
"simple_portal/handlers"
|
||||
"simple_portal/middleware"
|
||||
@@ -15,8 +17,8 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// loadTemplates loads HTML templates from templates/ directory recursively.
|
||||
// Custom implementation because Go's ParseGlob has issues with directories on Windows.
|
||||
// loadTemplates 加载 templates/ 目录下所有 HTML 模板
|
||||
// 自定义实现,因为 Go 的 ParseGlob 在 Windows 下有路径问题
|
||||
func loadTemplates() *template.Template {
|
||||
funcMap := template.FuncMap{
|
||||
"hasPrefix": strings.HasPrefix,
|
||||
@@ -24,7 +26,6 @@ func loadTemplates() *template.Template {
|
||||
"add": func(a, b int) int { return a + b },
|
||||
}
|
||||
t := template.New("").Funcs(funcMap)
|
||||
// 收集所有 .html 模板文件路径
|
||||
var files []string
|
||||
filepath.Walk("templates", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
@@ -38,7 +39,6 @@ func loadTemplates() *template.Template {
|
||||
if len(files) == 0 {
|
||||
log.Fatal("No template files found in templates/")
|
||||
}
|
||||
// 将 Windows 反斜杠路径转为正斜杠,避免模板名问题
|
||||
for i, f := range files {
|
||||
files[i] = filepath.ToSlash(f)
|
||||
}
|
||||
@@ -51,24 +51,30 @@ func loadTemplates() *template.Template {
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Initialize database
|
||||
// 加载配置文件(自动生成 + 补全缺失项)
|
||||
if err := config.Load(); err != nil {
|
||||
log.Fatalf("加载配置失败: %v", err)
|
||||
}
|
||||
log.Printf("配置加载成功,数据目录: %s,数据库: %s", config.Cfg.Data.Dir, config.Cfg.Database.Type)
|
||||
|
||||
// 初始化数据库
|
||||
if err := database.InitDB(); err != nil {
|
||||
log.Fatalf("Failed to initialize database: %v", err)
|
||||
log.Fatalf("初始化数据库失败: %v", err)
|
||||
}
|
||||
defer database.CloseDB()
|
||||
|
||||
// Create uploads directory
|
||||
if err := os.MkdirAll(filepath.Join(".", "data", "uploads"), 0755); err != nil {
|
||||
log.Fatalf("Failed to create uploads directory: %v", err)
|
||||
// 创建上传目录
|
||||
if err := os.MkdirAll(config.GetUploadDir(), 0755); err != nil {
|
||||
log.Fatalf("创建上传目录失败: %v", err)
|
||||
}
|
||||
|
||||
// Create session store
|
||||
// 创建 session 存储
|
||||
sessionStore := session.NewSessionStore()
|
||||
|
||||
// Create IP ban guard (in-memory fail counter)
|
||||
// 创建 IP 封禁守护
|
||||
ipBanGuard := middleware.NewIPBanGuard()
|
||||
|
||||
// Set Gin mode
|
||||
// 设置 Gin 模式
|
||||
ginMode := os.Getenv("GIN_MODE")
|
||||
if ginMode == "" {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
@@ -76,43 +82,43 @@ func main() {
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
// Load HTML templates (custom loader for nested directories)
|
||||
// 加载 HTML 模板
|
||||
r.SetHTMLTemplate(loadTemplates())
|
||||
|
||||
// Serve static files
|
||||
// 静态文件
|
||||
r.Static("/static", "./static")
|
||||
|
||||
// Inject session store and IP ban guard into context for handlers
|
||||
// 注入 session 和 IP 封禁守护
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("sessionStore", sessionStore)
|
||||
c.Set("ipBanGuard", ipBanGuard)
|
||||
c.Next()
|
||||
})
|
||||
|
||||
// Public routes (home page and uploads — no IP restriction)
|
||||
// 公开路由
|
||||
r.GET("/", handlers.HomeHandler)
|
||||
r.GET("/click/:id", handlers.CardClickHandler)
|
||||
r.GET("/search", handlers.SearchHandler)
|
||||
r.GET("/uploads/:filename", handlers.ServeUploadHandler)
|
||||
|
||||
// Admin routes with IP whitelist check applied to all /admin/* routes
|
||||
// 后台路由(IP 白名单)
|
||||
adminGroup := r.Group("/admin")
|
||||
adminGroup.Use(middleware.IPWhitelistRequired(func(sessionID string) bool {
|
||||
return sessionStore.Get(sessionID) != nil
|
||||
}))
|
||||
{
|
||||
// Public admin routes (login — no auth required, but IP whitelist applies)
|
||||
// 登录(无需认证,受 IP 白名单限制)
|
||||
adminGroup.GET("/login", handlers.LoginGet)
|
||||
adminGroup.POST("/login", handlers.LoginPost)
|
||||
|
||||
// Protected admin routes (auth required)
|
||||
// 需要认证的后台路由
|
||||
protected := adminGroup.Group("")
|
||||
protected.Use(middleware.AuthRequired(sessionStore))
|
||||
{
|
||||
protected.POST("/logout", handlers.Logout)
|
||||
protected.GET("/", handlers.AdminIndex)
|
||||
|
||||
// Cards management
|
||||
// 卡片管理
|
||||
protected.GET("/cards", handlers.CardsList)
|
||||
protected.GET("/cards/new", handlers.CardCreateGet)
|
||||
protected.POST("/cards", handlers.CardCreatePost)
|
||||
@@ -123,39 +129,50 @@ func main() {
|
||||
protected.POST("/cards/:id/move-up", handlers.CardMoveUp)
|
||||
protected.POST("/cards/:id/move-down", handlers.CardMoveDown)
|
||||
|
||||
// Image upload
|
||||
// 图片上传
|
||||
protected.POST("/upload", handlers.UploadHandler)
|
||||
|
||||
// Settings
|
||||
// 设置
|
||||
protected.GET("/settings", handlers.SettingsGet)
|
||||
protected.POST("/settings", handlers.SettingsPost)
|
||||
|
||||
// Security: login logs
|
||||
// 安全:登录日志
|
||||
protected.GET("/logs", handlers.LoginLogsGet)
|
||||
protected.POST("/logs/unban/:id", handlers.UnbanIP)
|
||||
|
||||
// Security: change password
|
||||
// 安全:修改密码
|
||||
protected.GET("/password", handlers.ChangePasswordGet)
|
||||
protected.POST("/password", handlers.ChangePasswordPost)
|
||||
|
||||
// Security: IP whitelist management
|
||||
// 安全:IP 白名单
|
||||
protected.GET("/ip-whitelist", handlers.IPWhitelistGet)
|
||||
protected.POST("/ip-whitelist/add", handlers.IPWhitelistAdd)
|
||||
protected.POST("/ip-whitelist/:id/delete", handlers.IPWhitelistDelete)
|
||||
|
||||
// Analytics: access logs
|
||||
// 分析:访问日志
|
||||
protected.GET("/access-logs", handlers.AccessLogsGet)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine port
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
}
|
||||
|
||||
log.Printf("Starting Portal server on :%s", port)
|
||||
if err := r.Run(":" + port); err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
// 启动服务器
|
||||
if config.Cfg.Server.Unix != "" {
|
||||
// Unix socket 模式
|
||||
listener, err := net.Listen("unix", config.Cfg.Server.Unix)
|
||||
if err != nil {
|
||||
log.Fatalf("监听 Unix socket 失败: %v", err)
|
||||
}
|
||||
// 设置 socket 文件权限,允许 nginx 等其他进程访问
|
||||
os.Chmod(config.Cfg.Server.Unix, 0666)
|
||||
log.Printf("启动 Portal 服务器,监听 Unix socket: %s", config.Cfg.Server.Unix)
|
||||
if err := r.RunListener(listener); err != nil {
|
||||
log.Fatalf("服务器启动失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
// TCP 模式
|
||||
addr := config.Cfg.Server.Addr
|
||||
log.Printf("启动 Portal 服务器,监听: %s", addr)
|
||||
if err := r.Run(addr); err != nil {
|
||||
log.Fatalf("服务器启动失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user