2 GoCheese -- Python private package repository and caching proxy
3 Copyright (C) 2019-2022 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/>.
30 "go.cypherpunks.ru/recfile"
33 func getMD(pkgName, version string) (*PkgMeta, []*PkgReleaseInfo, error) {
34 serial, releases, err := listDir(pkgName, true)
38 md, err := ioutil.ReadFile(filepath.Join(Root, pkgName, MDFile))
40 if !os.IsNotExist(err) {
44 info := PkgInfo{Name: pkgName}
46 info.Version = releases[len(releases)-1].Version
48 m, err := recfile.NewReader(bytes.NewReader(md)).NextMapWithSlice()
52 if v, ok := m[MDFieldToRecField[MDFieldVersion]]; ok {
55 if v, ok := m[MDFieldToRecField[MDFieldSummary]]; ok {
58 if v, ok := m[MDFieldToRecField[MDFieldDescriptionContentType]]; ok {
59 info.DescriptionContentType = v[0]
61 if v, ok := m[MDFieldToRecField[MDFieldKeywords]]; ok {
64 if v, ok := m[MDFieldToRecField[MDFieldHomePage]]; ok {
67 if v, ok := m[MDFieldToRecField[MDFieldAuthor]]; ok {
70 if v, ok := m[MDFieldToRecField[MDFieldAuthorEmail]]; ok {
71 info.AuthorEmail = v[0]
73 if v, ok := m[MDFieldToRecField[MDFieldMaintainer]]; ok {
74 info.Maintainer = v[0]
76 if v, ok := m[MDFieldToRecField[MDFieldMaintainerEmail]]; ok {
77 info.MaintainerEmail = v[0]
79 if v, ok := m[MDFieldToRecField[MDFieldLicense]]; ok {
82 if v, ok := m[MDFieldToRecField[MDFieldRequiresPython]]; ok {
83 info.RequiresPython = v[0]
85 if v, ok := m[MDFieldToRecField[MDFieldDescription]]; ok {
86 info.Description = v[0]
88 info.Classifier = m[MDFieldToRecField[MDFieldClassifier]]
89 info.Platform = m[MDFieldToRecField[MDFieldPlatform]]
90 info.SupportedPlatform = m[MDFieldToRecField[MDFieldSupportedPlatform]]
91 info.RequiresDist = m[MDFieldToRecField[MDFieldRequiresDist]]
92 info.RequiresExternal = m[MDFieldToRecField[MDFieldRequiresExternal]]
93 info.ProjectURL = m[MDFieldToRecField[MDFieldProjectURL]]
94 info.ProvidesExtra = m[MDFieldToRecField[MDFieldProvidesExtra]]
99 Releases: make(map[string][]*PkgReleaseInfo),
101 var lastVersion string
102 for _, release := range releases {
103 meta.Releases[release.Version] = append(
104 meta.Releases[release.Version], release,
106 lastVersion = release.Version
109 lastVersion = version
111 meta.URLs = meta.Releases[lastVersion]
112 return &meta, releases, nil
115 // https://warehouse.pypa.io/api-reference/json.html
116 func serveJSON(w http.ResponseWriter, r *http.Request) {
117 path := strings.TrimPrefix(r.URL.Path, *JSONURLPath)
118 parts := strings.Split(strings.TrimSuffix(path, "/"), "/")
119 if len(parts) < 2 || parts[len(parts)-1] != "json" {
120 http.Error(w, "invalid JSON API action", http.StatusBadRequest)
129 meta, _, err := getMD(pkgName, version)
131 if os.IsNotExist(err) {
134 log.Println("error", r.RemoteAddr, "json", pkgName, err)
135 http.Error(w, err.Error(), http.StatusInternalServerError)
139 w.Header().Set("Content-Type", "application/json")
140 buf, err := json.Marshal(&meta)
142 log.Println("error", r.RemoteAddr, "json", pkgName, err)
143 http.Error(w, err.Error(), http.StatusInternalServerError)