|  |  |  | //
 | 
					
						
							|  |  |  | // dir_render.go
 | 
					
						
							|  |  |  | // Copyright (C) 2024 veypi <i@veypi.com>
 | 
					
						
							|  |  |  | // 2024-10-18 19:35
 | 
					
						
							|  |  |  | // Distributed under terms of the GPL license.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package webdav | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"embed" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"html/template" | 
					
						
							|  |  |  | 	"io/fs" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/veypi/utils/logv" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type anyDirs interface { | 
					
						
							|  |  |  | 	len() int | 
					
						
							|  |  |  | 	name(i int) string | 
					
						
							|  |  |  | 	isDir(i int) bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type fileInfoDirs []fs.FileInfo | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (d fileInfoDirs) len() int          { return len(d) } | 
					
						
							|  |  |  | func (d fileInfoDirs) isDir(i int) bool  { return d[i].IsDir() } | 
					
						
							|  |  |  | func (d fileInfoDirs) name(i int) string { return d[i].Name() } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type dirEntryDirs []fs.DirEntry | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (d dirEntryDirs) len() int          { return len(d) } | 
					
						
							|  |  |  | func (d dirEntryDirs) isDir(i int) bool  { return d[i].IsDir() } | 
					
						
							|  |  |  | func (d dirEntryDirs) name(i int) string { return d[i].Name() } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //go:embed dir.html
 | 
					
						
							|  |  |  | var dirTemplate embed.FS // 嵌入文件系统
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func size2Label(s int64) string { | 
					
						
							|  |  |  | 	if s < 1024 { | 
					
						
							|  |  |  | 		return fmt.Sprintf("%d B", s) | 
					
						
							|  |  |  | 	} else if s < 1048576 { | 
					
						
							|  |  |  | 		return fmt.Sprintf("%d KB", s/1024) | 
					
						
							|  |  |  | 	} else if s < 1073741824 { | 
					
						
							|  |  |  | 		return fmt.Sprintf("%d MB", s/1024/1024) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return fmt.Sprintf("%d MB", s/1024/1024/1024) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func dirList(w http.ResponseWriter, r *http.Request, f File, rootPath string, rootIndex int) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Prefer to use ReadDir instead of Readdir,
 | 
					
						
							|  |  |  | 	// because the former doesn't require calling
 | 
					
						
							|  |  |  | 	// Stat on every entry of a directory on Unix.
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if e := recover(); e != nil { | 
					
						
							|  |  |  | 			err = fmt.Errorf("%v", e) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logv.Warn().Msgf("http: error reading directory: %v", err) | 
					
						
							|  |  |  | 			http.Error(w, "Error reading directory", http.StatusInternalServerError) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	dir_count := 0 | 
					
						
							|  |  |  | 	f_count := 0 | 
					
						
							|  |  |  | 	var file_bytes int64 | 
					
						
							|  |  |  | 	files := make([][4]any, 0, 5) | 
					
						
							|  |  |  | 	dirs := make([][4]any, 0, 5) | 
					
						
							|  |  |  | 	if d, ok := f.(fs.ReadDirFile); ok { | 
					
						
							|  |  |  | 		for _, item := range logv.AssertFuncErr(d.ReadDir(-1)) { | 
					
						
							|  |  |  | 			if item.IsDir() { | 
					
						
							|  |  |  | 				name := item.Name() + "/" | 
					
						
							|  |  |  | 				dir_count += 1 | 
					
						
							|  |  |  | 				fstat, _ := item.Info() | 
					
						
							|  |  |  | 				furl := url.URL{Path: name} | 
					
						
							|  |  |  | 				dirs = append(dirs, [4]any{name, furl.String(), "-----", fstat.ModTime().UTC()}) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				name := item.Name() | 
					
						
							|  |  |  | 				f_count += 1 | 
					
						
							|  |  |  | 				fstat, _ := item.Info() | 
					
						
							|  |  |  | 				file_bytes += fstat.Size() | 
					
						
							|  |  |  | 				furl := url.URL{Path: name} | 
					
						
							|  |  |  | 				files = append(files, [4]any{name, furl.String(), size2Label(fstat.Size()), fstat.ModTime().UTC()}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		for _, item := range logv.AssertFuncErr(d.ReadDir(-1)) { | 
					
						
							|  |  |  | 			name := item.Name() | 
					
						
							|  |  |  | 			f_count += 1 | 
					
						
							|  |  |  | 			fstat, _ := item.Info() | 
					
						
							|  |  |  | 			file_bytes += fstat.Size() | 
					
						
							|  |  |  | 			furl := url.URL{Path: name} | 
					
						
							|  |  |  | 			files = append(files, [4]any{name, furl.String(), size2Label(fstat.Size()), fstat.ModTime().UTC()}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dirBody := logv.AssertFuncErr(dirTemplate.ReadFile("dir.html")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.Header().Set("Content-Type", "text/html; charset=utf-8") | 
					
						
							|  |  |  | 	tpl := logv.AssertFuncErr(template.New("").Parse(string(dirBody))) | 
					
						
							|  |  |  | 	pathTag := strings.Split(rootPath, "/") | 
					
						
							|  |  |  | 	logv.AssertError(tpl.Execute(w, map[string]any{"files": files, "dirs": dirs, "path": pathTag[rootIndex:], "root": strings.Join(pathTag[:rootIndex], "/") + "/", "cdir": dir_count, "cfile": f_count, "size": size2Label(file_bytes)})) | 
					
						
							|  |  |  | } |