Merge pull request #8552 from TencentBlueKing/v3.14.x

v3.14.x合入master
This commit is contained in:
扬扬扬扬扬
2025-11-25 15:04:46 +08:00
committed by GitHub
108 changed files with 1810 additions and 826 deletions

View File

@@ -5,11 +5,11 @@ spec_version: 1
release:
# 发布版本号;
# 资源配置更新,需更新此版本号才会发布资源版本,此版本号和 sdk 版本号一致,错误设置会影响调用方使用
version: 3.14.6
version: 3.14.7-alpha2
# 版本标题
title: "3.14.6"
title: "3.14.7-alpha2"
# 版本描述
comment: "3.14.6"
comment: "3.14.7-alpha2"
# 定义网关基本信息,用于命令 `sync_apigw_config`
apigateway:
@@ -75,6 +75,23 @@ grant_permissions:
- list_kube_container
- list_cached_kube_pod_label_key
- list_cached_kube_pod_label_value
- search_biz_inst_topo
- get_biz_internal_module
- list_biz_hosts
- list_hosts_without_biz
- find_host_biz_relations
- list_biz_hosts_topo
- find_module_host_relation
- resource_watch
- search_business
- search_cloud_area
- search_object_attribute
- find_topo_node_paths
- search_dynamic_group
- execute_dynamic_group
- get_biz_brief_cache_topo
- list_business_set
- list_business_in_business_set
- bk_app_code: {{ environ.BK_NODEMAN_APP_CODE }}
grant_dimension: "resource"
resource_names:

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
### Description
Add hosts to the business idle hosts. This interface ensures that hosts are either added successfully together or fail
together (v3.10.25+, Permission: Host pool host allocation to business permission)
together (v3.10.25+, Permission: Edit business host permission)
### Parameters

View File

@@ -11,7 +11,7 @@ Clone Host Properties (Permission: Business Host Editing Permission)
| bk_org_id | int | Yes | Source host ID |
| bk_dst_id | int | Yes | Target host ID |
| bk_biz_id | int | Yes | Business ID |
| bk_cloud_id | int | No | Control area ID |
| bk_cloud_id | int | Yes | Control area ID |
Note: Cloning using the internal IP of the host and cloning using the identity ID of the host, these two methods can
only be used separately and cannot be mixed.

View File

@@ -58,7 +58,7 @@ business attribute fields. The combination supports only AND operations, can be
"bk_biz_set_attr":{
"bk_biz_set_name":"biz_set",
"bk_biz_set_desc":"xxx",
"biz_set_maintainer":"xxx"
"bk_biz_maintainer":"xxx"
},
"bk_scope":{
"match_all":false,

View File

@@ -2,7 +2,7 @@
Update Module (Permission: Business Topology Editing Permission)
### Parameters
### URL Parameters
| Name | Type | Required | Description |
|---------------------|--------|----------|-------------------|
@@ -10,9 +10,8 @@ Update Module (Permission: Business Topology Editing Permission)
| bk_biz_id | int | Yes | Business ID |
| bk_set_id | int | Yes | Cluster ID |
| bk_module_id | int | Yes | Module ID |
| data | dict | Yes | Module data |
#### data
#### Parameters
| Name | Type | Required | Description |
|-----------------|--------|----------|-------------------|
@@ -21,7 +20,7 @@ Update Module (Permission: Business Topology Editing Permission)
| operator | string | No | Main maintainer |
| bk_bak_operator | string | No | Backup maintainer |
**Note: The data parameter here only explains the system-built editable parameters, and the rest of the parameters to be
**Note: The parameter here only explains the system-built editable parameters, and the rest of the parameters to be
filled depend on the user's own defined attribute fields. Modules created through service templates can only be modified
through service templates.**
@@ -29,15 +28,10 @@ through service templates.**
```json
{
"bk_biz_id": 1,
"bk_set_id": 1,
"bk_module_id": 1,
"data": {
"bk_module_name": "test",
"bk_module_type": "1",
"operator": "admin",
"bk_bak_operator": "admin"
}
"bk_module_name": "test",
"bk_module_type": "1",
"operator": "admin",
"bk_bak_operator": "admin"
}
```

View File

@@ -2,7 +2,7 @@
新增主机到业务空闲机
- 此接口保证主机要么同时添加成功,要么同时失败(v3.10.25+,权限:主机池主机分配到业务权限)
- 此接口保证主机要么同时添加成功,要么同时失败(v3.10.25+,权限:业务主机编辑权限)
### 输入参数

View File

@@ -11,7 +11,7 @@
| bk_org_id | int | 是 | 源主机ID |
| bk_dst_id | int | 是 | 目标主机ID |
| bk_biz_id | int | 是 | 业务ID |
| bk_cloud_id | int | | 管控区域ID |
| bk_cloud_id | int | | 管控区域ID |
注: 使用主机内网IP进行克隆与使用主机身份ID进行克隆这两种方式只能使用期中的一种不能混用。

View File

@@ -56,7 +56,7 @@
"bk_biz_set_attr":{
"bk_biz_set_name":"biz_set",
"bk_biz_set_desc":"xxx",
"biz_set_maintainer":"xxx"
"bk_biz_maintainer":"xxx"
},
"bk_scope":{
"match_all":false,

View File

@@ -2,7 +2,7 @@
更新模块(权限:业务拓扑编辑权限)
### 输入参数
### URL输入参数
| 参数名称 | 参数类型 | 必选 | 描述 |
|---------------------|--------|----|-------|
@@ -10,9 +10,8 @@
| bk_biz_id | int | 是 | 业务id |
| bk_set_id | int | 是 | 集群id |
| bk_module_id | int | 是 | 模块id |
| data | dict | 是 | 模块数据 |
#### data
#### 输入参数
| 参数名称 | 参数类型 | 必选 | 描述 |
|-----------------|--------|----|-------|
@@ -21,22 +20,16 @@
| operator | string | 否 | 主要维护人 |
| bk_bak_operator | string | 否 | 备份维护人 |
**注意:此处data参数仅对系统内置可编辑的参数做了说明,其余需要填写的参数取决于用户自己定义的属性字段;通过服务模板创建的模块,只能通过服务模板修改
**
**注意:此处输入参数仅对系统内置可编辑的参数做了说明,其余需要填写的参数取决于用户自己定义的属性字段;通过服务模板创建的模块,只能通过服务模板修改**
### 调用示例
```json
{
"bk_biz_id": 1,
"bk_set_id": 1,
"bk_module_id": 1,
"data": {
"bk_module_name": "test",
"bk_module_type": "1",
"operator": "admin",
"bk_bak_operator": "admin"
}
"bk_module_name": "test",
"bk_module_type": "1",
"operator": "admin",
"bk_bak_operator": "admin"
}
```

View File

@@ -6,6 +6,7 @@
| 参数名称 | 参数类型 | 必选 | 描述 |
|---------------------|--------|----|---------------|
| bk_biz_id | int | 是 | 业务ID |
| process_template_id | int | 是 | 进程模板ID |
| process_property | object | 是 | 需要更新的进程模板字段信息 |

View File

@@ -1,5 +1,5 @@
specVersion: 3
appVersion: "3.14.6"
appVersion: "3.14.7-alpha2"
app:
bkAppCode: "bk_cmdb_saas"
bkAppName: "蓝鲸配置平台"

View File

@@ -1,3 +1,40 @@
## [Version: v3.14.7-alpha2] - 2025-09-08
**功能优化**
- 导出文件名增加时间戳
- 主机与实例导出成功后退出不再提示确认关闭
- 动态分组IP默认分割
- 为job添加默认apigw接口权限
- apigw接口描述国际化
**缺陷修复**
- 修复枚举类型字段默认值选择异常的问题
## [Version: v3.14.7-alpha1] - 2025-08-06
**新增功能**
- 接入审计中心
- 添加前端版本更新检查并提示刷新功能
- 字段类型支持插件
**功能优化**
- 前端不使用proxy接口代理从用户管理查询用户列表的接口
- 删除gse目录并处理一些codeql扫描问题
- 更新license
- 用户态和应用态apigw接口文档调整
**缺陷修复**
- 采集主机数据中IP随机排序问题
- 修复切换业务集时出现参数错误的提示问题
- 修复业务集新建后操作无权限无法展示资源名称的问题
- 修复动态分组日期类型条件出现重复项的问题
- 修复动态分组条件类型保存错误
- 添加资源主机转移操作预鉴权
- 动态分组主机条件in或not in时查询错误
- 新增主机到模块选择主机后点击下一步后页面显示空白
- 项目列表多出一个项目图标字段
- 集群模板-英文状态下更新模版属性后“同步功能”不支持点击跳转了
- 服务实例-批量编辑标签时没有及时触发校验规则
- 修复主机/实例高级搜索 操作符为contains或者contains(cs)时输入\报错的问题
## [Version: v3.14.6] - 2025-05-28
**功能优化**
- mongodb、redis、elasticsearch支持tls连接

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_adminserver /data/cmdb/cmdb_adminserver
RUN mkdir /data/cmdb/cmdb_adminserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_apiserver /data/cmdb/cmdb_apiserver
RUN mkdir /data/cmdb/cmdb_apiserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_authserver /data/cmdb/cmdb_authserver
RUN mkdir /data/cmdb/cmdb_authserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_cacheservice /data/cmdb/cmdb_cacheservice
RUN mkdir /data/cmdb/cmdb_cacheservice/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_cloudserver /data/cmdb/cmdb_cloudserver
RUN mkdir /data/cmdb/cmdb_cloudserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_coreservice /data/cmdb/cmdb_coreservice
RUN mkdir /data/cmdb/cmdb_coreservice/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_datacollection /data/cmdb/cmdb_datacollection
RUN mkdir /data/cmdb/cmdb_datacollection/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_eventserver /data/cmdb/cmdb_eventserver
RUN mkdir /data/cmdb/cmdb_eventserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_hostserver /data/cmdb/cmdb_hostserver
RUN mkdir /data/cmdb/cmdb_hostserver/logs

View File

@@ -1,2 +1,2 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
RUN yum -y install jq

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_monstache /data/cmdb/monstache
RUN chmod +x /data/cmdb/monstache/monstache

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_operationserver /data/cmdb/cmdb_operationserver
RUN mkdir /data/cmdb/cmdb_operationserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_procserver /data/cmdb/cmdb_procserver
RUN mkdir /data/cmdb/cmdb_procserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_synchronizeserver /data/cmdb/cmdb_synchronizeserver
RUN mkdir /data/cmdb/cmdb_synchronizeserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_taskserver /data/cmdb/cmdb_taskserver
RUN mkdir /data/cmdb/cmdb_taskserver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_toposerver /data/cmdb/cmdb_toposerver
RUN mkdir /data/cmdb/cmdb_toposerver/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_transferservice /data/cmdb/cmdb_transferservice
RUN mkdir /data/cmdb/cmdb_transferservice/logs

View File

@@ -1,4 +1,4 @@
FROM hub.bktencent.com/blueking/centos7-cmdb:base
FROM mirrors.tencent.com/tencentos/tencentos3-minimal
ENV container docker
COPY cmdb_webserver /data/cmdb/cmdb_webserver
RUN mkdir /data/cmdb/cmdb_webserver/logs

View File

@@ -1,9 +1,9 @@
apiVersion: v2
appVersion: 3.14.6
appVersion: 3.14.7-alpha2
description: BlueKing Configuration Management DataBase (bk-cmdb) is an enterprise level configuration management serivce database.
name: bk-cmdb
type: application
version: 3.15.6
version: 3.15.7-alpha2
dependencies:
- name: common
repository: https://charts.bitnami.com/bitnami

View File

@@ -306,6 +306,14 @@ data:
# 用于解密根据RFC1423加密的证书密钥的PEM块
password: {{ .Values.common.apiGW.tls.password }}
# 审计中心数据上报配置
auditCenter:
enabled: {{ .Values.common.auditCenter.enabled }}
appCode: {{ .Values.common.auditCenter.appCode }}
appSecret: {{ .Values.common.auditCenter.appSecret }}
endpoint: {{ .Values.common.auditCenter.endpoint }}
token: {{ .Values.common.auditCenter.token }}
extra.yaml: |-

View File

@@ -1420,6 +1420,12 @@ common:
caFile:
# 用于解密根据RFC1423加密的证书密钥的PEM块
password:
auditCenter:
enabled: false
appCode:
appSecret:
endpoint:
token:
## @section zookeeper parameters
##

View File

@@ -1,9 +1,9 @@
apiVersion: v2
appVersion: 3.14.6
appVersion: 3.14.7-alpha2
description: BlueKing Configuration Management DataBase (bk-cmdb) is an enterprise level configuration management serivce database.
name: bk-cmdb-web
type: application
version: 3.15.6
version: 3.15.7-alpha2
dependencies:
- name: common
repository: https://charts.bitnami.com/bitnami

25
go.mod
View File

@@ -23,7 +23,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/gomodule/redigo v2.0.0+incompatible
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.4.0
github.com/google/uuid v1.6.0
github.com/gorilla/sessions v1.2.1
github.com/json-iterator/go v1.1.12
github.com/juju/ratelimit v1.0.1
@@ -64,7 +64,11 @@ require (
stathat.com/c/consistent v1.0.0
)
require github.com/mozillazg/go-pinyin v0.20.0
require (
github.com/mozillazg/go-pinyin v0.20.0
go.opentelemetry.io/proto/otlp v1.3.1
google.golang.org/protobuf v1.34.1
)
require (
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
@@ -87,11 +91,11 @@ require (
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
@@ -141,16 +145,15 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.17.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
google.golang.org/grpc v1.61.1 // indirect
google.golang.org/protobuf v1.36.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
google.golang.org/grpc v1.64.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

44
go.sum
View File

@@ -237,8 +237,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -281,8 +281,8 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
@@ -297,8 +297,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
@@ -597,8 +597,8 @@ go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJ
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -616,8 +616,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -630,8 +630,9 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -704,8 +705,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -955,11 +956,10 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No=
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -978,8 +978,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -993,8 +993,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -35,6 +35,7 @@
"module_property_set_template_id": "集群模板ID",
"module_property_apply_host_property_enabled": "主机属性自动应用",
"plat_property_bk_status": "状态",
"plat_property_bk_status_detail": "状态详情",
"plat_property_bk_cloud_vendor": "云厂商",
"plat_property_bk_vpc_id": "VPC唯一标识",
"plat_property_bk_vpc_name": "VPC名称",

View File

@@ -37,6 +37,7 @@
"plat_property_bk_cloud_name": "BK-Network Area",
"plat_property_bk_supplier_account": "Supplier",
"plat_property_bk_status": "Status",
"plat_property_bk_status_detail": "Status Detail",
"plat_property_bk_cloud_vendor": "Cloud vendor",
"plat_property_bk_vpc_id": "VPC ID",
"plat_property_bk_vpc_name": "VPC name",

View File

@@ -779,53 +779,70 @@ func (i IAM) SyncIAMSysInstances(ctx context.Context, redisCli redis.Client, obj
// 因为资源间的依赖关系,新建的顺序则反过来为 1.ResourceType 2.InstanceSelection 3.Action
// ActionGroup依赖于Action该资源的增删操作始终放在最后
// 先删除资源,再新增资源,因为实例视图的名称在系统中是唯一的,如果不先删,同样名称的实例视图将创建失败
options := &SyncRegisterOptions{
AddedActions: addedActions,
DeletedActions: deletedActions,
AddedInstanceSelections: addedInstanceSelections,
AddedResourceTypes: addedResourceTypes,
DeletedInstanceSelections: deletedInstanceSelections,
DeletedResourceTypes: deletedResourceTypes,
}
err = i.deleteIamResources(ctx, deletedActions, deletedInstanceSelections, deletedResourceTypes, rid)
return i.syncRegisterActions(ctx, iamResp, objects, options)
}
func (i IAM) syncRegisterActions(ctx context.Context, iamResp *SystemResp, objects []metadata.Object,
options *SyncRegisterOptions) error {
rid := commonutil.ExtractRequestIDFromContext(ctx)
err := i.deleteIamResources(ctx, options.DeletedActions, options.DeletedInstanceSelections,
options.DeletedResourceTypes, rid)
if err != nil {
return err
}
// add cmdb ResourceTypes in iam
if len(addedResourceTypes) > 0 {
if len(options.AddedResourceTypes) > 0 {
blog.Infof("begin add resourceTypes, count:%d, detail:%v, rid: %s",
len(addedResourceTypes), addedResourceTypes, rid)
if err := i.Client.RegisterResourcesTypes(ctx, addedResourceTypes); err != nil {
len(options.AddedResourceTypes), options.AddedResourceTypes, rid)
if err := i.Client.RegisterResourcesTypes(ctx, options.AddedResourceTypes); err != nil {
blog.ErrorJSON("sync iam sysInstances failed, add resourceType error: %s, resourceType: %s, rid: %s",
err, addedResourceTypes, rid)
err, options.AddedResourceTypes, rid)
return err
}
}
// add cmdb InstanceSelections in iam
if len(addedInstanceSelections) > 0 {
if len(options.AddedInstanceSelections) > 0 {
blog.Infof("begin add instanceSelections, count:%d, detail:%v, rid: %s",
len(addedInstanceSelections), addedInstanceSelections, rid)
if err := i.Client.RegisterInstanceSelections(ctx, addedInstanceSelections); err != nil {
len(options.AddedInstanceSelections), options.AddedInstanceSelections, rid)
if err := i.Client.RegisterInstanceSelections(ctx, options.AddedInstanceSelections); err != nil {
blog.ErrorJSON("sync iam sysInstances failed, add instanceSelections error: %s, instanceSelections: %s, "+
"rid: %s", err, addedInstanceSelections, rid)
"rid: %s", err, options.AddedInstanceSelections, rid)
return err
}
}
// add cmdb actions in iam
if len(addedActions) > 0 {
blog.Infof("begin add actions, count:%d, detail:%v, rid: %s", len(addedActions), addedActions, rid)
if err := i.Client.RegisterActions(ctx, addedActions); err != nil {
if len(options.AddedActions) > 0 {
blog.Infof("begin add actions, count:%d, detail:%v, rid: %s", len(options.AddedActions), options.AddedActions,
rid)
if err := i.Client.RegisterActions(ctx, options.AddedActions); err != nil {
blog.ErrorJSON("sync iam sysInstances failed, add IAM actions failed, error: %s, actions: %s, rid: %s",
err, addedActions, rid)
err, options.AddedActions, rid)
return err
}
}
// update action_groups in iam, the action groups contains only the existed actions in iam
if len(addedActions) > 0 || len(deletedActions) > 0 {
if len(options.AddedActions) > 0 || len(options.DeletedActions) > 0 {
actionMap := map[ActionID]struct{}{}
for _, action := range iamResp.Data.Actions {
if !isIAMSysInstanceAction(action.ID) {
actionMap[action.ID] = struct{}{}
}
}
for _, action := range cmdbActions {
for _, action := range options.CmdbActions {
actionMap[action.ID] = struct{}{}
}
@@ -842,7 +859,6 @@ func (i IAM) SyncIAMSysInstances(ctx context.Context, redisCli redis.Client, obj
}
}
}
return nil
}

View File

@@ -662,6 +662,17 @@ type RelateResourceType struct {
InstanceSelections []RelatedInstanceSelection `json:"related_instance_selections"`
}
// SyncRegisterOptions iam sync register options
type SyncRegisterOptions struct {
CmdbActions []ResourceAction
DeletedActions []ActionID
DeletedInstanceSelections []InstanceSelectionID
DeletedResourceTypes []TypeID
AddedActions []ResourceAction
AddedInstanceSelections []InstanceSelection
AddedResourceTypes []ResourceType
}
// Scope TODO
type Scope struct {
Op string `json:"op"`
@@ -710,7 +721,7 @@ const (
BusinessHostTransferSelection InstanceSelectionID = "business_host_transfer"
// BizSetSelection TODO
BizSetSelection InstanceSelectionID = "business_set_list"
//ProjectSelection project selection
// ProjectSelection project selection
ProjectSelection InstanceSelectionID = "project"
// BizHostInstanceSelection TODO
BizHostInstanceSelection InstanceSelectionID = "biz_host_instance"

View File

@@ -331,31 +331,40 @@ func (ps *parseStream) businessSet() *parseStream {
return ps
}
ps = ps.basicBizSetOperations()
if ps.shouldReturn() {
return ps
}
ps = ps.bizSetListOperations()
if ps.shouldReturn() {
return ps
}
ps = ps.bizSetTopoOperations()
if ps.shouldReturn() {
return ps
}
return ps
}
func (ps *parseStream) basicBizSetOperations() *parseStream {
if ps.hitPattern(findBizInBizSetPattern, http.MethodPost) {
bizSetIDVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_id")
if err != nil {
ps.err = err
return ps
}
bizSetID := bizSetIDVal.Int()
if bizSetID <= 0 {
ps.err = errors.New("invalid biz set id")
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.AccessBizSet,
InstanceID: bizSetID,
},
},
}
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
return ps
}
// update biz set, authorize if user has update permission to all of the specified biz set ids
if ps.hitPattern(updateBizSetPattern, http.MethodPut) {
bizSetIDsVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_ids")
@@ -363,40 +372,28 @@ func (ps *parseStream) businessSet() *parseStream {
ps.err = err
return ps
}
bizSetIDArr := bizSetIDsVal.Array()
if len(bizSetIDArr) == 0 {
ps.err = errors.New("bk_biz_set_ids is not set")
return ps
}
for _, bizSetIDVal := range bizSetIDArr {
bizSetID := bizSetIDVal.Int()
if bizSetID <= 0 {
ps.err = errors.New("invalid biz set id")
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.Update,
InstanceID: bizSetID,
},
},
}
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet, Action: meta.Update,
InstanceID: bizSetID}}}
}
return ps
}
if ps.hitPattern(deleteBizSetPattern, http.MethodPost) {
bizSetIDsVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_ids")
if err != nil {
ps.err = err
return ps
}
bizSetIDArr := bizSetIDsVal.Array()
if len(bizSetIDArr) == 0 {
ps.err = errors.New("bk_biz_set_ids is not set")
@@ -406,52 +403,32 @@ func (ps *parseStream) businessSet() *parseStream {
ps.err = errors.New("bk_biz_set_ids exceeds maximum length 100")
return ps
}
for _, bizSetIDVal := range bizSetIDArr {
bizSetID := bizSetIDVal.Int()
if bizSetID <= 0 {
ps.err = errors.New("invalid biz set id")
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.Delete,
InstanceID: bizSetID,
},
},
}
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet, Action: meta.Delete,
InstanceID: bizSetID}}}
}
return ps
}
if ps.hitPattern(findBizSetTopoPattern, http.MethodPost) {
bizSetIDVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_id")
if err != nil {
ps.err = err
return ps
}
bizSetID := bizSetIDVal.Int()
if bizSetID <= 0 {
ps.err = errors.New("invalid biz set id")
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.AccessBizSet,
InstanceID: bizSetID,
},
},
}
// create business set, this is not a normalize api.
if ps.hitPattern(createBizSetPattern, http.MethodPost) {
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet, Action: meta.Create}}}
return ps
}
// preview business set
if ps.hitPattern(previewBusinessSetPattern, http.MethodPost) {
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
Action: meta.SkipAction}}}
return ps
}
return ps
}
func (ps *parseStream) bizSetListOperations() *parseStream {
// find many business set list for the user with any business set resources
if ps.hitPattern(findmanyBusinessSetPattern, http.MethodPost) {
ps.Attribute.Resources = []meta.ResourceAttribute{
@@ -490,133 +467,81 @@ func (ps *parseStream) businessSet() *parseStream {
}
return ps
}
return ps
}
// create business set, this is not a normalize api.
if ps.hitPattern(createBizSetPattern, http.MethodPost) {
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.Create,
},
},
func (ps *parseStream) bizSetTopoOperations() *parseStream {
if ps.hitPattern(findBizSetTopoPattern, http.MethodPost) {
bizSetIDVal, err := ps.RequestCtx.getValueFromBody("bk_biz_set_id")
if err != nil {
ps.err = err
return ps
}
bizSetID := bizSetIDVal.Int()
if bizSetID <= 0 {
ps.err = errors.New("invalid biz set id")
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
return ps
}
// preview business set
if ps.hitPattern(previewBusinessSetPattern, http.MethodPost) {
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.SkipAction,
},
},
}
return ps
}
if ps.hitRegexp(listSetInBizSetRegexp, http.MethodPost) {
if len(ps.RequestCtx.Elements) != 8 {
ps.err = fmt.Errorf("get invalid url elements length %d", len(ps.RequestCtx.Elements))
return ps
}
bizSetID, err := strconv.ParseInt(ps.RequestCtx.Elements[5], 10, 64)
if err != nil {
ps.err = fmt.Errorf("get invalid business set id %s, err: %v", ps.RequestCtx.Elements[5], err)
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.AccessBizSet,
InstanceID: bizSetID,
},
},
}
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
return ps
}
if ps.hitRegexp(listModuleInBizSetRegexp, http.MethodPost) {
if len(ps.RequestCtx.Elements) != 10 {
ps.err = fmt.Errorf("get invalid url elements length %d", len(ps.RequestCtx.Elements))
return ps
}
bizSetID, err := strconv.ParseInt(ps.RequestCtx.Elements[5], 10, 64)
if err != nil {
ps.err = fmt.Errorf("get invalid business set id %s, err: %v", ps.RequestCtx.Elements[5], err)
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.AccessBizSet,
InstanceID: bizSetID,
},
},
}
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
return ps
}
if ps.hitRegexp(findBizSetTopoPathRegexp, http.MethodPost) {
if len(ps.RequestCtx.Elements) != 8 {
ps.err = fmt.Errorf("get invalid url elements length %d", len(ps.RequestCtx.Elements))
return ps
}
bizSetID, err := strconv.ParseInt(ps.RequestCtx.Elements[5], 10, 64)
if err != nil {
ps.err = fmt.Errorf("get invalid business set id %s, err: %v", ps.RequestCtx.Elements[5], err)
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.AccessBizSet,
InstanceID: bizSetID,
},
},
}
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
return ps
}
if ps.hitRegexp(countTopoHostAndSrvRegexp, http.MethodPost) {
if len(ps.RequestCtx.Elements) != 7 {
ps.err = fmt.Errorf("get invalid url elements length %d", len(ps.RequestCtx.Elements))
return ps
}
bizSetID, err := strconv.ParseInt(ps.RequestCtx.Elements[6], 10, 64)
if err != nil {
ps.err = fmt.Errorf("get invalid business set id %s, err: %v", ps.RequestCtx.Elements[6], err)
return ps
}
ps.Attribute.Resources = []meta.ResourceAttribute{
{
Basic: meta.Basic{
Type: meta.BizSet,
Action: meta.AccessBizSet,
InstanceID: bizSetID,
},
},
}
ps.Attribute.Resources = []meta.ResourceAttribute{{Basic: meta.Basic{Type: meta.BizSet,
Action: meta.AccessBizSet, InstanceID: bizSetID}}}
return ps
}
return ps
}

