feat: 支持审计日志,操作日志

This commit is contained in:
xiaojunnuo
2026-07-12 02:29:54 +08:00
parent 4250d0e266
commit f2855d6dac
83 changed files with 1252 additions and 1341 deletions
@@ -19,7 +19,6 @@ import sysauthority from "./certd/sys-authority";
import syscname from "./certd/sys-cname";
import tutorial from "./certd/tutorial";
import cron from "./certd/cron";
import audit from "./certd/audit";
// Note: @ is reserved in locale messages; use {'@'} when needed.
export default {
@@ -44,5 +43,4 @@ export default {
...syscname,
...tutorial,
...cron,
...audit,
};
@@ -1,4 +0,0 @@
export default {
"certd.auditLog": "Audit Log",
"certd.sysResources.auditLog": "Audit Log",
};
@@ -18,6 +18,7 @@ export default {
openKey: "Open API Key",
notification: "Notification Settings",
siteMonitorSetting: "Site Monitor Settings",
auditLog: "Audit Log",
userSecurity: "Security Settings",
userProfile: "Account Info",
userGrant: "Grant Delegation",
@@ -67,6 +68,7 @@ export default {
projectJoin: "Join Project",
currentProject: "Current Project",
projectMemberManager: "Project Member",
auditLog: "Audit Log",
domainMonitorSetting: "Domain Monitor Settings",
},
};
@@ -19,7 +19,6 @@ import sysauthority from "./certd/sys-authority";
import syscname from "./certd/sys-cname";
import tutorial from "./certd/tutorial";
import cron from "./certd/cron";
import audit from "./certd/audit";
// Note: @ is reserved in locale messages; use {'@'} when needed.
export default {
@@ -44,5 +43,4 @@ export default {
...syscname,
...tutorial,
...cron,
...audit,
};
@@ -1,4 +0,0 @@
export default {
"certd.auditLog": "操作日志",
"certd.sysResources.auditLog": "操作日志",
};
@@ -18,6 +18,7 @@ export default {
openKey: "开放接口密钥",
notification: "通知设置",
siteMonitorSetting: "站点监控设置",
auditLog: "操作日志",
userSecurity: "认证安全设置",
userProfile: "账号信息",
userGrant: "授权委托",
@@ -67,6 +68,7 @@ export default {
projectJoin: "加入项目",
currentProject: "当前项目",
projectMemberManager: "项目成员管理",
auditLog: "审计日志",
domainMonitorSetting: "域名监控设置",
jobHistory: "监控执行记录",
},
@@ -296,6 +296,11 @@ export const certdResources = [
icon: "ion:document-text-outline",
auth: true,
keepAlive: true,
isMenu: true,
show: () => {
const settingStore = useSettingStore();
return settingStore.isPlus;
},
},
},
{
@@ -417,9 +417,13 @@ export const sysResources = [
component: "/sys/audit/index.vue",
meta: {
icon: "ion:document-text-outline",
permission: "sys:settings:view",
keepAlive: true,
auth: true,
isMenu: true,
show: () => {
const settingStore = useSettingStore();
return settingStore.isPlus;
},
},
},
{
@@ -1,4 +1,6 @@
import { CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, UserPageQuery, UserPageRes, dict } from "@fast-crud/fast-crud";
import { useI18n } from "/src/locales";
import { useDicts } from "../dicts";
const typeDict = dict({
url: "/pi/audit/dict",
@@ -21,6 +23,8 @@ const actionDict = dict({
});
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n();
const { myProjectDict } = useDicts();
const api = context.api;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@@ -45,6 +49,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
view: { show: false },
edit: { show: false },
remove: { show: true },
copy: { show: false },
},
},
columns: {
@@ -61,7 +66,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: true,
component: {
name: "a-range-picker",
vModel: ["createTime_start", "createTime_end"],
},
},
column: { width: 170, sorter: true },
@@ -77,17 +81,16 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
},
action: {
title: "操作动作",
type: "dict-select",
dict: actionDict,
type: "text",
search: { show: true },
column: { width: 100 },
column: { width: 200, tooltip: true },
form: { show: false },
},
content: {
title: "内容",
title: "备注",
type: "text",
search: { show: true },
column: { minWidth: 300 },
column: { width: 700, tooltip: true },
form: { show: false },
},
ipAddress: {
@@ -96,6 +99,14 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
column: { width: 140 },
form: { show: false },
},
projectId: {
title: t("certd.fields.projectName"),
type: "dict-select",
dict: myProjectDict,
form: {
show: false,
},
},
},
},
};
@@ -1,4 +1,7 @@
import { CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, UserPageQuery, UserPageRes, dict } from "@fast-crud/fast-crud";
import { Modal } from "ant-design-vue";
import { useI18n } from "/src/locales";
import { useDicts } from "/@/views/certd/dicts";
const typeDict = dict({
url: "/sys/audit/dict",
@@ -21,6 +24,8 @@ const actionDict = dict({
});
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n();
const { myProjectDict } = useDicts();
const api = context.api;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@@ -31,19 +36,39 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
};
const cleanExpired = async () => {
await api.Clean(90);
crudExpose.doRefresh();
Modal.confirm({
title: "确认清理",
content: "确定要清理90天前的审计日志吗?此操作不可撤销。",
okText: "确认清理",
okType: "danger",
cancelText: "取消",
async onOk() {
await api.Clean(90);
crudExpose.doRefresh();
},
});
};
return {
crudOptions: {
request: { pageRequest, delRequest },
tabs: {
name: "scope",
show: true,
dict: {
data: [
{ value: "system", label: "系统级", color: "red" },
{ value: "user", label: "用户级", color: "blue" },
],
},
},
actionbar: {
buttons: {
add: { show: false },
clean: {
text: "清理过期日志(90天)",
type: "default",
type: "primary",
danger: true,
click: cleanExpired,
},
},
@@ -55,12 +80,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
view: { show: false },
edit: { show: false },
remove: { show: true },
copy: { show: false },
},
},
search: {
initialForm: {
sort: { prop: "id", asc: false },
},
initialForm: { scope: "system" },
},
columns: {
id: {
@@ -76,13 +100,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: true,
component: {
name: "a-range-picker",
vModel: ["createTime_start", "createTime_end"],
},
},
column: { width: 170, sorter: true },
form: { show: false },
},
userName: {
username: {
title: "操作人",
type: "text",
search: { show: true },
@@ -90,7 +113,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
form: { show: false },
},
type: {
title: "操作类型",
title: "类型",
type: "dict-select",
dict: typeDict,
search: { show: true },
@@ -98,18 +121,17 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
form: { show: false },
},
action: {
title: "操作动作",
type: "dict-select",
dict: actionDict,
title: "操作",
type: "text",
search: { show: true },
column: { width: 100 },
column: { width: 200, tooltip: true },
form: { show: false },
},
content: {
title: "内容",
title: "备注",
type: "text",
search: { show: true },
column: { minWidth: 300 },
column: { width: 500, tooltip: true },
form: { show: false },
},
ipAddress: {
@@ -118,6 +140,27 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
column: { width: 140 },
form: { show: false },
},
projectId: {
title: t("certd.fields.projectName"),
type: "dict-select",
dict: myProjectDict,
form: {
show: false,
},
},
scope: {
title: "范围",
type: "dict-select",
dict: dict({
data: [
{ value: "system", label: "系统级", color: "blue" },
{ value: "user", label: "用户级", color: "green" },
],
}),
search: { show: true },
column: { width: 100 },
form: { show: false },
},
},
},
};
@@ -0,0 +1,2 @@
ALTER TABLE `cd_audit_log` ADD COLUMN `scope` varchar(32) NOT NULL DEFAULT 'user';
CREATE INDEX `index_audit_log_scope` ON `cd_audit_log` (`scope`);
@@ -0,0 +1,2 @@
ALTER TABLE "cd_audit_log" ADD COLUMN "scope" varchar(32) NOT NULL DEFAULT ('user');
CREATE INDEX "index_audit_log_scope" ON "cd_audit_log" ("scope");
@@ -0,0 +1,2 @@
ALTER TABLE "cd_audit_log" ADD COLUMN "scope" varchar(32) NOT NULL DEFAULT ('user');
CREATE INDEX "index_audit_log_scope" ON "cd_audit_log" ("scope");
@@ -12,6 +12,7 @@ import cors from "@koa/cors";
import { GlobalExceptionMiddleware } from "./middleware/global-exception.js";
import { PreviewMiddleware } from "./middleware/preview.js";
import { AuthorityMiddleware } from "./middleware/authority.js";
import { AuditLogMiddleware } from "./middleware/audit-log.js";
import { logger } from "@certd/basic";
import { ResetPasswdMiddleware } from "./middleware/reset-passwd/middleware.js";
import DefaultConfig from "./config/config.default.js";
@@ -113,6 +114,7 @@ export class MainConfiguration {
PreviewMiddleware,
//授权处理
AuthorityMiddleware,
AuditLogMiddleware,
//resetPasswd,重置密码模式下不提供服务
ResetPasswdMiddleware,
@@ -132,5 +134,6 @@ export class MainConfiguration {
logger.info(text);
});
logger.info("当前环境:", this.app.getEnv()); // prod
}
}
@@ -1,8 +1,9 @@
import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core";
import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core";
import { BaseController, CommonException, Constants, SysSettingsService } from "@certd/lib-server";
import { CodeService } from "../../../modules/basic/service/code-service.js";
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
import { LoginService } from "../../../modules/login/service/login-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -19,7 +20,11 @@ export class ForgotPasswordController extends BaseController {
@Inject()
sysSettingsService: SysSettingsService;
@Post("/forgotPassword", { description: Constants.per.guest })
getAuditType(): string {
return AuditType.login;
}
@Post("/forgotPassword", { description: Constants.per.guest, summary: "找回密码" })
public async forgotPassword(
@Body(ALL)
body: any
@@ -28,15 +33,13 @@ export class ForgotPasswordController extends BaseController {
if (!sysSettings.selfServicePasswordRetrievalEnabled) {
throw new CommonException("暂未开启自助找回");
}
// 找回密码的验证码允许错误次数
const maxErrorCount = 5;
if (body.type === "email") {
this.codeService.checkEmailCode({
verificationType: "forgotPassword",
email: body.input,
validateCode: body.validateCode,
maxErrorCount: maxErrorCount,
maxErrorCount: 5,
throwError: true,
});
} else if (body.type === "mobile") {
@@ -45,14 +48,15 @@ export class ForgotPasswordController extends BaseController {
mobile: body.input,
phoneCode: body.phoneCode,
smsCode: body.validateCode,
maxErrorCount: maxErrorCount,
maxErrorCount: 5,
throwError: true,
});
} else {
throw new CommonException("暂不支持的找回类型,请联系管理员找回");
}
const username = await this.userService.forgotPassword(body);
const {id,username} = await this.userService.forgotPassword(body);
username && this.loginService.clearCacheOnSuccess(username);
this.auditLog({ userId: id, content: "用户请求找回密码" });
return this.ok();
}
}
@@ -1,10 +1,11 @@
import { ALL, Body, Controller, Inject, Post, Provide, RequestIP } from "@midwayjs/core";
import { ALL, Body, Controller, Inject, Post, Provide, RequestIP } from "@midwayjs/core";
import { LoginService } from "../../../modules/login/service/login-service.js";
import { AddonService, BaseController, Constants, SysPublicSettings, SysSettingsService } from "@certd/lib-server";
import { CodeService } from "../../../modules/basic/service/code-service.js";
import { checkComm } from "@certd/plus-core";
import { CaptchaService } from "../../../modules/basic/service/captcha-service.js";
import { PasskeyService } from "../../../modules/login/service/passkey-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -27,7 +28,11 @@ export class LoginController extends BaseController {
@Inject()
passkeyService: PasskeyService;
@Post("/login", { description: Constants.per.guest })
getAuditType(): string {
return AuditType.login;
}
@Post("/login", { description: Constants.per.guest, summary: "用户名密码登录" })
public async login(
@Body(ALL)
body: any,
@@ -38,16 +43,22 @@ export class LoginController extends BaseController {
if (settings.captchaEnabled === true) {
await this.captchaService.doValidate({ form: body.captcha, must: false, captchaAddonId: settings.captchaAddonId, req: { remoteIp } });
}
const token = await this.loginService.loginByPassword(body);
this.writeTokenCookie(token);
return this.ok(token);
try {
const token = await this.loginService.loginByPassword(body);
this.writeTokenCookie(token);
this.auditLog({ userId: token.userId, username: body.username, content: `用户「${body.username}」登录成功` });
return this.ok(token);
} catch (err:any) {
this.auditLog({userId:err.userId, username: body.username, content: `用户「${body.username}」登录失败` });
throw err;
}
}
private writeTokenCookie(token: { expire: any; token: any }) {
// this.loginService.writeTokenCookie(this.ctx,token);
}
@Post("/loginBySms", { description: Constants.per.guest })
@Post("/loginBySms", { description: Constants.per.guest, summary: "短信验证码登录" })
public async loginBySms(
@Body(ALL)
body: any
@@ -58,31 +69,42 @@ export class LoginController extends BaseController {
}
checkComm();
const token = await this.loginService.loginBySmsCode({
phoneCode: body.phoneCode,
mobile: body.mobile,
smsCode: body.smsCode,
randomStr: body.randomStr,
inviteCode: body.inviteCode,
});
try {
const token = await this.loginService.loginBySmsCode({
phoneCode: body.phoneCode,
mobile: body.mobile,
smsCode: body.smsCode,
randomStr: body.randomStr,
inviteCode: body.inviteCode,
});
this.writeTokenCookie(token);
return this.ok(token);
this.writeTokenCookie(token);
this.auditLog({ userId: token.userId, username: body.mobile, content: `用户「${body.mobile}」短信登录成功` });
return this.ok(token);
} catch (err) {
this.auditLog({ userId: err.userId, username: body.mobile, content: `用户「${body.mobile}」短信登录失败` });
throw err;
}
}
@Post("/loginByTwoFactor", { description: Constants.per.guest })
@Post("/loginByTwoFactor", { description: Constants.per.guest, summary: "两步验证登录" })
public async loginByTwoFactor(
@Body(ALL)
body: any
) {
const token = await this.loginService.loginByTwoFactor({
loginId: body.loginId,
verifyCode: body.verifyCode,
});
try {
const token = await this.loginService.loginByTwoFactor({
loginId: body.loginId,
verifyCode: body.verifyCode,
});
this.writeTokenCookie(token);
return this.ok(token);
this.writeTokenCookie(token);
this.auditLog({ userId: token.userId, username: body.loginId, content: `用户「${body.loginId}」两步验证登录成功` });
return this.ok(token);
} catch (err) {
this.auditLog({ userId: err.userId, username: body.loginId, content: `用户「${body.loginId}」两步验证登录失败` });
throw err;
}
}
@Post("/passkey/generateAuthentication", { description: Constants.per.guest })
@@ -92,24 +114,30 @@ export class LoginController extends BaseController {
return this.ok(options);
}
@Post("/loginByPasskey", { description: Constants.per.guest })
@Post("/loginByPasskey", { description: Constants.per.guest, summary: "Passkey登录" })
public async loginByPasskey(
@Body(ALL)
body: any
) {
const credential = body.credential;
const challenge = body.challenge;
try {
const credential = body.credential;
const challenge = body.challenge;
const token = await this.loginService.loginByPasskey(
{
credential,
challenge,
},
this.ctx
);
const token = await this.loginService.loginByPasskey(
{
credential,
challenge,
},
this.ctx
);
// this.writeTokenCookie(token);
return this.ok(token);
this.writeTokenCookie(token);
this.auditLog({ userId: token.userId, content: "用户Passkey登录成功" });
return this.ok(token);
} catch (err) {
this.auditLog({ userId: err.userId, content: "用户Passkey登录失败" });
throw err;
}
}
@Post("/logout", { description: Constants.per.authOnly })
@@ -1,4 +1,4 @@
import { logger, simpleNanoId, utils } from "@certd/basic";
import { logger, simpleNanoId, utils } from "@certd/basic";
import { addonRegistry, AddonService, BaseController, Constants, SysInstallInfo, SysSettingsService } from "@certd/lib-server";
import { checkPlus } from "@certd/plus-core";
import { ALL, Body, Controller, Get, Inject, Param, Post, Provide, Query } from "@midwayjs/core";
@@ -8,6 +8,7 @@ import { LoginService } from "../../../modules/login/service/login-service.js";
import { OauthBoundService } from "../../../modules/login/service/oauth-bound-service.js";
import { AddonGetterService } from "../../../modules/pipeline/service/addon-getter-service.js";
import { UserEntity } from "../../../modules/sys/authority/entity/user.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
import { IOauthProvider } from "../../../plugins/plugin-oauth/api.js";
type OauthProviderSetting = {
@@ -48,6 +49,10 @@ export class ConnectController extends BaseController {
@Inject()
addonService: AddonService;
getAuditType(): string {
return AuditType.login;
}
private async getOauthProvider(type: string) {
const publicSettings = await this.sysSettingsService.getPublicSettings();
if (!publicSettings?.oauthEnabled) {
@@ -68,12 +73,11 @@ export class ConnectController extends BaseController {
};
}
@Post("/login", { description: Constants.per.guest })
@Post("/login", { description: Constants.per.guest, summary: "第三方登录" })
public async login(@Body(ALL) body: { type: string; subtype?: string; forType?: string; from?: string }) {
const oauthProvider = await this.getOauthProvider(body.type);
const installInfo = await this.sysSettingsService.getSetting<SysInstallInfo>(SysInstallInfo);
const bindUrl = installInfo?.bindUrl || "";
//构造登录url
const redirectUrl = `${bindUrl}api/oauth/callback/${body.type}`;
const stateObj = {
@@ -95,8 +99,6 @@ export class ConnectController extends BaseController {
});
this.ctx.cookies.set("oauth_ticket", ticket, {
httpOnly: true,
// secure: true,
// sameSite: "strict",
});
return this.ok({ loginUrl, ticket });
}
@@ -153,7 +155,7 @@ export class ConnectController extends BaseController {
}
}
@Post("/getLogoutUrl", { description: Constants.per.guest })
@Post("/getLogoutUrl", { description: Constants.per.guest, summary: "第三方登出" })
public async logout(@Body(ALL) body: any) {
checkPlus();
const oauthProvider = await this.getOauthProvider(body.type);
@@ -195,7 +197,7 @@ export class ConnectController extends BaseController {
// this.loginService.writeTokenCookie(this.ctx,token);
}
@Post("/autoRegister", { description: Constants.per.guest })
@Post("/autoRegister", { description: Constants.per.guest, summary: "第三方自动注册" })
public async autoRegister(@Body(ALL) body: { validationCode: string; type: string; inviteCode?: string }) {
const validationValue = this.codeService.getValidationValue(body.validationCode);
if (!validationValue) {
@@ -219,12 +221,12 @@ export class ConnectController extends BaseController {
const loginRes = await this.loginService.generateToken(newUser);
this.writeTokenCookie(loginRes);
this.auditLog({ userId: newUser.id, content: `第三方账号自动注册,类型: ${body.type}` });
return this.ok(loginRes);
}
@Post("/bind", { description: Constants.per.loginOnly })
@Post("/bind", { description: Constants.per.loginOnly, summary: "绑定第三方账号" })
public async bind(@Body(ALL) body: any) {
//需要已登录
const userId = this.getUserId();
const validationValue = this.codeService.getValidationValue(body.validationCode);
if (!validationValue) {
@@ -238,17 +240,18 @@ export class ConnectController extends BaseController {
type,
openId,
});
this.auditLog({ userId, content: `第三方账号绑定,类型: ${body.type}` });
return this.ok(1);
}
@Post("/unbind", { description: Constants.per.loginOnly })
@Post("/unbind", { description: Constants.per.loginOnly, summary: "解绑第三方账号" })
public async unbind(@Body(ALL) body: any) {
//需要已登录
const userId = this.getUserId();
await this.oauthBoundService.unbind({
userId,
type: body.type,
});
this.auditLog({ userId, content: `第三方账号解绑,类型: ${body.type}` });
return this.ok(1);
}
@@ -1,9 +1,10 @@
import { ALL, Body, Controller, Inject, Post, Provide, RequestIP } from "@midwayjs/core";
import { ALL, Body, Controller, Inject, Post, Provide, RequestIP } from "@midwayjs/core";
import { BaseController, Constants, SysSettingsService } from "@certd/lib-server";
import { RegisterType } from "../../../modules/sys/authority/service/user-service.js";
import { CodeService } from "../../../modules/basic/service/code-service.js";
import { checkComm, checkPlus } from "@certd/plus-core";
import { LoginService } from "../../../modules/login/service/login-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
export type RegisterReq = {
type: RegisterType;
@@ -31,7 +32,11 @@ export class RegisterController extends BaseController {
@Inject()
sysSettingsService: SysSettingsService;
@Post("/register", { description: Constants.per.guest })
getAuditType(): string {
return AuditType.login;
}
@Post("/register", { description: Constants.per.guest, summary: "用户注册" })
public async register(
@Body(ALL)
body: RegisterReq,
@@ -60,6 +65,7 @@ export class RegisterController extends BaseController {
password: body.password,
} as any;
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
this.auditLog({ userId: newUser.id, username: body.username, content: `用户「${body.username}」注册成功` });
return this.ok(newUser);
} else if (body.type === "mobile") {
if (sysPublicSettings.mobileRegisterEnabled === false) {
@@ -80,6 +86,7 @@ export class RegisterController extends BaseController {
password: body.password,
} as any;
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
this.auditLog({ userId: newUser.id, username: body.mobile, content: `用户「${body.mobile}」注册成功` });
return this.ok(newUser);
} else if (body.type === "email") {
if (sysPublicSettings.emailRegisterEnabled === false) {
@@ -97,6 +104,7 @@ export class RegisterController extends BaseController {
password: body.password,
} as any;
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
this.auditLog({ userId: newUser.id, username: body.email, content: `用户「${body.email}」注册成功` });
return this.ok(newUser);
}
}
@@ -1,5 +1,6 @@
import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core";
import { BaseController, PlusService, SysInstallInfo, SysSettingsService } from "@certd/lib-server";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
export type PreBindUserReq = {
userId: number;
@@ -18,17 +19,21 @@ export class BasicController extends BaseController {
@Inject()
sysSettingsService: SysSettingsService;
@Post("/preBindUser", { description: "sys:settings:edit" })
getAuditType(): string {
return AuditType.account;
}
@Post("/preBindUser", { description: "sys:settings:edit", summary: "预绑定用户" })
public async preBindUser(@Body(ALL) body: PreBindUserReq) {
// 设置缓存内容
if (body.userId == null || body.userId <= 0) {
throw new Error("用户ID不能为空");
}
await this.plusService.userPreBind(body.userId);
await this.auditLog({ content: `预绑定了用户(ID:${body.userId})` });
return this.ok({});
}
@Post("/bindUser", { description: "sys:settings:edit" })
@Post("/bindUser", { description: "sys:settings:edit", summary: "绑定用户" })
public async bindUser(@Body(ALL) body: BindUserReq) {
if (body.userId == null || body.userId <= 0) {
throw new Error("用户ID不能为空");
@@ -36,20 +41,23 @@ export class BasicController extends BaseController {
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
installInfo.bindUserId = body.userId;
await this.sysSettingsService.saveSetting(installInfo);
await this.auditLog({ content: `绑定了用户(ID:${body.userId})` });
return this.ok({});
}
@Post("/unbindUser", { description: "sys:settings:edit" })
@Post("/unbindUser", { description: "sys:settings:edit", summary: "解绑用户" })
public async unbindUser() {
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
installInfo.bindUserId = null;
await this.sysSettingsService.saveSetting(installInfo);
await this.auditLog({ content: "解绑了用户" });
return this.ok({});
}
@Post("/updateLicense", { description: "sys:settings:edit" })
@Post("/updateLicense", { description: "sys:settings:edit", summary: "更新许可证" })
public async updateLicense(@Body(ALL) body: { license: string }) {
await this.plusService.updateLicense(body.license);
await this.auditLog({ content: "更新了许可证" });
return this.ok(true);
}
}
@@ -13,17 +13,20 @@ export class SysAuditLogController extends BaseController {
@Post("/page", { description: Constants.per.authOnly, summary: "分页查询审计日志" })
async page(@Body(ALL) body: any) {
if (body.query?.scope) {
delete body.query.userId;
}
const pageRet = await this.auditService.page(body);
return this.ok(pageRet);
}
@Post("/delete", { description: Constants.per.authOnly })
@Post("/delete", { description: Constants.per.authOnly, summary: "删除审计日志" })
async delete(@Query("id") id: number) {
await this.auditService.delete(id);
await this.auditService.delete([id]);
return this.ok({});
}
@Post("/clean", { description: Constants.per.authOnly })
@Post("/clean", { description: Constants.per.authOnly, summary: "清理过期审计日志" })
async clean(@Body("retentionDays") retentionDays: number) {
const count = await this.auditService.cleanExpired(retentionDays || 90);
return this.ok({ deletedCount: count });
@@ -1,7 +1,7 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core";
import { CrudController } from "@certd/lib-server";
import { PermissionService } from "../../../modules/sys/authority/service/permission-service.js";
import { AuditType, AuditAction } from "../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -15,8 +15,11 @@ export class PermissionController extends CrudController<PermissionService> {
getService() {
return this.service;
}
getAuditType(): string {
return AuditType.permission;
}
@Post("/page", { description: "sys:auth:per:view" })
@Post("/page", { description: "sys:auth:per:view", summary: "查询权限分页列表" })
async page(
@Body(ALL)
body
@@ -24,48 +27,42 @@ export class PermissionController extends CrudController<PermissionService> {
return await super.page(body);
}
@Post("/add", { description: "sys:auth:per:add" })
@Post("/add", { description: "sys:auth:per:add", summary: "添加权限" })
async add(
@Body(ALL)
bean
) {
const res = await super.add(bean);
await this.auditLog({
type: AuditType.permission,
action: AuditAction.add,
content: `新增了权限「${bean.name}」(ID:${res.data})`,
});
return res;
}
@Post("/update", { description: "sys:auth:per:edit" })
@Post("/update", { description: "sys:auth:per:edit", summary: "更新权限" })
async update(
@Body(ALL)
bean
) {
const res = await super.update(bean);
await this.auditLog({
type: AuditType.permission,
action: AuditAction.update,
content: `修改了权限「${bean.name}」(ID:${bean.id})`,
});
return res;
}
@Post("/delete", { description: "sys:auth:per:remove" })
@Post("/delete", { description: "sys:auth:per:remove", summary: "删除权限" })
async delete(
@Query("id")
id: number
) {
const res = await super.delete(id);
await this.auditLog({
type: AuditType.permission,
action: AuditAction.delete,
content: `删除了权限(ID:${id})`,
});
return res;
}
@Post("/tree", { description: "sys:auth:per:view" })
@Post("/tree", { description: "sys:auth:per:view", summary: "查询权限树" })
async tree() {
const tree = await this.service.tree({});
return this.ok(tree);
@@ -1,7 +1,7 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core";
import { CrudController } from "@certd/lib-server";
import { RoleService } from "../../../modules/sys/authority/service/role-service.js";
import { AuditType, AuditAction } from "../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -16,6 +16,10 @@ export class RoleController extends CrudController<RoleService> {
return this.service;
}
getAuditType(): string {
return AuditType.role;
}
@Post("/page", { description: "sys:auth:role:view" })
async page(
@Body(ALL)
@@ -30,34 +34,30 @@ export class RoleController extends CrudController<RoleService> {
return this.ok(ret);
}
@Post("/add", { description: "sys:auth:role:add" })
@Post("/add", { description: "sys:auth:role:add", summary: "新增角色" })
async add(
@Body(ALL)
bean
) {
const res = await super.add(bean);
await this.auditLog({
type: AuditType.role,
action: AuditAction.add,
content: `新增了角色「${bean.name}」(ID:${res.data})`,
});
return res;
}
@Post("/update", { description: "sys:auth:role:edit" })
@Post("/update", { description: "sys:auth:role:edit", summary: "修改角色" })
async update(
@Body(ALL)
bean
) {
const res = await super.update(bean);
await this.auditLog({
type: AuditType.role,
action: AuditAction.update,
content: `修改了角色「${bean.name}」(ID:${bean.id})`,
});
return res;
}
@Post("/delete", { description: "sys:auth:role:remove" })
@Post("/delete", { description: "sys:auth:role:remove", summary: "删除角色" })
async delete(
@Query("id")
id: number
@@ -67,8 +67,6 @@ export class RoleController extends CrudController<RoleService> {
}
const res = await super.delete(id);
await this.auditLog({
type: AuditType.role,
action: AuditAction.delete,
content: `删除了角色(ID:${id})`,
});
return res;
@@ -97,9 +95,10 @@ export class RoleController extends CrudController<RoleService> {
* @param roleId
* @param permissionIds
*/
@Post("/authz", { description: "sys:auth:role:edit" })
@Post("/authz", { description: "sys:auth:role:edit", summary: "角色授权" })
async authz(@Body("roleId") roleId, @Body("permissionIds") permissionIds) {
await this.service.authz(roleId, permissionIds);
await this.auditLog({ content: `为角色(ID:${roleId})进行了授权` });
return this.ok(null);
}
}
@@ -6,7 +6,7 @@ import { PermissionService } from "../../../modules/sys/authority/service/permis
import { Constants } from "@certd/lib-server";
import { In } from "typeorm";
import { LoginService } from "../../../modules/login/service/login-service.js";
import { AuditType, AuditAction } from "../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -25,6 +25,10 @@ export class UserController extends CrudController<UserService> {
@Inject()
loginService: LoginService;
getAuditType(): string {
return AuditType.user;
}
getService() {
return this.service;
}
@@ -93,35 +97,31 @@ export class UserController extends CrudController<UserService> {
return ret;
}
@Post("/add", { description: "sys:auth:user:add" })
@Post("/add", { description: "sys:auth:user:add", summary: "新增用户" })
async add(
@Body(ALL)
bean
) {
const res = await super.add(bean);
await this.auditLog({
type: AuditType.user,
action: AuditAction.add,
content: `新增了用户「${bean.username}」(ID:${res.data})`,
});
return res;
}
@Post("/update", { description: "sys:auth:user:edit" })
@Post("/update", { description: "sys:auth:user:edit", summary: "修改用户" })
async update(
@Body(ALL)
bean
) {
const res = await super.update(bean);
await this.auditLog({
type: AuditType.user,
action: AuditAction.update,
content: `修改了用户「${bean.username}」(ID:${bean.id})`,
});
return res;
}
@Post("/delete", { description: "sys:auth:user:remove" })
@Post("/delete", { description: "sys:auth:user:remove", summary: "删除用户" })
async delete(
@Query("id")
id: number
@@ -134,8 +134,6 @@ export class UserController extends CrudController<UserService> {
}
const res = await super.delete(id);
await this.auditLog({
type: AuditType.user,
action: AuditAction.delete,
content: `删除了用户(ID:${id})`,
});
return res;
@@ -144,13 +142,14 @@ export class UserController extends CrudController<UserService> {
/**
*
*/
@Post("/unlockBlock", { description: "sys:auth:user:edit" })
@Post("/unlockBlock", { description: "sys:auth:user:edit", summary: "解锁登录锁定" })
public async unlockBlock(@Body("id") id: number) {
const info = await this.service.info(id, ["password"]);
this.loginService.clearCacheOnSuccess(info.username);
if (info.mobile) {
this.loginService.clearCacheOnSuccess(info.mobile);
}
await this.auditLog({ content: `解锁了用户登录锁定(ID:${id})` });
return this.ok(info);
}
@@ -2,6 +2,7 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
import { CrudController } from "@certd/lib-server";
import { merge } from "lodash-es";
import { CnameProviderService } from "../../../modules/cname/service/cname-provider-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -16,6 +17,10 @@ export class CnameRecordController extends CrudController<CnameProviderService>
return this.service;
}
getAuditType(): string {
return AuditType.cname;
}
@Post("/page", { description: "sys:settings:view" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
@@ -27,7 +32,7 @@ export class CnameRecordController extends CrudController<CnameProviderService>
return super.list(body);
}
@Post("/add", { description: "sys:settings:edit" })
@Post("/add", { description: "sys:settings:edit", summary: "添加CNAME服务" })
async add(@Body(ALL) bean: any) {
const def: any = {
isDefault: false,
@@ -35,13 +40,17 @@ export class CnameRecordController extends CrudController<CnameProviderService>
};
merge(bean, def);
bean.userId = this.getUserId();
return super.add(bean);
const res = await super.add(bean);
await this.auditLog({ content: `添加了CNAME服务(ID:${res.data})` });
return res;
}
@Post("/update", { description: "sys:settings:edit" })
@Post("/update", { description: "sys:settings:edit", summary: "更新CNAME服务" })
async update(@Body(ALL) bean: any) {
bean.userId = this.getUserId();
return super.update(bean);
const res = await super.update(bean);
await this.auditLog({ content: `更新了CNAME服务(ID:${bean.id})` });
return res;
}
@Post("/info", { description: "sys:settings:view" })
@@ -49,26 +58,31 @@ export class CnameRecordController extends CrudController<CnameProviderService>
return super.info(id);
}
@Post("/delete", { description: "sys:settings:edit" })
@Post("/delete", { description: "sys:settings:edit", summary: "删除CNAME服务" })
async delete(@Query("id") id: number) {
return super.delete(id);
const res = await super.delete(id);
await this.auditLog({ content: `删除了CNAME服务(ID:${id})` });
return res;
}
@Post("/deleteByIds", { description: "sys:settings:edit" })
@Post("/deleteByIds", { description: "sys:settings:edit", summary: "批量删除CNAME服务" })
async deleteByIds(@Body("ids") ids: number[]) {
const res = await this.service.delete(ids);
await this.auditLog({ content: `批量删除了${ids.length}条CNAME服务` });
return this.ok(res);
}
@Post("/setDefault", { description: "sys:settings:edit" })
@Post("/setDefault", { description: "sys:settings:edit", summary: "设置默认CNAME服务" })
async setDefault(@Body("id") id: number) {
await this.service.setDefault(id);
await this.auditLog({ content: `设置了默认CNAME服务(ID:${id})` });
return this.ok();
}
@Post("/setDisabled", { description: "sys:settings:edit" })
@Post("/setDisabled", { description: "sys:settings:edit", summary: "禁用/启用CNAME服务" })
async setDisabled(@Body("id") id: number, @Body("disabled") disabled: boolean) {
await this.service.setDisabled(id, disabled);
await this.auditLog({ content: `${disabled ? "禁用" : "启用"}了CNAME服务(ID:${id})` });
return this.ok();
}
}
@@ -3,7 +3,7 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
import { ProjectService } from "../../../modules/sys/enterprise/service/project-service.js";
import { ProjectEntity } from "../../../modules/sys/enterprise/entity/project.js";
import { merge } from "lodash-es";
import { AuditType, AuditAction } from "../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -16,22 +16,26 @@ export class SysProjectController extends CrudController<ProjectEntity> {
@Inject()
sysSettingsService: SysSettingsService;
getAuditType(): string {
return AuditType.project;
}
getService<T>() {
return this.service;
}
@Post("/page", { description: "sys:settings:view" })
@Post("/page", { description: "sys:settings:view", summary: "查询项目分页列表" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
return await super.page(body);
}
@Post("/list", { description: "sys:settings:view" })
@Post("/list", { description: "sys:settings:view", summary: "查询项目列表" })
async list(@Body(ALL) body: any) {
return super.list(body);
}
@Post("/add", { description: "sys:settings:edit" })
@Post("/add", { description: "sys:settings:edit", summary: "添加项目" })
async add(@Body(ALL) bean: any) {
const def: any = {
isDefault: false,
@@ -45,54 +49,49 @@ export class SysProjectController extends CrudController<ProjectEntity> {
adminId: bean.userId,
});
await this.auditLog({
type: AuditType.project,
action: AuditAction.add,
content: `新增了项目「${bean.name}」(ID:${res.data})`,
});
return res;
}
@Post("/update", { description: "sys:settings:edit" })
@Post("/update", { description: "sys:settings:edit", summary: "更新项目" })
async update(@Body(ALL) bean: any) {
bean.userId = this.getUserId();
const res = await super.update(bean);
await this.auditLog({
type: AuditType.project,
action: AuditAction.update,
content: `修改了项目「${bean.name}」(ID:${bean.id})`,
});
return res;
}
@Post("/info", { description: "sys:settings:view" })
@Post("/info", { description: "sys:settings:view", summary: "查询项目详情" })
async info(@Query("id") id: number) {
return super.info(id);
}
@Post("/delete", { description: "sys:settings:edit" })
@Post("/delete", { description: "sys:settings:edit", summary: "删除项目" })
async delete(@Query("id") id: number) {
const res = await super.delete(id);
await this.auditLog({
type: AuditType.project,
action: AuditAction.delete,
content: `删除了项目(ID:${id})`,
});
return res;
}
@Post("/deleteByIds", { description: "sys:settings:edit" })
@Post("/deleteByIds", { description: "sys:settings:edit", summary: "批量删除项目" })
async deleteByIds(@Body("ids") ids: number[]) {
const res = await this.service.delete(ids);
await this.auditLog({
content: `批量删除了${ids.length}个项目`,
});
return this.ok(res);
}
@Post("/setDisabled", { description: "sys:settings:edit" })
@Post("/setDisabled", { description: "sys:settings:edit", summary: "禁用/启用项目" })
async setDisabled(@Body("id") id: number, @Body("disabled") disabled: boolean) {
await this.service.setDisabled(id, disabled);
const project = await this.service.info(id);
const actionText = disabled ? "禁用了" : "启用了";
await this.auditLog({
type: AuditType.project,
action: AuditAction.disable,
content: `${actionText}项目「${project.name}」(ID:${id})`,
});
return this.ok();
@@ -4,6 +4,7 @@ import { ProjectMemberEntity } from "../../../modules/sys/enterprise/entity/proj
import { ProjectMemberService } from "../../../modules/sys/enterprise/service/project-member-service.js";
import { merge } from "lodash-es";
import { ProjectService } from "../../../modules/sys/enterprise/service/project-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -15,7 +16,6 @@ export class SysProjectMemberController extends CrudController<ProjectMemberEnti
@Inject()
sysSettingsService: SysSettingsService;
@Inject()
projectService: ProjectService;
@@ -23,18 +23,22 @@ export class SysProjectMemberController extends CrudController<ProjectMemberEnti
return this.service;
}
@Post("/page", { description: "sys:settings:view" })
getAuditType(): string {
return AuditType.enterprise;
}
@Post("/page", { description: "sys:settings:view", summary: "查询项目成员分页列表" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
return await super.page(body);
}
@Post("/list", { description: "sys:settings:view" })
@Post("/list", { description: "sys:settings:view", summary: "查询项目成员列表" })
async list(@Body(ALL) body: any) {
return super.list(body);
}
@Post("/add", { description: "sys:settings:edit" })
@Post("/add", { description: "sys:settings:edit", summary: "添加项目成员" })
async add(@Body(ALL) bean: any) {
const def: any = {
isDefault: false,
@@ -47,10 +51,12 @@ export class SysProjectMemberController extends CrudController<ProjectMemberEnti
projectId: bean.projectId,
});
return super.add(bean);
const res = await super.add(bean);
await this.auditLog({ content: `添加了项目成员(ID:${res.data})` });
return res;
}
@Post("/update", { description: "sys:settings:edit" })
@Post("/update", { description: "sys:settings:edit", summary: "更新项目成员" })
async update(@Body(ALL) bean: any) {
if (!bean.id) {
throw new Error("id is required");
@@ -65,10 +71,11 @@ export class SysProjectMemberController extends CrudController<ProjectMemberEnti
permission: bean.permission,
status: bean.status,
});
await this.auditLog({ content: `更新了项目成员(ID:${bean.id})` });
return this.ok(res);
}
@Post("/info", { description: "sys:settings:view" })
@Post("/info", { description: "sys:settings:view", summary: "查询项目成员详情" })
async info(@Query("id") id: number) {
if (!id) {
throw new Error("id is required");
@@ -81,7 +88,7 @@ export class SysProjectMemberController extends CrudController<ProjectMemberEnti
return super.info(id);
}
@Post("/delete", { description: "sys:settings:edit" })
@Post("/delete", { description: "sys:settings:edit", summary: "删除项目成员" })
async delete(@Query("id") id: number) {
if (!id) {
throw new Error("id is required");
@@ -91,10 +98,12 @@ export class SysProjectMemberController extends CrudController<ProjectMemberEnti
userId: this.getUserId(),
projectId: projectId,
});
return super.delete(id);
const res = await super.delete(id);
await this.auditLog({ content: `删除了项目成员(ID:${id})` });
return res;
}
@Post("/deleteByIds", { description: "sys:settings:edit" })
@Post("/deleteByIds", { description: "sys:settings:edit", summary: "批量删除项目成员" })
async deleteByIds(@Body("ids") ids: number[]) {
for (const id of ids) {
if (!id) {
@@ -108,6 +117,7 @@ export class SysProjectMemberController extends CrudController<ProjectMemberEnti
await this.service.delete(id as any);
}
await this.auditLog({ content: `批量删除了${ids.length}个项目成员` });
return this.ok({});
}
}
@@ -2,6 +2,7 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
import { CrudController } from "@certd/lib-server";
import { SiteInfoService } from "../../../modules/monitor/service/site-info-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
@Provide()
@Controller("/api/sys/monitor/site")
@@ -14,6 +15,10 @@ export class SysSiteInfoController extends CrudController<SiteInfoService> {
return this.service;
}
getAuditType(): string {
return AuditType.monitor;
}
@Post("/page", { description: "sys:settings:view", summary: "管理员查询站点监控分页列表" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
@@ -44,12 +49,16 @@ export class SysSiteInfoController extends CrudController<SiteInfoService> {
@Post("/delete", { description: "sys:settings:edit", summary: "管理员删除站点监控" })
async delete(@Query("id") id: number) {
return await super.delete(id);
await super.delete(id);
await this.auditLog({ content: `管理员删除了站点监控(ID:${id})` });
return this.ok();
}
@Post("/batchDelete", { description: "sys:settings:edit", summary: "管理员批量删除站点监控" })
async batchDelete(@Body("ids") ids: number[]) {
const count = ids.length;
await this.service.delete(ids);
await this.auditLog({ content: `管理员批量删除了${count}条站点监控` });
return this.ok();
}
}
@@ -3,6 +3,7 @@ import { CrudController } from "@certd/lib-server";
import { ApiTags } from "@midwayjs/swagger";
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
import { checkPlus } from "@certd/plus-core";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
@Provide()
@Controller("/api/sys/pipeline")
@@ -15,6 +16,10 @@ export class SysPipelineController extends CrudController<PipelineService> {
return this.service;
}
getAuditType(): string {
return AuditType.pipeline;
}
@Post("/page", { description: "sys:settings:view", summary: "管理员查询用户流水线分页列表" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
@@ -41,13 +46,16 @@ export class SysPipelineController extends CrudController<PipelineService> {
@Post("/delete", { description: "sys:settings:edit", summary: "管理员删除用户流水线" })
async delete(@Query("id") id: number) {
await this.service.delete(id);
await this.auditLog({ content: `管理员删除了用户流水线(ID:${id})` });
return this.ok();
}
@Post("/batchDelete", { description: "sys:settings:edit", summary: "管理员批量删除用户流水线" })
async batchDelete(@Body("ids") ids: number[]) {
checkPlus();
const count = ids.length;
await this.service.batchDelete(ids);
await this.auditLog({ content: `管理员批量删除了${count}条用户流水线` });
return this.ok();
}
}
@@ -3,6 +3,7 @@ import { merge } from "lodash-es";
import { CrudController } from "@certd/lib-server";
import { PluginImportReq, PluginService } from "../../../modules/plugin/service/plugin-service.js";
import { CommPluginConfig, PluginConfig, PluginConfigService } from "../../../modules/plugin/service/plugin-config-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
*/
@@ -19,6 +20,10 @@ export class PluginController extends CrudController<PluginService> {
return this.service;
}
getAuditType(): string {
return AuditType.plugin;
}
@Post("/page", { description: "sys:settings:view" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
@@ -30,19 +35,22 @@ export class PluginController extends CrudController<PluginService> {
return super.list(body);
}
@Post("/add", { description: "sys:settings:edit" })
@Post("/add", { description: "sys:settings:edit", summary: "添加插件" })
async add(@Body(ALL) bean: any) {
const def: any = {
isDefault: false,
disabled: false,
};
merge(bean, def);
return super.add(bean);
const res = await super.add(bean);
await this.auditLog({ content: `新增了插件「${bean.name}」(ID:${res.data}, 类型:${bean.type})` });
return res;
}
@Post("/update", { description: "sys:settings:edit" })
@Post("/update", { description: "sys:settings:edit", summary: "更新插件" })
async update(@Body(ALL) bean: any) {
const res = await super.update(bean);
await this.auditLog({ content: `修改了插件「${bean.name}」(ID:${bean.id})` });
return res;
}
@@ -51,21 +59,25 @@ export class PluginController extends CrudController<PluginService> {
return super.info(id);
}
@Post("/delete", { description: "sys:settings:edit" })
@Post("/delete", { description: "sys:settings:edit", summary: "删除插件" })
async delete(@Query("id") id: number) {
const res = await this.service.deleteByIds([id]);
await this.auditLog({ content: `删除了插件(ID:${id})` });
return this.ok(res);
}
@Post("/deleteByIds", { description: "sys:settings:edit" })
@Post("/deleteByIds", { description: "sys:settings:edit", summary: "批量删除插件" })
async deleteByIds(@Body("ids") ids: number[]) {
const res = await this.service.deleteByIds(ids);
await this.auditLog({ content: `批量删除了${ids.length}条插件` });
return this.ok(res);
}
@Post("/setDisabled", { description: "sys:settings:edit" })
@Post("/setDisabled", { description: "sys:settings:edit", summary: "禁用/启用插件" })
async setDisabled(@Body(ALL) body: { id: number; name: string; type: string; disabled: boolean }) {
await this.service.setDisabled(body);
const { id, disabled } = body;
await this.auditLog({ content: `${disabled ? "禁用" : "启用"}了插件(ID:${id})` });
return this.ok();
}
@Post("/getCommPluginConfigs", { description: "sys:settings:view" })
@@ -74,9 +86,10 @@ export class PluginController extends CrudController<PluginService> {
return this.ok(res);
}
@Post("/saveCommPluginConfigs", { description: "sys:settings:edit" })
@Post("/saveCommPluginConfigs", { description: "sys:settings:edit", summary: "保存公共插件配置" })
async saveCommPluginConfigs(@Body(ALL) body: CommPluginConfig) {
const res = await this.pluginConfigService.saveCommPluginConfig(body);
await this.auditLog({ content: "保存了公共插件配置" });
return this.ok(res);
}
@Post("/getPluginByName", { description: "sys:settings:view" })
@@ -88,19 +101,21 @@ export class PluginController extends CrudController<PluginService> {
return this.ok(res);
}
@Post("/saveSetting", { description: "sys:settings:edit" })
@Post("/saveSetting", { description: "sys:settings:edit", summary: "保存插件设置" })
async saveSetting(@Body(ALL) body: PluginConfig) {
const res = await this.pluginConfigService.savePluginConfig(body);
await this.auditLog({ content: "保存了插件设置" });
return this.ok(res);
}
@Post("/import", { description: "sys:settings:edit" })
@Post("/import", { description: "sys:settings:edit", summary: "导入插件" })
async import(@Body(ALL) body: PluginImportReq) {
const res = await this.service.importPlugin(body);
await this.auditLog({ content: "导入了插件配置" });
return this.ok(res);
}
@Post("/export", { description: "sys:settings:edit" })
@Post("/export", { description: "sys:settings:edit", summary: "导出插件" })
async export(@Body("id") id: number) {
const res = await this.service.exportPlugin(id);
return this.ok(res);
@@ -2,7 +2,7 @@ import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core";
import { BaseController, SysSafeSetting } from "@certd/lib-server";
import { cloneDeep } from "lodash-es";
import { SafeService } from "../../../modules/sys/settings/safe-service.js";
import { AuditType, AuditAction } from "../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -12,6 +12,10 @@ export class SysSettingsController extends BaseController {
@Inject()
safeService: SafeService;
getAuditType(): string {
return AuditType.settings;
}
@Post("/get", { description: "sys:settings:view" })
async safeGet() {
const res = await this.safeService.getSafeSetting();
@@ -24,8 +28,6 @@ export class SysSettingsController extends BaseController {
async safeSave(@Body(ALL) body: any) {
await this.safeService.saveSafeSetting(body);
await this.auditLog({
type: AuditType.settings,
action: AuditAction.update,
content: "修改了安全设置",
});
return this.ok({});
@@ -34,9 +36,10 @@ export class SysSettingsController extends BaseController {
/**
*
*/
@Post("/hidden", { description: "sys:settings:edit" })
@Post("/hidden", { description: "sys:settings:edit", summary: "立刻隐藏系统" })
async hiddenImmediate() {
await this.safeService.hiddenImmediately();
await this.auditLog({ content: "立刻隐藏了系统" });
return this.ok({});
}
}
@@ -8,7 +8,7 @@ import { http, logger, utils } from "@certd/basic";
import { CodeService } from "../../../modules/basic/service/code-service.js";
import { SmsServiceFactory } from "../../../modules/basic/sms/factory.js";
import { RuntimeDepsService } from "../../../modules/runtime-deps/runtime-deps-service.js";
import { AuditType, AuditAction } from "../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -32,63 +32,71 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
return this.service;
}
@Post("/page", { description: "sys:settings:view" })
getAuditType(): string {
return AuditType.settings;
}
@Post("/page", { description: "sys:settings:view", summary: "查询系统设置分页列表" })
async page(@Body(ALL) body) {
return super.page(body);
}
@Post("/list", { description: "sys:settings:view" })
@Post("/list", { description: "sys:settings:view", summary: "查询系统设置列表" })
async list(@Body(ALL) body) {
return super.list(body);
}
@Post("/add", { description: "sys:settings:edit" })
@Post("/add", { description: "sys:settings:edit", summary: "添加系统设置" })
async add(@Body(ALL) bean) {
return super.add(bean);
const res = await super.add(bean);
await this.auditLog({ content: "添加了系统设置" });
return res;
}
@Post("/update", { description: "sys:settings:edit" })
@Post("/update", { description: "sys:settings:edit", summary: "更新系统设置" })
async update(@Body(ALL) bean) {
await this.service.checkUserId(bean.id, this.getUserId());
return super.update(bean);
const res = await super.update(bean);
await this.auditLog({ content: "更新了系统设置" });
return res;
}
@Post("/info", { description: "sys:settings:view" })
@Post("/info", { description: "sys:settings:view", summary: "查询系统设置详情" })
async info(@Query("id") id: number) {
await this.service.checkUserId(id, this.getUserId());
return super.info(id);
}
@Post("/delete", { description: "sys:settings:edit" })
@Post("/delete", { description: "sys:settings:edit", summary: "删除系统设置" })
async delete(@Query("id") id: number) {
await this.service.checkUserId(id, this.getUserId());
return super.delete(id);
const res = await super.delete(id);
await this.auditLog({ content: "删除了系统设置" });
return res;
}
@Post("/save", { description: "sys:settings:edit" })
@Post("/save", { description: "sys:settings:edit", summary: "保存系统设置" })
async save(@Body(ALL) bean: SysSettingsEntity) {
await this.service.save(bean);
await this.auditLog({
type: AuditType.settings,
action: AuditAction.update,
content: "修改了系统设置",
});
return this.ok({});
}
@Post("/get", { description: "sys:settings:view" })
@Post("/get", { description: "sys:settings:view", summary: "查询系统设置" })
async get(@Query("key") key: string) {
const entity = await this.service.getByKey(key);
return this.ok(entity);
}
// savePublicSettings
@Post("/getEmailSettings", { description: "sys:settings:view" })
@Post("/getEmailSettings", { description: "sys:settings:view", summary: "查询邮件服务器设置" })
async getEmailSettings(@Body(ALL) body) {
const conf = await getEmailSettings(this.service, this.userSettingsService);
return this.ok(conf);
}
@Post("/getEmailTemplates", { description: "sys:settings:view" })
@Post("/getEmailTemplates", { description: "sys:settings:view", summary: "查询邮件模板列表" })
async getEmailTemplates(@Body(ALL) body) {
const conf = await getEmailSettings(this.service, this.userSettingsService);
const templates = conf.templates || {};
@@ -107,15 +115,16 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
return this.ok(proviers);
}
@Post("/saveEmailSettings", { description: "sys:settings:edit" })
@Post("/saveEmailSettings", { description: "sys:settings:edit", summary: "保存邮件服务器设置" })
async saveEmailSettings(@Body(ALL) body) {
const conf = await getEmailSettings(this.service, this.userSettingsService);
merge(conf, body);
await this.service.saveSetting(conf);
await this.auditLog({ content: "保存了邮件服务器设置" });
return this.ok(conf);
}
@Post("/getSysSettings", { description: "sys:settings:view" })
@Post("/getSysSettings", { description: "sys:settings:view", summary: "查询系统配置" })
async getSysSettings() {
const publicSettings = await this.service.getPublicSettings();
let privateSettings = await this.service.getPrivateSettings();
@@ -124,7 +133,7 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
}
// savePublicSettings
@Post("/saveSysSettings", { description: "sys:settings:edit" })
@Post("/saveSysSettings", { description: "sys:settings:edit", summary: "保存系统设置" })
async saveSysSettings(@Body(ALL) body: { public: SysPublicSettings; private: SysPrivateSettings }) {
const publicSettings = await this.service.getPublicSettings();
const privateSettings = await this.service.getPrivateSettings();
@@ -132,16 +141,18 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
merge(privateSettings, body.private);
await this.service.savePublicSettings(publicSettings);
await this.service.savePrivateSettings(privateSettings);
await this.auditLog({ content: "保存了系统设置" });
return this.ok({});
}
@Post("/stopOtherUserTimer", { description: "sys:settings:edit" })
@Post("/stopOtherUserTimer", { description: "sys:settings:edit", summary: "停止其他用户定时任务" })
async stopOtherUserTimer(@Body(ALL) body) {
await this.pipelineService.stopOtherUserPipeline(1);
await this.auditLog({ content: "停止了其他用户定时任务" });
return this.ok({});
}
@Post("/testProxy", { description: "sys:settings:edit" })
@Post("/testProxy", { description: "sys:settings:edit", summary: "测试代理" })
async testProxy(@Body(ALL) body) {
const google = "https://www.google.com/";
const baidu = "https://www.baidu.com/";
@@ -179,19 +190,19 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
});
}
@Post("/testSms", { description: "sys:settings:edit" })
@Post("/testSms", { description: "sys:settings:edit", summary: "测试短信" })
async testSms(@Body(ALL) body) {
await this.codeService.sendSmsCode(body.phoneCode, body.mobile);
return this.ok({});
}
@Post("/getSmsTypeDefine", { description: "sys:settings:view" })
@Post("/getSmsTypeDefine", { description: "sys:settings:view", summary: "查询短信类型定义" })
async getSmsTypeDefine(@Body("type") type: string) {
const define = await SmsServiceFactory.getDefine(type);
return this.ok(define);
}
@Post("/safe/get", { description: "sys:settings:view" })
@Post("/safe/get", { description: "sys:settings:view", summary: "查询安全设置" })
async safeGet() {
const res = await this.service.getSetting<SysSafeSetting>(SysSafeSetting);
const clone: SysSafeSetting = cloneDeep(res);
@@ -199,7 +210,7 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
return this.ok(clone);
}
@Post("/safe/save", { description: "sys:settings:edit" })
@Post("/safe/save", { description: "sys:settings:edit", summary: "保存安全设置" })
async safeSave(@Body(ALL) body: any) {
if (body.hidden.openPassword) {
body.hidden.openPassword = utils.hash.md5(body.hidden.openPassword);
@@ -211,24 +222,26 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
throw new Error("首次设置需要填写解锁密码");
}
await this.service.saveSetting(blankSetting);
await this.auditLog({ content: "保存了安全设置" });
return this.ok({});
}
@Post("/captchaTest", { description: "sys:settings:edit" })
@Post("/captchaTest", { description: "sys:settings:edit", summary: "测试验证码" })
async captchaTest(@Body(ALL) body: any, @RequestIP() remoteIp: string) {
await this.codeService.checkCaptcha(body, { remoteIp });
return this.ok({});
}
@Post("/oauth/providers", { description: "sys:settings:view" })
@Post("/oauth/providers", { description: "sys:settings:view", summary: "查询OAuth提供商列表" })
async oauthProviders() {
const list = await addonRegistry.getDefineList("oauth");
return this.ok(list);
}
@Post("/clearRuntimeDeps", { description: "sys:settings:edit" })
@Post("/clearRuntimeDeps", { description: "sys:settings:edit", summary: "清除运行时依赖" })
async clearRuntimeDeps() {
await this.runtimeDepsService.clearRuntimeDeps();
await this.auditLog({ content: "清除了运行时依赖" });
return this.ok(true);
}
}
@@ -5,6 +5,7 @@ import { checkPlus } from "@certd/plus-core";
import { http, logger, utils } from "@certd/basic";
import { TaskServiceBuilder } from "../../../modules/pipeline/service/getter/task-service-getter.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
* Addon
@@ -24,6 +25,10 @@ export class AddonController extends CrudController<AddonService> {
return this.service;
}
getAuditType(): string {
return AuditType.addon;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询Addon分页列表" })
async page(@Body(ALL) body) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -68,7 +73,9 @@ export class AddonController extends CrudController<AddonService> {
if (define.needPlus) {
checkPlus();
}
return super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了Addon「${bean.name}」(ID:${res.data}, 类型:${bean.type})` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新Addon" })
@@ -91,7 +98,9 @@ export class AddonController extends CrudController<AddonService> {
}
delete bean.userId;
delete bean.projectId;
return super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了Addon「${bean.name}」(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询Addon详情" })
@@ -103,7 +112,9 @@ export class AddonController extends CrudController<AddonService> {
@Post("/delete", { description: Constants.per.authOnly, summary: "删除Addon" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.getService(), id, "write");
return super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了Addon(ID:${id})` });
return res;
}
@Post("/define", { description: Constants.per.authOnly, summary: "查询Addon插件定义" })
@@ -158,6 +169,7 @@ export class AddonController extends CrudController<AddonService> {
async setDefault(@Query("addonType") addonType: string, @Query("id") id: number) {
const { projectId, userId } = await this.checkOwner(this.getService(), id, "write", true);
const res = await this.service.setDefault(id, userId, addonType, projectId);
this.auditLog({ content: `设置了默认Addon(ID:${id})` });
return this.ok(res);
}
@@ -13,11 +13,20 @@ export class AuditLogController extends BaseController {
@Post("/page", { description: Constants.per.authOnly, summary: "分页查询当前用户操作日志" })
async page(@Body(ALL) body: any) {
const userId = this.getUserId();
if (!body.query) {
body.query = {};
const { projectId, userId } = await this.getProjectUserIdRead();
const query: any = { }
if (projectId) {
//如果是项目级别,排除userId参数,因为日志这里userId不是-1 ,而是实际的用户
query.projectId = projectId;
}else{
query.userId = userId;
}
body.query.userId = userId;
body.query = {
...body.query || {},
...query,
scope: "user"
}
const pageRet = await this.auditService.page(body);
return this.ok(pageRet);
}
@@ -3,6 +3,7 @@ import { Constants, CrudController } from "@certd/lib-server";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { GroupService } from "../../../modules/basic/service/group-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -20,6 +21,10 @@ export class GroupController extends CrudController<GroupService> {
return this.service;
}
getAuditType(): string {
return AuditType.pipelineGroup;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询分组分页列表" })
async page(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -52,7 +57,9 @@ export class GroupController extends CrudController<GroupService> {
const { projectId, userId } = await this.getProjectUserIdRead();
bean.projectId = projectId;
bean.userId = userId;
return await super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了分组「${bean.name}」(ID:${res.data})` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新分组" })
@@ -60,7 +67,9 @@ export class GroupController extends CrudController<GroupService> {
await this.checkOwner(this.getService(), bean.id, "write");
delete bean.userId;
delete bean.projectId;
return await super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了分组「${bean.name}」(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询分组详情" })
async info(@Query("id") id: number) {
@@ -71,7 +80,9 @@ export class GroupController extends CrudController<GroupService> {
@Post("/delete", { description: Constants.per.authOnly, summary: "删除分组" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.getService(), id, "write");
return await super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了分组(ID:${id})` });
return res;
}
@Post("/all", { description: Constants.per.authOnly, summary: "查询所有分组" })
@@ -2,6 +2,7 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
import { Constants, CrudController } from "@certd/lib-server";
import { ApiTags } from "@midwayjs/swagger";
import { CertApplyTemplateService } from "../../../modules/cert/service/cert-apply-template-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
@Provide()
@Controller("/api/cert/apply-template")
@@ -14,6 +15,10 @@ export class CertApplyTemplateController extends CrudController<CertApplyTemplat
return this.service;
}
getAuditType(): string {
return AuditType.certTemplate;
}
private removeContent(data: any) {
const records = Array.isArray(data) ? data : data?.records;
if (!records) {
@@ -53,7 +58,9 @@ export class CertApplyTemplateController extends CrudController<CertApplyTemplat
const { projectId, userId } = await this.getProjectUserIdWrite();
bean.projectId = projectId;
bean.userId = userId;
return super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了证书参数模版(ID:${res.data})` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新证书申请参数模版" })
@@ -61,7 +68,9 @@ export class CertApplyTemplateController extends CrudController<CertApplyTemplat
await this.checkOwner(this.getService(), bean.id, "write");
delete bean.userId;
delete bean.projectId;
return super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了证书参数模版(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询证书申请参数模版详情" })
@@ -73,13 +82,17 @@ export class CertApplyTemplateController extends CrudController<CertApplyTemplat
@Post("/delete", { description: Constants.per.authOnly, summary: "删除证书申请参数模版" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.getService(), id, "write");
return super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了证书参数模版(ID:${id})` });
return res;
}
@Post("/setDefault", { description: Constants.per.authOnly, summary: "设置默认证书申请参数模版" })
async setDefault(@Body("id") id: number) {
const { projectId, userId } = await this.getProjectUserIdWrite();
return this.ok(await this.service.setDefault(id, userId, projectId));
const defaultId = await this.service.setDefault(id, userId, projectId);
this.auditLog({ content: `设置了默认证书参数模版(ID:${id})` });
return this.ok(defaultId);
}
@Post("/default", { description: Constants.per.authOnly, summary: "查询默认证书申请参数模版" })
@@ -2,6 +2,7 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
import { Constants, CrudController } from "@certd/lib-server";
import { ApiTags } from "@midwayjs/swagger";
import { DnsPersistRecordService } from "../../../modules/cert/service/dns-persist-record-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
@Provide()
@Controller("/api/cert/dns-persist")
@@ -14,6 +15,10 @@ export class DnsPersistRecordController extends CrudController<DnsPersistRecordS
return this.service;
}
getAuditType(): string {
return AuditType.dnsPersist;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询DNS持久验证记录分页列表" })
async page(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -28,7 +33,9 @@ export class DnsPersistRecordController extends CrudController<DnsPersistRecordS
const { projectId, userId } = await this.getProjectUserIdWrite();
bean.projectId = projectId;
bean.userId = userId;
return super.add(bean);
const res = await this.getService().add(bean);
this.auditLog({ content: `新增了DNS持久验证记录(ID:${res.id})` });
return this.ok(res);
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新DNS持久验证记录" })
@@ -36,7 +43,9 @@ export class DnsPersistRecordController extends CrudController<DnsPersistRecordS
await this.checkOwner(this.getService(), bean.id, "write");
delete bean.userId;
delete bean.projectId;
return super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了DNS持久验证记录(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询DNS持久验证记录详情" })
@@ -49,6 +58,7 @@ export class DnsPersistRecordController extends CrudController<DnsPersistRecordS
async delete(@Query("id") id: number) {
await this.checkOwner(this.getService(), id, "write");
await this.service.delete(id as any);
this.auditLog({ content: `删除了DNS持久验证记录(ID:${id})` });
return this.ok({
message: this.service.lastDeleteMessage,
});
@@ -80,12 +90,16 @@ export class DnsPersistRecordController extends CrudController<DnsPersistRecordS
@Post("/triggerVerify", { description: Constants.per.authOnly, summary: "后台验证DNS持久验证记录" })
async triggerVerify(@Body(ALL) body: { id: number }) {
await this.checkOwner(this.getService(), body.id, "write");
return this.ok(await this.service.triggerVerify(body.id));
const res = await this.service.triggerVerify(body.id);
this.auditLog({ content: `触发了DNS持久验证(ID:${body.id})` });
return this.ok(res);
}
@Post("/createTxt", { description: Constants.per.authOnly, summary: "一键创建DNS持久验证TXT记录" })
async createTxt(@Body(ALL) body: { id: number; dnsProviderType?: string; dnsProviderAccess?: number }) {
await this.checkOwner(this.getService(), body.id, "write");
return this.ok(await this.service.createDnsTxt(body));
const res = await this.service.createDnsTxt(body);
this.auditLog({ content: `一键创建了DNS持久验证TXT记录(ID:${body.id})` });
return this.ok(res);
}
}
@@ -4,6 +4,7 @@ import { DomainService } from "../../../modules/cert/service/domain-service.js";
import { checkPlus } from "@certd/plus-core";
import { ApiTags } from "@midwayjs/swagger";
import { parseDomainByPsl } from "@certd/plugin-lib";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -19,6 +20,10 @@ export class DomainController extends CrudController<DomainService> {
return this.service;
}
getAuditType(): string {
return AuditType.domain;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询域名分页列表" })
async page(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -58,7 +63,9 @@ export class DomainController extends CrudController<DomainService> {
const { projectId, userId } = await this.getProjectUserIdRead();
bean.projectId = projectId;
bean.userId = userId;
return super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了域名「${bean.domain}` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新域名" })
@@ -66,7 +73,9 @@ export class DomainController extends CrudController<DomainService> {
await this.checkOwner(this.getService(), bean.id, "write");
delete bean.userId;
delete bean.projectId;
return super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了域名「${bean.domain}` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询域名详情" })
@@ -78,13 +87,16 @@ export class DomainController extends CrudController<DomainService> {
@Post("/delete", { description: Constants.per.authOnly, summary: "删除域名" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.getService(), id, "write");
return super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了域名(ID:${id})` });
return res;
}
@Post("/deleteByIds", { description: Constants.per.authOnly, summary: "批量删除域名" })
async deleteByIds(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead();
await this.service.batchDelete(body.ids, userId, projectId);
this.auditLog({ content: `批量删除了${body.ids.length}条域名` });
return this.ok();
}
@@ -99,6 +111,7 @@ export class DomainController extends CrudController<DomainService> {
projectId: projectId,
};
await this.service.startDomainImportTask(req);
this.auditLog({ content: "开始了域名导入任务" });
return this.ok();
}
@@ -123,6 +136,7 @@ export class DomainController extends CrudController<DomainService> {
key,
};
await this.service.deleteDomainImportTask(req);
this.auditLog({ content: "删除了域名导入任务" });
return this.ok();
}
@@ -139,6 +153,7 @@ export class DomainController extends CrudController<DomainService> {
key,
};
const item = await this.service.saveDomainImportTask(req);
this.auditLog({ content: "保存了域名导入任务" });
return this.ok(item);
}
@@ -149,6 +164,7 @@ export class DomainController extends CrudController<DomainService> {
userId: userId,
projectId: projectId,
});
this.auditLog({ content: "开始了同步域名过期时间任务" });
return this.ok();
}
@Post("/sync/expiration/status", { description: Constants.per.authOnly, summary: "查询同步域名过期时间任务状态" })
@@ -169,6 +185,7 @@ export class DomainController extends CrudController<DomainService> {
projectId: projectId,
setting: { ...body },
});
this.auditLog({ content: "保存了域名监控设置" });
return this.ok();
}
@@ -2,6 +2,7 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
import { Constants, CrudController } from "@certd/lib-server";
import { CnameRecordService } from "../../../modules/cname/service/cname-record-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -17,6 +18,10 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
return this.service;
}
getAuditType(): string {
return AuditType.cname;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询CNAME记录分页列表" })
async page(@Body(ALL) body: any) {
const { userId, projectId } = await this.getProjectUserIdRead();
@@ -56,7 +61,9 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
const { userId, projectId } = await this.getProjectUserIdWrite();
bean.userId = userId;
bean.projectId = projectId;
return super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了CNAME记录「${bean.domain}` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新CNAME记录" })
@@ -64,7 +71,9 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
await this.checkOwner(this.getService(), bean.id, "write");
delete bean.userId;
delete bean.projectId;
return super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了CNAME记录(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询CNAME记录详情" })
@@ -76,13 +85,16 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
@Post("/delete", { description: Constants.per.authOnly, summary: "删除CNAME记录" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.getService(), id, "write");
return super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了CNAME记录(ID:${id})` });
return res;
}
@Post("/deleteByIds", { description: Constants.per.authOnly, summary: "批量删除CNAME记录" })
async deleteByIds(@Body(ALL) body: any) {
const { userId, projectId } = await this.getProjectUserIdWrite();
await this.service.batchDelete(body.ids, userId, projectId);
this.auditLog({ content: `批量删除了${body.ids.length}条CNAME记录` });
return this.ok();
}
@Post("/getByDomain", { description: Constants.per.authOnly, summary: "根据域名获取CNAME记录" })
@@ -103,6 +115,7 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
async resetStatus(@Body(ALL) body: { id: number }) {
await this.checkOwner(this.getService(), body.id, "read");
const res = await this.service.resetStatus(body.id);
this.auditLog({ content: `重置了CNAME记录状态(ID:${body.id})` });
return this.ok(res);
}
@Post("/import", { description: Constants.per.authOnly, summary: "导入CNAME记录" })
@@ -114,6 +127,7 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
domainList: body.domainList,
cnameProviderId: body.cnameProviderId,
});
this.auditLog({ append: `提交${res.count}` });
return this.ok(res);
}
}
@@ -4,6 +4,7 @@ import { AuthService } from "../../../modules/sys/authority/service/auth-service
import { ProjectService } from "../../../modules/sys/enterprise/service/project-service.js";
import { ProjectMemberService } from "../../../modules/sys/enterprise/service/project-member-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -24,6 +25,10 @@ export class UserProjectController extends BaseController {
return this.service;
}
getAuditType(): string {
return AuditType.enterprise;
}
/**
* @param body
* @returns
@@ -63,6 +68,7 @@ export class UserProjectController extends BaseController {
async applyJoin(@Body(ALL) body: any) {
const userId = this.getUserId();
const res = await this.service.applyJoin({ userId, projectId: body.projectId });
this.auditLog({ content: "申请了加入项目" });
return this.ok(res);
}
@@ -84,6 +90,7 @@ export class UserProjectController extends BaseController {
status,
permission,
});
this.auditLog({ content: "更新了项目成员" });
return this.ok(res);
}
@@ -92,6 +99,7 @@ export class UserProjectController extends BaseController {
const { projectId } = await this.getProjectUserIdAdmin();
const { status, permission, userId } = body;
const res = await this.service.approveJoin({ userId, projectId: projectId, status, permission });
this.auditLog({ content: "审批了加入项目申请" });
return this.ok(res);
}
@@ -100,8 +108,9 @@ export class UserProjectController extends BaseController {
const { projectId } = await this.getProjectUserIdAdmin();
await this.projectMemberService.deleteWhere({
projectId,
userId: this.getUserId(),
userId: body.userId,
});
this.auditLog({ content: "删除了项目成员" });
return this.ok();
}
@@ -113,6 +122,7 @@ export class UserProjectController extends BaseController {
projectId,
userId,
});
this.auditLog({ content: "离开了项目" });
return this.ok();
}
}
@@ -5,6 +5,7 @@ import { ProjectMemberService } from "../../../modules/sys/enterprise/service/pr
import { merge } from "lodash-es";
import { ProjectService } from "../../../modules/sys/enterprise/service/project-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@Provide()
@@ -24,6 +25,10 @@ export class ProjectMemberController extends CrudController<ProjectMemberEntity>
return this.service;
}
getAuditType(): string {
return AuditType.enterprise;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询项目成员分页列表" })
async page(@Body(ALL) body: any) {
const { projectId } = await this.getProjectUserIdRead();
@@ -53,7 +58,9 @@ export class ProjectMemberController extends CrudController<ProjectMemberEntity>
projectId: bean.projectId,
});
return super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了项目成员(ID:${res.data})` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新项目成员" })
@@ -71,7 +78,7 @@ export class ProjectMemberController extends CrudController<ProjectMemberEntity>
permission: bean.permission,
status: bean.status,
});
this.auditLog({ content: `修改了项目成员(ID:${bean.id})` });
return this.ok(res);
}
@@ -98,7 +105,9 @@ export class ProjectMemberController extends CrudController<ProjectMemberEntity>
userId: this.getUserId(),
projectId: projectId,
});
return super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了项目成员(ID:${id})` });
return res;
}
@Post("/deleteByIds", { description: Constants.per.authOnly, summary: "批量删除项目成员" })
@@ -115,6 +124,7 @@ export class ProjectMemberController extends CrudController<ProjectMemberEntity>
await this.service.delete(id as any);
}
this.auditLog({ content: `批量删除了${ids.length}条项目成员` });
return this.ok({});
}
}
@@ -2,6 +2,7 @@ import { BaseController, Constants } from "@certd/lib-server";
import { Controller, Inject, Post, Provide } from "@midwayjs/core";
import { TransferService } from "../../../modules/sys/enterprise/service/transfer-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -16,6 +17,10 @@ export class TransferController extends BaseController {
return this.service;
}
getAuditType(): string {
return AuditType.enterprise;
}
/**
*
* @param body
@@ -38,6 +43,7 @@ export class TransferController extends BaseController {
const { projectId } = await this.getProjectUserIdRead();
const userId = this.getUserId();
await this.service.transferAll(userId, projectId);
this.auditLog({ content: "迁移了项目资源" });
return this.ok();
}
}
@@ -3,6 +3,7 @@ import { BaseController } from "@certd/lib-server";
import { Constants } from "@certd/lib-server";
import { EmailService } from "../../../modules/basic/service/email-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -13,10 +14,15 @@ export class EmailController extends BaseController {
@Inject()
emailService: EmailService;
getAuditType(): string {
return AuditType.mine;
}
@Post("/test", { description: Constants.per.authOnly, summary: "测试邮件发送" })
public async test(@Body("receiver") receiver) {
const userId = super.getUserId();
await this.emailService.test(userId, receiver);
this.auditLog({ content: "测试了邮件发送" });
return this.ok({});
}
@@ -31,6 +37,7 @@ export class EmailController extends BaseController {
public async add(@Body("email") email) {
const userId = super.getUserId();
await this.emailService.add(userId, email);
this.auditLog({ content: "添加了邮件" });
return this.ok({});
}
@@ -38,6 +45,7 @@ export class EmailController extends BaseController {
public async delete(@Body("email") email) {
const userId = super.getUserId();
await this.emailService.delete(userId, email);
this.auditLog({ content: "删除了邮件" });
return this.ok({});
}
}
@@ -9,6 +9,7 @@ import { http, logger, utils } from "@certd/basic";
import { ApiTags } from "@midwayjs/swagger";
import { CodeService } from "../../../modules/basic/service/code-service.js";
import { EmailService } from "../../../modules/basic/service/email-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -40,6 +41,10 @@ export class MineController extends BaseController {
@Inject()
emailService: EmailService;
getAuditType(): string {
return AuditType.mine;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询用户信息" })
public async info() {
const userId = this.getUserId();
@@ -73,6 +78,7 @@ export class MineController extends BaseController {
public async changePassword(@Body(ALL) body: any) {
const userId = this.getUserId();
await this.userService.changePassword(userId, body);
this.auditLog({ content: "修改了密码" });
return this.ok({});
}
@@ -80,6 +86,7 @@ export class MineController extends BaseController {
public async initPassword(@Body(ALL) body: any) {
const userId = this.getUserId();
await this.userService.initPassword(userId, body);
this.auditLog({ content: "初始化了密码" });
return this.ok({});
}
@@ -91,6 +98,7 @@ export class MineController extends BaseController {
avatar: body.avatar,
nickName: body.nickName,
});
this.auditLog({ content: "更新了个人资料" });
return this.ok({});
}
@@ -129,6 +137,7 @@ export class MineController extends BaseController {
phoneCode: body.phoneCode,
mobile: body.mobile,
});
this.auditLog({ content: "修改了手机号" });
return this.ok({});
}
@@ -145,6 +154,7 @@ export class MineController extends BaseController {
await this.userService.updateEmail(userId, {
email: body.email,
});
this.auditLog({ content: "修改了邮箱" });
return this.ok({});
}
@@ -198,6 +208,7 @@ export class MineController extends BaseController {
}),
});
this.auditLog({ content: "初始化了Let's Encrypt ACME账号和邮件通知" });
return this.ok({ success: true });
}
}
@@ -3,6 +3,7 @@ import { PasskeyService } from "../../../modules/login/service/passkey-service.j
import { BaseController, Constants } from "@certd/lib-server";
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
@Provide()
@Controller("/api/mine/passkey")
@@ -14,6 +15,10 @@ export class MinePasskeyController extends BaseController {
@Inject()
userService: UserService;
getAuditType(): string {
return AuditType.mine;
}
@Post("/generateRegistration", { description: Constants.per.authOnly, summary: "生成Passkey注册选项" })
public async generateRegistration(
@Body(ALL)
@@ -46,7 +51,7 @@ export class MinePasskeyController extends BaseController {
const deviceName = body.deviceName;
const result = await this.passkeyService.registerPasskey(userId, response, challenge, deviceName, this.ctx);
this.auditLog({ content: "验证注册了Passkey" });
return this.ok(result);
}
@@ -62,6 +67,7 @@ export class MinePasskeyController extends BaseController {
const result = await this.passkeyService.registerPasskey(userId, response, challenge, deviceName, this.ctx);
this.auditLog({ content: "注册了Passkey" });
return this.ok(result);
}
@@ -90,6 +96,7 @@ export class MinePasskeyController extends BaseController {
}
await this.passkeyService.delete([passkey.id]);
this.auditLog({ content: "解绑了Passkey" });
return this.ok({});
}
}
@@ -6,6 +6,7 @@ import { merge } from "lodash-es";
import { TwoFactorService } from "../../../modules/mine/service/two-factor-service.js";
import { isPlus } from "@certd/plus-core";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -19,6 +20,10 @@ export class UserTwoFactorSettingController extends BaseController {
@Inject()
twoFactorService: TwoFactorService;
getAuditType(): string {
return AuditType.mine;
}
@Post("/get", { description: Constants.per.authOnly, summary: "获取双因子认证设置" })
async get() {
const userId = this.getUserId();
@@ -42,6 +47,7 @@ export class UserTwoFactorSettingController extends BaseController {
}
await this.service.saveSetting(userId, null, setting);
this.auditLog({ content: "保存了双因子认证设置" });
return this.ok({});
}
@@ -62,6 +68,7 @@ export class UserTwoFactorSettingController extends BaseController {
userId,
verifyCode: bean.verifyCode,
});
this.auditLog({ content: "保存了验证器设置" });
return this.ok();
}
@@ -69,6 +76,7 @@ export class UserTwoFactorSettingController extends BaseController {
async authenticatorOff() {
const userId = this.getUserId();
await this.twoFactorService.offAuthenticator(userId);
this.auditLog({ content: "关闭了验证器" });
return this.ok();
}
}
@@ -6,6 +6,7 @@ import { UserGrantSetting } from "../../../modules/mine/service/models.js";
import { isPlus } from "@certd/plus-core";
import { merge } from "lodash-es";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -20,6 +21,10 @@ export class UserSettingsController extends CrudController<UserSettingsService>
return this.service;
}
getAuditType(): string {
return AuditType.mine;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询用户设置分页列表" })
async page(@Body(ALL) body) {
body.query = body.query ?? {};
@@ -62,6 +67,7 @@ export class UserSettingsController extends CrudController<UserSettingsService>
async save(@Body(ALL) bean: UserSettingsEntity) {
bean.userId = this.getUserId();
await this.service.save(bean);
this.auditLog({ content: "保存了用户设置" });
return this.ok({});
}
@@ -88,6 +94,7 @@ export class UserSettingsController extends CrudController<UserSettingsService>
merge(setting, bean);
await this.service.saveSetting(userId, null, setting);
this.auditLog({ content: "保存了授权设置" });
return this.ok({});
}
}
@@ -8,6 +8,7 @@ import { logger } from "@certd/basic";
import dayjs from "dayjs";
import { ApiTags } from "@midwayjs/swagger";
import { CertReader } from "@certd/plugin-lib";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -29,6 +30,10 @@ export class CertInfoController extends CrudController<CertInfoService> {
return this.service;
}
getAuditType(): string {
return AuditType.monitor;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询证书分页列表" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
@@ -117,7 +122,9 @@ export class CertInfoController extends CrudController<CertInfoService> {
const { projectId, userId } = await this.getProjectUserIdWrite();
bean.projectId = projectId;
bean.userId = userId;
return await super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了证书(ID:${res.data})` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新证书" })
@@ -125,7 +132,9 @@ export class CertInfoController extends CrudController<CertInfoService> {
await this.checkOwner(this.service, bean.id, "write");
delete bean.userId;
delete bean.projectId;
return await super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了证书(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询证书详情" })
async info(@Query("id") id: number) {
@@ -136,7 +145,9 @@ export class CertInfoController extends CrudController<CertInfoService> {
@Post("/delete", { description: Constants.per.authOnly, summary: "删除证书" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.service, id, "write");
return await super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了证书(ID:${id})` });
return res;
}
@Post("/all", { description: Constants.per.authOnly, summary: "查询所有证书" })
@@ -4,6 +4,7 @@ import { ApiTags } from "@midwayjs/swagger";
import { SiteInfoService } from "../../../modules/monitor/index.js";
import { JobHistoryService } from "../../../modules/monitor/service/job-history-service.js";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -22,6 +23,10 @@ export class JobHistoryController extends CrudController<JobHistoryService> {
return this.service;
}
getAuditType(): string {
return AuditType.jobHistory;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询监控运行历史分页列表" })
async page(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -54,12 +59,15 @@ export class JobHistoryController extends CrudController<JobHistoryService> {
@Post("/delete", { description: Constants.per.authOnly, summary: "删除监控运行历史" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.service, id, "write");
return await super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了监控运行历史(ID:${id})` });
return res;
}
@Post("/batchDelete", { description: Constants.per.authOnly, summary: "批量删除监控运行历史" })
async batchDelete(@Body("ids") ids: number[]) {
const { projectId, userId } = await this.getProjectUserIdWrite();
await this.service.batchDelete(ids, userId, projectId);
this.auditLog({ content: `批量删除了${ids.length}条监控运行历史` });
return this.ok();
}
}
@@ -7,7 +7,7 @@ import { merge } from "lodash-es";
import { SiteIpService } from "../../../modules/monitor/service/site-ip-service.js";
import { utils } from "@certd/basic";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType, AuditAction } from "../../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -25,6 +25,10 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
return this.service;
}
getAuditType(): string {
return AuditType.monitor;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询站点监控分页列表" })
async page(@Body(ALL) body: any) {
body.query = body.query ?? {};
@@ -76,8 +80,6 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
this.service.check(entity.id, true, 0);
}
this.auditLog({
type: AuditType.monitor,
action: AuditAction.add,
content: `新增了站点监控「${bean.name}」(ID:${res.id}, 域名:${bean.domain})`,
});
return this.ok(res);
@@ -94,8 +96,6 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
this.service.check(entity.id, true, 0);
}
this.auditLog({
type: AuditType.monitor,
action: AuditAction.update,
content: `修改了站点监控「${bean.name}」(ID:${bean.id})`,
});
return this.ok();
@@ -111,8 +111,6 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
await this.checkOwner(this.service, id, "write");
const res = await super.delete(id);
this.auditLog({
type: AuditType.monitor,
action: AuditAction.delete,
content: `删除了站点监控(ID:${id})`,
});
return res;
@@ -121,11 +119,9 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
@Post("/batchDelete", { description: Constants.per.authOnly, summary: "批量删除站点监控" })
async batchDelete(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdWrite();
await this.service.batchDelete(body.ids, userId, projectId);
const count = await this.service.batchDelete(body.ids, userId, projectId);
this.auditLog({
type: AuditType.monitor,
action: AuditAction.batchDelete,
content: `批量删除了${body.ids.length}条站点监控`,
content: `批量删除了${count}条站点监控`,
});
return this.ok();
}
@@ -154,6 +150,7 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
userId,
projectId,
});
this.auditLog({ content: "导入了站点监控" });
return this.ok();
}
@@ -191,6 +188,7 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
projectId: projectId,
key,
});
this.auditLog({ content: "删除了站点监控导入任务" });
return this.ok();
}
@@ -203,6 +201,7 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
userId: userId,
projectId: projectId,
});
this.auditLog({ content: "开始了站点监控导入任务" });
return this.ok();
}
@@ -4,6 +4,7 @@ import { AuthService } from "../../../modules/sys/authority/service/auth-service
import { SiteIpService } from "../../../modules/monitor/service/site-ip-service.js";
import { SiteInfoService } from "../../../modules/monitor/index.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -22,6 +23,10 @@ export class SiteInfoController extends CrudController<SiteIpService> {
return this.service;
}
getAuditType(): string {
return AuditType.siteIp;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询站点IP分页列表" })
async page(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -57,6 +62,7 @@ export class SiteInfoController extends CrudController<SiteIpService> {
const { domain, httpsPort } = siteEntity;
this.service.check(res.id, domain, httpsPort);
}
this.auditLog({ content: `新增了站点IP(ID:${res.id})` });
return this.ok(res);
}
@@ -71,6 +77,7 @@ export class SiteInfoController extends CrudController<SiteIpService> {
const { domain, httpsPort } = siteEntity;
this.service.check(siteEntity.id, domain, httpsPort);
}
this.auditLog({ content: `修改了站点IP(ID:${bean.id})` });
return this.ok();
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询站点IP详情" })
@@ -85,6 +92,7 @@ export class SiteInfoController extends CrudController<SiteIpService> {
const entity = await this.service.info(id);
const res = await super.delete(id);
await this.service.updateIpCount(entity.siteId);
this.auditLog({ content: `删除了站点IP(ID:${id})` });
return res;
}
@@ -124,6 +132,7 @@ export class SiteInfoController extends CrudController<SiteIpService> {
siteId: body.siteId,
projectId,
});
this.auditLog({ content: "导入了站点IP" });
return this.ok();
}
}
@@ -3,7 +3,7 @@ import { Constants, CrudController } from "@certd/lib-server";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { OpenKeyService } from "../../../modules/open/service/open-key-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType, AuditAction } from "../../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*/
@@ -19,6 +19,10 @@ export class OpenKeyController extends CrudController<OpenKeyService> {
return this.service;
}
getAuditType(): string {
return AuditType.openKey;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询开放API密钥分页列表" })
async page(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -58,8 +62,6 @@ export class OpenKeyController extends CrudController<OpenKeyService> {
body.userId = userId;
const res = await this.service.add(body);
this.auditLog({
type: AuditType.openKey,
action: AuditAction.add,
content: `新增了API密钥(ID:${res.id}, scope:${body.scope})`,
});
return this.ok(res);
@@ -72,8 +74,6 @@ export class OpenKeyController extends CrudController<OpenKeyService> {
delete bean.projectId;
await this.service.update(bean);
this.auditLog({
type: AuditType.openKey,
action: AuditAction.update,
content: `修改了API密钥(ID:${bean.id})`,
});
return this.ok();
@@ -102,8 +102,6 @@ export class OpenKeyController extends CrudController<OpenKeyService> {
await this.checkOwner(this.getService(), id, "write");
const res = await super.delete(id);
this.auditLog({
type: AuditType.openKey,
action: AuditAction.delete,
content: `删除了API密钥(ID:${id})`,
});
return res;
@@ -4,7 +4,7 @@ import { AccessService } from "@certd/lib-server";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { AccessDefine } from "@certd/pipeline";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType, AuditAction } from "../../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -21,6 +21,10 @@ export class AccessController extends CrudController<AccessService> {
return this.service;
}
getAuditType(): string {
return AuditType.access;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询授权配置分页列表" })
async page(@Body(ALL) body) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -60,8 +64,6 @@ export class AccessController extends CrudController<AccessService> {
bean.projectId = projectId;
const res = await super.add(bean);
this.auditLog({
type: AuditType.access,
action: AuditAction.add,
content: `新增了授权「${bean.name}」(ID:${res.data}, 类型:${bean.type})`,
});
return res;
@@ -74,8 +76,6 @@ export class AccessController extends CrudController<AccessService> {
delete bean.projectId;
const res = await super.update(bean);
this.auditLog({
type: AuditType.access,
action: AuditAction.update,
content: `修改了授权「${bean.name}」(ID:${bean.id})`,
});
return res;
@@ -91,8 +91,6 @@ export class AccessController extends CrudController<AccessService> {
await this.checkOwner(this.getService(), id, "write");
const res = await super.delete(id);
this.auditLog({
type: AuditType.access,
action: AuditAction.delete,
content: `删除了授权(ID:${id})`,
});
return res;
@@ -5,7 +5,7 @@ import { AuthService } from "../../../modules/sys/authority/service/auth-service
import { NotificationDefine } from "@certd/pipeline";
import { checkPlus } from "@certd/plus-core";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType, AuditAction } from "../../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -22,6 +22,10 @@ export class NotificationController extends CrudController<NotificationService>
return this.service;
}
getAuditType(): string {
return AuditType.notification;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询通知配置分页列表" })
async page(@Body(ALL) body) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -64,8 +68,6 @@ export class NotificationController extends CrudController<NotificationService>
}
const res = await super.add(bean);
this.auditLog({
type: AuditType.notification,
action: AuditAction.add,
content: `新增了通知配置「${bean.name}」(ID:${res.data}, 类型:${bean.type})`,
});
return res;
@@ -92,8 +94,6 @@ export class NotificationController extends CrudController<NotificationService>
delete bean.projectId;
const res = await super.update(bean);
this.auditLog({
type: AuditType.notification,
action: AuditAction.update,
content: `修改了通知配置「${bean.name}」(ID:${bean.id})`,
});
return res;
@@ -109,8 +109,6 @@ export class NotificationController extends CrudController<NotificationService>
await this.checkOwner(this.getService(), id, "write");
const res = await super.delete(id);
this.auditLog({
type: AuditType.notification,
action: AuditAction.delete,
content: `删除了通知配置(ID:${id})`,
});
return res;
@@ -172,6 +170,7 @@ export class NotificationController extends CrudController<NotificationService>
const { projectId, userId } = await this.getProjectUserIdRead();
await this.checkOwner(this.getService(), id, "write");
const res = await this.service.setDefault(id, userId, projectId);
this.auditLog({ content: `设置了默认通知配置(ID:${id})` });
return this.ok(res);
}
@@ -7,7 +7,7 @@ import { HistoryService } from "../../../modules/pipeline/service/history-servic
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { PipelineEntity } from "../../../modules/pipeline/entity/pipeline.js";
import { AuditType, AuditAction } from "../../../modules/sys/enterprise/service/audit-constants.js";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
const pipelineExample = `
// 流水线配置示例,实际传送时要去掉注释
@@ -124,11 +124,14 @@ export class PipelineController extends CrudController<PipelineService> {
@Inject()
siteInfoService: SiteInfoService;
getService() {
return this.service;
}
getAuditType(): string {
return AuditType.pipeline;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询流水线分页列表" })
async page(@Body(ALL) body) {
const isAdmin = await this.authService.isAdmin(this.ctx);
@@ -216,8 +219,6 @@ export class PipelineController extends CrudController<PipelineService> {
const title = bean.title;
this.auditLog({
type: AuditType.pipeline,
action: isNew ? AuditAction.add : AuditAction.update,
content: isNew ? `创建了流水线「${title}」(ID:${bean.id})` : `修改了流水线「${title}」(ID:${bean.id})`,
projectId,
});
@@ -240,8 +241,6 @@ export class PipelineController extends CrudController<PipelineService> {
await this.checkOwner(this.getService(), id, "write", true);
await this.service.delete(id);
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.delete,
content: `删除了流水线(ID:${id})`,
});
return this.ok({});
@@ -253,6 +252,7 @@ export class PipelineController extends CrudController<PipelineService> {
delete bean.userId;
delete bean.projectId;
await this.service.disabled(bean.id, bean.disabled);
this.auditLog({ content: `${bean.disabled ? "禁用" : "启用"}了流水线(ID:${bean.id})` });
return this.ok({});
}
@@ -268,8 +268,6 @@ export class PipelineController extends CrudController<PipelineService> {
await this.checkOwner(this.getService(), id, "write", true);
await this.service.trigger(id, stepId, true);
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.execute,
content: `手动执行了流水线(ID:${id})`,
});
return this.ok({});
@@ -280,8 +278,6 @@ export class PipelineController extends CrudController<PipelineService> {
await this.checkOwner(this.historyService, historyId, "write", true);
await this.service.cancel(historyId);
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.cancel,
content: `取消了流水线执行(historyId:${historyId})`,
});
return this.ok({});
@@ -307,91 +303,77 @@ export class PipelineController extends CrudController<PipelineService> {
@Post("/batchDelete", { description: Constants.per.authOnly, summary: "批量删除流水线" })
async batchDelete(@Body("ids") ids: number[]) {
await this.checkPermissionCall(async ({ userId, projectId }) => {
await this.service.batchDelete(ids, userId, projectId);
const count = await this.checkPermissionCall(async ({ userId, projectId }) => {
return await this.service.batchDelete(ids, userId, projectId);
});
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.batchDelete,
content: `批量删除了${ids.length}条流水线`,
content: `批量删除了${count}条流水线`,
});
return this.ok({});
}
@Post("/batchUpdateGroup", { description: Constants.per.authOnly, summary: "批量更新流水线分组" })
async batchUpdateGroup(@Body("ids") ids: number[], @Body("groupId") groupId: number) {
await this.checkPermissionCall(async ({ userId, projectId }) => {
await this.service.batchUpdateGroup(ids, groupId, userId, projectId);
const count = await this.checkPermissionCall(async ({ userId, projectId }) => {
return await this.service.batchUpdateGroup(ids, groupId, userId, projectId);
});
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.batchUpdate,
content: `批量修改了${ids.length}条流水线分组`,
content: `批量修改了${count}条流水线分组`,
});
return this.ok({});
}
@Post("/batchUpdateTrigger", { description: Constants.per.authOnly, summary: "批量更新流水线触发器" })
async batchUpdateTrigger(@Body("ids") ids: number[], @Body("trigger") trigger: any) {
await this.checkPermissionCall(async ({ userId, projectId }) => {
await this.service.batchUpdateTrigger(ids, trigger, userId, projectId);
const count = await this.checkPermissionCall(async ({ userId, projectId }) => {
return await this.service.batchUpdateTrigger(ids, trigger, userId, projectId);
});
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.batchUpdate,
content: `批量修改了${ids.length}条流水线触发器`,
content: `批量修改了${count}条流水线触发器`,
});
return this.ok({});
}
@Post("/batchUpdateNotification", { description: Constants.per.authOnly, summary: "批量更新流水线通知" })
async batchUpdateNotification(@Body("ids") ids: number[], @Body("notification") notification: any) {
await this.checkPermissionCall(async ({ userId, projectId }) => {
await this.service.batchUpdateNotifications(ids, notification, userId, projectId);
const count = await this.checkPermissionCall(async ({ userId, projectId }) => {
return await this.service.batchUpdateNotifications(ids, notification, userId, projectId);
});
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.batchUpdate,
content: `批量修改了${ids.length}条流水线通知`,
content: `批量修改了${count}条流水线通知`,
});
return this.ok({});
}
@Post("/batchUpdateCertApplyOptions", { description: Constants.per.authOnly, summary: "批量更新证书申请任务配置" })
async batchUpdateCertApplyOptions(@Body("ids") ids: number[], @Body("options") options: any) {
await this.checkPermissionCall(async ({ userId, projectId }) => {
await this.service.batchUpdateCertApplyOptions(ids, options, userId, projectId);
const count = await this.checkPermissionCall(async ({ userId, projectId }) => {
return await this.service.batchUpdateCertApplyOptions(ids, options, userId, projectId);
});
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.batchUpdate,
content: `批量修改了${ids.length}条流水线证书申请配置`,
content: `批量修改了${count}条流水线证书申请配置`,
});
return this.ok({});
}
@Post("/batchRerun", { description: Constants.per.authOnly, summary: "批量重新运行流水线" })
async batchRerun(@Body("ids") ids: number[], @Body("force") force: boolean) {
await this.checkPermissionCall(async ({ userId, projectId }) => {
await this.service.batchRerun(ids, force, userId, projectId);
const count = await this.checkPermissionCall(async ({ userId, projectId }) => {
return await this.service.batchRerun(ids, force, userId, projectId);
});
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.execute,
content: `批量重新执行了${ids.length}条流水线`,
content: `批量重新执行了${count}条流水线`,
});
return this.ok({});
}
@Post("/batchTransfer", { description: Constants.per.authOnly, summary: "批量迁移流水线" })
async batchTransfer(@Body("ids") ids: number[], @Body("toProjectId") toProjectId: number) {
await this.checkPermissionCall(async ({}) => {
await this.service.batchTransfer(ids, toProjectId);
const count = await this.checkPermissionCall(async ({}) => {
return await this.service.batchTransfer(ids, toProjectId);
});
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.batchUpdate,
content: `批量迁移了${ids.length}条流水线`,
content: `批量迁移了${count}条流水线`,
});
return this.ok({});
}
@@ -401,8 +383,6 @@ export class PipelineController extends CrudController<PipelineService> {
await this.checkOwner(this.getService(), id, "write", true);
const res = await this.service.refreshWebhookKey(id);
this.auditLog({
type: AuditType.pipeline,
action: AuditAction.update,
content: `刷新了流水线(ID:${id})的Webhook密钥`,
});
return this.ok({
@@ -3,6 +3,7 @@ import { Constants, CrudController } from "@certd/lib-server";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { PipelineGroupService } from "../../../modules/pipeline/service/pipeline-group-service.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -20,6 +21,10 @@ export class PipelineGroupController extends CrudController<PipelineGroupService
return this.service;
}
getAuditType(): string {
return AuditType.pipelineGroup;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询流水线分组分页列表" })
async page(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -52,7 +57,9 @@ export class PipelineGroupController extends CrudController<PipelineGroupService
const { projectId, userId } = await this.getProjectUserIdRead();
bean.userId = userId;
bean.projectId = projectId;
return await super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了流水线分组「${bean.name}」(ID:${res.data})` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新流水线分组" })
@@ -60,7 +67,9 @@ export class PipelineGroupController extends CrudController<PipelineGroupService
await this.checkOwner(this.getService(), bean.id, "write");
delete bean.userId;
delete bean.projectId;
return await super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了流水线分组「${bean.name}」(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询流水线分组详情" })
async info(@Query("id") id: number) {
@@ -71,7 +80,9 @@ export class PipelineGroupController extends CrudController<PipelineGroupService
@Post("/delete", { description: Constants.per.authOnly, summary: "删除流水线分组" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.getService(), id, "write");
return await super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了流水线分组(ID:${id})` });
return res;
}
@Post("/all", { description: Constants.per.authOnly, summary: "查询所有流水线分组" })
@@ -4,6 +4,7 @@ import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/c
import { SubDomainService } from "../../../modules/pipeline/service/sub-domain-service.js";
import { TaskServiceBuilder } from "../../../modules/pipeline/service/getter/task-service-getter.js";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
*
@@ -22,6 +23,10 @@ export class SubDomainController extends CrudController<SubDomainService> {
return this.service;
}
getAuditType(): string {
return AuditType.subDomain;
}
@Post("/parseDomain", { description: Constants.per.authOnly, summary: "解析域名" })
async parseDomain(@Body("fullDomain") fullDomain: string) {
const { projectId, userId } = await this.getProjectUserIdRead();
@@ -64,7 +69,9 @@ export class SubDomainController extends CrudController<SubDomainService> {
const { userId, projectId } = await this.getProjectUserIdRead();
bean.userId = userId;
bean.projectId = projectId;
return super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了子域名(ID:${res.data})` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新子域名" })
@@ -72,7 +79,9 @@ export class SubDomainController extends CrudController<SubDomainService> {
await this.checkOwner(this.getService(), bean.id, "write");
delete bean.userId;
delete bean.projectId;
return super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了子域名(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询子域名详情" })
async info(@Query("id") id: number) {
@@ -83,7 +92,9 @@ export class SubDomainController extends CrudController<SubDomainService> {
@Post("/delete", { description: Constants.per.authOnly, summary: "删除子域名" })
async delete(@Query("id") id: number) {
await this.checkOwner(this.getService(), id, "write");
return super.delete(id);
const res = await super.delete(id);
this.auditLog({ content: `删除了子域名(ID:${id})` });
return res;
}
@Post("/batchDelete", { description: Constants.per.authOnly, summary: "批量删除子域名" })
@@ -3,6 +3,7 @@ import { Constants, CrudController } from "@certd/lib-server";
import { TemplateService } from "../../../modules/pipeline/service/template-service.js";
import { checkPlus } from "@certd/plus-core";
import { ApiTags } from "@midwayjs/swagger";
import { AuditType } from "../../../modules/sys/enterprise/service/audit-constants.js";
/**
* 线
@@ -18,6 +19,10 @@ export class TemplateController extends CrudController<TemplateService> {
return this.service;
}
getAuditType(): string {
return AuditType.template;
}
@Post("/page", { description: Constants.per.authOnly, summary: "查询流水线模版分页列表" })
async page(@Body(ALL) body) {
body.query = body.query ?? {};
@@ -52,7 +57,9 @@ export class TemplateController extends CrudController<TemplateService> {
bean.userId = userId;
bean.projectId = projectId;
checkPlus();
return super.add(bean);
const res = await super.add(bean);
this.auditLog({ content: `新增了流水线模版「${bean.name}」(ID:${res.data})` });
return res;
}
@Post("/update", { description: Constants.per.authOnly, summary: "更新流水线模版" })
@@ -60,7 +67,9 @@ export class TemplateController extends CrudController<TemplateService> {
await this.checkOwner(this.service, bean.id, "write");
delete bean.userId;
delete bean.projectId;
return super.update(bean);
const res = await super.update(bean);
this.auditLog({ content: `修改了流水线模版「${bean.name}」(ID:${bean.id})` });
return res;
}
@Post("/info", { description: Constants.per.authOnly, summary: "查询流水线模版详情" })
async info(@Query("id") id: number) {
@@ -72,6 +81,7 @@ export class TemplateController extends CrudController<TemplateService> {
async delete(@Query("id") id: number) {
const { userId, projectId } = await this.getProjectUserIdWrite();
await this.service.batchDelete([id], userId, projectId);
this.auditLog({ content: `删除了流水线模版(ID:${id})` });
return this.ok({});
}
@@ -79,6 +89,7 @@ export class TemplateController extends CrudController<TemplateService> {
async batchDelete(@Body("ids") ids: number[]) {
const { userId, projectId } = await this.getProjectUserIdWrite();
await this.service.batchDelete(ids, userId, projectId);
this.auditLog({ content: `批量删除了${ids.length}条流水线模版` });
return this.ok({});
}
@@ -95,6 +106,7 @@ export class TemplateController extends CrudController<TemplateService> {
body.projectId = projectId;
checkPlus();
const res = await this.service.createPipelineByTemplate(body);
this.auditLog({ content: `根据模版创建了流水线(ID:${res.id})` });
return this.ok(res);
}
}
@@ -0,0 +1,116 @@
/// <reference types="mocha" />
import assert from "node:assert/strict";
import { Constants } from "@certd/lib-server";
import { AuditLogMiddleware } from "./audit-log.js";
class TestController {
import() {}
noAudit() {}
getAuditType(): string {
return "pipeline";
}
}
function createMiddleware() {
const middleware = new AuditLogMiddleware();
const records: any[] = [];
middleware.auditService = {
async log(record: any) {
records.push(record);
},
} as any;
(middleware as any).isPlusFn = async () => true;
return { middleware, records };
}
function createCtx(method = "import") {
return {
path: "/api/pi/cname/record/import",
status: 200,
body: Constants.res.success,
ip: "127.0.0.1",
user: { id: 1, username: "admin" },
projectId: 3,
request: {
query: { id: "5" },
body: { name: "test" },
},
requestContext: {
async getAsync() {
return new TestController();
},
},
auditRouteInfo: {
controllerClz: TestController,
method,
summary: "导入CNAME记录",
},
auditLog: {
enabled: true,
append: ["提交2条"],
},
} as any;
}
describe("AuditLogMiddleware", () => {
it("writes audit log after successful response", async () => {
const { middleware, records } = createMiddleware();
await middleware.resolve()(createCtx(), async () => {});
assert.equal(records.length, 1);
assert.equal(records[0].userId, 1);
assert.equal(records[0].type, "pipeline");
assert.equal(records[0].action, "导入CNAME记录");
assert.equal(records[0].content, "提交2条");
assert.equal(records[0].username, "admin");
assert.equal(records[0].projectId, 3);
assert.equal(records[0].ipAddress, "127.0.0.1");
assert.equal(records[0].scope, "user");
});
it("uses bean type and action overrides", async () => {
const { middleware, records } = createMiddleware();
const ctx = createCtx();
ctx.auditLog = { enabled: true, type: "settings", action: "修改设置", append: ["修改了安全设置"] };
await middleware.resolve()(ctx, async () => {});
assert.equal(records[0].type, "settings");
assert.equal(records[0].action, "修改设置");
assert.equal(records[0].content, "修改了安全设置");
});
it("skips when auditLog not enabled", async () => {
const { middleware, records } = createMiddleware();
const ctx = createCtx("noAudit");
ctx.auditLog = {};
await middleware.resolve()(ctx, async () => {});
assert.equal(records.length, 0);
});
it("skips failed response", async () => {
const { middleware, records } = createMiddleware();
const ctx = createCtx();
ctx.body = Constants.res.error;
await middleware.resolve()(ctx, async () => {});
assert.equal(records.length, 0);
});
it("skips anonymous request", async () => {
const { middleware, records } = createMiddleware();
const ctx = createCtx();
ctx.user = undefined;
await middleware.resolve()(ctx, async () => {});
assert.equal(records.length, 0);
});
});
@@ -0,0 +1,100 @@
import { Inject, Provide } from "@midwayjs/core";
import { IMidwayKoaContext, IWebMiddleware, NextFunction } from "@midwayjs/koa";
import { Constants } from "@certd/lib-server";
import { AuditService } from "../modules/sys/enterprise/service/audit-service.js";
@Provide()
export class AuditLogMiddleware implements IWebMiddleware {
@Inject()
auditService: AuditService;
private isPlusFn: (() => Promise<boolean>) | null = null;
private async getIsPlus(): Promise<boolean> {
if (!this.isPlusFn) {
try {
const mod = await import("@certd/plus-core");
this.isPlusFn = async () => mod.isPlus();
} catch {
this.isPlusFn = async () => false;
}
}
return this.isPlusFn();
}
resolve() {
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
await next();
await this.writeAuditLog(ctx);
};
}
private async writeAuditLog(ctx: IMidwayKoaContext) {
const routeInfo = ctx.auditRouteInfo;
if (!routeInfo) {
return;
}
if (!ctx.auditLog?.enabled) {
return;
}
const isPlus = await this.getIsPlus();
if (!isPlus) {
return;
}
if (!this.isSuccessResponse(ctx)) {
return;
}
const auditLog = ctx.auditLog;
if (!auditLog.enabled) {
return;
}
const type = auditLog.type || (await this.resolveControllerType(routeInfo.controllerClz, ctx as any));
const action = auditLog.action || routeInfo.summary || "";
const append = auditLog.append;
const appendList = Array.isArray(append) ? append : append ? [append] : [];
const content = auditLog.content || appendList.filter(item => item && String(item).trim()).join(" ");
if (!content) {
return;
}
const projectId = auditLog.projectId ?? ctx.projectId ?? 0;
const scope = auditLog.scope || (ctx.path.startsWith("/api/sys/") ? "system" : "user");
const ipAddress = ctx.ip || "";
await this.auditService.log({
userId: auditLog.userId ?? ctx.user?.id ?? 0,
type: type || "unknown",
action,
content,
username: auditLog.username || ctx.user?.username,
projectId,
ipAddress,
scope,
});
}
private async resolveControllerType(controllerClz: any, ctx: any): Promise<string | undefined> {
if (!controllerClz || !ctx.requestContext) {
return undefined;
}
try {
const controller = await ctx.requestContext.getAsync(controllerClz);
return controller?.getAuditType?.();
} catch {
return undefined;
}
}
private isSuccessResponse(ctx: IMidwayKoaContext) {
if (ctx.status >= 400) {
return false;
}
const body = ctx.body as any;
if (body?.code == null) {
return true;
}
return body.code === Constants.res.success.code;
}
}
@@ -11,7 +11,7 @@ function createMiddleware(permission: string) {
middleware.secret = "test-secret";
middleware.webRouterService = {
async getMatchedRouterInfo() {
return { description: permission };
return { description: permission, summary: "测试路由" };
},
} as any;
return middleware;
@@ -41,6 +41,7 @@ describe("AuthorityMiddleware guestOptionalAuth", () => {
assert.equal(called, true);
assert.equal(ctx.user, undefined);
assert.equal(ctx.auditRouteInfo.summary, "测试路由");
});
it("sets user when token is provided", async () => {
@@ -40,6 +40,8 @@ export class AuthorityMiddleware implements IWebMiddleware {
await next();
return;
}
ctx.auditRouteInfo = routeInfo;
this.extractProjectId(ctx);
let permission = routeInfo.description || "";
permission = permission.trim().split(" ")[0];
if (permission == null || permission === "") {
@@ -109,6 +111,16 @@ export class AuthorityMiddleware implements IWebMiddleware {
return;
}
private extractProjectId(ctx: IMidwayKoaContext) {
const headerVal = ctx.headers["project-id"] as string;
const queryVal = (ctx.request as any)?.query?.projectId;
const bodyVal = (ctx.request as any)?.body?.projectId;
const raw = headerVal || queryVal || bodyVal;
if (raw != null && raw !== "") {
ctx.projectId = parseInt(String(raw), 10) || 0;
}
}
private getTokenFromRequest(ctx: IMidwayKoaContext) {
let token = ctx.get("Authorization") || "";
token = token.replace("Bearer ", "").trim();
@@ -67,7 +67,7 @@ export class AutoCron {
await this.registerDomainExpireCheckCron();
await this.registerAuditCleanCron();
// await this.registerAuditCleanCron();
}
async registerSiteMonitorCron() {
@@ -503,6 +503,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
},
})
);
return { count: domains.length };
}
async _import(req: { userId: number; projectId: number; domains: string[]; cnameProviderId: any }, task: BackTask) {
@@ -64,7 +64,7 @@ export class LoginService {
cache.delete(`login_block_duration:${username}`);
}
addErrorTimes(username: string, errorMessage: string) {
addErrorTimes(username: string, errorMessage: string, userId?: number) {
const errorTimesKey = `login_error_times:${username}`;
const blockTimesKey = `login_block_times:${username}`;
const blockDurationKey = `login_block_duration:${username}`;
@@ -100,13 +100,13 @@ export class LoginService {
});
// 清除error次数
cache.delete(errorTimesKey);
throw new LoginErrorException(`登录失败次数过多,请${leftMin}分钟后重试`, 0);
throw new LoginErrorException(`登录失败次数过多,请${leftMin}分钟后重试`, 0, userId);
}
const leftTimes = maxRetryTimes - errorTimes;
if (leftTimes < 3) {
throw new LoginErrorException(`登录失败(${errorMessage}),剩余尝试次数:${leftTimes}`, leftTimes);
throw new LoginErrorException(`登录失败(${errorMessage}),剩余尝试次数:${leftTimes}`, leftTimes, userId);
}
throw new LoginErrorException(errorMessage, leftTimes);
throw new LoginErrorException(errorMessage, leftTimes, userId);
}
async register(type: string, user: UserEntity, inviteCode?: string, withTx?: (tx: EntityManager) => Promise<void>) {
@@ -166,7 +166,7 @@ export class LoginService {
}
const right = await this.userService.checkPassword(password, info.password, info.passwordVersion);
if (!right) {
this.addErrorTimes(username, "用户名或密码错误");
this.addErrorTimes(username, "用户名或密码错误", info.id);
}
this.clearCacheOnSuccess(username);
return this.onLoginSuccess(info);
@@ -251,6 +251,7 @@ export class LoginService {
return {
token,
expire,
userId: user.id,
};
}
@@ -805,11 +805,12 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
});
}
async batchDelete(ids: number[], userId: number, projectId?: number): Promise<void> {
async batchDelete(ids: number[], userId: number, projectId?: number): Promise<number> {
const userProjectQuery = this.buildUserProjectQuery(userId, projectId);
await this.repository.delete({
const result = await this.repository.delete({
id: In(ids),
...userProjectQuery,
});
return result.affected || 0;
}
}
@@ -962,7 +962,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
return result;
}
async batchDelete(ids: number[], userId?: number, projectId?: number) {
async batchDelete(ids: number[], userId?: number, projectId?: number): Promise<number> {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级Certd专业版");
}
@@ -975,9 +975,10 @@ export class PipelineService extends BaseService<PipelineEntity> {
}
await this.delete(id);
}
return ids.length;
}
async batchUpdateGroup(ids: number[], groupId: number, userId: any, projectId?: number) {
async batchUpdateGroup(ids: number[], groupId: number, userId: any, projectId?: number): Promise<number> {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级Certd专业版");
}
@@ -988,19 +989,20 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (projectId) {
query.projectId = projectId;
}
await this.repository.update(
const result = await this.repository.update(
{
id: In(ids),
...query,
},
{ groupId }
);
return result.affected || 0;
}
/**
*
*/
async batchTransfer(ids: number[], projectId: number) {
async batchTransfer(ids: number[], projectId: number): Promise<number> {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级Certd专业版");
}
@@ -1082,9 +1084,10 @@ export class PipelineService extends BaseService<PipelineEntity> {
await this.repository.save(entity);
await this.save(entity);
}
return ids.length;
}
async batchUpdateTrigger(ids: number[], trigger: any, userId: any, projectId?: number) {
async batchUpdateTrigger(ids: number[], trigger: any, userId: any, projectId?: number): Promise<any> {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级Certd专业版");
}
@@ -1137,9 +1140,10 @@ export class PipelineService extends BaseService<PipelineEntity> {
}
await this.doUpdatePipelineJson(item, pipeline);
}
return list.length;
}
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any, projectId?: number) {
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any, projectId?: number): Promise<number> {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级Certd专业版");
}
@@ -1178,9 +1182,10 @@ export class PipelineService extends BaseService<PipelineEntity> {
];
await this.doUpdatePipelineJson(item, pipeline);
}
return list.length;
}
async batchUpdateCertApplyOptions(ids: number[], options: CertApplyStepInputPatch, userId: any, projectId?: number) {
async batchUpdateCertApplyOptions(ids: number[], options: CertApplyStepInputPatch, userId: any, projectId?: number): Promise<number> {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级Certd专业版");
}
@@ -1209,9 +1214,10 @@ export class PipelineService extends BaseService<PipelineEntity> {
}
await this.doUpdatePipelineJson(item, pipeline);
}
return list.length;
}
async batchRerun(ids: number[], force: boolean, userId: any, projectId?: number) {
async batchRerun(ids: number[], force: boolean, userId: any, projectId?: number): Promise<number> {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级Certd专业版");
}
@@ -1237,6 +1243,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
//异步执行
this.startBatchRerun(userId, ids, force);
return ids.length;
}
startBatchRerun(userId: number, ids: number[], force: boolean) {
@@ -87,7 +87,7 @@ export class TemplateService extends BaseService<TemplateEntity> {
};
}
async batchDelete(ids: number[], userId: number, projectId?: number) {
async batchDelete(ids: number[], userId: number, projectId?: number): Promise<number> {
const where: any = {
id: In(ids),
};
@@ -102,6 +102,7 @@ export class TemplateService extends BaseService<TemplateEntity> {
const pipelineIds = list.map(item => item.pipelineId);
await this.delete(ids);
await this.pipelineService.batchDelete(pipelineIds, userId, projectId);
return ids.length;
}
async createPipelineByTemplate(body: PipelineEntity) {
@@ -272,7 +272,7 @@ export class UserService extends BaseService<UserEntity> {
// return;
}
await this.resetPassword(user.id, data.password);
return user.username;
return user;
}
async getByUsername(username: any) {
@@ -10,8 +10,11 @@ export class AuditLogEntity {
@Column({ name: "user_id", comment: "UserId" })
userId: number;
@Column({ name: "scope", comment: "system/user" })
scope: string;
@Column({ name: "username", comment: "用户名" })
userName: string;
username: string;
@Column({ name: "project_id", comment: "ProjectId" })
projectId: number;
@@ -4,11 +4,27 @@ export const AuditType = {
monitor: "monitor",
notification: "notification",
openKey: "openKey",
cname: "cname",
user: "user",
role: "role",
permission: "permission",
project: "project",
settings: "settings",
domain: "domain",
dnsPersist: "dnsPersist",
certTemplate: "certTemplate",
pipelineGroup: "pipelineGroup",
subDomain: "subDomain",
template: "template",
mine: "mine",
login: "login",
addon: "addon",
enterprise: "enterprise",
plugin: "plugin",
siteIp: "siteIp",
jobHistory: "jobHistory",
account: "account",
plus: "plus",
} as const;
export const AuditTypeMap = {
@@ -17,11 +33,27 @@ export const AuditTypeMap = {
monitor: "站点监控",
notification: "通知设置",
openKey: "API密钥",
cname: "CNAME记录",
user: "用户管理",
role: "角色管理",
permission: "权限管理",
project: "项目管理",
settings: "系统设置",
domain: "域名管理",
dnsPersist: "持久验证记录",
certTemplate: "证书参数模版",
pipelineGroup: "流水线分组",
subDomain: "子域名托管",
template: "流水线模版",
mine: "个人设置",
login: "登录日志",
addon: "扩展插件",
enterprise: "企业管理",
plugin: "插件管理",
siteIp: "站点IP",
jobHistory: "监控历史",
account: "账号管理",
plus: "Plus许可",
} as const;
export const AuditAction = {
@@ -33,6 +65,16 @@ export const AuditAction = {
batchDelete: "batchDelete",
batchUpdate: "batchUpdate",
disable: "disable",
import: "import",
bind: "bind",
unbind: "unbind",
register: "register",
login: "login",
resetStatus: "resetStatus",
setDefault: "setDefault",
save: "save",
trigger: "trigger",
active: "active",
} as const;
export const AuditActionMap = {
@@ -44,6 +86,16 @@ export const AuditActionMap = {
batchDelete: "批量删除",
batchUpdate: "批量修改",
disable: "禁用",
import: "导入",
bind: "绑定",
unbind: "解绑",
register: "注册",
login: "登录",
resetStatus: "重置状态",
setDefault: "设置默认",
save: "保存",
trigger: "触发",
active: "激活",
} as const;
export const AuditTypeColorMap: Record<string, string> = {
@@ -52,11 +104,27 @@ export const AuditTypeColorMap: Record<string, string> = {
monitor: "green",
notification: "purple",
openKey: "red",
cname: "cyan",
user: "cyan",
role: "geekblue",
permission: "lime",
project: "gold",
settings: "magenta",
domain: "blue",
dnsPersist: "purple",
certTemplate: "orange",
pipelineGroup: "cyan",
subDomain: "geekblue",
template: "blue",
mine: "default",
login: "red",
addon: "orange",
enterprise: "gold",
plugin: "magenta",
siteIp: "green",
jobHistory: "default",
account: "geekblue",
plus: "volcano",
};
export const AuditActionColorMap: Record<string, string> = {
@@ -68,6 +136,16 @@ export const AuditActionColorMap: Record<string, string> = {
batchDelete: "volcano",
batchUpdate: "purple",
disable: "default",
import: "cyan",
bind: "green",
unbind: "red",
register: "green",
login: "blue",
resetStatus: "orange",
setDefault: "cyan",
save: "purple",
trigger: "orange",
active: "green",
};
export function buildAuditTypeDict() {
@@ -1,108 +1,66 @@
import { BaseService } from "@certd/lib-server";
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { LessThan, Repository } from "typeorm";
import { AuditLogEntity } from "../entity/audit-log.js";
import { logger } from "@certd/basic";
import { Cron } from "../../../cron/cron.js";
export type AuditPageReq = {
page?: { offset: number; limit: number };
query?: {
userId?: number;
type?: string;
action?: string;
createTime_start?: string;
createTime_end?: string;
};
sort?: { prop: string; asc: boolean };
};
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class AuditService {
@Inject()
dataSourceManager: TypeORMDataSourceManager;
export class AuditService extends BaseService<AuditLogEntity> {
@InjectEntityModel(AuditLogEntity)
repository: Repository<AuditLogEntity>;
@Inject()
cron: Cron;
getRepository(): Repository<AuditLogEntity> {
return this.dataSourceManager.getDataSource("default").getRepository(AuditLogEntity);
getRepository() {
return this.repository;
}
async log(params: { userId: number; type: string; action: string; content: string; username?: string; projectId?: number; projectName?: string; ipAddress?: string }): Promise<void> {
async page(pageReq: any) {
if (pageReq.query?.createTime) {
const start = pageReq.query.createTime[0];
const end = pageReq.query.createTime[1];
delete pageReq.query.createTime;
pageReq.buildQuery = (qb: any) => {
qb.andWhere("main.createTime BETWEEN :start AND :end", { start, end });
};
}
return await super.page(pageReq);
}
async log(params: { userId: number; type: string; action: string; content: string; username?: string; projectId?: number; ipAddress?: string; scope?: string }): Promise<void> {
try {
let { username } = params;
if (!username && params.userId != null) {
const userRepo = this.dataSourceManager.getDataSource("default").getRepository("sys_user" as any);
const userRepo = this.repository.manager.connection.getRepository("sys_user" as any);
const user = await userRepo.findOne({ where: { id: params.userId } });
username = user?.username || "";
}
const repo = this.getRepository();
const entity = new AuditLogEntity();
entity.userId = params.userId;
entity.userName = username || "";
entity.username = username || "";
entity.type = params.type;
entity.action = params.action;
entity.content = params.content;
entity.projectId = params.projectId || 0;
entity.projectName = params.projectName || "";
entity.projectName = "";
entity.ipAddress = params.ipAddress || "";
await repo.save(entity);
entity.scope = params.scope || "user";
await this.repository.save(entity);
} catch (e) {
logger.error("写入审计日志失败:", e);
}
}
async page(pageReq: AuditPageReq) {
const repo = this.getRepository();
const { page, query, sort } = pageReq;
const offset = page?.offset ?? 0;
const limit = page?.limit ?? 20;
const qb = repo.createQueryBuilder("main");
if (query?.userId != null) {
qb.andWhere("main.userId = :userId", { userId: query.userId });
}
if (query?.type) {
qb.andWhere("main.type = :type", { type: query.type });
}
if (query?.action) {
qb.andWhere("main.action = :action", { action: query.action });
}
if (query?.createTime_start) {
qb.andWhere("main.createTime >= :start", { start: query.createTime_start });
}
if (query?.createTime_end) {
qb.andWhere("main.createTime <= :end", { end: query.createTime_end });
}
if (sort?.prop) {
qb.orderBy("main." + sort.prop, sort.asc ? "ASC" : "DESC");
} else {
qb.orderBy("main.id", "DESC");
}
qb.skip(offset).take(limit);
const list = await qb.getMany();
const total = await qb.getCount();
return {
records: list,
total,
offset,
limit,
};
}
async cleanExpired(retentionDays: number): Promise<number> {
const repo = this.getRepository();
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - retentionDays);
const result = await repo.delete({
const result = await this.repository.delete({
createTime: LessThan(cutoff),
} as any);
const deletedCount = result.affected || 0;
@@ -112,11 +70,6 @@ export class AuditService {
return deletedCount;
}
async delete(id: number): Promise<void> {
const repo = this.getRepository();
await repo.delete({ id });
}
registerCleanCron() {
this.cron.register({
name: "audit.cleanExpired",
+9
View File
@@ -0,0 +1,9 @@
import { AuditLogContext } from "@certd/lib-server";
declare module "koa" {
interface Context {
auditLog?: AuditLogContext;
auditRouteInfo?: { controllerClz?: any; method?: any; summary?: string; description?: string };
projectId?: number;
}
}