perf: 修复删除历史记录没有删除log的bug,新增history管理页面,演示站点启动时不自动启动非管理员用户的定时任务

This commit is contained in:
xiaojunnuo
2024-08-05 12:49:44 +08:00
parent 0227155ab4
commit f78ae93eed
25 changed files with 562 additions and 77 deletions
@@ -0,0 +1,38 @@
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { RoleService } from './role-service.js';
import { BaseService } from '../../../basic/base-service.js';
/**
* 权限校验
*/
@Provide()
@Scope(ScopeEnum.Singleton)
export class AuthService {
@Inject()
roleService: RoleService;
async checkPermission(ctx: any, permission: string) {
//如果不是仅校验登录,还需要校验是否拥有权限
const roleIds: number[] = ctx.user.roles;
const permissions = await this.roleService.getCachedPermissionSetByRoleIds(roleIds);
if (!permissions.has(permission)) {
return false;
}
return true;
}
async isAdmin(ctx: any) {
const roleIds: number[] = ctx.user.roles;
if (roleIds.includes(1)) {
return true;
}
}
async checkEntityUserId(ctx: any, service: BaseService<any>, id: any = 0, userKey = 'userId') {
const isAdmin = await this.isAdmin(ctx);
if (isAdmin) {
return true;
}
await service.checkUserId(id, ctx.user.id, userKey);
}
}
@@ -1,6 +1,6 @@
import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
import { PipelineService } from '../service/pipeline-service.js';
import { logger } from '../../../utils/logger.js';
import { PipelineService } from '../pipeline/service/pipeline-service.js';
import { logger } from '../../utils/logger.js';
@Autoload()
@Scope(ScopeEnum.Singleton)
@@ -8,6 +8,9 @@ export class AutoRegisterCron {
@Inject()
pipelineService: PipelineService;
@Config('preview.enabled')
private preview: boolean;
// @Inject()
// echoPlugin: EchoPlugin;
@Config('cron.immediateTriggerOnce')
@@ -16,7 +19,7 @@ export class AutoRegisterCron {
@Init()
async init() {
logger.info('加载定时trigger开始');
await this.pipelineService.onStartup(this.immediateTriggerOnce);
await this.pipelineService.onStartup(this.immediateTriggerOnce, this.preview);
// logger.info(this.echoPlugin, this.echoPlugin.test);
// logger.info('加载定时trigger完成');
//
@@ -11,6 +11,8 @@ import { CommonException } from '../../../basic/exception/common-exception.js';
import { PermissionException } from '../../../basic/exception/permission-exception.js';
import * as fs from 'fs';
import { logger } from '../../../utils/logger.js';
import { AuthService } from '../../authority/service/auth-service.js';
import { SysSettingsService } from '../../system/service/sys-settings-service.js';
/**
* 证书
@@ -25,19 +27,35 @@ export class HistoryController extends CrudController<HistoryService> {
@Inject()
logService: HistoryLogService;
@Inject()
authService: AuthService;
@Inject()
sysSettingsService: SysSettingsService;
getService() {
return this.service;
}
@Post('/page', { summary: Constants.per.authOnly })
async page(@Body(ALL) body) {
body.query.userId = this.ctx.user.id;
return super.page(body);
const isAdmin = await this.authService.isAdmin(this.ctx);
const publicSettings = await this.sysSettingsService.getPublicSettings();
if (!(publicSettings.managerOtherUserPipeline && isAdmin)) {
body.query.userId = this.ctx.user.id;
}
const res = await super.page(body);
return res;
}
@Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body) {
body.userId = this.ctx.user.id;
const isAdmin = await this.authService.isAdmin(this.ctx);
if (!isAdmin) {
body.userId = this.ctx.user.id;
}
if (body.pipelineId == null) {
return this.ok([]);
}
@@ -56,7 +74,7 @@ export class HistoryController extends CrudController<HistoryService> {
@Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) {
await this.service.checkUserId(bean.id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
return super.update(bean);
}
@@ -64,7 +82,7 @@ export class HistoryController extends CrudController<HistoryService> {
async save(@Body(ALL) bean: HistoryEntity) {
bean.userId = this.ctx.user.id;
if (bean.id > 0) {
await this.service.checkUserId(bean.id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
}
await this.service.save(bean);
return this.ok(bean.id);
@@ -74,7 +92,7 @@ export class HistoryController extends CrudController<HistoryService> {
async saveLog(@Body(ALL) bean: HistoryLogEntity) {
bean.userId = this.ctx.user.id;
if (bean.id > 0) {
await this.service.checkUserId(bean.id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
}
await this.logService.save(bean);
return this.ok(bean.id);
@@ -82,26 +100,37 @@ export class HistoryController extends CrudController<HistoryService> {
@Post('/delete', { summary: Constants.per.authOnly })
async delete(@Query('id') id) {
await this.service.checkUserId(id, this.ctx.user.id);
return super.delete(id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), id);
await super.delete(id);
return this.ok();
}
@Post('/deleteByIds', { summary: Constants.per.authOnly })
async deleteByIds(@Body(ALL) body) {
await this.authService.checkEntityUserId(this.ctx, this.getService(), body.ids);
const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? null : this.ctx.user.id;
await this.getService().deleteByIds(body.ids, userId);
return this.ok();
}
@Post('/detail', { summary: Constants.per.authOnly })
async detail(@Query('id') id) {
await this.service.checkUserId(id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), id);
const detail = await this.service.detail(id);
return this.ok(detail);
}
@Post('/logs', { summary: Constants.per.authOnly })
async logs(@Query('id') id) {
await this.logService.checkUserId(id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.logService, id);
const logInfo = await this.logService.info(id);
return this.ok(logInfo);
}
@Post('/files', { summary: Constants.per.authOnly })
async files(@Query('pipelineId') pipelineId, @Query('historyId') historyId) {
await this.authService.checkEntityUserId(this.ctx, this.service, historyId);
const files = await this.getFiles(historyId, pipelineId);
return this.ok(files);
}
@@ -125,6 +154,7 @@ export class HistoryController extends CrudController<HistoryService> {
@Get('/download', { summary: Constants.per.authOnly })
async download(@Query('pipelineId') pipelineId, @Query('historyId') historyId, @Query('fileId') fileId) {
await this.authService.checkEntityUserId(this.ctx, this.service, historyId);
const files = await this.getFiles(historyId, pipelineId);
const file = files.find(f => f.id === fileId);
if (file == null) {
@@ -4,6 +4,8 @@ import { PipelineService } from '../service/pipeline-service.js';
import { PipelineEntity } from '../entity/pipeline.js';
import { Constants } from '../../../basic/constants.js';
import { HistoryService } from '../service/history-service.js';
import { AuthService } from '../../authority/service/auth-service.js';
import { SysSettingsService } from '../../system/service/sys-settings-service.js';
/**
* 证书
@@ -15,6 +17,10 @@ export class PipelineController extends CrudController<PipelineService> {
service: PipelineService;
@Inject()
historyService: HistoryService;
@Inject()
authService: AuthService;
@Inject()
sysSettingsService: SysSettingsService;
getService() {
return this.service;
@@ -22,7 +28,11 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/page', { summary: Constants.per.authOnly })
async page(@Body(ALL) body) {
body.query.userId = this.ctx.user.id;
const isAdmin = await this.authService.isAdmin(this.ctx);
const publicSettings = await this.sysSettingsService.getPublicSettings();
if (!(publicSettings.managerOtherUserPipeline && isAdmin)) {
body.query.userId = this.ctx.user.id;
}
const title = body.query.title;
delete body.query.title;
@@ -47,7 +57,7 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) {
await this.service.checkUserId(bean.id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
return super.update(bean);
}
@@ -55,7 +65,7 @@ export class PipelineController extends CrudController<PipelineService> {
async save(@Body(ALL) bean: PipelineEntity) {
bean.userId = this.ctx.user.id;
if (bean.id > 0) {
await this.service.checkUserId(bean.id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
}
await this.service.save(bean);
return this.ok(bean.id);
@@ -63,28 +73,28 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/delete', { summary: Constants.per.authOnly })
async delete(@Query('id') id) {
await this.service.checkUserId(id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), id);
await this.service.delete(id);
return this.ok({});
}
@Post('/detail', { summary: Constants.per.authOnly })
async detail(@Query('id') id) {
await this.service.checkUserId(id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), id);
const detail = await this.service.detail(id);
return this.ok(detail);
}
@Post('/trigger', { summary: Constants.per.authOnly })
async trigger(@Query('id') id) {
await this.service.checkUserId(id, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.getService(), id);
await this.service.trigger(id);
return this.ok({});
}
@Post('/cancel', { summary: Constants.per.authOnly })
async cancel(@Query('historyId') historyId) {
await this.historyService.checkUserId(historyId, this.ctx.user.id);
await this.authService.checkEntityUserId(this.ctx, this.historyService, historyId);
await this.service.cancel(historyId);
return this.ok({});
}
@@ -35,4 +35,13 @@ export class HistoryEntity {
default: () => 'CURRENT_TIMESTAMP',
})
updateTime: Date;
pipelineTitle: string;
fillPipelineTitle() {
if (this.pipeline) {
const pipeline = JSON.parse(this.pipeline);
this.pipelineTitle = pipeline.title;
}
}
}
@@ -1,6 +1,6 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { In, Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service.js';
import { HistoryLogEntity } from '../entity/history-log.js';
@@ -24,4 +24,11 @@ export class HistoryLogService extends BaseService<HistoryLogEntity> {
await this.add(bean);
}
}
async deleteByHistoryIds(numbers: number[]) {
if (numbers.length === 0) {
return;
}
await this.repository.delete({ historyId: In(numbers) });
}
}
@@ -1,6 +1,6 @@
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { In, Repository } from 'typeorm';
import { BaseService } from '../../../basic/base-service.js';
import { HistoryEntity } from '../entity/history.js';
import { PipelineEntity } from '../entity/pipeline.js';
@@ -28,6 +28,14 @@ export class HistoryService extends BaseService<HistoryEntity> {
return this.repository;
}
async page(query, page, sort, buildQuery) {
const res = await super.page(query, page, sort, buildQuery);
for (const item of res.records) {
item.fillPipelineTitle();
}
return res;
}
async save(bean: HistoryEntity) {
if (bean.id > 0) {
await this.update(bean);
@@ -51,7 +59,7 @@ export class HistoryService extends BaseService<HistoryEntity> {
};
const { id } = await this.add(bean);
//清除大于pipeline.keepHistoryCount的历史记录
this.clear(pipeline.id, pipeline.keepHistoryCount);
await this.clear(pipeline.id, pipeline.keepHistoryCount);
return id;
}
@@ -85,7 +93,6 @@ export class HistoryService extends BaseService<HistoryEntity> {
skip: 0,
take: deleteCountBatch,
});
await this.repository.remove(list);
for (const historyEntity of list) {
const id = historyEntity.id;
@@ -95,6 +102,9 @@ export class HistoryService extends BaseService<HistoryEntity> {
logger.error('删除文件失败', e);
}
}
await this.repository.remove(list);
await this.logService.deleteByHistoryIds(list.map(item => item.id));
shouldDeleteCount -= deleteCountBatch;
}
@@ -124,4 +134,12 @@ export class HistoryService extends BaseService<HistoryEntity> {
});
return files;
}
async deleteByIds(ids: number[], userId: number) {
await this.repository.delete({
id: In(ids),
userId,
});
await this.logService.deleteByHistoryIds(ids);
}
}
@@ -100,11 +100,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
await this.registerTriggerById(bean.id);
}
/**
* 应用启动后初始加载记录
*/
async onStartup(immediateTriggerOnce: boolean) {
logger.info('加载定时trigger开始');
async foreachPipeline(callback: (pipeline: PipelineEntity) => void) {
const idEntityList = await this.repository.find({
select: {
id: true,
@@ -135,14 +131,35 @@ export class PipelineService extends BaseService<PipelineEntity> {
});
for (const entity of list) {
const pipeline = JSON.parse(entity.content ?? '{}');
try {
await this.registerTriggers(pipeline, immediateTriggerOnce);
} catch (e) {
logger.error('加载定时trigger失败:', e);
}
await callback(entity);
}
}
}
async stopOtherUserPipeline(userId: number) {
await this.foreachPipeline(async entity => {
if (entity.userId !== userId) {
await this.clearTriggers(entity.id);
}
});
}
/**
* 应用启动后初始加载记录
*/
async onStartup(immediateTriggerOnce: boolean, preview: boolean) {
logger.info('加载定时trigger开始');
await this.foreachPipeline(async entity => {
if (preview && entity.userId !== 1) {
return;
}
const pipeline = JSON.parse(entity.content ?? '{}');
try {
await this.registerTriggers(pipeline, immediateTriggerOnce);
} catch (e) {
logger.error('加载定时trigger失败:', e);
}
});
logger.info('定时器数量:', this.cron.getTaskSize());
}
@@ -4,6 +4,7 @@ import { SysSettingsService } from '../service/sys-settings-service.js';
import { SysSettingsEntity } from '../entity/sys-settings.js';
import { SysPublicSettings } from '../service/models.js';
import * as _ from 'lodash-es';
import { PipelineService } from '../../pipeline/service/pipeline-service.js';
/**
*/
@@ -12,6 +13,8 @@ import * as _ from 'lodash-es';
export class SysSettingsController extends CrudController<SysSettingsService> {
@Inject()
service: SysSettingsService;
@Inject()
pipelineService: PipelineService;
getService() {
return this.service;
@@ -73,4 +76,9 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
await this.service.savePublicSettings(setting);
return this.ok({});
}
@Post('/stopOtherUserTimer', { summary: 'sys:settings:edit' })
async stopOtherUserTimer(@Body(ALL) body) {
await this.pipelineService.stopOtherUserPipeline(1);
return this.ok({});
}
}
@@ -12,6 +12,7 @@ export class SysPublicSettings extends BaseSettings {
static __title__ = '系统公共设置';
static __access__ = 'public';
registerEnabled = false;
managerOtherUserPipeline = false;
}
export class SysPrivateSettings extends BaseSettings {