mirror of
https://github.com/eiblog/eiblog.git
synced 2026-03-01 00:34:58 +08:00
chore: optmize code
This commit is contained in:
+2
-2
@@ -17,7 +17,7 @@
|
|||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
bin
|
bin
|
||||||
assets/*.xml
|
cmd/eiblog/etc/assets/*.xml
|
||||||
assets/*.txt
|
cmd/eiblog/etc/assets/*.txt
|
||||||
db.sqlite
|
db.sqlite
|
||||||
cmd/*/backend
|
cmd/*/backend
|
||||||
@@ -210,12 +210,14 @@ func handleAPIPostDelete(c *gin.Context) {
|
|||||||
ids = append(ids, id)
|
ids = append(ids, id)
|
||||||
}
|
}
|
||||||
// elasticsearch
|
// elasticsearch
|
||||||
err := internal.ESClient.ElasticDelIndex(ids)
|
if internal.ESClient != nil {
|
||||||
if err != nil {
|
err := internal.ESClient.ElasticDelIndex(ids)
|
||||||
logrus.Error("handleAPIPostDelete.ElasticDelIndex: ", err)
|
if err != nil {
|
||||||
|
logrus.Error("handleAPIPostDelete.ElasticDelIndex: ", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO disqus delete
|
// TODO disqus delete
|
||||||
responseNotice(c, NoticeSuccess, "删除成功", "")
|
responseNotice(c, NoticeSuccess, "删除成功,已移入到回收箱", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleAPIPostCreate 创建文章
|
// handleAPIPostCreate 创建文章
|
||||||
@@ -291,7 +293,9 @@ func handleAPIPostCreate(c *gin.Context) {
|
|||||||
// 异步执行,快
|
// 异步执行,快
|
||||||
go func() {
|
go func() {
|
||||||
// elastic
|
// elastic
|
||||||
internal.ESClient.ElasticAddIndex(article)
|
if internal.ESClient != nil {
|
||||||
|
internal.ESClient.ElasticAddIndex(article)
|
||||||
|
}
|
||||||
// rss
|
// rss
|
||||||
internal.Pinger.PingFunc(internal.Ei.Blogger.BTitle, slug)
|
internal.Pinger.PingFunc(internal.Ei.Blogger.BTitle, slug)
|
||||||
}()
|
}()
|
||||||
@@ -332,7 +336,9 @@ func handleAPIPostCreate(c *gin.Context) {
|
|||||||
// 异步执行,快
|
// 异步执行,快
|
||||||
go func() {
|
go func() {
|
||||||
// elastic
|
// elastic
|
||||||
internal.ESClient.ElasticAddIndex(article)
|
if internal.ESClient != nil {
|
||||||
|
internal.ESClient.ElasticAddIndex(article)
|
||||||
|
}
|
||||||
// rss
|
// rss
|
||||||
internal.Pinger.PingFunc(internal.Ei.Blogger.BTitle, slug)
|
internal.Pinger.PingFunc(internal.Ei.Blogger.BTitle, slug)
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -2,47 +2,68 @@ package file
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/eiblog/eiblog/cmd/eiblog/config"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterRoutes register routes
|
// RegisterRoutes register routes
|
||||||
func RegisterRoutes(e *gin.Engine) {
|
func RegisterRoutes(e *gin.Engine) {
|
||||||
e.GET("/rss.html", handleFeed)
|
e.GET("/rss.html", handleFeed())
|
||||||
e.GET("/feed", handleFeed)
|
e.GET("/feed", handleFeed())
|
||||||
e.GET("/opensearch.xml", handleOpensearch)
|
e.GET("/opensearch.xml", handleOpensearch())
|
||||||
e.GET("/sitemap.xml", handleSitemap)
|
e.GET("/sitemap.xml", handleSitemap())
|
||||||
e.GET("/robots.txt", handleRobots)
|
e.GET("/robots.txt", handleRobots())
|
||||||
e.GET("/crossdomain.xml", handleCrossDomain)
|
e.GET("/crossdomain.xml", handleCrossDomain())
|
||||||
e.GET("/favicon.ico", handleFavicon)
|
e.GET("/favicon.ico", handleFavicon())
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleFeed feed.xml
|
// handleFeed feed.xml
|
||||||
func handleFeed(c *gin.Context) {
|
func handleFeed() gin.HandlerFunc {
|
||||||
http.ServeFile(c.Writer, c.Request, "assets/feed.xml")
|
path := filepath.Join(config.EtcDir, "assets", "feed.xml")
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
http.ServeFile(c.Writer, c.Request, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleOpensearch opensearch.xml
|
// handleOpensearch opensearch.xml
|
||||||
func handleOpensearch(c *gin.Context) {
|
func handleOpensearch() gin.HandlerFunc {
|
||||||
http.ServeFile(c.Writer, c.Request, "assets/opensearch.xml")
|
path := filepath.Join(config.EtcDir, "assets", "opensearch.xml")
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
http.ServeFile(c.Writer, c.Request, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleRobots robotx.txt
|
// handleRobots robotx.txt
|
||||||
func handleRobots(c *gin.Context) {
|
func handleRobots() gin.HandlerFunc {
|
||||||
http.ServeFile(c.Writer, c.Request, "assets/robots.txt")
|
path := filepath.Join(config.EtcDir, "assets", "robots.txt")
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
http.ServeFile(c.Writer, c.Request, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleSitemap sitemap.xml
|
// handleSitemap sitemap.xml
|
||||||
func handleSitemap(c *gin.Context) {
|
func handleSitemap() gin.HandlerFunc {
|
||||||
http.ServeFile(c.Writer, c.Request, "assets/sitemap.xml")
|
path := filepath.Join(config.EtcDir, "assets", "sitemap.xml")
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
http.ServeFile(c.Writer, c.Request, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleCrossDomain crossdomain.xml
|
// handleCrossDomain crossdomain.xml
|
||||||
func handleCrossDomain(c *gin.Context) {
|
func handleCrossDomain() gin.HandlerFunc {
|
||||||
http.ServeFile(c.Writer, c.Request, "assets/crossdomain.xml")
|
path := filepath.Join(config.EtcDir, "assets", "crossdomain.xml")
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
http.ServeFile(c.Writer, c.Request, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleFavicon favicon.ico
|
// handleFavicon favicon.ico
|
||||||
func handleFavicon(c *gin.Context) {
|
func handleFavicon() gin.HandlerFunc {
|
||||||
http.ServeFile(c.Writer, c.Request, "assets/favicon.ico")
|
path := filepath.Join(config.EtcDir, "assets", "favicon.ico")
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
http.ServeFile(c.Writer, c.Request, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
package file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"text/template"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/cmd/eiblog/config"
|
|
||||||
"github.com/eiblog/eiblog/cmd/eiblog/handler/internal"
|
|
||||||
"github.com/eiblog/eiblog/tools"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var xmlTmpl *template.Template
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
root := filepath.Join(config.EtcDir, "template", "*.xml")
|
|
||||||
|
|
||||||
var err error
|
|
||||||
xmlTmpl, err = template.New("").Funcs(template.FuncMap{
|
|
||||||
"dateformat": tools.DateFormat,
|
|
||||||
"imgtonormal": tools.ImgToNormal,
|
|
||||||
}).ParseGlob(root)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
generateOpensearch()
|
|
||||||
generateRobots()
|
|
||||||
generateCrossdomain()
|
|
||||||
go timerFeed()
|
|
||||||
go timerSitemap()
|
|
||||||
}
|
|
||||||
|
|
||||||
// timerFeed 定时刷新feed
|
|
||||||
func timerFeed() {
|
|
||||||
tpl := xmlTmpl.Lookup("feedTpl.xml")
|
|
||||||
if tpl == nil {
|
|
||||||
logrus.Info("file: not found: feedTpl.xml")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
_, _, articles := internal.Ei.PageArticleFE(1, 20)
|
|
||||||
params := map[string]interface{}{
|
|
||||||
"Title": internal.Ei.Blogger.BTitle,
|
|
||||||
"SubTitle": internal.Ei.Blogger.SubTitle,
|
|
||||||
"Host": config.Conf.Host,
|
|
||||||
"FeedrURL": config.Conf.FeedRPC.FeedrURL,
|
|
||||||
"BuildDate": now.Format(time.RFC1123Z),
|
|
||||||
"Articles": articles,
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile("assets/feed.xml", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: timerFeed.OpenFile: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
err = tpl.Execute(f, params)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: timerFeed.Execute: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
time.AfterFunc(time.Hour*4, timerFeed)
|
|
||||||
}
|
|
||||||
|
|
||||||
// timerSitemap 定时刷新sitemap
|
|
||||||
func timerSitemap() {
|
|
||||||
tpl := xmlTmpl.Lookup("sitemapTpl.xml")
|
|
||||||
if tpl == nil {
|
|
||||||
logrus.Info("file: not found: sitemapTpl.xml")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
params := map[string]interface{}{
|
|
||||||
"Articles": internal.Ei.Articles,
|
|
||||||
"Host": config.Conf.Host,
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile("assets/sitemap.xml", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: timerSitemap.OpenFile: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
err = tpl.Execute(f, params)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: timerSitemap.Execute: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
time.AfterFunc(time.Hour*24, timerSitemap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateOpensearch 生成opensearch.xml
|
|
||||||
func generateOpensearch() {
|
|
||||||
tpl := xmlTmpl.Lookup("opensearchTpl.xml")
|
|
||||||
if tpl == nil {
|
|
||||||
logrus.Info("file: not found: opensearchTpl.xml")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
params := map[string]string{
|
|
||||||
"BTitle": internal.Ei.Blogger.BTitle,
|
|
||||||
"SubTitle": internal.Ei.Blogger.SubTitle,
|
|
||||||
"Host": config.Conf.Host,
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile("assets/opensearch.xml", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: generateOpensearch.OpenFile: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
err = tpl.Execute(f, params)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: generateOpensearch.Execute: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateRobots 生成robots.txt
|
|
||||||
func generateRobots() {
|
|
||||||
tpl := xmlTmpl.Lookup("robotsTpl.xml")
|
|
||||||
if tpl == nil {
|
|
||||||
logrus.Info("file: not found: robotsTpl.xml")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
params := map[string]string{
|
|
||||||
"Host": config.Conf.Host,
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile("assets/robots.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: generateRobots.OpenFile: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
err = tpl.Execute(f, params)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: generateRobots.Execute: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateCrossdomain 生成crossdomain.xml
|
|
||||||
func generateCrossdomain() {
|
|
||||||
tpl := xmlTmpl.Lookup("crossdomainTpl.xml")
|
|
||||||
if tpl == nil {
|
|
||||||
logrus.Info("file: not found: crossdomainTpl.xml")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
params := map[string]string{
|
|
||||||
"Host": config.Conf.Host,
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile("assets/crossdomain.xml", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: generateCrossdomain.OpenFile: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
err = tpl.Execute(f, params)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("file: generateCrossdomain.Execute: ", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,14 +15,9 @@ import (
|
|||||||
"github.com/eiblog/eiblog/cmd/eiblog/handler/internal/store"
|
"github.com/eiblog/eiblog/cmd/eiblog/handler/internal/store"
|
||||||
"github.com/eiblog/eiblog/pkg/model"
|
"github.com/eiblog/eiblog/pkg/model"
|
||||||
"github.com/eiblog/eiblog/tools"
|
"github.com/eiblog/eiblog/tools"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Ei eiblog cache
|
|
||||||
Ei *Cache
|
|
||||||
|
|
||||||
// PagesCh regenerate pages chan
|
// PagesCh regenerate pages chan
|
||||||
PagesCh = make(chan string, 2)
|
PagesCh = make(chan string, 2)
|
||||||
// PageSeries the page series regenerate flag
|
// PageSeries the page series regenerate flag
|
||||||
@@ -32,32 +27,8 @@ var (
|
|||||||
|
|
||||||
// ArticleStartID article start id
|
// ArticleStartID article start id
|
||||||
ArticleStartID = 11
|
ArticleStartID = 11
|
||||||
// TrashArticleExp trash article timeout
|
|
||||||
TrashArticleExp = time.Duration(-48) * time.Hour
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
// init timezone
|
|
||||||
var err error
|
|
||||||
tools.TimeLocation, err = time.LoadLocation(config.Conf.General.Timezone)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// Ei init
|
|
||||||
Ei = &Cache{
|
|
||||||
lock: sync.Mutex{},
|
|
||||||
TagArticles: make(map[string]model.SortedArticles),
|
|
||||||
ArticlesMap: make(map[string]*model.Article),
|
|
||||||
}
|
|
||||||
err = Ei.loadOrInit()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
go Ei.regeneratePages()
|
|
||||||
go Ei.timerClean()
|
|
||||||
go Ei.timerDisqus()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache 整站缓存
|
// Cache 整站缓存
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
@@ -76,10 +47,27 @@ type Cache struct {
|
|||||||
ArticlesMap map[string]*model.Article // slug:article
|
ArticlesMap map[string]*model.Article // slug:article
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewCache 缓存整个博客数据
|
||||||
|
func NewCache() (*Cache, error) {
|
||||||
|
// Ei init
|
||||||
|
cache := &Cache{
|
||||||
|
lock: sync.Mutex{},
|
||||||
|
TagArticles: make(map[string]model.SortedArticles),
|
||||||
|
ArticlesMap: make(map[string]*model.Article),
|
||||||
|
}
|
||||||
|
err := cache.loadOrInit()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// 异步渲染series,archive页面
|
||||||
|
go cache.regeneratePages()
|
||||||
|
return cache, nil
|
||||||
|
}
|
||||||
|
|
||||||
// AddArticle 添加文章
|
// AddArticle 添加文章
|
||||||
func (c *Cache) AddArticle(article *model.Article) error {
|
func (cache *Cache) AddArticle(article *model.Article) error {
|
||||||
c.lock.Lock()
|
cache.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer cache.lock.Unlock()
|
||||||
|
|
||||||
// store
|
// store
|
||||||
err := Store.InsertArticle(context.Background(), article, ArticleStartID)
|
err := Store.InsertArticle(context.Background(), article, ArticleStartID)
|
||||||
@@ -91,32 +79,32 @@ func (c *Cache) AddArticle(article *model.Article) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// 正式发布文章
|
// 正式发布文章
|
||||||
c.refreshCache(article, false)
|
cache.refreshCache(article, false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepArticle 替换文章
|
// RepArticle 替换文章
|
||||||
func (c *Cache) RepArticle(oldArticle, newArticle *model.Article) {
|
func (cache *Cache) RepArticle(oldArticle, newArticle *model.Article) {
|
||||||
c.lock.Lock()
|
cache.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer cache.lock.Unlock()
|
||||||
|
|
||||||
c.ArticlesMap[newArticle.Slug] = newArticle
|
cache.ArticlesMap[newArticle.Slug] = newArticle
|
||||||
GenerateExcerptMarkdown(newArticle)
|
GenerateExcerptMarkdown(newArticle)
|
||||||
if newArticle.ID < ArticleStartID {
|
if newArticle.ID < ArticleStartID {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if oldArticle != nil { // 移除旧文章
|
if oldArticle != nil { // 移除旧文章
|
||||||
c.refreshCache(oldArticle, true)
|
cache.refreshCache(oldArticle, true)
|
||||||
}
|
}
|
||||||
c.refreshCache(newArticle, false)
|
cache.refreshCache(newArticle, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelArticle 删除文章
|
// DelArticle 删除文章
|
||||||
func (c *Cache) DelArticle(id int) error {
|
func (cache *Cache) DelArticle(id int) error {
|
||||||
c.lock.Lock()
|
cache.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer cache.lock.Unlock()
|
||||||
|
|
||||||
article, _ := c.FindArticleByID(id)
|
article, _ := cache.FindArticleByID(id)
|
||||||
if article == nil {
|
if article == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -128,30 +116,30 @@ func (c *Cache) DelArticle(id int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// drop from tags,series,archives
|
// drop from tags,series,archives
|
||||||
c.refreshCache(article, true)
|
cache.refreshCache(article, true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSerie 添加专题
|
// AddSerie 添加专题
|
||||||
func (c *Cache) AddSerie(serie *model.Serie) error {
|
func (cache *Cache) AddSerie(serie *model.Serie) error {
|
||||||
c.lock.Lock()
|
cache.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer cache.lock.Unlock()
|
||||||
|
|
||||||
err := Store.InsertSerie(context.Background(), serie)
|
err := Store.InsertSerie(context.Background(), serie)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.Series = append(c.Series, serie)
|
cache.Series = append(cache.Series, serie)
|
||||||
PagesCh <- PageSeries
|
PagesCh <- PageSeries
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelSerie 删除专题
|
// DelSerie 删除专题
|
||||||
func (c *Cache) DelSerie(id int) error {
|
func (cache *Cache) DelSerie(id int) error {
|
||||||
c.lock.Lock()
|
cache.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer cache.lock.Unlock()
|
||||||
|
|
||||||
for i, serie := range c.Series {
|
for i, serie := range cache.Series {
|
||||||
if serie.ID == id {
|
if serie.ID == id {
|
||||||
if len(serie.Articles) > 0 {
|
if len(serie.Articles) > 0 {
|
||||||
return errors.New("请删除该专题下的所有文章")
|
return errors.New("请删除该专题下的所有文章")
|
||||||
@@ -160,8 +148,8 @@ func (c *Cache) DelSerie(id int) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.Series[i] = nil
|
cache.Series[i] = nil
|
||||||
c.Series = append(c.Series[:i], c.Series[i+1:]...)
|
cache.Series = append(cache.Series[:i], cache.Series[i+1:]...)
|
||||||
PagesCh <- PageSeries
|
PagesCh <- PageSeries
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -170,12 +158,12 @@ func (c *Cache) DelSerie(id int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PageArticleFE 文章翻页
|
// PageArticleFE 文章翻页
|
||||||
func (c *Cache) PageArticleFE(page int, pageSize int) (prev,
|
func (cache *Cache) PageArticleFE(page int, pageSize int) (prev,
|
||||||
next int, articles []*model.Article) {
|
next int, articles []*model.Article) {
|
||||||
|
|
||||||
var l int
|
var l int
|
||||||
for l = len(c.Articles); l > 0; l-- {
|
for l = len(cache.Articles); l > 0; l-- {
|
||||||
if c.Articles[l-1].ID >= ArticleStartID {
|
if cache.Articles[l-1].ID >= ArticleStartID {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,12 +188,12 @@ func (c *Cache) PageArticleFE(page int, pageSize int) (prev,
|
|||||||
if e > l {
|
if e > l {
|
||||||
e = l
|
e = l
|
||||||
}
|
}
|
||||||
articles = c.Articles[s:e]
|
articles = cache.Articles[s:e]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// PageArticleBE 后台文章分页
|
// PageArticleBE 后台文章分页
|
||||||
func (c *Cache) PageArticleBE(se int, kw string, draft, del bool, p,
|
func (cache *Cache) PageArticleBE(se int, kw string, draft, del bool, p,
|
||||||
n int) ([]*model.Article, int) {
|
n int) ([]*model.Article, int) {
|
||||||
|
|
||||||
search := store.SearchArticles{
|
search := store.SearchArticles{
|
||||||
@@ -238,8 +226,8 @@ func (c *Cache) PageArticleBE(se int, kw string, draft, del bool, p,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindArticleByID 通过ID查找文章
|
// FindArticleByID 通过ID查找文章
|
||||||
func (c *Cache) FindArticleByID(id int) (*model.Article, int) {
|
func (cache *Cache) FindArticleByID(id int) (*model.Article, int) {
|
||||||
for i, article := range c.Articles {
|
for i, article := range cache.Articles {
|
||||||
if article.ID == id {
|
if article.ID == id {
|
||||||
return article, i
|
return article, i
|
||||||
}
|
}
|
||||||
@@ -248,32 +236,32 @@ func (c *Cache) FindArticleByID(id int) (*model.Article, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// refreshCache 刷新缓存
|
// refreshCache 刷新缓存
|
||||||
func (c *Cache) refreshCache(article *model.Article, del bool) {
|
func (cache *Cache) refreshCache(article *model.Article, del bool) {
|
||||||
if del {
|
if del {
|
||||||
_, idx := c.FindArticleByID(article.ID)
|
_, idx := cache.FindArticleByID(article.ID)
|
||||||
|
|
||||||
delete(c.ArticlesMap, article.Slug)
|
delete(cache.ArticlesMap, article.Slug)
|
||||||
c.Articles = append(c.Articles[:idx], c.Articles[idx+1:]...)
|
cache.Articles = append(cache.Articles[:idx], cache.Articles[idx+1:]...)
|
||||||
// 从链表移除
|
// 从链表移除
|
||||||
c.recalcLinkedList(article, true)
|
cache.recalcLinkedList(article, true)
|
||||||
// 从tag、serie、archive移除
|
// 从tag、serie、archive移除
|
||||||
c.redelArticle(article)
|
cache.redelArticle(article)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 添加文章
|
// 添加文章
|
||||||
defer GenerateExcerptMarkdown(article)
|
defer GenerateExcerptMarkdown(article)
|
||||||
|
|
||||||
c.ArticlesMap[article.Slug] = article
|
cache.ArticlesMap[article.Slug] = article
|
||||||
c.Articles = append([]*model.Article{article}, c.Articles...)
|
cache.Articles = append([]*model.Article{article}, cache.Articles...)
|
||||||
sort.Sort(c.Articles)
|
sort.Sort(cache.Articles)
|
||||||
// 从链表添加
|
// 从链表添加
|
||||||
c.recalcLinkedList(article, false)
|
cache.recalcLinkedList(article, false)
|
||||||
// 从tag、serie、archive添加
|
// 从tag、serie、archive添加
|
||||||
c.readdArticle(article, true)
|
cache.readdArticle(article, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// recalcLinkedList 重算文章链表
|
// recalcLinkedList 重算文章链表
|
||||||
func (c *Cache) recalcLinkedList(article *model.Article, del bool) {
|
func (cache *Cache) recalcLinkedList(article *model.Article, del bool) {
|
||||||
// 删除操作
|
// 删除操作
|
||||||
if del {
|
if del {
|
||||||
if article.Prev == nil && article.Next != nil {
|
if article.Prev == nil && article.Next != nil {
|
||||||
@@ -287,56 +275,56 @@ func (c *Cache) recalcLinkedList(article *model.Article, del bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 添加操作
|
// 添加操作
|
||||||
_, idx := c.FindArticleByID(article.ID)
|
_, idx := cache.FindArticleByID(article.ID)
|
||||||
if idx == 0 && c.Articles[idx+1].ID >= ArticleStartID {
|
if idx == 0 && cache.Articles[idx+1].ID >= ArticleStartID {
|
||||||
article.Next = c.Articles[idx+1]
|
article.Next = cache.Articles[idx+1]
|
||||||
c.Articles[idx+1].Prev = article
|
cache.Articles[idx+1].Prev = article
|
||||||
} else if idx > 0 && c.Articles[idx-1].ID >= ArticleStartID {
|
} else if idx > 0 && cache.Articles[idx-1].ID >= ArticleStartID {
|
||||||
article.Prev = c.Articles[idx-1]
|
article.Prev = cache.Articles[idx-1]
|
||||||
if c.Articles[idx-1].Next != nil {
|
if cache.Articles[idx-1].Next != nil {
|
||||||
article.Next = c.Articles[idx-1].Next
|
article.Next = cache.Articles[idx-1].Next
|
||||||
c.Articles[idx-1].Next.Prev = article
|
cache.Articles[idx-1].Next.Prev = article
|
||||||
}
|
}
|
||||||
c.Articles[idx-1].Next = article
|
cache.Articles[idx-1].Next = article
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// readdArticle 添加文章到tag、series、archive
|
// readdArticle 添加文章到tag、series、archive
|
||||||
func (c *Cache) readdArticle(article *model.Article, needSort bool) {
|
func (cache *Cache) readdArticle(article *model.Article, needSort bool) {
|
||||||
// tag
|
// tag
|
||||||
for _, tag := range article.Tags {
|
for _, tag := range article.Tags {
|
||||||
c.TagArticles[tag] = append(c.TagArticles[tag], article)
|
cache.TagArticles[tag] = append(cache.TagArticles[tag], article)
|
||||||
if needSort {
|
if needSort {
|
||||||
sort.Sort(c.TagArticles[tag])
|
sort.Sort(cache.TagArticles[tag])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// series
|
// series
|
||||||
for i, serie := range c.Series {
|
for i, serie := range cache.Series {
|
||||||
if serie.ID != article.SerieID {
|
if serie.ID != article.SerieID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Series[i].Articles = append(c.Series[i].Articles, article)
|
cache.Series[i].Articles = append(cache.Series[i].Articles, article)
|
||||||
if needSort {
|
if needSort {
|
||||||
sort.Sort(c.Series[i].Articles)
|
sort.Sort(cache.Series[i].Articles)
|
||||||
PagesCh <- PageSeries // 重建专题
|
PagesCh <- PageSeries // 重建专题
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// archive
|
// archive
|
||||||
y, m, _ := article.CreatedAt.Date()
|
y, m, _ := article.CreatedAt.Date()
|
||||||
for i, archive := range c.Archives {
|
for i, archive := range cache.Archives {
|
||||||
ay, am, _ := archive.Time.Date()
|
ay, am, _ := archive.Time.Date()
|
||||||
if y != ay || m != am {
|
if y != ay || m != am {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Archives[i].Articles = append(c.Archives[i].Articles, article)
|
cache.Archives[i].Articles = append(cache.Archives[i].Articles, article)
|
||||||
if needSort {
|
if needSort {
|
||||||
sort.Sort(c.Archives[i].Articles)
|
sort.Sort(cache.Archives[i].Articles)
|
||||||
PagesCh <- PageArchive // 重建归档
|
PagesCh <- PageArchive // 重建归档
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 新建归档
|
// 新建归档
|
||||||
c.Archives = append(c.Archives, &model.Archive{
|
cache.Archives = append(cache.Archives, &model.Archive{
|
||||||
Time: article.CreatedAt,
|
Time: article.CreatedAt,
|
||||||
Articles: model.SortedArticles{article},
|
Articles: model.SortedArticles{article},
|
||||||
})
|
})
|
||||||
@@ -346,25 +334,25 @@ func (c *Cache) readdArticle(article *model.Article, needSort bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// redelArticle 从tag、series、archive删除文章
|
// redelArticle 从tag、series、archive删除文章
|
||||||
func (c *Cache) redelArticle(article *model.Article) {
|
func (cache *Cache) redelArticle(article *model.Article) {
|
||||||
// tag
|
// tag
|
||||||
for _, tag := range article.Tags {
|
for _, tag := range article.Tags {
|
||||||
for i, v := range c.TagArticles[tag] {
|
for i, v := range cache.TagArticles[tag] {
|
||||||
if v == article {
|
if v == article {
|
||||||
c.TagArticles[tag] = append(c.TagArticles[tag][0:i], c.TagArticles[tag][i+1:]...)
|
cache.TagArticles[tag] = append(cache.TagArticles[tag][0:i], cache.TagArticles[tag][i+1:]...)
|
||||||
if len(c.TagArticles[tag]) == 0 {
|
if len(cache.TagArticles[tag]) == 0 {
|
||||||
delete(c.TagArticles, tag)
|
delete(cache.TagArticles, tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// serie
|
// serie
|
||||||
for i, serie := range c.Series {
|
for i, serie := range cache.Series {
|
||||||
if serie.ID == article.SerieID {
|
if serie.ID == article.SerieID {
|
||||||
for j, v := range serie.Articles {
|
for j, v := range serie.Articles {
|
||||||
if v == article {
|
if v == article {
|
||||||
c.Series[i].Articles = append(c.Series[i].Articles[0:j],
|
cache.Series[i].Articles = append(cache.Series[i].Articles[0:j],
|
||||||
c.Series[i].Articles[j+1:]...)
|
cache.Series[i].Articles[j+1:]...)
|
||||||
PagesCh <- PageSeries
|
PagesCh <- PageSeries
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -372,15 +360,15 @@ func (c *Cache) redelArticle(article *model.Article) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// archive
|
// archive
|
||||||
for i, archive := range c.Archives {
|
for i, archive := range cache.Archives {
|
||||||
ay, am, _ := archive.Time.Date()
|
ay, am, _ := archive.Time.Date()
|
||||||
if y, m, _ := article.CreatedAt.Date(); ay == y && am == m {
|
if y, m, _ := article.CreatedAt.Date(); ay == y && am == m {
|
||||||
for j, v := range archive.Articles {
|
for j, v := range archive.Articles {
|
||||||
if v == article {
|
if v == article {
|
||||||
c.Archives[i].Articles = append(c.Archives[i].Articles[0:j],
|
cache.Archives[i].Articles = append(cache.Archives[i].Articles[0:j],
|
||||||
c.Archives[i].Articles[j+1:]...)
|
cache.Archives[i].Articles[j+1:]...)
|
||||||
if len(c.Archives[i].Articles) == 0 {
|
if len(cache.Archives[i].Articles) == 0 {
|
||||||
c.Archives = append(c.Archives[:i], c.Archives[i+1:]...)
|
cache.Archives = append(cache.Archives[:i], cache.Archives[i+1:]...)
|
||||||
}
|
}
|
||||||
PagesCh <- PageArchive
|
PagesCh <- PageArchive
|
||||||
break
|
break
|
||||||
@@ -391,7 +379,7 @@ func (c *Cache) redelArticle(article *model.Article) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// loadOrInit 读取数据或初始化
|
// loadOrInit 读取数据或初始化
|
||||||
func (c *Cache) loadOrInit() error {
|
func (cache *Cache) loadOrInit() error {
|
||||||
// blogger
|
// blogger
|
||||||
blogger := &model.Blogger{
|
blogger := &model.Blogger{
|
||||||
BlogName: strings.Title(config.Conf.Account.Username),
|
BlogName: strings.Title(config.Conf.Account.Username),
|
||||||
@@ -404,7 +392,7 @@ func (c *Cache) loadOrInit() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.Blogger = blogger
|
cache.Blogger = blogger
|
||||||
if created { // init articles: about blogroll
|
if created { // init articles: about blogroll
|
||||||
about := &model.Article{
|
about := &model.Article{
|
||||||
ID: 1, // 固定ID
|
ID: 1, // 固定ID
|
||||||
@@ -432,8 +420,7 @@ func (c *Cache) loadOrInit() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// account
|
// account
|
||||||
pwd := tools.EncryptPasswd(config.Conf.Account.Username,
|
pwd := tools.EncryptPasswd(config.Conf.Account.Username, config.Conf.Account.Password)
|
||||||
config.Conf.Account.Password)
|
|
||||||
|
|
||||||
account := &model.Account{
|
account := &model.Account{
|
||||||
Username: config.Conf.Account.Username,
|
Username: config.Conf.Account.Username,
|
||||||
@@ -443,13 +430,13 @@ func (c *Cache) loadOrInit() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.Account = account
|
cache.Account = account
|
||||||
// series
|
// series
|
||||||
series, err := Store.LoadAllSerie(context.Background())
|
series, err := Store.LoadAllSerie(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.Series = series
|
cache.Series = series
|
||||||
// all articles
|
// all articles
|
||||||
search := store.SearchArticles{
|
search := store.SearchArticles{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
@@ -464,7 +451,7 @@ func (c *Cache) loadOrInit() error {
|
|||||||
// 渲染页面
|
// 渲染页面
|
||||||
GenerateExcerptMarkdown(v)
|
GenerateExcerptMarkdown(v)
|
||||||
|
|
||||||
c.ArticlesMap[v.Slug] = v
|
cache.ArticlesMap[v.Slug] = v
|
||||||
// 分析文章
|
// 分析文章
|
||||||
if v.ID < ArticleStartID {
|
if v.ID < ArticleStartID {
|
||||||
continue
|
continue
|
||||||
@@ -476,9 +463,9 @@ func (c *Cache) loadOrInit() error {
|
|||||||
articles[i+1].ID >= ArticleStartID {
|
articles[i+1].ID >= ArticleStartID {
|
||||||
v.Next = articles[i+1]
|
v.Next = articles[i+1]
|
||||||
}
|
}
|
||||||
c.readdArticle(v, false)
|
cache.readdArticle(v, false)
|
||||||
}
|
}
|
||||||
Ei.Articles = articles
|
cache.Articles = articles
|
||||||
// 重建专题与归档
|
// 重建专题与归档
|
||||||
PagesCh <- PageSeries
|
PagesCh <- PageSeries
|
||||||
PagesCh <- PageArchive
|
PagesCh <- PageArchive
|
||||||
@@ -486,15 +473,15 @@ func (c *Cache) loadOrInit() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// regeneratePages 重新生成series,archive页面
|
// regeneratePages 重新生成series,archive页面
|
||||||
func (c *Cache) regeneratePages() {
|
func (cache *Cache) regeneratePages() {
|
||||||
for {
|
for {
|
||||||
switch page := <-PagesCh; page {
|
switch page := <-PagesCh; page {
|
||||||
case PageSeries:
|
case PageSeries:
|
||||||
sort.Sort(c.Series)
|
sort.Sort(cache.Series)
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
buf.WriteString(c.Blogger.SeriesSay)
|
buf.WriteString(cache.Blogger.SeriesSay)
|
||||||
buf.WriteString("\n\n")
|
buf.WriteString("\n\n")
|
||||||
for _, series := range c.Series {
|
for _, series := range cache.Series {
|
||||||
buf.WriteString(fmt.Sprintf("### %s{#toc-%d}", series.Name, series.ID))
|
buf.WriteString(fmt.Sprintf("### %s{#toc-%d}", series.Name, series.ID))
|
||||||
buf.WriteByte('\n')
|
buf.WriteByte('\n')
|
||||||
buf.WriteString(series.Desc)
|
buf.WriteString(series.Desc)
|
||||||
@@ -507,16 +494,16 @@ func (c *Cache) regeneratePages() {
|
|||||||
}
|
}
|
||||||
buf.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
}
|
}
|
||||||
c.PageSeries = string(PageRender(buf.Bytes()))
|
cache.PageSeries = string(PageRender(buf.Bytes()))
|
||||||
case PageArchive:
|
case PageArchive:
|
||||||
sort.Sort(c.Archives)
|
sort.Sort(cache.Archives)
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
buf.WriteString(c.Blogger.ArchivesSay + "\n")
|
buf.WriteString(cache.Blogger.ArchivesSay + "\n")
|
||||||
var (
|
var (
|
||||||
currentYear string
|
currentYear string
|
||||||
gt12Month = len(c.Archives) > 12
|
gt12Month = len(cache.Archives) > 12
|
||||||
)
|
)
|
||||||
for _, archive := range c.Archives {
|
for _, archive := range cache.Archives {
|
||||||
t := archive.Time.In(tools.TimeLocation)
|
t := archive.Time.In(tools.TimeLocation)
|
||||||
if gt12Month {
|
if gt12Month {
|
||||||
year := t.Format("2006 年")
|
year := t.Format("2006 年")
|
||||||
@@ -540,32 +527,7 @@ func (c *Cache) regeneratePages() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.PageArchives = string(PageRender(buf.Bytes()))
|
cache.PageArchives = string(PageRender(buf.Bytes()))
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// timerClean 定时清理文章
|
|
||||||
func (c *Cache) timerClean() {
|
|
||||||
ticker := time.NewTicker(time.Hour)
|
|
||||||
|
|
||||||
for now := range ticker.C {
|
|
||||||
exp := now.Add(TrashArticleExp)
|
|
||||||
err := Store.CleanArticles(context.Background(), exp)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("cache.timerClean.CleanArticles: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// timerDisqus disqus定时操作
|
|
||||||
func (c *Cache) timerDisqus() {
|
|
||||||
ticker := time.NewTicker(5 * time.Hour)
|
|
||||||
|
|
||||||
for range ticker.C {
|
|
||||||
err := DisqusClient.PostsCount(c.ArticlesMap)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Error("cache.timerDisqus.PostsCount: ", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,75 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/cmd/eiblog/config"
|
"github.com/eiblog/eiblog/cmd/eiblog/config"
|
||||||
"github.com/eiblog/eiblog/cmd/eiblog/handler/internal/store"
|
"github.com/eiblog/eiblog/cmd/eiblog/handler/internal/store"
|
||||||
"github.com/eiblog/eiblog/pkg/third/disqus"
|
"github.com/eiblog/eiblog/pkg/third/disqus"
|
||||||
"github.com/eiblog/eiblog/pkg/third/es"
|
"github.com/eiblog/eiblog/pkg/third/es"
|
||||||
"github.com/eiblog/eiblog/pkg/third/pinger"
|
"github.com/eiblog/eiblog/pkg/third/pinger"
|
||||||
"github.com/eiblog/eiblog/pkg/third/qiniu"
|
"github.com/eiblog/eiblog/pkg/third/qiniu"
|
||||||
|
"github.com/eiblog/eiblog/tools"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ESClient *es.ESClient
|
XMLTemplate *template.Template // template/xml模板
|
||||||
DisqusClient *disqus.DisqusClient
|
HTMLTemplate *template.Template // website/html模板
|
||||||
QiniuClient *qiniu.QiniuClient
|
|
||||||
Pinger *pinger.Pinger
|
Store store.Store // 数据库存储
|
||||||
Store store.Store
|
Ei *Cache // 博客数据缓存
|
||||||
|
|
||||||
|
ESClient *es.ESClient // es 客户端
|
||||||
|
DisqusClient *disqus.DisqusClient // disqus 客户端
|
||||||
|
QiniuClient *qiniu.QiniuClient // qiniu客户端
|
||||||
|
Pinger *pinger.Pinger // pinger 客户端
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
ESClient, err = es.NewESClient(config.Conf.ESHost)
|
tools.TimeLocation, err = time.LoadLocation(config.Conf.General.Timezone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal("init es client: ", err)
|
logrus.Fatal("init timezone: ", err)
|
||||||
|
}
|
||||||
|
// 模板解析初始化
|
||||||
|
root := filepath.Join(config.EtcDir, "template", "*.xml")
|
||||||
|
XMLTemplate, err = template.New("eiblog").Funcs(tools.TplFuncMap).ParseGlob(root)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal("init xml template: ", err)
|
||||||
|
}
|
||||||
|
root = filepath.Join(config.EtcDir, "website")
|
||||||
|
files := tools.ReadDirFiles(root, func(fi fs.DirEntry) bool {
|
||||||
|
// should not read dir & .DS_Store
|
||||||
|
return strings.HasPrefix(fi.Name(), ".")
|
||||||
|
})
|
||||||
|
HTMLTemplate, err = template.New("eiblog").Funcs(tools.TplFuncMap).ParseFiles(files...)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal("init html template: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据库初始化
|
||||||
|
logrus.Info("store drivers: ", store.Drivers())
|
||||||
|
Store, err = store.NewStore(config.Conf.Database)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal("init store: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ei, err = NewCache()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal("init blog cache: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Conf.ESHost != "" {
|
||||||
|
ESClient, err = es.NewESClient(config.Conf.ESHost)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal("init es client: ", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DisqusClient, err = disqus.NewDisqusClient(config.Conf.Host, config.Conf.Disqus)
|
DisqusClient, err = disqus.NewDisqusClient(config.Conf.Host, config.Conf.Disqus)
|
||||||
@@ -41,9 +87,6 @@ func init() {
|
|||||||
logrus.Fatal("init pinger: ", err)
|
logrus.Fatal("init pinger: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Info("store drivers: ", store.Drivers())
|
// 启动定时器
|
||||||
Store, err = store.NewStore(config.Conf.Database.Driver, config.Conf.Database.Source)
|
go startTimer()
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal("init store: ", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/eiblog/eiblog/pkg/config"
|
||||||
"github.com/eiblog/eiblog/pkg/model"
|
"github.com/eiblog/eiblog/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +20,10 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
store, err = NewStore("mongodb", "mongodb://127.0.0.1:27017")
|
store, err = NewStore(config.Database{
|
||||||
|
Driver: "mongodb",
|
||||||
|
Source: "mongodb://127.0.0.1:27017",
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/eiblog/eiblog/pkg/config"
|
||||||
"github.com/eiblog/eiblog/pkg/model"
|
"github.com/eiblog/eiblog/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,13 +100,14 @@ func Drivers() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewStore 新建存储
|
// NewStore 新建存储
|
||||||
func NewStore(name string, source string) (Store, error) {
|
func NewStore(conf config.Database) (Store, error) {
|
||||||
storeMu.RLock()
|
storeMu.RLock()
|
||||||
driver, ok := stores[name]
|
driver, ok := stores[conf.Driver]
|
||||||
storeMu.RUnlock()
|
storeMu.RUnlock()
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("store: unknown driver %q (forgotten import?)", name)
|
return nil, fmt.Errorf("store: unknown driver %q (forgotten import?)", conf.Driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
return driver.Init(name, source)
|
return driver.Init(conf.Driver, conf.Source)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/eiblog/eiblog/cmd/eiblog/config"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func startTimer() {
|
||||||
|
err := generateOpensearch()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("startTimer.generateOpensearch: ", err)
|
||||||
|
}
|
||||||
|
err = generateRobots()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("startTimer.generateRobots: ", err)
|
||||||
|
}
|
||||||
|
err = generateCrossdomain()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("startTimer.generateCrossdomain: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(time.Hour)
|
||||||
|
for now := range ticker.C {
|
||||||
|
// generate feed & sitemap
|
||||||
|
if now.Hour()%4 == 0 {
|
||||||
|
err = generateFeed()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("startTimer.generateFeed: ", err)
|
||||||
|
}
|
||||||
|
err = generateSitemap()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("startTimer.generateSitemap: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// clean expired articles
|
||||||
|
exp := now.Add(-48 * time.Hour)
|
||||||
|
err := Store.CleanArticles(context.Background(), exp)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("startTimer.CleanArticles: ", err)
|
||||||
|
}
|
||||||
|
// fetch disqus count
|
||||||
|
if now.Hour()%5 == 0 {
|
||||||
|
err = DisqusClient.PostsCount(Ei.ArticlesMap)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("startTimer.PostsCount: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateFeed 定时刷新feed
|
||||||
|
func generateFeed() error {
|
||||||
|
tpl := XMLTemplate.Lookup("feedTpl.xml")
|
||||||
|
if tpl == nil {
|
||||||
|
return errors.New("not found: feedTpl.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, articles := Ei.PageArticleFE(1, 20)
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"Title": Ei.Blogger.BTitle,
|
||||||
|
"SubTitle": Ei.Blogger.SubTitle,
|
||||||
|
"Host": config.Conf.Host,
|
||||||
|
"FeedrURL": config.Conf.FeedRPC.FeedrURL,
|
||||||
|
"BuildDate": time.Now().Format(time.RFC1123Z),
|
||||||
|
"Articles": articles,
|
||||||
|
}
|
||||||
|
path := filepath.Join(config.EtcDir, "assets", "feed.xml")
|
||||||
|
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return tpl.Execute(f, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateSitemap 定时刷新sitemap
|
||||||
|
func generateSitemap() error {
|
||||||
|
tpl := XMLTemplate.Lookup("sitemapTpl.xml")
|
||||||
|
if tpl == nil {
|
||||||
|
return errors.New("not found: sitemapTpl.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"Articles": Ei.Articles,
|
||||||
|
"Host": config.Conf.Host,
|
||||||
|
}
|
||||||
|
path := filepath.Join(config.EtcDir, "assets", "sitemap.xml")
|
||||||
|
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return tpl.Execute(f, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateOpensearch 生成opensearch.xml
|
||||||
|
func generateOpensearch() error {
|
||||||
|
tpl := XMLTemplate.Lookup("opensearchTpl.xml")
|
||||||
|
if tpl == nil {
|
||||||
|
return errors.New("not found: opensearchTpl.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string]string{
|
||||||
|
"BTitle": Ei.Blogger.BTitle,
|
||||||
|
"SubTitle": Ei.Blogger.SubTitle,
|
||||||
|
"Host": config.Conf.Host,
|
||||||
|
}
|
||||||
|
path := filepath.Join(config.EtcDir, "assets", "opensearch.xml")
|
||||||
|
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return tpl.Execute(f, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateRobots 生成robots.txt
|
||||||
|
func generateRobots() error {
|
||||||
|
tpl := XMLTemplate.Lookup("robotsTpl.xml")
|
||||||
|
if tpl == nil {
|
||||||
|
return errors.New("not found: robotsTpl.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string]string{
|
||||||
|
"Host": config.Conf.Host,
|
||||||
|
}
|
||||||
|
path := filepath.Join(config.EtcDir, "assets", "robots.txt")
|
||||||
|
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return tpl.Execute(f, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateCrossdomain 生成crossdomain.xml
|
||||||
|
func generateCrossdomain() error {
|
||||||
|
tpl := XMLTemplate.Lookup("crossdomainTpl.xml")
|
||||||
|
if tpl == nil {
|
||||||
|
return errors.New("not found: crossdomainTpl.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
params := map[string]string{
|
||||||
|
"Host": config.Conf.Host,
|
||||||
|
}
|
||||||
|
path := filepath.Join(config.EtcDir, "assets", "crossdomain.xml")
|
||||||
|
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return tpl.Execute(f, params)
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
htemplate "html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@@ -242,19 +242,19 @@ func renderHTMLAdminLayout(c *gin.Context, name string, data gin.H) {
|
|||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
// special page
|
// special page
|
||||||
if name == "login.html" {
|
if name == "login.html" {
|
||||||
err := htmlTmpl.ExecuteTemplate(c.Writer, name, data)
|
err := internal.HTMLTemplate.ExecuteTemplate(c.Writer, name, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
err := htmlTmpl.ExecuteTemplate(&buf, name, data)
|
err := internal.HTMLTemplate.ExecuteTemplate(&buf, name, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
data["LayoutContent"] = htemplate.HTML(buf.String())
|
data["LayoutContent"] = template.HTML(buf.String())
|
||||||
err = htmlTmpl.ExecuteTemplate(c.Writer, "adminLayout.html", data)
|
err = internal.HTMLTemplate.ExecuteTemplate(c.Writer, "adminLayout.html", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
htemplate "html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -143,14 +143,14 @@ func handleSearchPage(c *gin.Context) {
|
|||||||
params["Description"] = "站内搜索," + internal.Ei.Blogger.SubTitle
|
params["Description"] = "站内搜索," + internal.Ei.Blogger.SubTitle
|
||||||
params["Path"] = ""
|
params["Path"] = ""
|
||||||
params["CurrentPage"] = "search-post"
|
params["CurrentPage"] = "search-post"
|
||||||
|
|
||||||
q := strings.TrimSpace(c.Query("q"))
|
q := strings.TrimSpace(c.Query("q"))
|
||||||
if q != "" {
|
params["Word"] = q
|
||||||
|
|
||||||
|
if q != "" && internal.ESClient != nil {
|
||||||
start, err := strconv.Atoi(c.Query("start"))
|
start, err := strconv.Atoi(c.Query("start"))
|
||||||
if start < 1 || err != nil {
|
if start < 1 || err != nil {
|
||||||
start = 1
|
start = 1
|
||||||
}
|
}
|
||||||
params["Word"] = q
|
|
||||||
|
|
||||||
vals := c.Request.URL.Query()
|
vals := c.Request.URL.Query()
|
||||||
result, err := internal.ESClient.ElasticSearch(q, config.Conf.General.PageNum, start-1)
|
result, err := internal.ESClient.ElasticSearch(q, config.Conf.General.PageNum, start-1)
|
||||||
@@ -386,19 +386,19 @@ func renderHTMLHomeLayout(c *gin.Context, name string, data gin.H) {
|
|||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
// special page
|
// special page
|
||||||
if name == "disqus.html" {
|
if name == "disqus.html" {
|
||||||
err := htmlTmpl.ExecuteTemplate(c.Writer, name, data)
|
err := internal.HTMLTemplate.ExecuteTemplate(c.Writer, name, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
err := htmlTmpl.ExecuteTemplate(&buf, name, data)
|
err := internal.HTMLTemplate.ExecuteTemplate(&buf, name, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
data["LayoutContent"] = htemplate.HTML(buf.String())
|
data["LayoutContent"] = template.HTML(buf.String())
|
||||||
err = htmlTmpl.ExecuteTemplate(c.Writer, "homeLayout.html", data)
|
err = internal.HTMLTemplate.ExecuteTemplate(c.Writer, "homeLayout.html", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,9 @@
|
|||||||
package page
|
package page
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/eiblog/eiblog/cmd/eiblog/config"
|
|
||||||
"github.com/eiblog/eiblog/tools"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// htmlTmpl html template cache
|
|
||||||
var htmlTmpl *template.Template
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
htmlTmpl = template.New("eiblog").Funcs(tools.TplFuncMap)
|
|
||||||
root := filepath.Join(config.EtcDir, "website")
|
|
||||||
files := tools.ReadDirFiles(root, func(fi fs.DirEntry) bool {
|
|
||||||
// should not read dir & .DS_Store
|
|
||||||
return strings.HasPrefix(fi.Name(), ".") || fi.IsDir()
|
|
||||||
})
|
|
||||||
_, err := htmlTmpl.ParseFiles(files...)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterRoutes register routes
|
// RegisterRoutes register routes
|
||||||
func RegisterRoutes(e *gin.Engine) {
|
func RegisterRoutes(e *gin.Engine) {
|
||||||
e.NoRoute(handleNotFound)
|
e.NoRoute(handleNotFound)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ func init() {
|
|||||||
TplFuncMap["join"] = Join
|
TplFuncMap["join"] = Join
|
||||||
TplFuncMap["isnotzero"] = IsNotZero
|
TplFuncMap["isnotzero"] = IsNotZero
|
||||||
TplFuncMap["getavatar"] = GetAvatar
|
TplFuncMap["getavatar"] = GetAvatar
|
||||||
|
TplFuncMap["imgtonormal"] = ImgToNormal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Str2html string to html
|
// Str2html string to html
|
||||||
|
|||||||
Reference in New Issue
Block a user