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