Merge remote-tracking branch 'origin/v2-dev' into v2-dev

This commit is contained in:
xiaojunnuo
2024-10-28 21:34:14 +08:00
21 changed files with 335 additions and 22 deletions

View File

@@ -67,6 +67,12 @@ export class AccessController extends CrudController<AccessService> {
return this.ok(access);
}
@Post('/getSecretPlain', { summary: Constants.per.authOnly })
async getSecretPlain(@Body(ALL) body: { id: number; key: string }) {
const value = await this.service.getById(body.id, this.getUserId());
return this.ok(value[body.key]);
}
@Post('/accessTypeDict', { summary: Constants.per.authOnly })
async getAccessTypeDict() {
const list = this.service.getDefineList();

View File

@@ -30,6 +30,7 @@ export class ResetPasswdMiddleware implements IWebMiddleware {
logger.info('开始重置1号管理员用户的密码');
const newPasswd = '123456';
await this.userService.resetPassword(1, newPasswd);
await this.userService.updateStatus(1, 1);
logger.info(`重置1号管理员用户的密码完成新密码为${newPasswd}`);
}
}

View File

@@ -198,6 +198,9 @@ export class UserService extends BaseService<UserEntity> {
}
async resetPassword(userId: any, newPasswd: string) {
if (!userId) {
throw new CommonException('userId不能为空');
}
const param = {
id: userId,
password: newPasswd,
@@ -210,15 +213,19 @@ export class UserService extends BaseService<UserEntity> {
ids = ids.split(',');
ids = ids.map(id => parseInt(id));
}
if (ids instanceof Array) {
if (ids.includes(1)) {
throw new CommonException('不能删除管理员');
}
if (ids.length === 0) {
return;
}
if (ids.includes(1)) {
throw new CommonException('不能删除管理员');
}
await super.delete(ids);
}
async isAdmin(userId: any) {
if (!userId) {
throw new CommonException('userId不能为空');
}
const userRoles = await this.userRoleService.find({
where: {
userId,
@@ -229,4 +236,13 @@ export class UserService extends BaseService<UserEntity> {
return true;
}
}
async updateStatus(id: number, status: number) {
if (!id) {
throw new CommonException('userId不能为空');
}
await this.repository.update(id, {
status,
});
}
}

View File

@@ -55,4 +55,21 @@ export class TencentSslClient {
this.checkRet(res);
return res;
}
async DescribeCertificates(params: any) {
const client = await this.getSslClient();
const res = await client.DescribeCertificates(params);
this.checkRet(res);
return res;
}
async doRequest(action: string, params: any) {
const client = await this.getSslClient();
if (!client[action]) {
throw new Error(`action ${action} not found`);
}
const res = await client[action](params);
this.checkRet(res);
return res;
}
}

View File

@@ -0,0 +1,202 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AbstractPlusTaskPlugin, TencentAccess } from '@certd/plugin-plus';
import { TencentSslClient } from '../../lib/index.js';
import dayjs from 'dayjs';
import { remove } from 'lodash-es';
@IsTaskPlugin({
name: 'TencentDeleteExpiringCert',
title: '删除腾讯云即将过期证书',
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: '仅删除未使用的证书',
default: {
strategy: {
runStrategy: RunStrategy.AlwaysRun,
},
},
needPlus: true,
})
export class TencentDeleteExpiringCert extends AbstractPlusTaskPlugin {
@TaskInput({
title: 'Access提供者',
helper: 'access 授权',
component: {
name: 'access-selector',
type: 'tencent',
},
required: true,
})
accessId!: string;
@TaskInput({
title: '关键字筛选',
helper: '仅匹配ID、备注名称、域名包含关键字的证书可以不填',
required: false,
component: {
name: 'a-input',
},
})
searchKey!: string;
@TaskInput({
title: '最大删除数量',
helper: '单次运行最大删除数量',
value: 100,
component: {
name: 'a-input-number',
vModel: 'value',
},
required: true,
})
maxCount!: number;
@TaskInput({
title: '即将过期天数',
helper:
'仅删除有效期小于此天数的证书,\n<span class="color-red">注意:`1.26.14`版本之前Certd创建的证书流水线默认是到期前20天才更新证书需要将之前创建的证书申请任务的更新天数改为35天保证删除之前就已经替换掉即将过期证书</span>',
value: 30,
component: {
name: 'a-input-number',
vModel: 'value',
},
required: true,
})
expiringDays!: number;
@TaskInput({
title: '检查超时时间',
helper: '检查删除任务结果超时时间,单位分钟',
value: 10,
component: {
name: 'a-input-number',
vModel: 'value',
},
required: true,
})
checkTimeout!: number;
async onInstance() {}
async execute(): Promise<void> {
const access = await this.accessService.getById<TencentAccess>(this.accessId);
const sslClient = new TencentSslClient({
access,
logger: this.logger,
});
const params = {
Limit: this.maxCount ?? 100,
SearchKey: this.searchKey,
ExpirationSort: 'ASC',
FilterSource: 'upload',
// FilterExpiring: 1,
};
const res = await sslClient.DescribeCertificates(params);
let certificates = res?.Certificates;
if (!certificates && !certificates.length) {
this.logger.info('没有找到证书');
return;
}
certificates = certificates.filter((item: any) => {
const endTime = item.CertEndTime;
return dayjs(endTime).add(this.expiringDays, 'day').isBefore(dayjs());
});
for (const certificate of certificates) {
this.logger.info(`证书ID:${certificate.CertificateId}, 过期时间:${certificate.CertEndTime}Alias:${certificate.Alias},证书域名:${certificate.Domain}`);
}
this.logger.info(`即将过期的证书数量:${certificates.length}`);
if (certificates.length === 0) {
this.logger.info('没有即将过期的证书, 无需删除');
return;
}
const certIds = certificates.map((cert: any) => cert.CertificateId);
const deleteRes = await sslClient.doRequest('DeleteCertificates', {
CertificateIds: certIds,
IsSync: true,
});
this.logger.info('删除任务已提交: ', JSON.stringify(deleteRes));
const ids = deleteRes?.CertTaskIds;
if (!ids && !ids.length) {
this.logger.error('没有找到任务ID');
return;
}
const taskIds = ids.map((id: any) => id.TaskId);
const startTime = Date.now();
const results = {};
const statusCount = {
success: 0,
failed: 0,
unauthorized: 0,
unbind: 0,
timeout: 0,
};
const total = taskIds.length;
while (Date.now() < startTime + this.checkTimeout * 60 * 1000) {
this.checkSignal();
const taskResultRes = await sslClient.doRequest('DescribeDeleteCertificatesTaskResult', {
TaskIds: taskIds,
});
const result = taskResultRes.DeleteTaskResult;
if (!result || result.length === 0) {
this.logger.info('暂未获取到有效的任务结果');
continue;
}
for (const item of result) {
//遍历结果
const status = item.Status;
if (status !== 0) {
remove(taskIds, id => id === item.TaskId);
}
// Status : 0表示任务进行中、 1表示任务成功、 2表示任务失败、3表示未授权服务角色导致任务失败、4表示有未解绑的云资源导致任务失败、5表示查询关联云资源超时导致任务失败
if (status === 0) {
this.logger.info(`任务${item.TaskId}<${item.CertId}>: 进行中`);
} else if (status === 1) {
this.logger.info(`任务${item.TaskId}<${item.CertId}>: 成功`);
results[item.TaskId] = '成功';
statusCount.success++;
} else if (status === 2) {
this.logger.error(`任务${item.TaskId}<${item.CertId}>: 失败`);
results[item.TaskId] = '失败';
statusCount.failed++;
} else if (status === 3) {
this.logger.error(`任务${item.TaskId}<${item.CertId}>: 未授权服务角色导致任务失败`);
results[item.TaskId] = '未授权服务角色导致任务失败';
statusCount.unauthorized++;
} else if (status === 4) {
this.logger.error(`任务${item.TaskId}<${item.CertId}>: 有未解绑的云资源导致任务失败`);
results[item.TaskId] = '有未解绑的云资源导致任务失败';
statusCount.unbind++;
} else if (status === 5) {
this.logger.error(`任务${item.TaskId}<${item.CertId}>: 查询关联云资源超时导致任务失败`);
results[item.TaskId] = '查询关联云资源超时导致任务失败';
statusCount.timeout++;
} else {
this.logger.info(`任务${item.TaskId}<${item.CertId}>: 未知状态:${status}`);
statusCount.failed++;
}
}
this.logger.info(
// eslint-disable-next-line max-len
`任务总数:${total}, 进行中:${taskIds.length} 成功:${statusCount.success}, 未授权服务角色导致失败:${statusCount.unauthorized}, 未解绑关联资源失败:${statusCount.unbind}, 查询关联资源超时:${statusCount.timeout},未知原因失败:${statusCount.failed}`
);
if (taskIds.length === 0) {
this.logger.info('任务已全部完成');
if (statusCount.unauthorized > 0) {
throw new Error('有未授权服务角色导致任务失败需给Access授权服务角色SSL_QCSLinkedRoleInReplaceLoadCertificate');
}
return;
}
await this.ctx.utils.sleep(10000);
}
this.logger.error('检查任务结果超时', JSON.stringify(results));
}
}
new TencentDeleteExpiringCert();

View File

@@ -5,3 +5,4 @@ export * from './deploy-to-cdn-v2/index.js';
export * from './upload-to-tencent/index.js';
export * from './deploy-to-cos/index.js';
export * from './deploy-to-eo/index.js';
export * from './delete-expiring-cert/index.js';