2 GoCheese -- Python private package repository and caching proxy
3 Copyright (C) 2019-2023 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 "go.cypherpunks.ru/recfile"
32 func getMD(pkgName, version string) (*PkgMeta, []*PkgReleaseInfo, error) {
33 serial, releases, err := listDir(pkgName, true)
37 md, err := os.ReadFile(filepath.Join(Root, pkgName, MDFile))
39 if !os.IsNotExist(err) {
43 info := PkgInfo{Name: pkgName}
45 info.Version = releases[len(releases)-1].Version
47 m, err := recfile.NewReader(bytes.NewReader(md)).NextMapWithSlice()
51 if v, ok := m[MDFieldToRecField[MDFieldVersion]]; ok {
54 if v, ok := m[MDFieldToRecField[MDFieldSummary]]; ok {
57 if v, ok := m[MDFieldToRecField[MDFieldDescriptionContentType]]; ok {
58 info.DescriptionContentType = v[0]
60 if v, ok := m[MDFieldToRecField[MDFieldKeywords]]; ok {
63 if v, ok := m[MDFieldToRecField[MDFieldHomePage]]; ok {
66 if v, ok := m[MDFieldToRecField[MDFieldAuthor]]; ok {
69 if v, ok := m[MDFieldToRecField[MDFieldAuthorEmail]]; ok {
70 info.AuthorEmail = v[0]
72 if v, ok := m[MDFieldToRecField[MDFieldMaintainer]]; ok {
73 info.Maintainer = v[0]
75 if v, ok := m[MDFieldToRecField[MDFieldMaintainerEmail]]; ok {
76 info.MaintainerEmail = v[0]
78 if v, ok := m[MDFieldToRecField[MDFieldLicense]]; ok {
81 if v, ok := m[MDFieldToRecField[MDFieldRequiresPython]]; ok {
82 info.RequiresPython = v[0]
84 if v, ok := m[MDFieldToRecField[MDFieldDescription]]; ok {
85 info.Description = v[0]
87 info.Classifier = m[MDFieldToRecField[MDFieldClassifier]]
88 info.Platform = m[MDFieldToRecField[MDFieldPlatform]]
89 info.SupportedPlatform = m[MDFieldToRecField[MDFieldSupportedPlatform]]
90 info.RequiresDist = m[MDFieldToRecField[MDFieldRequiresDist]]
91 info.RequiresExternal = m[MDFieldToRecField[MDFieldRequiresExternal]]
92 info.ProjectURL = m[MDFieldToRecField[MDFieldProjectURL]]
93 info.ProvidesExtra = m[MDFieldToRecField[MDFieldProvidesExtra]]
98 Releases: make(map[string][]*PkgReleaseInfo),
100 var lastVersion string
101 for _, release := range releases {
102 meta.Releases[release.Version] = append(
103 meta.Releases[release.Version], release,
105 lastVersion = release.Version
108 lastVersion = version
110 meta.URLs = meta.Releases[lastVersion]
111 return &meta, releases, nil
114 // https://warehouse.pypa.io/api-reference/json.html
115 func serveJSON(w http.ResponseWriter, r *http.Request) {
116 path := strings.TrimPrefix(r.URL.Path, *JSONURLPath)
117 parts := strings.Split(strings.TrimSuffix(path, "/"), "/")
118 if len(parts) < 2 || parts[len(parts)-1] != "json" {
119 http.Error(w, "invalid JSON API action", http.StatusBadRequest)
128 meta, _, err := getMD(pkgName, version)
130 if os.IsNotExist(err) {
133 log.Println("error", r.RemoteAddr, "json", pkgName, err)
134 http.Error(w, err.Error(), http.StatusInternalServerError)
138 w.Header().Set("Content-Type", "application/json")
139 buf, err := json.Marshal(&meta)
141 log.Println("error", r.RemoteAddr, "json", pkgName, err)
142 http.Error(w, err.Error(), http.StatusInternalServerError)