Merge remote-tracking branch 'base-version/main' into dev
# Conflicts: # zt-module-system/zt-module-system-server/src/main/java/com/zt/plat/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java
This commit is contained in:
@@ -7,6 +7,12 @@ ALTER TABLE infra_file ADD aes_iv VARCHAR(128);
|
|||||||
|
|
||||||
COMMENT ON COLUMN infra_file.aes_iv IS 'AES加密时的随机IV(Base64编码)';
|
COMMENT ON COLUMN infra_file.aes_iv IS 'AES加密时的随机IV(Base64编码)';
|
||||||
|
|
||||||
|
-- 3. Databus 客户端凭证新增匿名访问配置(DM8)
|
||||||
|
ALTER TABLE databus_api_client_credential ADD allow_anonymous BIT DEFAULT '0' NOT NULL;
|
||||||
|
ALTER TABLE databus_api_client_credential ADD anonymous_user_id BIGINT;
|
||||||
|
COMMENT ON COLUMN databus_api_client_credential.allow_anonymous IS '是否允许匿名访问';
|
||||||
|
COMMENT ON COLUMN databus_api_client_credential.anonymous_user_id IS '匿名访问固定用户';
|
||||||
|
|
||||||
-- 3 业务附件统一管理
|
-- 3 业务附件统一管理
|
||||||
DROP TABLE IF EXISTS infra_bsn_file;
|
DROP TABLE IF EXISTS infra_bsn_file;
|
||||||
CREATE TABLE infra_bsn_file (
|
CREATE TABLE infra_bsn_file (
|
||||||
|
|||||||
@@ -656,6 +656,7 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (36, 3, '本部门数据权限', '3', 'system_data_scope', 0, '', '', '本部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:16', '0');
|
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (36, 3, '本部门数据权限', '3', 'system_data_scope', 0, '', '', '本部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:16', '0');
|
||||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (37, 4, '本部门及以下数据权限', '4', 'system_data_scope', 0, '', '', '本部门及以下数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:21', '0');
|
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (37, 4, '本部门及以下数据权限', '4', 'system_data_scope', 0, '', '', '本部门及以下数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:21', '0');
|
||||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (38, 5, '仅本人数据权限', '5', 'system_data_scope', 0, '', '', '仅本人数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:23', '0');
|
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (38, 5, '仅本人数据权限', '5', 'system_data_scope', 0, '', '', '仅本人数据权限', 'admin', '2021-01-05 17:03:48', '', '2022-02-01 16:47:23', '0');
|
||||||
|
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (200010, 6, '公司及所属部门数据权限', '6', 'system_data_scope', 0, '', '', '公司及所属部门数据权限', 'admin', '2021-01-05 17:03:48', '', '2025-10-24 00:00:00', '0');
|
||||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (39, 0, '成功', '0', 'system_login_result', 0, 'success', '', '登陆结果 - 成功', '', '2021-01-18 06:17:36', '1', '2022-02-16 13:23:49', '0');
|
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (39, 0, '成功', '0', 'system_login_result', 0, 'success', '', '登陆结果 - 成功', '', '2021-01-18 06:17:36', '1', '2022-02-16 13:23:49', '0');
|
||||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (40, 10, '账号或密码不正确', '10', 'system_login_result', 0, 'primary', '', '登陆结果 - 账号或密码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:27', '0');
|
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (40, 10, '账号或密码不正确', '10', 'system_login_result', 0, 'primary', '', '登陆结果 - 账号或密码不正确', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:24:27', '0');
|
||||||
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (41, 20, '用户被禁用', '20', 'system_login_result', 0, 'warning', '', '登陆结果 - 用户被禁用', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:23:57', '0');
|
INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_type, css_class, remark, creator, create_time, updater, update_time, deleted) VALUES (41, 20, '用户被禁用', '20', 'system_login_result', 0, 'warning', '', '登陆结果 - 用户被禁用', '', '2021-01-18 06:17:54', '1', '2022-02-16 13:23:57', '0');
|
||||||
|
|||||||
@@ -212,6 +212,8 @@ CREATE TABLE databus_api_client_credential (
|
|||||||
signature_type VARCHAR(32) NOT NULL,
|
signature_type VARCHAR(32) NOT NULL,
|
||||||
enabled BIT DEFAULT '1' NOT NULL,
|
enabled BIT DEFAULT '1' NOT NULL,
|
||||||
remark VARCHAR(255),
|
remark VARCHAR(255),
|
||||||
|
allow_anonymous BIT DEFAULT '0' NOT NULL,
|
||||||
|
anonymous_user_id BIGINT,
|
||||||
creator VARCHAR(64) DEFAULT '' NOT NULL,
|
creator VARCHAR(64) DEFAULT '' NOT NULL,
|
||||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
updater VARCHAR(64) DEFAULT '' NOT NULL,
|
updater VARCHAR(64) DEFAULT '' NOT NULL,
|
||||||
@@ -231,6 +233,8 @@ COMMENT ON COLUMN databus_api_client_credential.encryption_type IS '加密算法
|
|||||||
COMMENT ON COLUMN databus_api_client_credential.signature_type IS '签名算法';
|
COMMENT ON COLUMN databus_api_client_credential.signature_type IS '签名算法';
|
||||||
COMMENT ON COLUMN databus_api_client_credential.enabled IS '是否启用';
|
COMMENT ON COLUMN databus_api_client_credential.enabled IS '是否启用';
|
||||||
COMMENT ON COLUMN databus_api_client_credential.remark IS '备注';
|
COMMENT ON COLUMN databus_api_client_credential.remark IS '备注';
|
||||||
|
COMMENT ON COLUMN databus_api_client_credential.allow_anonymous IS '是否允许匿名访问';
|
||||||
|
COMMENT ON COLUMN databus_api_client_credential.anonymous_user_id IS '匿名访问固定用户';
|
||||||
COMMENT ON COLUMN databus_api_client_credential.creator IS '创建者';
|
COMMENT ON COLUMN databus_api_client_credential.creator IS '创建者';
|
||||||
COMMENT ON COLUMN databus_api_client_credential.create_time IS '创建时间';
|
COMMENT ON COLUMN databus_api_client_credential.create_time IS '创建时间';
|
||||||
COMMENT ON COLUMN databus_api_client_credential.updater IS '更新者';
|
COMMENT ON COLUMN databus_api_client_credential.updater IS '更新者';
|
||||||
|
|||||||
@@ -9,11 +9,7 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Base64;
|
import java.util.*;
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用的签名、加解密工具类
|
* 通用的签名、加解密工具类
|
||||||
@@ -26,7 +22,7 @@ public final class CryptoSignatureUtils {
|
|||||||
public static final String SIGNATURE_TYPE_SHA256 = "SHA256";
|
public static final String SIGNATURE_TYPE_SHA256 = "SHA256";
|
||||||
|
|
||||||
private static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding";
|
private static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding";
|
||||||
private static final String SIGNATURE_FIELD = "signature";
|
public static final String SIGNATURE_FIELD = "signature";
|
||||||
|
|
||||||
private CryptoSignatureUtils() {
|
private CryptoSignatureUtils() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.zt.plat.framework.common.validation;
|
||||||
|
|
||||||
|
import jakarta.validation.Constraint;
|
||||||
|
import jakarta.validation.Payload;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码复杂度校验注解,要求至少包含大小写字母、数字、特殊字符中的三种。
|
||||||
|
* @author chenbowen
|
||||||
|
*/
|
||||||
|
@Target({
|
||||||
|
ElementType.METHOD,
|
||||||
|
ElementType.FIELD,
|
||||||
|
ElementType.ANNOTATION_TYPE,
|
||||||
|
ElementType.CONSTRUCTOR,
|
||||||
|
ElementType.PARAMETER,
|
||||||
|
ElementType.TYPE_USE
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(validatedBy = PasswordValidator.class)
|
||||||
|
public @interface Password {
|
||||||
|
|
||||||
|
String message() default "密码必须包含大写字母、小写字母、数字、特殊字符中的至少三种";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.zt.plat.framework.common.validation;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import jakarta.validation.ConstraintValidator;
|
||||||
|
import jakarta.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码复杂度校验:至少命中以下类别中的三类:大写字母、小写字母、数字、特殊字符。
|
||||||
|
*/
|
||||||
|
public class PasswordValidator implements ConstraintValidator<Password, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(Password constraintAnnotation) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
if (StrUtil.isBlank(value)) {
|
||||||
|
// 空值交由 @NotEmpty 等注解处理;在无需修改密码时视为空密码通过
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int categories = 0;
|
||||||
|
if (value.matches(".*[A-Z].*")) {
|
||||||
|
categories++;
|
||||||
|
}
|
||||||
|
if (value.matches(".*[a-z].*")) {
|
||||||
|
categories++;
|
||||||
|
}
|
||||||
|
if (value.matches(".*[0-9].*")) {
|
||||||
|
categories++;
|
||||||
|
}
|
||||||
|
if (value.matches(".*[^A-Za-z0-9].*")) {
|
||||||
|
categories++;
|
||||||
|
}
|
||||||
|
return categories >= 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.zt.plat.framework.common.validation;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class PasswordValidatorTest {
|
||||||
|
|
||||||
|
private final PasswordValidator validator = new PasswordValidator();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAcceptBlankPassword() {
|
||||||
|
assertTrue(validator.isValid(null, null));
|
||||||
|
assertTrue(validator.isValid("", null));
|
||||||
|
assertTrue(validator.isValid(" ", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectInsufficientComplexity() {
|
||||||
|
assertFalse(validator.isValid("abcdef", null));
|
||||||
|
assertFalse(validator.isValid("ABCDEF", null));
|
||||||
|
assertFalse(validator.isValid("ABC123", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAcceptComplexPassword() {
|
||||||
|
assertTrue(validator.isValid("Abc123!", null));
|
||||||
|
assertTrue(validator.isValid("1a#BCdef", null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import com.zt.plat.module.databus.controller.admin.gateway.vo.credential.ApiClie
|
|||||||
import com.zt.plat.module.databus.controller.admin.gateway.vo.credential.ApiClientCredentialSaveReqVO;
|
import com.zt.plat.module.databus.controller.admin.gateway.vo.credential.ApiClientCredentialSaveReqVO;
|
||||||
import com.zt.plat.module.databus.controller.admin.gateway.vo.credential.ApiClientCredentialSimpleRespVO;
|
import com.zt.plat.module.databus.controller.admin.gateway.vo.credential.ApiClientCredentialSimpleRespVO;
|
||||||
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiClientCredentialDO;
|
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiClientCredentialDO;
|
||||||
|
import com.zt.plat.module.databus.service.gateway.ApiAnonymousUserService;
|
||||||
import com.zt.plat.module.databus.service.gateway.ApiClientCredentialService;
|
import com.zt.plat.module.databus.service.gateway.ApiClientCredentialService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@@ -26,6 +27,7 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||||
|
import static org.springframework.util.CollectionUtils.isEmpty;
|
||||||
|
|
||||||
@Tag(name = "管理后台 - API 客户端凭证")
|
@Tag(name = "管理后台 - API 客户端凭证")
|
||||||
@RestController
|
@RestController
|
||||||
@@ -35,19 +37,24 @@ import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
|||||||
public class ApiClientCredentialController {
|
public class ApiClientCredentialController {
|
||||||
|
|
||||||
private final ApiClientCredentialService credentialService;
|
private final ApiClientCredentialService credentialService;
|
||||||
|
private final ApiAnonymousUserService anonymousUserService;
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "分页查询客户端凭证")
|
@Operation(summary = "分页查询客户端凭证")
|
||||||
public CommonResult<PageResult<ApiClientCredentialRespVO>> page(ApiClientCredentialPageReqVO reqVO) {
|
public CommonResult<PageResult<ApiClientCredentialRespVO>> page(ApiClientCredentialPageReqVO reqVO) {
|
||||||
PageResult<ApiClientCredentialDO> page = credentialService.getPage(reqVO);
|
PageResult<ApiClientCredentialDO> page = credentialService.getPage(reqVO);
|
||||||
return success(ApiClientCredentialConvert.INSTANCE.convertPage(page));
|
PageResult<ApiClientCredentialRespVO> respPage = ApiClientCredentialConvert.INSTANCE.convertPage(page);
|
||||||
|
populateAnonymousInfo(respPage.getList());
|
||||||
|
return success(respPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@Operation(summary = "查询凭证详情")
|
@Operation(summary = "查询凭证详情")
|
||||||
public CommonResult<ApiClientCredentialRespVO> get(@RequestParam("id") Long id) {
|
public CommonResult<ApiClientCredentialRespVO> get(@RequestParam("id") Long id) {
|
||||||
ApiClientCredentialDO credential = credentialService.get(id);
|
ApiClientCredentialDO credential = credentialService.get(id);
|
||||||
return success(ApiClientCredentialConvert.INSTANCE.convert(credential));
|
ApiClientCredentialRespVO respVO = ApiClientCredentialConvert.INSTANCE.convert(credential);
|
||||||
|
populateAnonymousInfo(List.of(respVO));
|
||||||
|
return success(respVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@@ -76,4 +83,14 @@ public class ApiClientCredentialController {
|
|||||||
List<ApiClientCredentialDO> list = credentialService.listEnabled();
|
List<ApiClientCredentialDO> list = credentialService.listEnabled();
|
||||||
return success(ApiClientCredentialConvert.INSTANCE.convertSimpleList(list));
|
return success(ApiClientCredentialConvert.INSTANCE.convertSimpleList(list));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void populateAnonymousInfo(List<ApiClientCredentialRespVO> list) {
|
||||||
|
if (isEmpty(list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list.stream()
|
||||||
|
.filter(item -> Boolean.TRUE.equals(item.getAllowAnonymous()) && item.getAnonymousUserId() != null)
|
||||||
|
.forEach(item -> anonymousUserService.find(item.getAnonymousUserId())
|
||||||
|
.ifPresent(details -> item.setAnonymousUserNickname(details.getNickname())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.zt.plat.module.databus.controller.admin.gateway.convert.ApiDefinition
|
|||||||
import com.zt.plat.module.databus.controller.admin.gateway.vo.ApiGatewayInvokeReqVO;
|
import com.zt.plat.module.databus.controller.admin.gateway.vo.ApiGatewayInvokeReqVO;
|
||||||
import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionDetailRespVO;
|
import com.zt.plat.module.databus.controller.admin.gateway.vo.definition.ApiDefinitionDetailRespVO;
|
||||||
import com.zt.plat.module.databus.framework.integration.gateway.core.ApiGatewayExecutionService;
|
import com.zt.plat.module.databus.framework.integration.gateway.core.ApiGatewayExecutionService;
|
||||||
|
import com.zt.plat.module.databus.framework.integration.gateway.core.IntegrationFlowManager;
|
||||||
import com.zt.plat.module.databus.framework.integration.gateway.model.ApiGatewayResponse;
|
import com.zt.plat.module.databus.framework.integration.gateway.model.ApiGatewayResponse;
|
||||||
import com.zt.plat.module.databus.service.gateway.ApiDefinitionService;
|
import com.zt.plat.module.databus.service.gateway.ApiDefinitionService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -27,6 +28,7 @@ public class ApiGatewayController {
|
|||||||
|
|
||||||
private final ApiGatewayExecutionService executionService;
|
private final ApiGatewayExecutionService executionService;
|
||||||
private final ApiDefinitionService apiDefinitionService;
|
private final ApiDefinitionService apiDefinitionService;
|
||||||
|
private final IntegrationFlowManager integrationFlowManager;
|
||||||
|
|
||||||
@PostMapping(value = "/invoke", consumes = MediaType.APPLICATION_JSON_VALUE)
|
@PostMapping(value = "/invoke", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||||
@Operation(summary = "测试调用 API 编排")
|
@Operation(summary = "测试调用 API 编排")
|
||||||
@@ -43,4 +45,12 @@ public class ApiGatewayController {
|
|||||||
return success(definitions);
|
return success(definitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/cache/refresh")
|
||||||
|
@Operation(summary = "刷新 API 缓存")
|
||||||
|
public CommonResult<Boolean> refreshCache() {
|
||||||
|
apiDefinitionService.refreshAllCache();
|
||||||
|
integrationFlowManager.refreshAll();
|
||||||
|
return success(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,15 @@ public class ApiClientCredentialRespVO {
|
|||||||
@Schema(description = "备注", example = "默认应用凭证")
|
@Schema(description = "备注", example = "默认应用凭证")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
@Schema(description = "允许匿名访问", example = "false")
|
||||||
|
private Boolean allowAnonymous;
|
||||||
|
|
||||||
|
@Schema(description = "匿名访问固定用户 ID", example = "1024")
|
||||||
|
private Long anonymousUserId;
|
||||||
|
|
||||||
|
@Schema(description = "匿名访问固定用户昵称", example = "张三")
|
||||||
|
private String anonymousUserNickname;
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user