97 lines
3.5 KiB
TypeScript
97 lines
3.5 KiB
TypeScript
|
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)));
|
||
|
}
|
||
|
}
|