v2
xiaojunnuo 2025-01-15 22:58:11 +08:00
parent 6877b865a7
commit 87620b9072
13 changed files with 130 additions and 68 deletions

View File

@ -72,19 +72,19 @@ export const Constants = {
},
openKeyError: {
code: 20000,
message: 'openKey错误',
message: 'ApiToken错误',
},
openKeySignError: {
code: 20001,
message: 'openKey签名错误',
message: 'ApiToken签名错误',
},
openKeyExpiresError: {
code: 20002,
message: 'openKey时间戳错误',
message: 'ApiToken时间戳错误',
},
openKeySignTypeError: {
code: 20003,
message: 'openKey签名类型不支持',
message: 'ApiToken签名类型不支持',
},
openParamError: {
code: 20010,
@ -94,5 +94,9 @@ export const Constants = {
code: 20011,
message: '证书不存在',
},
openCertNotReady: {
code: 20012,
message: '证书还未生成',
},
},
};

View File

@ -2,10 +2,10 @@
*
*/
export class BaseException extends Error {
status: number;
code: number;
constructor(name, code, message) {
super(message);
this.name = name;
this.status = code;
this.code = code;
}
}

View File

@ -9,7 +9,7 @@ export class Result<T> {
}
static error(code = 1, msg) {
return new Result(code, msg, null);
return new Result(code, msg);
}
static success(msg, data?) {

View File

@ -126,32 +126,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: false
}
},
"pipeline.title": {
title: "已关联流水线",
search: { show: false },
type: "link",
form: {
show: false
},
column: {
width: 280,
sorter: true,
component: {}
}
},
applyTime: {
title: "申请时间",
search: {
show: false
},
type: "datetime",
form: {
show: false
},
column: {
sorter: true
}
},
expiresTime: {
title: "过期时间",
search: {
@ -175,18 +149,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}
}
},
fromType: {
title: "来源",
search: {
show: true
},
type: "text",
form: { show: false },
column: {
width: 100,
sorter: true
}
},
certProvider: {
title: "证书颁发机构",
search: {
@ -197,7 +159,33 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: false
},
column: {
width: 400
width: 200
}
},
applyTime: {
title: "申请时间",
search: {
show: false
},
type: "datetime",
form: {
show: false
},
column: {
sorter: true
}
},
"pipeline.title": {
title: "已关联流水线",
search: { show: false },
type: "link",
form: {
show: false
},
column: {
width: 350,
sorter: true,
component: {}
}
}
}

View File

@ -42,10 +42,11 @@ export function createApi() {
params: { id }
});
},
async ListAll() {
async GetApiToken(id: number) {
return await request({
url: apiPrefix + "/all",
method: "post"
url: apiPrefix + "/getApiToken",
method: "post",
data: { id }
});
}
};

View File

