chore: trash

This commit is contained in:
deepzz0
2021-04-27 18:04:59 +08:00
parent 63b55b2df8
commit a39e3aac3b
8 changed files with 423 additions and 26 deletions

27
pkg/cache/cache.go vendored
View File

@@ -24,9 +24,9 @@ var (
Ei *Cache
// regenerate pages chan
pagesCh = make(chan string, 2)
pageSeries = "series-md"
pageArchive = "archive-md"
PagesCh = make(chan string, 2)
PageSeries = "series-md"
PageArchive = "archive-md"
)
func init() {
@@ -141,6 +141,11 @@ 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){
//
// }
// loadOrInit 读取数据或初始化
func (c *Cache) loadOrInit() error {
blogapp := config.Conf.BlogApp
@@ -218,8 +223,8 @@ func (c *Cache) loadOrInit() error {
}
Ei.Articles = articles
// 重建专题与归档
pagesCh <- pageSeries
pagesCh <- pageArchive
PagesCh <- PageSeries
PagesCh <- PageArchive
return nil
}
@@ -239,7 +244,7 @@ func (c *Cache) rebuildArticle(article *model.Article, needSort bool) {
c.Series[i].Articles = append(c.Series[i].Articles, article)
if needSort {
sort.Sort(c.Series[i].Articles)
pagesCh <- pageSeries // 重建专题
PagesCh <- PageSeries // 重建专题
}
}
}
@@ -251,7 +256,7 @@ func (c *Cache) rebuildArticle(article *model.Article, needSort bool) {
}
if needSort {
sort.Sort(c.Archives[i].Articles)
pagesCh <- pageArchive // 重建归档
PagesCh <- PageArchive // 重建归档
}
return
}
@@ -261,15 +266,15 @@ func (c *Cache) rebuildArticle(article *model.Article, needSort bool) {
Articles: model.SortedArticles{article},
})
if needSort { // 重建归档
pagesCh <- pageArchive
PagesCh <- PageArchive
}
}
// regeneratePages 重新生成series,archive页面
func (c *Cache) regeneratePages() {
for {
switch page := <-pagesCh; page {
case pageSeries:
switch page := <-PagesCh; page {
case PageSeries:
sort.Sort(c.Series)
buf := bytes.Buffer{}
buf.WriteString(c.Blogger.SeriesSay)
@@ -288,7 +293,7 @@ func (c *Cache) regeneratePages() {
buf.WriteString("\n\n")
}
c.PageSeries = string(render.RenderPage(buf.Bytes()))
case pageArchive:
case PageArchive:
sort.Sort(c.Archives)
buf := bytes.Buffer{}
buf.WriteString(c.Blogger.ArchivesSay + "\n")

View File

@@ -280,6 +280,21 @@ func (db *mongodb) RecoverArticle(ctx context.Context, id int) error {
return err
}
// LoadArticle 查找文章
func (db *mongodb) LoadArticle(ctx context.Context, id int) (*model.Article, error) {
collection := db.Database(mongoDBName).Collection(collectionArticle)
filter := bson.M{"id": id}
result := collection.FindOne(ctx, filter)
err := result.Err()
if err != nil {
return nil, err
}
article := &model.Article{}
err = result.Decode(article)
return article, err
}
// LoadAllArticle 读取所有文章
func (db *mongodb) LoadAllArticle(ctx context.Context) (model.SortedArticles, error) {
collection := db.Database(mongoDBName).Collection(collectionArticle)

View File

@@ -48,6 +48,8 @@ type Store interface {
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 读取回收箱

View File

@@ -3,7 +3,9 @@ package admin
import (
"context"
"fmt"
"net/http"
"strconv"
"time"
"github.com/eiblog/eiblog/pkg/cache"
@@ -14,6 +16,13 @@ import (
"github.com/sirupsen/logrus"
)
// 通知cookie
const (
NoticeSuccess = "success"
NoticeNotice = "notice"
NoticeError = "error"
)
// RegisterRoutes register routes
func RegisterRoutes(e *gin.Engine) {
e.POST("/admin/login", handleAcctLogin)
@@ -21,6 +30,9 @@ func RegisterRoutes(e *gin.Engine) {
// RegisterRoutesAuthz register routes
func RegisterRoutesAuthz(group gin.IRoutes) {
group.GET("/draft-delete", handleDraftDelete)
group.POST("/api/account", handleAPIAccount)
}
// handleAcctLogin 登录接口
@@ -50,3 +62,150 @@ func handleAcctLogin(c *gin.Context) {
})
c.Redirect(http.StatusFound, "/admin/profile")
}
// handleDraftDelete 删除草稿
func handleDraftDelete(c *gin.Context) {
id, err := strconv.Atoi(c.Query("cid"))
if err != nil || id < 1 {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
return
}
err = cache.Ei.RemoveArticle(context.Background(), id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "删除错误"})
return
}
c.Redirect(http.StatusFound, "/admin/write-post")
}
// handleAPIAccount 更新账户信息
func handleAPIAccount(c *gin.Context) {
e := c.PostForm("email")
pn := c.PostForm("phoneNumber")
ad := c.PostForm("address")
if (e != "" && !tools.ValidateEmail(e)) || (pn != "" &&
!tools.ValidatePhoneNo(pn)) {
responseNotice(c, NoticeNotice, "参数错误", "")
return
}
err := cache.Ei.UpdateAccount(context.Background(), cache.Ei.Account.Username,
map[string]interface{}{
"email": e,
"phone_n": pn,
"address": ad,
})
if err != nil {
logrus.Error("handleAPIAccount.UpdateAccount: ", err)
responseNotice(c, NoticeNotice, err.Error(), "")
return
}
cache.Ei.Account.Email = e
cache.Ei.Account.PhoneN = pn
cache.Ei.Account.Address = ad
responseNotice(c, NoticeSuccess, "更新成功", "")
}
// handleAPIBlogger 更新博客信息
func handleAPIBlogger(c *gin.Context) {
bn := c.PostForm("blogName")
bt := c.PostForm("bTitle")
ba := c.PostForm("beiAn")
st := c.PostForm("subTitle")
ss := c.PostForm("seriessay")
as := c.PostForm("archivessay")
if bn == "" || bt == "" {
responseNotice(c, NoticeNotice, "参数错误", "")
return
}
err := cache.Ei.UpdateBlogger(context.Background(), map[string]interface{}{
"blog_name": bn,
"b_title": bt,
"sub_title": st,
"series_say": ss,
"archives_say": as,
})
if err != nil {
logrus.Error("handleAPIBlogger.UpdateBlogger: ", err)
responseNotice(c, NoticeNotice, err.Error(), "")
return
}
cache.Ei.Blogger.BlogName = bn
cache.Ei.Blogger.BTitle = bt
cache.Ei.Blogger.BeiAn = ba
cache.Ei.Blogger.SubTitle = st
cache.Ei.Blogger.SeriesSay = ss
cache.Ei.Blogger.ArchivesSay = as
cache.PagesCh <- cache.PageSeries
cache.PagesCh <- cache.PageArchive
responseNotice(c, NoticeSuccess, "更新成功", "")
}
// handleAPIPassword 更新密码
func handleAPIPassword(c *gin.Context) {
od := c.PostForm("old")
nw := c.PostForm("new")
cf := c.PostForm("confirm")
if nw != cf {
responseNotice(c, NoticeNotice, "两次密码输入不一致", "")
return
}
if !tools.ValidatePassword(nw) {
responseNotice(c, NoticeNotice, "密码格式错误", "")
return
}
if cache.Ei.Account.Password != tools.EncryptPasswd(cache.Ei.Account.Username, od) {
responseNotice(c, NoticeNotice, "原始密码不正确", "")
return
}
newPwd := tools.EncryptPasswd(cache.Ei.Account.Username, nw)
err := cache.Ei.UpdateAccount(context.Background(), cache.Ei.Account.Username,
map[string]interface{}{
"password": newPwd,
})
if err != nil {
logrus.Error("handleAPIPassword.UpdateAccount: ", err)
responseNotice(c, NoticeNotice, err.Error(), "")
return
}
cache.Ei.Account.Password = newPwd
responseNotice(c, NoticeSuccess, "更新成功", "")
}
// handleAPIPostDelete 删除文章,移入回收箱
func handleAPIPostDelete(c *gin.Context) {
// var ids []int32
// for _, v := range c.PostFormArray("cid[]") {
// i, err := strconv.Atoi(v)
// if err != nil || int32(i) < config.Conf.BlogApp.General.StartID {
// responseNotice(c, NoticeNotice, "参数错误", "")
// return
// }
// ids = append(ids, int32(i))
// }
// err := DelArticles(ids...)
// if err != nil {
// logd.Error(err)
// responseNotice(c, NOTICE_NOTICE, err.Error(), "")
// return
// }
//
// // elasticsearch
// err = ElasticDelIndex(ids)
// if err != nil {
// logrus.Error("handleAPIPostDelete.")
// }
// // TODO disqus delete
// responseNotice(c, NoticeSuccess, "删除成功", "")
}
func responseNotice(c *gin.Context, typ, content, hl string) {
if hl != "" {
c.SetCookie("notice_highlight", hl, 86400, "/", "", true, false)
}
c.SetCookie("notice_type", typ, 86400, "/", "", true, false)
c.SetCookie("notice", fmt.Sprintf("[\"%s\"]", content), 86400, "/", "", true, false)
c.Redirect(http.StatusFound, c.Request.Referer())
}

View File

@@ -3,12 +3,18 @@ package page
import (
"bytes"
"context"
"encoding/json"
"fmt"
htemplate "html/template"
"net/http"
"strconv"
"github.com/eiblog/eiblog/pkg/cache"
"github.com/eiblog/eiblog/pkg/config"
"github.com/eiblog/eiblog/pkg/core/blog"
"github.com/eiblog/eiblog/pkg/model"
"github.com/sirupsen/logrus"
"github.com/gin-gonic/gin"
)
@@ -34,6 +40,181 @@ func handleLoginPage(c *gin.Context) {
renderHTMLAdminLayout(c, "login.html", params)
}
// handleAdminProfile 个人配置
func handleAdminProfile(c *gin.Context) {
params := baseBEParams(c)
params["Title"] = "个人配置 | " + cache.Ei.Blogger.BTitle
params["Path"] = c.Request.URL.Path
params["Console"] = true
params["Ei"] = cache.Ei
renderHTMLAdminLayout(c, "admin-profile", params)
}
type T struct {
ID string `json:"id"`
Tags string `json:"tags"`
}
// handleAdminPost 写文章页
func handleAdminPost(c *gin.Context) {
params := baseBEParams(c)
id, err := strconv.Atoi(c.Query("cid"))
if err == nil && id > 0 {
article, _ := cache.Ei.LoadArticle(context.Background(), id)
if article != nil {
params["Title"] = "编辑文章 | " + cache.Ei.Blogger.BTitle
params["Edit"] = article
}
}
if params["Title"] == nil {
params["Title"] = "撰写文章 | " + cache.Ei.Blogger.BTitle
}
params["Path"] = c.Request.URL.Path
params["Domain"] = config.Conf.BlogApp.Host
params["Series"] = cache.Ei.Series
var tags []T
for tag, _ := range cache.Ei.TagArticles {
tags = append(tags, T{tag, tag})
}
str, _ := json.Marshal(tags)
params["Tags"] = string(str)
renderHTMLAdminLayout(c, "admin-post", params)
}
// handleAdminPosts 文章管理页
func handleAdminPosts(c *gin.Context) {
kw := c.Query("keywords")
tmp := c.Query("serie")
se, err := strconv.Atoi(tmp)
if err != nil || se < 1 {
se = 0
}
pg, err := strconv.Atoi(c.Query("page"))
if err != nil || pg < 1 {
pg = 1
}
vals := c.Request.URL.Query()
params := baseBEParams(c)
params["Title"] = "文章管理 | " + cache.Ei.Blogger.BTitle
params["Manage"] = true
params["Path"] = c.Request.URL.Path
params["Series"] = cache.Ei.Series
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)
if pg < max {
vals.Set("page", fmt.Sprint(pg+1))
params["Next"] = vals.Encode()
}
if pg > 1 {
vals.Set("page", fmt.Sprint(pg-1))
params["Prev"] = vals.Encode()
}
params["PP"] = make(map[int]string, max)
for i := 0; i < max; i++ {
vals.Set("page", fmt.Sprint(i+1))
params["PP"].(map[int]string)[i+1] = vals.Encode()
}
params["Cur"] = pg
renderHTMLAdminLayout(c, "admin-posts", params)
}
// handleAdminSeries 专题列表
func handleAdminSeries(c *gin.Context) {
params := baseBEParams(c)
params["Title"] = "专题管理 | " + cache.Ei.Blogger.BTitle
params["Manage"] = true
params["Path"] = c.Request.URL.Path
params["List"] = cache.Ei.Series
renderHTMLAdminLayout(c, "admin-series", params)
}
// handleAdminSerie 编辑专题
func handleAdminSerie(c *gin.Context) {
params := baseBEParams(c)
id, err := strconv.Atoi(c.Query("mid"))
params["Title"] = "新增专题 | " + cache.Ei.Blogger.BTitle
if err == nil && id > 0 {
var serie *model.Serie
for _, v := range cache.Ei.Series {
if v.ID == id {
params["Title"] = "编辑专题 | " + cache.Ei.Blogger.BTitle
params["Edit"] = serie
break
}
}
}
params["Manage"] = true
params["Path"] = c.Request.URL.Path
renderHTMLAdminLayout(c, "admin-serie", params)
}
// handleAdminTags 标签列表
func handleAdminTags(c *gin.Context) {
params := baseBEParams(c)
params["Title"] = "标签管理 | " + cache.Ei.Blogger.BTitle
params["Manage"] = true
params["Path"] = c.Request.URL.Path
params["List"] = cache.Ei.TagArticles
renderHTMLAdminLayout(c, "admin-tags", params)
}
// handleAdminDraft 草稿箱页
func handleAdminDraft(c *gin.Context) {
params := baseBEParams(c)
params["Title"] = "草稿箱 | " + cache.Ei.Blogger.BTitle
params["Manage"] = true
params["Path"] = c.Request.URL.Path
var err error
params["List"], err = cache.Ei.LoadDraftArticles(context.Background())
if err != nil {
logrus.Error("handleDraft.LoadDraftArticles: ", err)
c.Status(http.StatusBadRequest)
} else {
c.Status(http.StatusOK)
}
renderHTMLAdminLayout(c, "admin-draft", params)
}
// handleAdminTrash 回收箱页
func handleAdminTrash(c *gin.Context) {
params := baseBEParams(c)
params["Title"] = "回收箱 | " + cache.Ei.Blogger.BTitle
params["Manage"] = true
params["Path"] = c.Request.URL.Path
var err error
params["List"], err = cache.Ei.LoadTrashArticles(context.Background())
if err != nil {
logrus.Error("handleTrash.LoadTrashArticles: ", err)
c.HTML(http.StatusBadRequest, "backLayout.html", params)
return
}
renderHTMLAdminLayout(c, "admin-trash", params)
}
// handleAdminGeneral 基本设置
func handleAdminGeneral(c *gin.Context) {
params := baseBEParams(c)
params["Title"] = "基本设置 | " + cache.Ei.Blogger.BTitle
params["Setting"] = true
params["Path"] = c.Request.URL.Path
renderHTMLAdminLayout(c, "admin-general", params)
}
// handleAdminDiscussion 阅读设置
func handleAdminDiscussion(c *gin.Context) {
params := baseBEParams(c)
params["Title"] = "阅读设置 | " + cache.Ei.Blogger.BTitle
params["Setting"] = true
params["Path"] = c.Request.URL.Path
renderHTMLAdminLayout(c, "admin-discussion", params)
}
// renderHTMLAdminLayout 渲染admin页面
func renderHTMLAdminLayout(c *gin.Context, name string, data gin.H) {
c.Header("Content-Type", "text/html; charset=utf-8")

View File

@@ -50,5 +50,17 @@ func RegisterRoutes(e *gin.Engine) {
// RegisterRoutesAuthz register admin
func RegisterRoutesAuthz(group gin.IRoutes) {
// console
group.GET("/profile", handleAdminProfile)
// write
group.GET("/write-post", handleAdminPost)
// manage
group.GET("/manage-posts", handleAdminPosts)
group.GET("/manage-series", handleAdminSeries)
group.GET("/add-serie", handleAdminSerie)
group.GET("/manage-tags", handleAdminTags)
group.GET("/manage-draft", handleAdminDraft)
group.GET("/manage-trash", handleAdminTrash)
group.GET("/options-general", handleAdminGeneral)
group.GET("/options-discussion", handleAdminDiscussion)
}

23
tools/validate.go Normal file
View File

@@ -0,0 +1,23 @@
// Package tools provides ...
package tools
import "regexp"
var regexpEmail = regexp.MustCompile(`^(\w)+([\.\-]\w+)*@(\w)+((\.\w+)+)$`)
// ValidateEmail 校验邮箱
func ValidateEmail(e string) bool {
return regexpEmail.MatchString(e)
}
var regexpPhoneNo = regexp.MustCompile(`^\+\d+$`)
// ValidatePhoneNo 校验手机号
func ValidatePhoneNo(no string) bool {
return regexpPhoneNo.MatchString(no)
}
// ValidatePassword 校验米阿莫
func ValidatePassword(pwd string) bool {
return len(pwd) > 5 && len(pwd) < 32
}

View File

@@ -3,15 +3,15 @@
<div class="typecho-page-title">
<h2>个人设置</h2>
</div>
{{with .Account}}
{{with .Ei}}
<div class="row typecho-page-main">
<div class="col-mb-12 col-tb-3">
<p>
<img class="profile-avatar" src="//{{$.Qiniu.Domain}}/static/img/avatar.png" alt="{{.BlogName}}" />
<img class="profile-avatar" src="//{{$.Qiniu.Domain}}/static/img/avatar.png" alt="{{.Blogger.BlogName}}" />
</p>
<h2>{{.BlogName}}</h2>
<p>{{.SubTitle}}</p>
<p>最后登录: {{dateformat .LoginAt "2006/01/02 15:04"}}</p>
<h2>{{.Blogger.BlogName}}</h2>
<p>{{.Blogger.SubTitle}}</p>
<p>最后登录: {{dateformat .Account.LoginAt "2006/01/02 15:04"}}</p>
</div>
<div class="col-mb-12 col-tb-6 col-tb-offset-1 typecho-content-panel" role="form">
<section>
@@ -21,7 +21,7 @@
<li>
<label class="typecho-label" for="info-0-1">
个人邮箱</label>
<input id="info-0-1" name="email" type="text" class="text" value="{{.Email}}" />
<input id="info-0-1" name="email" type="text" class="text" value="{{.Account.Email}}" />
<p class="description">
用于发送告警邮件及其它通知, 建议填写, 如: example@163.com.</p>
</li>
@@ -30,7 +30,7 @@
<li>
<label class="typecho-label" for="info-0-2">
移动电话</label>
<input id="info-0-2" name="phoneNumber" type="text" class="text" value="{{.PhoneN}}" />
<input id="info-0-2" name="phoneNumber" type="text" class="text" value="{{.Account.PhoneN}}" />
<p class="description">
选择填写, 如: 8615123456789.</p>
</li>
@@ -39,7 +39,7 @@
<li>
<label class="typecho-label" for="info-0-3">
家庭住址</label>
<input id="info-0-3" name="address" type="text" class="text" value="{{.Address}}" />
<input id="info-0-3" name="address" type="text" class="text" value="{{.Account.Address}}" />
<p class="description">
选择填写, 如: xx省xx市xx区(县)xxxx小区xxx号.</p>
</li>
@@ -60,7 +60,7 @@
<li>
<label class="typecho-label" for="blog-0-1">
博客昵称 *</label>
<input id="blog-0-1" name="blogName" type="text" class="text" value="{{.BlogName}}" />
<input id="blog-0-1" name="blogName" type="text" class="text" value="{{.Blogger.BlogName}}" />
<p class="description">
用户昵称可以与用户名不同, 用于前台显示.
<br />如果你将此项留空, 将默认使用登录用户名.</p>
@@ -70,7 +70,7 @@
<li>
<label class="typecho-label" for="blog-0-2">
标题显示 *</label>
<input id="blog-0-2" name="bTitle" type="text" class="text" value="{{.BTitle}}" />
<input id="blog-0-2" name="bTitle" type="text" class="text" value="{{.Blogger.BTitle}}" />
<p class="description">
用于所有页面的title组成, 如: Deepzz's Blog</p>
</li>
@@ -79,7 +79,7 @@
<li>
<label class="typecho-label" for="blog-0-3">
个人格言</label>
<input id="blog-0-3" name="subTitle" type="text" class="text" value="{{.SubTitle}}" />
<input id="blog-0-3" name="subTitle" type="text" class="text" value="{{.Blogger.SubTitle}}" />
<p class="description">
简介或格言, 如: 生活百般滋味, 人生需要笑对.</p>
</li>
@@ -88,7 +88,7 @@
<li>
<label class="typecho-label" for="blog-0-4">
备案号</label>
<input id="blog-0-4" name="beiAn" type="text" class="text" value="{{.BeiAn}}" />
<input id="blog-0-4" name="beiAn" type="text" class="text" value="{{.Blogger.BeiAn}}" />
<p class="description">
用于底部显示, 不添加则不显示, 如: 蜀 ICP 备 16021362 号</p>
</li>
@@ -97,7 +97,7 @@
<li>
<label class="typecho-label" for="blog-0-5">
专题前说</label>
<textarea id="blog-0-5" name="seriessay">{{.SeriesSay}}</textarea>
<textarea id="blog-0-5" name="seriessay">{{.Blogger.SeriesSay}}</textarea>
<p class="description">
此文字用于专题前述, 会在专题最前方显示.</p>
</li>
@@ -106,7 +106,7 @@
<li>
<label class="typecho-label" for="blog-0-6">
归档前说</label>
<textarea id="blog-0-6" name="archivessay">{{.ArchivesSay}}</textarea>
<textarea id="blog-0-6" name="archivessay">{{.Blogger.ArchivesSay}}</textarea>
<p class="description">
此文字用于归档前述, 会在归档最前方显示.</p>
</li>