--- /dev/null
+/*
+GoCheese -- Python private package repository and caching proxy
+Copyright (C) 2019-2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package main
+
+import (
+ "bytes"
+ "html/template"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "sort"
+ "strings"
+)
+
+var (
+ HRRootTmpl = template.Must(template.New("hr-root").Parse(`<!DOCTYPE html>
+<html>
+ <head>
+ <title>{{.Version}}: human readable listing</title>
+ </head>
+ <body>
+ <ul>{{range .Packages}}
+ <li><a href="/hr/{{.}}/">{{.}}</a></li>
+{{- end}}
+ </ul>
+ </body>
+</html>
+`))
+ HRPkgTmpl = template.Must(template.New("hr-pkg").Parse(`<!DOCTYPE html>
+<html>
+ <head>
+ <title>{{.Version}}: package {{.PkgName}}</title>
+ </head>
+ <body>
+ <dl>
+ {{with .Info.Name}}<dt>Name</dt><dd>{{.}}</dd>{{end}}
+ {{with .Info.Version}}<dt>Version</dt><dd>{{.}}</dd>{{end}}
+
+ {{with .Info.Platform}}<dt>Platform</dt><dd><ul>
+ {{range .}}<li>{{.}}</li>
+ {{end}}</ul></dd>{{end}}
+
+ {{with .Info.SupportedPlatform}}<dt>SupportedPlatform</dt><dd><ul>
+ {{range .}}<li>{{.}}</li>
+ {{end}}</ul></dd>{{end}}
+
+ <dt>Summary</dt><dd>{{.Info.Summary}}</dd>
+ <dt>Description</dt><dd><pre>
+{{.Info.Description}}
+ </pre></dd>
+
+ {{with .Info.DescriptionContentType}}<dt>DescriptionContentType</dt><dd>{{.}}</dd>{{end}}
+ {{with .Info.Keywords}}<dt>Keywords</dt><dd>{{.}}</dd>{{end}}
+ {{with .Info.HomePage}}<dt>HomePage</dt><dd>{{.}}</dd>{{end}}
+ {{with .Info.Author}}<dt>Author</dt><dd>{{.}}</dd>{{end}}
+ {{with .Info.AuthorEmail}}<dt>AuthorEmail</dt><dd>{{.}}</dd>{{end}}
+ {{with .Info.Maintainer}}<dt>Maintainer</dt><dd>{{.}}</dd>{{end}}
+ {{with .Info.MaintainerEmail}}<dt>MaintainerEmail</dt><dd>{{.}}</dd>{{end}}
+ {{with .Info.License}}<dt>License</dt><dd>{{.}}</dd>{{end}}
+
+ {{with .Info.Classifier}}<dt>Classifier</dt><dd><ul>
+ {{range .}}<li>{{.}}</li>
+ {{end}}</ul></dd>{{end}}
+
+ {{with .Info.RequiresDist}}<dt>RequiresDist</dt><dd><ul>
+ {{range .}}<li>{{.}}</li>
+ {{end}}</ul></dd>{{end}}
+
+ {{with .Info.RequiresPython}}<dt>RequiresPython</dt><dd>{{.}}</dd>{{end}}
+
+ {{with .Info.RequiresExternal}}<dt>RequiresExternal</dt><dd><ul>
+ {{range .}}<li>{{.}}</li>
+ {{end}}</ul></dd>{{end}}
+
+ {{with .Info.ProjectURL}}<dt>ProjectURL</dt><dd><ul>
+ {{range .}}<li>{{.}}</li>
+ {{end}}</ul></dd>{{end}}
+
+ {{with .Info.ProvidesExtra}}<dt>ProvidesExtra</dt><dd><ul>
+ {{range .}}<li>{{.}}</li>
+ {{end}}</ul></dd>{{end}}
+ </dl>
+
+ <h2>Releases</h2>
+ <table border=1>
+ <tr>
+ <th>Filename</th>
+ <th>Version</th>
+ <th>Uploaded</th>
+ <th>Size</th>
+ <th>Digests</th>
+ </tr>
+ {{range .Releases}}{{if .Size}}
+ <tr>
+ <td>{{.Filename}}</th>
+ <td>{{.Version}}</th>
+ <td>{{.UploadTimeISO8601}}</th>
+ <td>{{.Size}}</th>
+ <td><ul>{{range $a, $d := .Digests}}
+ <li>{{$a}}: <tt>{{$d}}</tt></li>
+ {{end}}</ul></td>
+ </tr>{{end}}{{end}}
+ </table>
+ </body>
+</html>
+`))
+)
+
+func serveHRRoot(w http.ResponseWriter, r *http.Request) {
+ files, err := ioutil.ReadDir(*Root)
+ if err != nil {
+ log.Println("error", r.RemoteAddr, "hr-root", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ packages := make([]string, 0, len(files))
+ for _, f := range files {
+ packages = append(packages, f.Name())
+ }
+ sort.Strings(packages)
+ var buf bytes.Buffer
+ err = HRRootTmpl.Execute(&buf, struct {
+ Version string
+ Packages []string
+ }{
+ Version: UserAgent,
+ Packages: packages,
+ })
+ if err != nil {
+ log.Println("error", r.RemoteAddr, "hr-root", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ w.Write(buf.Bytes())
+}
+
+func serveHRPkg(w http.ResponseWriter, r *http.Request) {
+ cols := strings.Split(strings.TrimRight(r.URL.Path, "/"), "/")
+ pkgName := cols[len(cols)-1]
+ meta, releases, err := getMetadata(pkgName, "")
+ if err != nil {
+ if os.IsNotExist(err) {
+ http.NotFound(w, r)
+ } else {
+ log.Println("error", r.RemoteAddr, "json", pkgName, err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+ return
+ }
+ var buf bytes.Buffer
+ err = HRPkgTmpl.Execute(&buf, struct {
+ Version string
+ PkgName string
+ Info PkgInfo
+ Releases []*PkgReleaseInfo
+ }{
+ Version: UserAgent,
+ PkgName: pkgName,
+ Info: meta.Info,
+ Releases: releases,
+ })
+ if err != nil {
+ log.Println("error", r.RemoteAddr, "root", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ w.Write(buf.Bytes())
+}