// Copyright 2013 gopm authors. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package cmd import ( "errors" "fmt" "github.com/Unknwon/com" "github.com/gpmgo/gopm/doc" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/opt" "io/ioutil" "net/http" "net/url" "os" "os/exec" "path/filepath" "strconv" "strings" "time" ) var ( dbDir = "~/.gopm/db" ) const ( STOP = iota LOCALRUN RUNNING ) var CmdServe = &Command{ UsageLine: "serve [:port]", Short: "serve for package search", Long: ` serve provide a web service to search packages, download packages The serve flags are: -l only service for localhost ip `, } func init() { CmdServe.Run = runServe CmdServe.Flags = map[string]bool{ "-l": false, } } func printServePrompt(flag string) { switch flag { case "-l": com.ColorLog("[INFO] You enabled start a service only localhost.\n") } } // Not implemented func autoPort() string { return "8991" } func exePath() (string, error) { file, err := exec.LookPath(os.Args[0]) if err != nil { return "", err } return filepath.Abs(file) } // search packages func runServe(cmd *Command, args []string) { // Check flags. num := checkFlags(cmd.Flags, args, printServePrompt) if num == -1 { return } args = args[num:] var listen string var port string if cmd.Flags["-l"] { listen += "127.0.0.1" port = autoPort() } else { listen += "0.0.0.0" port = "8991" } // Check length of arguments. if len(args) >= 1 { port = args[0] } err := startService(listen, port) if err != nil { com.ColorLog("[ERRO] %v\n", err) } } func splitWord(word string, res *map[string]bool) { for i, _ := range word { for j, _ := range word[i:] { w := word[i : i+j+1] (*res)[w] = true } } return } func splitPkgName(pkgName string) (res map[string]bool) { //var src string ps := strings.Split(pkgName, "/") if len(ps) > 1 { ps = ps[1:] } res = make(map[string]bool) res[strings.Join(ps, "/")] = true for _, w := range ps { splitWord(w, &res) } return } func splitSynopsis(synopsis string) map[string]bool { res := make(map[string]bool) ss := strings.Fields(synopsis) for _, s := range ss { res[s] = true } return res } var ( ro *opt.ReadOptions = &opt.ReadOptions{} wo *opt.WriteOptions = &opt.WriteOptions{} ) func dbGet(key string) (string, error) { v, err := db.Get([]byte(key), ro) return string(v), err } func dbPut(key string, value string) error { //fmt.Println("put ", key, ": ", value) return db.Put([]byte(key), []byte(value), wo) } func batchPut(batch *leveldb.Batch, key string, value string) error { //fmt.Println("put ", key, ": ", value) batch.Put([]byte(key), []byte(value)) return nil } func getServeHost() string { return "localhost" } func getServePort() string { return "8991" } // for exernal of serve to add node to db func saveNode(nod *doc.Node) error { urlPath := fmt.Sprintf("http://%v:%v/add", getServeHost(), getServePort()) resp, err := http.PostForm(urlPath, url.Values{"importPath": {nod.ImportPath}, "synopsis": {nod.Synopsis}, "downloadURL": {nod.DownloadURL}, "isGetDeps": {strconv.FormatBool(nod.IsGetDeps)}, "type": {nod.Type}, "value": {nod.Value}}) if err != nil { com.ColorLog("[ERRO] Fail to save node[ %s ]\n", err) return err } defer resp.Body.Close() if resp.StatusCode == 200 { return nil } return errors.New("save node failed with " + resp.Status) } // for inetrnal of serve to add node to db func addNode(nod *doc.Node) error { batch := new(leveldb.Batch) strLastId, err := dbGet("lastId") if err != nil { if err == leveldb.ErrNotFound { strLastId = "0" err = batchPut(batch, "lastId", strLastId) } else { return err } } if err != nil { return err } lastId, err := strconv.ParseInt(strLastId, 0, 64) if err != nil { return err } nodKey := fmt.Sprintf("index:%v", nod.ImportPath) id, err := dbGet(nodKey) if err != nil { if err == leveldb.ErrNotFound { id = fmt.Sprintf("%v", lastId+1) err = batchPut(batch, "lastId", id) if err == nil { err = batchPut(batch, nodKey, id) } if err == nil { err = batchPut(batch, "pkg:"+id, nod.ImportPath) } if err == nil { err = batchPut(batch, "desc:"+id, nod.Synopsis) } if err == nil { err = batchPut(batch, "down:"+id, nod.DownloadURL) } if err == nil { err = batchPut(batch, "deps:"+id, strconv.FormatBool(nod.IsGetDeps)) } // save totals total, err := dbGet("total") if err != nil { if err == leveldb.ErrNotFound { total = "1" } else { return err } } else { totalInt, err := strconv.ParseInt(total, 0, 64) if err != nil { return err } totalInt = totalInt + 1 total = fmt.Sprintf("%v", totalInt) } err = batchPut(batch, "total", total) } else { return err } } if err != nil { return err } // save vers vers, err := dbGet("ver:" + id) needSplit := (err == leveldb.ErrNotFound) if err != nil { if err != leveldb.ErrNotFound { return err } } else { return nil } if vers == "" { //fmt.Println(nod) vers = nod.VerString() } else { if !strings.Contains(vers, nod.VerString()) { vers = vers + "," + nod.VerString() } else { return nil } } err = batchPut(batch, "ver:"+id, vers) if err != nil { return err } if !needSplit { return nil } // indexing package name keys := splitPkgName(nod.ImportPath) for key, _ := range keys { err = batchPut(batch, fmt.Sprintf("key:%v:%v", strings.ToLower(key), id), "") if err != nil { return err } } if nod.Synopsis != "" { fields := splitSynopsis(nod.Synopsis) for field, _ := range fields { err = batchPut(batch, fmt.Sprintf("key:%v:%v", strings.ToLower(field), id), "") if err != nil { return err } } } return db.Write(batch, wo) } func rmPkg(nod *doc.Node) { } var db *leveldb.DB // service should be run func AutoRun() error { s, _, _ := runningStatus() if s == STOP { // current path curPath, err := os.Getwd() if err != nil { return err } attr := &os.ProcAttr{ Dir: curPath, Env: os.Environ(), //Files: []*os.File{nil, nil, nil}, Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, } p, err := exePath() if err != nil { return err } //com.ColorLog("[INFO] now is starting search daemon ...\n") _, err = os.StartProcess(p, []string{"gopm", "serve", "-l"}, attr) if err != nil { return err } time.Sleep(time.Second) } return nil } func runningStatus() (int, int, int) { pFile, err := getPidPath() if err != nil { return STOP, 0, 0 } contentByte, err := ioutil.ReadFile(pFile) if err != nil { return STOP, 0, 0 } content := string(contentByte) if len(content) < 0 || !strings.Contains(content, ",") { return STOP, 0, 0 } cs := strings.Split(string(content), ",") if len(cs) != 3 { return STOP, 0, 0 } status, err := strconv.Atoi(cs[0]) if err != nil { return STOP, 0, 0 } if status < STOP || status > RUNNING { return STOP, 0, 0 } pid, err := strconv.Atoi(cs[1]) if err != nil { return STOP, 0, 0 } _, err = os.FindProcess(pid) if err != nil { return STOP, 0, 0 } port, err := strconv.Atoi(cs[2]) if err != nil { return STOP, 0, 0 } return status, pid, port } func getPidPath() (string, error) { homeDir, err := com.HomeDir() if err != nil { return "", err } pFile := strings.Replace("~/.gopm/var/", "~", homeDir, -1) os.MkdirAll(pFile, os.ModePerm) return pFile + "pid", nil } func startService(listen, port string) error { homeDir, err := com.HomeDir() if err != nil { return err } pFile, err := getPidPath() if err != nil { return err } f, err := os.OpenFile(pFile, os.O_RDWR|os.O_CREATE, 0700) if err != nil { return err } defer f.Close() _, err = f.WriteString(fmt.Sprintf("%v,%v,%v", RUNNING, os.Getpid(), port)) if err != nil { return err } dbDir = strings.Replace(dbDir, "~", homeDir, -1) db, err = leveldb.OpenFile(dbDir, nil) if err != nil { return err } defer db.Close() // these handlers should only access by localhost http.HandleFunc("/add", addHandler) http.HandleFunc("/rm", rmHandler) // these handlers can be accessed according listen's ip http.HandleFunc("/search", searchHandler) http.HandleFunc("/searche", searcheHandler) http.ListenAndServe(listen+":"+port, nil) return nil } func searchHandler(w http.ResponseWriter, r *http.Request) { r.ParseForm() ids := make(map[string]bool) for key, _ := range r.Form { iter := db.NewIterator(ro) rkey := fmt.Sprintf("key:%v:", strings.ToLower(key)) if iter.Seek([]byte(rkey)) { k := iter.Key() if !strings.HasPrefix(string(k), rkey) { break } else { ids[string(k)] = true } } for iter.Next() { k := iter.Key() if !strings.HasPrefix(string(k), rkey) { break } ids[string(k)] = true } } pkgs := make([]string, 0) for id, _ := range ids { idkeys := strings.SplitN(id, ":", -1) rId := idkeys[len(idkeys)-1] //fmt.Println(rId) pkg, err := dbGet(fmt.Sprintf("pkg:%v", rId)) if err != nil { com.ColorLog(err.Error()) continue } desc, err := dbGet(fmt.Sprintf("desc:%v", rId)) if err != nil { com.ColorLog(err.Error()) continue } pkgs = append(pkgs, fmt.Sprintf(`{"pkg":"%v", "desc":"%v"}`, pkg, desc)) } w.Write([]byte("[" + strings.Join(pkgs, ", ") + "]")) } func searcheHandler(w http.ResponseWriter, r *http.Request) { //if r.Method == "POST" { r.ParseForm() pkgs := make([]string, 0) for key, _ := range r.Form { rId, err := dbGet("index:" + key) if err != nil { com.ColorLog(err.Error()) continue } desc, err := dbGet(fmt.Sprintf("desc:%v", rId)) if err != nil { com.ColorLog(err.Error()) continue } pkgs = append(pkgs, fmt.Sprintf(`{"pkg":"%v", "desc":"%v"}`, key, desc)) } w.Write([]byte("[" + strings.Join(pkgs, ", ") + "]")) //} } func addHandler(w http.ResponseWriter, r *http.Request) { //if r.Method == "POST" { r.ParseForm() nod := new(doc.Node) nod.ImportPath = r.FormValue("importPath") nod.Synopsis = r.FormValue("synopsis") nod.DownloadURL = r.FormValue("downloadURL") isGetDeps, err := strconv.ParseBool(r.FormValue("isGetDeps")) if err != nil { com.ColorLog("[ERRO] SEVER: Cannot get deps") } nod.IsGetDeps = isGetDeps nod.Type = r.FormValue("type") nod.Value = r.FormValue("value") err = addNode(nod) if err != nil { com.ColorLog("[ERRO] SEVER: Cannot add node[ %s ]\n", err) } //} } func rmHandler(w http.ResponseWriter, r *http.Request) { }