mirror of
https://github.com/IrosTheBeggar/mStream.git
synced 2025-10-27 07:31:02 +00:00
Merge 7c115b2976 into 7d40b034ed
This commit is contained in:
commit
f25139216f
@ -13,7 +13,8 @@
|
||||
"start": "node cli-boot-wrapper.js",
|
||||
"pack": "electron-builder --dir",
|
||||
"dist": "electron-builder",
|
||||
"wizard": "npm install --only=prod && node cli-boot-wrapper.js"
|
||||
"wizard": "npm install --only=prod && node cli-boot-wrapper.js",
|
||||
"dev": "node --watch-path=./src cli-boot-wrapper.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@ -44,7 +44,8 @@ exports.setup = (mstream) => {
|
||||
req.user = {
|
||||
vpaths: Object.keys(config.program.folders),
|
||||
username: 'mstream-user',
|
||||
admin: true
|
||||
admin: true,
|
||||
editFilePrivileges: false
|
||||
};
|
||||
|
||||
return next();
|
||||
|
||||
@ -3,7 +3,6 @@ const fs = require('fs').promises;
|
||||
const fsOld = require('fs');
|
||||
const busboy = require("busboy");
|
||||
const Joi = require('joi');
|
||||
const mkdirp = require('make-dir');
|
||||
const winston = require('winston');
|
||||
const fileExplorer = require('../util/file-explorer');
|
||||
const vpath = require('../util/vpath');
|
||||
@ -42,12 +41,6 @@ exports.setup = (mstream) => {
|
||||
// Get vPath Info
|
||||
const pathInfo = vpath.getVPathInfo(value.directory, req.user);
|
||||
|
||||
// Do not allow browsing outside the directory
|
||||
if (pathInfo.fullPath.substring(0, pathInfo.basePath.length) !== pathInfo.basePath) {
|
||||
winston.warn(`user '${req.user.username}' attempted to access a directory they don't have access to: ${pathInfo.fullPath}`)
|
||||
throw new Error('Access to directory not allowed');
|
||||
}
|
||||
|
||||
// get directory contents
|
||||
const folderContents = await fileExplorer.getDirectoryContents(pathInfo.fullPath, config.program.supportedAudioFiles, value.sort, value.pullMetadata, value.directory, req.user);
|
||||
|
||||
@ -91,21 +84,15 @@ exports.setup = (mstream) => {
|
||||
// Get vPath Info
|
||||
const pathInfo = vpath.getVPathInfo(req.body.directory, req.user);
|
||||
|
||||
// Do not allow browsing outside the directory
|
||||
if (pathInfo.fullPath.substring(0, pathInfo.basePath.length) !== pathInfo.basePath) {
|
||||
winston.warn(`user '${req.user.username}' attempted to access a directory they don't have access to: ${pathInfo.fullPath}`)
|
||||
throw new Error('Access to directory not allowed');
|
||||
}
|
||||
|
||||
res.json(await recursiveFileScan(pathInfo.fullPath, [], pathInfo.relativePath, pathInfo.vpath));
|
||||
});
|
||||
|
||||
mstream.post('/api/v1/file-explorer/upload', (req, res) => {
|
||||
mstream.post('/api/v1/file-explorer/upload', async (req, res) => {
|
||||
if (config.program.noUpload === true) { throw new WebError('Uploading Disabled'); }
|
||||
if (!req.headers['data-location']) { throw new WebError('No Location Provided', 403); }
|
||||
|
||||
const pathInfo = vpath.getVPathInfo(decodeURI(req.headers['data-location']), req.user);
|
||||
mkdirp.sync(pathInfo.fullPath);
|
||||
await fs.mkdir(pathInfo.fullPath, { recursive: true });
|
||||
|
||||
const bb = busboy({ headers: req.headers });
|
||||
bb.on('file', (fieldname, file, info) => {
|
||||
@ -134,4 +121,67 @@ exports.setup = (mstream) => {
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
mstream.post("/api/v1/file-explorer/rename", async (req, res) => {
|
||||
const schema = Joi.object({
|
||||
path: Joi.string().required(),
|
||||
newName: Joi.string().required()
|
||||
});
|
||||
joiValidate(schema, req.body);
|
||||
|
||||
const pathInfo = vpath.getVPathInfo(req.body.path, req.user);
|
||||
|
||||
// check permissions
|
||||
if (config.program.folders[pathInfo.vpath].edit.rename !== true) { throw new WebError('Rename Disabled'); }
|
||||
if (req.user.editFilePrivileges.rename !== true) { throw new WebError('Rename Disabled'); }
|
||||
|
||||
// check if path exists
|
||||
await fs.stat(pathInfo.fullPath);
|
||||
|
||||
// rename
|
||||
const newt = path.join(path.dirname(pathInfo.fullPath), req.body.newName);
|
||||
await fs.rename(pathInfo.fullPath, newt);
|
||||
|
||||
res.json({});
|
||||
});
|
||||
|
||||
mstream.delete("/api/v1/file-explorer/delete", async (req, res) => {
|
||||
const schema = Joi.object({ path: Joi.string().required() });
|
||||
joiValidate(schema, req.body);
|
||||
|
||||
const pathInfo = vpath.getVPathInfo(req.body.path, req.user);
|
||||
|
||||
// check permissions
|
||||
if (config.program.folders[pathInfo.vpath].edit.delete !== true) { throw new WebError('Rename Disabled'); }
|
||||
if (req.user.editFilePrivileges.delete !== true) { throw new WebError('Rename Disabled'); }
|
||||
|
||||
// check if path exists
|
||||
const stat = await fs.stat(pathInfo.fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
await fs.rm(pathInfo.fullPath);
|
||||
} else {
|
||||
await fs.unlink(pathInfo.fullPath);
|
||||
}
|
||||
|
||||
// delete
|
||||
res.json({});
|
||||
});
|
||||
|
||||
// mstream.post("/api/v1/file-explorer/move", async (req, res) => {
|
||||
|
||||
// });
|
||||
|
||||
mstream.post("/api/v1/file-explorer/mkdir", async (req, res) => {
|
||||
const schema = Joi.object({ path: Joi.string().required(), newDirName: Joi.string().required() });
|
||||
joiValidate(schema, req.body);
|
||||
|
||||
const pathInfo = vpath.getVPathInfo(req.body.path, req.user);
|
||||
|
||||
// check permissions
|
||||
if (config.program.folders[pathInfo.vpath].edit.mkdir !== true) { throw new WebError('Rename Disabled'); }
|
||||
if (req.user.editFilePrivileges.mkdir !== true) { throw new WebError('Rename Disabled'); }
|
||||
|
||||
await fs.mkdir(pathInfo.fullPath, { recursive: true });
|
||||
});
|
||||
}
|
||||
@ -54,6 +54,13 @@ const federationOptions = Joi.object({
|
||||
federateUsersMode: Joi.boolean().default(false),
|
||||
});
|
||||
|
||||
const editFileOptions = Joi.object({
|
||||
rename: Joi.boolean().default(false),
|
||||
move: Joi.boolean().default(false),
|
||||
delete: Joi.boolean().default(false),
|
||||
mkdir: Joi.boolean().default(false),
|
||||
});
|
||||
|
||||
const schema = Joi.object({
|
||||
address: Joi.string().ip({ cidr: 'forbidden' }).default('::'),
|
||||
port: Joi.number().default(3000),
|
||||
@ -81,11 +88,13 @@ const schema = Joi.object({
|
||||
Joi.object({
|
||||
root: Joi.string().required(),
|
||||
type: Joi.string().valid('music', 'audio-books').default('music'),
|
||||
edit: editFileOptions.default(editFileOptions.validate({}).value),
|
||||
})
|
||||
).default({}),
|
||||
users: Joi.object().pattern(
|
||||
Joi.string(),
|
||||
Joi.object({
|
||||
editFilePrivileges: Joi.boolean().default(false),
|
||||
password: Joi.string().required(),
|
||||
admin: Joi.boolean().default(false),
|
||||
salt: Joi.string().required(),
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const path = require('path');
|
||||
const config = require('../state/config');
|
||||
const winston = require('winston');
|
||||
|
||||
exports.getVPathInfo = (url, user) => {
|
||||
if (!config.program) { throw new Error('Not Configured'); }
|
||||
@ -17,10 +18,18 @@ exports.getVPathInfo = (url, user) => {
|
||||
}
|
||||
|
||||
const baseDir = config.program.folders[vpath].root;
|
||||
const fullPath = path.join(baseDir, path.relative(vpath, url));
|
||||
|
||||
// Do not allow browsing outside the directory
|
||||
if (fullPath.substring(0, baseDir.length) !== baseDir) {
|
||||
winston.warn(`user '${user.username}' attempted to access a directory they don't have access to: ${fullPath}`)
|
||||
throw new Error('Access to directory not allowed');
|
||||
}
|
||||
|
||||
return {
|
||||
vpath: vpath,
|
||||
basePath: baseDir,
|
||||
relativePath: path.relative(vpath, url),
|
||||
fullPath: path.join(baseDir, path.relative(vpath, url))
|
||||
fullPath: fullPath
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user