mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-07 07:10:27 +08:00
可以在线修改数据库配置
This commit is contained in:
1
go.mod
1
go.mod
@@ -16,4 +16,5 @@ require (
|
|||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa
|
||||||
google.golang.org/grpc v1.32.0
|
google.golang.org/grpc v1.32.0
|
||||||
google.golang.org/protobuf v1.25.0 // indirect
|
google.golang.org/protobuf v1.25.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,15 +1,87 @@
|
|||||||
package profile
|
package profile
|
||||||
|
|
||||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/go-yaml/yaml"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) Init() {
|
func (this *IndexAction) Init() {
|
||||||
this.Nav("", "", "")
|
this.Nav("", "", "index")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IndexAction) RunGet(params struct{}) {
|
func (this *IndexAction) RunGet(params struct{}) {
|
||||||
|
this.Data["error"] = ""
|
||||||
|
|
||||||
|
configFile := Tea.ConfigFile("api_db.yaml")
|
||||||
|
data, err := ioutil.ReadFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
this.Data["error"] = "read config file failed: api_db.yaml: " + err.Error()
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &dbs.Config{}
|
||||||
|
err = yaml.Unmarshal(data, config)
|
||||||
|
if err != nil {
|
||||||
|
this.Data["error"] = "parse config file failed: api_db.yaml: " + err.Error()
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DBs == nil {
|
||||||
|
this.Data["error"] = "can not find valid database config: api_db.yaml"
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dbConfig, ok := config.DBs[config.Default.DB]
|
||||||
|
if !ok {
|
||||||
|
this.Data["error"] = "can not find valid database config: api_db.yaml"
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dsn := dbConfig.Dsn
|
||||||
|
dsn = regexp.MustCompile(`tcp\((.+)\)`).ReplaceAllString(dsn, "$1")
|
||||||
|
dsnURL, err := url.Parse("mysql://" + dsn)
|
||||||
|
if err != nil {
|
||||||
|
this.Data["error"] = "parse dsn failed: " + err.Error()
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
host := dsnURL.Host
|
||||||
|
port := "3306"
|
||||||
|
index := strings.LastIndex(dsnURL.Host, ":")
|
||||||
|
if index > 0 {
|
||||||
|
host = dsnURL.Host[:index]
|
||||||
|
port = dsnURL.Host[index+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
password, _ := dsnURL.User.Password()
|
||||||
|
if len(password) > 0 {
|
||||||
|
password = strings.Repeat("*", len(password))
|
||||||
|
}
|
||||||
|
this.Data["dbConfig"] = maps.Map{
|
||||||
|
"host": host,
|
||||||
|
"port": port,
|
||||||
|
"username": dsnURL.User.Username(),
|
||||||
|
"password": password,
|
||||||
|
"database": filepath.Base(dsnURL.Path),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 测试连接
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ func init() {
|
|||||||
Helper(settingutils.NewHelper("database")).
|
Helper(settingutils.NewHelper("database")).
|
||||||
Prefix("/settings/database").
|
Prefix("/settings/database").
|
||||||
Get("", new(IndexAction)).
|
Get("", new(IndexAction)).
|
||||||
|
GetPost("/update", new(UpdateAction)).
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
139
internal/web/actions/default/settings/database/update.go
Normal file
139
internal/web/actions/default/settings/database/update.go
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/actions"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateAction) Init() {
|
||||||
|
this.Nav("", "", "update")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateAction) RunGet(params struct{}) {
|
||||||
|
this.Data["dbConfig"] = maps.Map{
|
||||||
|
"host": "",
|
||||||
|
"port": "",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"database": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile := Tea.ConfigFile("api_db.yaml")
|
||||||
|
data, err := ioutil.ReadFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &dbs.Config{}
|
||||||
|
err = yaml.Unmarshal(data, config)
|
||||||
|
if err != nil {
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DBs == nil {
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dbConfig, ok := config.DBs[config.Default.DB]
|
||||||
|
if !ok {
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dsn := dbConfig.Dsn
|
||||||
|
dsn = regexp.MustCompile(`tcp\((.+)\)`).ReplaceAllString(dsn, "$1")
|
||||||
|
dsnURL, err := url.Parse("mysql://" + dsn)
|
||||||
|
if err != nil {
|
||||||
|
this.Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
host := dsnURL.Host
|
||||||
|
port := "3306"
|
||||||
|
index := strings.LastIndex(dsnURL.Host, ":")
|
||||||
|
if index > 0 {
|
||||||
|
host = dsnURL.Host[:index]
|
||||||
|
port = dsnURL.Host[index+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
password, _ := dsnURL.User.Password()
|
||||||
|
this.Data["dbConfig"] = maps.Map{
|
||||||
|
"host": host,
|
||||||
|
"port": port,
|
||||||
|
"username": dsnURL.User.Username(),
|
||||||
|
"password": password,
|
||||||
|
"database": filepath.Base(dsnURL.Path),
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UpdateAction) RunPost(params struct {
|
||||||
|
Host string
|
||||||
|
Port int32
|
||||||
|
Database string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
|
||||||
|
Must *actions.Must
|
||||||
|
}) {
|
||||||
|
params.Must.
|
||||||
|
Field("host", params.Host).
|
||||||
|
Require("请输入主机地址").
|
||||||
|
Match(`^[\w\.-]+$`, "主机地址中不能包含特殊字符").
|
||||||
|
Field("port", params.Port).
|
||||||
|
Gt(0, "端口需要大于0").
|
||||||
|
Lt(65535, "端口需要小于65535").
|
||||||
|
Field("database", params.Database).
|
||||||
|
Require("请输入数据库名称").
|
||||||
|
Match(`^[\w\.-]+$`, "数据库名称中不能包含特殊字符").
|
||||||
|
Field("username", params.Username).
|
||||||
|
Require("请输入连接数据库的用户名").
|
||||||
|
Match(`^[\w\.-]+$`, "用户名中不能包含特殊字符")
|
||||||
|
|
||||||
|
if len(params.Password) > 0 {
|
||||||
|
params.Must.
|
||||||
|
Field("password", params.Password).
|
||||||
|
Match(`^[\w\.-]+$`, "密码中不能包含特殊字符")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
dsn := params.Username + ":" + params.Password + "@tcp(" + params.Host + ":" + fmt.Sprintf("%d", params.Port) + ")/" + params.Database
|
||||||
|
|
||||||
|
configFile := Tea.ConfigFile("api_db.yaml")
|
||||||
|
template := `default:
|
||||||
|
db: "prod"
|
||||||
|
prefix: ""
|
||||||
|
|
||||||
|
dbs:
|
||||||
|
prod:
|
||||||
|
driver: "mysql"
|
||||||
|
dsn: "` + dsn + `?charset=utf8mb4&timeout=30s"
|
||||||
|
prefix: "edge"
|
||||||
|
models:
|
||||||
|
package: internal/web/models
|
||||||
|
`
|
||||||
|
err := ioutil.WriteFile(configFile, []byte(template), 0666)
|
||||||
|
if err != nil {
|
||||||
|
this.Fail("保存配置失败:" + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 让本地的节点生效
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
5
web/views/@default/settings/database/@menu.html
Normal file
5
web/views/@default/settings/database/@menu.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<first-menu>
|
||||||
|
<menu-item href="/settings/database" code="index">详情</menu-item>
|
||||||
|
<menu-item href="/settings/database/update" code="update">修改</menu-item>
|
||||||
|
</first-menu>
|
||||||
|
<div class="margin"></div>
|
||||||
@@ -1,3 +1,38 @@
|
|||||||
{$layout}
|
{$layout}
|
||||||
|
{$template "menu"}
|
||||||
|
|
||||||
<p class="comment">此功能暂未开放,敬请期待。</p>
|
<p class="ui message error" v-if="error.length > 0">{{error}}</p>
|
||||||
|
<div v-show="error.length == 0 && dbConfig != null">
|
||||||
|
<table class="ui table selectable definition">
|
||||||
|
<tr>
|
||||||
|
<td class="title">主机地址</td>
|
||||||
|
<td>{{dbConfig.host}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>数据库端口</td>
|
||||||
|
<td>{{dbConfig.port}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>数据库名称</td>
|
||||||
|
<td>{{dbConfig.database}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>用户名</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="dbConfig.username.length > 0">{{dbConfig.username}}</span>
|
||||||
|
<span v-else class="disabled">不使用用户名。</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>密码</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="dbConfig.password.length > 0">{{dbConfig.password}}</span>
|
||||||
|
<span v-else class="disabled">不使用密码。</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui message tiny">
|
||||||
|
<p>在这里可以设置API节点可以使用的数据库。</p>
|
||||||
|
</div>
|
||||||
44
web/views/@default/settings/database/update.html
Normal file
44
web/views/@default/settings/database/update.html
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "menu"}
|
||||||
|
|
||||||
|
<form class="ui form" data-tea-action="$" data-tea-success="success">
|
||||||
|
<table class="ui table selectable definition">
|
||||||
|
<tr>
|
||||||
|
<td class="title">主机地址 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="host" v-model="dbConfig.host" maxlength="100"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>数据库端口 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="port" style="width:6em" v-model="dbConfig.port" maxlength="5"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>数据库名称 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="database" maxlength="100" v-model="dbConfig.database"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>用户名 *</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="username" maxlength="100" v-model="dbConfig.username"/>
|
||||||
|
<p class="comment">用来连接数据库的用户名。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>密码</td>
|
||||||
|
<td>
|
||||||
|
<input type="password" name="password" maxlength="100" v-model="dbConfig.password"/>
|
||||||
|
<p class="comment">用来连接数据库的密码。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="ui message small warning">
|
||||||
|
<p>修改数据库配置后需要重新配置API节点。</p>
|
||||||
|
</div>
|
||||||
3
web/views/@default/settings/database/update.js
Normal file
3
web/views/@default/settings/database/update.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.success = NotifySuccess("保存成功", "/settings/database")
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user