package handler import ( "encoding/json" "fmt" "gosimplenpm/internal/config" "gosimplenpm/internal/serviceidos" "gosimplenpm/internal/storage" "net/http" "net/url" "path" "strconv" "strings" "time" "github.com/gorilla/mux" "github.com/sirupsen/logrus" ) type NPMClientPutRequest struct { Request serviceidos.IndexJson } func Publish(lg *logrus.Logger, cfg config.Config, stg storage.Storage) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // (1) Parse Json Body // (2) Check if package exists in the folder. // (a) if it does, ckeck if it is the same version. If it is, return error. Else modify index.json from (2) // (b) If it does not, add the latest tag to the new index.json escapedName := mux.Vars(r)["name"] packageName, _ := url.PathUnescape(escapedName) lg.WithFields(logrus.Fields{ "function": "publish", }).Debugf("Package name => %s\n", packageName) var cr NPMClientPutRequest // Parse json body err := json.NewDecoder(r.Body).Decode(&cr.Request) if err != nil { lg.WithFields(logrus.Fields{ "function": "publish", }).Debugf("Error unmarshaling put request: %+v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // Extract relevant data from index.json fmt.Printf("cRequest => %+v\n", cr) index := 0 var tag string var version string var versionData serviceidos.IndexJsonVersions // TODO: Fix this as the order is not guaranteed for key, value := range cr.Request.DistTags { if index == 0 { tag = key version = value break } index++ } versionData = cr.Request.Versions[version] lg.WithFields(logrus.Fields{ "function": "publish", }).Debugf("For version(%s) with tag(%s), versionData => %+v\n", version, tag, versionData) // Rewrite the tarball path tarballFileName := strings.Split(versionData.Dist.Tarball, "/-/")[1] tarballFileName, _ = url.PathUnescape(tarballFileName) lg.WithFields(logrus.Fields{ "function": "publish", }).Debugf("TarballName => %s\n", tarballFileName) // versionData.Dist.Tarball = fmt.Sprintf("file://%s", packageFilePath) versionData.Dist.Tarball = fmt.Sprintf("http://%s/%s/-/%s", r.Host, url.PathEscape(packageName), url.PathEscape(tarballFileName)) lg.WithFields(logrus.Fields{ "function": "publish", }).Debugf("versionData.Dist.Tarball => %s\n", versionData.Dist.Tarball) tarBallFile := strings.Split(tarballFileName, "/")[1] packageFilePath := path.Join(cfg.RepoDir, packageName, tarBallFile) lg.WithFields(logrus.Fields{ "function": "publish", }).Debugf("PackageFilePath => %s\n", packageFilePath) fmt.Printf("PackageFilePath => %s\n", packageFilePath) // Try to get the index.json from the store fileToServe, found, err := stg.GetIndexJsonFromStore(packageName, cfg.RepoDir, lg) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } var jsonFile serviceidos.IndexJson if !found { // new package jsonFile = cr.Request jsonFile.DistTags["latest"] = version curTime := time.Now().Format(time.RFC3339) jsonFile.TimesPackage = map[string]string{ version: curTime, "created": curTime, "modified": curTime, "unpublished": "", } } else { // old package err = stg.ReadIndexJson(fileToServe, &jsonFile, lg) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Checking that you are not publishing over a pervious published version if jsonFile.Versions[version].Version == version { lg.WithFields(logrus.Fields{ "function": "publish", }).Debugf("Version %s of package %s already exists!!\n", version, packageName) http.Error(w, fmt.Sprintf("Version %s of package %s already exists!!\n", version, packageName), http.StatusBadRequest) return } // Rewrite attachments jsonFile.DistTags[tag] = version nAttachments := make(map[string]serviceidos.IndexJsonAttachments) nAttachments[fmt.Sprintf("%s-%s.tgz", packageName, version)] = cr.Request.Attachments[fmt.Sprintf("%s-%s.tgz", packageName, version)] jsonFile.Attachments = nAttachments // Merge in the new version data jsonFile.Versions[version] = versionData // Update the time field timesPackages := jsonFile.TimesPackage curTime := time.Now().Format(time.RFC3339) timesPackages["modified"] = curTime timesPackages[version] = curTime } lg.WithFields(logrus.Fields{ "function": "publish", }).Debugln("FiletoServe ==> ", fileToServe) // Write index.json err = stg.WriteIndexJson(fileToServe, &jsonFile, lg) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } lg.WithFields(logrus.Fields{ "function": "publish", }).Debugln("Package path => ", packageFilePath) // Write bundled package packageData := jsonFile.Attachments[fmt.Sprintf("%s-%s.tgz", packageName, version)].Data err = stg.WritePackageToStore(packageFilePath, packageData, lg) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } response := serviceidos.PublishPutResponse{ Ok: true, Name: packageName, } jsonString, _ := json.Marshal(response) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) w.Header().Set("Content-Length", strconv.Itoa(len(jsonString))) w.Write(jsonString) } }