mirror of
https://github.com/yv1ing/MollyBlog.git
synced 2025-09-16 14:53:45 +08:00
new: Complete modules related to home page, archive page and article page
This commit is contained in:
62
internal/mApp/mApp.go
Normal file
62
internal/mApp/mApp.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package mApp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
|
||||
"MollyBlog/config"
|
||||
"MollyBlog/internal/model"
|
||||
|
||||
"github.com/88250/lute"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MApp struct {
|
||||
Host string
|
||||
Port int
|
||||
Config *config.MConfig
|
||||
|
||||
lute *lute.Lute
|
||||
engine *gin.Engine
|
||||
|
||||
Posts []*model.MPost
|
||||
TaggedPosts []*model.MPost
|
||||
CategorizedPosts []*model.MPost
|
||||
|
||||
SrcFiles []model.MFileInfo
|
||||
}
|
||||
|
||||
const (
|
||||
SRC = "_post/src" // source markdown files
|
||||
DST = "_post/dst" // destination html files
|
||||
)
|
||||
|
||||
func (ma *MApp) Run() {
|
||||
ma.loadRoutes()
|
||||
ma.loadTemplates()
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", ma.Host, ma.Port)
|
||||
err := ma.engine.Run(addr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewMApp(cfg *config.MConfig) *MApp {
|
||||
engine := gin.Default()
|
||||
engine.SetFuncMap(template.FuncMap{
|
||||
"add": func(a, b int) int {
|
||||
return a + b
|
||||
},
|
||||
})
|
||||
|
||||
return &MApp{
|
||||
Host: cfg.Host,
|
||||
Port: cfg.Port,
|
||||
Config: cfg,
|
||||
|
||||
lute: lute.New(),
|
||||
engine: engine,
|
||||
}
|
||||
}
|
||||
187
internal/mApp/mHandler.go
Normal file
187
internal/mApp/mHandler.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package mApp
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"MollyBlog/internal/model"
|
||||
"MollyBlog/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (ma *MApp) IndexHandler(ctx *gin.Context) {
|
||||
// generate recent posts
|
||||
var recentPosts []model.MPost
|
||||
for i := 0; i < utils.Min(len(ma.Posts), ma.Config.MSite.Post.RecentPost.Number); i++ {
|
||||
tmpPost := *ma.Posts[i]
|
||||
tmpPost.Date = strings.Split(tmpPost.Date, " ")[0]
|
||||
|
||||
recentPosts = append(recentPosts, tmpPost)
|
||||
}
|
||||
|
||||
resData := gin.H{
|
||||
"site_info": gin.H{
|
||||
"logo": ma.Config.MSite.Info.Logo,
|
||||
"title": ma.Config.MSite.Info.Title,
|
||||
"author": ma.Config.MSite.Info.Author,
|
||||
"language": ma.Config.MSite.Info.Language,
|
||||
"copyright": template.HTML(ma.Config.MSite.Info.Copyright),
|
||||
},
|
||||
"menu": ma.Config.MSite.Menu,
|
||||
"recent_post": gin.H{
|
||||
"title": ma.Config.MSite.Post.RecentPost.Title,
|
||||
"posts": recentPosts,
|
||||
},
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, "index.html", resData)
|
||||
}
|
||||
|
||||
func (ma *MApp) PostHandler(ctx *gin.Context) {
|
||||
postHash := ctx.Param("hash")
|
||||
|
||||
var success bool
|
||||
var html string
|
||||
var realPost model.MPost
|
||||
for _, post := range ma.Posts {
|
||||
if post.HtmlHash == postHash {
|
||||
file, err := os.OpenFile(post.HtmlPath, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
success = false
|
||||
break
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
success = false
|
||||
break
|
||||
}
|
||||
|
||||
html = string(data)
|
||||
realPost = *post
|
||||
success = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var resPost gin.H
|
||||
if success {
|
||||
resPost = gin.H{
|
||||
"title": realPost.Title,
|
||||
"cover": realPost.Cover,
|
||||
"date": realPost.Date,
|
||||
"tags": realPost.TagHashes,
|
||||
"categories": realPost.CategoryHashes,
|
||||
"content": template.HTML(html),
|
||||
}
|
||||
}
|
||||
|
||||
resData := gin.H{
|
||||
"site_info": gin.H{
|
||||
"logo": ma.Config.MSite.Info.Logo,
|
||||
"title": ma.Config.MSite.Info.Title,
|
||||
"author": ma.Config.MSite.Info.Author,
|
||||
"language": ma.Config.MSite.Info.Language,
|
||||
"copyright": template.HTML(ma.Config.MSite.Info.Copyright),
|
||||
},
|
||||
"menu": ma.Config.MSite.Menu,
|
||||
"post": resPost,
|
||||
"status": gin.H{
|
||||
"toc_title": ma.Config.MSite.Post.TocTitle,
|
||||
"success": success,
|
||||
},
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, "post.html", resData)
|
||||
}
|
||||
|
||||
func (ma *MApp) ArchiveHandler(ctx *gin.Context) {
|
||||
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
|
||||
size := ma.Config.MSite.Post.Archive.Number
|
||||
|
||||
var prePage, curPage, nxtPage, allPage int
|
||||
allPage = (len(ma.Posts) + size - 1) / size
|
||||
|
||||
if allPage > 0 {
|
||||
if page <= 0 {
|
||||
curPage = 1
|
||||
} else if page > allPage {
|
||||
curPage = allPage
|
||||
} else {
|
||||
curPage = page
|
||||
}
|
||||
} else {
|
||||
curPage = 0
|
||||
}
|
||||
|
||||
prePage = curPage - 1
|
||||
nxtPage = curPage + 1
|
||||
|
||||
if prePage <= 0 {
|
||||
prePage = curPage
|
||||
}
|
||||
|
||||
if nxtPage > allPage {
|
||||
nxtPage = allPage
|
||||
}
|
||||
|
||||
// generate recent posts
|
||||
start := (curPage - 1) * size
|
||||
offset := curPage * size
|
||||
|
||||
var historyPosts []model.MPost
|
||||
if start >= 0 {
|
||||
for i := start; i < utils.Min(len(ma.Posts), offset); i++ {
|
||||
tmpPost := *ma.Posts[i]
|
||||
tmpPost.Date = strings.Split(tmpPost.Date, " ")[0]
|
||||
historyPosts = append(historyPosts, tmpPost)
|
||||
}
|
||||
}
|
||||
|
||||
resData := gin.H{
|
||||
"site_info": gin.H{
|
||||
"logo": ma.Config.MSite.Info.Logo,
|
||||
"title": ma.Config.MSite.Info.Title,
|
||||
"author": ma.Config.MSite.Info.Author,
|
||||
"language": ma.Config.MSite.Info.Language,
|
||||
"copyright": template.HTML(ma.Config.MSite.Info.Copyright),
|
||||
},
|
||||
"menu": ma.Config.MSite.Menu,
|
||||
"page_info": gin.H{
|
||||
"pre_page": prePage,
|
||||
"cur_page": curPage,
|
||||
"nxt_page": nxtPage,
|
||||
"all_page": allPage,
|
||||
},
|
||||
"history_post": gin.H{
|
||||
"title": ma.Config.MSite.Post.Archive.Title,
|
||||
"posts": historyPosts,
|
||||
},
|
||||
}
|
||||
|
||||
ctx.HTML(http.StatusOK, "archive.html", resData)
|
||||
}
|
||||
|
||||
func (ma *MApp) UpdateBlogHandler(ctx *gin.Context) {
|
||||
var err error
|
||||
|
||||
err = ma.loadMarkdownFiles()
|
||||
if err != nil {
|
||||
_ = ctx.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = ma.parseMarkdowns()
|
||||
if err != nil {
|
||||
_ = ctx.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{"msg": "ok"})
|
||||
}
|
||||
127
internal/mApp/mMarkdown.go
Normal file
127
internal/mApp/mMarkdown.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package mApp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"MollyBlog/internal/model"
|
||||
"MollyBlog/utils"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// loadMarkdownFiles load markdown source files
|
||||
func (ma *MApp) loadMarkdownFiles() error {
|
||||
var markdownList []model.MFileInfo
|
||||
|
||||
markdownPath := SRC
|
||||
err := filepath.Walk(markdownPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// filter markdown files
|
||||
if filepath.Ext(path) == ".md" {
|
||||
markdownList = append(markdownList, model.MFileInfo{
|
||||
Name: info.Name(),
|
||||
Path: path,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ma.SrcFiles = markdownList
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseMarkdowns parse markdown files to html
|
||||
func (ma *MApp) parseMarkdowns() error {
|
||||
htmlPath := DST
|
||||
|
||||
for _, file := range ma.SrcFiles {
|
||||
// read markdown file
|
||||
_mdFile, err := os.Open(file.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer _mdFile.Close()
|
||||
|
||||
_mdByte, err := io.ReadAll(_mdFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// extract FrontMatter and clean markdown bytes
|
||||
_frontMatter, _cleanBytes := utils.ExtractFrontMatter(_mdByte)
|
||||
|
||||
// parse frontmatter to post metadata
|
||||
var post model.MPost
|
||||
err = yaml.Unmarshal(_frontMatter, &post)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert into html format and save to html file
|
||||
_htmlPath := fmt.Sprintf("%s/%s.html", htmlPath, file.Name)
|
||||
_htmlFile, err := os.Create(_htmlPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer _htmlFile.Close()
|
||||
|
||||
// set options to lute
|
||||
ma.lute.SetToC(true)
|
||||
|
||||
_htmlByte := ma.lute.Markdown(file.Name, _cleanBytes)
|
||||
_, err = _htmlFile.Write(_htmlByte)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save html path to post's metadata
|
||||
post.HtmlHash = utils.Sha256Hash(_htmlByte)
|
||||
post.HtmlPath = _htmlPath
|
||||
|
||||
// save tags and categories to map
|
||||
for _, tag := range post.Tags {
|
||||
tagHash := utils.Sha256Hash([]byte(tag))
|
||||
post.TagHashes = append(post.TagHashes, model.MTag{
|
||||
Name: tag,
|
||||
Hash: tagHash,
|
||||
})
|
||||
|
||||
ma.TaggedPosts = append(ma.TaggedPosts, &post)
|
||||
}
|
||||
|
||||
for _, category := range post.Categories {
|
||||
categoryHash := utils.Sha256Hash([]byte(category))
|
||||
post.CategoryHashes = append(post.CategoryHashes, model.MCategory{
|
||||
Name: category,
|
||||
Hash: categoryHash,
|
||||
})
|
||||
|
||||
ma.CategorizedPosts = append(ma.CategorizedPosts, &post)
|
||||
}
|
||||
|
||||
// free the raw tag and category slice
|
||||
post.Tags = nil
|
||||
post.Categories = nil
|
||||
|
||||
ma.Posts = append(ma.Posts, &post)
|
||||
}
|
||||
|
||||
// sort Posts by date
|
||||
model.SortPostsByDate(ma.Posts)
|
||||
return nil
|
||||
}
|
||||
9
internal/mApp/mRouter.go
Normal file
9
internal/mApp/mRouter.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package mApp
|
||||
|
||||
func (ma *MApp) loadRoutes() {
|
||||
ma.engine.GET("/", ma.IndexHandler)
|
||||
ma.engine.GET("/archive", ma.ArchiveHandler)
|
||||
ma.engine.GET("/post/:hash", ma.PostHandler)
|
||||
|
||||
ma.engine.PUT("/update", ma.UpdateBlogHandler)
|
||||
}
|
||||
11
internal/mApp/mTemplate.go
Normal file
11
internal/mApp/mTemplate.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package mApp
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (ma *MApp) loadTemplates() {
|
||||
assetsPath := fmt.Sprintf("%s/assets", ma.Config.Template)
|
||||
htmlPath := fmt.Sprintf("%s/html/*.html", ma.Config.Template)
|
||||
|
||||
ma.engine.Static("/assets", assetsPath)
|
||||
ma.engine.LoadHTMLGlob(htmlPath)
|
||||
}
|
||||
Reference in New Issue
Block a user