Files
zero-fucks-given/apps/backend/app/base_modules/FileManager/services/path_service.ts
T

85 lines
2.0 KiB
TypeScript

import { randomUUID } from 'node:crypto'
import { posix } from 'node:path'
export interface FileNameMeta {
name: string
ext: string
}
export interface UniqueNameFormatterParams {
base: string
count: number
extension: string
}
export type UniqueNameFormatter = (params: UniqueNameFormatterParams) => string
export default class PathService {
public toStoragePath(path: string = ''): string {
let safePath = path.replace(/^(\.\.(\/|\\|$))+/, '')
safePath = safePath.startsWith('/') ? safePath.slice(1) : safePath
return safePath
}
public toClientPath(path: string = ''): string {
return path.replace(/^\/?uploads\//, '/')
}
public join(...parts: string[]): string {
return posix.join(...parts)
}
public dirname(path: string): string {
return this.toStoragePath(path).split('/').slice(0, -1).join('/')
}
public basename(path: string): string {
return path.split('/').reverse()[0]
}
public getFileNameMeta(name: string): FileNameMeta {
const extIndex = name.lastIndexOf('.')
const rawName = extIndex !== -1 ? name.slice(0, extIndex) : name
const ext = extIndex !== -1 ? name.slice(extIndex).trim() : ''
return {
name: rawName,
ext,
}
}
public sanitizeFileName(name: string): string {
return name.replace(/[()]/g, '').replace(/[^a-zA-Z0-9.\-_ ]/g, '')
}
public async generateUniqueName(
dir: string,
fileName: string,
exists: (path: string) => Promise<boolean>,
formatter?: UniqueNameFormatter
): Promise<string> {
const { ext, name } = this.getFileNameMeta(fileName)
const safeBase = this.sanitizeFileName(name)
let counter = 1
let newName = `${safeBase}${ext}`
while (await exists(this.join(dir, newName))) {
newName =
typeof formatter === 'function'
? formatter({ base: safeBase, count: counter, extension: ext })
: `${safeBase}-${counter}${ext}`
counter++
if (counter > 5) {
newName = `${randomUUID()}${ext}`
break
}
}
return newName
}
}