X-Git-Url: http://www.git.cypherpunks.ru/?p=gocheese.git;a=blobdiff_plain;f=list.go;h=8567877d336b49a3c51ffceb63ef942399d086da;hp=d8bd0732459d04fc805e1f6562418d71dae4c00b;hb=HEAD;hpb=60834a0713d5dcc6a9911511cb8618ce7358c824 diff --git a/list.go b/list.go index d8bd073..779e348 100644 --- a/list.go +++ b/list.go @@ -1,72 +1,53 @@ -/* -GoCheese -- Python private package repository and caching proxy -Copyright (C) 2019-2021 Sergey Matveev - -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 . -*/ +// GoCheese -- Python private package repository and caching proxy +// Copyright (C) 2019-2024 Sergey Matveev +// +// 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 . package main import ( "bytes" + _ "embed" "encoding/hex" "errors" + "fmt" "html/template" "io/fs" - "io/ioutil" "log" "net/http" "os" "path/filepath" "sort" + "strconv" "strings" "time" ) // https://warehouse.pypa.io/api-reference/legacy.html var ( - HTMLRootTmpl = template.Must(template.New("root").Parse(` - - - - Links for root - - {{$Refresh := .RefreshURLPath}}{{range .Packages}} - {{.}}
-{{- end}} - - -`)) - HTMLReleasesTmpl = template.Must(template.New("list").Parse(` - - - - Links for {{.PkgName}} - - {{$Refresh := .RefreshURLPath}}{{$PkgName := .PkgName}}{{range .Releases}} - {{.Filename}}
-{{- end}} - - -`)) - KnownExts = []string{".tar.bz2", ".tar.gz", ".whl", ".zip", ".egg", + //go:embed root.tmpl + HTMLRootTmplRaw string + HTMLRootTmpl = template.Must(template.New("root").Parse(HTMLRootTmplRaw)) + + //go:embed list.tmpl + HTMLReleasesTmplRaw string + HTMLReleasesTmpl = template.Must(template.New("list").Parse(HTMLReleasesTmplRaw)) + KnownExts = []string{".tar.bz2", ".tar.gz", ".whl", ".zip", ".egg", ".exe", ".dmg", ".msi", ".rpm", ".deb", ".tgz"} ) func listRoot(w http.ResponseWriter, r *http.Request) { - files, err := ioutil.ReadDir(*Root) + files, err := os.ReadDir(Root) if err != nil { log.Println("error", r.RemoteAddr, "root", err) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -113,7 +94,6 @@ func (a PkgReleaseInfoByName) Less(i, j int) bool { // Version format is too complicated: https://www.python.org/dev/peps/pep-0386/ // So here is very simple parser working good enough for most packages func filenameToVersion(fn string) string { - fn = strings.TrimSuffix(fn, GPGSigExt) var trimmed string for _, ext := range KnownExts { trimmed = strings.TrimSuffix(fn, ext) @@ -137,8 +117,8 @@ func filenameToVersion(fn string) string { return cols[0] } -func listDir(pkgName string, doSize bool) (int, []*PkgReleaseInfo, error) { - dirPath := filepath.Join(*Root, pkgName) +func listDir(pkgName string, doSize bool) (int64, []*PkgReleaseInfo, error) { + dirPath := filepath.Join(Root, pkgName) entries, err := os.ReadDir(dirPath) if err != nil { return 0, nil, err @@ -163,7 +143,7 @@ func listDir(pkgName string, doSize bool) (int, []*PkgReleaseInfo, error) { continue } delete(files, fn) - digest, err := ioutil.ReadFile(filepath.Join(dirPath, fn)) + digest, err := os.ReadFile(filepath.Join(dirPath, fn)) if err != nil { return 0, nil, err } @@ -193,10 +173,6 @@ func listDir(pkgName string, doSize bool) (int, []*PkgReleaseInfo, error) { } delete(files, fnClean) } - if _, exists := files[fnClean+GPGSigExt]; exists { - release.HasSig = true - delete(files, fnClean+GPGSigExt) - } } release.Digests[algo] = hex.EncodeToString(digest) } @@ -206,25 +182,33 @@ func listDir(pkgName string, doSize bool) (int, []*PkgReleaseInfo, error) { releases = append(releases, release) } sort.Sort(PkgReleaseInfoByName(releases)) - return len(entries), releases, nil + fi, err := os.Stat(dirPath) + if err != nil { + return 0, nil, err + } + serial := fi.ModTime().Unix() + if fi, err = os.Stat(filepath.Join(dirPath, MDFile)); err == nil { + serial += fi.ModTime().Unix() + } + return serial, releases, nil } func serveListDir( w http.ResponseWriter, r *http.Request, pkgName string, - autorefresh, gpgUpdate bool, + autorefresh bool, ) { - dirPath := filepath.Join(*Root, pkgName) + dirPath := filepath.Join(Root, pkgName) if autorefresh { - if !refreshDir(w, r, pkgName, "", gpgUpdate) { + if !refreshDir(w, r, pkgName, "") { return } } else if _, err := os.Stat(dirPath); os.IsNotExist(err) && - !refreshDir(w, r, pkgName, "", false) { + !refreshDir(w, r, pkgName, "") { return } - _, releases, err := listDir(pkgName, false) + serial, releases, err := listDir(pkgName, false) if err != nil { log.Println("error", r.RemoteAddr, "list", pkgName, err) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -258,5 +242,7 @@ func serveListDir( http.Error(w, err.Error(), http.StatusInternalServerError) return } + w.Header().Set("X-PyPI-Last-Serial", strconv.FormatInt(serial, 10)) w.Write(buf.Bytes()) + w.Write([]byte(fmt.Sprintf("\n", serial))) }