codex/main.go

184 lines
No EOL
5.4 KiB
Go

package main
import (
"fmt"
"html/template"
"log"
"net/http"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"github.com/shurcooL/github_flavored_markdown"
)
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)
output := github_flavored_markdown.Markdown(input)
//return template.HTML(res)
return template.HTML(output)
}
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)
}