1 // GoCheese -- Python private package repository and caching proxy
2 // Copyright (C) 2019-2024 Sergey Matveev <stargrave@stargrave.org>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
27 "go.cypherpunks.ru/recfile"
30 func getMD(pkgName, version string) (*PkgMeta, []*PkgReleaseInfo, error) {
31 serial, releases, err := listDir(pkgName, true)
35 md, err := os.ReadFile(filepath.Join(Root, pkgName, MDFile))
37 if !os.IsNotExist(err) {
41 info := PkgInfo{Name: pkgName}
43 info.Version = releases[len(releases)-1].Version
45 m, err := recfile.NewReader(bytes.NewReader(md)).NextMapWithSlice()
49 if v, ok := m[MDFieldToRecField[MDFieldVersion]]; ok {
52 if v, ok := m[MDFieldToRecField[MDFieldSummary]]; ok {
55 if v, ok := m[MDFieldToRecField[MDFieldDescriptionContentType]]; ok {
56 info.DescriptionContentType = v[0]
58 if v, ok := m[MDFieldToRecField[MDFieldKeywords]]; ok {
61 if v, ok := m[MDFieldToRecField[MDFieldHomePage]]; ok {
64 if v, ok := m[MDFieldToRecField[MDFieldAuthor]]; ok {
67 if v, ok := m[MDFieldToRecField[MDFieldAuthorEmail]]; ok {
68 info.AuthorEmail = v[0]
70 if v, ok := m[MDFieldToRecField[MDFieldMaintainer]]; ok {
71 info.Maintainer = v[0]
73 if v, ok := m[MDFieldToRecField[MDFieldMaintainerEmail]]; ok {
74 info.MaintainerEmail = v[0]
76 if v, ok := m[MDFieldToRecField[MDFieldLicense]]; ok {
79 if v, ok := m[MDFieldToRecField[MDFieldRequiresPython]]; ok {
80 info.RequiresPython = v[0]
82 if v, ok := m[MDFieldToRecField[MDFieldDescription]]; ok {
83 info.Description = v[0]
85 info.Classifier = m[MDFieldToRecField[MDFieldClassifier]]
86 info.Platform = m[MDFieldToRecField[MDFieldPlatform]]
87 info.SupportedPlatform = m[MDFieldToRecField[MDFieldSupportedPlatform]]
88 info.RequiresDist = m[MDFieldToRecField[MDFieldRequiresDist]]
89 info.RequiresExternal = m[MDFieldToRecField[MDFieldRequiresExternal]]
90 info.ProjectURL = m[MDFieldToRecField[MDFieldProjectURL]]
91 info.ProvidesExtra = m[MDFieldToRecField[MDFieldProvidesExtra]]
96 Releases: make(map[string][]*PkgReleaseInfo),
98 var lastVersion string
99 for _, release := range releases {
100 meta.Releases[release.Version] = append(
101 meta.Releases[release.Version], release,
103 lastVersion = release.Version
106 lastVersion = version
108 meta.URLs = meta.Releases[lastVersion]
109 return &meta, releases, nil
112 // https://warehouse.pypa.io/api-reference/json.html
113 func serveJSON(w http.ResponseWriter, r *http.Request) {
114 path := strings.TrimPrefix(r.URL.Path, *JSONURLPath)
115 parts := strings.Split(strings.TrimSuffix(path, "/"), "/")
116 if len(parts) < 2 || parts[len(parts)-1] != "json" {
117 http.Error(w, "invalid JSON API action", http.StatusBadRequest)
126 meta, _, err := getMD(pkgName, version)
128 if os.IsNotExist(err) {
131 log.Println("error", r.RemoteAddr, "json", pkgName, err)
132 http.Error(w, err.Error(), http.StatusInternalServerError)
136 w.Header().Set("Content-Type", "application/json")
137 buf, err := json.Marshal(&meta)
139 log.Println("error", r.RemoteAddr, "json", pkgName, err)
140 http.Error(w, err.Error(), http.StatusInternalServerError)