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:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,6 +1,9 @@
|
|||||||
# If you prefer the allow list template instead of the deny list, see community template:
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
#
|
|
||||||
|
# Work dir and files
|
||||||
|
.idea
|
||||||
|
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
*.exe~
|
*.exe~
|
||||||
|
|||||||
30
_post/dst/test.md.html
Normal file
30
_post/dst/test.md.html
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<!--more-->
|
||||||
|
<h1 id="Heading-level-1">Heading level 1</h1>
|
||||||
|
<h2 id="Heading-level-2">Heading level 2</h2>
|
||||||
|
<h3 id="Heading-level-3">Heading level 3</h3>
|
||||||
|
<h4 id="Heading-level-4">Heading level 4</h4>
|
||||||
|
<h5 id="Heading-level-5">Heading level 5</h5>
|
||||||
|
<h6 id="Heading-level-6">Heading level 6</h6>
|
||||||
|
<p>I just love <strong>bold text</strong>.</p>
|
||||||
|
<blockquote>
|
||||||
|
<p>Dorothy followed her through many of the beautiful rooms in her castle.</p>
|
||||||
|
</blockquote>
|
||||||
|
<ol>
|
||||||
|
<li>First item</li>
|
||||||
|
<li>Second item</li>
|
||||||
|
<li>Third item</li>
|
||||||
|
<li>Fourth item</li>
|
||||||
|
</ol>
|
||||||
|
<ul>
|
||||||
|
<li>First item</li>
|
||||||
|
<li>Second item</li>
|
||||||
|
<li>Third item</li>
|
||||||
|
<li>Fourth item</li>
|
||||||
|
</ul>
|
||||||
|
<pre><code class="language-go highlight-chroma"><span class="highlight-line"><span class="highlight-cl"><span class="highlight-kd">func</span> <span class="highlight-nf">main</span><span class="highlight-p">()</span> <span class="highlight-p">{</span>
|
||||||
|
</span></span><span class="highlight-line"><span class="highlight-cl"> <span class="highlight-nx">fmt</span><span class="highlight-p">.</span><span class="highlight-nf">Println</span><span class="highlight-p">(</span><span class="highlight-s">"Hello, World"</span><span class="highlight-p">)</span>
|
||||||
|
</span></span><span class="highlight-line"><span class="highlight-cl"><span class="highlight-p">}</span>
|
||||||
|
</span></span></code></pre>
|
||||||
|
<hr />
|
||||||
|
<p>This is a link: <a href="https://markdown.com.cn">Markdown link</a>。</p>
|
||||||
|
<p><img src="https://markdown.com.cn/assets/img/philly-magic-garden.9c0b4415.jpg" alt="Image" title="Magic Gardens" /></p>
|
||||||
50
_post/src/test.md
Normal file
50
_post/src/test.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
title: Example Markdown
|
||||||
|
date: 2024-12-16 12:33:04
|
||||||
|
tags: [Example, Markdown]
|
||||||
|
categories:
|
||||||
|
- Example
|
||||||
|
cover: https://markdown.com.cn/assets/img/philly-magic-garden.9c0b4415.jpg
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
# Heading level 1
|
||||||
|
|
||||||
|
## Heading level 2
|
||||||
|
|
||||||
|
### Heading level 3
|
||||||
|
|
||||||
|
#### Heading level 4
|
||||||
|
|
||||||
|
##### Heading level 5
|
||||||
|
|
||||||
|
###### Heading level 6
|
||||||
|
|
||||||
|
I just love **bold text**.
|
||||||
|
|
||||||
|
> Dorothy followed her through many of the beautiful rooms in her castle.
|
||||||
|
|
||||||
|
1. First item
|
||||||
|
2. Second item
|
||||||
|
3. Third item
|
||||||
|
4. Fourth item
|
||||||
|
|
||||||
|
- First item
|
||||||
|
- Second item
|
||||||
|
- Third item
|
||||||
|
- Fourth item
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Hello, World")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
This is a link: [Markdown link](https://markdown.com.cn)。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
37
config.yaml
Normal file
37
config.yaml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# app config #
|
||||||
|
host: 0.0.0.0
|
||||||
|
port: 8080
|
||||||
|
template: templates/default
|
||||||
|
|
||||||
|
# site config #
|
||||||
|
site:
|
||||||
|
# basic info
|
||||||
|
info:
|
||||||
|
logo: /assets/img/logo.png
|
||||||
|
title: Molly Blog
|
||||||
|
author: yvling
|
||||||
|
language: en
|
||||||
|
copyright: Copyright © 2024 Powered by <a href="https://github.com/yv1ing/MollyBlog">MollyBlog</a>
|
||||||
|
|
||||||
|
# menu config
|
||||||
|
menu:
|
||||||
|
items:
|
||||||
|
- name: Home
|
||||||
|
icon: fa-solid fa-house
|
||||||
|
url: /
|
||||||
|
- name: Archive
|
||||||
|
icon: fa-solid fa-box-archive
|
||||||
|
url: /archive
|
||||||
|
- name: About
|
||||||
|
icon: fa-solid fa-circle-info
|
||||||
|
url: /about
|
||||||
|
|
||||||
|
# post config
|
||||||
|
post:
|
||||||
|
toc_title: Content
|
||||||
|
recent_post:
|
||||||
|
title: Recent Posts
|
||||||
|
number: 10
|
||||||
|
archive:
|
||||||
|
title: Archive
|
||||||
|
number: 10
|
||||||
37
config/mConfig.go
Normal file
37
config/mConfig.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MConfig struct {
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Template string `yaml:"template"`
|
||||||
|
|
||||||
|
MSite mSite `yaml:"site"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var MConfigInstance *MConfig
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
configFile, err := os.OpenFile("config.yaml", os.O_RDONLY, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer configFile.Close()
|
||||||
|
|
||||||
|
configByte, err := io.ReadAll(configFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(configByte, &MConfigInstance)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
config/mInfo.go
Normal file
9
config/mInfo.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type mInfo struct {
|
||||||
|
Logo string `yaml:"logo"`
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
Author string `yaml:"author"`
|
||||||
|
Language string `yaml:"language"`
|
||||||
|
Copyright string `yaml:"copyright"`
|
||||||
|
}
|
||||||
11
config/mMenu.go
Normal file
11
config/mMenu.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type mMenu struct {
|
||||||
|
Items []mItem `yaml:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type mItem struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Icon string `yaml:"icon"`
|
||||||
|
Url string `yaml:"url"`
|
||||||
|
}
|
||||||
17
config/mPost.go
Normal file
17
config/mPost.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type mPost struct {
|
||||||
|
TocTitle string `yaml:"toc_title"`
|
||||||
|
RecentPost mRecentPost `yaml:"recent_post"`
|
||||||
|
Archive mArchive `yaml:"archive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type mRecentPost struct {
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
Number int `yaml:"number"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type mArchive struct {
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
Number int `yaml:"number"`
|
||||||
|
}
|
||||||
7
config/mSite.go
Normal file
7
config/mSite.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type mSite struct {
|
||||||
|
Info mInfo `yaml:"info"`
|
||||||
|
Menu mMenu `yaml:"menu"`
|
||||||
|
Post mPost `yaml:"post"`
|
||||||
|
}
|
||||||
40
go.mod
Normal file
40
go.mod
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
module MollyBlog
|
||||||
|
|
||||||
|
go 1.23
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/88250/lute v1.7.6
|
||||||
|
github.com/gin-gonic/gin v1.10.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alecthomas/chroma v0.10.0 // indirect
|
||||||
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
|
github.com/dlclark/regexp2 v1.8.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
|
golang.org/x/net v0.25.0 // indirect
|
||||||
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
|
golang.org/x/text v0.15.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
|
)
|
||||||
98
go.sum
Normal file
98
go.sum
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
github.com/88250/lute v1.7.6 h1:8F4i53POAR+oEkyClsdHYPU1A7Z8rdxfRfjarD+FL9w=
|
||||||
|
github.com/88250/lute v1.7.6/go.mod h1:+wUqx/1kdFDbWtxn9LYJlaCOAeol2pjSO6w+WJTVQsg=
|
||||||
|
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||||
|
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||||
|
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||||
|
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||||
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
|
github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0=
|
||||||
|
github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||||
|
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||||
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
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)
|
||||||
|
}
|
||||||
6
internal/model/mCategory.go
Normal file
6
internal/model/mCategory.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type MCategory struct {
|
||||||
|
Name string `yaml:"name" json:"name"`
|
||||||
|
Hash string `yaml:"hash" json:"hash"`
|
||||||
|
}
|
||||||
7
internal/model/mFileInfo.go
Normal file
7
internal/model/mFileInfo.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
// MFileInfo basic file info
|
||||||
|
type MFileInfo struct {
|
||||||
|
Name string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
49
internal/model/mPost.go
Normal file
49
internal/model/mPost.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MPost post metadata
|
||||||
|
type MPost struct {
|
||||||
|
Title string `yaml:"title" json:"title"`
|
||||||
|
Cover string `yaml:"cover" json:"cover"`
|
||||||
|
Date string `yaml:"date" json:"date"`
|
||||||
|
|
||||||
|
Tags []string `yaml:"tags" json:"tags"`
|
||||||
|
TagHashes []MTag `yaml:"tag_hashes" json:"tag_hashes"`
|
||||||
|
|
||||||
|
Categories []string `yaml:"categories" json:"categories"`
|
||||||
|
CategoryHashes []MCategory `yaml:"category_hashes" json:"category_hashes"`
|
||||||
|
|
||||||
|
HtmlHash string `yaml:"htmlHash" json:"html_hash"`
|
||||||
|
HtmlPath string `yaml:"htmlPath" json:"html_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MPostSlice []*MPost
|
||||||
|
|
||||||
|
func (a MPostSlice) Len() int {
|
||||||
|
return len(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a MPostSlice) Swap(i, j int) {
|
||||||
|
a[i], a[j] = a[j], a[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a MPostSlice) Less(i, j int) bool {
|
||||||
|
timeFormat := "2006-01-02 15:04:05"
|
||||||
|
t1, err1 := time.Parse(timeFormat, a[i].Date)
|
||||||
|
t2, err2 := time.Parse(timeFormat, a[j].Date)
|
||||||
|
|
||||||
|
if err1 != nil || err2 != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return t1.After(t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SortPostsByDate(Posts []*MPost) []*MPost {
|
||||||
|
sort.Sort(MPostSlice(Posts))
|
||||||
|
return Posts
|
||||||
|
}
|
||||||
6
internal/model/mTag.go
Normal file
6
internal/model/mTag.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type MTag struct {
|
||||||
|
Name string `yaml:"name" json:"name"`
|
||||||
|
Hash string `yaml:"hash" json:"hash"`
|
||||||
|
}
|
||||||
32
main.go
Normal file
32
main.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"MollyBlog/config"
|
||||||
|
"MollyBlog/internal/mApp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_, err := os.Stat(mApp.SRC)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = os.MkdirAll(mApp.SRC, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat(mApp.DST)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
err = os.MkdirAll(mApp.DST, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mapp := mApp.NewMApp(config.MConfigInstance)
|
||||||
|
mapp.Run()
|
||||||
|
}
|
||||||
25
templates/default/assets/css/archive.css
Normal file
25
templates/default/assets/css/archive.css
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
.history-post-title {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-post-item {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-post-item-wrap {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-post-item-date {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-post-pagination-item {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
97
templates/default/assets/css/global.css
Normal file
97
templates/default/assets/css/global.css
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
:root{
|
||||||
|
/* colors */
|
||||||
|
--primary-color: #8589e0;
|
||||||
|
--secondary-color: #b2cbd3;
|
||||||
|
|
||||||
|
--primary-text-color: #c9cacc;
|
||||||
|
--secondary-text-color: #9c9c9c;
|
||||||
|
|
||||||
|
--background-color: #191c1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'jetbrains-mono-medium';
|
||||||
|
src: url('../../assets/css/webfonts/jetbrains-mono-medium.woff2') format('woff2');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
/*font-family: jetbrains-mono-medium, serif;*/
|
||||||
|
background-color: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a, a:link, a:visited, a:active {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: var(--primary-color);
|
||||||
|
text-decoration-thickness: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-icon {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 576px){
|
||||||
|
.root-container {
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-logo {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-logo-img {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-logo-txt {
|
||||||
|
width: fit-content;
|
||||||
|
height: 40px;
|
||||||
|
line-height: 40px;
|
||||||
|
font-size: 36px;
|
||||||
|
padding-top: 2px;
|
||||||
|
margin-left: 10px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu-link {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
20
templates/default/assets/css/index.css
Normal file
20
templates/default/assets/css/index.css
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
.recent-post-title {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-item {
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-item-wrap {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-post-item-date {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
6
templates/default/assets/css/lib/bootstrap.min.css
vendored
Normal file
6
templates/default/assets/css/lib/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
templates/default/assets/css/lib/bootstrap.min.css.map
Normal file
1
templates/default/assets/css/lib/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
9
templates/default/assets/css/lib/fontawesome.all.min.css
vendored
Normal file
9
templates/default/assets/css/lib/fontawesome.all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
48
templates/default/assets/css/lib/xcode-dark.css
Normal file
48
templates/default/assets/css/lib/xcode-dark.css
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/* Background */ .highlight-bg { color: #ffffff; background-color: #1f1f24 }
|
||||||
|
/* PreWrapper */ .highlight-chroma { color: #ffffff; background-color: #1f1f24; }
|
||||||
|
/* Error */ .highlight-chroma .highlight-err { color: #960050 }
|
||||||
|
/* LineTableTD */ .highlight-chroma .highlight-lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
|
||||||
|
/* LineTable */ .highlight-chroma .highlight-lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
|
||||||
|
/* LineHighlight */ .highlight-chroma .highlight-hl { background-color: #353539 }
|
||||||
|
/* LineNumbersTable */ .highlight-chroma .highlight-lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||||
|
/* LineNumbers */ .highlight-chroma .highlight-ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||||
|
/* Line */ .highlight-chroma .highlight-line { display: flex; }
|
||||||
|
/* Keyword */ .highlight-chroma .highlight-k { color: #fc5fa3 }
|
||||||
|
/* KeywordConstant */ .highlight-chroma .highlight-kc { color: #fc5fa3 }
|
||||||
|
/* KeywordDeclaration */ .highlight-chroma .highlight-kd { color: #fc5fa3 }
|
||||||
|
/* KeywordNamespace */ .highlight-chroma .highlight-kn { color: #fc5fa3 }
|
||||||
|
/* KeywordPseudo */ .highlight-chroma .highlight-kp { color: #fc5fa3 }
|
||||||
|
/* KeywordReserved */ .highlight-chroma .highlight-kr { color: #fc5fa3 }
|
||||||
|
/* KeywordType */ .highlight-chroma .highlight-kt { color: #fc5fa3 }
|
||||||
|
/* NameBuiltin */ .highlight-chroma .highlight-nb { color: #d0a8ff }
|
||||||
|
/* NameBuiltinPseudo */ .highlight-chroma .highlight-bp { color: #a167e6 }
|
||||||
|
/* NameClass */ .highlight-chroma .highlight-nc { color: #5dd8ff }
|
||||||
|
/* NameFunction */ .highlight-chroma .highlight-nf { color: #41a1c0 }
|
||||||
|
/* NameVariable */ .highlight-chroma .highlight-nv { color: #41a1c0 }
|
||||||
|
/* LiteralString */ .highlight-chroma .highlight-s { color: #fc6a5d }
|
||||||
|
/* LiteralStringAffix */ .highlight-chroma .highlight-sa { color: #fc6a5d }
|
||||||
|
/* LiteralStringBacktick */ .highlight-chroma .highlight-sb { color: #fc6a5d }
|
||||||
|
/* LiteralStringChar */ .highlight-chroma .highlight-sc { color: #fc6a5d }
|
||||||
|
/* LiteralStringDelimiter */ .highlight-chroma .highlight-dl { color: #fc6a5d }
|
||||||
|
/* LiteralStringDoc */ .highlight-chroma .highlight-sd { color: #fc6a5d }
|
||||||
|
/* LiteralStringDouble */ .highlight-chroma .highlight-s2 { color: #fc6a5d }
|
||||||
|
/* LiteralStringEscape */ .highlight-chroma .highlight-se { color: #fc6a5d }
|
||||||
|
/* LiteralStringHeredoc */ .highlight-chroma .highlight-sh { color: #fc6a5d }
|
||||||
|
/* LiteralStringOther */ .highlight-chroma .highlight-sx { color: #fc6a5d }
|
||||||
|
/* LiteralStringRegex */ .highlight-chroma .highlight-sr { color: #fc6a5d }
|
||||||
|
/* LiteralStringSingle */ .highlight-chroma .highlight-s1 { color: #fc6a5d }
|
||||||
|
/* LiteralStringSymbol */ .highlight-chroma .highlight-ss { color: #fc6a5d }
|
||||||
|
/* LiteralNumber */ .highlight-chroma .highlight-m { color: #d0bf69 }
|
||||||
|
/* LiteralNumberBin */ .highlight-chroma .highlight-mb { color: #d0bf69 }
|
||||||
|
/* LiteralNumberFloat */ .highlight-chroma .highlight-mf { color: #d0bf69 }
|
||||||
|
/* LiteralNumberHex */ .highlight-chroma .highlight-mh { color: #d0bf69 }
|
||||||
|
/* LiteralNumberInteger */ .highlight-chroma .highlight-mi { color: #d0bf69 }
|
||||||
|
/* LiteralNumberIntegerLong */ .highlight-chroma .highlight-il { color: #d0bf69 }
|
||||||
|
/* LiteralNumberOct */ .highlight-chroma .highlight-mo { color: #d0bf69 }
|
||||||
|
/* Comment */ .highlight-chroma .highlight-c { color: #6c7986 }
|
||||||
|
/* CommentHashbang */ .highlight-chroma .highlight-ch { color: #6c7986 }
|
||||||
|
/* CommentMultiline */ .highlight-chroma .highlight-cm { color: #6c7986 }
|
||||||
|
/* CommentSingle */ .highlight-chroma .highlight-c1 { color: #6c7986 }
|
||||||
|
/* CommentSpecial */ .highlight-chroma .highlight-cs { color: #6c7986; font-style: italic }
|
||||||
|
/* CommentPreproc */ .highlight-chroma .highlight-cp { color: #fd8f3f }
|
||||||
|
/* CommentPreprocFile */ .highlight-chroma .highlight-cpf { color: #fd8f3f }
|
||||||
94
templates/default/assets/css/post.css
Normal file
94
templates/default/assets/css/post.css
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
pre {
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: var(--secondary-text-color) 1px dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-menu {
|
||||||
|
max-width: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-title {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-info {
|
||||||
|
max-width: 1000px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-info-wrap {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-info-item {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content {
|
||||||
|
line-height: 1.6;
|
||||||
|
max-width: 1000px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content h1,
|
||||||
|
.post-content h2 {
|
||||||
|
margin-top: 4rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content h3,
|
||||||
|
.post-content h4 {
|
||||||
|
margin-top: 3rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content h5,
|
||||||
|
.post-content h6 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content img {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-footer {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 2rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc-box-area {
|
||||||
|
position: fixed;
|
||||||
|
top: 4rem;
|
||||||
|
left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc-box-menu {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: none;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-box-menu-item {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc-title {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc-box li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
BIN
templates/default/assets/css/webfonts/fa-brands-400.ttf
Normal file
BIN
templates/default/assets/css/webfonts/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
templates/default/assets/css/webfonts/fa-brands-400.woff2
Normal file
BIN
templates/default/assets/css/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
templates/default/assets/css/webfonts/fa-regular-400.ttf
Normal file
BIN
templates/default/assets/css/webfonts/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
templates/default/assets/css/webfonts/fa-regular-400.woff2
Normal file
BIN
templates/default/assets/css/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
templates/default/assets/css/webfonts/fa-solid-900.ttf
Normal file
BIN
templates/default/assets/css/webfonts/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
templates/default/assets/css/webfonts/fa-solid-900.woff2
Normal file
BIN
templates/default/assets/css/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
templates/default/assets/css/webfonts/fa-v4compatibility.ttf
Normal file
BIN
templates/default/assets/css/webfonts/fa-v4compatibility.ttf
Normal file
Binary file not shown.
BIN
templates/default/assets/css/webfonts/fa-v4compatibility.woff2
Normal file
BIN
templates/default/assets/css/webfonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
BIN
templates/default/assets/img/logo.png
Normal file
BIN
templates/default/assets/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
7
templates/default/assets/js/lib/bootstrap.min.js
vendored
Normal file
7
templates/default/assets/js/lib/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
templates/default/assets/js/lib/bootstrap.min.js.map
Normal file
1
templates/default/assets/js/lib/bootstrap.min.js.map
Normal file
File diff suppressed because one or more lines are too long
6
templates/default/assets/js/lib/fontawesome.all.min.js
vendored
Normal file
6
templates/default/assets/js/lib/fontawesome.all.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
templates/default/assets/js/lib/jquery.min.js
vendored
Normal file
2
templates/default/assets/js/lib/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
51
templates/default/assets/js/post.js
Normal file
51
templates/default/assets/js/post.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
const tocBox = document.getElementById('toc-box');
|
||||||
|
|
||||||
|
const tList = document.getElementById('post-content').querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||||
|
const hList = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
|
||||||
|
|
||||||
|
let tmpHtml = `<div>\n`;
|
||||||
|
|
||||||
|
Array.from(tList, v => {
|
||||||
|
// get the level number
|
||||||
|
const H = hList.indexOf(v.nodeName) + 1 || 1;
|
||||||
|
tmpHtml += `<div class="li li-${H} toc-li"><a id="${v.id}" href="#">${v.textContent}</a></div>\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
tmpHtml += `</div>`
|
||||||
|
tocBox.innerHTML = tmpHtml;
|
||||||
|
|
||||||
|
Array.from(tList, v => {
|
||||||
|
const btn = document.getElementById('toc-box').querySelector(`#${v.id}`);
|
||||||
|
const ele = document.getElementById('post-content').querySelector(`#${v.id}`);
|
||||||
|
|
||||||
|
if (!btn || !ele) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
window.scrollTo({ top: ele.offsetTop - 60, behavior: 'smooth' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const tocMenu = document.getElementById('toc-box-menu');
|
||||||
|
|
||||||
|
window.addEventListener('scroll', (event) => {
|
||||||
|
const scrollFromTop = window.scrollY || document.documentElement.scrollTop;
|
||||||
|
if (scrollFromTop >= 100) {
|
||||||
|
tocMenu.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
tocMenu.style.display = 'none';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function goto(path) {
|
||||||
|
window.location.href = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
function backToTop() {
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth'
|
||||||
|
})
|
||||||
|
}
|
||||||
107
templates/default/html/archive.html
Normal file
107
templates/default/html/archive.html
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
{{ define "archive.html" }}
|
||||||
|
|
||||||
|
<html lang="{{ .site_info.language }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ .site_info.title }}</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="{{ .site_info.logo }}"/>
|
||||||
|
<link rel="stylesheet" href="../assets/css/lib/fontawesome.all.min.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/lib/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/global.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/archive.css">
|
||||||
|
<script src="../assets/js/lib/jquery.min.js"></script>
|
||||||
|
<script src="../assets/js/lib/bootstrap.min.js"></script>
|
||||||
|
<script src="../assets/js/lib/fontawesome.all.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="root-container">
|
||||||
|
<!-- header -->
|
||||||
|
<div class="container p-3">
|
||||||
|
<div class="row pt-3 pt-md-4 pt-lg-5">
|
||||||
|
<div class="col main-logo">
|
||||||
|
<div class="main-logo-img" style="background-image: url('{{ .site_info.logo }}');"></div>
|
||||||
|
<div class="main-logo-txt">
|
||||||
|
{{ .site_info.title }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- menu -->
|
||||||
|
<div class="container p-3">
|
||||||
|
<div class="row pt-lg-3">
|
||||||
|
<div class="col mx-auto post-menu">
|
||||||
|
{{ range $i, $v := .menu.Items }}
|
||||||
|
<i class="{{ $v.Icon }} m-icon"></i>
|
||||||
|
<a class="main-menu-link" href="{{ $v.Url }}"> {{ $v.Name }} </a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- body -->
|
||||||
|
<div class="container p-3">
|
||||||
|
<div class="row pt-lg-3">
|
||||||
|
<h4 class="history-post-title"># {{ .history_post.title }}</h4>
|
||||||
|
<div class="col-12 col-md-8">
|
||||||
|
<div>
|
||||||
|
{{ range $i, $v := .history_post.posts }}
|
||||||
|
<div class="row history-post-item">
|
||||||
|
<div class="col history-post-item-wrap">
|
||||||
|
<span class="d-none d-md-inline history-post-item-date">{{ $v.Date }} | </span>
|
||||||
|
<span>
|
||||||
|
<a href="/post/{{ $v.HtmlHash }}">{{ $v.Title }}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col d-none d-lg-block history-post-item-wrap">
|
||||||
|
<span style="color: var(--secondary-text-color)">
|
||||||
|
[
|
||||||
|
{{ range $i2, $v2 := $v.TagHashes }}
|
||||||
|
<a href="/tag/{{ $v2.Hash }}" style="color: var(--secondary-text-color)">{{ $v2.Name }}</a>
|
||||||
|
{{ if lt (add $i2 1) (len $v.TagHashes) }}
|
||||||
|
,
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
]
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row pt-3 pt-lg-4">
|
||||||
|
<div class="col-12 col-md-8">
|
||||||
|
<div class="history-post-pagination">
|
||||||
|
<span class="history-post-pagination-item">
|
||||||
|
<a href="/archive?page={{ .page_info.pre_page }}">
|
||||||
|
<
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="history-post-pagination-item">
|
||||||
|
{{ .page_info.cur_page }} / {{ .page_info.all_page }}
|
||||||
|
</span>
|
||||||
|
<span class="history-post-pagination-item">
|
||||||
|
<a href="/archive?page={{ .page_info.nxt_page }}">
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- footer -->
|
||||||
|
<div class="footer">
|
||||||
|
{{ .site_info.copyright }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
86
templates/default/html/index.html
Normal file
86
templates/default/html/index.html
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{{ define "index.html" }}
|
||||||
|
|
||||||
|
<html lang="{{ .site_info.language }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ .site_info.title }}</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="{{ .site_info.logo }}"/>
|
||||||
|
<link rel="stylesheet" href="../assets/css/lib/fontawesome.all.min.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/lib/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/global.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/index.css">
|
||||||
|
<script src="../assets/js/lib/jquery.min.js"></script>
|
||||||
|
<script src="../assets/js/lib/bootstrap.min.js"></script>
|
||||||
|
<script src="../assets/js/lib/fontawesome.all.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="root-container">
|
||||||
|
<!-- header -->
|
||||||
|
<div class="container p-3">
|
||||||
|
<div class="row pt-3 pt-md-4 pt-lg-5">
|
||||||
|
<div class="col main-logo">
|
||||||
|
<div class="main-logo-img" style="background-image: url('{{ .site_info.logo }}');"></div>
|
||||||
|
<div class="main-logo-txt">
|
||||||
|
{{ .site_info.title }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- menu -->
|
||||||
|
<div class="container p-3">
|
||||||
|
<div class="row pt-lg-3">
|
||||||
|
<div class="col mx-auto post-menu">
|
||||||
|
{{ range $i, $v := .menu.Items }}
|
||||||
|
<i class="{{ $v.Icon }} m-icon"></i>
|
||||||
|
<a class="main-menu-link" href="{{ $v.Url }}"> {{ $v.Name }} </a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- body -->
|
||||||
|
<div class="container p-3">
|
||||||
|
<div class="row pt-lg-3">
|
||||||
|
<h4 class="recent-post-title"># {{ .recent_post.title }}</h4>
|
||||||
|
<div class="col-12 col-md-8">
|
||||||
|
<div>
|
||||||
|
{{ range $i, $v := .recent_post.posts }}
|
||||||
|
<div class="row recent-post-item">
|
||||||
|
<div class="col recent-post-item-wrap">
|
||||||
|
<span class="d-none d-md-inline recent-post-item-date">{{ $v.Date }} | </span>
|
||||||
|
<span>
|
||||||
|
<a href="/post/{{ $v.HtmlHash }}">{{ $v.Title }}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="col d-none d-lg-block recent-post-item-wrap">
|
||||||
|
<span style="color: var(--secondary-text-color)">
|
||||||
|
[
|
||||||
|
{{ range $i2, $v2 := $v.TagHashes }}
|
||||||
|
<a href="/tag/{{ $v2.Hash }}" style="color: var(--secondary-text-color)">{{ $v2.Name }}</a>
|
||||||
|
{{ if lt (add $i2 1) (len $v.TagHashes) }}
|
||||||
|
,
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
]
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- footer -->
|
||||||
|
<div class="footer">
|
||||||
|
{{ .site_info.copyright }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
122
templates/default/html/post.html
Normal file
122
templates/default/html/post.html
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
{{ define "post.html" }}
|
||||||
|
|
||||||
|
<html lang="{{ .site_info.language }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{ .site_info.title }}</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="{{ .site_info.logo }}"/>
|
||||||
|
<link rel="stylesheet" href="../assets/css/lib/fontawesome.all.min.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/lib/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/lib/xcode-dark.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/global.css">
|
||||||
|
<link rel="stylesheet" href="../assets/css/post.css">
|
||||||
|
<script src="../assets/js/lib/jquery.min.js"></script>
|
||||||
|
<script src="../assets/js/lib/bootstrap.min.js"></script>
|
||||||
|
<script src="../assets/js/lib/fontawesome.all.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="root-container">
|
||||||
|
|
||||||
|
<!-- menu -->
|
||||||
|
<div class="container p-3">
|
||||||
|
<div class="row pt-3 pt-md-4 pt-lg-5">
|
||||||
|
<div class="col-12 col-md-9 mx-auto post-menu">
|
||||||
|
{{ range $i, $v := .menu.Items }}
|
||||||
|
<i class="{{ $v.Icon }} m-icon"></i>
|
||||||
|
<a class="main-menu-link" href="{{ $v.Url }}"> {{ $v.Name }} </a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- toc area -->
|
||||||
|
<div class="d-none d-lg-block" id="toc-box-area">
|
||||||
|
<div id="toc-title">
|
||||||
|
<h5>{{ .status.toc_title }}</h5>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div id="toc-box"></div>
|
||||||
|
<div id="toc-box-menu">
|
||||||
|
<hr/>
|
||||||
|
{{ range $i, $v := .menu.Items }}
|
||||||
|
<span class="toc-box-menu-item">
|
||||||
|
<i class="{{ $v.Icon }} m-icon" onclick="goto('{{ $v.Url }}')"></i>
|
||||||
|
</span>
|
||||||
|
{{ end }}
|
||||||
|
<span class="toc-box-menu-item" onclick="backToTop()">
|
||||||
|
<i class="fa-solid fa-circle-up"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- body -->
|
||||||
|
{{ if .status.success }}
|
||||||
|
<div class="container p-3">
|
||||||
|
<div class="row pt-lg-3">
|
||||||
|
<div class="col-12 col-md-9 post-info mx-auto">
|
||||||
|
<h2 class="post-title">{{ .post.title }}</h2>
|
||||||
|
<div>
|
||||||
|
<span class="post-info-wrap">
|
||||||
|
<span class="post-info-item">
|
||||||
|
{{ .post.date }}
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="post-info-wrap">
|
||||||
|
{{ $tags := .post.tags }}
|
||||||
|
{{ if gt (len $tags) 0 }}
|
||||||
|
<span class="post-info-item">
|
||||||
|
<i class="fa-solid fa-tags"></i>
|
||||||
|
{{ range $i, $v := $tags }}
|
||||||
|
<a href="/tag/{{ $v.Hash }}">{{ $v.Name }}</a>
|
||||||
|
{{ if lt (add $i 1) (len $tags) }},{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</span>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ $categories := .post.categories }}
|
||||||
|
{{ if gt (len $categories) 0 }}
|
||||||
|
<span class="post-info-item">
|
||||||
|
<i class="fa-solid fa-folder"></i>
|
||||||
|
{{ range $i, $v := $categories }}
|
||||||
|
<a href="/category/{{ $v.Hash }}">{{ $v.Name }}</a>
|
||||||
|
{{ if lt (add $i 1) (len $categories) }},{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</span>
|
||||||
|
{{ end }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row pt-5">
|
||||||
|
<div class="col-12 col-md-9 post-content mx-auto" id="post-content">
|
||||||
|
{{ .post.content }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<!-- post footer -->
|
||||||
|
{{ if .status.success }}
|
||||||
|
<div class="post-footer">
|
||||||
|
{{ .site_info.copyright }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- post footer -->
|
||||||
|
{{ if not .status.success }}
|
||||||
|
<div class="footer">
|
||||||
|
{{ .site_info.copyright }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<script src="../assets/js/post.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
11
utils/hash.go
Normal file
11
utils/hash.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sha256Hash(data []byte) string {
|
||||||
|
hash := sha256.Sum256(data)
|
||||||
|
return hex.EncodeToString(hash[:])
|
||||||
|
}
|
||||||
9
utils/markdown.go
Normal file
9
utils/markdown.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
// ExtractFrontMatter Extract markdown header information and return clean text
|
||||||
|
func ExtractFrontMatter(data []byte) ([]byte, []byte) {
|
||||||
|
re := regexp.MustCompile(`(?s)^\s*---\s*\n.*?\n\s*---\s*\n`)
|
||||||
|
return re.FindSubmatch(data)[0], re.ReplaceAll(data, nil)
|
||||||
|
}
|
||||||
9
utils/number.go
Normal file
9
utils/number.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
func Min(a, b int) int {
|
||||||
|
if a <= b {
|
||||||
|
return a
|
||||||
|
} else {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user