@@ -11,14 +11,14 @@ import { AxiosResponse } from 'axios'
1111import fs from 'node:fs'
1212import path from 'node:path'
1313import { Readable } from 'node:stream'
14- import { pipeline } from 'node:stream/promises'
1514import { extract as extractTar } from 'tar'
1615import { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface'
1716import { generateThumbnail } from '../../../common/image'
1817import { HTTP_METHOD } from '../../applications.constants'
1918import { SPACE_OPERATION } from '../../spaces/constants/spaces'
2019import { FastifySpaceRequest } from '../../spaces/interfaces/space-request.interface'
2120import { SpaceEnv } from '../../spaces/models/space-env.model'
21+ import { SpacesManager } from '../../spaces/services/spaces-manager.service'
2222import { realTrashPathFromSpace } from '../../spaces/utils/paths'
2323import { canAccessToSpace , haveSpaceEnvPermissions } from '../../spaces/utils/permissions'
2424import { UserModel } from '../../users/models/user.model'
@@ -68,7 +68,8 @@ export class FilesManager {
6868 constructor (
6969 private readonly http : HttpService ,
7070 private readonly filesQueries : FilesQueries ,
71- private readonly filesLockManager : FilesLockManager
71+ private readonly filesLockManager : FilesLockManager ,
72+ private readonly spacesManager : SpacesManager
7273 ) { }
7374
7475 sendFileFromSpace ( space : SpaceEnv , asAttachment = false , downloadName = '' ) : SendFile {
@@ -181,9 +182,10 @@ export class FilesManager {
181182 POST: create new resource
182183 PUT: create or update new resource (even if a parent path does not exist)
183184 */
185+ const overwrite = req . method === HTTP_METHOD . PUT
184186 const realParentPath = dirName ( space . realPath )
185187
186- if ( req . method === HTTP_METHOD . POST ) {
188+ if ( ! overwrite ) {
187189 if ( await isPathExists ( space . realPath ) ) {
188190 throw new FileError ( HttpStatus . BAD_REQUEST , 'Resource already exists' )
189191 }
@@ -198,14 +200,31 @@ export class FilesManager {
198200 const basePath = realParentPath + path . sep
199201
200202 for await ( const part of req . files ( ) ) {
201- // part.filename may contain a path like foo/bar.txt.
203+ // part.filename may contain a path like foo/bar.txt
202204 const dstFile = path . resolve ( basePath , part . filename )
203205 // prevent path traversal
204206 if ( ! dstFile . startsWith ( basePath ) ) {
205207 throw new FileError ( HttpStatus . FORBIDDEN , 'Location is not allowed' )
206208 }
207- // make dir in space
209+
208210 const dstDir = dirName ( dstFile )
211+
212+ if ( overwrite ) {
213+ // prevent errors when an uploaded file would replace a directory with the same name
214+ // only applies in `overwrite` cases
215+ if ( ( await isPathExists ( dstFile ) ) && ( await isPathIsDir ( dstFile ) ) ) {
216+ // If a directory already exists at the destination path, delete it to allow overwriting with the uploaded file
217+ const dstUrl = path . join ( path . dirname ( space . url ) , part . filename )
218+ const dstSpace = await this . spacesManager . spaceEnv ( user , dstUrl . split ( '/' ) )
219+ await this . delete ( user , dstSpace )
220+ } else if ( ( await isPathExists ( dstDir ) ) && ! ( await isPathIsDir ( dstDir ) ) ) {
221+ // If the destination's parent exists but is a file, remove it so we can create the directory
222+ const dstUrl = path . join ( path . dirname ( space . url ) , path . dirname ( part . filename ) )
223+ const dstSpace = await this . spacesManager . spaceEnv ( user , dstUrl . split ( '/' ) )
224+ await this . delete ( user , dstSpace )
225+ }
226+ }
227+ // make dir in space
209228 if ( ! ( await isPathExists ( dstDir ) ) ) {
210229 await makeDir ( dstDir , true )
211230 }
@@ -215,7 +234,7 @@ export class FilesManager {
215234 if ( ! ok ) throw new LockConflict ( fileLock , 'Conflicting lock' )
216235 // do
217236 try {
218- await pipeline ( part . file , fs . createWriteStream ( dstFile , { highWaterMark : DEFAULT_HIGH_WATER_MARK } ) )
237+ await writeFromStream ( dstFile , part . file )
219238 } finally {
220239 await this . filesLockManager . removeLock ( fileLock . key )
221240 }
0 commit comments