View File

@@ -78,8 +78,9 @@ func (h *host) TransferToNormalModule(ctx context.Context, header http.Header, i
// 如果主机属于空闲机模块,操作失败
// 如果主机属于故障机模块,操作失败
// 如果主机不在参数指定的模块中,操作失败
func (h *host) RemoveFromModule(ctx context.Context, header http.Header,
input *metadata.RemoveHostsFromModuleOption) (resp *metadata.OperaterException, err error) {
func (h *host) RemoveFromModule(ctx context.Context, header http.Header, input *metadata.RemoveHostsFromModuleOption) (
resp *metadata.OperaterException, err error) {
resp = new(metadata.OperaterException)
subPath := "/delete/host/host_module_relations"
@@ -96,6 +97,7 @@ func (h *host) RemoveFromModule(ctx context.Context, header http.Header,
// TransferToAnotherBusiness transfer host to other business module
func (h *host) TransferToAnotherBusiness(ctx context.Context, header http.Header,
input *metadata.TransferHostsCrossBusinessRequest) (resp *metadata.OperaterException, err error) {
resp = new(metadata.OperaterException)
subPath := "/set/module/host/relation/cross/business"
@@ -186,6 +188,7 @@ func (h *host) FindIdentifier(ctx context.Context, header http.Header, input *me
// GetHostByID TODO
func (h *host) GetHostByID(ctx context.Context, header http.Header, hostID int64) (resp *metadata.HostInstanceResult,
err error) {
resp = new(metadata.HostInstanceResult)
subPath := "/find/host/%d"
@@ -224,8 +227,9 @@ func (h *host) GetHosts(ctx context.Context, header http.Header, opt *metadata.Q
}
// LockHost TODO
func (h *host) LockHost(ctx context.Context, header http.Header,
input *metadata.HostLockRequest) (resp *metadata.HostLockResponse, err error) {
func (h *host) LockHost(ctx context.Context, header http.Header, input *metadata.HostLockRequest) (
resp *metadata.HostLockResponse, err error) {
resp = new(metadata.HostLockResponse)
subPath := "/find/host/lock"
@@ -240,8 +244,9 @@ func (h *host) LockHost(ctx context.Context, header http.Header,
}
// UnlockHost TODO
func (h *host) UnlockHost(ctx context.Context, header http.Header,
input *metadata.HostLockRequest) (resp *metadata.HostLockResponse, err error) {
func (h *host) UnlockHost(ctx context.Context, header http.Header, input *metadata.HostLockRequest) (
resp *metadata.HostLockResponse, err error) {
resp = new(metadata.HostLockResponse)
subPath := "/delete/host/lock"
@@ -256,8 +261,9 @@ func (h *host) UnlockHost(ctx context.Context, header http.Header,
}
// QueryHostLock TODO
func (h *host) QueryHostLock(ctx context.Context, header http.Header,
input *metadata.QueryHostLockRequest) (resp *metadata.HostLockQueryResponse, err error) {
func (h *host) QueryHostLock(ctx context.Context, header http.Header, input *metadata.QueryHostLockRequest) (
resp *metadata.HostLockQueryResponse, err error) {
resp = new(metadata.HostLockQueryResponse)
subPath := "/findmany/host/lock/search"
@@ -272,8 +278,8 @@ func (h *host) QueryHostLock(ctx context.Context, header http.Header,
}
// CreateDynamicGroup is dynamic group query datas base on conditions action api machinery.
func (h *host) CreateDynamicGroup(ctx context.Context, header http.Header,
data *metadata.DynamicGroup) (resp *metadata.IDResult, err error) {
func (h *host) CreateDynamicGroup(ctx context.Context, header http.Header, data *metadata.DynamicGroup) (
resp *metadata.IDResult, err error) {
resp = new(metadata.IDResult)
subPath := "/create/dynamicgroup"
@@ -289,8 +295,8 @@ func (h *host) CreateDynamicGroup(ctx context.Context, header http.Header,
}
// UpdateDynamicGroup is dynamic group update action api machinery.
func (h *host) UpdateDynamicGroup(ctx context.Context, bizID, id string,
header http.Header, data map[string]interface{}) (resp *metadata.BaseResp, err error) {
func (h *host) UpdateDynamicGroup(ctx context.Context, bizID, id string, header http.Header,
data map[string]interface{}) (resp *metadata.BaseResp, err error) {
resp = new(metadata.BaseResp)
subPath := "/update/dynamicgroup/%s/%s"
@@ -306,8 +312,8 @@ func (h *host) UpdateDynamicGroup(ctx context.Context, bizID, id string,
}
// DeleteDynamicGroup is dynamic group delete action api machinery.
func (h *host) DeleteDynamicGroup(ctx context.Context, bizID, id string,
header http.Header) (resp *metadata.BaseResp, err error) {
func (h *host) DeleteDynamicGroup(ctx context.Context, bizID, id string, header http.Header) (
resp *metadata.BaseResp, err error) {
resp = new(metadata.BaseResp)
subPath := "/delete/dynamicgroup/%s/%s"
@@ -323,8 +329,8 @@ func (h *host) DeleteDynamicGroup(ctx context.Context, bizID, id string,
}
// GetDynamicGroup is dynamic group query detail action api machinery.
func (h *host) GetDynamicGroup(ctx context.Context, bizID, id string,
header http.Header) (resp *metadata.GetDynamicGroupResult, err error) {
func (h *host) GetDynamicGroup(ctx context.Context, bizID, id string, header http.Header) (
resp *metadata.GetDynamicGroupResult, err error) {
resp = new(metadata.GetDynamicGroupResult)
subPath := "/find/dynamicgroup/%s/%s"
@@ -340,8 +346,8 @@ func (h *host) GetDynamicGroup(ctx context.Context, bizID, id string,
}
// SearchDynamicGroup is dynamic group search action api machinery.
func (h *host) SearchDynamicGroup(ctx context.Context, header http.Header,
opt *metadata.QueryCondition) (resp *metadata.SearchDynamicGroupResult, err error) {
func (h *host) SearchDynamicGroup(ctx context.Context, header http.Header, opt *metadata.QueryCondition) (
resp *metadata.SearchDynamicGroupResult, err error) {
resp = new(metadata.SearchDynamicGroupResult)
subPath := "/findmany/dynamicgroup/search"
@@ -357,8 +363,9 @@ func (h *host) SearchDynamicGroup(ctx context.Context, header http.Header,
}
// AddUserCustom TODO
func (h *host) AddUserCustom(ctx context.Context, user string, header http.Header,
dat map[string]interface{}) (resp *metadata.BaseResp, err error) {
func (h *host) AddUserCustom(ctx context.Context, user string, header http.Header, dat map[string]interface{}) (
resp *metadata.BaseResp, err error) {
resp = new(metadata.BaseResp)
subPath := "/create/usercustom/%s"
@@ -375,6 +382,7 @@ func (h *host) AddUserCustom(ctx context.Context, user string, header http.Heade
// UpdateUserCustomByID TODO
func (h *host) UpdateUserCustomByID(ctx context.Context, user string, id string, header http.Header,
dat map[string]interface{}) (resp *metadata.BaseResp, err error) {
resp = new(metadata.BaseResp)
subPath := "/update/usercustom/%s/%s"
@@ -389,8 +397,9 @@ func (h *host) UpdateUserCustomByID(ctx context.Context, user string, id string,
}
// GetUserCustomByUser TODO
func (h *host) GetUserCustomByUser(ctx context.Context, user string,
header http.Header) (resp *metadata.GetUserCustomResult, err error) {
func (h *host) GetUserCustomByUser(ctx context.Context, user string, header http.Header) (
resp *metadata.GetUserCustomResult, err error) {
resp = new(metadata.GetUserCustomResult)
subPath := "/find/usercustom/user/search/%s"
@@ -406,6 +415,7 @@ func (h *host) GetUserCustomByUser(ctx context.Context, user string,
// GetDefaultUserCustom TODO
func (h *host) GetDefaultUserCustom(ctx context.Context, header http.Header) (resp *metadata.GetUserCustomResult,
err error) {
resp = new(metadata.GetUserCustomResult)
subPath := "/find/usercustom/default"
@@ -420,8 +430,9 @@ func (h *host) GetDefaultUserCustom(ctx context.Context, header http.Header) (re
}
// UpdateDefaultUserCustom TODO
func (h *host) UpdateDefaultUserCustom(ctx context.Context, header http.Header,
dat map[string]interface{}) (resp *metadata.BaseResp, err error) {
func (h *host) UpdateDefaultUserCustom(ctx context.Context, header http.Header, dat map[string]interface{}) (
resp *metadata.BaseResp, err error) {
resp = new(metadata.BaseResp)
err = h.client.Put().
@@ -435,8 +446,9 @@ func (h *host) UpdateDefaultUserCustom(ctx context.Context, header http.Header,
}
// AddHostFavourite TODO
func (h *host) AddHostFavourite(ctx context.Context, user string, header http.Header,
dat *metadata.FavouriteParms) (resp *metadata.IDResult, err error) {
func (h *host) AddHostFavourite(ctx context.Context, user string, header http.Header, dat *metadata.FavouriteParms) (
resp *metadata.IDResult, err error) {
resp = new(metadata.IDResult)
subPath := "/create/hosts/favorites/%s"
@@ -453,6 +465,7 @@ func (h *host) AddHostFavourite(ctx context.Context, user string, header http.He
// UpdateHostFavouriteByID TODO
func (h *host) UpdateHostFavouriteByID(ctx context.Context, user string, id string, header http.Header,
dat map[string]interface{}) (resp *metadata.BaseResp, err error) {
resp = new(metadata.BaseResp)
subPath := "/update/hosts/favorites/%s/%s"
@@ -467,8 +480,9 @@ func (h *host) UpdateHostFavouriteByID(ctx context.Context, user string, id stri
}
// DeleteHostFavouriteByID TODO
func (h *host) DeleteHostFavouriteByID(ctx context.Context, user string, id string,
header http.Header) (resp *metadata.BaseResp, err error) {
func (h *host) DeleteHostFavouriteByID(ctx context.Context, user string, id string, header http.Header) (
resp *metadata.BaseResp, err error) {
resp = new(metadata.BaseResp)
subPath := "/delete/hosts/favorites/%s/%s"
@@ -483,8 +497,9 @@ func (h *host) DeleteHostFavouriteByID(ctx context.Context, user string, id stri
}
// ListHostFavourites TODO
func (h *host) ListHostFavourites(ctx context.Context, user string, header http.Header,
dat *metadata.QueryInput) (resp *metadata.GetHostFavoriteResult, err error) {
func (h *host) ListHostFavourites(ctx context.Context, user string, header http.Header, dat *metadata.QueryInput) (
resp *metadata.GetHostFavoriteResult, err error) {
resp = new(metadata.GetHostFavoriteResult)
subPath := "/findmany/hosts/favorites/search/%s"
@@ -499,8 +514,9 @@ func (h *host) ListHostFavourites(ctx context.Context, user string, header http.
}
// GetHostFavouriteByID TODO
func (h *host) GetHostFavouriteByID(ctx context.Context, user string, id string,
header http.Header) (resp *metadata.GetHostFavoriteWithIDResult, err error) {
func (h *host) GetHostFavouriteByID(ctx context.Context, user string, id string, header http.Header) (
resp *metadata.GetHostFavoriteWithIDResult, err error) {
resp = new(metadata.GetHostFavoriteWithIDResult)
subPath := "/find/hosts/favorites/search/%s/%s"
@@ -514,8 +530,9 @@ func (h *host) GetHostFavouriteByID(ctx context.Context, user string, id string,
}
// GetHostModulesIDs TODO
func (h *host) GetHostModulesIDs(ctx context.Context, header http.Header,
dat *metadata.ModuleHostConfigParams) (resp *metadata.GetHostModuleIDsResult, err error) {
func (h *host) GetHostModulesIDs(ctx context.Context, header http.Header, dat *metadata.ModuleHostConfigParams) (
resp *metadata.GetHostModuleIDsResult, err error) {
resp = new(metadata.GetHostModuleIDsResult)
subPath := "/findmany/meta/hosts/modules/search"
@@ -580,8 +597,9 @@ func (h *host) UpdateHostCloudAreaField(ctx context.Context, header http.Header,
}
// FindCloudAreaHostCount TODO
func (h *host) FindCloudAreaHostCount(ctx context.Context, header http.Header,
option metadata.CloudAreaHostCount) (resp *metadata.CloudAreaHostCountResult, err error) {
func (h *host) FindCloudAreaHostCount(ctx context.Context, header http.Header, option metadata.CloudAreaHostCount) (
resp *metadata.CloudAreaHostCountResult, err error) {
resp = new(metadata.CloudAreaHostCountResult)
subPath := "/findmany/cloudarea/hostcount"

View File

@@ -8,8 +8,9 @@ import (
)
// SearchInstCount TODO
func (s *operation) SearchInstCount(ctx context.Context, h http.Header,
data interface{}) (resp *metadata.CoreUint64Response, err error) {
func (s *operation) SearchInstCount(ctx context.Context, h http.Header, data interface{}) (
resp *metadata.CoreUint64Response, err error) {
resp = new(metadata.CoreUint64Response)
subPath := "/find/operation/inst/count"
@@ -24,8 +25,9 @@ func (s *operation) SearchInstCount(ctx context.Context, h http.Header,
}
// SearchChartData TODO
func (s *operation) SearchChartData(ctx context.Context, h http.Header,
data metadata.ChartConfig) (resp *metadata.Response, err error) {
func (s *operation) SearchChartData(ctx context.Context, h http.Header, data metadata.ChartConfig) (
resp *metadata.Response, err error) {
resp = new(metadata.Response)
subPath := "/find/operation/chart/data"
@@ -40,8 +42,9 @@ func (s *operation) SearchChartData(ctx context.Context, h http.Header,
}
// CreateOperationChart TODO
func (s *operation) CreateOperationChart(ctx context.Context, h http.Header,
data interface{}) (resp *metadata.CoreUint64Response, err error) {
func (s *operation) CreateOperationChart(ctx context.Context, h http.Header, data interface{}) (
resp *metadata.CoreUint64Response, err error) {
resp = new(metadata.CoreUint64Response)
subPath := "/create/operation/chart"
@@ -58,6 +61,7 @@ func (s *operation) CreateOperationChart(ctx context.Context, h http.Header,
// DeleteOperationChart TODO
func (s *operation) DeleteOperationChart(ctx context.Context, h http.Header, id string) (resp *metadata.Response,
err error) {
resp = new(metadata.Response)
subPath := "delete/operation/chart/%v"
@@ -72,8 +76,9 @@ func (s *operation) DeleteOperationChart(ctx context.Context, h http.Header, id
}
// SearchOperationCharts TODO
func (s *operation) SearchOperationCharts(ctx context.Context, h http.Header,
data interface{}) (resp *metadata.SearchChartResponse, err error) {
func (s *operation) SearchOperationCharts(ctx context.Context, h http.Header, data interface{}) (
resp *metadata.SearchChartResponse, err error) {
resp = new(metadata.SearchChartResponse)
subPath := "/findmany/operation/chart"
@@ -90,6 +95,7 @@ func (s *operation) SearchOperationCharts(ctx context.Context, h http.Header,
// UpdateOperationChart TODO
func (s *operation) UpdateOperationChart(ctx context.Context, h http.Header, data interface{}) (resp *metadata.Response,
err error) {
resp = new(metadata.Response)
subPath := "update/operation/chart"
@@ -106,6 +112,7 @@ func (s *operation) UpdateOperationChart(ctx context.Context, h http.Header, dat
// SearchTimerChartData TODO
func (s *operation) SearchTimerChartData(ctx context.Context, h http.Header, data interface{}) (resp *metadata.Response,
err error) {
resp = new(metadata.Response)
subPath := "/find/operation/timer/chart/data"
@@ -122,6 +129,7 @@ func (s *operation) SearchTimerChartData(ctx context.Context, h http.Header, dat
// UpdateChartPosition TODO
func (s *operation) UpdateChartPosition(ctx context.Context, h http.Header, data interface{}) (resp *metadata.Response,
err error) {
resp = new(metadata.Response)
subPath := "/update/operation/chart/position"
@@ -136,8 +144,9 @@ func (s *operation) UpdateChartPosition(ctx context.Context, h http.Header, data
}
// SearchChartCommon TODO
func (s *operation) SearchChartCommon(ctx context.Context, h http.Header,
data interface{}) (resp *metadata.SearchChartCommon, err error) {
func (s *operation) SearchChartCommon(ctx context.Context, h http.Header, data interface{}) (
resp *metadata.SearchChartCommon, err error) {
resp = new(metadata.SearchChartCommon)
subPath := "/find/operation/chart/common"
@@ -154,6 +163,7 @@ func (s *operation) SearchChartCommon(ctx context.Context, h http.Header,
// TimerFreshData TODO
func (s *operation) TimerFreshData(ctx context.Context, h http.Header, data interface{}) (resp *metadata.BoolResponse,
err error) {
resp = new(metadata.BoolResponse)
subPath := "/start/operation/chart/timer"

View File

@@ -22,8 +22,8 @@ type ServiceClientInterface interface {
CreateServiceInstance(ctx context.Context, h http.Header, data *metadata.CreateServiceInstanceInput) ([]int64,
errors.CCErrorCoder)
UpdateServiceInstances(ctx context.Context, h http.Header, bizID int64,
data map[string]interface{}) (resp *metadata.Response, err error)
UpdateServiceInstances(ctx context.Context, h http.Header, bizID int64, data map[string]interface{}) (
resp *metadata.Response, err error)
DeleteServiceInstance(ctx context.Context, h http.Header, data map[string]interface{}) (resp *metadata.Response,
err error)
SearchServiceInstance(ctx context.Context, h http.Header, data *metadata.GetServiceInstanceInModuleInput) (
@@ -32,13 +32,13 @@ type ServiceClientInterface interface {
data map[string]interface{}) (resp *metadata.ResponseInstData, err error)
ServiceInstanceAddLabels(ctx context.Context, h http.Header, data map[string]interface{}) (resp *metadata.Response,
err error)
ServiceInstanceRemoveLabels(ctx context.Context, h http.Header,
data map[string]interface{}) (resp *metadata.Response, err error)
ServiceInstanceRemoveLabels(ctx context.Context, h http.Header, data map[string]interface{}) (
resp *metadata.Response, err error)
ServiceInstanceFindLabels(ctx context.Context, h http.Header, data map[string]interface{}) (resp *metadata.Response,
err error)
CreateServiceTemplate(ctx context.Context, h http.Header,
data map[string]interface{}) (resp *metadata.ResponseDataMapStr, err error)
CreateServiceTemplate(ctx context.Context, h http.Header, data map[string]interface{}) (
resp *metadata.ResponseDataMapStr, err error)
DeleteServiceTemplate(ctx context.Context, h http.Header,
input *metadata.DeleteServiceTemplatesInput) errors.CCErrorCoder
SearchServiceTemplate(ctx context.Context, h http.Header,

View File

@@ -390,24 +390,14 @@ func (s *service) LimiterFilter() func(req *restful.Request, resp *restful.Respo
return
}
key := common.ApiCacheLimiterRulePrefix + rule.RuleName
result, err := s.cache.Eval(context.Background(), setRequestCntTTLScript, []string{key}, rule.TTL).Result()
exceedLimit, err := s.checkIfExceedLimit(rule, rid)
if err != nil {
blog.Errorf("redis Eval failed, key:%s, rule:%#v, err: %v, rid: %s", key, *rule, err, rid)
fchain.ProcessFilter(req, resp)
return
}
cnt, ok := result.(int64)
if !ok {
blog.Errorf("execute setRequestCntTTLScript failed, key:%s, rule:%#v, err: %v, rid: %s",
key, *rule, result, rid)
fchain.ProcessFilter(req, resp)
return
}
if cnt > rule.Limit {
if exceedLimit {
blog.Errorf("too many requests, matched rule is %#v, rid: %s", *rule, rid)
s.errorLimiterTotal.With(prometheus.Labels{
metrics.LabelAppCode: httpheader.GetAppCode(req.Request.Header),
metrics.LabelHandler: req.Request.RequestURI,
@@ -427,6 +417,29 @@ func (s *service) LimiterFilter() func(req *restful.Request, resp *restful.Respo
}
}
// checkIfExceedLimit checks if the request exceeds the rate limit, return true if request should be rejected
func (s *service) checkIfExceedLimit(rule *metadata.LimiterRule, rid string) (bool, error) {
key := common.ApiCacheLimiterRulePrefix + rule.RuleName
result, err := s.cache.Eval(context.Background(), setRequestCntTTLScript, []string{key}, rule.TTL).Result()
if err != nil {
blog.Errorf("redis Eval failed, key:%s, rule:%#v, err: %v, rid: %s", key, *rule, err, rid)
return false, err
}
cnt, ok := result.(int64)
if !ok {
blog.Errorf("execute setRequestCntTTLScript failed, key:%s, rule:%#v, result: %v, rid: %s",
key, *rule, result, rid)
return false, fmt.Errorf("execute setRequestCntTTLScript failed, result: %v", result)
}
if cnt > rule.Limit {
return true, nil
}
return false, nil
}
// JwtFilter the filter that handles the source of the jwt request
func (s *service) JwtFilter() func(req *restful.Request, resp *restful.Response, fchain *restful.FilterChain) {
return func(req *restful.Request, resp *restful.Response, fchain *restful.FilterChain) {

View File

@@ -112,8 +112,12 @@ func SetRid(header http.Header, value string) {
// SetBkAuth set blueking api gateway authorization info to http header
func SetBkAuth(header http.Header, value string) http.Header {
header.Set(BkAuthHeader, value)
return header
h := make(http.Header)
for key := range header {
h.Set(key, header.Get(key))
}
h.Set(BkAuthHeader, value)
return h
}
// SetBkJWT set blueking api gateway jwt info to http header

View File

@@ -22,13 +22,13 @@ type SysUserConfigItem struct {
ExpireAt int64 `json:"expire_at" bson:"expire_at"`
}
// ResponseSysUserConfigData TODO
// ResponseSysUserConfigData response data for sys user config
type ResponseSysUserConfigData struct {
RowType string `json:"type"`
BluekingModify SysUserConfigItem `json:"blueking_modify"`
}
// ReponseSysUserConfig TODO
// ReponseSysUserConfig response for sys user config
type ReponseSysUserConfig struct {
BaseResp `json:",inline"`
Data ResponseSysUserConfigData `json:"data"`

View File

@@ -32,6 +32,7 @@ import (
"configcenter/src/common/errors"
"configcenter/src/common/mapstr"
"configcenter/src/common/util"
"configcenter/src/common/valid/attribute/manager"
"github.com/tidwall/gjson"
"go.mongodb.org/mongo-driver/bson"
@@ -174,6 +175,7 @@ type CreateObjAttDesResp struct {
// Validate Attribute
func (attribute *Attribute) Validate(ctx context.Context, data interface{}, key string) errors.RawErrorInfo {
rid := util.ExtractRequestIDFromContext(ctx)
var attrValidatorMap = map[string]func(context.Context, interface{}, string) errors.RawErrorInfo{
common.FieldTypeSingleChar: attribute.validChar,
common.FieldTypeLongChar: attribute.validLongChar,
@@ -203,16 +205,29 @@ func (attribute *Attribute) Validate(ctx context.Context, data interface{}, key
// TODO what validation should do on these types
rawError = attribute.validTable(ctx, data, key)
default:
// notice: 注意default 这里的实现逻辑, 用break 做了执行流程的终止。 pr 建议,降低圈复杂度
validator, exists := attrValidatorMap[fieldType]
if !exists {
rawError = errors.RawErrorInfo{
ErrCode: common.CCErrCommUnexpectedFieldType,
Args: []interface{}{fieldType},
if exists {
rawError = validator(ctx, data, key)
break
}
// 是否为扩展字段类型
if handle, ok := manager.Get(fieldType); ok {
if err := handle.Validate(ctx, key, fieldType, attribute.IsRequired, attribute.Option, data); err != nil {
blog.Errorf("validate attribute fail, field type: %s, err: %v, rid: %s", fieldType, err, rid)
return errors.RawErrorInfo{
ErrCode: common.CCErrCommParamsInvalid,
Args: []interface{}{err.Error()},
}
}
break
}
rawError = errors.RawErrorInfo{
ErrCode: common.CCErrCommUnexpectedFieldType,
Args: []interface{}{fieldType},
}
rawError = validator(ctx, data, key)
}
// 如果出现了问题并且报错原内容为propertyID则替换为propertyName。
if rawError.ErrCode != 0 {

View File

@@ -39,12 +39,12 @@ type AuditQueryResult struct {
Info []AuditLog `json:"info"`
}
// CreateAuditLogParam TODO
// CreateAuditLogParam param for creating audit log
type CreateAuditLogParam struct {
Data []AuditLog `json:"data"`
}
// AuditQueryInput TODO
// AuditQueryInput query audit logs input
type AuditQueryInput struct {
Condition AuditQueryCondition `json:"condition"`
Page BasePage `json:"page,omitempty"`
@@ -75,7 +75,7 @@ func (input *AuditQueryInput) Validate() errors.RawErrorInfo {
return errors.RawErrorInfo{}
}
// AuditQueryCondition TODO
// AuditQueryCondition query audit logs condition struct
type AuditQueryCondition struct {
AuditType AuditType `json:"audit_type"`
User string `json:"user"`
@@ -107,7 +107,7 @@ func (a *AuditQueryCondition) Validate() error {
return nil
}
// OperationTimeCondition TODO
// OperationTimeCondition operation time condition struct
type OperationTimeCondition struct {
Start string `json:"start"`
End string `json:"end"`
@@ -174,7 +174,7 @@ type InstAuditCondition struct {
ID []int64 `json:"id"`
}
// AuditLog TODO
// AuditLog struct for audit log
type AuditLog struct {
ID int64 `json:"id" bson:"id"`
// AuditType is a high level abstract of the resource managed by this cmdb.

View File

@@ -40,7 +40,7 @@ type CloudAccount struct {
LastTime time.Time `json:"last_time" bson:"last_time"`
}
// Validate TODO
// Validate account validate
func (c *CloudAccount) Validate() (rawError errors.RawErrorInfo) {
if c.AccountName == "" {
return errors.RawErrorInfo{

View File

@@ -139,6 +139,11 @@ func (c *DynamicGroupCondition) Validate(attributeMap map[string]string) error {
return validAttributeValueType(attrType, c.Value)
}
return c.validateByOperator(operator, attrType, attributeType)
}
func (c *DynamicGroupCondition) validateByOperator(operator, attrType, attributeType string) error {
switch operator {
case DynamicGroupOperatorEQ, DynamicGroupOperatorNE:
return validAttributeValueType(attrType, c.Value)

View File

@@ -27,6 +27,7 @@ import (
"configcenter/src/common/metadata"
"configcenter/src/common/util"
"configcenter/src/common/valid"
"configcenter/src/common/valid/attribute/manager"
)
// ValidPropertyOption valid property field option
@@ -58,6 +59,14 @@ func ValidPropertyOption(kit *rest.Kit, propertyType string, option interface{},
return ValidIDRuleOption(kit, option, attrTypeMap)
}
if handle, ok := manager.Get(propertyType); ok {
if err := handle.ValidateOption(kit.Ctx, option, extraOpt); err != nil {
blog.Errorf("valid property option failed, property type: %s, option: %+v, extra opt: %+v, err: %v, rid: %s",
propertyType, option, extraOpt, err, kit.Rid)
return kit.CCError.Errorf(common.CCErrCommParamsIsInvalid, err.Error())
}
}
return nil
}

View File

@@ -0,0 +1,16 @@
### 说明
后续将插件的import放到目录下
##### 注意事项
- package 名字,必须为 init
- 不允许写业务逻辑
```
package init
import (
_ "configcenter/src/common/valid/attribute/plugins"
)
```

View File

@@ -0,0 +1,2 @@
// Package init for plugin initialization
package init

View File

@@ -0,0 +1,11 @@
// Package manager provides a way to manage attributes in the system.
package manager
import (
"configcenter/src/common/valid/attribute/manager/register"
// import init to register all attributes
_ "configcenter/src/common/valid/attribute/init"
)
// Get returns the Attribute by name
var Get = register.Get

View File

@@ -0,0 +1,68 @@
// Package register for attribute type registration and constraint
package register
import (
"context"
"fmt"
"configcenter/src/common/blog"
"configcenter/src/common/mapstr"
)
var (
attrTypeMap = map[string]AttributeTypeI{}
)
// AttributeTypeI interface defines the methods for attribute types
type AttributeTypeI interface {
// Name 展示名字
Name() string
// DisplayName 类型的唯一名字
DisplayName() string
// RealType common.FieldTypeXXX , 必须是基础之一
RealType() string
// Info 描述信息
Info() string
// 预留暂未使用, 在 Validate 前处理, 做数据转换,
// Transform(kit *rest.Kitvalue interface{})(interface{}, error)
// Validate 实际校验方法
Validate(ctx context.Context, objID string, propertyType string, required bool, option interface{}, value interface{}) error
// FillLostValue 填充默认值
FillLostValue(ctx context.Context, valData mapstr.MapStr, propertyId string, defaultValue, option interface{}) error
// ValidateOption 校验 Option
ValidateOption(ctx context.Context, option interface{}, defaultVal interface{}) error
}
// Register attribute type
// attrTypeMap is a map of attribute name to Attribute interface
// this function is used to register a new attribute type
// it will panic if the attribute name is empty or already exists
// it is called in init() function of each attribute type file
// so that all attribute types are registered when the program starts, not at runtime
func Register(attr AttributeTypeI) {
if attr == nil {
blog.Errorf("register attribute is nil")
panic("register attribute is nil")
}
name := attr.Name()
if name == "" {
blog.Errorf("register attribute name is empty")
panic("register attribute name is empty")
}
if _, exists := attrTypeMap[name]; exists {
blog.Errorf("attribute %s already exists", name)
panic(fmt.Sprintf("attribute %s already exists", name))
}
attrTypeMap[name] = attr
}
// Get returns the Attribute by name
func Get(name string) (AttributeTypeI, bool) {
a, ok := attrTypeMap[name]
return a, ok
}

View File

@@ -0,0 +1,7 @@
### 说明
预留插件实现目录
##### 注意事项
- package 名字,必须为 plugins

View File

@@ -1169,17 +1169,17 @@ func (h *HostSnap) getHostByVal(header http.Header, rid string, cloudID int64, i
}
func getIPsFromMsg(val *gjson.Result, agentID string, rid string) ([]string, []string) {
ipv4Map := make(map[string]struct{})
ipv6Map := make(map[string]struct{})
ipv4List := make([]string, 0)
ipv6List := make([]string, 0)
rootIP := val.Get("ip").String()
rootIP = strings.TrimSpace(rootIP)
if rootIP != metadata.IPv4LoopBackIpPrefix && rootIP != metadata.IPv6LoopBackIp &&
!strings.HasPrefix(rootIP, metadata.IPv6LinkLocalAddressPrefix) && net.ParseIP(rootIP) != nil {
if strings.Contains(rootIP, ":") {
ipv6Map[rootIP] = struct{}{}
ipv6List = append(ipv6List, rootIP)
} else {
ipv4Map[rootIP] = struct{}{}
ipv4List = append(ipv4List, rootIP)
}
}
// need to be compatible with the old and new versions of the format
@@ -1234,22 +1234,18 @@ func getIPsFromMsg(val *gjson.Result, agentID string, rid string) ([]string, []s
}
if strings.Contains(ip, ":") {
ipv6Map[ip] = struct{}{}
} else {
ipv4Map[ip] = struct{}{}
if !util.Contains(ipv6List, ip) {
ipv6List = append(ipv6List, ip)
}
continue
}
if !util.Contains(ipv4List, ip) {
ipv4List = append(ipv4List, ip)
}
}
}
ipv4List := make([]string, 0)
for ipv4 := range ipv4Map {
ipv4List = append(ipv4List, ipv4)
}
ipv6List := make([]string, 0)
for ipv6 := range ipv6Map {
ipv6List = append(ipv6List, ipv6)
}
return ipv4List, ipv6List
}

View File

@@ -1028,6 +1028,10 @@ func (sh *searchHost) searchByHostConds() errors.CCError {
}
}
return sh.searchHost(condition)
}
func (sh *searchHost) searchHost(condition map[string]interface{}) error {
query := &metadata.QueryInput{
Condition: condition,
TimeCondition: sh.conds.hostCond.TimeCondition,

View File

@@ -21,6 +21,7 @@ import (
"configcenter/src/ac/iam"
"configcenter/src/common/auth"
"configcenter/src/common/core/cc/config"
auditconf "configcenter/src/source_controller/cacheservice/audit/config"
"configcenter/src/storage/dal/mongo"
"configcenter/src/storage/dal/redis"
@@ -38,6 +39,7 @@ type Config struct {
WatchMongo mongo.Config
Redis redis.Config
Auth iam.AuthConfig
Audit *auditconf.Config
}
// NewServerOption create a ServerOption object

View File

@@ -28,6 +28,7 @@ import (
"configcenter/src/common/blog"
"configcenter/src/common/types"
"configcenter/src/source_controller/cacheservice/app/options"
auditconf "configcenter/src/source_controller/cacheservice/audit/config"
cachesvr "configcenter/src/source_controller/cacheservice/service"
"configcenter/src/storage/driver/mongodb"
"configcenter/src/storage/driver/redis"
@@ -63,8 +64,9 @@ func Run(ctx context.Context, cancel context.CancelFunc, op *options.ServerOptio
input := &backbone.BackboneParameter{
ConfigUpdate: cacheSvr.onCacheServiceConfigUpdate,
ConfigPath: op.ServConf.ExConfig,
SrvRegdiscv: backbone.SrvRegdiscv{Regdiscv: op.ServConf.RegDiscover, TLSConfig: op.ServConf.GetTLSClientConf()},
SrvInfo: svrInfo,
SrvRegdiscv: backbone.SrvRegdiscv{Regdiscv: op.ServConf.RegDiscover,
TLSConfig: op.ServConf.GetTLSClientConf()},
SrvInfo: svrInfo,
}
engine, err := backbone.NewBackbone(ctx, input)
@@ -144,5 +146,13 @@ func initResource(cacheSvr *CacheServer) error {
return err
}
cacheSvr.Config.Audit = new(auditconf.Config)
if cc.IsExist("auditCenter") {
if err = cc.UnmarshalKey("auditCenter", cacheSvr.Config.Audit); err != nil {
blog.Errorf("parse audit center config failed, err: %v", err)
return err
}
}
return nil
}

View File

@@ -0,0 +1,63 @@
/*
* Tencent is pleased to support the open source community by making
* 蓝鲸智云 - 配置平台 (BlueKing - Configuration System) available.
* Copyright (C) 2017 Tencent. All rights reserved.
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
* We undertake not to change the open source license (MIT license) applicable
* to the current version of the project delivered to anyone in the future.
*/
// Package audit is the data reporting service for audit center
package audit
import (
"configcenter/src/common/blog"
"configcenter/src/source_controller/cacheservice/audit/config"
"configcenter/src/storage/stream"
)
// Audit is the data reporting service for audit center
type Audit struct {
conf *config.Config
loopW stream.LoopInterface
client *auditClient
}
// RunAuditDataReporting run audit data reporting
func RunAuditDataReporting(conf *config.Config, loopW stream.LoopInterface) error {
if conf == nil || !conf.Enabled {
blog.Info("audit data reporting is disabled")
return nil
}
if err := conf.Validate(); err != nil {
blog.Errorf("audit data reporting config(%+v) is invalid, err: %v", *conf, err)
return err
}
client, err := newAuditClient(conf)
if err != nil {
blog.Errorf("init audit client failed, err: %v", err)
return err
}
audit := &Audit{
conf: conf,
loopW: loopW,
client: client,
}
if err = audit.watchAudit(); err != nil {
blog.Errorf("watch audit event and push to audit center failed, err: %v", err)
return err
}
return nil
}

View File

@@ -0,0 +1,95 @@
/*
* Tencent is pleased to support the open source community by making
* 蓝鲸智云 - 配置平台 (BlueKing - Configuration System) available.
* Copyright (C) 2017 Tencent. All rights reserved.
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
* We undertake not to change the open source license (MIT license) applicable
* to the current version of the project delivered to anyone in the future.
*/
package audit
import (
"context"
"fmt"
"net/http"
"configcenter/src/apimachinery/rest"
"configcenter/src/apimachinery/util"
"configcenter/src/common/blog"
"configcenter/src/source_controller/cacheservice/audit/config"
"go.opentelemetry.io/proto/otlp/collector/logs/v1"
"google.golang.org/protobuf/encoding/protojson"
)
type auditClient struct {
client rest.ClientInterface
token string
}
// ReportAuditData report audit data to audit center
func (a *auditClient) ReportAuditData(ctx context.Context, h http.Header, opt *v1.ExportLogsServiceRequest) error {
h.Set("Content-Type", "application/json")
h.Set("X-BK-TOKEN", a.token)
body, err := protojson.Marshal(opt)
if err != nil {
return fmt.Errorf("marshal audit request failed, err: %v", err)
}
res := a.client.Post().
WithContext(ctx).
Body(body).
SubResourcef("/v1/logs").
WithHeaders(h).
Do()
if res.StatusCode >= 300 || res.Err != nil {
return fmt.Errorf("http request failed, err: %v, status %s", res.Err, res.Status)
}
return nil
}
// newAuditClient new audit center client
func newAuditClient(conf *config.Config) (*auditClient, error) {
client, err := util.NewClient(nil)
if err != nil {
blog.Errorf("new http client failed, err: %v", err)
return nil, err
}
c := &util.Capability{
Client: client,
Discover: &discovery{endpoint: conf.Endpoint},
}
restCli := rest.NewRESTClient(c, "/")
return &auditClient{
client: restCli,
token: conf.Token,
}, nil
}
type discovery struct {
endpoint string
}
// GetServers get servers
func (s *discovery) GetServers() ([]string, error) {
return []string{s.endpoint}, nil
}
// GetServersChan get servers chan
func (s *discovery) GetServersChan() chan []string {
return nil
}

View File

@@ -0,0 +1,54 @@
/*
* Tencent is pleased to support the open source community by making
* 蓝鲸智云 - 配置平台 (BlueKing - Configuration System) available.
* Copyright (C) 2017 Tencent. All rights reserved.
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
* We undertake not to change the open source license (MIT license) applicable
* to the current version of the project delivered to anyone in the future.
*/
// Package config is the audit data reporting configuration
package config
import "errors"
// Config is the audit center related configuration
type Config struct {
Enabled bool `mapstructure:"enabled"`
AppCode string `mapstructure:"appCode"`
AppSecret string `mapstructure:"appSecret"`
Endpoint string `mapstructure:"endpoint"`
Token string `mapstructure:"token"`
}
// Validate Config
func (c Config) Validate() error {
if !c.Enabled {
return nil
}
if c.AppCode == "" {
return errors.New("appCode is not set")
}
if c.AppSecret == "" {
return errors.New("appSecret is not set")
}
if c.Endpoint == "" {
return errors.New("endpoint is not set")
}
if c.Token == "" {
return errors.New("token is not set")
}
return nil
}

View File

@@ -0,0 +1,255 @@
/*
* Tencent is pleased to support the open source community by making
* 蓝鲸智云 - 配置平台 (BlueKing - Configuration System) available.
* Copyright (C) 2017 Tencent. All rights reserved.
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
* We undertake not to change the open source license (MIT license) applicable
* to the current version of the project delivered to anyone in the future.
*/
package audit
import (
"context"
"encoding/json"
"net/http"
"strconv"
"time"
"configcenter/src/common"
"configcenter/src/common/blog"
"configcenter/src/common/mapstr"
"configcenter/src/common/metadata"
"configcenter/src/common/util"
tokenhandler "configcenter/src/source_controller/cacheservice/cache/token-handler"
"configcenter/src/storage/driver/mongodb"
"configcenter/src/storage/stream/types"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
collectorlogs "go.opentelemetry.io/proto/otlp/collector/logs/v1"
commonv1 "go.opentelemetry.io/proto/otlp/common/v1"
logsv1 "go.opentelemetry.io/proto/otlp/logs/v1"
resourcev1 "go.opentelemetry.io/proto/otlp/resource/v1"
)
// watchAudit watch audit log event and push to audit center
func (a *Audit) watchAudit() error {
ctx := util.SetDBReadPreference(context.Background(), common.SecondaryPreferredMode)
name := "audit"
tokenHandler := tokenhandler.NewSingleTokenHandler(name, mongodb.Client())
startAtTime, err := tokenHandler.GetStartWatchTime(ctx)
if err != nil {
blog.Errorf("get %s start watch time failed, err: %v", name, err)
return err
}
operationType := types.Insert
loopOptions := &types.LoopBatchOptions{
LoopOptions: types.LoopOptions{
Name: name,
WatchOpt: &types.WatchOptions{
Options: types.Options{
OperationType: &operationType,
Filter: make(mapstr.MapStr),
EventStruct: new(metadata.AuditLog),
Collection: common.BKTableNameAuditLog,
StartAtTime: startAtTime,
WatchFatalErrorCallback: tokenHandler.ResetWatchToken,
},
},
TokenHandler: tokenHandler,
RetryOptions: &types.RetryOptions{
MaxRetryCount: 2,
RetryDuration: 1 * time.Second,
},
},
EventHandler: &types.BatchHandler{
DoBatch: a.doBatch,
},
BatchSize: 100,
}
if err = a.loopW.WithBatch(loopOptions); err != nil {
blog.Errorf("watch %s failed, err: %v", name, err)
return err
}
return nil
}
// doBatch batch handle audit event
func (a *Audit) doBatch(es []*types.Event) (retry bool) {
if len(es) == 0 {
return false
}
rid := es[0].ID()
logs := make([]*logsv1.ResourceLogs, 0)
for idx := range es {
event := es[idx]
if event.OperationType != types.Insert {
blog.V(4).Infof("received ignored audit event operation type: %s, doc: %s, rid: %s", event.OperationType,
event.DocBytes, rid)
continue
}
resLog, isValid := a.convertToResourceLog(event, rid)
if !isValid {
blog.V(4).Infof("received invalid audit event: %+v, rid: %s", *event, rid)
continue
}
logs = append(logs, resLog)
blog.V(5).Infof("watch audit, received oid: %s, op-time: %s event, rid: %s", event.Oid,
event.ClusterTime.String(), rid)
}
if len(logs) == 0 {
return false
}
req := &collectorlogs.ExportLogsServiceRequest{
ResourceLogs: logs,
}
err := a.client.ReportAuditData(context.Background(), make(http.Header), req)
if err != nil {
blog.ErrorJSON("report audit log data failed, err: %s, req: %s, rid: %s", err, req, rid)
return true
}
return false
}
func (a *Audit) convertToResourceLog(event *types.Event, rid string) (*logsv1.ResourceLogs, bool) {
auditLog, ok := event.Document.(*metadata.AuditLog)
if !ok {
blog.Errorf("received invalid audit event doc(%#v), oid: %s, rid: %s", event.Document, event.Oid, rid)
return nil, false
}
detail, err := json.Marshal(auditLog.OperationDetail)
if err != nil {
blog.Errorf("marshal audit log detail failed, err: %v, auditLog: %v, rid: %s", err, *auditLog, rid)
return nil, false
}
var instanceID string
if intID, err := util.GetInt64ByInterface(auditLog.ResourceID); err == nil {
instanceID = strconv.FormatInt(intID, 10)
} else {
instanceID = util.GetStrByInterface(auditLog.ResourceID)
}
logAttributes := []*commonv1.KeyValue{
a.convertToStrKeyValue("event_id", strconv.FormatInt(auditLog.ID, 10)),
a.convertToStrKeyValue("event_content", string(detail)),
a.convertToStrKeyValue("request_id", auditLog.RequestID),
a.convertToIntKeyValue("start_time", auditLog.OperationTime.UnixMilli()),
a.convertToIntKeyValue("end_time", auditLog.OperationTime.UnixMilli()),
a.convertToStrKeyValue("bk_app_code", a.conf.AppCode),
a.convertToStrKeyValue("action_id", string(auditLog.Action)),
a.convertToStrKeyValue("resource_type_id", string(auditLog.ResourceType)),
a.convertToStrKeyValue("instance_id", instanceID),
a.convertToStrKeyValue("instance_name", auditLog.ResourceName),
a.convertToIntKeyValue("instance_sensitivity", 0),
a.convertToStrKeyValue("instance_data", "{}"),
a.convertToStrKeyValue("instance_origin_data", "{}"),
a.convertToStrKeyValue("extend_data", "{}"),
a.convertToIntKeyValue("result_code", 0),
a.convertToStrKeyValue("result_content", ""),
a.convertToStrKeyValue("bk_log_scope", "bk_audit_event"),
}
logAttributes = append(logAttributes, a.parseUserInfo(auditLog)...)
logAttributes = append(logAttributes, a.parseScopeInfo(auditLog)...)
return &logsv1.ResourceLogs{
Resource: &resourcev1.Resource{
Attributes: []*commonv1.KeyValue{
a.convertToStrKeyValue(string(semconv.ServiceNameKey), a.conf.AppCode),
a.convertToStrKeyValue("bk.data.token", a.conf.Token),
},
},
ScopeLogs: []*logsv1.ScopeLogs{{
Scope: &commonv1.InstrumentationScope{
Name: a.conf.AppCode,
},
LogRecords: []*logsv1.LogRecord{{
TimeUnixNano: uint64(auditLog.OperationTime.UnixNano()),
ObservedTimeUnixNano: uint64(time.Now().UnixNano()),
SeverityNumber: logsv1.SeverityNumber_SEVERITY_NUMBER_INFO,
SeverityText: "INFO",
Attributes: logAttributes,
}},
}},
SchemaUrl: semconv.SchemaURL,
}, true
}
func (a *Audit) parseUserInfo(auditLog *metadata.AuditLog) []*commonv1.KeyValue {
var accessType, userIdentifyType int64
if auditLog.OperateFrom == metadata.FromUser {
accessType = 0
userIdentifyType = 0
} else {
accessType = -1
userIdentifyType = 1
}
var userIdentifySrc, userIdentifySrcUsername string
if auditLog.AppCode != "" {
userIdentifySrc = "bk_app_code"
userIdentifySrcUsername = auditLog.AppCode
accessType = 1
}
return []*commonv1.KeyValue{
a.convertToStrKeyValue("username", auditLog.User),
a.convertToStrKeyValue("user_identify_tenant_id", auditLog.SupplierAccount),
a.convertToIntKeyValue("user_identify_type", userIdentifyType),
a.convertToStrKeyValue("user_identify_src", userIdentifySrc),
a.convertToStrKeyValue("user_identify_src_username", userIdentifySrcUsername),
a.convertToIntKeyValue("access_type", accessType),
a.convertToStrKeyValue("access_source_ip", ""),
a.convertToStrKeyValue("access_user_agent", ""),
}
}
func (a *Audit) parseScopeInfo(auditLog *metadata.AuditLog) []*commonv1.KeyValue {
var scopeType, scopeID string
if auditLog.BusinessID != 0 {
scopeType = "biz"
scopeID = strconv.FormatInt(auditLog.BusinessID, 10)
}
return []*commonv1.KeyValue{
a.convertToStrKeyValue("scope_type", scopeType),
a.convertToStrKeyValue("scope_id", scopeID),
}
}
func (a *Audit) convertToStrKeyValue(key, value string) *commonv1.KeyValue {
return &commonv1.KeyValue{
Key: key,
Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: value}},
}
}
func (a *Audit) convertToIntKeyValue(key string, value int64) *commonv1.KeyValue {
return &commonv1.KeyValue{
Key: key,
Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: value}},
}
}

View File

@@ -34,6 +34,7 @@ import (
"configcenter/src/common/rdapi"
"configcenter/src/common/webservice/restfulservice"
"configcenter/src/source_controller/cacheservice/app/options"
"configcenter/src/source_controller/cacheservice/audit"
"configcenter/src/source_controller/cacheservice/cache"
cacheop "configcenter/src/source_controller/cacheservice/cache"
"configcenter/src/source_controller/cacheservice/event/bsrelation"
@@ -69,6 +70,7 @@ type cacheService struct {
core core.Core
cacheSet *cache.ClientSet
authManager *extensions.AuthManager
audit *audit.Audit
}
// SetConfig TODO
@@ -78,11 +80,10 @@ func (s *cacheService) SetConfig(cfg options.Config, engine *backbone.Engine, er
s.cfg = cfg
s.engine = engine
if nil != err {
if err != nil {
s.err = err
}
if nil != lang {
if lang != nil {
s.langFactory = make(map[common.LanguageType]language.DefaultCCLanguageIf)
s.langFactory[common.Chinese] = lang.CreateDefaultCCLanguageIf(string(common.Chinese))
s.langFactory[common.English] = lang.CreateDefaultCCLanguageIf(string(common.English))
@@ -103,7 +104,6 @@ func (s *cacheService) SetConfig(cfg options.Config, engine *backbone.Engine, er
blog.Errorf("new loop stream failed, err: %v", loopErr)
return loopErr
}
event, eventErr := reflector.NewReflector(s.cfg.Mongo.GetMongoConf())
if eventErr != nil {
blog.Errorf("new reflector failed, err: %v", eventErr)
@@ -135,8 +135,7 @@ func (s *cacheService) SetConfig(cfg options.Config, engine *backbone.Engine, er
return dbErr
}
flowErr := flow.NewEvent(watcher, engine.ServiceManageInterface, watchDB, ccDB)
if flowErr != nil {
if flowErr := flow.NewEvent(watcher, engine.ServiceManageInterface, watchDB, ccDB); flowErr != nil {
blog.Errorf("new watch event failed, err: %v", flowErr)
return flowErr
}
@@ -151,6 +150,9 @@ func (s *cacheService) SetConfig(cfg options.Config, engine *backbone.Engine, er
return err
}
if err := audit.RunAuditDataReporting(cfg.Audit, loopW); err != nil {
return err
}
return nil
}

View File

@@ -29,7 +29,9 @@ import (
)
// CreateAccount TODO
func (c *cloudOperation) CreateAccount(kit *rest.Kit, account *metadata.CloudAccount) (*metadata.CloudAccount, errors.CCErrorCoder) {
func (c *cloudOperation) CreateAccount(kit *rest.Kit, account *metadata.CloudAccount) (
*metadata.CloudAccount, errors.CCErrorCoder) {
if err := c.validCreateAccount(kit, account); nil != err {
blog.Errorf("CreateAccount failed, valid error: %+v, rid: %s", err, kit.Rid)
return nil, err
@@ -50,7 +52,8 @@ func (c *cloudOperation) CreateAccount(kit *rest.Kit, account *metadata.CloudAcc
err = c.dbProxy.Table(common.BKTableNameCloudAccount).Insert(kit.Ctx, account)
if err != nil {
blog.ErrorJSON("CreateAccount failed, db insert failed, accountName: %s, err: %s, rid: %s", account.AccountName, err, kit.Rid)
blog.ErrorJSON("CreateAccount failed, db insert failed, accountName: %s, err: %s, rid: %s", account.AccountName,
err, kit.Rid)
return nil, kit.CCError.CCError(common.CCErrCommDBInsertFailed)
}
// 不返回bk_secret_key的值
@@ -59,7 +62,8 @@ func (c *cloudOperation) CreateAccount(kit *rest.Kit, account *metadata.CloudAcc
}
// SearchAccount TODO
func (c *cloudOperation) SearchAccount(kit *rest.Kit, option *metadata.SearchCloudOption) (*metadata.MultipleCloudAccount, errors.CCErrorCoder) {
func (c *cloudOperation) SearchAccount(kit *rest.Kit,
option *metadata.SearchCloudOption) (*metadata.MultipleCloudAccount, errors.CCErrorCoder) {
accounts := []metadata.CloudAccount{}
option.Condition = util.SetQueryOwner(option.Condition, kit.SupplierAccount)
err := c.dbProxy.Table(common.BKTableNameCloudAccount).Find(option.Condition).Fields(option.Fields...).
@@ -94,7 +98,8 @@ func (c *cloudOperation) SearchAccount(kit *rest.Kit, option *metadata.SearchClo
if accountTaskcntMap[accounts[i].AccountID] == 0 {
canDeleteAccount = true
}
results = append(results, metadata.CloudAccountWithExtraInfo{CloudAccount: accounts[i], CanDeleteAccount: canDeleteAccount})
results = append(results,
metadata.CloudAccountWithExtraInfo{CloudAccount: accounts[i], CanDeleteAccount: canDeleteAccount})
}
return &metadata.MultipleCloudAccount{Count: int64(count), Info: results}, nil
@@ -115,7 +120,8 @@ func (c *cloudOperation) UpdateAccount(kit *rest.Kit, accountID int64, option ma
option.Remove(common.BKCloudIDField)
option.Remove(common.BKOwnerIDField)
if e := c.dbProxy.Table(common.BKTableNameCloudAccount).Update(kit.Ctx, filter, option); e != nil {
blog.Errorf("UpdateAccount failed, mongodb failed, table: %s, filter: %+v, err: %+v, rid: %s", common.BKTableNameCloudAccount, filter, e, kit.Rid)
blog.Errorf("UpdateAccount failed, mongodb failed, table: %s, filter: %+v, err: %+v, rid: %s",
common.BKTableNameCloudAccount, filter, e, kit.Rid)
return kit.CCError.CCError(common.CCErrCommDBUpdateFailed)
}
return nil
@@ -131,18 +137,21 @@ func (c *cloudOperation) DeleteAccount(kit *rest.Kit, accountID int64) errors.CC
filter := map[string]interface{}{common.BKCloudAccountID: accountID}
filter = util.SetModOwner(filter, kit.SupplierAccount)
if e := c.dbProxy.Table(common.BKTableNameCloudAccount).Delete(kit.Ctx, filter); e != nil {
blog.Errorf("DeleteAccount failed, mongodb failed, table: %s, filter: %+v, err: %+v, rid: %s", common.BKTableNameCloudAccount, filter, e, kit.Rid)
blog.Errorf("DeleteAccount failed, mongodb failed, table: %s, filter: %+v, err: %+v, rid: %s",
common.BKTableNameCloudAccount, filter, e, kit.Rid)
return kit.CCError.CCError(common.CCErrCommDBDeleteFailed)
}
return nil
}
// SearchAccountConf 查询云厂商账户配置
func (c *cloudOperation) SearchAccountConf(kit *rest.Kit, option *metadata.SearchCloudOption) (*metadata.MultipleCloudAccountConf, errors.CCErrorCoder) {
func (c *cloudOperation) SearchAccountConf(kit *rest.Kit,
option *metadata.SearchCloudOption) (*metadata.MultipleCloudAccountConf, errors.CCErrorCoder) {
accountconfs := []metadata.CloudAccountConf{}
option.Condition = util.SetQueryOwner(option.Condition, kit.SupplierAccount)
err := c.dbProxy.Table(common.BKTableNameCloudAccount).Find(option.Condition).Fields(option.Fields...).
Start(uint64(option.Page.Start)).Limit(uint64(option.Page.Limit)).Sort(option.Page.Sort).All(kit.Ctx, &accountconfs)
Start(uint64(option.Page.Start)).Limit(uint64(option.Page.Limit)).Sort(option.Page.Sort).All(kit.Ctx,
&accountconfs)
if err != nil {
blog.ErrorJSON("SearchAccount failed, db insert failed, option: %s, err: %s, rid: %s", option, err, kit.Rid)
return nil, kit.CCError.CCError(common.CCErrCommDBSelectFailed)
@@ -182,7 +191,8 @@ func (c *cloudOperation) getAcccountTaskcntMap(kit *rest.Kit) (map[int64]int64,
Num int64 `bson:"num"`
}
resultAll := make([]accountTaskcnt, 0)
if err := c.dbProxy.Table(common.BKTableNameCloudSyncTask).AggregateAll(kit.Ctx, aggregateCond, &resultAll); err != nil {
if err := c.dbProxy.Table(common.BKTableNameCloudSyncTask).AggregateAll(kit.Ctx, aggregateCond,
&resultAll); err != nil {
return nil, err
}

View File

@@ -46,22 +46,18 @@ func New(cacheHost host.Interface) Searcher {
}
}
// ListHosts search host with topo node info and host property
func (s *Searcher) ListHosts(kit *rest.Kit, option metadata.ListHosts) (*metadata.ListHostResult, error) {
if key, err := option.Validate(); err != nil {
blog.Errorf("list hosts option %+v is invalid, key: %s, err: %v, rid: %s", option, key, err, kit.Rid)
return nil, err
}
func (s *Searcher) buildListHostsFilter(kit *rest.Kit, option metadata.ListHosts) ([]map[string]interface{},
[]interface{}, bool, bool, error) {
needHostIDFilter, hostIDs, err := s.listHostIDsByRelation(kit, &option)
if err != nil {
return nil, err
return nil, nil, false, false, err
}
filters := make([]map[string]interface{}, 0)
if needHostIDFilter {
if len(hostIDs) == 0 {
return &metadata.ListHostResult{Count: 0, Info: make([]map[string]interface{}, 0)}, nil
return nil, nil, true, false, nil
}
filters = append(filters, map[string]interface{}{
common.BKHostIDField: map[string]interface{}{common.BKDBIN: hostIDs},
@@ -71,12 +67,30 @@ func (s *Searcher) ListHosts(kit *rest.Kit, option metadata.ListHosts) (*metadat
propertyFilter, err := option.GetHostPropertyFilter(kit.Ctx)
if err != nil {
blog.Errorf("get host property filter failed, filter: %+v, err: %v, rid: %s", option, err, kit.Rid)
return nil, err
return nil, nil, false, false, err
}
if len(propertyFilter) > 0 {
filters = append(filters, propertyFilter)
}
return filters, hostIDs, false, needHostIDFilter, nil
}
// ListHosts search host with topo node info and host property
func (s *Searcher) ListHosts(kit *rest.Kit, option metadata.ListHosts) (*metadata.ListHostResult, error) {
if key, err := option.Validate(); err != nil {
blog.Errorf("list hosts option %+v is invalid, key: %s, err: %v, rid: %s", option, key, err, kit.Rid)
return nil, err
}
filters, hostIDs, noHostsMatched, needHostIDFilter, err := s.buildListHostsFilter(kit, option)
if err != nil {
return nil, err
}
if noHostsMatched {
return &metadata.ListHostResult{Count: 0, Info: make([]map[string]interface{}, 0)}, nil
}
finalFilter := map[string]interface{}{}
finalFilter = util.SetQueryOwner(finalFilter, util.ExtractOwnerFromContext(kit.Ctx))
if len(filters) > 0 {

View File

@@ -27,6 +27,7 @@ import (
"configcenter/src/common/metadata"
"configcenter/src/common/util"
"configcenter/src/common/valid"
"configcenter/src/common/valid/attribute/manager"
"configcenter/src/storage/driver/mongodb"
"go.mongodb.org/mongo-driver/bson/primitive"
@@ -40,63 +41,27 @@ func FillLostFieldValue(ctx context.Context, valData mapstr.MapStr, properties [
if ok && (field.PropertyType != common.FieldTypeIDRule || val != "") {
continue
}
switch field.PropertyType {
case common.FieldTypeSingleChar, common.FieldTypeLongChar:
if err := fillLostStringFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeEnum:
if err := fillLostEnumFieldValue(ctx, valData, field); err != nil {
return err
}
case common.FieldTypeEnumMulti:
if err := fillLostEnumMultiFieldValue(ctx, valData, field); err != nil {
return err
}
case common.FieldTypeEnumQuote:
if err := fillLostEnumQuoteFieldValue(ctx, valData, field); err != nil {
return err
}
case common.FieldTypeDate:
if err := fillLostDateFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeFloat:
if err := fillLostFloatFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeInt:
if err := fillLostIntFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeTime:
if err := fillLostTimeFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeUser:
if err := fillLostUserFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeOrganization:
if err := fillLostOrganizationFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeTimeZone:
if err := fillLostTimeZoneFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeList:
if err := fillLostListFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeBool:
if err := fillLostBoolFieldValue(valData, field); err != nil {
return err
}
case common.FieldTypeIDRule:
if field.PropertyType == common.FieldTypeIDRule {
idRuleField = &properties[idx]
default:
} else if fieldLostValueFunc, ok := ccSysFieldTypeRela[field.PropertyType]; ok {
if err := fieldLostValueFunc(valData, field); err != nil {
blog.Errorf("fill lost value failed, property type: %s, field: %+v, err: %v, rid: %s",
field.PropertyType, field, err, util.ExtractRequestIDFromContext(ctx))
return err
}
} else if fieldLostValueCtxFunc, ok := ccSysFieldTypeCtxRela[field.PropertyType]; ok {
if err := fieldLostValueCtxFunc(ctx, valData, field); err != nil {
blog.Errorf("fill lost value failed, property type: %s, field: %+v, err: %v, rid: %s",
field.PropertyType, field, err, util.ExtractRequestIDFromContext(ctx))
return err
}
} else if handle, ok := manager.Get(field.PropertyType); ok {
if err := handle.FillLostValue(ctx, valData, field.PropertyID, field.Default, field.Option); err != nil {
blog.Errorf("fill lost value failed, property type: %s, field: %+v, err: %v, rid: %s",
field.PropertyType, field, err, util.ExtractRequestIDFromContext(ctx))
return err
}
} else {
valData[field.PropertyID] = nil
}
}
@@ -111,6 +76,27 @@ func FillLostFieldValue(ctx context.Context, valData mapstr.MapStr, properties [
return nil
}
var ccSysFieldTypeRela = map[string]func(valData mapstr.MapStr, field metadata.Attribute) error{
common.FieldTypeSingleChar: fillLostStringFieldValue,
common.FieldTypeLongChar: fillLostStringFieldValue,
common.FieldTypeDate: fillLostDateFieldValue,
common.FieldTypeFloat: fillLostFloatFieldValue,
common.FieldTypeInt: fillLostIntFieldValue,
common.FieldTypeTime: fillLostTimeFieldValue,
common.FieldTypeUser: fillLostUserFieldValue,
common.FieldTypeOrganization: fillLostOrganizationFieldValue,
common.FieldTypeTimeZone: fillLostTimeZoneFieldValue,
common.FieldTypeList: fillLostListFieldValue,
common.FieldTypeBool: fillLostBoolFieldValue,
}
var ccSysFieldTypeCtxRela = map[string]func(ctx context.Context, valData mapstr.MapStr, field metadata.Attribute) error{
common.FieldTypeEnum: fillLostEnumFieldValue,
common.FieldTypeEnumMulti: fillLostEnumMultiFieldValue,
common.FieldTypeEnumQuote: fillLostEnumQuoteFieldValue,
}
func fillLostStringFieldValue(valData mapstr.MapStr, field metadata.Attribute) error {
valData[field.PropertyID] = ""
if field.Default == nil {

View File

@@ -37,6 +37,7 @@ import (
"configcenter/src/common/util"
"configcenter/src/common/valid"
attrvalid "configcenter/src/common/valid/attribute"
"configcenter/src/common/valid/attribute/manager"
"configcenter/src/storage/dal/types"
"configcenter/src/storage/driver/mongodb"
@@ -506,6 +507,7 @@ var validAttrPropertyTypes = map[string]struct{}{
func (m *modelAttribute) checkAttributeValidity(kit *rest.Kit, attribute metadata.Attribute,
propertyType string) error {
language := httpheader.GetLanguage(kit.Header)
lang := m.language.CreateDefaultCCLanguageIf(language)
if attribute.PropertyID != "" {
@@ -542,9 +544,27 @@ func (m *modelAttribute) checkAttributeValidity(kit *rest.Kit, attribute metadat
}
}
if err := m.validPropertyType(kit, attribute, propertyType); err != nil {
return err
}
if opt, ok := attribute.Option.(string); ok && opt != "" {
if common.AttributeOptionMaxLength < utf8.RuneCountInString(opt) {
return kit.CCError.Errorf(common.CCErrCommValExceedMaxFailed, lang.Language("model_attr_option_regex"),
common.AttributeOptionMaxLength)
}
}
return nil
}
func (m *modelAttribute) validPropertyType(kit *rest.Kit, attribute metadata.Attribute, propertyType string) error {
if attribute.PropertyType != "" {
if _, exists := validAttrPropertyTypes[attribute.PropertyType]; !exists {
return kit.CCError.Errorf(common.CCErrCommParamsIsInvalid, metadata.AttributeFieldPropertyType)
if _, ok := manager.Get(attribute.PropertyType); !ok {
return kit.CCError.Errorf(common.CCErrCommParamsIsInvalid, metadata.AttributeFieldPropertyType)
}
}
}
@@ -556,13 +576,6 @@ func (m *modelAttribute) checkAttributeValidity(kit *rest.Kit, attribute metadat
}
}
if opt, ok := attribute.Option.(string); ok && opt != "" {
if common.AttributeOptionMaxLength < utf8.RuneCountInString(opt) {
return kit.CCError.Errorf(common.CCErrCommValExceedMaxFailed, lang.Language("model_attr_option_regex"),
common.AttributeOptionMaxLength)
}
}
return nil
}

3
src/ui/.gitignore vendored
View File

@@ -20,6 +20,9 @@ eslint.html
# dev config
/builder/config/index.dev*.js
# serve config
/builder/config/index.svr.js
# mock
/mock/*
!/mock/index.js

67
src/ui/builder/serve.js Normal file
View File

@@ -0,0 +1,67 @@
/**
* Tencent is pleased to support the open source community by making 蓝鲸 available.
* Copyright (C) 2017 Tencent. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs')
const path = require('path')
const express = require('express')
const { createProxyMiddleware } = require('http-proxy-middleware')
// ---------- 运行时配置 ----------
const svrConfig = require('./config/index.svr')
const buildConfig = require('./config')
// ---------- 代理目标 ----------
const PROXY_TARGET = svrConfig.apiTarget
// ---------- Express 实例 ----------
const app = express()
// ---------- 1. 静态资源 ----------
const staticDir = buildConfig.build.assetsRoot
const publicPath = '/static'
// 把本地 dist 目录挂到 /static
app.use(publicPath, express.static(staticDir))
// ---------- 2. 读取并缓存 index.html 模板 ----------
const indexPath = path.join(staticDir, 'index.html')
if (!fs.existsSync(indexPath)) {
console.error(`❌ 未找到模板文件 ${indexPath}`)
process.exit(1)
}
const template = fs.readFileSync(indexPath, 'utf8')
// ---------- 3. 模板渲染 ----------
function render(html, data) {
return html.replace(/\{\{\.(\w+)\}\}/g, (_, key) => data[key] ?? '')
}
// ---------- 4. 根路由 & 模板注入 ----------
app.get('/', (req, res) => {
res.set('Content-Type', 'text/html')
res.send(render(template, svrConfig))
})
// ---------- 5. 其余全部代理 ----------
// 放在最末尾兜底,确保 /static 和 index.html 已处理完
app.use(createProxyMiddleware({
target: PROXY_TARGET,
changeOrigin: true,
logLevel: 'debug',
}))
// ---------- 6. 启动 ----------
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`✅ SPA server listening on http://localhost:${PORT}`)
console.log(` Static files -> http://localhost:${PORT}/static/**`)
console.log(` Everything else -> ${PROXY_TARGET}`)
})

View File

@@ -0,0 +1,81 @@
/*
* Tencent is pleased to support the open source community by making 蓝鲸 available.
* Copyright (C) 2017 Tencent. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
class BuildHashPlugin {
constructor(options = {}) {
this.options = Object.assign(
{
outputFile: 'build-hash.txt', // 默认输出文件名
algorithm: 'sha256', // 哈希算法
includeTimestamp: false, // 是否包含时间戳
customContent: null, // 自定义内容,会添加到哈希中
},
options,
)
}
apply(compiler) {
// 在完成构建后执行
compiler.hooks.done.tap('BuildHashPlugin', (stats) => {
const { compilation } = stats
const outputPath = compilation.outputOptions.path || compiler.outputPath
const hashFile = path.resolve(outputPath, this.options.outputFile)
// 1. 创建哈希生成器
const hash = crypto.createHash(this.options.algorithm)
// 2. 添加构建时间戳(可选)
if (this.options.includeTimestamp) {
hash.update(Date.now().toString())
}
// 3. 添加自定义内容(可选)
if (typeof this.options.customContent === 'string') {
hash.update(this.options.customContent)
}
// 4. 添加编译信息
hash.update(compilation.hash)
// 5. 添加所有依赖模块的修改时间
compilation.fileDependencies.forEach((filePath) => {
if (fs.existsSync(filePath)) {
const stats = fs.statSync(filePath)
hash.update(stats.mtimeMs.toString())
}
})
// 6. 生成最终哈希
const finalHash = hash.digest('hex')
// 7. 确保输出目录存在
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath, { recursive: true })
}
// 8. 写入文件
fs.writeFileSync(hashFile, finalHash)
// 9. 在控制台显示信息(可选)
if (this.options.verbose !== false) {
console.log(`\nBuild hash generated: ${finalHash}`)
console.log(`Hash file written to: ${hashFile}`)
}
})
}
}
module.exports = BuildHashPlugin

View File

@@ -18,10 +18,10 @@ const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
const ProgressBarPlugin = require('progress-bar-webpack-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const chalk = require('chalk')
const ReplaceStaticUrlPlugin = require('../utils/replace-static-url-plugin')
const GrabAPIPlugin = require('../utils/grab-api-plugin')
const chalk = require('chalk')
const BuildHashPlugin = require('../utils/build-hash-plugin')
const { isProd, resolveBase, modeValue } = require('../utils')
const devEnv = require('../config/dev.env')
@@ -95,7 +95,9 @@ const getProdPlugins = config => ([
ignoreOrder: true
}),
new ReplaceStaticUrlPlugin(config.build)
new ReplaceStaticUrlPlugin(config.build),
new BuildHashPlugin()
].concat((process.env.ANALYZER || config.build.bundleAnalyzerReport) ? [
new BundleAnalyzerPlugin()
] : []))

View File

@@ -11,12 +11,13 @@
"build": "node builder/build.js",
"build:analyzer": "cross-env ANALYZER=true node builder/build.js",
"start": "npm run dev -- --env",
"serve": "node builder/serve.js",
"lint": "eslint --ext .js,.vue ./src --cache",
"lint-output": "eslint --ext .js,.vue --output-file ./eslint.html --format html ./src",
"lint-fix": "eslint --fix --ext .js,.vue ./src"
},
"dependencies": {
"@babel/runtime": "^7.27.1",
"@babel/runtime": "^7.28.4",
"@blueking/bkui-library": "^0.0.0-beta.6",
"@blueking/functional-dependency": "^0.0.1-beta.10",
"@blueking/login-modal": "^1.0.0",
@@ -113,7 +114,7 @@
"minimist": "^1.2.5",
"os-browserify": "^0.3.0",
"path-browserify": "^1.0.1",
"path-to-regexp": "^6.2.1",
"path-to-regexp": "^6.3.0",
"postcss": "^8.2.10",
"postcss-deep-scopable": "0.0.2",
"postcss-loader": "^5.2.0",

View File

@@ -10,7 +10,7 @@
* limitations under the License.
*/
import Vue, { toRef, watch } from 'vue'
import Vue, { computed, toRef, watch } from 'vue'
import contentComponent from './export'
import store from '@/store'
import i18n from '@/i18n'
@@ -21,6 +21,7 @@ import isEqual from 'lodash/isEqual'
let instance = null
const [state, { setState, resetState }] = useState()
const [{ all }] = useTask()
const [, { reset: resetTask }] = useTask()
const visible = toRef(state, 'visible')
const title = toRef(state, 'title')
@@ -46,9 +47,10 @@ watch(visible, (value) => {
}, 200)
})
const hasChange = () => !isEqual(state.originFields.value, fields.value) || step.value !== 1 || status.value === 'pending'
const hasChange = computed(() => !isEqual(state.originFields.value, fields.value) || step.value !== 1 || status.value === 'pending')
const allFinished = computed(() => all.value.length && all.value.every(task => task.state === 'finished'))
const beforeClose = () => {
if (!hasChange()) {
if (!hasChange.value || allFinished.value) {
return true
}
return new Promise((resolve) => {

View File

@@ -55,7 +55,7 @@ const start = () => {
const [state] = useState()
const queue = new Array(Math.ceil(state.count.value / state.limit.value)).fill(null)
.map((_, index) => ({
name: `${state.bk_obj_id.value}_download_${index + 1}`,
name: `${state.bk_obj_id.value}_download_${Date.now()}_${index + 1}`,
state: 'waiting',
page: {
start: index * state.limit.value,

View File

@@ -110,6 +110,7 @@
:is="getComponentType(property)"
:placeholder="getPlaceholder(property)"
:property="property"
:is-paste-split="getPasteSplit(property.bk_property_id)"
:ref="`component-${property.id}`"
v-bind="getBindProps(property)"
v-model.trim="condition[property.id].value"
@@ -225,7 +226,7 @@
import Utils from './utils'
import { isContainerObject } from '@/service/container/common'
import ConditionPicker from '@/components/condition-picker'
import { setCursorPosition, getConditionSelect, updatePropertySelect } from '@/utils/util'
import { setCursorPosition, getConditionSelect, updatePropertySelect, isPasteSplit } from '@/utils/util'
import useSideslider from '@/hooks/use-sideslider'
import isEqual from 'lodash/isEqual'
import EditableBlock from '@/components/editable-block/index.vue'
@@ -402,6 +403,9 @@
this.setChanged = setChanged
},
methods: {
getPasteSplit(id) {
return isPasteSplit(id)
},
hasAddSelected(val, oldVal, addSelect) {
return val[0] && oldVal[0] && addSelect.length > 0
},

View File

@@ -32,6 +32,7 @@
<component class="form-el"
:is="getComponentType()"
:placeholder="getPlaceholder()"
:is-paste-split="getPasteSplit(property.bk_property_id)"
v-bind="getBindProps()"
v-model.trim="value"
@active-change="handleActiveChange">
@@ -53,6 +54,7 @@
import { mapGetters } from 'vuex'
import { isContainerObject } from '@/service/container/common'
import { QUERY_OPERATOR, QUERY_OPERATOR_HOST_SYMBOL, QUERY_OPERATOR_HOST_DESC } from '@/utils/query-builder-operator'
import { isPasteSplit } from '@/utils/util'
export default {
components: {
@@ -110,6 +112,9 @@
}
},
methods: {
getPasteSplit(id) {
return isPasteSplit(id)
},
getPlaceholder() {
return Utils.getPlaceholder(this.property)
},

View File

@@ -63,6 +63,7 @@
:is="getComponentType(property)"
:placeholder="getPlaceholder(property)"
:property="property"
:is-paste-split="getPasteSplit(property.bk_property_id)"
:ref="`component-${property.id}`"
v-bind="getBindProps(property)"
v-model.trim="condition[property.id].value"
@@ -105,7 +106,7 @@
import { setSearchQueryByCondition, resetConditionValue } from './general-model-filter.js'
import Utils from './utils'
import ConditionPicker from '@/components/condition-picker'
import { getConditionSelect, updatePropertySelect } from '@/utils/util'
import { getConditionSelect, updatePropertySelect, isPasteSplit } from '@/utils/util'
import isEqual from 'lodash/isEqual'
import useSideslider from '@/hooks/use-sideslider'
import { QUERY_OPERATOR, QUERY_OPERATOR_OTHER_SYMBOL, QUERY_OPERATOR_OTHER_DESC } from '@/utils/query-builder-operator'
@@ -196,6 +197,9 @@
this.setChanged = setChanged
},
methods: {
getPasteSplit(id) {
return isPasteSplit(id)
},
setCondition(nowCondition) {
const newCondition = this.$tools.clone(this.filterCondition)
Object.keys(nowCondition).forEach((id) => {

View File

@@ -201,10 +201,14 @@
}
},
defaultValue(val, old) {
// 检测选中值变化需要修正is_default
// 检测选中值变化需要修正is_default这里值的类型都是string
if (val && !isEqual(val, old)) {
this.enumList.forEach((item) => {
item.is_default = val.includes(item.id)
if (this.isDefaultCompMultiple && Array.isArray(val)) {
item.is_default = val.includes(item.id)
} else {
item.is_default = val === item.id
}
})
this.$emit('input', this.enumList)
}

View File

@@ -151,8 +151,9 @@
:value="defaultValue"
:property="field">
</cmdb-property-value>
<!-- bool类型字段未设置与默认值false都将显示为false -->
<span v-else
class="property-value">{{defaultValue || '--'}}</span>
class="property-value">{{defaultValue ?? '--'}}</span>
</div>
</div>
<template slot="footer" slot-scope="{ sticky }" v-if="canEdit">

View File

@@ -76,6 +76,7 @@
<script>
import formMixins from '@/mixins/form'
import { filterXSS } from '@/utils/util'
import TableDefaultSettings from './table-default-settings.vue'
import { PROPERTY_TYPES } from '@/dictionary/property-constants'
@@ -149,11 +150,7 @@
return !property.editable || property.isreadonly
},
htmlEncode(placeholder) {
let temp = document.createElement('div')
temp.innerHTML = placeholder
const output = temp.innerText
temp = null
return output
return filterXSS(placeholder)
},
getPlaceholder(property) {
const placeholderTxt = ['enum', 'list'].includes(property.bk_property_type) ? '请选择xx' : '请输入xx'

View File

@@ -80,6 +80,7 @@
.filter(value => value.length)
}
this.localValue = [...new Set([...this.localValue, ...val])]
return this.localValue
},
handleInputChange(value) {
this.$emit('inputchange', value)

View File

@@ -74,6 +74,7 @@
.filter(value => value.length)
}
this.localValue = [...new Set([...this.localValue, ...val])]
return this.localValue
},
handleInputChange(value) {
this.$emit('inputchange', value)

View File

@@ -95,15 +95,18 @@
methods: {
handlePasteFn(value) {
if (!value) return
if (this.onlyNumber && !isNumeric(value)) {
return
if (this.onlyNumber) {
// 如果当前需要分割功能在只能输入number的时候需要将value去掉分隔符
const detectionValue = this.isPasteSplit ? value.replace(/,|;|\n|\s/g, '') : value
if (!isNumeric(detectionValue)) return
}
let val = [value]
if (this.isPasteSplit && this.multiple) {
val = (value.split(/,|;|\n/)).map(value => value.trim())
val = (value.split(/,|;|\n|\s/)).map(value => value.trim())
.filter(value => value.length)
}
this.localValue = [...new Set([...this.localValue, ...val])]
return this.localValue
},
handleInputChange(value) {
this.$emit('inputchange', value)

View File

@@ -43,7 +43,7 @@
v-if="property.placeholder && $tools.isIconTipProperty(property.bk_property_type)"
v-bk-tooltips="{
trigger: 'mouseenter',
content: property.placeholder
content: htmlEncode(property.placeholder)
}">
</i>
</bk-checkbox>
@@ -65,7 +65,7 @@
disabled: !property.placeholder,
theme: 'light',
trigger: 'click',
content: property.placeholder
content: htmlEncode(property.placeholder)
}"
v-bind="$tools.getValidateEvents(property)"
v-validate="getValidateRules(property)"
@@ -125,6 +125,7 @@
import { BUILTIN_UNEDITABLE_FIELDS } from '@/dictionary/model-constants'
import useSideslider from '@/hooks/use-sideslider'
import cmdbDefaultPicker from '@/components/ui/other/default-value-picker'
import { filterXSS } from '@/utils/util'
export default {
name: 'cmdb-form-multiple',
@@ -242,11 +243,7 @@
this.editable = editable
},
htmlEncode(placeholder) {
let temp = document.createElement('div')
temp.innerHTML = placeholder
const output = temp.innerText
temp = null
return output
return filterXSS(placeholder)
},
getProperty(id) {
return this.properties.find(property => property.bk_property_id === id)

View File

@@ -123,6 +123,7 @@
import useSideslider from '@/hooks/use-sideslider'
import isEqual from 'lodash/isEqual'
import cmdbDefaultPicker from '@/components/ui/other/default-value-picker'
import { filterXSS } from '@/utils/util'
export default {
name: 'cmdb-form',
@@ -258,11 +259,7 @@
return property.isrequired
},
htmlEncode(placeholder) {
let temp = document.createElement('div')
temp.innerHTML = placeholder
const output = temp.innerText
temp = null
return output
return filterXSS(placeholder)
},
getValidateRules(property) {
const rules = this.$tools.getValidateRules(property)

View File

@@ -69,12 +69,7 @@
props() {
const props = { ...this.$attrs }
if (this.api) {
try {
const url = new URL(this.api)
props.api = `${window.API_HOST}proxy/get/usermanage${url.pathname}`
} catch (e) {
console.error(e)
}
props.api = this.api
} else {
props.fuzzySearchMethod = this.fuzzySearchMethod
props.exactSearchMethod = this.exactSearchMethod

View File

@@ -33,21 +33,11 @@
}
},
data() {
return {}
return {
api: window.ESB.userManage
}
},
computed: {
api() {
const { userManage } = window.ESB
if (userManage) {
try {
const url = new URL(userManage)
return `${window.API_HOST}proxy/get/usermanage${url.pathname}`
} catch (e) {
console.error(e)
}
}
return ''
},
localValue: {
get() {
if (this.value) {

View File

@@ -21,6 +21,9 @@ import {
MENU_RESOURCE_PROJECT_COLLECTION
} from '@/dictionary/menu-symbol'
// 字段搜索时不受cmdb-search-xx组件isPastesplit属性切割影响的字段 (一定会切割)
export const BUILTIN_PASTE_SPLIT_FIELDS = ['bk_biz_set_id', 'bk_biz_id', 'bk_host_id', 'bk_module_id', 'bk_set_id', 'id', 'bk_inst_id', 'bk_agent_id', 'bk_cloud_inst_id']
// 不能更新修改的字段(在可能发生编辑操作的页面里不显示出来)
export const BUILTIN_UNEDITABLE_FIELDS = ['bk_updated_by', 'bk_updated_at', 'bk_created_by', 'bk_created_at']

View File

@@ -0,0 +1,85 @@
/*
* Tencent is pleased to support the open source community by making 蓝鲸 available.
* Copyright (C) 2017 Tencent. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getCurrentScope, onScopeDispose, ref } from 'vue'
export default function useTimeoutPoll(fn, interval, options) {
const { immediate = false, max = 100 } = options || {}
const isActive = ref(false)
let timer = null
let times = 0
function clear() {
if (timer) {
clearTimeout(timer)
timer = null
}
}
function start() {
clear()
timer = setTimeout(() => {
timer = null
loop()
}, interval ?? 5000)
}
async function loop() {
if (!isActive.value) {
return
}
if (max !== -1 && times >= max) {
return
}
times += 1
await fn()
start()
}
function resume() {
if (!isActive.value) {
isActive.value = true
immediate ? loop() : start()
}
}
function pause() {
isActive.value = false
}
function reset() {
clear()
isActive.value = false
times = 0
}
if (immediate) {
resume()
}
if (getCurrentScope()) {
onScopeDispose(pause)
}
return {
isActive,
pause,
resume,
reset,
}
}

View File

@@ -1926,5 +1926,7 @@
"刚刚刷新": "刚刚刷新",
"大于1小时前刷新": "大于1小时前刷新",
"x分钟前刷新": "{time}分钟前刷新",
"注:已存在服务实例的主机,不能再勾选": "注:已存在服务实例的主机,不能再勾选"
"注:已存在服务实例的主机,不能再勾选": "注:已存在服务实例的主机,不能再勾选",
"版本更新": "版本更新",
"建议「刷新页面」体验新的特性,「暂不刷新」可能会遇到未知异常,可手动刷新解决。": "建议「刷新页面」体验新的特性,「暂不刷新」可能会遇到未知异常,可手动刷新解决。"
}

View File

@@ -1919,5 +1919,7 @@
"刚刚刷新": "Last refreshed just now",
"大于1小时前刷新": "Last refreshed more than 1h ago",
"x分钟前刷新": "Last refreshed {time}m ago",
"注:已存在服务实例的主机,不能再勾选": "Note: Hosts with existing service instances cannot be selected"
"注:已存在服务实例的主机,不能再勾选": "Note: Hosts with existing service instances cannot be selected",
"版本更新": "Version Update",
"建议「刷新页面」体验新的特性,「暂不刷新」可能会遇到未知异常,可手动刷新解决。": "It is recommended to \"Refresh Page\" to experience new features. \"Skip Refresh\" may cause unexpected errors. You can also \"Refresh Manually\" to resolve issues."
}

View File

@@ -28,6 +28,7 @@ import cmdbSearchComponent from './components/search/index'
import routerActions from './router/actions'
import tools from './utils/tools'
import { gotoLoginPage } from '@/utils/login-helper'
import { watchVersion } from '@/utils/check-version'
import clipboard from 'vue-clipboard2'
import './magicbox'
import './directives'
@@ -60,6 +61,10 @@ api.get(`${window.API_HOST}is_login`).then(() => {
return !subEnv ? <App /> : <IframeEntry />
}
})
if (process.env.NODE_ENV === 'production') {
watchVersion()
}
})
.catch(() => {
gotoLoginPage()

View File

@@ -0,0 +1,134 @@
/*
* Tencent is pleased to support the open source community by making 蓝鲸 available.
* Copyright (C) 2017 Tencent. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
import { bkInfoBox } from 'bk-magic-vue'
import i18n from '@/i18n'
import useTimeoutPoll from '@/hooks/use-timeout-poll'
const STATIC_PATH = `${window.Site.publicPath ?? '/'}static/`
const VERSION_FILE = 'build-hash.txt'
let localVersion = ''
let isShown = false
let dialog = null
let checkVersionPoll = null
const checkVersionChannel = new BroadcastChannel('check-version')
checkVersionChannel.addEventListener('message', ({ data }) => {
if (data?.type !== 'toggle') {
return
}
// 共享弹出状态,视之后有无需求多个标签页只允许一次弹出提示使用
// isShown = data.payload.isShown;
})
const hideDialog = (silent = false) => {
if (dialog) {
dialog.close()
}
isShown = false
dialog = null
checkVersionPoll.resume()
if (!silent) {
checkVersionChannel.postMessage({ type: 'toggle', payload: { isShown: false } })
}
}
const fetchBuildHash = async () => {
const response = await fetch(`${STATIC_PATH}${VERSION_FILE}?_=${Date.now()}`)
if (!response.ok) {
throw new Error('Failed to get build hash')
}
const newVersion = await response.text()
// localVersion 还不存在,认为是第一次加载
if (!localVersion) {
localVersion = newVersion
}
if (newVersion !== localVersion) {
showVersionDialog(newVersion)
}
}
const checkVersion = async () => {
try {
await fetchBuildHash()
} catch (error) {
console.error(error)
}
}
const handleVisibilityChange = () => {
if (!isShown && document.visibilityState === 'visible') {
checkVersionPoll.resume()
} else {
checkVersionPoll.pause()
}
}
const showVersionDialog = (newVersion) => {
if (isShown) {
return
}
dialog = bkInfoBox({
width: 650,
maskClose: false,
title: i18n.t('版本更新'),
subTitle: i18n.t('建议「刷新页面」体验新的特性,「暂不刷新」可能会遇到未知异常,可手动刷新解决。'),
okText: i18n.t('刷新'),
cancelText: i18n.t('取消'),
confirmFn: () => {
window.location.reload()
},
cancelFn: () => {
hideDialog()
},
})
if (dialog) {
isShown = true
localVersion = newVersion
checkVersionPoll.pause()
checkVersionChannel.postMessage({ type: 'toggle', payload: { isShown: true } })
}
}
const addVisibilityChangeListener = () => {
document.addEventListener('visibilitychange', handleVisibilityChange)
}
const removeVisibilityChangeListener = () => {
document.removeEventListener('visibilitychange', handleVisibilityChange)
}
const addBeforeunloadListener = () => {
window.addEventListener('beforeunload', () => {
hideDialog()
removeVisibilityChangeListener()
checkVersionPoll.reset()
checkVersionChannel.close()
})
}
export const watchVersion = () => {
checkVersionPoll = useTimeoutPoll(checkVersion, 5 * 60 * 1000, { immediate: true, max: -1 })
addVisibilityChangeListener()
addBeforeunloadListener()
}

View File

@@ -11,6 +11,8 @@
*/
import { t } from '@/i18n'
import xss from 'xss'
import { BUILTIN_PASTE_SPLIT_FIELDS } from '@/dictionary/model-constants.js'
const hex2grb = (hex) => {
const rgb = []
@@ -343,3 +345,29 @@ export function* paginateIterator(list, pageSize) {
index += pageSize
}
}
/**
* 过滤XSS攻击只保留纯文本
* @param {string} str 字符串
* @returns 过滤后的字符串
*/
export function filterXSS(str) {
if (!str) return str
const result = xss(str, {
whiteList: {},
stripIgnoreTag: true,
stripIgnoreTagBody: ['script']
})
return result
}
/**
* 设置当前字段是否分割字符串
* @param {string} id 字段ID
* @param {funtion} fn 自定义方法
* @return true | false
**/
export function isPasteSplit(id, fn = () => false) {
if (BUILTIN_PASTE_SPLIT_FIELDS.includes(id)) return true
return fn?.()
}

View File

@@ -153,6 +153,9 @@
}
},
watch: {
isModelInstance() {
this.condition.bk_obj_id = ''
},
instanceType() {
this.condition.resource_id = ''
this.condition.resource_name = ''

View File

@@ -118,14 +118,13 @@
let result = []
if (window.ESB.userManage) {
const url = new URL(window.ESB.userManage)
const params = {
app_code: 'bk-magicbox',
page: 1,
page_size: 100,
fuzzy_lookups: query
}
const api = `${window.API_HOST}proxy/get/usermanage${url.pathname}`
const api = window.ESB.userManage
const response = await jsonp(api, params)
if (response.code !== 0) {
console.error(response?.message)

Some files were not shown because too many files have changed in this diff Show More