mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-10-05 04:56:49 +08:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
709c2c6ac7 |
20
main.go
20
main.go
@@ -15,6 +15,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -46,6 +47,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
addr := flag.String("port", "8000", "http server port")
|
addr := flag.String("port", "8000", "http server port")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
http.HandleFunc("/instance/listDir", listDir)
|
||||||
http.HandleFunc("/instance/import", importInstance)
|
http.HandleFunc("/instance/import", importInstance)
|
||||||
http.HandleFunc("/instance/updateConfig", updateConfig)
|
http.HandleFunc("/instance/updateConfig", updateConfig)
|
||||||
http.HandleFunc("/instance/list", listInstance)
|
http.HandleFunc("/instance/list", listInstance)
|
||||||
@@ -59,6 +61,24 @@ 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) {
|
func importInstance(w http.ResponseWriter, r *http.Request) {
|
||||||
var e error
|
var e error
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@@ -41,10 +41,10 @@ func run() {
|
|||||||
config.splitFunc = config.splitByTime
|
config.splitFunc = config.splitByTime
|
||||||
}
|
}
|
||||||
config.createTime = time.Now()
|
config.createTime = time.Now()
|
||||||
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)
|
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 {
|
if err == nil {
|
||||||
config.file = file
|
stat, _ := config.file.Stat()
|
||||||
stat, _ := file.Stat()
|
|
||||||
config.currentSize = stat.Size()
|
config.currentSize = stat.Size()
|
||||||
AddWriter(config)
|
AddWriter(config)
|
||||||
} else {
|
} else {
|
||||||
@@ -71,3 +71,8 @@ func (l *LogRotate) Write(data []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
return
|
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
2
pm/dist/index.html
vendored
@@ -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.f5cfbf83.js rel=preload as=script><link href=/js/chunk-vendors.470a264b.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.470a264b.js></script><script src=/js/app.f5cfbf83.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
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
1
pm/dist/js/app.13e2de5f.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
pm/dist/js/app.f5cfbf83.js
vendored
2
pm/dist/js/app.f5cfbf83.js
vendored
File diff suppressed because one or more lines are too long
1
pm/dist/js/app.f5cfbf83.js.map
vendored
1
pm/dist/js/app.f5cfbf83.js.map
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
pm/dist/js/chunk-vendors.2e3b192a.js.map
vendored
Normal file
1
pm/dist/js/chunk-vendors.2e3b192a.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pm/dist/js/chunk-vendors.470a264b.js.map
vendored
1
pm/dist/js/chunk-vendors.470a264b.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -38,7 +38,7 @@
|
|||||||
"plugin:vue/essential",
|
"plugin:vue/essential",
|
||||||
"eslint:recommended"
|
"eslint:recommended"
|
||||||
],
|
],
|
||||||
"rules": {},
|
"rules": {"no-console": "off"},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"parser": "babel-eslint"
|
"parser": "babel-eslint"
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<i-input v-model="instanceName" :placeholder="defaultInstanceName">
|
<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>
|
<span slot="prepend">实例名称</span>
|
||||||
</i-input>
|
</i-input>
|
||||||
<i-input prefix="ios-home" v-model="instancePath" placeholder="输入实例所在的路径" search enter-button="Import" @on-search="doImport">
|
|
||||||
</i-input>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import PathSelector from "./PathSelector"
|
||||||
export default {
|
export default {
|
||||||
name: "ImportInstance",
|
name: "ImportInstance",
|
||||||
|
components:{
|
||||||
|
PathSelector
|
||||||
|
},
|
||||||
data(){
|
data(){
|
||||||
return {
|
return {
|
||||||
instancePath:"",
|
instancePath:"",
|
||||||
@@ -19,7 +22,10 @@
|
|||||||
},
|
},
|
||||||
computed:{
|
computed:{
|
||||||
defaultInstanceName(){
|
defaultInstanceName(){
|
||||||
return this.instancePath.replace(/\\/g,"/").split("/").pop()
|
let path = this.instancePath.replace(/\\/g,"/")
|
||||||
|
let s = path.split("/")
|
||||||
|
if(path.endsWith("/")) s.pop()
|
||||||
|
return s.pop()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
|
66
pm/src/components/PathSelector.vue
Normal file
66
pm/src/components/PathSelector.vue
Normal 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>
|
@@ -13,21 +13,18 @@
|
|||||||
<Step title="完成" content="完成实例创建"></Step>
|
<Step title="完成" content="完成实例创建"></Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
<div style="margin:50px;width:auto">
|
<div style="margin:50px;width:auto">
|
||||||
<i-input v-model="createPath" v-if="createStep==0">
|
<PathSelector v-model="createPath" v-if="createStep==0"></PathSelector>
|
||||||
<Button slot="prepend" icon="md-arrow-round-up" @click="goUp"></Button>
|
<div style="display: flex;flex-wrap: wrap" v-else-if="createStep==1">
|
||||||
</i-input>
|
<Card v-for="(item,name) in plugins" :key="name" style="width:200px;margin:5px">
|
||||||
<List v-else-if="createStep==1" border>
|
<Poptip :content="item.Description" slot="extra" width="200" word-wrap>
|
||||||
<ListItem v-for="(item,name) in plugins" :key="name">
|
<Icon size="18" type="ios-help-circle-outline" style="cursor:pointer"/>
|
||||||
<ListItemMeta :title="name" :description="item.Path"></ListItemMeta>
|
</Poptip>
|
||||||
{{item.Config}}
|
<Poptip :content="item.Path" trigger="hover" word-wrap slot="title">
|
||||||
<template slot="action">
|
<Checkbox v-model="item.enabled" style="color: #eb5e46">{{name}}</Checkbox>
|
||||||
<li @click="removePlugin(name)">
|
</Poptip>
|
||||||
<Icon type="ios-trash"/>
|
<i-input type="textarea" v-model="item.Config" placeholder="请输入toml格式"></i-input>
|
||||||
移除
|
</Card>
|
||||||
</li>
|
</div>
|
||||||
</template>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h3>实例名称:</h3>
|
<h3>实例名称:</h3>
|
||||||
<i-input
|
<i-input
|
||||||
@@ -90,14 +87,9 @@
|
|||||||
<i-input v-model="formPlugin.Name" placeholder="插件名称必须和插件注册时的名称一致"></i-input>
|
<i-input v-model="formPlugin.Name" placeholder="插件名称必须和插件注册时的名称一致"></i-input>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="插件包地址">
|
<FormItem label="插件包地址">
|
||||||
<i-input v-model="formPlugin.Path">
|
<i-input v-model="formPlugin.Path"></i-input>
|
||||||
<Button slot="append" @click="showBuiltinPlugin=true">内置插件</Button>
|
|
||||||
</i-input>
|
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<Alert show-icon
|
<Alert show-icon type="warning">
|
||||||
type="warning"
|
|
||||||
v-if="!isBuiltInPlugin(formPlugin.Path)"
|
|
||||||
>
|
|
||||||
如果该插件是私有仓库,请到服务器上输入:echo "machine {{privateHost}} login 用户名 password 密码" >> ~/.netrc
|
如果该插件是私有仓库,请到服务器上输入:echo "machine {{privateHost}} login 用户名 password 密码" >> ~/.netrc
|
||||||
并且添加环境变量GOPRIVATE={{privateHost}}
|
并且添加环境变量GOPRIVATE={{privateHost}}
|
||||||
</Alert>
|
</Alert>
|
||||||
@@ -106,19 +98,6 @@
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal v-model="showBuiltinPlugin">
|
|
||||||
<List>
|
|
||||||
<ListItem v-for="(item,name) in $store.state.defaultPlugins" :key="name">
|
|
||||||
<ListItemMeta :title="name" :description="item[2]"></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>
|
<CreateInstance v-model="showCreate" :info="createInfo"></CreateInstance>
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
@@ -127,32 +106,42 @@
|
|||||||
import CreateInstance from "../components/CreateInstance";
|
import CreateInstance from "../components/CreateInstance";
|
||||||
import InstanceList from "../components/InstanceList";
|
import InstanceList from "../components/InstanceList";
|
||||||
import ImportInstance from "../components/ImportInstance";
|
import ImportInstance from "../components/ImportInstance";
|
||||||
|
import PathSelector from "../components/PathSelector"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
CreateInstance,InstanceList,ImportInstance
|
CreateInstance, InstanceList, ImportInstance, PathSelector
|
||||||
},
|
},
|
||||||
data() {
|
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 {
|
return {
|
||||||
instanceName: "",
|
instanceName: "",
|
||||||
createStep: 0,
|
createStep: 0,
|
||||||
showCreate: false,
|
showCreate: false,
|
||||||
createInfo: null,
|
createInfo: null,
|
||||||
createPath: "/opt/monibuca",
|
createPath: "/opt/monibuca",
|
||||||
plugins: {},
|
plugins,
|
||||||
showAddPlugin: false,
|
showAddPlugin: false,
|
||||||
formPlugin: {},
|
formPlugin: {},
|
||||||
showBuiltinPlugin: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
pluginStr() {
|
pluginStr() {
|
||||||
return Object.values(this.plugins)
|
return Object.values(this.plugins).filter(x => x.enabled)
|
||||||
.map(x => x.Path)
|
.map(x => x.Path)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
},
|
},
|
||||||
configStr() {
|
configStr() {
|
||||||
return Object.values(this.plugins)
|
return Object.values(this.plugins).filter(x => x.enabled)
|
||||||
.map(
|
.map(
|
||||||
x => `[Plugins.${x.Name}]
|
x => `[Plugins.${x.Name}]
|
||||||
${x.Config || ""}`
|
${x.Config || ""}`
|
||||||
@@ -168,9 +157,6 @@ ${x.Config || ""}`
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
isBuiltInPlugin(path){
|
|
||||||
return Object.values(this.$store.state.defaultPlugins).some(x=>"github.com/langhuihui/monibuca/plugins/"+x[0]==path)
|
|
||||||
},
|
|
||||||
goUp() {
|
goUp() {
|
||||||
let paths = this.createPath.split("/");
|
let paths = this.createPath.split("/");
|
||||||
paths.pop();
|
paths.pop();
|
||||||
@@ -181,7 +167,7 @@ ${x.Config || ""}`
|
|||||||
this.createInfo = {
|
this.createInfo = {
|
||||||
Name: this.instanceName || this.createPath.split("/").pop(),
|
Name: this.instanceName || this.createPath.split("/").pop(),
|
||||||
Path: this.createPath,
|
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
|
Config: this.configStr
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -189,16 +175,6 @@ ${x.Config || ""}`
|
|||||||
this.plugins[this.formPlugin.Name] = this.formPlugin;
|
this.plugins[this.formPlugin.Name] = this.formPlugin;
|
||||||
this.formPlugin = {};
|
this.formPlugin = {};
|
||||||
},
|
},
|
||||||
removePlugin(name) {
|
|
||||||
delete this.plugins[name];
|
|
||||||
this.$forceUpdate();
|
|
||||||
},
|
|
||||||
addBuiltin(name, item) {
|
|
||||||
this.formPlugin.Name = name;
|
|
||||||
this.formPlugin.Path = "github.com/langhuihui/monibuca/plugins/"+item[0];
|
|
||||||
this.formPlugin.Config = item[1];
|
|
||||||
this.showBuiltinPlugin = false;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
Reference in New Issue
Block a user