gin framework
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -14,4 +14,7 @@
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
*.db
|
||||
/wiki
|
||||
/page/*
|
||||
__debug_bin
|
||||
|
||||
19
.vscode/launch.json
vendored
Normal file
19
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "exec",
|
||||
"program": "${workspaceFolder}/${workspaceFolderBasename}",
|
||||
"preLaunchTask": "build",
|
||||
"env": {
|
||||
"GIN_MODE": "release"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"go.testFlags": ["-v"]
|
||||
}
|
||||
15
.vscode/tasks.json
vendored
Normal file
15
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"type": "shell",
|
||||
"command": "go",
|
||||
"args": [
|
||||
"build", "-v", "-o", "${workspaceFolderBasename}"
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/avatars/avatar.png
Normal file
BIN
assets/avatars/avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/avatars/user.jpg
Normal file
BIN
assets/avatars/user.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
1
assets/css/ace.min.css
vendored
Normal file
1
assets/css/ace.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
5
assets/js/ace-elements.min.js
vendored
Normal file
5
assets/js/ace-elements.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
assets/js/ace-extra.min.js
vendored
Normal file
1
assets/js/ace-extra.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
assets/js/ace.min.js
vendored
Normal file
5
assets/js/ace.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
go.mod
Normal file
10
go.mod
Normal file
@@ -0,0 +1,10 @@
|
||||
module wiki
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/gin-gonic/gin v1.7.2
|
||||
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44
|
||||
golang.org/x/text v0.3.3
|
||||
)
|
||||
78
go.sum
Normal file
78
go.sum
Normal file
@@ -0,0 +1,78 @@
|
||||
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/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
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.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
|
||||
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
|
||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44 h1:7bSo/vjZKVYUoZfxpYh9Y9JV+9hi1gYJp4WIykhrZag=
|
||||
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
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/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
|
||||
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
6
i18n/en-US.ini
Normal file
6
i18n/en-US.ini
Normal file
@@ -0,0 +1,6 @@
|
||||
[nav]
|
||||
settings = Settings
|
||||
logout = Logout
|
||||
home = Home
|
||||
search = Search...
|
||||
welcome = Welcome
|
||||
6
i18n/zh-CN.ini
Normal file
6
i18n/zh-CN.ini
Normal file
@@ -0,0 +1,6 @@
|
||||
[nav]
|
||||
settings = 设置
|
||||
logout = 注销
|
||||
home = 首页
|
||||
search = 搜索...
|
||||
welcome = 欢迎
|
||||
0
page/.gitkeep
Normal file
0
page/.gitkeep
Normal file
21
views/404.html
Normal file
21
views/404.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="error-container">
|
||||
<div class="well">
|
||||
<h1 class="grey lighter smaller">
|
||||
<span class="blue bigger-125">
|
||||
<i class="ace-icon fa fa-sitemap"></i> {{.Code}}
|
||||
</span>
|
||||
页面未找到
|
||||
</h1>
|
||||
<hr />
|
||||
<h3 class="lighter smaller">We looked everywhere but we couldn't find it!</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.col -->
|
||||
</div><!-- /.row -->
|
||||
<script type="text/javascript">
|
||||
$('.page-content-area').ace_ajax('loadScripts', scripts, function () {
|
||||
//inline scripts related to this page
|
||||
});
|
||||
</script>
|
||||
176
views/layout.html
Normal file
176
views/layout.html
Normal file
@@ -0,0 +1,176 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<title>Go Wiki</title>
|
||||
<meta name="description" content="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<!-- bootstrap & fontawesome -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<!-- ace styles -->
|
||||
<link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" />
|
||||
<script src="/assets/js/ace-extra.min.js"></script>
|
||||
</head>
|
||||
<body class="no-skin">
|
||||
<div id="navbar" class="navbar navbar-default">
|
||||
<div class="navbar-container ace-save-state" id="navbar-container">
|
||||
<button type="button" class="navbar-toggle menu-toggler pull-left" id="menu-toggler" data-target="#sidebar">
|
||||
<span class="sr-only">Toggle sidebar</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<div class="navbar-header pull-left">
|
||||
<a href="#" class="navbar-brand">
|
||||
<small><i class="fa fa-leaf"></i> Go Wiki</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-buttons navbar-header pull-right" role="navigation">
|
||||
<ul class="nav ace-nav">
|
||||
<li class="light-blue dropdown-modal">
|
||||
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
|
||||
<img class="nav-user-photo" src="/assets/avatars/user.jpg" alt="Jason's Photo" />
|
||||
<span class="user-info"><small>{{$.i18n.Tr "nav.welcome"}}</small> Admin</span>
|
||||
<i class="ace-icon fa fa-caret-down"></i>
|
||||
</a>
|
||||
<ul class="user-menu dropdown-menu-right dropdown-menu dropdown-yellow dropdown-caret dropdown-close">
|
||||
<li>
|
||||
<a href="#">
|
||||
<i class="ace-icon fa fa-cog"></i> {{$.i18n.Tr "nav.settings"}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="#">
|
||||
<i class="ace-icon fa fa-power-off"></i> {{$.i18n.Tr "nav.logout"}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div><!-- /.navbar-container -->
|
||||
</div>
|
||||
<div class="main-container ace-save-state" id="main-container">
|
||||
<script type="text/javascript">
|
||||
try { ace.settings.check('main-container', 'fixed') } catch (e) {}
|
||||
</script>
|
||||
<div id="sidebar" class="sidebar responsive ace-save-state">
|
||||
<script type="text/javascript">
|
||||
try { ace.settings.loadState('sidebar') } catch (e) { }
|
||||
</script>
|
||||
<div class="sidebar-shortcuts" id="sidebar-shortcuts">
|
||||
<div class="sidebar-shortcuts-large" id="sidebar-shortcuts-large">
|
||||
<button class="btn btn-success">
|
||||
<i class="ace-icon fa fa-signal"></i>
|
||||
</button>
|
||||
<button class="btn btn-info">
|
||||
<i class="ace-icon fa fa-pencil"></i>
|
||||
</button>
|
||||
<button class="btn btn-warning">
|
||||
<i class="ace-icon fa fa-users"></i>
|
||||
</button>
|
||||
<button class="btn btn-danger">
|
||||
<i class="ace-icon fa fa-cogs"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sidebar-shortcuts-mini" id="sidebar-shortcuts-mini">
|
||||
<span class="btn btn-success"></span>
|
||||
<span class="btn btn-info"></span>
|
||||
<span class="btn btn-warning"></span>
|
||||
<span class="btn btn-danger"></span>
|
||||
</div>
|
||||
</div><!-- /.sidebar-shortcuts -->
|
||||
<ul class="nav nav-list">
|
||||
{{- template "menu" .Menu}}
|
||||
</ul><!-- /.nav-list -->
|
||||
<div class="sidebar-toggle sidebar-collapse" id="sidebar-collapse">
|
||||
<i id="sidebar-toggle-icon" class="ace-icon fa fa-angle-double-left ace-save-state" data-icon1="ace-icon fa fa-angle-double-left" data-icon2="ace-icon fa fa-angle-double-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
<div class="main-content-inner">
|
||||
<div class="breadcrumbs ace-save-state" id="breadcrumbs">
|
||||
<ul class="breadcrumb">
|
||||
<li>
|
||||
<i class="ace-icon fa fa-home home-icon"></i>
|
||||
<a href="#">{{$.i18n.Tr "nav.home"}}</a>
|
||||
</li>
|
||||
</ul><!-- /.breadcrumb -->
|
||||
<div class="nav-search" id="nav-search">
|
||||
<form class="form-search">
|
||||
<span class="input-icon">
|
||||
<input type="text" placeholder="{{$.i18n.Tr "nav.search"}}" class="nav-search-input" id="nav-search-input" autocomplete="off" />
|
||||
<i class="ace-icon fa fa-search nav-search-icon"></i>
|
||||
</span>
|
||||
</form>
|
||||
</div><!-- /.nav-search -->
|
||||
</div>
|
||||
<div class="page-content">
|
||||
<div class="page-content-area" data-ajax-content="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="footer-inner">
|
||||
<div class="footer-content">
|
||||
<span class="bigger-120">
|
||||
<span class="blue bolder">Ace</span>
|
||||
Application © 2021
|
||||
</span>
|
||||
|
||||
<span class="action-buttons">
|
||||
<a href="#">
|
||||
<i class="ace-icon fa fa-twitter-square light-blue bigger-150"></i>
|
||||
</a>
|
||||
<a href="#">
|
||||
<i class="ace-icon fa fa-facebook-square text-primary bigger-150"></i>
|
||||
</a>
|
||||
<a href="#">
|
||||
<i class="ace-icon fa fa-rss-square orange bigger-150"></i>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" id="btn-scroll-up" class="btn-scroll-up btn btn-sm btn-inverse">
|
||||
<i class="ace-icon fa fa-angle-double-up icon-only bigger-110"></i>
|
||||
</a>
|
||||
</div>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
if ('ontouchstart' in document.documentElement) document.write("<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery-mobile/1.4.1/jquery.mobile.min.js'>" + "<" + "/script>");
|
||||
</script>
|
||||
<script src="/assets/js/ace-elements.min.js"></script>
|
||||
<script src="/assets/js/ace.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
{{define "menu"}}
|
||||
{{- range .}}
|
||||
{{- if not .Status}}
|
||||
{{- else if and .Child (not .Path)}}
|
||||
<li>
|
||||
<a href="#" class="dropdown-toggle">
|
||||
<i class="menu-icon {{.Icon}}"></i>
|
||||
<span class="menu-text"> {{.Name}} </span>
|
||||
<b class="arrow fa fa-angle-down"></b>
|
||||
</a>
|
||||
<b class="arrow"></b>
|
||||
<ul class="submenu">
|
||||
{{- template "menu" .Child}}
|
||||
</ul>
|
||||
{{- else}}
|
||||
<li>
|
||||
<a data-url="{{.Path}}" href="#{{.Path}}">
|
||||
<i class="menu-icon {{.Icon}}"></i>
|
||||
<span class="menu-text"> {{.Name}} </span>
|
||||
</a>
|
||||
</li>
|
||||
{{- end}}
|
||||
</li>
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
156
wiki.go
Normal file
156
wiki.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/unknwon/i18n"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed assets/*/* views/*.html i18n/*.ini
|
||||
content embed.FS
|
||||
root Menu
|
||||
)
|
||||
|
||||
// Node 节点模型
|
||||
type Node struct {
|
||||
Name string `gorm:"size:64;not null"`
|
||||
Icon string `gorm:"size:32;default:fa fa-circlo-o" yaml:",omitempty"`
|
||||
Path string `gorm:"size:255"`
|
||||
Child []Node `gorm:"-" yaml:",omitempty"`
|
||||
Status bool `gorm:"default:false;not null"`
|
||||
}
|
||||
|
||||
type Menu []Node
|
||||
|
||||
func main() {
|
||||
// 加载语言文件
|
||||
localizer, err := loadI18n("i18n/*.ini")
|
||||
if err != nil {
|
||||
log.Fatalf("load i18n %v", err)
|
||||
}
|
||||
// 加载模板
|
||||
t, err := template.ParseFS(content, "views/*.html")
|
||||
if err != nil {
|
||||
log.Fatalf("parse template %v", err)
|
||||
}
|
||||
|
||||
r := gin.New()
|
||||
// 路由日志格式化
|
||||
r.Use(gin.LoggerWithFormatter(func(p gin.LogFormatterParams) string {
|
||||
return fmt.Sprintf("%s %s %d %s %d %s (%s)\n%s",
|
||||
p.TimeStamp.Format("2006/01/02 15:04:05"), p.Method, p.StatusCode,
|
||||
p.Path, p.BodySize, p.ClientIP, p.Latency, p.ErrorMessage,
|
||||
)
|
||||
}), localizer, gin.Recovery())
|
||||
|
||||
r.SetHTMLTemplate(t)
|
||||
// 静态文件
|
||||
r.GET("/assets/*filepath", func(c *gin.Context) {
|
||||
c.Header("Cache-Control", "public, max-age=3600")
|
||||
c.FileFromFS(c.Request.URL.Path, http.FS(content))
|
||||
})
|
||||
|
||||
r.GET("/", func(c *gin.Context) {
|
||||
page := strings.TrimSpace(c.Query("page"))
|
||||
if len(page) == 0 {
|
||||
c.Set("Notify", make([]string, 5))
|
||||
c.Set("Menu", root)
|
||||
c.HTML(http.StatusOK, "layout.html", c.Keys)
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("Code", http.StatusNotFound)
|
||||
c.HTML(http.StatusOK, "404.html", c.Keys)
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
if err = watchDocs(ctx, "page/*md"); err != nil {
|
||||
log.Fatalf("load markdown %v", err)
|
||||
}
|
||||
|
||||
// 监听并在 0.0.0.0:8080 上启动服务
|
||||
srv := &http.Server{Addr: ":8080", Handler: r}
|
||||
srv.RegisterOnShutdown(cancel)
|
||||
|
||||
// 监听进程退出
|
||||
sigint := make(chan os.Signal, 1)
|
||||
signal.Notify(sigint, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
||||
go srv.ListenAndServe()
|
||||
log.Printf("Listening and serving HTTP on %s", srv.Addr)
|
||||
|
||||
<-sigint
|
||||
if err := srv.Shutdown(context.Background()); err != nil {
|
||||
log.Printf("HTTP server Shutdown: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadI18n(pattern string) (gin.HandlerFunc, error) {
|
||||
list, err := fs.Glob(content, pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 加载语言文件
|
||||
for _, name := range list {
|
||||
f, err := content.ReadFile(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base, ext := filepath.Base(name), filepath.Ext(name)
|
||||
i18n.SetMessage(strings.TrimSuffix(base, ext), f)
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
tags, _, _ := language.ParseAcceptLanguage(c.GetHeader("Accept-Language"))
|
||||
local := new(i18n.Locale)
|
||||
c.Set("i18n", local)
|
||||
|
||||
for _, tag := range tags {
|
||||
local.Lang = tag.String()
|
||||
if i18n.IsExist(local.Lang) {
|
||||
break
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
}, nil
|
||||
}
|
||||
|
||||
func watchDocs(ctx context.Context, pattern string) error {
|
||||
// 文件监控
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return fmt.Errorf("create watcher: %w", err)
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Print("watch exit")
|
||||
return
|
||||
|
||||
case e := <-watcher.Events:
|
||||
log.Printf("load %s %v", filepath.Base(e.Name), e.Op)
|
||||
|
||||
case err := <-watcher.Errors:
|
||||
log.Printf("Watcher error: %v", err) // No need to exit here
|
||||
}
|
||||
}
|
||||
}()
|
||||
return watcher.Add(path.Dir(pattern))
|
||||
}
|
||||
Reference in New Issue
Block a user