Initial commit (code only without large binaries)
This commit is contained in:
114
EdgeUser/internal/web/actions/default/docs/docutils/cache.go
Normal file
114
EdgeUser/internal/web/actions/default/docs/docutils/cache.go
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package docutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/yuin/goldmark"
|
||||
highlighting "github.com/yuin/goldmark-highlighting/v2"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type cacheItem struct {
|
||||
ModifiedAt int64
|
||||
Data []byte
|
||||
}
|
||||
|
||||
var cacheMap = map[string]*cacheItem{} // path => *cacheItem
|
||||
var cacheLocker = &sync.RWMutex{}
|
||||
|
||||
func ReadMarkdownFile(path string, productName string, rootURL string) ([]byte, error) {
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stat.IsDir() {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
var modifiedAt = stat.ModTime().Unix()
|
||||
|
||||
cacheLocker.RLock()
|
||||
item, ok := cacheMap[path]
|
||||
if ok && item.ModifiedAt == modifiedAt {
|
||||
cacheLocker.RUnlock()
|
||||
return item.Data, nil
|
||||
}
|
||||
cacheLocker.RUnlock()
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// keywords
|
||||
data = bytes.ReplaceAll(data, []byte("GoEdge"), []byte(productName))
|
||||
data = bytes.ReplaceAll(data, []byte("GOEDGE"), []byte(productName))
|
||||
data = bytes.ReplaceAll(data, []byte("http://goedge.cn"), []byte("http://example.com"))
|
||||
data = bytes.ReplaceAll(data, []byte("https://goedge.cn"), []byte("https://example.com"))
|
||||
|
||||
var markdown = goldmark.New(
|
||||
goldmark.WithParserOptions(
|
||||
parser.WithAutoHeadingID(), // read note
|
||||
),
|
||||
goldmark.WithExtensions(extension.Table,
|
||||
extension.Strikethrough,
|
||||
highlighting.NewHighlighting(highlighting.WithStyle("github"), highlighting.WithFormatOptions(chromahtml.TabWidth(4))),
|
||||
),
|
||||
goldmark.WithRendererOptions(html.WithHardWraps()),
|
||||
)
|
||||
|
||||
var buf = &bytes.Buffer{}
|
||||
err = markdown.Convert(data, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = buf.Bytes()
|
||||
|
||||
// convert links
|
||||
{
|
||||
var reg = regexp.MustCompile(`(?U)<a href="(.+\.md)">`)
|
||||
data = reg.ReplaceAllFunc(data, func(link []byte) []byte {
|
||||
var pieces = reg.FindSubmatch(link)
|
||||
if len(pieces) > 1 {
|
||||
var newLink = append([]byte(rootURL), bytes.TrimSuffix(pieces[1], []byte(".md"))...)
|
||||
newLink = append(newLink, []byte(".html")...)
|
||||
newLink = []byte(filepath.Clean(string(newLink)))
|
||||
return bytes.ReplaceAll(link, pieces[1], newLink)
|
||||
}
|
||||
return link
|
||||
})
|
||||
}
|
||||
|
||||
// convert images
|
||||
{
|
||||
var reg = regexp.MustCompile(`(?U)<img src="(.+)"[^>]*>`)
|
||||
data = reg.ReplaceAllFunc(data, func(link []byte) []byte {
|
||||
var pieces = reg.FindSubmatch(link)
|
||||
if len(pieces) > 1 {
|
||||
var newLink = append([]byte(rootURL), pieces[1]...)
|
||||
newLink = []byte(filepath.Clean(string(newLink)))
|
||||
return []byte("<a href=\"" + string(newLink) + "\" target=\"_blank\">" + string(bytes.ReplaceAll(link, pieces[1], newLink)) + "</a>")
|
||||
}
|
||||
return link
|
||||
})
|
||||
}
|
||||
|
||||
// put into cache
|
||||
item = &cacheItem{
|
||||
ModifiedAt: modifiedAt,
|
||||
Data: data,
|
||||
}
|
||||
cacheLocker.Lock()
|
||||
cacheMap[path] = item
|
||||
cacheLocker.Unlock()
|
||||
|
||||
return data, nil
|
||||
}
|
||||
Reference in New Issue
Block a user