From d5792f05a9228245975aaf40423b9c1c9766fc8d Mon Sep 17 00:00:00 2001 From: zacYL <100330102+zacYL@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:12:51 +0800 Subject: [PATCH 01/24] =?UTF-8?q?feat:=20digest=E6=8E=A5=E5=8F=A3=E5=8F=AA?= =?UTF-8?q?=E8=BF=94=E5=9B=9ECONAN=5FMANIFEST#2770?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tencent/bkrepo/conan/controller/ConanController.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/backend/conan/biz-conan/src/main/kotlin/com/tencent/bkrepo/conan/controller/ConanController.kt b/src/backend/conan/biz-conan/src/main/kotlin/com/tencent/bkrepo/conan/controller/ConanController.kt index 2ad6d20855..b07f2d4232 100644 --- a/src/backend/conan/biz-conan/src/main/kotlin/com/tencent/bkrepo/conan/controller/ConanController.kt +++ b/src/backend/conan/biz-conan/src/main/kotlin/com/tencent/bkrepo/conan/controller/ConanController.kt @@ -40,11 +40,7 @@ import com.tencent.bkrepo.common.artifact.audit.NODE_CREATE_ACTION import com.tencent.bkrepo.common.artifact.audit.NODE_DOWNLOAD_ACTION import com.tencent.bkrepo.common.artifact.audit.NODE_RESOURCE import com.tencent.bkrepo.common.security.permission.Permission -import com.tencent.bkrepo.conan.constant.CONANFILE -import com.tencent.bkrepo.conan.constant.CONANINFO import com.tencent.bkrepo.conan.constant.CONAN_MANIFEST -import com.tencent.bkrepo.conan.constant.EXPORT_SOURCES_TGZ_NAME -import com.tencent.bkrepo.conan.constant.PACKAGE_TGZ_NAME import com.tencent.bkrepo.conan.pojo.artifact.ConanArtifactInfo import com.tencent.bkrepo.conan.pojo.artifact.ConanArtifactInfo.Companion.GET_CONANFILE_DOWNLOAD_URLS_V1 import com.tencent.bkrepo.conan.pojo.artifact.ConanArtifactInfo.Companion.GET_PACKAGE_DOWNLOAD_URLS_V1 @@ -86,7 +82,7 @@ class ConanController( ): ResponseEntity { return ConanCommonController.buildResponse( conanService.getConanFileDownloadUrls( - conanArtifactInfo, mutableListOf(CONAN_MANIFEST, EXPORT_SOURCES_TGZ_NAME, CONANFILE) + conanArtifactInfo, mutableListOf(CONAN_MANIFEST) ) ) } @@ -101,7 +97,7 @@ class ConanController( ): ResponseEntity { return ConanCommonController.buildResponse( conanService.getPackageDownloadUrls( - conanArtifactInfo, mutableListOf(CONAN_MANIFEST, CONANINFO, PACKAGE_TGZ_NAME) + conanArtifactInfo, mutableListOf(CONAN_MANIFEST) ) ) } From 8445cff78048d2f86b3073eeb4e17e385df09316 Mon Sep 17 00:00:00 2001 From: owen Date: Wed, 20 Nov 2024 14:49:55 +0800 Subject: [PATCH 02/24] =?UTF-8?q?feat:=20=E6=9C=8D=E5=8A=A1=E8=BD=AC?= =?UTF-8?q?=E5=8F=91=E5=88=B0=E6=9C=AC=E5=9C=B0=20#2747?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 服务转发到本地 #2747 * feat: 服务转发到本地 #2747 * feat: 服务转发到本地 #2747 * feat: 服务转发到本地 #2747 --- src/gateway/lua/util/host_util.lua | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/gateway/lua/util/host_util.lua b/src/gateway/lua/util/host_util.lua index 13cd775480..817fde13c2 100644 --- a/src/gateway/lua/util/host_util.lua +++ b/src/gateway/lua/util/host_util.lua @@ -49,8 +49,17 @@ function _M:get_addr(service_name) local router_srv_cache = ngx.shared.router_srv_store local router_srv_value = router_srv_cache:get(query_subdomain) + local service_in_local = config.service_in_local if router_srv_value == nil then + -- 是否取用本地配置, 取用本地配置时需要获取所有ip,使用tcp协议,并增加缓存时间 + local cache_time = 2 + local use_udp = true + if service_in_local ~= nil and service_in_local ~= "" then + cache_time = 3 + use_udp = false + end + if not ns_config.ip then ngx.log(ngx.ERR, "DNS ip not exist!") ngx.exit(503) @@ -77,9 +86,11 @@ function _M:get_addr(service_name) ngx.exit(503) return end - - local records, err = dns:query(query_subdomain, { qtype = dns.TYPE_SRV, additional_section = true }) - + if use_udp then + records, err = dns:query(query_subdomain, { qtype = dns.TYPE_SRV, additional_section = true }) + else + records, err = dns:tcp_query(query_subdomain, { qtype = dns.TYPE_SRV, additional_section = true }) + end if not records then ngx.log(ngx.ERR, "failed to query the DNS server: ", err) ngx.exit(503) @@ -114,7 +125,7 @@ function _M:get_addr(service_name) ngx.exit(503) return end - router_srv_cache:set(query_subdomain, table.concat(ips, ",") .. ":" .. port, 2) + router_srv_cache:set(query_subdomain, table.concat(ips, ",") .. ":" .. port, cache_time) else local func_itor = string.gmatch(router_srv_value, "([^:]+)") local ips_str = func_itor() @@ -125,7 +136,6 @@ function _M:get_addr(service_name) end end -- return with local service - local service_in_local = config.service_in_local if internal_ip ~= nil and service_in_local ~= nil and string.find(service_in_local, service_name) ~= nil then local service_ip = string.gsub(internal_ip, "\n", "") if arrayUtil:isInArray(service_ip, ips) then From 7604a08365fd2cf7c3f25b9b3367e90c41309564 Mon Sep 17 00:00:00 2001 From: yaoxuwan Date: Wed, 20 Nov 2024 15:16:40 +0800 Subject: [PATCH 03/24] =?UTF-8?q?fix:=20edge=E8=B0=83=E7=94=A8center=20fei?= =?UTF-8?q?gn=E8=AF=B7=E6=B1=82=E8=B6=85=E6=97=B6=E5=90=8E=EF=BC=8Cedge?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E9=80=BB=E8=BE=91=E9=9C=80=E8=A6=81=E7=BB=A7?= =?UTF-8?q?=E7=BB=AD=E6=89=A7=E8=A1=8C=20#2749?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt index 36232bf192..3686de5a29 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt @@ -38,6 +38,7 @@ import com.tencent.bkrepo.common.service.cluster.properties.ClusterProperties import com.tencent.bkrepo.common.service.exception.RemoteErrorCodeException import com.tencent.bkrepo.common.service.util.HeaderUtils import com.tencent.bkrepo.common.service.util.SpringContextUtils +import feign.RetryableException import org.slf4j.LoggerFactory import org.springframework.util.AntPathMatcher @@ -154,6 +155,11 @@ object ClusterUtils { if (!messageCodes.map { it.getCode() }.contains(e.errorCode)) { throw e } + } catch (e: RetryableException) { + if (e.message?.contains("Read time out") == true) { + logger.info("ignore feign exception: ${e.message}") + } + throw e } } } From 2bdbcc2bcce333f2e580a5a25b78cec7a1c823b6 Mon Sep 17 00:00:00 2001 From: yaoxuwan Date: Wed, 20 Nov 2024 15:33:05 +0800 Subject: [PATCH 04/24] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81websocket=20#24?= =?UTF-8?q?94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 支持websocket #2494 * feat: 支持websocket #2494 * feat: 支持websocket #2494 * feat: 支持websocket #2494 --- src/backend/build.gradle.kts | 1 + .../CustomMetricsPushAutoConfiguration.kt | 4 +- .../bkrepo/common/redis/RedisOperation.kt | 4 +- .../config/boot-config/build.gradle.kts | 1 + src/backend/settings.gradle.kts | 1 + .../websocket/biz-websocket/build.gradle.kts | 40 ++++++ .../websocket/config/WebSocketConfigurer.kt | 48 +++++++ .../websocket/config/WebSocketProperties.kt | 37 +++++ .../config/WebsocketConfiguration.kt | 117 ++++++++++++++++ .../WsThreadPoolTaskExeccutorConfiguration.kt | 65 +++++++++ .../websocket/constant/WebsocketKeys.kt | 34 +++++ .../controller/ClipboardController.kt | 52 +++++++ .../bkrepo/websocket/dispatch/Dispatcher.kt | 36 +++++ .../websocket/dispatch/TransferDispatch.kt | 54 ++++++++ .../dispatch/push/CopyPDUTransferPush.kt | 37 +++++ .../dispatch/push/PastePDUTransferPush.kt | 37 +++++ .../websocket/dispatch/push/TransferPush.kt | 33 +++++ .../exception/WebsocketExceptionHandler.kt | 48 +++++++ .../websocket/handler/SessionHandler.kt | 131 ++++++++++++++++++ ...SessionWebSocketHandlerDecoratorFactory.kt | 45 ++++++ .../listener/TransferPushListener.kt | 51 +++++++ .../bkrepo/websocket/pojo/fs/CopyPDU.kt | 40 ++++++ .../bkrepo/websocket/pojo/fs/PastePDU.kt | 36 +++++ .../websocket/service/ClipboardService.kt | 58 ++++++++ .../websocket/service/WebsocketService.kt | 63 +++++++++ .../bkrepo/websocket/util/HostUtils.kt | 84 +++++++++++ .../websocket/boot-websocket/build.gradle.kts | 34 +++++ .../bkrepo/websocket/WebsocketApplication.kt | 41 ++++++ .../src/main/resources/bootstrap.yml | 4 + src/backend/websocket/build.gradle.kts | 30 ++++ src/gateway/auth.conf | 6 + src/gateway/lua/auth/auth_web.lua | 6 + src/gateway/lua/auth/auth_websocket.lua | 55 ++++++++ src/gateway/lua/router_srv.lua | 2 +- src/gateway/vhosts/bkrepo.websocket.conf | 47 +++++++ .../gateway#vhosts#bkrepo.server.conf | 3 + 36 files changed, 1380 insertions(+), 5 deletions(-) create mode 100644 src/backend/websocket/biz-websocket/build.gradle.kts create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebSocketConfigurer.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebSocketProperties.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebsocketConfiguration.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WsThreadPoolTaskExeccutorConfiguration.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/constant/WebsocketKeys.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/controller/ClipboardController.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/Dispatcher.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/TransferDispatch.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/CopyPDUTransferPush.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/PastePDUTransferPush.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/TransferPush.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/exception/WebsocketExceptionHandler.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionHandler.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionWebSocketHandlerDecoratorFactory.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/listener/TransferPushListener.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/pojo/fs/CopyPDU.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/pojo/fs/PastePDU.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/service/ClipboardService.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/service/WebsocketService.kt create mode 100644 src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/util/HostUtils.kt create mode 100644 src/backend/websocket/boot-websocket/build.gradle.kts create mode 100644 src/backend/websocket/boot-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/WebsocketApplication.kt create mode 100644 src/backend/websocket/boot-websocket/src/main/resources/bootstrap.yml create mode 100644 src/backend/websocket/build.gradle.kts create mode 100644 src/gateway/lua/auth/auth_websocket.lua create mode 100644 src/gateway/vhosts/bkrepo.websocket.conf diff --git a/src/backend/build.gradle.kts b/src/backend/build.gradle.kts index 2813fe9475..0bd3c7fc73 100644 --- a/src/backend/build.gradle.kts +++ b/src/backend/build.gradle.kts @@ -87,6 +87,7 @@ allprojects { exclude(group = "log4j", module = "log4j") exclude(group = "org.slf4j", module = "slf4j-log4j12") exclude(group = "commons-logging", module = "commons-logging") + exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat") } tasks.withType { diff --git a/src/backend/common/common-metrics-push/src/main/kotlin/com/tencent/bkrepo/common/metrics/CustomMetricsPushAutoConfiguration.kt b/src/backend/common/common-metrics-push/src/main/kotlin/com/tencent/bkrepo/common/metrics/CustomMetricsPushAutoConfiguration.kt index 9b001532fd..4b30537e63 100644 --- a/src/backend/common/common-metrics-push/src/main/kotlin/com/tencent/bkrepo/common/metrics/CustomMetricsPushAutoConfiguration.kt +++ b/src/backend/common/common-metrics-push/src/main/kotlin/com/tencent/bkrepo/common/metrics/CustomMetricsPushAutoConfiguration.kt @@ -101,10 +101,10 @@ class CustomMetricsPushAutoConfiguration { fun customMetricsExporter( drive: PrometheusDrive, prometheusProperties: PrometheusProperties, - scheduler: ThreadPoolTaskScheduler, + taskScheduler: ThreadPoolTaskScheduler, customPushConfig: CustomPushConfig, ): CustomMetricsExporter { - return CustomMetricsExporter(customPushConfig, CollectorRegistry(), drive, prometheusProperties, scheduler) + return CustomMetricsExporter(customPushConfig, CollectorRegistry(), drive, prometheusProperties, taskScheduler) } diff --git a/src/backend/common/common-redis/src/main/kotlin/com/tencent/bkrepo/common/redis/RedisOperation.kt b/src/backend/common/common-redis/src/main/kotlin/com/tencent/bkrepo/common/redis/RedisOperation.kt index e851e8475b..de21b92aa1 100644 --- a/src/backend/common/common-redis/src/main/kotlin/com/tencent/bkrepo/common/redis/RedisOperation.kt +++ b/src/backend/common/common-redis/src/main/kotlin/com/tencent/bkrepo/common/redis/RedisOperation.kt @@ -63,8 +63,8 @@ class RedisOperation(private val redisTemplate: RedisTemplate) { } } - fun delete(key: String) { - redisTemplate.delete(key) + fun delete(key: String): Boolean { + return redisTemplate.delete(key) } fun delete(keys: Collection) { diff --git a/src/backend/config/boot-config/build.gradle.kts b/src/backend/config/boot-config/build.gradle.kts index 01bbf7507b..516184f9fb 100644 --- a/src/backend/config/boot-config/build.gradle.kts +++ b/src/backend/config/boot-config/build.gradle.kts @@ -32,4 +32,5 @@ dependencies { implementation("org.springframework.cloud:spring-cloud-config-server:3.1.6") implementation("org.springframework.cloud:spring-cloud-starter-bootstrap:3.1.6") + implementation("io.undertow:undertow-servlet") } diff --git a/src/backend/settings.gradle.kts b/src/backend/settings.gradle.kts index 23128e5dcc..e483e7aa46 100644 --- a/src/backend/settings.gradle.kts +++ b/src/backend/settings.gradle.kts @@ -93,3 +93,4 @@ includeAll(":router-controller") includeAll(":media") includeAll(":common:common-metadata") includeAll(":common:common-service") +includeAll(":websocket") diff --git a/src/backend/websocket/biz-websocket/build.gradle.kts b/src/backend/websocket/biz-websocket/build.gradle.kts new file mode 100644 index 0000000000..8e10e0a113 --- /dev/null +++ b/src/backend/websocket/biz-websocket/build.gradle.kts @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED " AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +dependencies { + api(project(":common:common-stream")) + api(project(":common:common-service")) + api(project(":common:common-artifact:artifact-service")) + implementation("org.springframework.boot:spring-boot-starter-websocket") + implementation("javax.websocket:javax.websocket-api") + implementation("io.undertow:undertow-servlet") + implementation("io.undertow:undertow-websockets-jsr") +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebSocketConfigurer.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebSocketConfigurer.kt new file mode 100644 index 0000000000..892cb634f9 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebSocketConfigurer.kt @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.config + +import com.tencent.bkrepo.common.artifact.config.ArtifactConfigurerSupport +import com.tencent.bkrepo.common.artifact.pojo.RepositoryType +import com.tencent.bkrepo.common.artifact.repository.local.LocalRepository +import com.tencent.bkrepo.common.artifact.repository.remote.RemoteRepository +import com.tencent.bkrepo.common.artifact.repository.virtual.VirtualRepository +import com.tencent.bkrepo.common.security.http.core.HttpAuthSecurityCustomizer +import org.springframework.context.annotation.Configuration + +@Configuration +class WebSocketConfigurer : ArtifactConfigurerSupport() { + + override fun getRepositoryType() = RepositoryType.NONE + override fun getLocalRepository(): LocalRepository = object : LocalRepository() {} + override fun getRemoteRepository(): RemoteRepository = object : RemoteRepository() {} + override fun getVirtualRepository(): VirtualRepository = object : VirtualRepository() {} + + override fun getAuthSecurityCustomizer() = + HttpAuthSecurityCustomizer { httpAuthSecurity -> httpAuthSecurity.withPrefix("/websocket") } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebSocketProperties.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebSocketProperties.kt new file mode 100644 index 0000000000..79fe623d87 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebSocketProperties.kt @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.config + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties("websocket") +data class WebSocketProperties( + var cacheLimit: Int = 3600, + var minThread: Int = 8, + var transfer: Boolean = false +) diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebsocketConfiguration.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebsocketConfiguration.kt new file mode 100644 index 0000000000..aced41f2e9 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebsocketConfiguration.kt @@ -0,0 +1,117 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.config + +import com.tencent.bkrepo.common.security.http.jwt.JwtAuthProperties +import com.tencent.bkrepo.common.security.manager.AuthenticationManager +import com.tencent.bkrepo.websocket.constant.APP_ENDPOINT +import com.tencent.bkrepo.websocket.constant.DESKTOP_ENDPOINT +import com.tencent.bkrepo.websocket.constant.USER_ENDPOINT +import com.tencent.bkrepo.websocket.dispatch.push.TransferPush +import com.tencent.bkrepo.websocket.handler.SessionWebSocketHandlerDecoratorFactory +import com.tencent.bkrepo.websocket.listener.TransferPushListener +import com.tencent.bkrepo.websocket.service.WebsocketService +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.messaging.Message +import org.springframework.messaging.simp.config.ChannelRegistration +import org.springframework.messaging.simp.config.MessageBrokerRegistry +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker +import org.springframework.web.socket.config.annotation.StompEndpointRegistry +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer +import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration +import java.util.function.Consumer + +@Configuration +@EnableWebSocketMessageBroker +@EnableConfigurationProperties(WebSocketProperties::class) +class WebsocketConfiguration( + private val webSocketProperties: WebSocketProperties, + private val websocketService: WebsocketService, + private val jwtAuthProperties: JwtAuthProperties, + private val authenticationManager: AuthenticationManager, + private val taskScheduler: ThreadPoolTaskScheduler +) : WebSocketMessageBrokerConfigurer { + + override fun configureMessageBroker(config: MessageBrokerRegistry) { + config.setCacheLimit(webSocketProperties.cacheLimit) + config.enableSimpleBroker("/topic") + .setHeartbeatValue(longArrayOf(3000,3000)) + .setTaskScheduler(taskScheduler) + config.setApplicationDestinationPrefixes("/app") + } + + override fun registerStompEndpoints(registry: StompEndpointRegistry) { + registry.addEndpoint(USER_ENDPOINT, APP_ENDPOINT, DESKTOP_ENDPOINT) + .setAllowedOriginPatterns("*") + registry.addEndpoint(USER_ENDPOINT, APP_ENDPOINT, DESKTOP_ENDPOINT) + .setAllowedOriginPatterns("*") + .withSockJS() + } + + @Override + override fun configureClientInboundChannel(registration: ChannelRegistration) { + var defaultCorePoolSize = webSocketProperties.minThread + if (defaultCorePoolSize < Runtime.getRuntime().availableProcessors() * 2) { + defaultCorePoolSize = Runtime.getRuntime().availableProcessors() * 2 + } + registration.taskExecutor().corePoolSize(defaultCorePoolSize) + .maxPoolSize(defaultCorePoolSize * 2) + .keepAliveSeconds(60) + } + + @Override + override fun configureClientOutboundChannel(registration: ChannelRegistration) { + var defaultCorePoolSize = webSocketProperties.minThread + if (defaultCorePoolSize < Runtime.getRuntime().availableProcessors() * 2) { + defaultCorePoolSize = Runtime.getRuntime().availableProcessors() * 2 + } + registration.taskExecutor().corePoolSize(defaultCorePoolSize).maxPoolSize(defaultCorePoolSize * 2) + } + + override fun configureWebSocketTransport(registration: WebSocketTransportRegistration) { + registration.addDecoratorFactory(wsHandlerDecoratorFactory()) + super.configureWebSocketTransport(registration) + } + + @Bean + fun wsHandlerDecoratorFactory(): SessionWebSocketHandlerDecoratorFactory { + return SessionWebSocketHandlerDecoratorFactory( + websocketService = websocketService, + authenticationManager = authenticationManager, + jwtAuthProperties = jwtAuthProperties + ) + } + + @Bean + fun websocketTransferConsumer(transferPushListener: TransferPushListener): Consumer> { + return Consumer { transferPushListener.accept(it) } + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WsThreadPoolTaskExeccutorConfiguration.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WsThreadPoolTaskExeccutorConfiguration.kt new file mode 100644 index 0000000000..9ac531f7d6 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WsThreadPoolTaskExeccutorConfiguration.kt @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.config + +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration +import org.springframework.boot.task.TaskExecutorBuilder +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Lazy +import org.springframework.context.annotation.Primary +import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor + +/** + * Websocket会注册自定义的ThreadPoolTaskExecutor + * + * [org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration.clientInboundChannelExecutor] + * + * [org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration.clientOutboundChannelExecutor] + * + * [org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration.brokerChannelExecutor] + * + * 导致默认的ThreadPoolTaskExecutor不会实例化 + * + * [org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration.applicationTaskExecutor] + * + */ +@Configuration +class WsThreadPoolTaskExeccutorConfiguration { + + @Lazy + @Bean(name = [ + TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME, + AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME + ]) + @Primary + fun applicationTaskExecutor(builder: TaskExecutorBuilder): ThreadPoolTaskExecutor { + return builder.build() + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/constant/WebsocketKeys.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/constant/WebsocketKeys.kt new file mode 100644 index 0000000000..bf0769d39c --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/constant/WebsocketKeys.kt @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.constant + +const val USER_ENDPOINT = "/ws/user" +const val APP_ENDPOINT = "/ws/app" +const val DESKTOP_ENDPOINT = "/ws/desktop" + +const val SESSION_ID = "sessionId" diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/controller/ClipboardController.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/controller/ClipboardController.kt new file mode 100644 index 0000000000..1f929a99e5 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/controller/ClipboardController.kt @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.controller + +import com.tencent.bkrepo.websocket.pojo.fs.CopyPDU +import com.tencent.bkrepo.websocket.pojo.fs.PastePDU +import com.tencent.bkrepo.websocket.service.ClipboardService +import org.springframework.messaging.handler.annotation.MessageMapping +import org.springframework.stereotype.Controller + +@Controller +@MessageMapping("/clipboard") +class ClipboardController( + private val clipboardService: ClipboardService +) { + + @MessageMapping("/copy") + fun copy(copyPDU: CopyPDU) { + clipboardService.copy(copyPDU) + } + + @MessageMapping("/paste") + fun paste(pastePDU: PastePDU) { + clipboardService.paste(pastePDU) + } +} + diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/Dispatcher.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/Dispatcher.kt new file mode 100644 index 0000000000..81c5cb5bd9 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/Dispatcher.kt @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.dispatch + +/** + * 下发接口 + */ +interface Dispatcher { + + fun dispatch(data: T) +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/TransferDispatch.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/TransferDispatch.kt new file mode 100644 index 0000000000..4986de5d81 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/TransferDispatch.kt @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.dispatch + +import com.tencent.bkrepo.common.api.util.toJsonString +import com.tencent.bkrepo.common.stream.event.supplier.MessageSupplier +import com.tencent.bkrepo.websocket.config.WebSocketProperties +import com.tencent.bkrepo.websocket.dispatch.push.TransferPush +import org.springframework.messaging.simp.SimpMessagingTemplate +import org.springframework.stereotype.Component + +@Component +class TransferDispatch( + private val messageSupplier: MessageSupplier, + private val simpMessagingTemplate: SimpMessagingTemplate, + private val webSocketProperties: WebSocketProperties +) : Dispatcher { + override fun dispatch(data: TransferPush) { + if (webSocketProperties.transfer) { + messageSupplier.delegateToSupplier(data, topic = TOPIC) + } else { + simpMessagingTemplate.convertAndSend(data.topic, data.data.toJsonString()) + } + } + + companion object { + private const val TOPIC = "websocket-transfer-out-0" + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/CopyPDUTransferPush.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/CopyPDUTransferPush.kt new file mode 100644 index 0000000000..7ccd8c870a --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/CopyPDUTransferPush.kt @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.dispatch.push + +import com.tencent.bkrepo.websocket.pojo.fs.CopyPDU + +class CopyPDUTransferPush( + copyPDU: CopyPDU, +) : TransferPush( + topic = "/topic/clipboard/copy/${copyPDU.workspaceName}", + data = copyPDU +) diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/PastePDUTransferPush.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/PastePDUTransferPush.kt new file mode 100644 index 0000000000..f6da31f94b --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/PastePDUTransferPush.kt @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.dispatch.push + +import com.tencent.bkrepo.websocket.pojo.fs.PastePDU + +class PastePDUTransferPush( + pastePDU: PastePDU, +) : TransferPush( + topic = "/topic/clipboard/paste/${pastePDU.workspaceName}", + data = pastePDU +) diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/TransferPush.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/TransferPush.kt new file mode 100644 index 0000000000..450654566f --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/dispatch/push/TransferPush.kt @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.dispatch.push + +open class TransferPush( + open val topic: String, + open val data: Any +) diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/exception/WebsocketExceptionHandler.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/exception/WebsocketExceptionHandler.kt new file mode 100644 index 0000000000..60ae6dbb17 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/exception/WebsocketExceptionHandler.kt @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.exception + +import com.tencent.bkrepo.common.api.exception.ErrorCodeException +import com.tencent.bkrepo.common.api.pojo.Response +import com.tencent.bkrepo.common.service.exception.AbstractExceptionHandler +import org.springframework.messaging.handler.annotation.MessageExceptionHandler +import org.springframework.web.bind.annotation.RestControllerAdvice + +@RestControllerAdvice +class WebsocketExceptionHandler : AbstractExceptionHandler() { + + @MessageExceptionHandler(ErrorCodeException::class) + fun handleException(exception: ErrorCodeException): Response { + return response(exception) + } + + @MessageExceptionHandler(Exception::class) + fun handleException(exception: Exception): Response { + return response(exception) + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionHandler.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionHandler.kt new file mode 100644 index 0000000000..86e6ed35d5 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionHandler.kt @@ -0,0 +1,131 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.handler + +import com.tencent.bkrepo.common.api.constant.AUTH_HEADER_UID +import com.tencent.bkrepo.common.api.constant.CharPool.COLON +import com.tencent.bkrepo.common.api.constant.HttpHeaders +import com.tencent.bkrepo.common.api.constant.PLATFORM_AUTH_PREFIX +import com.tencent.bkrepo.common.api.constant.PLATFORM_KEY +import com.tencent.bkrepo.common.api.constant.USER_KEY +import com.tencent.bkrepo.common.artifact.stream.closeQuietly +import com.tencent.bkrepo.common.security.exception.AuthenticationException +import com.tencent.bkrepo.common.security.http.jwt.JwtAuthProperties +import com.tencent.bkrepo.common.security.manager.AuthenticationManager +import com.tencent.bkrepo.common.security.util.JwtUtils +import com.tencent.bkrepo.websocket.constant.APP_ENDPOINT +import com.tencent.bkrepo.websocket.constant.SESSION_ID +import com.tencent.bkrepo.websocket.constant.USER_ENDPOINT +import com.tencent.bkrepo.websocket.service.WebsocketService +import com.tencent.bkrepo.websocket.util.HostUtils +import io.jsonwebtoken.ExpiredJwtException +import io.jsonwebtoken.MalformedJwtException +import io.jsonwebtoken.UnsupportedJwtException +import io.jsonwebtoken.security.SignatureException +import org.slf4j.LoggerFactory +import org.springframework.web.socket.CloseStatus +import org.springframework.web.socket.WebSocketHandler +import org.springframework.web.socket.WebSocketSession +import org.springframework.web.socket.handler.WebSocketHandlerDecorator +import java.util.Base64 + +class SessionHandler( + delegate: WebSocketHandler, + private val websocketService: WebsocketService, + private val authenticationManager: AuthenticationManager, + jwtProperties: JwtAuthProperties +) : WebSocketHandlerDecorator(delegate) { + + private val signingKey = JwtUtils.createSigningKey(jwtProperties.secretKey) + + // 链接关闭记录去除session + override fun afterConnectionClosed(session: WebSocketSession, closeStatus: CloseStatus) { + val uri = session.uri + if (closeStatus.code != CloseStatus.NORMAL.code && closeStatus.code != CloseStatus.PROTOCOL_ERROR.code) { + logger.warn("websocket close abnormal, [$closeStatus] [${session.uri}] [${session.remoteAddress}]") + } + val sessionId = HostUtils.getRealSession(session.uri?.query) + if (sessionId.isNullOrEmpty()) { + logger.warn("connection closed can not find sessionId, $uri| ${session.remoteAddress}") + super.afterConnectionClosed(session, closeStatus) + } + websocketService.removeCacheSession(sessionId!!) + + super.afterConnectionClosed(session, closeStatus) + } + + override fun afterConnectionEstablished(session: WebSocketSession) { + val uri = session.uri + val remoteAddr = session.remoteAddress + val sessionId = HostUtils.getRealSession(uri?.query) + try { + authorization(session) + } catch (e: Exception) { + val authException = e is AuthenticationException || e is ExpiredJwtException || + e is UnsupportedJwtException || e is MalformedJwtException || + e is SignatureException || e is IllegalArgumentException + if (authException) { + logger.info("auth failed: |$sessionId| $uri | $remoteAddr | ${e.message}") + session.closeQuietly() + } else { + throw e + } + } + } + + private fun authorization(session: WebSocketSession) { + val uri = session.uri + val remoteId = session.remoteAddress + val sessionId = HostUtils.getRealSession(uri?.query) + when { + uri == null -> throw AuthenticationException("uri is null") + uri.path.startsWith(USER_ENDPOINT) -> { + val platformToken = session.handshakeHeaders[HttpHeaders.AUTHORIZATION]?.firstOrNull()?.toString() + ?.removePrefix(PLATFORM_AUTH_PREFIX) ?: throw AuthenticationException("platform credential is null") + val (accessKey, secretKey) = String(Base64.getDecoder().decode(platformToken)).split(COLON) + val appId = authenticationManager.checkPlatformAccount(accessKey, secretKey) + session.attributes[PLATFORM_KEY] = appId + session.attributes[USER_KEY] = session.handshakeHeaders[AUTH_HEADER_UID] + } + uri.path.startsWith(APP_ENDPOINT) -> { + val token = session.handshakeHeaders[HttpHeaders.AUTHORIZATION]?.firstOrNull().orEmpty() + val claims = JwtUtils.validateToken(signingKey, token).body + session.attributes[USER_KEY] = claims.subject + } + else -> throw AuthenticationException("invalid uri") + } + websocketService.addCacheSession(sessionId!!) + session.attributes[SESSION_ID] = sessionId + logger.info("connection success: |$sessionId| $uri | $remoteId | ${session.attributes[USER_KEY]} ") + super.afterConnectionEstablished(session) + } + + companion object { + private val logger = LoggerFactory.getLogger(SessionHandler::class.java) + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionWebSocketHandlerDecoratorFactory.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionWebSocketHandlerDecoratorFactory.kt new file mode 100644 index 0000000000..6b1a45a695 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionWebSocketHandlerDecoratorFactory.kt @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.handler + +import com.tencent.bkrepo.common.security.http.jwt.JwtAuthProperties +import com.tencent.bkrepo.common.security.manager.AuthenticationManager +import com.tencent.bkrepo.websocket.service.WebsocketService +import org.springframework.web.socket.WebSocketHandler +import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory + +class SessionWebSocketHandlerDecoratorFactory ( + private val websocketService: WebsocketService, + private val authenticationManager: AuthenticationManager, + private val jwtAuthProperties: JwtAuthProperties, +) : WebSocketHandlerDecoratorFactory { + + override fun decorate(handler: WebSocketHandler): WebSocketHandler { + return SessionHandler(handler, websocketService, authenticationManager, jwtAuthProperties) + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/listener/TransferPushListener.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/listener/TransferPushListener.kt new file mode 100644 index 0000000000..1a21c7f3d1 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/listener/TransferPushListener.kt @@ -0,0 +1,51 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.listener + +import com.tencent.bkrepo.common.api.util.toJsonString +import com.tencent.bkrepo.websocket.dispatch.push.TransferPush +import org.slf4j.LoggerFactory +import org.springframework.messaging.Message +import org.springframework.messaging.simp.SimpMessagingTemplate +import org.springframework.stereotype.Component + +@Component +class TransferPushListener( + private val simpMessagingTemplate: SimpMessagingTemplate +) { + + fun accept(message: Message) { + logger.debug(message.payload.toString()) + val transferPush = message.payload + simpMessagingTemplate.convertAndSend(transferPush.topic, transferPush.data.toJsonString()) + } + + companion object { + private val logger = LoggerFactory.getLogger(TransferPushListener::class.java) + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/pojo/fs/CopyPDU.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/pojo/fs/CopyPDU.kt new file mode 100644 index 0000000000..01da102c98 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/pojo/fs/CopyPDU.kt @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.pojo.fs + +/** + * 复制协议数据单元 + */ +data class CopyPDU( + val projectId: String, + val userId: String, + val workspaceName: String, + val files: Map, + val timestamp: Long, + val dstPath: String? = null +) diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/pojo/fs/PastePDU.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/pojo/fs/PastePDU.kt new file mode 100644 index 0000000000..f295772852 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/pojo/fs/PastePDU.kt @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.pojo.fs + +/** + * 粘贴协议数据单元 + */ +data class PastePDU( + val workspaceName: String, + val timestamp: Long, +) diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/service/ClipboardService.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/service/ClipboardService.kt new file mode 100644 index 0000000000..641c2b68fb --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/service/ClipboardService.kt @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.service + +import com.tencent.bkrepo.websocket.dispatch.TransferDispatch +import com.tencent.bkrepo.websocket.dispatch.push.CopyPDUTransferPush +import com.tencent.bkrepo.websocket.dispatch.push.PastePDUTransferPush +import com.tencent.bkrepo.websocket.pojo.fs.CopyPDU +import com.tencent.bkrepo.websocket.pojo.fs.PastePDU +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service + +@Service +class ClipboardService( + private val transferDispatch: TransferDispatch +) { + + fun copy(copyPDU: CopyPDU) { + logger.info("CopyPDU: $copyPDU") + val copyPDUTransferPush = CopyPDUTransferPush(copyPDU) + transferDispatch.dispatch(copyPDUTransferPush) + } + + fun paste(pastePDU: PastePDU) { + logger.info("PastePDU: $pastePDU") + val pastePDUTransferPush = PastePDUTransferPush(pastePDU) + transferDispatch.dispatch(pastePDUTransferPush) + } + + companion object { + private val logger = LoggerFactory.getLogger(ClipboardService::class.java) + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/service/WebsocketService.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/service/WebsocketService.kt new file mode 100644 index 0000000000..559f495445 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/service/WebsocketService.kt @@ -0,0 +1,63 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.service + +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import java.util.Collections + +@Service +class WebsocketService { + private val cacheSessionList = Collections.synchronizedList(mutableListOf()) + + fun addCacheSession(sessionId: String) { + if (cacheSessionList.contains(sessionId)) { + logger.warn("this session[$sessionId] already in cacheSession") + return + } + cacheSessionList.add(sessionId) + } + + // 清楚实例内部缓存的session + fun removeCacheSession(sessionId: String) { + cacheSessionList.remove(sessionId) + } + + // 判断获取到的session是否由该实例持有 + fun isCacheSession(sessionId: String): Boolean { + if (cacheSessionList.contains(sessionId)) { + logger.debug("sessionId[$sessionId] is in this host") + return true + } + return false + } + + companion object { + private val logger = LoggerFactory.getLogger(WebsocketService::class.java) + } +} diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/util/HostUtils.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/util/HostUtils.kt new file mode 100644 index 0000000000..19a64fdc90 --- /dev/null +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/util/HostUtils.kt @@ -0,0 +1,84 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket.util + +import org.slf4j.LoggerFactory +import java.net.DatagramSocket +import java.net.InetAddress +import java.net.URL + +object HostUtils { + + fun getHostIp(gateway: String?): String { + try { + val localHost = InetAddress.getLocalHost() + return if (localHost.isLoopbackAddress) { + getFromUDP(gateway) ?: DEFAULT_IP + } else { + localHost.hostAddress + } + } catch (e: Throwable) { + logger.warn("Fail to get local host ip", e) + try { + return getFromUDP(gateway) ?: DEFAULT_IP + } catch (t: Throwable) { + logger.warn("Fail to use socket to get the localhost host") + } + } + return DEFAULT_IP + } + + private fun getFromUDP(gateway: String?): String? { + if (gateway.isNullOrBlank()) { + return null + } + + val gatewayHost = try { + val url = URL(gateway) + url.host + } catch (t: Throwable) { + logger.warn("Fail to get the gateway host", t) + return null + } + + DatagramSocket().use { socket -> + socket.connect(InetAddress.getByName(gatewayHost), 10002) + return socket.localAddress.hostAddress + } + } + + fun getRealSession(query: String?): String? { + if (query.isNullOrEmpty()) { + return null + } + return query.substringAfter("sessionId=").substringBefore("&t=") + } + + private const val DEFAULT_IP = "127.0.0.1" + private val logger = LoggerFactory.getLogger(javaClass) +} diff --git a/src/backend/websocket/boot-websocket/build.gradle.kts b/src/backend/websocket/boot-websocket/build.gradle.kts new file mode 100644 index 0000000000..49bd2145ce --- /dev/null +++ b/src/backend/websocket/boot-websocket/build.gradle.kts @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED " AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +dependencies { + implementation(project(":websocket:biz-websocket")) +} diff --git a/src/backend/websocket/boot-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/WebsocketApplication.kt b/src/backend/websocket/boot-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/WebsocketApplication.kt new file mode 100644 index 0000000000..5a71325d24 --- /dev/null +++ b/src/backend/websocket/boot-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/WebsocketApplication.kt @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.bkrepo.websocket + +import com.tencent.bkrepo.common.service.condition.MicroService +import org.springframework.boot.runApplication + +/** + * Websocket微服务启动类 + */ +@MicroService +class WebsocketApplication + +fun main(args: Array) { + runApplication(*args) +} diff --git a/src/backend/websocket/boot-websocket/src/main/resources/bootstrap.yml b/src/backend/websocket/boot-websocket/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000..1fcf7233d0 --- /dev/null +++ b/src/backend/websocket/boot-websocket/src/main/resources/bootstrap.yml @@ -0,0 +1,4 @@ +spring.application.name: websocket +server.port: 25914 + +group: ${spring.cloud.consul.discovery.instance-id} diff --git a/src/backend/websocket/build.gradle.kts b/src/backend/websocket/build.gradle.kts new file mode 100644 index 0000000000..aa1c8e01b8 --- /dev/null +++ b/src/backend/websocket/build.gradle.kts @@ -0,0 +1,30 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED " AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ diff --git a/src/gateway/auth.conf b/src/gateway/auth.conf index ff05036afc..7e66552fe0 100644 --- a/src/gateway/auth.conf +++ b/src/gateway/auth.conf @@ -5,3 +5,9 @@ internal; content_by_lua_file 'conf/lua/auth/auth_web.lua'; } + + # websocket验证身份 + location = /auth/websocket { + internal; + content_by_lua_file 'conf/lua/auth/auth_websocket.lua'; + } \ No newline at end of file diff --git a/src/gateway/lua/auth/auth_web.lua b/src/gateway/lua/auth/auth_web.lua index 3471d42bea..661ba3bc18 100644 --- a/src/gateway/lua/auth/auth_web.lua +++ b/src/gateway/lua/auth/auth_web.lua @@ -52,6 +52,9 @@ elseif config.auth_mode == "ticket" then local bk_ticket = cookieUtil:get_cookie("bk_ticket") if bk_ticket == nil then bk_ticket = ngx.var.http_x_devops_bk_ticket + if bk_ticket == nil then + bk_ticket = urlUtil:parseUrl(ngx.var.request_uri)["x-devops-bk-ticket"] + end if bk_ticket == nil then ngx.exit(401) return @@ -63,6 +66,9 @@ elseif config.auth_mode == "odc" then local bk_token = cookieUtil:get_cookie("bk_token") if bk_token == nil then bk_token = ngx.var.http_x_devops_bk_token + if bk_token == nil then + bk_token = urlUtil:parseUrl(ngx.var.request_uri)["x-devops-bk-token"] + end if bk_token == nil then ngx.exit(401) return diff --git a/src/gateway/lua/auth/auth_websocket.lua b/src/gateway/lua/auth/auth_websocket.lua new file mode 100644 index 0000000000..39a6e32ad8 --- /dev/null +++ b/src/gateway/lua/auth/auth_websocket.lua @@ -0,0 +1,55 @@ +--[[ +Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + +Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + +BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + +A copy of the MIT License is included in this file. + + +Terms of the MIT License: +--------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]] + +--- 获取Url请求参数中bk_token 和 bk_ticket + +local token, username + +local bk_ticket = urlUtil:parseUrl(ngx.var.request_uri)["x-devops-bk-ticket"] +local bk_token = urlUtil:parseUrl(ngx.var.request_uri)["x-devops-bk-token"] +local platform_token = ngx.var.http_authorization + +if platform_token ~= nil and string.find(string.lower(platform_token), "^platform") then + ngx.header["x-bkrepo-authorization"] = platform_token + ngx.header["x-bkrepo-uid"] = ngx.var.http_x_bkrepo_uid + ngx.exit(200) + return +end + +if bk_ticket == nil and bk_token == nil then + ngx.exit(401) + return +end + +if bk_ticket ~= nil then + username = oauthUtil:verify_ticket(bk_ticket, "ticket") + token = bk_ticket +end + +if bk_token ~= nil then + username = oauthUtil:verify_tai_token(bk_token) + token = bk_token +end + +--- 设置用户信息 +ngx.header["x-bkrepo-authorization"] = config.bkrepo.authorization +ngx.header["x-bkrepo-uid"] = username +ngx.header["x-bkrepo-bk-token"] = token +ngx.header["x-bkrepo-access-token"] = token +ngx.exit(200) diff --git a/src/gateway/lua/router_srv.lua b/src/gateway/lua/router_srv.lua index d27d6f3446..7eb361df0e 100644 --- a/src/gateway/lua/router_srv.lua +++ b/src/gateway/lua/router_srv.lua @@ -20,7 +20,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI -- 访问限制微服务 -- local allow_services = { "auth", "repository", "generic", "docker", "oci", "maven", "job", "helm", "pypi", "opdata", "rpm", "s3", "git", "npm", "fs-server", "analyst", - "replication", "git", "nuget", "composer", "media", "ddc", "conan", "job-schedule" } + "replication", "git", "nuget", "composer", "media", "ddc", "conan", "job-schedule", "websocket" } local service_name = ngx.var.service if not arrayUtil:isInArray(service_name, allow_services) then diff --git a/src/gateway/vhosts/bkrepo.websocket.conf b/src/gateway/vhosts/bkrepo.websocket.conf new file mode 100644 index 0000000000..c9afe23b3f --- /dev/null +++ b/src/gateway/vhosts/bkrepo.websocket.conf @@ -0,0 +1,47 @@ +location ~ (/web|)/websocket/(ws/user/.*|ws/desktop/.*) { + header_filter_by_lua_file 'conf/lua/cors_filter.lua'; + auth_request /auth/websocket; + # 设置auth的变量 + auth_request_set $uid $sent_http_x_bkrepo_uid; + auth_request_set $accessToken $sent_http_x_bkrepo_access_token; + auth_request_set $bk_token $sent_http_x_bkrepo_bk_token; + auth_request_set $authorization $sent_http_x_bkrepo_authorization; + + set $service "websocket"; + set $path $2; + set $target ''; + + access_by_lua_file 'conf/lua/router_srv.lua'; + proxy_set_header X-BKREPO-UID $uid; + proxy_set_header X-DEVOPS-BK-TOKEN $bk_token; + proxy_set_header X-DEVOPS-BK-TICKET $bk_token; + proxy_set_header X-DEVOPS-ACCESS-TOKEN $accessToken; + proxy_set_header authorization $authorization; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + proxy_http_version 1.1; + proxy_pass http://$target/$path?$args; +} + +location ~ /websocket/(ws/app.*) { + set $service "websocket"; + set $path $1; + set $target ''; + + access_by_lua_file 'conf/lua/router_srv.lua'; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + proxy_set_header Upgrade "websocket"; + proxy_set_header Connection "Upgrade"; + proxy_http_version 1.1; + proxy_pass http://$target/$path?$args; +} diff --git a/support-files/templates/gateway#vhosts#bkrepo.server.conf b/support-files/templates/gateway#vhosts#bkrepo.server.conf index e7d1278bc4..d0c1864a47 100644 --- a/support-files/templates/gateway#vhosts#bkrepo.server.conf +++ b/support-files/templates/gateway#vhosts#bkrepo.server.conf @@ -41,6 +41,9 @@ server { # web接口网关路径 include vhosts/bkrepo.web.conf; + # websocket网关路径 + include vhosts/bkrepo.websocket.conf; + # 后台的网关路径 include vhosts/bkrepo.backend.conf; } From 01210ad34357516a77cc616edaae15054ee55eb0 Mon Sep 17 00:00:00 2001 From: felixncheng Date: Thu, 21 Nov 2024 10:22:30 +0800 Subject: [PATCH 05/24] =?UTF-8?q?bug:=20=E4=BF=AE=E5=A4=8DGitRepoIntercept?= =?UTF-8?q?or=E4=B8=8D=E7=94=9F=E6=95=88=20#2755?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/bkrepo/git/interceptor/ProxyInterceptor.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/git/biz-git/src/main/kotlin/com/tencent/bkrepo/git/interceptor/ProxyInterceptor.kt b/src/backend/git/biz-git/src/main/kotlin/com/tencent/bkrepo/git/interceptor/ProxyInterceptor.kt index 4d9ff434e6..7fed0aa58c 100644 --- a/src/backend/git/biz-git/src/main/kotlin/com/tencent/bkrepo/git/interceptor/ProxyInterceptor.kt +++ b/src/backend/git/biz-git/src/main/kotlin/com/tencent/bkrepo/git/interceptor/ProxyInterceptor.kt @@ -22,9 +22,9 @@ class ProxyInterceptor : HandlerInterceptor { override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE) ?: return false - val repo = ArtifactContextHolder.getRepoDetail()!! + val repo = ArtifactContextHolder.getRepoDetailOrNull() // 只有PROXY类型的仓库才进行拦截 - if (repo.category != RepositoryCategory.PROXY) { + if (repo == null || repo.category != RepositoryCategory.PROXY) { return true } val projectId = repo.projectId From ee1d45b3862a4c3222ce1c8fefc8cb755cb4a027 Mon Sep 17 00:00:00 2001 From: kunlongli <16629885+cnlkl@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:59:19 +0800 Subject: [PATCH 06/24] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E6=89=A7=E8=A1=8C=E6=9C=8D=E5=8A=A1=E6=8B=89=E5=8F=96?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E5=A4=B1=E8=B4=A5=20#2766?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复扫描执行服务拉取镜像失败 #2766 * fix: 修复扫描执行服务拉取镜像失败 #2766 * fix: 调整制品分析日志级别 #2766 --- .../com/tencent/bkrepo/analyst/dispatcher/SubtaskPoller.kt | 4 ++-- .../bkrepo/analyst/dispatcher/SubtaskPushDispatcher.kt | 2 +- .../common/analysis/pojo/scanner/utils/DockerUtils.kt | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/dispatcher/SubtaskPoller.kt b/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/dispatcher/SubtaskPoller.kt index 8773f0e455..9248a7c853 100644 --- a/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/dispatcher/SubtaskPoller.kt +++ b/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/dispatcher/SubtaskPoller.kt @@ -69,9 +69,9 @@ open class SubtaskPoller( open fun dispatch() { executionClusterService.list().forEach { executor.execute { - logger.info("cluster [${it.name}] start to dispatch subtask") + logger.debug("cluster [${it.name}] start to dispatch subtask") dispatcherCache.get(it.name).dispatch() - logger.info("cluster [${it.name}] dispatch finished") + logger.debug("cluster [${it.name}] dispatch finished") } } } diff --git a/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/dispatcher/SubtaskPushDispatcher.kt b/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/dispatcher/SubtaskPushDispatcher.kt index d33538a120..321e9f499f 100644 --- a/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/dispatcher/SubtaskPushDispatcher.kt +++ b/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/dispatcher/SubtaskPushDispatcher.kt @@ -36,7 +36,7 @@ abstract class SubtaskPushDispatcher( override fun dispatch() { if (scanService.peek(executionCluster.name) == null) { - logger.info("cluster [${executionCluster.name}] has no subtask to dispatch") + logger.debug("cluster [${executionCluster.name}] has no subtask to dispatch") return } diff --git a/src/backend/common/common-analysis/src/main/kotlin/com/tencent/bkrepo/common/analysis/pojo/scanner/utils/DockerUtils.kt b/src/backend/common/common-analysis/src/main/kotlin/com/tencent/bkrepo/common/analysis/pojo/scanner/utils/DockerUtils.kt index b424947e50..3a4324b16f 100644 --- a/src/backend/common/common-analysis/src/main/kotlin/com/tencent/bkrepo/common/analysis/pojo/scanner/utils/DockerUtils.kt +++ b/src/backend/common/common-analysis/src/main/kotlin/com/tencent/bkrepo/common/analysis/pojo/scanner/utils/DockerUtils.kt @@ -65,9 +65,9 @@ object DockerUtils { password: String?, ) { val images = listImagesCmd().exec() - val exists = images.any { image -> - image.repoTags.any { it == tag } - } + val exists = images?.any { image -> + image.repoTags?.any { it == tag } ?: false + } ?: false if (exists) { return } From f8307c411d31772dff6ee2c43793d549c747dac3 Mon Sep 17 00:00:00 2001 From: kunlongli <16629885+cnlkl@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:59:34 +0800 Subject: [PATCH 07/24] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E6=9C=8D=E5=8A=A1=E8=8E=B7=E5=8F=96=E9=95=9C=E5=83=8F?= =?UTF-8?q?Manifest=E6=96=87=E4=BB=B6=E5=A4=B1=E8=B4=A5=E6=97=B6=E6=8A=9B?= =?UTF-8?q?=E5=87=BA=E7=9A=84=E5=BC=82=E5=B8=B8=20#2778?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analyst/service/impl/TemporaryScanTokenServiceImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/service/impl/TemporaryScanTokenServiceImpl.kt b/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/service/impl/TemporaryScanTokenServiceImpl.kt index aaae9fe479..0c39a5fcb3 100644 --- a/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/service/impl/TemporaryScanTokenServiceImpl.kt +++ b/src/backend/analyst/biz-analyst/src/main/kotlin/com/tencent/bkrepo/analyst/service/impl/TemporaryScanTokenServiceImpl.kt @@ -234,7 +234,7 @@ class TemporaryScanTokenServiceImpl( .build() ) if (nodes.records.isEmpty()) { - throw SystemErrorException(RESOURCE_NOT_FOUND, sha256) + throw ErrorCodeException(RESOURCE_NOT_FOUND, "file[$sha256] of [$projectId:$repoName] not found") } val fullPath = nodes.records[0][NodeDetail::fullPath.name].toString() return nodeService.getNodeDetail(ArtifactInfo(projectId, repoName, fullPath))?.let { node -> From 93848cc609274648d9da8858b004d6b87ea2f8f5 Mon Sep 17 00:00:00 2001 From: zacYL <100330102+zacYL@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:12:51 +0800 Subject: [PATCH 08/24] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8=E4=B8=B4?= =?UTF-8?q?=E6=97=B6token=E6=93=8D=E4=BD=9C=E6=97=B6=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E6=93=8D=E4=BD=9C=E7=94=A8=E6=88=B7,?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=88=A0=E6=8E=89=E5=AE=A1=E8=AE=A1=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E4=B8=ADtoken=E4=BF=A1=E6=81=AF#2777?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 使用临时token操作时设置对应操作用户,同时删掉审计日志中token信息#2777 * feat: 代码调整#2777 * feat: 操作调整#2777 --- .../bkrepo/common/api/constant/Constants.kt | 6 +++++ .../artifact/audit/ActionAuditContent.kt | 6 ++--- .../artifact/audit/BkAuditPostFilter.kt | 13 ++++++++++- .../controller/TemporaryAccessController.kt | 22 ++++++------------- .../generic/service/TemporaryAccessService.kt | 16 +++++++++++++- .../controller/user/UserMetadataController.kt | 16 +++++++------- .../controller/user/UserShareController.kt | 5 ----- .../service/file/impl/ShareServiceImpl.kt | 13 +++++++++-- 8 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/backend/common/common-api/src/main/kotlin/com/tencent/bkrepo/common/api/constant/Constants.kt b/src/backend/common/common-api/src/main/kotlin/com/tencent/bkrepo/common/api/constant/Constants.kt index b38e19b921..6f4344a61c 100644 --- a/src/backend/common/common-api/src/main/kotlin/com/tencent/bkrepo/common/api/constant/Constants.kt +++ b/src/backend/common/common-api/src/main/kotlin/com/tencent/bkrepo/common/api/constant/Constants.kt @@ -167,3 +167,9 @@ const val BKREPO_TRACE = "X-BKREPO-RID" */ const val CODE_PROJECT_PREFIX = "CODE_" const val CLOSED_SOURCE_PREFIX = "CLOSED_SOURCE_" + + +const val AUDITED_UID = "audited_uid" +const val AUDIT_REQUEST_URI = "audit_request_uri" +const val AUDIT_REQUEST_KEY = "http_request" +const val AUDIT_SHARE_USER_ID = "audit_share_user_id" diff --git a/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/audit/ActionAuditContent.kt b/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/audit/ActionAuditContent.kt index 200de35498..add2656c6a 100644 --- a/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/audit/ActionAuditContent.kt +++ b/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/audit/ActionAuditContent.kt @@ -68,9 +68,9 @@ object ActionAuditContent { // 节点 const val NODE_SHARE_CREATE_CONTENT = "create share link for node info $CONTENT_TEMPLATE in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" - const val NODE_SHARE_DOWNLOAD_CONTENT = "download share node $CONTENT_TEMPLATE with token [{{@TOKEN}}] in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" - const val NODE_DOWNLOAD_WITH_TOKEN_CONTENT = "download node $CONTENT_TEMPLATE with token [{{@TOKEN}}] in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" - const val NODE_UPLOAD_WITH_TOKEN_CONTENT = "upload node $CONTENT_TEMPLATE with token [{{@TOKEN}}] in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" + const val NODE_SHARE_DOWNLOAD_CONTENT = "download share node $CONTENT_TEMPLATE with token in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" + const val NODE_DOWNLOAD_WITH_TOKEN_CONTENT = "download node $CONTENT_TEMPLATE with token in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" + const val NODE_UPLOAD_WITH_TOKEN_CONTENT = "upload node $CONTENT_TEMPLATE with token in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" const val NODE_VIEW_CONTENT = "get node info $CONTENT_TEMPLATE in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" const val NODE_CREATE_CONTENT = "create node $CONTENT_TEMPLATE in repo $PROJECT_CODE_CONTENT_TEMPLATE|$REPO_NAME_CONTENT_TEMPLATE" diff --git a/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/audit/BkAuditPostFilter.kt b/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/audit/BkAuditPostFilter.kt index ceced95a67..f4f8aad908 100644 --- a/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/audit/BkAuditPostFilter.kt +++ b/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/audit/BkAuditPostFilter.kt @@ -56,10 +56,21 @@ package com.tencent.bkrepo.common.artifact.audit import com.tencent.bk.audit.filter.AuditPostFilter import com.tencent.bk.audit.model.AuditEvent +import com.tencent.bkrepo.common.api.constant.AUDITED_UID +import com.tencent.bkrepo.common.api.constant.AUDIT_REQUEST_KEY +import com.tencent.bkrepo.common.api.constant.AUDIT_REQUEST_URI +import com.tencent.bkrepo.common.api.constant.AUDIT_SHARE_USER_ID class BkAuditPostFilter: AuditPostFilter { override fun map(auditEvent: AuditEvent): AuditEvent { - auditEvent.scopeType = "project" + auditEvent.scopeType = PROJECT_RESOURCE + // 特殊处理, 使用token下载时下载用户是根据token去判断后塞入httpAttribute中, 初始化时无法获取 + if (auditEvent.extendData.isNullOrEmpty()) return auditEvent + auditEvent.extendData[AUDIT_SHARE_USER_ID] ?: return auditEvent + val auditedUid = auditEvent.extendData[AUDITED_UID]?.toString() + val auditRequestUri = auditEvent.extendData[AUDIT_REQUEST_URI] + auditEvent.username = auditedUid ?: auditEvent.username + auditEvent.addExtendData(AUDIT_REQUEST_KEY, auditRequestUri) return auditEvent } } \ No newline at end of file diff --git a/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/controller/TemporaryAccessController.kt b/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/controller/TemporaryAccessController.kt index 661a010118..7e6f9fcbd6 100644 --- a/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/controller/TemporaryAccessController.kt +++ b/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/controller/TemporaryAccessController.kt @@ -31,7 +31,6 @@ import com.tencent.bk.audit.annotations.ActionAuditRecord import com.tencent.bk.audit.annotations.AuditAttribute import com.tencent.bk.audit.annotations.AuditEntry import com.tencent.bk.audit.annotations.AuditInstanceRecord -import com.tencent.bk.audit.context.ActionAuditContext import com.tencent.bkrepo.auth.pojo.enums.PermissionAction import com.tencent.bkrepo.auth.pojo.token.TemporaryTokenCreateRequest import com.tencent.bkrepo.auth.pojo.token.TokenType @@ -41,13 +40,13 @@ import com.tencent.bkrepo.common.api.message.CommonMessageCode import com.tencent.bkrepo.common.api.pojo.Response import com.tencent.bkrepo.common.artifact.api.ArtifactFile import com.tencent.bkrepo.common.artifact.api.ArtifactPathVariable -import com.tencent.bkrepo.common.artifact.metrics.ChunkArtifactTransferMetrics -import com.tencent.bkrepo.common.artifact.router.Router -import com.tencent.bkrepo.common.metadata.permission.PermissionManager import com.tencent.bkrepo.common.artifact.audit.ActionAuditContent +import com.tencent.bkrepo.common.artifact.audit.NODE_CREATE_ACTION import com.tencent.bkrepo.common.artifact.audit.NODE_DOWNLOAD_ACTION import com.tencent.bkrepo.common.artifact.audit.NODE_RESOURCE -import com.tencent.bkrepo.common.artifact.audit.NODE_CREATE_ACTION +import com.tencent.bkrepo.common.artifact.metrics.ChunkArtifactTransferMetrics +import com.tencent.bkrepo.common.artifact.router.Router +import com.tencent.bkrepo.common.metadata.permission.PermissionManager import com.tencent.bkrepo.common.security.permission.Principal import com.tencent.bkrepo.common.security.permission.PrincipalType import com.tencent.bkrepo.common.service.util.HttpContextHolder @@ -131,10 +130,6 @@ class TemporaryAccessController( AuditAttribute( name = ActionAuditContent.REPO_NAME_TEMPLATE, value = "#artifactInfo?.repoName" - ), - AuditAttribute( - name = ActionAuditContent.TOKEN_TEMPLATE, - value = "#token" ) ], scopeId = "#artifactInfo?.projectId", @@ -151,7 +146,6 @@ class TemporaryAccessController( artifactInfo: GenericArtifactInfo ) { val downloadUser = downloadUserId ?: userId - ActionAuditContext.current().addExtendData("downloadUser", downloadUser) val tokenInfo = temporaryAccessService.validateToken(token, artifactInfo, TokenType.DOWNLOAD) temporaryAccessService.downloadByShare(downloadUser, tokenInfo.createdBy, artifactInfo) temporaryAccessService.decrementPermits(tokenInfo) @@ -170,8 +164,7 @@ class TemporaryAccessController( ), attributes = [ AuditAttribute(name = ActionAuditContent.PROJECT_CODE_TEMPLATE, value = "#artifactInfo?.projectId"), - AuditAttribute(name = ActionAuditContent.REPO_NAME_TEMPLATE, value = "#artifactInfo?.repoName"), - AuditAttribute(name = ActionAuditContent.TOKEN_TEMPLATE, value = "#token") + AuditAttribute(name = ActionAuditContent.REPO_NAME_TEMPLATE, value = "#artifactInfo?.repoName") ], scopeId = "#artifactInfo?.projectId", content = ActionAuditContent.NODE_DOWNLOAD_WITH_TOKEN_CONTENT @@ -200,9 +193,8 @@ class TemporaryAccessController( ), attributes = [ AuditAttribute(name = ActionAuditContent.PROJECT_CODE_TEMPLATE, value = "#artifactInfo?.projectId"), - AuditAttribute(name = ActionAuditContent.REPO_NAME_TEMPLATE, value = "#artifactInfo?.repoName"), - AuditAttribute(name = ActionAuditContent.TOKEN_TEMPLATE, value = "#token") - ], + AuditAttribute(name = ActionAuditContent.REPO_NAME_TEMPLATE, value = "#artifactInfo?.repoName") + ], scopeId = "#artifactInfo?.projectId", content = ActionAuditContent.NODE_UPLOAD_WITH_TOKEN_CONTENT ) diff --git a/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/service/TemporaryAccessService.kt b/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/service/TemporaryAccessService.kt index f9fea00d69..0c0096e8a2 100644 --- a/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/service/TemporaryAccessService.kt +++ b/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/service/TemporaryAccessService.kt @@ -31,12 +31,16 @@ package com.tencent.bkrepo.generic.service +import com.tencent.bk.audit.context.ActionAuditContext import com.tencent.bkrepo.auth.api.ServiceTemporaryTokenClient import com.tencent.bkrepo.auth.pojo.enums.PermissionAction import com.tencent.bkrepo.auth.pojo.token.TemporaryTokenCreateRequest import com.tencent.bkrepo.auth.pojo.token.TemporaryTokenInfo import com.tencent.bkrepo.auth.pojo.token.TokenType import com.tencent.bkrepo.common.api.constant.ANONYMOUS_USER +import com.tencent.bkrepo.common.api.constant.AUDITED_UID +import com.tencent.bkrepo.common.api.constant.AUDIT_REQUEST_URI +import com.tencent.bkrepo.common.api.constant.AUDIT_SHARE_USER_ID import com.tencent.bkrepo.common.api.constant.AUTH_HEADER_UID import com.tencent.bkrepo.common.api.constant.HttpStatus import com.tencent.bkrepo.common.api.constant.StringPool @@ -60,8 +64,8 @@ import com.tencent.bkrepo.common.artifact.path.PathUtils import com.tencent.bkrepo.common.artifact.repository.context.ArtifactContextHolder import com.tencent.bkrepo.common.artifact.repository.context.ArtifactDownloadContext import com.tencent.bkrepo.common.artifact.repository.context.ArtifactUploadContext -import com.tencent.bkrepo.common.metadata.service.repo.RepositoryService import com.tencent.bkrepo.common.metadata.permission.PermissionManager +import com.tencent.bkrepo.common.metadata.service.repo.RepositoryService import com.tencent.bkrepo.common.security.util.SecurityUtils import com.tencent.bkrepo.common.service.util.HeaderUtils import com.tencent.bkrepo.common.service.util.HttpContextHolder @@ -137,6 +141,11 @@ class TemporaryAccessService( ?: throw ErrorCodeException(ArtifactMessageCode.REPOSITORY_NOT_FOUND, repoName) val context = ArtifactDownloadContext(repo = repo, userId = downloadUser) HttpContextHolder.getRequest().setAttribute(USER_KEY, downloadUser) + ActionAuditContext.current().addExtendData(AUDITED_UID, downloadUser) + ActionAuditContext.current().addExtendData( + AUDIT_REQUEST_URI, "{${HttpContextHolder.getRequestOrNull()?.requestURI}}" + ) + ActionAuditContext.current().addExtendData(AUDIT_SHARE_USER_ID, shareBy) context.shareUserId = shareBy val repository = ArtifactContextHolder.getRepository(context.repositoryDetail.category) repository.download(context) @@ -449,6 +458,11 @@ class TemporaryAccessService( } // 设置审计uid到session中 HttpContextHolder.getRequestOrNull()?.setAttribute(USER_KEY, auditedUid) + ActionAuditContext.current().addExtendData(AUDITED_UID, auditedUid) + ActionAuditContext.current().addExtendData( + AUDIT_REQUEST_URI, "{${HttpContextHolder.getRequestOrNull()?.requestURI}}" + ) + ActionAuditContext.current().addExtendData(AUDIT_SHARE_USER_ID, tokenInfo.createdBy) // 校验ip授权 val clientIp = HttpContextHolder.getClientAddress() if (tokenInfo.authorizedIpList.isNotEmpty() && clientIp !in tokenInfo.authorizedIpList) { diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserMetadataController.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserMetadataController.kt index 7fccda0d93..2a792cd9f4 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserMetadataController.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserMetadataController.kt @@ -43,9 +43,10 @@ import com.tencent.bkrepo.common.artifact.api.ArtifactInfo import com.tencent.bkrepo.common.artifact.api.ArtifactPathVariable import com.tencent.bkrepo.common.artifact.api.DefaultArtifactInfo.Companion.DEFAULT_MAPPING_URI import com.tencent.bkrepo.common.artifact.audit.ActionAuditContent +import com.tencent.bkrepo.common.artifact.audit.NODE_EDIT_ACTION import com.tencent.bkrepo.common.artifact.audit.NODE_RESOURCE import com.tencent.bkrepo.common.artifact.audit.NODE_VIEW_ACTION -import com.tencent.bkrepo.common.artifact.audit.REPO_EDIT_ACTION +import com.tencent.bkrepo.common.metadata.service.metadata.MetadataService import com.tencent.bkrepo.common.security.permission.Permission import com.tencent.bkrepo.common.security.util.SecurityUtils import com.tencent.bkrepo.common.service.util.ResponseBuilder @@ -53,7 +54,6 @@ import com.tencent.bkrepo.repository.pojo.metadata.MetadataDeleteRequest import com.tencent.bkrepo.repository.pojo.metadata.MetadataSaveRequest import com.tencent.bkrepo.repository.pojo.metadata.UserMetadataDeleteRequest import com.tencent.bkrepo.repository.pojo.metadata.UserMetadataSaveRequest -import com.tencent.bkrepo.common.metadata.service.metadata.MetadataService import io.swagger.annotations.Api import io.swagger.annotations.ApiOperation import org.springframework.web.bind.annotation.DeleteMapping @@ -104,10 +104,10 @@ class UserMetadataController( } @AuditEntry( - actionId = REPO_EDIT_ACTION + actionId = NODE_EDIT_ACTION ) @ActionAuditRecord( - actionId = REPO_EDIT_ACTION, + actionId = NODE_EDIT_ACTION, instance = AuditInstanceRecord( resourceType = NODE_RESOURCE, instanceIds = "#artifactInfo?.getArtifactFullPath()", @@ -144,10 +144,10 @@ class UserMetadataController( } @AuditEntry( - actionId = REPO_EDIT_ACTION + actionId = NODE_EDIT_ACTION ) @ActionAuditRecord( - actionId = REPO_EDIT_ACTION, + actionId = NODE_EDIT_ACTION, instance = AuditInstanceRecord( resourceType = NODE_RESOURCE, instanceIds = "#artifactInfo?.getArtifactFullPath()", @@ -182,10 +182,10 @@ class UserMetadataController( } @AuditEntry( - actionId = REPO_EDIT_ACTION + actionId = NODE_EDIT_ACTION ) @ActionAuditRecord( - actionId = REPO_EDIT_ACTION, + actionId = NODE_EDIT_ACTION, instance = AuditInstanceRecord( resourceType = NODE_RESOURCE, instanceIds = "#artifactInfo?.getArtifactFullPath()", diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserShareController.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserShareController.kt index 6c7a04552a..c0152d8bca 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserShareController.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserShareController.kt @@ -165,10 +165,6 @@ class UserShareController( AuditAttribute( name = ActionAuditContent.REPO_NAME_TEMPLATE, value = "#artifactInfo?.repoName" - ), - AuditAttribute( - name = ActionAuditContent.TOKEN_TEMPLATE, - value = "#token" ) ], scopeId = "#artifactInfo?.projectId", @@ -183,7 +179,6 @@ class UserShareController( @ArtifactPathVariable artifactInfo: ArtifactInfo ) { val downloadUser = downloadUserId ?: userId - ActionAuditContext.current().addExtendData("downloadUser", downloadUser) shareService.download(downloadUser, token, artifactInfo) } } diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/impl/ShareServiceImpl.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/impl/ShareServiceImpl.kt index 55342317d6..acf1af7350 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/impl/ShareServiceImpl.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/impl/ShareServiceImpl.kt @@ -31,10 +31,14 @@ package com.tencent.bkrepo.repository.service.file.impl +import com.tencent.bk.audit.context.ActionAuditContext import com.tencent.bkrepo.auth.api.ServiceTemporaryTokenClient import com.tencent.bkrepo.auth.pojo.token.TemporaryTokenCreateRequest import com.tencent.bkrepo.auth.pojo.token.TokenType import com.tencent.bkrepo.common.api.constant.ANONYMOUS_USER +import com.tencent.bkrepo.common.api.constant.AUDITED_UID +import com.tencent.bkrepo.common.api.constant.AUDIT_REQUEST_URI +import com.tencent.bkrepo.common.api.constant.AUDIT_SHARE_USER_ID import com.tencent.bkrepo.common.api.constant.USER_KEY import com.tencent.bkrepo.common.api.exception.ErrorCodeException import com.tencent.bkrepo.common.artifact.api.ArtifactInfo @@ -42,6 +46,8 @@ import com.tencent.bkrepo.common.artifact.exception.NodeNotFoundException import com.tencent.bkrepo.common.artifact.message.ArtifactMessageCode import com.tencent.bkrepo.common.artifact.repository.context.ArtifactContextHolder import com.tencent.bkrepo.common.artifact.repository.context.ArtifactDownloadContext +import com.tencent.bkrepo.common.metadata.service.node.NodeService +import com.tencent.bkrepo.common.metadata.service.repo.RepositoryService import com.tencent.bkrepo.common.security.exception.PermissionException import com.tencent.bkrepo.common.service.cluster.condition.DefaultCondition import com.tencent.bkrepo.common.service.util.HttpContextHolder @@ -49,8 +55,6 @@ import com.tencent.bkrepo.repository.model.TShareRecord import com.tencent.bkrepo.repository.pojo.share.ShareRecordCreateRequest import com.tencent.bkrepo.repository.pojo.share.ShareRecordInfo import com.tencent.bkrepo.repository.service.file.ShareService -import com.tencent.bkrepo.common.metadata.service.node.NodeService -import com.tencent.bkrepo.common.metadata.service.repo.RepositoryService import org.slf4j.LoggerFactory import org.springframework.context.annotation.Conditional import org.springframework.data.mongodb.core.MongoTemplate @@ -144,6 +148,11 @@ class ShareServiceImpl( ?: throw ErrorCodeException(ArtifactMessageCode.REPOSITORY_NOT_FOUND, repoName) val context = ArtifactDownloadContext(repo = repo, userId = userId) HttpContextHolder.getRequest().setAttribute(USER_KEY, downloadUser) + ActionAuditContext.current().addExtendData(AUDITED_UID, downloadUser) + ActionAuditContext.current().addExtendData( + AUDIT_REQUEST_URI, "{${HttpContextHolder.getRequestOrNull()?.requestURI}}" + ) + ActionAuditContext.current().addExtendData(AUDIT_SHARE_USER_ID, shareRecord.createdBy) context.shareUserId = shareRecord.createdBy val repository = ArtifactContextHolder.getRepository(context.repositoryDetail.category) repository.download(context) From c8d8ca1450d89e58106042a7ee94adee4a1de3e2 Mon Sep 17 00:00:00 2001 From: yaoxuwan Date: Mon, 25 Nov 2024 09:57:00 +0800 Subject: [PATCH 09/24] =?UTF-8?q?fix:=20edge=E8=B0=83=E7=94=A8center=20fei?= =?UTF-8?q?gn=E8=AF=B7=E6=B1=82=E8=B6=85=E6=97=B6=E5=90=8E=EF=BC=8Cedge?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E9=80=BB=E8=BE=91=E9=9C=80=E8=A6=81=E7=BB=A7?= =?UTF-8?q?=E7=BB=AD=E6=89=A7=E8=A1=8C=20#2749?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt index 3686de5a29..5eb1954ade 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/util/ClusterUtils.kt @@ -158,6 +158,7 @@ object ClusterUtils { } catch (e: RetryableException) { if (e.message?.contains("Read time out") == true) { logger.info("ignore feign exception: ${e.message}") + return } throw e } From cf0f44bac2f36744f4db59b2ea531f86e7a987ec Mon Sep 17 00:00:00 2001 From: lannoy0523 <46735290+lannoy0523@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:57:48 +0800 Subject: [PATCH 10/24] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E6=89=A7=E8=A1=8C=E9=9B=86=E7=BE=A4=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E9=A1=B5=E9=9D=A2=20#2762?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 增加扫描执行集群配置管理页面 #2762 * feat: 增加扫描执行集群配置管理页面 #2762 * feat: 增加扫描执行集群配置管理页面 #2762 --- .../devops-op/src/api/executionClusters.js | 33 +++ src/frontend/devops-op/src/router/index.js | 25 +- .../components/EditClusterConfigDialog.vue | 276 ++++++++++++++++++ .../src/views/execution-clusters/index.vue | 119 ++++++++ 4 files changed, 441 insertions(+), 12 deletions(-) create mode 100644 src/frontend/devops-op/src/api/executionClusters.js create mode 100644 src/frontend/devops-op/src/views/execution-clusters/components/EditClusterConfigDialog.vue create mode 100644 src/frontend/devops-op/src/views/execution-clusters/index.vue diff --git a/src/frontend/devops-op/src/api/executionClusters.js b/src/frontend/devops-op/src/api/executionClusters.js new file mode 100644 index 0000000000..a5edd0b4b8 --- /dev/null +++ b/src/frontend/devops-op/src/api/executionClusters.js @@ -0,0 +1,33 @@ +import request from '@/utils/request' + +const PREFIX_SERVICES = 'analyst/api/execution/clusters' + +export function clusters() { + return request({ + url: `${PREFIX_SERVICES}`, + method: 'get' + }) +} + +export function update(body) { + return request({ + url: `${PREFIX_SERVICES}/${body.name}`, + method: 'put', + data: body + }) +} + +export function create(body) { + return request({ + url: `${PREFIX_SERVICES}`, + method: 'post', + data: body + }) +} + +export function remove(name) { + return request({ + url: `${PREFIX_SERVICES}/${name}`, + method: 'delete' + }) +} diff --git a/src/frontend/devops-op/src/router/index.js b/src/frontend/devops-op/src/router/index.js index e5e859f09c..fab87ed73c 100644 --- a/src/frontend/devops-op/src/router/index.js +++ b/src/frontend/devops-op/src/router/index.js @@ -26,6 +26,7 @@ export const ROUTER_NAME_FILE_SYSTEM_RECORD = 'FileSystemRecord' export const ROUTER_NAME_REPO_CONFIG = 'RepoConfig' export const ROUTER_NAME_RATE_LIMITER_CONFIG = 'RateLimiterConfig' export const ROUTER_NAME_PRELOAD_CONFIG = 'PreloadConfig' +export const ROUTER_NAME_EXECUTION_CLUSTERS_CONFIG = 'ExecutionClustersConfig' Vue.use(Router) @@ -174,6 +175,12 @@ export const asyncRoutes = [ name: ROUTER_NAME_PROJECT_METRICS, meta: { title: '仓库大小统计', icon: 'file' }, component: () => import('@/views/node/ProjectMetrics') + }, + { + path: 'preloadConfig', + name: ROUTER_NAME_PRELOAD_CONFIG, + meta: { title: '制品预加载配置', icon: 'service-config' }, + component: () => import('@/views/preload/index') } ] }, @@ -261,6 +268,12 @@ export const asyncRoutes = [ name: ROUTER_NAME_PROJECT_SCAN_CONFIGURATIONS, component: () => import('@/views/scan/ProjectScanConfiguration'), meta: { title: '项目配置', icon: 'setting' } + }, + { + path: 'executionClustersConfig', + name: ROUTER_NAME_EXECUTION_CLUSTERS_CONFIG, + meta: { title: '扫描执行集群配置', icon: 'service-config' }, + component: () => import('@/views/execution-clusters/index') } ] }, @@ -327,18 +340,6 @@ export const asyncRoutes = [ } ] }, - { - path: '/preload-config', - component: Layout, - children: [ - { - path: '/', - name: ROUTER_NAME_PRELOAD_CONFIG, - meta: { title: '制品预加载配置', icon: 'service-config' }, - component: () => import('@/views/preload/index') - } - ] - }, // 404 page must be placed at the end !!! { path: '*', redirect: '/404', hidden: true } ] diff --git a/src/frontend/devops-op/src/views/execution-clusters/components/EditClusterConfigDialog.vue b/src/frontend/devops-op/src/views/execution-clusters/components/EditClusterConfigDialog.vue new file mode 100644 index 0000000000..1c5e1b2983 --- /dev/null +++ b/src/frontend/devops-op/src/views/execution-clusters/components/EditClusterConfigDialog.vue @@ -0,0 +1,276 @@ + + + + diff --git a/src/frontend/devops-op/src/views/execution-clusters/index.vue b/src/frontend/devops-op/src/views/execution-clusters/index.vue new file mode 100644 index 0000000000..142f2d8457 --- /dev/null +++ b/src/frontend/devops-op/src/views/execution-clusters/index.vue @@ -0,0 +1,119 @@ + + + + + From 311cb3fe764eafd6339d358feb0a3a1b546ccee7 Mon Sep 17 00:00:00 2001 From: kunlongli <16629885+cnlkl@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:39:55 +0800 Subject: [PATCH 11/24] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=A2=84?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E8=AE=A1=E5=88=92=E7=94=9F=E6=88=90=E6=97=B6?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2node=E8=B6=85=E6=97=B6=20#2658?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复预加载计划生成时查询node超时 #2658 * fix: 修复预加载计划生成时查询node超时 #2658 * fix: 增加预加载策略生成相关debug日志 #2658 --- .../impl/ArtifactPreloadPlanServiceImpl.kt | 18 ++++- .../ArtifactPreloadPlanServiceImplTest.kt | 19 ++--- .../common/metadata/dao/node/NodeDao.kt | 48 +++++++++++ .../service/node/NodeBaseOperation.kt | 12 +++ .../service/node/impl/NodeBaseService.kt | 10 +++ .../mongo/dao/sharding/ShardingMongoDao.kt | 2 +- .../NodeQueryWithoutShardingKeyTest.kt | 79 ++++++++++++------- 7 files changed, 146 insertions(+), 42 deletions(-) diff --git a/src/backend/common/common-artifact/artifact-cache/src/main/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImpl.kt b/src/backend/common/common-artifact/artifact-cache/src/main/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImpl.kt index 55f0ced68b..36850d3289 100644 --- a/src/backend/common/common-artifact/artifact-cache/src/main/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImpl.kt +++ b/src/backend/common/common-artifact/artifact-cache/src/main/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImpl.kt @@ -55,7 +55,6 @@ import com.tencent.bkrepo.common.metadata.service.repo.RepositoryService import com.tencent.bkrepo.common.mongo.dao.util.Pages import com.tencent.bkrepo.repository.pojo.node.NodeDetail import com.tencent.bkrepo.repository.pojo.node.NodeInfo -import com.tencent.bkrepo.repository.pojo.node.NodeListOption import com.tencent.bkrepo.repository.pojo.repo.RepositoryInfo import org.slf4j.LoggerFactory import org.springframework.data.domain.PageRequest @@ -112,13 +111,20 @@ class ArtifactPreloadPlanServiceImpl( if (!properties.enabled) { return } - val option = NodeListOption(pageSize = properties.maxNodes, includeFolder = false) - val res = nodeService.listNodePageBySha256(sha256, option) - val nodes = res.records + val nodes = nodeService.listNodeBySha256( + sha256 = sha256, + limit = properties.maxNodes, + includeMetadata = false, + includeDeleted = false, + tillLimit = false + ) if (nodes.size >= properties.maxNodes) { // 限制查询出来的最大node数量,避免预加载计划创建时间过久 logger.warn("nodes of sha256[$sha256] exceed max page size[${properties.maxNodes}]") return + } else if (nodes.isEmpty()) { + logger.debug("nodes of sha256[$sha256] found") + return } // node属于同一项目仓库的概率较大,缓存避免频繁查询策略 val strategyCache = HashMap>() @@ -127,11 +133,15 @@ class ArtifactPreloadPlanServiceImpl( for (node in nodes) { val repo = repositoryCache.get(buildRepoId(node.projectId, node.repoName)) if (repo.storageCredentialsKey != credentialsKey) { + logger.debug("credentialsKey of repo[${repo.name}] not match dst credentialsKey[${credentialsKey}]") continue } val strategies = strategyCache.getOrPut(buildRepoId(node.projectId, node.repoName)) { strategyService.list(node.projectId, node.repoName) } + if (strategies.isEmpty()) { + logger.debug("preload strategy of repo[${repo.projectId}/${repo.name}] is empty") + } strategies.forEach { strategy -> matchAndGeneratePlan(strategy, node, repo.storageCredentialsKey)?.let { plans.add(it) } } diff --git a/src/backend/common/common-artifact/artifact-cache/src/test/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImplTest.kt b/src/backend/common/common-artifact/artifact-cache/src/test/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImplTest.kt index a08bdddc42..51d518c844 100644 --- a/src/backend/common/common-artifact/artifact-cache/src/test/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImplTest.kt +++ b/src/backend/common/common-artifact/artifact-cache/src/test/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImplTest.kt @@ -55,6 +55,7 @@ import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyString import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull @@ -135,9 +136,9 @@ class ArtifactPreloadPlanServiceImplTest @Autowired constructor( node.copy(fullPath = "test.txt"), node.copy(fullPath = "test2.txt"), ) - whenever(nodeService.listNodePageBySha256(anyString(), any())).thenReturn( - Pages.ofResponse(Pages.ofRequest(0, 2000), nodes.size.toLong(), nodes) - ) + whenever( + nodeService.listNodeBySha256(anyString(), any(), anyBoolean(), anyBoolean(), anyBoolean()) + ).thenReturn(nodes) preloadPlanService.generatePlan(null, UT_SHA256) plans = preloadPlanService.plans(UT_PROJECT_ID, UT_REPO_NAME, Pages.ofRequest(0, 10)).records @@ -177,9 +178,9 @@ class ArtifactPreloadPlanServiceImplTest @Autowired constructor( for (i in 0..1000) { nodes.add(buildNodeInfo()) } - whenever(nodeService.listNodePageBySha256(anyString(), any())).thenReturn( - Pages.ofResponse(Pages.ofRequest(0, 2000), nodes.size.toLong(), nodes) - ) + whenever( + nodeService.listNodeBySha256(anyString(), any(), anyBoolean(), anyBoolean(), anyBoolean()) + ).thenReturn(nodes) preloadPlanService.generatePlan(null, UT_SHA256) val plans = preloadPlanService.plans(UT_PROJECT_ID, UT_REPO_NAME, Pages.ofRequest(0, 10)).records assertEquals(0, plans.size) @@ -218,9 +219,9 @@ class ArtifactPreloadPlanServiceImplTest @Autowired constructor( buildRepo(projectId = projectId, repoName = repoName) ) val nodes = listOf(buildNodeInfo(projectId, repoName)) - whenever(nodeService.listNodePageBySha256(anyString(), any())).thenReturn( - Pages.ofResponse(Pages.ofRequest(0, 20), 1L, nodes) - ) + whenever( + nodeService.listNodeBySha256(anyString(), any(), anyBoolean(), anyBoolean(), anyBoolean()) + ).thenReturn(nodes) } // 构造测试数据 diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/dao/node/NodeDao.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/dao/node/NodeDao.kt index cf85cf3fea..e80e1c5ee7 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/dao/node/NodeDao.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/dao/node/NodeDao.kt @@ -31,6 +31,7 @@ package com.tencent.bkrepo.common.metadata.dao.node +import com.tencent.bkrepo.common.api.constant.DEFAULT_PAGE_SIZE import com.tencent.bkrepo.common.api.constant.StringPool import com.tencent.bkrepo.common.artifact.path.PathUtils import com.tencent.bkrepo.common.metadata.condition.SyncCondition @@ -145,6 +146,53 @@ class NodeDao : HashShardingMongoDao() { return pageWithoutShardingKey(pageRequest, query) } + /** + * 根据[sha256]查询node列表,用于不需要分页的场景提高查询速度 + * + * @param sha256 待查询sha256 + * @param limit 查询选项 + * @param includeMetadata 是否包含元数据 + * @param includeDeleted 是否包含被删除的制品 + * @param tillLimit 为true时将遍历所有分表直到查询到的结果数量达到limit + * + * @return 指定sha256的node列表 + */ + fun listBySha256( + sha256: String, + limit: Int = DEFAULT_PAGE_SIZE, + includeMetadata: Boolean =false, + includeDeleted: Boolean = true, + tillLimit: Boolean = true, + ): List { + // 构造查询条件 + val criteria = where(TNode::sha256).isEqualTo(sha256).and(TNode::folder).isEqualTo(false) + if (!includeDeleted) { + criteria.and(TNode::deleted).isEqualTo(null) + } + val query = Query(criteria) + if (!includeMetadata) { + query.fields().exclude(TNode::metadata.name) + } + + if (shardingCount <= 0 || shardingCount > MAX_SHARDING_COUNT_OF_PAGE_QUERY) { + throw UnsupportedOperationException() + } + + // 遍历所有分表进行查询 + val template = determineMongoTemplate() + val result = ArrayList() + for (sequence in 0 until shardingCount) { + query.limit(limit - result.size) + val collectionName = parseSequenceToCollectionName(sequence) + result.addAll(template.find(query, classType, collectionName)) + if (result.isNotEmpty() && !tillLimit || result.size == limit) { + break + } + } + + return result + } + companion object { fun buildRootNode(projectId: String, repoName: String): TNode { return TNode( diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/node/NodeBaseOperation.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/node/NodeBaseOperation.kt index 8b33618ed1..1fdabd0287 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/node/NodeBaseOperation.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/node/NodeBaseOperation.kt @@ -31,6 +31,7 @@ package com.tencent.bkrepo.common.metadata.service.node +import com.tencent.bkrepo.common.api.constant.DEFAULT_PAGE_SIZE import com.tencent.bkrepo.common.api.pojo.Page import com.tencent.bkrepo.common.artifact.api.ArtifactInfo import com.tencent.bkrepo.repository.pojo.node.NodeDetail @@ -66,6 +67,17 @@ interface NodeBaseOperation { */ fun listNodePageBySha256(sha256: String, option: NodeListOption): Page + /** + * 根据sha256列出指定数量的节点 + */ + fun listNodeBySha256( + sha256: String, + limit: Int = DEFAULT_PAGE_SIZE, + includeMetadata: Boolean =false, + includeDeleted: Boolean = true, + tillLimit: Boolean = true, + ): List + /** * 判断节点是否存在 */ diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/node/impl/NodeBaseService.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/node/impl/NodeBaseService.kt index 7ff238397f..eee31154b0 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/node/impl/NodeBaseService.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/node/impl/NodeBaseService.kt @@ -160,6 +160,16 @@ abstract class NodeBaseService( ) } + override fun listNodeBySha256( + sha256: String, + limit: Int, + includeMetadata: Boolean, + includeDeleted: Boolean, + tillLimit: Boolean + ): List { + return nodeDao.listBySha256(sha256, limit, includeMetadata, includeDeleted, tillLimit).map { convert(it)!! } + } + override fun checkExist(artifact: ArtifactInfo): Boolean { return nodeDao.exists(artifact.projectId, artifact.repoName, artifact.getArtifactFullPath()) } diff --git a/src/backend/common/common-mongo/src/main/kotlin/com/tencent/bkrepo/common/mongo/dao/sharding/ShardingMongoDao.kt b/src/backend/common/common-mongo/src/main/kotlin/com/tencent/bkrepo/common/mongo/dao/sharding/ShardingMongoDao.kt index ab19360ee1..58b66f815f 100644 --- a/src/backend/common/common-mongo/src/main/kotlin/com/tencent/bkrepo/common/mongo/dao/sharding/ShardingMongoDao.kt +++ b/src/backend/common/common-mongo/src/main/kotlin/com/tencent/bkrepo/common/mongo/dao/sharding/ShardingMongoDao.kt @@ -312,6 +312,6 @@ abstract class ShardingMongoDao : AbstractMongoDao() { companion object { private val logger = LoggerFactory.getLogger(ShardingMongoDao::class.java) - private const val MAX_SHARDING_COUNT_OF_PAGE_QUERY = 256 + const val MAX_SHARDING_COUNT_OF_PAGE_QUERY = 256 } } diff --git a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeQueryWithoutShardingKeyTest.kt b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeQueryWithoutShardingKeyTest.kt index b0a5a47d21..2ec4352d0f 100644 --- a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeQueryWithoutShardingKeyTest.kt +++ b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeQueryWithoutShardingKeyTest.kt @@ -40,7 +40,8 @@ import com.tencent.bkrepo.repository.pojo.node.service.NodeCreateRequest import com.tencent.bkrepo.common.metadata.service.node.NodeService import com.tencent.bkrepo.common.metadata.service.project.ProjectService import com.tencent.bkrepo.common.metadata.service.repo.RepositoryService -import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -61,68 +62,90 @@ class NodeQueryWithoutShardingKeyTest @Autowired constructor( private val nodeService: NodeService ) : ServiceBaseTest() { + // 创建测试数据sha256为sha256-0,sha256-1...sha256-8,sha256-9 + // sha256-0和sha256-1有18条数据,其余sha256各有15条数据 + val generateSha256Func = { i: Int -> "sha256-${i % 10}" } + @BeforeAll fun beforeAll() { initMock() + generateTestData(52, generateSha256Func) } @Test @DisplayName("测试按SHA256分页查询") fun testListNodePageBySha256() { - // 创建测试数据sha256为sha256-0,sha256-1...sha256-8,sha256-9 - // sha256-0和sha256-1有18条数据,其余sha256各有15条数据 - val generateSha256Func = { i: Int -> "sha256-${i % 10}" } - generateTestData(52, generateSha256Func) val option = NodeListOption(1, 5, includeMetadata = true, sort = true) // 测试获取不存在的Node列表 nodeService.listNodePageBySha256("notExistsSha256", option).apply { - Assertions.assertEquals(0L, totalRecords) - Assertions.assertEquals(0L, totalPages) - Assertions.assertTrue(records.isEmpty()) + assertEquals(0L, totalRecords) + assertEquals(0L, totalPages) + assertTrue(records.isEmpty()) } // 测试数据量小于pageSize的情况 nodeService.listNodePageBySha256(generateSha256Func(1), option.copy(pageSize = 20)).apply { - Assertions.assertEquals(18, totalRecords) - Assertions.assertEquals(1, totalPages) - Assertions.assertEquals(18, records.size) + assertEquals(18, totalRecords) + assertEquals(1, totalPages) + assertEquals(18, records.size) } // 测试获取数据量等于pageSize的页 nodeService.listNodePageBySha256(generateSha256Func(1), option.copy(pageSize = 4)).apply { - Assertions.assertEquals(18, totalRecords) - Assertions.assertEquals(5, totalPages) - Assertions.assertEquals(4, records.size) + assertEquals(18, totalRecords) + assertEquals(5, totalPages) + assertEquals(4, records.size) } // 测试获数据量小于pageSize的页 nodeService.listNodePageBySha256(generateSha256Func(1), option.copy(pageSize = 4, pageNumber = 5)).apply { - Assertions.assertEquals(18, totalRecords) - Assertions.assertEquals(5, totalPages) - Assertions.assertEquals(2, records.size) + assertEquals(18, totalRecords) + assertEquals(5, totalPages) + assertEquals(2, records.size) } // 测试获取不存在的页 nodeService.listNodePageBySha256(generateSha256Func(1), option.copy(pageSize = 4, pageNumber = 6)).apply { - Assertions.assertEquals(18, totalRecords) - Assertions.assertEquals(5, totalPages) - Assertions.assertEquals(0, records.size) + assertEquals(18, totalRecords) + assertEquals(5, totalPages) + assertEquals(0, records.size) } // 测试分页数据在两个分表的情况,[0,1,2,3,4, 5][6,7,8,9, 10,11][12,13,14,15,16,17] nodeService.listNodePageBySha256(generateSha256Func(1), option.copy(pageSize = 5, pageNumber = 2)).apply { - Assertions.assertEquals(18, totalRecords) - Assertions.assertEquals(4, totalPages) - Assertions.assertEquals(5, records.size) - Assertions.assertEquals(PROJECT_SHARDING_207, records[0].projectId) - Assertions.assertEquals(PROJECT_SHARDING_208, records[1].projectId) - Assertions.assertEquals(PROJECT_SHARDING_208, records[2].projectId) - Assertions.assertEquals(PROJECT_SHARDING_208, records[3].projectId) - Assertions.assertEquals(PROJECT_SHARDING_208, records[4].projectId) + assertEquals(18, totalRecords) + assertEquals(4, totalPages) + assertEquals(5, records.size) + assertEquals(PROJECT_SHARDING_207, records[0].projectId) + assertEquals(PROJECT_SHARDING_208, records[1].projectId) + assertEquals(PROJECT_SHARDING_208, records[2].projectId) + assertEquals(PROJECT_SHARDING_208, records[3].projectId) + assertEquals(PROJECT_SHARDING_208, records[4].projectId) } } + @Test + @DisplayName("测试按SHA256查询") + fun testListNodeBySha256() { + val sha256 = generateSha256Func(1) + + // 测试获取不存在的Node列表 + nodeService.listNodeBySha256("notExistsSha256").apply { assertEquals(0, size) } + + // 测试单表数据量小于limit的情况 + nodeService.listNodeBySha256(sha256, 20, tillLimit = false).apply { assertEquals(6, size) } + nodeService.listNodeBySha256(sha256, 100, tillLimit = true).apply { assertEquals(18, size) } + + // 测试单表数据量等于limit的页 + nodeService.listNodeBySha256(sha256, 6, tillLimit = true).apply { assertEquals(6, size) } + nodeService.listNodeBySha256(sha256, 6, tillLimit = false).apply { assertEquals(6, size) } + + // 测试获数据量小于pageSize的页 + nodeService.listNodeBySha256(sha256, 3, tillLimit = true).apply { assertEquals(3, size) } + nodeService.listNodeBySha256(sha256, 3, tillLimit = false).apply { assertEquals(3, size) } + } + private fun generateTestData(size: Int, generateSha256Func: (Int) -> String) { for (projectId in arrayOf(PROJECT_SHARDING_207, PROJECT_SHARDING_208, PROJECT_SHARDING_209)) { createProject(projectService, projectId) From 7b68f9a8e6af9d4c6a9a4a2d7a43db15c35c994c Mon Sep 17 00:00:00 2001 From: felixncheng Date: Mon, 25 Nov 2024 10:40:37 +0800 Subject: [PATCH 12/24] =?UTF-8?q?feat:=20media=E6=9C=8D=E5=8A=A1=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8F=82=E6=95=B0=E5=92=8C?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E5=99=A8=20#2768?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: media服务支持自定义参数和过滤器 #2768 * feat: media服务支持自定义参数和过滤器 #2768 --- .../boot-job-worker/shell/media-process.sh | 44 ------------------- .../tencent/bkrepo/media/config/WebConfig.kt | 16 +++++++ .../bkrepo/media/service/StreamService.kt | 7 ++- .../bkrepo/media/service/TokenService.kt | 2 +- .../bkrepo/media/service/TranscodeService.kt | 3 +- .../media/stream/MediaArtifactFileConsumer.kt | 3 -- .../bkrepo/media/stream/TranscodeConfig.kt | 3 +- .../bkrepo/media/stream/TranscodeParam.kt | 3 +- .../bkrepo/media/web/PluginDelegateFilter.kt | 20 +++++++++ 9 files changed, 48 insertions(+), 53 deletions(-) delete mode 100644 src/backend/job/boot-job-worker/shell/media-process.sh create mode 100644 src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/config/WebConfig.kt create mode 100644 src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/web/PluginDelegateFilter.kt diff --git a/src/backend/job/boot-job-worker/shell/media-process.sh b/src/backend/job/boot-job-worker/shell/media-process.sh deleted file mode 100644 index 38c0e8ce68..0000000000 --- a/src/backend/job/boot-job-worker/shell/media-process.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -get_field() { - local field_name=$1 - field_value=$(echo $DEVOPS_SCHEDULE_JOB_PARAMETERS| jq -r ".$field_name") - if [ -z "$field_value" ]; then - echo "Error: Field '$field_name' not found in input file" - exit 1 - fi - echo "$field_value" -} - -echo "1. 获取参数" -inputUrl=$(get_field inputUrl) -callbackUrl=$(get_field callbackUrl) -inputFileName=$(get_field inputFileName) -scale=$(get_field scale) -videoCodec=$(get_field videoCodec) -outputFileName=$(get_field outputFileName) - -echo "2. 下载待转码文件 - $inputFileName" -# 使用 -s 参数静默执行,-w "%{http_code}" 输出HTTP状态码 -http_status=$(curl -s -w "%{http_code}" -o $inputFileName $inputUrl) -if [ $http_status -ne 200 ];then - echo "文件下载失败[$http_status],Url: $inputUrl" - exit 1 -fi -ls -l - -echo "3. 开始转码 - $inputFileName > $outputFileName" -echo "ffmpeg -i $inputFileName -vf scale=$scale -c:a copy -c:v $videoCodec $outputFileName" -ffmpeg -i $inputFileName -vf scale=$scale -c:a copy -c:v $videoCodec $outputFileName -if [ $? -ne 0 ];then - echo "转码失败" - exit 1 -fi - -echo "4. 上传转码后的文件 - $outputFileName" -http_status=$(curl -s -w "%{http_code}" -X PUT -T $outputFileName "$callbackUrl") -if [ $http_status -ne 200 ];then - echo 文件上传失败[$http_status],Url: $callbackUrl - exit 1 -fi - -echo "转码完成 - $inputFileName" \ No newline at end of file diff --git a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/config/WebConfig.kt b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/config/WebConfig.kt new file mode 100644 index 0000000000..d3d6717aa0 --- /dev/null +++ b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/config/WebConfig.kt @@ -0,0 +1,16 @@ +package com.tencent.bkrepo.media.config + +import com.tencent.bkrepo.media.web.PluginDelegateFilter +import org.springframework.boot.web.servlet.FilterRegistrationBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class WebConfig { + @Bean + fun pluginDelegateFilter(): FilterRegistrationBean { + val registrationBean = FilterRegistrationBean() + registrationBean.filter = PluginDelegateFilter() + return registrationBean + } +} diff --git a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/StreamService.kt b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/StreamService.kt index 1833726fe9..74425c05f7 100644 --- a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/StreamService.kt +++ b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/StreamService.kt @@ -59,7 +59,7 @@ class StreamService( type = RepositoryType.MEDIA, category = RepositoryCategory.LOCAL, public = false, - display = display + display = display, ) repositoryService.createRepo(createRepoRequest) val nodeCreateRequest = NodeCreateRequest( @@ -100,17 +100,20 @@ class StreamService( userId: String, remux: Boolean = false, saveType: MediaType = MediaType.RAW, + transcodeExtraParams: String? = null, ): ClientStream { val repoId = RepositoryId(projectId, repoName) val repo = ArtifactContextHolder.getRepoDetail(repoId) val credentials = repo.storageCredentials ?: storageProperties.defaultStorageCredentials() + val transcodeConfig = getTranscodeConfig(projectId) + transcodeConfig?.let { it.extraParams = transcodeExtraParams } val fileConsumer = MediaArtifactFileConsumer( storageManager, transcodeService, repo, userId, STREAM_PATH, - getTranscodeConfig(projectId), + transcodeConfig, ) val recordingListener = if (remux) { RemuxRecordingListener(credentials.upload.location, scheduler, saveType, fileConsumer) diff --git a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/TokenService.kt b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/TokenService.kt index a35226c8d6..304915854d 100644 --- a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/TokenService.kt +++ b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/TokenService.kt @@ -150,7 +150,7 @@ class TokenService( /** * 检查token并返回token信息 */ - private fun checkToken(token: String): TemporaryTokenInfo { + fun checkToken(token: String): TemporaryTokenInfo { if (token.isBlank()) { throw ErrorCodeException(ArtifactMessageCode.TEMPORARY_TOKEN_INVALID, token) } diff --git a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/TranscodeService.kt b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/TranscodeService.kt index 15734405a3..8e2719f016 100644 --- a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/TranscodeService.kt +++ b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/service/TranscodeService.kt @@ -76,7 +76,7 @@ class TranscodeService( projectId = projectId, repoName = repoName, fullPath = newArtifactInfo.getArtifactFullPath(), - metadata = originMetadata + metadata = originMetadata, ) metadataService.saveMetadata(copyRequest) val removeContext = ArtifactRemoveContext(repo, originArtifactInfo) @@ -106,6 +106,7 @@ class TranscodeService( audioCodec = audioCodec, inputFileName = artifactInfo.getResponseName(), outputFileName = outputArtifactInfo.getResponseName(), + extraParams = transcodeConfig.extraParams.orEmpty(), ) } } diff --git a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/MediaArtifactFileConsumer.kt b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/MediaArtifactFileConsumer.kt index 6d7ed3f1e7..b8b43b9505 100644 --- a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/MediaArtifactFileConsumer.kt +++ b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/MediaArtifactFileConsumer.kt @@ -8,7 +8,6 @@ import com.tencent.bkrepo.media.service.TranscodeService import com.tencent.bkrepo.repository.pojo.metadata.MetadataModel import com.tencent.bkrepo.repository.pojo.node.service.NodeCreateRequest import com.tencent.bkrepo.repository.pojo.repo.RepositoryDetail -import org.slf4j.LoggerFactory import java.io.File /** @@ -39,7 +38,6 @@ class MediaArtifactFileConsumer( storageManager.storeArtifactFile(nodeCreateRequest, file, repo.storageCredentials) if (transcodeConfig != null) { transcodeService.transcode(artifactInfo, transcodeConfig, userId) - logger.info("Add transcode task for artifact[$artifactInfo]") } } @@ -68,7 +66,6 @@ class MediaArtifactFileConsumer( } companion object { - private val logger = LoggerFactory.getLogger(MediaArtifactFileConsumer::class.java) private const val METADATA_KEY_MEDIA_START_TIME = "media.startTime" private const val METADATA_KEY_MEDIA_STOP_TIME = "media.stopTime" } diff --git a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/TranscodeConfig.kt b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/TranscodeConfig.kt index bc785920ce..774f962809 100644 --- a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/TranscodeConfig.kt +++ b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/TranscodeConfig.kt @@ -4,5 +4,6 @@ data class TranscodeConfig( var scale: String = "", // 分辨率,比如1280x720 var videoCodec: String = "", // 视频编码 var audioCodec: String = "", // 音频编码 - var jobId: String = "", // 转码任务id + var jobId: String = "", // 转码任务id, + var extraParams: String? = null, ) diff --git a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/TranscodeParam.kt b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/TranscodeParam.kt index fdf6c9ce85..ddd85e9554 100644 --- a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/TranscodeParam.kt +++ b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/stream/TranscodeParam.kt @@ -7,5 +7,6 @@ data class TranscodeParam( val videoCodec: String? = null, // 视频编码 val audioCodec: String? = null, // 音频编码 var inputFileName: String, // 源文件名 - var outputFileName: String, // 输出文件名 + var outputFileName: String, // 输出文件名, + var extraParams: String, // 额外参数 ) diff --git a/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/web/PluginDelegateFilter.kt b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/web/PluginDelegateFilter.kt new file mode 100644 index 0000000000..2b67713438 --- /dev/null +++ b/src/backend/media/biz-media/src/main/kotlin/com/tencent/bkrepo/media/web/PluginDelegateFilter.kt @@ -0,0 +1,20 @@ +package com.tencent.bkrepo.media.web + +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse + +class PluginDelegateFilter : Filter { + override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { + if (delegate != null) { + delegate!!.doFilter(request, response, chain) + } else { + chain.doFilter(request, response) + } + } + + companion object { + var delegate: Filter? = null + } +} From 6290bb2c4a91ea2ee0856b8c95119bfa678d1523 Mon Sep 17 00:00:00 2001 From: yaoxuwan Date: Thu, 28 Nov 2024 09:25:07 +0800 Subject: [PATCH 13/24] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81websocket=20#24?= =?UTF-8?q?94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tencent/bkrepo/websocket/handler/SessionHandler.kt | 3 ++- support-files/templates/gateway#vhosts#bkrepo.server.conf | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionHandler.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionHandler.kt index 86e6ed35d5..e89fc9652c 100644 --- a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionHandler.kt +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/handler/SessionHandler.kt @@ -39,6 +39,7 @@ import com.tencent.bkrepo.common.security.http.jwt.JwtAuthProperties import com.tencent.bkrepo.common.security.manager.AuthenticationManager import com.tencent.bkrepo.common.security.util.JwtUtils import com.tencent.bkrepo.websocket.constant.APP_ENDPOINT +import com.tencent.bkrepo.websocket.constant.DESKTOP_ENDPOINT import com.tencent.bkrepo.websocket.constant.SESSION_ID import com.tencent.bkrepo.websocket.constant.USER_ENDPOINT import com.tencent.bkrepo.websocket.service.WebsocketService @@ -104,7 +105,7 @@ class SessionHandler( val sessionId = HostUtils.getRealSession(uri?.query) when { uri == null -> throw AuthenticationException("uri is null") - uri.path.startsWith(USER_ENDPOINT) -> { + uri.path.startsWith(USER_ENDPOINT) || uri.path.startsWith(DESKTOP_ENDPOINT) -> { val platformToken = session.handshakeHeaders[HttpHeaders.AUTHORIZATION]?.firstOrNull()?.toString() ?.removePrefix(PLATFORM_AUTH_PREFIX) ?: throw AuthenticationException("platform credential is null") val (accessKey, secretKey) = String(Base64.getDecoder().decode(platformToken)).split(COLON) diff --git a/support-files/templates/gateway#vhosts#bkrepo.server.conf b/support-files/templates/gateway#vhosts#bkrepo.server.conf index d0c1864a47..80a12de3cb 100644 --- a/support-files/templates/gateway#vhosts#bkrepo.server.conf +++ b/support-files/templates/gateway#vhosts#bkrepo.server.conf @@ -38,12 +38,12 @@ server { # 运营管理后台静态资源网关路径 include vhosts/bkrepo.admin.conf; - # web接口网关路径 - include vhosts/bkrepo.web.conf; - # websocket网关路径 include vhosts/bkrepo.websocket.conf; + # web接口网关路径 + include vhosts/bkrepo.web.conf; + # 后台的网关路径 include vhosts/bkrepo.backend.conf; } From 6176070afa49cec909d07b3881d8b6ac5510af5b Mon Sep 17 00:00:00 2001 From: felixncheng Date: Thu, 28 Nov 2024 09:40:54 +0800 Subject: [PATCH 14/24] =?UTF-8?q?bug:=20=E4=BF=AE=E5=A4=8D=E5=A4=A7?= =?UTF-8?q?=E5=86=99=E4=BB=BB=E5=8A=A1=E5=90=8D=E7=9A=84=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E8=B0=83=E5=BA=A6=E9=85=8D=E7=BD=AE=E5=88=B7=E6=96=B0=E4=B8=8D?= =?UTF-8?q?=E7=94=9F=E6=95=88=20#2787?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bkrepo/job/listener/RefreshJobPropertiesListener.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/listener/RefreshJobPropertiesListener.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/listener/RefreshJobPropertiesListener.kt index 50b5685e85..3338a75df1 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/listener/RefreshJobPropertiesListener.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/listener/RefreshJobPropertiesListener.kt @@ -53,7 +53,8 @@ class RefreshJobPropertiesListener(private val jobRegistrar: JobRegistrar) : Sma refreshKeys.forEach { key -> try { val jobName = key.substring(4, key.indexOf(".", 4)) - .replace(pattern) { it.value.last().uppercase() } + JOB_SUFFIX + .replace(pattern) { it.value.last().uppercase() } + .replaceFirstChar { it.lowercase() } + JOB_SUFFIX val jobBean = SpringContextUtils.getBean(BatchJob::class.java, jobName) logger.info("Job [$jobName] config updated") val job = JobUtils.parseBatchJob(jobBean) From ac962c056e1bfdbbc457979ea10aa881a278a6aa Mon Sep 17 00:00:00 2001 From: zacYL <100330102+zacYL@users.noreply.github.com> Date: Thu, 28 Nov 2024 10:46:22 +0800 Subject: [PATCH 15/24] =?UTF-8?q?feat:=20conan=20search=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=BD=93=E6=9F=A5=E8=AF=A2=E5=86=85=E5=AE=B9=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E6=97=B6=E8=BF=94=E5=9B=9E200=20#2804?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bkrepo/conan/service/impl/ConanSearchServiceImpl.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/backend/conan/biz-conan/src/main/kotlin/com/tencent/bkrepo/conan/service/impl/ConanSearchServiceImpl.kt b/src/backend/conan/biz-conan/src/main/kotlin/com/tencent/bkrepo/conan/service/impl/ConanSearchServiceImpl.kt index 4dc9ab4299..3b49c49421 100644 --- a/src/backend/conan/biz-conan/src/main/kotlin/com/tencent/bkrepo/conan/service/impl/ConanSearchServiceImpl.kt +++ b/src/backend/conan/biz-conan/src/main/kotlin/com/tencent/bkrepo/conan/service/impl/ConanSearchServiceImpl.kt @@ -27,10 +27,7 @@ package com.tencent.bkrepo.conan.service.impl -import com.tencent.bkrepo.common.api.constant.StringPool import com.tencent.bkrepo.common.metadata.service.packages.PackageService -import com.tencent.bkrepo.conan.constant.ConanMessageCode -import com.tencent.bkrepo.conan.exception.ConanSearchNotFoundException import com.tencent.bkrepo.conan.pojo.ConanInfo import com.tencent.bkrepo.conan.pojo.ConanSearchResult import com.tencent.bkrepo.conan.pojo.artifact.ConanArtifactInfo @@ -61,12 +58,6 @@ class ConanSearchServiceImpl : ConanSearchService { ): ConanSearchResult { val realPattern = pattern?.replace("*", ".*") val recipes = searchRecipes(projectId, repoName, realPattern, ignoreCase) - if (recipes.isEmpty()) { - throw ConanSearchNotFoundException( - ConanMessageCode.CONAN_SEARCH_NOT_FOUND, - pattern ?: StringPool.EMPTY, "$projectId/$repoName" - ) - } return ConanSearchResult(recipes) } From 5032f531e0aa3f4265b71ebe4ef037ef10301207 Mon Sep 17 00:00:00 2001 From: zacYL <100330102+zacYL@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:00:37 +0800 Subject: [PATCH 16/24] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=98=AF=E5=90=A6=E8=BF=94=E5=9B=9E=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=90=AF=E7=94=A8=E7=8A=B6=E6=80=81#2783?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bkrepo/common/metadata/config/RepositoryProperties.kt | 6 +++++- .../service/project/impl/CenterProjectServiceImpl.kt | 7 +++++-- .../service/project/impl/EdgeProjectServiceImpl.kt | 7 +++++-- .../metadata/service/project/impl/ProjectServiceImpl.kt | 3 +++ .../metadata/service/project/impl/RProjectServiceImpl.kt | 3 +++ 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/config/RepositoryProperties.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/config/RepositoryProperties.kt index e6044f4521..b995fd4181 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/config/RepositoryProperties.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/config/RepositoryProperties.kt @@ -57,5 +57,9 @@ data class RepositoryProperties( * 当目录节点上的num字段小于该值时,去db中实时count目录大小 * 注意: 此配置的值要比listCountLimit大 */ - var subNodeLimit: Long = 100000000L + var subNodeLimit: Long = 100000000L, + /** + * 是否返回真实项目启用禁用状态 + */ + var returnEnabled: Boolean = true ) diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/CenterProjectServiceImpl.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/CenterProjectServiceImpl.kt index 0fba41993b..b69fdb8d1e 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/CenterProjectServiceImpl.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/CenterProjectServiceImpl.kt @@ -30,6 +30,7 @@ package com.tencent.bkrepo.common.metadata.service.project.impl import com.tencent.bkrepo.auth.api.ServiceBkiamV3ResourceClient import com.tencent.bkrepo.auth.api.ServicePermissionClient import com.tencent.bkrepo.common.metadata.condition.SyncCondition +import com.tencent.bkrepo.common.metadata.config.RepositoryProperties import com.tencent.bkrepo.common.service.cluster.condition.CommitEdgeCenterCondition import com.tencent.bkrepo.common.metadata.dao.project.ProjectDao import com.tencent.bkrepo.common.metadata.dao.project.ProjectMetricsDao @@ -45,10 +46,12 @@ class CenterProjectServiceImpl( projectMetricsDao: ProjectMetricsDao, serviceBkiamV3ResourceClient: ServiceBkiamV3ResourceClient, storageCredentialService: StorageCredentialService, -) : ProjectServiceImpl( + repositoryProperties: RepositoryProperties, + ) : ProjectServiceImpl( projectDao, servicePermissionClient, projectMetricsDao, serviceBkiamV3ResourceClient, - storageCredentialService + storageCredentialService, + repositoryProperties ) diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/EdgeProjectServiceImpl.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/EdgeProjectServiceImpl.kt index 105cbfe21d..e467b2e28e 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/EdgeProjectServiceImpl.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/EdgeProjectServiceImpl.kt @@ -31,6 +31,7 @@ import com.tencent.bkrepo.auth.api.ServiceBkiamV3ResourceClient import com.tencent.bkrepo.auth.api.ServicePermissionClient import com.tencent.bkrepo.common.artifact.message.ArtifactMessageCode import com.tencent.bkrepo.common.metadata.condition.SyncCondition +import com.tencent.bkrepo.common.metadata.config.RepositoryProperties import com.tencent.bkrepo.common.metadata.util.ClusterUtils.reportMetadataToCenter import com.tencent.bkrepo.common.service.cluster.condition.CommitEdgeEdgeCondition import com.tencent.bkrepo.common.service.cluster.properties.ClusterProperties @@ -54,12 +55,14 @@ class EdgeProjectServiceImpl( clusterProperties: ClusterProperties, projectMetricsDao: ProjectMetricsDao, storageCredentialService: StorageCredentialService, -) : ProjectServiceImpl( + repositoryProperties: RepositoryProperties, + ) : ProjectServiceImpl( projectDao, servicePermissionClient, projectMetricsDao, serviceBkiamV3ResourceClient, - storageCredentialService + storageCredentialService, + repositoryProperties ) { private val centerProjectClient: ClusterProjectClient by lazy { diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/ProjectServiceImpl.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/ProjectServiceImpl.kt index 38c1ea1cbb..d7a6994332 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/ProjectServiceImpl.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/ProjectServiceImpl.kt @@ -37,6 +37,7 @@ import com.tencent.bkrepo.common.api.message.CommonMessageCode import com.tencent.bkrepo.common.api.pojo.Page import com.tencent.bkrepo.common.artifact.message.ArtifactMessageCode import com.tencent.bkrepo.common.metadata.condition.SyncCondition +import com.tencent.bkrepo.common.metadata.config.RepositoryProperties import com.tencent.bkrepo.common.metadata.dao.project.ProjectDao import com.tencent.bkrepo.common.metadata.dao.project.ProjectMetricsDao import com.tencent.bkrepo.common.metadata.listener.ResourcePermissionListener @@ -84,6 +85,7 @@ class ProjectServiceImpl( private val projectMetricsDao: ProjectMetricsDao, private val serviceBkiamV3ResourceClient: ServiceBkiamV3ResourceClient, private val storageCredentialService: StorageCredentialService, + private val repositoryProperties: RepositoryProperties, ) : ProjectService { @Autowired @@ -134,6 +136,7 @@ class ProjectServiceImpl( } override fun isProjectEnabled(name: String): Boolean { + if (!repositoryProperties.returnEnabled) return true val projectInfo = projectDao.findByName(name) ?: throw ErrorCodeException(ArtifactMessageCode.PROJECT_NOT_FOUND, name) return ProjectServiceHelper.isProjectEnabled(projectInfo) diff --git a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/RProjectServiceImpl.kt b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/RProjectServiceImpl.kt index 8ca465746a..6cad7f1c35 100644 --- a/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/RProjectServiceImpl.kt +++ b/src/backend/common/common-metadata/metadata-service/src/main/kotlin/com/tencent/bkrepo/common/metadata/service/project/impl/RProjectServiceImpl.kt @@ -36,6 +36,7 @@ import com.tencent.bkrepo.common.api.pojo.Page import com.tencent.bkrepo.common.artifact.message.ArtifactMessageCode import com.tencent.bkrepo.common.metadata.client.RAuthClient import com.tencent.bkrepo.common.metadata.condition.ReactiveCondition +import com.tencent.bkrepo.common.metadata.config.RepositoryProperties import com.tencent.bkrepo.common.metadata.dao.project.RProjectDao import com.tencent.bkrepo.common.metadata.dao.project.RProjectMetricsDao import com.tencent.bkrepo.common.metadata.listener.RResourcePermissionListener @@ -82,6 +83,7 @@ class RProjectServiceImpl( private val projectMetricsDao: RProjectMetricsDao, private val storageCredentialService: RStorageCredentialService, private val rAuthClient: RAuthClient, + private val repositoryProperties: RepositoryProperties, ) : RProjectService { @Autowired @@ -133,6 +135,7 @@ class RProjectServiceImpl( } override suspend fun isProjectEnabled(name: String): Boolean { + if (!repositoryProperties.returnEnabled) return true val projectInfo = projectDao.findByName(name) ?: throw ErrorCodeException(ArtifactMessageCode.PROJECT_NOT_FOUND, name) return ProjectServiceHelper.isProjectEnabled(projectInfo) From 079baecc103756f0648d8bf78aba548f832d5632 Mon Sep 17 00:00:00 2001 From: yaoxuwan Date: Mon, 2 Dec 2024 16:16:39 +0800 Subject: [PATCH 17/24] =?UTF-8?q?fix:=20websocket=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=85=B3=E9=97=AD=20#2809=20(#2811?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tencent/bkrepo/websocket/config/WebsocketConfiguration.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebsocketConfiguration.kt b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebsocketConfiguration.kt index aced41f2e9..16717a366c 100644 --- a/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebsocketConfiguration.kt +++ b/src/backend/websocket/biz-websocket/src/main/kotlin/com/tencent/bkrepo/websocket/config/WebsocketConfiguration.kt @@ -42,7 +42,6 @@ import org.springframework.context.annotation.Configuration import org.springframework.messaging.Message import org.springframework.messaging.simp.config.ChannelRegistration import org.springframework.messaging.simp.config.MessageBrokerRegistry -import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker import org.springframework.web.socket.config.annotation.StompEndpointRegistry import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer @@ -57,14 +56,11 @@ class WebsocketConfiguration( private val websocketService: WebsocketService, private val jwtAuthProperties: JwtAuthProperties, private val authenticationManager: AuthenticationManager, - private val taskScheduler: ThreadPoolTaskScheduler ) : WebSocketMessageBrokerConfigurer { override fun configureMessageBroker(config: MessageBrokerRegistry) { config.setCacheLimit(webSocketProperties.cacheLimit) config.enableSimpleBroker("/topic") - .setHeartbeatValue(longArrayOf(3000,3000)) - .setTaskScheduler(taskScheduler) config.setApplicationDestinationPrefixes("/app") } From aa25852d1428256d4b08dc8e0aa08fbcda2827e4 Mon Sep 17 00:00:00 2001 From: zacYL <100330102+zacYL@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:08:23 +0800 Subject: [PATCH 18/24] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=8A=A0?= =?UTF-8?q?=E5=9B=BA=E5=90=8E=E6=96=87=E4=BB=B6=E4=BF=A1=E6=81=AF#2792?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tencent/bkrepo/common/artifact/manager/StorageManager.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManager.kt b/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManager.kt index 78ee7e6b7a..c7095d3fb4 100644 --- a/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManager.kt +++ b/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManager.kt @@ -148,6 +148,8 @@ class StorageManager( forwardNode = forward(node, SecurityUtils.getUserId()) forwardNode?.let { logger.info("Load[${node.identity()}] forward to [${it.identity()}].") + ActionAuditContext.current().addExtendData("alphaApkSha256", it.sha256) + ActionAuditContext.current().addExtendData("alphaApkMd5", it.md5) } } val load = forwardNode ?: node From a5acb9a7e21601d48159b4dcdb9170d5fc880196 Mon Sep 17 00:00:00 2001 From: lannoy0523 <46735290+lannoy0523@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:14:11 +0800 Subject: [PATCH 19/24] =?UTF-8?q?feat:=20op=E6=9C=8D=E5=8A=A1=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E6=96=B0=E5=A2=9E=E5=9C=B0=E5=9D=80=E6=A8=A1=E7=B3=8A?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=20#2795?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devops-op/src/views/service/Instance.vue | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/frontend/devops-op/src/views/service/Instance.vue b/src/frontend/devops-op/src/views/service/Instance.vue index 4fffbb1d17..7c839e91ae 100644 --- a/src/frontend/devops-op/src/views/service/Instance.vue +++ b/src/frontend/devops-op/src/views/service/Instance.vue @@ -1,13 +1,12 @@