From 245d6e0fa0b8caa29ae610f50404915bd2be8a23 Mon Sep 17 00:00:00 2001 From: zaire Date: Mon, 16 Mar 2026 20:41:16 +0100 Subject: [PATCH 1/4] fixed prior merge conflic causing double'd embed, added readme parser with full markdown, added readme render to HTML, renamed filepath to fpath to use FilePath.Join for case insensitivity, fixed ident in html src (inline css), added readme css with proper accents --- go.mod | 2 + go.sum | 2 + main.go | 185 ++++++++++++++++++++++++--------------- templates/dir.html | 214 ++++++++++++++++++++++++++++++++------------- 4 files changed, 270 insertions(+), 133 deletions(-) create mode 100644 go.sum diff --git a/go.mod b/go.mod index c1104b3..0b80eac 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module forge.arimelody.space/ari/indir go 1.24.3 + +require github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..52d2bfd --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab h1:VYNivV7P8IRHUam2swVUNkhIdp0LRRFKe4hXNnoZKTc= +github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= diff --git a/main.go b/main.go index a00ecd0..778ec1b 100755 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + _ "embed" "fmt" "html/template" "io/fs" @@ -8,11 +9,15 @@ import ( "net/http" "os" "path" + "path/filepath" "slices" "strconv" "strings" "time" - _ "embed" + + "github.com/gomarkdown/markdown" + "github.com/gomarkdown/markdown/html" + "github.com/gomarkdown/markdown/parser" ) //go:embed templates/dir.html @@ -20,25 +25,25 @@ var dirTemplateSrc string type ( Directory struct { - Name string - Root bool - Files []*File + Name string + Root bool + Files []*File + Readme template.HTML } File struct { - Name string - URI string - IsDir bool - Size string + Name string + URI string + IsDir bool + Size string ModifiedDate string } ) -//go:embed templates/dir.html -var dirHTML string - func main() { - if len(os.Args) < 2 { printHelp() } + if len(os.Args) < 2 { + printHelp() + } host := "127.0.0.1" port := 8080 @@ -47,7 +52,9 @@ func main() { filesDir := "" i := 1 for { - if i >= len(os.Args) { break } + if i >= len(os.Args) { + break + } switch os.Args[i] { case "-h": fallthrough @@ -55,7 +62,7 @@ func main() { printHelp() case "--host": - if i + 1 >= len(os.Args) { + if i+1 >= len(os.Args) { fmt.Fprintf(os.Stderr, "fatal: --host argument cannot be empty\n") os.Exit(1) } @@ -63,7 +70,7 @@ func main() { host = os.Args[i] case "--port": - if i + 1 >= len(os.Args) { + if i+1 >= len(os.Args) { fmt.Fprintf(os.Stderr, "fatal: --port argument cannot be empty\n") os.Exit(1) } @@ -76,14 +83,18 @@ func main() { } case "--root": - if i + 1 >= len(os.Args) { + if i+1 >= len(os.Args) { fmt.Fprintf(os.Stderr, "fatal: --root argument cannot be empty\n") os.Exit(1) } i++ root = os.Args[i] - if !strings.HasPrefix(root, "/") { root = "/" + root } - if !strings.HasSuffix(root, "/") { root += "/" } + if !strings.HasPrefix(root, "/") { + root = "/" + root + } + if !strings.HasSuffix(root, "/") { + root += "/" + } default: if len(filesDir) > 0 { @@ -111,7 +122,9 @@ func main() { } fmt.Printf("Now hosting \"%s\" at http://%s:%d", filesDir, host, port) - if root != "/" { fmt.Printf("%s", root) } + if root != "/" { + fmt.Printf("%s", root) + } fmt.Println(".") http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), HTTPLog(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -121,8 +134,8 @@ func main() { } isRoot := r.URL.Path == root - filepath := path.Join(filesDir, strings.TrimPrefix(r.URL.Path, root)) - info, err := os.Stat(filepath) + fpath := path.Join(filesDir, strings.TrimPrefix(r.URL.Path, root)) + info, err := os.Stat(fpath) if err != nil { http.NotFound(w, r) return @@ -135,7 +148,7 @@ func main() { return } - file, err := os.Open(filepath) + file, err := os.Open(fpath) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -144,7 +157,7 @@ func main() { defer func() { err := file.Close() if err != nil { - fmt.Fprintf(os.Stderr, "failed to close file %s: %v\n", filepath, err) + fmt.Fprintf(os.Stderr, "failed to close file %s: %v\n", fpath, err) } }() @@ -159,30 +172,54 @@ func main() { _, err = file.WriteTo(w) if err != nil { - fmt.Fprintf(os.Stderr, "failed to send file %s: %v\n", filepath, err) + fmt.Fprintf(os.Stderr, "failed to send file %s: %v\n", fpath, err) } return } if !strings.HasSuffix(r.URL.Path, "/") { - http.Redirect(w, r, r.URL.Path + "/", http.StatusFound) + http.Redirect(w, r, r.URL.Path+"/", http.StatusFound) return } - data := Directory{ - Root: isRoot, - Name: r.URL.Path, - Files: []*File{}, + // embeded readme + var readmeHTML template.HTML + entries, err := os.ReadDir(fpath) + if err == nil { + for _, entry := range entries { + if strings.EqualFold(entry.Name(), "readme.md") { + src, err := os.ReadFile(filepath.Join(fpath, entry.Name())) + if err == nil { + mdFlags := html.CommonFlags | html.HrefTargetBlank + mdRenderer := html.NewRenderer(html.RendererOptions{Flags: mdFlags}) + mdParser := parser.NewWithExtensions(parser.CommonExtensions | parser.AutoHeadingIDs) + md := mdParser.Parse(src) + readmeHTML = template.HTML(markdown.Render(md, mdRenderer)) + } + break + } + } } - fsDir := os.DirFS(filepath) + data := Directory{ + Root: isRoot, + Name: r.URL.Path, + Files: []*File{}, + Readme: readmeHTML, + } + + fsDir := os.DirFS(fpath) directories, err := fs.ReadDir(fsDir, ".") for _, dir := range directories { name := dir.Name() - if slices.Contains(ignoredFiles, name) { continue } + if slices.Contains(ignoredFiles, name) { + continue + } info, err := dir.Info() - if err != nil { continue } + if err != nil { + continue + } var uri string if isRoot { @@ -213,10 +250,10 @@ func main() { dateStr := info.ModTime().Format("02-Jan-2006 15:04") data.Files = append(data.Files, &File{ - Name: name, - URI: uri, - IsDir: info.IsDir(), - Size: sizeStr, + Name: name, + URI: uri, + IsDir: info.IsDir(), + Size: sizeStr, ModifiedDate: dateStr, }) } @@ -232,52 +269,60 @@ func main() { } type LoggingResponseWriter struct { - http.ResponseWriter - Status int + http.ResponseWriter + Status int } -var COL_Reset = "\033[0m" -var COL_Red = "\033[31m" -var COL_Green = "\033[32m" +var COL_Reset = "\033[0m" +var COL_Red = "\033[31m" +var COL_Green = "\033[32m" var COL_Yellow = "\033[33m" -var COL_Blue = "\033[34m" +var COL_Blue = "\033[34m" var COL_Purple = "\033[35m" -var COL_Cyan = "\033[36m" -var COL_Gray = "\033[37m" -var COL_White = "\033[97m" +var COL_Cyan = "\033[36m" +var COL_Gray = "\033[37m" +var COL_White = "\033[97m" func HTTPLog(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - start := time.Now() + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() - lrw := LoggingResponseWriter{w, http.StatusOK} + lrw := LoggingResponseWriter{w, http.StatusOK} - next.ServeHTTP(&lrw, r) + next.ServeHTTP(&lrw, r) - after := time.Now() - difference := (after.Nanosecond() - start.Nanosecond()) / 1_000_000 - elapsed := "<1" - if difference >= 1 { - elapsed = strconv.Itoa(difference) - } + after := time.Now() + difference := (after.Nanosecond() - start.Nanosecond()) / 1_000_000 + elapsed := "<1" + if difference >= 1 { + elapsed = strconv.Itoa(difference) + } - statusColour := COL_Reset + statusColour := COL_Reset - if lrw.Status - 600 <= 0 { statusColour = COL_Red } - if lrw.Status - 500 <= 0 { statusColour = COL_Yellow } - if lrw.Status - 400 <= 0 { statusColour = COL_White } - if lrw.Status - 300 <= 0 { statusColour = COL_Green } + if lrw.Status-600 <= 0 { + statusColour = COL_Red + } + if lrw.Status-500 <= 0 { + statusColour = COL_Yellow + } + if lrw.Status-400 <= 0 { + statusColour = COL_White + } + if lrw.Status-300 <= 0 { + statusColour = COL_Green + } - fmt.Printf("[%s] %s %s - %s%d%s (%sms) (%s)\n", - after.Format(time.UnixDate), - r.Method, - r.URL.Path, - statusColour, - lrw.Status, - COL_Reset, - elapsed, - r.Header["User-Agent"][0]) - }) + fmt.Printf("[%s] %s %s - %s%d%s (%sms) (%s)\n", + after.Format(time.UnixDate), + r.Method, + r.URL.Path, + statusColour, + lrw.Status, + COL_Reset, + elapsed, + r.Header["User-Agent"][0]) + }) } func printHelp() { diff --git a/templates/dir.html b/templates/dir.html index 562cc95..6d20006 100644 --- a/templates/dir.html +++ b/templates/dir.html @@ -1,82 +1,157 @@ - + - - + + Files in {{.Name}}

