package main import ( "flag" "fmt" "html/template" "io" "log" "net/http" "os" "path" "slices" "strings" "time" ) type Entry struct { IsDir bool Name string Path string ModifiedAt string } func serveFile(w http.ResponseWriter, r *http.Request, filePath string) error { imgFormats := []string{".png", ".jpeg", ".jpg", ".bmp", ".svg", ".gif", ".avif"} videoFormats := []string{".mp4", ".webm"} for _, format := range imgFormats { if strings.HasSuffix(filePath, format) { http.ServeFile(w, r, filePath) return nil } } for _, format := range videoFormats { if strings.HasSuffix(filePath, format) { http.ServeFile(w, r, filePath) return nil } } if strings.HasSuffix(filePath, ".pdf") { http.ServeFile(w, r, filePath) return nil } f, err := os.OpenFile(filePath, os.O_RDONLY, 0644) if err != nil { return err } defer f.Close() data, err := io.ReadAll(f) if err != nil { return err } w.Header().Set("Content-Type", "text/plain") w.Write(data) return nil } var ( directory string ) func main() { flag.StringVar(&directory, "dir", "./", "directory to list files from") flag.Parse() tmpl := template.Must(template.ParseGlob("./views/**")) mux := http.NewServeMux() mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) { sortValue := r.URL.Query().Get("sort") if sortValue == "" { sortValue = "oldest" } directoriesFirst := r.URL.Query().Has("directoriesFirst") suffix := r.URL.Path dirPath := path.Join(directory, suffix) stat, err := os.Stat(dirPath) if err != nil { http.Error(w, err.Error(), 500) return } if !stat.IsDir() { err = serveFile(w, r, dirPath) if err != nil { http.Error(w, err.Error(), 500) } return } files, err := os.ReadDir(dirPath) if err != nil { http.Error(w, err.Error(), 500) return } entries := []Entry{} for _, file := range files { info, err := file.Info() if err != nil { fmt.Printf("error reading info for file %s: %v\n", file.Name(), err) continue } entries = append(entries, Entry{ IsDir: file.IsDir(), Name: file.Name(), Path: path.Join(suffix, file.Name()), ModifiedAt: info.ModTime().Format(time.DateTime), }) } slices.SortFunc(entries, func(a, b Entry) int { if directoriesFirst { if a.IsDir && !b.IsDir { return -1 } if b.IsDir && !a.IsDir { return 1 } } t1, err := time.Parse(time.DateTime, a.ModifiedAt) t2, err := time.Parse(time.DateTime, b.ModifiedAt) if err != nil { return 0 } switch sortValue { case "oldest": return t1.Compare(t2) case "newest": return t2.Compare(t1) default: return 0 } }) tmpl.ExecuteTemplate(w, "index.html", struct { Entries []Entry }{entries}) }) mux.HandleFunc("POST /delete", func(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, err.Error(), 500) return } files := []string{} for k, v := range r.Form { if k == "files" { files = v } } for _, filePath := range files { fullPath := path.Join(directory, filePath) stat, err := os.Stat(fullPath) if err != nil { continue } if stat.IsDir() { continue // for now } if err := os.Remove(fullPath); err != nil { fmt.Printf("couldn't delete file %s: %v\n", fullPath, err) } } http.Redirect(w, r, "/", http.StatusFound) }) if err := http.ListenAndServe(":5000", mux); err != nil { log.Fatalf("failed to start http server: %v\n", err) } }