Files
eiblog/front.go
2018-01-13 18:19:54 +08:00

455 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package main provides ...
// 这里是前端页面展示相关接口
package main
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
"github.com/eiblog/eiblog/setting"
"github.com/eiblog/utils/logd"
"github.com/gin-gonic/gin"
)
func Filter() gin.HandlerFunc {
return func(c *gin.Context) {
// 过滤黑名单
if BlackFilter(c) {
c.Abort()
return
}
// 重定向
if Redirect(c) {
c.Abort()
return
}
// 用户cookie用于统计
UserCookie(c)
c.Next()
}
}
// 黑名单过滤
func BlackFilter(c *gin.Context) bool {
ip := c.ClientIP()
if setting.BlackIP[ip] {
c.String(http.StatusForbidden, "Your IP is blacklisted.")
return true
}
return false
}
// 重定向
func Redirect(c *gin.Context) bool {
if setting.Conf.Mode.EnableHttps && c.Request.ProtoMajor == 1 {
var port string
if strings.Contains(c.Request.Host, ":") {
port = fmt.Sprintf(":%d", setting.Conf.Mode.HttpsPort)
}
c.Redirect(http.StatusMovedPermanently, "https://"+setting.Conf.Mode.Domain+port+c.Request.RequestURI)
return true
}
return false
}
// 用户识别
func UserCookie(c *gin.Context) {
cookie, err := c.Cookie("u")
if err != nil || cookie == "" {
c.SetCookie("u", RandUUIDv4(), 86400*730, "/", "", true, true)
}
}
// 解析静态文件版本
func StaticVersion(c *gin.Context) (version int) {
cookie, err := c.Request.Cookie("v")
if err != nil || cookie.Value != fmt.Sprint(setting.Conf.StaticVersion) {
return setting.Conf.StaticVersion
}
return 0
}
func GetBase() gin.H {
return gin.H{
"BlogName": Ei.BlogName,
"SubTitle": Ei.SubTitle,
"Twitter": setting.Conf.Twitter,
"CopyYear": time.Now().Year(),
"BTitle": Ei.BTitle,
"BeiAn": Ei.BeiAn,
"Domain": setting.Conf.Mode.Domain,
"Qiniu": setting.Conf.Qiniu,
"Disqus": setting.Conf.Disqus,
}
}
// not found
func HandleNotFound(c *gin.Context) {
h := GetBase()
h["Version"] = StaticVersion(c)
h["Title"] = "Not Found"
h["Description"] = "404 Not Found"
h["Path"] = ""
c.Status(http.StatusNotFound)
RenderHTMLFront(c, "notfound", h)
}
// 首页
func HandleHomePage(c *gin.Context) {
h := GetBase()
h["Version"] = StaticVersion(c)
h["Title"] = Ei.BTitle + " | " + Ei.SubTitle
h["Description"] = "博客首页," + Ei.SubTitle
h["Path"] = c.Request.URL.Path
h["CurrentPage"] = "blog-home"
pn, err := strconv.Atoi(c.Query("pn"))
if err != nil || pn < 1 {
pn = 1
}
h["Prev"], h["Next"], h["List"] = PageList(pn, setting.Conf.General.PageNum)
c.Status(http.StatusOK)
RenderHTMLFront(c, "home", h)
}
// 专题页
func HandleSeriesPage(c *gin.Context) {
h := GetBase()
h["Version"] = StaticVersion(c)
h["Title"] = "专题 | " + Ei.BTitle
h["Description"] = "专题列表," + Ei.SubTitle
h["Path"] = c.Request.URL.Path
h["CurrentPage"] = "series"
h["Article"] = Ei.PageSeries
c.Status(http.StatusOK)
RenderHTMLFront(c, "series", h)
}
// 归档页
func HandleArchivesPage(c *gin.Context) {
h := GetBase()
h["Version"] = StaticVersion(c)
h["Title"] = "归档 | " + Ei.BTitle
h["Description"] = "博客归档," + Ei.SubTitle
h["Path"] = c.Request.URL.Path
h["CurrentPage"] = "archives"
h["Article"] = Ei.PageArchives
c.Status(http.StatusOK)
RenderHTMLFront(c, "archives", h)
}
// 文章
func HandleArticlePage(c *gin.Context) {
path := c.Param("slug")
if !strings.HasSuffix(path, ".html") || Ei.MapArticles[path[:len(path)-5]] == nil {
HandleNotFound(c)
return
}
artc := Ei.MapArticles[path[:len(path)-5]]
h := GetBase()
h["Version"] = StaticVersion(c)
h["Title"] = artc.Title + " | " + Ei.BTitle
h["Path"] = c.Request.URL.Path
h["CurrentPage"] = "post-" + artc.Slug
var name string
if path == "blogroll.html" {
name = "blogroll"
h["Description"] = "友情连接," + Ei.SubTitle
} else if path == "about.html" {
name = "about"
h["Description"] = "关于作者," + Ei.SubTitle
} else {
h["Description"] = artc.Desc + "" + Ei.SubTitle
name = "article"
h["Copyright"] = Ei.Copyright
if !artc.UpdateTime.IsZero() {
h["Days"] = int(time.Now().Sub(artc.UpdateTime).Hours()) / 24
} else {
h["Days"] = int(time.Now().Sub(artc.CreateTime).Hours()) / 24
}
if artc.SerieID > 0 {
h["Serie"] = QuerySerie(artc.SerieID)
}
}
h["Article"] = artc
c.Status(http.StatusOK)
RenderHTMLFront(c, name, h)
}
// 搜索页
func HandleSearchPage(c *gin.Context) {
h := GetBase()
h["Version"] = StaticVersion(c)
h["Title"] = "站内搜索 | " + Ei.BTitle
h["Description"] = "站内搜索," + Ei.SubTitle
h["Path"] = ""
h["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
}
h["Word"] = q
var result *ESSearchResult
vals := c.Request.URL.Query()
result = Elasticsearch(q, setting.Conf.General.PageNum, start-1)
if result != nil {
result.Took /= 1000
for i, v := range result.Hits.Hits {
if artc := Ei.MapArticles[result.Hits.Hits[i].Source.Slug]; len(v.Highlight.Content) == 0 && artc != nil {
result.Hits.Hits[i].Highlight.Content = []string{artc.Excerpt}
}
}
h["SearchResult"] = result
if start-setting.Conf.General.PageNum > 0 {
vals.Set("start", fmt.Sprint(start-setting.Conf.General.PageNum))
h["Prev"] = vals.Encode()
}
if result.Hits.Total >= start+setting.Conf.General.PageNum {
vals.Set("start", fmt.Sprint(start+setting.Conf.General.PageNum))
h["Next"] = vals.Encode()
}
}
} else {
h["HotWords"] = setting.Conf.HotWords
}
c.Status(http.StatusOK)
RenderHTMLFront(c, "search", h)
}
// 评论页
func HandleDisqusFrom(c *gin.Context) {
params := strings.Split(c.Param("slug"), "|")
if len(params) != 4 || params[1] == "" {
c.String(http.StatusOK, "出错啦。。。")
return
}
artc := Ei.MapArticles[params[0]]
data := gin.H{
"Title": "发表评论 | " + Ei.BTitle,
"ATitle": artc.Title,
"Thread": params[1],
"Slug": artc.Slug,
}
err := Tmpl.ExecuteTemplate(c.Writer, "disqus.html", data)
if err != nil {
panic(err)
}
c.Header("Content-Type", "text/html; charset=utf-8")
}
// feed
func HandleFeed(c *gin.Context) {
http.ServeFile(c.Writer, c.Request, "static/feed.xml")
}
// opensearch
func HandleOpenSearch(c *gin.Context) {
http.ServeFile(c.Writer, c.Request, "static/opensearch.xml")
}
// robots
func HandleRobots(c *gin.Context) {
http.ServeFile(c.Writer, c.Request, "static/robots.txt")
}
// sitemap
func HandleSitemap(c *gin.Context) {
http.ServeFile(c.Writer, c.Request, "static/sitemap.xml")
}
// cross domain
func HandleCrossDomain(c *gin.Context) {
http.ServeFile(c.Writer, c.Request, "static/crossdomain.xml")
}
// favicon
func HandleFavicon(c *gin.Context) {
http.ServeFile(c.Writer, c.Request, "static/favicon.ico")
}
// 服务端推送谷歌统计
func HandleBeacon(c *gin.Context) {
ua := c.Request.UserAgent()
// TODO 过滤黑名单
vals := c.Request.URL.Query()
vals.Set("v", setting.Conf.Google.V)
vals.Set("tid", setting.Conf.Google.Tid)
vals.Set("t", setting.Conf.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", setting.Conf.Google.URL, strings.NewReader(vals.Encode()))
if err != nil {
logd.Error(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 {
logd.Error(err)
return
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
logd.Error(err)
return
}
if res.StatusCode/100 != 2 {
logd.Error(string(data))
}
}()
c.String(http.StatusNoContent, "accepted")
}
// 服务端获取评论详细
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"`
}
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"`
}
func HandleDisqus(c *gin.Context) {
slug := c.Param("slug")
cursor := c.Query("cursor")
dcs := DisqusComments{}
if artc := Ei.MapArticles[slug]; artc != nil {
dcs.Data.Thread = artc.Thread
}
postsList, err := PostsList(slug, cursor)
if err != nil {
logd.Error(err)
dcs.ErrNo = FAIL
dcs.ErrMsg = "系统错误"
} else {
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: ConvertStr(v.CreatedAt),
Message: v.Message,
IsDeleted: v.IsDeleted,
}
}
}
c.JSON(http.StatusOK, dcs)
}
// 发表评论
// [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"`
}
func HandleDisqusCreate(c *gin.Context) {
resp := &DisqusCreate{}
defer c.JSON(http.StatusOK, resp)
msg := c.PostForm("message")
email := c.PostForm("author_email")
name := c.PostForm("author_name")
thread := c.PostForm("thread")
identifier := c.PostForm("identifier")
if msg == "" || email == "" || name == "" || thread == "" || identifier == "" {
resp.ErrNo = FAIL
resp.ErrMsg = "参数错误"
return
}
pc := &PostComment{
Message: msg,
Parent: c.PostForm("parent"),
Thread: thread,
AuthorEmail: email,
AuthorName: name,
Identifier: identifier,
IpAddress: c.ClientIP(),
}
postDetail, err := PostCreate(pc)
if err != nil {
logd.Error(err)
resp.ErrNo = FAIL
resp.ErrMsg = "系统错误"
return
}
err = PostApprove(postDetail.Response.Id)
if err != nil {
logd.Error(err)
resp.ErrNo = FAIL
resp.ErrMsg = "系统错误"
return
}
resp.ErrNo = SUCCESS
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: ConvertStr(postDetail.Response.CreatedAt),
Message: postDetail.Response.Message,
IsDeleted: postDetail.Response.IsDeleted,
}
}
// 渲染页面
func RenderHTMLFront(c *gin.Context, name string, data gin.H) {
var buf bytes.Buffer
err := Tmpl.ExecuteTemplate(&buf, name, data)
if err != nil {
panic(err)
}
data["LayoutContent"] = template.HTML(buf.String())
err = Tmpl.ExecuteTemplate(c.Writer, "homeLayout.html", data)
if err != nil {
panic(err)
}
c.Header("Content-Type", "text/html; charset=utf-8")
}