188 lines
No EOL
5.5 KiB
Go
188 lines
No EOL
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/yuin/goldmark"
|
|
)
|
|
|
|
const (
|
|
PORT = 61594
|
|
|
|
FILES_DIR = "files"
|
|
TEMPLATES_DIR = "templates"
|
|
TIME_FORMAT = "Jan 2 2006 15:04:05"
|
|
|
|
POST_ICON = "/static/post.gif"
|
|
FILE_ICON = "/static/file.gif"
|
|
)
|
|
|
|
var (
|
|
headerPattern = regexp.MustCompile(`(?m)^# (.+)$`)
|
|
subHeaderPattern = regexp.MustCompile(`(?m)^## (.+)$`)
|
|
thirdHeaderPattern = regexp.MustCompile(`(?m)^### (.+)$`)
|
|
fourthHeaderPattern = regexp.MustCompile(`(?m)^#### (.+)$`)
|
|
fifthHeaderPattern = regexp.MustCompile(`(?m)^##### (.+)$`)
|
|
sixthHeaderPattern = regexp.MustCompile(`(?m)^###### (.+)$`)
|
|
boldPattern = regexp.MustCompile(`\*\*(.*?)\*\*|__(.*?)__`)
|
|
italicPattern = regexp.MustCompile(`\*(.*?)\*|_(.*?)_`)
|
|
inlineCodePattern = regexp.MustCompile("`([^`]+)`")
|
|
codeBlockPattern = regexp.MustCompile("(?s)```(.*?)```")
|
|
blockquotePattern = regexp.MustCompile(`(?m)^> (.+)$`)
|
|
linkPattern = regexp.MustCompile(`\[(.*?)\]\((.*?)\)`)
|
|
)
|
|
|
|
type FileEntry struct {
|
|
Name string
|
|
Timestamp string
|
|
ParentPath string
|
|
Icon string
|
|
}
|
|
|
|
type Page struct {
|
|
Title string
|
|
Content template.HTML
|
|
Files []FileEntry
|
|
Folders []string
|
|
BasePath string
|
|
ParentPath string
|
|
TimeStamp string
|
|
}
|
|
|
|
func listEntries(basePath string) ([]FileEntry, []string) {
|
|
entries, err := os.ReadDir(filepath.Join(FILES_DIR, strings.TrimPrefix(basePath, "/")))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
var files []FileEntry
|
|
var folders []string
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
folders = append(folders, entry.Name())
|
|
} else if strings.HasSuffix(entry.Name(), ".md") {
|
|
info, _ := entry.Info()
|
|
files = append(files, FileEntry{Name: entry.Name(), Timestamp: info.ModTime().Format(TIME_FORMAT), ParentPath: basePath, Icon: POST_ICON})
|
|
}else
|
|
{
|
|
info, _ := entry.Info()
|
|
files = append(files, FileEntry{Name: entry.Name(), Timestamp: info.ModTime().Format(TIME_FORMAT), ParentPath: basePath, Icon: FILE_ICON})
|
|
}
|
|
}
|
|
sort.Slice(files, func(i, j int) bool { return files[i].Name < files[j].Name })
|
|
sort.Strings(folders)
|
|
return files, folders
|
|
}
|
|
|
|
func readMarkdown(filename string) string {
|
|
content, err := os.ReadFile(filepath.Join(FILES_DIR, filename))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return string(content)
|
|
}
|
|
|
|
func renderMarkdown(content string) template.HTML {
|
|
// Compile regex patterns
|
|
/*content = codeBlockPattern.ReplaceAllString(content, "<pre><code>$1</code></pre>")
|
|
content = inlineCodePattern.ReplaceAllString(content, "<code>$1</code>")
|
|
content = headerPattern.ReplaceAllString(content, "<h1>$1</h1>")
|
|
content = subHeaderPattern.ReplaceAllString(content, "<h2>$1</h2>")
|
|
content = thirdHeaderPattern.ReplaceAllString(content, "<h3>$1</h3>")
|
|
content = fourthHeaderPattern.ReplaceAllString(content, "<h4>$1</h4>")
|
|
content = fifthHeaderPattern.ReplaceAllString(content, "<h5>$1</h5>")
|
|
content = sixthHeaderPattern.ReplaceAllString(content, "<h6>$1</h6>")
|
|
content = boldPattern.ReplaceAllString(content, "<b>$1$2</b>")
|
|
content = italicPattern.ReplaceAllString(content, "<i>$1$2</i>")
|
|
content = blockquotePattern.ReplaceAllString(content, "<blockquote>$1</blockquote>")
|
|
content = linkPattern.ReplaceAllString(content, `<a href="$2">$1</a>`)
|
|
|
|
leadingStarPattern := regexp.MustCompile(`(?m)^\*`)
|
|
content = leadingStarPattern.ReplaceAllString(content, "•")
|
|
|
|
paragraphPattern := regexp.MustCompile(`(?m)(^([^#<\n].+)$)\n?`)
|
|
content = paragraphPattern.ReplaceAllString(content, "<p><pre>$1</pre></p>")
|
|
|
|
return template.HTML(content)*/
|
|
input := []byte(content)
|
|
|
|
var buf bytes.Buffer
|
|
if err := goldmark.Convert(input, &buf); err != nil {
|
|
panic(err)
|
|
}
|
|
//return template.HTML(res)
|
|
return template.HTML(buf.String())
|
|
}
|
|
|
|
func loadTemplate(name string) *template.Template {
|
|
tmplPath := filepath.Join(TEMPLATES_DIR, name)
|
|
tmpl, err := template.ParseFiles(tmplPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return tmpl
|
|
}
|
|
|
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
|
basePath := strings.TrimPrefix(r.URL.Path, "/")
|
|
files, folders := listEntries(r.URL.Path)
|
|
page := Page{Files: files, Folders: folders, BasePath: basePath}
|
|
tmpl := loadTemplate("index.html")
|
|
tmpl.Execute(w, page)
|
|
}
|
|
|
|
func postHandler(w http.ResponseWriter, r *http.Request) {
|
|
filename := strings.TrimPrefix(r.URL.Path, "/")
|
|
|
|
parentPath := filepath.ToSlash(filepath.Dir(filename) + "/")
|
|
if parentPath == "." {
|
|
parentPath = ""
|
|
}
|
|
|
|
info, err := os.Stat(filepath.Join(FILES_DIR, filename))
|
|
if err != nil {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
timeStamp := info.ModTime().Format(TIME_FORMAT)
|
|
|
|
content := readMarkdown(filename)
|
|
page := Page{Title: filename, Content: renderMarkdown(content), ParentPath: parentPath, TimeStamp: timeStamp}
|
|
tmpl := loadTemplate("post.html")
|
|
tmpl.Execute(w, page)
|
|
}
|
|
|
|
func main() {
|
|
if _, err := os.Stat(FILES_DIR); os.IsNotExist(err) {
|
|
os.Mkdir(FILES_DIR, os.ModePerm)
|
|
}
|
|
|
|
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
path := strings.TrimPrefix(r.URL.Path, "/")
|
|
if strings.HasSuffix(path, ".md") {
|
|
postHandler(w, r)
|
|
} else if strings.Contains(path, ".") {
|
|
http.ServeFile(w, r, filepath.Join(FILES_DIR, path))
|
|
} else {
|
|
indexHandler(w, r)
|
|
}
|
|
})
|
|
|
|
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFile(w, r, "static/post.gif")
|
|
})
|
|
|
|
|
|
log.Println("Serving on :", PORT)
|
|
http.ListenAndServe(fmt.Sprintf(":%d", PORT), nil)
|
|
} |