]> Cypherpunks.ru repositories - gocheese.git/blob - json.go
Download link for 3.0.0 release
[gocheese.git] / json.go
1 /*
2 GoCheese -- Python private package repository and caching proxy
3 Copyright (C) 2019-2021 Sergey Matveev <stargrave@stargrave.org>
4
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.
8
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.
13
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/>.
16 */
17
18 package main
19
20 import (
21         "bytes"
22         "encoding/json"
23         "io/ioutil"
24         "log"
25         "net/http"
26         "os"
27         "path/filepath"
28         "strings"
29
30         "go.cypherpunks.ru/recfile"
31 )
32
33 func getMetadata(pkgName, version string) (*PkgMeta, []*PkgReleaseInfo, error) {
34         serial, releases, err := listDir(pkgName, true)
35         if err != nil {
36                 return nil, nil, err
37         }
38         metadata, err := ioutil.ReadFile(filepath.Join(Root, pkgName, MetadataFile))
39         if err != nil {
40                 if !os.IsNotExist(err) {
41                         return nil, nil, err
42                 }
43         }
44         info := PkgInfo{Name: pkgName}
45         if len(metadata) == 0 {
46                 info.Version = releases[len(releases)-1].Version
47         } else {
48                 m, err := recfile.NewReader(bytes.NewReader(metadata)).NextMapWithSlice()
49                 if err != nil {
50                         return nil, nil, err
51                 }
52                 if v, ok := m[metadataFieldToRecField(MetadataFieldVersion)]; ok {
53                         info.Version = v[0]
54                 }
55                 if v, ok := m[metadataFieldToRecField(MetadataFieldSummary)]; ok {
56                         info.Summary = v[0]
57                 }
58                 if v, ok := m[metadataFieldToRecField(MetadataFieldDescriptionContentType)]; ok {
59                         info.DescriptionContentType = v[0]
60                 }
61                 if v, ok := m[metadataFieldToRecField(MetadataFieldKeywords)]; ok {
62                         info.Keywords = v[0]
63                 }
64                 if v, ok := m[metadataFieldToRecField(MetadataFieldHomePage)]; ok {
65                         info.HomePage = v[0]
66                 }
67                 if v, ok := m[metadataFieldToRecField(MetadataFieldAuthor)]; ok {
68                         info.Author = v[0]
69                 }
70                 if v, ok := m[metadataFieldToRecField(MetadataFieldAuthorEmail)]; ok {
71                         info.AuthorEmail = v[0]
72                 }
73                 if v, ok := m[metadataFieldToRecField(MetadataFieldMaintainer)]; ok {
74                         info.Maintainer = v[0]
75                 }
76                 if v, ok := m[metadataFieldToRecField(MetadataFieldMaintainerEmail)]; ok {
77                         info.MaintainerEmail = v[0]
78                 }
79                 if v, ok := m[metadataFieldToRecField(MetadataFieldLicense)]; ok {
80                         info.License = v[0]
81                 }
82                 if v, ok := m[metadataFieldToRecField(MetadataFieldRequiresPython)]; ok {
83                         info.RequiresPython = v[0]
84                 }
85                 if v, ok := m[metadataFieldToRecField(MetadataFieldDescription)]; ok {
86                         info.Description = v[0]
87                 }
88                 info.Classifier = m[metadataFieldToRecField(MetadataFieldClassifier)]
89                 info.Platform = m[metadataFieldToRecField(MetadataFieldPlatform)]
90                 info.SupportedPlatform = m[metadataFieldToRecField(MetadataFieldSupportedPlatform)]
91                 info.RequiresDist = m[metadataFieldToRecField(MetadataFieldRequiresDist)]
92                 info.RequiresExternal = m[metadataFieldToRecField(MetadataFieldRequiresExternal)]
93                 info.ProjectURL = m[metadataFieldToRecField(MetadataFieldProjectURL)]
94                 info.ProvidesExtra = m[metadataFieldToRecField(MetadataFieldProvidesExtra)]
95         }
96         meta := PkgMeta{
97                 Info:       info,
98                 LastSerial: serial,
99                 Releases:   make(map[string][]*PkgReleaseInfo),
100         }
101         var lastVersion string
102         for _, release := range releases {
103                 meta.Releases[release.Version] = append(
104                         meta.Releases[release.Version], release,
105                 )
106                 lastVersion = release.Version
107         }
108         if version != "" {
109                 lastVersion = version
110         }
111         meta.URLs = meta.Releases[lastVersion]
112         return &meta, releases, nil
113 }
114
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)
121                 return
122         }
123         var version string
124         if len(parts) == 3 {
125                 version = parts[1]
126         }
127         pkgName := parts[0]
128
129         meta, _, err := getMetadata(pkgName, version)
130         if err != nil {
131                 if os.IsNotExist(err) {
132                         http.NotFound(w, r)
133                 } else {
134                         log.Println("error", r.RemoteAddr, "json", pkgName, err)
135                         http.Error(w, err.Error(), http.StatusInternalServerError)
136                 }
137                 return
138         }
139         w.Header().Set("Content-Type", "application/json")
140         buf, err := json.Marshal(&meta)
141         if err != nil {
142                 log.Println("error", r.RemoteAddr, "json", pkgName, err)
143                 http.Error(w, err.Error(), http.StatusInternalServerError)
144                 return
145         }
146         w.Write(buf)
147 }