mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-11 17:02:27 +08:00
chore: rename blog->eiblog
This commit is contained in:
516
pkg/core/eiblog/admin/admin.go
Normal file
516
pkg/core/eiblog/admin/admin.go
Normal file
@@ -0,0 +1,516 @@
|
||||
// Package admin provides ...
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eiblog/eiblog/pkg/cache"
|
||||
"github.com/eiblog/eiblog/pkg/config"
|
||||
"github.com/eiblog/eiblog/pkg/core/eiblog"
|
||||
"github.com/eiblog/eiblog/pkg/internal"
|
||||
"github.com/eiblog/eiblog/pkg/model"
|
||||
"github.com/eiblog/eiblog/tools"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"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)
|
||||
}
|
||||
|
||||
// RegisterRoutesAuthz register routes
|
||||
func RegisterRoutesAuthz(group gin.IRoutes) {
|
||||
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 登录接口
|
||||
func handleAcctLogin(c *gin.Context) {
|
||||
user := c.PostForm("user")
|
||||
pwd := c.PostForm("password")
|
||||
// code := c.PostForm("code") // 二次验证
|
||||
if user == "" || pwd == "" {
|
||||
logrus.Warnf("参数错误: %s %s", user, pwd)
|
||||
c.Redirect(http.StatusFound, "/admin/login")
|
||||
return
|
||||
}
|
||||
if cache.Ei.Account.Username != user ||
|
||||
cache.Ei.Account.Password != tools.EncryptPasswd(user, pwd) {
|
||||
logrus.Warnf("账号或密码错误 %s, %s", user, pwd)
|
||||
c.Redirect(http.StatusFound, "/admin/login")
|
||||
return
|
||||
}
|
||||
// 登录成功
|
||||
eiblog.SetLogin(c, user)
|
||||
|
||||
cache.Ei.Account.LoginIP = c.ClientIP()
|
||||
cache.Ei.Account.LoginAt = time.Now()
|
||||
cache.Ei.UpdateAccount(context.Background(), user, map[string]interface{}{
|
||||
"login_ip": cache.Ei.Account.LoginIP,
|
||||
"login_at": cache.Ei.Account.LoginAt,
|
||||
})
|
||||
c.Redirect(http.StatusFound, "/admin/profile")
|
||||
}
|
||||
|
||||
// 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, "更新成功", "")
|
||||
}
|
||||
|
||||
// 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, "更新成功", "")
|
||||
}
|
||||
|
||||
// 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, "更新成功", "")
|
||||
}
|
||||
|
||||
// handleDraftDelete 删除草稿, 物理删除
|
||||
func handleDraftDelete(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 {
|
||||
logrus.Error("handleDraftDelete.RemoveArticle: ", err)
|
||||
responseNotice(c, NoticeNotice, "删除失败", "")
|
||||
return
|
||||
}
|
||||
}
|
||||
responseNotice(c, NoticeSuccess, "删除成功", "")
|
||||
}
|
||||
|
||||
// handleAPIPostDelete 删除文章,移入回收箱
|
||||
func handleAPIPostDelete(c *gin.Context) {
|
||||
var ids []int
|
||||
for _, v := range c.PostFormArray("cid[]") {
|
||||
id, err := strconv.Atoi(v)
|
||||
if err != nil || id < config.Conf.EiBlogApp.General.StartID {
|
||||
responseNotice(c, NoticeNotice, "参数错误", "")
|
||||
return
|
||||
}
|
||||
err = cache.Ei.DelArticle(id)
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIPostDelete.DelArticle: ", err)
|
||||
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
// elasticsearch
|
||||
err := internal.ElasticDelIndex(ids)
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIPostDelete.ElasticDelIndex: ", err)
|
||||
}
|
||||
// TODO disqus delete
|
||||
responseNotice(c, NoticeSuccess, "删除成功", "")
|
||||
}
|
||||
|
||||
// handleAPIPostCreate 创建文章
|
||||
func handleAPIPostCreate(c *gin.Context) {
|
||||
var (
|
||||
err error
|
||||
do string
|
||||
cid int
|
||||
)
|
||||
defer func() {
|
||||
now := time.Now().In(tools.TimeLocation)
|
||||
switch do {
|
||||
case "auto": // 自动保存
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"fail": 1, "time": now.Format("15:04:05 PM"), "cid": cid})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": 0, "time": now.Format("15:04:05 PM"), "cid": cid})
|
||||
case "save", "publish": // 草稿,发布
|
||||
if err != nil {
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
uri := "/admin/manage-draft"
|
||||
if do == "publish" {
|
||||
uri = "/admin/manage-posts"
|
||||
}
|
||||
c.Redirect(http.StatusFound, uri)
|
||||
}
|
||||
}()
|
||||
|
||||
do = c.PostForm("do") // auto or save or publish
|
||||
slug := c.PostForm("slug")
|
||||
title := c.PostForm("title")
|
||||
text := c.PostForm("text")
|
||||
date := parseLocationDate(c.PostForm("date"))
|
||||
serie := c.PostForm("serie")
|
||||
tag := c.PostForm("tags")
|
||||
update := c.PostForm("update")
|
||||
if slug == "" || title == "" || text == "" {
|
||||
err = errors.New("参数错误")
|
||||
return
|
||||
}
|
||||
var tags []string
|
||||
if tag != "" {
|
||||
tags = strings.Split(tag, ",")
|
||||
}
|
||||
serieid, _ := strconv.Atoi(serie)
|
||||
article := &model.Article{
|
||||
Title: title,
|
||||
Content: text,
|
||||
Slug: slug,
|
||||
IsDraft: do != "publish",
|
||||
Author: cache.Ei.Account.Username,
|
||||
SerieID: serieid,
|
||||
Tags: tags,
|
||||
CreatedAt: date,
|
||||
}
|
||||
cid, err = strconv.Atoi(c.PostForm("cid"))
|
||||
// 新文章
|
||||
if err != nil || cid < 1 {
|
||||
err = cache.Ei.AddArticle(article)
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIPostCreate.AddArticle: ", err)
|
||||
return
|
||||
}
|
||||
if !article.IsDraft {
|
||||
// 异步执行,快
|
||||
go func() {
|
||||
// elastic
|
||||
internal.ElasticAddIndex(article)
|
||||
// rss
|
||||
internal.PingFunc(cache.Ei.Blogger.BTitle, slug)
|
||||
// disqus
|
||||
internal.ThreadCreate(article, cache.Ei.Blogger.BTitle)
|
||||
}()
|
||||
}
|
||||
return
|
||||
}
|
||||
// 旧文章
|
||||
article.ID = cid
|
||||
artc, _ := cache.Ei.FindArticleByID(article.ID)
|
||||
if artc != nil {
|
||||
article.IsDraft = false
|
||||
article.Count = artc.Count
|
||||
article.UpdatedAt = artc.UpdatedAt
|
||||
}
|
||||
if update == "true" || update == "1" {
|
||||
article.UpdatedAt = time.Now()
|
||||
}
|
||||
// 数据库更新
|
||||
err = cache.Ei.UpdateArticle(context.Background(), article.ID, map[string]interface{}{
|
||||
"title": article.Title,
|
||||
"content": article.Content,
|
||||
"serie_id": article.SerieID,
|
||||
"is_draft": article.IsDraft,
|
||||
"tags": article.Tags,
|
||||
"updated_at": article.UpdatedAt,
|
||||
"created_at": article.CreatedAt,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Error("handleAPIPostCreate.UpdateArticle: ", err)
|
||||
return
|
||||
}
|
||||
if !article.IsDraft {
|
||||
cache.Ei.RepArticle(artc, article)
|
||||
// 异步执行,快
|
||||
go func() {
|
||||
// elastic
|
||||
internal.ElasticAddIndex(article)
|
||||
// rss
|
||||
internal.PingFunc(cache.Ei.Blogger.BTitle, slug)
|
||||
// disqus
|
||||
if artc == nil {
|
||||
internal.ThreadCreate(article, cache.Ei.Blogger.BTitle)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// handleAPISerieDelete 只能逐一删除,专题下不能有文章
|
||||
func handleAPISerieDelete(c *gin.Context) {
|
||||
for _, v := range c.PostFormArray("mid[]") {
|
||||
id, err := strconv.Atoi(v)
|
||||
if err != nil || id < 1 {
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
err = cache.Ei.DelSerie(id)
|
||||
if err != nil {
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
}
|
||||
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")
|
||||
slug := c.PostForm("slug")
|
||||
desc := c.PostForm("description")
|
||||
if name == "" || slug == "" || desc == "" {
|
||||
responseNotice(c, NoticeNotice, "参数错误", "")
|
||||
return
|
||||
}
|
||||
mid, err := strconv.Atoi(c.PostForm("mid"))
|
||||
if err == nil && mid > 0 {
|
||||
var serie *model.Serie
|
||||
for _, v := range cache.Ei.Series {
|
||||
if v.ID == mid {
|
||||
serie = v
|
||||
break
|
||||
}
|
||||
}
|
||||
if serie == nil {
|
||||
responseNotice(c, NoticeNotice, "专题不存在", "")
|
||||
return
|
||||
}
|
||||
err = cache.Ei.UpdateSerie(context.Background(), mid, map[string]interface{}{
|
||||
"slug": slug,
|
||||
"name": name,
|
||||
"desc": desc,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Error("handleAPISerieCreate.UpdateSerie: ", err)
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
serie.Slug = slug
|
||||
serie.Name = name
|
||||
serie.Desc = desc
|
||||
} else {
|
||||
err = cache.Ei.AddSerie(&model.Serie{
|
||||
Slug: slug,
|
||||
Name: name,
|
||||
Desc: desc,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Error("handleAPISerieCreate.InsertSerie: ", err)
|
||||
responseNotice(c, NoticeNotice, err.Error(), "")
|
||||
return
|
||||
}
|
||||
}
|
||||
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, tools.TimeLocation)
|
||||
if err == nil {
|
||||
return t.UTC()
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
56
pkg/core/eiblog/api.go
Normal file
56
pkg/core/eiblog/api.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Package eiblog provides ...
|
||||
package eiblog
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @title APP Demo API
|
||||
// @version 1.0
|
||||
// @description This is a sample server celler server.
|
||||
|
||||
// @BasePath /api
|
||||
|
||||
// AuthFilter auth filter
|
||||
func AuthFilter(c *gin.Context) {
|
||||
if !IsLogined(c) {
|
||||
c.Abort()
|
||||
c.Status(http.StatusUnauthorized)
|
||||
c.Redirect(http.StatusFound, "/admin/login")
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
// SetLogin login user
|
||||
func SetLogin(c *gin.Context, username string) {
|
||||
session := sessions.Default(c)
|
||||
session.Set("username", username)
|
||||
session.Save()
|
||||
}
|
||||
|
||||
// SetLogout logout user
|
||||
func SetLogout(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
session.Delete("username")
|
||||
session.Save()
|
||||
}
|
||||
|
||||
// IsLogined account logined
|
||||
func IsLogined(c *gin.Context) bool {
|
||||
return GetUsername(c) != ""
|
||||
}
|
||||
|
||||
// GetUsername get logined account
|
||||
func GetUsername(c *gin.Context) string {
|
||||
session := sessions.Default(c)
|
||||
username := session.Get("username")
|
||||
if username == nil {
|
||||
return ""
|
||||
}
|
||||
return username.(string)
|
||||
}
|
||||
65
pkg/core/eiblog/docs/docs.go
Normal file
65
pkg/core/eiblog/docs/docs.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
// This file was generated by swaggo/swag at
|
||||
// 2021-04-29 11:12:59.414073 +0800 CST m=+0.019228292
|
||||
|
||||
package docs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/alecthomas/template"
|
||||
"github.com/swaggo/swag"
|
||||
)
|
||||
|
||||
var doc = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "This is a sample server celler server.",
|
||||
"title": "APP Demo API",
|
||||
"contact": {},
|
||||
"license": {},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "/api",
|
||||
"paths": {}
|
||||
}`
|
||||
|
||||
type swaggerInfo struct {
|
||||
Version string
|
||||
Host string
|
||||
BasePath string
|
||||
Schemes []string
|
||||
Title string
|
||||
Description string
|
||||
}
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = swaggerInfo{ Schemes: []string{}}
|
||||
|
||||
type s struct{}
|
||||
|
||||
func (s *s) ReadDoc() string {
|
||||
t, err := template.New("swagger_info").Funcs(template.FuncMap{
|
||||
"marshal": func(v interface {}) string {
|
||||
a, _ := json.Marshal(v)
|
||||
return string(a)
|
||||
},
|
||||
}).Parse(doc)
|
||||
if err != nil {
|
||||
return doc
|
||||
}
|
||||
|
||||
var tpl bytes.Buffer
|
||||
if err := t.Execute(&tpl, SwaggerInfo); err != nil {
|
||||
return doc
|
||||
}
|
||||
|
||||
return tpl.String()
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(swag.Name, &s{})
|
||||
}
|
||||
13
pkg/core/eiblog/docs/swagger.json
Normal file
13
pkg/core/eiblog/docs/swagger.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "This is a sample server celler server.",
|
||||
"title": "APP Demo API",
|
||||
"contact": {},
|
||||
"license": {},
|
||||
"version": "1.0"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "/api",
|
||||
"paths": {}
|
||||
}
|
||||
10
pkg/core/eiblog/docs/swagger.yaml
Normal file
10
pkg/core/eiblog/docs/swagger.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
basePath: /api
|
||||
host: '{{.Host}}'
|
||||
info:
|
||||
contact: {}
|
||||
description: This is a sample server celler server.
|
||||
license: {}
|
||||
title: APP Demo API
|
||||
version: "1.0"
|
||||
paths: {}
|
||||
swagger: "2.0"
|
||||
49
pkg/core/eiblog/file/file.go
Normal file
49
pkg/core/eiblog/file/file.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Package file provides ...
|
||||
package file
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RegisterRoutes register routes
|
||||
func RegisterRoutes(e *gin.Engine) {
|
||||
e.GET("/rss.html", handleFeed)
|
||||
e.GET("/feed", handleFeed)
|
||||
e.GET("/opensearch.xml", handleOpensearch)
|
||||
e.GET("/sitemap.xml", handleSitemap)
|
||||
e.GET("/robots.txt", handleRobots)
|
||||
e.GET("/crossdomain.xml", handleCrossDomain)
|
||||
e.GET("/favicon.ico", handleFavicon)
|
||||
}
|
||||
|
||||
// handleFeed feed.xml
|
||||
func handleFeed(c *gin.Context) {
|
||||
http.ServeFile(c.Writer, c.Request, "assets/feed.xml")
|
||||
}
|
||||
|
||||
// handleOpensearch opensearch.xml
|
||||
func handleOpensearch(c *gin.Context) {
|
||||
http.ServeFile(c.Writer, c.Request, "assets/opensearch.xml")
|
||||
}
|
||||
|
||||
// handleRobots robotx.txt
|
||||
func handleRobots(c *gin.Context) {
|
||||
http.ServeFile(c.Writer, c.Request, "assets/robots.txt")
|
||||
}
|
||||
|
||||
// handleSitemap sitemap.xml
|
||||
func handleSitemap(c *gin.Context) {
|
||||
http.ServeFile(c.Writer, c.Request, "assets/sitemap.xml")
|
||||
}
|
||||
|
||||
// handleCrossDomain crossdomain.xml
|
||||
func handleCrossDomain(c *gin.Context) {
|
||||
http.ServeFile(c.Writer, c.Request, "assets/crossdomain.xml")
|
||||
}
|
||||
|
||||
// handleFavicon favicon.ico
|
||||
func handleFavicon(c *gin.Context) {
|
||||
http.ServeFile(c.Writer, c.Request, "assets/favicon.ico")
|
||||
}
|
||||
163
pkg/core/eiblog/file/timer.go
Normal file
163
pkg/core/eiblog/file/timer.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// Package file provides ...
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/eiblog/eiblog/pkg/cache"
|
||||
"github.com/eiblog/eiblog/pkg/config"
|
||||
"github.com/eiblog/eiblog/tools"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var xmlTmpl *template.Template
|
||||
|
||||
func init() {
|
||||
root := filepath.Join(config.WorkDir, "conf", "tpl", "*.xml")
|
||||
|
||||
var err error
|
||||
xmlTmpl, err = template.New("").Funcs(template.FuncMap{
|
||||
"dateformat": tools.DateFormat,
|
||||
}).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 := cache.Ei.PageArticleFE(1, 20)
|
||||
params := map[string]interface{}{
|
||||
"Title": cache.Ei.Blogger.BTitle,
|
||||
"SubTitle": cache.Ei.Blogger.SubTitle,
|
||||
"Host": config.Conf.EiBlogApp.Host,
|
||||
"FeedrURL": config.Conf.EiBlogApp.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": cache.Ei.Articles,
|
||||
"Host": config.Conf.EiBlogApp.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": cache.Ei.Blogger.BTitle,
|
||||
"SubTitle": cache.Ei.Blogger.SubTitle,
|
||||
"Host": config.Conf.EiBlogApp.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.EiBlogApp.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.EiBlogApp.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
|
||||
}
|
||||
}
|
||||
264
pkg/core/eiblog/page/be.go
Normal file
264
pkg/core/eiblog/page/be.go
Normal file
@@ -0,0 +1,264 @@
|
||||
// Package page provides ...
|
||||
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/cache/store"
|
||||
"github.com/eiblog/eiblog/pkg/config"
|
||||
"github.com/eiblog/eiblog/pkg/core/eiblog"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// baseBEParams 基础参数
|
||||
func baseBEParams(c *gin.Context) gin.H {
|
||||
return gin.H{
|
||||
"Author": cache.Ei.Account.Username,
|
||||
"Qiniu": config.Conf.EiBlogApp.Qiniu,
|
||||
}
|
||||
}
|
||||
|
||||
// handleLoginPage 登录页面
|
||||
func handleLoginPage(c *gin.Context) {
|
||||
logout := c.Query("logout")
|
||||
if logout == "true" {
|
||||
eiblog.SetLogout(c)
|
||||
} else if eiblog.IsLogined(c) {
|
||||
c.Redirect(http.StatusFound, "/admin/profile")
|
||||
return
|
||||
}
|
||||
params := gin.H{"BTitle": cache.Ei.Blogger.BTitle}
|
||||
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.EiBlogApp.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
|
||||
params["List"], max = cache.Ei.PageArticleBE(se, kw, false, false,
|
||||
pg, config.Conf.EiBlogApp.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 {
|
||||
for _, v := range cache.Ei.Series {
|
||||
if v.ID == id {
|
||||
params["Title"] = "编辑专题 | " + cache.Ei.Blogger.BTitle
|
||||
params["Edit"] = v
|
||||
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)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
// 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
|
||||
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)
|
||||
} 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
|
||||
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.LoadArticleList: ", err)
|
||||
}
|
||||
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")
|
||||
// special page
|
||||
if name == "login.html" {
|
||||
err := htmlTmpl.ExecuteTemplate(c.Writer, name, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
err := htmlTmpl.ExecuteTemplate(&buf, name, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data["LayoutContent"] = htemplate.HTML(buf.String())
|
||||
err = htmlTmpl.ExecuteTemplate(c.Writer, "adminLayout.html", data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if c.Writer.Status() == 0 {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
}
|
||||
386
pkg/core/eiblog/page/fe.go
Normal file
386
pkg/core/eiblog/page/fe.go
Normal file
@@ -0,0 +1,386 @@
|
||||
// Package page provides ...
|
||||
package page
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
htemplate "html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eiblog/eiblog/pkg/cache"
|
||||
"github.com/eiblog/eiblog/pkg/config"
|
||||
"github.com/eiblog/eiblog/pkg/internal"
|
||||
"github.com/eiblog/eiblog/tools"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// baseFEParams 基础参数
|
||||
func baseFEParams(c *gin.Context) gin.H {
|
||||
version := 0
|
||||
|
||||
cookie, err := c.Request.Cookie("v")
|
||||
if err != nil || cookie.Value !=
|
||||
fmt.Sprint(config.Conf.EiBlogApp.StaticVersion) {
|
||||
version = config.Conf.EiBlogApp.StaticVersion
|
||||
}
|
||||
return gin.H{
|
||||
"BlogName": cache.Ei.Blogger.BlogName,
|
||||
"SubTitle": cache.Ei.Blogger.SubTitle,
|
||||
"BTitle": cache.Ei.Blogger.BTitle,
|
||||
"BeiAn": cache.Ei.Blogger.BeiAn,
|
||||
"Domain": config.Conf.EiBlogApp.Host,
|
||||
"CopyYear": time.Now().Year(),
|
||||
"Twitter": config.Conf.EiBlogApp.Twitter,
|
||||
"Qiniu": config.Conf.EiBlogApp.Qiniu,
|
||||
"Disqus": config.Conf.EiBlogApp.Disqus,
|
||||
"Version": version,
|
||||
}
|
||||
}
|
||||
|
||||
// handleNotFound not found page
|
||||
func handleNotFound(c *gin.Context) {
|
||||
params := baseFEParams(c)
|
||||
params["Title"] = "Not Found"
|
||||
params["Description"] = "404 Not Found"
|
||||
params["Path"] = ""
|
||||
c.Status(http.StatusNotFound)
|
||||
renderHTMLHomeLayout(c, "notfound", params)
|
||||
}
|
||||
|
||||
// handleHomePage 首页
|
||||
func handleHomePage(c *gin.Context) {
|
||||
params := baseFEParams(c)
|
||||
params["Title"] = cache.Ei.Blogger.BTitle + " | " + cache.Ei.Blogger.SubTitle
|
||||
params["Description"] = "博客首页," + cache.Ei.Blogger.SubTitle
|
||||
params["Path"] = c.Request.URL.Path
|
||||
params["CurrentPage"] = "blog-home"
|
||||
pn, err := strconv.Atoi(c.Query("pn"))
|
||||
if err != nil || pn < 1 {
|
||||
pn = 1
|
||||
}
|
||||
params["Prev"], params["Next"], params["List"] = cache.Ei.PageArticleFE(pn,
|
||||
config.Conf.EiBlogApp.General.PageNum)
|
||||
|
||||
renderHTMLHomeLayout(c, "home", params)
|
||||
}
|
||||
|
||||
// handleArticlePage 文章页
|
||||
func handleArticlePage(c *gin.Context) {
|
||||
slug := c.Param("slug")
|
||||
if !strings.HasSuffix(slug, ".html") || cache.Ei.ArticlesMap[slug[:len(slug)-5]] == nil {
|
||||
handleNotFound(c)
|
||||
return
|
||||
}
|
||||
article := cache.Ei.ArticlesMap[slug[:len(slug)-5]]
|
||||
params := baseFEParams(c)
|
||||
params["Title"] = article.Title + " | " + cache.Ei.Blogger.BTitle
|
||||
params["Path"] = c.Request.URL.Path
|
||||
params["CurrentPage"] = "post-" + article.Slug
|
||||
params["Article"] = article
|
||||
|
||||
var name string
|
||||
switch slug {
|
||||
case "blogroll.html":
|
||||
name = "blogroll"
|
||||
params["Description"] = "友情连接," + cache.Ei.Blogger.SubTitle
|
||||
case "about.html":
|
||||
name = "about"
|
||||
params["Description"] = "关于作者," + cache.Ei.Blogger.SubTitle
|
||||
default:
|
||||
params["Description"] = article.Desc + "," + cache.Ei.Blogger.SubTitle
|
||||
name = "article"
|
||||
params["Copyright"] = cache.Ei.Blogger.Copyright
|
||||
if !article.UpdatedAt.IsZero() {
|
||||
params["Days"] = int(time.Now().Sub(article.UpdatedAt).Hours()) / 24
|
||||
} else {
|
||||
params["Days"] = int(time.Now().Sub(article.CreatedAt).Hours()) / 24
|
||||
}
|
||||
if article.SerieID > 0 {
|
||||
for _, series := range cache.Ei.Series {
|
||||
if series.ID == article.SerieID {
|
||||
params["Serie"] = series
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
renderHTMLHomeLayout(c, name, params)
|
||||
}
|
||||
|
||||
// handleSeriesPage 专题页
|
||||
func handleSeriesPage(c *gin.Context) {
|
||||
params := baseFEParams(c)
|
||||
params["Title"] = "专题 | " + cache.Ei.Blogger.BTitle
|
||||
params["Description"] = "专题列表," + cache.Ei.Blogger.SubTitle
|
||||
params["Path"] = c.Request.URL.Path
|
||||
params["CurrentPage"] = "series"
|
||||
params["Article"] = cache.Ei.PageSeries
|
||||
renderHTMLHomeLayout(c, "series", params)
|
||||
}
|
||||
|
||||
// handleArchivePage 归档页
|
||||
func handleArchivePage(c *gin.Context) {
|
||||
params := baseFEParams(c)
|
||||
params["Title"] = "归档 | " + cache.Ei.Blogger.BTitle
|
||||
params["Description"] = "博客归档," + cache.Ei.Blogger.SubTitle
|
||||
params["Path"] = c.Request.URL.Path
|
||||
params["CurrentPage"] = "archives"
|
||||
params["Article"] = cache.Ei.PageArchives
|
||||
renderHTMLHomeLayout(c, "archives", params)
|
||||
}
|
||||
|
||||
// handleSearchPage 搜索页
|
||||
func handleSearchPage(c *gin.Context) {
|
||||
params := baseFEParams(c)
|
||||
params["Title"] = "站内搜索 | " + cache.Ei.Blogger.BTitle
|
||||
params["Description"] = "站内搜索," + cache.Ei.Blogger.SubTitle
|
||||
params["Path"] = ""
|
||||
params["CurrentPage"] = "search-post"
|
||||
|
||||
q := strings.TrimSpace(c.Query("q"))
|
||||
if q != "" {
|
||||
start, err := strconv.Atoi(c.Query("start"))
|
||||
if start < 1 || err != nil {
|
||||
start = 1
|
||||
}
|
||||
params["Word"] = q
|
||||
|
||||
vals := c.Request.URL.Query()
|
||||
result, err := internal.ElasticSearch(q, config.Conf.EiBlogApp.General.PageNum, start-1)
|
||||
if err != nil {
|
||||
logrus.Error("HandleSearchPage.ElasticSearch: ", err)
|
||||
} else {
|
||||
result.Took /= 1000
|
||||
for i, v := range result.Hits.Hits {
|
||||
article := cache.Ei.ArticlesMap[v.Source.Slug]
|
||||
if len(v.Highlight.Content) == 0 && article != nil {
|
||||
result.Hits.Hits[i].Highlight.Content = []string{article.Excerpt}
|
||||
}
|
||||
}
|
||||
params["SearchResult"] = result
|
||||
if num := start - config.Conf.EiBlogApp.General.PageNum; num > 0 {
|
||||
vals.Set("start", fmt.Sprint(num))
|
||||
params["Prev"] = vals.Encode()
|
||||
}
|
||||
if num := start + config.Conf.EiBlogApp.General.PageNum; result.Hits.Total >= num {
|
||||
vals.Set("start", fmt.Sprint(num))
|
||||
params["Next"] = vals.Encode()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
params["HotWords"] = config.Conf.EiBlogApp.HotWords
|
||||
}
|
||||
renderHTMLHomeLayout(c, "search", params)
|
||||
}
|
||||
|
||||
// disqusComments 服务端获取评论详细
|
||||
type disqusComments struct {
|
||||
ErrNo int `json:"errno"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
Data struct {
|
||||
Next string `json:"next"`
|
||||
Total int `json:"total"`
|
||||
Comments []commentsDetail `json:"comments"`
|
||||
Thread string `json:"thread"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// handleDisqusList 获取评论列表
|
||||
func handleDisqusList(c *gin.Context) {
|
||||
dcs := &disqusComments{}
|
||||
defer c.JSON(http.StatusOK, dcs)
|
||||
|
||||
slug := c.Param("slug")
|
||||
cursor := c.Query("cursor")
|
||||
if artc := cache.Ei.ArticlesMap[slug]; artc != nil {
|
||||
dcs.Data.Thread = artc.Thread
|
||||
}
|
||||
postsList, err := internal.PostsList(slug, cursor)
|
||||
if err != nil {
|
||||
logrus.Error("hadnleDisqusList.PostsList: ", err)
|
||||
dcs.ErrNo = 0
|
||||
dcs.ErrMsg = "系统错误"
|
||||
return
|
||||
}
|
||||
dcs.ErrNo = postsList.Code
|
||||
if postsList.Cursor.HasNext {
|
||||
dcs.Data.Next = postsList.Cursor.Next
|
||||
}
|
||||
dcs.Data.Total = len(postsList.Response)
|
||||
dcs.Data.Comments = make([]commentsDetail, len(postsList.Response))
|
||||
for i, v := range postsList.Response {
|
||||
if dcs.Data.Thread == "" {
|
||||
dcs.Data.Thread = v.Thread
|
||||
}
|
||||
dcs.Data.Comments[i] = commentsDetail{
|
||||
ID: v.ID,
|
||||
Name: v.Author.Name,
|
||||
Parent: v.Parent,
|
||||
Url: v.Author.ProfileUrl,
|
||||
Avatar: v.Author.Avatar.Cache,
|
||||
CreatedAtStr: tools.ConvertStr(v.CreatedAt),
|
||||
Message: v.Message,
|
||||
IsDeleted: v.IsDeleted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleDisqusPage 评论页
|
||||
func handleDisqusPage(c *gin.Context) {
|
||||
array := strings.Split(c.Param("slug"), "|")
|
||||
if len(array) != 4 || array[1] == "" {
|
||||
c.String(http.StatusOK, "出错啦。。。")
|
||||
return
|
||||
}
|
||||
article := cache.Ei.ArticlesMap[array[0]]
|
||||
params := gin.H{
|
||||
"Titile": "发表评论 | " + cache.Ei.Blogger.BTitle,
|
||||
"ATitle": article.Title,
|
||||
"Thread": array[1],
|
||||
"Slug": article.Slug,
|
||||
}
|
||||
renderHTMLHomeLayout(c, "disqus.html", params)
|
||||
}
|
||||
|
||||
// 发表评论
|
||||
// [thread:[5279901489] parent:[] identifier:[post-troubleshooting-https]
|
||||
// next:[] author_name:[你好] author_email:[chenqijing2@163.com] message:[fdsfdsf]]
|
||||
type disqusCreate struct {
|
||||
ErrNo int `json:"errno"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
Data commentsDetail `json:"data"`
|
||||
}
|
||||
|
||||
type commentsDetail struct {
|
||||
ID string `json:"id"`
|
||||
Parent int `json:"parent"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Avatar string `json:"avatar"`
|
||||
CreatedAtStr string `json:"createdAtStr"`
|
||||
Message string `json:"message"`
|
||||
IsDeleted bool `json:"isDeleted"`
|
||||
}
|
||||
|
||||
// handleDisqusCreate 评论文章
|
||||
func handleDisqusCreate(c *gin.Context) {
|
||||
resp := &disqusCreate{}
|
||||
defer c.JSON(http.StatusOK, resp)
|
||||
|
||||
msg := c.PostForm("message")
|
||||
email := c.PostForm("author_name")
|
||||
name := c.PostForm("author_name")
|
||||
thread := c.PostForm("thread")
|
||||
identifier := c.PostForm("identifier")
|
||||
if msg == "" || email == "" || name == "" || thread == "" || identifier == "" {
|
||||
resp.ErrNo = 1
|
||||
resp.ErrMsg = "参数错误"
|
||||
return
|
||||
}
|
||||
logrus.Info("email: %s comments: %s", email, thread)
|
||||
|
||||
comment := internal.PostComment{
|
||||
Message: msg,
|
||||
Parent: c.PostForm("parent"),
|
||||
Thread: thread,
|
||||
AuthorEmail: email,
|
||||
AuthorName: name,
|
||||
Identifier: identifier,
|
||||
IpAddress: c.ClientIP(),
|
||||
}
|
||||
postDetail, err := internal.PostCreate(&comment)
|
||||
if err != nil {
|
||||
logrus.Error("handleDisqusCreate.PostCreate: ", err)
|
||||
resp.ErrNo = 1
|
||||
resp.ErrMsg = "提交评论失败,请重试"
|
||||
return
|
||||
}
|
||||
err = internal.PostApprove(postDetail.Response.ID)
|
||||
if err != nil {
|
||||
logrus.Error("handleDisqusCreate.PostApprove: ", err)
|
||||
resp.ErrNo = 1
|
||||
resp.ErrMsg = "提交评论失败,请重试"
|
||||
}
|
||||
resp.ErrNo = 0
|
||||
resp.Data = commentsDetail{
|
||||
ID: postDetail.Response.ID,
|
||||
Name: name,
|
||||
Parent: postDetail.Response.Parent,
|
||||
Url: postDetail.Response.Author.ProfileUrl,
|
||||
Avatar: postDetail.Response.Author.Avatar.Cache,
|
||||
CreatedAtStr: tools.ConvertStr(postDetail.Response.CreatedAt),
|
||||
Message: postDetail.Response.Message,
|
||||
IsDeleted: postDetail.Response.IsDeleted,
|
||||
}
|
||||
}
|
||||
|
||||
// handleBeaconPage 服务端推送谷歌统计
|
||||
func handleBeaconPage(c *gin.Context) {
|
||||
ua := c.Request.UserAgent()
|
||||
|
||||
vals := c.Request.URL.Query()
|
||||
vals.Set("v", config.Conf.EiBlogApp.Google.V)
|
||||
vals.Set("tid", config.Conf.EiBlogApp.Google.Tid)
|
||||
vals.Set("t", config.Conf.EiBlogApp.Google.T)
|
||||
cookie, _ := c.Cookie("u")
|
||||
vals.Set("cid", cookie)
|
||||
|
||||
vals.Set("dl", c.Request.Referer())
|
||||
vals.Set("uip", c.ClientIP())
|
||||
go func() {
|
||||
req, err := http.NewRequest("POST", config.Conf.EiBlogApp.Google.URL,
|
||||
strings.NewReader(vals.Encode()))
|
||||
if err != nil {
|
||||
logrus.Error("HandleBeaconPage.NewRequest: ", err)
|
||||
return
|
||||
}
|
||||
req.Header.Set("User-Agent", ua)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
logrus.Error("HandleBeaconPage.Do: ", err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
logrus.Error("HandleBeaconPage.ReadAll: ", err)
|
||||
return
|
||||
}
|
||||
if res.StatusCode/100 != 2 {
|
||||
logrus.Error(string(data))
|
||||
}
|
||||
}()
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// renderHTMLHomeLayout homelayout html
|
||||
func renderHTMLHomeLayout(c *gin.Context, name string, data gin.H) {
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
// special page
|
||||
if name == "disqus.html" {
|
||||
err := htmlTmpl.ExecuteTemplate(c.Writer, name, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
err := htmlTmpl.ExecuteTemplate(&buf, name, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data["LayoutContent"] = htemplate.HTML(buf.String())
|
||||
err = htmlTmpl.ExecuteTemplate(c.Writer, "homeLayout.html", data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if c.Writer.Status() == 0 {
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
}
|
||||
66
pkg/core/eiblog/page/page.go
Normal file
66
pkg/core/eiblog/page/page.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Package page provides ...
|
||||
package page
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"github.com/eiblog/eiblog/pkg/config"
|
||||
"github.com/eiblog/eiblog/tools"
|
||||
|
||||
"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.WorkDir, "website")
|
||||
files := tools.ReadDirFiles(root, func(name string) bool {
|
||||
if name == ".DS_Store" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
_, err := htmlTmpl.ParseFiles(files...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes register routes
|
||||
func RegisterRoutes(e *gin.Engine) {
|
||||
e.NoRoute(handleNotFound)
|
||||
|
||||
e.GET("/", handleHomePage)
|
||||
e.GET("/post/:slug", handleArticlePage)
|
||||
e.GET("/series.html", handleSeriesPage)
|
||||
e.GET("/archives.html", handleArchivePage)
|
||||
e.GET("/search.html", handleSearchPage)
|
||||
e.GET("/disqus/post-:slug", handleDisqusList)
|
||||
e.GET("/disqus/form/post-:slug", handleDisqusPage)
|
||||
e.POST("/disqus/create", handleDisqusCreate)
|
||||
e.GET("/beacon.html", handleBeaconPage)
|
||||
|
||||
// login page
|
||||
e.GET("/admin/login", handleLoginPage)
|
||||
}
|
||||
|
||||
// RegisterRoutesAuthz register admin
|
||||
func RegisterRoutesAuthz(group gin.IRoutes) {
|
||||
// console
|
||||
group.GET("/profile", handleAdminProfile)
|
||||
// write
|
||||
group.GET("/write-post", handleAdminPost)
|
||||
group.GET("/draft-delete", handleDraftDelete)
|
||||
// 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)
|
||||
}
|
||||
15
pkg/core/eiblog/swag/swag.go
Normal file
15
pkg/core/eiblog/swag/swag.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Package swag provides ...
|
||||
package swag
|
||||
|
||||
import (
|
||||
_ "github.com/eiblog/eiblog/pkg/core/eiblog/docs" // docs
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
"github.com/swaggo/gin-swagger/swaggerFiles"
|
||||
)
|
||||
|
||||
// RegisterRoutes register routes
|
||||
func RegisterRoutes(group gin.IRoutes) {
|
||||
group.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
}
|
||||
Reference in New Issue
Block a user