From 3b359d92d8469d3f7870851df1693490abeb09c9 Mon Sep 17 00:00:00 2001 From: Liang Ding Date: Fri, 19 Dec 2014 15:43:59 +0800 Subject: [PATCH] separate users' configurations from wide.json --- conf/user.go | 112 ++++++++++++++++ conf/users/admin.json | 26 ++++ conf/wide.go | 235 ++++++++++++---------------------- conf/wide.json | 32 +---- editor/editors.go | 4 +- editor/formatter.go | 2 +- file/files.go | 23 +--- main.go | 22 ++-- notification/notifications.go | 2 +- output/outputs.go | 10 +- session/sessions.go | 2 +- session/users.go | 55 +++++++- shell/shells.go | 4 +- 13 files changed, 302 insertions(+), 227 deletions(-) create mode 100644 conf/user.go create mode 100644 conf/users/admin.json diff --git a/conf/user.go b/conf/user.go new file mode 100644 index 0000000..476b958 --- /dev/null +++ b/conf/user.go @@ -0,0 +1,112 @@ +// Copyright (c) 2014, B3log +// +// 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 conf + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +// LatestSessionContent represents the latest session content. +type LatestSessionContent struct { + FileTree []string // paths of expanding nodes of file tree + Files []string // paths of files of opening editor tabs + CurrentFile string // path of file of the current focused editor tab +} + +// User configuration. +type User struct { + Name string + Password string + Email string + Gravatar string // see http://gravatar.com + Workspace string // the GOPATH of this user + Locale string + GoFormat string + FontFamily string + FontSize string + Theme string + Editor *editor + LatestSessionContent *LatestSessionContent +} + +// Editor configuration of a user. +type editor struct { + FontFamily string + FontSize string + LineHeight string + Theme string + TabSize string +} + +// NewUser creates a user with the specified username, password, email and workspace. +func NewUser(username, password, email, workspace string) *User { + hash := md5.New() + hash.Write([]byte(email)) + gravatar := hex.EncodeToString(hash.Sum(nil)) + + return &User{Name: username, Password: password, Email: email, Gravatar: gravatar, Workspace: workspace, + Locale: Wide.Locale, GoFormat: "gofmt", FontFamily: "Helvetica", FontSize: "13px", Theme: "default", + Editor: &editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit", LineHeight: "17px", + Theme: "wide", TabSize: "4"}} +} + +// Save saves the user's configurations in conf/users/{username}.json. +func (u *User) Save() bool { + bytes, err := json.MarshalIndent(u, "", " ") + + if nil != err { + logger.Error(err) + + return false + } + + if err = ioutil.WriteFile("conf/users/"+u.Name+".json", bytes, 0644); nil != err { + logger.Error(err) + + return false + } + + return true +} + +// GetWorkspace gets workspace path of the user. +// +// Compared to the use of Wide.Workspace, this function will be processed as follows: +// 1. Replace {WD} variable with the actual directory path +// 2. Replace ${GOPATH} with enviorment variable GOPATH +// 3. Replace "/" with "\\" (Windows) +func (u *User) GetWorkspace() string { + w := strings.Replace(u.Workspace, "{WD}", Wide.WD, 1) + w = strings.Replace(w, "${GOPATH}", os.Getenv("GOPATH"), 1) + + return filepath.FromSlash(w) +} + +// GetOwner gets the user the specified path belongs to. Returns "" if not found. +func GetOwner(path string) string { + for _, user := range Users { + if strings.HasPrefix(path, user.GetWorkspace()) { + return user.Name + } + } + + return "" +} diff --git a/conf/users/admin.json b/conf/users/admin.json new file mode 100644 index 0000000..c6065ed --- /dev/null +++ b/conf/users/admin.json @@ -0,0 +1,26 @@ +{ + "Name": "admin", + "Password": "admin", + "Email": "", + "Gravatar": "d41d8cd98f00b204e9800998ecf8427e", + "Workspace": "${GOPATH}", + "Locale": "en_US", + "GoFormat": "gofmt", + "FontFamily": "Helvetica", + "FontSize": "13px", + "Theme": "black", + "Editor": { + "FontFamily": "Consolas, 'Courier New', monospace", + "FontSize": "13px", + "LineHeight": "17px", + "Theme": "wide", + "TabSize": "4" + }, + "LatestSessionContent": { + "FileTree": [ + "D:\\GoGoGo\\src" + ], + "Files": [], + "CurrentFile": "" + } +} \ No newline at end of file diff --git a/conf/wide.go b/conf/wide.go index 1ebc3af..b7ff045 100644 --- a/conf/wide.go +++ b/conf/wide.go @@ -12,13 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package conf includes configurations related manipulations, all configurations (including user configurations) are -// stored in wide.json. +// Package conf includes configurations related manipulations. package conf import ( - "crypto/md5" - "encoding/hex" "encoding/json" "io/ioutil" "os" @@ -40,108 +37,102 @@ const ( PathSeparator = string(os.PathSeparator) // PathListSeparator holds the OS-specific path list separator. PathListSeparator = string(os.PathListSeparator) -) -const ( // WideVersion holds the current wide version. WideVersion = "1.1.0" // CodeMirrorVer holds the current editor version. CodeMirrorVer = "4.8" ) -// LatestSessionContent represents the latest session content. -type LatestSessionContent struct { - FileTree []string // paths of expanding nodes of file tree - Files []string // paths of files of opening editor tabs - CurrentFile string // path of file of the current focused editor tab -} - -// User configuration. -type User struct { - Name string - Password string - Email string - Gravatar string // see http://gravatar.com - Workspace string // the GOPATH of this user - Locale string - GoFormat string - FontFamily string - FontSize string - Theme string - Editor *Editor - LatestSessionContent *LatestSessionContent -} - -// Editor configuration of a user. -type Editor struct { - FontFamily string - FontSize string - LineHeight string - Theme string - TabSize string -} - // Configuration. type conf struct { - IP string // server ip, ${ip} - Port string // server port - Context string // server context - Server string // server host and port ({IP}:{Port}) - StaticServer string // static resources server scheme, host and port (http://{IP}:{Port}) - LogLevel string // logging level - Channel string // channel (ws://{IP}:{Port}) - HTTPSessionMaxAge int // HTTP session max age (in seciond) - StaticResourceVersion string // version of static resources - MaxProcs int // Go max procs - RuntimeMode string // runtime mode (dev/prod) - WD string // current working direcitory, ${pwd} - Locale string // default locale - Users []*User // configurations of users + IP string // server ip, ${ip} + Port string // server port + Context string // server context + Server string // server host and port ({IP}:{Port}) + StaticServer string // static resources server scheme, host and port (http://{IP}:{Port}) + LogLevel string // logging level: trace/debug/info/warn/error + Channel string // channel (ws://{IP}:{Port}) + HTTPSessionMaxAge int // HTTP session max age (in seciond) + StaticResourceVersion string // version of static resources + MaxProcs int // Go max procs + RuntimeMode string // runtime mode (dev/prod) + WD string // current working direcitory, ${pwd} + Locale string // default locale } -// Configuration variable. -var Wide conf - -// A raw copy of configuration variable. -// -// Save function will use this variable to persist. -var rawWide conf - // Logger. var logger = log.NewLogger(os.Stdout) +// Wide configurations. +var Wide *conf + +// configurations of users. +var Users []*User + // Indicates whether runs via Docker. var Docker bool -// NewUser creates a user with the specified username, password, email and workspace. -func NewUser(username, password, email, workspace string) *User { - hash := md5.New() - hash.Write([]byte(email)) - gravatar := hex.EncodeToString(hash.Sum(nil)) - - return &User{Name: username, Password: password, Email: email, Gravatar: gravatar, Workspace: workspace, - Locale: Wide.Locale, GoFormat: "gofmt", FontFamily: "Helvetica", FontSize: "13px", Theme: "default", - Editor: &Editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit", LineHeight: "17px", - Theme: "wide", TabSize: "4"}} +// Load loads the Wide configurations from wide.json and users' configurations from users/{username}.json. +func Load(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel string, confDocker bool) { + initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel, confDocker) + initUsers() } -// Load loads the configurations from wide.json. -func Load(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel string, - confDocker bool) { - bytes, _ := ioutil.ReadFile(confPath) +func initUsers() { + f, err := os.Open("conf/users") + if nil != err { + logger.Error(err) - err := json.Unmarshal(bytes, &Wide) + os.Exit(-1) + } + + names, err := f.Readdirnames(-1) + if nil != err { + logger.Error(err) + + os.Exit(-1) + } + f.Close() + + for _, name := range names { + user := &User{} + + bytes, _ := ioutil.ReadFile("conf/users/" + name) + + err := json.Unmarshal(bytes, user) + if err != nil { + logger.Errorf("Parses [%s] error: %v", name, err) + + os.Exit(-1) + } + + Users = append(Users, user) + } + + initWorkspaceDirs() + initCustomizedConfs() +} + +func initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel string, confDocker bool) { + bytes, err := ioutil.ReadFile(confPath) + if nil != err { + logger.Error(err) + + os.Exit(-1) + } + + Wide = &conf{} + + err = json.Unmarshal(bytes, Wide) if err != nil { - logger.Error("Parses wide.json error: ", err) + logger.Error("Parses [wide.json] error: ", err) os.Exit(-1) } log.SetLevel(Wide.LogLevel) - // keep the raw content - json.Unmarshal(bytes, &rawWide) - logger.Debug("Conf: \n" + string(bytes)) // Working Driectory @@ -204,12 +195,6 @@ func Load(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer Wide.Server = strings.Replace(Wide.Server, "{Port}", Wide.Port, 1) Wide.StaticServer = strings.Replace(Wide.StaticServer, "{Port}", Wide.Port, 1) - - // upgrade if need - upgrade() - - initWorkspaceDirs() - initCustomizedConfs() } // FixedTimeCheckEnv checks Wide runtime enviorment periodically (7 minutes). @@ -261,20 +246,9 @@ func checkEnv() { } } -// FixedTimeSave saves configurations (wide.json) periodically (1 minute). -// -// Main goal of this function is to save user session content, for restoring session content while user open Wide next time. -func FixedTimeSave() { - go func() { - for _ = range time.Tick(time.Minute) { - Save() - } - }() -} - // GetUserWorkspace gets workspace path with the specified username, returns "" if not found. -func (c *conf) GetUserWorkspace(username string) string { - for _, user := range c.Users { +func GetUserWorkspace(username string) string { + for _, user := range Users { if user.Name == username { return user.GetWorkspace() } @@ -284,8 +258,8 @@ func (c *conf) GetUserWorkspace(username string) string { } // GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports". -func (c *conf) GetGoFmt(username string) string { - for _, user := range c.Users { +func GetGoFmt(username string) string { + for _, user := range Users { if user.Name == username { switch user.GoFormat { case "gofmt": @@ -302,22 +276,9 @@ func (c *conf) GetGoFmt(username string) string { return "gofmt" } -// GetWorkspace gets workspace path of the user. -// -// Compared to the use of Wide.Workspace, this function will be processed as follows: -// 1. Replace {WD} variable with the actual directory path -// 2. Replace ${GOPATH} with enviorment variable GOPATH -// 3. Replace "/" with "\\" (Windows) -func (u *User) GetWorkspace() string { - w := strings.Replace(u.Workspace, "{WD}", Wide.WD, 1) - w = strings.Replace(w, "${GOPATH}", os.Getenv("GOPATH"), 1) - - return filepath.FromSlash(w) -} - // GetUser gets configuration of the user specified by the given username, returns nil if not found. -func (*conf) GetUser(username string) *User { - for _, user := range Wide.Users { +func GetUser(username string) *User { + for _, user := range Users { if user.Name == username { return user } @@ -326,13 +287,10 @@ func (*conf) GetUser(username string) *User { return nil } -// Save saves Wide configurations. +// Save saves Wide and all users' configurations. func Save() bool { - // just the Users field are volatile - rawWide.Users = Wide.Users - - // format - bytes, err := json.MarshalIndent(rawWide, "", " ") + // Wide, XXX: does we need to save wide.json? + bytes, err := json.MarshalIndent(Wide, "", " ") if nil != err { logger.Error(err) @@ -346,40 +304,17 @@ func Save() bool { return false } - return true -} - -// upgrade upgrades the wide.json. -func upgrade() { // Users - for _, user := range Wide.Users { - if "" == user.Theme { - user.Theme = "default" // since 1.1.0 - } - - if "" == user.Editor.Theme { - user.Editor.Theme = "wide" // since 1.1.0 - } - - if "" == user.Editor.TabSize { - user.Editor.TabSize = "4" // since 1.1.0 - } - - if "" != user.Email && "" == user.Gravatar { - hash := md5.New() - hash.Write([]byte(user.Email)) - gravatar := hex.EncodeToString(hash.Sum(nil)) - - user.Gravatar = gravatar - } + for _, user := range Users { + user.Save() } - Save() + return true } // initCustomizedConfs initializes the user customized configurations. func initCustomizedConfs() { - for _, user := range Wide.Users { + for _, user := range Users { UpdateCustomizedConf(user.Name) } } @@ -389,7 +324,7 @@ func initCustomizedConfs() { // 1. /static/user/{username}/style.css func UpdateCustomizedConf(username string) { var u *User - for _, user := range Wide.Users { // maybe it is a beauty of the trade-off of the another world between design and implementation + for _, user := range Users { // maybe it is a beauty of the trade-off of the another world between design and implementation if user.Name == username { u = user } @@ -438,7 +373,7 @@ func UpdateCustomizedConf(username string) { func initWorkspaceDirs() { paths := []string{} - for _, user := range Wide.Users { + for _, user := range Users { paths = append(paths, filepath.SplitList(user.GetWorkspace())...) } diff --git a/conf/wide.json b/conf/wide.json index 5e20bb5..c386746 100644 --- a/conf/wide.json +++ b/conf/wide.json @@ -11,35 +11,5 @@ "MaxProcs": 4, "RuntimeMode": "dev", "WD": "${pwd}", - "Locale": "en_US", - "Users": [ - { - "Name": "admin", - "Password": "admin", - "Email": "", - "Gravatar": "", - "Workspace": "${GOPATH}", - "Locale": "en_US", - "GoFormat": "gofmt", - "FontFamily": "Helvetica", - "FontSize": "13px", - "Theme": "default", - "Editor": { - "FontFamily": "Consolas, 'Courier New', monospace", - "FontSize": "13px", - "LineHeight": "17px", - "Theme": "wide", - "TabSize": "4" - }, - "LatestSessionContent": { - "FileTree": [ - "D:\\GoGoGo\\src", - "D:\\GoGoGo\\src\\demo", - "D:\\GoGoGo\\src\\hello" - ], - "Files": [], - "CurrentFile": "" - } - } - ] + "Locale": "en_US" } \ No newline at end of file diff --git a/editor/editors.go b/editor/editors.go index 1c834cb..42fd650 100644 --- a/editor/editors.go +++ b/editor/editors.go @@ -147,7 +147,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) { // logger.Infof("offset: %d", offset) - userWorkspace := conf.Wide.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(username) workspaces := filepath.SplitList(userWorkspace) libPath := "" for _, workspace := range workspaces { @@ -455,7 +455,7 @@ func getCursorOffset(code string, line, ch int) (offset int) { } func setCmdEnv(cmd *exec.Cmd, username string) { - userWorkspace := conf.Wide.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(username) cmd.Env = append(cmd.Env, "GOPATH="+userWorkspace, diff --git a/editor/formatter.go b/editor/formatter.go index dd89421..fa6c59b 100644 --- a/editor/formatter.go +++ b/editor/formatter.go @@ -77,7 +77,7 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) { return } - fmt := conf.Wide.GetGoFmt(username) + fmt := conf.GetGoFmt(username) argv := []string{filePath} cmd := exec.Command(fmt, argv...) diff --git a/file/files.go b/file/files.go index e4498f9..cf6202b 100644 --- a/file/files.go +++ b/file/files.go @@ -82,7 +82,7 @@ func GetFiles(w http.ResponseWriter, r *http.Request) { } username := session.Values["username"].(string) - userWorkspace := conf.Wide.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(username) workspaces := filepath.SplitList(userWorkspace) root := Node{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", Children: []*Node{}} @@ -163,14 +163,16 @@ func GetFile(w http.ResponseWriter, r *http.Request) { data["mode"] = "img" - user := GetUsre(path) - if nil == user { + username := conf.GetOwner(path) + if "" == username { logger.Warnf("The path [%s] has no owner") data["path"] = "" return } + user := conf.GetUser(username) + data["path"] = "/workspace/" + user.Name + "/" + strings.Replace(path, user.GetWorkspace(), "", 1) return @@ -356,7 +358,7 @@ func Find(w http.ResponseWriter, r *http.Request) { } username := session.Values["username"].(string) - userWorkspace := conf.Wide.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(username) workspaces := filepath.SplitList(userWorkspace) if "" != path && !util.File.IsDir(path) { @@ -406,7 +408,7 @@ func SearchText(w http.ResponseWriter, r *http.Request) { dir := args["dir"].(string) if "" == dir { - userWorkspace := conf.Wide.GetUserWorkspace(wSession.Username) + userWorkspace := conf.GetUserWorkspace(wSession.Username) workspaces := filepath.SplitList(userWorkspace) dir = workspaces[0] } @@ -736,14 +738,3 @@ func searchInFile(path string, text string) []*Snippet { return ret } - -// GetUsre gets the user the specified path belongs to. Returns nil if not found. -func GetUsre(path string) *conf.User { - for _, user := range conf.Wide.Users { - if strings.HasPrefix(path, user.GetWorkspace()) { - return user - } - } - - return nil -} diff --git a/main.go b/main.go index 4ee8e92..1d63417 100644 --- a/main.go +++ b/main.go @@ -47,13 +47,13 @@ var logger *log.Logger // The only one init function in Wide. func init() { confPath := flag.String("conf", "conf/wide.json", "path of wide.json") - confIP := flag.String("ip", "", "ip to visit") - confPort := flag.String("port", "", "port to visit") + confIP := flag.String("ip", "", "this will overwrite Wide.IP if specified") + confPort := flag.String("port", "", "this will overwrite Wide.Port if specified") confServer := flag.String("server", "", "this will overwrite Wide.Server if specified") - confLogLevel := flag.String("log_level", "info", "logging level: trace/debug/info/warn/error") + confLogLevel := flag.String("log_level", "info", "this will overwrite Wide.LogLevel if specified") confStaticServer := flag.String("static_server", "", "this will overwrite Wide.StaticServer if specified") confContext := flag.String("context", "", "this will overwrite Wide.Context if specified") - confChannel := flag.String("channel", "", "this will overwrite Wide.XXXChannel if specified") + confChannel := flag.String("channel", "", "this will overwrite Wide.Channel if specified") confStat := flag.Bool("stat", false, "whether report statistics periodically") confDocker := flag.Bool("docker", false, "whether run in a docker container") @@ -77,8 +77,8 @@ func init() { *confDocker) conf.FixedTimeCheckEnv() - conf.FixedTimeSave() + session.FixedTimeSave() session.FixedTimeRelease() if *confStat { @@ -103,7 +103,7 @@ func main() { serveSingle("/favicon.ico", "./static/favicon.ico") // workspaces - for _, user := range conf.Wide.Users { + for _, user := range conf.Users { http.Handle(conf.Wide.Context+"/workspace/"+user.Name+"/", http.StripPrefix(conf.Wide.Context+"/workspace/"+user.Name+"/", http.FileServer(http.Dir(user.GetWorkspace())))) } @@ -193,7 +193,7 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { wideSession := session.WideSessions.New(httpSession, sid) username := httpSession.Values["username"].(string) - user := conf.Wide.GetUser(username) + user := conf.GetUser(username) if nil == user { logger.Warnf("Not found user [%s]", username) @@ -248,8 +248,8 @@ func startHandler(w http.ResponseWriter, r *http.Request) { httpSession.Save(r, w) username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale - userWorkspace := conf.Wide.GetUserWorkspace(username) + locale := conf.GetUser(username).Locale + userWorkspace := conf.GetUserWorkspace(username) sid := r.URL.Query()["sid"][0] wSession := session.WideSessions.Get(sid) @@ -288,7 +288,7 @@ func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) { httpSession.Save(r, w) username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale + locale := conf.GetUser(username).Locale model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale} @@ -320,7 +320,7 @@ func aboutHandler(w http.ResponseWriter, r *http.Request) { httpSession.Save(r, w) username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale + locale := conf.GetUser(username).Locale model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, "ver": conf.WideVersion, "goos": runtime.GOOS, "goarch": runtime.GOARCH, "gover": runtime.Version()} diff --git a/notification/notifications.go b/notification/notifications.go index 91fed48..297329c 100644 --- a/notification/notifications.go +++ b/notification/notifications.go @@ -64,7 +64,7 @@ func event2Notification(e *event.Event) { httpSession, _ := session.HTTPSession.Get(wsChannel.Request, "wide-session") username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale + locale := conf.GetUser(username).Locale var notification *Notification diff --git a/output/outputs.go b/output/outputs.go index 2d7118c..21dd2d5 100644 --- a/output/outputs.go +++ b/output/outputs.go @@ -263,7 +263,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { return } username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale + locale := conf.GetUser(username).Locale var args map[string]interface{} @@ -522,7 +522,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { return } username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale + locale := conf.GetUser(username).Locale var args map[string]interface{} @@ -640,7 +640,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { return } username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale + locale := conf.GetUser(username).Locale var args map[string]interface{} @@ -805,7 +805,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { return } username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale + locale := conf.GetUser(username).Locale var args map[string]interface{} @@ -937,7 +937,7 @@ func StopHandler(w http.ResponseWriter, r *http.Request) { } func setCmdEnv(cmd *exec.Cmd, username string) { - userWorkspace := conf.Wide.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(username) cmd.Env = append(cmd.Env, "GOPATH="+userWorkspace, diff --git a/session/sessions.go b/session/sessions.go index 5c92aea..d675b9e 100644 --- a/session/sessions.go +++ b/session/sessions.go @@ -242,7 +242,7 @@ func SaveContent(w http.ResponseWriter, r *http.Request) { wSession.Content = args.LatestSessionContent - for _, user := range conf.Wide.Users { + for _, user := range conf.Users { if user.Name == wSession.Username { // update the variable in-memory, conf.FixedTimeSave() function will persist it periodically user.LatestSessionContent = wSession.Content diff --git a/session/users.go b/session/users.go index 21e6591..98a5822 100644 --- a/session/users.go +++ b/session/users.go @@ -26,6 +26,7 @@ import ( "strconv" "sync" "text/template" + "time" "github.com/b3log/wide/conf" "github.com/b3log/wide/i18n" @@ -61,7 +62,7 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) { httpSession.Save(r, w) username := httpSession.Values["username"].(string) - user := conf.Wide.GetUser(username) + user := conf.GetUser(username) if "GET" == r.Method { model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(user.Locale), "user": user, @@ -134,7 +135,7 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) { conf.UpdateCustomizedConf(username) - succ = conf.Save() + succ = user.Save() } // LoginHandler handles request of user login. @@ -178,7 +179,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) { } succ = false - for _, user := range conf.Wide.Users { + for _, user := range conf.Users { if user.Name == args.Username && user.Password == args.Password { succ = true @@ -219,7 +220,7 @@ func SignUpUser(w http.ResponseWriter, r *http.Request) { if "GET" == r.Method { // show the user sign up page - firstUserWorkspace := conf.Wide.GetUserWorkspace(conf.Wide.Users[0].Name) + firstUserWorkspace := conf.GetUserWorkspace(conf.Users[0].Name) dir := filepath.Dir(firstUserWorkspace) model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale), @@ -266,6 +267,46 @@ func SignUpUser(w http.ResponseWriter, r *http.Request) { } } +// FixedTimeSave saves online users' configurations periodically (1 minute). +// +// Main goal of this function is to save user session content, for restoring session content while user open Wide next time. +func FixedTimeSave() { + go func() { + for _ = range time.Tick(time.Minute) { + users := getOnlineUsers() + + for _, u := range users { + if u.Save() { + logger.Tracef("Saved online user [%s]'s configurations") + } + } + } + }() +} + +func getOnlineUsers() []*conf.User { + ret := []*conf.User{} + + usernames := map[string]string{} // distinct username + for _, s := range WideSessions { + usernames[s.Username] = "" + } + + for _, username := range usernames { + u := conf.GetUser(username) + + if nil == u { + logger.Warnf("Not found user [%s]", username) + + continue + } + + ret = append(ret, u) + } + + return ret +} + // addUser add a user with the specified username, password and email. // // 1. create the user's workspace @@ -276,7 +317,7 @@ func addUser(username, password, email string) string { addUserMutex.Lock() defer addUserMutex.Unlock() - for _, user := range conf.Wide.Users { + for _, user := range conf.Users { if user.Name == username { return userExists } @@ -286,12 +327,12 @@ func addUser(username, password, email string) string { } } - firstUserWorkspace := conf.Wide.GetUserWorkspace(conf.Wide.Users[0].Name) + firstUserWorkspace := conf.GetUserWorkspace(conf.Users[0].Name) dir := filepath.Dir(firstUserWorkspace) workspace := filepath.Join(dir, username) newUser := conf.NewUser(username, password, email, workspace) - conf.Wide.Users = append(conf.Wide.Users, newUser) + conf.Users = append(conf.Users, newUser) if !conf.Save() { return userCreateError diff --git a/shell/shells.go b/shell/shells.go index 96969d9..424275f 100644 --- a/shell/shells.go +++ b/shell/shells.go @@ -63,7 +63,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { wideSession := session.WideSessions.New(httpSession, sid) username := httpSession.Values["username"].(string) - locale := conf.Wide.GetUser(username).Locale + locale := conf.GetUser(username).Locale model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, "session": wideSession} @@ -182,7 +182,7 @@ func pipeCommands(username string, commands ...*exec.Cmd) string { } func setCmdEnv(cmd *exec.Cmd, username string) { - userWorkspace := conf.Wide.GetUserWorkspace(username) + userWorkspace := conf.GetUserWorkspace(username) cmd.Env = append(cmd.Env, "TERM="+os.Getenv("TERM"),