fix: ASW-143 application Merge request 状态调整
This commit is contained in:
@@ -64,6 +64,7 @@ public enum RCode {
|
|||||||
GIT_MERGE_FAILED(203002, "Merge failed"),
|
GIT_MERGE_FAILED(203002, "Merge failed"),
|
||||||
GIT_PARENT_COMMITID_NOT_FOUND(203003, "Parent commitId not found"),
|
GIT_PARENT_COMMITID_NOT_FOUND(203003, "Parent commitId not found"),
|
||||||
GIT_BINARY_CONFLICT_ERROR(203004, "Binary file conflict found; resolve conflicts in binary files manually"),
|
GIT_BINARY_CONFLICT_ERROR(203004, "Binary file conflict found; resolve conflicts in binary files manually"),
|
||||||
|
GIT_MERGE_NOT_SUPPORTED(203005, "Cannot merge in the {0} state"),
|
||||||
|
|
||||||
|
|
||||||
// Runner
|
// Runner
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import net.geedge.asw.common.util.T;
|
|||||||
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
|
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
|
||||||
import net.geedge.asw.module.app.service.IApplicationMergeService;
|
import net.geedge.asw.module.app.service.IApplicationMergeService;
|
||||||
import net.geedge.asw.module.app.service.IGitService;
|
import net.geedge.asw.module.app.service.IGitService;
|
||||||
|
import net.geedge.asw.module.app.service.impl.ApplicationMergeServiceImpl.MergeRequestStatus;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -259,12 +260,18 @@ public class GitController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{workspaceId}/mr/{mrId}")
|
@PutMapping("/{workspaceId}/mr/{mrId}")
|
||||||
public synchronized R mergeMr(@PathVariable("mrId") String mrId) {
|
public synchronized R mergeMr(@PathVariable("mrId") String mrId, @RequestBody(required = false) Map<String, Object> body) {
|
||||||
ApplicationMergeEntity record = applicationMergeService.mergeMr(mrId);
|
String action = T.MapUtil.getStr(body, "action");
|
||||||
if (T.StrUtil.equals("success", record.getStatus())) {
|
T.VerifyUtil.is(action).notEmpty(RCode.PARAM_CANNOT_EMPTY);
|
||||||
return R.ok().putData("record", record);
|
|
||||||
}
|
ApplicationMergeEntity record = applicationMergeService.mergeMr(mrId, action);
|
||||||
|
if(T.BooleanUtil.and(
|
||||||
|
T.ObjectUtil.equals("merge", action),
|
||||||
|
T.ObjectUtil.notEqual(MergeRequestStatus.MERGED.toString(), record.getStatus())
|
||||||
|
)){
|
||||||
return R.error(RCode.GIT_MERGE_FAILED).putData("record", record);
|
return R.error(RCode.GIT_MERGE_FAILED).putData("record", record);
|
||||||
}
|
}
|
||||||
|
return R.ok().putData("record", record);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,6 @@ public interface IApplicationMergeService extends IService<ApplicationMergeEntit
|
|||||||
|
|
||||||
void resolveMrConflicts(String mrId, Map<String, Object> body);
|
void resolveMrConflicts(String mrId, Map<String, Object> body);
|
||||||
|
|
||||||
ApplicationMergeEntity mergeMr(String mrId);
|
ApplicationMergeEntity mergeMr(String mrId, String action);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package net.geedge.asw.module.app.service.impl;
|
package net.geedge.asw.module.app.service.impl;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.log.Log;
|
import cn.hutool.log.Log;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import net.geedge.asw.common.config.Query;
|
import net.geedge.asw.common.config.Query;
|
||||||
|
import net.geedge.asw.common.util.ASWException;
|
||||||
import net.geedge.asw.common.util.RCode;
|
import net.geedge.asw.common.util.RCode;
|
||||||
import net.geedge.asw.common.util.T;
|
import net.geedge.asw.common.util.T;
|
||||||
import net.geedge.asw.module.app.dao.ApplicationMergeDao;
|
import net.geedge.asw.module.app.dao.ApplicationMergeDao;
|
||||||
@@ -76,6 +78,9 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
|
|||||||
entity.setCreateTimestamp(System.currentTimeMillis());
|
entity.setCreateTimestamp(System.currentTimeMillis());
|
||||||
entity.setCreateUserId(StpUtil.getLoginIdAsString());
|
entity.setCreateUserId(StpUtil.getLoginIdAsString());
|
||||||
|
|
||||||
|
// 默认状态
|
||||||
|
entity.setStatus(MergeRequestStatus.OPEN.toString());
|
||||||
|
|
||||||
// save
|
// save
|
||||||
this.save(entity);
|
this.save(entity);
|
||||||
|
|
||||||
@@ -209,13 +214,21 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
|
|||||||
* merge operation
|
* merge operation
|
||||||
*
|
*
|
||||||
* @param mrId
|
* @param mrId
|
||||||
|
* @param action merge|close
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ApplicationMergeEntity mergeMr(String mrId) {
|
public ApplicationMergeEntity mergeMr(String mrId, String action) {
|
||||||
ApplicationMergeEntity entity = this.getById(mrId);
|
ApplicationMergeEntity entity = this.getById(mrId);
|
||||||
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
|
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "merge": {
|
||||||
|
MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
|
||||||
|
if (!mergeRequestStatus.canMerge()) {
|
||||||
|
throw new ASWException(RCode.GIT_MERGE_NOT_SUPPORTED.setParam(mergeRequestStatus.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
String srcBranch = entity.getSourceBranch();
|
String srcBranch = entity.getSourceBranch();
|
||||||
String tgtBranch = entity.getTargetBranch();
|
String tgtBranch = entity.getTargetBranch();
|
||||||
|
|
||||||
@@ -226,11 +239,11 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
|
|||||||
try {
|
try {
|
||||||
String commitId = gitService.mergeBranch(workspaceId, srcBranch, tgtBranch, message);
|
String commitId = gitService.mergeBranch(workspaceId, srcBranch, tgtBranch, message);
|
||||||
entity.setCommitId(commitId);
|
entity.setCommitId(commitId);
|
||||||
entity.setStatus("success");
|
entity.setStatus(MergeRequestStatus.MERGED.toString());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e, "[newMr] [merge error] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [msg: {}]", workspaceId, srcBranch, tgtBranch, e.getMessage());
|
log.error(e, "[newMr] [merge error] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [msg: {}]", workspaceId, srcBranch, tgtBranch, e.getMessage());
|
||||||
entity.setCommitId("");
|
entity.setCommitId("");
|
||||||
entity.setStatus("failed");
|
entity.setStatus(MergeRequestStatus.FAILED.toString());
|
||||||
entity.setMessage(e.getMessage());
|
entity.setMessage(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,5 +266,75 @@ public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao
|
|||||||
);
|
);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
case "close": {
|
||||||
|
String updateStatus = StrUtil.equals(MergeRequestStatus.OPEN.toString(), entity.getStatus()) ? MergeRequestStatus.CLOSED.toString() : entity.getStatus();
|
||||||
|
entity.setStatus(updateStatus);
|
||||||
|
|
||||||
|
this.update(new LambdaUpdateWrapper<ApplicationMergeEntity>()
|
||||||
|
.eq(ApplicationMergeEntity::getId, mrId)
|
||||||
|
.set(ApplicationMergeEntity::getStatus, updateStatus)
|
||||||
|
);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static enum MergeRequestStatus {
|
||||||
|
OPEN {
|
||||||
|
// 默认状态
|
||||||
|
public String toString() {
|
||||||
|
return "open";
|
||||||
|
}
|
||||||
|
public boolean canMerge() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MERGED {
|
||||||
|
// 合并成功
|
||||||
|
public String toString() {
|
||||||
|
return "merged";
|
||||||
|
}
|
||||||
|
public boolean canMerge() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FAILED {
|
||||||
|
// 合并失败
|
||||||
|
public String toString() {
|
||||||
|
return "failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canMerge() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CLOSED {
|
||||||
|
// 合并关闭
|
||||||
|
public String toString() {
|
||||||
|
return "closed";
|
||||||
|
}
|
||||||
|
public boolean canMerge() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private MergeRequestStatus() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MergeRequestStatus getInstance(String name) {
|
||||||
|
for (MergeRequestStatus v : values()) {
|
||||||
|
if (StrUtil.equalsIgnoreCase(v.name(), name)) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean canMerge();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -153,5 +153,7 @@ INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_
|
|||||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (245, '203003', 'GIT_PARENT_COMMITID_NOT_FOUND', '未找到 parent commitId', 'zh', '', 'admin', 1724030366000);
|
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (245, '203003', 'GIT_PARENT_COMMITID_NOT_FOUND', '未找到 parent commitId', 'zh', '', 'admin', 1724030366000);
|
||||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (246, '203004', 'GIT_BINARY_CONFLICT_ERROR', 'Binary file conflict found; resolve conflicts in binary files manually', 'en', '', 'admin', 1724030366000);
|
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (246, '203004', 'GIT_BINARY_CONFLICT_ERROR', 'Binary file conflict found; resolve conflicts in binary files manually', 'en', '', 'admin', 1724030366000);
|
||||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (247, '203004', 'GIT_BINARY_CONFLICT_ERROR', '发现二进制文件冲突;手动解决二进制文件中的冲突', 'zh', '', 'admin', 1724030366000);
|
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (247, '203004', 'GIT_BINARY_CONFLICT_ERROR', '发现二进制文件冲突;手动解决二进制文件中的冲突', 'zh', '', 'admin', 1724030366000);
|
||||||
|
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (248, '203005', 'GIT_MERGE_NOT_SUPPORTED', 'Cannot merge in the {0} state', 'en', '', 'admin', 1724030366000);
|
||||||
|
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (249, '203005', 'GIT_MERGE_NOT_SUPPORTED', '无法在{0}状态下合并', 'zh', '', 'admin', 1724030366000);
|
||||||
|
|
||||||
SET FOREIGN_KEY_CHECKS = 1;
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `
|
|||||||
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2011', 'branch_delete', 'buttons.delete', '2000', 'button', '', '', '', 11, 1722478572000, 1);
|
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2011', 'branch_delete', 'buttons.delete', '2000', 'button', '', '', '', 11, 1722478572000, 1);
|
||||||
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2012', 'mr_view', 'buttons.view', '2000', 'button', '', '', '', 12, 1722478572000, 1);
|
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2012', 'mr_view', 'buttons.view', '2000', 'button', '', '', '', 12, 1722478572000, 1);
|
||||||
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2013', 'mr_add', 'buttons.add', '2000', 'button', '', '', '', 13, 1722478572000, 1);
|
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2013', 'mr_add', 'buttons.add', '2000', 'button', '', '', '', 13, 1722478572000, 1);
|
||||||
|
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2014', 'mr_resolve_conflict', 'buttons.mr.resolve.conflict', '2000', 'button', '', '', '', 14, 1722478572000, 1);
|
||||||
|
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2015', 'mr_merge', 'buttons.mr.merge', '2000', 'button', '', '', '', 15, 1722478572000, 1);
|
||||||
|
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('2016', 'mr_close_merge_request', 'buttons.mr.close.merge.request', '2000', 'button', '', '', '', 16, 1722478572000, 1);
|
||||||
|
|
||||||
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('3000', 'pcaps', 'overall.pcaps', '0', 'menu', '', '/pcaps', 'asw-icon icon-Pcaps', 2, 1722478572000, 1);
|
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('3000', 'pcaps', 'overall.pcaps', '0', 'menu', '', '/pcaps', 'asw-icon icon-Pcaps', 2, 1722478572000, 1);
|
||||||
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('3001', 'pacp_view', 'buttons.view', '3000', 'button', '', '', '', 1, 1722478572000, 1);
|
INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('3001', 'pacp_view', 'buttons.view', '3000', 'button', '', '', '', 1, 1722478572000, 1);
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2010');
|
|||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2011');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2011');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2012');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2012');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2013');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2013');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2014');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2015');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '2016');
|
||||||
|
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '3000');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '3000');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '3001');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('owner', '3001');
|
||||||
@@ -89,6 +92,9 @@ INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2010');
|
|||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2011');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2011');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2012');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2012');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2013');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2013');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2014');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2015');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '2016');
|
||||||
|
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '3000');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '3000');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '3001');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('maintainer', '3001');
|
||||||
@@ -147,6 +153,9 @@ INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2008');
|
|||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2010');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2010');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2012');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2012');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2013');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2013');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2014');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2015');
|
||||||
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '2016');
|
||||||
|
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '3000');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '3000');
|
||||||
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '3001');
|
INSERT INTO `sys_role_menu`(`role_id`, `menu_id`) VALUES ('developer', '3001');
|
||||||
|
|||||||
Reference in New Issue
Block a user