-
-
+
+
{{ data.params.keys }}
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- {{ $t('redis.loadMore') }}
+
+ {{ $t('redis.loadMore') }}
+
+ {{ $t('redis.addKey') }}
+
+ flush
+
+
+ keys:{{ state.dbsize }}
+
+
+
+
+
+
+
+
+
+
+ {{ node.label }}
+
- {{ $t('redis.addKey') }}
+ ({{ data.keyCount }})
+
+
+
+
- flush
-
-
- keys:{{ state.dbsize }}
-
-
+
+
+
-
-
-
-
-
-
-
- {{ node.label }}
-
-
- ({{ data.keyCount }})
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -204,6 +208,7 @@ import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI18nFormValidate, useI18nOperateSuccessMsg } from '@/hooks/useI18n';
import { Rules } from '@/common/rule';
+import ResourceOpPanel from '../component/ResourceOpPanel.vue';
const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
diff --git a/server/go.mod b/server/go.mod
index c5113a66..62632c46 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -32,9 +32,9 @@ require (
github.com/tidwall/gjson v1.18.0
github.com/veops/go-ansiterm v0.0.5
go.mongodb.org/mongo-driver v1.16.0 // mongo
- golang.org/x/crypto v0.35.0 // ssh
+ golang.org/x/crypto v0.36.0 // ssh
golang.org/x/oauth2 v0.26.0
- golang.org/x/sync v0.11.0
+ golang.org/x/sync v0.12.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
// gorm
@@ -93,8 +93,8 @@ require (
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect
golang.org/x/image v0.23.0 // indirect
golang.org/x/net v0.34.0 // indirect
- golang.org/x/sys v0.30.0 // indirect
- golang.org/x/text v0.22.0 // indirect
+ golang.org/x/sys v0.31.0 // indirect
+ golang.org/x/text v0.23.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
diff --git a/server/internal/db/application/db_data_sync.go b/server/internal/db/application/db_data_sync.go
index c1e71a41..6264e830 100644
--- a/server/internal/db/application/db_data_sync.go
+++ b/server/internal/db/application/db_data_sync.go
@@ -159,7 +159,7 @@ func (app *dataSyncAppImpl) RunCronJob(ctx context.Context, id uint64) error {
break
}
}
- return errorx.NewBiz("get column data type... ignore~")
+ return dbi.NewStopWalkQueryError("get column data type... ignore~")
})
updSql = fmt.Sprintf("and %s > %s", task.UpdField, updFieldDataType.DataType.SQLValue(task.UpdFieldVal))
diff --git a/server/internal/db/application/db_sql_exec.go b/server/internal/db/application/db_sql_exec.go
index 62e14884..dbc4855c 100644
--- a/server/internal/db/application/db_sql_exec.go
+++ b/server/internal/db/application/db_sql_exec.go
@@ -333,7 +333,6 @@ func (d *dbSqlExecAppImpl) saveSqlExecLog(dbSqlExecRecord *entity.DbSqlExec, res
func (d *dbSqlExecAppImpl) doSelect(ctx context.Context, sqlExecParam *sqlExecParam) (*dto.DbSqlExecRes, error) {
maxCount := config.GetDbms().MaxResultSet
- selectStmt := sqlExecParam.Stmt
selectSql := sqlExecParam.Sql
sqlExecParam.SqlExecRecord.Type = entity.DbSqlExecTypeQuery
@@ -343,49 +342,7 @@ func (d *dbSqlExecAppImpl) doSelect(ctx context.Context, sqlExecParam *sqlExecPa
}
}
- if selectStmt != nil {
- needCheckLimit := false
- var limit *sqlstmt.Limit
- switch stmt := selectStmt.(type) {
- case *sqlstmt.SimpleSelectStmt:
- qs := stmt.QuerySpecification
- limit = qs.Limit
- if qs.SelectElements != nil && (qs.SelectElements.Star != "" || len(qs.SelectElements.Elements) > 1) {
- needCheckLimit = true
- }
- case *sqlstmt.UnionSelectStmt:
- limit = stmt.Limit
- selectSql = selectStmt.GetText()
- needCheckLimit = true
- }
-
- // 如果配置为0,则不校验分页参数
- if needCheckLimit && maxCount != 0 {
- if limit == nil {
- return nil, errorx.NewBizI(ctx, imsg.ErrNoLimitStmt)
- }
- if limit.RowCount > maxCount {
- return nil, errorx.NewBizI(ctx, imsg.ErrLimitInvalid, "count", maxCount)
- }
- }
- } else {
- if maxCount != 0 {
- if !strings.Contains(selectSql, "limit") &&
- // 兼容oracle rownum分页
- !strings.Contains(selectSql, "rownum") &&
- // 兼容mssql offset分页
- !strings.Contains(selectSql, "offset") &&
- // 兼容mssql top 分页 with result as ({query sql}) select top 100 * from result
- !strings.Contains(selectSql, " top ") {
- // 判断是不是count语句
- if !strings.Contains(selectSql, "count(") {
- return nil, errorx.NewBizI(ctx, imsg.ErrNoLimitStmt)
- }
- }
- }
- }
-
- return d.doQuery(ctx, sqlExecParam.DbConn, selectSql)
+ return d.doQuery(ctx, sqlExecParam.DbConn, selectSql, maxCount)
}
func (d *dbSqlExecAppImpl) doOtherRead(ctx context.Context, sqlExecParam *sqlExecParam) (*dto.DbSqlExecRes, error) {
@@ -398,7 +355,7 @@ func (d *dbSqlExecAppImpl) doOtherRead(ctx context.Context, sqlExecParam *sqlExe
}
}
- return d.doQuery(ctx, sqlExecParam.DbConn, selectSql)
+ return d.doQuery(ctx, sqlExecParam.DbConn, selectSql, 0)
}
func (d *dbSqlExecAppImpl) doExecDDL(ctx context.Context, sqlExecParam *sqlExecParam) (*dto.DbSqlExecRes, error) {
@@ -588,11 +545,23 @@ func (d *dbSqlExecAppImpl) doInsert(ctx context.Context, sqlExecParam *sqlExecPa
return d.doExec(ctx, sqlExecParam.DbConn, sqlExecParam.Sql)
}
-func (d *dbSqlExecAppImpl) doQuery(ctx context.Context, dbConn *dbi.DbConn, sql string) (*dto.DbSqlExecRes, error) {
- cols, res, err := dbConn.QueryContext(ctx, sql)
+func (d *dbSqlExecAppImpl) doQuery(ctx context.Context, dbConn *dbi.DbConn, sql string, maxRows int) (*dto.DbSqlExecRes, error) {
+ res := make([]map[string]any, 0, 16)
+ nowRows := 0
+ cols, err := dbConn.WalkQueryRows(ctx, sql, func(row map[string]any, columns []*dbi.QueryColumn) error {
+ nowRows++
+ // 超过指定的最大查询记录数,则停止查询
+ if maxRows != 0 && nowRows > maxRows {
+ return dbi.NewStopWalkQueryError(fmt.Sprintf("exceed the maximum number of query records %d", maxRows))
+ }
+ res = append(res, row)
+ return nil
+ })
+
if err != nil {
return nil, err
}
+
return &dto.DbSqlExecRes{
Sql: sql,
Columns: cols,
diff --git a/server/internal/db/dbm/dbi/conn.go b/server/internal/db/dbm/dbi/conn.go
index 8de5fb60..f69bcf6c 100644
--- a/server/internal/db/dbm/dbi/conn.go
+++ b/server/internal/db/dbm/dbi/conn.go
@@ -70,11 +70,7 @@ func (d *DbConn) QueryContext(ctx context.Context, querySql string, args ...any)
return nil
}, args...)
- if err != nil {
- return nil, nil, wrapSqlError(err)
- }
-
- return cols, result, nil
+ return cols, result, err
}
// 将查询结果映射至struct,可具体参考sqlx库
@@ -95,7 +91,15 @@ func (d *DbConn) Query2Struct(execSql string, dest any) error {
// WalkQueryRows 游标方式遍历查询结果集, walkFn返回error不为nil, 则跳出遍历并取消查询
func (d *DbConn) WalkQueryRows(ctx context.Context, querySql string, walkFn WalkQueryRowsFunc, args ...any) ([]*QueryColumn, error) {
- return d.walkQueryRows(ctx, querySql, walkFn, args...)
+ if qcs, err := d.walkQueryRows(ctx, querySql, walkFn, args...); err != nil {
+ // 如果是手动停止 则默认返回当前已遍历查询的数据即可
+ if _, ok := err.(*StopWalkQueryError); ok {
+ return qcs, nil
+ }
+ return qcs, wrapSqlError(err)
+ } else {
+ return qcs, nil
+ }
}
// WalkTableRows 游标方式遍历指定表的结果集, walkFn返回error不为nil, 则跳出遍历并取消查询
@@ -242,3 +246,18 @@ func wrapSqlError(err error) error {
}
return err
}
+
+// StopWalkQueryError 自定义的停止遍历查询错误类型
+type StopWalkQueryError struct {
+ Reason string
+}
+
+// Error 实现 error 接口
+func (e *StopWalkQueryError) Error() string {
+ return fmt.Sprintf("stop walk query: %s", e.Reason)
+}
+
+// NewStopWalkQueryError 创建一个带有reason的StopWalkQueryError
+func NewStopWalkQueryError(reason string) *StopWalkQueryError {
+ return &StopWalkQueryError{Reason: reason}
+}
diff --git a/server/internal/db/dbm/dbi/metasql/pgsql_meta.sql b/server/internal/db/dbm/dbi/metasql/pgsql_meta.sql
index 2c31d0a2..d47e4e07 100644
--- a/server/internal/db/dbm/dbi/metasql/pgsql_meta.sql
+++ b/server/internal/db/dbm/dbi/metasql/pgsql_meta.sql
@@ -13,26 +13,31 @@ order by
n.nspname
---------------------------------------
--PGSQL_TABLE_INFO 表详细信息
-SELECT
- c.relname AS "tableName",
- obj_description(c.oid) AS "tableComment",
- pg_total_relation_size(c.oid) AS "dataLength",
- pg_indexes_size(c.oid) AS "indexLength",
- psut.n_live_tup AS "tableRows"
+SELECT DISTINCT
+ c.relname AS "tableName",
+ COALESCE(b.description, '') AS "tableComment",
+ pg_total_relation_size(c.oid) AS "dataLength",
+ pg_indexes_size(c.oid) AS "indexLength",
+ psut.n_live_tup AS "tableRows"
FROM
- pg_class c
-JOIN
- pg_namespace n ON c.relnamespace = n.oid
-JOIN
- pg_stat_user_tables psut ON psut.relid = c.oid
+ pg_class c
+ LEFT JOIN pg_description b ON c.oid = b.objoid AND b.objsubid = 0
+ JOIN pg_stat_user_tables psut ON psut.relid = c.oid
WHERE
- has_table_privilege(c.oid, 'SELECT')
- and n.nspname = current_schema()
- {{if .tableNames}}
- and c.relname in ({{.tableNames}})
- {{end}}
+ c.relkind = 'r'
+ AND c.relnamespace = (
+ SELECT
+ oid
+ FROM
+ pg_namespace
+ WHERE
+ nspname = current_schema()
+ {{if .tableNames}}
+ and c.relname in ({{.tableNames}})
+ {{end}}
+ )
ORDER BY
- c.relname;
+ c.relname;
---------------------------------------
--PGSQL_INDEX_INFO 表索引信息
SELECT a.indexname AS "indexName",
diff --git a/server/internal/db/domain/entity/db.go b/server/internal/db/domain/entity/db.go
index 541d379d..e8545dfe 100644
--- a/server/internal/db/domain/entity/db.go
+++ b/server/internal/db/domain/entity/db.go
@@ -7,7 +7,7 @@ import (
type Db struct {
model.Model
- Code string `json:"code" gorm:"size:32;not null;index:idx_code"`
+ Code string `json:"code" gorm:"size:32;not null;index:idx_db_code"`
Name string `json:"name" gorm:"size:255;not null;"`
GetDatabaseMode DbGetDatabaseMode `json:"getDatabaseMode" gorm:"comment:库名获取方式(-1.实时获取、1.指定库名)"` // 获取数据库方式
Database string `json:"database" gorm:"size:2000;"`
diff --git a/server/internal/db/imsg/en.go b/server/internal/db/imsg/en.go
index 23fad90b..b6c3e75d 100644
--- a/server/internal/db/imsg/en.go
+++ b/server/internal/db/imsg/en.go
@@ -23,8 +23,6 @@ var En = map[i18n.MsgId]string{
ErrExistRunFailSql: "There is an execution error in sql",
ErrNeedSubmitWorkTicket: "This operation needs to submit a work ticket for approval",
- ErrNoLimitStmt: "Please complete the paging information before executing",
- ErrLimitInvalid: "The number of query result sets should be less than the {{.count}} number configured by the system",
// db transfer
LogDtsSave: "dts - Save data transfer task",
diff --git a/server/internal/db/imsg/imsg.go b/server/internal/db/imsg/imsg.go
index b93f9ef4..6517798d 100644
--- a/server/internal/db/imsg/imsg.go
+++ b/server/internal/db/imsg/imsg.go
@@ -33,8 +33,6 @@ const (
ErrExistRunFailSql
ErrNeedSubmitWorkTicket
- ErrNoLimitStmt
- ErrLimitInvalid
// db transfer
LogDtsSave
diff --git a/server/internal/db/imsg/zh_cn.go b/server/internal/db/imsg/zh_cn.go
index 5c4983e8..d3d94bdf 100644
--- a/server/internal/db/imsg/zh_cn.go
+++ b/server/internal/db/imsg/zh_cn.go
@@ -23,8 +23,6 @@ var Zh_CN = map[i18n.MsgId]string{
ErrExistRunFailSql: "存在执行错误的sql",
ErrNeedSubmitWorkTicket: "该操作需要提交工单审批执行",
- ErrNoLimitStmt: "请完善分页信息后执行",
- ErrLimitInvalid: "查询结果集数需小于系统配置的{{.count}}条",
// db transfer
LogDtsSave: "dts-保存数据迁移任务",
diff --git a/server/internal/flow/domain/entity/procdef.go b/server/internal/flow/domain/entity/procdef.go
index 1d0a110e..dbfa40e1 100644
--- a/server/internal/flow/domain/entity/procdef.go
+++ b/server/internal/flow/domain/entity/procdef.go
@@ -47,7 +47,11 @@ func (p *Procdef) MatchCondition(bizType string, param map[string]any) bool {
return true
}
- res := stringx.TemplateResolve(*p.Condition, collx.Kvs("bizType", bizType, "param", param))
+ res, err := stringx.TemplateResolve(*p.Condition, collx.Kvs("bizType", bizType, "param", param))
+ if err != nil {
+ logx.ErrorTrace("parse condition error", err.Error())
+ return true
+ }
return strings.TrimSpace(res) == "1"
}
diff --git a/server/internal/tag/application/resouce_auth_cert.go b/server/internal/tag/application/resouce_auth_cert.go
index 971bbf11..0d49cb09 100644
--- a/server/internal/tag/application/resouce_auth_cert.go
+++ b/server/internal/tag/application/resouce_auth_cert.go
@@ -316,6 +316,12 @@ func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.R
return errorx.NewBizI(ctx, imsg.ErrAcNameExist, "acName", rac.Name)
}
}
+ if rac.Type == 0 {
+ rac.Type = entity.AuthCertTypePrivate
+ }
+ if rac.CiphertextType == 0 {
+ rac.CiphertextType = entity.AuthCertCiphertextTypePassword
+ }
// 公共凭证
if rac.Type == entity.AuthCertTypePublic {
diff --git a/server/internal/tag/domain/entity/tag_tree.go b/server/internal/tag/domain/entity/tag_tree.go
index d2760012..80c4863a 100644
--- a/server/internal/tag/domain/entity/tag_tree.go
+++ b/server/internal/tag/domain/entity/tag_tree.go
@@ -14,11 +14,11 @@ import (
type TagTree struct {
model.Model
- Type TagType `json:"type" gorm:"not null;default:-1;comment:类型: -1.普通标签; 1机器 2db 3redis 4mongo"` // 类型: -1.普通标签; 其他值则为对应的资源类型
- Code string `json:"code" gorm:"not null;size:50;comment:标识符"` // 标识编码, 若类型不为-1,则为对应资源编码
- CodePath string `json:"codePath" gorm:"not null;size:800;comment:标识符路径"` // 标识路径,tag1/tag2/tagType1|tagCode/tagType2|yyycode/,非普通标签类型段含有标签类型
- Name string `json:"name" gorm:"size:50;comment:名称"` // 名称
- Remark string `json:"remark" gorm:"size:255;"` // 备注说明
+ Type TagType `json:"type" gorm:"not null;default:-1;comment:类型: -1.普通标签; 1机器 2db 3redis 4mongo"` // 类型: -1.普通标签; 其他值则为对应的资源类型
+ Code string `json:"code" gorm:"not null;size:50;index:idx_tag_code;comment:标识符"` // 标识编码, 若类型不为-1,则为对应资源编码
+ CodePath string `json:"codePath" gorm:"not null;size:800;index:idx_tag_code_path,length:255;comment:标识符路径"` // 标识路径,tag1/tag2/tagType1|tagCode/tagType2|yyycode/,非普通标签类型段含有标签类型
+ Name string `json:"name" gorm:"size:50;comment:名称"` // 名称
+ Remark string `json:"remark" gorm:"size:255;"` // 备注说明
}
type TagType int8
diff --git a/server/migration/migrations/init.go b/server/migration/migrations/init.go
index f7f63ad4..fd36f782 100644
--- a/server/migration/migrations/init.go
+++ b/server/migration/migrations/init.go
@@ -157,6 +157,15 @@ func initRole(tx *gorm.DB) error {
role.Creator = "admin"
role.Modifier = "admin"
+ roleResource := &sysentity.RoleResource{
+ RoleId: role.Id,
+ ResourceId: 1,
+ CreateTime: &now,
+ CreatorId: 1,
+ Creator: "admin",
+ }
+
+ tx.Create(roleResource)
return tx.Create(role).Error
}
diff --git a/server/pkg/utils/stringx/stringx.go b/server/pkg/utils/stringx/stringx.go
index 2d6cf127..e551daab 100644
--- a/server/pkg/utils/stringx/stringx.go
+++ b/server/pkg/utils/stringx/stringx.go
@@ -7,14 +7,6 @@ import (
"unicode/utf8"
)
-// 逻辑空字符串(由于gorm更新结构体只更新非零值,所以使用该值最为逻辑空字符串,方便更新结构体)
-const LogicEmptyStr = "-"
-
-// 是否为逻辑上空字符串
-func IsLogicEmpty(str string) bool {
- return str == "" || str == LogicEmptyStr
-}
-
// 可判断中文
func Len(str string) int {
return len([]rune(str))
@@ -89,15 +81,18 @@ func UnicodeIndex(str, substr string) int {
}
// 字符串模板解析
-func TemplateResolve(temp string, data any) string {
- t, _ := template.New("string-temp").Parse(temp)
+func TemplateResolve(temp string, data any) (string, error) {
+ t, err := template.New("string-temp").Parse(temp)
+ if err != nil {
+ return "", err
+ }
var tmplBytes bytes.Buffer
- err := t.Execute(&tmplBytes, data)
+ err = t.Execute(&tmplBytes, data)
if err != nil {
- panic(err)
+ return "", err
}
- return tmplBytes.String()
+ return tmplBytes.String(), nil
}
func ReverStrTemplate(temp, str string, res map[string]any) {
diff --git a/server/pkg/utils/structx/structx_test.go b/server/pkg/utils/structx/structx_test.go
index 0bba00a2..2ed59c68 100644
--- a/server/pkg/utils/structx/structx_test.go
+++ b/server/pkg/utils/structx/structx_test.go
@@ -190,7 +190,7 @@ func TestTemplateResolve(t *testing.T) {
d := make(map[string]string)
d["Name"] = "黄先生"
d["Age"] = "23jlfdsjf"
- resolve := stringx.TemplateResolve("{{.Name}} is name, and {{.Age}} is age", d)
+ resolve, _ := stringx.TemplateResolve("{{.Name}} is name, and {{.Age}} is age", d)
fmt.Println(resolve)
}