package handlers
import (
"context"
"encoding/json"
"net/http"
"strings"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
"yh_web/server/config"
"yh_web/server/models"
"github.com/gin-gonic/gin"
)
const homepageSlug = "index"
const officialSiteConfigID = "official_site_id"
type officialSiteDoc struct {
ID string `bson:"_id"`
SiteID string `bson:"site_id"`
}
// getOfficialSiteID 从 system_config 读取官网站点 ID;未设置则返回第一个站点的 ID
func getOfficialSiteID(ctx context.Context) string {
coll := config.GetDB(config.DBName).Collection("system_config")
var doc officialSiteDoc
err := coll.FindOne(ctx, bson.M{"_id": officialSiteConfigID}).Decode(&doc)
if err == nil && doc.SiteID != "" {
return doc.SiteID
}
sitesColl := config.GetDB(config.DBName).Collection("sites")
opts := options.FindOne().SetSort(bson.D{{Key: "created_at", Value: -1}})
var site models.Site
if err := sitesColl.FindOne(ctx, bson.M{}, opts).Decode(&site); err == nil {
return site.ID.Hex()
}
return ""
}
// GetWebHomepage 前台:获取官网站点首页数据(无需鉴权)
func GetWebHomepage(c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
siteID := getOfficialSiteID(ctx)
if siteID == "" {
c.JSON(http.StatusOK, defaultHomepageData())
return
}
coll := config.GetDB(config.DBName).Collection("pages")
var page models.Page
err := coll.FindOne(ctx, bson.M{"site_id": siteID, "slug": homepageSlug}).Decode(&page)
if err != nil {
if err == mongo.ErrNoDocuments {
c.JSON(http.StatusOK, defaultHomepageData())
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
var data models.HomepageData
if page.Content != "" {
if err := json.Unmarshal([]byte(page.Content), &data); err != nil {
c.JSON(http.StatusOK, defaultHomepageData())
return
}
} else {
data = defaultHomepageData()
}
c.JSON(http.StatusOK, data)
}
// GetHomepage 获取站点首页数据
func GetHomepage(c *gin.Context) {
siteID := c.Param("site_id")
if siteID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "请提供 site_id"})
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
coll := config.GetDB(config.DBName).Collection("pages")
var page models.Page
err := coll.FindOne(ctx, bson.M{"site_id": siteID, "slug": homepageSlug}).Decode(&page)
if err != nil {
if err == mongo.ErrNoDocuments {
c.JSON(http.StatusOK, defaultHomepageData())
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
var data models.HomepageData
if page.Content != "" {
if err := json.Unmarshal([]byte(page.Content), &data); err != nil {
c.JSON(http.StatusOK, defaultHomepageData())
return
}
} else {
data = defaultHomepageData()
}
c.JSON(http.StatusOK, data)
}
// UpdateHomepage 更新站点首页数据
func UpdateHomepage(c *gin.Context) {
siteID := c.Param("site_id")
if siteID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "请提供 site_id"})
return
}
var data models.HomepageData
if err := c.ShouldBindJSON(&data); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
body, err := json.Marshal(data)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
coll := config.GetDB(config.DBName).Collection("pages")
now := time.Now().Format(time.RFC3339)
filter := bson.M{"site_id": siteID, "slug": homepageSlug}
update := bson.M{
"$set": bson.M{
"site_id": siteID,
"slug": homepageSlug,
"title": data.Title,
"type": "homepage",
"content": string(body),
"updated_at": now,
},
}
opts := options.UpdateOne().SetUpsert(true)
_, err = coll.UpdateOne(ctx, filter, update, opts)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "保存成功"})
}
// DownloadHomepage 下载首页 HTML
func DownloadHomepage(c *gin.Context) {
siteID := c.Param("site_id")
if siteID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "请提供 site_id"})
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
coll := config.GetDB(config.DBName).Collection("pages")
var page models.Page
var data models.HomepageData
err := coll.FindOne(ctx, bson.M{"site_id": siteID, "slug": homepageSlug}).Decode(&page)
if err == nil && page.Content != "" {
_ = json.Unmarshal([]byte(page.Content), &data)
}
if err != nil || page.Content == "" {
data = defaultHomepageData()
}
html := renderHomepageHTML(&data)
c.Header("Content-Disposition", "attachment; filename=index.html")
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(html))
}
func defaultHomepageData() models.HomepageData {
return models.HomepageData{
LogoText: "YUHENG ONE",
NavLinks: []models.NavLink{{Label: "MISSION", URL: "#"}, {Label: "DOWNLOAD", URL: "#"}, {Label: "CONTACT", URL: "#"}},
Title: "宇恒一号",
Subtitle: "INTERSTELLAR EXPLORER EDITION",
Description: "跨越星际的智能伙伴 · 探索无限可能
\n 引领您进入前所未有的数字宇宙",
DownloadText: "START EXPLORING",
DownloadURL: "#",
Platforms: []models.PlatformItem{
{Name: "WINDOWS", URL: "#"},
{Name: "MACOS", URL: "#"},
{Name: "LINUX", URL: "#"},
{Name: "IOS", URL: "#"},
{Name: "ANDROID", URL: "#"},
},
Version: "VERSION 3.2.1",
LaunchYear: "LAUNCH: 2024",
BadgeText: "FREE ACCESS",
Features: []models.FeatureItem{
{Title: "星际导航", Desc: "先进的AI导航系统,精准定位您的需求,引领探索之旅"},
{Title: "量子同步", Desc: "跨维度数据同步技术,您的数据在多宇宙中保持一致"},
{Title: "星际防护", Desc: "来自未来的安全加密协议,守护您的数字资产安全"},
},
FooterText: "© 2024 YUHENG ONE // STELLAR EXPLORATION INITIATIVE",
}
}
// renderHomepageHTML 根据数据生成首页 HTML(简化版,保留原样式与结构)
func renderHomepageHTML(d *models.HomepageData) string {
if d == nil {
d = &models.HomepageData{}
}
titleChars := splitTitle(d.Title)
navHTML := ""
for _, l := range d.NavLinks {
navHTML += `` + escape(l.Label) + ``
}
platformsHTML := ""
for _, p := range d.Platforms {
platformsHTML += `
` + escape(f.Desc) + `
") sb.WriteString(escape(d.Subtitle)) sb.WriteString("
\n")
sb.WriteString(strings.ReplaceAll(escape(d.Description), "\n", "
\n"))
sb.WriteString("