mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-11 07:00:47 +02:00
[filestore] remove user files endpoints (#28125)
* [filestore] remove user files endpoints * [web] remove user files integration for filestore GitOrigin-RevId: 565fa68a659c07420ee6141d0f276b4e4d2972e0
This commit is contained in:
@@ -15,7 +15,6 @@ const { Cookie } = require('tough-cookie')
|
||||
const ClsiCookieManager = require('./ClsiCookieManager')(
|
||||
Settings.apis.clsi?.backendGroupName
|
||||
)
|
||||
const Features = require('../../infrastructure/Features')
|
||||
const NewBackendCloudClsiCookieManager = require('./ClsiCookieManager')(
|
||||
Settings.apis.clsi_new?.backendGroupName
|
||||
)
|
||||
@@ -750,18 +749,9 @@ function _finaliseRequest(projectId, options, project, docs, files) {
|
||||
for (let path in files) {
|
||||
const file = files[path]
|
||||
path = path.replace(/^\//, '') // Remove leading /
|
||||
|
||||
const filestoreURL = `${Settings.apis.filestore.url}/project/${project._id}/file/${file._id}`
|
||||
let url = filestoreURL
|
||||
let fallbackURL
|
||||
if (file.hash && Features.hasFeature('project-history-blobs')) {
|
||||
url = getFilestoreBlobURL(historyId, file.hash)
|
||||
fallbackURL = filestoreURL
|
||||
}
|
||||
resources.push({
|
||||
path,
|
||||
url,
|
||||
fallbackURL,
|
||||
url: getFilestoreBlobURL(historyId, file.hash),
|
||||
modified: file.created?.getTime(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ const metrics = require('@overleaf/metrics')
|
||||
const { promisify, callbackify } = require('util')
|
||||
const { promisifyMultiResult } = require('@overleaf/promise-utils')
|
||||
const ProjectGetter = require('../Project/ProjectGetter')
|
||||
const FileStoreHandler = require('../FileStore/FileStoreHandler')
|
||||
const Features = require('../../infrastructure/Features')
|
||||
const Modules = require('../../infrastructure/Modules')
|
||||
|
||||
function getProjectLastUpdatedAt(projectId, callback) {
|
||||
@@ -361,25 +359,19 @@ function resyncProjectHistory(
|
||||
doc: doc.doc._id,
|
||||
path: doc.path,
|
||||
}))
|
||||
const hasFilestore = Features.hasFeature('filestore')
|
||||
if (!hasFilestore) {
|
||||
// Files without a hash likely do not have a blob. Abort.
|
||||
for (const { file } of files) {
|
||||
if (!file.hash) {
|
||||
return callback(
|
||||
new OError('found file with missing hash', { projectId, file })
|
||||
)
|
||||
}
|
||||
// Files without a hash likely do not have a blob. Abort.
|
||||
for (const { file } of files) {
|
||||
if (!file.hash) {
|
||||
return callback(
|
||||
new OError('found file with missing hash', { projectId, file })
|
||||
)
|
||||
}
|
||||
}
|
||||
files = files.map(file => ({
|
||||
file: file.file._id,
|
||||
path: file.path,
|
||||
url: hasFilestore
|
||||
? FileStoreHandler._buildUrl(projectId, file.file._id)
|
||||
: undefined,
|
||||
_hash: file.file.hash,
|
||||
createdBlob: !hasFilestore,
|
||||
createdBlob: true,
|
||||
metadata: buildFileMetadataForHistory(file.file),
|
||||
}))
|
||||
|
||||
@@ -480,15 +472,12 @@ function updateProjectStructure(
|
||||
changes.newDocs,
|
||||
historyRangesSupport
|
||||
)
|
||||
const hasFilestore = Features.hasFeature('filestore')
|
||||
if (!hasFilestore) {
|
||||
for (const newEntity of changes.newFiles || []) {
|
||||
if (!newEntity.file.hash) {
|
||||
// Files without a hash likely do not have a blob. Abort.
|
||||
return callback(
|
||||
new OError('found file with missing hash', { newEntity })
|
||||
)
|
||||
}
|
||||
for (const newEntity of changes.newFiles || []) {
|
||||
if (!newEntity.file.hash) {
|
||||
// Files without a hash likely do not have a blob. Abort.
|
||||
return callback(
|
||||
new OError('found file with missing hash', { newEntity })
|
||||
)
|
||||
}
|
||||
}
|
||||
const {
|
||||
@@ -623,8 +612,6 @@ function _getUpdates(
|
||||
})
|
||||
}
|
||||
}
|
||||
const hasFilestore = Features.hasFeature('filestore')
|
||||
|
||||
for (const id in newEntitiesHash) {
|
||||
const newEntity = newEntitiesHash[id]
|
||||
const oldEntity = oldEntitiesHash[id]
|
||||
@@ -638,10 +625,9 @@ function _getUpdates(
|
||||
docLines: newEntity.docLines,
|
||||
ranges: newEntity.ranges,
|
||||
historyRangesSupport,
|
||||
url: newEntity.file != null && hasFilestore ? newEntity.url : undefined,
|
||||
hash: newEntity.file != null ? newEntity.file.hash : undefined,
|
||||
hash: newEntity.file?.hash,
|
||||
metadata: buildFileMetadataForHistory(newEntity.file),
|
||||
createdBlob: (newEntity.createdBlob || !hasFilestore) ?? false,
|
||||
createdBlob: true,
|
||||
})
|
||||
} else if (newEntity.path !== oldEntity.path) {
|
||||
// entity renamed
|
||||
|
||||
@@ -4,8 +4,6 @@ import logger from '@overleaf/logger'
|
||||
import ProjectEntityHandler from '../Project/ProjectEntityHandler.js'
|
||||
import ProjectGetter from '../Project/ProjectGetter.js'
|
||||
import HistoryManager from '../History/HistoryManager.js'
|
||||
import FileStoreHandler from '../FileStore/FileStoreHandler.js'
|
||||
import Features from '../../infrastructure/Features.js'
|
||||
let ProjectZipStreamManager
|
||||
|
||||
export default ProjectZipStreamManager = {
|
||||
@@ -111,22 +109,17 @@ export default ProjectZipStreamManager = {
|
||||
},
|
||||
|
||||
getFileStream: (projectId, file, callback) => {
|
||||
if (Features.hasFeature('project-history-blobs')) {
|
||||
HistoryManager.requestBlobWithFallback(
|
||||
projectId,
|
||||
file.hash,
|
||||
file._id,
|
||||
(error, result) => {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
const { stream } = result
|
||||
callback(null, stream)
|
||||
HistoryManager.requestBlobWithProjectId(
|
||||
projectId,
|
||||
file.hash,
|
||||
(error, result) => {
|
||||
if (error) {
|
||||
return callback(error)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
FileStoreHandler.getFileStream(projectId, file._id, {}, callback)
|
||||
}
|
||||
const { stream } = result
|
||||
callback(null, stream)
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
addAllFilesToArchive(projectId, archive, callback) {
|
||||
|
||||
@@ -4,11 +4,9 @@ import { pipeline } from 'node:stream/promises'
|
||||
import logger from '@overleaf/logger'
|
||||
import { expressify } from '@overleaf/promise-utils'
|
||||
import Metrics from '@overleaf/metrics'
|
||||
import FileStoreHandler from './FileStoreHandler.js'
|
||||
import ProjectLocator from '../Project/ProjectLocator.js'
|
||||
import HistoryManager from '../History/HistoryManager.js'
|
||||
import Errors from '../Errors/Errors.js'
|
||||
import Features from '../../infrastructure/Features.js'
|
||||
import { preparePlainTextResponse } from '../../infrastructure/Response.js'
|
||||
|
||||
async function getFile(req, res) {
|
||||
@@ -55,25 +53,15 @@ async function getFile(req, res) {
|
||||
status: Boolean(file?.hash),
|
||||
})
|
||||
|
||||
let source, stream, contentLength
|
||||
let stream, contentLength
|
||||
try {
|
||||
if (Features.hasFeature('project-history-blobs') && file?.hash) {
|
||||
// Get the file from history
|
||||
;({ source, stream, contentLength } =
|
||||
await HistoryManager.promises.requestBlobWithFallback(
|
||||
projectId,
|
||||
file.hash,
|
||||
fileId
|
||||
))
|
||||
} else {
|
||||
// The file-hash is missing. Fall back to filestore.
|
||||
stream = await FileStoreHandler.promises.getFileStream(
|
||||
// Get the file from history
|
||||
;({ stream, contentLength } =
|
||||
await HistoryManager.promises.requestBlobWithProjectId(
|
||||
projectId,
|
||||
fileId,
|
||||
queryString
|
||||
)
|
||||
source = 'filestore'
|
||||
}
|
||||
file.hash,
|
||||
'GET'
|
||||
))
|
||||
} catch (err) {
|
||||
if (err instanceof Errors.NotFoundError) {
|
||||
return res.status(404).end()
|
||||
@@ -97,7 +85,6 @@ async function getFile(req, res) {
|
||||
// allow the browser to cache these immutable files
|
||||
// note: both "private" and "max-age" appear to be required for caching
|
||||
res.setHeader('Cache-Control', 'private, max-age=3600')
|
||||
res.appendHeader('X-Served-By', source)
|
||||
try {
|
||||
await pipeline(stream, res)
|
||||
} catch (err) {
|
||||
@@ -150,20 +137,14 @@ async function getFileHead(req, res) {
|
||||
status: Boolean(file?.hash),
|
||||
})
|
||||
|
||||
let fileSize, source
|
||||
let fileSize
|
||||
try {
|
||||
if (Features.hasFeature('project-history-blobs') && file?.hash) {
|
||||
;({ source, contentLength: fileSize } =
|
||||
await HistoryManager.promises.requestBlobWithFallback(
|
||||
projectId,
|
||||
file.hash,
|
||||
fileId,
|
||||
'HEAD'
|
||||
))
|
||||
} else {
|
||||
fileSize = await FileStoreHandler.promises.getFileSize(projectId, fileId)
|
||||
source = 'filestore'
|
||||
}
|
||||
;({ contentLength: fileSize } =
|
||||
await HistoryManager.promises.requestBlobWithProjectId(
|
||||
projectId,
|
||||
file.hash,
|
||||
'HEAD'
|
||||
))
|
||||
} catch (err) {
|
||||
if (err instanceof Errors.NotFoundError) {
|
||||
return res.status(404).end()
|
||||
@@ -174,7 +155,6 @@ async function getFileHead(req, res) {
|
||||
}
|
||||
|
||||
res.setHeader('Content-Length', fileSize)
|
||||
res.appendHeader('X-Served-By', source)
|
||||
res.status(200).end()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
const _ = require('lodash')
|
||||
const logger = require('@overleaf/logger')
|
||||
const fs = require('fs')
|
||||
const request = require('request')
|
||||
const settings = require('@overleaf/settings')
|
||||
const Async = require('async')
|
||||
const FileHashManager = require('./FileHashManager')
|
||||
const HistoryManager = require('../History/HistoryManager')
|
||||
const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler')
|
||||
const { File } = require('../../models/File')
|
||||
const Errors = require('../Errors/Errors')
|
||||
const OError = require('@overleaf/o-error')
|
||||
const { promisifyAll } = require('@overleaf/promise-utils')
|
||||
const Features = require('../../infrastructure/Features')
|
||||
|
||||
const ONE_MIN_IN_MS = 60 * 1000
|
||||
const FIVE_MINS_IN_MS = ONE_MIN_IN_MS * 5
|
||||
|
||||
const FileStoreHandler = {
|
||||
RETRY_ATTEMPTS: 3,
|
||||
@@ -40,27 +32,14 @@ const FileStoreHandler = {
|
||||
},
|
||||
|
||||
_uploadToHistory(historyId, hash, size, fsPath, callback) {
|
||||
if (Features.hasFeature('project-history-blobs')) {
|
||||
Async.retry(
|
||||
FileStoreHandler.RETRY_ATTEMPTS,
|
||||
cb =>
|
||||
HistoryManager.uploadBlobFromDisk(historyId, hash, size, fsPath, cb),
|
||||
error => {
|
||||
if (error) return callback(error, false)
|
||||
callback(null, true)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
callback(null, false)
|
||||
}
|
||||
},
|
||||
|
||||
_uploadToFileStore(projectId, fileArgs, fsPath, callback) {
|
||||
Async.retry(
|
||||
FileStoreHandler.RETRY_ATTEMPTS,
|
||||
(cb, results) =>
|
||||
FileStoreHandler._doUploadFileFromDisk(projectId, fileArgs, fsPath, cb),
|
||||
callback
|
||||
cb =>
|
||||
HistoryManager.uploadBlobFromDisk(historyId, hash, size, fsPath, cb),
|
||||
error => {
|
||||
if (error) return callback(error, false)
|
||||
callback(null, true)
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
@@ -99,274 +78,23 @@ const FileStoreHandler = {
|
||||
hash,
|
||||
stat.size,
|
||||
fsPath,
|
||||
function (err, createdBlob) {
|
||||
function (err) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
fileArgs = { ...fileArgs, hash }
|
||||
FileStoreHandler._uploadToFileStore(
|
||||
projectId,
|
||||
fileArgs,
|
||||
fsPath,
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
OError.tag(err, 'Error uploading file, retries failed', {
|
||||
projectId,
|
||||
fileArgs,
|
||||
})
|
||||
return callback(err)
|
||||
}
|
||||
callback(err, result.url, result.fileRef, createdBlob)
|
||||
}
|
||||
)
|
||||
callback(err, new File(fileArgs), true)
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
_doUploadFileFromDisk(projectId, fileArgs, fsPath, callback) {
|
||||
const callbackOnce = _.once(callback)
|
||||
|
||||
const fileRef = new File(fileArgs)
|
||||
const fileId = fileRef._id
|
||||
const url = FileStoreHandler._buildUrl(projectId, fileId)
|
||||
|
||||
if (!Features.hasFeature('filestore')) {
|
||||
return callbackOnce(null, { url, fileRef })
|
||||
}
|
||||
|
||||
const readStream = fs.createReadStream(fsPath)
|
||||
readStream.on('error', function (err) {
|
||||
logger.warn(
|
||||
{ err, projectId, fileId, fsPath },
|
||||
'something went wrong on the read stream of uploadFileFromDisk'
|
||||
)
|
||||
callbackOnce(err)
|
||||
})
|
||||
readStream.on('open', function () {
|
||||
const opts = {
|
||||
method: 'post',
|
||||
uri: url,
|
||||
timeout: FIVE_MINS_IN_MS,
|
||||
headers: {
|
||||
'X-File-Hash-From-Web': fileArgs.hash,
|
||||
}, // send the hash to the filestore as a custom header so it can be checked
|
||||
}
|
||||
const writeStream = request(opts)
|
||||
writeStream.on('error', function (err) {
|
||||
logger.warn(
|
||||
{ err, projectId, fileId, fsPath },
|
||||
'something went wrong on the write stream of uploadFileFromDisk'
|
||||
)
|
||||
callbackOnce(err)
|
||||
})
|
||||
writeStream.on('response', function (response) {
|
||||
if (![200, 201].includes(response.statusCode)) {
|
||||
const err = new OError(
|
||||
`non-ok response from filestore for upload: ${response.statusCode}`,
|
||||
{ statusCode: response.statusCode }
|
||||
)
|
||||
return callbackOnce(err)
|
||||
}
|
||||
callbackOnce(null, { url, fileRef })
|
||||
}) // have to pass back an object because async.retry only accepts a single result argument
|
||||
readStream.pipe(writeStream)
|
||||
})
|
||||
},
|
||||
|
||||
getFileStreamNew(project, file, query, callback) {
|
||||
const projectId = project._id
|
||||
const historyId = project.overleaf?.history?.id
|
||||
const fileId = file._id
|
||||
const hash = file.hash
|
||||
if (historyId && hash && Features.hasFeature('project-history-blobs')) {
|
||||
// new behaviour - request from history
|
||||
const range = _extractRange(query?.range)
|
||||
HistoryManager.requestBlobWithFallback(
|
||||
projectId,
|
||||
hash,
|
||||
fileId,
|
||||
'GET',
|
||||
range,
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
}
|
||||
const { stream } = result
|
||||
callback(null, stream)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
// original behaviour
|
||||
FileStoreHandler.getFileStream(projectId, fileId, query, callback)
|
||||
}
|
||||
},
|
||||
|
||||
getFileStream(projectId, fileId, query, callback) {
|
||||
if (!Features.hasFeature('filestore')) {
|
||||
return callback(
|
||||
new Errors.NotFoundError('filestore is disabled, file not found')
|
||||
)
|
||||
}
|
||||
|
||||
let queryString = '?from=getFileStream'
|
||||
if (query != null && query.format != null) {
|
||||
queryString += `&format=${query.format}`
|
||||
}
|
||||
const opts = {
|
||||
method: 'get',
|
||||
uri: `${this._buildUrl(projectId, fileId)}${queryString}`,
|
||||
timeout: FIVE_MINS_IN_MS,
|
||||
headers: {},
|
||||
}
|
||||
if (query != null && query.range != null) {
|
||||
const rangeText = query.range
|
||||
if (rangeText && rangeText.match != null && rangeText.match(/\d+-\d+/)) {
|
||||
opts.headers.range = `bytes=${query.range}`
|
||||
}
|
||||
}
|
||||
const readStream = request(opts)
|
||||
readStream.on('error', err =>
|
||||
logger.err(
|
||||
{ err, projectId, fileId, query, opts },
|
||||
'error in file stream'
|
||||
)
|
||||
)
|
||||
callback(null, readStream)
|
||||
},
|
||||
|
||||
getFileSize(projectId, fileId, callback) {
|
||||
const url = this._buildUrl(projectId, fileId)
|
||||
request.head(`${url}?from=getFileSize`, (err, res) => {
|
||||
if (err) {
|
||||
OError.tag(err, 'failed to get file size from filestore', {
|
||||
projectId,
|
||||
fileId,
|
||||
})
|
||||
return callback(err)
|
||||
}
|
||||
if (res.statusCode === 404) {
|
||||
return callback(new Errors.NotFoundError('file not found in filestore'))
|
||||
}
|
||||
if (res.statusCode !== 200) {
|
||||
logger.warn(
|
||||
{ projectId, fileId, statusCode: res.statusCode },
|
||||
'filestore returned non-200 response'
|
||||
)
|
||||
return callback(new Error('filestore returned non-200 response'))
|
||||
}
|
||||
const fileSize = res.headers['content-length']
|
||||
callback(null, fileSize)
|
||||
})
|
||||
},
|
||||
|
||||
deleteFile(projectId, fileId, callback) {
|
||||
logger.debug({ projectId, fileId }, 'telling file store to delete file')
|
||||
const opts = {
|
||||
method: 'delete',
|
||||
uri: this._buildUrl(projectId, fileId),
|
||||
timeout: FIVE_MINS_IN_MS,
|
||||
}
|
||||
request(opts, function (err, response) {
|
||||
if (err) {
|
||||
logger.warn(
|
||||
{ err, projectId, fileId },
|
||||
'something went wrong deleting file from filestore'
|
||||
)
|
||||
}
|
||||
callback(err)
|
||||
})
|
||||
},
|
||||
|
||||
deleteProject(projectId, callback) {
|
||||
if (!Features.hasFeature('filestore')) {
|
||||
return callback() // if filestore is not in use, we don't need to delete anything
|
||||
}
|
||||
request(
|
||||
{
|
||||
method: 'delete',
|
||||
uri: this._buildUrl(projectId),
|
||||
timeout: FIVE_MINS_IN_MS,
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
return callback(
|
||||
OError.tag(
|
||||
err,
|
||||
'something went wrong deleting a project in filestore',
|
||||
{ projectId }
|
||||
)
|
||||
)
|
||||
}
|
||||
callback()
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
copyFile(oldProjectId, oldFileId, newProjectId, newFileId, callback) {
|
||||
logger.debug(
|
||||
{ oldProjectId, oldFileId, newProjectId, newFileId },
|
||||
'telling filestore to copy a file'
|
||||
)
|
||||
const opts = {
|
||||
method: 'put',
|
||||
json: {
|
||||
source: {
|
||||
project_id: oldProjectId,
|
||||
file_id: oldFileId,
|
||||
},
|
||||
},
|
||||
uri: this._buildUrl(newProjectId, newFileId),
|
||||
timeout: FIVE_MINS_IN_MS,
|
||||
}
|
||||
request(opts, function (err, response) {
|
||||
if (err) {
|
||||
OError.tag(
|
||||
err,
|
||||
'something went wrong telling filestore api to copy file',
|
||||
{
|
||||
oldProjectId,
|
||||
oldFileId,
|
||||
newProjectId,
|
||||
newFileId,
|
||||
}
|
||||
)
|
||||
callback(err)
|
||||
} else if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
// successful response
|
||||
callback(null, opts.uri)
|
||||
} else {
|
||||
err = new OError(
|
||||
`non-ok response from filestore for copyFile: ${response.statusCode}`,
|
||||
{
|
||||
uri: opts.uri,
|
||||
statusCode: response.statusCode,
|
||||
}
|
||||
)
|
||||
callback(err)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
_buildUrl(projectId, fileId) {
|
||||
return (
|
||||
`${settings.apis.filestore.url}/project/${projectId}` +
|
||||
(fileId ? `/file/${fileId}` : '')
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
function _extractRange(range) {
|
||||
if (typeof range === 'string' && /\d+-\d+/.test(range)) {
|
||||
return `bytes=${range}`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FileStoreHandler
|
||||
module.exports.promises = promisifyAll(FileStoreHandler, {
|
||||
multiResult: {
|
||||
uploadFileFromDisk: ['url', 'fileRef', 'createdBlob'],
|
||||
uploadFileFromDiskWithHistoryId: ['url', 'fileRef', 'createdBlob'],
|
||||
uploadFileFromDisk: ['fileRef', 'createdBlob'],
|
||||
uploadFileFromDiskWithHistoryId: ['fileRef', 'createdBlob'],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -52,13 +52,12 @@ async function requestBlob(method, req, res) {
|
||||
}
|
||||
|
||||
const range = req.get('Range')
|
||||
let stream, source, contentLength
|
||||
let stream, contentLength
|
||||
try {
|
||||
;({ stream, source, contentLength } =
|
||||
await HistoryManager.promises.requestBlobWithFallback(
|
||||
;({ stream, contentLength } =
|
||||
await HistoryManager.promises.requestBlobWithProjectId(
|
||||
projectId,
|
||||
hash,
|
||||
req.query.fallback,
|
||||
method,
|
||||
range
|
||||
))
|
||||
@@ -66,7 +65,6 @@ async function requestBlob(method, req, res) {
|
||||
if (err instanceof Errors.NotFoundError) return res.status(404).end()
|
||||
throw err
|
||||
}
|
||||
res.appendHeader('X-Served-By', source)
|
||||
|
||||
if (contentLength) res.setHeader('Content-Length', contentLength) // set on HEAD
|
||||
res.setHeader('Content-Type', 'application/octet-stream')
|
||||
|
||||
@@ -11,9 +11,8 @@ const OError = require('@overleaf/o-error')
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
const ProjectGetter = require('../Project/ProjectGetter')
|
||||
const HistoryBackupDeletionHandler = require('./HistoryBackupDeletionHandler')
|
||||
const { db, ObjectId, waitForDb } = require('../../infrastructure/mongodb')
|
||||
const { db, waitForDb } = require('../../infrastructure/mongodb')
|
||||
const Metrics = require('@overleaf/metrics')
|
||||
const logger = require('@overleaf/logger')
|
||||
const { NotFoundError } = require('../Errors/Errors')
|
||||
|
||||
const HISTORY_V1_URL = settings.apis.v1_history.url
|
||||
@@ -169,22 +168,25 @@ async function copyBlob(sourceHistoryId, targetHistoryId, hash) {
|
||||
)
|
||||
}
|
||||
|
||||
async function requestBlobWithFallback(
|
||||
async function requestBlobWithProjectId(
|
||||
projectId,
|
||||
hash,
|
||||
fileId,
|
||||
method = 'GET',
|
||||
range = ''
|
||||
) {
|
||||
const project = await ProjectGetter.promises.getProject(projectId, {
|
||||
'overleaf.history.id': true,
|
||||
})
|
||||
return requestBlob(project.overleaf.history.id, hash, method, range)
|
||||
}
|
||||
|
||||
async function requestBlob(historyId, hash, method = 'GET', range = '') {
|
||||
// Talk to history-v1 directly to avoid streaming via project-history.
|
||||
let url = new URL(HISTORY_V1_URL)
|
||||
url.pathname += `/projects/${project.overleaf.history.id}/blobs/${hash}`
|
||||
const url = new URL(HISTORY_V1_URL)
|
||||
url.pathname += `/projects/${historyId}/blobs/${hash}`
|
||||
|
||||
const opts = { method, headers: { Range: range } }
|
||||
let stream, response, source
|
||||
let stream, response
|
||||
try {
|
||||
;({ stream, response } = await fetchStreamWithResponse(url, {
|
||||
...opts,
|
||||
@@ -193,38 +195,18 @@ async function requestBlobWithFallback(
|
||||
password: settings.apis.v1_history.pass,
|
||||
},
|
||||
}))
|
||||
source = 'history-v1'
|
||||
} catch (err) {
|
||||
if (err instanceof RequestFailedError && err.response.status === 404) {
|
||||
if (ObjectId.isValid(fileId)) {
|
||||
url = new URL(settings.apis.filestore.url)
|
||||
url.pathname = `/project/${projectId}/file/${fileId}`
|
||||
try {
|
||||
;({ stream, response } = await fetchStreamWithResponse(url, opts))
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof RequestFailedError &&
|
||||
err.response.status === 404
|
||||
) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
throw err
|
||||
}
|
||||
logger.warn({ projectId, hash, fileId }, 'missing history blob')
|
||||
source = 'filestore'
|
||||
} else {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
throw new NotFoundError()
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
Metrics.inc('request_blob', 1, { path: source })
|
||||
Metrics.inc('request_blob', 1, { path: 'history-v1' })
|
||||
return {
|
||||
url,
|
||||
stream,
|
||||
source,
|
||||
contentLength: response.headers.get('Content-Length'),
|
||||
contentLength: parseInt(response.headers.get('Content-Length'), 10),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +399,8 @@ module.exports = {
|
||||
getCurrentContent: callbackify(getCurrentContent),
|
||||
uploadBlobFromDisk: callbackify(uploadBlobFromDisk),
|
||||
copyBlob: callbackify(copyBlob),
|
||||
requestBlobWithFallback: callbackify(requestBlobWithFallback),
|
||||
requestBlob: callbackify(requestBlob),
|
||||
requestBlobWithProjectId: callbackify(requestBlobWithProjectId),
|
||||
getLatestHistory: callbackify(getLatestHistory),
|
||||
getChanges: callbackify(getChanges),
|
||||
promises: {
|
||||
@@ -431,7 +414,8 @@ module.exports = {
|
||||
getContentAtVersion,
|
||||
uploadBlobFromDisk,
|
||||
copyBlob,
|
||||
requestBlobWithFallback,
|
||||
requestBlob,
|
||||
requestBlobWithProjectId,
|
||||
getLatestHistory,
|
||||
getChanges,
|
||||
},
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Pass settings to enable consistent unit tests from .js and .mjs modules
|
||||
function projectHistoryURLWithFilestoreFallback(
|
||||
Settings,
|
||||
projectId,
|
||||
historyId,
|
||||
fileRef,
|
||||
origin
|
||||
) {
|
||||
const filestoreURL = `${Settings.apis.filestore.url}/project/${projectId}/file/${fileRef._id}?from=${origin}`
|
||||
// TODO: When this file is converted to ES modules we will be able to use Features.hasFeature('project-history-blobs'). Currently we can't stub the feature return value in tests.
|
||||
if (fileRef.hash && Settings.filestoreMigrationLevel >= 1) {
|
||||
return {
|
||||
url: `${Settings.apis.project_history.url}/project/${historyId}/blob/${fileRef.hash}`,
|
||||
fallbackURL: filestoreURL,
|
||||
}
|
||||
} else {
|
||||
return { url: filestoreURL }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { projectHistoryURLWithFilestoreFallback }
|
||||
@@ -15,7 +15,6 @@ const AuthorizationManager = require('../Authorization/AuthorizationManager')
|
||||
const ProjectLocator = require('../Project/ProjectLocator')
|
||||
const DocstoreManager = require('../Docstore/DocstoreManager')
|
||||
const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
|
||||
const FileStoreHandler = require('../FileStore/FileStoreHandler')
|
||||
const _ = require('lodash')
|
||||
const LinkedFilesHandler = require('./LinkedFilesHandler')
|
||||
const {
|
||||
@@ -25,6 +24,7 @@ const {
|
||||
SourceFileNotFoundError,
|
||||
} = require('./LinkedFilesErrors')
|
||||
const { promisify } = require('@overleaf/promise-utils')
|
||||
const HistoryManager = require('../History/HistoryManager')
|
||||
|
||||
module.exports = ProjectFileAgent = {
|
||||
createLinkedFile(
|
||||
@@ -134,17 +134,17 @@ module.exports = ProjectFileAgent = {
|
||||
}
|
||||
) // Created
|
||||
} else if (type === 'file') {
|
||||
return FileStoreHandler.getFileStreamNew(
|
||||
sourceProject,
|
||||
entity,
|
||||
null,
|
||||
function (err, fileStream) {
|
||||
return HistoryManager.requestBlob(
|
||||
sourceProject.overleaf.history.id,
|
||||
entity.hash,
|
||||
'GET',
|
||||
function (err, result) {
|
||||
if (err != null) {
|
||||
return callback(err)
|
||||
}
|
||||
return LinkedFilesHandler.importFromStream(
|
||||
projectId,
|
||||
fileStream,
|
||||
result.stream,
|
||||
linkedFileData,
|
||||
name,
|
||||
parentFolderId,
|
||||
|
||||
@@ -844,9 +844,6 @@ const _ProjectController = {
|
||||
isInvitedMember
|
||||
),
|
||||
capabilities,
|
||||
projectHistoryBlobsEnabled: Features.hasFeature(
|
||||
'project-history-blobs'
|
||||
),
|
||||
roMirrorOnClientNoLocalStorage:
|
||||
Settings.adminOnlyLogin || project.name.startsWith('Debug: '),
|
||||
languages: Settings.languages,
|
||||
|
||||
@@ -16,7 +16,6 @@ const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
|
||||
const DocstoreManager = require('../Docstore/DocstoreManager')
|
||||
const EditorRealTimeController = require('../Editor/EditorRealTimeController')
|
||||
const HistoryManager = require('../History/HistoryManager')
|
||||
const FilestoreHandler = require('../FileStore/FileStoreHandler')
|
||||
const ChatApiHandler = require('../Chat/ChatApiHandler')
|
||||
const { promiseMapWithLimit } = require('@overleaf/promise-utils')
|
||||
const { READ_PREFERENCE_SECONDARY } = require('../../infrastructure/mongodb')
|
||||
@@ -350,7 +349,6 @@ async function expireDeletedProject(projectId) {
|
||||
deletedProject.project._id,
|
||||
historyId
|
||||
),
|
||||
FilestoreHandler.promises.deleteProject(deletedProject.project._id),
|
||||
ChatApiHandler.promises.destroyProject(deletedProject.project._id),
|
||||
ProjectAuditLogEntry.deleteMany({ projectId }),
|
||||
Modules.promises.hooks.fire('projectExpired', deletedProject.project._id),
|
||||
|
||||
@@ -7,7 +7,6 @@ const { Doc } = require('../../models/Doc')
|
||||
const { File } = require('../../models/File')
|
||||
const DocstoreManager = require('../Docstore/DocstoreManager')
|
||||
const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
|
||||
const FileStoreHandler = require('../FileStore/FileStoreHandler')
|
||||
const HistoryManager = require('../History/HistoryManager')
|
||||
const ProjectCreationHandler = require('./ProjectCreationHandler')
|
||||
const ProjectDeleter = require('./ProjectDeleter')
|
||||
@@ -20,7 +19,6 @@ const SafePath = require('./SafePath')
|
||||
const TpdsProjectFlusher = require('../ThirdPartyDataStore/TpdsProjectFlusher')
|
||||
const _ = require('lodash')
|
||||
const TagsHandler = require('../Tags/TagsHandler')
|
||||
const Features = require('../../infrastructure/Features')
|
||||
const ClsiCacheManager = require('../Compile/ClsiCacheManager')
|
||||
|
||||
module.exports = {
|
||||
@@ -225,66 +223,29 @@ async function _copyFiles(sourceEntries, sourceProject, targetProject) {
|
||||
async sourceEntry => {
|
||||
const sourceFile = sourceEntry.file
|
||||
const path = sourceEntry.path
|
||||
const file = new File({ name: SafePath.clean(sourceFile.name) })
|
||||
const file = new File({
|
||||
name: SafePath.clean(sourceFile.name),
|
||||
hash: sourceFile.hash,
|
||||
})
|
||||
if (sourceFile.linkedFileData != null) {
|
||||
file.linkedFileData = sourceFile.linkedFileData
|
||||
file.created = sourceFile.created
|
||||
}
|
||||
if (sourceFile.hash != null) {
|
||||
file.hash = sourceFile.hash
|
||||
}
|
||||
let createdBlob = false
|
||||
const usingFilestore = Features.hasFeature('filestore')
|
||||
if (file.hash != null && Features.hasFeature('project-history-blobs')) {
|
||||
try {
|
||||
await HistoryManager.promises.copyBlob(
|
||||
sourceHistoryId,
|
||||
targetHistoryId,
|
||||
file.hash
|
||||
)
|
||||
createdBlob = true
|
||||
if (!usingFilestore) {
|
||||
return { createdBlob, file, path, url: null }
|
||||
}
|
||||
} catch (err) {
|
||||
if (!usingFilestore) {
|
||||
throw OError.tag(err, 'unexpected error copying blob', {
|
||||
sourceProjectId: sourceProject._id,
|
||||
targetProjectId: targetProject._id,
|
||||
sourceFile,
|
||||
sourceHistoryId,
|
||||
})
|
||||
} else {
|
||||
logger.error(
|
||||
{
|
||||
err,
|
||||
sourceProjectId: sourceProject._id,
|
||||
targetProjectId: targetProject._id,
|
||||
sourceFile,
|
||||
sourceHistoryId,
|
||||
},
|
||||
'unexpected error copying blob'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (createdBlob && Features.hasFeature('project-history-blobs')) {
|
||||
return { createdBlob, file, path, url: null }
|
||||
}
|
||||
if (!usingFilestore) {
|
||||
// Note: This is also checked in app.mjs
|
||||
throw new OError(
|
||||
'bad config: need to enable either filestore or project-history-blobs'
|
||||
try {
|
||||
await HistoryManager.promises.copyBlob(
|
||||
sourceHistoryId,
|
||||
targetHistoryId,
|
||||
file.hash
|
||||
)
|
||||
return { createdBlob: true, file, path }
|
||||
} catch (err) {
|
||||
throw OError.tag(err, 'unexpected error copying blob', {
|
||||
sourceProjectId: sourceProject._id,
|
||||
targetProjectId: targetProject._id,
|
||||
sourceFile,
|
||||
sourceHistoryId,
|
||||
})
|
||||
}
|
||||
const url = await FileStoreHandler.promises.copyFile(
|
||||
sourceProject._id,
|
||||
sourceFile._id,
|
||||
targetProject._id,
|
||||
file._id
|
||||
)
|
||||
|
||||
return { createdBlob, file, path, url }
|
||||
}
|
||||
)
|
||||
return targetEntries
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
let ProjectEditorHandler
|
||||
const _ = require('lodash')
|
||||
const Path = require('path')
|
||||
const Features = require('../../infrastructure/Features')
|
||||
|
||||
module.exports = ProjectEditorHandler = {
|
||||
trackChangesAvailable: false,
|
||||
@@ -98,18 +97,12 @@ module.exports = ProjectEditorHandler = {
|
||||
},
|
||||
|
||||
buildFileModelView(file) {
|
||||
const additionalFileProperties = {}
|
||||
|
||||
if (Features.hasFeature('project-history-blobs')) {
|
||||
additionalFileProperties.hash = file.hash
|
||||
}
|
||||
|
||||
return {
|
||||
_id: file._id,
|
||||
name: file.name,
|
||||
linkedFileData: file.linkedFileData,
|
||||
created: file.created,
|
||||
...additionalFileProperties,
|
||||
hash: file.hash,
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ const addFile = wrapWithLock({
|
||||
if (!SafePath.isCleanFilename(fileName)) {
|
||||
throw new Errors.InvalidNameError('invalid element name')
|
||||
}
|
||||
const { url, fileRef, createdBlob } =
|
||||
const { fileRef, createdBlob } =
|
||||
await ProjectEntityUpdateHandler._uploadFile(
|
||||
projectId,
|
||||
folderId,
|
||||
@@ -347,7 +347,6 @@ const addFile = wrapWithLock({
|
||||
folderId,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl: url,
|
||||
createdBlob,
|
||||
source,
|
||||
}
|
||||
@@ -357,7 +356,6 @@ const addFile = wrapWithLock({
|
||||
folderId,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
createdBlob,
|
||||
source,
|
||||
}) {
|
||||
@@ -374,7 +372,6 @@ const addFile = wrapWithLock({
|
||||
createdBlob,
|
||||
file: fileRef,
|
||||
path: result && result.path && result.path.fileSystem,
|
||||
url: fileStoreUrl,
|
||||
},
|
||||
]
|
||||
await DocumentUpdaterHandler.promises.updateProjectStructure(
|
||||
@@ -548,7 +545,7 @@ const upsertFile = wrapWithLock({
|
||||
name: fileName,
|
||||
linkedFileData,
|
||||
}
|
||||
const { url, fileRef, createdBlob } =
|
||||
const { fileRef, createdBlob } =
|
||||
await FileStoreHandler.promises.uploadFileFromDisk(
|
||||
projectId,
|
||||
fileArgs,
|
||||
@@ -563,7 +560,6 @@ const upsertFile = wrapWithLock({
|
||||
linkedFileData,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl: url,
|
||||
createdBlob,
|
||||
source,
|
||||
}
|
||||
@@ -574,7 +570,6 @@ const upsertFile = wrapWithLock({
|
||||
fileName,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
createdBlob,
|
||||
source,
|
||||
}) {
|
||||
@@ -639,7 +634,6 @@ const upsertFile = wrapWithLock({
|
||||
createdBlob,
|
||||
file: fileRef,
|
||||
path: path.fileSystem,
|
||||
url: fileStoreUrl,
|
||||
},
|
||||
],
|
||||
newProject: project,
|
||||
@@ -659,7 +653,6 @@ const upsertFile = wrapWithLock({
|
||||
existingFile._id,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
folderId,
|
||||
source,
|
||||
createdBlob
|
||||
@@ -673,7 +666,6 @@ const upsertFile = wrapWithLock({
|
||||
folderId,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
createdBlob,
|
||||
source,
|
||||
})
|
||||
@@ -733,15 +725,12 @@ const upsertFileWithPath = wrapWithLock({
|
||||
name: fileName,
|
||||
linkedFileData,
|
||||
}
|
||||
const {
|
||||
url: fileStoreUrl,
|
||||
fileRef,
|
||||
createdBlob,
|
||||
} = await FileStoreHandler.promises.uploadFileFromDisk(
|
||||
projectId,
|
||||
fileArgs,
|
||||
fsPath
|
||||
)
|
||||
const { fileRef, createdBlob } =
|
||||
await FileStoreHandler.promises.uploadFileFromDisk(
|
||||
projectId,
|
||||
fileArgs,
|
||||
fsPath
|
||||
)
|
||||
|
||||
return {
|
||||
projectId,
|
||||
@@ -751,7 +740,6 @@ const upsertFileWithPath = wrapWithLock({
|
||||
linkedFileData,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
createdBlob,
|
||||
source,
|
||||
}
|
||||
@@ -764,7 +752,6 @@ const upsertFileWithPath = wrapWithLock({
|
||||
linkedFileData,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
createdBlob,
|
||||
source,
|
||||
}) {
|
||||
@@ -787,7 +774,6 @@ const upsertFileWithPath = wrapWithLock({
|
||||
linkedFileData,
|
||||
userId,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
createdBlob,
|
||||
source,
|
||||
})
|
||||
@@ -1084,15 +1070,12 @@ const convertDocToFile = wrapWithLock({
|
||||
}
|
||||
await DocumentUpdaterHandler.promises.deleteDoc(projectId, docId, false)
|
||||
const fsPath = await FileWriter.promises.writeLinesToDisk(projectId, lines)
|
||||
const {
|
||||
url: fileStoreUrl,
|
||||
fileRef,
|
||||
createdBlob,
|
||||
} = await FileStoreHandler.promises.uploadFileFromDisk(
|
||||
projectId,
|
||||
{ name: doc.name, rev: rev + 1 },
|
||||
fsPath
|
||||
)
|
||||
const { fileRef, createdBlob } =
|
||||
await FileStoreHandler.promises.uploadFileFromDisk(
|
||||
projectId,
|
||||
{ name: doc.name, rev: rev + 1 },
|
||||
fsPath
|
||||
)
|
||||
try {
|
||||
await fs.promises.unlink(fsPath)
|
||||
} catch (err) {
|
||||
@@ -1103,7 +1086,6 @@ const convertDocToFile = wrapWithLock({
|
||||
doc,
|
||||
path: docPath,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
userId,
|
||||
source,
|
||||
createdBlob,
|
||||
@@ -1114,7 +1096,6 @@ const convertDocToFile = wrapWithLock({
|
||||
doc,
|
||||
path,
|
||||
fileRef,
|
||||
fileStoreUrl,
|
||||
userId,
|
||||
source,
|
||||
createdBlob,
|
||||
@@ -1133,7 +1114,7 @@ const convertDocToFile = wrapWithLock({
|
||||
userId,
|
||||
{
|
||||
oldDocs: [{ doc, path }],
|
||||
newFiles: [{ file: fileRef, path, url: fileStoreUrl, createdBlob }],
|
||||
newFiles: [{ file: fileRef, path, createdBlob }],
|
||||
newProject: project,
|
||||
},
|
||||
source
|
||||
@@ -1380,7 +1361,6 @@ const ProjectEntityUpdateHandler = {
|
||||
fileId,
|
||||
userId,
|
||||
newFileRef,
|
||||
fileStoreUrl,
|
||||
folderId,
|
||||
source,
|
||||
createdBlob
|
||||
@@ -1409,7 +1389,6 @@ const ProjectEntityUpdateHandler = {
|
||||
file: updatedFileRef,
|
||||
createdBlob,
|
||||
path: path.fileSystem,
|
||||
url: fileStoreUrl,
|
||||
},
|
||||
]
|
||||
const projectHistoryId = project.overleaf?.history?.id
|
||||
|
||||
@@ -24,7 +24,6 @@ import _ from 'lodash'
|
||||
import Async from 'async'
|
||||
import Errors from '../Errors/Errors.js'
|
||||
import { promisify } from '@overleaf/promise-utils'
|
||||
import HistoryURLHelper from '../History/HistoryURLHelper.js'
|
||||
|
||||
let ReferencesHandler
|
||||
|
||||
@@ -167,21 +166,15 @@ export default ReferencesHandler = {
|
||||
const bibDocUrls = docIds.map(docId =>
|
||||
ReferencesHandler._buildDocUrl(projectId, docId)
|
||||
)
|
||||
const bibFileUrls = fileRefs.map(fileRef =>
|
||||
HistoryURLHelper.projectHistoryURLWithFilestoreFallback(
|
||||
settings,
|
||||
projectId,
|
||||
historyId,
|
||||
fileRef,
|
||||
'bibFileUrls'
|
||||
)
|
||||
)
|
||||
const bibFileUrls = fileRefs.map(fileRef => ({
|
||||
url: `${settings.apis.project_history.url}/project/${historyId}/blob/${fileRef.hash}`,
|
||||
}))
|
||||
const sourceURLs = bibDocUrls.concat(bibFileUrls)
|
||||
return request.post(
|
||||
{
|
||||
url: `${settings.apis.references.url}/project/${projectId}/index`,
|
||||
json: {
|
||||
docUrls: sourceURLs.map(item => item.fallbackURL || item.url),
|
||||
docUrls: sourceURLs.map(item => item.url),
|
||||
sourceURLs,
|
||||
fullIndex: isFullIndex,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,6 @@ const metrics = require('@overleaf/metrics')
|
||||
const Path = require('path')
|
||||
const { fetchNothing } = require('@overleaf/fetch-utils')
|
||||
const settings = require('@overleaf/settings')
|
||||
const HistoryURLHelper = require('../History/HistoryURLHelper')
|
||||
|
||||
const CollaboratorsGetter =
|
||||
require('../Collaborators/CollaboratorsGetter').promises
|
||||
@@ -81,24 +80,14 @@ async function addFile(params) {
|
||||
rev,
|
||||
folderId,
|
||||
} = params
|
||||
// Go through project-history to avoid the need for handling history-v1 authentication.
|
||||
const { url, fallbackURL } =
|
||||
HistoryURLHelper.projectHistoryURLWithFilestoreFallback(
|
||||
settings,
|
||||
projectId,
|
||||
historyId,
|
||||
{ _id: fileId, hash },
|
||||
'tpdsAddFile'
|
||||
)
|
||||
|
||||
await addEntity({
|
||||
projectId,
|
||||
path,
|
||||
projectName,
|
||||
rev,
|
||||
folderId,
|
||||
streamOrigin: url,
|
||||
streamFallback: fallbackURL,
|
||||
// Go through project-history to avoid the need for handling history-v1 authentication.
|
||||
streamOrigin: `${settings.apis.project_history.url}/project/${historyId}/blob/${hash}`,
|
||||
entityId: fileId,
|
||||
entityType: 'file',
|
||||
})
|
||||
|
||||
@@ -194,14 +194,14 @@ async function _createFile(project, projectPath, fsPath) {
|
||||
throw new OError('missing history id')
|
||||
}
|
||||
const fileName = Path.basename(projectPath)
|
||||
const { createdBlob, fileRef, url } =
|
||||
const { createdBlob, fileRef } =
|
||||
await FileStoreHandler.promises.uploadFileFromDiskWithHistoryId(
|
||||
projectId,
|
||||
historyId,
|
||||
{ name: fileName },
|
||||
fsPath
|
||||
)
|
||||
return { createdBlob, file: fileRef, path: projectPath, url }
|
||||
return { createdBlob, file: fileRef, path: projectPath }
|
||||
}
|
||||
|
||||
async function _notifyDocumentUpdater(project, userId, changes) {
|
||||
|
||||
@@ -19,7 +19,6 @@ const trackChangesModuleAvailable =
|
||||
* @property {boolean | undefined} enableGithubSync
|
||||
* @property {boolean | undefined} enableGitBridge
|
||||
* @property {boolean | undefined} enableHomepage
|
||||
* @property {number} filestoreMigrationLevel
|
||||
* @property {boolean | undefined} enableSaml
|
||||
* @property {boolean | undefined} ldap
|
||||
* @property {boolean | undefined} oauth
|
||||
@@ -29,14 +28,6 @@ const trackChangesModuleAvailable =
|
||||
*/
|
||||
|
||||
const Features = {
|
||||
validateSettings() {
|
||||
if (![0, 1, 2].includes(Settings.filestoreMigrationLevel)) {
|
||||
throw new Error(
|
||||
`invalid OVERLEAF_FILESTORE_MIGRATION_LEVEL=${Settings.filestoreMigrationLevel}, expected 0, 1 or 2`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
@@ -97,10 +88,6 @@ const Features = {
|
||||
_.get(Settings, ['apis', 'linkedUrlProxy', 'url']) &&
|
||||
Settings.enabledLinkedFileTypes.includes('url')
|
||||
)
|
||||
case 'project-history-blobs':
|
||||
return Settings.filestoreMigrationLevel > 0
|
||||
case 'filestore':
|
||||
return Settings.filestoreMigrationLevel < 2
|
||||
case 'support':
|
||||
return supportModuleAvailable
|
||||
case 'symbol-palette':
|
||||
|
||||
Reference in New Issue
Block a user