mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-28 11:31:55 +02:00
219 lines
6.1 KiB
JavaScript
219 lines
6.1 KiB
JavaScript
const { callbackify } = require('util')
|
|
const UpdateMerger = require('./UpdateMerger')
|
|
const logger = require('@overleaf/logger')
|
|
const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
|
|
const ProjectCreationHandler = require('../Project/ProjectCreationHandler')
|
|
const ProjectDeleter = require('../Project/ProjectDeleter')
|
|
const ProjectGetter = require('../Project/ProjectGetter')
|
|
const ProjectHelper = require('../Project/ProjectHelper')
|
|
const ProjectRootDocManager = require('../Project/ProjectRootDocManager')
|
|
const FileTypeManager = require('../Uploads/FileTypeManager')
|
|
const CooldownManager = require('../Cooldown/CooldownManager')
|
|
const Errors = require('../Errors/Errors')
|
|
const Modules = require('../../infrastructure/Modules')
|
|
const {
|
|
BackgroundTaskTracker,
|
|
} = require('../../infrastructure/GracefulShutdown')
|
|
|
|
const ROOT_DOC_TIMEOUT_LENGTH = 30 * 1000
|
|
|
|
const rootDocResets = new BackgroundTaskTracker('root doc resets')
|
|
|
|
async function newUpdate(
|
|
userId,
|
|
projectId,
|
|
projectName,
|
|
path,
|
|
updateRequest,
|
|
source
|
|
) {
|
|
const project = await getOrCreateProject(userId, projectId, projectName)
|
|
if (project == null) {
|
|
return null
|
|
}
|
|
|
|
const projectIsOnCooldown =
|
|
await CooldownManager.promises.isProjectOnCooldown(project._id)
|
|
if (projectIsOnCooldown) {
|
|
throw new Errors.TooManyRequestsError('project on cooldown')
|
|
}
|
|
|
|
const shouldIgnore = await FileTypeManager.promises.shouldIgnore(path)
|
|
if (shouldIgnore) {
|
|
return null
|
|
}
|
|
|
|
const metadata = await UpdateMerger.promises.mergeUpdate(
|
|
userId,
|
|
project._id,
|
|
path,
|
|
updateRequest,
|
|
source
|
|
)
|
|
return metadata
|
|
}
|
|
|
|
async function deleteUpdate(userId, projectId, projectName, path, source) {
|
|
logger.debug({ userId, filePath: path }, 'handling delete update from tpds')
|
|
let projects = []
|
|
if (projectId) {
|
|
const project = await findProjectByIdWithRWAccess(userId, projectId)
|
|
if (project) {
|
|
projects = [project]
|
|
}
|
|
} else {
|
|
projects = await ProjectGetter.promises.findUsersProjectsByName(
|
|
userId,
|
|
projectName
|
|
)
|
|
}
|
|
const activeProjects = projects.filter(
|
|
project => !ProjectHelper.isArchivedOrTrashed(project, userId)
|
|
)
|
|
|
|
if (activeProjects.length === 0) {
|
|
logger.debug(
|
|
{ userId, filePath: path, projectName },
|
|
'project not found from tpds update, ignoring folder or project'
|
|
)
|
|
return
|
|
}
|
|
|
|
if (projects.length > 1) {
|
|
// There is more than one project with that name, and one of them is
|
|
// active (previous condition)
|
|
await handleDuplicateProjects(userId, projectName)
|
|
return
|
|
}
|
|
|
|
const project = activeProjects[0]
|
|
if (path === '/') {
|
|
logger.debug(
|
|
{ userId, filePath: path, projectName, projectId: project._id },
|
|
'project found for delete update, path is root so marking project as deleted'
|
|
)
|
|
await ProjectDeleter.promises.markAsDeletedByExternalSource(project._id)
|
|
} else {
|
|
await UpdateMerger.promises.deleteUpdate(userId, project._id, path, source)
|
|
}
|
|
}
|
|
|
|
async function getOrCreateProject(userId, projectId, projectName) {
|
|
if (projectId) {
|
|
return findProjectByIdWithRWAccess(userId, projectId)
|
|
} else {
|
|
return getOrCreateProjectByName(userId, projectName)
|
|
}
|
|
}
|
|
|
|
async function findProjectByIdWithRWAccess(userId, projectId) {
|
|
const allProjects = await ProjectGetter.promises.findAllUsersProjects(
|
|
userId,
|
|
'name archived trashed'
|
|
)
|
|
for (const projects of [allProjects.owned, allProjects.readAndWrite]) {
|
|
for (const project of projects) {
|
|
if (project._id.toString() === projectId) {
|
|
return project
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async function getOrCreateProjectByName(userId, projectName) {
|
|
const projects = await ProjectGetter.promises.findUsersProjectsByName(
|
|
userId,
|
|
projectName
|
|
)
|
|
|
|
if (projects.length === 0) {
|
|
// No project with that name -- active, archived or trashed -- has been
|
|
// found. Create one.
|
|
const project = await ProjectCreationHandler.promises.createBlankProject(
|
|
userId,
|
|
projectName
|
|
)
|
|
|
|
// have a crack at setting the root doc after a while, on creation
|
|
// we won't have it yet, but should have been sent it it within 30
|
|
// seconds
|
|
rootDocResets.add()
|
|
setTimeout(() => {
|
|
ProjectRootDocManager.promises
|
|
.setRootDocAutomatically(project._id)
|
|
.then(() => {
|
|
rootDocResets.done()
|
|
})
|
|
.catch(err => {
|
|
logger.warn({ err }, 'failed to set root doc after project creation')
|
|
})
|
|
}, ROOT_DOC_TIMEOUT_LENGTH)
|
|
return project
|
|
}
|
|
|
|
const activeProjects = projects.filter(
|
|
project => !ProjectHelper.isArchivedOrTrashed(project, userId)
|
|
)
|
|
if (activeProjects.length === 0) {
|
|
// All projects with that name are archived or trashed. Ignore.
|
|
return null
|
|
}
|
|
|
|
if (projects.length > 1) {
|
|
// There is more than one project with that name, and one of them is
|
|
// active (previous condition)
|
|
await handleDuplicateProjects(userId, projectName)
|
|
return null
|
|
}
|
|
|
|
return activeProjects[0]
|
|
}
|
|
|
|
async function handleDuplicateProjects(userId, projectName) {
|
|
await Modules.promises.hooks.fire(
|
|
'removeDropbox',
|
|
userId,
|
|
'duplicate-projects'
|
|
)
|
|
await NotificationsBuilder.promises
|
|
.dropboxDuplicateProjectNames(userId)
|
|
.create(projectName)
|
|
}
|
|
|
|
async function createFolder(userId, projectId, projectName, path) {
|
|
const project = await getOrCreateProject(userId, projectId, projectName)
|
|
if (project == null) {
|
|
return null
|
|
}
|
|
|
|
const projectIsOnCooldown =
|
|
await CooldownManager.promises.isProjectOnCooldown(project._id)
|
|
if (projectIsOnCooldown) {
|
|
throw new Errors.TooManyRequestsError('project on cooldown')
|
|
}
|
|
|
|
const shouldIgnore = await FileTypeManager.promises.shouldIgnore(path)
|
|
if (shouldIgnore) {
|
|
return null
|
|
}
|
|
|
|
const folder = await UpdateMerger.promises.createFolder(project._id, path)
|
|
return {
|
|
folderId: folder._id,
|
|
parentFolderId: folder.parentFolder_id,
|
|
projectId: project._id,
|
|
path,
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
newUpdate: callbackify(newUpdate),
|
|
deleteUpdate: callbackify(deleteUpdate),
|
|
createFolder: callbackify(createFolder),
|
|
promises: {
|
|
newUpdate,
|
|
deleteUpdate,
|
|
createFolder,
|
|
},
|
|
}
|