初始化项目
This commit is contained in:
parent
c0eba18e88
commit
b8489d4629
|
@ -1,3 +1,7 @@
|
|||
.idea
|
||||
.vscode
|
||||
go.sum
|
||||
|
||||
# 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
|
||||
#
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# 项目介绍
|
||||
本项目初衷是为了实现markdown的文档的展示
|
||||
|
||||
# 实现方式
|
||||
|
||||
该项目使用gin实现,markdown使用`gomarkdown/markdown`渲染成html代码块。由templates渲染到页面中
|
||||
|
||||
# 配置
|
||||
```yaml
|
||||
server:
|
||||
port: 8081
|
||||
dir:
|
||||
root: \Gitlab\note
|
||||
main: 专栏
|
||||
|
||||
```
|
|
@ -0,0 +1,5 @@
|
|||
server:
|
||||
port: 8081
|
||||
dir:
|
||||
root: \Gitlab\note
|
||||
main: PDF
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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("")
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -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}
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -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')
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,6 @@
|
|||
package util
|
||||
|
||||
type FileModel struct {
|
||||
FileName string
|
||||
FilePath string
|
||||
}
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue