mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-27 23:02:07 +08:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5563ddc0d2 | ||
![]() |
95657bd6df |
104
main.go
104
main.go
@@ -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,7 +46,8 @@ 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/import", importInstance)
|
||||||
|
http.HandleFunc("/instance/updateConfig", updateConfig)
|
||||||
http.HandleFunc("/instance/list", listInstance)
|
http.HandleFunc("/instance/list", listInstance)
|
||||||
http.HandleFunc("/instance/create", initInstance)
|
http.HandleFunc("/instance/create", initInstance)
|
||||||
http.HandleFunc("/instance/restart", restartInstance)
|
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 {
|
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")
|
||||||
@@ -160,18 +227,18 @@ func restartInstance(w http.ResponseWriter, r *http.Request) {
|
|||||||
needBuild := r.URL.Query().Get("build") != ""
|
needBuild := r.URL.Query().Get("build") != ""
|
||||||
if instance, ok := instances[instanceName]; ok {
|
if instance, ok := instances[instanceName]; ok {
|
||||||
if needUpdate {
|
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()))
|
sse.WriteEvent("failed", []byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if needBuild {
|
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()))
|
sse.WriteEvent("failed", []byte(err.Error()))
|
||||||
return
|
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()))
|
sse.WriteEvent("failed", []byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -180,10 +247,7 @@ func restartInstance(w http.ResponseWriter, r *http.Request) {
|
|||||||
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 {
|
|
||||||
cmd.Dir = p.Path
|
|
||||||
return sse.WriteExec(cmd)
|
|
||||||
}
|
|
||||||
func (p *InstanceDesc) command(name string, args ...string) (cmd *exec.Cmd) {
|
func (p *InstanceDesc) command(name string, args ...string) (cmd *exec.Cmd) {
|
||||||
cmd = exec.Command(name, args...)
|
cmd = exec.Command(name, args...)
|
||||||
cmd.Dir = p.Path
|
cmd.Dir = p.Path
|
||||||
@@ -223,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
|
||||||
}
|
}
|
||||||
@@ -243,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()
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var ConfigRaw []byte
|
var ConfigRaw []byte
|
||||||
var Version = "0.2.3"
|
var Version = "0.2.5"
|
||||||
var EngineInfo = &struct {
|
var EngineInfo = &struct {
|
||||||
Version string
|
Version string
|
||||||
StartTime time.Time
|
StartTime time.Time
|
||||||
|
@@ -99,6 +99,7 @@ func summary(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func sysInfo(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)
|
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
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.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.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
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
1
pm/dist/js/app.5c3b9309.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
pm/dist/js/app.9b5890f5.js
vendored
2
pm/dist/js/app.9b5890f5.js
vendored
File diff suppressed because one or more lines are too long
1
pm/dist/js/app.9b5890f5.js.map
vendored
1
pm/dist/js/app.9b5890f5.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.f693d643.js.map
vendored
Normal file
1
pm/dist/js/chunk-vendors.f693d643.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pm/dist/js/chunk-vendors.f701a5a3.js.map
vendored
1
pm/dist/js/chunk-vendors.f701a5a3.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -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>
|
||||||
|
39
pm/src/components/ImportInstance.vue
Normal file
39
pm/src/components/ImportInstance.vue
Normal 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>
|
@@ -1,30 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<List border>
|
<div>
|
||||||
<ListItem v-for="item in instances" :key="item.Name">
|
<List border>
|
||||||
<ListItemMeta :title="item.Name" :description="item.Path"></ListItemMeta>
|
<ListItem v-for="item in instances" :key="item.Name">
|
||||||
<template v-if="hasGateway(item)">
|
<ListItemMeta :title="item.Name" :description="item.Path"></ListItemMeta>
|
||||||
{{item.Info}}
|
{{item.Info}}
|
||||||
</template>
|
<template slot="action">
|
||||||
<template slot="action">
|
<li @click="changeConfig(item)">
|
||||||
<li v-if="hasGateway(item)" @click="window.open(gateWayHref(item),'_blank')">
|
<Icon type="ios-settings"/>
|
||||||
<Icon type="md-browsers"/>
|
修改配置
|
||||||
管理界面
|
</li>
|
||||||
</li>
|
<li v-if="hasGateway(item)" @click="openGateway(item)">
|
||||||
<li @click="restart(item)">
|
<Icon type="md-browsers"/>
|
||||||
<Icon type="ios-refresh"/>
|
管理界面
|
||||||
重启
|
</li>
|
||||||
</li>
|
<li @click="currentItem=item,showRestart=true">
|
||||||
<li @click="shutdown(item)">
|
<Icon type="ios-refresh"/>
|
||||||
<Icon type="ios-power"/>
|
重启
|
||||||
关闭
|
</li>
|
||||||
</li>
|
<li @click="shutdown(item)">
|
||||||
</template>
|
<Icon type="ios-power"/>
|
||||||
</ListItem>
|
关闭
|
||||||
<Modal v-model="showRestart">
|
</li>
|
||||||
|
</template>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
<Modal v-model="showRestart" title="重启选项" @on-ok="restart">
|
||||||
<Checkbox v-model="update">go get -u</Checkbox>
|
<Checkbox v-model="update">go get -u</Checkbox>
|
||||||
<Checkbox v-model="build">go build</Checkbox>
|
<Checkbox v-model="build">go build</Checkbox>
|
||||||
</Modal>
|
</Modal>
|
||||||
</List>
|
<Modal v-model="showConfig" title="修改实例配置" @on-ok="submitConfigChange">
|
||||||
|
<i-input type="textarea" v-model="currentConfig" :rows="20">
|
||||||
|
|
||||||
|
</i-input>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -33,7 +42,15 @@
|
|||||||
export default {
|
export default {
|
||||||
name: "InstanceList",
|
name: "InstanceList",
|
||||||
data() {
|
data() {
|
||||||
return {instances: {}, showRestart: false, update: false, build: false}
|
return {
|
||||||
|
instances: [],
|
||||||
|
showRestart: false,
|
||||||
|
update: false,
|
||||||
|
build: false,
|
||||||
|
showConfig: false,
|
||||||
|
currentItem: null,
|
||||||
|
currentConfig: ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
window.ajax.getJSON("/instance/list").then(x => {
|
window.ajax.getJSON("/instance/list").then(x => {
|
||||||
@@ -49,17 +66,44 @@
|
|||||||
} else {
|
} else {
|
||||||
instance.Info = "实例未配置网关插件"
|
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) {
|
hasGateway(item) {
|
||||||
return item.Config.Plugins.hasOwnProperty("GateWay")
|
return item.Config.Plugins.hasOwnProperty("GateWay")
|
||||||
},
|
},
|
||||||
gateWayHref(item) {
|
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({
|
const msg = this.$Message.loading({
|
||||||
content: 'restart ' + item.Name + '...',
|
content: 'restart ' + item.Name + '...',
|
||||||
duration: 0
|
duration: 0
|
||||||
|
@@ -79,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">
|
||||||
@@ -124,11 +126,11 @@
|
|||||||
<script>
|
<script>
|
||||||
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";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
CreateInstance,InstanceList
|
CreateInstance,InstanceList,ImportInstance
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
Reference in New Issue
Block a user