summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authora73x <[email protected]>2024-08-25 15:14:42 +0100
committera73x <[email protected]>2024-08-25 15:14:42 +0100
commitffdc0a5c34a0b78cec776049efde074147112ae5 (patch)
tree8b0a644790c630b0d5ae0512073d2566b87314ca /web
parent92a974b622f26645579e51c946a1266862384002 (diff)
feat: ssh access
Diffstat (limited to 'web')
-rw-r--r--web/web.go178
1 files changed, 178 insertions, 0 deletions
diff --git a/web/web.go b/web/web.go
new file mode 100644
index 0000000..aa78f51
--- /dev/null
+++ b/web/web.go
@@ -0,0 +1,178 @@
+package web
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/fs"
+ "net/http"
+ "path/filepath"
+ "strings"
+ "text/template"
+ "time"
+
+ "git.sr.ht/~a73x/home"
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/parser"
+ "go.abhg.dev/goldmark/frontmatter"
+ "go.uber.org/zap"
+)
+
+type CommonData struct {
+ Posts []PostData
+}
+
+type PostData struct {
+ Title string
+ Path string
+}
+
+func GeneratePosts(mux *http.ServeMux) ([]PostData, error) {
+ converter := goldmark.New(
+ goldmark.WithExtensions(
+ &frontmatter.Extender{},
+ ))
+
+ t, err := template.ParseFS(home.Content, "templates/layouts/*.html")
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse layouts: %v", err)
+ }
+
+ final := []PostData{}
+ posts, err := fs.Glob(home.Content, "posts/*.md")
+ if err != nil {
+ return nil, fmt.Errorf("failed to glob posts: %v", err)
+ }
+
+ for _, post := range posts {
+ postName := filepath.Base(post)
+ postName = strings.Split(postName, ".")[0] + ".html"
+
+ // parse markdown
+ input, err := fs.ReadFile(home.Content, post)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read post: %v", err)
+ }
+
+ var b bytes.Buffer
+ ctx := parser.NewContext()
+ if err := converter.Convert(input, &b, parser.WithContext(ctx)); err != nil {
+ return nil, err
+ }
+
+ d := frontmatter.Get(ctx)
+ var meta map[string]string
+ if err := d.Decode(&meta); err != nil {
+ return nil, err
+ }
+
+ foo, err := t.ParseFS(home.Content, "templates/post.html")
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse post template: %v", err)
+ }
+
+ content, err := io.ReadAll(&b)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read post template: %v", err)
+ }
+
+ type IPostData struct {
+ Title string
+ Post string
+ }
+
+ mux.HandleFunc("/posts/"+postName, func(w http.ResponseWriter, r *http.Request) {
+ if err := foo.ExecuteTemplate(w, "post.html", IPostData{
+ Title: meta["title"],
+ Post: string(content),
+ }); err != nil {
+ fmt.Println(err)
+ }
+ })
+
+ final = append(final, PostData{
+ Title: meta["title"],
+ Path: "posts/" + postName,
+ })
+ }
+
+ return final, nil
+}
+
+func loadTemplates(mux *http.ServeMux, data any) error {
+ tmplFiles, err := fs.ReadDir(home.Content, "templates")
+ if err != nil {
+ return fmt.Errorf("failed to parse template layouts")
+ }
+
+ for _, tmpl := range tmplFiles {
+ if tmpl.IsDir() {
+ continue
+ }
+
+ pt, err := template.ParseFS(home.Content, "templates/"+tmpl.Name(), "templates/layouts/*.html")
+ if err != nil {
+ return fmt.Errorf("failed to parse template "+tmpl.Name(), err)
+ }
+
+ if tmpl.Name() == "index.html" {
+ mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ pt.ExecuteTemplate(w, tmpl.Name(), data)
+ })
+ } else if tmpl.Name() == "page.html" {
+ continue
+ } else {
+ mux.HandleFunc("/"+tmpl.Name(), func(w http.ResponseWriter, r *http.Request) {
+ pt, err := template.ParseFS(home.Content, "templates/"+tmpl.Name(), "templates/layouts/*.html")
+ if err != nil {
+ fmt.Printf("failed to parse template "+tmpl.Name(), err)
+ }
+ pt.ExecuteTemplate(w, tmpl.Name(), data)
+ })
+ }
+ }
+ return nil
+}
+func New() (*http.Server, error) {
+ logger, _ := zap.NewProduction()
+ loggingMiddleware := func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ start := time.Now()
+ next.ServeHTTP(w, r)
+ logger.Info("request received",
+ zap.String("url", r.URL.Path),
+ zap.String("method", r.Method),
+ zap.Duration("duration", time.Since(start)),
+ zap.String("user-agent", r.UserAgent()),
+ )
+ })
+ }
+
+ mux := http.NewServeMux()
+ postData, err := GeneratePosts(mux)
+ if err != nil {
+ return nil, fmt.Errorf("failed to generate posts: %v", err)
+ }
+
+ data := CommonData{
+ Posts: postData,
+ }
+
+ if err := loadTemplates(mux, data); err != nil {
+ return nil, fmt.Errorf("failed to parse templates: %v", err)
+ }
+
+ staticFs, err := fs.Sub(home.Content, "public/static")
+ if err != nil {
+ return nil, fmt.Errorf("failed to setup static handler: %v", err)
+ }
+
+ mux.Handle("GET /static", http.FileServer(http.FS(staticFs)))
+
+ server := http.Server{
+ Addr: ":8080",
+ Handler: loggingMiddleware(mux),
+ }
+
+ return &server, nil
+}