diff --git a/controller/MonitorController.go b/controller/MonitorController.go index 6587ee3..71cb50b 100644 --- a/controller/MonitorController.go +++ b/controller/MonitorController.go @@ -3,9 +3,7 @@ package controller import ( "github.com/zhenorzz/goploy/core" "github.com/zhenorzz/goploy/model" - "net" - "strconv" - "time" + "github.com/zhenorzz/goploy/service" ) // Monitor struct @@ -44,15 +42,13 @@ func (Monitor) GetTotal(gp *core.Goploy) *core.Response { // Check one monitor func (Monitor) Check(gp *core.Goploy) *core.Response { type ReqData struct { - Domain string `json:"domain" validate:"required"` - Port int `json:"port" validate:"min=0,max=65535"` + URL string `json:"url" validate:"required"` } var reqData ReqData if err := verify(gp.Body, &reqData); err != nil { return &core.Response{Code: core.Error, Message: err.Error()} } - _, err := net.DialTimeout("tcp", reqData.Domain+":"+strconv.Itoa(reqData.Port), 5*time.Second) - if err != nil { + if err := (service.Gnet{URL: reqData.URL}.Ping()); err != nil { return &core.Response{Code: core.Error, Message: err.Error()} } return &core.Response{Message: "Connected"} @@ -62,8 +58,7 @@ func (Monitor) Check(gp *core.Goploy) *core.Response { func (Monitor) Add(gp *core.Goploy) *core.Response { type ReqData struct { Name string `json:"name" validate:"required"` - Domain string `json:"domain" validate:"required"` - Port int `json:"port" validate:"min=0,max=65535"` + URL string `json:"url" validate:"required"` Second int `json:"second" validate:"gt=0"` Times uint16 `json:"times" validate:"gt=0"` NotifyType uint8 `json:"notifyType" validate:"gt=0"` @@ -79,8 +74,7 @@ func (Monitor) Add(gp *core.Goploy) *core.Response { id, err := model.Monitor{ NamespaceID: gp.Namespace.ID, Name: reqData.Name, - Domain: reqData.Domain, - Port: reqData.Port, + URL: reqData.URL, Second: reqData.Second, Times: reqData.Times, NotifyType: reqData.NotifyType, @@ -104,7 +98,7 @@ func (Monitor) Edit(gp *core.Goploy) *core.Response { type ReqData struct { ID int64 `json:"id" validate:"gt=0"` Name string `json:"name" validate:"required"` - Domain string `json:"domain" validate:"required"` + URL string `json:"url" validate:"required"` Port int `json:"port" validate:"min=0,max=65535"` Second int `json:"second" validate:"gt=0"` Times uint16 `json:"times" validate:"gt=0"` @@ -120,8 +114,7 @@ func (Monitor) Edit(gp *core.Goploy) *core.Response { err := model.Monitor{ ID: reqData.ID, Name: reqData.Name, - Domain: reqData.Domain, - Port: reqData.Port, + URL: reqData.URL, Second: reqData.Second, Times: reqData.Times, NotifyType: reqData.NotifyType, diff --git a/controller/UserController.go b/controller/UserController.go index 86ee509..439f7d2 100644 --- a/controller/UserController.go +++ b/controller/UserController.go @@ -2,9 +2,7 @@ package controller import ( "database/sql" - "github.com/patrickmn/go-cache" "net/http" - "strconv" "time" "github.com/zhenorzz/goploy/core" @@ -37,9 +35,9 @@ func (User) Login(gp *core.Goploy) *core.Response { return &core.Response{Code: core.AccountDisabled, Message: "Account is disabled"} } - namespaceList, err := core.GetNamespace(userData.ID) + namespaceList, err := model.Namespace{UserID: userData.ID}.GetAllByUserID() if err != nil { - return &core.Response{Code: core.Error, Message: "尚未分配空间,请联系管理员"} + return &core.Response{Code: core.Error, Message: "No space assigned, please contact the administrator"} } token, err := userData.CreateToken() @@ -47,15 +45,7 @@ func (User) Login(gp *core.Goploy) *core.Response { return &core.Response{Code: core.Error, Message: err.Error()} } - model.User{ID: userData.ID, LastLoginTime: time.Now().Format("20060102150405")}.UpdateLastLoginTime() - - core.Cache.Set("userInfo:"+strconv.Itoa(int(userData.ID)), &userData, cache.DefaultExpiration) - - namespaceList, err = model.Namespace{UserID: userData.ID}.GetAllByUserID() - if err != nil { - return &core.Response{Code: core.Error, Message: err.Error()} - } - core.Cache.Set("namespace:"+strconv.Itoa(int(userData.ID)), &namespaceList, cache.DefaultExpiration) + _ = model.User{ID: userData.ID, LastLoginTime: time.Now().Format("20060102150405")}.UpdateLastLoginTime() cookie := http.Cookie{Name: core.LoginCookieName, Value: token, Path: "/", MaxAge: 86400, HttpOnly: true} http.SetCookie(gp.ResponseWriter, &cookie) diff --git a/core/Cache.go b/core/Cache.go deleted file mode 100644 index 6a2f913..0000000 --- a/core/Cache.go +++ /dev/null @@ -1,47 +0,0 @@ -package core - -import ( - "strconv" - "time" - - "github.com/zhenorzz/goploy/model" - - "github.com/patrickmn/go-cache" -) - -// Cache uint -var Cache = cache.New(24*time.Hour, 48*time.Hour) - -// GetUserInfo return model.User and error -func GetUserInfo(userID int64) (model.User, error) { - var userData model.User - var err error - if x, found := Cache.Get("userInfo:" + strconv.Itoa(int(userID))); found { - userData = *x.(*model.User) - } else { - userData, err = model.User{ID: userID}.GetData() - if err != nil { - return userData, err - } - - Cache.Set("userInfo:"+strconv.Itoa(int(userID)), &userData, cache.DefaultExpiration) - } - return userData, nil -} - -// GetNamespace return model.Namespaces and error -func GetNamespace(userID int64) (model.Namespaces, error) { - var namespaceList model.Namespaces - var err error - if x, found := Cache.Get("namespace:" + strconv.Itoa(int(userID))); found { - namespaceList = *x.(*model.Namespaces) - } else { - namespaceList, err = model.Namespace{UserID: userID}.GetAllByUserID() - if err != nil { - return namespaceList, err - } - - Cache.Set("namespace:"+strconv.Itoa(int(userID)), &namespaceList, cache.DefaultExpiration) - } - return namespaceList, nil -} diff --git a/core/Route.go b/core/Route.go index 7fa8465..a6f2aa5 100644 --- a/core/Route.go +++ b/core/Route.go @@ -148,12 +148,10 @@ func (rt *Router) checkLogin(w http.ResponseWriter, r *http.Request) (*Goploy, * return nil, &Response{Code: Deny, Message: "Invalid namespace"} } - namespaceList, err := GetNamespace(int64(claims["id"].(float64))) + namespaceList, err := model.Namespace{UserID: int64(claims["id"].(float64))}.GetAllByUserID() if err != nil { return nil, &Response{Code: Deny, Message: "Get namespace list error"} - } - - if len(namespaceList) == 0 { + } else if len(namespaceList) == 0 { return nil, &Response{Code: Deny, Message: "No available namespace"} } @@ -166,8 +164,7 @@ func (rt *Router) checkLogin(w http.ResponseWriter, r *http.Request) (*Goploy, * if namespace == (model.Namespace{}) { return nil, &Response{Code: NamespaceInvalid, Message: "Namespace no permission, please login again"} } - - userInfo, err = GetUserInfo(int64(claims["id"].(float64))) + userInfo, err = model.User{ID: int64(claims["id"].(float64))}.GetData() if err != nil { return nil, &Response{Code: Deny, Message: "Get user information error"} } diff --git a/docker/Dockerfile b/docker/Dockerfile index f941a7a..c3f382b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ FROM alpine LABEL maintainer="zhenorzz@gmail.com" -ARG GOPLOY_VER=v1.3.5 +ARG GOPLOY_VER=v1.3.6 ENV GOPLOY_VER=${GOPLOY_VER} ENV MYSQL_PORT=3306 diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 1c351ea..d7a8a8b 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -1,5 +1,19 @@ # change log +## 1.3.6 + +*2021-10-15* + +### New features +- monitor support http + +### Optimization +- delete cache + +### Bug fixes +- fix task block +- fix symlink rollback + ## 1.3.5 *2021-09-18* diff --git a/main.go b/main.go index 51ab248..680e181 100644 --- a/main.go +++ b/main.go @@ -36,7 +36,7 @@ var ( s string ) -const appVersion = "1.3.5" +const appVersion = "1.3.6" func init() { flag.StringVar(&core.AssetDir, "asset-dir", "", "default: ./") diff --git a/model/MonitorModel.go b/model/MonitorModel.go index 60f30ce..5d60439 100644 --- a/model/MonitorModel.go +++ b/model/MonitorModel.go @@ -13,8 +13,7 @@ type Monitor struct { ID int64 `json:"id"` NamespaceID int64 `json:"namespaceId"` Name string `json:"name"` - Domain string `json:"domain"` - Port int `json:"port"` + URL string `json:"url"` Second int `json:"second"` Times uint16 `json:"times"` NotifyType uint8 `json:"notifyType"` @@ -33,7 +32,7 @@ type Monitors []Monitor // GetList - func (m Monitor) GetList(pagination Pagination) (Monitors, error) { rows, err := sq. - Select("id, name, domain, port, second, times, notify_type, notify_target, notify_times, description, error_content, state, insert_time, update_time"). + Select("id, name, url, second, times, notify_type, notify_target, notify_times, description, error_content, state, insert_time, update_time"). From(monitorTable). Where(sq.Eq{ "namespace_id": m.NamespaceID, @@ -53,8 +52,7 @@ func (m Monitor) GetList(pagination Pagination) (Monitors, error) { if err := rows.Scan( &monitor.ID, &monitor.Name, - &monitor.Domain, - &monitor.Port, + &monitor.URL, &monitor.Second, &monitor.Times, &monitor.NotifyType, @@ -95,13 +93,13 @@ func (m Monitor) GetTotal() (int64, error) { func (m Monitor) GetData() (Monitor, error) { var monitor Monitor err := sq. - Select("id, name, domain, ip, port, second, times, notify_type, notify_target, notify_times, state"). + Select("id, name, url, second, times, notify_type, notify_target, notify_times, state"). From(monitorTable). Where(sq.Eq{"id": m.ID}). OrderBy("id DESC"). RunWith(DB). QueryRow(). - Scan(&monitor.ID, &monitor.Name, &monitor.Domain, &monitor.Port, &monitor.Second, &monitor.Times, &monitor.NotifyType, &monitor.NotifyTarget, &monitor.NotifyTimes, &monitor.State) + Scan(&monitor.ID, &monitor.Name, &monitor.URL, &monitor.Second, &monitor.Times, &monitor.NotifyType, &monitor.NotifyTarget, &monitor.NotifyTimes, &monitor.State) if err != nil { return monitor, errors.New("数据查询失败") } @@ -111,7 +109,7 @@ func (m Monitor) GetData() (Monitor, error) { // GetAllByState - func (m Monitor) GetAllByState() (Monitors, error) { rows, err := sq. - Select("id, name, domain, port, second, times, notify_type, notify_target, notify_times, description"). + Select("id, name, url, second, times, notify_type, notify_target, notify_times, description"). From(monitorTable). Where(sq.Eq{ "state": m.State, @@ -128,8 +126,7 @@ func (m Monitor) GetAllByState() (Monitors, error) { if err := rows.Scan( &monitor.ID, &monitor.Name, - &monitor.Domain, - &monitor.Port, + &monitor.URL, &monitor.Second, &monitor.Times, &monitor.NotifyType, @@ -148,8 +145,8 @@ func (m Monitor) GetAllByState() (Monitors, error) { func (m Monitor) AddRow() (int64, error) { result, err := sq. Insert(monitorTable). - Columns("namespace_id", "name", "domain", "port", "second", "times", "notify_type", "notify_target", "notify_times", "description", "error_content"). - Values(m.NamespaceID, m.Name, m.Domain, m.Port, m.Second, m.Times, m.NotifyType, m.NotifyTarget, m.NotifyTimes, m.Description, ""). + Columns("namespace_id", "name", "url", "second", "times", "notify_type", "notify_target", "notify_times", "description", "error_content"). + Values(m.NamespaceID, m.Name, m.URL, m.Second, m.Times, m.NotifyType, m.NotifyTarget, m.NotifyTimes, m.Description, ""). RunWith(DB). Exec() if err != nil { @@ -165,8 +162,7 @@ func (m Monitor) EditRow() error { Update(monitorTable). SetMap(sq.Eq{ "name": m.Name, - "domain": m.Domain, - "port": m.Port, + "url": m.URL, "second": m.Second, "times": m.Times, "notify_type": m.NotifyType, @@ -208,11 +204,11 @@ func (m Monitor) TurnOff(errorContent string) error { _, err := sq. Update(monitorTable). SetMap(sq.Eq{ - "state": Disable, + "state": Disable, "error_content": errorContent, }). Where(sq.Eq{"id": m.ID}). RunWith(DB). Exec() return err -} \ No newline at end of file +} diff --git a/model/sql/1.3.6.sql b/model/sql/1.3.6.sql new file mode 100644 index 0000000..0357f15 --- /dev/null +++ b/model/sql/1.3.6.sql @@ -0,0 +1,3 @@ +ALTER TABLE `goploy`.`monitor` +DROP COLUMN `port`, +CHANGE COLUMN `domain` `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL AFTER `name`; \ No newline at end of file diff --git a/model/sql/goploy.sql b/model/sql/goploy.sql index b6a770f..ea22d6e 100644 --- a/model/sql/goploy.sql +++ b/model/sql/goploy.sql @@ -130,8 +130,7 @@ CREATE TABLE IF NOT EXISTS `monitor` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `namespace_id` int(10) unsigned NOT NULL, `name` varchar(255) NOT NULL, - `domain` varchar(50) NOT NULL, - `port` smallint(5) unsigned NOT NULL DEFAULT '80', + `url` varchar(255) NOT NULL, `second` int(10) unsigned NOT NULL DEFAULT '1' COMMENT 'How many seconds to run', `times` smallint(5) unsigned NOT NULL DEFAULT '1' COMMENT 'How many times of failures', `description` varchar(255) NOT NULL DEFAULT '', @@ -222,7 +221,7 @@ CREATE TABLE IF NOT EXISTS `namespace_user` ( UNIQUE KEY `uk_namespace_user` (`namespace_id`,`user_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -CREATE TABLE IF NOT EXISTS `goploy`.`system_config` ( +CREATE TABLE IF NOT EXISTS `system_config` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `key` varchar(255) NOT NULL DEFAULT '', `value` varchar(255) NOT NULL DEFAULT '', @@ -233,5 +232,5 @@ CREATE TABLE IF NOT EXISTS `goploy`.`system_config` ( REPLACE INTO `user`(`id`, `account`, `password`, `name`, `contact`, `state`, `super_manager`) VALUES (1, 'admin', '$2a$10$89ZJ2xeJj35GOw11Qiucr.phaEZP4.kBX6aKTs7oWFp1xcGBBgijm', '超管', '', 1, 1); REPLACE INTO `namespace`(`id`, `name`) VALUES (1, 'goploy'); REPLACE INTO `namespace_user`(`id`, `namespace_id`, `user_id`, `role`) VALUES (1, 1, 1, 'admin'); -REPLACE INTO `system_config` (`key`, `value`) VALUES ('version', '1.3.4'); +REPLACE INTO `system_config` (`key`, `value`) VALUES ('version', '1.3.6'); diff --git a/service/NetService.go b/service/NetService.go new file mode 100644 index 0000000..0f60373 --- /dev/null +++ b/service/NetService.go @@ -0,0 +1,39 @@ +package service + +import ( + "errors" + "net" + "net/http" + "net/url" + "strconv" + "time" +) + +// Gnet - +type Gnet struct { + URL string +} + +func (gnet Gnet) Ping() error { + urlParse, err := url.Parse(gnet.URL) + if err != nil { + return err + } + switch urlParse.Scheme { + case "tcp": + conn, err := net.DialTimeout("tcp", urlParse.Host, 5*time.Second) + if err != nil { + return err + } + _ = conn.Close() + case "http", "https": + if resp, err := http.Get(urlParse.String()); err != nil { + return err + } else if resp.StatusCode != 200 { + return errors.New("Unexpected response status code: " + strconv.Itoa(resp.StatusCode)) + } + default: + return errors.New("URL scheme " + urlParse.Scheme + " does not supported") + } + return nil +} diff --git a/task/MonitorTask.go b/task/MonitorTask.go index d34dbef..f58e314 100644 --- a/task/MonitorTask.go +++ b/task/MonitorTask.go @@ -4,41 +4,49 @@ import ( "bytes" "database/sql" "encoding/json" - "github.com/patrickmn/go-cache" "github.com/zhenorzz/goploy/core" "github.com/zhenorzz/goploy/model" + "github.com/zhenorzz/goploy/service" "github.com/zhenorzz/goploy/ws" - "net" "net/http" - "strconv" "time" ) +type MonitorCache struct { + errorTimes int + notifyTimes int + time int64 +} + +var monitorCaches = map[int64]MonitorCache{} + func monitorTask() { monitors, err := model.Monitor{State: model.Enable}.GetAllByState() if err != nil && err != sql.ErrNoRows { core.Log(core.ERROR, "get monitor list error, detail:"+err.Error()) } + monitorIDs := map[int64]struct{}{} for _, monitor := range monitors { - monitorCache := map[string]int64{"errorTimes": 0, "notifyTimes": 0, "time": 0} - if x, found := core.Cache.Get("monitor:" + strconv.Itoa(int(monitor.ID))); found { - monitorCache = x.(map[string]int64) + monitorIDs[monitor.ID] = struct{}{} + monitorCache, ok := monitorCaches[monitor.ID] + if !ok { + monitorCaches[monitor.ID] = MonitorCache{} } + now := time.Now().Unix() - if int(now-monitorCache["time"]) > monitor.Second { - monitorCache["time"] = now - conn, err := net.DialTimeout("tcp", monitor.Domain+":"+strconv.Itoa(monitor.Port), 5*time.Second) - if err != nil { - monitorCache["errorTimes"]++ + if int(now-monitorCache.time) > monitor.Second { + monitorCache.time = now + if err := (service.Gnet{URL: monitor.URL}.Ping()); err != nil { + monitorCache.errorTimes++ core.Log(core.ERROR, "monitor "+monitor.Name+" encounter error, "+err.Error()) - if monitor.Times == uint16(monitorCache["errorTimes"]) { - monitorCache["errorTimes"] = 0 - monitorCache["notifyTimes"]++ + if monitor.Times == uint16(monitorCache.errorTimes) { + monitorCache.errorTimes = 0 + monitorCache.notifyTimes++ notice(monitor, err) - if monitor.NotifyTimes == uint16(monitorCache["notifyTimes"]) { - monitorCache["notifyTimes"] = 0 - monitor.TurnOff(err.Error()) + if monitor.NotifyTimes == uint16(monitorCache.notifyTimes) { + monitorCache.notifyTimes = 0 + _ = monitor.TurnOff(err.Error()) ws.GetHub().Data <- &ws.Data{ Type: ws.TypeMonitor, Message: ws.MonitorMessage{MonitorID: monitor.ID, State: ws.MonitorTurnOff, ErrorContent: err.Error()}, @@ -46,10 +54,15 @@ func monitorTask() { } } } else { - monitorCache["errorTimes"] = 0 - conn.Close() + monitorCache.errorTimes = 0 } - core.Cache.Set("monitor:"+strconv.Itoa(int(monitor.ID)), monitorCache, cache.DefaultExpiration) + monitorCaches[monitor.ID] = monitorCache + } + } + + for cacheID := range monitorCaches { + if _, ok := monitorIDs[cacheID]; !ok { + delete(monitorCaches, cacheID) } } } @@ -74,7 +87,7 @@ func notice(monitor model.Monitor, err error) { }, } b, _ := json.Marshal(msg) - http.Post(monitor.NotifyTarget, "application/json", bytes.NewBuffer(b)) + _, _ = http.Post(monitor.NotifyTarget, "application/json", bytes.NewBuffer(b)) } else if monitor.NotifyType == model.NotifyDingTalk { type markdown struct { Title string `json:"title"` @@ -94,7 +107,7 @@ func notice(monitor model.Monitor, err error) { }, } b, _ := json.Marshal(msg) - http.Post(monitor.NotifyTarget, "application/json", bytes.NewBuffer(b)) + _, _ = http.Post(monitor.NotifyTarget, "application/json", bytes.NewBuffer(b)) } else if monitor.NotifyType == model.NotifyFeiShu { type message struct { Title string `json:"title"` @@ -109,14 +122,14 @@ func notice(monitor model.Monitor, err error) { Text: text, } b, _ := json.Marshal(msg) - http.Post(monitor.NotifyTarget, "application/json", bytes.NewBuffer(b)) + _, _ = http.Post(monitor.NotifyTarget, "application/json", bytes.NewBuffer(b)) } else if monitor.NotifyType == model.NotifyCustom { type message struct { Code int `json:"code"` Message string `json:"message"` Data struct { MonitorName string `json:"monitorName"` - Domain string `json:"domain"` + URL string `json:"url"` Port int `json:"port"` Second int `json:"second"` Times uint16 `json:"times"` @@ -128,11 +141,10 @@ func notice(monitor model.Monitor, err error) { Message: "Monitor:" + monitor.Name + "can not access", } msg.Data.MonitorName = monitor.Name - msg.Data.Domain = monitor.Domain - msg.Data.Port = monitor.Port + msg.Data.URL = monitor.URL msg.Data.Second = monitor.Second msg.Data.Times = monitor.Times b, _ := json.Marshal(msg) - http.Post(monitor.NotifyTarget, "application/json", bytes.NewBuffer(b)) + _, _ = http.Post(monitor.NotifyTarget, "application/json", bytes.NewBuffer(b)) } } diff --git a/task/Task.go b/task/Task.go index 4a4aaf6..a94a3b0 100644 --- a/task/Task.go +++ b/task/Task.go @@ -2,6 +2,7 @@ package task import ( "context" + "sync" "sync/atomic" "time" ) @@ -19,16 +20,32 @@ func ticker() { // create ticker minute := time.Tick(time.Minute) second := time.Tick(time.Second) - for { - select { - case <-second: - monitorTask() - case <-minute: - projectTask() - case <-stop: - return + var wg sync.WaitGroup + wg.Add(2) + go func() { + for { + select { + case <-second: + monitorTask() + case <-stop: + wg.Done() + return + } } - } + }() + + go func() { + for { + select { + case <-minute: + projectTask() + case <-stop: + wg.Done() + return + } + } + }() + wg.Wait() } func Shutdown(ctx context.Context) error { diff --git a/web/src/api/monitor.ts b/web/src/api/monitor.ts index 9787485..17b7a3d 100644 --- a/web/src/api/monitor.ts +++ b/web/src/api/monitor.ts @@ -6,8 +6,7 @@ export class MonitorData { id: number namespaceId: number name: string - domain: string - port: number + url: string second: number times: number notifyType: number @@ -49,8 +48,7 @@ export class MonitorAdd extends Request { readonly method = 'post' public param: { name: string - domain: string - port: number + url: string second: number times: number notifyType: number @@ -70,8 +68,7 @@ export class MonitorEdit extends Request { public param: { id: number name: string - domain: string - port: number + url: string second: number times: number notifyType: number @@ -99,8 +96,10 @@ export class MonitorCheck extends Request { readonly url = '/monitor/check' readonly method = 'post' readonly timeout = 100000 - public param: ID - constructor(param: ID) { + public param: { + url: string + } + constructor(param: MonitorCheck['param']) { super() this.param = param } diff --git a/web/src/views/monitor/index.vue b/web/src/views/monitor/index.vue index b2791e4..1ac24ec 100644 --- a/web/src/views/monitor/index.vue +++ b/web/src/views/monitor/index.vue @@ -132,15 +132,15 @@ - - - - - + + + @@ -232,8 +232,7 @@ export default defineComponent({ formData: { id: 0, name: '', - domain: '', - port: 80, + url: '', second: 3, times: 1, notifyType: 1, @@ -243,9 +242,7 @@ export default defineComponent({ }, formRules: { name: [{ required: true, message: 'Name required', trigger: 'blur' }], - domain: [ - { required: true, message: 'Host or IP required', trigger: 'blur' }, - ], + url: [{ required: true, message: 'URL required', trigger: 'blur' }], port: [ { type: 'number',