/* GoCheese -- Python private package repository and caching proxy Copyright (C) 2019-2021 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package main import ( "bytes" "encoding/json" "io/ioutil" "log" "net/http" "os" "path/filepath" "strings" "go.cypherpunks.ru/recfile" ) func getMetadata(pkgName, version string) (*PkgMeta, []*PkgReleaseInfo, error) { serial, releases, err := listDir(pkgName, true) if err != nil { return nil, nil, err } metadata, err := ioutil.ReadFile(filepath.Join(Root, pkgName, MetadataFile)) if err != nil { if !os.IsNotExist(err) { return nil, nil, err } } info := PkgInfo{Name: pkgName} if len(metadata) == 0 { info.Version = releases[len(releases)-1].Version } else { m, err := recfile.NewReader(bytes.NewReader(metadata)).NextMapWithSlice() if err != nil { return nil, nil, err } if v, ok := m[metadataFieldToRecField(MetadataFieldVersion)]; ok { info.Version = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldSummary)]; ok { info.Summary = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldDescriptionContentType)]; ok { info.DescriptionContentType = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldKeywords)]; ok { info.Keywords = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldHomePage)]; ok { info.HomePage = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldAuthor)]; ok { info.Author = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldAuthorEmail)]; ok { info.AuthorEmail = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldMaintainer)]; ok { info.Maintainer = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldMaintainerEmail)]; ok { info.MaintainerEmail = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldLicense)]; ok { info.License = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldRequiresPython)]; ok { info.RequiresPython = v[0] } if v, ok := m[metadataFieldToRecField(MetadataFieldDescription)]; ok { info.Description = v[0] } info.Classifier = m[metadataFieldToRecField(MetadataFieldClassifier)] info.Platform = m[metadataFieldToRecField(MetadataFieldPlatform)] info.SupportedPlatform = m[metadataFieldToRecField(MetadataFieldSupportedPlatform)] info.RequiresDist = m[metadataFieldToRecField(MetadataFieldRequiresDist)] info.RequiresExternal = m[metadataFieldToRecField(MetadataFieldRequiresExternal)] info.ProjectURL = m[metadataFieldToRecField(MetadataFieldProjectURL)] info.ProvidesExtra = m[metadataFieldToRecField(MetadataFieldProvidesExtra)] } meta := PkgMeta{ Info: info, LastSerial: serial, Releases: make(map[string][]*PkgReleaseInfo), } var lastVersion string for _, release := range releases { meta.Releases[release.Version] = append( meta.Releases[release.Version], release, ) lastVersion = release.Version } if version != "" { lastVersion = version } meta.URLs = meta.Releases[lastVersion] return &meta, releases, nil } // https://warehouse.pypa.io/api-reference/json.html func serveJSON(w http.ResponseWriter, r *http.Request) { path := strings.TrimPrefix(r.URL.Path, *JSONURLPath) parts := strings.Split(strings.TrimSuffix(path, "/"), "/") if len(parts) < 2 || parts[len(parts)-1] != "json" { http.Error(w, "invalid JSON API action", http.StatusBadRequest) return } var version string if len(parts) == 3 { version = parts[1] } pkgName := parts[0] meta, _, err := getMetadata(pkgName, version) if err != nil { if os.IsNotExist(err) { http.NotFound(w, r) } else { log.Println("error", r.RemoteAddr, "json", pkgName, err) http.Error(w, err.Error(), http.StatusInternalServerError) } return } w.Header().Set("Content-Type", "application/json") buf, err := json.Marshal(&meta) if err != nil { log.Println("error", r.RemoteAddr, "json", pkgName, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Write(buf) }