mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 13:10:26 +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