mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	feat: 新增数据库表信息查看及其他优化
This commit is contained in:
		
							
								
								
									
										40
									
								
								base/cache/timed_cache.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								base/cache/timed_cache.go
									
									
									
									
										vendored
									
									
								
							@@ -52,15 +52,15 @@ type TimedCache struct {
 | 
				
			|||||||
type timedcache struct {
 | 
					type timedcache struct {
 | 
				
			||||||
	defaultExpiration time.Duration
 | 
						defaultExpiration time.Duration
 | 
				
			||||||
	updateAccessTime  bool // 是否更新最后访问时间
 | 
						updateAccessTime  bool // 是否更新最后访问时间
 | 
				
			||||||
	items             map[string]*Item
 | 
						items             map[interface{}]*Item
 | 
				
			||||||
	mu                sync.RWMutex
 | 
						mu                sync.RWMutex
 | 
				
			||||||
	onEvicted         func(string, interface{}) // 移除时回调函数
 | 
						onEvicted         func(interface{}, interface{}) // 移除时回调函数
 | 
				
			||||||
	janitor           *janitor
 | 
						janitor           *janitor
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Add an item to the cache only if an item doesn't already exist for the given
 | 
					// Add an item to the cache only if an item doesn't already exist for the given
 | 
				
			||||||
// key, or if the existing item has expired. Returns an error otherwise.
 | 
					// key, or if the existing item has expired. Returns an error otherwise.
 | 
				
			||||||
func (c *timedcache) Add(k string, x interface{}, d time.Duration) error {
 | 
					func (c *timedcache) Add(k interface{}, x interface{}, d time.Duration) error {
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	defer c.mu.Unlock()
 | 
						defer c.mu.Unlock()
 | 
				
			||||||
	_, found := c.get(k)
 | 
						_, found := c.get(k)
 | 
				
			||||||
@@ -71,13 +71,13 @@ func (c *timedcache) Add(k string, x interface{}, d time.Duration) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *timedcache) Put(k string, x interface{}) {
 | 
					func (c *timedcache) Put(k interface{}, x interface{}) {
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	defer c.mu.Unlock()
 | 
						defer c.mu.Unlock()
 | 
				
			||||||
	c.set(k, x, c.defaultExpiration)
 | 
						c.set(k, x, c.defaultExpiration)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *timedcache) AddIfAbsent(k string, x interface{}) {
 | 
					func (c *timedcache) AddIfAbsent(k interface{}, x interface{}) {
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	defer c.mu.Unlock()
 | 
						defer c.mu.Unlock()
 | 
				
			||||||
	_, found := c.get(k)
 | 
						_, found := c.get(k)
 | 
				
			||||||
@@ -87,7 +87,7 @@ func (c *timedcache) AddIfAbsent(k string, x interface{}) {
 | 
				
			|||||||
	c.set(k, x, c.defaultExpiration)
 | 
						c.set(k, x, c.defaultExpiration)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *timedcache) ComputeIfAbsent(k string, getValueFunc func(string) (interface{}, error)) (interface{}, error) {
 | 
					func (c *timedcache) ComputeIfAbsent(k interface{}, getValueFunc func(interface{}) (interface{}, error)) (interface{}, error) {
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	defer c.mu.Unlock()
 | 
						defer c.mu.Unlock()
 | 
				
			||||||
	value, found := c.get(k)
 | 
						value, found := c.get(k)
 | 
				
			||||||
@@ -103,7 +103,7 @@ func (c *timedcache) ComputeIfAbsent(k string, getValueFunc func(string) (interf
 | 
				
			|||||||
	return value, nil
 | 
						return value, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *timedcache) set(k string, x interface{}, d time.Duration) {
 | 
					func (c *timedcache) set(k interface{}, x interface{}, d time.Duration) {
 | 
				
			||||||
	var e int64
 | 
						var e int64
 | 
				
			||||||
	if d == DefaultExpiration {
 | 
						if d == DefaultExpiration {
 | 
				
			||||||
		d = c.defaultExpiration
 | 
							d = c.defaultExpiration
 | 
				
			||||||
@@ -120,13 +120,13 @@ func (c *timedcache) set(k string, x interface{}, d time.Duration) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Get an item from the cache. Returns the item or nil, and a bool indicating
 | 
					// Get an item from the cache. Returns the item or nil, and a bool indicating
 | 
				
			||||||
// whether the key was found.
 | 
					// whether the key was found.
 | 
				
			||||||
func (c *timedcache) Get(k string) (interface{}, bool) {
 | 
					func (c *timedcache) Get(k interface{}) (interface{}, bool) {
 | 
				
			||||||
	c.mu.RLock()
 | 
						c.mu.RLock()
 | 
				
			||||||
	defer c.mu.RUnlock()
 | 
						defer c.mu.RUnlock()
 | 
				
			||||||
	return c.get(k)
 | 
						return c.get(k)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *timedcache) get(k string) (interface{}, bool) {
 | 
					func (c *timedcache) get(k interface{}) (interface{}, bool) {
 | 
				
			||||||
	item, found := c.items[k]
 | 
						item, found := c.items[k]
 | 
				
			||||||
	if !found {
 | 
						if !found {
 | 
				
			||||||
		return nil, false
 | 
							return nil, false
 | 
				
			||||||
@@ -145,7 +145,7 @@ func (c *timedcache) get(k string) (interface{}, bool) {
 | 
				
			|||||||
// item's value is not an integer, if it was not found, or if it is not
 | 
					// item's value is not an integer, if it was not found, or if it is not
 | 
				
			||||||
// possible to increment it by n. To retrieve the incremented value, use one
 | 
					// possible to increment it by n. To retrieve the incremented value, use one
 | 
				
			||||||
// of the specialized methods, e.g. IncrementInt64.
 | 
					// of the specialized methods, e.g. IncrementInt64.
 | 
				
			||||||
func (c *timedcache) Increment(k string, n int64) error {
 | 
					func (c *timedcache) Increment(k interface{}, n int64) error {
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	v, found := c.items[k]
 | 
						v, found := c.items[k]
 | 
				
			||||||
	if !found || v.Expired() {
 | 
						if !found || v.Expired() {
 | 
				
			||||||
@@ -198,10 +198,10 @@ func (c *timedcache) Count() int {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Copies all unexpired items in the cache into a new map and returns it.
 | 
					// Copies all unexpired items in the cache into a new map and returns it.
 | 
				
			||||||
func (c *timedcache) Items() map[string]*Item {
 | 
					func (c *timedcache) Items() map[interface{}]*Item {
 | 
				
			||||||
	c.mu.RLock()
 | 
						c.mu.RLock()
 | 
				
			||||||
	defer c.mu.RUnlock()
 | 
						defer c.mu.RUnlock()
 | 
				
			||||||
	m := make(map[string]*Item, len(c.items))
 | 
						m := make(map[interface{}]*Item, len(c.items))
 | 
				
			||||||
	now := time.Now().UnixNano()
 | 
						now := time.Now().UnixNano()
 | 
				
			||||||
	for k, v := range c.items {
 | 
						for k, v := range c.items {
 | 
				
			||||||
		// "Inlining" of Expired
 | 
							// "Inlining" of Expired
 | 
				
			||||||
@@ -216,7 +216,7 @@ func (c *timedcache) Items() map[string]*Item {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 删除指定key的数据
 | 
					// 删除指定key的数据
 | 
				
			||||||
func (c *timedcache) Delete(k string) {
 | 
					func (c *timedcache) Delete(k interface{}) {
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	v, evicted := c.delete(k)
 | 
						v, evicted := c.delete(k)
 | 
				
			||||||
	c.mu.Unlock()
 | 
						c.mu.Unlock()
 | 
				
			||||||
@@ -225,7 +225,7 @@ func (c *timedcache) Delete(k string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *timedcache) delete(k string) (interface{}, bool) {
 | 
					func (c *timedcache) delete(k interface{}) (interface{}, bool) {
 | 
				
			||||||
	// 如果有移除回调函数,则返回值及是否有删除回调函数用于进行回调处理
 | 
						// 如果有移除回调函数,则返回值及是否有删除回调函数用于进行回调处理
 | 
				
			||||||
	if c.onEvicted != nil {
 | 
						if c.onEvicted != nil {
 | 
				
			||||||
		if v, found := c.items[k]; found {
 | 
							if v, found := c.items[k]; found {
 | 
				
			||||||
@@ -238,7 +238,7 @@ func (c *timedcache) delete(k string) (interface{}, bool) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type keyAndValue struct {
 | 
					type keyAndValue struct {
 | 
				
			||||||
	key   string
 | 
						key   interface{}
 | 
				
			||||||
	value interface{}
 | 
						value interface{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -265,7 +265,7 @@ func (c *timedcache) DeleteExpired() {
 | 
				
			|||||||
// 清空所有缓存
 | 
					// 清空所有缓存
 | 
				
			||||||
func (c *timedcache) Clear() {
 | 
					func (c *timedcache) Clear() {
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	c.items = map[string]*Item{}
 | 
						c.items = map[interface{}]*Item{}
 | 
				
			||||||
	c.mu.Unlock()
 | 
						c.mu.Unlock()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -378,7 +378,7 @@ func runJanitor(c *timedcache, ci time.Duration) {
 | 
				
			|||||||
	go j.Run(c)
 | 
						go j.Run(c)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newCache(de time.Duration, m map[string]*Item) *timedcache {
 | 
					func newCache(de time.Duration, m map[interface{}]*Item) *timedcache {
 | 
				
			||||||
	if de == 0 {
 | 
						if de == 0 {
 | 
				
			||||||
		de = -1
 | 
							de = -1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -389,7 +389,7 @@ func newCache(de time.Duration, m map[string]*Item) *timedcache {
 | 
				
			|||||||
	return c
 | 
						return c
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]*Item) *TimedCache {
 | 
					func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[interface{}]*Item) *TimedCache {
 | 
				
			||||||
	c := newCache(de, m)
 | 
						c := newCache(de, m)
 | 
				
			||||||
	// This trick ensures that the janitor goroutine (which--granted it
 | 
						// This trick ensures that the janitor goroutine (which--granted it
 | 
				
			||||||
	// was enabled--is running DeleteExpired on c forever) does not keep
 | 
						// was enabled--is running DeleteExpired on c forever) does not keep
 | 
				
			||||||
@@ -410,12 +410,12 @@ func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]*Item)
 | 
				
			|||||||
// manually. If the cleanup interval is less than one, expired items are not
 | 
					// manually. If the cleanup interval is less than one, expired items are not
 | 
				
			||||||
// deleted from the cache before calling c.DeleteExpired().
 | 
					// deleted from the cache before calling c.DeleteExpired().
 | 
				
			||||||
func NewTimedCache(defaultExpiration, cleanupInterval time.Duration) *TimedCache {
 | 
					func NewTimedCache(defaultExpiration, cleanupInterval time.Duration) *TimedCache {
 | 
				
			||||||
	items := make(map[string]*Item)
 | 
						items := make(map[interface{}]*Item)
 | 
				
			||||||
	return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
 | 
						return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 调用删除函数时,会回调该剔除函数
 | 
					// 调用删除函数时,会回调该剔除函数
 | 
				
			||||||
func (c *TimedCache) OnEvicted(f func(string, interface{})) *TimedCache {
 | 
					func (c *TimedCache) OnEvicted(f func(interface{}, interface{})) *TimedCache {
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	c.onEvicted = f
 | 
						c.onEvicted = f
 | 
				
			||||||
	c.mu.Unlock()
 | 
						c.mu.Unlock()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -131,11 +131,9 @@ func (r *RequestWrapper) PostMulipart(files []MultipartFile, reqParams map[strin
 | 
				
			|||||||
		_, err = io.Copy(part, reader)
 | 
							_, err = io.Copy(part, reader)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// 如果有其他参数,则写入body
 | 
						// 如果有其他参数,则写入body
 | 
				
			||||||
	if reqParams != nil {
 | 
						for k, v := range reqParams {
 | 
				
			||||||
		for k, v := range reqParams {
 | 
							if err := writer.WriteField(k, v); err != nil {
 | 
				
			||||||
			if err := writer.WriteField(k, v); err != nil {
 | 
								return createRequestError(err)
 | 
				
			||||||
				return createRequestError(err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := writer.Close(); err != nil {
 | 
						if err := writer.Close(); err != nil {
 | 
				
			||||||
@@ -219,7 +217,7 @@ func request(rw *RequestWrapper) *ResponseWrapper {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func setRequestHeader(req *http.Request, header map[string]string) {
 | 
					func setRequestHeader(req *http.Request, header map[string]string) {
 | 
				
			||||||
	req.Header.Set("User-Agent", "golang/mayflyjob")
 | 
						req.Header.Set("User-Agent", "golang/mayfly")
 | 
				
			||||||
	for k, v := range header {
 | 
						for k, v := range header {
 | 
				
			||||||
		req.Header.Set(k, v)
 | 
							req.Header.Set(k, v)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -145,10 +145,12 @@ func GetByConditionTo(conditionModel interface{}, toModel interface{}) error {
 | 
				
			|||||||
// 获取分页结果
 | 
					// 获取分页结果
 | 
				
			||||||
func GetPage(pageParam *PageParam, conditionModel interface{}, toModels interface{}, orderBy ...string) *PageResult {
 | 
					func GetPage(pageParam *PageParam, conditionModel interface{}, toModels interface{}, orderBy ...string) *PageResult {
 | 
				
			||||||
	var count int64
 | 
						var count int64
 | 
				
			||||||
	global.Db.Model(conditionModel).Where(conditionModel).Count(&count)
 | 
						err := global.Db.Model(conditionModel).Where(conditionModel).Count(&count).Error
 | 
				
			||||||
 | 
						biz.ErrIsNilAppendErr(err, " 查询错误:%s")
 | 
				
			||||||
	if count == 0 {
 | 
						if count == 0 {
 | 
				
			||||||
		return &PageResult{Total: 0, List: []string{}}
 | 
							return &PageResult{Total: 0, List: []string{}}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	page := pageParam.PageNum
 | 
						page := pageParam.PageNum
 | 
				
			||||||
	pageSize := pageParam.PageSize
 | 
						pageSize := pageParam.PageSize
 | 
				
			||||||
	var orderByStr string
 | 
						var orderByStr string
 | 
				
			||||||
@@ -157,7 +159,7 @@ func GetPage(pageParam *PageParam, conditionModel interface{}, toModels interfac
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		orderByStr = strings.Join(orderBy, ",")
 | 
							orderByStr = strings.Join(orderBy, ",")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err := global.Db.Model(conditionModel).Where(conditionModel).Order(orderByStr).Limit(pageSize).Offset((page - 1) * pageSize).Find(toModels).Error
 | 
						err = global.Db.Model(conditionModel).Where(conditionModel).Order(orderByStr).Limit(pageSize).Offset((page - 1) * pageSize).Find(toModels).Error
 | 
				
			||||||
	biz.ErrIsNil(err, "查询失败")
 | 
						biz.ErrIsNil(err, "查询失败")
 | 
				
			||||||
	return &PageResult{Total: count, List: toModels}
 | 
						return &PageResult{Total: count, List: toModels}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,8 +22,8 @@
 | 
				
			|||||||
    "sortablejs": "^1.13.0",
 | 
					    "sortablejs": "^1.13.0",
 | 
				
			||||||
    "sql-formatter": "^2.3.3",
 | 
					    "sql-formatter": "^2.3.3",
 | 
				
			||||||
    "vue": "^3.0.5",
 | 
					    "vue": "^3.0.5",
 | 
				
			||||||
    "vue-class-component": "^8.0.0-0",
 | 
					 | 
				
			||||||
    "vue-router": "^4.0.2",
 | 
					    "vue-router": "^4.0.2",
 | 
				
			||||||
 | 
					    "vue3-json-editor": "^1.1.3",
 | 
				
			||||||
    "vuex": "^4.0.0-rc.2",
 | 
					    "vuex": "^4.0.0-rc.2",
 | 
				
			||||||
    "xterm": "^4.9.0",
 | 
					    "xterm": "^4.9.0",
 | 
				
			||||||
    "xterm-addon-fit": "^0.4.0"
 | 
					    "xterm-addon-fit": "^0.4.0"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
window.globalConfig = {
 | 
					window.globalConfig = {
 | 
				
			||||||
    "BaseApiUrl": "http://localhost:8888/api"
 | 
					    "BaseApiUrl": "/api",
 | 
				
			||||||
 | 
					    "BaseWsUrl": "ws://localhost:8888"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
const config = {
 | 
					const config = {
 | 
				
			||||||
    baseApiUrl:  (window as any).globalConfig.BaseApiUrl
 | 
					    baseApiUrl:  `${(window as any).globalConfig.BaseApiUrl}/api`,
 | 
				
			||||||
 | 
					    baseWsUrl: `${(window as any).globalConfig.BaseWsUrl}/api`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default config
 | 
					export default config
 | 
				
			||||||
							
								
								
									
										21
									
								
								mayfly_go_web/src/common/utils/format.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								mayfly_go_web/src/common/utils/format.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 格式化字节单位
 | 
				
			||||||
 | 
					 * @param size byte size
 | 
				
			||||||
 | 
					 * @returns 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function formatByteSize(size: any) {
 | 
				
			||||||
 | 
					    const value = Number(size);
 | 
				
			||||||
 | 
					        if (size && !isNaN(value)) {
 | 
				
			||||||
 | 
					            const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'];
 | 
				
			||||||
 | 
					            let index = 0;
 | 
				
			||||||
 | 
					            let k = value;
 | 
				
			||||||
 | 
					            if (value >= 1024) {
 | 
				
			||||||
 | 
					                while (k > 1024) {
 | 
				
			||||||
 | 
					                    k = k / 1024;
 | 
				
			||||||
 | 
					                    index++;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return `${k.toFixed(2)}${units[index]}`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return '-';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -32,7 +32,7 @@
 | 
				
			|||||||
                        >编辑</el-button
 | 
					                        >编辑</el-button
 | 
				
			||||||
                    >
 | 
					                    >
 | 
				
			||||||
                    <el-button
 | 
					                    <el-button
 | 
				
			||||||
                       v-auth="permissions.delDb"
 | 
					                        v-auth="permissions.delDb"
 | 
				
			||||||
                        :disabled="chooseId == null"
 | 
					                        :disabled="chooseId == null"
 | 
				
			||||||
                        @click="deleteDb(chooseId)"
 | 
					                        @click="deleteDb(chooseId)"
 | 
				
			||||||
                        type="danger"
 | 
					                        type="danger"
 | 
				
			||||||
@@ -69,6 +69,12 @@
 | 
				
			|||||||
                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
					                    {{ $filters.dateFormat(scope.row.createTime) }}
 | 
				
			||||||
                </template>
 | 
					                </template>
 | 
				
			||||||
            </el-table-column>
 | 
					            </el-table-column>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <el-table-column label="更多信息" min-width="100">
 | 
				
			||||||
 | 
					                <template #default="scope">
 | 
				
			||||||
 | 
					                    <el-link @click.prevent="tableInfo(scope.row)" type="success">表信息</el-link>
 | 
				
			||||||
 | 
					                </template>
 | 
				
			||||||
 | 
					            </el-table-column>
 | 
				
			||||||
        </el-table>
 | 
					        </el-table>
 | 
				
			||||||
        <el-pagination
 | 
					        <el-pagination
 | 
				
			||||||
            @current-change="handlePageChange"
 | 
					            @current-change="handlePageChange"
 | 
				
			||||||
@@ -80,26 +86,99 @@
 | 
				
			|||||||
            :page-size="query.pageSize"
 | 
					            :page-size="query.pageSize"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <db-edit @val-change="valChange" :projects="projects" :title="dbEditDialog.title" v-model:visible="dbEditDialog.visible" v-model:db="dbEditDialog.data"></db-edit>
 | 
					        <el-dialog
 | 
				
			||||||
 | 
					            width="75%"
 | 
				
			||||||
 | 
					            :title="`${chooseData ? chooseData.database : ''} 表信息`"
 | 
				
			||||||
 | 
					            :before-close="closeTableInfo"
 | 
				
			||||||
 | 
					            v-model="tableInfoDialog.visible"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					            <el-table border :data="tableInfoDialog.infos" size="small">
 | 
				
			||||||
 | 
					                <el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip></el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="tableComment" label="备注" min-width="150" show-overflow-tooltip></el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column
 | 
				
			||||||
 | 
					                    prop="tableRows"
 | 
				
			||||||
 | 
					                    label="Rows"
 | 
				
			||||||
 | 
					                    min-width="70"
 | 
				
			||||||
 | 
					                    sortable
 | 
				
			||||||
 | 
					                    :sort-method="(a, b) => parseInt(a.tableRows) - parseInt(b.tableRows)"
 | 
				
			||||||
 | 
					                ></el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column
 | 
				
			||||||
 | 
					                    property="dataLength"
 | 
				
			||||||
 | 
					                    label="数据大小"
 | 
				
			||||||
 | 
					                    sortable
 | 
				
			||||||
 | 
					                    :sort-method="(a, b) => parseInt(a.dataLength) - parseInt(b.dataLength)"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <template #default="scope">
 | 
				
			||||||
 | 
					                        {{ formatByteSize(scope.row.dataLength) }}
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column
 | 
				
			||||||
 | 
					                    property="indexLength"
 | 
				
			||||||
 | 
					                    label="索引大小"
 | 
				
			||||||
 | 
					                    sortable
 | 
				
			||||||
 | 
					                    :sort-method="(a, b) => parseInt(a.indexLength) - parseInt(b.indexLength)"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <template #default="scope">
 | 
				
			||||||
 | 
					                        {{ formatByteSize(scope.row.indexLength) }}
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column property="createTime" label="创建时间" min-width="150"> </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column label="更多信息" min-width="100">
 | 
				
			||||||
 | 
					                    <template #default="scope">
 | 
				
			||||||
 | 
					                        <el-link @click.prevent="showColumns(scope.row)" type="primary">字段</el-link>
 | 
				
			||||||
 | 
					                        <el-link class="ml5" @click.prevent="showTableIndex(scope.row)" type="success">索引</el-link>
 | 
				
			||||||
 | 
					                        <el-link class="ml5" @click.prevent="showCreateDdl(scope.row)" type="info">SQL</el-link>
 | 
				
			||||||
 | 
					                    </template>
 | 
				
			||||||
 | 
					                </el-table-column>
 | 
				
			||||||
 | 
					            </el-table>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog width="40%" :title="`${chooseTableName} 字段信息`" v-model="columnDialog.visible">
 | 
				
			||||||
 | 
					            <el-table border :data="columnDialog.columns" size="mini">
 | 
				
			||||||
 | 
					                <el-table-column prop="columnName" label="名称" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column prop="columnComment" label="备注" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column width="120" prop="columnType" label="类型" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
 | 
					            </el-table>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog width="40%" :title="`${chooseTableName} 索引信息`" v-model="indexDialog.visible">
 | 
				
			||||||
 | 
					            <el-table border :data="indexDialog.indexs" size="mini">
 | 
				
			||||||
 | 
					                <el-table-column prop="indexName" label="索引名" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column prop="columnName" label="列名" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column prop="seqInIndex" label="列序列号" show-overflow-tooltip> </el-table-column>
 | 
				
			||||||
 | 
					                <el-table-column prop="indexType" label="类型"> </el-table-column>
 | 
				
			||||||
 | 
					            </el-table>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <el-dialog width="55%" :title="`${chooseTableName} Create-DDL`" v-model="ddlDialog.visible">
 | 
				
			||||||
 | 
					            <el-input disabled type="textarea" :autosize="{ minRows: 15, maxRows: 30 }" v-model="ddlDialog.ddl"> </el-input>
 | 
				
			||||||
 | 
					        </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <db-edit
 | 
				
			||||||
 | 
					            @val-change="valChange"
 | 
				
			||||||
 | 
					            :projects="projects"
 | 
				
			||||||
 | 
					            :title="dbEditDialog.title"
 | 
				
			||||||
 | 
					            v-model:visible="dbEditDialog.visible"
 | 
				
			||||||
 | 
					            v-model:db="dbEditDialog.data"
 | 
				
			||||||
 | 
					        ></db-edit>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang='ts'>
 | 
					<script lang='ts'>
 | 
				
			||||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
					import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
 | 
					import { formatByteSize } from '@/common/utils/format';
 | 
				
			||||||
import DbEdit from './DbEdit.vue';
 | 
					import DbEdit from './DbEdit.vue';
 | 
				
			||||||
import { dbApi } from './api';
 | 
					import { dbApi } from './api';
 | 
				
			||||||
import { projectApi } from '../project/api.ts';
 | 
					import { projectApi } from '../project/api.ts';
 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
    name: 'DbList',
 | 
					    name: 'DbList',
 | 
				
			||||||
    components: {
 | 
					    components: {
 | 
				
			||||||
        ProjectEnvSelect,
 | 
					 | 
				
			||||||
        DbEdit,
 | 
					        DbEdit,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup() {
 | 
					    setup() {
 | 
				
			||||||
        const state = reactive({
 | 
					        const state = reactive({
 | 
				
			||||||
             permissions: {
 | 
					            permissions: {
 | 
				
			||||||
                saveDb: 'db:save',
 | 
					                saveDb: 'db:save',
 | 
				
			||||||
                delDb: 'db:del',
 | 
					                delDb: 'db:del',
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@@ -118,6 +197,24 @@ export default defineComponent({
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            datas: [],
 | 
					            datas: [],
 | 
				
			||||||
            total: 0,
 | 
					            total: 0,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            chooseTableName: '',
 | 
				
			||||||
 | 
					            tableInfoDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                infos: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            columnDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                columns: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            indexDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                indexs: [],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            ddlDialog: {
 | 
				
			||||||
 | 
					                visible: false,
 | 
				
			||||||
 | 
					                ddl: '',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            dbEditDialog: {
 | 
					            dbEditDialog: {
 | 
				
			||||||
                visible: false,
 | 
					                visible: false,
 | 
				
			||||||
                data: null,
 | 
					                data: null,
 | 
				
			||||||
@@ -179,6 +276,47 @@ export default defineComponent({
 | 
				
			|||||||
            } catch (err) {}
 | 
					            } catch (err) {}
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const tableInfo = async (row: any) => {
 | 
				
			||||||
 | 
					            state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: row.id });
 | 
				
			||||||
 | 
					            state.tableInfoDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const closeTableInfo = () => {
 | 
				
			||||||
 | 
					            state.tableInfoDialog.visible = false;
 | 
				
			||||||
 | 
					            state.tableInfoDialog.infos = [];
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const showColumns = async (row: any) => {
 | 
				
			||||||
 | 
					            state.chooseTableName = row.tableName;
 | 
				
			||||||
 | 
					            state.columnDialog.columns = await dbApi.columnMetadata.request({
 | 
				
			||||||
 | 
					                id: state.chooseId,
 | 
				
			||||||
 | 
					                tableName: row.tableName,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            state.columnDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const showTableIndex = async (row: any) => {
 | 
				
			||||||
 | 
					            state.chooseTableName = row.tableName;
 | 
				
			||||||
 | 
					            state.indexDialog.indexs = await dbApi.tableIndex.request({
 | 
				
			||||||
 | 
					                id: state.chooseId,
 | 
				
			||||||
 | 
					                tableName: row.tableName,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            state.indexDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const showCreateDdl = async (row: any) => {
 | 
				
			||||||
 | 
					            state.chooseTableName = row.tableName;
 | 
				
			||||||
 | 
					            const res = await dbApi.tableDdl.request({
 | 
				
			||||||
 | 
					                id: state.chooseId,
 | 
				
			||||||
 | 
					                tableName: row.tableName,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            state.ddlDialog.ddl = res[0]['Create Table'];
 | 
				
			||||||
 | 
					            console.log(state.ddlDialog);
 | 
				
			||||||
 | 
					            state.ddlDialog.visible = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            ...toRefs(state),
 | 
					            ...toRefs(state),
 | 
				
			||||||
            // enums,
 | 
					            // enums,
 | 
				
			||||||
@@ -188,6 +326,12 @@ export default defineComponent({
 | 
				
			|||||||
            editDb,
 | 
					            editDb,
 | 
				
			||||||
            valChange,
 | 
					            valChange,
 | 
				
			||||||
            deleteDb,
 | 
					            deleteDb,
 | 
				
			||||||
 | 
					            tableInfo,
 | 
				
			||||||
 | 
					            closeTableInfo,
 | 
				
			||||||
 | 
					            showColumns,
 | 
				
			||||||
 | 
					            showTableIndex,
 | 
				
			||||||
 | 
					            showCreateDdl,
 | 
				
			||||||
 | 
					            formatByteSize,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,9 @@ export const dbApi = {
 | 
				
			|||||||
    dbs: Api.create("/dbs", 'get'),
 | 
					    dbs: Api.create("/dbs", 'get'),
 | 
				
			||||||
    saveDb: Api.create("/dbs", 'post'),
 | 
					    saveDb: Api.create("/dbs", 'post'),
 | 
				
			||||||
    deleteDb: Api.create("/dbs/{id}", 'delete'),
 | 
					    deleteDb: Api.create("/dbs/{id}", 'delete'),
 | 
				
			||||||
 | 
					    tableInfos: Api.create("/dbs/{id}/t-infos", 'get'),
 | 
				
			||||||
 | 
					    tableIndex: Api.create("/dbs/{id}/t-index", 'get'),
 | 
				
			||||||
 | 
					    tableDdl: Api.create("/dbs/{id}/t-create-ddl", 'get'),
 | 
				
			||||||
    tableMetadata: Api.create("/dbs/{id}/t-metadata", 'get'),
 | 
					    tableMetadata: Api.create("/dbs/{id}/t-metadata", 'get'),
 | 
				
			||||||
    columnMetadata: Api.create("/dbs/{id}/c-metadata", 'get'),
 | 
					    columnMetadata: Api.create("/dbs/{id}/c-metadata", 'get'),
 | 
				
			||||||
    // 获取表即列提示
 | 
					    // 获取表即列提示
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,10 +98,8 @@
 | 
				
			|||||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
					import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
				
			||||||
import { useRouter } from 'vue-router';
 | 
					import { useRouter } from 'vue-router';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { DynamicFormDialog } from '@/components/dynamic-form';
 | 
					 | 
				
			||||||
// import Monitor from './Monitor.vue';
 | 
					// import Monitor from './Monitor.vue';
 | 
				
			||||||
import { machineApi } from './api';
 | 
					import { machineApi } from './api';
 | 
				
			||||||
import SshTerminal from './SshTerminal.vue';
 | 
					 | 
				
			||||||
import ServiceManage from './ServiceManage.vue';
 | 
					import ServiceManage from './ServiceManage.vue';
 | 
				
			||||||
import FileManage from './FileManage.vue';
 | 
					import FileManage from './FileManage.vue';
 | 
				
			||||||
import MachineEdit from './MachineEdit.vue';
 | 
					import MachineEdit from './MachineEdit.vue';
 | 
				
			||||||
@@ -109,10 +107,8 @@ import MachineEdit from './MachineEdit.vue';
 | 
				
			|||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
    name: 'MachineList',
 | 
					    name: 'MachineList',
 | 
				
			||||||
    components: {
 | 
					    components: {
 | 
				
			||||||
        SshTerminal,
 | 
					 | 
				
			||||||
        ServiceManage,
 | 
					        ServiceManage,
 | 
				
			||||||
        FileManage,
 | 
					        FileManage,
 | 
				
			||||||
        DynamicFormDialog,
 | 
					 | 
				
			||||||
        MachineEdit,
 | 
					        MachineEdit,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup() {
 | 
					    setup() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import 'xterm/css/xterm.css';
 | 
				
			|||||||
import { Terminal } from 'xterm';
 | 
					import { Terminal } from 'xterm';
 | 
				
			||||||
import { FitAddon } from 'xterm-addon-fit';
 | 
					import { FitAddon } from 'xterm-addon-fit';
 | 
				
			||||||
import { getSession } from '@/common/utils/storage.ts';
 | 
					import { getSession } from '@/common/utils/storage.ts';
 | 
				
			||||||
 | 
					import config from '@/common/config'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: 'Xterm',
 | 
					    name: 'Xterm',
 | 
				
			||||||
@@ -82,7 +83,7 @@ export default {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        initSocket() {
 | 
					        initSocket() {
 | 
				
			||||||
            this.socket = new WebSocket(`ws://localhost:8888/api/machines/${this.machineId}/terminal?token=${getSession('token')}`);
 | 
					            this.socket = new WebSocket(`${config.baseWsUrl}/machines/${this.machineId}/terminal?token=${getSession('token')}`);
 | 
				
			||||||
            // 监听socket连接
 | 
					            // 监听socket连接
 | 
				
			||||||
            this.socket.onopen = this.open;
 | 
					            this.socket.onopen = this.open;
 | 
				
			||||||
            // 监听socket错误信息
 | 
					            // 监听socket错误信息
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            <el-button @click="showEnv(chooseData)" :disabled="chooseId == null" type="info" icon="el-icon-setting" size="mini">环境管理</el-button>
 | 
					            <el-button @click="showEnv(chooseData)" :disabled="chooseId == null" type="info" icon="el-icon-setting" size="mini">环境管理</el-button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <el-button v-auth="'role:del'" :disabled="chooseId == null" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
 | 
					            <el-button
 | 
				
			||||||
 | 
					                v-auth="permissions.delProject"
 | 
				
			||||||
 | 
					                @click="delProject"
 | 
				
			||||||
 | 
					                :disabled="chooseId == null"
 | 
				
			||||||
 | 
					                type="danger"
 | 
				
			||||||
 | 
					                icon="el-icon-delete"
 | 
				
			||||||
 | 
					                size="mini"
 | 
				
			||||||
 | 
					                >删除</el-button
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div style="float: right">
 | 
					            <div style="float: right">
 | 
				
			||||||
                <el-input
 | 
					                <el-input
 | 
				
			||||||
@@ -69,7 +77,7 @@
 | 
				
			|||||||
        <el-dialog width="400px" title="项目编辑" :before-close="cancelAddProject" v-model="addProjectDialog.visible">
 | 
					        <el-dialog width="400px" title="项目编辑" :before-close="cancelAddProject" v-model="addProjectDialog.visible">
 | 
				
			||||||
            <el-form :model="addProjectDialog.form" size="small" label-width="70px">
 | 
					            <el-form :model="addProjectDialog.form" size="small" label-width="70px">
 | 
				
			||||||
                <el-form-item label="项目名:" required>
 | 
					                <el-form-item label="项目名:" required>
 | 
				
			||||||
                    <el-input :disabled="addProjectDialog.form.id" v-model="addProjectDialog.form.name" auto-complete="off"></el-input>
 | 
					                    <el-input :disabled="addProjectDialog.form.id ? true : false" v-model="addProjectDialog.form.name" auto-complete="off"></el-input>
 | 
				
			||||||
                </el-form-item>
 | 
					                </el-form-item>
 | 
				
			||||||
                <el-form-item label="描述:">
 | 
					                <el-form-item label="描述:">
 | 
				
			||||||
                    <el-input v-model="addProjectDialog.form.remark" auto-complete="off"></el-input>
 | 
					                    <el-input v-model="addProjectDialog.form.remark" auto-complete="off"></el-input>
 | 
				
			||||||
@@ -160,7 +168,14 @@
 | 
				
			|||||||
            <el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
 | 
					            <el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
 | 
				
			||||||
                <el-form :model="showMemDialog.memForm" size="small" label-width="70px">
 | 
					                <el-form :model="showMemDialog.memForm" size="small" label-width="70px">
 | 
				
			||||||
                    <el-form-item label="账号:">
 | 
					                    <el-form-item label="账号:">
 | 
				
			||||||
                        <el-select style="width: 100%" remote :remote-method="getAccount" v-model="showMemDialog.memForm.accountId" filterable placeholder="请选择">
 | 
					                        <el-select
 | 
				
			||||||
 | 
					                            style="width: 100%"
 | 
				
			||||||
 | 
					                            remote
 | 
				
			||||||
 | 
					                            :remote-method="getAccount"
 | 
				
			||||||
 | 
					                            v-model="showMemDialog.memForm.accountId"
 | 
				
			||||||
 | 
					                            filterable
 | 
				
			||||||
 | 
					                            placeholder="请选择"
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
                            <el-option v-for="item in showMemDialog.accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
 | 
					                            <el-option v-for="item in showMemDialog.accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
 | 
				
			||||||
                        </el-select>
 | 
					                        </el-select>
 | 
				
			||||||
                    </el-form-item>
 | 
					                    </el-form-item>
 | 
				
			||||||
@@ -187,7 +202,6 @@ import { projectApi } from './api';
 | 
				
			|||||||
import { accountApi } from '../../system/api';
 | 
					import { accountApi } from '../../system/api';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { notEmpty, notNull } from '@/common/assert';
 | 
					import { notEmpty, notNull } from '@/common/assert';
 | 
				
			||||||
import { auth } from '../../../common/utils/authFunction';
 | 
					 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
    name: 'ProjectList',
 | 
					    name: 'ProjectList',
 | 
				
			||||||
    components: {},
 | 
					    components: {},
 | 
				
			||||||
@@ -195,6 +209,7 @@ export default defineComponent({
 | 
				
			|||||||
        const state = reactive({
 | 
					        const state = reactive({
 | 
				
			||||||
            permissions: {
 | 
					            permissions: {
 | 
				
			||||||
                saveProject: 'project:save',
 | 
					                saveProject: 'project:save',
 | 
				
			||||||
 | 
					                delProject: 'project:del',
 | 
				
			||||||
                saveMember: 'project:member:add',
 | 
					                saveMember: 'project:member:add',
 | 
				
			||||||
                delMember: 'project:member:del',
 | 
					                delMember: 'project:member:del',
 | 
				
			||||||
                saveEnv: 'project:env:add',
 | 
					                saveEnv: 'project:env:add',
 | 
				
			||||||
@@ -262,7 +277,7 @@ export default defineComponent({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const showAddProjectDialog = (data: any) => {
 | 
					        const showAddProjectDialog = (data: any) => {
 | 
				
			||||||
            if (data) {
 | 
					            if (data) {
 | 
				
			||||||
                state.addProjectDialog.form = data;
 | 
					                state.addProjectDialog.form = { ...data };
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                state.addProjectDialog.form = {} as any;
 | 
					                state.addProjectDialog.form = {} as any;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -285,6 +300,21 @@ export default defineComponent({
 | 
				
			|||||||
            cancelAddProject();
 | 
					            cancelAddProject();
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const delProject = async () => {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                await ElMessageBox.confirm(`确定删除该项目?`, '提示', {
 | 
				
			||||||
 | 
					                    confirmButtonText: '确定',
 | 
				
			||||||
 | 
					                    cancelButtonText: '取消',
 | 
				
			||||||
 | 
					                    type: 'warning',
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                await projectApi.delProject.request({ id: state.chooseId });
 | 
				
			||||||
 | 
					                ElMessage.success('删除成功');
 | 
				
			||||||
 | 
					                state.chooseData = null;
 | 
				
			||||||
 | 
					                state.chooseId = null;
 | 
				
			||||||
 | 
					                search();
 | 
				
			||||||
 | 
					            } catch (err) {}
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const choose = (item: any) => {
 | 
					        const choose = (item: any) => {
 | 
				
			||||||
            if (!item) {
 | 
					            if (!item) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@@ -380,11 +410,6 @@ export default defineComponent({
 | 
				
			|||||||
            state.showEnvDialog.addVisible = false;
 | 
					            state.showEnvDialog.addVisible = false;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const roleEditChange = (data: any) => {
 | 
					 | 
				
			||||||
            ElMessage.success('修改成功!');
 | 
					 | 
				
			||||||
            search();
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            ...toRefs(state),
 | 
					            ...toRefs(state),
 | 
				
			||||||
            search,
 | 
					            search,
 | 
				
			||||||
@@ -392,6 +417,7 @@ export default defineComponent({
 | 
				
			|||||||
            choose,
 | 
					            choose,
 | 
				
			||||||
            showAddProjectDialog,
 | 
					            showAddProjectDialog,
 | 
				
			||||||
            addProject,
 | 
					            addProject,
 | 
				
			||||||
 | 
					            delProject,
 | 
				
			||||||
            cancelAddProject,
 | 
					            cancelAddProject,
 | 
				
			||||||
            showMembers,
 | 
					            showMembers,
 | 
				
			||||||
            setMemebers,
 | 
					            setMemebers,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ export const projectApi = {
 | 
				
			|||||||
    accountProjects: Api.create("/accounts/projects", 'get'),
 | 
					    accountProjects: Api.create("/accounts/projects", 'get'),
 | 
				
			||||||
    projects: Api.create("/projects", 'get'),
 | 
					    projects: Api.create("/projects", 'get'),
 | 
				
			||||||
    saveProject: Api.create("/projects", 'post'),
 | 
					    saveProject: Api.create("/projects", 'post'),
 | 
				
			||||||
 | 
					    delProject: Api.create("/projects", 'delete'),
 | 
				
			||||||
    // 获取项目下的环境信息
 | 
					    // 获取项目下的环境信息
 | 
				
			||||||
    projectEnvs:  Api.create("/projects/{projectId}/envs", 'get'),
 | 
					    projectEnvs:  Api.create("/projects/{projectId}/envs", 'get'),
 | 
				
			||||||
    saveProjectEnv:  Api.create("/projects/{projectId}/envs", 'post'),
 | 
					    saveProjectEnv:  Api.create("/projects/{projectId}/envs", 'post'),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,174 +195,6 @@ export default defineComponent({
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
// @Component({
 | 
					 | 
				
			||||||
//   name: 'RedisList',
 | 
					 | 
				
			||||||
//   components: {
 | 
					 | 
				
			||||||
//     Info,
 | 
					 | 
				
			||||||
//     DynamicFormDialog
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
// })
 | 
					 | 
				
			||||||
// export default class RedisList extends Vue {
 | 
					 | 
				
			||||||
//   validatePort = (rule: any, value: any, callback: any) => {
 | 
					 | 
				
			||||||
//     if (value > 65535 || value < 1) {
 | 
					 | 
				
			||||||
//       callback(new Error('端口号错误'))
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
//     callback()
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   redisTable = []
 | 
					 | 
				
			||||||
//   permission = redisPermission
 | 
					 | 
				
			||||||
//   keyPermission = redisKeyPermission
 | 
					 | 
				
			||||||
//   currentId = null
 | 
					 | 
				
			||||||
//   currentData: any = null
 | 
					 | 
				
			||||||
//   params = {
 | 
					 | 
				
			||||||
//     host: null,
 | 
					 | 
				
			||||||
//     clusterId: null
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
//   redisInfo = {
 | 
					 | 
				
			||||||
//     url: ''
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
//   clusters = [
 | 
					 | 
				
			||||||
//     {
 | 
					 | 
				
			||||||
//       id: 0,
 | 
					 | 
				
			||||||
//       name: '单机'
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
//   ]
 | 
					 | 
				
			||||||
//   infoDialog = {
 | 
					 | 
				
			||||||
//     title: '',
 | 
					 | 
				
			||||||
//     visible: false,
 | 
					 | 
				
			||||||
//     info: {
 | 
					 | 
				
			||||||
//       Server: {},
 | 
					 | 
				
			||||||
//       Keyspace: {},
 | 
					 | 
				
			||||||
//       Clients: {},
 | 
					 | 
				
			||||||
//       CPU: {},
 | 
					 | 
				
			||||||
//       Memory: {}
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
//   formDialog = {
 | 
					 | 
				
			||||||
//     visible: false,
 | 
					 | 
				
			||||||
//     title: '',
 | 
					 | 
				
			||||||
//     formInfo: {
 | 
					 | 
				
			||||||
//       createApi: redisApi.save,
 | 
					 | 
				
			||||||
//       updateApi: redisApi.update,
 | 
					 | 
				
			||||||
//       formRows: [
 | 
					 | 
				
			||||||
//         [
 | 
					 | 
				
			||||||
//           {
 | 
					 | 
				
			||||||
//             type: 'input',
 | 
					 | 
				
			||||||
//             label: '主机:',
 | 
					 | 
				
			||||||
//             name: 'host',
 | 
					 | 
				
			||||||
//             placeholder: '请输入节点ip',
 | 
					 | 
				
			||||||
//             rules: [
 | 
					 | 
				
			||||||
//               {
 | 
					 | 
				
			||||||
//                 required: true,
 | 
					 | 
				
			||||||
//                 message: '请输入节点ip',
 | 
					 | 
				
			||||||
//                 trigger: ['blur', 'change']
 | 
					 | 
				
			||||||
//               }
 | 
					 | 
				
			||||||
//             ]
 | 
					 | 
				
			||||||
//           }
 | 
					 | 
				
			||||||
//         ],
 | 
					 | 
				
			||||||
//         [
 | 
					 | 
				
			||||||
//           {
 | 
					 | 
				
			||||||
//             type: 'input',
 | 
					 | 
				
			||||||
//             label: '端口号:',
 | 
					 | 
				
			||||||
//             name: 'port',
 | 
					 | 
				
			||||||
//             placeholder: '请输入节点端口号',
 | 
					 | 
				
			||||||
//             inputType: 'number',
 | 
					 | 
				
			||||||
//             rules: [
 | 
					 | 
				
			||||||
//               {
 | 
					 | 
				
			||||||
//                 required: true,
 | 
					 | 
				
			||||||
//                 message: '请输入节点端口号',
 | 
					 | 
				
			||||||
//                 trigger: ['blur', 'change']
 | 
					 | 
				
			||||||
//               }
 | 
					 | 
				
			||||||
//             ]
 | 
					 | 
				
			||||||
//           }
 | 
					 | 
				
			||||||
//         ],
 | 
					 | 
				
			||||||
//         [
 | 
					 | 
				
			||||||
//           {
 | 
					 | 
				
			||||||
//             type: 'input',
 | 
					 | 
				
			||||||
//             label: '密码:',
 | 
					 | 
				
			||||||
//             name: 'pwd',
 | 
					 | 
				
			||||||
//             placeholder: '请输入节点密码',
 | 
					 | 
				
			||||||
//             inputType: 'password'
 | 
					 | 
				
			||||||
//           }
 | 
					 | 
				
			||||||
//         ],
 | 
					 | 
				
			||||||
//         [
 | 
					 | 
				
			||||||
//           {
 | 
					 | 
				
			||||||
//             type: 'input',
 | 
					 | 
				
			||||||
//             label: '描述:',
 | 
					 | 
				
			||||||
//             name: 'description',
 | 
					 | 
				
			||||||
//             placeholder: '请输入节点描述',
 | 
					 | 
				
			||||||
//             inputType: 'textarea'
 | 
					 | 
				
			||||||
//           }
 | 
					 | 
				
			||||||
//         ]
 | 
					 | 
				
			||||||
//       ]
 | 
					 | 
				
			||||||
//     },
 | 
					 | 
				
			||||||
//     formData: { port: 6379 }
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   mounted() {
 | 
					 | 
				
			||||||
//     this.search()
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   choose(item: any) {
 | 
					 | 
				
			||||||
//     if (!item) {
 | 
					 | 
				
			||||||
//       return
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
//     this.currentId = item.id
 | 
					 | 
				
			||||||
//     this.currentData = item
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   // connect() {
 | 
					 | 
				
			||||||
//   //   Req.post('/open/redis/connect', this.form, res => {
 | 
					 | 
				
			||||||
//   //     this.redisInfo = res
 | 
					 | 
				
			||||||
//   //   })
 | 
					 | 
				
			||||||
//   // }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   async deleteNode() {
 | 
					 | 
				
			||||||
//     await redisApi.del.request({ id: this.currentId })
 | 
					 | 
				
			||||||
//     this.$message.success('删除成功')
 | 
					 | 
				
			||||||
//     this.search()
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   manage(row: any) {
 | 
					 | 
				
			||||||
//     this.$router.push(`/redis_operation/${row.clusterId}/${row.id}`)
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   info(redis: any) {
 | 
					 | 
				
			||||||
//     redisApi.info.request({ id: redis.id }).then(res => {
 | 
					 | 
				
			||||||
//       this.infoDialog.info = res
 | 
					 | 
				
			||||||
//       this.infoDialog.title = `'${redis.host}' info`
 | 
					 | 
				
			||||||
//       this.infoDialog.visible = true
 | 
					 | 
				
			||||||
//     })
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   search() {
 | 
					 | 
				
			||||||
//     redisApi.list.request(this.params).then(res => {
 | 
					 | 
				
			||||||
//       this.redisTable = res
 | 
					 | 
				
			||||||
//     })
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   openFormDialog(redis: any) {
 | 
					 | 
				
			||||||
//     let dialogTitle
 | 
					 | 
				
			||||||
//     if (redis) {
 | 
					 | 
				
			||||||
//       this.formDialog.formData = this.currentData
 | 
					 | 
				
			||||||
//       dialogTitle = '编辑redis节点'
 | 
					 | 
				
			||||||
//     } else {
 | 
					 | 
				
			||||||
//       this.formDialog.formData = { port: 6379 }
 | 
					 | 
				
			||||||
//       dialogTitle = '添加redis节点'
 | 
					 | 
				
			||||||
//     }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//     this.formDialog.title = dialogTitle
 | 
					 | 
				
			||||||
//     this.formDialog.visible = true
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//   submitSuccess() {
 | 
					 | 
				
			||||||
//     this.currentId = null
 | 
					 | 
				
			||||||
//     this.currentData = null
 | 
					 | 
				
			||||||
//     this.search()
 | 
					 | 
				
			||||||
//   }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// }
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <el-dialog :title="keyValue.key" v-model="visible" :before-close="cancel" :show-close="false" width="750px">
 | 
					    <el-dialog :title="keyValue.key" v-model="visible" :before-close="cancel" :show-close="false" width="800px">
 | 
				
			||||||
        <el-form>
 | 
					        <el-form>
 | 
				
			||||||
            <el-form-item>
 | 
					            <el-form-item>
 | 
				
			||||||
                <el-input v-model="keyValue.value" type="textarea" :autosize="{ minRows: 10, maxRows: 20 }" autocomplete="off"></el-input>
 | 
					                <!-- <el-input v-model="keyValue.value" type="textarea" :autosize="{ minRows: 10, maxRows: 20 }" autocomplete="off"></el-input> -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
            </el-form-item>
 | 
					            </el-form-item>
 | 
				
			||||||
 | 
					            <vue3-json-editor v-model="keyValue.jsonValue" @json-change="valueChange" :show-btns="false" :expandedOnStart="true" />
 | 
				
			||||||
        </el-form>
 | 
					        </el-form>
 | 
				
			||||||
        <template #footer>
 | 
					        <template #footer>
 | 
				
			||||||
            <div class="dialog-footer">
 | 
					            <div class="dialog-footer">
 | 
				
			||||||
@@ -18,8 +21,13 @@ import { defineComponent, reactive, watch, toRefs } from 'vue';
 | 
				
			|||||||
import { redisApi } from './api';
 | 
					import { redisApi } from './api';
 | 
				
			||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
import { isTrue } from '@/common/assert';
 | 
					import { isTrue } from '@/common/assert';
 | 
				
			||||||
 | 
					import { Vue3JsonEditor } from 'vue3-json-editor';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
    name: 'ValueDialog',
 | 
					    name: 'ValueDialog',
 | 
				
			||||||
 | 
					    components: {
 | 
				
			||||||
 | 
					        Vue3JsonEditor,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    props: {
 | 
					    props: {
 | 
				
			||||||
        visible: {
 | 
					        visible: {
 | 
				
			||||||
            type: Boolean,
 | 
					            type: Boolean,
 | 
				
			||||||
@@ -52,23 +60,30 @@ export default defineComponent({
 | 
				
			|||||||
            () => props.keyValue,
 | 
					            () => props.keyValue,
 | 
				
			||||||
            (val) => {
 | 
					            (val) => {
 | 
				
			||||||
                state.keyValue = val;
 | 
					                state.keyValue = val;
 | 
				
			||||||
                if (state.keyValue.type != 'string') {
 | 
					                if (typeof val.value == 'string') {
 | 
				
			||||||
                    state.keyValue.value = JSON.stringify(val.value, undefined, 2)
 | 
					                    state.keyValue.jsonValue = JSON.parse(val.value)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    state.keyValue.jsonValue = val.value;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                // state.keyValue.value = JSON.stringify(val.value, undefined, 2)
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const saveValue = async () => {
 | 
					        const saveValue = async () => {
 | 
				
			||||||
            isTrue(state.keyValue.type == 'string', "暂不支持除string外其他类型修改")
 | 
					            isTrue(state.keyValue.type == 'string', '暂不支持除string外其他类型修改');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await redisApi.saveStringValue.request(state.keyValue);
 | 
					            await redisApi.saveStringValue.request(state.keyValue);
 | 
				
			||||||
            ElMessage.success('保存成功');
 | 
					            ElMessage.success('保存成功');
 | 
				
			||||||
            cancel();
 | 
					            cancel();
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const valueChange = (val: any) => {
 | 
				
			||||||
 | 
					            state.keyValue.value = JSON.stringify(val);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            ...toRefs(state),
 | 
					            ...toRefs(state),
 | 
				
			||||||
            saveValue,
 | 
					            saveValue,
 | 
				
			||||||
 | 
					            valueChange,
 | 
				
			||||||
            cancel,
 | 
					            cancel,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,7 +91,6 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
					import { toRefs, reactive, onMounted, defineComponent } from 'vue';
 | 
				
			||||||
import RoleEdit from './RoleEdit.vue';
 | 
					import RoleEdit from './RoleEdit.vue';
 | 
				
			||||||
import { rolePermission } from '../permissions';
 | 
					 | 
				
			||||||
import ResourceEdit from './ResourceEdit.vue';
 | 
					import ResourceEdit from './ResourceEdit.vue';
 | 
				
			||||||
import ShowResource from './ShowResource.vue';
 | 
					import ShowResource from './ShowResource.vue';
 | 
				
			||||||
import { roleApi, resourceApi } from '../api';
 | 
					import { roleApi, resourceApi } from '../api';
 | 
				
			||||||
@@ -157,7 +156,7 @@ export default defineComponent({
 | 
				
			|||||||
            state.chooseData = item;
 | 
					            state.chooseData = item;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const roleEditChange = (data: any) => {
 | 
					        const roleEditChange = () => {
 | 
				
			||||||
            ElMessage.success('修改成功!');
 | 
					            ElMessage.success('修改成功!');
 | 
				
			||||||
            search();
 | 
					            search();
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +0,0 @@
 | 
				
			|||||||
app:
 | 
					 | 
				
			||||||
  name: mock-server
 | 
					 | 
				
			||||||
  version: 1.0.0
 | 
					 | 
				
			||||||
server:
 | 
					 | 
				
			||||||
  # debug release test
 | 
					 | 
				
			||||||
  model: release
 | 
					 | 
				
			||||||
  port: 8888
 | 
					 | 
				
			||||||
  cors: true
 | 
					 | 
				
			||||||
  # 静态资源
 | 
					 | 
				
			||||||
  static:
 | 
					 | 
				
			||||||
    - relative-path: /static
 | 
					 | 
				
			||||||
      root: ./static/static
 | 
					 | 
				
			||||||
  # 静态文件
 | 
					 | 
				
			||||||
  static-file:
 | 
					 | 
				
			||||||
    - relative-path: /
 | 
					 | 
				
			||||||
      filepath: ./static/index.html
 | 
					 | 
				
			||||||
    - relative-path: /favicon.ico
 | 
					 | 
				
			||||||
      filepath: ./static/favicon.ico
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
redis:
 | 
					 | 
				
			||||||
  host: 127.0.0.1
 | 
					 | 
				
			||||||
  port: 6379
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mysql:
 | 
					 | 
				
			||||||
  host: localhost:3306
 | 
					 | 
				
			||||||
  username: root
 | 
					 | 
				
			||||||
  password: 111049
 | 
					 | 
				
			||||||
  db-name: mayfly-job
 | 
					 | 
				
			||||||
  config: charset=utf8&loc=Local&parseTime=true
 | 
					 | 
				
			||||||
@@ -1,23 +0,0 @@
 | 
				
			|||||||
package form
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type MockData struct {
 | 
					 | 
				
			||||||
	Method        string   `json:"method" binding:"required"`
 | 
					 | 
				
			||||||
	Enable        uint     `json:"enable"`
 | 
					 | 
				
			||||||
	Description   string   `valid:"Required" json:"description"`
 | 
					 | 
				
			||||||
	Data          string   `valid:"Required" json:"data"`
 | 
					 | 
				
			||||||
	EffectiveUser []string `json:"effectiveUser"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Machine struct {
 | 
					 | 
				
			||||||
	Name     string `json:"name"`
 | 
					 | 
				
			||||||
	Ip       string `json:"ip"`       // IP地址
 | 
					 | 
				
			||||||
	Username string `json:"username"` // 用户名
 | 
					 | 
				
			||||||
	Password string `json:"-"`
 | 
					 | 
				
			||||||
	Port     int    `json:"port"` // 端口号
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type MachineService struct {
 | 
					 | 
				
			||||||
	Name    string `json:"name"`
 | 
					 | 
				
			||||||
	Ip      string `json:"ip"`      // IP地址
 | 
					 | 
				
			||||||
	Service string `json:"service"` // 服务命令
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,79 +0,0 @@
 | 
				
			|||||||
package controllers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"mayfly-go/base/biz"
 | 
					 | 
				
			||||||
	"mayfly-go/base/ctx"
 | 
					 | 
				
			||||||
	"mayfly-go/base/ginx"
 | 
					 | 
				
			||||||
	"mayfly-go/base/rediscli"
 | 
					 | 
				
			||||||
	"mayfly-go/base/utils"
 | 
					 | 
				
			||||||
	"mayfly-go/mock-server/controllers/form"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const key = "ccbscf:mock:data"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// @router /api/mock-datas/:method [get]
 | 
					 | 
				
			||||||
func GetMockData(rc *ctx.ReqCtx) {
 | 
					 | 
				
			||||||
	g := rc.GinCtx
 | 
					 | 
				
			||||||
	method := g.Param("method")
 | 
					 | 
				
			||||||
	params := utils.MapBuilder("method", method).ToMap()
 | 
					 | 
				
			||||||
	// 调用该mock数据的用户,若该数据指定了生效用户,则需要校验是否可访问
 | 
					 | 
				
			||||||
	username := g.Query("username")
 | 
					 | 
				
			||||||
	if username != "" {
 | 
					 | 
				
			||||||
		params["username"] = username
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// 记录日志使用
 | 
					 | 
				
			||||||
	rc.ReqParam = params
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mockData := &form.MockData{}
 | 
					 | 
				
			||||||
	// 从redis中获取key为 ‘ccbscf:mock:data’,field为‘method’的hash值
 | 
					 | 
				
			||||||
	json.Unmarshal([]byte(rediscli.HGet(key, method)), mockData)
 | 
					 | 
				
			||||||
	// 数据不存在或者状态为禁用
 | 
					 | 
				
			||||||
	biz.IsTrue(mockData.Enable == 1, "无该mock数据")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	eu := mockData.EffectiveUser
 | 
					 | 
				
			||||||
	// 如果设置的生效用户为空,则表示所有用户都生效
 | 
					 | 
				
			||||||
	if len(eu) == 0 {
 | 
					 | 
				
			||||||
		rc.ResData = mockData.Data
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	biz.IsTrue(utils.StrLen(username) != 0, "该用户无法访问该mock数据")
 | 
					 | 
				
			||||||
	// 判断该用户是否在该数据指定的生效用户中
 | 
					 | 
				
			||||||
	for _, e := range eu {
 | 
					 | 
				
			||||||
		if username == e {
 | 
					 | 
				
			||||||
			rc.ResData = mockData.Data
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	panic(biz.NewBizErr("该用户无法访问该mock数据"))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// @router /api/mock-datas [put]
 | 
					 | 
				
			||||||
func UpdateMockData(rc *ctx.ReqCtx) {
 | 
					 | 
				
			||||||
	mockData := &form.MockData{}
 | 
					 | 
				
			||||||
	ginx.BindJsonAndValid(rc.GinCtx, mockData)
 | 
					 | 
				
			||||||
	rc.ReqParam = mockData.Method
 | 
					 | 
				
			||||||
	val, _ := json.Marshal(mockData)
 | 
					 | 
				
			||||||
	rediscli.HSet(key, mockData.Method, val)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// @router /api/mock-datas [post]
 | 
					 | 
				
			||||||
func CreateMockData(rc *ctx.ReqCtx) {
 | 
					 | 
				
			||||||
	mockData := &form.MockData{}
 | 
					 | 
				
			||||||
	ginx.BindJsonAndValid(rc.GinCtx, mockData)
 | 
					 | 
				
			||||||
	biz.IsTrue(!rediscli.HExist(key, mockData.Method), "该方法已存在")
 | 
					 | 
				
			||||||
	val, _ := json.Marshal(mockData)
 | 
					 | 
				
			||||||
	rediscli.HSet(key, mockData.Method, val)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// @router /api/mock-datas [get]
 | 
					 | 
				
			||||||
func GetAllData(rc *ctx.ReqCtx) {
 | 
					 | 
				
			||||||
	rc.ResData = rediscli.HGetAll(key)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// @router /api/mock-datas/:method [delete]
 | 
					 | 
				
			||||||
func DeleteMockData(rc *ctx.ReqCtx) {
 | 
					 | 
				
			||||||
	method := rc.GinCtx.Param("method")
 | 
					 | 
				
			||||||
	rc.ReqParam = method
 | 
					 | 
				
			||||||
	rediscli.HDel(key, method)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,42 +0,0 @@
 | 
				
			|||||||
package initialize
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"mayfly-go/base/config"
 | 
					 | 
				
			||||||
	"mayfly-go/base/middleware"
 | 
					 | 
				
			||||||
	"mayfly-go/mock-server/routers"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/gin-gonic/gin"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func InitRouter() *gin.Engine {
 | 
					 | 
				
			||||||
	// server配置
 | 
					 | 
				
			||||||
	serverConfig := config.Conf.Server
 | 
					 | 
				
			||||||
	gin.SetMode(serverConfig.Model)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var router = gin.New()
 | 
					 | 
				
			||||||
	// 设置静态资源
 | 
					 | 
				
			||||||
	if staticConfs := serverConfig.Static; staticConfs != nil {
 | 
					 | 
				
			||||||
		for _, scs := range *staticConfs {
 | 
					 | 
				
			||||||
			router.Static(scs.RelativePath, scs.Root)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// 设置静态文件
 | 
					 | 
				
			||||||
	if staticFileConfs := serverConfig.StaticFile; staticFileConfs != nil {
 | 
					 | 
				
			||||||
		for _, sfs := range *staticFileConfs {
 | 
					 | 
				
			||||||
			router.StaticFile(sfs.RelativePath, sfs.Filepath)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// 是否允许跨域
 | 
					 | 
				
			||||||
	if serverConfig.Cors {
 | 
					 | 
				
			||||||
		router.Use(middleware.Cors())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 设置路由组
 | 
					 | 
				
			||||||
	api := router.Group("/api")
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		routers.InitMockRouter(api) // 注册mock路由
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return router
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
{"/Users/hml/Desktop/project/go/mayfly-go/mock-server/controllers":1615026881770981752}
 | 
					 | 
				
			||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"mayfly-go/base/rediscli"
 | 
					 | 
				
			||||||
	"mayfly-go/base/starter"
 | 
					 | 
				
			||||||
	"mayfly-go/mock-server/initialize"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
	rediscli.SetCli(starter.ConnRedis())
 | 
					 | 
				
			||||||
	starter.RunWebServer(initialize.InitRouter())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,38 +0,0 @@
 | 
				
			|||||||
package routers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"mayfly-go/base/ctx"
 | 
					 | 
				
			||||||
	"mayfly-go/mock-server/controllers"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/gin-gonic/gin"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func InitMockRouter(router *gin.RouterGroup) {
 | 
					 | 
				
			||||||
	mock := router.Group("mock-datas")
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// 获取mock数据
 | 
					 | 
				
			||||||
		mock.GET(":method", func(c *gin.Context) {
 | 
					 | 
				
			||||||
			rc := ctx.NewReqCtxWithGin(c).WithNeedToken(false).WithLog(ctx.NewLogInfo("获取mock数据"))
 | 
					 | 
				
			||||||
			rc.Handle(controllers.GetMockData)
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		mock.GET("", func(c *gin.Context) {
 | 
					 | 
				
			||||||
			ctx.NewReqCtxWithGin(c).WithNeedToken(false).Handle(controllers.GetAllData)
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		mock.POST("", func(c *gin.Context) {
 | 
					 | 
				
			||||||
			rc := ctx.NewReqCtxWithGin(c).WithNeedToken(false).WithLog(ctx.NewLogInfo("保存新增mock数据"))
 | 
					 | 
				
			||||||
			rc.Handle(controllers.CreateMockData)
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		mock.PUT("", func(c *gin.Context) {
 | 
					 | 
				
			||||||
			rc := ctx.NewReqCtxWithGin(c).WithNeedToken(false).WithLog(ctx.NewLogInfo("修改mock数据"))
 | 
					 | 
				
			||||||
			rc.Handle(controllers.UpdateMockData)
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		mock.DELETE(":method", func(c *gin.Context) {
 | 
					 | 
				
			||||||
			rc := ctx.NewReqCtxWithGin(c).WithNeedToken(false).WithLog(ctx.NewLogInfo("删除mock数据"))
 | 
					 | 
				
			||||||
			rc.Handle(controllers.DeleteMockData)
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 4.2 KiB  | 
@@ -1 +0,0 @@
 | 
				
			|||||||
<!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>mayfly-go-front</title><link href="/static/css/chunk-0bdde738.53f73f21.css" rel="prefetch"><link href="/static/css/chunk-1ccf71b8.f03c28a3.css" rel="prefetch"><link href="/static/css/chunk-4852ffd2.676f6792.css" rel="prefetch"><link href="/static/css/chunk-76193938.2d81c5bb.css" rel="prefetch"><link href="/static/css/chunk-a034c660.1463bb24.css" rel="prefetch"><link href="/static/js/chunk-0bdde738.d5de5f8f.js" rel="prefetch"><link href="/static/js/chunk-1ccf71b8.93b9c9d3.js" rel="prefetch"><link href="/static/js/chunk-4852ffd2.ccfafec1.js" rel="prefetch"><link href="/static/js/chunk-6e9f0a70.0aa40cfd.js" rel="prefetch"><link href="/static/js/chunk-76193938.803db4d0.js" rel="prefetch"><link href="/static/js/chunk-a034c660.9487808f.js" rel="prefetch"><link href="/static/css/app.b4088619.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.16da611a.css" rel="preload" as="style"><link href="/static/js/app.0e96251b.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.1a569244.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.16da611a.css" rel="stylesheet"><link href="/static/css/app.b4088619.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but mayfly-go-front doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.1a569244.js"></script><script src="/static/js/app.0e96251b.js"></script></body></html>
 | 
					 | 
				
			||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
#app{background-color:#222d32}.main{display:flex}.main .el-menu:not(.el-menu--collapse){width:230px}.main .app{width:100%;background-color:#ecf0f5}.main .aside{position:fixed;margin-top:50px;z-index:10;background-color:#222d32;transition:all .3s ease-in-out}.main .aside .menu{overflow-y:auto;height:100vh}.main .app-body{margin-left:230px;transition:margin-left .3s ease-in-out}.main .main-container{margin-top:88px;padding:2px;min-height:calc(100vh - 88px)}.header{width:100%;position:fixed;display:flex;z-index:10}.header,.header .logo{height:50px;background-color:#303643}.header .logo{width:230px;text-align:center;line-height:50px;color:#fff;transition:all .3s ease-in-out}.header .logo .min{display:none}.header .right{position:absolute;right:0}.header .header-btn{overflow:hidden;height:50px;display:inline-block;text-align:center;line-height:50px;cursor:pointer;padding:0 14px;color:#fff}.header .header-btn .el-badge__content{top:14px;right:7px;text-align:center;font-size:9px;padding:0 3px;background-color:#00a65a;color:#fff;border:none;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.header .header-btn:hover{background-color:#222d32}.menu{border-right:none;-moz-user-select:-moz-none;-moz-user-select:none;-o-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.el-menu--vertical{min-width:190px}.setting-category{padding:10px 0;border-bottom:1px solid #eee}#mainContainer iframe{border:none;outline:none;width:100%;height:100%;position:absolute;background-color:#ecf0f5}.el-menu-item,.el-submenu__title{font-weight:500}#nav-bar{margin-top:50px;height:38px;width:100%;z-index:8;background:#fff;box-shadow:0 1px 3px 0 rgba(0,0,0,.12),0 0 3px 0 rgba(0,0,0,.04);position:fixed;top:0}*{padding:0;margin:0;outline:none;box-sizing:border-box}body{font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Arial,sans-serif}a{color:#3c8dbc;text-decoration:none}::-webkit-scrollbar{width:4px;height:8px;background-color:#f5f5f5}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,.3);background-color:#f5f5f5}.el-menu .fa{vertical-align:middle;margin-right:5px;width:24px;text-align:center}.el-menu .fa:not(.is-children){font-size:14px}.gray-mode{filter:grayscale(100%)}.fade-enter-active,.fade-leave-active{transition:opacity .2s ease-in-out}.fade-enter,.fade-leave-to{opacity:0}.none-select{moz-user-select:-moz-none;-moz-user-select:none;-o-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.toolbar{width:100%;padding:8px;background-color:#fff;overflow:hidden;line-height:32px;border:1px solid #e6ebf5}.fl{float:left}
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1 +0,0 @@
 | 
				
			|||||||
.cm-s-panda-syntax{background:#292a2b;color:#e6e6e6;line-height:1.5;font-family:Operator Mono,Source Code Pro,Menlo,Monaco,Consolas,Courier New,monospace}.cm-s-panda-syntax .CodeMirror-cursor{border-color:#ff2c6d}.cm-s-panda-syntax .CodeMirror-activeline-background{background:rgba(99,123,156,.1)}.cm-s-panda-syntax .CodeMirror-selected{background:#fff}.cm-s-panda-syntax .cm-comment{font-style:italic;color:#676b79}.cm-s-panda-syntax .cm-operator{color:#f3f3f3}.cm-s-panda-syntax .cm-string{color:#19f9d8}.cm-s-panda-syntax .cm-string-2{color:#ffb86c}.cm-s-panda-syntax .cm-tag{color:#ff2c6d}.cm-s-panda-syntax .cm-meta{color:#b084eb}.cm-s-panda-syntax .cm-number{color:#ffb86c}.cm-s-panda-syntax .cm-atom{color:#ff2c6d}.cm-s-panda-syntax .cm-keyword{color:#ff75b5}.cm-s-panda-syntax .cm-variable{color:#ffb86c}.cm-s-panda-syntax .cm-type,.cm-s-panda-syntax .cm-variable-2,.cm-s-panda-syntax .cm-variable-3{color:#ff9ac1}.cm-s-panda-syntax .cm-def{color:#e6e6e6}.cm-s-panda-syntax .cm-property{color:#f3f3f3}.cm-s-panda-syntax .cm-attribute,.cm-s-panda-syntax .cm-unit{color:#ffb86c}.cm-s-panda-syntax .CodeMirror-matchingbracket{border-bottom:1px dotted #19f9d8;padding-bottom:2px;color:#e6e6e6}.cm-s-panda-syntax .CodeMirror-gutters{background:#292a2b;border-right-color:hsla(0,0%,100%,.1)}.cm-s-panda-syntax .CodeMirror-linenumber{color:#e6e6e6;opacity:.6}.CodeMirror-lint-markers{width:16px}.CodeMirror-lint-tooltip{background-color:#ffd;border:1px solid #000;border-radius:4px 4px 4px 4px;color:#000;font-family:monospace;font-size:10pt;overflow:hidden;padding:2px 5px;position:fixed;white-space:pre;white-space:pre-wrap;z-index:100;max-width:600px;opacity:0;transition:opacity .4s;-moz-transition:opacity .4s;-webkit-transition:opacity .4s;-o-transition:opacity .4s;-ms-transition:opacity .4s}.CodeMirror-lint-mark{background-position:0 100%;background-repeat:repeat-x}.CodeMirror-lint-mark-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=")}.CodeMirror-lint-mark-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")}.CodeMirror-lint-marker{background-position:50%;background-repeat:no-repeat;cursor:pointer;display:inline-block;height:16px;width:16px;vertical-align:middle;position:relative}.CodeMirror-lint-message{padding-left:18px;background-position:0 0;background-repeat:no-repeat}.CodeMirror-lint-marker-warning,.CodeMirror-lint-message-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=")}.CodeMirror-lint-marker-error,.CodeMirror-lint-message-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=")}.CodeMirror-lint-marker-multiple{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");background-repeat:no-repeat;background-position:100% 100%;width:100%;height:100%}#jsonedit .CodeMirror{overflow-y:scroll!important;height:400px!important}.el-dialog__body{padding:2px 2px}
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1 +0,0 @@
 | 
				
			|||||||
.login{display:flex;justify-content:center;align-items:center;position:absolute;height:100%;width:100%;background-color:#e4e5e6}.login .login-form{width:375px;height:435px;padding:30px;background-color:#fff;text-align:left;border-radius:4px;position:relative;margin-left:0;margin-right:0;zoom:1;display:block}.login .login-form .login-header{text-align:center;font-size:16px;font-weight:700;margin-bottom:20px}
 | 
					 | 
				
			||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
.active-plate-main{width:100%;height:130px}.active-plate-main .active-list{display:flex;list-style:none;padding-top:15px}.active-plate-main .active-list .item{position:relative;flex:1;text-align:center}.active-plate-main .active-list .item .num{font-size:42px;font-weight:700;font-family:sans-serif}.active-plate-main .active-list .item .desc{font-size:16px}.active-plate-main .active-list .item:after{position:absolute;top:18px;right:0;content:"";display:block;width:1px;height:56px;background:#e7eef0}.active-plate-main .active-list .item:last-of-type:after{background:none}.card-main{border-radius:8px;background:#fff;margin-bottom:20px;padding-bottom:10px}.title{color:#060606;font-size:16px;padding:20px 32px}.title span{padding-left:17px;font-size:12px;color:#dededf}.pie-main{padding:28px}.bar-main,.gauge-main,.pie-main{width:100%;height:360px;background:#fff}.bar-main{padding:28px}.funnel-main{height:295px}.funnel-main,.line-main{width:100%;padding:28px;background:#fff}.base-chart,.line-main{height:360px}.base-chart{width:100%;padding:28px;background:#fff}.count-style{font-size:50px}.xterm{font-feature-settings:"liga" 0;position:relative;-moz-user-select:none;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm{cursor:text}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility,.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:.5}.xterm-underline{text-decoration:underline}.el-dialog__body{padding:2px 2px}
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 38 KiB  | 
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1 +0,0 @@
 | 
				
			|||||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-76193938"],{"9d64":function(e,t,n){e.exports=n.p+"static/img/logo.e92f231a.png"},a248:function(e,t,n){"use strict";n("e16b")},e16b:function(e,t,n){},ede4:function(e,t,n){"use strict";n.r(t);var r=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"login"},[n("div",{staticClass:"login-form"},[e._m(0),n("el-input",{staticStyle:{"margin-bottom":"18px"},attrs:{placeholder:"请输入用户名","suffix-icon":"fa fa-user"},model:{value:e.loginForm.username,callback:function(t){e.$set(e.loginForm,"username",t)},expression:"loginForm.username"}}),n("el-input",{staticStyle:{"margin-bottom":"18px"},attrs:{placeholder:"请输入密码","suffix-icon":"fa fa-keyboard-o",type:"password",autocomplete:"new-password"},model:{value:e.loginForm.password,callback:function(t){e.$set(e.loginForm,"password",t)},expression:"loginForm.password"}}),n("el-button",{staticStyle:{width:"100%","margin-bottom":"18px"},attrs:{type:"primary",loading:e.loginLoading},nativeOn:{click:function(t){return e.login(t)}}},[e._v("登录")]),n("div",[n("el-checkbox",{model:{value:e.remember,callback:function(t){e.remember=t},expression:"remember"}},[e._v("记住密码")])],1)],1)])},a=[function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"login-header"},[r("img",{attrs:{src:n("9d64"),width:"150",height:"120",alt:""}})])}],o=n("60b5"),i=n("303e"),s=n("acf6"),u=n("e378"),c=n("a8e5"),l=(n("6a61"),n("21c9")),m=n("d789"),g={login:function(e){return m["a"].request("POST","/accounts/login",e,null)},captcha:function(){return m["a"].request("GET","/open/captcha",null,null)},logout:function(e){return m["a"].request("POST","/sys/accounts/logout/{token}",e,null)}},p=n("e4a1"),d=n("79cb"),f=function(e){Object(u["a"])(n,e);var t=Object(c["a"])(n);function n(){var e;return Object(i["a"])(this,n),e=t.apply(this,arguments),e.loginForm={username:"",password:"",uuid:""},e.remember=!1,e.loginLoading=!1,e}return Object(s["a"])(n,[{key:"mounted",value:function(){var e,t=this.getRemember();null!=t&&(e=JSON.parse(t)),e?(this.remember=!0,this.loginForm.username=e.username,this.loginForm.password=e.password):this.remember=!1}},{key:"getCaptcha",value:function(){var e=Object(o["a"])(regeneratorRuntime.mark((function e(){var t;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return e.next=2,g.captcha();case 2:t=e.sent,this.loginForm.uuid=t.uuid;case 4:case"end":return e.stop()}}),e,this)})));function t(){return e.apply(this,arguments)}return t}()},{key:"login",value:function(){var e=Object(o["a"])(regeneratorRuntime.mark((function e(){var t,n=this;return regeneratorRuntime.wrap((function(e){while(1)switch(e.prev=e.next){case 0:return this.loginLoading=!0,e.prev=1,e.next=4,g.login(this.loginForm);case 4:t=e.sent,this.remember?localStorage.setItem("remember",JSON.stringify(this.loginForm)):localStorage.removeItem("remember"),setTimeout((function(){d["a"].saveToken(t.token),n.$notify({title:"登录成功",message:"很高兴你使用Mayfly Admin!别忘了给个Star哦。",type:"success"}),n.loginLoading=!1;var e=n.$route.query.redirect;e?n.$router.push(e):n.$router.push({path:"/"})}),500),e.next=12;break;case 9:e.prev=9,e.t0=e["catch"](1),this.loginLoading=!1;case 12:case"end":return e.stop()}}),e,this,[[1,9]])})));function t(){return e.apply(this,arguments)}return t}()},{key:"getRemember",value:function(){return localStorage.getItem("remember")}}]),n}(p["c"]);f=Object(l["a"])([Object(p["a"])({name:"Login"})],f);var h=f,b=h,v=(n("a248"),n("5d22")),w=Object(v["a"])(b,r,a,!1,null,null,null);t["default"]=w.exports}}]);
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -48,6 +48,22 @@ func (d *Db) DeleteDb(rc *ctx.ReqCtx) {
 | 
				
			|||||||
	d.DbApp.Delete(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
 | 
						d.DbApp.Delete(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Db) TableInfos(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						rc.ResData = d.DbApp.GetDbInstance(GetDbId(rc.GinCtx)).GetTableInfos()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Db) TableIndex(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						tn := rc.GinCtx.Query("tableName")
 | 
				
			||||||
 | 
						biz.NotEmpty(tn, "tableName不能为空")
 | 
				
			||||||
 | 
						rc.ResData = d.DbApp.GetDbInstance(GetDbId(rc.GinCtx)).GetTableIndex(tn)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *Db) GetCreateTableDdl(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						tn := rc.GinCtx.Query("tableName")
 | 
				
			||||||
 | 
						biz.NotEmpty(tn, "tableName不能为空")
 | 
				
			||||||
 | 
						rc.ResData = d.DbApp.GetDbInstance(GetDbId(rc.GinCtx)).GetCreateTableDdl(tn)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @router /api/db/:dbId/exec-sql [get]
 | 
					// @router /api/db/:dbId/exec-sql [get]
 | 
				
			||||||
func (d *Db) ExecSql(rc *ctx.ReqCtx) {
 | 
					func (d *Db) ExecSql(rc *ctx.ReqCtx) {
 | 
				
			||||||
	g := rc.GinCtx
 | 
						g := rc.GinCtx
 | 
				
			||||||
@@ -84,7 +100,6 @@ func (d *Db) ExecSql(rc *ctx.ReqCtx) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		rc.ResData = colAndRes
 | 
							rc.ResData = colAndRes
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @router /api/db/:dbId/t-metadata [get]
 | 
					// @router /api/db/:dbId/t-metadata [get]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,10 @@ func (p *Project) SaveProject(rc *ctx.ReqCtx) {
 | 
				
			|||||||
	p.ProjectApp.SaveProject(project)
 | 
						p.ProjectApp.SaveProject(project)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *Project) DelProject(rc *ctx.ReqCtx) {
 | 
				
			||||||
 | 
						p.ProjectApp.DelProject(uint64(ginx.QueryInt(rc.GinCtx, "id", 0)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取项目下的环境信息
 | 
					// 获取项目下的环境信息
 | 
				
			||||||
func (p *Project) GetProjectEnvs(rc *ctx.ReqCtx) {
 | 
					func (p *Project) GetProjectEnvs(rc *ctx.ReqCtx) {
 | 
				
			||||||
	projectEnvs := &[]entity.ProjectEnv{}
 | 
						projectEnvs := &[]entity.ProjectEnv{}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,8 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"mayfly-go/base/biz"
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
 | 
						"mayfly-go/base/cache"
 | 
				
			||||||
 | 
						"mayfly-go/base/global"
 | 
				
			||||||
	"mayfly-go/base/model"
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
	"mayfly-go/server/devops/domain/entity"
 | 
						"mayfly-go/server/devops/domain/entity"
 | 
				
			||||||
	"mayfly-go/server/devops/domain/repository"
 | 
						"mayfly-go/server/devops/domain/repository"
 | 
				
			||||||
@@ -90,17 +92,24 @@ func (d *dbAppImpl) Delete(id uint64) {
 | 
				
			|||||||
	d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: id})
 | 
						d.dbSqlRepo.DeleteBy(&entity.DbSql{DbId: id})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var mutex sync.Mutex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (da *dbAppImpl) GetDbInstance(id uint64) *DbInstance {
 | 
					func (da *dbAppImpl) GetDbInstance(id uint64) *DbInstance {
 | 
				
			||||||
 | 
						mutex.Lock()
 | 
				
			||||||
 | 
						defer mutex.Unlock()
 | 
				
			||||||
	// Id不为0,则为需要缓存
 | 
						// Id不为0,则为需要缓存
 | 
				
			||||||
	needCache := id != 0
 | 
						needCache := id != 0
 | 
				
			||||||
	if needCache {
 | 
						if needCache {
 | 
				
			||||||
		load, ok := dbCache.Load(id)
 | 
							load, ok := dbCache.Get(id)
 | 
				
			||||||
		if ok {
 | 
							if ok {
 | 
				
			||||||
			return load.(*DbInstance)
 | 
								return load.(*DbInstance)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	d := da.GetById(id)
 | 
						d := da.GetById(id)
 | 
				
			||||||
	biz.NotNil(d, "数据库信息不存在")
 | 
						biz.NotNil(d, "数据库信息不存在")
 | 
				
			||||||
 | 
						global.Log.Infof("连接db: %s:%d/%s", d.Host, d.Port, d.Database)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DB, err := sql.Open(d.Type, getDsn(d))
 | 
						DB, err := sql.Open(d.Type, getDsn(d))
 | 
				
			||||||
	biz.ErrIsNil(err, fmt.Sprintf("Open %s failed, err:%v\n", d.Type, err))
 | 
						biz.ErrIsNil(err, fmt.Sprintf("Open %s failed, err:%v\n", d.Type, err))
 | 
				
			||||||
	perr := DB.Ping()
 | 
						perr := DB.Ping()
 | 
				
			||||||
@@ -117,17 +126,23 @@ func (da *dbAppImpl) GetDbInstance(id uint64) *DbInstance {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	dbi := &DbInstance{Id: id, Type: d.Type, db: DB}
 | 
						dbi := &DbInstance{Id: id, Type: d.Type, db: DB}
 | 
				
			||||||
	if needCache {
 | 
						if needCache {
 | 
				
			||||||
		dbCache.LoadOrStore(d.Id, dbi)
 | 
							dbCache.Put(id, dbi)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return dbi
 | 
						return dbi
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var dbCache sync.Map
 | 
					// 客户端连接缓存,30分钟内没有访问则会被关闭
 | 
				
			||||||
 | 
					var dbCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
 | 
				
			||||||
 | 
						WithUpdateAccessTime(true).
 | 
				
			||||||
 | 
						OnEvicted(func(key interface{}, value interface{}) {
 | 
				
			||||||
 | 
							global.Log.Info(fmt.Sprintf("删除db连接缓存 id: %d", key))
 | 
				
			||||||
 | 
							value.(*DbInstance).Close()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetDbInstanceByCache(id uint64) *DbInstance {
 | 
					func GetDbInstanceByCache(id uint64) *DbInstance {
 | 
				
			||||||
	if load, ok := dbCache.Load(id); ok {
 | 
						if load, ok := dbCache.Get(fmt.Sprint(id)); ok {
 | 
				
			||||||
		return load.(*DbInstance)
 | 
							return load.(*DbInstance)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
@@ -153,13 +168,17 @@ type DbInstance struct {
 | 
				
			|||||||
// 依次返回 列名数组,结果map,错误
 | 
					// 依次返回 列名数组,结果map,错误
 | 
				
			||||||
func (d *DbInstance) SelectData(sql string) ([]string, []map[string]string, error) {
 | 
					func (d *DbInstance) SelectData(sql string) ([]string, []map[string]string, error) {
 | 
				
			||||||
	sql = strings.Trim(sql, " ")
 | 
						sql = strings.Trim(sql, " ")
 | 
				
			||||||
	if !strings.HasPrefix(sql, "SELECT") && !strings.HasPrefix(sql, "select") {
 | 
						isSelect := strings.HasPrefix(sql, "SELECT") || strings.HasPrefix(sql, "select")
 | 
				
			||||||
 | 
						isShow := strings.HasPrefix(sql, "show")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !isSelect && !isShow {
 | 
				
			||||||
		return nil, nil, errors.New("该sql非查询语句")
 | 
							return nil, nil, errors.New("该sql非查询语句")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// 没加limit,则默认限制50条
 | 
						// 没加limit,则默认限制50条
 | 
				
			||||||
	if !strings.Contains(sql, "limit") && !strings.Contains(sql, "LIMIT") {
 | 
						if isSelect && !strings.Contains(sql, "limit") && !strings.Contains(sql, "LIMIT") {
 | 
				
			||||||
		sql = sql + " LIMIT 50"
 | 
							sql = sql + " LIMIT 50"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rows, err := d.db.Query(sql)
 | 
						rows, err := d.db.Query(sql)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
@@ -224,10 +243,9 @@ func (d *DbInstance) Exec(sql string) (int64, error) {
 | 
				
			|||||||
	return res.RowsAffected()
 | 
						return res.RowsAffected()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 关闭连接,并从缓存中移除
 | 
					// 关闭连接
 | 
				
			||||||
func (d *DbInstance) Close() {
 | 
					func (d *DbInstance) Close() {
 | 
				
			||||||
	d.db.Close()
 | 
						d.db.Close()
 | 
				
			||||||
	dbCache.Delete(d.Id)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取dataSourceName
 | 
					// 获取dataSourceName
 | 
				
			||||||
@@ -241,6 +259,7 @@ func getDsn(d *entity.Db) string {
 | 
				
			|||||||
func CloseDb(id uint64) {
 | 
					func CloseDb(id uint64) {
 | 
				
			||||||
	if di := GetDbInstanceByCache(id); di != nil {
 | 
						if di := GetDbInstanceByCache(id); di != nil {
 | 
				
			||||||
		di.Close()
 | 
							di.Close()
 | 
				
			||||||
 | 
							dbCache.Delete(id)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -252,10 +271,22 @@ const (
 | 
				
			|||||||
	create_time createTime from information_schema.tables
 | 
						create_time createTime from information_schema.tables
 | 
				
			||||||
	WHERE table_schema = (SELECT database())`
 | 
						WHERE table_schema = (SELECT database())`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// mysql 表信息
 | 
				
			||||||
 | 
						MYSQL_TABLE_INFO = `SELECT table_name tableName, table_comment tableComment, table_rows tableRows,
 | 
				
			||||||
 | 
						data_length dataLength, index_length indexLength, create_time createTime 
 | 
				
			||||||
 | 
						FROM information_schema.tables 
 | 
				
			||||||
 | 
					    WHERE table_schema = (SELECT database())`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// mysql 索引信息
 | 
				
			||||||
 | 
						MYSQL_INDEX_INFO = `SELECT index_name indexName, column_name columnName, index_type indexType,
 | 
				
			||||||
 | 
						SEQ_IN_INDEX seqInIndex, INDEX_COMMENT indexComment
 | 
				
			||||||
 | 
						FROM information_schema.STATISTICS 
 | 
				
			||||||
 | 
					    WHERE table_schema = (SELECT database()) AND table_name = '%s'`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// mysql 列信息元数据
 | 
						// mysql 列信息元数据
 | 
				
			||||||
	MYSQL_COLOUMN_MA = `SELECT table_name tableName, column_name columnName, column_type columnType,
 | 
						MYSQL_COLOUMN_MA = `SELECT table_name tableName, column_name columnName, column_type columnType,
 | 
				
			||||||
	column_comment columnComment, column_key columnKey, extra from information_schema.columns
 | 
						column_comment columnComment, column_key columnKey, extra from information_schema.columns
 | 
				
			||||||
	WHERE table_name in (%s) AND table_schema = (SELECT database()) ORDER BY ordinal_position limit 15000`
 | 
						WHERE table_name in (%s) AND table_schema = (SELECT database()) ORDER BY ordinal_position limit 18000`
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *DbInstance) GetTableMetedatas() []map[string]string {
 | 
					func (d *DbInstance) GetTableMetedatas() []map[string]string {
 | 
				
			||||||
@@ -283,3 +314,30 @@ func (d *DbInstance) GetColumnMetadatas(tableNames ...string) []map[string]strin
 | 
				
			|||||||
	biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
 | 
						biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
 | 
				
			||||||
	return res
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *DbInstance) GetTableInfos() []map[string]string {
 | 
				
			||||||
 | 
						var sql string
 | 
				
			||||||
 | 
						if d.Type == "mysql" {
 | 
				
			||||||
 | 
							sql = MYSQL_TABLE_INFO
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, res, _ := d.SelectData(sql)
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *DbInstance) GetTableIndex(tableName string) []map[string]string {
 | 
				
			||||||
 | 
						var sql string
 | 
				
			||||||
 | 
						if d.Type == "mysql" {
 | 
				
			||||||
 | 
							sql = fmt.Sprintf(MYSQL_INDEX_INFO, tableName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, res, _ := d.SelectData(sql)
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *DbInstance) GetCreateTableDdl(tableName string) []map[string]string {
 | 
				
			||||||
 | 
						var sql string
 | 
				
			||||||
 | 
						if d.Type == "mysql" {
 | 
				
			||||||
 | 
							sql = fmt.Sprintf("show create table %s ", tableName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, res, _ := d.SelectData(sql)
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,8 @@ type Project interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	SaveProject(project *entity.Project)
 | 
						SaveProject(project *entity.Project)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DelProject(id uint64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 根据项目id获取所有该项目下的环境信息列表
 | 
						// 根据项目id获取所有该项目下的环境信息列表
 | 
				
			||||||
	ListEnvByProjectId(projectId uint64, listPtr interface{})
 | 
						ListEnvByProjectId(projectId uint64, listPtr interface{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,6 +66,12 @@ func (p *projectAppImpl) SaveProject(project *entity.Project) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectAppImpl) DelProject(id uint64) {
 | 
				
			||||||
 | 
						p.projectRepo.Delete(id)
 | 
				
			||||||
 | 
						p.projectEnvRepo.DeleteEnvs(id)
 | 
				
			||||||
 | 
						p.projectMemberRepo.DeleteMems(id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 根据项目id获取所有该项目下的环境信息列表
 | 
					// 根据项目id获取所有该项目下的环境信息列表
 | 
				
			||||||
func (p *projectAppImpl) ListEnvByProjectId(projectId uint64, listPtr interface{}) {
 | 
					func (p *projectAppImpl) ListEnvByProjectId(projectId uint64, listPtr interface{}) {
 | 
				
			||||||
	p.projectEnvRepo.ListEnv(&entity.ProjectEnv{ProjectId: projectId}, listPtr)
 | 
						p.projectEnvRepo.ListEnv(&entity.ProjectEnv{ProjectId: projectId}, listPtr)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,15 @@
 | 
				
			|||||||
package application
 | 
					package application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"mayfly-go/base/biz"
 | 
						"mayfly-go/base/biz"
 | 
				
			||||||
 | 
						"mayfly-go/base/cache"
 | 
				
			||||||
 | 
						"mayfly-go/base/global"
 | 
				
			||||||
	"mayfly-go/base/model"
 | 
						"mayfly-go/base/model"
 | 
				
			||||||
	"mayfly-go/server/devops/domain/entity"
 | 
						"mayfly-go/server/devops/domain/entity"
 | 
				
			||||||
	"mayfly-go/server/devops/domain/repository"
 | 
						"mayfly-go/server/devops/domain/repository"
 | 
				
			||||||
	"mayfly-go/server/devops/infrastructure/persistence"
 | 
						"mayfly-go/server/devops/infrastructure/persistence"
 | 
				
			||||||
	"sync"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/go-redis/redis"
 | 
						"github.com/go-redis/redis"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -85,7 +88,7 @@ func (r *redisAppImpl) GetRedisInstance(id uint64) *RedisInstance {
 | 
				
			|||||||
	// Id不为0,则为需要缓存
 | 
						// Id不为0,则为需要缓存
 | 
				
			||||||
	needCache := id != 0
 | 
						needCache := id != 0
 | 
				
			||||||
	if needCache {
 | 
						if needCache {
 | 
				
			||||||
		load, ok := redisCache.Load(id)
 | 
							load, ok := redisCache.Get(id)
 | 
				
			||||||
		if ok {
 | 
							if ok {
 | 
				
			||||||
			return load.(*RedisInstance)
 | 
								return load.(*RedisInstance)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -93,6 +96,8 @@ func (r *redisAppImpl) GetRedisInstance(id uint64) *RedisInstance {
 | 
				
			|||||||
	// 缓存不存在,则回调获取redis信息
 | 
						// 缓存不存在,则回调获取redis信息
 | 
				
			||||||
	re := r.GetById(id)
 | 
						re := r.GetById(id)
 | 
				
			||||||
	biz.NotNil(re, "redis信息不存在")
 | 
						biz.NotNil(re, "redis信息不存在")
 | 
				
			||||||
 | 
						global.Log.Infof("连接redis: %s", re.Host)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rcli := redis.NewClient(&redis.Options{
 | 
						rcli := redis.NewClient(&redis.Options{
 | 
				
			||||||
		Addr:     re.Host,
 | 
							Addr:     re.Host,
 | 
				
			||||||
		Password: re.Password, // no password set
 | 
							Password: re.Password, // no password set
 | 
				
			||||||
@@ -104,14 +109,20 @@ func (r *redisAppImpl) GetRedisInstance(id uint64) *RedisInstance {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	ri := &RedisInstance{Id: id, Cli: rcli}
 | 
						ri := &RedisInstance{Id: id, Cli: rcli}
 | 
				
			||||||
	if needCache {
 | 
						if needCache {
 | 
				
			||||||
		redisCache.LoadOrStore(re.Id, ri)
 | 
							redisCache.Put(re.Id, ri)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ri
 | 
						return ri
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var redisCache sync.Map
 | 
					// redis客户端连接缓存,30分钟内没有访问则会被关闭
 | 
				
			||||||
 | 
					var redisCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
 | 
				
			||||||
 | 
						WithUpdateAccessTime(true).
 | 
				
			||||||
 | 
						OnEvicted(func(key interface{}, value interface{}) {
 | 
				
			||||||
 | 
							global.Log.Info(fmt.Sprintf("删除redis连接缓存 id: %d", key))
 | 
				
			||||||
 | 
							value.(*RedisInstance).Cli.Close()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// redis实例
 | 
					// redis实例
 | 
				
			||||||
type RedisInstance struct {
 | 
					type RedisInstance struct {
 | 
				
			||||||
@@ -121,7 +132,7 @@ type RedisInstance struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 关闭redis连接
 | 
					// 关闭redis连接
 | 
				
			||||||
func CloseRedis(id uint64) {
 | 
					func CloseRedis(id uint64) {
 | 
				
			||||||
	if load, ok := redisCache.Load(id); ok {
 | 
						if load, ok := redisCache.Get(id); ok {
 | 
				
			||||||
		load.(*RedisInstance).Cli.Close()
 | 
							load.(*RedisInstance).Cli.Close()
 | 
				
			||||||
		redisCache.Delete(id)
 | 
							redisCache.Delete(id)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,4 +13,6 @@ type Project interface {
 | 
				
			|||||||
	Save(p *entity.Project)
 | 
						Save(p *entity.Project)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Update(project *entity.Project)
 | 
						Update(project *entity.Project)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Delete(id uint64)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,4 +7,6 @@ type ProjectEnv interface {
 | 
				
			|||||||
	ListEnv(condition *entity.ProjectEnv, toEntity interface{}, orderBy ...string)
 | 
						ListEnv(condition *entity.ProjectEnv, toEntity interface{}, orderBy ...string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Save(entity *entity.ProjectEnv)
 | 
						Save(entity *entity.ProjectEnv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DeleteEnvs(projectId uint64)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,4 +16,6 @@ type ProjectMemeber interface {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// 根据成员id和项目id删除关联关系
 | 
						// 根据成员id和项目id删除关联关系
 | 
				
			||||||
	DeleteByPidMid(projectId, accountId uint64)
 | 
						DeleteByPidMid(projectId, accountId uint64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DeleteMems(projectId uint64)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,14 +28,14 @@ type Cli struct {
 | 
				
			|||||||
// 机器客户端连接缓存,30分钟内没有访问则会被关闭
 | 
					// 机器客户端连接缓存,30分钟内没有访问则会被关闭
 | 
				
			||||||
var cliCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
 | 
					var cliCache = cache.NewTimedCache(30*time.Minute, 5*time.Second).
 | 
				
			||||||
	WithUpdateAccessTime(true).
 | 
						WithUpdateAccessTime(true).
 | 
				
			||||||
	OnEvicted(func(key string, value interface{}) {
 | 
						OnEvicted(func(key interface{}, value interface{}) {
 | 
				
			||||||
		global.Log.Info(fmt.Sprintf("删除机器连接缓存 id: %s", key))
 | 
							global.Log.Info(fmt.Sprintf("删除机器连接缓存 id: %d", key))
 | 
				
			||||||
		value.(*Cli).Close()
 | 
							value.(*Cli).Close()
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建
 | 
					// 从缓存中获取客户端信息,不存在则回调获取机器信息函数,并新建
 | 
				
			||||||
func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, error) {
 | 
					func GetCli(machineId uint64, getMachine func(uint64) *entity.Machine) (*Cli, error) {
 | 
				
			||||||
	cli, err := cliCache.ComputeIfAbsent(fmt.Sprint(machineId), func(key string) (interface{}, error) {
 | 
						cli, err := cliCache.ComputeIfAbsent(machineId, func(key interface{}) (interface{}, error) {
 | 
				
			||||||
		c, err := newClient(getMachine(machineId))
 | 
							c, err := newClient(getMachine(machineId))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
@@ -210,7 +210,7 @@ func (c *Cli) RunTerminal(shell string, stdout, stderr io.Writer) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 关闭指定机器的连接
 | 
					// 关闭指定机器的连接
 | 
				
			||||||
func Close(id uint64) {
 | 
					func Close(id uint64) {
 | 
				
			||||||
	if cli, ok := cliCache.Get(fmt.Sprint(id)); ok {
 | 
						if cli, ok := cliCache.Get(id); ok {
 | 
				
			||||||
		cli.(*Cli).Close()
 | 
							cli.(*Cli).Close()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,3 +18,7 @@ func (p *projectEnvRepo) ListEnv(condition *entity.ProjectEnv, toEntity interfac
 | 
				
			|||||||
func (p *projectEnvRepo) Save(entity *entity.ProjectEnv) {
 | 
					func (p *projectEnvRepo) Save(entity *entity.ProjectEnv) {
 | 
				
			||||||
	biz.ErrIsNilAppendErr(model.Insert(entity), "保存环境失败:%s")
 | 
						biz.ErrIsNilAppendErr(model.Insert(entity), "保存环境失败:%s")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectEnvRepo) DeleteEnvs(projectId uint64) {
 | 
				
			||||||
 | 
						model.DeleteByCondition(&entity.ProjectEnv{ProjectId: projectId})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,3 +26,7 @@ func (p *projectMemeberRepo) GetPageList(condition *entity.ProjectMember, pagePa
 | 
				
			|||||||
func (p *projectMemeberRepo) DeleteByPidMid(projectId, accountId uint64) {
 | 
					func (p *projectMemeberRepo) DeleteByPidMid(projectId, accountId uint64) {
 | 
				
			||||||
	model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId, AccountId: accountId})
 | 
						model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId, AccountId: accountId})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectMemeberRepo) DeleteMems(projectId uint64) {
 | 
				
			||||||
 | 
						model.DeleteByCondition(&entity.ProjectMember{ProjectId: projectId})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,3 +26,7 @@ func (p *projectRepo) Save(project *entity.Project) {
 | 
				
			|||||||
func (p *projectRepo) Update(project *entity.Project) {
 | 
					func (p *projectRepo) Update(project *entity.Project) {
 | 
				
			||||||
	biz.ErrIsNil(model.UpdateById(project), "更新项目信息")
 | 
						biz.ErrIsNil(model.UpdateById(project), "更新项目信息")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *projectRepo) Delete(id uint64) {
 | 
				
			||||||
 | 
						model.DeleteById(new(entity.Project), id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,18 @@ func InitDbRouter(router *gin.RouterGroup) {
 | 
				
			|||||||
				Handle(d.DeleteDb)
 | 
									Handle(d.DeleteDb)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							db.GET(":dbId/t-infos", func(c *gin.Context) {
 | 
				
			||||||
 | 
								ctx.NewReqCtxWithGin(c).Handle(d.TableInfos)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							db.GET(":dbId/t-index", func(c *gin.Context) {
 | 
				
			||||||
 | 
								ctx.NewReqCtxWithGin(c).Handle(d.TableIndex)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							db.GET(":dbId/t-create-ddl", func(c *gin.Context) {
 | 
				
			||||||
 | 
								ctx.NewReqCtxWithGin(c).WithNeedToken(false).Handle(d.GetCreateTableDdl)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// db.GET(":dbId/exec-sql", controllers.SelectData)
 | 
							// db.GET(":dbId/exec-sql", controllers.SelectData)
 | 
				
			||||||
		db.GET(":dbId/exec-sql", func(g *gin.Context) {
 | 
							db.GET(":dbId/exec-sql", func(g *gin.Context) {
 | 
				
			||||||
			rc := ctx.NewReqCtxWithGin(g).WithLog(ctx.NewLogInfo("执行Sql语句"))
 | 
								rc := ctx.NewReqCtxWithGin(g).WithLog(ctx.NewLogInfo("执行Sql语句"))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,15 @@ func InitProjectRouter(router *gin.RouterGroup) {
 | 
				
			|||||||
				Handle(m.SaveProject)
 | 
									Handle(m.SaveProject)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							delProjectLog := ctx.NewLogInfo("删除项目信息")
 | 
				
			||||||
 | 
							delPP := ctx.NewPermission("project:del")
 | 
				
			||||||
 | 
							// 删除项目
 | 
				
			||||||
 | 
							project.DELETE("", func(c *gin.Context) {
 | 
				
			||||||
 | 
								ctx.NewReqCtxWithGin(c).WithLog(delProjectLog).
 | 
				
			||||||
 | 
									WithRequiredPermission(delPP).
 | 
				
			||||||
 | 
									Handle(m.DelProject)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 获取项目下的环境信息列表
 | 
							// 获取项目下的环境信息列表
 | 
				
			||||||
		project.GET("/:projectId/envs", func(c *gin.Context) {
 | 
							project.GET("/:projectId/envs", func(c *gin.Context) {
 | 
				
			||||||
			ctx.NewReqCtxWithGin(c).Handle(m.GetProjectEnvs)
 | 
								ctx.NewReqCtxWithGin(c).Handle(m.GetProjectEnvs)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user