doc/storage.texi | 5 +++-- list.go | 20 ++++++++++++++++---- metadata.go | 2 +- diff --git a/doc/storage.texi b/doc/storage.texi index a787445fdef5fddad3de4fb9918b50861f298dc2210a309706eb0c9fb70a5848..85c382e65d42036aaab6563263319e24fd6b1b60d04288065d944def40a4440e 100644 --- a/doc/storage.texi +++ b/doc/storage.texi @@ -46,7 +46,8 @@ @file{.asc} signature file. @file{private-package} is private package, because it contains @file{.internal} file. It can be uploaded and queries to it are not -proxied to upstream PyPI. You have to create it manually. If you upload -GPG signature, then it will be also stored. +proxied to upstream PyPI. You have to create it manually. Each packages release file has @code{mtime} set to its upload time. +Package's serial is a sum of @code{mtime}s of the directory and +@file{.metadata.rec} (if it exists). diff --git a/list.go b/list.go index 69e5085640ae6e1696d1f4189f58644ceab504a0a6929fc47e6db7828db1580f..ce1418c765de67cd028712846f511ada63cc8ba1c51521a02b71c72e9eb5aa45 100644 --- a/list.go +++ b/list.go @@ -21,6 +21,7 @@ import ( "bytes" "encoding/hex" "errors" + "fmt" "html/template" "io/fs" "io/ioutil" @@ -29,6 +30,7 @@ "net/http" "os" "path/filepath" "sort" + "strconv" "strings" "time" ) @@ -39,7 +41,7 @@ HTMLRootTmpl = template.Must(template.New("root").Parse(` - Links for root + Simple index {{$Refresh := .RefreshURLPath}}{{range .Packages}} {{.}}
@@ -137,7 +139,7 @@ } return cols[0] } -func listDir(pkgName string, doSize bool) (int, []*PkgReleaseInfo, error) { +func listDir(pkgName string, doSize bool) (int64, []*PkgReleaseInfo, error) { dirPath := filepath.Join(Root, pkgName) entries, err := os.ReadDir(dirPath) if err != nil { @@ -206,7 +208,15 @@ for _, release := range releaseFiles { 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, MetadataFile)); err == nil { + serial += fi.ModTime().Unix() + } + return serial, releases, nil } func serveListDir( @@ -224,7 +234,7 @@ } else if _, err := os.Stat(dirPath); os.IsNotExist(err) && !refreshDir(w, r, pkgName, "", false) { 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 +268,7 @@ log.Println("error", r.RemoteAddr, "list", pkgName, err) 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))) } diff --git a/metadata.go b/metadata.go index b0626affeb23d12d12a4f0161498784dcb735cfdf99f70dd3fee10fccc4f8b39..2034d9ebb0966fbc2c10cf806bc0a7f33f86eeae2dd08ccf4aff57e960315dbe 100644 --- a/metadata.go +++ b/metadata.go @@ -108,7 +108,7 @@ } type PkgMeta struct { Info PkgInfo `json:"info"` - LastSerial int `json:"last_serial"` + LastSerial int64 `json:"last_serial"` Releases map[string][]*PkgReleaseInfo `json:"releases"` URLs []*PkgReleaseInfo `json:"urls"` }