@ -72,13 +72,30 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}
},
rowHandle: {
width: 200,
width: 300,
fixed: "right",
buttons: {
view: { show: true },
copy: { show: false },
edit: { show: false },
remove: { show: true }
remove: { show: true },
gen: {
text: "测试ApiToken",
async click({ row }) {
const apiToken = await api.GetApiToken(row.id);
Modal.info({
title: "ApiToken",
content: () => {
return (
<div>
<div>ApiKey3使</div>
<div>{apiToken}</div>
</div>
);
}
});
}
}
}
},
columns: {

View File

@ -19,7 +19,6 @@ import * as libServer from '@certd/lib-server';
import * as commercial from '@certd/commercial-core';
import * as upload from '@midwayjs/upload';
import { setLogger } from '@certd/acme-client';
process.on('uncaughtException', error => {
console.error('未捕获的异常:', error);
// 在这里可以添加日志记录、发送错误通知等操作

View File

@ -10,6 +10,6 @@ export class BaseOpenController extends BaseController {
const encrypted = encryptor.encrypt(data);
return this.ok(encrypted);
}
super.ok(res);
return super.ok(res);
}
}

View File

@ -6,7 +6,8 @@ import { OpenKey } from '../../../modules/open/service/open-key-service.js';
import { BaseOpenController } from '../base-open-controller.js';
export type CertGetReq = {
domains: string;
domains?: string;
certId: number;
};
/**
@ -25,17 +26,13 @@ export class OpenCertController extends BaseOpenController {
const openKey: OpenKey = this.ctx.openKey;
const userId = openKey.userId;
if (!userId) {
return Constants.res.openKeyError;
throw new CodeException(Constants.res.openKeyError);
}
const domains = bean.domains || query.domains;
if (!domains) {
throw new CodeException(Constants.res.openParamError);
}
const domainArr = domains.split(',');
const res: CertInfo = await this.certInfoService.getCertInfo({
userId,
domains: domainArr,
domains: bean.domains || query.domains,
certId: bean.certId || query.certId,
});
return this.ok(res);
}

View File

@ -62,4 +62,11 @@ export class OpenKeyController extends CrudController<OpenKeyService> {
await this.service.checkUserId(id, this.getUserId());
return await super.delete(id);
}
@Post('/getApiToken', { summary: Constants.per.authOnly })
async getApiToken(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
const token = await this.service.getApiToken(id);
return this.ok(token);
}
}

View File

@ -88,8 +88,7 @@ export class AuthorityMiddleware implements IWebMiddleware {
async doOpenHandler(ctx: IMidwayKoaContext, next: Next) {
//开放接口
let openKey = ctx.get('Authorization') || '';
openKey = openKey.replace('Bearer ', '').trim();
const openKey = ctx.get('x-api-token') || '';
if (!openKey) {
ctx.status = 401;
ctx.body = Constants.res.auth;

View File

@ -72,10 +72,29 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
});
}
async getCertInfo(param: { domains: string[]; userId: number }) {
const { domains, userId } = param;
async getCertInfo(params: { domains?: string; certId?: number; userId: number }) {
const { domains, certId, userId } = params;
if (certId) {
return await this.getCertInfoById({ id: certId, userId });
}
return await this.getCertInfoByDomains({
domains,
userId,
});
}
private async getCertInfoByDomains(params: { domains: string; userId: number }) {
const { domains, userId } = params;
if (!domains) {
throw new CodeException(Constants.res.openCertNotFound);
}
const domainArr = domains.split(',');
const list = await this.find({
select: {
id: true,
domains: true,
},
where: {
userId,
},
@ -83,12 +102,25 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
//遍历查找
const matched = list.find(item => {
const itemDomains = item.domains.split(',');
return utils.domain.match(domains, itemDomains);
return utils.domain.match(domainArr, itemDomains);
});
if (!matched || !matched.certInfo) {
if (!matched) {
throw new CodeException(Constants.res.openCertNotFound);
}
const certInfo = JSON.parse(matched.certInfo) as CertInfo;
return await this.getCertInfoById({ id: matched.id, userId: userId });
}
async getCertInfoById(req: { id: number; userId: number }) {
const entity = await this.info(req.id);
if (!entity || entity.userId !== req.userId) {
throw new CodeException(Constants.res.openCertNotFound);
}
if (!entity.certInfo) {
throw new CodeException(Constants.res.openCertNotReady);
}
const certInfo = JSON.parse(entity.certInfo) as CertInfo;
const certReader = new CertReader(certInfo);
return certReader.toCertInfo();
}

View File

@ -5,6 +5,7 @@ import { Repository } from 'typeorm';
import { OpenKeyEntity } from '../entity/open-key.js';
import { utils } from '@certd/basic';
import crypto from 'crypto';
import dayjs from 'dayjs';
export type OpenKey = {
userId: number;
@ -85,4 +86,21 @@ export class OpenKeyService extends BaseService<OpenKeyEntity> {
encrypt: encrypt,
};
}
async getApiToken(id: number) {
const entity = await this.repository.findOne({ where: { id } });
if (!entity) {
throw new Error('id不存在');
}
const { keyId, keySecret } = entity;
const openKey = {
keyId,
t: dayjs().unix(),
encrypt: false,
signType: 'md5',
};
const content = JSON.stringify(openKey);
const sign = utils.hash.md5(content + keySecret);
return Buffer.from(content).toString('base64') + '.' + sign;
}
}