Compare commits

...

3 Commits

Author SHA1 Message Date
langhuihui
5563ddc0d2 增强对实例的控制 2020-02-14 09:54:53 +08:00
langhuihui
95657bd6df 增强对实例的控制 2020-02-13 17:41:39 +08:00
langhuihui
b9e19e75c8 增强对实例的控制 2020-02-13 10:43:32 +08:00
20 changed files with 505 additions and 315 deletions

137
main.go
View File

@@ -6,6 +6,7 @@ import (
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"mime" "mime"
@@ -14,6 +15,7 @@ import (
"os/exec" "os/exec"
"os/user" "os/user"
"path" "path"
"regexp"
"runtime" "runtime"
"strings" "strings"
@@ -44,10 +46,12 @@ func main() {
} }
addr := flag.String("port", "8000", "http server port") addr := flag.String("port", "8000", "http server port")
flag.Parse() flag.Parse()
http.HandleFunc("/list", listInstance) http.HandleFunc("/instance/import", importInstance)
http.HandleFunc("/create", initInstance) http.HandleFunc("/instance/updateConfig", updateConfig)
http.HandleFunc("/upgrade/engine", upgradeEngine) http.HandleFunc("/instance/list", listInstance)
http.HandleFunc("/restart/instance", restartInstance) http.HandleFunc("/instance/create", initInstance)
http.HandleFunc("/instance/restart", restartInstance)
http.HandleFunc("/instance/shutdown", shutdownInstance)
http.HandleFunc("/", website) http.HandleFunc("/", website)
fmt.Printf("start listen at %s", *addr) fmt.Printf("start listen at %s", *addr)
if err := http.ListenAndServe(":"+*addr, nil); err != nil { if err := http.ListenAndServe(":"+*addr, nil); err != nil {
@@ -55,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 { func readInstances() error {
if homeDir, err := Home(); err == nil { if homeDir, err := Home(); err == nil {
instancesDir = path.Join(homeDir, ".monibuca") instancesDir = path.Join(homeDir, ".monibuca")
@@ -140,35 +208,50 @@ func initInstance(w http.ResponseWriter, r *http.Request) {
} }
instances[instanceDesc.Name] = instanceDesc instances[instanceDesc.Name] = instanceDesc
} }
func upgradeEngine(w http.ResponseWriter, r *http.Request) { func shutdownInstance(w http.ResponseWriter, r *http.Request) {
sse := util.NewSSE(w, r.Context())
instanceName := r.URL.Query().Get("instance") instanceName := r.URL.Query().Get("instance")
if instance, ok := instances[instanceName]; ok { if instance, ok := instances[instanceName]; ok {
if err := instance.writeExecSSE(sse, exec.Command("go", "get", "-u", "github.com/langhuihui/monibuca/monica")); err != nil { if err := instance.command("kill", "-9", "`cat pid`").Run(); err == nil {
sse.WriteEvent("failed", []byte(err.Error())) w.Write([]byte("success"))
} else { } else {
sse.Write([]byte("success")) w.Write([]byte(err.Error()))
} }
} else { } else {
sse.WriteEvent("failed", []byte("no such instance")) w.Write([]byte("no such instance"))
} }
} }
func restartInstance(w http.ResponseWriter, r *http.Request) { func restartInstance(w http.ResponseWriter, r *http.Request) {
sse := util.NewSSE(w, r.Context()) sse := util.NewSSE(w, r.Context())
instanceName := r.URL.Query().Get("instance") instanceName := r.URL.Query().Get("instance")
needUpdate := r.URL.Query().Get("update") != ""
needBuild := r.URL.Query().Get("build") != ""
if instance, ok := instances[instanceName]; ok { if instance, ok := instances[instanceName]; ok {
if err := instance.writeExecSSE(sse, exec.Command("sh", "restart.sh")); err != nil { if needUpdate {
if err := sse.WriteExec(instance.command("go", "get", "-u")); err != nil {
sse.WriteEvent("failed", []byte(err.Error())) sse.WriteEvent("failed", []byte(err.Error()))
} else { return
sse.Write([]byte("success"))
} }
}
if needBuild {
if err := sse.WriteExec(instance.command("go", "build")); err != nil {
sse.WriteEvent("failed", []byte(err.Error()))
return
}
}
if err := sse.WriteExec(instance.command("sh", "restart.sh")); err != nil {
sse.WriteEvent("failed", []byte(err.Error()))
return
}
sse.Write([]byte("success"))
} else { } else {
sse.WriteEvent("failed", []byte("no such instance")) sse.WriteEvent("failed", []byte("no such instance"))
} }
} }
func (p *InstanceDesc) writeExecSSE(sse *util.SSE, cmd *exec.Cmd) error {
func (p *InstanceDesc) command(name string, args ...string) (cmd *exec.Cmd) {
cmd = exec.Command(name, args...)
cmd.Dir = p.Path cmd.Dir = p.Path
return sse.WriteExec(cmd) return
} }
func (p *InstanceDesc) createDir(sse *util.SSE, clearDir bool) (err error) { func (p *InstanceDesc) createDir(sse *util.SSE, clearDir bool) (err error) {
if clearDir { if clearDir {
@@ -204,12 +287,12 @@ func main(){
return return
} }
sse.WriteEvent("step", []byte("3:文件创建成功!")) 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 { if err != nil {
return return
} }
sse.WriteEvent("step", []byte("4:go mod 初始化完成!")) 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 { if err != nil {
return return
} }
@@ -224,7 +307,25 @@ func main(){
if err != nil { if err != nil {
return 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) { func Home() (string, error) {
user, err := user.Current() user, err := user.Current()

View File

@@ -4,12 +4,17 @@ import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"log" "log"
"time"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
) )
var ConfigRaw []byte var ConfigRaw []byte
var Version = "0.2.2" var Version = "0.2.5"
var EngineInfo = &struct {
Version string
StartTime time.Time
}{Version, time.Now()}
func Run(configFile string) (err error) { func Run(configFile string) (err error) {
log.Printf("start monibuca version:%s", Version) log.Printf("start monibuca version:%s", Version)

View File

@@ -1,28 +1,27 @@
package QoS package QoS
import ( import (
"strings"
. "github.com/langhuihui/monibuca/monica" . "github.com/langhuihui/monibuca/monica"
) )
var ( // var (
selectMap = map[string][]string{ // selectMap = map[string][]string{
"low": {"low", "medium", "high"}, // "low": {"low", "medium", "high"},
"medium": {"medium", "low", "high"}, // "medium": {"medium", "low", "high"},
"high": {"high", "medium", "low"}, // "high": {"high", "medium", "low"},
} // }
) // )
func getQualityName(name string, qualityLevel string) string { // func getQualityName(name string, qualityLevel string) string {
if qualityLevel == "" { // for _, l := range selectMap[qualityLevel] {
return name // if _, ok := AllRoom.Load(name + "/" + l); ok {
} // return name + "/" + l
for _, l := range selectMap[qualityLevel] { // }
if _, ok := AllRoom.Load(name + "/" + l); ok { // }
return name + "/" + l // return name + "/" + qualityLevel
} // }
}
return name + "/" + qualityLevel
}
var config = struct { var config = struct {
Suffix []string Suffix []string
@@ -39,8 +38,23 @@ func init() {
func run() { func run() {
OnDropHooks.AddHook(func(s *OutputStream) { OnDropHooks.AddHook(func(s *OutputStream) {
if s.TotalDrop > s.TotalPacket>>2 { if s.TotalDrop > s.TotalPacket>>2 {
//TODO var newStreamPath = ""
//s.Control<-&ChangeRoomCmd{s,AllRoom.Get()} for i, suf := range config.Suffix {
if strings.HasSuffix(s.StreamPath, suf) {
if i < len(config.Suffix)-1 {
newStreamPath = s.StreamPath + "/" + config.Suffix[i+1]
break
}
} else {
newStreamPath = s.StreamPath + "/" + suf
break
}
}
if newStreamPath != "" {
if _, ok := AllRoom.Load(newStreamPath); ok {
s.Control <- &ChangeRoomCmd{s, AllRoom.Get(newStreamPath)}
}
}
} }
}) })
} }

View File

@@ -16,7 +16,7 @@ import (
var ( var (
config = new(ListenerConfig) config = new(ListenerConfig)
startTime = time.Now()
dashboardPath string dashboardPath string
) )
@@ -99,7 +99,8 @@ func summary(w http.ResponseWriter, r *http.Request) {
} }
} }
func sysInfo(w http.ResponseWriter, r *http.Request) { func sysInfo(w http.ResponseWriter, r *http.Request) {
bytes, err := json.Marshal(&struct{ Version string }{Version: Version}) w.Header().Set("Access-Control-Allow-Origin", "*")
bytes, err := json.Marshal(EngineInfo)
if err == nil { if err == nil {
_, err = w.Write(bytes) _, err = w.Write(bytes)
} }

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.c617bcf5.js rel=preload as=script><link href=/js/chunk-vendors.6b87e1b5.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.6b87e1b5.js></script><script src=/js/app.c617bcf5.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.5c3b9309.js rel=preload as=script><link href=/js/chunk-vendors.f693d643.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.f693d643.js></script><script src=/js/app.5c3b9309.js></script></body></html>

2
pm/dist/js/app.5c3b9309.js vendored Normal file

File diff suppressed because one or more lines are too long

1
pm/dist/js/app.5c3b9309.js.map vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

5
pm/package-lock.json generated
View File

@@ -979,6 +979,11 @@
"@hapi/hoek": "8.5.0" "@hapi/hoek": "8.5.0"
} }
}, },
"@iarna/toml": {
"version": "2.2.3",
"resolved": "https://registry.npm.taobao.org/@iarna/toml/download/@iarna/toml-2.2.3.tgz",
"integrity": "sha1-8GC/bqr65NVqfaxhiYCDiwaW4qs="
},
"@intervolga/optimize-cssnano-plugin": { "@intervolga/optimize-cssnano-plugin": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npm.taobao.org/@intervolga/optimize-cssnano-plugin/download/@intervolga/optimize-cssnano-plugin-1.0.6.tgz", "resolved": "https://registry.npm.taobao.org/@intervolga/optimize-cssnano-plugin/download/@intervolga/optimize-cssnano-plugin-1.0.6.tgz",

View File

@@ -8,6 +8,7 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"@iarna/toml": "^2.2.3",
"core-js": "^3.4.4", "core-js": "^3.4.4",
"view-design": "^4.0.0", "view-design": "^4.0.0",
"vue": "^2.6.10", "vue": "^2.6.10",

View File

@@ -1,5 +1,5 @@
<template> <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"> <Steps :current="currentStep" size="small" :status="status">
<Step title="解析请求"></Step> <Step title="解析请求"></Step>
<Step title="创建目录"></Step> <Step title="创建目录"></Step>
@@ -30,7 +30,7 @@ export default {
start() { start() {
this.status = "process"; this.status = "process";
eventSource = new EventSource( eventSource = new EventSource(
"/create?info=" + "/instance/create?info=" +
JSON.stringify(this.info) + JSON.stringify(this.info) +
(this.clearDir ? "&clear=true" : "") (this.clearDir ? "&clear=true" : "")
); );

View File

@@ -1,60 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@@ -0,0 +1,39 @@
<template>
<div>
<i-input v-model="instanceName" :placeholder="defaultInstanceName"></i-input>
<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:"",
instanceName:""
}
},
computed:{
defaultInstanceName(){
return this.instancePath.replace(/\\/g,"/").split("/").pop()
}
},
methods:{
doImport(){
window.ajax.get("/instance/import?path="+this.instancePath+"&name="+this.instanceName).then(x=>{
if(x=="success"){
this.$Message.success("导入成功!")
}else{
this.$Message.error(x)
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,152 @@
<template>
<div>
<List border>
<ListItem v-for="item in instances" :key="item.Name">
<ListItemMeta :title="item.Name" :description="item.Path"></ListItemMeta>
{{item.Info}}
<template slot="action">
<li @click="changeConfig(item)">
<Icon type="ios-settings"/>
修改配置
</li>
<li v-if="hasGateway(item)" @click="openGateway(item)">
<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>
<Modal v-model="showConfig" title="修改实例配置" @on-ok="submitConfigChange">
<i-input type="textarea" v-model="currentConfig" :rows="20">
</i-input>
</Modal>
</div>
</template>
<script>
import toml from "@iarna/toml"
export default {
name: "InstanceList",
data() {
return {
instances: [],
showRestart: false,
update: false,
build: false,
showConfig: false,
currentItem: null,
currentConfig: ""
}
},
mounted() {
window.ajax.getJSON("/instance/list").then(x => {
for (let name in x) {
let instance = x[name]
instance.Config = toml.parse(instance.Config)
if (this.hasGateway(instance)) {
window.ajax.getJSON("//" + this.gateWayHref(instance) + "/api/sysInfo").then(x => {
instance.Info = "引擎版本:" + x.Version + "启动时间:" + x.StartTime
}).catch(() => {
instance.Info = "无法访问实例"
})
} else {
instance.Info = "实例未配置网关插件"
}
this.instances.push(instance)
}
// this.instances = x;
});
},
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)
}
},
openGateway(item){
window.open(this.gateWayHref(item),'_blank')
},
hasGateway(item) {
return item.Config.Plugins.hasOwnProperty("GateWay")
},
gateWayHref(item) {
return location.hostname + ":" + item.Config.Plugins.GateWay.ListenAddr.split(":").pop()
},
restart() {
let item = this.currentItem
const msg = this.$Message.loading({
content: 'restart ' + item.Name + '...',
duration: 0
});
let arg = item.Name
if (this.update) {
arg += "&update=true"
}
if (this.build) {
arg += "&build=true"
}
const es = new EventSource("/instance/restart?instance=" + arg)
es.onmessage = evt => {
if (evt.data == "success") {
this.$Message.success("重启成功!")
msg()
} else {
this.$Message.info(evt.data)
}
}
es.addEventListener("failed", evt => {
this.$Message.error(evt.data)
msg()
})
es.onerror = e => {
if (e) this.$Message.error(e);
msg()
es.close()
}
},
shutdown(item) {
window.ajax.get("/instance/shutdown?instance=" + item.Name).then(x => {
if (x == "success") {
this.$Message.success("已关闭实例")
} else {
this.$Message.error(x)
}
})
},
}
}
</script>
<style scoped>
</style>

View File

@@ -1,18 +0,0 @@
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'home',
components: {
HelloWorld
}
}
</script>

View File

@@ -4,19 +4,7 @@
<Content class="content"> <Content class="content">
<Tabs value="name1"> <Tabs value="name1">
<TabPane label="实例" name="name1"> <TabPane label="实例" name="name1">
<List border> <InstanceList></InstanceList>
<ListItem v-for="item in instances" :key="item.Name">
<ListItemMeta :title="item.Name" :description="item.Path"></ListItemMeta>
<template slot="action">
<li @click="restart(item)">
<Icon type="ios-refresh" />重启
</li>
<li @click="upgrade(item)">
<Icon type="ios-sync" />升级引擎
</li>
</template>
</ListItem>
</List>
</TabPane> </TabPane>
<TabPane label="创建" name="name2"> <TabPane label="创建" name="name2">
<Steps :current="createStep"> <Steps :current="createStep">
@@ -34,7 +22,8 @@
{{item.Config}} {{item.Config}}
<template slot="action"> <template slot="action">
<li @click="removePlugin(name)"> <li @click="removePlugin(name)">
<Icon type="ios-trash" />移除 <Icon type="ios-trash"/>
移除
</li> </li>
</template> </template>
</ListItem> </ListItem>
@@ -65,7 +54,8 @@
@click="createStep--" @click="createStep--"
v-if="createStep!=0" v-if="createStep!=0"
> >
<Icon type="ios-arrow-back"></Icon>上一步 <Icon type="ios-arrow-back"></Icon>
上一步
</Button> </Button>
<Button <Button
size="large" size="large"
@@ -89,7 +79,9 @@
</ButtonGroup> </ButtonGroup>
</div> </div>
</TabPane> </TabPane>
<TabPane label="导入" name="name3"></TabPane> <TabPane label="导入" name="name3">
<ImportInstance></ImportInstance>
</TabPane>
</Tabs> </Tabs>
</Content> </Content>
<Modal v-model="showAddPlugin" title="添加Plugin" @on-ok="addPlugin"> <Modal v-model="showAddPlugin" title="添加Plugin" @on-ok="addPlugin">
@@ -120,7 +112,8 @@
<ListItemMeta :title="name" :description="item"></ListItemMeta> <ListItemMeta :title="name" :description="item"></ListItemMeta>
<template slot="action"> <template slot="action">
<li @click="addBuiltin(name,item)"> <li @click="addBuiltin(name,item)">
<Icon type="ios-add" />添加 <Icon type="ios-add"/>
添加
</li> </li>
</template> </template>
</ListItem> </ListItem>
@@ -131,11 +124,13 @@
</template> </template>
<script> <script>
import CreateInstance from "../components/CreateInstance"; import CreateInstance from "../components/CreateInstance";
import InstanceList from "../components/InstanceList";
import ImportInstance from "../components/ImportInstance";
export default { export default {
components: { components: {
CreateInstance CreateInstance,InstanceList,ImportInstance
}, },
data() { data() {
return { return {
@@ -144,7 +139,6 @@ export default {
showCreate: false, showCreate: false,
createInfo: null, createInfo: null,
createPath: "/opt/monibuca", createPath: "/opt/monibuca",
instances: {},
plugins: {}, plugins: {},
showAddPlugin: false, showAddPlugin: false,
formPlugin: {}, formPlugin: {},
@@ -192,12 +186,9 @@ ${x.Config || ""}`
); );
} }
}, },
mounted() {
window.ajax.getJSON("/list").then(x => {
this.instances = x;
});
},
methods: { methods: {
goUp() { goUp() {
let paths = this.createPath.split("/"); let paths = this.createPath.split("/");
paths.pop(); paths.pop();
@@ -226,69 +217,21 @@ ${x.Config || ""}`
this.formPlugin.Config = this.defaultConfig[name]; this.formPlugin.Config = this.defaultConfig[name];
this.showBuiltinPlugin = false; this.showBuiltinPlugin = false;
}, },
restart(item){
const msg = this.$Message.loading({
content: 'restart '+item.Name+'...',
duration: 0
});
var es = new EventSource("/restart/instance?instance="+item.Name)
es.onmessage = evt => {
if(evt.data=="success"){
this.$Message.success("重启成功!")
msg()
}else{
this.$Message.info(evt.data)
} }
} };
es.addEventListener("failed",evt=>{
this.$Message.error(evt.data)
msg()
})
es.onerror = e => {
this.$Message.error(e);
msg()
es.close()
}
},
upgrade(item){
const msg = this.$Message.loading({
content: 'upgrade '+item.Name+'...',
duration: 0
});
var es = new EventSource("/upgrade/engine?instance="+item.Name)
es.onmessage = evt => {
if(evt.data=="success"){
this.$Message.success("更新完毕")
msg()
}else{
this.$Message.info(evt.data)
}
}
es.addEventListener("failed",evt=>{
this.$Message.error(evt.data)
msg()
})
es.onerror = e => {
this.$Message.error(e);
msg()
es.close()
}
}
}
};
</script> </script>
<style> <style>
.content { .content {
background: white; background: white;
} }
pre { pre {
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
} }
.ivu-tabs .ivu-tabs-tabpane { .ivu-tabs .ivu-tabs-tabpane {
padding: 20px; padding: 20px;
} }
</style> </style>