import fs from"node:fs";import path from"node:path";import{minimatch}from"minimatch";import AbstractFileStorage from"./storage.mjs";import Files from"./files.mjs";import{encodeURL}from"../../common/utils/helpers.mjs";import{MEDIA_MIME_TYPES}from"../../common/constants.mjs";import mime from"mime-types";export default class LocalFileStorage extends AbstractFileStorage{constructor(t,e){super(t),this.root=e}async createDirectory(t,e={}){if(this.root!==paths.data||!e.isAdmin)throw new Error("You may not create directories in this location");const r=path.join(this.root,t),o=path.relative(this.root,r);if(o&&(o.startsWith("..")||path.isAbsolute(o)))throw new Error("You are not permitted to create a subdirectory in this location");return fs.mkdirSync(decodeURIComponent(r)),r}getDirectories(t){return fs.readdirSync(t).reduce(((e,r)=>{const o=path.join(t,r);return this.isDirectory(o)&&e.push(o),e}),[])}async getFiles({target:t=".",extensions:e=[],wildcard:r=!1,isAdmin:o=!1,type:i}={}){const s=t;t=decodeURIComponent(t);let n=path.join(this.root,t);const a=e.map((t=>t.toLowerCase())),c=path.relative(this.root,n);c&&(c.startsWith("..")||path.isAbsolute(c))&&(n=this.root,t="");let h="";(r||fs.existsSync(n)&&!this.isDirectory(n))&&(h=path.basename(t),t=path.dirname(t),n=path.dirname(n));const l=Object.keys(this.configuration);l.sort(((t,e)=>{const r=e.split("/").length-t.split("/").length;return 0!==r?r:""===t?1:""===e?-1:t.compare(e)}));let p=l.find((t=>RegExp(`^${t}/`).test(`${s}/`)))||"";const m=this.configuration[p]||{private:!1,gridSize:null};if(!fs.existsSync(n)||m.private&&!o)throw new Error(`Directory ${t} does not exist or is not accessible in this storage location`);const d={target:encodeURL(t),private:m.private,gridSize:m.gridSize,dirs:[],privateDirs:[],files:[],extensions:e},u=fs.readdirSync(n).map((t=>path.join(n,t))).reduce(((e,s)=>{const n=path.basename(s);if(n.startsWith("."))return e;if(r&&!minimatch(n,h))return e;if(this.isDirectory(s)){const r=encodeURL(path.posix.join(t,n));if(r in this.configuration&&this.configuration[r].private){if(!o)return e;e.privateDirs.push(r)}return e.dirs.push(s),e}if("folder"===i)return e;const c=path.extname(n).toLowerCase();return(a.length&&a.includes(c)||!a.length&&""!==c)&&e.files.push(s),e}),d);return u.dirs=this.toClientPaths(u.dirs,this.root),u.files=this.toClientPaths(u.files,this.root),u}isDirectory(t){let e=null;try{e=fs.lstatSync(t)}catch(t){return!1}if(e.isDirectory())return!0;if(e.isSymbolicLink())try{return fs.statSync(t).isDirectory()}catch(t){return!1}return!1}async upload(t,{target:e,contentType:r,overwrite:o=!1}={}){if(this.root!==paths.data)throw new Error("You may not upload to this location");const i=path.join(this.root,e);if(!fs.existsSync(i))throw new Error(`Target directory ${i} does not exist.`);const s=path.relative(this.root,i);if(s&&(s.startsWith("..")||path.isAbsolute(s)))throw new Error("You may not upload files to this location.");const n=path.join(i,t.name),a=this.toClientPath(n,this.root);if(fs.existsSync(n)){if(!o)throw new Error("You may not upload non-media files which would overwrite an existing file on the server.");const t=r??mime.lookup(n);if(!MEDIA_MIME_TYPES.includes(t))throw new Error(`You may not overwrite an existing file with a MIME type of ${t}.`)}return new Promise((r=>{t.mv(n,(o=>{const i=""!==e?e:"User Data storage";if(o)throw o.message=`Unable to place ${t.name} in ${i}`,o;const s=`${t.name} saved to ${i}`;logger.info(s),r({status:"success",message:s,path:a})}))}))}toClientPath(t,e){return LocalFileStorage.toClientPath(t,e)}static toClientPath(t,e){const r=Files.standardizePath(path.relative(e,t));try{return decodeURIComponent(r)!==r?r:encodeURL(r)}catch(t){return encodeURL(r)}}}