mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-09 16:12:26 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41704917db | ||
|
|
f6ba716f55 | ||
|
|
b2e6c168c5 | ||
|
|
eca741896f | ||
|
|
17792e5a7e | ||
|
|
04289c633e | ||
|
|
3a5eb6fccc | ||
|
|
f6d8656c83 | ||
|
|
4690d5123b | ||
|
|
a9e8e39d34 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,3 +19,4 @@ backend
|
|||||||
# vendor/
|
# vendor/
|
||||||
bin
|
bin
|
||||||
assets/*.*
|
assets/*.*
|
||||||
|
db.sqlite
|
||||||
|
|||||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -2,6 +2,23 @@
|
|||||||
|
|
||||||
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.
|
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.18](https://github.com/eiblog/eiblog/compare/v2.1.17...v2.1.18) (2023-01-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **backup:** can not execute ([f6ba716](https://github.com/eiblog/eiblog/commit/f6ba716f554cfb638752875c4842e4ffb1b7e9a6))
|
||||||
|
* disqus api using http post ([b2e6c16](https://github.com/eiblog/eiblog/commit/b2e6c168c5f63b29cf5c2884e04dd99caa677bc0))
|
||||||
|
|
||||||
|
### [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)
|
### [2.1.16](https://github.com/eiblog/eiblog/compare/v2.1.15...v2.1.16) (2022-11-20)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ FROM alpine:latest
|
|||||||
LABEL maintainer="deepzz.qi@gmail.com"
|
LABEL maintainer="deepzz.qi@gmail.com"
|
||||||
|
|
||||||
RUN sed -i "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g" /etc/apk/repositories \
|
RUN sed -i "s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g" /etc/apk/repositories \
|
||||||
&& apk add --update --no-cache tzdata ca-certificates mongodb-tools
|
&& apk add --update --no-cache tzdata ca-certificates mongodb-tools libc6-compat
|
||||||
COPY README.md /app/README.md
|
COPY README.md /app/README.md
|
||||||
COPY CHANGELOG.md /app/CHANGELOG.md
|
COPY CHANGELOG.md /app/CHANGELOG.md
|
||||||
COPY LICENSE /app/LICENSE
|
COPY LICENSE /app/LICENSE
|
||||||
|
|||||||
12
pkg/cache/cache.go
vendored
12
pkg/cache/cache.go
vendored
@@ -25,9 +25,11 @@ var (
|
|||||||
// Ei eiblog cache
|
// Ei eiblog cache
|
||||||
Ei *Cache
|
Ei *Cache
|
||||||
|
|
||||||
// regenerate pages chan
|
// PagesCh regenerate pages chan
|
||||||
PagesCh = make(chan string, 2)
|
PagesCh = make(chan string, 2)
|
||||||
PageSeries = "series-md"
|
// PageSeries the page series regenerate flag
|
||||||
|
PageSeries = "series-md"
|
||||||
|
// PageArchive the page archive regenerate flag
|
||||||
PageArchive = "archive-md"
|
PageArchive = "archive-md"
|
||||||
|
|
||||||
// ArticleStartID article start id
|
// ArticleStartID article start id
|
||||||
@@ -518,7 +520,7 @@ func (c *Cache) regeneratePages() {
|
|||||||
}
|
}
|
||||||
buf.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
}
|
}
|
||||||
c.PageSeries = string(render.RenderPage(buf.Bytes()))
|
c.PageSeries = string(render.PageRender(buf.Bytes()))
|
||||||
case PageArchive:
|
case PageArchive:
|
||||||
sort.Sort(c.Archives)
|
sort.Sort(c.Archives)
|
||||||
buf := bytes.Buffer{}
|
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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
pkg/cache/render/render.go
vendored
16
pkg/cache/render/render.go
vendored
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
// blackfriday 配置
|
// blackfriday 配置
|
||||||
const (
|
const (
|
||||||
commonHtmlFlags = 0 |
|
commonHTMLFlags = 0 |
|
||||||
blackfriday.HTML_TOC |
|
blackfriday.HTML_TOC |
|
||||||
blackfriday.HTML_USE_XHTML |
|
blackfriday.HTML_USE_XHTML |
|
||||||
blackfriday.HTML_USE_SMARTYPANTS |
|
blackfriday.HTML_USE_SMARTYPANTS |
|
||||||
@@ -42,9 +42,9 @@ var (
|
|||||||
regHeader = regexp.MustCompile("</nav></div>")
|
regHeader = regexp.MustCompile("</nav></div>")
|
||||||
)
|
)
|
||||||
|
|
||||||
// RenderPage 渲染markdown
|
// PageRender 渲染markdown
|
||||||
func RenderPage(md []byte) []byte {
|
func PageRender(md []byte) []byte {
|
||||||
renderer := blackfriday.HtmlRenderer(commonHtmlFlags, "", "")
|
renderer := blackfriday.HtmlRenderer(commonHTMLFlags, "", "")
|
||||||
return blackfriday.Markdown(md, renderer, commonExtensions)
|
return blackfriday.Markdown(md, renderer, commonExtensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,12 +56,12 @@ func GenerateExcerptMarkdown(article *model.Article) {
|
|||||||
index := strings.Index(article.Content, "\r\n")
|
index := strings.Index(article.Content, "\r\n")
|
||||||
prefix := article.Content[len(blogapp.General.DescPrefix):index]
|
prefix := article.Content[len(blogapp.General.DescPrefix):index]
|
||||||
|
|
||||||
article.Desc = tools.IgnoreHtmlTag(prefix)
|
article.Desc = tools.IgnoreHTMLTag(prefix)
|
||||||
article.Content = article.Content[index:]
|
article.Content = article.Content[index:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找目录
|
// 查找目录
|
||||||
content := RenderPage([]byte(article.Content))
|
content := PageRender([]byte(article.Content))
|
||||||
index := regHeader.FindIndex(content)
|
index := regHeader.FindIndex(content)
|
||||||
if index != nil {
|
if index != nil {
|
||||||
article.Header = string(content[0:index[1]])
|
article.Header = string(content[0:index[1]])
|
||||||
@@ -73,7 +73,7 @@ func GenerateExcerptMarkdown(article *model.Article) {
|
|||||||
// excerpt
|
// excerpt
|
||||||
index = regIdentifier.FindStringIndex(article.Content)
|
index = regIdentifier.FindStringIndex(article.Content)
|
||||||
if index != nil {
|
if index != nil {
|
||||||
article.Excerpt = tools.IgnoreHtmlTag(article.Content[:index[0]])
|
article.Excerpt = tools.IgnoreHTMLTag(article.Content[:index[0]])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
uc := []rune(article.Content)
|
uc := []rune(article.Content)
|
||||||
@@ -81,5 +81,5 @@ func GenerateExcerptMarkdown(article *model.Article) {
|
|||||||
if len(uc) < length {
|
if len(uc) < length {
|
||||||
length = len(uc)
|
length = len(uc)
|
||||||
}
|
}
|
||||||
article.Excerpt = tools.IgnoreHtmlTag(string(uc[0:length]))
|
article.Excerpt = tools.IgnoreHTMLTag(string(uc[0:length]))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ func handleAPIPostCreate(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
// 旧文章
|
// 旧文章
|
||||||
article.ID = cid
|
article.ID = cid
|
||||||
artc, _ := cache.Ei.FindArticleByID(article.ID)
|
artc, _ := cache.Ei.FindArticleByID(article.ID) // cache
|
||||||
if artc != nil {
|
if artc != nil {
|
||||||
article.IsDraft = false
|
article.IsDraft = false
|
||||||
article.Count = artc.Count
|
article.Count = artc.Count
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ func handleAdminProfile(c *gin.Context) {
|
|||||||
renderHTMLAdminLayout(c, "admin-profile", params)
|
renderHTMLAdminLayout(c, "admin-profile", params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// T tag struct
|
||||||
type T struct {
|
type T struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Tags string `json:"tags"`
|
Tags string `json:"tags"`
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package page
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
htemplate "html/template"
|
htemplate "html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -198,7 +199,8 @@ func handleDisqusList(c *gin.Context) {
|
|||||||
|
|
||||||
slug := c.Param("slug")
|
slug := c.Param("slug")
|
||||||
cursor := c.Query("cursor")
|
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
|
dcs.Data.Thread = artc.Thread
|
||||||
}
|
}
|
||||||
postsList, err := internal.PostsList(slug, cursor)
|
postsList, err := internal.PostsList(slug, cursor)
|
||||||
@@ -222,13 +224,25 @@ func handleDisqusList(c *gin.Context) {
|
|||||||
ID: v.ID,
|
ID: v.ID,
|
||||||
Name: v.Author.Name,
|
Name: v.Author.Name,
|
||||||
Parent: v.Parent,
|
Parent: v.Parent,
|
||||||
Url: v.Author.ProfileUrl,
|
URL: v.Author.ProfileURL,
|
||||||
Avatar: v.Author.Avatar.Cache,
|
Avatar: v.Author.Avatar.Cache,
|
||||||
CreatedAtStr: tools.ConvertStr(v.CreatedAt),
|
CreatedAtStr: tools.ConvertStr(v.CreatedAt),
|
||||||
Message: v.Message,
|
Message: v.Message,
|
||||||
IsDeleted: v.IsDeleted,
|
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 评论页
|
// handleDisqusPage 评论页
|
||||||
@@ -261,7 +275,7 @@ type commentsDetail struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Parent int `json:"parent"`
|
Parent int `json:"parent"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Url string `json:"url"`
|
URL string `json:"url"`
|
||||||
Avatar string `json:"avatar"`
|
Avatar string `json:"avatar"`
|
||||||
CreatedAtStr string `json:"createdAtStr"`
|
CreatedAtStr string `json:"createdAtStr"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
@@ -274,7 +288,7 @@ func handleDisqusCreate(c *gin.Context) {
|
|||||||
defer c.JSON(http.StatusOK, resp)
|
defer c.JSON(http.StatusOK, resp)
|
||||||
|
|
||||||
msg := c.PostForm("message")
|
msg := c.PostForm("message")
|
||||||
email := c.PostForm("author_name")
|
email := c.PostForm("author_email")
|
||||||
name := c.PostForm("author_name")
|
name := c.PostForm("author_name")
|
||||||
thread := c.PostForm("thread")
|
thread := c.PostForm("thread")
|
||||||
identifier := c.PostForm("identifier")
|
identifier := c.PostForm("identifier")
|
||||||
@@ -283,7 +297,7 @@ func handleDisqusCreate(c *gin.Context) {
|
|||||||
resp.ErrMsg = "参数错误"
|
resp.ErrMsg = "参数错误"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logrus.Info("email: %s comments: %s", email, thread)
|
logrus.Infof("email: %s comments: %s", email, thread)
|
||||||
|
|
||||||
comment := internal.PostComment{
|
comment := internal.PostComment{
|
||||||
Message: msg,
|
Message: msg,
|
||||||
@@ -292,7 +306,7 @@ func handleDisqusCreate(c *gin.Context) {
|
|||||||
AuthorEmail: email,
|
AuthorEmail: email,
|
||||||
AuthorName: name,
|
AuthorName: name,
|
||||||
Identifier: identifier,
|
Identifier: identifier,
|
||||||
IpAddress: c.ClientIP(),
|
IPAddress: c.ClientIP(),
|
||||||
}
|
}
|
||||||
postDetail, err := internal.PostCreate(&comment)
|
postDetail, err := internal.PostCreate(&comment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -312,7 +326,7 @@ func handleDisqusCreate(c *gin.Context) {
|
|||||||
ID: postDetail.Response.ID,
|
ID: postDetail.Response.ID,
|
||||||
Name: name,
|
Name: name,
|
||||||
Parent: postDetail.Response.Parent,
|
Parent: postDetail.Response.Parent,
|
||||||
Url: postDetail.Response.Author.ProfileUrl,
|
URL: postDetail.Response.Author.ProfileURL,
|
||||||
Avatar: postDetail.Response.Author.Avatar.Cache,
|
Avatar: postDetail.Response.Author.Avatar.Cache,
|
||||||
CreatedAtStr: tools.ConvertStr(postDetail.Response.CreatedAt),
|
CreatedAtStr: tools.ConvertStr(postDetail.Response.CreatedAt),
|
||||||
Message: postDetail.Response.Message,
|
Message: postDetail.Response.Message,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
package page
|
package page
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
@@ -17,10 +18,15 @@ var htmlTmpl *template.Template
|
|||||||
func init() {
|
func init() {
|
||||||
htmlTmpl = template.New("eiblog").Funcs(tools.TplFuncMap)
|
htmlTmpl = template.New("eiblog").Funcs(tools.TplFuncMap)
|
||||||
root := filepath.Join(config.WorkDir, "website")
|
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" {
|
if name == ".DS_Store" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// should not read template dir
|
||||||
|
if fi.IsDir() && name == "template" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
_, err := htmlTmpl.ParseFiles(files...)
|
_, err := htmlTmpl.ParseFiles(files...)
|
||||||
|
|||||||
@@ -16,11 +16,12 @@ import (
|
|||||||
|
|
||||||
// disqus api
|
// disqus api
|
||||||
const (
|
const (
|
||||||
apiPostsCount = "https://disqus.com/api/3.0/threads/set.json"
|
apiPostsCount = "https://disqus.com/api/3.0/threads/set.json"
|
||||||
apiPostsList = "https://disqus.com/api/3.0/threads/listPosts.json"
|
apiPostsList = "https://disqus.com/api/3.0/threads/listPosts.json"
|
||||||
apiPostCreate = "https://disqus.com/api/3.0/posts/create.json"
|
apiPostCreate = "https://disqus.com/api/3.0/posts/create.json"
|
||||||
apiPostApprove = "https://disqus.com/api/3.0/posts/approve.json"
|
apiPostApprove = "https://disqus.com/api/3.0/posts/approve.json"
|
||||||
apiThreadCreate = "https://disqus.com/api/3.0/threads/create.json"
|
apiThreadCreate = "https://disqus.com/api/3.0/threads/create.json"
|
||||||
|
apiThreadDetails = "https://disqus.com/api/3.0/threads/details.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkDisqusConfig() error {
|
func checkDisqusConfig() error {
|
||||||
@@ -95,8 +96,8 @@ func PostsCount(articles map[string]*model.Article) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// postsListResp 获取评论列表
|
// PostsListResp 获取评论列表
|
||||||
type postsListResp struct {
|
type PostsListResp struct {
|
||||||
Cursor struct {
|
Cursor struct {
|
||||||
HasNext bool
|
HasNext bool
|
||||||
Next string
|
Next string
|
||||||
@@ -113,7 +114,7 @@ type postDetail struct {
|
|||||||
IsDeleted bool
|
IsDeleted bool
|
||||||
Author struct {
|
Author struct {
|
||||||
Name string
|
Name string
|
||||||
ProfileUrl string
|
ProfileURL string
|
||||||
Avatar struct {
|
Avatar struct {
|
||||||
Cache string
|
Cache string
|
||||||
}
|
}
|
||||||
@@ -122,7 +123,7 @@ type postDetail struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PostsList 评论列表
|
// PostsList 评论列表
|
||||||
func PostsList(slug, cursor string) (*postsListResp, error) {
|
func PostsList(slug, cursor string) (*PostsListResp, error) {
|
||||||
if err := checkDisqusConfig(); err != nil {
|
if err := checkDisqusConfig(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -148,7 +149,7 @@ func PostsList(slug, cursor string) (*postsListResp, error) {
|
|||||||
return nil, errors.New(string(b))
|
return nil, errors.New(string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &postsListResp{}
|
result := &PostsListResp{}
|
||||||
err = json.Unmarshal(b, result)
|
err = json.Unmarshal(b, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -163,18 +164,19 @@ type PostComment struct {
|
|||||||
Thread string
|
Thread string
|
||||||
AuthorEmail string
|
AuthorEmail string
|
||||||
AuthorName string
|
AuthorName string
|
||||||
IpAddress string
|
IPAddress string
|
||||||
Identifier string
|
Identifier string
|
||||||
UserAgent string
|
UserAgent string
|
||||||
}
|
}
|
||||||
|
|
||||||
type postCreateResp struct {
|
// PostCreateResp create comments resp
|
||||||
|
type PostCreateResp struct {
|
||||||
Code int
|
Code int
|
||||||
Response postDetail
|
Response postDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostCreate 评论文章
|
// PostCreate 评论文章
|
||||||
func PostCreate(pc *PostComment) (*postCreateResp, error) {
|
func PostCreate(pc *PostComment) (*PostCreateResp, error) {
|
||||||
if err := checkDisqusConfig(); err != nil {
|
if err := checkDisqusConfig(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -201,7 +203,7 @@ func PostCreate(pc *PostComment) (*postCreateResp, error) {
|
|||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, errors.New(string(b))
|
return nil, errors.New(string(b))
|
||||||
}
|
}
|
||||||
result := &postCreateResp{}
|
result := &PostCreateResp{}
|
||||||
err = json.Unmarshal(b, result)
|
err = json.Unmarshal(b, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -294,3 +296,46 @@ func ThreadCreate(article *model.Article, btitle string) error {
|
|||||||
article.Thread = result.Response.ID
|
article.Thread = result.Response.ID
|
||||||
return nil
|
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 := httpGet(apiThreadDetails + "?" + vals.Encode())
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func checkESConfig() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ElasticSearch 搜索文章
|
// ElasticSearch 搜索文章
|
||||||
func ElasticSearch(query string, size, from int) (*searchIndexResult, error) {
|
func ElasticSearch(query string, size, from int) (*SearchIndexResult, error) {
|
||||||
if err := checkESConfig(); err != nil {
|
if err := checkESConfig(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ func ElasticAddIndex(article *model.Article) error {
|
|||||||
img := tools.PickFirstImage(article.Content)
|
img := tools.PickFirstImage(article.Content)
|
||||||
mapping := map[string]interface{}{
|
mapping := map[string]interface{}{
|
||||||
"title": article.Title,
|
"title": article.Title,
|
||||||
"content": tools.IgnoreHtmlTag(article.Content),
|
"content": tools.IgnoreHTMLTag(article.Content),
|
||||||
"slug": article.Slug,
|
"slug": article.Slug,
|
||||||
"tag": article.Tags,
|
"tag": article.Tags,
|
||||||
"img": img,
|
"img": img,
|
||||||
@@ -241,8 +241,8 @@ func deleteIndexDocument(index, typ string, ids []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// searchIndexResult 查询结果
|
// SearchIndexResult 查询结果
|
||||||
type searchIndexResult struct {
|
type SearchIndexResult struct {
|
||||||
Took float32 `json:"took"`
|
Took float32 `json:"took"`
|
||||||
Hits struct {
|
Hits struct {
|
||||||
Total int `json:"total"`
|
Total int `json:"total"`
|
||||||
@@ -264,7 +264,7 @@ type searchIndexResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// indexQueryDSL 语句查询文档
|
// 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,
|
rawurl := fmt.Sprintf("%s/%s/%s/_search?size=%d&from=%d", config.Conf.ESHost,
|
||||||
index, typ, size, from)
|
index, typ, size, from)
|
||||||
resp, err := httpPost(rawurl, dsl)
|
resp, err := httpPost(rawurl, dsl)
|
||||||
@@ -276,7 +276,7 @@ func indexQueryDSL(index, typ string, size, from int, dsl []byte) (*searchIndexR
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
result := &searchIndexResult{}
|
result := &SearchIndexResult{}
|
||||||
err = json.Unmarshal(data, result)
|
err = json.Unmarshal(data, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Article struct {
|
|||||||
SerieID int `gorm:"column:serie_id;not null" bson:"serie_id"` // 专题ID
|
SerieID int `gorm:"column:serie_id;not null" bson:"serie_id"` // 专题ID
|
||||||
Tags pq.StringArray `gorm:"column:tags;type:text[]" bson:"tags"` // tags
|
Tags pq.StringArray `gorm:"column:tags;type:text[]" bson:"tags"` // tags
|
||||||
IsDraft bool `gorm:"column:is_draft;not null" bson:"is_draft"` // 是否是草稿
|
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"` // 删除时间
|
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"` // 更新时间
|
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
|
Header string `gorm:"-" bson:"-"` // header
|
||||||
Excerpt string `gorm:"-" bson:"-"` // 预览信息
|
Excerpt string `gorm:"-" bson:"-"` // 预览信息
|
||||||
Desc string `gorm:"-" bson:"-"` // 描述
|
Desc string `gorm:"-" bson:"-"` // 描述
|
||||||
Thread string `gorm:"-" bson:"-"` // disqus thread
|
|
||||||
Prev *Article `gorm:"-" bson:"-"` // 上篇文章
|
Prev *Article `gorm:"-" bson:"-"` // 上篇文章
|
||||||
Next *Article `gorm:"-" bson:"-"` // 下篇文章
|
Next *Article `gorm:"-" bson:"-"` // 下篇文章
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -22,13 +23,13 @@ func EncryptPasswd(name, pass string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadDirFiles 读取目录
|
// 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)
|
fileInfos, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, fi := range fileInfos {
|
for _, fi := range fileInfos {
|
||||||
if filter(fi.Name()) {
|
if filter(fi) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
@@ -42,19 +43,19 @@ func ReadDirFiles(dir string, filter func(name string) bool) (files []string) {
|
|||||||
|
|
||||||
// 2016-10-22T07:03:01
|
// 2016-10-22T07:03:01
|
||||||
const (
|
const (
|
||||||
JUST_NOW = "几秒前"
|
JustNow = "几秒前"
|
||||||
MINUTES_AGO = "%d分钟前"
|
MinutesAgo = "%d分钟前"
|
||||||
HOURS_AGO = "%d小时前"
|
HoursAgo = "%d小时前"
|
||||||
DAYS_AGO = "%d天前"
|
DaysAgo = "%d天前"
|
||||||
MONTH_AGO = "%d月前"
|
MonthAgo = "%d月前"
|
||||||
YEARS_AGO = "%d年前"
|
YearsAgo = "%d年前"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConvertStr 时间转换为间隔
|
// ConvertStr 时间转换为间隔
|
||||||
func ConvertStr(str string) string {
|
func ConvertStr(str string) string {
|
||||||
t, err := time.ParseInLocation("2006-01-02T15:04:05", str, time.UTC)
|
t, err := time.ParseInLocation("2006-01-02T15:04:05", str, time.UTC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return JUST_NOW
|
return JustNow
|
||||||
}
|
}
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
y1, m1, d1 := t.Date()
|
y1, m1, d1 := t.Date()
|
||||||
@@ -62,17 +63,17 @@ func ConvertStr(str string) string {
|
|||||||
h1, mi1, s1 := t.Clock()
|
h1, mi1, s1 := t.Clock()
|
||||||
h2, mi2, s2 := now.Clock()
|
h2, mi2, s2 := now.Clock()
|
||||||
if y := y2 - y1; y > 1 || (y == 1 && m2-m1 >= 0) {
|
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) {
|
} 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) {
|
} 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) {
|
} 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) {
|
} 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 获取天数
|
// dayIn 获取天数
|
||||||
@@ -120,8 +121,8 @@ var (
|
|||||||
regexpEnter = regexp.MustCompile(`\s+`)
|
regexpEnter = regexp.MustCompile(`\s+`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// IgnoreHtmlTag 去掉 html tag
|
// IgnoreHTMLTag 去掉 html tag
|
||||||
func IgnoreHtmlTag(src string) string {
|
func IgnoreHTMLTag(src string) string {
|
||||||
// 去除所有尖括号内的HTML代码
|
// 去除所有尖括号内的HTML代码
|
||||||
src = regexpBrackets.ReplaceAllString(src, "")
|
src = regexpBrackets.ReplaceAllString(src, "")
|
||||||
// 去除换行符
|
// 去除换行符
|
||||||
|
|||||||
Reference in New Issue
Block a user