tethys.backend/providers/drive/path_prefixer/index.ts

97 lines
3.5 KiB
TypeScript
Raw Normal View History

import { slash } from '@poppinss/utils';
import { relative, normalize } from 'path';
import { PathTraversalDetectedException } from '../exceptions/index.js';
/**
* Path prefixer for resolving and prefixing paths for disk drivers
*/
export default class PathPrefixer {
/**
* Separator used for dividing path segments is always unix-style forward slash
*/
separator: '/';
/**
* Prefix used for path prefixing. Can be empty string for cloud drivers.
*/
prefix: string;
// constructor(prefix?: string);
constructor(prefix: string = '') {
/**
* Separator used for dividing path segments is always unix-style forward slash
*/
this.separator = '/';
// strip slashes from the end of the prefix
this.prefix = prefix.replace(/\/+$/g, '');
// always end prefix with separator if it is not empty
if (this.prefix !== '' || prefix === this.separator) {
this.prefix += this.separator;
}
}
/**
* Normalize given path to always use `/` as separator and resolve relative paths using `.` and `..`.
* It also guards against path traversal beyond the root.
*/
normalizePath(path: string): string {
// const converted = (0, utils_1.slash)(path);
const converted = slash(path);
const parts = [];
for (const part of converted.split(this.separator)) {
if (['', '.'].includes(part)) {
continue;
}
if (part === '..') {
// if we are traversing beyond the root
if (parts.length === 0) {
throw PathTraversalDetectedException.invoke(converted);
}
parts.pop();
} else {
parts.push(part);
}
}
return parts.join(this.separator);
}
/**
* Ruturns normalized and prefixed location path.
*/
prefixPath(location: string): string {
return this.prefix + this.normalizePath(location);
}
/**
* Ruturns normalized and prefixed location path for directory so always ending with slash.
* Useful for cloud drivers prefix when listitng files.
*/
prefixDirectoryPath(location: string): string {
return this.prefixPath(location) + this.separator;
}
/**
* Returns normalized path after stripping the current prefix from it.
* It is a reverse operation of `prefixPath`.
*/
stripPrefix(location: string): string {
// const path = (0, path_1.relative)(this.prefix, (0, utils_1.slash)(location));
const path = relative(this.prefix, slash(location));
return this.normalizePath(path);
}
/**
* Returns a new instance of `PathPrefixer` which is using as prefix stripped prefix from path of current `PathPrefixer`.
*/
withStrippedPrefix(path: string): PathPrefixer {
return new PathPrefixer(this.stripPrefix(path));
}
/**
* Returns a new instance of `PathPrefixer` which is using as prefix current prefix merged with provided prefix.
*/
withPrefix(prefix: string): PathPrefixer {
return new PathPrefixer(this.prefixPath(prefix));
}
/**
* Returns a new instance of `PathPrefixer` which is using as prefix provided normalized path.
*/
static fromPath(path: string): PathPrefixer {
// return new this((0, utils_1.slash)((0, path_1.normalize)(path)));
return new this(slash(normalize(path)));
}
}