wide/conf/wide.go

395 lines
9.9 KiB
Go
Raw Normal View History

2014-10-29 09:05:10 +03:00
// Configurations manipulations, all configurations (including user configurations) are stored in wide.json.
2014-08-18 17:45:43 +04:00
package conf
import (
"encoding/json"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
2014-09-15 09:03:44 +04:00
"runtime"
2014-08-18 17:45:43 +04:00
"strings"
2014-11-01 08:35:02 +03:00
"text/template"
2014-09-15 14:03:52 +04:00
"time"
2014-09-08 07:37:34 +04:00
2014-09-15 10:24:40 +04:00
"github.com/b3log/wide/event"
2014-09-08 07:37:34 +04:00
"github.com/b3log/wide/util"
"github.com/golang/glog"
2014-08-18 17:45:43 +04:00
)
2014-10-21 06:47:16 +04:00
const (
2014-10-29 09:05:10 +03:00
PathSeparator = string(os.PathSeparator) // OS-specific path separator
PathListSeparator = string(os.PathListSeparator) // OS-specific path list separator
)
2014-10-21 06:47:16 +04:00
const (
WideVersion = "1.0.1" // wide version
CodeMirrorVer = "4.7" // editor version
2014-10-21 06:47:16 +04:00
)
2014-10-13 10:34:42 +04:00
2014-10-29 09:05:10 +03:00
// The latest session content.
2014-09-22 19:13:07 +04:00
type LatestSessionContent struct {
2014-10-29 09:05:10 +03:00
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
2014-09-22 19:13:07 +04:00
}
2014-11-01 12:33:22 +03:00
// User configuration.
2014-08-31 14:50:38 +04:00
type User struct {
2014-09-22 19:13:07 +04:00
Name string
Password string
2014-10-29 09:05:10 +03:00
Workspace string // the GOPATH of this user
2014-10-23 17:43:35 +04:00
Locale string
2014-10-26 13:08:01 +03:00
GoFormat string
2014-11-02 05:39:33 +03:00
FontFamily string
FontSize string
Editor *Editor
2014-09-22 19:13:07 +04:00
LatestSessionContent *LatestSessionContent
2014-08-31 09:31:26 +04:00
}
2014-11-01 12:33:22 +03:00
// Editor configuration of a user.
2014-11-02 05:39:33 +03:00
type Editor struct {
FontFamily string
FontSize string
2014-11-01 12:33:22 +03:00
}
2014-10-29 09:05:10 +03:00
// Configuration.
2014-08-23 19:07:24 +04:00
type conf struct {
2014-11-06 16:28:25 +03:00
IP string // server ip, ${ip}
2014-10-29 09:05:10 +03:00
Server string // server host and port ({IP}:7070)
StaticServer string // static resources server scheme, host and port (http://{IP}:7070)
EditorChannel string // editor channel (ws://{IP}:7070)
OutputChannel string // output channel (ws://{IP}:7070)
ShellChannel string // shell channel(ws://{IP}:7070)
SessionChannel string // wide session channel (ws://{IP}:7070)
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)
2014-11-06 16:28:25 +03:00
WD string // current working direcitory, ${pwd}
2014-10-29 09:05:10 +03:00
Locale string // default locale
Users []*User // configurations of users
2014-08-18 17:45:43 +04:00
}
2014-10-29 09:05:10 +03:00
// Configuration variable.
2014-08-23 19:07:24 +04:00
var Wide conf
2014-09-22 19:13:07 +04:00
2014-10-29 09:05:10 +03:00
// A raw copy of configuration variable.
2014-09-25 09:37:59 +04:00
//
2014-10-29 09:05:10 +03:00
// Save function will use this variable to persist.
2014-08-31 14:50:38 +04:00
var rawWide conf
2014-08-18 17:45:43 +04:00
2014-10-29 09:05:10 +03:00
// FixedTimeCheckEnv checks Wide runtime enviorment periodically (7 minutes).
2014-09-25 09:37:59 +04:00
//
2014-10-29 09:05:10 +03:00
// Exits process if found fatal issues (such as not found $GOPATH),
// Notifies user by notification queue if found warning issues (such as not found gocode).
2014-09-22 19:13:07 +04:00
func FixedTimeCheckEnv() {
2014-11-04 19:45:07 +03:00
checkEnv() // check immediately
2014-09-15 14:03:52 +04:00
go func() {
2014-10-10 10:24:47 +04:00
for _ = range time.Tick(time.Minute * 7) {
2014-11-04 19:45:07 +03:00
checkEnv()
}
}()
}
2014-10-28 19:04:46 +03:00
2014-11-04 19:45:07 +03:00
func checkEnv() {
if "" == os.Getenv("GOPATH") {
glog.Fatal("Not found $GOPATH, please configure it before running Wide")
2014-09-15 14:03:52 +04:00
2014-11-04 19:45:07 +03:00
os.Exit(-1)
}
2014-10-28 19:04:46 +03:00
2014-11-04 19:45:07 +03:00
if "" == os.Getenv("GOROOT") {
glog.Fatal("Not found $GOROOT, please configure it before running Wide")
2014-09-15 14:03:52 +04:00
2014-11-04 19:45:07 +03:00
os.Exit(-1)
}
2014-10-28 19:04:46 +03:00
2014-11-04 19:45:07 +03:00
gocode := Wide.GetExecutableInGOBIN("gocode")
cmd := exec.Command(gocode, "close")
_, err := cmd.Output()
if nil != err {
event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}
2014-09-15 14:03:52 +04:00
2014-11-04 19:45:07 +03:00
glog.Warningf("Not found gocode [%s]", gocode)
}
2014-10-28 19:04:46 +03:00
2014-11-04 19:45:07 +03:00
ide_stub := Wide.GetExecutableInGOBIN("ide_stub")
cmd = exec.Command(ide_stub, "version")
_, err = cmd.Output()
if nil != err {
event.EventQueue <- &event.Event{Code: event.EvtCodeIDEStubNotFound}
glog.Warningf("Not found ide_stub [%s]", ide_stub)
}
2014-09-22 19:13:07 +04:00
}
2014-10-29 09:05:10 +03:00
// FixedTimeSave saves configurations (wide.json) periodically (1 minute).
2014-09-25 09:37:59 +04:00
//
2014-10-29 09:05:10 +03:00
// Main goal of this function is to save user session content, for restoring session content while user open Wide next time.
2014-09-22 19:13:07 +04:00
func FixedTimeSave() {
go func() {
2014-10-10 10:24:47 +04:00
for _ = range time.Tick(time.Minute) {
2014-09-22 19:13:07 +04:00
Save()
2014-09-15 14:03:52 +04:00
}
}()
2014-09-15 10:24:40 +04:00
}
2014-10-29 09:05:10 +03:00
// GetUserWorkspace gets workspace path with the specified username, returns "" if not found.
2014-10-21 06:25:45 +04:00
func (c *conf) GetUserWorkspace(username string) string {
for _, user := range c.Users {
2014-09-13 12:50:18 +04:00
if user.Name == username {
return user.GetWorkspace()
2014-09-13 12:50:18 +04:00
}
}
return ""
2014-09-05 07:24:53 +04:00
}
2014-10-29 09:05:10 +03:00
// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found.
2014-10-26 13:08:01 +03:00
func (c *conf) GetGoFmt(username string) string {
for _, user := range c.Users {
if user.Name == username {
switch user.GoFormat {
case "gofmt":
return "gofmt"
case "goimports":
return c.GetExecutableInGOBIN("goimports")
default:
glog.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
return "gofmt"
}
}
}
2014-09-30 12:27:01 +04:00
2014-10-26 13:08:01 +03:00
return "gofmt"
2014-09-30 12:27:01 +04:00
}
2014-10-29 09:05:10 +03:00
// GetWorkspace gets workspace path of the user.
2014-10-28 05:57:16 +03:00
//
2014-10-29 09:05:10 +03:00
// Compared to the use of Wide.Workspace, this function will be processed as follows:
2014-11-06 16:28:25 +03:00
// 1. Replace {WD} variable with the actual directory path
// 2. Replace ${GOPATH} with enviorment variable GOPATH
// 3. Replace "/" with "\\" (Windows)
2014-10-28 05:57:16 +03:00
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)
2014-10-28 05:57:16 +03:00
}
2014-10-29 09:05:10 +03:00
// GetUser gets configuration of the user specified by the given username, returns nil if not found.
2014-09-23 18:29:53 +04:00
func (*conf) GetUser(username string) *User {
for _, user := range Wide.Users {
if user.Name == username {
return user
}
}
return nil
}
2014-10-29 09:05:10 +03:00
// GetExecutableInGOBIN gets executable file under GOBIN path.
2014-10-26 10:41:34 +03:00
//
2014-10-29 09:05:10 +03:00
// The specified executable should not with extension, this function will append .exe if on Windows.
2014-10-26 10:41:34 +03:00
func (*conf) GetExecutableInGOBIN(executable string) string {
if util.OS.IsWindows() {
executable += ".exe"
}
2014-09-15 09:03:44 +04:00
2014-10-28 08:22:39 +03:00
gopaths := filepath.SplitList(os.Getenv("GOPATH"))
2014-09-17 10:35:48 +04:00
2014-10-26 10:41:34 +03:00
for _, gopath := range gopaths {
// $GOPATH/bin/$GOOS_$GOARCH/executable
ret := gopath + PathSeparator + "bin" + PathSeparator +
os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") + PathSeparator + executable
if isExist(ret) {
return ret
}
2014-09-17 10:35:48 +04:00
2014-10-26 10:41:34 +03:00
// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
ret = gopath + PathSeparator + "bin" + PathSeparator +
runtime.GOOS + "_" + runtime.GOARCH + PathSeparator + executable
if isExist(ret) {
return ret
}
2014-09-15 10:48:03 +04:00
2014-10-26 10:41:34 +03:00
// $GOPATH/bin/executable
ret = gopath + PathSeparator + "bin" + PathSeparator + executable
if isExist(ret) {
return ret
}
2014-09-15 09:03:44 +04:00
}
2014-09-17 10:35:48 +04:00
2014-10-26 10:41:34 +03:00
// $GOBIN/executable
return os.Getenv("GOBIN") + PathSeparator + executable
2014-09-15 09:03:44 +04:00
}
2014-10-29 09:05:10 +03:00
// Save saves Wide configurations.
2014-08-31 14:50:38 +04:00
func Save() bool {
2014-10-29 09:05:10 +03:00
// just the Users field are volatile
2014-08-31 14:50:38 +04:00
rawWide.Users = Wide.Users
2014-10-29 09:05:10 +03:00
// format
2014-08-31 14:50:38 +04:00
bytes, err := json.MarshalIndent(rawWide, "", " ")
if nil != err {
glog.Error(err)
return false
}
if err = ioutil.WriteFile("conf/wide.json", bytes, 0644); nil != err {
glog.Error(err)
return false
}
return true
}
2014-10-29 09:05:10 +03:00
// Load loads the configurations from wide.json.
2014-08-31 14:50:38 +04:00
func Load() {
2014-08-18 17:45:43 +04:00
bytes, _ := ioutil.ReadFile("conf/wide.json")
err := json.Unmarshal(bytes, &Wide)
if err != nil {
glog.Error(err)
os.Exit(-1)
}
2014-10-29 09:05:10 +03:00
// keep the raw content
2014-08-31 14:50:38 +04:00
json.Unmarshal(bytes, &rawWide)
2014-08-18 17:45:43 +04:00
ip, err := util.Net.LocalIP()
if err != nil {
glog.Error(err)
os.Exit(-1)
}
2014-11-06 16:28:25 +03:00
glog.V(5).Infof("${ip} [%s]", ip)
2014-09-19 20:56:32 +04:00
2014-11-06 16:28:25 +03:00
Wide.WD = util.OS.Pwd()
glog.V(5).Infof("${pwd} [%s]", Wide.WD)
2014-08-18 17:45:43 +04:00
2014-11-06 16:28:25 +03:00
Wide.IP = strings.Replace(Wide.IP, "${ip}", ip, 1)
2014-08-28 06:51:03 +04:00
2014-11-06 16:28:25 +03:00
Wide.Server = strings.Replace(Wide.Server, "{IP}", Wide.IP, 1)
Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", Wide.IP, 1)
Wide.EditorChannel = strings.Replace(Wide.EditorChannel, "{IP}", Wide.IP, 1)
Wide.OutputChannel = strings.Replace(Wide.OutputChannel, "{IP}", Wide.IP, 1)
Wide.ShellChannel = strings.Replace(Wide.ShellChannel, "{IP}", Wide.IP, 1)
Wide.SessionChannel = strings.Replace(Wide.SessionChannel, "{IP}", Wide.IP, 1)
glog.V(5).Info("Conf: \n" + string(bytes))
2014-10-28 05:57:16 +03:00
initWorkspaceDirs()
2014-11-01 08:35:02 +03:00
initCustomizedConfs()
}
// initCustomizedConfs initializes the user customized configurations.
func initCustomizedConfs() {
for _, user := range Wide.Users {
UpdateCustomizedConf(user.Name)
}
}
// UpdateCustomizedConf creates (if not exists) or updates user customized configuration files.
//
// 1. /static/user/{username}/style.css
func UpdateCustomizedConf(username string) {
2014-11-01 12:33:22 +03:00
var u *User = nil
2014-11-01 12:54:36 +03:00
for _, user := range Wide.Users { // maybe it is a beauty of the trade-off of the another world between design and implementation
2014-11-01 12:33:22 +03:00
if user.Name == username {
u = user
}
}
if nil == u {
return
}
2014-11-02 05:39:33 +03:00
model := map[string]interface{}{"user": u}
2014-11-01 08:35:02 +03:00
t, err := template.ParseFiles("static/user/style.css.tmpl")
if nil != err {
glog.Error(err)
os.Exit(-1)
}
wd := util.OS.Pwd()
2014-11-01 12:33:22 +03:00
dir := filepath.Clean(wd + "/static/user/" + u.Name)
if err := os.MkdirAll(dir, 0755); nil != err {
2014-11-01 08:35:02 +03:00
glog.Error(err)
os.Exit(-1)
}
fout, err := os.Create(dir + PathSeparator + "style.css")
if nil != err {
glog.Error(err)
os.Exit(-1)
}
defer fout.Close()
2014-11-01 12:45:43 +03:00
if err := t.Execute(fout, model); nil != err {
glog.Error(err)
os.Exit(-1)
}
2014-10-28 05:57:16 +03:00
}
2014-11-12 09:49:14 +03:00
// initWorkspaceDirs initializes the directories of users' workspaces.
2014-10-28 05:57:16 +03:00
//
2014-10-29 09:05:10 +03:00
// Creates directories if not found on path of workspace.
2014-10-28 05:57:16 +03:00
func initWorkspaceDirs() {
2014-11-12 09:49:14 +03:00
paths := []string{}
2014-10-28 05:57:16 +03:00
for _, user := range Wide.Users {
2014-10-28 08:22:39 +03:00
paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
2014-10-28 05:57:16 +03:00
}
for _, path := range paths {
CreateWorkspaceDir(path)
2014-10-28 05:57:16 +03:00
}
}
2014-11-01 08:35:02 +03:00
// CreateWorkspaceDir creates (if not exists) directories on the path.
2014-10-28 05:57:16 +03:00
//
2014-10-29 09:05:10 +03:00
// 1. root directory:{path}
// 2. src directory: {path}/src
// 3. package directory: {path}/pkg
// 4. binary directory: {path}/bin
func CreateWorkspaceDir(path string) {
2014-10-28 05:57:16 +03:00
createDir(path)
createDir(path + PathSeparator + "src")
createDir(path + PathSeparator + "pkg")
createDir(path + PathSeparator + "bin")
}
2014-10-29 09:05:10 +03:00
// createDir creates a directory on the path if it not exists.
2014-10-28 05:57:16 +03:00
func createDir(path string) {
if !isExist(path) {
if err := os.MkdirAll(path, 0775); nil != err {
glog.Error(err)
os.Exit(-1)
}
glog.V(7).Infof("Created a directory [%s]", path)
}
2014-08-18 17:45:43 +04:00
}
2014-09-15 10:48:03 +04:00
2014-10-29 09:05:10 +03:00
// isExist determines whether the file spcified by the given filename is exists.
2014-09-15 10:48:03 +04:00
func isExist(filename string) bool {
_, err := os.Stat(filename)
return err == nil || os.IsExist(err)
}