chore: history projectId

This commit is contained in:
xiaojunnuo
2026-02-11 18:11:33 +08:00
parent 806a69fef3
commit 638a7f0ab4
15 changed files with 224 additions and 146 deletions
+9 -9
View File
@@ -15,25 +15,25 @@
}, },
"scripts": { "scripts": {
"start": "lerna bootstrap --hoist", "start": "lerna bootstrap --hoist",
"start:server": "cd ./packages/ui/certd-server && npm start", "start:server": "cd ./packages/ui/certd-server && pnpm start",
"devb": "lerna run dev-build", "devb": "lerna run dev-build",
"i-all": "lerna link && lerna exec npm install ", "i-all": "lerna link && lerna exec npm install ",
"publish": "npm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && npm run afterpublishOnly ", "publish": "pnpm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && pnpm run afterpublishOnly ",
"afterpublishOnly": "npm run copylogs && time /t >trigger/build.trigger && git add ./trigger/build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && npm run commitAll", "afterpublishOnly": "pnpm run copylogs && time /t >trigger/build.trigger && git add ./trigger/build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && pnpm run commitAll",
"transform-sql": "cd ./packages/ui/certd-server/db/ && node --experimental-json-modules transform.js", "transform-sql": "cd ./packages/ui/certd-server/db/ && node --experimental-json-modules transform.js",
"plugin-doc-gen": "cd ./packages/ui/certd-server/ && npm run export-metadata", "plugin-doc-gen": "cd ./packages/ui/certd-server/ && pnpm run export-metadata",
"commitAll": "git add . && git commit -m \"build: publish\" && git push && npm run commitPro", "commitAll": "git add . && git commit -m \"build: publish\" && git push && pnpm run commitPro",
"commitPro": "cd ./packages/pro/ && git add . && git commit -m \"build: publish\" && git push", "commitPro": "cd ./packages/pro/ && git add . && git commit -m \"build: publish\" && git push",
"copylogs": "copyfiles \"CHANGELOG.md\" ./docs/guide/changelogs/", "copylogs": "copyfiles \"CHANGELOG.md\" ./docs/guide/changelogs/",
"prepublishOnly1": "npm run check && lerna run build ", "prepublishOnly1": "pnpm run check && lerna run build ",
"prepublishOnly2": "npm run check && npm run before-build && lerna run build && npm run plugin-doc-gen", "prepublishOnly2": "pnpm run check && pnpm run before-build && lerna run build && pnpm run plugin-doc-gen",
"before-build": "npm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"", "before-build": "pnpm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
"deploy1": "node --experimental-json-modules ./scripts/deploy.js ", "deploy1": "node --experimental-json-modules ./scripts/deploy.js ",
"check": "node --experimental-json-modules ./scripts/publish-check.js", "check": "node --experimental-json-modules ./scripts/publish-check.js",
"init": "lerna run build", "init": "lerna run build",
"init:dev": "lerna run build", "init:dev": "lerna run build",
"docs:dev": "vitepress dev docs", "docs:dev": "vitepress dev docs",
"docs:build": "npm run copylogs && vitepress build docs", "docs:build": "pnpm run copylogs && vitepress build docs",
"docs:preview": "vitepress preview docs", "docs:preview": "vitepress preview docs",
"pub": "echo 1", "pub": "echo 1",
"dev": "pnpm run -r --parallel compile ", "dev": "pnpm run -r --parallel compile ",
@@ -100,5 +100,20 @@ export abstract class BaseController {
await projectService.checkPermission({userId,projectId,permission}) await projectService.checkPermission({userId,projectId,permission})
} }
/**
*
* @param service 检查记录是否属于某用户或某项目
* @param id
*/
async checkEntityOwner(service:any,id:number,permission:string){
let { projectId,userId } = await this.getProjectUserId(permission)
const authService:any = await this.applicationContext.getAsync("authService");
if (projectId) {
await authService.checkEntityProjectId(service, id, projectId);
}else{
await authService.checkEntityUserId(this.ctx, service, id);
}
return {projectId,userId}
}
} }
@@ -206,18 +206,28 @@ export abstract class BaseService<T> {
return await qb.getMany(); return await qb.getMany();
} }
async checkUserId(id: any = 0, userId: number, userKey = 'userId') { async checkUserId(ids: number | number[] = 0, userId: number, userKey = 'userId') {
const res = await this.getRepository().findOne({ if (ids == null) {
throw new ValidateException('id不能为空');
}
if (userId == null) {
throw new ValidateException('userId不能为空');
}
if (!Array.isArray(ids)) {
ids = [ids];
}
const res = await this.getRepository().find({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
select: { [userKey]: true }, select: { [userKey]: true },
where: { where: {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
id, id: In(ids),
[userKey]: userId,
}, },
}); });
if (!res || res[userKey] === userId) { if (!res || res.length === ids.length) {
return; return;
} }
throw new PermissionException('权限不足'); throw new PermissionException('权限不足');
@@ -52,12 +52,17 @@ export const useProjectStore = defineStore("app.project", () => {
} }
const projects = await api.MyProjectList(); const projects = await api.MyProjectList();
myProjects.value = projects; myProjects.value = projects;
if (projects.length > 0 && !currentProjectId.value) {
changeCurrentProject(projects[0].id, true);
}
} }
function changeCurrentProject(id: string) { function changeCurrentProject(id: string, silent?: boolean) {
currentProjectId.value = id; currentProjectId.value = id;
LocalStorage.set("currentProjectId", id); LocalStorage.set("currentProjectId", id);
message.success("切换项目成功"); if (!silent) {
message.success("切换项目成功");
}
} }
async function reload() { async function reload() {
@@ -19,6 +19,8 @@ export const projectPermissionDict = dict({
export const myProjectDict = dict({ export const myProjectDict = dict({
url: "/enterprise/project/list", url: "/enterprise/project/list",
value: "id",
label: "name",
}); });
export const userDict = dict({ export const userDict = dict({
@@ -640,6 +640,9 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
title: t("certd.fields.projectName"), title: t("certd.fields.projectName"),
type: "dict-select", type: "dict-select",
dict: myProjectDict, dict: myProjectDict,
form: {
show: false,
},
}, },
updateTime: { updateTime: {
title: t("certd.fields.updateTime"), title: t("certd.fields.updateTime"),
@@ -13,8 +13,7 @@ CREATE TABLE "cd_project"
CREATE INDEX "index_project_user_id" ON "cd_project" ("user_id"); CREATE INDEX "index_project_user_id" ON "cd_project" ("user_id");
CREATE INDEX "index_project_admin_id" ON "cd_project" ("admin_id"); CREATE INDEX "index_project_admin_id" ON "cd_project" ("admin_id");
INSERT INTO cd_project (id, user_id, "name", "disabled") VALUES (1, 1, 'default', false); INSERT INTO cd_project (id, user_id, "admin_id", "name", "disabled") VALUES (1, 0, 1,'default', false);
ALTER TABLE cd_cert_info ADD COLUMN project_id integer; ALTER TABLE cd_cert_info ADD COLUMN project_id integer;
CREATE INDEX "index_cert_project_id" ON "cd_cert_info" ("project_id"); CREATE INDEX "index_cert_project_id" ON "cd_cert_info" ("project_id");
+4 -4
View File
@@ -19,16 +19,16 @@
"dev-new": "cross-env NODE_ENV=dev-new mwtsc --watch --run @midwayjs/mock/app", "dev-new": "cross-env NODE_ENV=dev-new mwtsc --watch --run @midwayjs/mock/app",
"rm-newdb": "rimraf ./data/db-new.sqlite", "rm-newdb": "rimraf ./data/db-new.sqlite",
"test": "cross-env NODE_ENV=unittest mocha", "test": "cross-env NODE_ENV=unittest mocha",
"cov": "cross-env c8 --all --reporter=text --reporter=lcovonly npm run test", "cov": "cross-env c8 --all --reporter=text --reporter=lcovonly pnpm run test",
"lint": "mwts check", "lint": "mwts check",
"lint:fix": "mwts fix", "lint:fix": "mwts fix",
"ci": "npm run cov", "ci": "pnpm run cov",
"build-only": "cross-env NODE_ENV=production mwtsc --cleanOutDir --skipLibCheck", "build-only": "cross-env NODE_ENV=production mwtsc --cleanOutDir --skipLibCheck",
"build": "npm run build-only && npm run export-metadata", "build": "pnpm run build-only && pnpm run export-metadata",
"export-metadata": "node export-plugin-yaml.js", "export-metadata": "node export-plugin-yaml.js",
"export-metadata-only": "node export-plugin-yaml.js docoff", "export-metadata-only": "node export-plugin-yaml.js docoff",
"dev-build": "echo 1", "dev-build": "echo 1",
"build-on-docker": "node ./before-build.js && npm run build-only && npm run export-metadata-only", "build-on-docker": "node ./before-build.js && pnpm run build-only && pnpm run export-metadata-only",
"up-mw-deps": "npx midway-version -u -w", "up-mw-deps": "npx midway-version -u -w",
"heap": "cross-env NODE_ENV=production clinic heapprofiler -- node --optimize-for-size --inspect ./bootstrap.js", "heap": "cross-env NODE_ENV=production clinic heapprofiler -- node --optimize-for-size --inspect ./bootstrap.js",
"flame": "clinic flame -- node ./bootstrap.js", "flame": "clinic flame -- node ./bootstrap.js",
@@ -1,17 +1,17 @@
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
import { CommonException, Constants, CrudController, PermissionException, SysSettingsService } from "@certd/lib-server";
import { PipelineEntity } from "../../../modules/pipeline/entity/pipeline.js";
import { HistoryService } from "../../../modules/pipeline/service/history-service.js";
import { HistoryLogService } from "../../../modules/pipeline/service/history-log-service.js";
import { HistoryEntity } from "../../../modules/pipeline/entity/history.js";
import { HistoryLogEntity } from "../../../modules/pipeline/entity/history-log.js";
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
import * as fs from "fs";
import { logger } from "@certd/basic"; import { logger } from "@certd/basic";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js"; import { CommonException, Constants, CrudController, PermissionException, SysSettingsService } from "@certd/lib-server";
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
import * as fs from "fs";
import { In } from "typeorm"; import { In } from "typeorm";
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
import { UserGrantSetting } from "../../../modules/mine/service/models.js"; import { UserGrantSetting } from "../../../modules/mine/service/models.js";
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
import { HistoryLogEntity } from "../../../modules/pipeline/entity/history-log.js";
import { HistoryEntity } from "../../../modules/pipeline/entity/history.js";
import { PipelineEntity } from "../../../modules/pipeline/entity/pipeline.js";
import { HistoryLogService } from "../../../modules/pipeline/service/history-log-service.js";
import { HistoryService } from "../../../modules/pipeline/service/history-service.js";
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
/** /**
* 证书 * 证书
@@ -41,12 +41,18 @@ export class HistoryController extends CrudController<HistoryService> {
@Post('/page', { summary: Constants.per.authOnly }) @Post('/page', { summary: Constants.per.authOnly })
async page(@Body(ALL) body: any) { async page(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdRead()
body.query.projectId = projectId
const isAdmin = await this.authService.isAdmin(this.ctx); const isAdmin = await this.authService.isAdmin(this.ctx);
const publicSettings = await this.sysSettingsService.getPublicSettings(); const publicSettings = await this.sysSettingsService.getPublicSettings();
const pipelineQuery: any = {}; const pipelineQuery: any = {};
if (!(publicSettings.managerOtherUserPipeline && isAdmin)) { if (!(publicSettings.managerOtherUserPipeline && isAdmin)) {
body.query.userId = this.getUserId(); body.query.userId = userId;
pipelineQuery.userId = this.getUserId(); pipelineQuery.userId = userId;
}
if (projectId) {
pipelineQuery.projectId = projectId;
} }
let pipelineIds: any = null; let pipelineIds: any = null;
@@ -82,10 +88,14 @@ export class HistoryController extends CrudController<HistoryService> {
@Post('/list', { summary: Constants.per.authOnly }) @Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body) { async list(@Body(ALL) body) {
const { projectId, userId } = await this.getProjectUserIdRead()
body.query.projectId = projectId
const isAdmin = this.authService.isAdmin(this.ctx); const isAdmin = this.authService.isAdmin(this.ctx);
if (!isAdmin) { if (!isAdmin) {
body.userId = this.getUserId(); body.userId = userId;
} }
if (body.pipelineId == null) { if (body.pipelineId == null) {
return this.ok([]); return this.ok([]);
} }
@@ -106,7 +116,8 @@ export class HistoryController extends CrudController<HistoryService> {
triggerType: true, triggerType: true,
endTime: true, endTime: true,
createTime: true, createTime: true,
updateTime: true updateTime: true,
projectId: true,
}; };
} }
const listRet = await this.getService().list({ const listRet = await this.getService().list({
@@ -135,32 +146,45 @@ export class HistoryController extends CrudController<HistoryService> {
@Post('/add', { summary: Constants.per.authOnly }) @Post('/add', { summary: Constants.per.authOnly })
async add(@Body(ALL) bean: PipelineEntity) { async add(@Body(ALL) bean: PipelineEntity) {
bean.userId = this.getUserId(); const { projectId, userId } = await this.getProjectUserIdRead()
bean.projectId = projectId
bean.userId = userId;
return super.add(bean); return super.add(bean);
} }
@Post('/update', { summary: Constants.per.authOnly }) @Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) { async update(@Body(ALL) bean) {
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id); await this.checkEntityOwner(this.getService(), bean.id,"write");
delete bean.userId; delete bean.userId;
return super.update(bean); return super.update(bean);
} }
@Post('/save', { summary: Constants.per.authOnly }) @Post('/save', { summary: Constants.per.authOnly })
async save(@Body(ALL) bean: HistoryEntity) { async save(@Body(ALL) bean: HistoryEntity) {
bean.userId = this.getUserId(); const { projectId,userId } = await this.getProjectUserIdWrite()
bean.userId = userId;
bean.projectId = projectId;
if (bean.id > 0) { if (bean.id > 0) {
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id); //修改
delete bean.projectId;
delete bean.userId;
await this.checkEntityOwner(this.getService(), bean.id,"write");
} }
await this.service.save(bean); await this.service.save(bean);
return this.ok(bean.id); return this.ok(bean.id);
} }
@Post('/saveLog', { summary: Constants.per.authOnly }) @Post('/saveLog', { summary: Constants.per.authOnly })
async saveLog(@Body(ALL) bean: HistoryLogEntity) { async saveLog(@Body(ALL) bean: HistoryLogEntity) {
bean.userId = this.getUserId(); const { projectId,userId } = await this.getProjectUserIdWrite()
bean.projectId = projectId;
bean.userId = userId;
if (bean.id > 0) { if (bean.id > 0) {
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id); //修改
delete bean.projectId;
delete bean.userId;
await this.checkEntityOwner(this.logService, bean.id,"write");
} }
await this.logService.save(bean); await this.logService.save(bean);
return this.ok(bean.id); return this.ok(bean.id);
@@ -168,30 +192,30 @@ export class HistoryController extends CrudController<HistoryService> {
@Post('/delete', { summary: Constants.per.authOnly }) @Post('/delete', { summary: Constants.per.authOnly })
async delete(@Query('id') id: number) { async delete(@Query('id') id: number) {
await this.authService.checkEntityUserId(this.ctx, this.getService(), id); await this.checkEntityOwner(this.getService(), id,"write");
await super.delete(id); await super.delete(id);
return this.ok(); return this.ok();
} }
@Post('/deleteByIds', { summary: Constants.per.authOnly }) @Post('/deleteByIds', { summary: Constants.per.authOnly })
async deleteByIds(@Body(ALL) body: any) { async deleteByIds(@Body(ALL) body: any) {
await this.authService.checkEntityUserId(this.ctx, this.getService(), body.ids); let {userId} = await this.checkEntityOwner(this.getService(), body.ids,"write");
const isAdmin = await this.authService.isAdmin(this.ctx); const isAdmin = await this.authService.isAdmin(this.ctx);
const userId = isAdmin ? null : this.getUserId(); userId = isAdmin ? null : userId;
await this.getService().deleteByIds(body.ids, userId); await this.getService().deleteByIds(body.ids, userId);
return this.ok(); return this.ok();
} }
@Post('/detail', { summary: Constants.per.authOnly }) @Post('/detail', { summary: Constants.per.authOnly })
async detail(@Query('id') id: number) { async detail(@Query('id') id: number) {
await this.authService.checkEntityUserId(this.ctx, this.getService(), id); await this.checkEntityOwner(this.getService(), id,"read");
const detail = await this.service.detail(id); const detail = await this.service.detail(id);
return this.ok(detail); return this.ok(detail);
} }
@Post('/logs', { summary: Constants.per.authOnly }) @Post('/logs', { summary: Constants.per.authOnly })
async logs(@Query('id') id: number) { async logs(@Query('id') id: number) {
await this.authService.checkEntityUserId(this.ctx, this.logService, id); await this.checkEntityOwner(this.logService, id,"read");
const logInfo = await this.logService.info(id); const logInfo = await this.logService.info(id);
return this.ok(logInfo); return this.ok(logInfo);
} }
@@ -213,7 +237,14 @@ export class HistoryController extends CrudController<HistoryService> {
if (history == null) { if (history == null) {
throw new CommonException('historyId is null'); throw new CommonException('historyId is null');
} }
if (history.userId !== this.getUserId()) { const {projectId} = await this.getProjectUserIdRead()
if (projectId) {
//enterprise模式
if(history.projectId !== projectId){
throw new PermissionException("您没有权限下载该流水线证书,请先加入该项目:"+history.projectId);
}
//有权限下载
}else if (history.userId !== this.getUserId()) {
// 如果是管理员,检查用户是否有授权管理员查看 // 如果是管理员,检查用户是否有授权管理员查看
const isAdmin = await this.isAdmin() const isAdmin = await this.isAdmin()
if (!isAdmin) { if (!isAdmin) {
@@ -95,25 +95,16 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/update', { summary: Constants.per.authOnly }) @Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) { async update(@Body(ALL) bean) {
const { projectId } = await this.getProjectUserIdWrite() await this.checkEntityOwner(this.getService(), bean.id,"write");
if (projectId) {
await this.authService.checkEntityProjectId(this.getService(), projectId, bean.id);
} else {
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
}
delete bean.userId; delete bean.userId;
return super.update(bean); return super.update(bean);
} }
@Post('/save', { summary: Constants.per.authOnly }) @Post('/save', { summary: Constants.per.authOnly })
async save(@Body(ALL) bean: { addToMonitorEnabled: boolean, addToMonitorDomains: string } & PipelineEntity) { async save(@Body(ALL) bean: { addToMonitorEnabled: boolean, addToMonitorDomains: string } & PipelineEntity) {
const { projectId, userId } = await this.getProjectUserIdWrite() const { userId } = await this.getProjectUserIdWrite()
if (bean.id > 0) { if (bean.id > 0) {
if (projectId) { await this.checkEntityOwner(this.getService(), bean.id,"write");
await this.authService.checkEntityProjectId(this.getService(), projectId, bean.id);
} else {
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
}
} else { } else {
bean.userId = userId; bean.userId = userId;
} }
@@ -140,24 +131,14 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/delete', { summary: Constants.per.authOnly }) @Post('/delete', { summary: Constants.per.authOnly })
async delete(@Query('id') id: number) { async delete(@Query('id') id: number) {
const { projectId } = await this.getProjectUserIdWrite() await this.checkEntityOwner(this.getService(), id,"write");
if (projectId) {
await this.authService.checkEntityProjectId(this.getService(), projectId, id);
} else {
await this.authService.checkEntityUserId(this.ctx, this.getService(), id);
}
await this.service.delete(id); await this.service.delete(id);
return this.ok({}); return this.ok({});
} }
@Post('/disabled', { summary: Constants.per.authOnly }) @Post('/disabled', { summary: Constants.per.authOnly })
async disabled(@Body(ALL) bean) { async disabled(@Body(ALL) bean) {
const { projectId } = await this.getProjectUserIdWrite() await this.checkEntityOwner(this.getService(), bean.id,"write");
if (projectId) {
await this.authService.checkEntityProjectId(this.getService(), projectId, bean.id);
} else {
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
}
delete bean.userId; delete bean.userId;
await this.service.disabled(bean.id, bean.disabled); await this.service.disabled(bean.id, bean.disabled);
return this.ok({}); return this.ok({});
@@ -165,36 +146,21 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/detail', { summary: Constants.per.authOnly }) @Post('/detail', { summary: Constants.per.authOnly })
async detail(@Query('id') id: number) { async detail(@Query('id') id: number) {
const { projectId } = await this.getProjectUserIdRead() await this.checkEntityOwner(this.getService(), id,"read");
if (projectId) {
await this.authService.checkEntityProjectId(this.getService(), projectId, id);
} else {
await this.authService.checkEntityUserId(this.ctx, this.getService(), id);
}
const detail = await this.service.detail(id); const detail = await this.service.detail(id);
return this.ok(detail); return this.ok(detail);
} }
@Post('/trigger', { summary: Constants.per.authOnly }) @Post('/trigger', { summary: Constants.per.authOnly })
async trigger(@Query('id') id: number, @Query('stepId') stepId?: string) { async trigger(@Query('id') id: number, @Query('stepId') stepId?: string) {
const { projectId } = await this.getProjectUserIdWrite() await this.checkEntityOwner(this.getService(), id,"write");
if (projectId) {
await this.authService.checkEntityProjectId(this.getService(), projectId, id);
} else {
await this.authService.checkEntityUserId(this.ctx, this.getService(), id);
}
await this.service.trigger(id, stepId, true); await this.service.trigger(id, stepId, true);
return this.ok({}); return this.ok({});
} }
@Post('/cancel', { summary: Constants.per.authOnly }) @Post('/cancel', { summary: Constants.per.authOnly })
async cancel(@Query('historyId') historyId: number) { async cancel(@Query('historyId') historyId: number) {
const { projectId } = await this.getProjectUserIdWrite() await this.checkEntityOwner(this.historyService, historyId,"write");
if (projectId) {
await this.authService.checkEntityProjectId(this.historyService, projectId, historyId);
} else {
await this.authService.checkEntityUserId(this.ctx, this.historyService, historyId);
}
await this.service.cancel(historyId); await this.service.cancel(historyId);
return this.ok({}); return this.ok({});
} }
@@ -66,7 +66,8 @@ export class HistoryService extends BaseService<HistoryEntity> {
pipelineId: pipeline.id, pipelineId: pipeline.id,
title: pipeline.title, title: pipeline.title,
status: 'start', status: 'start',
triggerType triggerType,
projectId: pipeline.projectId,
}; };
const { id } = await this.add(bean); const { id } = await this.add(bean);
//清除大于pipeline.keepHistoryCount的历史记录 //清除大于pipeline.keepHistoryCount的历史记录
@@ -747,20 +747,35 @@ export class PipelineService extends BaseService<PipelineEntity> {
return; return;
} }
async getProjectId(pipelineId: number) {
const pipelineEntity = await this.repository.findOne({
select: {
projectId: true,
},
where: {
id: pipelineId,
},
});
return pipelineEntity.projectId;
}
private async saveHistory(history: RunHistory) { private async saveHistory(history: RunHistory) {
//修改pipeline状态 //修改pipeline状态
const pipelineEntity = new PipelineEntity(); let pipelineEntity = new PipelineEntity();
pipelineEntity.id = parseInt(history.pipeline.id); pipelineEntity.id = parseInt(history.pipeline.id);
pipelineEntity.status = history.pipeline.status.result + ""; pipelineEntity.status = history.pipeline.status.result + "";
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime; pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
await this.update(pipelineEntity); await this.update(pipelineEntity);
const projectId = await this.getProjectId(pipelineEntity.id);
pipelineEntity.projectId = projectId;
const entity: HistoryEntity = new HistoryEntity(); const entity: HistoryEntity = new HistoryEntity();
entity.id = parseInt(history.id); entity.id = parseInt(history.id);
entity.userId = history.pipeline.userId; entity.userId = history.pipeline.userId;
entity.status = pipelineEntity.status; entity.status = pipelineEntity.status;
entity.pipeline = JSON.stringify(history.pipeline); entity.pipeline = JSON.stringify(history.pipeline);
entity.pipelineId = parseInt(history.pipeline.id); entity.pipelineId = parseInt(history.pipeline.id);
entity.projectId = pipelineEntity.projectId;
await this.historyService.save(entity); await this.historyService.save(entity);
const logEntity: HistoryLogEntity = new HistoryLogEntity(); const logEntity: HistoryLogEntity = new HistoryLogEntity();
@@ -769,6 +784,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
logEntity.pipelineId = entity.pipelineId; logEntity.pipelineId = entity.pipelineId;
logEntity.historyId = entity.id; logEntity.historyId = entity.id;
logEntity.logs = JSON.stringify(history.logs); logEntity.logs = JSON.stringify(history.logs);
logEntity.projectId = pipelineEntity.projectId;
await this.historyLogService.addOrUpdate(logEntity); await this.historyLogService.addOrUpdate(logEntity);
} }
@@ -984,7 +1000,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
throw new NeedVIPException("此功能需要升级专业版"); throw new NeedVIPException("此功能需要升级专业版");
} }
if (!userId || ids.length === 0) { if (userId == null || ids.length === 0) {
return; return;
} }
const where:any = { const where:any = {
@@ -28,15 +28,15 @@ export class AuthService {
} }
//管理员有权限查看其他用户的数据 //管理员有权限查看其他用户的数据
async checkEntityUserId(ctx: any, service: any, id: any = 0, userKey = 'userId') { async checkEntityUserId(ctx: any, service: any, ids: number| number[] = null, userKey = 'userId') {
const isAdmin = await this.isAdmin(ctx); const isAdmin = await this.isAdmin(ctx);
if (isAdmin) { if (isAdmin) {
return true; return true;
} }
await service.checkUserId(id, ctx.user.id, userKey); await service.checkUserId(ids, ctx.user.id, userKey);
} }
async checkEntityProjectId(service:any,projectId = 0,id:any=0){ async checkEntityProjectId(service:any,ids:number| number[] = null,projectId = null){
await service.checkUserId(id, projectId , "projectId"); await service.checkUserId(ids, projectId , "projectId");
} }
} }
@@ -9,6 +9,12 @@ import { PermissionService } from './permission-service.js';
import * as _ from 'lodash-es'; import * as _ from 'lodash-es';
import { RolePermissionService } from './role-permission-service.js'; import { RolePermissionService } from './role-permission-service.js';
import { LRUCache } from 'lru-cache'; import { LRUCache } from 'lru-cache';
const permissionCache = new LRUCache<string, any>({
max: 1000,
ttl: 1000 * 60 * 10,
});
/** /**
* 角色 * 角色
*/ */
@@ -24,10 +30,7 @@ export class RoleService extends BaseService<RoleEntity> {
@Inject() @Inject()
rolePermissionService: RolePermissionService; rolePermissionService: RolePermissionService;
permissionCache = new LRUCache<string, any>({
max: 1000,
ttl: 1000 * 60 * 10,
});
//@ts-ignore //@ts-ignore
getRepository() { getRepository() {
@@ -84,7 +87,7 @@ export class RoleService extends BaseService<RoleEntity> {
//再添加 //再添加
await this.addRoles(userId, roles); await this.addRoles(userId, roles);
this.permissionCache.clear(); permissionCache.clear();
} }
async getPermissionTreeByRoleId(id: any) { async getPermissionTreeByRoleId(id: any) {
@@ -105,7 +108,7 @@ export class RoleService extends BaseService<RoleEntity> {
permissionId, permissionId,
}); });
} }
this.permissionCache.clear(); permissionCache.clear();
} }
async getPermissionSetByRoleIds(roleIds: number[]): Promise<Set<string>> { async getPermissionSetByRoleIds(roleIds: number[]): Promise<Set<string>> {
@@ -120,12 +123,12 @@ export class RoleService extends BaseService<RoleEntity> {
async getCachedPermissionSetByRoleIds(roleIds: number[]): Promise<Set<string>> { async getCachedPermissionSetByRoleIds(roleIds: number[]): Promise<Set<string>> {
const roleIdsKey = roleIds.join(','); const roleIdsKey = roleIds.join(',');
let permissionSet = this.permissionCache.get(roleIdsKey); let permissionSet = permissionCache.get(roleIdsKey);
if (permissionSet) { if (permissionSet) {
return permissionSet; return permissionSet;
} }
permissionSet = await this.getPermissionSetByRoleIds(roleIds); permissionSet = await this.getPermissionSetByRoleIds(roleIds);
this.permissionCache.set(roleIdsKey, permissionSet); permissionCache.set(roleIdsKey, permissionSet);
return permissionSet; return permissionSet;
} }
@@ -1,10 +1,16 @@
import { BaseService, SysSettingsService } from '@certd/lib-server'; import { BaseService, SysSettingsService } from '@certd/lib-server';
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core'; import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm'; import { InjectEntityModel } from '@midwayjs/typeorm';
import { LRUCache } from 'lru-cache';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { ProjectEntity } from '../entity/project.js'; import { ProjectEntity } from '../entity/project.js';
import { ProjectMemberService } from './project-member-service.js'; import { ProjectMemberService } from './project-member-service.js';
const projectCache = new LRUCache<string, any>({
max: 1000,
ttl: 1000 * 60 * 10,
});
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Request, { allowDowngrade: true })
export class ProjectService extends BaseService<ProjectEntity> { export class ProjectService extends BaseService<ProjectEntity> {
@@ -23,7 +29,7 @@ export class ProjectService extends BaseService<ProjectEntity> {
} }
async add(bean: ProjectEntity) { async add(bean: ProjectEntity) {
const {name} = bean; const { name } = bean;
if (!name) { if (!name) {
throw new Error('项目名称不能为空'); throw new Error('项目名称不能为空');
} }
@@ -33,17 +39,25 @@ export class ProjectService extends BaseService<ProjectEntity> {
userId: 0, userId: 0,
}, },
}); });
if (exist) { if (exist) {
throw new Error('项目名称已存在'); throw new Error('项目名称已存在');
} }
bean.disabled = false bean.disabled = false
return await super.add(bean) const res= await super.add(bean)
projectCache.clear();
return res;
}
async update( bean: ProjectEntity) {
const res= await super.update(bean)
projectCache.clear();
return res;
} }
async setDisabled(id: number, disabled: boolean) { async setDisabled(id: number, disabled: boolean) {
await this.repository.update({ await this.repository.update({
id, id,
userId:0, userId: 0,
}, { }, {
disabled, disabled,
}); });
@@ -55,7 +69,7 @@ export class ProjectService extends BaseService<ProjectEntity> {
const projectIds = memberList.map(item => item.projectId); const projectIds = memberList.map(item => item.projectId);
const projectList = await this.repository.createQueryBuilder('project') const projectList = await this.repository.createQueryBuilder('project')
.where(' project.disabled = false') .where(' project.disabled = false')
.where(' project.userId = :userId', { userId:0 }) .where(' project.userId = :userId', { userId: 0 })
.where(' project.id IN (:...projectIds) or project.adminId = :userId', { projectIds, userId }) .where(' project.id IN (:...projectIds) or project.adminId = :userId', { projectIds, userId })
.getMany(); .getMany();
@@ -67,7 +81,7 @@ export class ProjectService extends BaseService<ProjectEntity> {
projectList.forEach(item => { projectList.forEach(item => {
if (item.adminId === userId) { if (item.adminId === userId) {
item.permission = 'admin'; item.permission = 'admin';
}else{ } else {
item.permission = memberPermissionMap[item.id] || 'read'; item.permission = memberPermissionMap[item.id] || 'read';
} }
}) })
@@ -75,21 +89,21 @@ export class ProjectService extends BaseService<ProjectEntity> {
return projectList return projectList
} }
async checkAdminPermission({userId, projectId}: {userId: number, projectId: number}) { async checkAdminPermission({ userId, projectId }: { userId: number, projectId: number }) {
return await this.checkPermission({ return await this.checkPermission({
userId, userId,
projectId, projectId,
permission: 'admin', permission: 'admin',
}) })
} }
async checkWritePermission({userId, projectId}: {userId: number, projectId: number}) { async checkWritePermission({ userId, projectId }: { userId: number, projectId: number }) {
return await this.checkPermission({ return await this.checkPermission({
userId, userId,
projectId, projectId,
permission: 'write', permission: 'write',
}) })
} }
async checkReadPermission({userId, projectId}: {userId: number, projectId: number}) { async checkReadPermission({ userId, projectId }: { userId: number, projectId: number }) {
return await this.checkPermission({ return await this.checkPermission({
userId, userId,
projectId, projectId,
@@ -97,47 +111,60 @@ export class ProjectService extends BaseService<ProjectEntity> {
}) })
} }
async checkPermission({userId, projectId, permission}: {userId: number, projectId: number, permission: string}) { async checkPermission({ userId, projectId, permission }: { userId: number, projectId: number, permission: string }) {
if (permission !== 'admin' && permission !== 'write' && permission !== 'read') { if (permission !== 'admin' && permission !== 'write' && permission !== 'read') {
throw new Error('权限类型错误'); throw new Error('权限类型错误');
}
if (!userId ){
throw new Error('用户ID不能为空');
}
if (!projectId ){
throw new Error('项目ID不能为空');
}
const project = await this.findOne({
select: ['id', 'userId', 'adminId', 'disabled'],
where: {
id: projectId,
},
});
if (!project) {
throw new Error('项目不存在');
} }
if (project.adminId === userId) { if (!userId) {
//创建者拥有管理权限 throw new Error('用户ID不能为空');
return true
} }
if (project.disabled) { if (!projectId) {
throw new Error('项目已禁用'); throw new Error('项目ID不能为空');
} }
const member = await this.projectMemberService.getMember(projectId,userId);
if (!member) { const cacheKey = `projectPermission:${projectId}:${userId}`
throw new Error('项目成员不存在'); let savedPermission = projectCache.get(cacheKey);
if (!savedPermission){
const project = await this.findOne({
select: ['id', 'userId', 'adminId', 'disabled'],
where: {
id: projectId,
},
});
if (!project) {
throw new Error('项目不存在');
}
if (project.adminId === userId) {
//创建者拥有管理权限
savedPermission = 'admin';
}else{
if (project.disabled) {
throw new Error('项目已禁用');
}
const member = await this.projectMemberService.getMember(projectId, userId);
if (!member) {
throw new Error('项目成员不存在');
}
savedPermission = member.permission;
}
} }
projectCache.set(cacheKey, savedPermission,{ttl: 3 * 60 * 1000});
if (!savedPermission) {
throw new Error('权限不足');
}
if (permission === 'read') { if (permission === 'read') {
return true return true
} }
if (permission === 'write') { if (permission === 'write') {
if (member.permission === 'admin' || member.permission === 'write') { if (savedPermission === 'admin' || savedPermission === 'write') {
return true return true
}else{ } else {
throw new Error('权限不足'); throw new Error('权限不足');
} }
} }
if (member.permission !== permission) { if (savedPermission !== permission) {
throw new Error('权限不足'); throw new Error('权限不足');
} }
return true return true