mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-24 17:51:51 +02:00
[web] Add deletedReason parameter to project deletion methods (#32221)
* [web] Add deletedReason parameter to project deletion methods * revert sinon.match.any in ProjectDuplicator negative assertion Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> GitOrigin-RevId: d1595eefe0e36150231ee9646fe5eba0786fd1f5
This commit is contained in:
committed by
Copybot
parent
c9ba2ac025
commit
6486ef3e1e
@@ -5,6 +5,7 @@ import ProjectEntityUpdateHandler from '../Project/ProjectEntityUpdateHandler.mj
|
||||
import ProjectOptionsHandler from '../Project/ProjectOptionsHandler.mjs'
|
||||
import ProjectDetailsHandler from '../Project/ProjectDetailsHandler.mjs'
|
||||
import ProjectDeleter from '../Project/ProjectDeleter.mjs'
|
||||
import { DeletedProjectReasons } from '../Project/DeletedProjectReasons.mjs'
|
||||
import EditorRealTimeController from './EditorRealTimeController.mjs'
|
||||
import async from 'async'
|
||||
import PublicAccessLevels from '../Authorization/PublicAccessLevels.mjs'
|
||||
@@ -445,7 +446,11 @@ const EditorController = {
|
||||
|
||||
deleteProject(projectId, callback) {
|
||||
Metrics.inc('editor.delete-project')
|
||||
ProjectDeleter.deleteProject(projectId, callback)
|
||||
ProjectDeleter.deleteProject(
|
||||
projectId,
|
||||
{ deletedReason: DeletedProjectReasons.USER },
|
||||
callback
|
||||
)
|
||||
},
|
||||
|
||||
renameEntity(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export const DeletedProjectReasons = /** @type {const} */ ({
|
||||
USER: 'user',
|
||||
ACCOUNT_DELETION: 'account-deletion',
|
||||
ZIP_IMPORT_FAILURE: 'zip-import-failure',
|
||||
CLONE_FAILURE: 'clone-failure',
|
||||
GITHUB_IMPORT_FAILURE: 'github-import-failure',
|
||||
SCRIPT: 'script',
|
||||
})
|
||||
@@ -7,6 +7,7 @@ import logger from '@overleaf/logger'
|
||||
import { expressify } from '@overleaf/promise-utils'
|
||||
import mongodb from 'mongodb-legacy'
|
||||
import ProjectDeleter from './ProjectDeleter.mjs'
|
||||
import { DeletedProjectReasons } from './DeletedProjectReasons.mjs'
|
||||
import ProjectDuplicator from './ProjectDuplicator.mjs'
|
||||
import ProjectCreationHandler from './ProjectCreationHandler.mjs'
|
||||
import EditorController from '../Editor/EditorController.mjs'
|
||||
@@ -171,6 +172,7 @@ const _ProjectController = {
|
||||
await ProjectDeleter.promises.deleteProject(projectId, {
|
||||
deleterUser: user,
|
||||
ipAddress: req.ip,
|
||||
deletedReason: DeletedProjectReasons.USER,
|
||||
})
|
||||
ProjectAuditLogHandler.addEntryIfManagedInBackground(
|
||||
projectId,
|
||||
|
||||
@@ -22,6 +22,7 @@ import EditorRealTimeController from '../Editor/EditorRealTimeController.mjs'
|
||||
import HistoryManager from '../History/HistoryManager.mjs'
|
||||
import ChatApiHandler from '../Chat/ChatApiHandler.mjs'
|
||||
import { promiseMapWithLimit } from '@overleaf/promise-utils'
|
||||
import { DeletedProjectReasons } from './DeletedProjectReasons.mjs'
|
||||
|
||||
const PROJECT_EXPIRATION_BATCH_SIZE = 10000
|
||||
|
||||
@@ -84,7 +85,11 @@ async function deleteUsersProjects(userId) {
|
||||
{ userId, projectCount: projects.length },
|
||||
'found user projects to delete'
|
||||
)
|
||||
await promiseMapWithLimit(5, projects, project => deleteProject(project._id))
|
||||
await promiseMapWithLimit(5, projects, project =>
|
||||
deleteProject(project._id, {
|
||||
deletedReason: DeletedProjectReasons.ACCOUNT_DELETION,
|
||||
})
|
||||
)
|
||||
logger.info({ userId }, 'deleted all user projects')
|
||||
await CollaboratorsHandler.promises.removeUserFromAllProjects(userId)
|
||||
}
|
||||
@@ -214,6 +219,7 @@ async function deleteProject(projectId, options = {}) {
|
||||
deleterId:
|
||||
options.deleterUser != null ? options.deleterUser._id : undefined,
|
||||
deleterIpAddress: options.ipAddress,
|
||||
deletedReason: options.deletedReason,
|
||||
deletedProjectId: project._id,
|
||||
deletedProjectOwnerId: project.owner_ref,
|
||||
deletedProjectCollaboratorIds: project.collaberator_refs,
|
||||
|
||||
@@ -10,6 +10,7 @@ import DocumentUpdaterHandler from '../DocumentUpdater/DocumentUpdaterHandler.mj
|
||||
import HistoryManager from '../History/HistoryManager.mjs'
|
||||
import ProjectCreationHandler from './ProjectCreationHandler.mjs'
|
||||
import ProjectDeleter from './ProjectDeleter.mjs'
|
||||
import { DeletedProjectReasons } from './DeletedProjectReasons.mjs'
|
||||
import ProjectEntityMongoUpdateHandler from './ProjectEntityMongoUpdateHandler.mjs'
|
||||
import ProjectEntityUpdateHandler from './ProjectEntityUpdateHandler.mjs'
|
||||
import ProjectGetter from './ProjectGetter.mjs'
|
||||
@@ -161,7 +162,9 @@ async function duplicate(
|
||||
} catch (err) {
|
||||
// Clean up broken clone on error.
|
||||
// Make sure we delete the new failed project, not the original one!
|
||||
await ProjectDeleter.promises.deleteProject(newProject._id)
|
||||
await ProjectDeleter.promises.deleteProject(newProject._id, {
|
||||
deletedReason: DeletedProjectReasons.CLONE_FAILURE,
|
||||
})
|
||||
throw OError.tag(err, 'error cloning project, broken clone deleted', {
|
||||
originalProjectId,
|
||||
newProjectName,
|
||||
|
||||
@@ -13,6 +13,7 @@ import ProjectEntityMongoUpdateHandler from '../Project/ProjectEntityMongoUpdate
|
||||
import ProjectRootDocManager from '../Project/ProjectRootDocManager.mjs'
|
||||
import ProjectDetailsHandler from '../Project/ProjectDetailsHandler.mjs'
|
||||
import ProjectDeleter from '../Project/ProjectDeleter.mjs'
|
||||
import { DeletedProjectReasons } from '../Project/DeletedProjectReasons.mjs'
|
||||
import TpdsProjectFlusher from '../ThirdPartyDataStore/TpdsProjectFlusher.mjs'
|
||||
import logger from '@overleaf/logger'
|
||||
import OError from '@overleaf/o-error'
|
||||
@@ -55,7 +56,9 @@ async function createProjectFromZipArchive(ownerId, defaultName, zipPath) {
|
||||
} catch (err) {
|
||||
// no need to wait for the cleanup here
|
||||
ProjectDeleter.promises
|
||||
.deleteProject(project._id)
|
||||
.deleteProject(project._id, {
|
||||
deletedReason: DeletedProjectReasons.ZIP_IMPORT_FAILURE,
|
||||
})
|
||||
.catch(err =>
|
||||
logger.error(
|
||||
{ err, projectId: project._id },
|
||||
@@ -97,7 +100,9 @@ async function createProjectFromZipArchiveWithName(
|
||||
} catch (err) {
|
||||
// no need to wait for the cleanup here
|
||||
ProjectDeleter.promises
|
||||
.deleteProject(project._id)
|
||||
.deleteProject(project._id, {
|
||||
deletedReason: DeletedProjectReasons.ZIP_IMPORT_FAILURE,
|
||||
})
|
||||
.catch(err =>
|
||||
logger.error(
|
||||
{ err, projectId: project._id },
|
||||
|
||||
@@ -20,6 +20,7 @@ export const DeleterDataSchema = new Schema({
|
||||
deletedProjectLastUpdatedAt: { type: Date },
|
||||
deletedProjectOverleafId: { type: Number },
|
||||
deletedProjectOverleafHistoryId: { type: Schema.Types.Mixed },
|
||||
deletedReason: { type: String },
|
||||
})
|
||||
|
||||
const DeletedProjectSchema = new Schema(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import minimist from 'minimist'
|
||||
import ProjectDeleter from '../app/src/Features/Project/ProjectDeleter.mjs'
|
||||
import { DeletedProjectReasons } from '../app/src/Features/Project/DeletedProjectReasons.mjs'
|
||||
import { scriptRunner } from './lib/ScriptRunner.mjs'
|
||||
|
||||
async function main() {
|
||||
@@ -11,7 +12,9 @@ async function main() {
|
||||
}
|
||||
console.log(`Soft deleting project ${projectId}`)
|
||||
// soft delete, project will be permanently deleted after 90 days
|
||||
await ProjectDeleter.promises.deleteProject(projectId)
|
||||
await ProjectDeleter.promises.deleteProject(projectId, {
|
||||
deletedReason: DeletedProjectReasons.SCRIPT,
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -684,7 +684,7 @@ describe('EditorController', function () {
|
||||
describe('deleteProject', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.err = 'errro'
|
||||
ctx.ProjectDeleter.deleteProject.callsArgWith(1, ctx.err)
|
||||
ctx.ProjectDeleter.deleteProject.callsArgWith(2, ctx.err)
|
||||
})
|
||||
|
||||
it('should call the project handler', async function (ctx) {
|
||||
@@ -692,7 +692,9 @@ describe('EditorController', function () {
|
||||
ctx.EditorController.deleteProject(ctx.project_id, err => {
|
||||
err.should.equal(ctx.err)
|
||||
ctx.ProjectDeleter.deleteProject
|
||||
.calledWith(ctx.project_id)
|
||||
.calledWith(ctx.project_id, {
|
||||
deletedReason: 'user',
|
||||
})
|
||||
.should.equal(true)
|
||||
resolve()
|
||||
})
|
||||
|
||||
@@ -710,6 +710,7 @@ describe('ProjectController', function () {
|
||||
.calledWith(ctx.project_id, {
|
||||
deleterUser: ctx.user,
|
||||
ipAddress: ctx.req.ip,
|
||||
deletedReason: 'user',
|
||||
})
|
||||
.should.equal(true)
|
||||
code.should.equal(200)
|
||||
|
||||
@@ -288,7 +288,7 @@ describe('ProjectDeleter', function () {
|
||||
{ 'deleterData.deletedProjectId': project._id },
|
||||
{
|
||||
project,
|
||||
deleterData: sinon.match.object,
|
||||
deleterData: sinon.match.has('deletedReason', 'account-deletion'),
|
||||
},
|
||||
{ upsert: true }
|
||||
)
|
||||
@@ -343,6 +343,7 @@ describe('ProjectDeleter', function () {
|
||||
it('should save a DeletedProject with additional deleterData', async function (ctx) {
|
||||
ctx.deleterData.deleterIpAddress = ctx.ip
|
||||
ctx.deleterData.deleterId = ctx.user._id
|
||||
ctx.deleterData.deletedReason = 'user'
|
||||
|
||||
ctx.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
||||
ctx.DeletedProjectMock.expects('updateOne')
|
||||
@@ -359,6 +360,7 @@ describe('ProjectDeleter', function () {
|
||||
await ctx.ProjectDeleter.promises.deleteProject(ctx.project._id, {
|
||||
deleterUser: ctx.user,
|
||||
ipAddress: ctx.ip,
|
||||
deletedReason: 'user',
|
||||
})
|
||||
ctx.DeletedProjectMock.verify()
|
||||
})
|
||||
|
||||
@@ -459,7 +459,8 @@ describe('ProjectDuplicator', function () {
|
||||
|
||||
it('should delete the broken cloned project', function (ctx) {
|
||||
ctx.ProjectDeleter.promises.deleteProject.should.have.been.calledWith(
|
||||
ctx.newBlankProject._id
|
||||
ctx.newBlankProject._id,
|
||||
{ deletedReason: 'clone-failure' }
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -445,7 +445,8 @@ describe('ProjectUploadManager', function () {
|
||||
|
||||
it('should cleanup the blank project created', async function (ctx) {
|
||||
ctx.ProjectDeleter.promises.deleteProject.should.have.been.calledWith(
|
||||
ctx.project._id
|
||||
ctx.project._id,
|
||||
{ deletedReason: 'zip-import-failure' }
|
||||
)
|
||||
})
|
||||
|
||||
@@ -471,7 +472,8 @@ describe('ProjectUploadManager', function () {
|
||||
|
||||
it('should cleanup the blank project created', function (ctx) {
|
||||
ctx.ProjectDeleter.promises.deleteProject.should.have.been.calledWith(
|
||||
ctx.project._id
|
||||
ctx.project._id,
|
||||
{ deletedReason: 'zip-import-failure' }
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user