Compare commits

..

1 Commits

Author SHA1 Message Date
langhuihui
95657bd6df 增强对实例的控制 2020-02-13 17:41:39 +08:00
11 changed files with 204 additions and 47 deletions

104
main.go
View File

@@ -6,6 +6,7 @@ import (
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"mime"
@@ -14,6 +15,7 @@ import (
"os/exec"
"os/user"
"path"
"regexp"
"runtime"
"strings"
@@ -44,7 +46,8 @@ func main() {
}
addr := flag.String("port", "8000", "http server port")
flag.Parse()
http.HandleFunc("/instance/import", importInstance)
http.HandleFunc("/instance/updateConfig", updateConfig)
http.HandleFunc("/instance/list", listInstance)
http.HandleFunc("/instance/create", initInstance)
http.HandleFunc("/instance/restart", restartInstance)
@@ -56,6 +59,70 @@ func main() {
}
}
func importInstance(w http.ResponseWriter, r *http.Request) {
var e error
defer func() {
result := "success"
if e != nil {
result = e.Error()
}
w.Write([]byte(result))
}()
name := r.URL.Query().Get("name")
if importPath := r.URL.Query().Get("path"); importPath != "" {
f, err := os.Open(importPath)
if e = err; err != nil {
return
}
children, err := f.Readdir(0)
if e = err; err == nil {
var hasMain, hasConfig, hasMod, hasRestart bool
for _, child := range children {
switch child.Name() {
case "main.go":
hasMain = true
case "config.toml":
hasConfig = true
case "go.mod":
hasMod = true
case "restart.sh":
hasRestart = true
}
}
if hasMain && hasConfig && hasMod && hasRestart {
if name == "" {
_, name = path.Split(importPath)
}
config, err := ioutil.ReadFile(path.Join(importPath, "config.toml"))
if e = err; err != nil {
return
}
mainGo, err := ioutil.ReadFile(path.Join(importPath, "main.go"))
if e = err; err != nil {
return
}
reg, err := regexp.Compile("_ \"(.+)\"")
if e = err; err != nil {
return
}
instances[name] = &InstanceDesc{
Name: name,
Path: importPath,
Plugins: nil,
Config: string(config),
}
for _, m := range reg.FindAllStringSubmatch(string(mainGo), -1) {
instances[name].Plugins = append(instances[name].Plugins, m[1])
}
} else {
e = errors.New("路径中缺少文件")
}
}
} else {
w.Write([]byte("参数错误"))
}
}
func readInstances() error {
if homeDir, err := Home(); err == nil {
instancesDir = path.Join(homeDir, ".monibuca")
@@ -160,18 +227,18 @@ func restartInstance(w http.ResponseWriter, r *http.Request) {
needBuild := r.URL.Query().Get("build") != ""
if instance, ok := instances[instanceName]; ok {
if needUpdate {
if err := instance.writeExecSSE(sse, exec.Command("go", "get", "-u")); err != nil {
if err := sse.WriteExec(instance.command("go", "get", "-u")); err != nil {
sse.WriteEvent("failed", []byte(err.Error()))
return
}
}
if needBuild {
if err := instance.writeExecSSE(sse, exec.Command("go", "build")); err != nil {
if err := sse.WriteExec(instance.command("go", "build")); err != nil {
sse.WriteEvent("failed", []byte(err.Error()))
return
}
}
if err := instance.writeExecSSE(sse, exec.Command("sh", "restart.sh")); err != nil {
if err := sse.WriteExec(instance.command("sh", "restart.sh")); err != nil {
sse.WriteEvent("failed", []byte(err.Error()))
return
}
@@ -180,10 +247,7 @@ func restartInstance(w http.ResponseWriter, r *http.Request) {
sse.WriteEvent("failed", []byte("no such instance"))
}
}
func (p *InstanceDesc) writeExecSSE(sse *util.SSE, cmd *exec.Cmd) error {
cmd.Dir = p.Path
return sse.WriteExec(cmd)
}
func (p *InstanceDesc) command(name string, args ...string) (cmd *exec.Cmd) {
cmd = exec.Command(name, args...)
cmd.Dir = p.Path
@@ -223,12 +287,12 @@ func main(){
return
}
sse.WriteEvent("step", []byte("3:文件创建成功!"))
err = p.writeExecSSE(sse, exec.Command("go", "mod", "init", p.Name))
err = sse.WriteExec(p.command("go", "mod", "init", p.Name))
if err != nil {
return
}
sse.WriteEvent("step", []byte("4:go mod 初始化完成!"))
err = p.writeExecSSE(sse, exec.Command("go", "build"))
err = sse.WriteExec(p.command("go", "build"))
if err != nil {
return
}
@@ -243,7 +307,25 @@ func main(){
if err != nil {
return
}
return p.writeExecSSE(sse, exec.Command("sh", "restart.sh"))
return sse.WriteExec(p.command("sh", "restart.sh"))
}
func updateConfig(w http.ResponseWriter, r *http.Request) {
instanceName := r.URL.Query().Get("instance")
if instance, ok := instances[instanceName]; ok {
f, err := os.OpenFile(path.Join(instance.Path, "config.toml"), os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
w.Write([]byte(err.Error()))
return
}
_, err = io.Copy(f, r.Body)
if err != nil {
w.Write([]byte(err.Error()))
return
}
w.Write([]byte("success"))
} else {
w.Write([]byte("no such instance"))
}
}
func Home() (string, error) {
user, err := user.Current()

View File

@@ -10,7 +10,7 @@ import (
)
var ConfigRaw []byte
var Version = "0.2.3"
var Version = "0.2.4"
var EngineInfo = &struct {
Version string
StartTime time.Time

2
pm/dist/index.html vendored
View File

@@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>Monibuca Instance Manager</title><script src=ajax.js></script><link href=/css/app.200d2f8f.css rel=preload as=style><link href=/css/chunk-vendors.22ebf426.css rel=preload as=style><link href=/js/app.9b5890f5.js rel=preload as=script><link href=/js/chunk-vendors.f701a5a3.js rel=preload as=script><link href=/css/chunk-vendors.22ebf426.css rel=stylesheet><link href=/css/app.200d2f8f.css rel=stylesheet></head><body><noscript><strong>We're sorry but pm doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.f701a5a3.js></script><script src=/js/app.9b5890f5.js></script></body></html>
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>Monibuca Instance Manager</title><script src=ajax.js></script><link href=/css/app.200d2f8f.css rel=preload as=style><link href=/css/chunk-vendors.22ebf426.css rel=preload as=style><link href=/js/app.fab2a06f.js rel=preload as=script><link href=/js/chunk-vendors.f701a5a3.js rel=preload as=script><link href=/css/chunk-vendors.22ebf426.css rel=stylesheet><link href=/css/app.200d2f8f.css rel=stylesheet></head><body><noscript><strong>We're sorry but pm doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.f701a5a3.js></script><script src=/js/app.fab2a06f.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
pm/dist/js/app.fab2a06f.js vendored Normal file

File diff suppressed because one or more lines are too long

1
pm/dist/js/app.fab2a06f.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
<template>
<Modal v-bind="$attrs" v-on="$listeners" :title="info.Path">
<Modal v-bind="$attrs" v-on="$listeners" :title="info && info.Path">
<Steps :current="currentStep" size="small" :status="status">
<Step title="解析请求"></Step>
<Step title="创建目录"></Step>

View File

@@ -0,0 +1,32 @@
<template>
<div>
<i-input prefix="ios-home" v-model="instancePath" placeholder="输入实例所在的路径" search enter-button="Import" @on-search="doImport">
</i-input>
</div>
</template>
<script>
export default {
name: "ImportInstance",
data(){
return {
instancePath:""
}
},
methods:{
doImport(){
window.ajax.get("/instance/import?path="+this.instancePath).then(x=>{
if(x=="success"){
this.$Message.success("导入成功!")
}else{
this.$Message.error(x)
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,30 +1,39 @@
<template>
<List border>
<ListItem v-for="item in instances" :key="item.Name">
<ListItemMeta :title="item.Name" :description="item.Path"></ListItemMeta>
<template v-if="hasGateway(item)">
<div>
<List border>
<ListItem v-for="item in instances" :key="item.Name">
<ListItemMeta :title="item.Name" :description="item.Path"></ListItemMeta>
{{item.Info}}
</template>
<template slot="action">
<li v-if="hasGateway(item)" @click="window.open(gateWayHref(item),'_blank')">
<Icon type="md-browsers"/>
管理界面
</li>
<li @click="restart(item)">
<Icon type="ios-refresh"/>
重启
</li>
<li @click="shutdown(item)">
<Icon type="ios-power"/>
关闭
</li>
</template>
</ListItem>
<Modal v-model="showRestart">
<template slot="action">
<li @click="changeConfig(item)">
<Icon type="ios-settings"/>
修改配置
</li>
<li v-if="hasGateway(item)" @click="window.open(gateWayHref(item),'_blank')">
<Icon type="md-browsers"/>
管理界面
</li>
<li @click="currentItem=item,showRestart=true">
<Icon type="ios-refresh"/>
重启
</li>
<li @click="shutdown(item)">
<Icon type="ios-power"/>
关闭
</li>
</template>
</ListItem>
</List>
<Modal v-model="showRestart" title="重启选项" @on-ok="restart">
<Checkbox v-model="update">go get -u</Checkbox>
<Checkbox v-model="build">go build</Checkbox>
</Modal>
</List>
<Modal v-model="showConfig" title="修改实例配置" @on-ok="submitConfigChange">
<i-input type="textarea" v-model="currentConfig" :rows="20">
</i-input>
</Modal>
</div>
</template>
<script>
@@ -33,7 +42,15 @@
export default {
name: "InstanceList",
data() {
return {instances: {}, showRestart: false, update: false, build: false}
return {
instances: [],
showRestart: false,
update: false,
build: false,
showConfig: false,
currentItem: null,
currentConfig: ""
}
},
mounted() {
window.ajax.getJSON("/instance/list").then(x => {
@@ -49,17 +66,41 @@
} else {
instance.Info = "实例未配置网关插件"
}
this.instances.push(instance)
}
this.instances = x;
// this.instances = x;
});
}, methods: {
},
methods: {
changeConfig(item) {
this.showConfig = true
this.currentItem = item
this.currentConfig = toml.stringify(item.Config)
},
submitConfigChange() {
try {
this.currentItem.Config = toml.parse(this.currentConfig)
window.ajax.post("/instance/updateConfig?instance=" + this.currentItem.Name, this.currentConfig).then(x => {
if (x == "success") {
this.$Message.success("更新成功!")
} else {
this.$Message.error(x)
}
}).catch(e => {
this.$Message.error(e)
})
} catch (e) {
this.$Message.error(e)
}
},
hasGateway(item) {
return item.Config.Plugins.hasOwnProperty("GateWay")
},
gateWayHref(item) {
return location.hostname + ":" + item.Config.Plugins.GateWay.split(":").pop()
return location.hostname + ":" + item.Config.Plugins.GateWay.ListenAddr.split(":").pop()
},
restart(item) {
restart() {
let item = this.currentItem
const msg = this.$Message.loading({
content: 'restart ' + item.Name + '...',
duration: 0

View File

@@ -79,7 +79,9 @@
</ButtonGroup>
</div>
</TabPane>
<TabPane label="导入" name="name3"></TabPane>
<TabPane label="导入" name="name3">
<ImportInstance></ImportInstance>
</TabPane>
</Tabs>
</Content>
<Modal v-model="showAddPlugin" title="添加Plugin" @on-ok="addPlugin">
@@ -124,11 +126,11 @@
<script>
import CreateInstance from "../components/CreateInstance";
import InstanceList from "../components/InstanceList";
import ImportInstance from "../components/ImportInstance";
export default {
components: {
CreateInstance,InstanceList
CreateInstance,InstanceList,ImportInstance
},
data() {
return {