Compare commits

..

7 Commits

Author SHA1 Message Date
henry.chen
eca741896f chore(release): 2.1.17 2023-01-05 13:28:48 +08:00
henry.chen
17792e5a7e fix: fist comment of disqus error 2023-01-05 13:25:18 +08:00
henry.chen
04289c633e chore: optimize variable naming 2023-01-05 11:07:56 +08:00
henry.chen
3a5eb6fccc chore(release): 2.1.18 2023-01-05 09:17:39 +08:00
henry.chen
f6d8656c83 fix: 1. template read panic
2. optimization variable naming
2023-01-05 09:17:31 +08:00
henry.chen
4690d5123b chore(release): 2.1.17 2023-01-05 00:00:11 +08:00
henry.chen
a9e8e39d34 fix(disqus): failed to commit disqus comments 2023-01-04 23:58:50 +08:00
12 changed files with 139 additions and 60 deletions

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@ backend
# vendor/
bin
assets/*.*
db.sqlite

View File

@@ -2,6 +2,15 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [2.1.17](https://github.com/eiblog/eiblog/compare/v2.1.16...v2.1.17) (2023-01-05)
### Bug Fixes
* 1. template read panic ([f6d8656](https://github.com/eiblog/eiblog/commit/f6d8656c83591584581383643d109611d7ed2caa))
* **disqus:** failed to commit disqus comments ([a9e8e39](https://github.com/eiblog/eiblog/commit/a9e8e39d342488ec46175997f3df9ab109f2fecf))
* fist comment of disqus error ([17792e5](https://github.com/eiblog/eiblog/commit/17792e5a7edb7e84623d9307555e7983ba306565))
### [2.1.16](https://github.com/eiblog/eiblog/compare/v2.1.15...v2.1.16) (2022-11-20)

12
pkg/cache/cache.go vendored
View File

@@ -25,9 +25,11 @@ var (
// Ei eiblog cache
Ei *Cache
// regenerate pages chan
PagesCh = make(chan string, 2)
PageSeries = "series-md"
// PagesCh regenerate pages chan
PagesCh = make(chan string, 2)
// PageSeries the page series regenerate flag
PageSeries = "series-md"
// PageArchive the page archive regenerate flag
PageArchive = "archive-md"
// ArticleStartID article start id
@@ -518,7 +520,7 @@ func (c *Cache) regeneratePages() {
}
buf.WriteString("\n")
}
c.PageSeries = string(render.RenderPage(buf.Bytes()))
c.PageSeries = string(render.PageRender(buf.Bytes()))
case PageArchive:
sort.Sort(c.Archives)
buf := bytes.Buffer{}
@@ -551,7 +553,7 @@ func (c *Cache) regeneratePages() {
}
}
}
c.PageArchives = string(render.RenderPage(buf.Bytes()))
c.PageArchives = string(render.PageRender(buf.Bytes()))
}
}
}

View File

@@ -14,7 +14,7 @@ import (
// blackfriday 配置
const (
commonHtmlFlags = 0 |
commonHTMLFlags = 0 |
blackfriday.HTML_TOC |
blackfriday.HTML_USE_XHTML |
blackfriday.HTML_USE_SMARTYPANTS |
@@ -42,9 +42,9 @@ var (
regHeader = regexp.MustCompile("</nav></div>")
)
// RenderPage 渲染markdown
func RenderPage(md []byte) []byte {
renderer := blackfriday.HtmlRenderer(commonHtmlFlags, "", "")
// PageRender 渲染markdown
func PageRender(md []byte) []byte {
renderer := blackfriday.HtmlRenderer(commonHTMLFlags, "", "")
return blackfriday.Markdown(md, renderer, commonExtensions)
}
@@ -56,12 +56,12 @@ func GenerateExcerptMarkdown(article *model.Article) {
index := strings.Index(article.Content, "\r\n")
prefix := article.Content[len(blogapp.General.DescPrefix):index]
article.Desc = tools.IgnoreHtmlTag(prefix)
article.Desc = tools.IgnoreHTMLTag(prefix)
article.Content = article.Content[index:]
}
// 查找目录
content := RenderPage([]byte(article.Content))
content := PageRender([]byte(article.Content))
index := regHeader.FindIndex(content)
if index != nil {
article.Header = string(content[0:index[1]])
@@ -73,7 +73,7 @@ func GenerateExcerptMarkdown(article *model.Article) {
// excerpt
index = regIdentifier.FindStringIndex(article.Content)
if index != nil {
article.Excerpt = tools.IgnoreHtmlTag(article.Content[:index[0]])
article.Excerpt = tools.IgnoreHTMLTag(article.Content[:index[0]])
return
}
uc := []rune(article.Content)
@@ -81,5 +81,5 @@ func GenerateExcerptMarkdown(article *model.Article) {
if len(uc) < length {
length = len(uc)
}
article.Excerpt = tools.IgnoreHtmlTag(string(uc[0:length]))
article.Excerpt = tools.IgnoreHTMLTag(string(uc[0:length]))
}

View File

@@ -301,7 +301,7 @@ func handleAPIPostCreate(c *gin.Context) {
}
// 旧文章
article.ID = cid
artc, _ := cache.Ei.FindArticleByID(article.ID)
artc, _ := cache.Ei.FindArticleByID(article.ID) // cache
if artc != nil {
article.IsDraft = false
article.Count = artc.Count

View File

@@ -50,6 +50,7 @@ func handleAdminProfile(c *gin.Context) {
renderHTMLAdminLayout(c, "admin-profile", params)
}
// T tag struct
type T struct {
ID string `json:"id"`
Tags string `json:"tags"`

View File

@@ -3,6 +3,7 @@ package page
import (
"bytes"
"context"
"fmt"
htemplate "html/template"
"io/ioutil"
@@ -198,7 +199,8 @@ func handleDisqusList(c *gin.Context) {
slug := c.Param("slug")
cursor := c.Query("cursor")
if artc := cache.Ei.ArticlesMap[slug]; artc != nil {
artc := cache.Ei.ArticlesMap[slug]
if artc != nil {
dcs.Data.Thread = artc.Thread
}
postsList, err := internal.PostsList(slug, cursor)
@@ -222,13 +224,25 @@ func handleDisqusList(c *gin.Context) {
ID: v.ID,
Name: v.Author.Name,
Parent: v.Parent,
Url: v.Author.ProfileUrl,
URL: v.Author.ProfileURL,
Avatar: v.Author.Avatar.Cache,
CreatedAtStr: tools.ConvertStr(v.CreatedAt),
Message: v.Message,
IsDeleted: v.IsDeleted,
}
}
// query thread & update
if artc != nil && artc.Thread == "" {
if dcs.Data.Thread != "" {
artc.Thread = dcs.Data.Thread
} else if internal.ThreadDetails(artc) == nil {
dcs.Data.Thread = artc.Thread
}
cache.Ei.UpdateArticle(context.Background(), artc.ID,
map[string]interface{}{
"thread": artc.Thread,
})
}
}
// handleDisqusPage 评论页
@@ -261,7 +275,7 @@ type commentsDetail struct {
ID string `json:"id"`
Parent int `json:"parent"`
Name string `json:"name"`
Url string `json:"url"`
URL string `json:"url"`
Avatar string `json:"avatar"`
CreatedAtStr string `json:"createdAtStr"`
Message string `json:"message"`
@@ -274,7 +288,7 @@ func handleDisqusCreate(c *gin.Context) {
defer c.JSON(http.StatusOK, resp)
msg := c.PostForm("message")
email := c.PostForm("author_name")
email := c.PostForm("author_email")
name := c.PostForm("author_name")
thread := c.PostForm("thread")
identifier := c.PostForm("identifier")
@@ -283,7 +297,7 @@ func handleDisqusCreate(c *gin.Context) {
resp.ErrMsg = "参数错误"
return
}
logrus.Info("email: %s comments: %s", email, thread)
logrus.Infof("email: %s comments: %s", email, thread)
comment := internal.PostComment{
Message: msg,
@@ -292,7 +306,7 @@ func handleDisqusCreate(c *gin.Context) {
AuthorEmail: email,
AuthorName: name,
Identifier: identifier,
IpAddress: c.ClientIP(),
IPAddress: c.ClientIP(),
}
postDetail, err := internal.PostCreate(&comment)
if err != nil {
@@ -312,7 +326,7 @@ func handleDisqusCreate(c *gin.Context) {
ID: postDetail.Response.ID,
Name: name,
Parent: postDetail.Response.Parent,
Url: postDetail.Response.Author.ProfileUrl,
URL: postDetail.Response.Author.ProfileURL,
Avatar: postDetail.Response.Author.Avatar.Cache,
CreatedAtStr: tools.ConvertStr(postDetail.Response.CreatedAt),
Message: postDetail.Response.Message,

View File

@@ -2,6 +2,7 @@
package page
import (
"io/fs"
"path/filepath"
"text/template"
@@ -17,10 +18,15 @@ var htmlTmpl *template.Template
func init() {
htmlTmpl = template.New("eiblog").Funcs(tools.TplFuncMap)
root := filepath.Join(config.WorkDir, "website")
files := tools.ReadDirFiles(root, func(name string) bool {
files := tools.ReadDirFiles(root, func(fi fs.FileInfo) bool {
name := fi.Name()
if name == ".DS_Store" {
return true
}
// should not read template dir
if fi.IsDir() && name == "template" {
return true
}
return false
})
_, err := htmlTmpl.ParseFiles(files...)

View File

@@ -16,11 +16,12 @@ import (
// disqus api
const (
apiPostsCount = "https://disqus.com/api/3.0/threads/set.json"
apiPostsList = "https://disqus.com/api/3.0/threads/listPosts.json"
apiPostCreate = "https://disqus.com/api/3.0/posts/create.json"
apiPostApprove = "https://disqus.com/api/3.0/posts/approve.json"
apiThreadCreate = "https://disqus.com/api/3.0/threads/create.json"
apiPostsCount = "https://disqus.com/api/3.0/threads/set.json"
apiPostsList = "https://disqus.com/api/3.0/threads/listPosts.json"
apiPostCreate = "https://disqus.com/api/3.0/posts/create.json"
apiPostApprove = "https://disqus.com/api/3.0/posts/approve.json"
apiThreadCreate = "https://disqus.com/api/3.0/threads/create.json"
apiThreadDetails = "https://disqus.com/api/3.0/threads/details.json"
)
func checkDisqusConfig() error {
@@ -95,8 +96,8 @@ func PostsCount(articles map[string]*model.Article) error {
return nil
}
// postsListResp 获取评论列表
type postsListResp struct {
// PostsListResp 获取评论列表
type PostsListResp struct {
Cursor struct {
HasNext bool
Next string
@@ -113,7 +114,7 @@ type postDetail struct {
IsDeleted bool
Author struct {
Name string
ProfileUrl string
ProfileURL string
Avatar struct {
Cache string
}
@@ -122,7 +123,7 @@ type postDetail struct {
}
// PostsList 评论列表
func PostsList(slug, cursor string) (*postsListResp, error) {
func PostsList(slug, cursor string) (*PostsListResp, error) {
if err := checkDisqusConfig(); err != nil {
return nil, err
}
@@ -148,7 +149,7 @@ func PostsList(slug, cursor string) (*postsListResp, error) {
return nil, errors.New(string(b))
}
result := &postsListResp{}
result := &PostsListResp{}
err = json.Unmarshal(b, result)
if err != nil {
return nil, err
@@ -163,18 +164,19 @@ type PostComment struct {
Thread string
AuthorEmail string
AuthorName string
IpAddress string
IPAddress string
Identifier string
UserAgent string
}
type postCreateResp struct {
// PostCreateResp create comments resp
type PostCreateResp struct {
Code int
Response postDetail
}
// PostCreate 评论文章
func PostCreate(pc *PostComment) (*postCreateResp, error) {
func PostCreate(pc *PostComment) (*PostCreateResp, error) {
if err := checkDisqusConfig(); err != nil {
return nil, err
}
@@ -201,7 +203,7 @@ func PostCreate(pc *PostComment) (*postCreateResp, error) {
if resp.StatusCode != http.StatusOK {
return nil, errors.New(string(b))
}
result := &postCreateResp{}
result := &PostCreateResp{}
err = json.Unmarshal(b, result)
if err != nil {
return nil, err
@@ -294,3 +296,46 @@ func ThreadCreate(article *model.Article, btitle string) error {
article.Thread = result.Response.ID
return nil
}
// threadDetailsResp thread info
type threadDetailsResp struct {
Code int
Response struct {
ID string
}
}
// ThreadDetails thread详细
func ThreadDetails(article *model.Article) error {
if err := checkDisqusConfig(); err != nil {
return err
}
vals := url.Values{}
vals.Set("api_key", config.Conf.EiBlogApp.Disqus.PublicKey)
vals.Set("access_token", config.Conf.EiBlogApp.Disqus.AccessToken)
vals.Set("forum", config.Conf.EiBlogApp.Disqus.ShortName)
vals.Set("thread:ident", "post-"+article.Slug)
resp, err := httpPost(apiThreadDetails, vals)
if err != nil {
return err
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(string(b))
}
result := &threadDetailsResp{}
err = json.Unmarshal(b, result)
if err != nil {
return err
}
article.Thread = result.Response.ID
return nil
}

View File

@@ -49,7 +49,7 @@ func checkESConfig() error {
}
// ElasticSearch 搜索文章
func ElasticSearch(query string, size, from int) (*searchIndexResult, error) {
func ElasticSearch(query string, size, from int) (*SearchIndexResult, error) {
if err := checkESConfig(); err != nil {
return nil, err
}
@@ -114,7 +114,7 @@ func ElasticAddIndex(article *model.Article) error {
img := tools.PickFirstImage(article.Content)
mapping := map[string]interface{}{
"title": article.Title,
"content": tools.IgnoreHtmlTag(article.Content),
"content": tools.IgnoreHTMLTag(article.Content),
"slug": article.Slug,
"tag": article.Tags,
"img": img,
@@ -241,8 +241,8 @@ func deleteIndexDocument(index, typ string, ids []string) error {
return nil
}
// searchIndexResult 查询结果
type searchIndexResult struct {
// SearchIndexResult 查询结果
type SearchIndexResult struct {
Took float32 `json:"took"`
Hits struct {
Total int `json:"total"`
@@ -264,7 +264,7 @@ type searchIndexResult struct {
}
// indexQueryDSL 语句查询文档
func indexQueryDSL(index, typ string, size, from int, dsl []byte) (*searchIndexResult, error) {
func indexQueryDSL(index, typ string, size, from int, dsl []byte) (*SearchIndexResult, error) {
rawurl := fmt.Sprintf("%s/%s/%s/_search?size=%d&from=%d", config.Conf.ESHost,
index, typ, size, from)
resp, err := httpPost(rawurl, dsl)
@@ -276,7 +276,7 @@ func indexQueryDSL(index, typ string, size, from int, dsl []byte) (*searchIndexR
if err != nil {
return nil, err
}
result := &searchIndexResult{}
result := &SearchIndexResult{}
err = json.Unmarshal(data, result)
if err != nil {
return nil, err

View File

@@ -20,6 +20,7 @@ type Article struct {
SerieID int `gorm:"column:serie_id;not null" bson:"serie_id"` // 专题ID
Tags pq.StringArray `gorm:"column:tags;type:text[]" bson:"tags"` // tags
IsDraft bool `gorm:"column:is_draft;not null" bson:"is_draft"` // 是否是草稿
Thread string `gorm:"column:thread" bson:"thread"` // disqus thread
DeletedAt time.Time `gorm:"column:deleted_at;not null" bson:"deleted_at"` // 删除时间
UpdatedAt time.Time `gorm:"column:updated_at;default:current_timestamp" bson:"updated_at"` // 更新时间
@@ -28,7 +29,6 @@ type Article struct {
Header string `gorm:"-" bson:"-"` // header
Excerpt string `gorm:"-" bson:"-"` // 预览信息
Desc string `gorm:"-" bson:"-"` // 描述
Thread string `gorm:"-" bson:"-"` // disqus thread
Prev *Article `gorm:"-" bson:"-"` // 上篇文章
Next *Article `gorm:"-" bson:"-"` // 下篇文章
}

View File

@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"fmt"
"io"
"io/fs"
"io/ioutil"
"path"
"regexp"
@@ -22,13 +23,13 @@ func EncryptPasswd(name, pass string) string {
}
// ReadDirFiles 读取目录
func ReadDirFiles(dir string, filter func(name string) bool) (files []string) {
func ReadDirFiles(dir string, filter func(fi fs.FileInfo) bool) (files []string) {
fileInfos, err := ioutil.ReadDir(dir)
if err != nil {
return
}
for _, fi := range fileInfos {
if filter(fi.Name()) {
if filter(fi) {
continue
}
if fi.IsDir() {
@@ -42,19 +43,19 @@ func ReadDirFiles(dir string, filter func(name string) bool) (files []string) {
// 2016-10-22T07:03:01
const (
JUST_NOW = "几秒前"
MINUTES_AGO = "%d分钟前"
HOURS_AGO = "%d小时前"
DAYS_AGO = "%d天前"
MONTH_AGO = "%d月前"
YEARS_AGO = "%d年前"
JustNow = "几秒前"
MinutesAgo = "%d分钟前"
HoursAgo = "%d小时前"
DaysAgo = "%d天前"
MonthAgo = "%d月前"
YearsAgo = "%d年前"
)
// ConvertStr 时间转换为间隔
func ConvertStr(str string) string {
t, err := time.ParseInLocation("2006-01-02T15:04:05", str, time.UTC)
if err != nil {
return JUST_NOW
return JustNow
}
now := time.Now().UTC()
y1, m1, d1 := t.Date()
@@ -62,17 +63,17 @@ func ConvertStr(str string) string {
h1, mi1, s1 := t.Clock()
h2, mi2, s2 := now.Clock()
if y := y2 - y1; y > 1 || (y == 1 && m2-m1 >= 0) {
return fmt.Sprintf(YEARS_AGO, y)
return fmt.Sprintf(YearsAgo, y)
} else if m := y*12 + int(m2-m1); m > 1 || (m == 1 && d2-d1 >= 0) {
return fmt.Sprintf(MONTH_AGO, m)
return fmt.Sprintf(MonthAgo, m)
} else if d := m*dayIn(y1, m1) + d2 - d1; d > 1 || (d == 1 && h2-h1 >= 0) {
return fmt.Sprintf(DAYS_AGO, d)
return fmt.Sprintf(DaysAgo, d)
} else if h := d*24 + h2 - h1; h > 1 || (h == 1 && mi2-mi1 >= 0) {
return fmt.Sprintf(HOURS_AGO, h)
return fmt.Sprintf(HoursAgo, h)
} else if mi := h*60 + mi2 - mi1; mi > 1 || (mi == 1 && s2-s1 >= 0) {
return fmt.Sprintf(MINUTES_AGO, mi)
return fmt.Sprintf(MinutesAgo, mi)
}
return JUST_NOW
return JustNow
}
// dayIn 获取天数
@@ -120,8 +121,8 @@ var (
regexpEnter = regexp.MustCompile(`\s+`)
)
// IgnoreHtmlTag 去掉 html tag
func IgnoreHtmlTag(src string) string {
// IgnoreHTMLTag 去掉 html tag
func IgnoreHTMLTag(src string) string {
// 去除所有尖括号内的HTML代码
src = regexpBrackets.ReplaceAllString(src, "")
// 去除换行符