diff options
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/html/autoindex.go | 68 | ||||
| -rw-r--r-- | pkg/html/notfound.go | 25 | ||||
| -rw-r--r-- | pkg/server/serve.go (renamed from pkg/server/handle.go) | 4 | ||||
| -rw-r--r-- | pkg/site/fs.go | 84 | ||||
| -rw-r--r-- | pkg/site/site.go | 2 |
5 files changed, 163 insertions, 20 deletions
diff --git a/pkg/html/autoindex.go b/pkg/html/autoindex.go new file mode 100644 index 0000000..4c31134 --- /dev/null +++ b/pkg/html/autoindex.go @@ -0,0 +1,68 @@ +package html + +import ( + "fmt" + "strconv" + + . "github.com/LMBishop/scrapbook/web/skeleton" + . "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +type File struct { + Name string + IsDir bool + Size int64 + Mtime int +} + +func IndexPage(dir string, err bool, files []File) Node { + return Page("Index of "+dir, + H1(Text("Index of "+dir)), + + Div( + Class("table files-table"), + Group{ + Span( + Class("header name"), + Text("Name"), + ), + Span( + Class("header size"), + Text("Size"), + ), + Span( + Class("header last-modified"), + Text("Last modified"), + ), + }, + + If(files != nil, Map(files, func(file File) Node { + var fileName string + if file.IsDir { + fileName = file.Name + "/" + } else { + fileName = file.Name + } + return Group{ + A( + Class("name"), + Href(fileName), + Text(fileName), + ), + Span( + Class("size"), + If(file.IsDir, Text("--")), + If(!file.IsDir, Text(strconv.FormatInt(file.Size, 10)+" bytes")), + ), + Span( + Class("last-modified"), + Text("--"), + ), + } + })), + ), + + If(err, AlertError(fmt.Sprintf("Failed to list directory"))), + ) +} diff --git a/pkg/html/notfound.go b/pkg/html/notfound.go new file mode 100644 index 0000000..ceb521f --- /dev/null +++ b/pkg/html/notfound.go @@ -0,0 +1,25 @@ +package html + +import ( + "fmt" + + . "github.com/LMBishop/scrapbook/web/skeleton" + . "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +func NotFoundUrlPage(url, host string) Node { + return Page("Page not found", + H1(Text("Page not found")), + + P(Text(fmt.Sprintf("The URL %s could not be found on site %s", url, host))), + ) +} + +func NotFoundSitePage(host string) Node { + return Page("Site not found", + H1(Text("Site not found")), + + P(Text(fmt.Sprintf("The site %s is unknown", host))), + ) +} diff --git a/pkg/server/handle.go b/pkg/server/serve.go index afe65ce..9e7b722 100644 --- a/pkg/server/handle.go +++ b/pkg/server/serve.go @@ -1,9 +1,9 @@ package server import ( - "fmt" "net/http" + "github.com/LMBishop/scrapbook/pkg/html" "github.com/LMBishop/scrapbook/pkg/index" ) @@ -12,7 +12,7 @@ func ServeSite(siteIndex *index.SiteIndex) func(w http.ResponseWriter, r *http.R site := siteIndex.GetSiteByHost(r.Host) if site == nil { w.WriteHeader(http.StatusNotFound) - fmt.Fprintf(w, "unknown host %s", r.Host) + html.NotFoundSitePage(r.Host) return } diff --git a/pkg/site/fs.go b/pkg/site/fs.go index e8ee7fc..c9bbe21 100644 --- a/pkg/site/fs.go +++ b/pkg/site/fs.go @@ -1,45 +1,95 @@ package site import ( + "log/slog" "net/http" + "os" "path/filepath" "strings" + + "github.com/LMBishop/scrapbook/pkg/config" + "github.com/LMBishop/scrapbook/pkg/html" ) -type siteFS struct { - fs http.FileSystem +type SiteFileServer struct { + root http.FileSystem + siteConfig *config.SiteConfig +} + +func NewSiteFileServer(root http.FileSystem, siteConfig *config.SiteConfig) *SiteFileServer { + return &SiteFileServer{root: root, siteConfig: siteConfig} } -func (sfs siteFS) Open(path string) (http.File, error) { - f, err := sfs.fs.Open(path) +func (fs *SiteFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + path := filepath.Clean(r.URL.Path) + + file, err := fs.root.Open(path) if err != nil { if strings.HasSuffix(path, ".html") { - return nil, err + html.NotFoundUrlPage(path, fs.siteConfig.Host).Render(w) + return } htmlPath := path + ".html" - f, err = sfs.fs.Open(htmlPath) + file, err = fs.root.Open(htmlPath) if err != nil { - return nil, err + html.NotFoundUrlPage(path, fs.siteConfig.Host).Render(w) + return } } + defer file.Close() - s, err := f.Stat() + info, err := file.Stat() if err != nil { - return nil, err + html.NotFoundUrlPage(path, fs.siteConfig.Host).Render(w) + return } - if s.IsDir() { - index := filepath.Join(path, "index.html") - if _, err := sfs.fs.Open(index); err != nil { - closeErr := f.Close() - if closeErr != nil { - return nil, closeErr + if info.IsDir() { + indexPath := filepath.Join(path, "index.html") + if _, err := fs.root.Open(indexPath); os.IsNotExist(err) { + if fs.siteConfig.Flags&config.FlagIndex == 0 { + html.NotFoundUrlPage(path, fs.siteConfig.Host).Render(w) + return } + files, err := fs.listFiles(path) + if path != "/" { + files = append([]html.File{{Name: "..", IsDir: true, Size: 0}}, files...) + } + if err != nil { + html.IndexPage(path, true, files).Render(w) + slog.Error("could not list directory for index page generation", "host", fs.siteConfig.Host, "path", path, "error", err) + } else { + html.IndexPage(path, false, files).Render(w) + } + return + } + http.ServeFile(w, r, indexPath) + } else { + http.ServeFile(w, r, path) + } +} + +func (fs *SiteFileServer) listFiles(dir string) ([]html.File, error) { + file, err := fs.root.Open(dir) + if err != nil { + return nil, err + } + defer file.Close() + + entries, err := file.Readdir(-1) + if err != nil { + return nil, err + } - return nil, err + var files []html.File + for _, entry := range entries { + if !entry.IsDir() { + files = append(files, html.File{Name: entry.Name(), IsDir: false, Size: entry.Size()}) + } else { + files = append(files, html.File{Name: entry.Name(), IsDir: true, Size: 0}) } } - return f, nil + return files, nil } diff --git a/pkg/site/site.go b/pkg/site/site.go index 82f25ad..8b7af41 100644 --- a/pkg/site/site.go +++ b/pkg/site/site.go @@ -30,7 +30,7 @@ func NewSite(name string, dir string, config *config.SiteConfig) *Site { site.Name = name site.Path = dir site.SiteConfig = config - site.Handler = http.FileServer(siteFS{http.Dir(path.Join(dir, "default"))}) + site.Handler = NewSiteFileServer(http.Dir(path.Join(dir, "default")), config) return &site } |
