]> Cypherpunks.ru repositories - gocheese.git/blob - json.go
io/ioutil is deprecated since Go 1.16
[gocheese.git] / json.go
1 /*
2 GoCheese -- Python private package repository and caching proxy
3 Copyright (C) 2019-2022 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         "log"
24         "net/http"
25         "os"
26         "path/filepath"
27         "strings"
28
29         "go.cypherpunks.ru/recfile"
30 )
31
32 func getMD(pkgName, version string) (*PkgMeta, []*PkgReleaseInfo, error) {
33         serial, releases, err := listDir(pkgName, true)
34         if err != nil {
35                 return nil, nil, err
36         }
37         md, err := os.ReadFile(filepath.Join(Root, pkgName, MDFile))
38         if err != nil {
39                 if !os.IsNotExist(err) {
40                         return nil, nil, err
41                 }
42         }
43         info := PkgInfo{Name: pkgName}
44         if len(md) == 0 {
45                 info.Version = releases[len(releases)-1].Version
46         } else {
47                 m, err := recfile.NewReader(bytes.NewReader(md)).NextMapWithSlice()
48                 if err != nil {
49                         return nil, nil, err
50                 }
51                 if v, ok := m[MDFieldToRecField[MDFieldVersion]]; ok {
52                         info.Version = v[0]
53                 }
54                 if v, ok := m[MDFieldToRecField[MDFieldSummary]]; ok {
55                         info.Summary = v[0]
56                 }
57                 if v, ok := m[MDFieldToRecField[MDFieldDescriptionContentType]]; ok {
58                         info.DescriptionContentType = v[0]
59                 }
60                 if v, ok := m[MDFieldToRecField[MDFieldKeywords]]; ok {
61                         info.Keywords = v[0]
62                 }
63                 if v, ok := m[MDFieldToRecField[MDFieldHomePage]]; ok {
64                         info.HomePage = v[0]
65                 }
66                 if v, ok := m[MDFieldToRecField[MDFieldAuthor]]; ok {
67                         info.Author = v[0]
68                 }
69                 if v, ok := m[MDFieldToRecField[MDFieldAuthorEmail]]; ok {
70                         info.AuthorEmail = v[0]
71                 }
72                 if v, ok := m[MDFieldToRecField[MDFieldMaintainer]]; ok {
73                         info.Maintainer = v[0]
74                 }
75                 if v, ok := m[MDFieldToRecField[MDFieldMaintainerEmail]]; ok {
76                         info.MaintainerEmail = v[0]
77                 }
78                 if v, ok := m[MDFieldToRecField[MDFieldLicense]]; ok {
79                         info.License = v[0]
80                 }
81                 if v, ok := m[MDFieldToRecField[MDFieldRequiresPython]]; ok {
82                         info.RequiresPython = v[0]
83                 }
84                 if v, ok := m[MDFieldToRecField[MDFieldDescription]]; ok {
85                         info.Description = v[0]
86                 }
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]]
94         }
95         meta := PkgMeta{
96                 Info:       info,
97                 LastSerial: serial,
98                 Releases:   make(map[string][]*PkgReleaseInfo),
99         }
100         var lastVersion string
101         for _, release := range releases {
102                 meta.Releases[release.Version] = append(
103                         meta.Releases[release.Version], release,
104                 )
105                 lastVersion = release.Version
106         }
107         if version != "" {
108                 lastVersion = version
109         }
110         meta.URLs = meta.Releases[lastVersion]
111         return &meta, releases, nil
112 }
113
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)
120                 return
121         }
122         var version string
123         if len(parts) == 3 {
124                 version = parts[1]
125         }
126         pkgName := parts[0]
127
128         meta, _, err := getMD(pkgName, version)
129         if err != nil {
130                 if os.IsNotExist(err) {
131                         http.NotFound(w, r)
132                 } else {
133                         log.Println("error", r.RemoteAddr, "json", pkgName, err)
134                         http.Error(w, err.Error(), http.StatusInternalServerError)
135                 }
136                 return
137         }
138         w.Header().Set("Content-Type", "application/json")
139         buf, err := json.Marshal(&meta)
140         if err != nil {
141                 log.Println("error", r.RemoteAddr, "json", pkgName, err)
142                 http.Error(w, err.Error(), http.StatusInternalServerError)
143                 return
144         }
145         w.Write(buf)
146 }