func setCmdEnv(cmd *exec.Cmd, username string) { userWorkspace := conf.GetUserWorkspace(username) cmd.Env = append(cmd.Env, "GOPATH="+userWorkspace, "GOOS="+runtime.GOOS, "GOARCH="+runtime.GOARCH, "GOROOT="+runtime.GOROOT(), "PATH="+os.Getenv("PATH")) }
// FindHandler handles request of find files under the specified directory with the specified filename pattern. func FindHandler(w http.ResponseWriter, r *http.Request) { httpSession, _ := session.HTTPSession.Get(r, "wide-session") if httpSession.IsNew { http.Error(w, "Forbidden", http.StatusForbidden) return } username := httpSession.Values["username"].(string) result := util.NewResult() defer util.RetResult(w, r, result) var args map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&args); err != nil { logger.Error(err) result.Succ = false return } path := args["path"].(string) // path of selected file in file tree if !util.Go.IsAPI(path) && !session.CanAccess(username, path) { http.Error(w, "Forbidden", http.StatusForbidden) return } name := args["name"].(string) userWorkspace := conf.GetUserWorkspace(username) workspaces := filepath.SplitList(userWorkspace) if "" != path && !util.File.IsDir(path) { path = filepath.Dir(path) } founds := foundPaths{} for _, workspace := range workspaces { rs := find(workspace+conf.PathSeparator+"src", name, []*string{}) for _, r := range rs { substr := util.Str.LCS(path, *r) founds = append(founds, &foundPath{Path: filepath.ToSlash(*r), score: len(substr)}) } } sort.Sort(founds) result.Data = founds }
// CanAccess determines whether the user specified by the given username can access the specified path. func CanAccess(username, path string) bool { path = filepath.FromSlash(path) userWorkspace := conf.GetUserWorkspace(username) workspaces := filepath.SplitList(userWorkspace) for _, workspace := range workspaces { if strings.HasPrefix(path, workspace) { return true } } return false }
// SearchTextHandler handles request of searching files under the specified directory with the specified keyword. func SearchTextHandler(w http.ResponseWriter, r *http.Request) { httpSession, _ := session.HTTPSession.Get(r, "wide-session") if httpSession.IsNew { http.Error(w, "Forbidden", http.StatusForbidden) return } result := util.NewResult() defer util.RetResult(w, r, result) var args map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&args); err != nil { logger.Error(err) result.Succ = false return } sid := args["sid"].(string) wSession := session.WideSessions.Get(sid) if nil == wSession { result.Succ = false return } // XXX: just one directory dir := args["dir"].(string) if "" == dir { userWorkspace := conf.GetUserWorkspace(wSession.Username) workspaces := filepath.SplitList(userWorkspace) dir = workspaces[0] } extension := args["extension"].(string) text := args["text"].(string) founds := []*Snippet{} if util.File.IsDir(dir) { founds = search(dir, extension, text, []*Snippet{}) } else { founds = searchInFile(dir, text) } result.Data = founds }
func setCmdEnv(cmd *exec.Cmd, username string) { userWorkspace := conf.GetUserWorkspace(username) cmd.Env = append(cmd.Env, "GOPATH="+userWorkspace, "GOOS="+runtime.GOOS, "GOARCH="+runtime.GOARCH, "GOROOT="+runtime.GOROOT(), "PATH="+os.Getenv("PATH")) if util.OS.IsWindows() { // FIXME: for some weird issues on Windows, such as: The requested service provider could not be loaded or initialized. cmd.Env = append(cmd.Env, os.Environ()...) } }
// GetFilesHandler handles request of constructing user workspace file tree. // // The Go API source code package also as a child node, // so that users can easily view the Go API source code in file tree. func GetFilesHandler(w http.ResponseWriter, r *http.Request) { httpSession, _ := session.HTTPSession.Get(r, "wide-session") if httpSession.IsNew { http.Error(w, "Forbidden", http.StatusForbidden) return } username := httpSession.Values["username"].(string) result := util.NewResult() defer util.RetGzResult(w, r, result) userWorkspace := conf.GetUserWorkspace(username) workspaces := filepath.SplitList(userWorkspace) root := Node{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", IsParent: true, Children: []*Node{}} if nil == apiNode { // lazy init initAPINode() } // workspace node process for _, workspace := range workspaces { workspacePath := workspace + conf.PathSeparator + "src" workspaceNode := Node{ Id: filepath.ToSlash(workspacePath), // jQuery API can't accept "\", so we convert it to "/" Name: workspace[strings.LastIndex(workspace, conf.PathSeparator)+1:], Path: filepath.ToSlash(workspacePath), IconSkin: "ico-ztree-dir-workspace ", Type: "d", Creatable: true, Removable: false, IsGoAPI: false, Children: []*Node{}} walk(workspacePath, &workspaceNode, true, true, false) // add workspace node root.Children = append(root.Children, &workspaceNode) } // add Go API node root.Children = append(root.Children, apiNode) result.Data = root }
func authWorkspace(username, path string) bool { path = filepath.FromSlash(path) if strings.HasPrefix(path, util.Go.GetAPIPath()) { return true } userWorkspace := conf.GetUserWorkspace(username) workspaces := filepath.SplitList(userWorkspace) for _, workspace := range workspaces { if strings.HasPrefix(path, workspace) { return true } } return false }
// addUser add a user with the specified username, password and email. // // 1. create the user's workspace // 2. generate 'Hello, 世界' demo code in the workspace (a console version and a HTTP version) // 3. update the user customized configurations, such as style.css // 4. serve files of the user's workspace via HTTP // // Note: user [playground] is a reserved mock user func addUser(username, password, email string) string { if !conf.Wide.AllowRegister { return notAllowRegister } if "playground" == username { return userExists } addUserMutex.Lock() defer addUserMutex.Unlock() for _, user := range conf.Users { if strings.ToLower(user.Name) == strings.ToLower(username) { return userExists } if strings.ToLower(user.Email) == strings.ToLower(email) { return emailExists } } firstUserWorkspace := conf.GetUserWorkspace(conf.Users[0].Name) dir := filepath.Dir(firstUserWorkspace) workspace := filepath.Join(dir, username) newUser := conf.NewUser(username, password, email, workspace) conf.Users = append(conf.Users, newUser) if !newUser.Save() { return userCreateError } conf.CreateWorkspaceDir(workspace) helloWorld(workspace) conf.UpdateCustomizedConf(username) http.Handle("/workspace/"+username+"/", http.StripPrefix("/workspace/"+username+"/", http.FileServer(http.Dir(newUser.GetWorkspace())))) logger.Infof("Created a user [%s]", username) return userCreated }
// startHandler handles request of start page. func startHandler(w http.ResponseWriter, r *http.Request) { httpSession, _ := session.HTTPSession.Get(r, "wide-session") if httpSession.IsNew { http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound) return } httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge if "" != conf.Wide.Context { httpSession.Options.Path = conf.Wide.Context } httpSession.Save(r, w) username := httpSession.Values["username"].(string) locale := conf.GetUser(username).Locale userWorkspace := conf.GetUserWorkspace(username) sid := r.URL.Query()["sid"][0] wSession := session.WideSessions.Get(sid) if nil == wSession { logger.Errorf("Session [%s] not found", sid) } model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, "username": username, "workspace": userWorkspace, "ver": conf.WideVersion, "sid": sid} t, err := template.ParseFiles("views/start.html") if nil != err { logger.Error(err) http.Error(w, err.Error(), 500) return } t.Execute(w, model) }
// New creates a wide session. func (sessions *wSessions) New(httpSession *sessions.Session, sid string) *WideSession { mutex.Lock() defer mutex.Unlock() username := httpSession.Values["username"].(string) now := time.Now() ret := &WideSession{ ID: sid, Username: username, HTTPSession: httpSession, EventQueue: nil, State: sessionStateActive, Content: &conf.LatestSessionContent{}, Created: now, Updated: now, } *sessions = append(*sessions, ret) if "playground" == username { return ret } // create user event queue ret.EventQueue = event.UserEventQueues.New(sid) // add a filesystem watcher to notify front-end after the files changed watcher, err := fsnotify.NewWatcher() if err != nil { logger.Error(err) return ret } go func() { defer util.Recover() workspaces := filepath.SplitList(conf.GetUserWorkspace(username)) for _, workspace := range workspaces { filepath.Walk(filepath.Join(workspace, "src"), func(dirPath string, f os.FileInfo, err error) error { if ".git" == f.Name() { // XXX: discard other unconcered dirs return filepath.SkipDir } if f.IsDir() { if err = watcher.Add(dirPath); nil != err { logger.Error(err, dirPath) } logger.Tracef("Added a file watcher [%s]", dirPath) } return nil }) } ret.FileWatcher = watcher }() go func() { defer util.Recover() for { ch := SessionWS[sid] if nil == ch { return // release this gorutine } select { case event := <-watcher.Events: path := event.Name dir := filepath.Dir(path) ch = SessionWS[sid] if nil == ch { return // release this gorutine } if event.Op&fsnotify.Create == fsnotify.Create { if err = watcher.Add(path); nil != err { logger.Warn(err, path) } logger.Tracef("Added a file watcher [%s]", path) cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "create-file"} ch.WriteJSON(&cmd) } else if event.Op&fsnotify.Remove == fsnotify.Remove { cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "remove-file"} ch.WriteJSON(&cmd) } else if event.Op&fsnotify.Rename == fsnotify.Rename { cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "rename-file"} ch.WriteJSON(&cmd) } case err := <-watcher.Errors: if nil != err { logger.Error("File watcher ERROR: ", err) } } } }() return ret }
// SignUpUserHandler handles request of registering user. func SignUpUserHandler(w http.ResponseWriter, r *http.Request) { if "GET" == r.Method { // show the user sign up page firstUserWorkspace := conf.GetUserWorkspace(conf.Users[0].Name) dir := filepath.Dir(firstUserWorkspace) model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale), "locale": conf.Wide.Locale, "ver": conf.WideVersion, "dir": dir, "pathSeparator": conf.PathSeparator, "year": time.Now().Year()} t, err := template.ParseFiles("views/sign_up.html") if nil != err { logger.Error(err) http.Error(w, err.Error(), 500) return } t.Execute(w, model) return } // non-GET request as add user request result := util.NewResult() defer util.RetResult(w, r, result) var args map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&args); err != nil { logger.Error(err) result.Succ = false return } username := args["username"].(string) password := args["password"].(string) email := args["email"].(string) msg := addUser(username, password, email) if userCreated != msg { result.Succ = false result.Msg = msg return } // create a HTTP session httpSession, _ := HTTPSession.Get(r, "wide-session") httpSession.Values["username"] = username httpSession.Values["id"] = strconv.Itoa(rand.Int()) httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge if "" != conf.Wide.Context { httpSession.Options.Path = conf.Wide.Context } httpSession.Save(r, w) }
// AutocompleteHandler handles request of code autocompletion. func AutocompleteHandler(w http.ResponseWriter, r *http.Request) { var args map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&args); err != nil { logger.Error(err) http.Error(w, err.Error(), 500) return } session, _ := session.HTTPSession.Get(r, "wide-session") if session.IsNew { http.Error(w, "Forbidden", http.StatusForbidden) return } username := session.Values["username"].(string) path := args["path"].(string) fout, err := os.Create(path) if nil != err { logger.Error(err) http.Error(w, err.Error(), 500) return } code := args["code"].(string) fout.WriteString(code) if err := fout.Close(); nil != err { logger.Error(err) http.Error(w, err.Error(), 500) return } line := int(args["cursorLine"].(float64)) ch := int(args["cursorCh"].(float64)) offset := getCursorOffset(code, line, ch) logger.Tracef("offset: %d", offset) userWorkspace := conf.GetUserWorkspace(username) workspaces := filepath.SplitList(userWorkspace) libPath := "" for _, workspace := range workspaces { userLib := workspace + conf.PathSeparator + "pkg" + conf.PathSeparator + runtime.GOOS + "_" + runtime.GOARCH libPath += userLib + conf.PathListSeparator } logger.Tracef("gocode set lib-path [%s]", libPath) // FIXME: using gocode set lib-path has some issues while accrossing workspaces gocode := util.Go.GetExecutableInGOBIN("gocode") exec.Command(gocode, []string{"set", "lib-path", libPath}...).Run() argv := []string{"-f=json", "--in=" + path, "autocomplete", strconv.Itoa(offset)} cmd := exec.Command(gocode, argv...) output, err := cmd.CombinedOutput() if nil != err { logger.Error(err) http.Error(w, err.Error(), 500) return } w.Header().Set("Content-Type", "application/json") w.Write(output) }