package handlers import ( "net/http" "regexp" "strings" "time" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" "go_blog/models" "gorm.io/gorm" ) // slugRe matches characters that are not URL-safe. var slugRe = regexp.MustCompile(`[^a-z0-9\-]+`) // generateSlug converts a title into a URL-friendly slug. func generateSlug(title string) string { s := strings.ToLower(title) s = strings.ReplaceAll(s, " ", "-") s = slugRe.ReplaceAllString(s, "") s = strings.Trim(s, "-") // Collapse multiple dashes. s = regexp.MustCompile(`-{2,}`).ReplaceAllString(s, "-") return s } // ArticleCreatePage renders the article creation form. func ArticleCreatePage(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { tr := getTr(c) data := DefaultData(c) data["Title"] = tr["article_create_title"] c.HTML(http.StatusOK, "article_create", data) } } // ArticleCreate handles the POST request to create a new article. func ArticleCreate(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { tr := getTr(c) title := strings.TrimSpace(c.PostForm("title")) content := strings.TrimSpace(c.PostForm("content")) slug := strings.TrimSpace(c.PostForm("slug")) summary := strings.TrimSpace(c.PostForm("summary")) cover := strings.TrimSpace(c.PostForm("cover")) statusStr := c.PostForm("status") isTop := c.PostForm("is_top") == "1" // Validate required fields. if title == "" { data := DefaultData(c) data["Title"] = tr["article_create_title"] data["Error"] = tr["article_title_required"] data["FormTitle"] = title data["FormSlug"] = slug data["FormSummary"] = summary data["FormContent"] = content data["FormCover"] = cover data["FormStatus"] = statusStr c.HTML(http.StatusOK, "article_create", data) return } if content == "" { data := DefaultData(c) data["Title"] = tr["article_create_title"] data["Error"] = tr["article_content_required"] data["FormTitle"] = title data["FormSlug"] = slug data["FormSummary"] = summary data["FormContent"] = content data["FormCover"] = cover data["FormStatus"] = statusStr c.HTML(http.StatusOK, "article_create", data) return } // Auto-generate slug if empty. if slug == "" { slug = generateSlug(title) } // Parse status: default to draft (0). status := models.ArticleDraft if statusStr == "1" { status = models.ArticlePublished } // Get author ID from session. session := sessions.Default(c) userID := session.Get("user_id") if userID == nil { c.Redirect(http.StatusFound, "/login") return } // The session stores user.ID, which is a gorm uint — but be defensive // across int / uint / int64 storage so a type mismatch never panics. var authorID uint switch v := userID.(type) { case uint: authorID = v case int: authorID = uint(v) case int64: authorID = uint(v) case float64: authorID = uint(v) default: c.Redirect(http.StatusFound, "/login") return } var publishedAt *time.Time if status == models.ArticlePublished { now := time.Now() publishedAt = &now } article := models.Article{ AuthorID: authorID, Title: title, Slug: slug, Summary: summary, Content: content, Cover: cover, Status: status, IsTop: isTop, PublishedAt: publishedAt, } if err := db.Create(&article).Error; err != nil { data := DefaultData(c) data["Title"] = tr["article_create_title"] data["Error"] = tr["article_error"] data["FormTitle"] = title data["FormSlug"] = slug data["FormSummary"] = summary data["FormContent"] = content data["FormCover"] = cover data["FormStatus"] = statusStr c.HTML(http.StatusOK, "article_create", data) return } // Success: redirect to dashboard. c.Redirect(http.StatusFound, "/admin") } }