Files in {{.Name}}

-
+
@@ -89,8 +164,7 @@ footer { - {{end}} - {{range .Files}} + {{end}} {{range .Files}} @@ -98,10 +172,24 @@ footer { {{end}}
Name
{{.Name}} {{if .IsDir}}—{{else}}{{.Size}}{{end}}
-
+
+ {{if .Readme}} +
+

README

+
+ {{.Readme}} +
+ {{end}}
From 73fdf8bb46fb0079522637899ef4d20764ccb8e5 Mon Sep 17 00:00:00 2001 From: supitszaire Date: Mon, 16 Mar 2026 21:00:51 +0100 Subject: [PATCH 2/4] format fix --- main.go | 146 ++++++++++++++++++++++++-------------------------------- 1 file changed, 62 insertions(+), 84 deletions(-) diff --git a/main.go b/main.go index 778ec1b..267f123 100755 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - _ "embed" "fmt" "html/template" "io/fs" @@ -14,7 +13,8 @@ import ( "strconv" "strings" "time" - + _ "embed" + "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/parser" @@ -25,25 +25,23 @@ var dirTemplateSrc string type ( Directory struct { - Name string - Root bool - Files []*File + Name string + Root bool + Files []*File Readme template.HTML } File struct { - Name string - URI string - IsDir bool - Size string + Name string + URI string + IsDir bool + Size string ModifiedDate string } ) func main() { - if len(os.Args) < 2 { - printHelp() - } + if len(os.Args) < 2 { printHelp() } host := "127.0.0.1" port := 8080 @@ -52,9 +50,7 @@ func main() { filesDir := "" i := 1 for { - if i >= len(os.Args) { - break - } + if i >= len(os.Args) { break } switch os.Args[i] { case "-h": fallthrough @@ -62,7 +58,7 @@ func main() { printHelp() case "--host": - if i+1 >= len(os.Args) { + if i + 1 >= len(os.Args) { fmt.Fprintf(os.Stderr, "fatal: --host argument cannot be empty\n") os.Exit(1) } @@ -70,7 +66,7 @@ func main() { host = os.Args[i] case "--port": - if i+1 >= len(os.Args) { + if i + 1 >= len(os.Args) { fmt.Fprintf(os.Stderr, "fatal: --port argument cannot be empty\n") os.Exit(1) } @@ -83,18 +79,14 @@ func main() { } case "--root": - if i+1 >= len(os.Args) { + if i + 1 >= len(os.Args) { fmt.Fprintf(os.Stderr, "fatal: --root argument cannot be empty\n") os.Exit(1) } i++ root = os.Args[i] - if !strings.HasPrefix(root, "/") { - root = "/" + root - } - if !strings.HasSuffix(root, "/") { - root += "/" - } + if !strings.HasPrefix(root, "/") { root = "/" + root } + if !strings.HasSuffix(root, "/") { root += "/" } default: if len(filesDir) > 0 { @@ -122,9 +114,7 @@ func main() { } fmt.Printf("Now hosting \"%s\" at http://%s:%d", filesDir, host, port) - if root != "/" { - fmt.Printf("%s", root) - } + if root != "/" { fmt.Printf("%s", root) } fmt.Println(".") http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), HTTPLog(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -178,7 +168,7 @@ func main() { } if !strings.HasSuffix(r.URL.Path, "/") { - http.Redirect(w, r, r.URL.Path+"/", http.StatusFound) + http.Redirect(w, r, r.URL.Path + "/", http.StatusFound) return } @@ -200,11 +190,11 @@ func main() { } } } - + data := Directory{ - Root: isRoot, - Name: r.URL.Path, - Files: []*File{}, + Root: isRoot, + Name: r.URL.Path, + Files: []*File{}, Readme: readmeHTML, } @@ -212,14 +202,10 @@ func main() { directories, err := fs.ReadDir(fsDir, ".") for _, dir := range directories { name := dir.Name() - if slices.Contains(ignoredFiles, name) { - continue - } + if slices.Contains(ignoredFiles, name) { continue } info, err := dir.Info() - if err != nil { - continue - } + if err != nil { continue } var uri string if isRoot { @@ -250,10 +236,10 @@ func main() { dateStr := info.ModTime().Format("02-Jan-2006 15:04") data.Files = append(data.Files, &File{ - Name: name, - URI: uri, - IsDir: info.IsDir(), - Size: sizeStr, + Name: name, + URI: uri, + IsDir: info.IsDir(), + Size: sizeStr, ModifiedDate: dateStr, }) } @@ -269,60 +255,52 @@ func main() { } type LoggingResponseWriter struct { - http.ResponseWriter - Status int + http.ResponseWriter + Status int } -var COL_Reset = "\033[0m" -var COL_Red = "\033[31m" -var COL_Green = "\033[32m" +var COL_Reset = "\033[0m" +var COL_Red = "\033[31m" +var COL_Green = "\033[32m" var COL_Yellow = "\033[33m" -var COL_Blue = "\033[34m" +var COL_Blue = "\033[34m" var COL_Purple = "\033[35m" -var COL_Cyan = "\033[36m" -var COL_Gray = "\033[37m" -var COL_White = "\033[97m" +var COL_Cyan = "\033[36m" +var COL_Gray = "\033[37m" +var COL_White = "\033[97m" func HTTPLog(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - start := time.Now() + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() - lrw := LoggingResponseWriter{w, http.StatusOK} + lrw := LoggingResponseWriter{w, http.StatusOK} - next.ServeHTTP(&lrw, r) + next.ServeHTTP(&lrw, r) - after := time.Now() - difference := (after.Nanosecond() - start.Nanosecond()) / 1_000_000 - elapsed := "<1" - if difference >= 1 { - elapsed = strconv.Itoa(difference) - } + after := time.Now() + difference := (after.Nanosecond() - start.Nanosecond()) / 1_000_000 + elapsed := "<1" + if difference >= 1 { + elapsed = strconv.Itoa(difference) + } - statusColour := COL_Reset + statusColour := COL_Reset - if lrw.Status-600 <= 0 { - statusColour = COL_Red - } - if lrw.Status-500 <= 0 { - statusColour = COL_Yellow - } - if lrw.Status-400 <= 0 { - statusColour = COL_White - } - if lrw.Status-300 <= 0 { - statusColour = COL_Green - } + if lrw.Status - 600 <= 0 { statusColour = COL_Red } + if lrw.Status - 500 <= 0 { statusColour = COL_Yellow } + if lrw.Status - 400 <= 0 { statusColour = COL_White } + if lrw.Status - 300 <= 0 { statusColour = COL_Green } - fmt.Printf("[%s] %s %s - %s%d%s (%sms) (%s)\n", - after.Format(time.UnixDate), - r.Method, - r.URL.Path, - statusColour, - lrw.Status, - COL_Reset, - elapsed, - r.Header["User-Agent"][0]) - }) + fmt.Printf("[%s] %s %s - %s%d%s (%sms) (%s)\n", + after.Format(time.UnixDate), + r.Method, + r.URL.Path, + statusColour, + lrw.Status, + COL_Reset, + elapsed, + r.Header["User-Agent"][0]) + }) } func printHelp() { From ef3dc28e7ceab083559d1b9f80565ac4cc237703 Mon Sep 17 00:00:00 2001 From: supitszaire Date: Mon, 16 Mar 2026 21:01:53 +0100 Subject: [PATCH 3/4] format fix --- templates/dir.html | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/templates/dir.html b/templates/dir.html index 6d20006..1936d24 100644 --- a/templates/dir.html +++ b/templates/dir.html @@ -56,42 +56,37 @@ .readme { margin-top: 2rem; } - - .readme h1, - .readme h2, - .readme h3, - .readme h4, - .readme h5, - .readme h6 { + + .readme h1, .readme h2, .readme h3, + .readme h4, .readme h5, .readme h6 { color: #f0f0f0; margin-top: 1.2rem; } - - .readme p, - .readme li { + + .readme p, .readme li { line-height: 1.7; color: #d0d0d0; } - + .readme a { color: #b7fd49; } - + .readme a:hover { color: white; text-decoration: underline; } - + .readme code { background: #1e1e1e; border: 1px solid #333; border-radius: 3px; - padding: 0.1em, 0.4em; - font-family: "Monaspace Argon", monospace; - font-size: 0.9em; + padding: .1em, .4em; + font-family: 'Monaspace Argon', monospace; + font-size: .9em; color: #b7fd79; } - + .readme pre { background: #1e1e1e; border: 3px solid #333; @@ -99,35 +94,35 @@ padding: 1em; overflow-x: auto; } - + .readme pre code { border: none; padding: 0; background: transparent; } - + .readme blockquote { border-left: 3px solid #b7fd49; margin: 0; padding-left: 1em; color: #a0a0a0; } - + .readme hr { border-color: #333; } - + .readme table { width: auto; } - + .readme table td, .readme table th { border: 1px solid #333; - padding: 0.3em 0.6em; + padding: .3em .6em; width: auto; } - + footer { padding: 1em 0; } From da76681c29388b9d3a67b5032fc3fac123238b17 Mon Sep 17 00:00:00 2001 From: supitszaire Date: Mon, 16 Mar 2026 21:08:06 +0100 Subject: [PATCH 4/4] removed README header in html --- templates/dir.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/dir.html b/templates/dir.html index 1936d24..b0b9ebf 100644 --- a/templates/dir.html +++ b/templates/dir.html @@ -170,8 +170,6 @@
{{if .Readme}}
-

README

-
{{.Readme}}
{{end}}