package handlers import ( "context" "net/http" "strings" "time" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo/options" "yh_web/server/config" "yh_web/server/models" "github.com/gin-gonic/gin" ) // effectivePagePath 对外访问路径:优先 route_path,否则 /{slug};index 且无 route_path 时返回空(由首页单独处理) func effectivePagePath(p models.Page) string { if p.RoutePath != "" { path := strings.TrimSpace(p.RoutePath) if !strings.HasPrefix(path, "/") { path = "/" + path } return path } if p.Slug == "" || p.Slug == homepageSlug { return "" } return "/" + p.Slug } // GetWebRoutes 前台:获取站点已发布页面的动态路由列表(无需鉴权) func GetWebRoutes(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if config.MongoClient == nil { c.JSON(http.StatusOK, gin.H{"site_id": "", "routes": []any{}}) return } siteID := c.Query("site_id") if siteID == "" { siteID = getOfficialSiteID(ctx) } if siteID == "" { c.JSON(http.StatusOK, gin.H{"site_id": "", "routes": []any{}}) return } coll := config.GetDB(config.DBName).Collection("pages") opts := options.Find().SetSort(bson.D{{Key: "updated_at", Value: -1}}) cursor, err := coll.Find(ctx, bson.M{"site_id": siteID}, opts) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(ctx) var pages []models.Page if err = cursor.All(ctx, &pages); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } routes := make([]gin.H, 0) for _, p := range pages { if p.Published != nil && !*p.Published { continue } path := effectivePagePath(p) if path == "" { continue } routes = append(routes, gin.H{ "path": path, "title": p.Title, "slug": p.Slug, "id": p.ID.Hex(), "mode": p.ContentMode, }) } c.JSON(http.StatusOK, gin.H{"site_id": siteID, "routes": routes}) } // GetWebPageByPath 前台:按路径取单页内容(无需鉴权) func GetWebPageByPath(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if config.MongoClient == nil { c.JSON(http.StatusServiceUnavailable, gin.H{"error": "服务暂不可用"}) return } siteID := c.Query("site_id") if siteID == "" { siteID = getOfficialSiteID(ctx) } path := strings.TrimSpace(c.Query("path")) if siteID == "" || path == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "缺少 site_id 或 path"}) return } if !strings.HasPrefix(path, "/") { path = "/" + path } coll := config.GetDB(config.DBName).Collection("pages") var page models.Page tryDecode := func(filter bson.M) bool { page = models.Page{} err := coll.FindOne(ctx, filter).Decode(&page) return err == nil && !page.ID.IsZero() } if !tryDecode(bson.M{"site_id": siteID, "route_path": path}) { alt := strings.TrimPrefix(path, "/") if alt != "" { tryDecode(bson.M{"site_id": siteID, "route_path": alt}) } } if page.ID.IsZero() { slug := strings.TrimPrefix(path, "/") if slug != "" { tryDecode(bson.M{"site_id": siteID, "slug": slug}) } } if page.ID.IsZero() { c.JSON(http.StatusNotFound, gin.H{"error": "页面不存在"}) return } if page.Published != nil && !*page.Published { c.JSON(http.StatusNotFound, gin.H{"error": "页面未发布"}) return } c.JSON(http.StatusOK, gin.H{ "id": page.ID.Hex(), "site_id": page.SiteID, "slug": page.Slug, "title": page.Title, "type": page.Type, "content": page.Content, "content_mode": page.ContentMode, "route_path": page.RoutePath, "updated_at": page.UpdatedAt, }) }