Use custom dirlist

This commit is contained in:
Melon 2024-03-18 13:20:37 +00:00
parent aa32eec589
commit d7b6f38f40
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
6 changed files with 172 additions and 73 deletions

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.22
require (
github.com/MrMelon54/exit-reload v0.0.1
github.com/dustin/go-humanize v1.0.1
github.com/golang-migrate/migrate/v4 v4.17.0
github.com/julienschmidt/httprouter v1.3.0
github.com/thanhpk/randstr v1.0.6

2
go.sum
View File

@ -2,6 +2,8 @@ github.com/MrMelon54/exit-reload v0.0.1 h1:sxHa59tNEQMcikwuX2+93lw6Vi1+R7oCRF8a0
github.com/MrMelon54/exit-reload v0.0.1/go.mod h1:PLiSfmUzwdpTTQP3BBfUPhkqPwaIZjx0DuXBnM76Bug=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU=
github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=

126
routes/dirlist.go Normal file
View File

@ -0,0 +1,126 @@
package routes
import (
_ "embed"
"github.com/dustin/go-humanize"
"github.com/julienschmidt/httprouter"
"html/template"
"io"
"log"
"net/http"
"os"
"path"
"path/filepath"
"strings"
)
//go:embed dirlist.go.html
var dirListHtml string
var dirListTemplate = template.Must(template.New("dirlist").Parse(dirListHtml))
func (r *routeCtx) handleFiles(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
if containsDotDot(req.URL.Path) {
http.Error(rw, "invalid URL path", http.StatusBadRequest)
return
}
if strings.HasSuffix(req.URL.Path, "/") {
r.handleDirList(rw, req)
return
}
open, err := os.Open(filepath.Join(r.basePath, req.URL.Path))
if err != nil {
http.Error(rw, "404 Not Found", http.StatusNotFound)
return
}
stat, err := open.Stat()
if err != nil {
http.Error(rw, "500 Internal Server Error: Failed to stat file", http.StatusInternalServerError)
return
}
http.ServeContent(rw, req, open.Name(), stat.ModTime(), open)
}
type fileInfo struct {
Name string
URL string
Size string
ModTime string
}
func (r *routeCtx) handleDirList(rw http.ResponseWriter, req *http.Request) {
openDir, err := os.ReadDir(filepath.Join(r.basePath, req.URL.Path))
if err != nil {
http.Error(rw, "404 Not Found", http.StatusNotFound)
return
}
fileInfos := make([]*fileInfo, len(openDir))
for i := range openDir {
info, err := openDir[i].Info()
if err != nil {
http.Error(rw, "500 Internal Server Error: Failed to stat file", http.StatusInternalServerError)
return
}
url := path.Join(req.URL.Path, info.Name())
name := path.Base(url)
size := ""
if info.IsDir() {
url += "/"
name += "/"
} else {
size = humanize.IBytes(uint64(info.Size()))
}
fileInfos[i] = &fileInfo{
Name: name,
URL: url,
Size: size,
ModTime: info.ModTime().Format("2006-01-02 15:04:05 -0700"),
}
}
err = dirListTemplate.Execute(rw, map[string]any{
"Name": r.name,
"Path": req.URL.Path,
"Files": fileInfos,
})
if err != nil {
log.Println("[GoMVN] Index template error: ", err)
}
}
func (r *routeCtx) handlePut(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
p, err := r.pathUtils.ParsePath(req)
if err != nil {
http.Error(rw, "404 Not Found", http.StatusNotFound)
return
}
p = filepath.Join(r.basePath, p)
err = os.MkdirAll(filepath.Dir(p), os.ModePerm)
if err != nil {
http.Error(rw, "500 Failed to create directory", http.StatusInternalServerError)
return
}
create, err := os.Create(p)
if err != nil {
http.Error(rw, "500 Failed to open file", http.StatusInternalServerError)
return
}
_, err = io.Copy(create, req.Body)
if err != nil {
http.Error(rw, "500 Failed to write file", http.StatusInternalServerError)
return
}
}
func containsDotDot(v string) bool {
if !strings.Contains(v, "..") {
return false
}
for _, ent := range strings.FieldsFunc(v, isSlashRune) {
if ent == ".." {
return true
}
}
return false
}
func isSlashRune(r rune) bool { return r == '/' || r == '\\' }

41
routes/dirlist.go.html Normal file
View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ .Name }} - {{ .Path }}</title>
<style>
html, body {
font-size: 13px;
font-family: monospace;
}
table tr.header {
border-bottom-color: #3a3e41;
}
table tr td {
padding-inline: 10px;
}
.bold {
font-weight: bold;
}
</style>
</head>
<body>
<h1>{{.Name}}</h1>
<table>
<tr class="header">
<th>Name</th>
<th>Last Modified</th>
<th>Size</th>
</tr>
{{range .Files}}
<tr>
<td class="bold"><a href="{{ .URL }}">{{ .Name }}</a></td>
<td>{{ .ModTime }}</td>
<td>{{ .Size }}</td>
</tr>
{{end}}
</table>
</body>
</html>

