Compare commits

...

4 Commits

Author SHA1 Message Date
langhuihui
709c2c6ac7 增加目录候选功能 2020-02-17 11:48:58 +08:00
langhuihui
f96bc11ddb 增强对实例的控制 2020-02-15 21:07:47 +08:00
langhuihui
5563ddc0d2 增强对实例的控制 2020-02-14 09:54:53 +08:00
langhuihui
95657bd6df 增强对实例的控制 2020-02-13 17:41:39 +08:00
20 changed files with 483 additions and 133 deletions

126
main.go
View File

@@ -6,6 +6,7 @@ import (
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"mime"
@@ -14,6 +15,8 @@ import (
"os/exec"
"os/user"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
@@ -44,7 +47,9 @@ func main() {
}
addr := flag.String("port", "8000", "http server port")
flag.Parse()
http.HandleFunc("/instance/listDir", listDir)
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 +61,88 @@ func main() {
}
}
func listDir(w http.ResponseWriter, r *http.Request) {
if input := r.URL.Query().Get("input"); input != "" {
if dir, err := os.Open(filepath.Dir(input)); err == nil {
var dirs []string
if infos, err := dir.Readdir(0); err == nil {
for _, info := range infos {
if info.IsDir() {
dirs = append(dirs, info.Name())
}
}
if bytes, err := json.Marshal(dirs); err == nil {
w.Write(bytes)
}
}
}
}
}
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 +247,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 +267,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 +307,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
}
@@ -238,12 +322,30 @@ func main(){
binFile := strings.TrimSuffix(p.Path, "/")
_, binFile = path.Split(binFile)
build.WriteString(binFile)
build.WriteString(" > log.txt & echo $! > pid\n")
build.WriteString(" & echo $! > pid\n")
err = ioutil.WriteFile(path.Join(p.Path, "restart.sh"), build.Bytes(), 0777)
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.6"
var EngineInfo = &struct {
Version string
StartTime time.Time

View File

@@ -99,6 +99,7 @@ func summary(w http.ResponseWriter, r *http.Request) {
}
}
func sysInfo(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
bytes, err := json.Marshal(EngineInfo)
if err == nil {
_, err = w.Write(bytes)

View File

@@ -0,0 +1,78 @@
package logrotate
import (
"fmt"
. "github.com/langhuihui/monibuca/monica"
"log"
"os"
"path"
"time"
)
var config = new(LogRotate)
type LogRotate struct {
Path string
Size int64
Days int
file *os.File
currentSize int64
createTime time.Time
hours float64
splitFunc func() bool
}
func init() {
InstallPlugin(&PluginConfig{
Name: "LogRotate",
Type: PLUGIN_HOOK,
Config: config,
Run: run,
})
}
func run() {
if config.Size > 0 {
config.splitFunc = config.splitBySize
} else {
if config.Days == 0 {
config.Days = 1
}
config.hours = float64(config.Days) * 24
config.splitFunc = config.splitByTime
}
config.createTime = time.Now()
err := os.MkdirAll(config.Path, 0666)
config.file, err = os.OpenFile(path.Join(config.Path, fmt.Sprintf("%s.log", config.createTime.Format("2006-01-02T15:04:05"))), os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0666)
if err == nil {
stat, _ := config.file.Stat()
config.currentSize = stat.Size()
AddWriter(config)
} else {
log.Println(err)
}
}
func (l *LogRotate) splitBySize() bool {
return l.currentSize >= l.Size
}
func (l *LogRotate) splitByTime() bool {
return time.Since(l.createTime).Hours() > l.hours
}
func (l *LogRotate) Write(data []byte) (n int, err error) {
n, err = l.file.Write(data)
l.currentSize += int64(n)
if err == nil {
if l.splitFunc() {
l.createTime = time.Now()
if file, err := os.OpenFile(path.Join(l.Path, fmt.Sprintf("%s.log", l.createTime.Format("2006-01-02T15:04:05"))), os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0666); err == nil {
l.file = file
l.currentSize = 0
}
}
}
return
}
//func (l *LogRotate) FindLog(grep string) string{
// cmd:=exec.Command("grep",fmt.Sprintf("\"%s\"",grep),l.Path)
// err:=cmd.Run()
//}

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.13e2de5f.js rel=preload as=script><link href=/js/chunk-vendors.2e3b192a.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.2e3b192a.js></script><script src=/js/app.13e2de5f.js></script></body></html>

2
pm/dist/js/app.13e2de5f.js vendored Normal file

File diff suppressed because one or more lines are too long

1
pm/dist/js/app.13e2de5f.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

View File

@@ -38,7 +38,7 @@
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"rules": {"no-console": "off"},
"parserOptions": {
"parser": "babel-eslint"
}

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,47 @@
<template>
<div>
<PathSelector v-model="instancePath" placeholder="输入实例所在的路径"></PathSelector>
<i-input style="width: 300px;margin:40px auto" v-model="instanceName" :placeholder="defaultInstanceName" search enter-button="Import" @on-search="doImport">
<span slot="prepend">实例名称</span>
</i-input>
</div>
</template>
<script>
import PathSelector from "./PathSelector"
export default {
name: "ImportInstance",
components:{
PathSelector
},
data(){
return {
instancePath:"",
instanceName:""
}
},
computed:{
defaultInstanceName(){
let path = this.instancePath.replace(/\\/g,"/")
let s = path.split("/")
if(path.endsWith("/")) s.pop()
return s.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

@@ -1,39 +1,60 @@
<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)">
{{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">
<div>
<List border>
<ListItem v-for="item in instances" :key="item.Name">
<ListItemMeta :title="item.Name" :description="item.Path"></ListItemMeta>
<template v-if="item.Info.StartTime">
引擎版本{{item.Info.Version}} <br>启动时间
<StartTime :value="item.Info.StartTime"></StartTime>
</template>
<template v-else>{{item.Info}}</template>
<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>
</List>
<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"
import StartTime from "./StartTime"
export default {
name: "InstanceList",
components: {StartTime},
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 => {
@@ -41,25 +62,52 @@
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
window.ajax.getJSON(this.gateWayHref(instance) + "/api/sysInfo").then(x => {
instance.Info = x
}).catch(() => {
instance.Info = "无法访问实例"
})
} 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)
}
},
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.split(":").pop()
return "http://" + 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
@@ -85,7 +133,7 @@
msg()
})
es.onerror = e => {
if (e) this.$Message.error(e);
if (e && e.toString()) this.$Message.error(e);
msg()
es.close()
}

View File

@@ -0,0 +1,66 @@
<template>
<div>
<i-input ref="input" v-bind="$attrs" v-on="$listeners" clearable @on-change="onInput">
<Button slot="prepend" icon="md-arrow-round-up" @click="goUp"></Button>
</i-input>
<CellGroup @on-click="onSelectCand">
<Cell v-for="item in candidate" :key="item" :title="item" :name="item"></Cell>
</CellGroup>
</div>
</template>
<script>
export default {
name: "PathSelector",
data() {
return {
candidate: [],
lastInput: "",
searching: false,
}
},
methods: {
dir(){
let paths = this.$refs.input.value.split("/");
paths.pop();
return paths.join("/");
},
goUp() {
this.lastInput = this.$attrs.value = this.dir()
this.$refs.input.$emit('input', this.$attrs.value)
this.search(this.lastInput)
},
onSelectCand(name) {
this.lastInput = this.$attrs.value = this.dir()+"/"+name+"/"
this.$refs.input.$emit('input', this.$attrs.value)
this.search(this.lastInput)
},
onInput(evt) {
this.lastInput = evt.target.value
this.search(this.lastInput)
},
search(v) {
if(this.searching)return
window.ajax.getJSON("/instance/listDir?input=" + v).then(x => {
this.candidate = x
if (this.lastInput != v) {
this.search(this.lastInput)
}else{
this.searching = false
}
}).catch(e => {
this.$Message.error(e)
if (this.lastInput != v) {
this.search(this.lastInput)
}else{
this.searching = false
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,18 @@
<template>
<Poptip trigger="hover" :content="'⌚️'+ new Date(value).toLocaleString()">
<Time :time="new Date(value)"></Time>
</Poptip>
</template>
<script>
export default {
name: "StartTime",
props:{
value:String
}
}
</script>
<style scoped>
</style>

View File

@@ -1,10 +1,40 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
defaultPlugins:{
GateWay:[
"gateway",'ListenAddr = ":8081"',"网关插件提供各种API服务包括信息采集和控制等控制台页面展示静态资源服务器"
],
LogRotate:[
"logrotate",`Path = "log"
Size = 0
Days = 1`,"日志分割插件Size 代表按照字节数分割0代表采用时间分割"
],
Jessica:[
"jessica",'ListenAddr = ":8080"',"WebSocket协议订阅采用私有协议搭配Jessibuca播放器实现低延时播放"
],
Cluster:[
"cluster",'Master = "localhost:2019"\nListenAddr = ":2019"',"集群插件可以实现级联转发功能Master代表上游服务器ListenAdder代表源服务器监听端口可只配置一项"
],
RTMP:[
"rtmp",'ListenAddr = ":1935"',"rtmp协议实现基本发布和订阅功能"
],
RecordFlv:[
"record",'Path="./resource"',"录制视频流到flv文件"
],
HDL:[
"HDL",'ListenAddr = ":2020"',"Http-flv格式实现可以对接CDN厂商进行回源拉流"
],
Auth:[
"auth",'Key = "www.monibuca.com"',"一个鉴权验证模块"
],
Qos:[
"QoS",'Suffix = ["high","medium","low"]',"质量控制插件,可以动态改变订阅的不同的质量的流"
]
}
},
mutations: {
},

View File

@@ -13,21 +13,18 @@
<Step title="完成" content="完成实例创建"></Step>
</Steps>
<div style="margin:50px;width:auto">
<i-input v-model="createPath" v-if="createStep==0">
<Button slot="prepend" icon="md-arrow-round-up" @click="goUp"></Button>
</i-input>
<List v-else-if="createStep==1" border>
<ListItem v-for="(item,name) in plugins" :key="name">
<ListItemMeta :title="name" :description="item.Path"></ListItemMeta>
{{item.Config}}
<template slot="action">
<li @click="removePlugin(name)">
<Icon type="ios-trash"/>
移除
</li>
</template>
</ListItem>
</List>
<PathSelector v-model="createPath" v-if="createStep==0"></PathSelector>
<div style="display: flex;flex-wrap: wrap" v-else-if="createStep==1">
<Card v-for="(item,name) in plugins" :key="name" style="width:200px;margin:5px">
<Poptip :content="item.Description" slot="extra" width="200" word-wrap>
<Icon size="18" type="ios-help-circle-outline" style="cursor:pointer"/>
</Poptip>
<Poptip :content="item.Path" trigger="hover" word-wrap slot="title">
<Checkbox v-model="item.enabled" style="color: #eb5e46">{{name}}</Checkbox>
</Poptip>
<i-input type="textarea" v-model="item.Config" placeholder="请输入toml格式"></i-input>
</Card>
</div>
<div v-else>
<h3>实例名称</h3>
<i-input
@@ -79,7 +76,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">
@@ -88,14 +87,9 @@
<i-input v-model="formPlugin.Name" placeholder="插件名称必须和插件注册时的名称一致"></i-input>
</FormItem>
<FormItem label="插件包地址">
<i-input v-model="formPlugin.Path">
<Button slot="append" @click="showBuiltinPlugin=true">内置插件</Button>
</i-input>
<i-input v-model="formPlugin.Path"></i-input>
</FormItem>
<Alert
type="show-icon"
v-if="!Object.values(builtinPlugins).includes(formPlugin.Path)"
>
<Alert show-icon type="warning">
如果该插件是私有仓库请到服务器上输入echo "machine {{privateHost}} login 用户名 password 密码" >> ~/.netrc
并且添加环境变量GOPRIVATE={{privateHost}}
</Alert>
@@ -104,19 +98,6 @@
</FormItem>
</Form>
</Modal>
<Modal v-model="showBuiltinPlugin">
<List>
<ListItem v-for="(item,name) in builtinPlugins" :key="name">
<ListItemMeta :title="name" :description="item"></ListItemMeta>
<template slot="action">
<li @click="addBuiltin(name,item)">
<Icon type="ios-add"/>
添加
</li>
</template>
</ListItem>
</List>
</Modal>
<CreateInstance v-model="showCreate" :info="createInfo"></CreateInstance>
</Layout>
</template>
@@ -124,53 +105,43 @@
<script>
import CreateInstance from "../components/CreateInstance";
import InstanceList from "../components/InstanceList";
import ImportInstance from "../components/ImportInstance";
import PathSelector from "../components/PathSelector"
export default {
components: {
CreateInstance,InstanceList
CreateInstance, InstanceList, ImportInstance, PathSelector
},
data() {
let plugins = {}
for (let name in this.$store.state.defaultPlugins) {
plugins[name] = {
Name: name,
enabled: ["GateWay", "LogRotate", "Jessica"].includes(name),
Path: "github.com/langhuihui/monibuca/plugins/" + this.$store.state.defaultPlugins[name][0],
Config: this.$store.state.defaultPlugins[name][1],
Description: this.$store.state.defaultPlugins[name][2],
}
}
return {
instanceName: "",
createStep: 0,
showCreate: false,
createInfo: null,
createPath: "/opt/monibuca",
plugins: {},
plugins,
showAddPlugin: false,
formPlugin: {},
showBuiltinPlugin: false,
builtinPlugins: {
Auth: "github.com/langhuihui/monibuca/plugins/auth",
Cluster: "github.com/langhuihui/monibuca/plugins/cluster",
GateWay: "github.com/langhuihui/monibuca/plugins/gateway",
HDL: "github.com/langhuihui/monibuca/plugins/HDL",
Jessica: "github.com/langhuihui/monibuca/plugins/jessica",
QoS: "github.com/langhuihui/monibuca/plugins/QoS",
RecordFlv: "github.com/langhuihui/monibuca/plugins/record",
RTMP: "github.com/langhuihui/monibuca/plugins/rtmp"
},
defaultConfig: {
Auth: 'Key = "www.monibuca.com"',
RecordFlv: 'Path="./resource"',
QoS: 'Suffix = ["high","medium","low"]',
Cluster: 'Master = "localhost:2019"\nListenAddr = ":2019"',
GateWay: 'ListenAddr = ":8081"',
RTMP: 'ListenAddr = ":1935"',
Jessica: 'ListenAddr = ":8080"',
HDL: 'ListenAddr = ":2020"'
}
};
},
computed: {
pluginStr() {
return Object.values(this.plugins)
return Object.values(this.plugins).filter(x => x.enabled)
.map(x => x.Path)
.join("\n");
},
configStr() {
return Object.values(this.plugins)
return Object.values(this.plugins).filter(x => x.enabled)
.map(
x => `[Plugins.${x.Name}]
${x.Config || ""}`
@@ -186,7 +157,6 @@ ${x.Config || ""}`
},
methods: {
goUp() {
let paths = this.createPath.split("/");
paths.pop();
@@ -197,7 +167,7 @@ ${x.Config || ""}`
this.createInfo = {
Name: this.instanceName || this.createPath.split("/").pop(),
Path: this.createPath,
Plugins: Object.values(this.plugins).map(x => x.Path),
Plugins: Object.values(this.plugins).filter(x => x.enabled).map(x => x.Path),
Config: this.configStr
};
},
@@ -205,16 +175,6 @@ ${x.Config || ""}`
this.plugins[this.formPlugin.Name] = this.formPlugin;
this.formPlugin = {};
},
removePlugin(name) {
delete this.plugins[name];
this.$forceUpdate();
},
addBuiltin(name, item) {
this.formPlugin.Name = name;
this.formPlugin.Path = item;
this.formPlugin.Config = this.defaultConfig[name];
this.showBuiltinPlugin = false;
},
}
};
</script>