mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-07 07:12:27 +08:00
chore: complete api
This commit is contained in:
@@ -49,10 +49,10 @@ func runHTTPServer(endRun chan bool) {
|
||||
root := filepath.Join(config.WorkDir, "assets")
|
||||
e.Static("/static", root)
|
||||
|
||||
// frontend pages
|
||||
page.RegisterRoutes(e)
|
||||
// static files
|
||||
file.RegisterRoutes(e)
|
||||
// frontend pages
|
||||
page.RegisterRoutes(e)
|
||||
// unauthz api
|
||||
admin.RegisterRoutes(e)
|
||||
|
||||
|
||||
403
pkg/cache/cache.go
vendored
403
pkg/cache/cache.go
vendored
@@ -71,8 +71,64 @@ type Cache struct {
|
||||
ArticlesMap map[string]*model.Article // slug:article
|
||||
}
|
||||
|
||||
// PageArticles 文章翻页
|
||||
func (c *Cache) PageArticles(page int, pageSize int) (prev,
|
||||
// AddArticle 添加文章
|
||||
func (c *Cache) AddArticle(article *model.Article) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
// store
|
||||
err := c.InsertArticle(context.Background(), article)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 是否是草稿
|
||||
if article.IsDraft {
|
||||
return nil
|
||||
}
|
||||
// 正式发布文章
|
||||
c.refreshCache(article, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RepArticle 替换文章
|
||||
func (c *Cache) RepArticle(oldArticle, newArticle *model.Article) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.ArticlesMap[newArticle.Slug] = newArticle
|
||||
render.GenerateExcerptMarkdown(newArticle)
|
||||
if newArticle.ID < config.Conf.BlogApp.General.StartID {
|
||||
return
|
||||
}
|
||||
if oldArticle != nil { // 移除旧文章
|
||||
c.refreshCache(oldArticle, true)
|
||||
}
|
||||
c.refreshCache(newArticle, false)
|
||||
}
|
||||
|
||||
// DelArticles 删除文章
|
||||
func (c *Cache) DelArticles(ids []int) error {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
for _, id := range ids {
|
||||
article, _ := c.FindArticleByID(id)
|
||||
|
||||
// set delete
|
||||
err := c.UpdateArticle(context.Background(), id, map[string]interface{}{
|
||||
"deleted_at": time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// drop from tags,series,archives
|
||||
c.refreshCache(article, true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PageArticleFE 文章翻页
|
||||
func (c *Cache) PageArticleFE(page int, pageSize int) (prev,
|
||||
next int, articles []*model.Article) {
|
||||
|
||||
var l int
|
||||
@@ -106,114 +162,37 @@ func (c *Cache) PageArticles(page int, pageSize int) (prev,
|
||||
return
|
||||
}
|
||||
|
||||
// PageArticlesBE 后台文章分页
|
||||
// func (c *Cache) PageArticleBE(se int, kw string, draft, del bool, p, n int)(max int, artcs []*model.Article){
|
||||
//
|
||||
// }
|
||||
// PageArticleBE 后台文章分页
|
||||
func (c *Cache) PageArticleBE(se int, kw string, draft, del bool, p,
|
||||
n int) ([]*model.Article, int) {
|
||||
|
||||
// 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)
|
||||
search := store.SearchArticles{
|
||||
Page: p,
|
||||
Limit: n,
|
||||
Fields: make(map[string]interface{}),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddArticle 添加文章
|
||||
func (c *Cache) AddArticle(article *model.Article) error {
|
||||
err := c.InsertArticle(context.Background(), article)
|
||||
if draft {
|
||||
search.Fields[store.SearchArticleDraft] = true
|
||||
} else if del {
|
||||
search.Fields[store.SearchArticleTrash] = true
|
||||
} else {
|
||||
search.Fields[store.SearchArticleDraft] = false
|
||||
if se > 0 {
|
||||
search.Fields[store.SearchArticleSerieID] = se
|
||||
}
|
||||
if kw != "" {
|
||||
search.Fields[store.SearchArticleTitle] = kw
|
||||
}
|
||||
}
|
||||
articles, count, err := c.LoadArticleList(context.Background(), search)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, 0
|
||||
}
|
||||
// 正式发布文章
|
||||
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)
|
||||
max := count / n
|
||||
if max%n > 0 {
|
||||
max++
|
||||
}
|
||||
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)
|
||||
return articles, max
|
||||
}
|
||||
|
||||
// FindArticleByID 通过ID查找文章
|
||||
@@ -226,86 +205,59 @@ func (c *Cache) FindArticleByID(id int) (*model.Article, int) {
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
// loadOrInit 读取数据或初始化
|
||||
func (c *Cache) loadOrInit() error {
|
||||
blogapp := config.Conf.BlogApp
|
||||
// blogger
|
||||
blogger := &model.Blogger{
|
||||
BlogName: strings.Title(blogapp.Account.Username),
|
||||
SubTitle: "Rome was not built in one day.",
|
||||
BeiAn: "蜀ICP备xxxxxxxx号-1",
|
||||
BTitle: fmt.Sprintf("%s's Blog", strings.Title(blogapp.Account.Username)),
|
||||
Copyright: `本站使用「<a href="//creativecommons.org/licenses/by/4.0/">署名 4.0 国际</a>」创作共享协议,转载请注明作者及原网址。`,
|
||||
}
|
||||
created, err := c.LoadInsertBlogger(context.Background(), blogger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Blogger = blogger
|
||||
if created { // init articles: about blogroll
|
||||
about := &model.Article{
|
||||
ID: 1, // 固定ID
|
||||
Author: blogapp.Account.Username,
|
||||
Title: "关于",
|
||||
Slug: "about",
|
||||
}
|
||||
err = c.InsertArticle(context.Background(), about)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 推送到 disqus
|
||||
go internal.ThreadCreate(about, blogger.BTitle)
|
||||
blogroll := &model.Article{
|
||||
ID: 2, // 固定ID
|
||||
Author: blogapp.Account.Username,
|
||||
Title: "友情链接",
|
||||
Slug: "blogroll",
|
||||
}
|
||||
err = c.InsertArticle(context.Background(), blogroll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// account
|
||||
pwd := tools.EncryptPasswd(blogapp.Account.Password,
|
||||
blogapp.Account.Password)
|
||||
// refreshCache 刷新缓存
|
||||
func (c *Cache) refreshCache(article *model.Article, del bool) {
|
||||
if del {
|
||||
_, idx := c.FindArticleByID(article.ID)
|
||||
|
||||
account := &model.Account{
|
||||
Username: blogapp.Account.Username,
|
||||
Password: pwd,
|
||||
delete(c.ArticlesMap, article.Slug)
|
||||
c.Articles = append(Ei.Articles[:idx], Ei.Articles[idx+1:]...)
|
||||
// 从链表移除
|
||||
c.recalcLinkedList(article, true)
|
||||
// 从tag、serie、archive移除
|
||||
c.redelArticle(article)
|
||||
}
|
||||
_, err = c.LoadInsertAccount(context.Background(), account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Account = account
|
||||
// all articles
|
||||
articles, err := c.LoadAllArticle(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, v := range articles {
|
||||
// 渲染页面
|
||||
render.GenerateExcerptMarkdown(v)
|
||||
// 添加文章
|
||||
defer render.GenerateExcerptMarkdown(article)
|
||||
|
||||
c.ArticlesMap[v.Slug] = v
|
||||
// 分析文章
|
||||
if v.ID < blogapp.General.StartID {
|
||||
continue
|
||||
c.ArticlesMap[article.Slug] = article
|
||||
c.Articles = append([]*model.Article{article}, c.Articles...)
|
||||
sort.Sort(c.Articles)
|
||||
// 从链表添加
|
||||
c.recalcLinkedList(article, false)
|
||||
// 从tag、serie、archive添加
|
||||
c.readdArticle(article, true)
|
||||
}
|
||||
|
||||
// recalcLinkedList 重算文章链表
|
||||
func (c *Cache) recalcLinkedList(article *model.Article, del bool) {
|
||||
// 删除操作
|
||||
if del {
|
||||
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
|
||||
}
|
||||
if i > 0 {
|
||||
v.Prev = Ei.Articles[i-1]
|
||||
}
|
||||
if Ei.Articles[i+1].ID >= blogapp.General.StartID {
|
||||
v.Next = Ei.Articles[i+1]
|
||||
}
|
||||
c.readdArticle(v, false)
|
||||
return
|
||||
}
|
||||
// 添加操作
|
||||
_, 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
|
||||
}
|
||||
Ei.Articles = articles
|
||||
// 重建专题与归档
|
||||
PagesCh <- PageSeries
|
||||
PagesCh <- PageArchive
|
||||
return nil
|
||||
}
|
||||
|
||||
// readdArticle 添加文章到tag、series、archive
|
||||
@@ -396,6 +348,97 @@ func (c *Cache) redelArticle(article *model.Article) {
|
||||
}
|
||||
}
|
||||
|
||||
// loadOrInit 读取数据或初始化
|
||||
func (c *Cache) loadOrInit() error {
|
||||
blogapp := config.Conf.BlogApp
|
||||
// blogger
|
||||
blogger := &model.Blogger{
|
||||
BlogName: strings.Title(blogapp.Account.Username),
|
||||
SubTitle: "Rome was not built in one day.",
|
||||
BeiAn: "蜀ICP备xxxxxxxx号-1",
|
||||
BTitle: fmt.Sprintf("%s's Blog", strings.Title(blogapp.Account.Username)),
|
||||
Copyright: `本站使用「<a href="//creativecommons.org/licenses/by/4.0/">署名 4.0 国际</a>」创作共享协议,转载请注明作者及原网址。`,
|
||||
}
|
||||
created, err := c.LoadInsertBlogger(context.Background(), blogger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Blogger = blogger
|
||||
if created { // init articles: about blogroll
|
||||
about := &model.Article{
|
||||
ID: 1, // 固定ID
|
||||
Author: blogapp.Account.Username,
|
||||
Title: "关于",
|
||||
Slug: "about",
|
||||
UpdatedAt: time.Now(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
err = c.InsertArticle(context.Background(), about)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 推送到 disqus
|
||||
go internal.ThreadCreate(about, blogger.BTitle)
|
||||
blogroll := &model.Article{
|
||||
ID: 2, // 固定ID
|
||||
Author: blogapp.Account.Username,
|
||||
Title: "友情链接",
|
||||
Slug: "blogroll",
|
||||
UpdatedAt: time.Now(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
err = c.InsertArticle(context.Background(), blogroll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// account
|
||||
pwd := tools.EncryptPasswd(blogapp.Account.Password,
|
||||
blogapp.Account.Password)
|
||||
|
||||
account := &model.Account{
|
||||
Username: blogapp.Account.Username,
|
||||
Password: pwd,
|
||||
}
|
||||
_, err = c.LoadInsertAccount(context.Background(), account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Account = account
|
||||
// all articles
|
||||
search := store.SearchArticles{
|
||||
Page: 1,
|
||||
Limit: 9999,
|
||||
Fields: map[string]interface{}{store.SearchArticleDraft: false},
|
||||
}
|
||||
articles, _, err := c.LoadArticleList(context.Background(), search)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, v := range articles {
|
||||
// 渲染页面
|
||||
render.GenerateExcerptMarkdown(v)
|
||||
|
||||
c.ArticlesMap[v.Slug] = v
|
||||
// 分析文章
|
||||
if v.ID < blogapp.General.StartID {
|
||||
continue
|
||||
}
|
||||
if i > 0 {
|
||||
v.Prev = Ei.Articles[i-1]
|
||||
}
|
||||
if Ei.Articles[i+1].ID >= blogapp.General.StartID {
|
||||
v.Next = Ei.Articles[i+1]
|
||||
}
|
||||
c.readdArticle(v, false)
|
||||
}
|
||||
Ei.Articles = articles
|
||||
// 重建专题与归档
|
||||
PagesCh <- PageSeries
|
||||
PagesCh <- PageArchive
|
||||
return nil
|
||||
}
|
||||
|
||||
// regeneratePages 重新生成series,archive页面
|
||||
func (c *Cache) regeneratePages() {
|
||||
for {
|
||||
|
||||
115
pkg/cache/store/mongodb.go
vendored
115
pkg/cache/store/mongodb.go
vendored
@@ -233,16 +233,6 @@ func (db *mongodb) RemoveArticle(ctx context.Context, id int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteArticle 软删除文章,放入回收箱
|
||||
func (db *mongodb) DeleteArticle(ctx context.Context, id int) error {
|
||||
collection := db.Database(mongoDBName).Collection(collectionArticle)
|
||||
|
||||
filter := bson.M{"id": id}
|
||||
update := bson.M{"$set": bson.M{"deletetime": time.Now()}}
|
||||
_, err := collection.UpdateOne(ctx, filter, update)
|
||||
return err
|
||||
}
|
||||
|
||||
// CleanArticles 清理回收站文章
|
||||
func (db *mongodb) CleanArticles(ctx context.Context) error {
|
||||
collection := db.Database(mongoDBName).Collection(collectionArticle)
|
||||
@@ -270,16 +260,6 @@ func (db *mongodb) UpdateArticle(ctx context.Context, id int,
|
||||
return err
|
||||
}
|
||||
|
||||
// RecoverArticle 恢复文章到草稿
|
||||
func (db *mongodb) RecoverArticle(ctx context.Context, id int) error {
|
||||
collection := db.Database(mongoDBName).Collection(collectionArticle)
|
||||
|
||||
filter := bson.M{"id": id}
|
||||
update := bson.M{"$set": bson.M{"deletetime": time.Time{}, "isdraft": true}}
|
||||
_, err := collection.UpdateOne(ctx, filter, update)
|
||||
return err
|
||||
}
|
||||
|
||||
// LoadArticle 查找文章
|
||||
func (db *mongodb) LoadArticle(ctx context.Context, id int) (*model.Article, error) {
|
||||
collection := db.Database(mongoDBName).Collection(collectionArticle)
|
||||
@@ -295,76 +275,55 @@ func (db *mongodb) LoadArticle(ctx context.Context, id int) (*model.Article, err
|
||||
return article, err
|
||||
}
|
||||
|
||||
// LoadAllArticle 读取所有文章
|
||||
func (db *mongodb) LoadAllArticle(ctx context.Context) (model.SortedArticles, error) {
|
||||
// LoadArticleList 获取文章列表
|
||||
func (db *mongodb) LoadArticleList(ctx context.Context, search SearchArticles) (
|
||||
model.SortedArticles, int, error) {
|
||||
collection := db.Database(mongoDBName).Collection(collectionArticle)
|
||||
|
||||
filter := bson.M{"isdraft": false, "deletetime": bson.M{"$eq": time.Time{}}}
|
||||
cur, err := collection.Find(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
filter := bson.M{}
|
||||
for k, v := range search.Fields {
|
||||
switch k {
|
||||
case SearchArticleDraft:
|
||||
if ok := v.(bool); ok {
|
||||
filter["is_draft"] = true
|
||||
} else {
|
||||
filter["is_draft"] = false
|
||||
filter["deleted_at"] = bson.M{"$eq": time.Time{}}
|
||||
}
|
||||
case SearchArticleTitle:
|
||||
filter["title"] = bson.M{
|
||||
"$regex": v.(string),
|
||||
"$options": "$i",
|
||||
}
|
||||
case SearchArticleSerieID:
|
||||
filter["serie_id"] = v.(int)
|
||||
case SearchArticleTrash:
|
||||
filter["deleted_at"] = bson.M{"$nq": time.Time{}}
|
||||
}
|
||||
}
|
||||
// search count
|
||||
count, err := collection.CountDocuments(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
opts := options.Find().SetLimit(int64(search.Limit)).
|
||||
SetSkip(int64((search.Page - 1) * search.Limit)).
|
||||
SetSort(bson.M{"created_at": -1})
|
||||
cur, err := collection.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer cur.Close(ctx)
|
||||
|
||||
var articles model.SortedArticles
|
||||
for cur.Next(ctx) {
|
||||
obj := model.Article{}
|
||||
err = cur.Decode(&obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
articles = append(articles, &obj)
|
||||
}
|
||||
sort.Sort(articles)
|
||||
return articles, nil
|
||||
}
|
||||
|
||||
// LoadTrashArticles 读取回收箱
|
||||
func (db *mongodb) LoadTrashArticles(ctx context.Context) (model.SortedArticles, error) {
|
||||
collection := db.Database(mongoDBName).Collection(collectionArticle)
|
||||
|
||||
filter := bson.M{"deletetime": bson.M{"$ne": time.Time{}}}
|
||||
cur, err := collection.Find(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cur.Close(ctx)
|
||||
|
||||
var articles model.SortedArticles
|
||||
for cur.Next(ctx) {
|
||||
obj := model.Article{}
|
||||
err = cur.Decode(&obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
articles = append(articles, &obj)
|
||||
}
|
||||
sort.Sort(articles)
|
||||
return articles, nil
|
||||
}
|
||||
|
||||
// LoadDraftArticles 读取草稿箱
|
||||
func (db *mongodb) LoadDraftArticles(ctx context.Context) (model.SortedArticles, error) {
|
||||
collection := db.Database(mongoDBName).Collection(collectionArticle)
|
||||
|
||||
filter := bson.M{"isdraft": true}
|
||||
cur, err := collection.Find(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cur.Close(ctx)
|
||||
|
||||
var articles model.SortedArticles
|
||||
for cur.Next(ctx) {
|
||||
obj := model.Article{}
|
||||
err = cur.Decode(&obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
articles = append(articles, &obj)
|
||||
}
|
||||
sort.Sort(articles)
|
||||
return articles, nil
|
||||
return articles, int(count), nil
|
||||
}
|
||||
|
||||
// counter counter
|
||||
|
||||
27
pkg/cache/store/store.go
vendored
27
pkg/cache/store/store.go
vendored
@@ -15,6 +15,21 @@ var (
|
||||
stores = make(map[string]Driver)
|
||||
)
|
||||
|
||||
// search field
|
||||
const (
|
||||
SearchArticleDraft = "draft"
|
||||
SearchArticleTrash = "trash"
|
||||
SearchArticleTitle = "title"
|
||||
SearchArticleSerieID = "serieid"
|
||||
)
|
||||
|
||||
// SearchArticles 搜索字段
|
||||
type SearchArticles struct {
|
||||
Page int // 第几页/1
|
||||
Limit int // 每页大小
|
||||
Fields map[string]interface{} // 字段:值
|
||||
}
|
||||
|
||||
// Store 存储后端
|
||||
type Store interface {
|
||||
// LoadInsertBlogger 读取或创建博客
|
||||
@@ -40,22 +55,14 @@ type Store interface {
|
||||
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
|
||||
// LoadArticle 查找文章
|
||||
LoadArticle(ctx context.Context, id int) (*model.Article, 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)
|
||||
// LoadArticleList 查找文章列表
|
||||
LoadArticleList(ctx context.Context, search SearchArticles) (model.SortedArticles, int, error)
|
||||
}
|
||||
|
||||
// Driver 存储驱动
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eiblog/eiblog/pkg/cache"
|
||||
@@ -37,6 +38,18 @@ func RegisterRoutesAuthz(group gin.IRoutes) {
|
||||
group.GET("/draft-delete", handleDraftDelete)
|
||||
|
||||
group.POST("/api/account", handleAPIAccount)
|
||||
group.POST("/api/blog", handleAPIBlogger)
|
||||
group.POST("/api/password", handleAPIPassword)
|
||||
group.POST("/api/post-delete", handleAPIPostDelete)
|
||||
group.POST("/api/post-add", handleAPIPostCreate)
|
||||
group.POST("/api/serie-delete", handleAPISerieDelete)
|
||||
group.POST("/api/serie-add", handleAPISerieCreate)
|
||||
group.POST("/api/serie-sort", handleAPISerieSort)
|
||||
group.POST("/api/draft-delete", handleDraftDelete)
|
||||
group.POST("/api/trash-delete", handleAPITrashDelete)
|
||||
group.POST("/api/trash-recover", handleAPITrashRecover)
|
||||
group.POST("/api/file-upload", handleAPIQiniuUpload)
|
||||
group.POST("/api/file-delete", handleAPIQiniuDelete)
|
||||
}
|
||||
|
||||
// handleAcctLogin 登录接口
|
||||
@@ -67,7 +80,7 @@ func handleAcctLogin(c *gin.Context) {
|
||||
c.Redirect(http.StatusFound, "/admin/profile")
|
||||
}
|
||||
|
||||
// handleDraftDelete 删除草稿
|
||||
// handleDraftDelete 删除草稿, 物理删除
|
||||
func handleDraftDelete(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Query("cid"))
|
||||
if err != nil || id < 1 {
|
||||
@@ -189,7 +202,7 @@ func handleAPIPostDelete(c *gin.Context) {
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
err := cache.Ei.DeleteArticles(ids)
|
||||
err := cache.Ei.DelArticles(ids)
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIPostDelete.DeleteArticles: ", err)
|
||||
|
||||
@@ -261,7 +274,7 @@ func handleAPIPostCreate(c *gin.Context) {
|
||||
if err != nil || cid < 1 {
|
||||
err = cache.Ei.AddArticle(article)
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIPostCreate.InsertArticle: ", err)
|
||||
logrus.Error("handleAPIPostCreate.AddArticle: ", err)
|
||||
return
|
||||
}
|
||||
if !article.IsDraft {
|
||||
@@ -289,12 +302,12 @@ func handleAPIPostCreate(c *gin.Context) {
|
||||
artc.UpdatedAt = time.Now()
|
||||
}
|
||||
// 数据库更新
|
||||
err = cache.Ei.UpdateArticle(context.Background(), artc.ID, map[string]interface{}{
|
||||
err = cache.Ei.UpdateArticle(context.Background(), article.ID, map[string]interface{}{
|
||||
"title": article.Title,
|
||||
"content": article.Content,
|
||||
"serie_id": article.SerieID,
|
||||
"tags": article.Tags,
|
||||
"is_draft": article.IsDraft,
|
||||
"tags": article.Tags,
|
||||
"updated_at": article.UpdatedAt,
|
||||
"created_at": article.CreatedAt,
|
||||
})
|
||||
@@ -303,7 +316,7 @@ func handleAPIPostCreate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
if !artc.IsDraft {
|
||||
cache.Ei.ReplaceArticle(artc, article)
|
||||
cache.Ei.RepArticle(artc, article)
|
||||
// 异步执行,快
|
||||
go func() {
|
||||
// elastic
|
||||
@@ -349,6 +362,12 @@ func handleAPISerieDelete(c *gin.Context) {
|
||||
responseNotice(c, NoticeSuccess, "删除成功", "")
|
||||
}
|
||||
|
||||
// handleAPISerieSort 专题排序
|
||||
func handleAPISerieSort(c *gin.Context) {
|
||||
v := c.PostFormArray("mid[]")
|
||||
logrus.Debug(v)
|
||||
}
|
||||
|
||||
// handleAPISerieCreate 添加专题,如果专题有提交 mid 即更新专题
|
||||
func handleAPISerieCreate(c *gin.Context) {
|
||||
name := c.PostForm("name")
|
||||
@@ -396,6 +415,92 @@ func handleAPISerieCreate(c *gin.Context) {
|
||||
responseNotice(c, NoticeSuccess, "操作成功", "")
|
||||
}
|
||||
|
||||
// handleAPITrashDelete 删除回收箱, 物理删除
|
||||
func handleAPITrashDelete(c *gin.Context) {
|
||||
for _, v := range c.PostFormArray("mid[]") {
|
||||
id, err := strconv.Atoi(v)
|
||||
if err != nil || id < 1 {
|
||||
responseNotice(c, NoticeNotice, "参数错误", "")
|
||||
return
|
||||
}
|
||||
err = cache.Ei.RemoveArticle(context.Background(), id)
|
||||
if err != nil {
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
}
|
||||
responseNotice(c, NoticeSuccess, "删除成功", "")
|
||||
}
|
||||
|
||||
// handleAPITrashRecover 恢复到草稿
|
||||
func handleAPITrashRecover(c *gin.Context) {
|
||||
for _, v := range c.PostFormArray("mid[]") {
|
||||
id, err := strconv.Atoi(v)
|
||||
if err != nil || id < 1 {
|
||||
responseNotice(c, NoticeNotice, "参数错误", "")
|
||||
return
|
||||
|
||||
}
|
||||
err = cache.Ei.UpdateArticle(context.Background(), id, map[string]interface{}{
|
||||
"deleted_at": time.Time{},
|
||||
"is_draft": true,
|
||||
})
|
||||
if err != nil {
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
}
|
||||
responseNotice(c, NoticeSuccess, "恢复成功", "")
|
||||
}
|
||||
|
||||
// handleAPIQiniuUpload 上传文件
|
||||
func handleAPIQiniuUpload(c *gin.Context) {
|
||||
type Size interface {
|
||||
Size() int64
|
||||
}
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIQiniuUpload.FormFile: ", err)
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
s, ok := file.(Size)
|
||||
if !ok {
|
||||
logrus.Error("assert failed")
|
||||
c.String(http.StatusBadRequest, "false")
|
||||
return
|
||||
}
|
||||
filename := strings.ToLower(header.Filename)
|
||||
url, err := internal.QiniuUpload(filename, s.Size(), file)
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIQiniuUpload.QiniuUpload: ", err)
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
typ := header.Header.Get("Content-Type")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"title": filename,
|
||||
"isImage": typ[:5] == "image",
|
||||
"url": url,
|
||||
"bytes": fmt.Sprintf("%dkb", s.Size()/1000),
|
||||
})
|
||||
}
|
||||
|
||||
// handleAPIQiniuDelete 删除文件
|
||||
func handleAPIQiniuDelete(c *gin.Context) {
|
||||
defer c.String(http.StatusOK, "删掉了吗?鬼知道。。。")
|
||||
|
||||
name := c.PostForm("title")
|
||||
if name == "" {
|
||||
logrus.Error("handleAPIQiniuDelete.PostForm: 参数错误")
|
||||
return
|
||||
}
|
||||
err := internal.QiniuDelete(name)
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIQiniuDelete.QiniuDelete: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// parseLocationDate 解析日期
|
||||
func parseLocationDate(date string) time.Time {
|
||||
t, err := time.ParseInLocation("2006-01-02 15:04", date, time.Local)
|
||||
|
||||
@@ -42,7 +42,7 @@ func timerFeed() {
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
_, _, articles := cache.Ei.PageArticles(1, 20)
|
||||
_, _, articles := cache.Ei.PageArticleFE(1, 20)
|
||||
params := map[string]interface{}{
|
||||
"Titile": cache.Ei.Blogger.BTitle,
|
||||
"SubTitle": cache.Ei.Blogger.SubTitle,
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/eiblog/eiblog/pkg/cache"
|
||||
"github.com/eiblog/eiblog/pkg/cache/store"
|
||||
"github.com/eiblog/eiblog/pkg/config"
|
||||
"github.com/eiblog/eiblog/pkg/core/blog"
|
||||
"github.com/eiblog/eiblog/pkg/model"
|
||||
@@ -23,7 +24,7 @@ import (
|
||||
func baseBEParams(c *gin.Context) gin.H {
|
||||
return gin.H{
|
||||
"Author": cache.Ei.Account.Username,
|
||||
"Qiniu": config.Conf.BlogApp.Qiniu.Domain,
|
||||
"Qiniu": config.Conf.BlogApp.Qiniu,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,8 +104,8 @@ func handleAdminPosts(c *gin.Context) {
|
||||
params["Serie"] = se
|
||||
params["KW"] = kw
|
||||
var max int
|
||||
// TODO
|
||||
// max, params["List"] = cache.Ei.PageListBack(se, kw, false, false, pg, setting.Conf.General.PageSize)
|
||||
params["List"], max = cache.Ei.PageArticleBE(se, kw, false, false,
|
||||
pg, config.Conf.BlogApp.General.PageSize)
|
||||
if pg < max {
|
||||
vals.Set("page", fmt.Sprint(pg+1))
|
||||
params["Next"] = vals.Encode()
|
||||
@@ -171,7 +172,12 @@ func handleAdminDraft(c *gin.Context) {
|
||||
params["Manage"] = true
|
||||
params["Path"] = c.Request.URL.Path
|
||||
var err error
|
||||
params["List"], err = cache.Ei.LoadDraftArticles(context.Background())
|
||||
search := store.SearchArticles{
|
||||
Page: 1,
|
||||
Limit: 9999,
|
||||
Fields: map[string]interface{}{store.SearchArticleDraft: true},
|
||||
}
|
||||
params["List"], _, err = cache.Ei.LoadArticleList(context.Background(), search)
|
||||
if err != nil {
|
||||
logrus.Error("handleDraft.LoadDraftArticles: ", err)
|
||||
c.Status(http.StatusBadRequest)
|
||||
@@ -188,9 +194,14 @@ func handleAdminTrash(c *gin.Context) {
|
||||
params["Manage"] = true
|
||||
params["Path"] = c.Request.URL.Path
|
||||
var err error
|
||||
params["List"], err = cache.Ei.LoadTrashArticles(context.Background())
|
||||
search := store.SearchArticles{
|
||||
Page: 1,
|
||||
Limit: 9999,
|
||||
Fields: map[string]interface{}{store.SearchArticleTrash: true},
|
||||
}
|
||||
params["List"], _, err = cache.Ei.LoadArticleList(context.Background(), search)
|
||||
if err != nil {
|
||||
logrus.Error("handleTrash.LoadTrashArticles: ", err)
|
||||
logrus.Error("handleTrash.LoadArticleList: ", err)
|
||||
c.HTML(http.StatusBadRequest, "backLayout.html", params)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func handleHomePage(c *gin.Context) {
|
||||
if err != nil || pn < 1 {
|
||||
pn = 1
|
||||
}
|
||||
params["Prev"], params["Next"], params["List"] = cache.Ei.PageArticles(pn,
|
||||
params["Prev"], params["Next"], params["List"] = cache.Ei.PageArticleFE(pn,
|
||||
config.Conf.BlogApp.General.PageNum)
|
||||
|
||||
renderHTMLHomeLayout(c, "home", params)
|
||||
|
||||
Reference in New Issue
Block a user