View File

@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>GoMVN - {{.Name}}</title>
<style>
.dir {
font-weight: bold;
font-family: monospace;
}
</style>
</head>
<body>
<h1>{{.Name}}</h1>
{{range $k, $v := .Repositories}}
<ul>
{{range $v}}
<li><a href="{{ $k }}/{{ .GetPath }}" class="dir">{{ .MvnGroup }}:{{ .Artifact }}:{{ .Version }}</a>, last modified {{ .Modified }}</li>
{{end}}
</ul>
{{end}}
</body>
</html>

View File

@ -7,13 +7,8 @@ import (
"github.com/1f349/gomvn/paths"
"github.com/julienschmidt/httprouter"
"github.com/thanhpk/randstr"
"html/template"
"io"
"log"
"net/http"
"os"
"path"
"path/filepath"
)
type routeCtx struct {
@ -63,9 +58,9 @@ func Router(db *database.Queries, name, basePath string, repository []string) ht
rWeb := httprouter.New()
rWeb.PUT("/*filepath", base.repoAuth(base.handlePut))
rWeb.GET("/", base.handleIndex)
rWeb.GET("/", base.handleFiles)
for _, repo := range repository {
rWeb.ServeFiles(path.Join("/", repo, "*filepath"), http.FS(os.DirFS(filepath.Join(basePath, repo))))
rWeb.GET(path.Join("/", repo, "*filepath"), base.handleFiles)
}
mux := http.NewServeMux()
@ -85,47 +80,3 @@ func Router(db *database.Queries, name, basePath string, repository []string) ht
return mux
}
//go:embed index.go.html
var indexHtml string
var indexTemplate = template.Must(template.New("index").Parse(indexHtml))
func (r *routeCtx) handleIndex(rw http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
repositories, err := paths.GetRepositories(r.basePath, r.repository)
if err != nil {
http.Error(rw, "500 Internal Server Error", http.StatusInternalServerError)
return
}
err = indexTemplate.Execute(rw, map[string]any{
"Name": r.name,
"Repositories": repositories,
})
if err != nil {
log.Println("[GoMVN] Index template error: ", err)
}
}
func (r *routeCtx) handlePut(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
p, err := r.pathUtils.ParsePath(req)
if err != nil {
http.Error(rw, "404 Not Found", http.StatusNotFound)
return
}
p = filepath.Join(r.basePath, p)
err = os.MkdirAll(filepath.Dir(p), os.ModePerm)
if err != nil {
http.Error(rw, "500 Failed to create directory", http.StatusInternalServerError)
return
}
create, err := os.Create(p)
if err != nil {
http.Error(rw, "500 Failed to open file", http.StatusInternalServerError)
return
}
_, err = io.Copy(create, req.Body)
if err != nil {
http.Error(rw, "500 Failed to write file", http.StatusInternalServerError)
return
}
}