diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 302562e..2a5ef60 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -34,6 +34,7 @@ func New(dir string) *Handler { "urlenc": url.PathEscape, "base": filepath.Base, "add1": func(i int) int { return i + 1 }, + "trimExt": func(s string) string { return strings.TrimSuffix(s, filepath.Ext(s)) }, }).Parse(allTemplates), ) return h @@ -44,9 +45,12 @@ func New(dir string) *Handler { // ----------------------------------------------------------------------- type isoInfo struct { - Name string - Size int64 - ModTime time.Time + Name string + Size int64 + ModTime time.Time + Description string + HasImage bool + ImageExt string } func (h *Handler) ListISOs(w http.ResponseWriter, r *http.Request) { @@ -70,7 +74,31 @@ func (h *Handler) ListISOs(w http.ResponseWriter, r *http.Request) { if err != nil { continue } - isos = append(isos, isoInfo{Name: e.Name(), Size: info.Size(), ModTime: info.ModTime()}) + + baseName := strings.TrimSuffix(e.Name(), filepath.Ext(e.Name())) + + // Look for Description (.txt) + desc, _ := os.ReadFile(filepath.Join(h.dir, baseName+".txt")) + + // Look for Image (.png, .jpg, .jpeg) + hasImg := false + imgExt := "" + for _, ext := range []string{".png", ".jpg", ".jpeg"} { + if _, err := os.Stat(filepath.Join(h.dir, baseName+ext)); err == nil { + hasImg = true + imgExt = ext + break + } + } + + isos = append(isos, isoInfo{ + Name: e.Name(), + Size: info.Size(), + ModTime: info.ModTime(), + Description: string(desc), + HasImage: hasImg, + ImageExt: imgExt, + }) } sort.Slice(isos, func(i, j int) bool { @@ -119,7 +147,6 @@ func (h *Handler) BrowseISO(w http.ResponseWriter, r *http.Request) { return } - // Dirs first, then files; both alphabetically. sort.Slice(entries, func(i, j int) bool { if entries[i].IsDir != entries[j].IsDir { return entries[i].IsDir @@ -165,12 +192,25 @@ func (h *Handler) DownloadFile(w http.ResponseWriter, r *http.Request) { defer rc.Close() filename := filepath.Base(internalPath) - ct := mime.TypeByExtension(filepath.Ext(filename)) + ext := strings.ToLower(filepath.Ext(filename)) + ct := mime.TypeByExtension(ext) if ct == "" { ct = "application/octet-stream" } - w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename=%q`, filename)) + // Determine if browser can/should view it inline + viewable := false + switch ext { + case ".pdf", ".txt", ".jpg", ".jpeg", ".png", ".gif", ".mp4", ".mp3", ".webp", ".svg": + viewable = true + } + + if !viewable { + w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename=%q`, filename)) + } else { + w.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename=%q`, filename)) + } + w.Header().Set("Content-Type", ct) w.Header().Set("Content-Length", fmt.Sprintf("%d", size)) @@ -179,6 +219,21 @@ func (h *Handler) DownloadFile(w http.ResponseWriter, r *http.Request) { } } +// ----------------------------------------------------------------------- +// Route: GET /raw/{path...} → serve actual files from the disk +// ----------------------------------------------------------------------- + +func (h *Handler) RawFile(w http.ResponseWriter, r *http.Request) { + fileName := strings.TrimPrefix(r.URL.Path, "/raw/") + if fileName == "" || strings.Contains(fileName, "..") { + http.Error(w, "invalid path", http.StatusBadRequest) + return + } + + fullPath := filepath.Join(h.dir, fileName) + http.ServeFile(w, r, fullPath) +} + // ----------------------------------------------------------------------- // Helpers // ----------------------------------------------------------------------- @@ -193,7 +248,6 @@ func (h *Handler) openISO(name string) (*iso.Reader, error) { return iso.Open(filepath.Join(h.dir, name)) } -// parsePath splits "/browse/myfile.iso/some/dir" → ("myfile.iso", "some/dir", true). func parsePath(urlPath, prefix string) (isoName, internalPath string, ok bool) { rest := strings.TrimPrefix(urlPath, prefix) if rest == "" { @@ -280,7 +334,6 @@ func fileIcon(name string) string { } } -// allTemplates contains both the index and browse HTML templates. const allTemplates = ` {{define "css"}}