gin framework
This commit is contained in:
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