feat(core): Change workflow deletions to soft deletes (#14894)

Adds soft‑deletion support for workflows through a new boolean column `isArchived`.

When a workflow is archived we now set `isArchived` flag to true and the workflows
stays in the database and is omitted from the default workflow listing query.

Archived workflows can be viewed in read-only mode, but they cannot be activated.

Archived workflows are still available by ID and can be invoked as sub-executions,
so existing Execute Workflow nodes continue to work. Execution engine doesn't
care about isArchived flag.

Users can restore workflows via Unarchive action at the UI.
This commit is contained in:
Jaakko Husso
2025-05-06 17:48:24 +03:00
committed by GitHub
parent 32b72011e6
commit 3a13139f78
64 changed files with 1616 additions and 124 deletions

View File

@@ -382,23 +382,63 @@ export class WorkflowsController {
@Delete('/:workflowId')
@ProjectScope('workflow:delete')
async delete(req: WorkflowRequest.Delete) {
const { workflowId } = req.params;
async delete(req: AuthenticatedRequest, _res: Response, @Param('workflowId') workflowId: string) {
const workflow = await this.workflowService.delete(req.user, workflowId);
if (!workflow) {
this.logger.warn('User attempted to delete a workflow without permissions', {
workflowId,
userId: req.user.id,
});
throw new BadRequestError(
'Could not delete the workflow - you can only remove workflows owned by you',
throw new ForbiddenError(
'Could not delete the workflow - workflow was not found in your projects',
);
}
return true;
}
@Post('/:workflowId/archive')
@ProjectScope('workflow:delete')
async archive(
req: AuthenticatedRequest,
_res: Response,
@Param('workflowId') workflowId: string,
) {
const workflow = await this.workflowService.archive(req.user, workflowId);
if (!workflow) {
this.logger.warn('User attempted to archive a workflow without permissions', {
workflowId,
userId: req.user.id,
});
throw new ForbiddenError(
'Could not archive the workflow - workflow was not found in your projects',
);
}
return workflow;
}
@Post('/:workflowId/unarchive')
@ProjectScope('workflow:delete')
async unarchive(
req: AuthenticatedRequest,
_res: Response,
@Param('workflowId') workflowId: string,
) {
const workflow = await this.workflowService.unarchive(req.user, workflowId);
if (!workflow) {
this.logger.warn('User attempted to unarchive a workflow without permissions', {
workflowId,
userId: req.user.id,
});
throw new ForbiddenError(
'Could not unarchive the workflow - workflow was not found in your projects',
);
}
return workflow;
}
@Post('/:workflowId/run')
@ProjectScope('workflow:execute')
async runManually(