Fix #119
This commit is contained in:
parent
7ec8a77005
commit
cfd5367919
|
@ -1,4 +1,4 @@
|
||||||
// 编辑器操作.
|
// Editor manipulations.
|
||||||
package editor
|
package editor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -11,6 +11,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/b3log/wide/conf"
|
"github.com/b3log/wide/conf"
|
||||||
"github.com/b3log/wide/file"
|
"github.com/b3log/wide/file"
|
||||||
|
@ -20,23 +21,24 @@ import (
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
var editorWS = map[string]*websocket.Conn{}
|
// WSHandler handles request of creating editor channel.
|
||||||
|
|
||||||
// 建立编辑器通道.
|
|
||||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
sid := session.Values["id"].(string)
|
sid := httpSession.Values["id"].(string)
|
||||||
|
|
||||||
editorWS[sid], _ = websocket.Upgrade(w, r, nil, 1024, 1024)
|
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||||
|
editorChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
||||||
|
|
||||||
|
session.EditorWS[sid] = &editorChan
|
||||||
|
|
||||||
ret := map[string]interface{}{"output": "Editor initialized", "cmd": "init-editor"}
|
ret := map[string]interface{}{"output": "Editor initialized", "cmd": "init-editor"}
|
||||||
editorWS[sid].WriteJSON(&ret)
|
editorChan.Conn.WriteJSON(&ret)
|
||||||
|
|
||||||
glog.Infof("Open a new [Editor] with session [%s], %d", sid, len(editorWS))
|
glog.Infof("Open a new [Editor] with session [%s], %d", sid, len(session.EditorWS))
|
||||||
|
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
for {
|
for {
|
||||||
if err := editorWS[sid].ReadJSON(&args); err != nil {
|
if err := session.EditorWS[sid].Conn.ReadJSON(&args); err != nil {
|
||||||
if err.Error() == "EOF" {
|
if err.Error() == "EOF" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -73,14 +75,14 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
ret = map[string]interface{}{"output": string(output.Bytes()), "cmd": "autocomplete"}
|
ret = map[string]interface{}{"output": string(output.Bytes()), "cmd": "autocomplete"}
|
||||||
|
|
||||||
if err := editorWS[sid].WriteJSON(&ret); err != nil {
|
if err := session.EditorWS[sid].Conn.WriteJSON(&ret); err != nil {
|
||||||
glog.Error("Editor WS ERROR: " + err.Error())
|
glog.Error("Editor WS ERROR: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动完成(代码补全).
|
// AutocompleteHandler handles request of code autocompletion.
|
||||||
func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var args map[string]interface{}
|
var args map[string]interface{}
|
||||||
|
|
||||||
|
@ -133,7 +135,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
glog.V(5).Infof("gocode set lib-path %s", libPath)
|
glog.V(5).Infof("gocode set lib-path %s", libPath)
|
||||||
|
|
||||||
// FIXME: 使用 gocode set lib-path 在多工作空间环境下肯定是有问题的,需要考虑其他实现方式
|
// FIXME: using gocode set lib-path has some issues while accrossing workspaces
|
||||||
gocode := conf.Wide.GetExecutableInGOBIN("gocode")
|
gocode := conf.Wide.GetExecutableInGOBIN("gocode")
|
||||||
argv := []string{"set", "lib-path", libPath}
|
argv := []string{"set", "lib-path", libPath}
|
||||||
exec.Command(gocode, argv...).Run()
|
exec.Command(gocode, argv...).Run()
|
||||||
|
@ -157,7 +159,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write(output)
|
w.Write(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看表达式信息.
|
// GetExprInfoHandler handles request of getting expression infomation.
|
||||||
func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -203,7 +205,6 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// glog.Infof("offset [%d]", offset)
|
// glog.Infof("offset [%d]", offset)
|
||||||
|
|
||||||
// TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现
|
|
||||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-info", "."}
|
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-info", "."}
|
||||||
cmd := exec.Command(ide_stub, argv...)
|
cmd := exec.Command(ide_stub, argv...)
|
||||||
|
@ -229,7 +230,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data["info"] = exprInfo
|
data["info"] = exprInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找声明.
|
// FindDeclarationHandler handles request of finding declaration.
|
||||||
func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -275,7 +276,6 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// glog.Infof("offset [%d]", offset)
|
// glog.Infof("offset [%d]", offset)
|
||||||
|
|
||||||
// TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现
|
|
||||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-def", "."}
|
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-def", "."}
|
||||||
cmd := exec.Command(ide_stub, argv...)
|
cmd := exec.Command(ide_stub, argv...)
|
||||||
|
@ -309,7 +309,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data["cursorCh"] = cursorCh
|
data["cursorCh"] = cursorCh
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找使用.
|
// FindUsagesHandler handles request of finding usages.
|
||||||
func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -355,7 +355,6 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
offset := getCursorOffset(code, line, ch)
|
offset := getCursorOffset(code, line, ch)
|
||||||
// glog.Infof("offset [%d]", offset)
|
// glog.Infof("offset [%d]", offset)
|
||||||
|
|
||||||
// TODO: 目前是调用 liteide_stub 工具来查找使用,后续需要重新实现
|
|
||||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-use", "."}
|
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-use", "."}
|
||||||
cmd := exec.Command(ide_stub, argv...)
|
cmd := exec.Command(ide_stub, argv...)
|
||||||
|
@ -396,18 +395,19 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data["founds"] = usages
|
data["founds"] = usages
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算光标偏移位置.
|
// getCursorOffset calculates the cursor offset.
|
||||||
//
|
//
|
||||||
// line 指定了行号(第一行为 0),ch 指定了列号(第一列为 0).
|
// line is the line number, starts with 0 that means the first line
|
||||||
|
// ch is the column number, starts with 0 that means the first column
|
||||||
func getCursorOffset(code string, line, ch int) (offset int) {
|
func getCursorOffset(code string, line, ch int) (offset int) {
|
||||||
lines := strings.Split(code, "\n")
|
lines := strings.Split(code, "\n")
|
||||||
|
|
||||||
// 计算前几行长度
|
// calculate sum length of lines before
|
||||||
for i := 0; i < line; i++ {
|
for i := 0; i < line; i++ {
|
||||||
offset += len(lines[i])
|
offset += len(lines[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算当前行、当前列长度
|
// calculate length of the current line and column
|
||||||
curLine := lines[line]
|
curLine := lines[line]
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
r := []rune(curLine)
|
r := []rune(curLine)
|
||||||
|
@ -415,8 +415,8 @@ func getCursorOffset(code string, line, ch int) (offset int) {
|
||||||
buffer.WriteString(string(r[i]))
|
buffer.WriteString(string(r[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += line // 加换行符
|
offset += len(buffer.String()) // append length of current line
|
||||||
offset += len(buffer.String()) // 加当前行列偏移
|
offset += line // append number of '\n'
|
||||||
|
|
||||||
return offset
|
return offset
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,9 @@ import (
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 格式化 Go 源码文件.
|
// GoFmtHandler handles request of formatting Go source code.
|
||||||
// 根据用户的 GoFormat 配置选择格式化工具:
|
//
|
||||||
|
// This function will select a format tooll based on user's configuration:
|
||||||
// 1. gofmt
|
// 1. gofmt
|
||||||
// 2. goimports
|
// 2. goimports
|
||||||
func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -38,8 +39,8 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
filePath := args["file"].(string)
|
filePath := args["file"].(string)
|
||||||
|
|
||||||
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
|
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
|
||||||
if strings.HasPrefix(filePath, apiPath) { // 如果是 Go API 源码文件
|
if strings.HasPrefix(filePath, apiPath) { // if it is Go API source code
|
||||||
// 忽略修改
|
// ignore it
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,8 +89,8 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化 HTML 文件.
|
// HTMLFmtHandler handles request of formatting HTML source code.
|
||||||
// FIXME:依赖的工具 gohtml 格式化 HTML 时有问题
|
// FIXME: gohtml has some issues...
|
||||||
func HTMLFmtHandler(w http.ResponseWriter, r *http.Request) {
|
func HTMLFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -143,68 +144,3 @@ func HTMLFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化 JSON 文件.
|
|
||||||
func JSONFmtHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
data := map[string]interface{}{"succ": true}
|
|
||||||
defer util.RetJSON(w, r, data)
|
|
||||||
|
|
||||||
var args map[string]interface{}
|
|
||||||
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
|
||||||
glog.Error(err)
|
|
||||||
data["succ"] = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
filePath := args["file"].(string)
|
|
||||||
|
|
||||||
fout, err := os.Create(filePath)
|
|
||||||
|
|
||||||
if nil != err {
|
|
||||||
glog.Error(err)
|
|
||||||
data["succ"] = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code := args["code"].(string)
|
|
||||||
|
|
||||||
fout.WriteString(code)
|
|
||||||
if err := fout.Close(); nil != err {
|
|
||||||
glog.Error(err)
|
|
||||||
data["succ"] = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
obj := new(interface{})
|
|
||||||
if err := json.Unmarshal([]byte(code), &obj); nil != err {
|
|
||||||
glog.Error(err)
|
|
||||||
data["succ"] = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
glog.Info(obj)
|
|
||||||
|
|
||||||
bytes, err := json.MarshalIndent(obj, "", " ")
|
|
||||||
if nil != err {
|
|
||||||
data["succ"] = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code = string(bytes)
|
|
||||||
data["code"] = code
|
|
||||||
|
|
||||||
fout, err = os.Create(filePath)
|
|
||||||
fout.WriteString(code)
|
|
||||||
if err := fout.Close(); nil != err {
|
|
||||||
glog.Error(err)
|
|
||||||
data["succ"] = false
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,53 +1,52 @@
|
||||||
// 事件处理.
|
// Event manipulations.
|
||||||
package event
|
package event
|
||||||
|
|
||||||
import "github.com/golang/glog"
|
import "github.com/golang/glog"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EvtCodeGOPATHNotFound = iota // 事件代码:找不到环境变量 $GOPATH
|
EvtCodeGOPATHNotFound = iota // event code: not found $GOPATH env variable
|
||||||
EvtCodeGOROOTNotFound // 事件代码:找不到环境变量 $GOROOT
|
EvtCodeGOROOTNotFound // event code: not found $GOROOT env variable
|
||||||
EvtCodeGocodeNotFound // 事件代码:找不到 gocode
|
EvtCodeGocodeNotFound // event code: not found gocode
|
||||||
EvtCodeIDEStubNotFound // 事件代码:找不到 IDE stub
|
EvtCodeIDEStubNotFound // event code: not found ide_stub
|
||||||
EvtCodeServerInternalError // 事件代码:服务器内部错误
|
EvtCodeServerInternalError // event code: server internal error
|
||||||
)
|
)
|
||||||
|
|
||||||
// 事件队列最大长度.
|
// Max length of queue.
|
||||||
const MaxQueueLength = 10
|
const MaxQueueLength = 10
|
||||||
|
|
||||||
// 事件结构.
|
// Event.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
Code int `json:"code"` // 事件代码
|
Code int `json:"code"` // event code
|
||||||
Sid string `json:"sid"` // 用户会话 id
|
Sid string `json:"sid"` // wide session id related
|
||||||
Data interface{} `json:"data"` // 事件数据
|
Data interface{} `json:"data"` // event data
|
||||||
}
|
}
|
||||||
|
|
||||||
// 全局事件队列.
|
// Global event queue.
|
||||||
//
|
//
|
||||||
// 入队的事件将分发到每个用户的事件队列中.
|
// Every event in this queue will be dispatched to each user event queue.
|
||||||
var EventQueue = make(chan *Event, MaxQueueLength)
|
var EventQueue = make(chan *Event, MaxQueueLength)
|
||||||
|
|
||||||
// 用户事件队列.
|
// User event queue.
|
||||||
type UserEventQueue struct {
|
type UserEventQueue struct {
|
||||||
Sid string // 关联的会话 id
|
Sid string // wide session id related
|
||||||
Queue chan *Event // 队列
|
Queue chan *Event // queue
|
||||||
Handlers []Handler // 事件处理器集
|
Handlers []Handler // event handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
// 事件队列集类型.
|
|
||||||
type Queues map[string]*UserEventQueue
|
type Queues map[string]*UserEventQueue
|
||||||
|
|
||||||
// 用户事件队列集.
|
// User event queues.
|
||||||
//
|
//
|
||||||
// <sid, *UserEventQueue>
|
// <sid, *UserEventQueue>
|
||||||
var UserEventQueues = Queues{}
|
var UserEventQueues = Queues{}
|
||||||
|
|
||||||
// 加载事件处理.
|
// Load initializes the event handling.
|
||||||
func Load() {
|
func Load() {
|
||||||
go func() {
|
go func() {
|
||||||
for event := range EventQueue {
|
for event := range EventQueue {
|
||||||
glog.V(5).Infof("收到全局事件 [%d]", event.Code)
|
glog.V(5).Infof("Received a global event [code=%d]", event.Code)
|
||||||
|
|
||||||
// 将事件分发到每个用户的事件队列里
|
// dispatch the event to each user event queue
|
||||||
for _, userQueue := range UserEventQueues {
|
for _, userQueue := range UserEventQueues {
|
||||||
event.Sid = userQueue.Sid
|
event.Sid = userQueue.Sid
|
||||||
|
|
||||||
|
@ -57,14 +56,14 @@ func Load() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为用户队列添加事件处理器.
|
// AddHandler adds the specified handlers to user event queues.
|
||||||
func (uq *UserEventQueue) AddHandler(handlers ...Handler) {
|
func (uq *UserEventQueue) AddHandler(handlers ...Handler) {
|
||||||
for _, handler := range handlers {
|
for _, handler := range handlers {
|
||||||
uq.Handlers = append(uq.Handlers, handler)
|
uq.Handlers = append(uq.Handlers, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化一个用户事件队列.
|
// New initializes a user event queue with the specified wide session id.
|
||||||
func (ueqs Queues) New(sid string) *UserEventQueue {
|
func (ueqs Queues) New(sid string) *UserEventQueue {
|
||||||
q := ueqs[sid]
|
q := ueqs[sid]
|
||||||
if nil != q {
|
if nil != q {
|
||||||
|
@ -80,11 +79,11 @@ func (ueqs Queues) New(sid string) *UserEventQueue {
|
||||||
|
|
||||||
ueqs[sid] = q
|
ueqs[sid] = q
|
||||||
|
|
||||||
go func() { // 队列开始监听事件
|
go func() { // start listening
|
||||||
for evt := range q.Queue {
|
for evt := range q.Queue {
|
||||||
glog.V(5).Infof("Session [%s] received a event [%d]", sid, evt.Code)
|
glog.V(5).Infof("Session [%s] received a event [%d]", sid, evt.Code)
|
||||||
|
|
||||||
// 将事件交给事件处理器进行处理
|
// process event by each handlers
|
||||||
for _, handler := range q.Handlers {
|
for _, handler := range q.Handlers {
|
||||||
handler.Handle(evt)
|
handler.Handle(evt)
|
||||||
}
|
}
|
||||||
|
@ -94,7 +93,7 @@ func (ueqs Queues) New(sid string) *UserEventQueue {
|
||||||
return q
|
return q
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭一个用户事件队列.
|
// Close closes a user event queue with the specified wide session id.
|
||||||
func (ueqs Queues) Close(sid string) {
|
func (ueqs Queues) Close(sid string) {
|
||||||
q := ueqs[sid]
|
q := ueqs[sid]
|
||||||
if nil == q {
|
if nil == q {
|
||||||
|
@ -104,15 +103,15 @@ func (ueqs Queues) Close(sid string) {
|
||||||
delete(ueqs, sid)
|
delete(ueqs, sid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 事件处理接口.
|
// Type of event handler.
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
Handle(event *Event)
|
Handle(event *Event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 函数指针包装.
|
// Type of handler function.
|
||||||
type HandleFunc func(event *Event)
|
type HandleFunc func(event *Event)
|
||||||
|
|
||||||
// 事件处理默认实现.
|
// Default implementation of event handling.
|
||||||
func (fn HandleFunc) Handle(event *Event) {
|
func (fn HandleFunc) Handle(event *Event) {
|
||||||
fn(event)
|
fn(event)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ type FileNode struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
IconSkin string `json:"iconSkin"` // Value should be end with a space
|
IconSkin string `json:"iconSkin"` // Value should be end with a space
|
||||||
Type string `json:"type"` // "f": file,"d": directory
|
Type string `json:"type"` // "f": file, "d": directory
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
FileNodes []*FileNode `json:"children"`
|
FileNodes []*FileNode `json:"children"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// 国际化操作.
|
// Internationalization manipulations.
|
||||||
package i18n
|
package i18n
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -10,16 +10,17 @@ import (
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Locale.
|
||||||
type locale struct {
|
type locale struct {
|
||||||
Name string
|
Name string
|
||||||
Langs map[string]interface{}
|
Langs map[string]interface{}
|
||||||
TimeZone string
|
TimeZone string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 所有的 locales.
|
// All locales.
|
||||||
var Locales = map[string]locale{}
|
var Locales = map[string]locale{}
|
||||||
|
|
||||||
// 加载国际化配置.
|
// Load loads i18n message configurations.
|
||||||
func Load() {
|
func Load() {
|
||||||
f, _ := os.Open("i18n")
|
f, _ := os.Open("i18n")
|
||||||
names, _ := f.Readdirnames(-1)
|
names, _ := f.Readdirnames(-1)
|
||||||
|
@ -57,12 +58,12 @@ func load(localeStr string) {
|
||||||
glog.V(5).Infof("Loaded [%s] locale configuration", localeStr)
|
glog.V(5).Infof("Loaded [%s] locale configuration", localeStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取语言配置项.
|
// Get gets message with the specified locale and key.
|
||||||
func Get(locale, key string) interface{} {
|
func Get(locale, key string) interface{} {
|
||||||
return Locales[locale].Langs[key]
|
return Locales[locale].Langs[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取语言配置.
|
// GetAll gets all messages with the specified locale.
|
||||||
func GetAll(locale string) map[string]interface{} {
|
func GetAll(locale string) map[string]interface{} {
|
||||||
return Locales[locale].Langs
|
return Locales[locale].Langs
|
||||||
}
|
}
|
||||||
|
|
1
main.go
1
main.go
|
@ -309,7 +309,6 @@ func main() {
|
||||||
http.HandleFunc("/find/decl", handlerWrapper(editor.FindDeclarationHandler))
|
http.HandleFunc("/find/decl", handlerWrapper(editor.FindDeclarationHandler))
|
||||||
http.HandleFunc("/find/usages", handlerWrapper(editor.FindUsagesHandler))
|
http.HandleFunc("/find/usages", handlerWrapper(editor.FindUsagesHandler))
|
||||||
http.HandleFunc("/html/fmt", handlerWrapper(editor.HTMLFmtHandler))
|
http.HandleFunc("/html/fmt", handlerWrapper(editor.HTMLFmtHandler))
|
||||||
http.HandleFunc("/json/fmt", handlerWrapper(editor.JSONFmtHandler))
|
|
||||||
|
|
||||||
// shell
|
// shell
|
||||||
http.HandleFunc("/shell/ws", handlerWrapper(shell.WSHandler))
|
http.HandleFunc("/shell/ws", handlerWrapper(shell.WSHandler))
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// 通知.
|
// Notification manipulations.
|
||||||
package notification
|
package notification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -16,15 +16,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Error = "ERROR" // 通知.严重程度:ERROR
|
Error = "ERROR" // notification.severity: ERROR
|
||||||
Warn = "WARN" // 通知.严重程度:WARN
|
Warn = "WARN" // notification.severity: WARN
|
||||||
Info = "INFO" // 通知.严重程度:INFO
|
Info = "INFO" // notification.severity: INFO
|
||||||
|
|
||||||
Setup = "Setup" // 通知.类型:安装
|
Setup = "Setup" // notification.type: setup
|
||||||
Server = "Server" // 通知.类型:服务器
|
Server = "Server" // notification.type: server
|
||||||
)
|
)
|
||||||
|
|
||||||
// 通知结构.
|
// Notification.
|
||||||
type Notification struct {
|
type Notification struct {
|
||||||
event *event.Event
|
event *event.Event
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
@ -32,9 +32,9 @@ type Notification struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户事件处理:将事件转为通知,并通过通知通道推送给前端.
|
// event2Notification processes user event by converting the specified event to a notification, and then push it to front
|
||||||
|
// browser with notification channel.
|
||||||
//
|
//
|
||||||
// 当用户事件队列接收到事件时将会调用该函数进行处理.
|
|
||||||
func event2Notification(e *event.Event) {
|
func event2Notification(e *event.Event) {
|
||||||
if nil == session.NotificationWS[e.Sid] {
|
if nil == session.NotificationWS[e.Sid] {
|
||||||
return
|
return
|
||||||
|
@ -71,7 +71,7 @@ func event2Notification(e *event.Event) {
|
||||||
wsChannel.Refresh()
|
wsChannel.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 建立通知通道.
|
// WSHandler handles request of creating notification channel.
|
||||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sid := r.URL.Query()["sid"][0]
|
sid := r.URL.Query()["sid"][0]
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
glog.V(4).Infof("Open a new [Notification] with session [%s], %d", sid, len(session.NotificationWS))
|
glog.V(4).Infof("Open a new [Notification] with session [%s], %d", sid, len(session.NotificationWS))
|
||||||
|
|
||||||
// 添加用户事件处理器
|
// add user event handler
|
||||||
wSession.EventQueue.AddHandler(event.HandleFunc(event2Notification))
|
wSession.EventQueue.AddHandler(event.HandleFunc(event2Notification))
|
||||||
|
|
||||||
input := map[string]interface{}{}
|
input := map[string]interface{}{}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// 构建、运行、go tool 操作.
|
// Build, run and go tool manipulations.
|
||||||
package output
|
package output
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -25,11 +25,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
lintSeverityError = "error" // Lint 严重级别:错误
|
lintSeverityError = "error" // lint severity: error
|
||||||
lintSeverityWarn = "warning" // Lint 严重级别:警告
|
lintSeverityWarn = "warning" // lint severity: warning
|
||||||
)
|
)
|
||||||
|
|
||||||
// 代码 Lint 结构.
|
// Code lint.
|
||||||
type Lint struct {
|
type Lint struct {
|
||||||
File string `json:"file"`
|
File string `json:"file"`
|
||||||
LineNo int `json:"lineNo"`
|
LineNo int `json:"lineNo"`
|
||||||
|
@ -37,7 +37,7 @@ type Lint struct {
|
||||||
Msg string `json:"msg"`
|
Msg string `json:"msg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 建立输出通道.
|
// WSHandler handles request of creating output channel.
|
||||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sid := r.URL.Query()["sid"][0]
|
sid := r.URL.Query()["sid"][0]
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
glog.V(4).Infof("Open a new [Output] with session [%s], %d", sid, len(session.OutputWS))
|
glog.V(4).Infof("Open a new [Output] with session [%s], %d", sid, len(session.OutputWS))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 运行一个可执行文件.
|
// RunHandler handles request of executing a binary file.
|
||||||
func RunHandler(w http.ResponseWriter, r *http.Request) {
|
func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -105,7 +105,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加到用户进程集中
|
// add the process to user's process set
|
||||||
processes.add(wSession, cmd.Process)
|
processes.add(wSession, cmd.Process)
|
||||||
|
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
|
@ -117,7 +117,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
glog.V(3).Infof("Session [%s] is running [id=%d, file=%s]", sid, runningId, filePath)
|
glog.V(3).Infof("Session [%s] is running [id=%d, file=%s]", sid, runningId, filePath)
|
||||||
|
|
||||||
// 在读取程序输出前先返回一次,使前端获取到 run 状态与 pid
|
// push once for front-end to get the 'run' state and pid
|
||||||
if nil != session.OutputWS[sid] {
|
if nil != session.OutputWS[sid] {
|
||||||
wsChannel := session.OutputWS[sid]
|
wsChannel := session.OutputWS[sid]
|
||||||
|
|
||||||
|
@ -129,15 +129,14 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
buf, err := reader.ReadBytes('\n')
|
buf, err := reader.ReadBytes('\n')
|
||||||
|
|
||||||
if nil != err || 0 == len(buf) {
|
if nil != err || 0 == len(buf) {
|
||||||
// 从用户进程集中移除这个执行完毕(或是被主动停止)的进程
|
// remove the exited process from user process set
|
||||||
processes.remove(wSession, cmd.Process)
|
processes.remove(wSession, cmd.Process)
|
||||||
|
|
||||||
glog.V(3).Infof("Session [%s] 's running [id=%d, file=%s] has done", sid, runningId, filePath)
|
glog.V(3).Infof("Session [%s] 's running [id=%d, file=%s] has done", sid, runningId, filePath)
|
||||||
|
@ -153,8 +152,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
@ -170,15 +168,14 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}(rand.Int())
|
}(rand.Int())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建可执行文件.
|
// BuildHandler handles request of building.
|
||||||
func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -237,7 +234,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
executable = filepath.Join(curDir, executable)
|
executable = filepath.Join(curDir, executable)
|
||||||
|
|
||||||
// 先把可执行文件删了
|
// remove executable file before building
|
||||||
err = os.RemoveAll(executable)
|
err = os.RemoveAll(executable)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
glog.Info(err)
|
glog.Info(err)
|
||||||
|
@ -269,7 +266,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
|
|
||||||
if nil != session.OutputWS[sid] {
|
if nil != session.OutputWS[sid] {
|
||||||
// 在前端 output 中显示“开始构建”
|
// display "START [go build]" in front-end browser
|
||||||
|
|
||||||
channelRet["output"] = "<span class='start-build'>" + i18n.Get(locale, "start-build").(string) + "</span>\n"
|
channelRet["output"] = "<span class='start-build'>" + i18n.Get(locale, "start-build").(string) + "</span>\n"
|
||||||
channelRet["cmd"] = "start-build"
|
channelRet["cmd"] = "start-build"
|
||||||
|
@ -282,8 +279,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||||
|
@ -301,19 +297,18 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
glog.V(3).Infof("Session [%s] is building [id=%d, dir=%s]", sid, runningId, curDir)
|
glog.V(3).Infof("Session [%s] is building [id=%d, dir=%s]", sid, runningId, curDir)
|
||||||
|
|
||||||
// 一次性读取
|
// read all
|
||||||
buf, _ := ioutil.ReadAll(reader)
|
buf, _ := ioutil.ReadAll(reader)
|
||||||
|
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
channelRet["cmd"] = "build"
|
channelRet["cmd"] = "build"
|
||||||
channelRet["executable"] = executable
|
channelRet["executable"] = executable
|
||||||
|
|
||||||
if 0 == len(buf) { // 说明构建成功,没有错误信息输出
|
if 0 == len(buf) { // build success
|
||||||
// 设置下一次执行命令(前端会根据该参数发送请求)
|
|
||||||
channelRet["nextCmd"] = args["nextCmd"]
|
channelRet["nextCmd"] = args["nextCmd"]
|
||||||
channelRet["output"] = "<span class='build-succ'>" + i18n.Get(locale, "build-succ").(string) + "</span>\n"
|
channelRet["output"] = "<span class='build-succ'>" + i18n.Get(locale, "build-succ").(string) + "</span>\n"
|
||||||
|
|
||||||
go func() { // 运行 go install,生成的库用于 gocode lib-path
|
go func() { // go install, for subsequent gocode lib-path
|
||||||
cmd := exec.Command("go", "install")
|
cmd := exec.Command("go", "install")
|
||||||
cmd.Dir = curDir
|
cmd.Dir = curDir
|
||||||
|
|
||||||
|
@ -324,15 +319,16 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
glog.Warning(string(out))
|
glog.Warning(string(out))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
} else { // 构建失败
|
} else { // build error
|
||||||
// 解析错误信息,返回给编辑器 gutter lint
|
// build gutter lint
|
||||||
|
|
||||||
errOut := string(buf)
|
errOut := string(buf)
|
||||||
channelRet["output"] = "<span class='build-error'>" + i18n.Get(locale, "build-error").(string) + "</span>\n" + errOut
|
channelRet["output"] = "<span class='build-error'>" + i18n.Get(locale, "build-error").(string) + "</span>\n" + errOut
|
||||||
|
|
||||||
lines := strings.Split(errOut, "\n")
|
lines := strings.Split(errOut, "\n")
|
||||||
|
|
||||||
if lines[0][0] == '#' {
|
if lines[0][0] == '#' {
|
||||||
lines = lines[1:] // 跳过第一行
|
lines = lines[1:] // skip the first line
|
||||||
}
|
}
|
||||||
|
|
||||||
lints := []*Lint{}
|
lints := []*Lint{}
|
||||||
|
@ -343,7 +339,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if line[0] == '\t' {
|
if line[0] == '\t' {
|
||||||
// 添加到上一个 lint 中
|
// append to the last lint
|
||||||
last := len(lints)
|
last := len(lints)
|
||||||
msg := lints[last-1].Msg
|
msg := lints[last-1].Msg
|
||||||
msg += line
|
msg += line
|
||||||
|
@ -385,14 +381,13 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}(rand.Int())
|
}(rand.Int())
|
||||||
}
|
}
|
||||||
|
|
||||||
// go test.
|
// GoTestHandler handles request of go test.
|
||||||
func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -443,7 +438,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
|
|
||||||
if nil != session.OutputWS[sid] {
|
if nil != session.OutputWS[sid] {
|
||||||
// 在前端 output 中显示“开始 go test
|
// display "START [go test]" in front-end browser
|
||||||
|
|
||||||
channelRet["output"] = "<span class='start-test'>" + i18n.Get(locale, "start-test").(string) + "</span>\n"
|
channelRet["output"] = "<span class='start-test'>" + i18n.Get(locale, "start-test").(string) + "</span>\n"
|
||||||
channelRet["cmd"] = "start-test"
|
channelRet["cmd"] = "start-test"
|
||||||
|
@ -456,8 +451,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||||
|
@ -477,10 +471,10 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
channelRet["cmd"] = "go test"
|
channelRet["cmd"] = "go test"
|
||||||
|
|
||||||
// 一次性读取
|
// read all
|
||||||
buf, _ := ioutil.ReadAll(reader)
|
buf, _ := ioutil.ReadAll(reader)
|
||||||
|
|
||||||
// 同步点,等待 go test 执行完成
|
// waiting for go test finished
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
|
|
||||||
if !cmd.ProcessState.Success() {
|
if !cmd.ProcessState.Success() {
|
||||||
|
@ -501,13 +495,12 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
}(rand.Int())
|
}(rand.Int())
|
||||||
}
|
}
|
||||||
|
|
||||||
// go install.
|
// GoInstallHandler handles request of go install.
|
||||||
func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -560,7 +553,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
|
|
||||||
if nil != session.OutputWS[sid] {
|
if nil != session.OutputWS[sid] {
|
||||||
// 在前端 output 中显示“开始 go install”
|
// display "START [go install]" in front-end browser
|
||||||
|
|
||||||
channelRet["output"] = "<span class='start-install'>" + i18n.Get(locale, "start-install").(string) + "</span>\n"
|
channelRet["output"] = "<span class='start-install'>" + i18n.Get(locale, "start-install").(string) + "</span>\n"
|
||||||
channelRet["cmd"] = "start-install"
|
channelRet["cmd"] = "start-install"
|
||||||
|
@ -573,8 +566,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||||
|
@ -592,19 +584,20 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
glog.V(3).Infof("Session [%s] is running [go install] [id=%d, dir=%s]", sid, runningId, curDir)
|
glog.V(3).Infof("Session [%s] is running [go install] [id=%d, dir=%s]", sid, runningId, curDir)
|
||||||
|
|
||||||
// 一次性读取
|
// read all
|
||||||
buf, _ := ioutil.ReadAll(reader)
|
buf, _ := ioutil.ReadAll(reader)
|
||||||
|
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
channelRet["cmd"] = "go install"
|
channelRet["cmd"] = "go install"
|
||||||
|
|
||||||
if 0 != len(buf) { // 构建失败
|
if 0 != len(buf) { // build error
|
||||||
// 解析错误信息,返回给编辑器 gutter lint
|
// build gutter lint
|
||||||
|
|
||||||
errOut := string(buf)
|
errOut := string(buf)
|
||||||
lines := strings.Split(errOut, "\n")
|
lines := strings.Split(errOut, "\n")
|
||||||
|
|
||||||
if lines[0][0] == '#' {
|
if lines[0][0] == '#' {
|
||||||
lines = lines[1:] // 跳过第一行
|
lines = lines[1:] // skip the first line
|
||||||
}
|
}
|
||||||
|
|
||||||
lints := []*Lint{}
|
lints := []*Lint{}
|
||||||
|
@ -615,7 +608,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if line[0] == '\t' {
|
if line[0] == '\t' {
|
||||||
// 添加到上一个 lint 中
|
// append to the last lint
|
||||||
last := len(lints)
|
last := len(lints)
|
||||||
msg := lints[last-1].Msg
|
msg := lints[last-1].Msg
|
||||||
msg += line
|
msg += line
|
||||||
|
@ -661,14 +654,13 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}(rand.Int())
|
}(rand.Int())
|
||||||
}
|
}
|
||||||
|
|
||||||
// go get.
|
// GoGetHandler handles request of go get.
|
||||||
func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -719,7 +711,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
|
|
||||||
if nil != session.OutputWS[sid] {
|
if nil != session.OutputWS[sid] {
|
||||||
// 在前端 output 中显示“开始 go get
|
// display "START [go get]" in front-end browser
|
||||||
|
|
||||||
channelRet["output"] = "<span class='start-get'>" + i18n.Get(locale, "start-get").(string) + "</span>\n"
|
channelRet["output"] = "<span class='start-get'>" + i18n.Get(locale, "start-get").(string) + "</span>\n"
|
||||||
channelRet["cmd"] = "start-get"
|
channelRet["cmd"] = "start-get"
|
||||||
|
@ -732,8 +724,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||||
|
@ -754,7 +745,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
channelRet := map[string]interface{}{}
|
channelRet := map[string]interface{}{}
|
||||||
channelRet["cmd"] = "go get"
|
channelRet["cmd"] = "go get"
|
||||||
|
|
||||||
// 一次性读取
|
// read all
|
||||||
buf, _ := ioutil.ReadAll(reader)
|
buf, _ := ioutil.ReadAll(reader)
|
||||||
|
|
||||||
if 0 != len(buf) {
|
if 0 != len(buf) {
|
||||||
|
@ -776,13 +767,12 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChannel.Refresh()
|
||||||
wsChannel.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
}(rand.Int())
|
}(rand.Int())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 结束正在运行的进程.
|
// StopHandler handles request of stoping a running process.
|
||||||
func StopHandler(w http.ResponseWriter, r *http.Request) {
|
func StopHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
|
|
@ -8,18 +8,18 @@ import (
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 进程集类型.
|
// Type of process set.
|
||||||
type procs map[string][]*os.Process
|
type procs map[string][]*os.Process
|
||||||
|
|
||||||
// 所有用户正在运行的程序进程集.
|
// Processse of all users.
|
||||||
//
|
//
|
||||||
// <sid, []*os.Process>
|
// <sid, []*os.Process>
|
||||||
var processes = procs{}
|
var processes = procs{}
|
||||||
|
|
||||||
// 排它锁,防止并发修改.
|
// Exclusive lock.
|
||||||
var mutex sync.Mutex
|
var mutex sync.Mutex
|
||||||
|
|
||||||
// 添加用户执行进程.
|
// add adds the specified process to the user process set.
|
||||||
func (procs *procs) add(wSession *session.WideSession, proc *os.Process) {
|
func (procs *procs) add(wSession *session.WideSession, proc *os.Process) {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
@ -30,13 +30,13 @@ func (procs *procs) add(wSession *session.WideSession, proc *os.Process) {
|
||||||
userProcesses = append(userProcesses, proc)
|
userProcesses = append(userProcesses, proc)
|
||||||
(*procs)[sid] = userProcesses
|
(*procs)[sid] = userProcesses
|
||||||
|
|
||||||
// 会话关联进程
|
// bind process with wide session
|
||||||
wSession.SetProcesses(userProcesses)
|
wSession.SetProcesses(userProcesses)
|
||||||
|
|
||||||
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除用户执行进程.
|
// remove removes the specified process from the user process set.
|
||||||
func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) {
|
func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
@ -48,10 +48,10 @@ func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) {
|
||||||
var newProcesses []*os.Process
|
var newProcesses []*os.Process
|
||||||
for i, p := range userProcesses {
|
for i, p := range userProcesses {
|
||||||
if p.Pid == proc.Pid {
|
if p.Pid == proc.Pid {
|
||||||
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...)
|
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...) // remove it
|
||||||
(*procs)[sid] = newProcesses
|
(*procs)[sid] = newProcesses
|
||||||
|
|
||||||
// 会话关联进程
|
// bind process with wide session
|
||||||
wSession.SetProcesses(newProcesses)
|
wSession.SetProcesses(newProcesses)
|
||||||
|
|
||||||
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
||||||
|
@ -61,7 +61,7 @@ func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 结束用户正在执行的进程.
|
// kill kills a process specified by the given pid.
|
||||||
func (procs *procs) kill(wSession *session.WideSession, pid int) {
|
func (procs *procs) kill(wSession *session.WideSession, pid int) {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
@ -80,7 +80,7 @@ func (procs *procs) kill(wSession *session.WideSession, pid int) {
|
||||||
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...)
|
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...)
|
||||||
(*procs)[sid] = newProcesses
|
(*procs)[sid] = newProcesses
|
||||||
|
|
||||||
// 会话关联进程
|
// bind process with wide session
|
||||||
wSession.SetProcesses(newProcesses)
|
wSession.SetProcesses(newProcesses)
|
||||||
|
|
||||||
glog.V(3).Infof("Killed a process [pid=%d] of session [%s]", pid, sid)
|
glog.V(3).Infof("Killed a process [pid=%d] of session [%s]", pid, sid)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// 会话操作.
|
// Session manipulations.
|
||||||
//
|
//
|
||||||
// Wide 服务器端需要维护两种会话:
|
// Wide server side needs maintain two kinds of sessions:
|
||||||
//
|
//
|
||||||
// 1. HTTP 会话:主要用于验证登录
|
// 1. HTTP session: mainly used for login authentication
|
||||||
// 2. Wide 会话:浏览器 tab 打开/刷新会创建一个,并和 HTTP 会话进行关联
|
// 2. Wide session: browser tab open/refresh will create one, and associates with HTTP session
|
||||||
//
|
//
|
||||||
// 当会话失效时:释放所有和该会话相关的资源,例如运行中的程序进程、事件队列等.
|
// When a session gone: release all resources associated with it, such as running processes, event queues.
|
||||||
package session
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -26,52 +26,55 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SessionStateActive = iota // 会话状态:活的
|
SessionStateActive = iota // session state: active
|
||||||
SessionStateClosed // 会话状态:已关闭(这个状态目前暂时没有使用到)
|
SessionStateClosed // session state: closed (not used so far)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// 会话通道. <sid, *util.WSChannel>
|
// Session channels. <sid, *util.WSChannel>
|
||||||
SessionWS = map[string]*util.WSChannel{}
|
SessionWS = map[string]*util.WSChannel{}
|
||||||
|
|
||||||
// 输出通道. <sid, *util.WSChannel>
|
// Editor channels. <sid, *util.WSChannel>
|
||||||
|
EditorWS = map[string]*util.WSChannel{}
|
||||||
|
|
||||||
|
// Output channels. <sid, *util.WSChannel>
|
||||||
OutputWS = map[string]*util.WSChannel{}
|
OutputWS = map[string]*util.WSChannel{}
|
||||||
|
|
||||||
// 通知通道. <sid, *util.WSChannel>
|
// Notification channels. <sid, *util.WSChannel>
|
||||||
NotificationWS = map[string]*util.WSChannel{}
|
NotificationWS = map[string]*util.WSChannel{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 用户 HTTP 会话,用于验证登录.
|
// HTTP session store.
|
||||||
var HTTPSession = sessions.NewCookieStore([]byte("BEYOND"))
|
var HTTPSession = sessions.NewCookieStore([]byte("BEYOND"))
|
||||||
|
|
||||||
// Wide 会话,对应一个浏览器 tab.
|
// Wide session, associated with a browser tab.
|
||||||
type WideSession struct {
|
type WideSession struct {
|
||||||
Id string // 唯一标识
|
Id string // id
|
||||||
Username string // 用户名
|
Username string // username
|
||||||
HTTPSession *sessions.Session // 关联的 HTTP 会话
|
HTTPSession *sessions.Session // HTTP session related
|
||||||
Processes []*os.Process // 关联的进程集
|
Processes []*os.Process // process set
|
||||||
EventQueue *event.UserEventQueue // 关联的事件队列
|
EventQueue *event.UserEventQueue // event queue
|
||||||
State int // 状态
|
State int // state
|
||||||
Content *conf.LatestSessionContent // 最近一次会话内容
|
Content *conf.LatestSessionContent // the latest session content
|
||||||
Created time.Time // 创建时间
|
Created time.Time // create time
|
||||||
Updated time.Time // 最近一次使用时间
|
Updated time.Time // the latest use time
|
||||||
}
|
}
|
||||||
|
|
||||||
// 会话集类型.
|
// Type of wide sessions.
|
||||||
type Sessions []*WideSession
|
type Sessions []*WideSession
|
||||||
|
|
||||||
// 所有 Wide 会话集.
|
// Wide sessions.
|
||||||
var WideSessions Sessions
|
var WideSessions Sessions
|
||||||
|
|
||||||
// 排它锁,防止并发修改.
|
// Exclusive lock.
|
||||||
var mutex sync.Mutex
|
var mutex sync.Mutex
|
||||||
|
|
||||||
// 在一些特殊情况(例如浏览器不间断刷新/在源代码视图刷新)下 Wide 会话集内会出现无效会话,该函数定时(1 小时)检查并移除这些无效会话.
|
// In some special cases (such as a browser uninterrupted refresh / refresh in the source code view) will occur
|
||||||
|
// some invalid sessions, the function checks and removes these invalid sessions periodically (1 hour).
|
||||||
//
|
//
|
||||||
// 无效会话:在检查时间内 30 分钟都没有使用过的会话,参考 WideSession.Updated 字段.
|
// Invalid sessions: sessions that not used within 30 minutes, refers to WideSession.Updated field.
|
||||||
func FixedTimeRelease() {
|
func FixedTimeRelease() {
|
||||||
go func() {
|
go func() {
|
||||||
// 1 小时进行一次检查
|
|
||||||
for _ = range time.Tick(time.Hour) {
|
for _ = range time.Tick(time.Hour) {
|
||||||
hour, _ := time.ParseDuration("-30m")
|
hour, _ := time.ParseDuration("-30m")
|
||||||
threshold := time.Now().Add(hour)
|
threshold := time.Now().Add(hour)
|
||||||
|
@ -87,7 +90,9 @@ func FixedTimeRelease() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 建立会话通道. 通道断开时销毁会话状态,回收相关资源.
|
// WSHandler handles request of creating session channel.
|
||||||
|
//
|
||||||
|
// When a channel closed, releases all resources associated with it.
|
||||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sid := r.URL.Query()["sid"][0]
|
sid := r.URL.Query()["sid"][0]
|
||||||
wSession := WideSessions.Get(sid)
|
wSession := WideSessions.Get(sid)
|
||||||
|
@ -129,7 +134,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 会话内容保存.
|
// SaveContent handles request of session content storing.
|
||||||
func SaveContent(w http.ResponseWriter, r *http.Request) {
|
func SaveContent(w http.ResponseWriter, r *http.Request) {
|
||||||
data := map[string]interface{}{"succ": true}
|
data := map[string]interface{}{"succ": true}
|
||||||
defer util.RetJSON(w, r, data)
|
defer util.RetJSON(w, r, data)
|
||||||
|
@ -157,7 +162,7 @@ func SaveContent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
for _, user := range conf.Wide.Users {
|
for _, user := range conf.Wide.Users {
|
||||||
if user.Name == wSession.Username {
|
if user.Name == wSession.Username {
|
||||||
// 更新配置(内存变量),conf.FixedTimeSave() 会负责定时持久化
|
// update the variable in-memory, conf.FixedTimeSave() function will persist it periodically
|
||||||
user.LatestSessionContent = wSession.Content
|
user.LatestSessionContent = wSession.Content
|
||||||
|
|
||||||
wSession.Refresh()
|
wSession.Refresh()
|
||||||
|
@ -167,19 +172,19 @@ func SaveContent(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置会话关联的进程集.
|
// SetProcesses binds process set with the wide session.
|
||||||
func (s *WideSession) SetProcesses(ps []*os.Process) {
|
func (s *WideSession) SetProcesses(ps []*os.Process) {
|
||||||
s.Processes = ps
|
s.Processes = ps
|
||||||
|
|
||||||
s.Refresh()
|
s.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新会话最近一次使用时间.
|
// Refresh refreshes the channel by updating its use time.
|
||||||
func (s *WideSession) Refresh() {
|
func (s *WideSession) Refresh() {
|
||||||
s.Updated = time.Now()
|
s.Updated = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建一个 Wide 会话.
|
// New creates a wide session.
|
||||||
func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession {
|
func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
@ -189,7 +194,7 @@ func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession {
|
||||||
id := strconv.Itoa(rand.Int())
|
id := strconv.Itoa(rand.Int())
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
// 创建用户事件队列
|
// create user event queue
|
||||||
userEventQueue := event.UserEventQueues.New(id)
|
userEventQueue := event.UserEventQueues.New(id)
|
||||||
|
|
||||||
ret := &WideSession{
|
ret := &WideSession{
|
||||||
|
@ -208,7 +213,7 @@ func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 Wide 会话.
|
// Get gets a wide session with the specified session id.
|
||||||
func (sessions *Sessions) Get(sid string) *WideSession {
|
func (sessions *Sessions) Get(sid string) *WideSession {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
@ -222,26 +227,26 @@ func (sessions *Sessions) Get(sid string) *WideSession {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除 Wide 会话,释放相关资源.
|
// Remove removes a wide session specified with the given session id, releases resources associated with it.
|
||||||
//
|
//
|
||||||
// 会话相关资源:
|
// Session-related resources:
|
||||||
//
|
//
|
||||||
// 1. 用户事件队列
|
// 1. user event queue
|
||||||
// 2. 运行中的进程集
|
// 2. process set
|
||||||
// 3. WebSocket 通道
|
// 3. websocket channels
|
||||||
func (sessions *Sessions) Remove(sid string) {
|
func (sessions *Sessions) Remove(sid string) {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
||||||
for i, s := range *sessions {
|
for i, s := range *sessions {
|
||||||
if s.Id == sid {
|
if s.Id == sid {
|
||||||
// 从会话集中移除
|
// remove from session set
|
||||||
*sessions = append((*sessions)[:i], (*sessions)[i+1:]...)
|
*sessions = append((*sessions)[:i], (*sessions)[i+1:]...)
|
||||||
|
|
||||||
// 关闭用户事件队列
|
// close user event queue
|
||||||
event.UserEventQueues.Close(sid)
|
event.UserEventQueues.Close(sid)
|
||||||
|
|
||||||
// 杀进程
|
// kill processes
|
||||||
for _, p := range s.Processes {
|
for _, p := range s.Processes {
|
||||||
if err := p.Kill(); nil != err {
|
if err := p.Kill(); nil != err {
|
||||||
glog.Errorf("Can't kill process [%d] of session [%s]", p.Pid, sid)
|
glog.Errorf("Can't kill process [%d] of session [%s]", p.Pid, sid)
|
||||||
|
@ -250,7 +255,7 @@ func (sessions *Sessions) Remove(sid string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回收所有通道
|
// close websocket channels
|
||||||
if ws, ok := OutputWS[sid]; ok {
|
if ws, ok := OutputWS[sid]; ok {
|
||||||
ws.Close()
|
ws.Close()
|
||||||
delete(OutputWS, sid)
|
delete(OutputWS, sid)
|
||||||
|
@ -268,12 +273,13 @@ func (sessions *Sessions) Remove(sid string) {
|
||||||
|
|
||||||
glog.V(3).Infof("Removed a session [%s]", s.Id)
|
glog.V(3).Infof("Removed a session [%s]", s.Id)
|
||||||
|
|
||||||
cnt := 0 // 统计当前 HTTP 会话关联的 Wide 会话数量
|
cnt := 0 // count wide sessions associated with HTTP session
|
||||||
for _, s := range *sessions {
|
for _, s := range *sessions {
|
||||||
if s.HTTPSession.ID == s.HTTPSession.ID {
|
if s.HTTPSession.ID == s.HTTPSession.ID {
|
||||||
cnt++
|
cnt++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.V(3).Infof("User [%s] has [%d] sessions", s.Username, cnt)
|
glog.V(3).Infof("User [%s] has [%d] sessions", s.Username, cnt)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -281,7 +287,7 @@ func (sessions *Sessions) Remove(sid string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 username 指定的用户的所有 Wide 会话.
|
// GetByUsername gets wide sessions.
|
||||||
func (sessions *Sessions) GetByUsername(username string) []*WideSession {
|
func (sessions *Sessions) GetByUsername(username string) []*WideSession {
|
||||||
mutex.Lock()
|
mutex.Lock()
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package session
|
package session
|
||||||
|
|
||||||
// TODO: 这个文件内的功能目前没有使用,只是开了个头 :p
|
// TODO: this file not used currently, just a beginning :p
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
|
@ -18,12 +18,12 @@ import (
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Shell 通道.
|
// Shell channel.
|
||||||
//
|
//
|
||||||
// <sid, *util.WSChannel>>
|
// <sid, *util.WSChannel>>
|
||||||
var ShellWS = map[string]*util.WSChannel{}
|
var ShellWS = map[string]*util.WSChannel{}
|
||||||
|
|
||||||
// Shell 首页.
|
// IndexHandler handles request of Shell index.
|
||||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
|
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
|
||||||
httpSession.Save(r, w)
|
httpSession.Save(r, w)
|
||||||
|
|
||||||
// 创建一个 Wide 会话
|
// create a wide session
|
||||||
wideSession := session.WideSessions.New(httpSession)
|
wideSession := session.WideSessions.New(httpSession)
|
||||||
|
|
||||||
username := httpSession.Values["username"].(string)
|
username := httpSession.Values["username"].(string)
|
||||||
|
@ -61,7 +61,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
t.Execute(w, model)
|
t.Execute(w, model)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 建立 Shell 通道.
|
// WSHandler handles request of creating Shell channel.
|
||||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||||
username := httpSession.Values["username"].(string)
|
username := httpSession.Values["username"].(string)
|
||||||
|
@ -122,12 +122,10 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新通道最近使用时间
|
wsChan.Refresh()
|
||||||
wsChan.Time = time.Now()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 以管道方式执行多个命令.
|
|
||||||
func pipeCommands(username string, commands ...*exec.Cmd) string {
|
func pipeCommands(username string, commands ...*exec.Cmd) string {
|
||||||
for i, command := range commands[:len(commands)-1] {
|
for i, command := range commands[:len(commands)-1] {
|
||||||
setCmdEnv(command, username)
|
setCmdEnv(command, username)
|
||||||
|
@ -147,7 +145,7 @@ func pipeCommands(username string, commands ...*exec.Cmd) string {
|
||||||
|
|
||||||
out, err := last.CombinedOutput()
|
out, err := last.CombinedOutput()
|
||||||
|
|
||||||
// 结束进程,释放资源
|
// release resources
|
||||||
for _, command := range commands[:len(commands)-1] {
|
for _, command := range commands[:len(commands)-1] {
|
||||||
command.Wait()
|
command.Wait()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// 工具.
|
// Utilities.
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -8,10 +8,10 @@ import (
|
||||||
|
|
||||||
type mynet struct{}
|
type mynet struct{}
|
||||||
|
|
||||||
// 网络工具.
|
// Network utilities.
|
||||||
var Net = mynet{}
|
var Net = mynet{}
|
||||||
|
|
||||||
// 获取第一块网卡的 IP 地址.
|
// LocalIP gets the first NIC's IP address.
|
||||||
func (*mynet) LocalIP() (string, error) {
|
func (*mynet) LocalIP() (string, error) {
|
||||||
tt, err := net.Interfaces()
|
tt, err := net.Interfaces()
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,15 @@ import (
|
||||||
|
|
||||||
type myos struct{}
|
type myos struct{}
|
||||||
|
|
||||||
// 操作系统工具.
|
// OS utilities.
|
||||||
var OS = myos{}
|
var OS = myos{}
|
||||||
|
|
||||||
// 判断是否是 Windows 操作系统.
|
// IsWindows determines whether current OS is Windows.
|
||||||
func (*myos) IsWindows() bool {
|
func (*myos) IsWindows() bool {
|
||||||
return "windows" == runtime.GOOS
|
return "windows" == runtime.GOOS
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前执行程序的工作目录的绝对路径.
|
// Pwd gets the path of current working directory.
|
||||||
func (*myos) Pwd() string {
|
func (*myos) Pwd() string {
|
||||||
file, _ := exec.LookPath(os.Args[0])
|
file, _ := exec.LookPath(os.Args[0])
|
||||||
pwd, _ := filepath.Abs(file)
|
pwd, _ := filepath.Abs(file)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// panic 恢复.
|
// Recover recovers a panic.
|
||||||
func Recover() {
|
func Recover() {
|
||||||
if re := recover(); re != nil {
|
if re := recover(); re != nil {
|
||||||
glog.Errorf("PANIC RECOVERED:\n %v, %s", re, debug.Stack())
|
glog.Errorf("PANIC RECOVERED:\n %v, %s", re, debug.Stack())
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP 返回 JSON 统一处理.
|
// RetJSON writes HTTP response with "Content-Type, application/json".
|
||||||
func RetJSON(w http.ResponseWriter, r *http.Request, res map[string]interface{}) {
|
func RetJSON(w http.ResponseWriter, r *http.Request, res map[string]interface{}) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
|
|
@ -7,20 +7,20 @@ import (
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 一个用户会话的 WebSocket 通道结构.
|
// WebSocket channel.
|
||||||
type WSChannel struct {
|
type WSChannel struct {
|
||||||
Sid string // 用户会话 id
|
Sid string // wide session id
|
||||||
Conn *websocket.Conn // WebSocket 连接
|
Conn *websocket.Conn // websocket connection
|
||||||
Request *http.Request // 关联的 HTTP 请求
|
Request *http.Request // HTTP request related
|
||||||
Time time.Time // 该通道最近一次使用时间
|
Time time.Time // the latest use time
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭通道.
|
// Close closed the channel.
|
||||||
func (c *WSChannel) Close() {
|
func (c *WSChannel) Close() {
|
||||||
c.Conn.Close()
|
c.Conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh refreshes the channel by updating its Time.
|
// Refresh refreshes the channel by updating its use time.
|
||||||
func (c *WSChannel) Refresh() {
|
func (c *WSChannel) Refresh() {
|
||||||
c.Time = time.Now()
|
c.Time = time.Now()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue