mirror of
https://github.com/IrosTheBeggar/mStream.git
synced 2025-10-27 07:31:02 +00:00
390 lines
12 KiB
JavaScript
390 lines
12 KiB
JavaScript
const fs = require("fs").promises;
|
|
const path = require("path");
|
|
const child = require('child_process');
|
|
const express = require('express');
|
|
const auth = require('./auth');
|
|
const config = require('../state/config');
|
|
const mStreamServer = require('../server');
|
|
const dbQueue = require('../db/task-queue');
|
|
const logger = require('../logger');
|
|
const db = require('../db/manager');
|
|
const syncthing = require('../state/syncthing');
|
|
|
|
exports.loadFile = async (file) => {
|
|
return JSON.parse(await fs.readFile(file, 'utf-8'));
|
|
}
|
|
|
|
exports.saveFile = async (saveData, file) => {
|
|
return await fs.writeFile(file, JSON.stringify(saveData, null, 2), 'utf8')
|
|
}
|
|
|
|
exports.addDirectory = async (directory, vpath, autoAccess, isAudioBooks, mstream) => {
|
|
// confirm directory is real
|
|
const stat = await fs.stat(directory);
|
|
if (!stat.isDirectory()) { throw `${directory} is not a directory` };
|
|
|
|
if (config.program.folders[vpath]) { throw `'${vpath}' is already loaded into memory`; }
|
|
|
|
// This extra step is so we can handle the process like a SQL transaction
|
|
// The new var is a copy so the original program isn't touched
|
|
// Once the file save is complete, the new user will be added
|
|
const memClone = JSON.parse(JSON.stringify(config.program.folders));
|
|
memClone[vpath] = { root: directory };
|
|
if (isAudioBooks) { memClone[vpath].type = 'audio-books'; }
|
|
|
|
// add directory to config file
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.folders = memClone;
|
|
if (autoAccess === true) {
|
|
const memCloneUsers = JSON.parse(JSON.stringify(config.program.users));
|
|
Object.values(memCloneUsers).forEach(user => {
|
|
user.vpaths.push(vpath);
|
|
});
|
|
loadConfig.users = memCloneUsers;
|
|
}
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
// add directory to program
|
|
config.program.folders[vpath] = memClone;
|
|
|
|
if (autoAccess === true) {
|
|
Object.values(config.program.users).forEach(user => {
|
|
user.vpaths.push(vpath);
|
|
});
|
|
}
|
|
|
|
// add directory to server routing
|
|
mstream.use(`/media/${vpath}/`, express.static(directory));
|
|
}
|
|
|
|
exports.removeDirectory = async (vpath) => {
|
|
if (!config.program.folders[vpath]) { throw `'${vpath}' not found`; }
|
|
|
|
const memCloneFolders = JSON.parse(JSON.stringify(config.program.folders));
|
|
delete memCloneFolders[vpath];
|
|
|
|
const memCloneUsers = JSON.parse(JSON.stringify(config.program.users));
|
|
Object.values(memCloneUsers).forEach(user => {
|
|
if (user.vpaths.includes(vpath)) {
|
|
user.vpaths.splice(user.vpaths.indexOf(vpath), 1);
|
|
}
|
|
});
|
|
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.folders = memCloneFolders;
|
|
loadConfig.users = memCloneUsers;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
db.getFileCollection().findAndRemove({ 'vpath': { '$eq': vpath } });
|
|
db.saveFilesDB();
|
|
|
|
// reboot server
|
|
mStreamServer.reboot();
|
|
}
|
|
|
|
exports.addUser = async (username, password, admin, vpaths) => {
|
|
if (config.program.users[username]) { throw `'${username}' is already loaded into memory`; }
|
|
|
|
// hash password
|
|
const hash = await auth.hashPassword(password);
|
|
|
|
const newUser = {
|
|
vpaths: vpaths,
|
|
password: hash.hashPassword,
|
|
salt: hash.salt,
|
|
admin: admin
|
|
};
|
|
|
|
// This extra step is so we can handle the process like a SQL transaction
|
|
// The new var is a copy so the original program isn't touched
|
|
// Once the file save is complete, the new user will be added
|
|
const memClone = JSON.parse(JSON.stringify(config.program.users));
|
|
memClone[username] = newUser;
|
|
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.users = memClone;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.users[username] = newUser;
|
|
|
|
// TODO: add user from scrobbler
|
|
}
|
|
|
|
exports.deleteUser = async (username) => {
|
|
if (!config.program.users[username]) { throw `'${username}' does not exist`; }
|
|
|
|
const memClone = JSON.parse(JSON.stringify(config.program.users));
|
|
delete memClone[username];
|
|
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.users = memClone;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
delete config.program.users[username];
|
|
|
|
db.getUserMetadataCollection().findAndRemove({ 'user': { '$eq': username } });
|
|
db.saveUserDB();
|
|
|
|
db.getPlaylistCollection().findAndRemove({ 'user': { '$eq': username } });
|
|
db.saveUserDB();
|
|
|
|
db.getShareCollection().findAndRemove({ 'user': { '$eq': username } });
|
|
db.saveUserDB();
|
|
|
|
// TODO: Remove user from scrobbler
|
|
}
|
|
|
|
exports.editUserPassword = async (username, password) => {
|
|
if (!config.program.users[username]) { throw `'${username}' does not exist`; }
|
|
|
|
const hash = await auth.hashPassword(password);
|
|
|
|
const memClone = JSON.parse(JSON.stringify(config.program.users));
|
|
memClone[username].password = hash.hashPassword;
|
|
memClone[username].salt = hash.salt;
|
|
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.users = memClone;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.users[username].password = hash.hashPassword;
|
|
config.program.users[username].salt = hash.salt;
|
|
}
|
|
|
|
exports.editUserVPaths = async (username, vpaths) => {
|
|
if (!config.program.users[username]) { throw `'${username}' does not exist`; }
|
|
|
|
const memClone = JSON.parse(JSON.stringify(config.program.users));
|
|
memClone[username].vpaths = vpaths;
|
|
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.users = memClone;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.users[username].vpaths = vpaths;
|
|
}
|
|
|
|
exports.editUserAccess = async (username, admin) => {
|
|
if (!config.program.users[username]) { throw `'${username}' does not exist`; }
|
|
|
|
const memClone = JSON.parse(JSON.stringify(config.program.users));
|
|
memClone[username].admin = admin;
|
|
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.users = memClone;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.users[username].admin = admin;
|
|
}
|
|
|
|
exports.editPort = async (port) => {
|
|
if (config.program.port === port) { return; }
|
|
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.port = port;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
// reboot server
|
|
mStreamServer.reboot();
|
|
}
|
|
|
|
exports.editMaxRequestSize = async (maxRequestSize) => {
|
|
if (config.program.maxRequestSize === maxRequestSize) { return; }
|
|
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.maxRequestSize = maxRequestSize;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
// reboot server
|
|
mStreamServer.reboot();
|
|
}
|
|
|
|
exports.editUpload = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.noUpload = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.noUpload = val;
|
|
}
|
|
|
|
|
|
exports.editAddress = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.address = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
mStreamServer.reboot();
|
|
}
|
|
|
|
exports.editSecret = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.secret = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.secret = val;
|
|
}
|
|
|
|
exports.editScanInterval = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.scanOptions) { loadConfig.scanOptions = {}; }
|
|
loadConfig.scanOptions.scanInterval = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.scanOptions.scanInterval = val;
|
|
|
|
// update timer
|
|
dbQueue.resetScanInterval();
|
|
}
|
|
|
|
exports.editSaveInterval = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.scanOptions) { loadConfig.scanOptions = {}; }
|
|
loadConfig.scanOptions.saveInterval = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.scanOptions.saveInterval = val;
|
|
}
|
|
|
|
exports.editSkipImg = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.scanOptions) { loadConfig.scanOptions = {}; }
|
|
loadConfig.scanOptions.skipImg = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.scanOptions.skipImg = val;
|
|
}
|
|
|
|
exports.editPause = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.scanOptions) { loadConfig.scanOptions = {}; }
|
|
loadConfig.scanOptions.pause = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.scanOptions.pause = val;
|
|
}
|
|
|
|
exports.editBootScanDelay = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.scanOptions) { loadConfig.scanOptions = {}; }
|
|
loadConfig.scanOptions.bootScanDelay = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.scanOptions.bootScanDelay = val;
|
|
}
|
|
|
|
exports.editMaxConcurrentTasks = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.scanOptions) { loadConfig.scanOptions = {}; }
|
|
loadConfig.scanOptions.maxConcurrentTasks = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.scanOptions.maxConcurrentTasks = val;
|
|
}
|
|
|
|
exports.editCompressImages = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.scanOptions) { loadConfig.scanOptions = {}; }
|
|
loadConfig.scanOptions.compressImage = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.scanOptions.compressImage = val;
|
|
}
|
|
|
|
exports.editWriteLogs = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.writeLogs = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.writeLogs = val;
|
|
|
|
if (val === false) {
|
|
logger.reset();
|
|
} else {
|
|
logger.addFileLogger(config.program.storage.logsDirectory);
|
|
}
|
|
}
|
|
|
|
exports.enableTranscode = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.transcode) { loadConfig.transcode = {}; }
|
|
loadConfig.transcode.enabled = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.transcode.enabled = val;
|
|
}
|
|
|
|
exports.editDefaultCodec = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.transcode) { loadConfig.transcode = {}; }
|
|
loadConfig.transcode.defaultCodec = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.transcode.defaultCodec = val;
|
|
}
|
|
|
|
exports.editDefaultBitrate = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.transcode) { loadConfig.transcode = {}; }
|
|
loadConfig.transcode.defaultBitrate = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.transcode.defaultBitrate = val;
|
|
}
|
|
|
|
exports.editDefaultAlgorithm = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
if (!loadConfig.transcode) { loadConfig.transcode = {}; }
|
|
loadConfig.transcode.algorithm = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.transcode.algorithm = val;
|
|
}
|
|
|
|
exports.lockAdminApi = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.lockAdmin = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.lockAdmin = val;
|
|
}
|
|
|
|
exports.enableFederation = async (val) => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.federation.enabled = val;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.federation.enabled = val;
|
|
|
|
syncthing.setup();
|
|
}
|
|
|
|
exports.removeSSL = async () => {
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
delete loadConfig.ssl;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
delete config.program.ssl;
|
|
mStreamServer.reboot();
|
|
}
|
|
|
|
function testSSL(jsonLoad) {
|
|
return new Promise((resolve, reject) => {
|
|
child.fork(path.join(__dirname, './ssl-test.js'), [JSON.stringify(jsonLoad)], { silent: true }).on('close', (code) => {
|
|
if (code !== 0) {
|
|
return reject('SSL Failure');
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
exports.setSSL = async (cert, key) => {
|
|
const sslObj = { key, cert };
|
|
await testSSL(sslObj);
|
|
const loadConfig = await this.loadFile(config.configFile);
|
|
loadConfig.ssl = sslObj;
|
|
await this.saveFile(loadConfig, config.configFile);
|
|
|
|
config.program.ssl = sslObj;
|
|
mStreamServer.reboot();
|
|
} |