wide/conf/wide.go

464 lines
11 KiB
Go
Raw Normal View History

2019-12-11 06:30:38 +03:00
// Copyright (c) 2014-present, b3log.org
//
2014-11-12 18:13:14 +03:00
// 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
//
2018-03-12 07:28:33 +03:00
// https://www.apache.org/licenses/LICENSE-2.0
//
2014-11-12 18:13:14 +03:00
// 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 includes configurations related manipulations.
2014-08-18 17:45:43 +04:00
package conf
import (
"encoding/json"
2019-05-23 13:28:11 +03:00
"html/template"
2014-08-18 17:45:43 +04:00
"os"
"os/exec"
"path/filepath"
2014-12-08 12:43:25 +03:00
"sort"
"strconv"
2014-08-18 17:45:43 +04:00
"strings"
2014-09-15 14:03:52 +04:00
"time"
2014-09-08 07:37:34 +04:00
2019-12-01 15:21:00 +03:00
"github.com/88250/gulu"
"github.com/88250/wide/event"
2014-08-18 17:45:43 +04:00
)
2014-10-21 06:47:16 +04:00
const (
2014-12-07 06:12:27 +03:00
// PathSeparator holds the OS-specific path separator.
PathSeparator = string(os.PathSeparator)
// PathListSeparator holds the OS-specific path list separator.
PathListSeparator = string(os.PathListSeparator)
2014-10-21 06:47:16 +04:00
2015-10-12 08:33:18 +03:00
// WideVersion holds the current Wide's version.
2019-05-24 06:53:30 +03:00
WideVersion = "1.6.0"
2014-12-07 06:12:27 +03:00
// CodeMirrorVer holds the current editor version.
2015-03-24 12:27:39 +03:00
CodeMirrorVer = "5.1"
2019-05-16 18:17:25 +03:00
// UserAgent represents HTTP client user agent.
2019-12-13 15:50:35 +03:00
UserAgent = "Wide/" + WideVersion + "; +https://github.com/88250/wide"
2015-02-13 04:59:51 +03:00
HelloWorld = `package main
import "fmt"
func main() {
2021-04-11 17:42:50 +03:00
fmt.Println("欢迎通过《边看边练 Go 系列》来学习 Go 语言 https://ld246.com/article/1437497122181")
2015-02-13 04:59:51 +03:00
}
`
2014-10-21 06:47:16 +04:00
)
2014-10-13 10:34:42 +04:00
2014-10-29 09:05:10 +03:00
// Configuration.
2014-08-23 19:07:24 +04:00
type conf struct {
2019-05-23 13:28:11 +03:00
Server string // server
LogLevel string // logging level: trace/debug/info/warn/error
Data string // data directory
RuntimeMode string // runtime mode (dev/prod)
HTTPSessionMaxAge int // HTTP session max age (in seciond)
StaticResourceVersion string // version of static resources
Locale string // default locale
2019-05-23 13:42:59 +03:00
Autocomplete bool // default autocomplete
2019-05-23 13:28:11 +03:00
SiteStatCode template.HTML // site statistic code
2022-08-09 04:43:46 +03:00
ReadOnly bool // read-only mode
2023-07-31 12:46:18 +03:00
OAuthLoginURL string
OAuthAccessTokenURL string
OAuthUserInfoURL string
OAuthClientID string
OAuthClientSecret string
2014-08-18 17:45:43 +04:00
}
2014-12-13 13:47:41 +03:00
// Logger.
2019-05-24 16:04:25 +03:00
var logger = gulu.Log.NewLogger(os.Stdout)
2014-12-13 13:47:41 +03:00
// Wide configurations.
var Wide *conf
// configurations of users.
var Users []*User
2019-05-16 05:52:31 +03:00
// Indicates whether Docker is available.
var Docker bool
2015-02-13 04:59:51 +03:00
2019-05-16 13:42:47 +03:00
// Docker image to run user's program
const DockerImageGo = "golang"
2019-05-16 18:37:04 +03:00
// Load loads the Wide configurations from wide.json and users' configurations from users/{userId}.json.
2022-08-09 04:56:45 +03:00
func Load(confPath, confData, confServer, confLogLevel, confReadOnly string, confSiteStatCode template.HTML) {
initWide(confPath, confData, confServer, confLogLevel, confReadOnly, confSiteStatCode)
2019-05-16 07:13:11 +03:00
initUsers()
2019-05-16 05:52:31 +03:00
cmd := exec.Command("docker", "version")
_, err := cmd.CombinedOutput()
if nil != err {
2019-12-14 11:32:28 +03:00
logger.Warnf("Not found 'docker' installed, running user's code will cause security problem")
2019-05-16 07:48:44 +03:00
} else {
Docker = true
2019-05-16 05:52:31 +03:00
}
}
2019-05-16 07:13:11 +03:00
func initUsers() {
2019-05-16 20:41:04 +03:00
f, err := os.Open(Wide.Data + PathSeparator + "users")
if nil != err {
logger.Error(err)
os.Exit(-1)
}
names, err := f.Readdirnames(-1)
if nil != err {
logger.Error(err)
os.Exit(-1)
}
f.Close()
2014-12-14 05:19:23 +03:00
for _, name := range names {
2015-02-16 06:16:56 +03:00
if strings.HasPrefix(name, ".") { // hiden files that not be created by Wide
continue
}
2016-02-26 06:12:33 +03:00
if ".json" != filepath.Ext(name) { // such as backup (*.json~) not be created by Wide
continue
}
user := &User{}
2023-07-31 12:46:18 +03:00
bytes, _ := os.ReadFile(filepath.Join(Wide.Data, "users", name))
err := json.Unmarshal(bytes, user)
if err != nil {
2015-12-30 05:06:32 +03:00
logger.Errorf("Parses [%s] error: %v, skip loading this user", name, err)
2015-12-30 05:06:32 +03:00
continue
}
2015-05-11 10:46:17 +03:00
// Compatibility upgrade (1.3.0): https://github.com/b3log/wide/issues/83
if "" == user.Keymap {
user.Keymap = "wide"
}
// Compatibility upgrade (1.5.3): https://github.com/b3log/wide/issues/308
2023-07-31 13:27:08 +03:00
//if "" == user.GoBuildArgsForLinux {
// user.GoBuildArgsForLinux = "-i"
//}
//if "" == user.GoBuildArgsForWindows {
// user.GoBuildArgsForWindows = "-i"
//}
//if "" == user.GoBuildArgsForDarwin {
// user.GoBuildArgsForDarwin = "-i"
//}
Users = append(Users, user)
}
initWorkspaceDirs()
initCustomizedConfs()
2014-12-14 05:19:23 +03:00
}
2022-08-09 04:56:45 +03:00
func initWide(confPath, confData, confServer, confLogLevel, confReadOnly string, confSiteStatCode template.HTML) {
2023-07-31 12:46:18 +03:00
bytes, err := os.ReadFile(confPath)
if nil != err {
logger.Error(err)
2014-12-14 05:19:23 +03:00
os.Exit(-1)
}
Wide = &conf{}
err = json.Unmarshal(bytes, Wide)
2014-12-14 05:19:23 +03:00
if err != nil {
logger.Error("Parses [wide.json] error: ", err)
2014-12-14 05:19:23 +03:00
os.Exit(-1)
}
2019-05-23 13:42:59 +03:00
Wide.Autocomplete = true // default to true
2015-01-13 08:54:19 +03:00
// Logging Level
2019-05-24 16:04:25 +03:00
gulu.Log.SetLevel(Wide.LogLevel)
2015-01-13 08:54:19 +03:00
if "" != confLogLevel {
Wide.LogLevel = confLogLevel
2019-05-24 16:04:25 +03:00
gulu.Log.SetLevel(confLogLevel)
2015-01-13 08:54:19 +03:00
}
2014-12-14 05:19:23 +03:00
logger.Debug("Conf: \n" + string(bytes))
2015-02-13 04:59:51 +03:00
// User Home
2019-05-24 16:04:25 +03:00
home, err := gulu.OS.Home()
2015-02-16 05:15:09 +03:00
if nil != err {
logger.Error("Can't get user's home, please report this issue to developer", err)
2015-02-16 05:06:17 +03:00
2015-02-16 05:15:09 +03:00
os.Exit(-1)
2015-02-13 04:59:51 +03:00
}
2015-02-16 05:15:09 +03:00
logger.Debugf("${user.home} [%s]", home)
2015-02-13 04:59:51 +03:00
2019-05-16 20:41:04 +03:00
// Data directory
if "" != confData {
Wide.Data = confData
2016-12-14 13:15:53 +03:00
}
2019-05-16 20:41:04 +03:00
Wide.Data = strings.Replace(Wide.Data, "${home}", home, -1)
Wide.Data = filepath.Clean(Wide.Data)
2019-05-17 06:25:44 +03:00
if err := os.MkdirAll(Wide.Data+"/playground/", 0755); nil != err {
logger.Errorf("Create data directory [%s] error", err)
2015-02-13 04:59:51 +03:00
2019-05-17 06:25:44 +03:00
os.Exit(-1)
}
if err := os.MkdirAll(Wide.Data+"/users/", 0755); nil != err {
logger.Errorf("Create data directory [%s] error", err)
os.Exit(-1)
}
if err := os.MkdirAll(Wide.Data+"/workspaces/", 0755); nil != err {
logger.Errorf("Create data directory [%s] error", err)
os.Exit(-1)
2015-02-13 04:59:51 +03:00
}
2014-12-14 05:19:23 +03:00
// Server
if "" != confServer {
Wide.Server = confServer
}
2022-08-09 04:56:45 +03:00
if "" != confReadOnly {
Wide.ReadOnly, _ = strconv.ParseBool(confReadOnly)
}
2019-05-23 13:28:11 +03:00
// SiteStatCode
if "" != confSiteStatCode {
Wide.SiteStatCode = confSiteStatCode
}
2015-10-14 05:08:06 +03:00
time := strconv.FormatInt(time.Now().UnixNano(), 10)
logger.Debugf("${time} [%s]", time)
Wide.StaticResourceVersion = strings.Replace(Wide.StaticResourceVersion, "${time}", time, 1)
2014-12-14 05:19:23 +03: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() {
2019-12-01 15:21:00 +03: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() {
2019-08-06 07:04:27 +03:00
defer gulu.Panic.Recover(nil)
2015-03-16 06:24:55 +03:00
2014-11-26 06:07:06 +03:00
cmd := exec.Command("go", "version")
buf, err := cmd.CombinedOutput()
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Error("Not found 'go' command, please make sure Go has been installed correctly")
2014-09-15 14:03:52 +04:00
2014-11-04 19:45:07 +03:00
os.Exit(-1)
}
2014-12-14 18:05:54 +03:00
logger.Trace(string(buf))
2014-10-28 19:04:46 +03:00
2014-11-26 06:07:06 +03:00
if "" == os.Getenv("GOPATH") {
2014-12-13 13:47:41 +03:00
logger.Error("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
2019-05-24 16:04:25 +03:00
gocode := gulu.Go.GetExecutableInGOBIN("gocode")
2014-12-31 11:53:34 +03:00
cmd = exec.Command(gocode)
2014-11-26 06:07:06 +03:00
_, err = cmd.Output()
2014-11-04 19:45:07 +03:00
if nil != err {
event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}
2014-09-15 14:03:52 +04:00
logger.Warnf("Not found gocode [%s], please install it with this command: go get github.com/stamblerre/gocode", gocode)
2014-11-04 19:45:07 +03:00
}
2014-10-28 19:04:46 +03:00
2019-05-24 16:04:25 +03:00
ideStub := gulu.Go.GetExecutableInGOBIN("gotools")
2014-12-07 06:12:27 +03:00
cmd = exec.Command(ideStub, "version")
2014-11-04 19:45:07 +03:00
_, err = cmd.Output()
if nil != err {
event.EventQueue <- &event.Event{Code: event.EvtCodeIDEStubNotFound}
2015-08-05 07:58:39 +03:00
logger.Warnf("Not found gotools [%s], please install it with this command: go get github.com/visualfc/gotools", ideStub)
2014-11-04 19:45:07 +03:00
}
2014-09-22 19:13:07 +04:00
}
2019-05-16 18:17:25 +03:00
// GetUserWorkspace gets workspace path with the specified user id, returns "" if not found.
func GetUserWorkspace(userId string) string {
for _, user := range Users {
2019-05-16 18:17:25 +03:00
if user.Id == userId {
2017-05-05 08:35:09 +03:00
return user.WorkspacePath()
2014-09-13 12:50:18 +04:00
}
}
return ""
2014-09-05 07:24:53 +04:00
}
2014-11-26 06:54:01 +03:00
// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports".
2019-05-16 18:17:25 +03:00
func GetGoFmt(userId string) string {
for _, user := range Users {
2019-05-16 18:17:25 +03:00
if user.Id == userId {
2014-10-26 13:08:01 +03:00
switch user.GoFormat {
case "gofmt":
return "gofmt"
case "goimports":
2019-05-24 16:04:25 +03:00
return gulu.Go.GetExecutableInGOBIN("goimports")
2014-10-26 13:08:01 +03:00
default:
2014-12-13 13:47:41 +03:00
logger.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
2014-10-26 13:08:01 +03:00
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
}
2019-05-16 18:17:25 +03:00
// 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", "", "")
2015-02-15 06:09:17 +03:00
}
for _, user := range Users {
2019-05-16 18:17:25 +03:00
if user.Id == id {
2014-09-23 18:29:53 +04:00
return user
}
}
return nil
}
2014-11-01 08:35:02 +03:00
// initCustomizedConfs initializes the user customized configurations.
func initCustomizedConfs() {
for _, user := range Users {
2019-05-16 18:17:25 +03:00
UpdateCustomizedConf(user.Id)
2014-11-01 08:35:02 +03:00
}
}
// UpdateCustomizedConf creates (if not exists) or updates user customized configuration files.
//
2019-05-17 04:27:22 +03:00
// 1. /static/users/{userId}/style.css
2019-05-16 18:17:25 +03:00
func UpdateCustomizedConf(userId string) {
2014-12-07 06:12:27 +03:00
var u *User
for _, user := range Users { // maybe it is a beauty of the trade-off of the another world between design and implementation
2019-05-16 18:17:25 +03:00
if user.Id == userId {
2014-11-01 12:33:22 +03:00
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 {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2014-11-01 08:35:02 +03:00
os.Exit(-1)
}
2019-05-17 04:27:22 +03:00
dir := filepath.Clean(Wide.Data + "/static/users/" + userId)
if err := os.MkdirAll(dir, 0755); nil != err {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2014-11-01 08:35:02 +03:00
os.Exit(-1)
}
fout, err := os.Create(dir + PathSeparator + "style.css")
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2014-11-01 08:35:02 +03:00
os.Exit(-1)
}
defer fout.Close()
2014-11-01 12:45:43 +03:00
if err := t.Execute(fout, model); nil != err {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2014-11-01 12:45:43 +03:00
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 Users {
2017-05-05 08:35:09 +03:00
paths = append(paths, filepath.SplitList(user.WorkspacePath())...)
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) {
2019-05-24 16:04:25 +03:00
if !gulu.File.IsExist(path) {
2014-10-28 05:57:16 +03:00
if err := os.MkdirAll(path, 0775); nil != err {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2014-10-28 05:57:16 +03:00
os.Exit(-1)
}
2015-02-13 08:15:58 +03:00
2015-02-27 05:53:37 +03:00
logger.Tracef("Created a dir [%s]", path)
2014-10-28 05:57:16 +03:00
}
2014-08-18 17:45:43 +04:00
}
2014-11-30 05:18:17 +03:00
// GetEditorThemes gets the names of editor themes.
func GetEditorThemes() []string {
ret := []string{}
f, _ := os.Open("static/js/overwrite/codemirror" + "/theme")
names, _ := f.Readdirnames(-1)
f.Close()
for _, name := range names {
ret = append(ret, name[:strings.LastIndex(name, ".")])
}
2014-12-08 12:43:25 +03:00
sort.Strings(ret)
2014-11-30 05:18:17 +03:00
return ret
}
// GetThemes gets the names of themes.
func GetThemes() []string {
ret := []string{}
f, _ := os.Open("static/css/themes")
names, _ := f.Readdirnames(-1)
f.Close()
for _, name := range names {
ret = append(ret, name[:strings.LastIndex(name, ".")])
}
2014-12-08 12:43:25 +03:00
sort.Strings(ret)
2014-11-30 05:18:17 +03:00
return ret
}