This commit is contained in:
parent
db0c3992d0
commit
24c75e8125
59
conf/wide.go
59
conf/wide.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/b3log/wide/event"
|
"github.com/b3log/wide/event"
|
||||||
_ "github.com/b3log/wide/i18n"
|
_ "github.com/b3log/wide/i18n"
|
||||||
|
@ -38,35 +39,42 @@ type conf struct {
|
||||||
var Wide conf
|
var Wide conf
|
||||||
var rawWide conf
|
var rawWide conf
|
||||||
|
|
||||||
// 检查 Wide 运行环境.
|
// 定时检查 Wide 运行环境.
|
||||||
// 如果是特别严重的问题(比如 $GOPATH 不存在)则退出进程。另一些不太严重的问题(比如 gocode 不存在)则放入全局通知队列。
|
// 如果是特别严重的问题(比如 $GOPATH 不存在)则退出进程。另一些不太严重的问题(比如 gocode 不存在)则放入全局通知队列。
|
||||||
func (*conf) CheckEnv() {
|
func CheckEnv() {
|
||||||
if "" == os.Getenv("GOPATH") {
|
go func() {
|
||||||
glog.Fatal("Not found $GOPATH")
|
for {
|
||||||
os.Exit(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if "" == os.Getenv("GOROOT") {
|
if "" == os.Getenv("GOPATH") {
|
||||||
glog.Fatal("Not found $GOROOT")
|
glog.Fatal("Not found $GOPATH")
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
os.Exit(-1)
|
if "" == os.Getenv("GOROOT") {
|
||||||
}
|
glog.Fatal("Not found $GOROOT")
|
||||||
|
|
||||||
gocode := Wide.GetGocode()
|
os.Exit(-1)
|
||||||
cmd := exec.Command(gocode, "close")
|
}
|
||||||
_, err := cmd.Output()
|
|
||||||
if nil != err {
|
|
||||||
event.EventQueue <- event.EvtGocodeNotFount
|
|
||||||
glog.Warningf("Not found gocode [%s]", gocode)
|
|
||||||
}
|
|
||||||
|
|
||||||
ide_stub := Wide.GetIDEStub()
|
gocode := Wide.GetGocode()
|
||||||
cmd = exec.Command(ide_stub, "version")
|
cmd := exec.Command(gocode, "close")
|
||||||
_, err = cmd.Output()
|
_, err := cmd.Output()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
event.EventQueue <- event.EvtIDEStubNotFound
|
event.EventQueue <- event.EvtGocodeNotFount
|
||||||
glog.Warningf("Not found ide_stub [%s]", ide_stub)
|
glog.Warningf("Not found gocode [%s]", gocode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ide_stub := Wide.GetIDEStub()
|
||||||
|
cmd = exec.Command(ide_stub, "version")
|
||||||
|
_, err = cmd.Output()
|
||||||
|
if nil != err {
|
||||||
|
event.EventQueue <- event.EvtIDEStubNotFound
|
||||||
|
glog.Warningf("Not found ide_stub [%s]", ide_stub)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 username 指定的用户的工作空间路径.
|
// 获取 username 指定的用户的工作空间路径.
|
||||||
|
@ -140,9 +148,6 @@ func Save() bool {
|
||||||
|
|
||||||
// 加载 Wide 配置.
|
// 加载 Wide 配置.
|
||||||
func Load() {
|
func Load() {
|
||||||
// 检查 Wide 运行环境
|
|
||||||
Wide.CheckEnv()
|
|
||||||
|
|
||||||
bytes, _ := ioutil.ReadFile("conf/wide.json")
|
bytes, _ := ioutil.ReadFile("conf/wide.json")
|
||||||
|
|
||||||
err := json.Unmarshal(bytes, &Wide)
|
err := json.Unmarshal(bytes, &Wide)
|
||||||
|
|
|
@ -1,38 +1,55 @@
|
||||||
// 事件处理.
|
// 事件处理.
|
||||||
package event
|
package event
|
||||||
|
|
||||||
import (
|
import "github.com/golang/glog"
|
||||||
"github.com/golang/glog"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EvtGOPATHNotFound = iota // 事件:找不到环境变量 $GOPATH
|
EvtGOPATHNotFound = iota // 事件:找不到环境变量 $GOPATH
|
||||||
EvtGOROOTNotFound // 事件:找不到环境变量 $GOROOT
|
EvtGOROOTNotFound // 事件:找不到环境变量 $GOROOT
|
||||||
EvtGocodeNotFount // 事件:找不到 gocode
|
EvtGocodeNotFount // 事件:找不到 gocode
|
||||||
EvtIDEStubNotFound // 事件:找不到 IDE stub
|
EvtIDEStubNotFound // 事件:找不到 IDE stub
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const MaxQueueLength = 10
|
||||||
|
|
||||||
// 全局事件队列.
|
// 全局事件队列.
|
||||||
// 入队的事件将分发到每个用户的通知队列.
|
// 入队的事件将分发到每个用户的通知队列.
|
||||||
var EventQueue = make(chan int, 10)
|
var EventQueue = make(chan int, MaxQueueLength)
|
||||||
|
|
||||||
// 用户事件队列.
|
// 用户事件队列.
|
||||||
// 入队的事件将翻译为通知,并通过通知通道推送到前端.
|
// 入队的事件将翻译为通知,并通过通知通道推送到前端.
|
||||||
var UserEventQueue map[string]chan int
|
var UserEventQueues = map[string]chan int{}
|
||||||
|
|
||||||
// 加载事件处理.
|
// 加载事件处理.
|
||||||
func Load() {
|
func Load() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for event := range EventQueue {
|
||||||
// 获取事件
|
|
||||||
event := <-EventQueue
|
|
||||||
|
|
||||||
glog.V(5).Info("收到全局事件 [%d]", event)
|
glog.V(5).Info("收到全局事件 [%d]", event)
|
||||||
|
|
||||||
// 将事件分发到每个用户的事件队列里
|
// 将事件分发到每个用户的事件队列里
|
||||||
for _, userQueue := range UserEventQueue {
|
for _, userQueue := range UserEventQueues {
|
||||||
userQueue <- event
|
userQueue <- event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加一个用户事件队列.
|
||||||
|
func InitUserQueue(sid string) {
|
||||||
|
// FIXME: 会话过期后需要销毁对应的用户事件队列
|
||||||
|
|
||||||
|
q := UserEventQueues[sid]
|
||||||
|
if nil != q {
|
||||||
|
close(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
q = make(chan int, MaxQueueLength)
|
||||||
|
UserEventQueues[sid] = q
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for event := range q {
|
||||||
|
glog.Infof("Session [%s] received a event [%d]", sid, event)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
7
main.go
7
main.go
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/b3log/wide/event"
|
"github.com/b3log/wide/event"
|
||||||
"github.com/b3log/wide/file"
|
"github.com/b3log/wide/file"
|
||||||
"github.com/b3log/wide/i18n"
|
"github.com/b3log/wide/i18n"
|
||||||
|
"github.com/b3log/wide/notification"
|
||||||
"github.com/b3log/wide/output"
|
"github.com/b3log/wide/output"
|
||||||
"github.com/b3log/wide/shell"
|
"github.com/b3log/wide/shell"
|
||||||
"github.com/b3log/wide/user"
|
"github.com/b3log/wide/user"
|
||||||
|
@ -31,6 +32,9 @@ func init() {
|
||||||
|
|
||||||
// 加载配置
|
// 加载配置
|
||||||
conf.Load()
|
conf.Load()
|
||||||
|
|
||||||
|
// 定时检查 Wide 运行环境
|
||||||
|
conf.CheckEnv()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wide 首页.
|
// Wide 首页.
|
||||||
|
@ -108,6 +112,9 @@ func main() {
|
||||||
http.HandleFunc("/shell/ws", shell.WSHandler)
|
http.HandleFunc("/shell/ws", shell.WSHandler)
|
||||||
http.HandleFunc("/shell", shell.IndexHandler)
|
http.HandleFunc("/shell", shell.IndexHandler)
|
||||||
|
|
||||||
|
// 通知
|
||||||
|
http.HandleFunc("/notification/ws", notification.WSHandler)
|
||||||
|
|
||||||
// 用户
|
// 用户
|
||||||
http.HandleFunc("/user/new", user.AddUser)
|
http.HandleFunc("/user/new", user.AddUser)
|
||||||
http.HandleFunc("/user/repos/init", user.InitGitRepos)
|
http.HandleFunc("/user/repos/init", user.InitGitRepos)
|
||||||
|
|
|
@ -6,9 +6,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"time"
|
||||||
|
|
||||||
"github.com/b3log/wide/conf"
|
"github.com/b3log/wide/conf"
|
||||||
|
"github.com/b3log/wide/event"
|
||||||
"github.com/b3log/wide/user"
|
"github.com/b3log/wide/user"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
@ -22,25 +23,42 @@ type Notification struct {
|
||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 一个用户会话的 WebSocket 通道结构.
|
||||||
|
type WSChannel struct {
|
||||||
|
Conn *websocket.Conn // WebSocket 连接
|
||||||
|
Time time.Time // 该通道最近一次使用时间
|
||||||
|
}
|
||||||
|
|
||||||
// 通知通道.
|
// 通知通道.
|
||||||
var notificationWS = map[string]*websocket.Conn{}
|
// <username, {<sid1, WSChannel1>, <sid2, WSChannel2>}>
|
||||||
|
var notificationWSs = map[string]map[string]WSChannel{}
|
||||||
|
|
||||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := user.Session.Get(r, "wide-session")
|
session, _ := user.Session.Get(r, "wide-session")
|
||||||
username := session.Values["username"].(string)
|
username := session.Values["username"].(string)
|
||||||
sid := session.Values["id"].(string)
|
sid := session.Values["id"].(string)
|
||||||
|
|
||||||
notificationWS[sid], _ = websocket.Upgrade(w, r, nil, 1024, 1024)
|
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||||
|
wsChan := WSChannel{Conn: conn, Time: time.Now()}
|
||||||
|
|
||||||
|
wsChans := notificationWSs[username]
|
||||||
|
if nil == wsChans {
|
||||||
|
wsChans = map[string]WSChannel{}
|
||||||
|
}
|
||||||
|
|
||||||
|
wsChans[sid] = wsChan
|
||||||
|
|
||||||
ret := map[string]interface{}{"output": "Notification initialized", "cmd": "init-notification"}
|
ret := map[string]interface{}{"output": "Notification initialized", "cmd": "init-notification"}
|
||||||
notificationWS[sid].WriteJSON(&ret)
|
wsChan.Conn.WriteJSON(&ret)
|
||||||
|
|
||||||
glog.Infof("Open a new [Notification] with session [%s], %d", sid, len(notificationWS))
|
glog.Infof("Open a new [Notification] with session [%s], %d", sid, len(wsChans))
|
||||||
|
|
||||||
|
event.InitUserQueue(sid)
|
||||||
|
|
||||||
input := map[string]interface{}{}
|
input := map[string]interface{}{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := notificationWS[sid].ReadJSON(&input); err != nil {
|
if err := wsChan.Conn.ReadJSON(&input); err != nil {
|
||||||
if err.Error() == "EOF" {
|
if err.Error() == "EOF" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -49,35 +67,16 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Error("Shell WS ERROR: " + err.Error())
|
glog.Error("Notification WS ERROR: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
inputCmd := input["cmd"].(string)
|
|
||||||
|
|
||||||
cmds := strings.Split(inputCmd, "|")
|
|
||||||
commands := []*exec.Cmd{}
|
|
||||||
for _, cmdWithArgs := range cmds {
|
|
||||||
cmdWithArgs = strings.TrimSpace(cmdWithArgs)
|
|
||||||
cmdWithArgs := strings.Split(cmdWithArgs, " ")
|
|
||||||
args := []string{}
|
|
||||||
if len(cmdWithArgs) > 1 {
|
|
||||||
args = cmdWithArgs[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command(cmdWithArgs[0], args...)
|
|
||||||
commands = append(commands, cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
output := ""
|
output := ""
|
||||||
if !strings.Contains(inputCmd, "clear") {
|
|
||||||
output = pipeCommands(username, commands...)
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = map[string]interface{}{"output": output, "cmd": "shell-output"}
|
ret = map[string]interface{}{"output": output, "cmd": "notification-output"}
|
||||||
|
|
||||||
if err := notificationWS[sid].WriteJSON(&ret); err != nil {
|
if err := wsChan.Conn.WriteJSON(&ret); err != nil {
|
||||||
glog.Error("Shell WS ERROR: " + err.Error())
|
glog.Error("Notification WS ERROR: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
var notificationWS = new WebSocket(config.channel.shell + '/notification/ws');
|
||||||
|
notificationWS.onopen = function() {
|
||||||
|
console.log('[notification onopen] connected');
|
||||||
|
};
|
||||||
|
notificationWS.onmessage = function(e) {
|
||||||
|
console.log('[notification onmessage]' + e.data);
|
||||||
|
var data = JSON.parse(e.data);
|
||||||
|
if ('init-notification' !== data.cmd) {
|
||||||
|
$('#notification').val(data.output);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
notificationWS.onclose = function(e) {
|
||||||
|
console.log('[notification onclose] disconnected (' + e.code + ')');
|
||||||
|
delete notificationWS;
|
||||||
|
};
|
||||||
|
notificationWS.onerror = function(e) {
|
||||||
|
console.log('[notification onerror] ' + e);
|
||||||
|
};
|
||||||
|
|
||||||
|
var notification = {
|
||||||
|
init: function() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
notification.init();
|
||||||
|
});
|
|
@ -143,6 +143,10 @@
|
||||||
<textarea id="output"></textarea>
|
<textarea id="output"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<textarea id="notification"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -196,6 +200,7 @@
|
||||||
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/lint/go-lint.js?{{.Wide.StaticResourceVersion}}"></script>
|
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/lint/go-lint.js?{{.Wide.StaticResourceVersion}}"></script>
|
||||||
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/tabs.js?{{.Wide.StaticResourceVersion}}"></script>
|
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/tabs.js?{{.Wide.StaticResourceVersion}}"></script>
|
||||||
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/editor.js?{{.Wide.StaticResourceVersion}}"></script>
|
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/editor.js?{{.Wide.StaticResourceVersion}}"></script>
|
||||||
|
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/notification.js?{{.Wide.StaticResourceVersion}}"></script>
|
||||||
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/tree.js?{{.Wide.StaticResourceVersion}}"></script>
|
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/tree.js?{{.Wide.StaticResourceVersion}}"></script>
|
||||||
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/wide.js?{{.Wide.StaticResourceVersion}}"></script>
|
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/wide.js?{{.Wide.StaticResourceVersion}}"></script>
|
||||||
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/menu.js?{{.Wide.StaticResourceVersion}}"></script>
|
<script type="text/javascript" src="{{.Wide.StaticServer}}/static/js/menu.js?{{.Wide.StaticResourceVersion}}"></script>
|
||||||
|
|
Loading…
Reference in New Issue