Files
2026-06-23 14:57:36 +08:00

207 lines
7.0 KiB
Go

package main
import (
"flag"
"fmt"
"log"
"net"
"os"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"go_blog/config"
"go_blog/handlers"
"go_blog/middleware"
"go_blog/models"
)
func main() {
// 0. Parse command-line flags.
configFlag := flag.String("config", "", "path to config file (default: OS-aware path)")
flag.Parse()
// 1. Load configuration (auto-creates if missing).
cfg := config.LoadConfig(*configFlag)
// 2. Initialize the database (auto-migrates, seeds admin).
db := models.InitDB(cfg)
// 2b. Warm the platform configuration cache from the database.
models.LoadConfigCache(db)
// 3. Create session store (cookie-based).
store := cookie.NewStore([]byte(cfg.Secret))
store.Options(sessions.Options{
Path: "/",
MaxAge: 86400, // 24 hours
HttpOnly: true, // prevent XSS access
Secure: false, // set true in production with HTTPS
})
// 4. Create Gin router.
router := gin.Default()
// 5. Load HTML templates.
router.LoadHTMLGlob("templates/**/*.html")
// 6. Serve uploaded files (avatars etc.) from the storage path.
router.Static("/uploads", cfg.Path)
// 6. Global session middleware.
router.Use(sessions.Sessions("blog_session", store))
// 7. Global context middleware (sets IsLoggedIn, Username for templates).
router.Use(middleware.SetUserContext(db))
// 8. Register routes.
router.GET("/", handlers.HomePage(db))
router.GET("/search", handlers.SearchPage(db))
router.GET("/api/articles", handlers.HomeArticlesAPI(db))
router.GET("/rss", handlers.RSSFeed(db))
router.GET("/feed", handlers.RSSFeed(db))
router.GET("/login", handlers.LoginPage())
router.POST("/login", handlers.Login(db))
router.GET("/register", handlers.RegisterPage(db))
router.POST("/register", handlers.Register(db))
router.POST("/logout", handlers.Logout())
router.GET("/article/:slug", handlers.ArticleDetail(db))
router.POST("/article/:slug/comments", handlers.PostComment(db))
// Protected admin routes (admin role only).
admin := router.Group("/admin")
admin.Use(middleware.AuthRequired(), middleware.AdminRequired(db))
{
admin.GET("", handlers.AdminDashboard(db))
admin.GET("/articles", handlers.ArticleListPage(db))
admin.GET("/articles/new", handlers.ArticleCreatePage(db))
admin.POST("/articles/new", handlers.ArticleCreate(db))
admin.GET("/articles/:id/edit", handlers.ArticleEditPage(db))
admin.POST("/articles/:id/edit", handlers.ArticleUpdate(db))
admin.POST("/articles/:id/delete", handlers.ArticleDelete(db))
}
// Protected admin comment management routes (admin role only).
comments := router.Group("/admin/comments")
comments.Use(middleware.AuthRequired(), middleware.AdminRequired(db))
{
comments.GET("", handlers.CommentListPage(db))
comments.POST("/:id/approve", handlers.CommentApprove(db))
comments.POST("/:id/reject", handlers.CommentReject(db))
comments.POST("/:id/delete", handlers.CommentDelete(db))
}
// Protected admin user-management routes (admin role only).
users := router.Group("/admin/users")
users.Use(middleware.AuthRequired(), middleware.AdminRequired(db))
{
users.GET("", handlers.UserListPage(db))
users.GET("/new", handlers.UserCreatePage(db))
users.POST("/new", handlers.UserCreate(db))
users.GET("/:id/edit", handlers.UserEditPage(db))
users.POST("/:id/edit", handlers.UserUpdate(db))
users.POST("/:id/delete", handlers.UserDelete(db))
}
// Protected article attachment routes (admin role only).
attachments := router.Group("/admin/articles")
attachments.Use(middleware.AuthRequired(), middleware.AdminRequired(db))
{
attachments.POST("/attachments", handlers.UploadAttachment(db, cfg.Path))
attachments.POST("/attachments/:id/delete", handlers.DeleteAttachment(db, cfg.Path))
attachments.GET("/:id/attachments", handlers.ListAttachments(db))
}
// Protected admin settings routes (platform configuration).
settings := router.Group("/admin/settings")
settings.Use(middleware.AuthRequired(), middleware.AdminRequired(db))
{
settings.GET("/site", handlers.SiteSettingsPage(db))
settings.POST("/site", handlers.SiteSettingsSave(db, cfg.Path))
settings.GET("/navlinks", handlers.NavLinksSettingsPage(db))
settings.POST("/navlinks", handlers.NavLinksSettingsSave(db))
settings.GET("/upload", handlers.UploadSettingsPage(db))
settings.POST("/upload", handlers.UploadSettingsSave(db))
settings.GET("/download", handlers.DownloadSettingsPage(db))
settings.POST("/download", handlers.DownloadSettingsSave(db))
settings.GET("/comments", handlers.CommentSettingsPage(db))
settings.POST("/comments", handlers.CommentSettingsSave(db))
}
// Protected admin analytics routes (reading statistics).
analytics := router.Group("/admin/analytics")
analytics.Use(middleware.AuthRequired(), middleware.AdminRequired(db))
{
analytics.GET("/views", handlers.ViewAnalyticsPage(db))
}
// Protected profile routes.
profile := router.Group("/profile")
profile.Use(middleware.AuthRequired())
{
profile.GET("", handlers.ProfilePage(db))
profile.POST("", handlers.UpdateProfile(db, cfg.Path))
profile.POST("/avatar", handlers.UploadAvatar(db, cfg.Path))
}
// Protected user article management routes (for non-admin users).
myArticles := router.Group("/my")
myArticles.Use(middleware.AuthRequired())
{
myArticles.GET("/articles", handlers.MyArticlesPage(db))
myArticles.GET("/articles/new", handlers.MyArticleCreatePage(db))
myArticles.POST("/articles/new", handlers.MyArticleCreate(db))
myArticles.GET("/articles/:id/edit", handlers.MyArticleEditPage(db))
myArticles.POST("/articles/:id/edit", handlers.MyArticleUpdate(db))
myArticles.POST("/articles/:id/delete", handlers.MyArticleDelete(db))
}
// Protected article attachment routes for user articles.
myAttachments := router.Group("/my/articles")
myAttachments.Use(middleware.AuthRequired())
{
myAttachments.POST("/attachments", handlers.UploadAttachment(db, cfg.Path))
myAttachments.POST("/attachments/:id/delete", handlers.DeleteAttachment(db, cfg.Path))
myAttachments.GET("/:id/attachments", handlers.ListAttachments(db))
}
// 9. Start the server.
webPort := cfg.Web.Port
socketPath := cfg.Web.Socket
usePort := webPort != "" && webPort != "0"
useSocket := socketPath != ""
if !usePort && !useSocket {
log.Fatalf("Neither port nor socket is configured — at least one must be enabled")
}
if usePort {
go func() {
addr := fmt.Sprintf(":%s", webPort)
log.Printf("Go Blog starting on http://localhost%s", addr)
if err := router.Run(addr); err != nil {
log.Fatalf("Failed to start HTTP server: %v", err)
}
}()
}
if useSocket {
go func() {
os.Remove(socketPath) // remove stale socket file if exists
listener, err := net.Listen("unix", socketPath)
if err != nil {
log.Fatalf("Failed to listen on unix socket %s: %v", socketPath, err)
}
log.Printf("Go Blog starting on unix socket %s", socketPath)
if err := router.RunListener(listener); err != nil {
log.Fatalf("Failed to serve on unix socket: %v", err)
}
}()
}
// Block forever.
select {}
}