From 6b74e1d208123c8a07a0fbb4dd35c6fce783ccb6 Mon Sep 17 00:00:00 2001 From: deepzz0 Date: Tue, 27 Apr 2021 16:16:32 +0800 Subject: [PATCH] chore: rename field name --- cmd/blog/main.go | 11 +- conf/tpl/sitemapTpl.xml | 2 +- go.mod | 2 + go.sum | 4 + pkg/cache/cache.go | 14 +- pkg/cache/store/mongodb.go | 6 +- pkg/core/blog/admin/admin.go | 52 +++ pkg/core/blog/api.go | 50 +-- pkg/core/blog/page/be.go | 61 +++ pkg/core/blog/page/fe.go | 386 ++++++++++++++++++ pkg/core/blog/page/page.go | 374 +---------------- pkg/model/account.go | 22 +- pkg/model/archive.go | 4 +- pkg/model/article.go | 28 +- pkg/model/blogger.go | 14 +- pkg/model/series.go | 10 +- .../{backLayout.html => adminLayout.html} | 0 17 files changed, 586 insertions(+), 454 deletions(-) create mode 100644 pkg/core/blog/admin/admin.go create mode 100644 pkg/core/blog/page/be.go create mode 100644 pkg/core/blog/page/fe.go rename website/admin/{backLayout.html => adminLayout.html} (100%) diff --git a/cmd/blog/main.go b/cmd/blog/main.go index 8ffc79f..99757d0 100644 --- a/cmd/blog/main.go +++ b/cmd/blog/main.go @@ -6,6 +6,8 @@ import ( "path/filepath" "github.com/eiblog/eiblog/pkg/config" + "github.com/eiblog/eiblog/pkg/core/blog" + "github.com/eiblog/eiblog/pkg/core/blog/admin" "github.com/eiblog/eiblog/pkg/core/blog/file" "github.com/eiblog/eiblog/pkg/core/blog/page" "github.com/eiblog/eiblog/pkg/core/blog/swag" @@ -51,8 +53,15 @@ func runHTTPServer(endRun chan bool) { page.RegisterRoutes(e) // static files file.RegisterRoutes(e) + // unauthz api + admin.RegisterRoutes(e) - // api router + // admin router + group := e.Group("/admin", blog.AuthFilter) + { + page.RegisterRoutesAuthz(group) + admin.RegisterRoutesAuthz(group) + } // start address := fmt.Sprintf(":%d", config.Conf.BlogApp.HTTPPort) diff --git a/conf/tpl/sitemapTpl.xml b/conf/tpl/sitemapTpl.xml index c894e58..8320683 100644 --- a/conf/tpl/sitemapTpl.xml +++ b/conf/tpl/sitemapTpl.xml @@ -3,7 +3,7 @@ {{range .Articles}} https://{{$.Host}}/post/{{.Slug}}.html - {{dateformat .CreateTime "2006-01-02"}} + {{dateformat .CreatedAt "2006-01-02"}} 0.6 {{end}} diff --git a/go.mod b/go.mod index 46e44a9..34afde8 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 + github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753 github.com/gin-contrib/sessions v0.0.3 @@ -17,5 +18,6 @@ require ( go.mongodb.org/mongo-driver v1.5.1 google.golang.org/grpc v1.35.0 google.golang.org/protobuf v1.25.0 + gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect gopkg.in/yaml.v2 v2.3.0 ) diff --git a/go.sum b/go.sum index 69e1812..1d7f0b0 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f h1:hjWy8ptp0ggYgv/3A8dixSB9KTRgDcZH2D3Ap5hrwOs= +github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f/go.mod h1:8jMj6ab9czIU5udq3ovaK9/5sCIyQ1JWteFMn8w2QRI= github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae h1:V6YC640Gs5jEUYfCimyuXsTW5gzNcIEESG4MGmOJCtA= github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae/go.mod h1:HzHqTCGEAkSSzBM3shBvQHsHRQYUvjNOIC4mHipZ6tI= github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753 h1:Nygjtnh1nF5zejJF7pZnsoFh77wOPS+jlfhikjkJg60= @@ -350,6 +352,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 1f6cbae..4cf3e6f 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -235,7 +235,7 @@ func (c *Cache) rebuildArticle(article *model.Article, needSort bool) { } // series for i, series := range c.Series { - if series.ID == article.SerieID { + if series.ID == article.SeriesID { c.Series[i].Articles = append(c.Series[i].Articles, article) if needSort { sort.Sort(c.Series[i].Articles) @@ -244,7 +244,7 @@ func (c *Cache) rebuildArticle(article *model.Article, needSort bool) { } } // archive - y, m, _ := article.CreateTime.Date() + y, m, _ := article.CreatedAt.Date() for i, archive := range c.Archives { if ay, am, _ := archive.Time.Date(); y == ay && m == am { c.Archives[i].Articles = append(c.Archives[i].Articles, article) @@ -257,7 +257,7 @@ func (c *Cache) rebuildArticle(article *model.Article, needSort bool) { } // 新建归档 c.Archives = append(c.Archives, &model.Archive{ - Time: article.CreateTime, + Time: article.CreatedAt, Articles: model.SortedArticles{article}, }) if needSort { // 重建归档 @@ -282,7 +282,7 @@ func (c *Cache) regeneratePages() { for _, article := range series.Articles { //eg. * [标题一](/post/hello-world.html) (Man 02, 2006) str := fmt.Sprintf(`* [%s](/post/%s.html) (%s)`, - article.Title, article.Slug, article.CreateTime.Format("Jan 02, 2006")) + article.Title, article.Slug, article.CreatedAt.Format("Jan 02, 2006")) buf.WriteString(str) } buf.WriteString("\n\n") @@ -291,7 +291,7 @@ func (c *Cache) regeneratePages() { case pageArchive: sort.Sort(c.Archives) buf := bytes.Buffer{} - buf.WriteString(c.Blogger.ArchivesSay + "\n") + buf.WriteString(c.Blogger.ArchiveSay + "\n") var ( currentYear string gt12Month = len(Ei.Archives) > 12 @@ -309,11 +309,11 @@ func (c *Cache) regeneratePages() { for i, article := range archive.Articles { if i == 0 && gt12Month { str := fmt.Sprintf(`* *[%s](/post/%s.html) (%s)`, - article.Title, article.Slug, article.CreateTime.Format("Jan 02, 2006")) + article.Title, article.Slug, article.CreatedAt.Format("Jan 02, 2006")) buf.WriteString(str) } else { str := fmt.Sprintf(`* [%s](/post/%s.html) (%s)`, - article.Title, article.Slug, article.CreateTime.Format("Jan 02, 2006")) + article.Title, article.Slug, article.CreatedAt.Format("Jan 02, 2006")) buf.WriteString(str) } buf.WriteByte('\n') diff --git a/pkg/cache/store/mongodb.go b/pkg/cache/store/mongodb.go index 831eae1..70c440c 100644 --- a/pkg/cache/store/mongodb.go +++ b/pkg/cache/store/mongodb.go @@ -52,21 +52,21 @@ func (db *mongodb) Init(source string) (Store, error) { db.Client = client // create index indexModel := mongo.IndexModel{ - Keys: bson.D{{"username", 1}}, + Keys: bson.D{bson.E{Key: "username", Value: 1}}, Options: options.Index().SetUnique(true).SetSparse(true), } db.Database(mongoDBName).Collection(collectionAccount). Indexes(). CreateOne(context.Background(), indexModel) indexModel = mongo.IndexModel{ - Keys: bson.D{{"slug", 1}}, + Keys: bson.D{bson.E{Key: "slug", Value: 1}}, Options: options.Index().SetUnique(true).SetSparse(true), } db.Database(mongoDBName).Collection(collectionArticle). Indexes(). CreateOne(context.Background(), indexModel) indexModel = mongo.IndexModel{ - Keys: bson.D{{"slug", 1}}, + Keys: bson.D{bson.E{Key: "slug", Value: 1}}, Options: options.Index().SetUnique(true).SetSparse(true), } db.Database(mongoDBName).Collection(collectionSeries). diff --git a/pkg/core/blog/admin/admin.go b/pkg/core/blog/admin/admin.go new file mode 100644 index 0000000..0d63537 --- /dev/null +++ b/pkg/core/blog/admin/admin.go @@ -0,0 +1,52 @@ +// Package admin provides ... +package admin + +import ( + "context" + "net/http" + "time" + + "github.com/eiblog/eiblog/pkg/cache" + "github.com/eiblog/eiblog/pkg/core/blog" + "github.com/eiblog/eiblog/tools" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" +) + +// RegisterRoutes register routes +func RegisterRoutes(e *gin.Engine) { + e.POST("/admin/login", handleAcctLogin) +} + +// RegisterRoutesAuthz register routes +func RegisterRoutesAuthz(group gin.IRoutes) { +} + +// handleAcctLogin 登录接口 +func handleAcctLogin(c *gin.Context) { + user := c.PostForm("user") + pwd := c.PostForm("password") + // code := c.PostForm("code") // 二次验证 + if user == "" || pwd == "" { + logrus.Warnf("参数错误: %s %s", user, pwd) + c.Redirect(http.StatusFound, "/admin/login") + return + } + if cache.Ei.Account.Username != user || + cache.Ei.Account.Password != tools.EncryptPasswd(user, pwd) { + logrus.Warnf("账号或密码错误 %s, %s", user, pwd) + c.Redirect(http.StatusFound, "/admin/login") + return + } + // 登录成功 + blog.SetLogin(c, user) + + cache.Ei.Account.LoginIP = c.ClientIP() + cache.Ei.Account.LoginAt = time.Now() + cache.Ei.UpdateAccount(context.Background(), user, map[string]interface{}{ + "login_ip": cache.Ei.Account.LoginIP, + "login_at": cache.Ei.Account.LoginAt, + }) + c.Redirect(http.StatusFound, "/admin/profile") +} diff --git a/pkg/core/blog/api.go b/pkg/core/blog/api.go index 8ad75f2..f8cca50 100644 --- a/pkg/core/blog/api.go +++ b/pkg/core/blog/api.go @@ -1,5 +1,5 @@ -// Package eiblog provides ... -package eiblog +// Package blog provides ... +package blog import ( "net/http" @@ -14,67 +14,43 @@ import ( // @BasePath /api -// LogStatus log status -type LogStatus int - -// user log status -var ( - LogStatusOut LogStatus = 0 - LogStatusTFA LogStatus = 1 - LogStatusIn LogStatus = 2 -) - // AuthFilter auth filter func AuthFilter(c *gin.Context) { if !IsLogined(c) { c.Abort() c.Status(http.StatusUnauthorized) + c.Redirect(http.StatusFound, "/admin/login") return } c.Next() } -// SetLogStatus login user -func SetLogStatus(c *gin.Context, uid string, status LogStatus) { +// SetLogin login user +func SetLogin(c *gin.Context, username string) { session := sessions.Default(c) - session.Set("uid", uid) - session.Set("status", int(status)) + session.Set("username", username) session.Save() } // SetLogout logout user func SetLogout(c *gin.Context) { session := sessions.Default(c) - session.Set("status", int(LogStatusOut)) + session.Delete("username") session.Save() } // IsLogined account logined func IsLogined(c *gin.Context) bool { - status := GetLogStatus(c) - if status < 0 { - return false - } - return status == LogStatusIn + return GetUsername(c) != "" } -// GetUserID get logined account uuid -func GetUserID(c *gin.Context) string { +// GetUsername get logined account +func GetUsername(c *gin.Context) string { session := sessions.Default(c) - uid := session.Get("uid") - if uid == nil { + username := session.Get("username") + if username == nil { return "" } - return uid.(string) -} - -// GetLogStatus get account log status -func GetLogStatus(c *gin.Context) LogStatus { - session := sessions.Default(c) - status := session.Get("status") - if status == nil { - return -1 - } - return LogStatus(status.(int)) + return username.(string) } diff --git a/pkg/core/blog/page/be.go b/pkg/core/blog/page/be.go new file mode 100644 index 0000000..7e7a3b6 --- /dev/null +++ b/pkg/core/blog/page/be.go @@ -0,0 +1,61 @@ +// Package page provides ... +package page + +import ( + "bytes" + htemplate "html/template" + "net/http" + + "github.com/eiblog/eiblog/pkg/cache" + "github.com/eiblog/eiblog/pkg/config" + "github.com/eiblog/eiblog/pkg/core/blog" + + "github.com/gin-gonic/gin" +) + +// baseBEParams 基础参数 +func baseBEParams(c *gin.Context) gin.H { + return gin.H{ + "Author": cache.Ei.Account.Username, + "Qiniu": config.Conf.BlogApp.Qiniu.Domain, + } +} + +// handleLoginPage 登录页面 +func handleLoginPage(c *gin.Context) { + logout := c.Query("logout") + if logout == "true" { + blog.SetLogout(c) + } else if blog.IsLogined(c) { + c.Redirect(http.StatusFound, "/admin/profile") + return + } + params := gin.H{"BTitle": cache.Ei.Blogger.BTitle} + renderHTMLAdminLayout(c, "login.html", params) +} + +// renderHTMLAdminLayout 渲染admin页面 +func renderHTMLAdminLayout(c *gin.Context, name string, data gin.H) { + c.Header("Content-Type", "text/html; charset=utf-8") + // special page + if name == "login.html" { + err := htmlTmpl.ExecuteTemplate(c.Writer, name, data) + if err != nil { + panic(err) + } + return + } + buf := bytes.Buffer{} + err := htmlTmpl.ExecuteTemplate(&buf, name, data) + if err != nil { + panic(err) + } + data["LayoutContent"] = htemplate.HTML(buf.String()) + err = htmlTmpl.ExecuteTemplate(c.Writer, "adminLayout.html", data) + if err != nil { + panic(err) + } + if c.Writer.Status() == 0 { + c.Status(http.StatusOK) + } +} diff --git a/pkg/core/blog/page/fe.go b/pkg/core/blog/page/fe.go new file mode 100644 index 0000000..7b8e265 --- /dev/null +++ b/pkg/core/blog/page/fe.go @@ -0,0 +1,386 @@ +// Package page provides ... +package page + +import ( + "bytes" + "fmt" + htemplate "html/template" + "io/ioutil" + "net/http" + "strconv" + "strings" + "time" + + "github.com/eiblog/eiblog/pkg/cache" + "github.com/eiblog/eiblog/pkg/config" + "github.com/eiblog/eiblog/pkg/internal" + "github.com/eiblog/eiblog/tools" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" +) + +// baseFEParams 基础参数 +func baseFEParams(c *gin.Context) gin.H { + version := 0 + + cookie, err := c.Request.Cookie("v") + if err != nil || cookie.Value != + fmt.Sprint(config.Conf.BlogApp.StaticVersion) { + version = config.Conf.BlogApp.StaticVersion + } + return gin.H{ + "BlogName": cache.Ei.Blogger.BlogName, + "SubTitle": cache.Ei.Blogger.SubTitle, + "BTitle": cache.Ei.Blogger.BTitle, + "BeiAn": cache.Ei.Blogger.BeiAn, + "Domain": config.Conf.BlogApp.Host, + "CopyYear": time.Now().Year(), + "Twitter": config.Conf.BlogApp.Twitter, + "Qiniu": config.Conf.BlogApp.Qiniu, + "Disqus": config.Conf.BlogApp.Disqus, + "Version": version, + } +} + +// handleNotFound not found page +func handleNotFound(c *gin.Context) { + params := baseFEParams(c) + params["Title"] = "Not Found" + params["Description"] = "404 Not Found" + params["Path"] = "" + c.Status(http.StatusNotFound) + renderHTMLHomeLayout(c, "notfound", params) +} + +// handleHomePage 首页 +func handleHomePage(c *gin.Context) { + params := baseFEParams(c) + params["Title"] = cache.Ei.Blogger.BTitle + " | " + cache.Ei.Blogger.SubTitle + params["Description"] = "博客首页," + cache.Ei.Blogger.SubTitle + params["Path"] = c.Request.URL.Path + params["CurrentPage"] = "blog-home" + pn, err := strconv.Atoi(c.Query("pn")) + if err != nil || pn < 1 { + pn = 1 + } + params["Prev"], params["Next"], params["List"] = cache.Ei.PageArticles(pn, + config.Conf.BlogApp.General.PageNum) + + renderHTMLHomeLayout(c, "home", params) +} + +// handleArticlePage 文章页 +func handleArticlePage(c *gin.Context) { + slug := c.Param("slug") + if !strings.HasSuffix(slug, ".html") || cache.Ei.ArticlesMap[slug[:len(slug)-5]] == nil { + handleNotFound(c) + return + } + article := cache.Ei.ArticlesMap[slug[:len(slug)-5]] + params := baseFEParams(c) + params["Title"] = article.Title + " | " + cache.Ei.Blogger.BTitle + params["Path"] = c.Request.URL.Path + params["CurrentPage"] = "post-" + article.Slug + params["Article"] = article + + var name string + switch slug { + case "blogroll.html": + name = "blogroll" + params["Description"] = "友情连接," + cache.Ei.Blogger.SubTitle + case "about.html": + name = "about" + params["Description"] = "关于作者," + cache.Ei.Blogger.SubTitle + default: + params["Description"] = article.Desc + "," + cache.Ei.Blogger.SubTitle + name = "article" + params["Copyright"] = cache.Ei.Blogger.Copyright + if !article.UpdatedAt.IsZero() { + params["Days"] = int(time.Now().Sub(article.UpdatedAt).Hours()) / 24 + } else { + params["Days"] = int(time.Now().Sub(article.CreatedAt).Hours()) / 24 + } + if article.SeriesID > 0 { + for _, series := range cache.Ei.Series { + if series.ID == article.SeriesID { + params["Serie"] = series + } + } + } + } + renderHTMLHomeLayout(c, name, params) +} + +// handleSeriesPage 专题页 +func handleSeriesPage(c *gin.Context) { + params := baseFEParams(c) + params["Title"] = "专题 | " + cache.Ei.Blogger.BTitle + params["Description"] = "专题列表," + cache.Ei.Blogger.SubTitle + params["Path"] = c.Request.URL.Path + params["CurrentPage"] = "series" + params["Article"] = cache.Ei.PageSeries + renderHTMLHomeLayout(c, "series", params) +} + +// handleArchivePage 归档页 +func handleArchivePage(c *gin.Context) { + params := baseFEParams(c) + params["Title"] = "归档 | " + cache.Ei.Blogger.BTitle + params["Description"] = "博客归档," + cache.Ei.Blogger.SubTitle + params["Path"] = c.Request.URL.Path + params["CurrentPage"] = "archives" + params["Article"] = cache.Ei.PageArchives + renderHTMLHomeLayout(c, "archives", params) +} + +// handleSearchPage 搜索页 +func handleSearchPage(c *gin.Context) { + params := baseFEParams(c) + params["Title"] = "站内搜索 | " + cache.Ei.Blogger.BTitle + params["Description"] = "站内搜索," + cache.Ei.Blogger.SubTitle + params["Path"] = "" + params["CurrentPage"] = "search-post" + + q := strings.TrimSpace(c.Query("q")) + if q != "" { + start, err := strconv.Atoi(c.Query("start")) + if start < 1 || err != nil { + start = 1 + } + params["Word"] = q + + vals := c.Request.URL.Query() + result, err := internal.ElasticSearch(q, config.Conf.BlogApp.General.PageNum, start-1) + if err != nil { + logrus.Error("HandleSearchPage.ElasticSearch: ", err) + } else { + result.Took /= 1000 + for i, v := range result.Hits.Hits { + article := cache.Ei.ArticlesMap[v.Source.Slug] + if len(v.Highlight.Content) == 0 && article != nil { + result.Hits.Hits[i].Highlight.Content = []string{article.Excerpt} + } + } + params["SearchResult"] = result + if num := start - config.Conf.BlogApp.General.PageNum; num > 0 { + vals.Set("start", fmt.Sprint(num)) + params["Prev"] = vals.Encode() + } + if num := start + config.Conf.BlogApp.General.PageNum; result.Hits.Total >= num { + vals.Set("start", fmt.Sprint(num)) + params["Next"] = vals.Encode() + } + } + } else { + params["HotWords"] = config.Conf.BlogApp.HotWords + } + renderHTMLHomeLayout(c, "search", params) +} + +// disqusComments 服务端获取评论详细 +type disqusComments struct { + ErrNo int `json:"errno"` + ErrMsg string `json:"errmsg"` + Data struct { + Next string `json:"next"` + Total int `json:"total"` + Comments []commentsDetail `json:"comments"` + Thread string `json:"thread"` + } `json:"data"` +} + +// handleDisqusList 获取评论列表 +func handleDisqusList(c *gin.Context) { + dcs := &disqusComments{} + defer c.JSON(http.StatusOK, dcs) + + slug := c.Param("slug") + cursor := c.Query("cursor") + if artc := cache.Ei.ArticlesMap[slug]; artc != nil { + dcs.Data.Thread = artc.Thread + } + postsList, err := internal.PostsList(slug, cursor) + if err != nil { + logrus.Error("hadnleDisqusList.PostsList: ", err) + dcs.ErrNo = 0 + dcs.ErrMsg = "系统错误" + return + } + dcs.ErrNo = postsList.Code + if postsList.Cursor.HasNext { + dcs.Data.Next = postsList.Cursor.Next + } + dcs.Data.Total = len(postsList.Response) + dcs.Data.Comments = make([]commentsDetail, len(postsList.Response)) + for i, v := range postsList.Response { + if dcs.Data.Thread == "" { + dcs.Data.Thread = v.Thread + } + dcs.Data.Comments[i] = commentsDetail{ + ID: v.ID, + Name: v.Author.Name, + Parent: v.Parent, + Url: v.Author.ProfileUrl, + Avatar: v.Author.Avatar.Cache, + CreatedAtStr: tools.ConvertStr(v.CreatedAt), + Message: v.Message, + IsDeleted: v.IsDeleted, + } + } +} + +// handleDisqusPage 评论页 +func handleDisqusPage(c *gin.Context) { + array := strings.Split(c.Param("slug"), "|") + if len(array) != 4 || array[1] == "" { + c.String(http.StatusOK, "出错啦。。。") + return + } + article := cache.Ei.ArticlesMap[array[0]] + params := gin.H{ + "Titile": "发表评论 | " + cache.Ei.Blogger.BTitle, + "ATitle": article.Title, + "Thread": array[1], + "Slug": article.Slug, + } + renderHTMLHomeLayout(c, "disqus.html", params) +} + +// 发表评论 +// [thread:[5279901489] parent:[] identifier:[post-troubleshooting-https] +// next:[] author_name:[你好] author_email:[chenqijing2@163.com] message:[fdsfdsf]] +type disqusCreate struct { + ErrNo int `json:"errno"` + ErrMsg string `json:"errmsg"` + Data commentsDetail `json:"data"` +} + +type commentsDetail struct { + ID string `json:"id"` + Parent int `json:"parent"` + Name string `json:"name"` + Url string `json:"url"` + Avatar string `json:"avatar"` + CreatedAtStr string `json:"createdAtStr"` + Message string `json:"message"` + IsDeleted bool `json:"isDeleted"` +} + +// handleDisqusCreate 评论文章 +func handleDisqusCreate(c *gin.Context) { + resp := &disqusCreate{} + defer c.JSON(http.StatusOK, resp) + + msg := c.PostForm("message") + email := c.PostForm("author_name") + name := c.PostForm("author_name") + thread := c.PostForm("thread") + identifier := c.PostForm("identifier") + if msg == "" || email == "" || name == "" || thread == "" || identifier == "" { + resp.ErrNo = 1 + resp.ErrMsg = "参数错误" + return + } + logrus.Info("email: %s comments: %s", email, thread) + + comment := internal.PostComment{ + Message: msg, + Parent: c.PostForm("parent"), + Thread: thread, + AuthorEmail: email, + AuthorName: name, + Identifier: identifier, + IpAddress: c.ClientIP(), + } + postDetail, err := internal.PostCreate(&comment) + if err != nil { + logrus.Error("handleDisqusCreate.PostCreate: ", err) + resp.ErrNo = 1 + resp.ErrMsg = "提交评论失败,请重试" + return + } + err = internal.PostApprove(postDetail.Response.ID) + if err != nil { + logrus.Error("handleDisqusCreate.PostApprove: ", err) + resp.ErrNo = 1 + resp.ErrMsg = "提交评论失败,请重试" + } + resp.ErrNo = 0 + resp.Data = commentsDetail{ + ID: postDetail.Response.ID, + Name: name, + Parent: postDetail.Response.Parent, + Url: postDetail.Response.Author.ProfileUrl, + Avatar: postDetail.Response.Author.Avatar.Cache, + CreatedAtStr: tools.ConvertStr(postDetail.Response.CreatedAt), + Message: postDetail.Response.Message, + IsDeleted: postDetail.Response.IsDeleted, + } +} + +// handleBeaconPage 服务端推送谷歌统计 +func handleBeaconPage(c *gin.Context) { + ua := c.Request.UserAgent() + + vals := c.Request.URL.Query() + vals.Set("v", config.Conf.BlogApp.Google.V) + vals.Set("tid", config.Conf.BlogApp.Google.Tid) + vals.Set("t", config.Conf.BlogApp.Google.T) + cookie, _ := c.Cookie("u") + vals.Set("cid", cookie) + + vals.Set("dl", c.Request.Referer()) + vals.Set("uip", c.ClientIP()) + go func() { + req, err := http.NewRequest("POST", config.Conf.BlogApp.Google.URL, + strings.NewReader(vals.Encode())) + if err != nil { + logrus.Error("HandleBeaconPage.NewRequest: ", err) + return + } + req.Header.Set("User-Agent", ua) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + res, err := http.DefaultClient.Do(req) + if err != nil { + logrus.Error("HandleBeaconPage.Do: ", err) + return + } + defer res.Body.Close() + data, err := ioutil.ReadAll(res.Body) + if err != nil { + logrus.Error("HandleBeaconPage.ReadAll: ", err) + return + } + if res.StatusCode/100 != 2 { + logrus.Error(string(data)) + } + }() + c.Status(http.StatusNoContent) +} + +// renderHTMLHomeLayout homelayout html +func renderHTMLHomeLayout(c *gin.Context, name string, data gin.H) { + c.Header("Content-Type", "text/html; charset=utf-8") + // special page + if name == "disqus.html" { + err := htmlTmpl.ExecuteTemplate(c.Writer, name, data) + if err != nil { + panic(err) + } + return + } + buf := bytes.Buffer{} + err := htmlTmpl.ExecuteTemplate(&buf, name, data) + if err != nil { + panic(err) + } + data["LayoutContent"] = htemplate.HTML(buf.String()) + err = htmlTmpl.ExecuteTemplate(c.Writer, "homeLayout.html", data) + if err != nil { + panic(err) + } + if c.Writer.Status() == 0 { + c.Status(http.StatusOK) + } +} diff --git a/pkg/core/blog/page/page.go b/pkg/core/blog/page/page.go index df64888..25ed60c 100644 --- a/pkg/core/blog/page/page.go +++ b/pkg/core/blog/page/page.go @@ -2,25 +2,14 @@ package page import ( - "bytes" - "fmt" - htemplate "html/template" - "io/ioutil" - "net/http" "path/filepath" - "strconv" - "strings" "text/template" - "time" - "github.com/eiblog/eiblog/pkg/cache" "github.com/eiblog/eiblog/pkg/config" - "github.com/eiblog/eiblog/pkg/internal" "github.com/eiblog/eiblog/tools" "github.com/eiblog/utils/tmpl" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" ) // htmlTmpl html template cache @@ -54,365 +43,12 @@ func RegisterRoutes(e *gin.Engine) { e.GET("/disqus/form/post-:slug", handleDisqusPage) e.POST("/disqus/create", handleDisqusCreate) e.GET("/beacon.html", handleBeaconPage) + + // login page + e.GET("/admin/login", handleLoginPage) } -// baseParams 基础参数 -func baseParams(c *gin.Context) gin.H { - version := 0 +// RegisterRoutesAuthz register admin +func RegisterRoutesAuthz(group gin.IRoutes) { - cookie, err := c.Request.Cookie("v") - if err != nil || cookie.Value != - fmt.Sprint(config.Conf.BlogApp.StaticVersion) { - version = config.Conf.BlogApp.StaticVersion - } - return gin.H{ - "BlogName": cache.Ei.Blogger.BlogName, - "SubTitle": cache.Ei.Blogger.SubTitle, - "BTitle": cache.Ei.Blogger.BTitle, - "BeiAn": cache.Ei.Blogger.BeiAn, - "Domain": config.Conf.BlogApp.Host, - "CopyYear": time.Now().Year(), - "Twitter": config.Conf.BlogApp.Twitter, - "Qiniu": config.Conf.BlogApp.Qiniu, - "Disqus": config.Conf.BlogApp.Disqus, - "Version": version, - } -} - -// handleNotFound not found page -func handleNotFound(c *gin.Context) { - params := baseParams(c) - params["Title"] = "Not Found" - params["Description"] = "404 Not Found" - params["Path"] = "" - c.Status(http.StatusNotFound) - renderHTMLHomeLayout(c, "notfound", params) -} - -// handleHomePage 首页 -func handleHomePage(c *gin.Context) { - params := baseParams(c) - params["Title"] = cache.Ei.Blogger.BTitle + " | " + cache.Ei.Blogger.SubTitle - params["Description"] = "博客首页," + cache.Ei.Blogger.SubTitle - params["Path"] = c.Request.URL.Path - params["CurrentPage"] = "blog-home" - pn, err := strconv.Atoi(c.Query("pn")) - if err != nil || pn < 1 { - pn = 1 - } - params["Prev"], params["Next"], params["List"] = cache.Ei.PageArticles(pn, - config.Conf.BlogApp.General.PageNum) - - renderHTMLHomeLayout(c, "home", params) -} - -// handleArticlePage 文章页 -func handleArticlePage(c *gin.Context) { - slug := c.Param("slug") - if !strings.HasSuffix(slug, ".html") || cache.Ei.ArticlesMap[slug[:len(slug)-5]] == nil { - handleNotFound(c) - return - } - article := cache.Ei.ArticlesMap[slug[:len(slug)-5]] - params := baseParams(c) - params["Title"] = article.Title + " | " + cache.Ei.Blogger.BTitle - params["Path"] = c.Request.URL.Path - params["CurrentPage"] = "post-" + article.Slug - params["Article"] = article - - var name string - switch slug { - case "blogroll.html": - name = "blogroll" - params["Description"] = "友情连接," + cache.Ei.Blogger.SubTitle - case "about.html": - name = "about" - params["Description"] = "关于作者," + cache.Ei.Blogger.SubTitle - default: - params["Description"] = article.Desc + "," + cache.Ei.Blogger.SubTitle - name = "article" - params["Copyright"] = cache.Ei.Blogger.Copyright - if !article.UpdateTime.IsZero() { - params["Days"] = int(time.Now().Sub(article.UpdateTime).Hours()) / 24 - } else { - params["Days"] = int(time.Now().Sub(article.CreateTime).Hours()) / 24 - } - if article.SerieID > 0 { - for _, series := range cache.Ei.Series { - if series.ID == article.SerieID { - params["Serie"] = series - } - } - } - } - renderHTMLHomeLayout(c, name, params) -} - -// handleSeriesPage 专题页 -func handleSeriesPage(c *gin.Context) { - params := baseParams(c) - params["Title"] = "专题 | " + cache.Ei.Blogger.BTitle - params["Description"] = "专题列表," + cache.Ei.Blogger.SubTitle - params["Path"] = c.Request.URL.Path - params["CurrentPage"] = "series" - params["Article"] = cache.Ei.PageSeries - renderHTMLHomeLayout(c, "series", params) -} - -// handleArchivePage 归档页 -func handleArchivePage(c *gin.Context) { - params := baseParams(c) - params["Title"] = "归档 | " + cache.Ei.Blogger.BTitle - params["Description"] = "博客归档," + cache.Ei.Blogger.SubTitle - params["Path"] = c.Request.URL.Path - params["CurrentPage"] = "archives" - params["Article"] = cache.Ei.PageArchives - renderHTMLHomeLayout(c, "archives", params) -} - -// handleSearchPage 搜索页 -func handleSearchPage(c *gin.Context) { - params := baseParams(c) - params["Title"] = "站内搜索 | " + cache.Ei.Blogger.BTitle - params["Description"] = "站内搜索," + cache.Ei.Blogger.SubTitle - params["Path"] = "" - params["CurrentPage"] = "search-post" - - q := strings.TrimSpace(c.Query("q")) - if q != "" { - start, err := strconv.Atoi(c.Query("start")) - if start < 1 || err != nil { - start = 1 - } - params["Word"] = q - - vals := c.Request.URL.Query() - result, err := internal.ElasticSearch(q, config.Conf.BlogApp.General.PageNum, start-1) - if err != nil { - logrus.Error("HandleSearchPage.ElasticSearch: ", err) - } else { - result.Took /= 1000 - for i, v := range result.Hits.Hits { - article := cache.Ei.ArticlesMap[v.Source.Slug] - if len(v.Highlight.Content) == 0 && article != nil { - result.Hits.Hits[i].Highlight.Content = []string{article.Excerpt} - } - } - params["SearchResult"] = result - if num := start - config.Conf.BlogApp.General.PageNum; num > 0 { - vals.Set("start", fmt.Sprint(num)) - params["Prev"] = vals.Encode() - } - if num := start + config.Conf.BlogApp.General.PageNum; result.Hits.Total >= num { - vals.Set("start", fmt.Sprint(num)) - params["Next"] = vals.Encode() - } - } - } else { - params["HotWords"] = config.Conf.BlogApp.HotWords - } - renderHTMLHomeLayout(c, "search", params) -} - -// disqusComments 服务端获取评论详细 -type disqusComments struct { - ErrNo int `json:"errno"` - ErrMsg string `json:"errmsg"` - Data struct { - Next string `json:"next"` - Total int `json:"total"` - Comments []commentsDetail `json:"comments"` - Thread string `json:"thread"` - } `json:"data"` -} - -// handleDisqusList 获取评论列表 -func handleDisqusList(c *gin.Context) { - dcs := &disqusComments{} - defer c.JSON(http.StatusOK, dcs) - - slug := c.Param("slug") - cursor := c.Query("cursor") - if artc := cache.Ei.ArticlesMap[slug]; artc != nil { - dcs.Data.Thread = artc.Thread - } - postsList, err := internal.PostsList(slug, cursor) - if err != nil { - logrus.Error("hadnleDisqusList.PostsList: ", err) - dcs.ErrNo = 0 - dcs.ErrMsg = "系统错误" - return - } - dcs.ErrNo = postsList.Code - if postsList.Cursor.HasNext { - dcs.Data.Next = postsList.Cursor.Next - } - dcs.Data.Total = len(postsList.Response) - dcs.Data.Comments = make([]commentsDetail, len(postsList.Response)) - for i, v := range postsList.Response { - if dcs.Data.Thread == "" { - dcs.Data.Thread = v.Thread - } - dcs.Data.Comments[i] = commentsDetail{ - ID: v.ID, - Name: v.Author.Name, - Parent: v.Parent, - Url: v.Author.ProfileUrl, - Avatar: v.Author.Avatar.Cache, - CreatedAtStr: tools.ConvertStr(v.CreatedAt), - Message: v.Message, - IsDeleted: v.IsDeleted, - } - } -} - -// handleDisqusPage 评论页 -func handleDisqusPage(c *gin.Context) { - array := strings.Split(c.Param("slug"), "|") - if len(array) != 4 || array[1] == "" { - c.String(http.StatusOK, "出错啦。。。") - return - } - article := cache.Ei.ArticlesMap[array[0]] - params := gin.H{ - "Titile": "发表评论 | " + cache.Ei.Blogger.BTitle, - "ATitle": article.Title, - "Thread": array[1], - "Slug": article.Slug, - } - err := htmlTmpl.ExecuteTemplate(c.Writer, "disqus.html", params) - if err != nil { - panic(err) - } - c.Header("Content-Type", "text/html; charset=utf-8") -} - -// 发表评论 -// [thread:[5279901489] parent:[] identifier:[post-troubleshooting-https] -// next:[] author_name:[你好] author_email:[chenqijing2@163.com] message:[fdsfdsf]] -type disqusCreate struct { - ErrNo int `json:"errno"` - ErrMsg string `json:"errmsg"` - Data commentsDetail `json:"data"` -} - -type commentsDetail struct { - ID string `json:"id"` - Parent int `json:"parent"` - Name string `json:"name"` - Url string `json:"url"` - Avatar string `json:"avatar"` - CreatedAtStr string `json:"createdAtStr"` - Message string `json:"message"` - IsDeleted bool `json:"isDeleted"` -} - -// handleDisqusCreate 评论文章 -func handleDisqusCreate(c *gin.Context) { - resp := &disqusCreate{} - defer c.JSON(http.StatusOK, resp) - - msg := c.PostForm("message") - email := c.PostForm("author_name") - name := c.PostForm("author_name") - thread := c.PostForm("thread") - identifier := c.PostForm("identifier") - if msg == "" || email == "" || name == "" || thread == "" || identifier == "" { - resp.ErrNo = 1 - resp.ErrMsg = "参数错误" - return - } - logrus.Info("email: %s comments: %s", email, thread) - - comment := internal.PostComment{ - Message: msg, - Parent: c.PostForm("parent"), - Thread: thread, - AuthorEmail: email, - AuthorName: name, - Identifier: identifier, - IpAddress: c.ClientIP(), - } - postDetail, err := internal.PostCreate(&comment) - if err != nil { - logrus.Error("handleDisqusCreate.PostCreate: ", err) - resp.ErrNo = 1 - resp.ErrMsg = "提交评论失败,请重试" - return - } - err = internal.PostApprove(postDetail.Response.ID) - if err != nil { - logrus.Error("handleDisqusCreate.PostApprove: ", err) - resp.ErrNo = 1 - resp.ErrMsg = "提交评论失败,请重试" - } - resp.ErrNo = 0 - resp.Data = commentsDetail{ - ID: postDetail.Response.ID, - Name: name, - Parent: postDetail.Response.Parent, - Url: postDetail.Response.Author.ProfileUrl, - Avatar: postDetail.Response.Author.Avatar.Cache, - CreatedAtStr: tools.ConvertStr(postDetail.Response.CreatedAt), - Message: postDetail.Response.Message, - IsDeleted: postDetail.Response.IsDeleted, - } -} - -// handleBeaconPage 服务端推送谷歌统计 -func handleBeaconPage(c *gin.Context) { - ua := c.Request.UserAgent() - - vals := c.Request.URL.Query() - vals.Set("v", config.Conf.BlogApp.Google.V) - vals.Set("tid", config.Conf.BlogApp.Google.Tid) - vals.Set("t", config.Conf.BlogApp.Google.T) - cookie, _ := c.Cookie("u") - vals.Set("cid", cookie) - - vals.Set("dl", c.Request.Referer()) - vals.Set("uip", c.ClientIP()) - go func() { - req, err := http.NewRequest("POST", config.Conf.BlogApp.Google.URL, - strings.NewReader(vals.Encode())) - if err != nil { - logrus.Error("HandleBeaconPage.NewRequest: ", err) - return - } - req.Header.Set("User-Agent", ua) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - res, err := http.DefaultClient.Do(req) - if err != nil { - logrus.Error("HandleBeaconPage.Do: ", err) - return - } - defer res.Body.Close() - data, err := ioutil.ReadAll(res.Body) - if err != nil { - logrus.Error("HandleBeaconPage.ReadAll: ", err) - return - } - if res.StatusCode/100 != 2 { - logrus.Error(string(data)) - } - }() - c.Status(http.StatusNoContent) -} - -// renderHTMLHomeLayout homelayout html -func renderHTMLHomeLayout(c *gin.Context, name string, data gin.H) { - buf := bytes.Buffer{} - err := htmlTmpl.ExecuteTemplate(&buf, name, data) - if err != nil { - panic(err) - } - data["LayoutContent"] = htemplate.HTML(buf.String()) - err = htmlTmpl.ExecuteTemplate(c.Writer, "homeLayout.html", data) - if err != nil { - panic(err) - } - if c.Writer.Status() == 0 { - c.Status(http.StatusOK) - } - c.Header("Content-Type", "text/html; charset=utf-8") } diff --git a/pkg/model/account.go b/pkg/model/account.go index a82e0e7..5e59ac2 100644 --- a/pkg/model/account.go +++ b/pkg/model/account.go @@ -3,17 +3,19 @@ package model import "time" +// use snake_case as column name + // Account 博客账户 type Account struct { - Username string `gorm:"primaryKey"` // 用户名 - Password string `gorm:"not null"` // 密码 - Email string `gorm:"not null"` // 邮件地址 - PhoneN string `gorm:"not null"` // 手机号 - Address string `gorm:"not null"` // 地址信息 + Username string `gorm:"column:username;primaryKey" bson:"username"` // 用户名 + Password string `gorm:"column:password;not null" bson:"password"` // 密码 + Email string `gorm:"column:email;not null" bson:"email"` // 邮件地址 + PhoneN string `gorm:"column:phone_n;not null" bson:"phone_n"` // 手机号 + Address string `gorm:"column:address;not null" bson:"address"` // 地址信息 - LogoutTime time.Time `gorm:"default:null"` // 登出时间 - LoginIP string `gorm:"default:null"` // 最近登录IP - LoginUA string `gorm:"default:null"` // 最近登录IP - LoginTime time.Time `gorm:"default:now()"` // 最近登录时间 - CreateTime time.Time `gorm:"default:now()"` // 创建时间 + LogoutAt time.Time `gorm:"column:logout_at;default:null" bson:"logout_at"` // 登出时间 + LoginIP string `gorm:"column:login_ip;default:null" bson:"login_ip"` // 最近登录IP + LoginUA string `gorm:"column:login_ua;default:null" bson:"login_ua"` // 最近登录IP + LoginAt time.Time `gorm:"column:login_at;default:now()" bson:"login_at"` // 最近登录时间 + CreatedAt time.Time `gorm:"column:creatd_at;default:now()" bson:"created_at"` // 创建时间 } diff --git a/pkg/model/archive.go b/pkg/model/archive.go index 0ef98d7..9515126 100644 --- a/pkg/model/archive.go +++ b/pkg/model/archive.go @@ -3,9 +3,11 @@ package model import "time" +// use snake_case as column name + // Archive 归档 type Archive struct { - Time time.Time + Time time.Time `gorm:"column:time;not null" bson:"time"` Articles SortedArticles `gorm:"-" bson:"-"` // 归档下的文章 } diff --git a/pkg/model/article.go b/pkg/model/article.go index d08f708..a53159c 100644 --- a/pkg/model/article.go +++ b/pkg/model/article.go @@ -3,21 +3,23 @@ package model import "time" +// use snake_case as column name + // Article 文章 type Article struct { - ID int `gorm:"primaryKey;autoIncrement"` // 自增ID - Author string `gorm:"not null"` // 作者名 - Slug string `gorm:"not null;uniqueIndex"` // 文章缩略名 - Title string `gorm:"not null"` // 标题 - Count int `gorm:"not null"` // 评论数量 - Content string `gorm:"not null"` // markdown内容 - SerieID int `gorm:"not null"` // 专题ID - Tags string `gorm:"not null"` // tag,以逗号隔开 - IsDraft bool `gorm:"not null"` // 是否是草稿 + ID int `gorm:"column:id;primaryKey" bson:"id"` // 自增ID + Author string `gorm:"column:author;not null" bson:"author"` // 作者名 + Slug string `gorm:"column:slug;not null;uniqueIndex" bson:"slug"` // 文章缩略名 + Title string `gorm:"column:title;not null" bson:"title"` // 标题 + Count int `gorm:"column:count;not null" bson:"count"` // 评论数量 + Content string `gorm:"column:content;not null" bson:"content"` // markdown内容 + SeriesID int `gorm:"column:series_id;not null" bson:"series_id"` // 专题ID + Tags string `gorm:"column:tags;not null" bson:"tags"` // tag,以逗号隔开 + IsDraft bool `gorm:"column:is_draft;not null" bson:"is_draft"` // 是否是草稿 - DeleteTime time.Time `gorm:"default:null"` // 删除时间 - UpdateTime time.Time `gorm:"default:now()"` // 更新时间 - CreateTime time.Time `gorm:"default:now()"` // 创建时间 + DeletedAt time.Time `gorm:"column:deleted_at;default:null" bson:"deleted_at"` // 删除时间 + UpdatedAt time.Time `gorm:"column:updated_at;default:now()" bson:"updated_at"` // 更新时间 + CreatedAt time.Time `gorm:"column:created_at;default:now()" bson:"created_at"` // 创建时间 Header string `gorm:"-" bson:"-"` // header Excerpt string `gorm:"-" bson:"-"` // 预览信息 @@ -34,7 +36,7 @@ type SortedArticles []*Article func (s SortedArticles) Len() int { return len(s) } // Less 对比 -func (s SortedArticles) Less(i, j int) bool { return s[i].CreateTime.After(s[j].CreateTime) } +func (s SortedArticles) Less(i, j int) bool { return s[i].CreatedAt.After(s[j].CreatedAt) } // Swap 交换 func (s SortedArticles) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/pkg/model/blogger.go b/pkg/model/blogger.go index 002d27a..5d33bf3 100644 --- a/pkg/model/blogger.go +++ b/pkg/model/blogger.go @@ -3,12 +3,12 @@ package model // Blogger 博客信息 type Blogger struct { - BlogName string `gorm:"not null"` // 博客名 - SubTitle string `gorm:"not null"` // 子标题 - BeiAn string `gorm:"not null"` // 备案号 - BTitle string `gorm:"not null"` // 底部title - Copyright string `gorm:"not null"` // 版权声明 + BlogName string `gorm:"column:blog_name;not null" bson:"blog_name"` // 博客名 + SubTitle string `gorm:"column:sub_title;not null" bson:"sub_title"` // 子标题 + BeiAn string `gorm:"column:bei_an;not null" bson:"bei_an"` // 备案号 + BTitle string `gorm:"column:b_title;not null" bson:"b_title"` // 底部title + Copyright string `gorm:"column:copyright;not null" bson:"copyright"` // 版权声明 - SeriesSay string `gorm:"not null"` // 专题说明 - ArchivesSay string `gorm:"not null"` // 归档说明 + SeriesSay string `gorm:"column:series_say;not null" bson:"series_say"` // 专题说明 + ArchiveSay string `gorm:"column:archive_say;not null" bson:"archive_say"` // 归档说明 } diff --git a/pkg/model/series.go b/pkg/model/series.go index 35938db..0f65861 100644 --- a/pkg/model/series.go +++ b/pkg/model/series.go @@ -5,11 +5,11 @@ import "time" // Series 专题 type Series struct { - ID int `gorm:"primaryKey;autoIncrement"` // 自增ID - Slug string `gorm:"not null;uniqueIndex"` // 缩略名 - Name string `gorm:"not null"` // 专题名 - Desc string `gorm:"not null"` // 专题描述 - CreateTime time.Time `gorm:"default:now()"` // 创建时间 + ID int `gorm:"column:id;primaryKey" bson:"id"` // 自增ID + Slug string `gorm:"column:slug;not null;uniqueIndex" bson:"slug"` // 缩略名 + Name string `gorm:"column:name;not null" bson:"name"` // 专题名 + Desc string `gorm:"column:desc;not null" bson:"desc"` // 专题描述 + CreatedAt time.Time `gorm:"column:created_at;default:now()" bson:"created_at"` // 创建时间 Articles SortedArticles `gorm:"-" bson:"-"` // 专题下的文章 } diff --git a/website/admin/backLayout.html b/website/admin/adminLayout.html similarity index 100% rename from website/admin/backLayout.html rename to website/admin/adminLayout.html