wide/conf/wide.go

335 lines
8.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Wide 配置相关,所有配置(包括用户配置)都是保存在 wide.json 中.
package conf
import (
"encoding/json"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/b3log/wide/event"
_ "github.com/b3log/wide/i18n"
"github.com/b3log/wide/util"
"github.com/golang/glog"
)
const (
PathSeparator = string(os.PathSeparator) // 系统文件路径分隔符
PathListSeparator = string(os.PathListSeparator) // 系统路径列表分隔符
)
// 最后一次会话内容结构.
type LatestSessionContent struct {
FileTree []string // 文件树展开的路径集
Files []string // 编辑器打开的文件路径集
CurrentFile string // 当前编辑器文件路径
}
// 用户结构.
type User struct {
Name string
Password string
Workspace string // 该用户的工作空间 GOPATH 路径
Locale string
GoFormat string
LatestSessionContent *LatestSessionContent
}
// 配置结构.
type conf struct {
Server string // 服务地址({IP}:7070
StaticServer string // 静态资源服务地址http://{IP}:7070
EditorChannel string // 编辑器通道地址ws://{IP}:7070
OutputChannel string // 输出窗口通道地址ws://{IP}:7070
ShellChannel string // Shell 通道地址ws://{IP}:7070
SessionChannel string // Wide 会话通道地址ws://{IP}:7070
HTTPSessionMaxAge int // HTTP 会话失效时间(秒)
StaticResourceVersion string // 静态资源版本
MaxProcs int // 并发执行数
RuntimeMode string // 运行模式
Pwd string // 工作目录
Workspace string // 主工作空间 GOPATH 路径
Locale string // 默认的区域
Users []*User // 用户集
}
// 配置.
var Wide conf
// 维护非变化部分的配置.
//
// 只有 Users 是会运行时变化的,保存回写文件时要使用这个变量.
var rawWide conf
// 定时检查 Wide 运行环境.
//
// 如果是特别严重的问题(比如 $GOPATH 不存在)则退出进程,另一些不太严重的问题(比如 gocode 不存在)则放入全局通知队列.
func FixedTimeCheckEnv() {
go func() {
// 7 分钟进行一次检查环境
for _ = range time.Tick(time.Minute * 7) {
if "" == os.Getenv("GOPATH") {
glog.Fatal("Not found $GOPATH")
os.Exit(-1)
}
if "" == os.Getenv("GOROOT") {
glog.Fatal("Not found $GOROOT")
os.Exit(-1)
}
gocode := Wide.GetExecutableInGOBIN("gocode")
cmd := exec.Command(gocode, "close")
_, err := cmd.Output()
if nil != err {
event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}
glog.Warningf("Not found gocode [%s]", gocode)
}
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)
}
}
}()
}
// 定时1 分钟)保存配置.
//
// 主要目的是保存用户会话内容,以备下一次用户打开 Wide 时进行会话还原.
func FixedTimeSave() {
go func() {
// 1 分钟进行一次配置保存
for _ = range time.Tick(time.Minute) {
Save()
}
}()
}
// 获取 username 指定的用户的工作空间路径,查找不到时返回空字符串.
func (c *conf) GetUserWorkspace(username string) string {
for _, user := range c.Users {
if user.Name == username {
ret := strings.Replace(user.Workspace, "{pwd}", c.Pwd, 1)
return filepath.FromSlash(ret)
}
}
return ""
}
// 获取主工作空间路径.
//
// 相比起使用 Wide.Workspace该函数会做如下处理
// 1. 替换里面的 {pwd} 变量为实际的目录路径
// 2. 把 / 替换为 \\ Windows
func (c *conf) GetWorkspace() string {
return filepath.FromSlash(strings.Replace(c.Workspace, "{pwd}", c.Pwd, 1))
}
// 获取 username 指定的用户的 Go 源码格式化工具路径,查找不到时返回 "gofmt".
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"
}
}
}
return "gofmt"
}
// 获取用户的工作空间路径.
//
// 相比起使用 User.Workspace该函数会做如下处理
// 1. 替换里面的 {pwd} 变量为实际的目录路径
// 2. 把 / 替换为 \\ Windows
func (u *User) GetWorkspace() string {
return filepath.FromSlash(strings.Replace(u.Workspace, "{pwd}", Wide.Pwd, 1))
}
// 获取 username 指定的用户配置.
func (*conf) GetUser(username string) *User {
for _, user := range Wide.Users {
if user.Name == username {
return user
}
}
return nil
}
// 获取 GOBIN 中 executable 指定的文件路径.
//
// 函数内部会判断操作系统,如果是 Windows 则在 executable 实参后加入 .exe 后缀.
func (*conf) GetExecutableInGOBIN(executable string) string {
if util.OS.IsWindows() {
executable += ".exe"
}
gopaths := filepath.SplitList(os.Getenv("GOPATH"))
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
}
// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
ret = gopath + PathSeparator + "bin" + PathSeparator +
runtime.GOOS + "_" + runtime.GOARCH + PathSeparator + executable
if isExist(ret) {
return ret
}
// $GOPATH/bin/executable
ret = gopath + PathSeparator + "bin" + PathSeparator + executable
if isExist(ret) {
return ret
}
}
// $GOBIN/executable
return os.Getenv("GOBIN") + PathSeparator + executable
}
// 保存 Wide 配置.
func Save() bool {
// 只有 Users 是会运行时变化的,其他属性只能手工维护 wide.json 配置文件
rawWide.Users = Wide.Users
// 原始配置文件内容
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
}
// 加载 Wide 配置.
func Load() {
bytes, _ := ioutil.ReadFile("conf/wide.json")
err := json.Unmarshal(bytes, &Wide)
if err != nil {
glog.Error(err)
os.Exit(-1)
}
// 保存未经变量替换处理的原始配置文件,用于写回时
json.Unmarshal(bytes, &rawWide)
ip, err := util.Net.LocalIP()
if err != nil {
glog.Error(err)
os.Exit(-1)
}
glog.V(3).Infof("IP [%s]", ip)
Wide.Server = strings.Replace(Wide.Server, "{IP}", ip, 1)
Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", ip, 1)
Wide.EditorChannel = strings.Replace(Wide.EditorChannel, "{IP}", ip, 1)
Wide.OutputChannel = strings.Replace(Wide.OutputChannel, "{IP}", ip, 1)
Wide.ShellChannel = strings.Replace(Wide.ShellChannel, "{IP}", ip, 1)
Wide.SessionChannel = strings.Replace(Wide.SessionChannel, "{IP}", ip, 1)
Wide.Pwd = util.OS.Pwd()
glog.V(3).Infof("pwd [%s]", Wide.Pwd)
glog.V(3).Info("Conf: \n" + string(bytes))
initWorkspaceDirs()
}
// 初始化主工作空间、各个用户的工作空间目录.
//
// 如果不存在 Workspace 配置所指定的目录路径则创建该目录.
func initWorkspaceDirs() {
paths := filepath.SplitList(Wide.GetWorkspace())
for _, user := range Wide.Users {
paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
}
for _, path := range paths {
createWorkspaceDir(path)
}
}
// 在 path 指定的路径下创建工作空间目录.
//
// 1. 根目录:{path}
// 2. 源码目录:{path}/src
// 3. 包目录:{path}/pkg
// 4. 可执行文件目录:{path}/bin
func createWorkspaceDir(path string) {
// {path}, workspace root
createDir(path)
// {path}/src
createDir(path + PathSeparator + "src")
// {path}/pkg
createDir(path + PathSeparator + "pkg")
// {path}/bin
createDir(path + PathSeparator + "bin")
}
// 在 path 指定的路径下不存在目录或文件则创建目录.
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)
}
}
// 检查文件或目录是否存在.
//
// 如果由 filename 指定的文件或目录存在则返回 true否则返回 false.
func isExist(filename string) bool {
_, err := os.Stat(filename)
return err == nil || os.IsExist(err)
}