This commit is contained in:
Liang Ding 2019-05-16 23:17:25 +08:00
parent 912c5821f1
commit b5aa4f9a7d
No known key found for this signature in database
GPG Key ID: 136F30F901A2231D
26 changed files with 348 additions and 394 deletions

2
.gitignore vendored
View File

@ -1,8 +1,6 @@
/wide.exe
/wide
/static/user/admin/style.css
/header
/header.exe

View File

@ -15,9 +15,6 @@
package conf
import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"io/ioutil"
"os"
@ -25,8 +22,6 @@ import (
"regexp"
"strings"
"time"
"github.com/b3log/wide/util"
)
// Panel represents a UI panel.
@ -52,11 +47,9 @@ type LatestSessionContent struct {
// User configuration.
type User struct {
Id string
Name string
Password string
Salt string
Email string
Gravatar string // see http://gravatar.com
Avatar string
Workspace string // the GOPATH of this user (maybe contain several paths splitted by os.PathListSeparator)
Locale string
GoFormat string
@ -83,27 +76,6 @@ type editor struct {
TabSize string
}
// NewUser creates a user with the specified username, password, email and workspace.
func NewUser(username, password, email, workspace string) *User {
md5hash := md5.New()
md5hash.Write([]byte(email))
gravatar := hex.EncodeToString(md5hash.Sum(nil))
salt := util.Rand.String(16)
password = Salt(password, salt)
now := time.Now().UnixNano()
return &User{Name: username, Password: password, Salt: salt, Email: email, Gravatar: gravatar, Workspace: workspace,
Locale: Wide.Locale, GoFormat: "gofmt",
GoBuildArgsForLinux: "-i", GoBuildArgsForWindows: "-i", GoBuildArgsForDarwin: "-i",
FontFamily: "Helvetica", FontSize: "13px", Theme: "default",
Keymap: "wide",
Created: now, Updated: now, Lived: now,
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, "", " ")
@ -115,12 +87,12 @@ func (u *User) Save() bool {
}
if "" == string(bytes) {
logger.Error("Truncated user [" + u.Name + "]")
logger.Error("Truncated user [" + u.Id + "]")
return false
}
if err = ioutil.WriteFile(filepath.Join(Wide.Users, u.Name+".json"), bytes, 0644); nil != err {
if err = ioutil.WriteFile(filepath.Join(Wide.Users, u.Id+".json"), bytes, 0644); nil != err {
logger.Error(err)
return false
@ -129,6 +101,20 @@ func (u *User) Save() bool {
return true
}
// NewUser creates a user with the specified username and workspace.
func NewUser(id, name, avatar, workspace string) *User {
now := time.Now().UnixNano()
return &User{Id: id, Name: name, Avatar: avatar, Workspace: workspace,
Locale: Wide.Locale, GoFormat: "gofmt",
GoBuildArgsForLinux: "-i", GoBuildArgsForWindows: "-i", GoBuildArgsForDarwin: "-i",
FontFamily: "Helvetica", FontSize: "13px", Theme: "default",
Keymap: "wide",
Created: now, Updated: now, Lived: now,
Editor: &editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit", LineHeight: "17px",
Theme: "wide", TabSize: "4"}}
}
// WorkspacePath gets workspace path of the user.
//
// Compared to the use of Wide.Workspace, this function will be processed as follows:
@ -168,17 +154,9 @@ func (u *User) BuildArgs(os string) []string {
func GetOwner(path string) string {
for _, user := range Users {
if strings.HasPrefix(path, user.WorkspacePath()) {
return user.Name
return user.Id
}
}
return ""
}
// Salt salts the specified password with the specified salt.
func Salt(password, salt string) string {
sha1hash := sha1.New()
sha1hash.Write([]byte(password + salt))
return hex.EncodeToString(sha1hash.Sum(nil))
}

View File

@ -1,55 +0,0 @@
{
"Name": "admin",
"Password": "d1bfca21893c908e64fabda01d71294b1ccdcaa7",
"Salt": "dnoyeb",
"Email": "",
"Gravatar": "d41d8cd98f00b204e9800998ecf8427e",
"Workspace": "${GOPATH}",
"Locale": "en_US",
"GoFormat": "gofmt",
"GoBuildArgsForLinux": "-i",
"GoBuildArgsForWindows": "-i",
"GoBuildArgsForDarwin": "-i",
"FontFamily": "Helvetica",
"FontSize": "13px",
"Theme": "default",
"Keymap": "wide",
"Created": 1414080000000000000,
"Updated": 1492963306222604966,
"Lived": 1492963306222604966,
"Editor": {
"FontFamily": "Consolas, 'Courier New', monospace",
"FontSize": "13px",
"LineHeight": "17px",
"Theme": "wide",
"TabSize": "4"
},
"LatestSessionContent": {
"fileTree": [
"/Users/Vanessa/Work/Code/GoGoGo/src",
"/Users/Vanessa/Work/Code/GoGoGo/src/github.com",
"/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log",
"/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide",
"/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide/.idea",
"/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide/static",
"/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide/static/css",
"/Users/Vanessa/Work/Code/GoGoGo/src/github.com/b3log/wide/static/js"
],
"files": [],
"currentFile": "",
"layout": {
"side": {
"state": "normal",
"size": 200
},
"sideRight": {
"state": "normal",
"size": 200
},
"bottom": {
"state": "normal",
"size": 100
}
}
}
}

View File

@ -42,6 +42,8 @@ const (
WideVersion = "1.5.3"
// CodeMirrorVer holds the current editor version.
CodeMirrorVer = "5.1"
// UserAgent represents HTTP client user agent.
UserAgent = "Wide/" + WideVersion + "; +https://github.com/b3log/wide"
HelloWorld = `package main
@ -70,7 +72,7 @@ type conf struct {
Locale string // default locale
Playground string // playground directory
Users string // users directory
UsersWorkspaces string // users' workspaces directory (admin defaults to ${GOPATH}, others using this)
UsersWorkspaces string // users' workspaces directory
AllowRegister bool // allow register or not
Autocomplete bool // default autocomplete
}
@ -335,10 +337,10 @@ func checkEnv() {
}
}
// GetUserWorkspace gets workspace path with the specified username, returns "" if not found.
func GetUserWorkspace(username string) string {
// GetUserWorkspace gets workspace path with the specified user id, returns "" if not found.
func GetUserWorkspace(userId string) string {
for _, user := range Users {
if user.Name == username {
if user.Id == userId {
return user.WorkspacePath()
}
}
@ -347,9 +349,9 @@ func GetUserWorkspace(username string) string {
}
// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports".
func GetGoFmt(username string) string {
func GetGoFmt(userId string) string {
for _, user := range Users {
if user.Name == username {
if user.Id == userId {
switch user.GoFormat {
case "gofmt":
return "gofmt"
@ -365,15 +367,14 @@ func GetGoFmt(username string) string {
return "gofmt"
}
// GetUser gets configuration of the user specified by the given username, returns nil if not found.
func GetUser(username string) *User {
if "playground" == username { // reserved user for Playground
// mock it
return NewUser("playground", "", "", "")
// GetUser gets configuration of the user specified by the given user id, returns nil if not found.
func GetUser(id string) *User {
if "playground" == id { // reserved user for Playground
return NewUser("playground", "playground", "", "")
}
for _, user := range Users {
if user.Name == username {
if user.Id == id {
return user
}
}
@ -384,17 +385,17 @@ func GetUser(username string) *User {
// initCustomizedConfs initializes the user customized configurations.
func initCustomizedConfs() {
for _, user := range Users {
UpdateCustomizedConf(user.Name)
UpdateCustomizedConf(user.Id)
}
}
// UpdateCustomizedConf creates (if not exists) or updates user customized configuration files.
//
// 1. /static/user/{username}/style.css
func UpdateCustomizedConf(username string) {
func UpdateCustomizedConf(userId string) {
var u *User
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 {
if user.Id == userId {
u = user
}
}
@ -413,7 +414,7 @@ func UpdateCustomizedConf(username string) {
}
wd := util.OS.Pwd()
dir := filepath.Clean(wd + "/static/user/" + u.Name)
dir := filepath.Clean(wd + "/static/user/" + u.Id)
if err := os.MkdirAll(dir, 0755); nil != err {
logger.Error(err)

View File

@ -117,7 +117,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := session.Values["username"].(string)
uid := session.Values["uid"].(string)
path := args["path"].(string)
@ -147,7 +147,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
logger.Tracef("offset: %d", offset)
userWorkspace := conf.GetUserWorkspace(username)
userWorkspace := conf.GetUserWorkspace(uid)
workspaces := filepath.SplitList(userWorkspace)
libPath := ""
for _, workspace := range workspaces {
@ -183,7 +183,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
defer util.RetResult(w, r, result)
session, _ := session.HTTPSession.Get(r, "wide-session")
username := session.Values["username"].(string)
uid := session.Values["uid"].(string)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
@ -228,7 +228,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command(ideStub, argv...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
output, err := cmd.CombinedOutput()
if nil != err {
@ -259,7 +259,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := session.Values["username"].(string)
uid := session.Values["uid"].(string)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
@ -304,7 +304,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command(ideStub, argv...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
output, err := cmd.CombinedOutput()
if nil != err {
@ -347,7 +347,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := session.Values["username"].(string)
uid := session.Values["uid"].(string)
var args map[string]interface{}
@ -392,7 +392,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command(ideStub, argv...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
output, err := cmd.CombinedOutput()
if nil != err {
@ -453,8 +453,8 @@ func getCursorOffset(code string, line, ch int) (offset int) {
return offset
}
func setCmdEnv(cmd *exec.Cmd, username string) {
userWorkspace := conf.GetUserWorkspace(username)
func setCmdEnv(cmd *exec.Cmd, userId string) {
userWorkspace := conf.GetUserWorkspace(userId)
cmd.Env = append(cmd.Env,
"GOPATH="+userWorkspace,

View File

@ -80,12 +80,12 @@ func GetFilesHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetGzResult(w, r, result)
userWorkspace := conf.GetUserWorkspace(username)
userWorkspace := conf.GetUserWorkspace(uid)
workspaces := filepath.SplitList(userWorkspace)
root := Node{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", IsParent: true, Children: []*Node{}}
@ -129,12 +129,12 @@ func RefreshDirectoryHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
r.ParseForm()
path := r.FormValue("path")
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
if !util.Go.IsAPI(path) && !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -162,7 +162,7 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
@ -178,7 +178,7 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) {
path := args["path"].(string)
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
if !util.Go.IsAPI(path) && !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -212,7 +212,7 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
user := conf.GetUser(username)
user := conf.GetUser(uid)
data["path"] = "/workspace/" + user.Name + "/" + strings.Replace(path, user.WorkspacePath(), "", 1)
@ -238,7 +238,7 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
@ -255,7 +255,7 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
filePath := args["file"].(string)
sid := args["sid"].(string)
if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) {
if util.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -294,7 +294,7 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
@ -310,7 +310,7 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) {
path := args["path"].(string)
if util.Go.IsAPI(path) || !session.CanAccess(username, path) {
if util.Go.IsAPI(path) || !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -331,9 +331,9 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) {
}
if "f" == fileType {
logger.Debugf("Created a file [%s] by user [%s]", path, wSession.Username)
logger.Debugf("Created a file [%s] by user [%s]", path, wSession.UserId)
} else {
logger.Debugf("Created a dir [%s] by user [%s]", path, wSession.Username)
logger.Debugf("Created a dir [%s] by user [%s]", path, wSession.UserId)
}
}
@ -346,7 +346,7 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
@ -362,7 +362,7 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) {
path := args["path"].(string)
if util.Go.IsAPI(path) || !session.CanAccess(username, path) {
if util.Go.IsAPI(path) || !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -381,7 +381,7 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
logger.Debugf("Removed a file [%s] by user [%s]", path, wSession.Username)
logger.Debugf("Removed a file [%s] by user [%s]", path, wSession.UserId)
}
// RenameFileHandler handles request of renaming file or directory.
@ -392,7 +392,7 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
@ -408,14 +408,14 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
oldPath := args["oldPath"].(string)
if util.Go.IsAPI(oldPath) ||
!session.CanAccess(username, oldPath) {
!session.CanAccess(uid, oldPath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
newPath := args["newPath"].(string)
if util.Go.IsAPI(newPath) || !session.CanAccess(username, newPath) {
if util.Go.IsAPI(newPath) || !session.CanAccess(uid, newPath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -434,7 +434,7 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
logger.Debugf("Renamed a file [%s] to [%s] by user [%s]", oldPath, newPath, wSession.Username)
logger.Debugf("Renamed a file [%s] to [%s] by user [%s]", oldPath, newPath, wSession.UserId)
}
// Use to find results sorting.
@ -457,7 +457,7 @@ func FindHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
@ -471,7 +471,7 @@ func FindHandler(w http.ResponseWriter, r *http.Request) {
}
path := args["path"].(string) // path of selected file in file tree
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
if !util.Go.IsAPI(path) && !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -479,7 +479,7 @@ func FindHandler(w http.ResponseWriter, r *http.Request) {
name := args["name"].(string)
userWorkspace := conf.GetUserWorkspace(username)
userWorkspace := conf.GetUserWorkspace(uid)
workspaces := filepath.SplitList(userWorkspace)
if "" != path && !util.File.IsDir(path) {
@ -536,7 +536,7 @@ func SearchTextHandler(w http.ResponseWriter, r *http.Request) {
dir := args["dir"].(string)
if "" == dir {
userWorkspace := conf.GetUserWorkspace(wSession.Username)
userWorkspace := conf.GetUserWorkspace(wSession.UserId)
workspaces := filepath.SplitList(userWorkspace)
dir = workspaces[0]
}

7
go.mod
View File

@ -3,12 +3,19 @@ module github.com/b3log/wide
go 1.12
require (
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/fsnotify/fsnotify v1.4.7
github.com/gorilla/context v0.0.0-20141217160251-215affda49ad // indirect
github.com/gorilla/securecookie v0.0.0-20150327155805-8e98dd730fc4 // indirect
github.com/gorilla/sessions v0.0.0-20150417174705-f61c3ec2cf65
github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a
github.com/hashicorp/go-version v1.2.0
github.com/moul/http2curl v1.0.0 // indirect
github.com/parnurzeal/gorequest v0.2.15
github.com/pkg/errors v0.8.1 // indirect
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
golang.org/x/net v0.0.0-20190514140710-3ec191127204 // indirect
golang.org/x/sys v0.0.0-20190515190549-87c872767d25 // indirect
golang.org/x/text v0.3.2
)

26
go.sum
View File

@ -1,5 +1,11 @@
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs=
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhPhOPHULCQQDnGhRelpFWHMLhQVWDsS0v4=
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v0.0.0-20141217160251-215affda49ad h1:wJwKN6X6iRRVnjdBgrkWjhBOvYm7yw5boqXwFUnBtbE=
github.com/gorilla/context v0.0.0-20141217160251-215affda49ad/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/securecookie v0.0.0-20150327155805-8e98dd730fc4 h1:X0DbEdoaUJT+NZ8mLHRNMSLmogzqLhsA1Eh6gs7Y7Zg=
@ -10,8 +16,28 @@ github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a h1:p/PGT+3UGSK7e
github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/parnurzeal/gorequest v0.2.15 h1:oPjDCsF5IkD4gUk6vIgsxYNaSgvAnIh1EJeROn3HdJU=
github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190514140710-3ec191127204 h1:4yG6GqBtw9C+UrLp6s2wtSniayy/Vd/3F7ffLE427XI=
golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190515190549-87c872767d25 h1:SSKQq5sjDoW0L0NaDoVl7d7HmtTxM0ezm0Ef9azs4uQ=
golang.org/x/sys v0.0.0-20190515190549-87c872767d25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

32
main.go
View File

@ -158,9 +158,7 @@ func main() {
http.HandleFunc(conf.Wide.Context+"/notification/ws", handlerWrapper(notification.WSHandler))
// user
http.HandleFunc(conf.Wide.Context+"/login", handlerWrapper(session.LoginHandler))
http.HandleFunc(conf.Wide.Context+"/logout", handlerWrapper(session.LogoutHandler))
http.HandleFunc(conf.Wide.Context+"/signup", handlerWrapper(session.SignUpUserHandler))
http.HandleFunc(conf.Wide.Context+"/preference", handlerWrapper(session.PreferenceHandler))
// playground
@ -197,8 +195,8 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
if "playground" == username { // reserved user for Playground
uid := httpSession.Values["uid"].(string)
if "playground" == uid { // reserved user for Playground
http.Redirect(w, r, conf.Wide.Context+"/login", http.StatusFound)
return
@ -210,9 +208,9 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
}
httpSession.Save(r, w)
user := conf.GetUser(username)
user := conf.GetUser(uid)
if nil == user {
logger.Warnf("Not found user [%s]", username)
logger.Warnf("Not found user [%s]", uid)
http.Redirect(w, r, conf.Wide.Context+"/login", http.StatusFound)
@ -221,14 +219,14 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
locale := user.Locale
wideSessions := session.WideSessions.GetByUsername(username)
wideSessions := session.WideSessions.GetByUserId(uid)
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
"username": username, "sid": session.WideSessions.GenId(), "latestSessionContent": user.LatestSessionContent,
"uid": uid, "sid": session.WideSessions.GenId(), "latestSessionContent": user.LatestSessionContent,
"pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer,
"user": user, "editorThemes": conf.GetEditorThemes(), "crossPlatforms": util.Go.GetCrossPlatforms()}
logger.Debugf("User [%s] has [%d] sessions", username, len(wideSessions))
logger.Debugf("User [%s] has [%d] sessions", uid, len(wideSessions))
t, err := template.ParseFiles("views/index.html")
if nil != err {
@ -279,9 +277,9 @@ func startHandler(w http.ResponseWriter, r *http.Request) {
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
userWorkspace := conf.GetUserWorkspace(username)
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
userWorkspace := conf.GetUserWorkspace(uid)
sid := r.URL.Query()["sid"][0]
wSession := session.WideSessions.Get(sid)
@ -290,7 +288,7 @@ func startHandler(w http.ResponseWriter, r *http.Request) {
}
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
"username": username, "workspace": userWorkspace, "ver": conf.WideVersion, "sid": sid}
"uid": uid, "workspace": userWorkspace, "ver": conf.WideVersion, "sid": sid}
t, err := template.ParseFiles("views/start.html")
@ -319,8 +317,8 @@ func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale}
@ -351,8 +349,8 @@ func aboutHandler(w http.ResponseWriter, r *http.Request) {
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).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()}

View File

@ -63,8 +63,8 @@ func event2Notification(e *event.Event) {
}
httpSession, _ := session.HTTPSession.Get(wsChannel.Request, "wide-session")
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var notification *Notification

View File

@ -44,8 +44,8 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
user := conf.GetUser(username)
uid := httpSession.Values["uid"].(string)
user := conf.GetUser(uid)
locale := user.Locale
var args map[string]interface{}
@ -61,7 +61,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
filePath := args["file"].(string)
if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) {
if util.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -101,7 +101,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", goBuildArgs...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
executable := filepath.Base(curDir) + suffix
executable = filepath.Join(curDir, executable)

View File

@ -43,8 +43,8 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
@ -58,7 +58,7 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
sid := args["sid"].(string)
filePath := args["path"].(string)
if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) {
if util.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -75,7 +75,7 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
suffix = ".exe"
}
user := conf.GetUser(username)
user := conf.GetUser(uid)
goBuildArgs := []string{}
goBuildArgs = append(goBuildArgs, "build")
goBuildArgs = append(goBuildArgs, user.BuildArgs(goos)...)
@ -83,7 +83,7 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", goBuildArgs...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
for i, env := range cmd.Env {
if strings.HasPrefix(env, "GOOS=") {

View File

@ -41,8 +41,8 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
@ -60,7 +60,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", "get")
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
stdout, err := cmd.StdoutPipe()
if nil != err {
@ -114,7 +114,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
defer util.Recover()
defer cmd.Wait()
logger.Debugf("User [%s, %s] is running [go get] [runningId=%d]", username, sid, runningId)
logger.Debugf("User [%s, %s] is running [go get] [runningId=%d]", uid, sid, runningId)
channelRet := map[string]interface{}{}
channelRet["cmd"] = "go get"
@ -123,11 +123,11 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
buf, _ := ioutil.ReadAll(reader)
if 0 != len(buf) {
logger.Debugf("User [%s, %s] 's [go get] [runningId=%d] has done (with error)", username, sid, runningId)
logger.Debugf("User [%s, %s] 's [go get] [runningId=%d] has done (with error)", uid, sid, runningId)
channelRet["output"] = "<span class='get-error'>" + i18n.Get(locale, "get-error").(string) + "</span>\n" + string(buf)
} else {
logger.Debugf("User [%s, %s] 's running [go get] [runningId=%d] has done", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go get] [runningId=%d] has done", uid, sid, runningId)
channelRet["output"] = "<span class='get-succ'>" + i18n.Get(locale, "get-succ").(string) + "</span>\n"
}

View File

@ -43,8 +43,8 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
@ -63,7 +63,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", "install")
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
logger.Debugf("go install %s", curDir)
@ -119,7 +119,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
defer util.Recover()
defer cmd.Wait()
logger.Debugf("User [%s, %s] is running [go install] [id=%d, dir=%s]", username, sid, runningId, curDir)
logger.Debugf("User [%s, %s] is running [go install] [id=%d, dir=%s]", uid, sid, runningId, curDir)
// read all
buf, _ := ioutil.ReadAll(reader)
@ -183,7 +183,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
}
if nil != session.OutputWS[sid] {
logger.Debugf("User [%s, %s] 's running [go install] [id=%d, dir=%s] has done", username, sid, runningId, curDir)
logger.Debugf("User [%s, %s] 's running [go install] [id=%d, dir=%s] has done", uid, sid, runningId, curDir)
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)

View File

@ -104,8 +104,8 @@ func parsePath(curDir, outputLine string) string {
return tagStart + text + tagEnd + msgPart
}
func setCmdEnv(cmd *exec.Cmd, username string) {
userWorkspace := conf.GetUserWorkspace(username)
func setCmdEnv(cmd *exec.Cmd, uid string) {
userWorkspace := conf.GetUserWorkspace(uid)
cache, err := os.UserCacheDir()
if nil != err {
logger.Warnf("Get user cache dir failed [" + err.Error() + "]")

View File

@ -86,7 +86,7 @@ func (procs *procs) Kill(wSession *session.WideSession, pid int) {
for i, p := range userProcesses {
if p.Pid == pid {
if err := p.Kill(); nil != err {
logger.Errorf("Kill a process [pid=%d] of user [%s, %s] failed [error=%v]", pid, wSession.Username, sid, err)
logger.Errorf("Kill a process [pid=%d] of user [%s, %s] failed [error=%v]", pid, wSession.UserId, sid, err)
} else {
var newProcesses []*os.Process
@ -96,7 +96,7 @@ func (procs *procs) Kill(wSession *session.WideSession, pid int) {
// bind process with wide session
wSession.SetProcesses(newProcesses)
logger.Debugf("Killed a process [pid=%d] of user [%s, %s]", pid, wSession.Username, sid)
logger.Debugf("Killed a process [pid=%d] of user [%s, %s]", pid, wSession.UserId, sid)
}
return

View File

@ -106,7 +106,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
go func(runningId int) {
defer util.Recover()
logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.Username, sid, runningId, filePath)
logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.UserId, sid, runningId, filePath)
go func() {
defer util.Recover()

View File

@ -41,8 +41,8 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
@ -61,7 +61,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", "test", "-v")
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
stdout, err := cmd.StdoutPipe()
if nil != err {
@ -114,7 +114,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
go func(runningId int) {
defer util.Recover()
logger.Debugf("User [%s, %s] is running [go test] [runningId=%d]", username, sid, runningId)
logger.Debugf("User [%s, %s] is running [go test] [runningId=%d]", uid, sid, runningId)
channelRet := map[string]interface{}{}
channelRet["cmd"] = "go test"
@ -126,11 +126,11 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
cmd.Wait()
if !cmd.ProcessState.Success() {
logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done (with error)", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done (with error)", uid, sid, runningId)
channelRet["output"] = "<span class='test-error'>" + i18n.Get(locale, "test-error").(string) + "</span>\n" + string(buf)
} else {
logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done", uid, sid, runningId)
channelRet["output"] = "<span class='test-succ'>" + i18n.Get(locale, "test-succ").(string) + "</span>\n" + string(buf)
}

View File

@ -41,8 +41,8 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
@ -61,7 +61,7 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", "vet", ".")
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
stdout, err := cmd.StdoutPipe()
if nil != err {
@ -114,7 +114,7 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
go func(runningId int) {
defer util.Recover()
logger.Debugf("User [%s, %s] is running [go vet] [runningId=%d]", username, sid, runningId)
logger.Debugf("User [%s, %s] is running [go vet] [runningId=%d]", uid, sid, runningId)
channelRet := map[string]interface{}{}
channelRet["cmd"] = "go vet"
@ -126,11 +126,11 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
cmd.Wait()
if !cmd.ProcessState.Success() {
logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done (with error)", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done (with error)", uid, sid, runningId)
channelRet["output"] = "<span class='vet-error'>" + i18n.Get(locale, "vet-error").(string) + "</span>\n" + string(buf)
} else {
logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done", uid, sid, runningId)
channelRet["output"] = "<span class='vet-succ'>" + i18n.Get(locale, "vet-succ").(string) + "</span>\n" + string(buf)
}

View File

@ -52,7 +52,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
locale := conf.Wide.Locale
@ -92,9 +92,9 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
"code": template.HTML(code), "ver": conf.WideVersion, "year": time.Now().Year(),
"embed": embed, "disqus": disqus, "fileName": fileName}
wideSessions := session.WideSessions.GetByUsername(username)
wideSessions := session.WideSessions.GetByUserId(uid)
logger.Debugf("User [%s] has [%d] sessions", username, len(wideSessions))
logger.Debugf("User [%s] has [%d] sessions", uid, len(wideSessions))
t, err := template.ParseFiles("views/playground/index.html")

View File

@ -108,7 +108,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
go func(runningId int) {
defer util.Recover()
logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.Username, sid, runningId, filePath)
logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.UserId, sid, runningId, filePath)
go func() {
defer util.Recover()

143
session/oauthctl.go Normal file
View File

@ -0,0 +1,143 @@
// Copyright (c) 2014-2019, b3log.org & hacpai.com
//
// 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
//
// https://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 session
import (
"crypto/tls"
"github.com/b3log/wide/conf"
"math/rand"
"net/http"
"strconv"
"strings"
"time"
"github.com/b3log/wide/util"
"github.com/parnurzeal/gorequest"
)
var states = map[string]string{}
// RedirectGitHubHandler redirects to GitHub auth page.
func RedirectGitHubHandler(w http.ResponseWriter, r *http.Request) {
requestResult := util.NewResult()
_, _, errs := gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
Get("https://hacpai.com/oauth/wide/client").
Set("user-agent", conf.UserAgent).Timeout(10 * time.Second).EndStruct(requestResult)
if nil != errs {
logger.Errorf("Get oauth client id failed: %+v", errs)
http.Error(w, "Get oauth info failed", http.StatusInternalServerError)
return
}
if 0 != requestResult.Code {
logger.Errorf("get oauth client id failed [code=%d, msg=%s]", requestResult.Code, requestResult.Msg)
http.Error(w, "Get oauth info failed", http.StatusNotFound)
return
}
data := requestResult.Data.(map[string]interface{})
clientId := data["clientId"].(string)
loginAuthURL := data["loginAuthURL"].(string)
referer := r.URL.Query().Get("referer")
if "" == referer || !strings.Contains(referer, "://") {
referer = conf.Wide.Server + referer
}
if strings.HasSuffix(referer, "/") {
referer = referer[:len(referer)-1]
}
state := util.Rand.String(16) + referer
states[state] = state
path := loginAuthURL + "?client_id=" + clientId + "&state=" + state + "&scope=public_repo,read:user,user:follow"
logger.Infof("redirect to github [" + path + "]")
http.Redirect(w, r, path, http.StatusSeeOther)
}
func GithubCallbackHandler(w http.ResponseWriter, r *http.Request) {
logger.Infof("Github callback [" + r.URL.String() + "]")
state := r.URL.Query().Get("state")
if _, exist := states[state]; !exist {
http.Error(w, "Get state param failed", http.StatusBadRequest)
return
}
delete(states, state)
referer := state[16:]
if strings.Contains(referer, "__0") || strings.Contains(referer, "__1") {
referer = referer[:len(referer)-len("__0")]
}
accessToken := r.URL.Query().Get("ak")
githubUser := GitHubUserInfo(accessToken)
if nil == githubUser {
logger.Warnf("Can not get user info with token [" + accessToken + "]")
http.Error(w, "Get user info failed", http.StatusUnauthorized)
return
}
githubId := githubUser["userId"].(string)
userName := githubUser["userName"].(string)
avatar := githubUser["userAvatar"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
user := conf.GetUser(githubId)
if nil == user {
msg := addUser(githubId, userName, avatar)
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)
logger.Debugf("Created a HTTP session [%s] for user [%s]", httpSession.Values["id"].(string), userName)
}
// GitHubUserInfo returns GitHub user info specified by the given access token.
func GitHubUserInfo(accessToken string) (ret map[string]interface{}) {
result := map[string]interface{}{}
response, data, errors := gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
Get("https://hacpai.com/github/user?ak="+accessToken).Timeout(7*time.Second).
Set("User-Agent", conf.UserAgent).EndStruct(&result)
if nil != errors || http.StatusOK != response.StatusCode {
logger.Errorf("Get github user info failed: %+v, %s", errors, data)
return nil
}
if 0 != result["sc"].(float64) {
return nil
}
return result["data"].(map[string]interface{})
}

View File

@ -46,7 +46,7 @@ import (
const (
sessionStateActive = iota
sessionStateClosed // (not used so far)
sessionStateClosed // (not used so far)
)
// Logger.
@ -75,7 +75,7 @@ var HTTPSession = sessions.NewCookieStore([]byte("BEYOND"))
// WideSession represents a session associated with a browser tab.
type WideSession struct {
ID string // id
Username string // username
UserId string // user id
HTTPSession *sessions.Session // HTTP session related
Processes []*os.Process // process set
EventQueue *event.UserEventQueue // event queue
@ -111,7 +111,7 @@ func FixedTimeRelease() {
for _, s := range WideSessions {
if s.Updated.Before(threshold) {
logger.Debugf("Removes a invalid session [%s], user [%s]", s.ID, s.Username)
logger.Debugf("Removes a invalid session [%s], user [%s]", s.ID, s.UserId)
WideSessions.Remove(s.ID)
}
@ -139,7 +139,7 @@ func FixedTimeReport() {
go func() {
defer util.Recover()
for _ = range time.Tick(10 * time.Minute) {
for _ = range time.Tick(10*time.Minute) {
users := userReports{}
processSum := 0
@ -147,7 +147,7 @@ func FixedTimeReport() {
processCnt := len(s.Processes)
processSum += processCnt
if report, exists := contains(users, s.Username); exists {
if report, exists := contains(users, s.UserId); exists {
if s.Updated.After(report.updated) {
report.updated = s.Updated
}
@ -155,7 +155,7 @@ func FixedTimeReport() {
report.sessionCnt++
report.processCnt += processCnt
} else {
users = append(users, &userReport{username: s.Username, sessionCnt: 1, processCnt: processCnt, updated: s.Updated})
users = append(users, &userReport{username: s.UserId, sessionCnt: 1, processCnt: processCnt, updated: s.Updated})
}
}
@ -231,7 +231,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
wSession = WideSessions.new(httpSession, sid)
logger.Tracef("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.Username)
logger.Tracef("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.UserId)
}
logger.Tracef("Open a new [Session Channel] with session [%s], %d", sid, len(SessionWS))
@ -263,7 +263,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
for {
if err := wsChan.ReadJSON(&input); err != nil {
logger.Tracef("[Session Channel] of session [%s] disconnected, releases all resources with it, user [%s]", sid, wSession.Username)
logger.Tracef("[Session Channel] of session [%s] disconnected, releases all resources with it, user [%s]", sid, wSession.UserId)
return
}
@ -307,7 +307,7 @@ func SaveContentHandler(w http.ResponseWriter, r *http.Request) {
wSession.Content = args.LatestSessionContent
for _, user := range conf.Users {
if user.Name == wSession.Username {
if user.Id == wSession.UserId {
// update the variable in-memory, session.FixedTimeSave() function will persist it periodically
user.LatestSessionContent = wSession.Content
@ -376,9 +376,9 @@ func (sessions *wSessions) Remove(sid string) {
// kill processes
for _, p := range s.Processes {
if err := p.Kill(); nil != err {
logger.Errorf("Can't kill process [%d] of session [%s], user [%s]", p.Pid, sid, s.Username)
logger.Errorf("Can't kill process [%d] of session [%s], user [%s]", p.Pid, sid, s.UserId)
} else {
logger.Debugf("Killed a process [%d] of session [%s], user [%s]", p.Pid, sid, s.Username)
logger.Debugf("Killed a process [%d] of session [%s], user [%s]", p.Pid, sid, s.UserId)
}
}
@ -410,12 +410,12 @@ func (sessions *wSessions) Remove(sid string) {
cnt := 0 // count wide sessions associated with HTTP session
for _, ses := range *sessions {
if ses.Username == s.Username {
if ses.UserId == s.UserId {
cnt++
}
}
logger.Debugf("Removed a session [%s] of user [%s], it has [%d] sessions currently", sid, s.Username, cnt)
logger.Debugf("Removed a session [%s] of user [%s], it has [%d] sessions currently", sid, s.UserId, cnt)
return
}
@ -423,14 +423,14 @@ func (sessions *wSessions) Remove(sid string) {
}
// GetByUsername gets wide sessions.
func (sessions *wSessions) GetByUsername(username string) []*WideSession {
func (sessions *wSessions) GetByUserId(userId string) []*WideSession {
mutex.Lock()
defer mutex.Unlock()
ret := []*WideSession{}
for _, s := range *sessions {
if s.Username == username {
if s.UserId == userId {
ret = append(ret, s)
}
}
@ -443,12 +443,12 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
mutex.Lock()
defer mutex.Unlock()
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
now := time.Now()
ret := &WideSession{
ID: sid,
Username: username,
UserId: uid,
HTTPSession: httpSession,
EventQueue: nil,
State: sessionStateActive,
@ -459,7 +459,7 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
*sessions = append(*sessions, ret)
if "playground" == username {
if "playground" == uid {
return ret
}
@ -526,7 +526,7 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
go func() {
defer util.Recover()
workspaces := filepath.SplitList(conf.GetUserWorkspace(username))
workspaces := filepath.SplitList(conf.GetUserWorkspace(uid))
for _, workspace := range workspaces {
filepath.Walk(filepath.Join(workspace, "src"), func(dirPath string, f os.FileInfo, err error) error {
if strings.HasPrefix(f.Name(), ".") || "node_modules" == f.Name() || "vendor" == f.Name() {

View File

@ -15,15 +15,11 @@
package session
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"math/rand"
"net/http"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"text/template"
@ -38,7 +34,6 @@ const (
// TODO: i18n
userExists = "user exists"
emailExists = "email exists"
userCreated = "user created"
userCreateError = "user create error"
notAllowRegister = "not allow register"
@ -63,8 +58,8 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
user := conf.GetUser(username)
uid := httpSession.Values["uid"].(string)
user := conf.GetUser(uid)
if "GET" == r.Method {
tmpLinux := user.GoBuildArgsForLinux
@ -116,8 +111,6 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
Keymap string
Workspace string
Username string
Password string
Email string
Locale string
Theme string
EditorFontFamily string
@ -143,14 +136,6 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
user.Keymap = args.Keymap
// XXX: disallow change workspace at present
// user.Workspace = args.Workspace
if user.Password != args.Password {
user.Password = conf.Salt(args.Password, user.Salt)
}
user.Email = args.Email
hash := md5.New()
hash.Write([]byte(user.Email))
user.Gravatar = hex.EncodeToString(hash.Sum(nil))
user.Locale = args.Locale
user.Theme = args.Theme
@ -160,7 +145,7 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
user.Editor.Theme = args.EditorTheme
user.Editor.TabSize = args.EditorTabSize
conf.UpdateCustomizedConf(username)
conf.UpdateCustomizedConf(uid)
now := time.Now().UnixNano()
user.Lived = now
@ -169,66 +154,6 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
result.Succ = user.Save()
}
// LoginHandler handles request of user login.
func LoginHandler(w http.ResponseWriter, r *http.Request) {
if "GET" == r.Method {
// show the login page
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale),
"locale": conf.Wide.Locale, "ver": conf.WideVersion, "year": time.Now().Year()}
t, err := template.ParseFiles("views/login.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
return
}
t.Execute(w, model)
return
}
// non-GET request as login request
result := util.NewResult()
defer util.RetResult(w, r, result)
args := struct {
Username string
Password string
}{}
args.Username = r.FormValue("username")
args.Password = r.FormValue("password")
result.Succ = false
for _, user := range conf.Users {
if user.Name == args.Username && user.Password == conf.Salt(args.Password, user.Salt) {
result.Succ = true
break
}
}
if !result.Succ {
return
}
// create a HTTP session
httpSession, _ := HTTPSession.Get(r, "wide-session")
httpSession.Values["username"] = args.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)
logger.Debugf("Created a HTTP session [%s] for user [%s]", httpSession.Values["id"].(string), args.Username)
}
// LogoutHandler handles request of user logout (exit).
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
@ -240,66 +165,6 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request) {
httpSession.Save(r, w)
}
// SignUpUserHandler handles request of registering user.
func SignUpUserHandler(w http.ResponseWriter, r *http.Request) {
if "GET" == r.Method {
// show the user sign up page
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale),
"locale": conf.Wide.Locale, "ver": conf.WideVersion, "dir": conf.Wide.UsersWorkspaces,
"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)
}
// 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.
@ -313,11 +178,11 @@ func FixedTimeSave() {
}()
}
// CanAccess determines whether the user specified by the given username can access the specified path.
func CanAccess(username, path string) bool {
// CanAccess determines whether the user specified by the given user id can access the specified path.
func CanAccess(userId, path string) bool {
path = filepath.FromSlash(path)
userWorkspace := conf.GetUserWorkspace(username)
userWorkspace := conf.GetUserWorkspace(userId)
workspaces := filepath.SplitList(userWorkspace)
for _, workspace := range workspaces {
@ -342,20 +207,20 @@ func SaveOnlineUsers() {
func getOnlineUsers() []*conf.User {
ret := []*conf.User{}
usernames := map[string]string{} // distinct username
uids := map[string]string{} // distinct uid
for _, s := range WideSessions {
usernames[s.Username] = s.Username
uids[s.UserId] = s.UserId
}
for _, username := range usernames {
u := conf.GetUser(username)
for _, uid := range uids {
u := conf.GetUser(uid)
if "playground" == username { // user [playground] is a reserved mock user
if "playground" == uid { // user [playground] is a reserved mock user
continue
}
if nil == u {
logger.Warnf("Not found user [%s]", username)
logger.Warnf("Not found user [%s]", uid)
continue
}
@ -366,7 +231,7 @@ func getOnlineUsers() []*conf.User {
return ret
}
// addUser add a user with the specified username, password and email.
// addUser add a user with the specified user id, username and avatar.
//
// 1. create the user's workspace
// 2. generate 'Hello, 世界' demo code in the workspace (a console version and a HTTP version)
@ -374,12 +239,12 @@ func getOnlineUsers() []*conf.User {
// 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 {
func addUser(userId, userName, userAvatar string) string {
if !conf.Wide.AllowRegister {
return notAllowRegister
}
if "playground" == username {
if "playground" == userId {
return userExists
}
@ -387,32 +252,26 @@ func addUser(username, password, email string) string {
defer addUserMutex.Unlock()
for _, user := range conf.Users {
if strings.ToLower(user.Name) == strings.ToLower(username) {
if strings.ToLower(user.Id) == strings.ToLower(userId) {
return userExists
}
if strings.ToLower(user.Email) == strings.ToLower(email) {
return emailExists
}
}
workspace := filepath.Join(conf.Wide.UsersWorkspaces, username)
newUser := conf.NewUser(username, password, email, workspace)
workspace := filepath.Join(conf.Wide.UsersWorkspaces, userId)
newUser := conf.NewUser(userId, userName, userAvatar, workspace)
conf.Users = append(conf.Users, newUser)
if !newUser.Save() {
return userCreateError
}
conf.CreateWorkspaceDir(workspace)
helloWorld(workspace)
conf.UpdateCustomizedConf(username)
conf.UpdateCustomizedConf(userId)
http.Handle("/workspace/"+username+"/",
http.StripPrefix("/workspace/"+username+"/", http.FileServer(http.Dir(newUser.WorkspacePath()))))
http.Handle("/workspace/"+userId+"/",
http.StripPrefix("/workspace/"+userId+"/", http.FileServer(http.Dir(newUser.WorkspacePath()))))
logger.Infof("Created a user [%s]", username)
logger.Infof("Created a user [%s]", userId)
return userCreated
}

View File

@ -1 +0,0 @@
This directory is used to hold user _admin_ customized style file `style.css`, it will be generated when wide startup automatically.

View File

@ -29,16 +29,16 @@ var retLogger = log.NewLogger(os.Stdout)
// Result.
type Result struct {
Succ bool `json:"succ"` // successful or not
Code string `json:"code"` // return code
Code int `json:"code"` // return code
Msg string `json:"msg"` // message
Data interface{} `json:"data"` // data object
}
// NewResult creates a result with Succ=true, Code="0", Msg="", Data=nil.
// NewResult creates a result with Succ=true, Code=0, Msg="", Data=nil.
func NewResult() *Result {
return &Result{
Succ: true,
Code: "0",
Code: 0,
Msg: "",
Data: nil,
}