476 lines
12 KiB
JavaScript
476 lines
12 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const os = require('os');
|
|
const axios = require('axios');
|
|
const exec = require('child_process').exec;
|
|
const spawn = require('child_process').spawn;
|
|
|
|
const tryParse = (j, def) => {
|
|
try {
|
|
return JSON.parse(j);
|
|
} catch (e) {
|
|
return def;
|
|
}
|
|
};
|
|
|
|
module.exports.detectRepertoryMounts = (directory, version) => {
|
|
return new Promise((resolve, reject) => {
|
|
const processOptions = {
|
|
detached: true,
|
|
shell: false,
|
|
windowsHide: true,
|
|
};
|
|
|
|
const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
|
const args = [];
|
|
args.push('-status');
|
|
|
|
const process = new spawn(command, args, processOptions);
|
|
let result = '';
|
|
|
|
process.on('error', (err) => {
|
|
reject(err);
|
|
});
|
|
|
|
process.stdout.on('data', (d)=> {
|
|
result += d;
|
|
});
|
|
|
|
process.on('exit', () => {
|
|
resolve(tryParse(result, {
|
|
Hyperspace: {
|
|
Active: false,
|
|
Location: '',
|
|
PID: -1,
|
|
},
|
|
Sia: {
|
|
Active: false,
|
|
Location: '',
|
|
PID: -1,
|
|
},
|
|
SiaPrime: {
|
|
Active: false,
|
|
Location: '',
|
|
PID: -1,
|
|
},
|
|
}));
|
|
});
|
|
process.unref();
|
|
});
|
|
};
|
|
|
|
module.exports.downloadFile = (url, destination, progressCallback, completeCallback) => {
|
|
try {
|
|
if (fs.existsSync(destination)) {
|
|
fs.unlinkSync(destination);
|
|
}
|
|
} catch (e) {
|
|
completeCallback(false, e);
|
|
return;
|
|
}
|
|
|
|
axios
|
|
.get(url, {
|
|
responseType: 'stream',
|
|
})
|
|
.then((response) => {
|
|
const stream = fs.createWriteStream(destination);
|
|
const total = parseInt(response.headers['content-length'], 10);
|
|
let downloaded = 0;
|
|
response.data.on('data', (chunk) => {
|
|
stream.write(Buffer.from(chunk));
|
|
downloaded += chunk.length;
|
|
progressCallback((downloaded / total * 100.0).toFixed(2));
|
|
});
|
|
|
|
response.data.on('end', () => {
|
|
stream.end(() => {
|
|
completeCallback();
|
|
});
|
|
});
|
|
|
|
response.data.on('error', (e) => {
|
|
stream.end(() => {
|
|
completeCallback(e);
|
|
});
|
|
});
|
|
})
|
|
.catch((e)=> {
|
|
completeCallback(e);
|
|
});
|
|
};
|
|
|
|
module.exports.executeAndWait = command => {
|
|
return new Promise((resolve, reject) => {
|
|
const retryExecute = (count, lastError) => {
|
|
if (++count <= 5) {
|
|
exec(command, (error) => {
|
|
if (error) {
|
|
if (error.code === 1) {
|
|
setTimeout(() => {
|
|
retryExecute(count, error);
|
|
}, 1000);
|
|
} else {
|
|
reject(error);
|
|
}
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
} else {
|
|
reject(lastError);
|
|
}
|
|
};
|
|
retryExecute(0);
|
|
});
|
|
};
|
|
|
|
module.exports.executeAsync = (command, args=[]) => {
|
|
return new Promise((resolve, reject) => {
|
|
const launchProcess = (count, timeout) => {
|
|
const processOptions = {
|
|
detached: true,
|
|
shell: true,
|
|
};
|
|
|
|
const process = new spawn(command, args, processOptions);
|
|
const pid = process.pid;
|
|
|
|
process.on('error', (err) => {
|
|
if (++count === 5) {
|
|
reject(err, pid);
|
|
} else {
|
|
clearTimeout(timeout);
|
|
setTimeout(()=>launchProcess(count, setTimeout(() => resolve(), 3000)), 1000);
|
|
}
|
|
});
|
|
|
|
process.on('exit', (code) => {
|
|
if (code !== 0) {
|
|
if (++count === 5) {
|
|
reject(code, pid);
|
|
} else {
|
|
clearTimeout(timeout);
|
|
setTimeout(() => launchProcess(count, setTimeout(() => resolve(), 3000)), 1000);
|
|
}
|
|
}
|
|
});
|
|
|
|
process.unref();
|
|
};
|
|
|
|
launchProcess(0, setTimeout(() => resolve(), 3000));
|
|
});
|
|
};
|
|
|
|
module.exports.executeMount = (directory, version, storageType, location, exitCallback) => {
|
|
return new Promise((resolve) => {
|
|
const processOptions = {
|
|
detached: true,
|
|
shell: true,
|
|
};
|
|
|
|
const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
|
const args = [];
|
|
if (storageType.toLowerCase() === 'hyperspace') {
|
|
args.push('-hs');
|
|
} else if (storageType.toLowerCase() === 'siaprime') {
|
|
args.push('-sp');
|
|
}
|
|
if ((os.platform() === 'linux') || (os.platform() === 'darwin')) {
|
|
args.push('-o');
|
|
args.push('big_writes');
|
|
} else if (os.platform() === 'win32') {
|
|
args.push('-hidden');
|
|
}
|
|
args.push(location);
|
|
|
|
const process = new spawn(command, args, processOptions);
|
|
const pid = process.pid;
|
|
|
|
const timeout = setTimeout(() => {
|
|
resolve(pid);
|
|
}, 3000);
|
|
|
|
process.on('error', (err) => {
|
|
clearTimeout(timeout);
|
|
exitCallback(err, pid);
|
|
});
|
|
process.on('exit', (code) => {
|
|
clearTimeout(timeout);
|
|
exitCallback(code, pid);
|
|
});
|
|
process.unref();
|
|
});
|
|
};
|
|
|
|
module.exports.getConfig = (directory, version, storageType) => {
|
|
return new Promise((resolve, reject) => {
|
|
const processOptions = {
|
|
detached: true,
|
|
shell: false,
|
|
windowsHide: true,
|
|
};
|
|
|
|
const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
|
const args = [];
|
|
args.push('-dc');
|
|
if (storageType.toLowerCase() === 'hyperspace') {
|
|
args.push('-hs');
|
|
} else if (storageType.toLowerCase() === 'siaprime') {
|
|
args.push('-sp');
|
|
}
|
|
|
|
const process = new spawn(command, args, processOptions);
|
|
let result = '';
|
|
|
|
process.on('error', (err) => {
|
|
reject(err);
|
|
});
|
|
|
|
process.stdout.on('data', (d)=> {
|
|
result += d;
|
|
});
|
|
|
|
process.on('exit', () => {
|
|
const lines = result
|
|
.replace(/\r\n/g, '\n')
|
|
.split('\n');
|
|
|
|
const code = parseInt(lines[0], 10);
|
|
if (code === 0) {
|
|
lines.shift();
|
|
resolve({
|
|
Code: code,
|
|
Data: JSON.parse(lines.join('\n')),
|
|
});
|
|
} else {
|
|
resolve(code);
|
|
}
|
|
});
|
|
process.unref();
|
|
});
|
|
};
|
|
|
|
module.exports.getConfigTemplate = (directory, version, storageType) => {
|
|
return new Promise((resolve, reject) => {
|
|
const processOptions = {
|
|
detached: true,
|
|
shell: false,
|
|
windowsHide: true,
|
|
};
|
|
|
|
const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
|
const args = [];
|
|
args.push('-gt');
|
|
if (storageType.toLowerCase() === 'hyperspace') {
|
|
args.push('-hs');
|
|
} else if (storageType.toLowerCase() === 'siaprime') {
|
|
args.push('-sp');
|
|
}
|
|
|
|
const process = new spawn(command, args, processOptions);
|
|
let result = '';
|
|
|
|
process.on('error', (err) => {
|
|
reject(err);
|
|
});
|
|
|
|
process.stdout.on('data', (d)=> {
|
|
result += d;
|
|
});
|
|
|
|
process.on('exit', () => {
|
|
resolve(JSON.parse(result));
|
|
});
|
|
process.unref();
|
|
});
|
|
};
|
|
|
|
module.exports.getMissingDependencies = dependencies => {
|
|
return new Promise((resolve, reject) => {
|
|
if (!dependencies) {
|
|
reject(Error('Dependency list is invalid'));
|
|
}
|
|
|
|
let missing = [];
|
|
if (os.platform() === 'win32') {
|
|
let count = 0;
|
|
const resolveIfComplete = () => {
|
|
if (++count === dependencies.length) {
|
|
resolve(missing);
|
|
}
|
|
};
|
|
|
|
const Registry = require('winreg');
|
|
for (const dep of dependencies) {
|
|
let hive = null;
|
|
const hiveName = dep.registry[0].split('\\')[0];
|
|
switch (hiveName) {
|
|
case 'HKEY_CLASSES_ROOT':
|
|
hive = Registry.HKCR;
|
|
break;
|
|
case 'HKEY_CURRENT_CONFIG':
|
|
hive = Registry.HKCC;
|
|
break;
|
|
case 'HKEY_CURRENT_USER':
|
|
hive = Registry.HKCU;
|
|
break;
|
|
case 'HKEY_LOCAL_MACHINE':
|
|
hive = Registry.HKLM;
|
|
break;
|
|
case 'HKEY_USERS':
|
|
hive = Registry.HKU;
|
|
break;
|
|
default:
|
|
throw Error('Invalid registry hive: ' + hiveName);
|
|
}
|
|
|
|
const key = dep.registry[0].substr(hiveName.length);
|
|
const regKey = new Registry({
|
|
hive: hive,
|
|
key: key
|
|
});
|
|
regKey.valueExists('DisplayName', (err, exists) => {
|
|
if (err || !exists) {
|
|
regKey.valueExists('ProductName', (err, exists) => {
|
|
if (err || !exists) {
|
|
missing.push(dep);
|
|
}
|
|
resolveIfComplete();
|
|
});
|
|
} else {
|
|
resolveIfComplete();
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
for (const dep of dependencies) {
|
|
try {
|
|
if (!fs.lstatSync(dep.file).isFile()) {
|
|
missing.push(dep);
|
|
}
|
|
} catch (e) {
|
|
missing.push(dep);
|
|
}
|
|
}
|
|
resolve(missing);
|
|
}
|
|
});
|
|
};
|
|
|
|
//https://stackoverflow.com/questions/31645738/how-to-create-full-path-with-nodes-fs-mkdirsync
|
|
module.exports.mkDirByPathSync = (targetDir, { isRelativeToScript = false } = {}) => {
|
|
const sep = path.sep;
|
|
const initDir = path.isAbsolute(targetDir) ? sep : '';
|
|
const baseDir = isRelativeToScript ? __dirname : '.';
|
|
|
|
return targetDir.split(sep).reduce((parentDir, childDir) => {
|
|
const curDir = path.resolve(baseDir, parentDir, childDir);
|
|
try {
|
|
fs.mkdirSync(curDir);
|
|
} catch (err) {
|
|
if (err.code === 'EEXIST') { // curDir already exists!
|
|
return curDir;
|
|
}
|
|
|
|
// To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
|
|
if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure.
|
|
throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);
|
|
}
|
|
|
|
const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
|
|
if (!caughtErr || (caughtErr && (targetDir === curDir))) {
|
|
throw err; // Throw if it's just the last created dir.
|
|
}
|
|
}
|
|
|
|
return curDir;
|
|
}, initDir);
|
|
};
|
|
|
|
module.exports.removeDirectoryRecursively = (p) => {
|
|
if (fs.existsSync(p)) {
|
|
fs
|
|
.readdirSync(p)
|
|
.forEach(file => {
|
|
const curPath = path.join(p, file);
|
|
if (fs.lstatSync(curPath).isDirectory()) {
|
|
module.exports.removeDirectoryRecursively(curPath);
|
|
} else {
|
|
fs.unlinkSync(curPath);
|
|
}
|
|
});
|
|
fs.rmdirSync(p);
|
|
}
|
|
};
|
|
|
|
module.exports.resolvePath = str => {
|
|
if (os.platform() === 'win32') {
|
|
return str.replace(/%([^%]+)%/g, (_, n) => {
|
|
return process.env[n];
|
|
});
|
|
} else {
|
|
return str.replace('~', os.homedir());
|
|
}
|
|
};
|
|
|
|
module.exports.setConfigValue = (name, value, directory, storageType, version) => {
|
|
return new Promise((resolve, reject) => {
|
|
const processOptions = {
|
|
detached: true,
|
|
shell: false,
|
|
windowsHide: true,
|
|
};
|
|
|
|
const command = path.join(directory, version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
|
const args = [];
|
|
args.push('-set');
|
|
args.push(name);
|
|
args.push(value);
|
|
if (storageType.toLowerCase() === 'hyperspace') {
|
|
args.push('-hs');
|
|
} else if (storageType.toLowerCase() === 'siaprime') {
|
|
args.push('-sp');
|
|
}
|
|
|
|
const process = new spawn(command, args, processOptions);
|
|
|
|
process.on('error', (err) => {
|
|
reject(err);
|
|
});
|
|
|
|
process.on('exit', () => {
|
|
resolve();
|
|
});
|
|
|
|
process.unref();
|
|
});
|
|
};
|
|
|
|
module.exports.stopProcessByPID = pid => {
|
|
return new Promise((resolve, reject) => {
|
|
const processOptions = {
|
|
detached: true,
|
|
shell: false,
|
|
windowsHide: true,
|
|
};
|
|
|
|
const command = (os.platform() === 'win32') ? 'taskkill.exe' : 'kill';
|
|
const args = [];
|
|
if (os.platform() === 'win32') {
|
|
args.push('/PID');
|
|
}
|
|
args.push(pid);
|
|
|
|
const process = new spawn(command, args, processOptions);
|
|
|
|
process.on('error', (err) => {
|
|
reject(err);
|
|
});
|
|
process.on('exit', () => {
|
|
setTimeout(()=>resolve(pid), 3000);
|
|
});
|
|
process.unref();
|
|
});
|
|
}; |