mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-04 13:52:26 +08:00
chore: add api
This commit is contained in:
208
pkg/cache/cache.go
vendored
208
pkg/cache/cache.go
vendored
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/pkg/cache/render"
|
"github.com/eiblog/eiblog/pkg/cache/render"
|
||||||
@@ -37,6 +38,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
// Ei init
|
// Ei init
|
||||||
Ei = &Cache{
|
Ei = &Cache{
|
||||||
|
lock: sync.Mutex{},
|
||||||
Store: store,
|
Store: store,
|
||||||
TagArticles: make(map[string]model.SortedArticles),
|
TagArticles: make(map[string]model.SortedArticles),
|
||||||
ArticlesMap: make(map[string]*model.Article),
|
ArticlesMap: make(map[string]*model.Article),
|
||||||
@@ -52,6 +54,7 @@ func init() {
|
|||||||
|
|
||||||
// Cache 整站缓存
|
// Cache 整站缓存
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
|
lock sync.Mutex
|
||||||
store.Store
|
store.Store
|
||||||
|
|
||||||
// load from db
|
// load from db
|
||||||
@@ -68,44 +71,6 @@ type Cache struct {
|
|||||||
ArticlesMap map[string]*model.Article // slug:article
|
ArticlesMap map[string]*model.Article // slug:article
|
||||||
}
|
}
|
||||||
|
|
||||||
// // LoadInsertAccount 读取或创建账户
|
|
||||||
// LoadInsertAccount(ctx context.Context, acct *model.Account) (*model.Account, error)
|
|
||||||
// // UpdateAccount 更新账户
|
|
||||||
// UpdateAccount(ctx context.Context, name string, fields map[string]interface{}) error
|
|
||||||
//
|
|
||||||
// // LoadInsertBlogger 读取或创建博客
|
|
||||||
// LoadInsertBlogger(ctx context.Context, blogger *model.Blogger) (*model.Blogger, error)
|
|
||||||
// // UpdateBlogger 更新博客
|
|
||||||
// UpdateBlogger(ctx context.Context, fields map[string]interface{}) error
|
|
||||||
//
|
|
||||||
// // InsertSeries 创建专题
|
|
||||||
// InsertSeries(ctx context.Context, series *model.Series) error
|
|
||||||
// // RemoveSeries 删除专题
|
|
||||||
// RemoveSeries(ctx context.Context, id int) error
|
|
||||||
// // UpdateSeries 更新专题
|
|
||||||
// UpdateSeries(ctx context.Context, id int, fields map[string]interface{}) error
|
|
||||||
// // LoadAllSeries 读取所有专题
|
|
||||||
// LoadAllSeries(ctx context.Context) (model.SortedSeries, error)
|
|
||||||
//
|
|
||||||
// // InsertArticle 创建文章
|
|
||||||
// InsertArticle(ctx context.Context, article *model.Article) error
|
|
||||||
// // RemoveArticle 硬删除文章
|
|
||||||
// RemoveArticle(ctx context.Context, id int) error
|
|
||||||
// // DeleteArticle 软删除文章,放入回收箱
|
|
||||||
// DeleteArticle(ctx context.Context, id int) error
|
|
||||||
// // CleanArticles 清理回收站文章
|
|
||||||
// CleanArticles(ctx context.Context) error
|
|
||||||
// // UpdateArticle 更新文章
|
|
||||||
// UpdateArticle(ctx context.Context, id int, fields map[string]interface{}) error
|
|
||||||
// // RecoverArticle 恢复文章到草稿
|
|
||||||
// RecoverArticle(ctx context.Context, id int) error
|
|
||||||
// // LoadAllArticle 读取所有文章
|
|
||||||
// LoadAllArticle(ctx context.Context) (model.SortedArticles, error)
|
|
||||||
// // LoadTrashArticles 读取回收箱
|
|
||||||
// LoadTrashArticles(ctx context.Context) (model.SortedArticles, error)
|
|
||||||
// // LoadDraftArticles 读取草稿箱
|
|
||||||
// LoadDraftArticles(ctx context.Context) (model.SortedArticles, error)
|
|
||||||
|
|
||||||
// PageArticles 文章翻页
|
// PageArticles 文章翻页
|
||||||
func (c *Cache) PageArticles(page int, pageSize int) (prev,
|
func (c *Cache) PageArticles(page int, pageSize int) (prev,
|
||||||
next int, articles []*model.Article) {
|
next int, articles []*model.Article) {
|
||||||
@@ -146,6 +111,121 @@ func (c *Cache) PageArticles(page int, pageSize int) (prev,
|
|||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// DeleteArticles 删除文章
|
||||||
|
func (c *Cache) DeleteArticles(ids []int) error {
|
||||||
|
c.lock.Lock()
|
||||||
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
article, idx := c.FindArticleByID(id)
|
||||||
|
// drop from linkedList
|
||||||
|
if article.Prev == nil && article.Next != nil {
|
||||||
|
article.Next.Prev = nil
|
||||||
|
} else if article.Prev != nil && article.Next == nil {
|
||||||
|
article.Prev.Next = nil
|
||||||
|
} else if article.Prev != nil && article.Next != nil {
|
||||||
|
article.Prev.Next = article.Next
|
||||||
|
article.Next.Prev = article.Prev
|
||||||
|
}
|
||||||
|
// drop from articles
|
||||||
|
c.Articles = append(c.Articles[:idx], c.Articles[idx+1:]...)
|
||||||
|
delete(c.ArticlesMap, article.Slug)
|
||||||
|
// set delete
|
||||||
|
err := c.UpdateArticle(context.Background(), id, map[string]interface{}{
|
||||||
|
"deletetime": time.Now(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// drop from tags,series,archives
|
||||||
|
c.redelArticle(article)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddArticle 添加文章
|
||||||
|
func (c *Cache) AddArticle(article *model.Article) error {
|
||||||
|
err := c.InsertArticle(context.Background(), article)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 正式发布文章
|
||||||
|
if !article.IsDraft {
|
||||||
|
defer render.GenerateExcerptMarkdown(article)
|
||||||
|
|
||||||
|
c.ArticlesMap[article.Slug] = article
|
||||||
|
c.Articles = append([]*model.Article{article}, c.Articles...)
|
||||||
|
sort.Sort(c.Articles)
|
||||||
|
|
||||||
|
article, idx := c.FindArticleByID(article.ID)
|
||||||
|
if idx == 0 && c.Articles[idx+1].ID >=
|
||||||
|
config.Conf.BlogApp.General.StartID {
|
||||||
|
article.Next = c.Articles[idx+1]
|
||||||
|
c.Articles[idx+1].Prev = article
|
||||||
|
} else if idx > 0 && c.Articles[idx-1].ID >=
|
||||||
|
config.Conf.BlogApp.General.StartID {
|
||||||
|
article.Prev = c.Articles[idx-1]
|
||||||
|
if c.Articles[idx-1].Next != nil {
|
||||||
|
article.Next = c.Articles[idx-1].Next
|
||||||
|
c.Articles[idx-1].Next.Prev = article
|
||||||
|
}
|
||||||
|
c.Articles[idx-1].Next = article
|
||||||
|
}
|
||||||
|
c.readdArticle(article, true)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceArticle 替换文章
|
||||||
|
func (c *Cache) ReplaceArticle(oldArticle, newArticle *model.Article) {
|
||||||
|
c.ArticlesMap[newArticle.Slug] = newArticle
|
||||||
|
render.GenerateExcerptMarkdown(newArticle)
|
||||||
|
if newArticle.ID < config.Conf.BlogApp.General.StartID {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if oldArticle != nil {
|
||||||
|
article, idx := c.FindArticleByID(oldArticle.ID)
|
||||||
|
if article.Prev == nil && article.Next != nil {
|
||||||
|
article.Next.Prev = nil
|
||||||
|
} else if article.Prev != nil && article.Next == nil {
|
||||||
|
article.Prev.Next = nil
|
||||||
|
} else if article.Prev != nil && article.Next != nil {
|
||||||
|
article.Prev.Next = article.Next
|
||||||
|
article.Next.Prev = article.Prev
|
||||||
|
}
|
||||||
|
c.Articles = append(Ei.Articles[:idx], Ei.Articles[idx+1:]...)
|
||||||
|
|
||||||
|
c.redelArticle(article)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Articles = append(c.Articles, newArticle)
|
||||||
|
sort.Sort(c.Articles)
|
||||||
|
_, idx := c.FindArticleByID(newArticle.ID)
|
||||||
|
if idx == 0 && c.Articles[idx+1].ID >= config.Conf.BlogApp.General.StartID {
|
||||||
|
newArticle.Next = c.Articles[idx+1]
|
||||||
|
c.Articles[idx+1].Prev = newArticle
|
||||||
|
} else if idx > 0 && c.Articles[idx-1].ID >=
|
||||||
|
config.Conf.BlogApp.General.StartID {
|
||||||
|
newArticle.Prev = Ei.Articles[idx-1]
|
||||||
|
if c.Articles[idx-1].Next != nil {
|
||||||
|
newArticle.Next = Ei.Articles[idx-1].Next
|
||||||
|
c.Articles[idx-1].Next.Prev = newArticle
|
||||||
|
}
|
||||||
|
c.Articles[idx-1].Next = newArticle
|
||||||
|
}
|
||||||
|
c.readdArticle(newArticle, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindArticleByID 通过ID查找文章
|
||||||
|
func (c *Cache) FindArticleByID(id int) (*model.Article, int) {
|
||||||
|
for i, article := range c.Articles {
|
||||||
|
if article.ID == id {
|
||||||
|
return article, i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, -1
|
||||||
|
}
|
||||||
|
|
||||||
// loadOrInit 读取数据或初始化
|
// loadOrInit 读取数据或初始化
|
||||||
func (c *Cache) loadOrInit() error {
|
func (c *Cache) loadOrInit() error {
|
||||||
blogapp := config.Conf.BlogApp
|
blogapp := config.Conf.BlogApp
|
||||||
@@ -219,7 +299,7 @@ func (c *Cache) loadOrInit() error {
|
|||||||
if Ei.Articles[i+1].ID >= blogapp.General.StartID {
|
if Ei.Articles[i+1].ID >= blogapp.General.StartID {
|
||||||
v.Next = Ei.Articles[i+1]
|
v.Next = Ei.Articles[i+1]
|
||||||
}
|
}
|
||||||
c.rebuildArticle(v, false)
|
c.readdArticle(v, false)
|
||||||
}
|
}
|
||||||
Ei.Articles = articles
|
Ei.Articles = articles
|
||||||
// 重建专题与归档
|
// 重建专题与归档
|
||||||
@@ -228,8 +308,8 @@ func (c *Cache) loadOrInit() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// rebuildArticle 重建缓存tag、series、archive
|
// readdArticle 添加文章到tag、series、archive
|
||||||
func (c *Cache) rebuildArticle(article *model.Article, needSort bool) {
|
func (c *Cache) readdArticle(article *model.Article, needSort bool) {
|
||||||
// tag
|
// tag
|
||||||
tags := strings.Split(article.Tags, ",")
|
tags := strings.Split(article.Tags, ",")
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
@@ -270,6 +350,52 @@ func (c *Cache) rebuildArticle(article *model.Article, needSort bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// redelArticle 从tag、series、archive删除文章
|
||||||
|
func (c *Cache) redelArticle(article *model.Article) {
|
||||||
|
// tag
|
||||||
|
tags := strings.Split(article.Tags, ",")
|
||||||
|
for _, tag := range tags {
|
||||||
|
for i, v := range c.TagArticles[tag] {
|
||||||
|
if v == article {
|
||||||
|
c.TagArticles[tag] = append(c.TagArticles[tag][0:i], c.TagArticles[tag][i+1:]...)
|
||||||
|
if len(c.TagArticles[tag]) == 0 {
|
||||||
|
delete(c.TagArticles, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// serie
|
||||||
|
for i, serie := range c.Series {
|
||||||
|
if serie.ID == article.SerieID {
|
||||||
|
for j, v := range serie.Articles {
|
||||||
|
if v == article {
|
||||||
|
Ei.Series[i].Articles = append(Ei.Series[i].Articles[0:j],
|
||||||
|
Ei.Series[i].Articles[j+1:]...)
|
||||||
|
PagesCh <- PageSeries
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// archive
|
||||||
|
for i, archive := range c.Archives {
|
||||||
|
ay, am, _ := archive.Time.Date()
|
||||||
|
if y, m, _ := article.CreatedAt.Date(); ay == y && am == m {
|
||||||
|
for j, v := range archive.Articles {
|
||||||
|
if v == article {
|
||||||
|
c.Archives[i].Articles = append(c.Archives[i].Articles[0:j],
|
||||||
|
c.Archives[i].Articles[j+1:]...)
|
||||||
|
if len(c.Archives[i].Articles) == 0 {
|
||||||
|
c.Archives = append(c.Archives[:i], c.Archives[i+1:]...)
|
||||||
|
}
|
||||||
|
PagesCh <- PageArchive
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// regeneratePages 重新生成series,archive页面
|
// regeneratePages 重新生成series,archive页面
|
||||||
func (c *Cache) regeneratePages() {
|
func (c *Cache) regeneratePages() {
|
||||||
for {
|
for {
|
||||||
|
|||||||
@@ -3,13 +3,17 @@ package admin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/pkg/cache"
|
"github.com/eiblog/eiblog/pkg/cache"
|
||||||
|
"github.com/eiblog/eiblog/pkg/config"
|
||||||
"github.com/eiblog/eiblog/pkg/core/blog"
|
"github.com/eiblog/eiblog/pkg/core/blog"
|
||||||
|
"github.com/eiblog/eiblog/pkg/internal"
|
||||||
|
"github.com/eiblog/eiblog/pkg/model"
|
||||||
"github.com/eiblog/eiblog/tools"
|
"github.com/eiblog/eiblog/tools"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -176,29 +180,229 @@ func handleAPIPassword(c *gin.Context) {
|
|||||||
|
|
||||||
// handleAPIPostDelete 删除文章,移入回收箱
|
// handleAPIPostDelete 删除文章,移入回收箱
|
||||||
func handleAPIPostDelete(c *gin.Context) {
|
func handleAPIPostDelete(c *gin.Context) {
|
||||||
// var ids []int32
|
var ids []int
|
||||||
// for _, v := range c.PostFormArray("cid[]") {
|
for _, v := range c.PostFormArray("cid[]") {
|
||||||
// i, err := strconv.Atoi(v)
|
id, err := strconv.Atoi(v)
|
||||||
// if err != nil || int32(i) < config.Conf.BlogApp.General.StartID {
|
if err != nil || id < config.Conf.BlogApp.General.StartID {
|
||||||
// responseNotice(c, NoticeNotice, "参数错误", "")
|
responseNotice(c, NoticeNotice, "参数错误", "")
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
// ids = append(ids, int32(i))
|
ids = append(ids, id)
|
||||||
// }
|
}
|
||||||
// err := DelArticles(ids...)
|
err := cache.Ei.DeleteArticles(ids)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// logd.Error(err)
|
logrus.Error("handleAPIPostDelete.DeleteArticles: ", err)
|
||||||
// responseNotice(c, NOTICE_NOTICE, err.Error(), "")
|
|
||||||
// return
|
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||||
// }
|
return
|
||||||
//
|
}
|
||||||
// // elasticsearch
|
// elasticsearch
|
||||||
// err = ElasticDelIndex(ids)
|
err = internal.ElasticDelIndex(ids)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// logrus.Error("handleAPIPostDelete.")
|
logrus.Error("handleAPIPostDelete.ElasticDelIndex: ", err)
|
||||||
// }
|
}
|
||||||
// // TODO disqus delete
|
// TODO disqus delete
|
||||||
// responseNotice(c, NoticeSuccess, "删除成功", "")
|
responseNotice(c, NoticeSuccess, "删除成功", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleAPIPostCreate 创建文章
|
||||||
|
func handleAPIPostCreate(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
do string
|
||||||
|
cid int
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
switch do {
|
||||||
|
case "auto": // 自动保存
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{"fail": 1, "time": time.Now().Format("15:04:05 PM"), "cid": cid})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{"success": 0, "time": time.Now().Format("15:04:05 PM"), "cid": cid})
|
||||||
|
case "save", "publish": // 草稿,发布
|
||||||
|
if err != nil {
|
||||||
|
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uri := "/admin/manage-draft"
|
||||||
|
if do == "publish" {
|
||||||
|
uri = "/admin/manage-posts"
|
||||||
|
}
|
||||||
|
c.Redirect(http.StatusFound, uri)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
do = c.PostForm("do") // auto or save or publish
|
||||||
|
slug := c.PostForm("slug")
|
||||||
|
title := c.PostForm("title")
|
||||||
|
text := c.PostForm("text")
|
||||||
|
date := parseLocationDate(c.PostForm("date"))
|
||||||
|
serie := c.PostForm("serie")
|
||||||
|
tag := c.PostForm("tags")
|
||||||
|
update := c.PostForm("update")
|
||||||
|
if slug == "" || title == "" || text == "" {
|
||||||
|
err = errors.New("参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serieid, _ := strconv.Atoi(serie)
|
||||||
|
article := &model.Article{
|
||||||
|
Title: title,
|
||||||
|
Content: text,
|
||||||
|
Slug: slug,
|
||||||
|
IsDraft: do != "publish",
|
||||||
|
Author: cache.Ei.Account.Username,
|
||||||
|
SerieID: serieid,
|
||||||
|
Tags: tag,
|
||||||
|
CreatedAt: date,
|
||||||
|
}
|
||||||
|
cid, err = strconv.Atoi(c.PostForm("cid"))
|
||||||
|
// 新文章
|
||||||
|
if err != nil || cid < 1 {
|
||||||
|
err = cache.Ei.AddArticle(article)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("handleAPIPostCreate.InsertArticle: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !article.IsDraft {
|
||||||
|
// 异步执行,快
|
||||||
|
go func() {
|
||||||
|
// elastic
|
||||||
|
internal.ElasticAddIndex(article)
|
||||||
|
// rss
|
||||||
|
internal.PingFunc(cache.Ei.Blogger.BTitle, slug)
|
||||||
|
// disqus
|
||||||
|
internal.ThreadCreate(article, cache.Ei.Blogger.BTitle)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 旧文章
|
||||||
|
article.ID = cid
|
||||||
|
artc, _ := cache.Ei.FindArticleByID(article.ID)
|
||||||
|
if artc != nil {
|
||||||
|
article.IsDraft = false
|
||||||
|
article.Count = artc.Count
|
||||||
|
article.UpdatedAt = artc.UpdatedAt
|
||||||
|
}
|
||||||
|
if update == "true" || update == "1" {
|
||||||
|
artc.UpdatedAt = time.Now()
|
||||||
|
}
|
||||||
|
// 数据库更新
|
||||||
|
err = cache.Ei.UpdateArticle(context.Background(), artc.ID, map[string]interface{}{
|
||||||
|
"title": article.Title,
|
||||||
|
"content": article.Content,
|
||||||
|
"serie_id": article.SerieID,
|
||||||
|
"tags": article.Tags,
|
||||||
|
"is_draft": article.IsDraft,
|
||||||
|
"updated_at": article.UpdatedAt,
|
||||||
|
"created_at": article.CreatedAt,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("handleAPIPostCreate.UpdateArticle: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !artc.IsDraft {
|
||||||
|
cache.Ei.ReplaceArticle(artc, article)
|
||||||
|
// 异步执行,快
|
||||||
|
go func() {
|
||||||
|
// elastic
|
||||||
|
internal.ElasticAddIndex(article)
|
||||||
|
// rss
|
||||||
|
internal.PingFunc(cache.Ei.Blogger.BTitle, slug)
|
||||||
|
// disqus
|
||||||
|
if artc == nil {
|
||||||
|
internal.ThreadCreate(article, cache.Ei.Blogger.BTitle)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleAPISerieDelete 只能逐一删除,专题下不能有文章
|
||||||
|
func handleAPISerieDelete(c *gin.Context) {
|
||||||
|
for _, v := range c.PostFormArray("mid[]") {
|
||||||
|
id, err := strconv.Atoi(v)
|
||||||
|
if err != nil || id < 1 {
|
||||||
|
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, serie := range cache.Ei.Series {
|
||||||
|
if serie.ID == id {
|
||||||
|
if len(serie.Articles) > 0 {
|
||||||
|
logrus.Error("handleAPISerieDelete.failed: ")
|
||||||
|
responseNotice(c, NoticeNotice, "请删除该专题下的所有文章", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = cache.Ei.RemoveSerie(context.Background(), id)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("handleAPISerieDelete.RemoveSerie: ")
|
||||||
|
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cache.Ei.Series[i] = nil
|
||||||
|
cache.Ei.Series = append(cache.Ei.Series[:i], cache.Ei.Series[i+1:]...)
|
||||||
|
cache.PagesCh <- cache.PageSeries
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseNotice(c, NoticeSuccess, "删除成功", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleAPISerieCreate 添加专题,如果专题有提交 mid 即更新专题
|
||||||
|
func handleAPISerieCreate(c *gin.Context) {
|
||||||
|
name := c.PostForm("name")
|
||||||
|
slug := c.PostForm("slug")
|
||||||
|
desc := c.PostForm("description")
|
||||||
|
if name == "" || slug == "" || desc == "" {
|
||||||
|
responseNotice(c, NoticeNotice, "参数错误", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mid, err := strconv.Atoi(c.PostForm("mid"))
|
||||||
|
if err == nil && mid > 0 {
|
||||||
|
var serie *model.Serie
|
||||||
|
for _, v := range cache.Ei.Series {
|
||||||
|
if v.ID == mid {
|
||||||
|
serie = v
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if serie == nil {
|
||||||
|
responseNotice(c, NoticeNotice, "专题不存在", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = cache.Ei.UpdateSerie(context.Background(), mid, map[string]interface{}{
|
||||||
|
"slug": slug,
|
||||||
|
"name": name,
|
||||||
|
"desc": desc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("handleAPISerieCreate.UpdateSerie: ", err)
|
||||||
|
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = cache.Ei.InsertSerie(context.Background(), &model.Serie{
|
||||||
|
Slug: slug,
|
||||||
|
Name: name,
|
||||||
|
Desc: desc,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("handleAPISerieCreate.InsertSerie: ", err)
|
||||||
|
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseNotice(c, NoticeSuccess, "操作成功", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLocationDate 解析日期
|
||||||
|
func parseLocationDate(date string) time.Time {
|
||||||
|
t, err := time.ParseInLocation("2006-01-02 15:04", date, time.Local)
|
||||||
|
if err == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
func responseNotice(c *gin.Context, typ, content, hl string) {
|
func responseNotice(c *gin.Context, typ, content, hl string) {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func handleAdminPost(c *gin.Context) {
|
|||||||
params["Domain"] = config.Conf.BlogApp.Host
|
params["Domain"] = config.Conf.BlogApp.Host
|
||||||
params["Series"] = cache.Ei.Series
|
params["Series"] = cache.Ei.Series
|
||||||
var tags []T
|
var tags []T
|
||||||
for tag, _ := range cache.Ei.TagArticles {
|
for tag := range cache.Ei.TagArticles {
|
||||||
tags = append(tags, T{tag, tag})
|
tags = append(tags, T{tag, tag})
|
||||||
}
|
}
|
||||||
str, _ := json.Marshal(tags)
|
str, _ := json.Marshal(tags)
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/pkg/config"
|
"github.com/eiblog/eiblog/pkg/config"
|
||||||
|
"github.com/eiblog/eiblog/pkg/model"
|
||||||
|
"github.com/eiblog/eiblog/tools"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -22,6 +24,9 @@ const (
|
|||||||
SearchFilter = `"filter":{"bool":{"must":[%s]}}`
|
SearchFilter = `"filter":{"bool":{"must":[%s]}}`
|
||||||
SearchTerm = `{"term":{"%s":"%s"}}`
|
SearchTerm = `{"term":{"%s":"%s"}}`
|
||||||
SearchDate = `{"range":{"date":{"gte":"%s","lte": "%s","format": "yyyy-MM-dd||yyyy-MM||yyyy"}}}` // 2016-10||/M
|
SearchDate = `{"range":{"date":{"gte":"%s","lte": "%s","format": "yyyy-MM-dd||yyyy-MM||yyyy"}}}` // 2016-10||/M
|
||||||
|
|
||||||
|
ElasticIndex = "eiblog"
|
||||||
|
ElasticType = "article"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -30,7 +35,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mappings := fmt.Sprintf(`{"mappings":{"%s":{"properties":{"content":{"analyzer":"ik_syno","search_analyzer":"ik_syno","term_vector":"with_positions_offsets","type":"string"},"date":{"index":"not_analyzed","type":"date"},"slug":{"type":"string"},"tag":{"index":"not_analyzed","type":"string"},"title":{"analyzer":"ik_syno","search_analyzer":"ik_syno","term_vector":"with_positions_offsets","type":"string"}}}}}`, "article")
|
mappings := fmt.Sprintf(`{"mappings":{"%s":{"properties":{"content":{"analyzer":"ik_syno","search_analyzer":"ik_syno","term_vector":"with_positions_offsets","type":"string"},"date":{"index":"not_analyzed","type":"date"},"slug":{"type":"string"},"tag":{"index":"not_analyzed","type":"string"},"title":{"analyzer":"ik_syno","search_analyzer":"ik_syno","term_vector":"with_positions_offsets","type":"string"}}}}}`, "article")
|
||||||
err := createIndexAndMappings("eiblog", "article", []byte(mappings))
|
err := createIndexAndMappings(ElasticIndex, ElasticType, []byte(mappings))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -97,7 +102,40 @@ func ElasticSearch(query string, size, from int) (*searchIndexResult, error) {
|
|||||||
if kw != "" {
|
if kw != "" {
|
||||||
dsl = strings.Replace(strings.Replace(`{"highlight":{"fields":{"content":{},"title":{}},"post_tags":["\u003c/b\u003e"],"pre_tags":["\u003cb\u003e"]},"query":{"dis_max":{"queries":[{"match":{"title":{"boost":4,"minimum_should_match":"50%","query":"$1"}}},{"match":{"content":{"boost":4,"minimum_should_match":"75%","query":"$1"}}},{"match":{"tag":{"boost":2,"minimum_should_match":"100%","query":"$1"}}},{"match":{"slug":{"boost":1,"minimum_should_match":"100%","query":"$1"}}}],"tie_breaker":0.3}},$2}`, "$1", kw, -1), "$2", fmt.Sprintf(SearchFilter, strings.Join(filter, ",")), -1)
|
dsl = strings.Replace(strings.Replace(`{"highlight":{"fields":{"content":{},"title":{}},"post_tags":["\u003c/b\u003e"],"pre_tags":["\u003cb\u003e"]},"query":{"dis_max":{"queries":[{"match":{"title":{"boost":4,"minimum_should_match":"50%","query":"$1"}}},{"match":{"content":{"boost":4,"minimum_should_match":"75%","query":"$1"}}},{"match":{"tag":{"boost":2,"minimum_should_match":"100%","query":"$1"}}},{"match":{"slug":{"boost":1,"minimum_should_match":"100%","query":"$1"}}}],"tie_breaker":0.3}},$2}`, "$1", kw, -1), "$2", fmt.Sprintf(SearchFilter, strings.Join(filter, ",")), -1)
|
||||||
}
|
}
|
||||||
return indexQueryDSL("article", "eiblog", size, from, []byte(dsl))
|
return indexQueryDSL(ElasticIndex, ElasticType, size, from, []byte(dsl))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElasticAddIndex 添加或更新索引
|
||||||
|
func ElasticAddIndex(article *model.Article) error {
|
||||||
|
if err := checkESConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := strings.Split(article.Tags, ",")
|
||||||
|
img := tools.PickFirstImage(article.Content)
|
||||||
|
mapping := map[string]interface{}{
|
||||||
|
"title": article.Title,
|
||||||
|
"content": tools.IgnoreHtmlTag(article.Content),
|
||||||
|
"slug": article.Slug,
|
||||||
|
"tag": tags,
|
||||||
|
"img": img,
|
||||||
|
"date": article.CreatedAt,
|
||||||
|
}
|
||||||
|
data, _ := json.Marshal(mapping)
|
||||||
|
return indexOrUpdateDocument(ElasticIndex, ElasticType, article.ID, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElasticDelIndex 删除索引
|
||||||
|
func ElasticDelIndex(ids []int) error {
|
||||||
|
if err := checkESConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var target []string
|
||||||
|
for _, id := range ids {
|
||||||
|
target = append(target, fmt.Sprint(id))
|
||||||
|
}
|
||||||
|
return deleteIndexDocument(ElasticIndex, ElasticType, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
// indicesCreateResult 索引创建结果
|
// indicesCreateResult 索引创建结果
|
||||||
@@ -139,7 +177,7 @@ func createIndexAndMappings(index, typ string, mappings []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// indexOrUpdateDocument 创建或更新索引
|
// indexOrUpdateDocument 创建或更新索引
|
||||||
func indexOrUpdateDocument(index, typ string, id int32, doc []byte) (err error) {
|
func indexOrUpdateDocument(index, typ string, id int, doc []byte) (err error) {
|
||||||
rawurl := fmt.Sprintf("%s/%s/%s/%d", config.Conf.ESHost, index, typ, id)
|
rawurl := fmt.Sprintf("%s/%s/%s/%d", config.Conf.ESHost, index, typ, id)
|
||||||
resp, err := httpPut(rawurl, doc)
|
resp, err := httpPut(rawurl, doc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ type Account struct {
|
|||||||
PhoneN string `gorm:"column:phone_n;not null" bson:"phone_n"` // 手机号
|
PhoneN string `gorm:"column:phone_n;not null" bson:"phone_n"` // 手机号
|
||||||
Address string `gorm:"column:address;not null" bson:"address"` // 地址信息
|
Address string `gorm:"column:address;not null" bson:"address"` // 地址信息
|
||||||
|
|
||||||
LogoutAt time.Time `gorm:"column:logout_at;default:null" bson:"logout_at"` // 登出时间
|
LogoutAt time.Time `gorm:"column:logout_at;default:null" bson:"logout_at"` // 登出时间
|
||||||
LoginIP string `gorm:"column:login_ip;default:null" bson:"login_ip"` // 最近登录IP
|
LoginIP string `gorm:"column:login_ip;default:null" bson:"login_ip"` // 最近登录IP
|
||||||
LoginUA string `gorm:"column:login_ua;default:null" bson:"login_ua"` // 最近登录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"` // 最近登录时间
|
LoginAt time.Time `gorm:"column:login_at;default:now()" bson:"login_at"` // 最近登录时间
|
||||||
CreatedAt time.Time `gorm:"column:creatd_at;default:now()" bson:"created_at"` // 创建时间
|
CreatedAt time.Time `gorm:"column:created_at;default:now()" bson:"created_at"` // 创建时间
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,3 +103,27 @@ var monthToDays = map[time.Month]int{
|
|||||||
func isLeapYear(year int) bool {
|
func isLeapYear(year int) bool {
|
||||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var regexpImg = regexp.MustCompile(`data-src="(.*?)"`)
|
||||||
|
|
||||||
|
// PickFirstImage 获取第一张图片
|
||||||
|
func PickFirstImage(html string) string {
|
||||||
|
sli := regexpImg.FindAllStringSubmatch(html, 1)
|
||||||
|
if len(sli) > 0 && len(sli[0]) > 1 {
|
||||||
|
return sli[0][1]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
regexpBrackets = regexp.MustCompile(`<[\S\s]+?>`)
|
||||||
|
regexpEnter = regexp.MustCompile(`\s+`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// IgnoreHtmlTag 去掉 html tag
|
||||||
|
func IgnoreHtmlTag(src string) string {
|
||||||
|
// 去除所有尖括号内的HTML代码
|
||||||
|
src = regexpBrackets.ReplaceAllString(src, "")
|
||||||
|
// 去除换行符
|
||||||
|
return regexpEnter.ReplaceAllString(src, "")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// Package tools provides ...
|
// Package tools provides ...
|
||||||
package tools
|
package tools
|
||||||
|
|
||||||
import "regexp"
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
var regexpEmail = regexp.MustCompile(`^(\w)+([\.\-]\w+)*@(\w)+((\.\w+)+)$`)
|
var regexpEmail = regexp.MustCompile(`^(\w)+([\.\-]\w+)*@(\w)+((\.\w+)+)$`)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user