初始化项目

This commit is contained in:
lianglianglee 2023-08-03 10:29:01 +08:00
parent c0eba18e88
commit b8489d4629
17 changed files with 9487 additions and 0 deletions

4
.gitignore vendored
View File

@ -1,3 +1,7 @@
.idea
.vscode
go.sum
# 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
# #

16
README.md Normal file
View File

@ -0,0 +1,16 @@
# 项目介绍
本项目初衷是为了实现markdown的文档的展示
# 实现方式
该项目使用gin实现markdown使用`gomarkdown/markdown`渲染成html代码块。由templates渲染到页面中
# 配置
```yaml
server:
port: 8081
dir:
root: \Gitlab\note
main: 专栏
```

5
config/application.yaml Normal file
View File

@ -0,0 +1,5 @@
server:
port: 8081
dir:
root: \Gitlab\note
main: PDF

View File

@ -0,0 +1,60 @@
package controller
import (
"gin-demo/response"
"gin-demo/util"
"os"
"strings"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
type CategoryController struct {
}
func MarkdownLoad() CategoryController {
return CategoryController{}
}
func (c CategoryController) Show(ctx *gin.Context) {
urlPath := ""
urlPath = ctx.Request.URL.Path
//fmt.Println(urlPath)
if strings.HasPrefix(urlPath, "/static") {
if _, err := os.Stat("." + urlPath); err == nil {
ctx.File("." + urlPath)
ctx.Header("Cache-Control", "public,s-maxage=300,max-age=31536000")
ctx.Abort() // file found, stop the handler chain
return
}
}
rootDir := viper.GetString("dir.root")
path := rootDir + urlPath
pathInfo, err := os.Stat(path)
listResult := util.GetFileList(path)
if err != nil {
response.Fail(ctx, gin.H{})
}
if pathInfo.IsDir() {
if urlPath == "/" {
path = path + viper.GetString("dir.main")
}
htmlContext := util.GetDirStr(path)
//fmt.Println(htmlContext)
response.Success(ctx, gin.H{"title": pathInfo.Name(), "htmlContext": htmlContext, "list": listResult})
} else if strings.HasSuffix(pathInfo.Name(), ".md") {
// 后缀判断
htmlContext := util.GetMdStr(path)
//fmt.Println(htmlContext)
title := pathInfo.Name()
title = strings.ReplaceAll(title, ".md", "")
response.Success(ctx, gin.H{"title": title, "htmlContext": htmlContext, "list": listResult})
} else {
ctx.Header("Cache-Control", "public,s-maxage=300,max-age=31536000")
ctx.File(path)
ctx.Abort()
}
}

11
go.mod Normal file
View File

@ -0,0 +1,11 @@
module gin-demo
go 1.16
require (
github.com/gin-gonic/gin v1.8.1
github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/spf13/viper v1.6.3
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2 // indirect
)

34
main.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"gin-demo/route"
"os"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
func main() {
InitConfig()
r := gin.Default()
r.LoadHTMLGlob("templates/*.html")
// r.StaticFS("/static", http.Dir("./static"))
r = route.CollectRoute(r)
port := viper.GetString("server.port")
if port != "" {
panic(r.Run(":" + port))
}
panic(r.Run()) // listen and serve on 0.0.0.0:8080
}
func InitConfig() {
workDir, _ := os.Getwd()
viper.SetConfigName("application")
viper.SetConfigType("yml")
viper.AddConfigPath(workDir + "/config")
err := viper.ReadInConfig()
if err != nil {
panic("")
}
}

19
response/response.go Normal file
View File

@ -0,0 +1,19 @@
package response
import (
"net/http"
"github.com/gin-gonic/gin"
)
func Response(ctx *gin.Context, httpStatus int, data gin.H) {
ctx.HTML(httpStatus, "index.html", data)
}
func Success(ctx *gin.Context, data gin.H) {
Response(ctx, http.StatusOK, data)
}
func Fail(ctx *gin.Context, data gin.H) {
ctx.HTML(http.StatusNotFound, "404.html", data)
}

14
route/routes.go Normal file
View File

@ -0,0 +1,14 @@
package route
import (
"gin-demo/controller"
"github.com/gin-gonic/gin"
)
func CollectRoute(r *gin.Engine) *gin.Engine {
categoryController := controller.MarkdownLoad()
r.GET("/*path", categoryController.Show)
return r
}

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

9
static/highlight.min.css vendored Normal file
View File

@ -0,0 +1,9 @@
/*!
Theme: Default
Description: Original highlight.js style
Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
Maintainer: @highlightjs/core-team
Website: https://highlightjs.org/
License: see project LICENSE
Touched: 2021
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{background:#f3f3f3;color:#444}.hljs-comment{color:#697070}.hljs-punctuation,.hljs-tag{color:#444a}.hljs-tag .hljs-attr,.hljs-tag .hljs-name{color:#444}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#ab5656}.hljs-literal{color:#695}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta .hljs-string{color:#38a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}

1173
static/highlight.min.js vendored Normal file

File diff suppressed because one or more lines are too long

7744
static/index.css Normal file

File diff suppressed because it is too large Load Diff

97
static/index.js Normal file
View File

@ -0,0 +1,97 @@
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
window.onload = function () {
var title = document.title
var ele = document.getElementById(title + ".md");
if (ele) {
ele.classList.add("current-tab")
}
var eleList = document.getElementsByClassName("menu-item")
for (var i = 0; i < eleList.length; i++) { //遍历数组
if (eleList[i].id.startsWith(title) && i > 0) {
document.getElementById("prePage").innerHTML = "<a href='" + eleList[i - 1].getAttribute("href") + "'>上一页</a>"
}
if (eleList[i].id.startsWith(title) && i < eleList.length) {
document.getElementById("nextPage").innerHTML = "<a href='" + eleList[i + 1].getAttribute("href") + "'>下一页</a>"
}
}
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
hljs.initHighlightingOnLoad()
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}

95
templates/404.html Normal file
View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<!-- saved from url=(0027)https://www.dropbox.com/404 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>404</title>
</head>
<style>
body,input,textarea,select,button,.normal {
font-family: "Open Sans","lucida grande","Segoe UI",arial,verdana,"lucida sans unicode",tahoma,sans-serif;
font-size: 13px;
color: #000;
font-weight: normal
}
body {
color: #444;
font-size: 16px;
font-weight: normal;
text-align: center;
width: 100%
}
#errorbox {
line-height: 1.6em;
margin: 0 auto;
max-width: 600px;
padding: 0 20px;
text-align: left;
word-wrap: break-word
}
#errorbox a {
color: #005ff8;
text-decoration: none
}
#errorbox h1 {
color: #222;
font-size: 1.5em;
font-weight: normal;
margin: 10px 0
}
#errorbox .not-found {
color: rgba(0,0,0,0.75);
font-size: 21px;
font-weight: lighter;
line-height: 36px;
text-align: center
}
#errorbox .not-found h1 {
color: rgba(0,0,0,0.75);
font-size: 38px;
font-weight: lighter;
line-height: 47px
}
#errorbox .not-found--links {
margin: 48px 0;
font-size: 14px;
font-weight: normal;
line-height: 20px
}
#errorbox .not-found--links ul {
padding: 0;
margin: 0;
list-style-type: none
}
#errorbox .not-found--links ul li {
margin-top: 10px
}
#errorbox .not-found--links ul li a:hover {
text-decoration: underline
}
</style>
<body>
<div id="errorbox">
<div class="not-found"><h1>Error (404)</h1>
<div class="not-found--links">
<ul>
<li><a href="/">Home</a></li>
</ul>
</div>
</div>
</div>
</body>
</html>

100
templates/index.html Normal file
View File

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<meta http-equiv='content-language' content='zh-cn'>
<meta name='description' content={{.title}}>
<link rel="icon" href="/static/favicon.png">
<title >{{.title}} </title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<link rel="stylesheet" href="/static/highlight.min.css">
<script src="/static/highlight.min.js"></script>
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
<script async defer data-website-id="83e5d5db-9d06-40e3-b780-cbae722fdf8c"
src="http://analyze.lianglianglee.com/umami.js"></script>
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
{{range $i, $v := .list}}
<li>
<a class ="menu-item" id="{{$v.FileName}}" href="{{$v.FilePath}}">{{$v.FileName}}</a>
</li>
{{end}}
<li><a href="/assets/捐赠.md">捐赠</a></li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<p class="title">{{.title}}</p>
<div>{{ .htmlContext }}</div>
</div>
<div>
<div id="prePage" style="float: left">
</div>
<div id="nextPage" style="float: right">
</div>
</div>
</div>
</div>
</div>
<div class="copyright">
<hr />
<p>© 2019 - 2023 <a href="mailto:lll941107@gmail.com" target="_blank">Liangliang Lee</a>.
Powered by <a href="https://github.com/gin-gonic/gin" target="_blank">gin</a> and <a
href="https://github.com/kaiiiz/hexo-theme-book" target="_blank">hexo-theme-book</a>.</p>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script src="/static/index.js"></script>
</html>

6
util/FileModel.go Normal file
View File

@ -0,0 +1,6 @@
package util
type FileModel struct {
FileName string
FilePath string
}

100
util/FileUtil.go Normal file
View File

@ -0,0 +1,100 @@
package util
import (
"html/template"
"os"
"path/filepath"
"sort"
"strings"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
"github.com/spf13/viper"
)
func GetMdStr(path string) template.HTML {
fileMeta, err := os.Stat(path)
if err != nil {
return "读取错误"
}
if fileMeta.IsDir() {
return GetDirStr(path)
} else {
context, err := os.ReadFile(path)
if err != nil {
return "error"
}
htmlContext := MdToHtml(context)
html := template.HTML(htmlContext)
return html
}
}
func GetDirStr(path string) template.HTML {
listResult := GetFileList(path)
context := ""
for i := 0; i < len(listResult); i++ {
item := listResult[i]
if !strings.HasPrefix(item.FileName, ".") && item.FileName != "assets" {
context += "- [" + item.FileName + "](" + item.FilePath + ") \n"
}
}
htmlContext := MdToHtml([]byte(context))
html := template.HTML(htmlContext)
return html
}
func GetFileList(path string) []FileModel {
var result []FileModel
fileMeta, err := os.Stat(path)
if err != nil {
return result
}
rootDir := viper.GetString("dir.root")
if !fileMeta.IsDir() {
path = filepath.Dir(path)
}
list, err := os.ReadDir(path)
if err != nil {
return result
}
for i := 0; i < len(list); i++ {
item := list[i]
fileInfo, err := item.Info()
//fmt.Println(fileInfo.Name())
if err != nil {
continue
}
if strings.HasPrefix(fileInfo.Name(), ".") || fileInfo.Name() == "assets" {
continue
}
if !strings.HasSuffix(path, "/") {
path = path + "/"
}
filePath := strings.Replace(path+fileInfo.Name(), rootDir, "", -1)
if !strings.HasPrefix(filePath, "/") {
filePath = "/" + filePath
}
result = append(result, FileModel{FileName: fileInfo.Name(), FilePath: filePath})
}
// 用 age 排序,年龄相等的元素保持原始顺序
sort.SliceStable(result, func(i, j int) bool {
return result[i].FileName < result[j].FileName
})
return result
}
func MdToHtml(context []byte) []byte {
// create markdown parser with extensions
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
p := parser.NewWithExtensions(extensions)
doc := p.Parse(context)
// create HTML renderer with extensions
htmlFlags := html.CommonFlags | html.HrefTargetBlank
opts := html.RendererOptions{Flags: htmlFlags}
renderer := html.NewRenderer(opts)
return markdown.Render(doc, renderer)
}