mirror of
https://github.com/IrosTheBeggar/mStream.git
synced 2025-10-27 07:31:02 +00:00
Working file explorer and downloads
This commit is contained in:
parent
384e56c6ee
commit
aa371ae26c
@ -2,74 +2,62 @@ const fs = require('fs'); // File System
|
||||
const fe = require('path');
|
||||
|
||||
|
||||
exports.setup = function(args, rootDir){
|
||||
let loadJson;
|
||||
try{
|
||||
if(fe.extname(args[args.length-1]) === '.json' && fs.statSync(args[args.length-1]).isFile()){
|
||||
loadJson = JSON.parse(fs.readFileSync(args[args.length-1], 'utf8'));
|
||||
}else{
|
||||
return require('./configure-commander.js').setup(args);
|
||||
}
|
||||
}catch(error){
|
||||
console.log(error);
|
||||
return {error:"Failed to parse JSON file"};
|
||||
}
|
||||
|
||||
|
||||
exports.setup = function(loadJson, rootDir){
|
||||
// TODO REDO THIS WHOLE THING
|
||||
var errorArray = [];
|
||||
|
||||
// Check for port
|
||||
if(!loadJson.port){
|
||||
loadJson.port = 5050;
|
||||
}
|
||||
if(!isInt(loadJson.port) || loadJson.port < 0 || loadJson.port > 65535){
|
||||
return {error:"BAD PORT, WILL ABORT"};
|
||||
|
||||
// Check for UI
|
||||
if(!loadJson.userinterface){
|
||||
loadJson.userinterface = 'public';
|
||||
}
|
||||
|
||||
// TODO: Add comprehensive DB checks
|
||||
if(!loadJson.database_plugin){
|
||||
return {error:"Please Configure DB"};
|
||||
if(!loadJson.database_plugin || !loadJson.database_plugin.dbPath){
|
||||
loadJson.database_plugin.dbPath = 'mstream.db';
|
||||
}
|
||||
|
||||
|
||||
if(loadJson.userinterface){
|
||||
if(!fs.statSync( fe.join(rootDir, loadJson.userinterface) ).isDirectory()){
|
||||
return {error:"Could not find userinterface"};
|
||||
if(!loadJson.folders || typeof loadJson.folders !== 'object'){
|
||||
errorArray.push('No Folders');
|
||||
loadJson.error = errorArray;
|
||||
return loadJson;
|
||||
}
|
||||
|
||||
for(let folder in loadJson.folders){
|
||||
if(typeof loadJson.folders[folder] === 'string'){
|
||||
let folderString = loadJson.folders[folder];
|
||||
loadJson.folders[folder] = {
|
||||
root: folderString
|
||||
};
|
||||
}
|
||||
|
||||
// Verify path is real
|
||||
if(!loadJson.folders[folder].root || !fs.statSync( loadJson.folders[folder].root).isDirectory()){
|
||||
errorArray.push(loadJson.folders[folder].root + ' is not a real path');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Normalize for all OS
|
||||
// Make sure it's a directory
|
||||
// Loop through and makeure all user Dirs are real
|
||||
if(loadJson.users){
|
||||
for (let username in loadJson.users) {
|
||||
// TODO: Check usernames for forbidden chars
|
||||
|
||||
// TODO: Make sure all music directories are unique
|
||||
// TODO: No subsets/super-sets/duplicates
|
||||
if( !fs.statSync( loadJson.users[username].musicDir ).isDirectory()){
|
||||
return {error:loadJson.users[username].username + " music directory could not be found"};
|
||||
}
|
||||
}
|
||||
console.log(loadJson.users)
|
||||
if(!loadJson.users || typeof loadJson.users !== 'object'){
|
||||
errorArray.push('No Users');
|
||||
loadJson.error = errorArray;
|
||||
return loadJson;
|
||||
}
|
||||
|
||||
// TODO: Preform a full range of checks
|
||||
if(loadJson.tunnel){
|
||||
if(loadJson.tunnel.refreshInterval && !isInt(loadJson.tunnel.refreshInterval)){
|
||||
return {error:"Refresh interval must be an integer"};
|
||||
for(let user in loadJson.users){
|
||||
if(typeof loadJson.users[user].vpaths === 'string'){
|
||||
loadJson.users[user].vpaths = [loadJson.users[user].vpaths];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(errorArray.length > 0){
|
||||
loadJson.error = errorArray;
|
||||
}
|
||||
// Export JSON
|
||||
return loadJson;
|
||||
}
|
||||
|
||||
|
||||
function isInt(value) {
|
||||
if (isNaN(value)) {
|
||||
return false;
|
||||
}
|
||||
var x = parseFloat(value);
|
||||
return (x | 0) === x;
|
||||
}
|
||||
|
||||
// TODO: This should sum up all errors before returing to user
|
||||
|
||||
@ -1,337 +0,0 @@
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
const fe = require('path');
|
||||
const crypto = require('crypto');
|
||||
|
||||
var db;
|
||||
|
||||
|
||||
function getFileType(filename){
|
||||
return filename.split(".").pop();
|
||||
}
|
||||
|
||||
exports.getNumberOfFiles = function(username, callback){
|
||||
db.get("SELECT Count(*) FROM items WHERE user = ?;", [username], function(err, row){
|
||||
if(err){
|
||||
console.log('SQL ERROR!');
|
||||
console.log(err);
|
||||
}
|
||||
callback(row['Count(*)']);
|
||||
});
|
||||
}
|
||||
|
||||
exports.setup = function (mstream, dbSettings){
|
||||
db = new sqlite3.Database(dbSettings.dbPath);
|
||||
|
||||
// Setup DB
|
||||
// TODO: Add the following cols
|
||||
// rating
|
||||
var itemsSql = "CREATE TABLE IF NOT EXISTS items ( \
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT, \
|
||||
title varchar DEFAULT NULL, \
|
||||
artist varchar DEFAULT NULL, \
|
||||
year int DEFAULT NULL, \
|
||||
album varchar DEFAULT NULL, \
|
||||
path TEXT NOT NULL, \
|
||||
format VARCHAR, \
|
||||
track INTEGER, \
|
||||
disk INTEGER, \
|
||||
user VARCHAR, \
|
||||
filesize INTEGER, \
|
||||
file_created_date INTEGER, \
|
||||
file_modified_date INTEGER, \
|
||||
hash VARCHAR, \
|
||||
album_art_file TEXT, \
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP \
|
||||
);";
|
||||
|
||||
var playlistSql = "CREATE TABLE IF NOT EXISTS mstream_playlists ( \
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT, \
|
||||
playlist_name varchar, \
|
||||
filepath varchar, \
|
||||
hide int DEFAULT 0, \
|
||||
user VARCHAR, \
|
||||
created DATETIME DEFAULT CURRENT_TIMESTAMP \
|
||||
);";
|
||||
|
||||
// Create tables
|
||||
db.run(itemsSql, function() {});
|
||||
db.run(playlistSql, function() {});
|
||||
|
||||
|
||||
// Metadata lookup
|
||||
mstream.post('/db/metadata', function (req, res){
|
||||
var relativePath = req.body.filepath;
|
||||
var fullpath = fe.join(req.user.musicDir, relativePath);
|
||||
|
||||
// Find entry matching path
|
||||
db.get("SELECT * FROM items WHERE path = ?", [fullpath], function(err, row){
|
||||
if(err || !row){
|
||||
res.status(500).json({ error: 'DB Error' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Return metadata
|
||||
res.json({
|
||||
"filepath":relativePath,
|
||||
"metadata":{
|
||||
"artist":row.artist,
|
||||
"hash": row.hash,
|
||||
"album":row.album,
|
||||
"track":row.track,
|
||||
"title":row.title,
|
||||
"year":row.year,
|
||||
"album-art":row.album_art_file
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// TODO: This needs to be tested to see if it works on extra large playlists (think thousands of entries)
|
||||
// TODO: Ban saving playlists that are > 10,000 items long
|
||||
mstream.post('/playlist/save', function (req, res){
|
||||
var title = req.body.title;
|
||||
var songs = req.body.songs;
|
||||
|
||||
// Check if this playlist already exists
|
||||
db.all("SELECT id FROM mstream_playlists WHERE playlist_name = ? AND user = ?;", [title, req.user.username], function(err, rows) {
|
||||
db.serialize(function() {
|
||||
// We need to delete anys existing entries
|
||||
if(rows && rows.length > 0){
|
||||
db.run("DELETE FROM mstream_playlists WHERE playlist_name = ? AND user = ?;", [title, req.user.username]);
|
||||
}
|
||||
|
||||
// Now we add the new entries
|
||||
var sql2 = "insert into mstream_playlists (playlist_name, filepath, user) values ";
|
||||
var sqlParser = [];
|
||||
|
||||
while(songs.length > 0) {
|
||||
var song = songs.shift();
|
||||
|
||||
sql2 += "(?, ?, ?), ";
|
||||
sqlParser.push(title);
|
||||
sqlParser.push( fe.join(req.user.musicDir, song) );
|
||||
sqlParser.push( req.user.username );
|
||||
}
|
||||
|
||||
sql2 = sql2.slice(0, -2);
|
||||
sql2 += ";";
|
||||
|
||||
db.run(sql2, sqlParser, function(){
|
||||
res.json({success: true});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Attach API calls to functions
|
||||
mstream.get('/playlist/getall', function (req, res){
|
||||
// TODO: In V2 we need to change this to ignore hidden playlists
|
||||
// TODO: db.all("SELECT DISTINCT playlist_name FROM mstream_playlists WHERE hide=0;", function(err, rows){
|
||||
db.all("SELECT DISTINCT playlist_name FROM mstream_playlists WHERE user = ?", [req.user.username], function(err, rows){
|
||||
var playlists = [];
|
||||
|
||||
// loop through files
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
if(rows[i].playlist_name){
|
||||
playlists.push({name: rows[i].playlist_name});
|
||||
}
|
||||
}
|
||||
|
||||
res.json(playlists);
|
||||
});
|
||||
});
|
||||
|
||||
mstream.post('/playlist/load', function (req, res){
|
||||
var playlist = req.body.playlistname;
|
||||
|
||||
db.all("SELECT * FROM mstream_playlists WHERE playlist_name = ? AND user = ? ORDER BY id COLLATE NOCASE ASC", [playlist, req.user.username], function(err, rows){
|
||||
var returnThis = [];
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
|
||||
// var tempName = rows[i].filepath.split('/').slice(-1)[0];
|
||||
var tempName = fe.basename(rows[i].filepath);
|
||||
var extension = getFileType(rows[i].filepath);
|
||||
var filepath = fe.relative(req.user.musicDir, rows[i].filepath);
|
||||
filepath = filepath.replace(/\\/g, '/');
|
||||
|
||||
returnThis.push({filepath: filepath, metadata:'' });
|
||||
}
|
||||
|
||||
res.json(returnThis);
|
||||
});
|
||||
});
|
||||
mstream.post('/playlist/delete', function(req, res){
|
||||
var playlistname = req.body.playlistname;
|
||||
|
||||
// Handle a soft delete
|
||||
if(req.body.hide && parseInt(req.body.hide) == true ){
|
||||
db.run("UPDATE mstream_playlists SET hide = 1 WHERE playlist_name = ? AND user = ?;", [playlistname, req.user.username], function(){
|
||||
res.json({success: true});
|
||||
});
|
||||
}else{
|
||||
// Delete playlist from DB
|
||||
db.run("DELETE FROM mstream_playlists WHERE playlist_name = ? AND user = ?;", [playlistname, req.user.username], function(){
|
||||
res.json({success: true});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mstream.post('/db/search', function(req, res){
|
||||
var searchTerm = "%" + req.body.search + "%" ;
|
||||
|
||||
var returnThis = {"albums":[], "artists":[]};
|
||||
|
||||
// TODO: Combine SQL calls into one
|
||||
db.serialize(function() {
|
||||
|
||||
var sqlAlbum = "SELECT DISTINCT album FROM items WHERE items.album LIKE ? AND user = ? ORDER BY album COLLATE NOCASE ASC;";
|
||||
db.all(sqlAlbum, [searchTerm, req.user.username], function(err, rows) {
|
||||
if(err){
|
||||
res.status(500).json({ error: 'DB Error' });
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
if(rows[i].album){
|
||||
returnThis.albums.push(rows[i].album);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var sqlArtist = "SELECT DISTINCT artist FROM items WHERE items.artist LIKE ? AND user = ? ORDER BY artist COLLATE NOCASE ASC;";
|
||||
db.all(sqlArtist, [searchTerm, req.user.username], function(err, rows) {
|
||||
if(err){
|
||||
res.status(500).json({ error: 'DB Error' });
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
if(rows[i].artist){
|
||||
returnThis.artists.push(rows[i].artist);
|
||||
}
|
||||
}
|
||||
|
||||
res.json(returnThis);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
mstream.get('/db/artists', function (req, res) {
|
||||
var artists = {"artists":[]};
|
||||
|
||||
var sql = "SELECT DISTINCT artist FROM items WHERE user = ? ORDER BY artist COLLATE NOCASE ASC;";
|
||||
db.all(sql, [req.user.username], function(err, rows) {
|
||||
if(err){
|
||||
res.status(500).json({ error: 'DB Error' });
|
||||
return;
|
||||
}
|
||||
|
||||
var returnArray = [];
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
if(rows[i].artist){
|
||||
artists.artists.push(rows[i].artist);
|
||||
}
|
||||
}
|
||||
|
||||
res.json(artists);
|
||||
});
|
||||
});
|
||||
|
||||
mstream.post('/db/artists-albums', function (req, res) {
|
||||
var albums = {"albums":[]};
|
||||
|
||||
// TODO: Make a list of all songs without null albums and add them to the response
|
||||
var sql = "SELECT album, album_art_file FROM items WHERE artist = ? AND user = ? GROUP BY album ORDER BY album COLLATE NOCASE ASC;";
|
||||
var searchTerms = [];
|
||||
searchTerms.push(req.body.artist);
|
||||
searchTerms.push(req.user.username);
|
||||
|
||||
db.all(sql, searchTerms, function(err, rows) {
|
||||
if(err){
|
||||
res.status(500).json({ error: 'DB Error' });
|
||||
return;
|
||||
}
|
||||
|
||||
for (let row of rows){
|
||||
if(row.album){
|
||||
albums.albums.push({
|
||||
name: row.album,
|
||||
album_art_file: row.album_art_file
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
res.json(albums);
|
||||
});
|
||||
});
|
||||
|
||||
mstream.get('/db/albums', function (req, res) {
|
||||
var albums = {"albums":[]};
|
||||
|
||||
// TODO: Seperate albums with same name by different artists
|
||||
var sql = "SELECT album, album_art_file FROM items WHERE user = ? GROUP BY album ORDER BY album COLLATE NOCASE ASC;";
|
||||
db.all(sql, req.user.username, function(err, rows) {
|
||||
if(err){
|
||||
res.status(500).json({ error: 'DB Error' });
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
if(rows[i].album){
|
||||
albums.albums.push({
|
||||
name: rows[i].album,
|
||||
album_art_file: rows[i].album_art_file
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
res.json(albums);
|
||||
});
|
||||
});
|
||||
|
||||
mstream.post('/db/album-songs', function (req, res) {
|
||||
var sql = "SELECT title, album_art_file, artist, album, hash, format, year, cast(path as TEXT), track FROM items WHERE album = ? AND user = ? ORDER BY track ASC;";
|
||||
var searchTerms = [];
|
||||
searchTerms.push(req.body.album);
|
||||
searchTerms.push(req.user.username);
|
||||
|
||||
db.all(sql, searchTerms, function(err, rows) {
|
||||
if(err){
|
||||
res.status(500).json({ error: 'DB Error' });
|
||||
return;
|
||||
}
|
||||
|
||||
var songs = [];
|
||||
// Format data for API
|
||||
for(var i in rows ){
|
||||
var path = String(rows[i]['cast(path as TEXT)']);
|
||||
var relativePath = fe.relative(req.user.musicDir, path);
|
||||
relativePath = relativePath.replace(/\\/g, '/');
|
||||
|
||||
songs.push({
|
||||
"filepath": relativePath,
|
||||
"metadata": {
|
||||
"hash": rows[i].hash,
|
||||
"artist": rows[i].artist,
|
||||
"album": rows[i].album,
|
||||
"track": rows[i].track,
|
||||
"title": rows[i].title,
|
||||
"year": rows[i].year,
|
||||
"album-art": rows[i].album_art_file,
|
||||
"filename": fe.basename( path )
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
res.json(songs);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
@ -1,104 +0,0 @@
|
||||
// functions that store data into the SQLite DB
|
||||
// These functions will take in JSON arrays of song data and then save that dat to the DB
|
||||
var sqlite3;
|
||||
|
||||
try{
|
||||
sqlite3 = require('sqlite3').verbose();
|
||||
}catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
var db;
|
||||
|
||||
exports.setup = function(dbPath){
|
||||
try{
|
||||
db = new sqlite3.Database(dbPath);
|
||||
}catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
exports.getUserFiles = function(thisUser, callback){
|
||||
db.all("SELECT path, file_modified_date FROM items WHERE user = ?;", thisUser.username, function(err, rows){
|
||||
// Format results
|
||||
if(!rows){
|
||||
rows = [];
|
||||
}
|
||||
// callback function
|
||||
callback(rows);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param arrayOfSongs
|
||||
* @param username
|
||||
* @return Promise
|
||||
*/
|
||||
exports.insertEntries = function(arrayOfSongs, username){
|
||||
var sql2 = "insert into items (title,artist,year,album,path,format, track, disk, user, filesize, file_modified_date, file_created_date, hash, album_art_file) values ";
|
||||
var sqlParser = [];
|
||||
|
||||
while(arrayOfSongs.length > 0) {
|
||||
var song = arrayOfSongs.pop();
|
||||
|
||||
var songTitle = null;
|
||||
var songYear = null;
|
||||
var songAlbum = null;
|
||||
var artistString = null;
|
||||
|
||||
if(song.artist && song.artist.length > 0){
|
||||
artistString = song.artist;
|
||||
}
|
||||
if(song.title && song.title.length > 0){
|
||||
songTitle = song.title;
|
||||
}
|
||||
if(song.year){
|
||||
songYear = song.year;
|
||||
}
|
||||
if(song.album && song.album.length > 0){
|
||||
songAlbum = song.album;
|
||||
}
|
||||
|
||||
sql2 += "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?), ";
|
||||
sqlParser.push(songTitle);
|
||||
sqlParser.push(artistString);
|
||||
sqlParser.push(songYear);
|
||||
sqlParser.push(songAlbum);
|
||||
sqlParser.push(song.filePath);
|
||||
sqlParser.push(song.format);
|
||||
sqlParser.push(song.track.no);
|
||||
sqlParser.push(song.disk.no);
|
||||
sqlParser.push(username);
|
||||
sqlParser.push(song.filesize);
|
||||
sqlParser.push(song.modified);
|
||||
sqlParser.push(song.created);
|
||||
sqlParser.push(song.hash);
|
||||
sqlParser.push(song.albumArtFilename);
|
||||
}
|
||||
|
||||
sql2 = sql2.slice(0, -2);
|
||||
sql2 += ";";
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
db.run(sql2, sqlParser, function(err) {
|
||||
if(err)
|
||||
reject(err);
|
||||
else
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: Function that removes all files from the given DB
|
||||
exports.purgeDB = function(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
exports.deleteFile = function(path, user, callback){
|
||||
let sql = "DELETE FROM items WHERE path = ? AND user = ?;";
|
||||
db.run(sql, [path, user], function() {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
@ -23,10 +23,8 @@ exports.setup = function(mstream, program){
|
||||
//streaming magic
|
||||
archive.pipe(res);
|
||||
|
||||
var fileArray;
|
||||
|
||||
|
||||
// Get the POSTed files
|
||||
var fileArray;
|
||||
if(req.allowedFiles){
|
||||
fileArray = allowedFiles;
|
||||
}else{
|
||||
@ -35,10 +33,12 @@ exports.setup = function(mstream, program){
|
||||
|
||||
for(var i in fileArray) {
|
||||
// TODO: Confirm each item in posted data is a real file
|
||||
var fileString = fileArray[i];
|
||||
|
||||
// TODO: Add file by ataching user's musicdir to the relative directory supplied
|
||||
archive.file(fe.join( req.user.musicDir, fileString), { name: fe.basename(fileString) });
|
||||
let pathInfo = program.getVPathInfo(fileArray[i]);
|
||||
if(pathInfo == false){
|
||||
console.log('Bad Path');
|
||||
continue;
|
||||
}
|
||||
archive.file(pathInfo.fullPath, { name: fe.basename(fileArray[i]) });
|
||||
}
|
||||
|
||||
archive.finalize();
|
||||
|
||||
@ -9,14 +9,39 @@ exports.setup = function(mstream, program){
|
||||
var directories = [];
|
||||
var filesArray = [];
|
||||
|
||||
var directory = '';
|
||||
if(req.body.dir){
|
||||
directory = req.body.dir;
|
||||
// Return vpaths if no path is given
|
||||
if(req.body.dir === '' || req.body.dir === '/' ){
|
||||
for(let dir of req.user.vpaths){
|
||||
directories.push({
|
||||
type:"directory",
|
||||
name: dir
|
||||
})
|
||||
}
|
||||
return res.json( { path: '/', contents: directories} );
|
||||
}
|
||||
|
||||
var path = fe.join(req.user.musicDir, directory);
|
||||
var directory = req.body.dir;
|
||||
console.log(directory)
|
||||
|
||||
|
||||
|
||||
let pathInfo = program.getVPathInfo(directory);
|
||||
console.log(pathInfo)
|
||||
if(pathInfo == false){
|
||||
res.status(500).json({ error: 'Could not find file' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the user has access to the given vpath and that the vapth exists
|
||||
if(!req.user.vpaths.includes(pathInfo.vpath) ){
|
||||
res.status(500).json({ error: 'Access Denied' });
|
||||
return;
|
||||
}
|
||||
|
||||
var path = pathInfo.fullPath;
|
||||
|
||||
// Make sure it's a directory
|
||||
if(!fs.statSync( path).isDirectory()){
|
||||
if(!fs.statSync(path).isDirectory()){
|
||||
res.status(500).json({ error: 'Not a directory' });
|
||||
return;
|
||||
}
|
||||
|
||||
109
modules/login.js
109
modules/login.js
@ -1,8 +1,10 @@
|
||||
exports.setup = function(mstream, program, express){
|
||||
const jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
|
||||
const uuidV4 = require('uuid/v4');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// Convenience variable
|
||||
var Users = program.users;
|
||||
|
||||
// Crypto Config
|
||||
var hashConfig = {
|
||||
// size of the generated hash
|
||||
@ -14,64 +16,37 @@ exports.setup = function(mstream, program, express){
|
||||
encoding : 'base64'
|
||||
};
|
||||
|
||||
// TODO: Add New user functionality
|
||||
// Check for root user and password
|
||||
// Add credentials to user array
|
||||
// TODO: Need a way to store and use already hashed passwords
|
||||
|
||||
|
||||
// TODO: Add/delete user functionality
|
||||
// TODO: password change function
|
||||
mstream.post('/change-password-request', function (req, res) {
|
||||
// Get email address from request
|
||||
// validate email against user array
|
||||
// Generate change password token
|
||||
// Invalidate all other change password tokens
|
||||
// Email the user the token
|
||||
|
||||
res.status(500).json( {error: 'Coming Soon'} );
|
||||
});
|
||||
|
||||
mstream.post('/change-password', function (req, res){
|
||||
// Check token
|
||||
// Get new password
|
||||
// Hash password and update user array
|
||||
|
||||
res.status(500).json( {error: 'Coming Soon'} );
|
||||
});
|
||||
|
||||
mstream.post('/sunset-user', function(req,res){
|
||||
// Removes all user info
|
||||
res.status(500).json( {error: 'Coming Soon'} );
|
||||
});
|
||||
|
||||
mstream.post('/add-user', function(req,res){
|
||||
// Add a user
|
||||
res.status(500).json( {error: 'Coming Soon'} );
|
||||
});
|
||||
|
||||
|
||||
// Create the user array
|
||||
var Users = program.users;
|
||||
var permissionsMap = {};
|
||||
// mstream.post('/change-password-request', function (req, res) {
|
||||
// // Get email address from request
|
||||
// // validate email against user array
|
||||
// // Generate change password token
|
||||
// // Invalidate all other change password tokens
|
||||
// // Email the user the token
|
||||
// res.status(500).json( {error: 'Coming Soon'} );
|
||||
// });
|
||||
// mstream.post('/change-password', function (req, res){
|
||||
// // Check token
|
||||
// // Get new password
|
||||
// // Hash password and update user array
|
||||
// res.status(500).json( {error: 'Coming Soon'} );
|
||||
// });
|
||||
// mstream.post('/sunset-user', function(req,res){
|
||||
// // Removes all user info
|
||||
// res.status(500).json( {error: 'Coming Soon'} );
|
||||
// });
|
||||
// mstream.post('/add-user', function(req,res){
|
||||
// // Add a user
|
||||
// res.status(500).json( {error: 'Coming Soon'} );
|
||||
// });
|
||||
|
||||
// Loop through users and setup passwords
|
||||
for (let username in Users) {
|
||||
// Setup user password
|
||||
generateSaltedPassword(username, Users[username]["password"]);
|
||||
|
||||
// If dir has not been added yet
|
||||
if ( !(Users[username].musicDir in permissionsMap) ){
|
||||
// Generate unique vPath if necessary
|
||||
// The best way is to store the vPath in the JSON file
|
||||
if(!Users[username].vPath){
|
||||
Users[username].vPath = uuidV4();
|
||||
}
|
||||
|
||||
// Add to permissionsMap
|
||||
permissionsMap[Users[username].musicDir] = Users[username].vPath;
|
||||
}else{
|
||||
Users[username].vPath = permissionsMap[Users[username].musicDir];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -104,10 +79,6 @@ exports.setup = function(mstream, program, express){
|
||||
res.status(598).json({error:'Access Denied'});
|
||||
});
|
||||
|
||||
mstream.get('/guest-access-denied', function (req, res) {
|
||||
res.status(597).json({error:'Access Denied'});
|
||||
});
|
||||
|
||||
// Authenticate User
|
||||
mstream.post('/login', function(req, res) {
|
||||
if(!req.body.username || !req.body.password){
|
||||
@ -130,14 +101,10 @@ exports.setup = function(mstream, program, express){
|
||||
return res.redirect('/login-failed');
|
||||
}
|
||||
|
||||
var vPath = Users[username].vPath;
|
||||
|
||||
// return the information including token as JSON
|
||||
res.json(
|
||||
{
|
||||
success: true,
|
||||
message: 'Welcome To mStream',
|
||||
vPath: vPath,
|
||||
vpaths: Users[username].vpaths,
|
||||
token: jwt.sign({username: username}, program.secret) // Make the token
|
||||
}
|
||||
);
|
||||
@ -162,12 +129,8 @@ exports.setup = function(mstream, program, express){
|
||||
// User may access those files and no others
|
||||
if(decoded.shareToken && decoded.shareToken === true){
|
||||
// We limit the endpoints to download and anythign in the allowedFiles array
|
||||
// TODO: There's gotta be a better way to handle vpaths
|
||||
// TODO: Add vpath to allowedFiles when it's created ???
|
||||
|
||||
// TODO: fix this hacky shit. vPAths aren't gauranteed to be 38 chars long
|
||||
if(req.path !== '/download' && decoded.allowedFiles.indexOf(decodeURIComponent(req.path.substring(38))) === -1){ // The substring is to cut out the vPath
|
||||
return res.redirect('/guest-access-denied');
|
||||
if(req.path !== '/download' && decoded.allowedFiles.indexOf(decodeURIComponent(req.path)) === -1){
|
||||
return res.redirect('/access-denied');
|
||||
}
|
||||
req.allowedFiles = decoded.allowedFiles;
|
||||
next();
|
||||
@ -176,20 +139,14 @@ exports.setup = function(mstream, program, express){
|
||||
|
||||
// Check for any hardcoded restrictions baked right into token
|
||||
if(decoded.restrictedFunctions && decoded.restrictedFunctions.indexOf(req.path) != -1){
|
||||
return res.redirect('/guest-access-denied');
|
||||
return res.redirect('/access-denied');
|
||||
}
|
||||
|
||||
// TODO: Verify that users in token exist and vPath matches
|
||||
// TODO: Longterm goal - use vPath from request variable instead of having the user manually add it
|
||||
// Setup User variable for api endpoints to access
|
||||
req.user = Users[decoded.username];
|
||||
req.user.username = decoded.username;
|
||||
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
// Setup Music Dirs here so they are protected by middleware
|
||||
for (var key in permissionsMap) {
|
||||
mstream.use( '/' + permissionsMap[key] + '/' , express.static( key ));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,40 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
const fe = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// Check if we are in an electron enviroment
|
||||
if(process.versions['electron']){
|
||||
// off to a seperate electron boot enviroment
|
||||
require('./mstream-electron.js');
|
||||
}else{
|
||||
// Get the server config
|
||||
const program = require('./modules/configure-json-file.js').setup(process.argv, __dirname);
|
||||
if(program.error){
|
||||
console.log(program.error);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
const serve = require('./mstream.js');
|
||||
serve.serveit(program);
|
||||
return;
|
||||
}
|
||||
|
||||
var program;
|
||||
try{
|
||||
console.log(fs.readFileSync(process.argv[process.argv.length-1], "utf8"))
|
||||
if(fe.extname(process.argv[process.argv.length-1]) === '.json' && fs.statSync(process.argv[process.argv.length-1]).isFile()){
|
||||
let loadJson = JSON.parse(fs.readFileSync(process.argv[process.argv.length-1], 'utf8'));
|
||||
program = require('./modules/configure-json-file.js').setup(loadJson, __dirname);
|
||||
}else{
|
||||
// User did not provide a JSON file
|
||||
program = require('./modules/configure-commander.js').setup(process.argv);
|
||||
}
|
||||
}catch(error){
|
||||
// This condition is hit only if the user entered a json file as an argument and the file did not exist or is invalid JSON
|
||||
console.log("ERROR: Failed to parse JSON file");
|
||||
console.log(error);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(program)
|
||||
|
||||
// Check for errors
|
||||
if(program.error){
|
||||
console.log(program)
|
||||
console.log(program.error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Boot the server
|
||||
const serve = require('./mstream.js');
|
||||
serve.serveit(program);
|
||||
|
||||
68
mstream.js
68
mstream.js
@ -69,6 +69,41 @@ exports.serveit = function (program, callback) {
|
||||
// Move to after login systm
|
||||
mstream.use( '/album-art', express.static(program.albumArtDir ));
|
||||
|
||||
// This is a convenience function. It gets the vPath from any url string
|
||||
program.getVPathInfo = function(url){
|
||||
// remove leading slashes
|
||||
if(url.charAt(0) === '/'){
|
||||
url = url.substr(1);
|
||||
}
|
||||
|
||||
var fileArray = url.split('/');
|
||||
console.log(fileArray)
|
||||
var vpath = fileArray.shift();
|
||||
console.log(fileArray)
|
||||
|
||||
// Make sure the path exists
|
||||
if(!program.folders[vpath]){
|
||||
return false;
|
||||
}
|
||||
var baseDir = program.folders[vpath].root;
|
||||
var newPath = '';
|
||||
for(var dir of fileArray){
|
||||
if(dir === ''){
|
||||
continue;
|
||||
}
|
||||
console.log(dir)
|
||||
newPath += dir + '/' ;
|
||||
}
|
||||
|
||||
console.log(newPath)
|
||||
var fullpath = fe.join( baseDir, newPath)
|
||||
return {
|
||||
vpath: vpath,
|
||||
basePath: baseDir,
|
||||
relativePath: newPath,
|
||||
fullPath: fullpath
|
||||
};
|
||||
}
|
||||
|
||||
// Setup Secret for JWT
|
||||
try{
|
||||
@ -101,27 +136,30 @@ exports.serveit = function (program, callback) {
|
||||
require('./modules/login.js').setup(mstream, program, express);
|
||||
program.auth = true;
|
||||
}else{
|
||||
// Store the vPath incase any of the plugins need it
|
||||
program.vPath = 'music-vpath';
|
||||
|
||||
program.users= {
|
||||
program.users = {
|
||||
"mstream-user":{
|
||||
musicDir: program.musicDir,
|
||||
vPath: program.vPath,
|
||||
username: "mstream-user"
|
||||
vpaths: [],
|
||||
username: "mstream-user",
|
||||
admin: true
|
||||
}
|
||||
}
|
||||
// Fill in the necessary data
|
||||
// Fill iin user vpaths
|
||||
for (var key in program.folders) {
|
||||
program.users['mstream-user'].vpaths.push(key);
|
||||
}
|
||||
|
||||
// Fill in the necessary middleware
|
||||
mstream.use(function(req, res, next) {
|
||||
req.user = {
|
||||
username:"mstream-user",
|
||||
musicDir:program.musicDir,
|
||||
vPath: program.vPath
|
||||
};
|
||||
req.user = program.users['mstream-user'];
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
mstream.use( '/' + program.vPath + '/' , express.static( program.musicDir ));
|
||||
// Setup all folders with express static
|
||||
for (var key in program.folders) {
|
||||
console.log(key)
|
||||
console.log(program.folders[key])
|
||||
mstream.use( '/' + key + '/' , express.static( program.folders[key].root ));
|
||||
}
|
||||
|
||||
// Test function
|
||||
@ -129,7 +167,7 @@ exports.serveit = function (program, callback) {
|
||||
mstream.get('/ping', function(req, res){
|
||||
// TODO: Guest status
|
||||
res.json({
|
||||
vPath: req.user.vPath,
|
||||
vpaths: req.user.vpaths,
|
||||
guest: false
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user