rats-search/src/background/background.js
2018-04-03 18:37:34 +03:00

401 lines
10 KiB
JavaScript

// This is main process of Electron, started as first thing when your
// app starts. It runs through entire life of your application.
// It doesn't have any windows which you can see on screen, but we can open
// window from here.
import path from "path";
import url from "url";
import os from 'os';
import { app, Menu, ipcMain, Tray, dialog } from "electron";
import createWindow from "./helpers/window";
import { autoUpdater } from 'electron-updater'
import appPath from './electronAppPath'
import dbPatcher from './dbPatcher'
import { devMenuTemplate } from "./menu/dev_menu_template";
import { editMenuTemplate } from "./menu/edit_menu_template";
import { settingsMenuTemplate } from "./menu/config_menu_template";
import { aboutMenuTemplate } from "./menu/about_menu_template";
import { manageMenuTemplate } from "./menu/manage_menu_template";
// Special module holding environment variables which you declared
// in config/env_xxx.json file.
import env from "env";
const { spawn, exec } = require('child_process')
const fs = require('fs')
const iconv = require('iconv-lite');
// plugins and dev tool
require('electron-context-menu')({})
const setApplicationMenu = () => {
const menus = [editMenuTemplate, manageMenuTemplate, settingsMenuTemplate, aboutMenuTemplate];
if (env.name !== "production") {
menus.push(devMenuTemplate);
}
// append version as disabled menu item
menus.push({
label: app.getVersion()
})
Menu.setApplicationMenu(Menu.buildFromTemplate(menus));
};
// Save userData in separate folders for each environment.
// Thanks to this you can use production and development versions of the app
// on same machine like those are two separate apps.
if (env.name !== "production") {
const userDataPath = app.getPath("userData");
app.setPath("userData", `${userDataPath} (${env.name})`);
}
// portative version
let portative = false
if(env.name === "production") {
if(fs.existsSync(path.dirname(process.execPath) + `/data`))
{
portative = true;
app.setPath("userData", path.dirname(process.execPath) + `/data`);
}
}
const resourcesPath = env.name === "production" ? process.resourcesPath : 'resources'
const spiderCall = require('./spider')
const appConfig = require('./config')
let mainWindow = undefined
let sphinx = undefined
let spider = undefined
const util = require('util');
if (!fs.existsSync(app.getPath("userData"))){
fs.mkdirSync(app.getPath("userData"));
}
const logFile = fs.createWriteStream(app.getPath("userData") + '/rats.log', {flags : 'w'});
const logStdout = process.stdout;
console.log = (...d) => {
const date = (new Date).toLocaleTimeString()
logFile.write(`[${date}] ` + util.format(...d) + '\n');
logStdout.write(util.format(...d) + '\n');
};
// print os info
console.log('Rats', app.getVersion())
console.log('Platform:', os.platform())
console.log('Arch:', os.arch())
console.log('OS Release:', os.release())
console.log('CPU:', os.cpus()[0].model)
console.log('CPU Logic cores:', os.cpus().length)
console.log('Total memory:', (os.totalmem() / (1024 * 1024)).toFixed(2), 'MB')
console.log('Free memory:', (os.freemem() / (1024 * 1024)).toFixed(2), 'MB')
if(portative)
console.log('portative compability')
const shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
// Someone tried to run a second instance, we should focus our window.
console.log('openned second application, just focus this one')
if (mainWindow) {
if (mainWindow.isMinimized())
mainWindow.restore();
mainWindow.focus();
}
});
if (shouldQuit) {
console.log('closed because of second application')
app.exit(0);
}
const writeSphinxConfig = (path, dbPath) => {
let config = `
index torrents
{
type = rt
path = ${dbPath}/database/torrents
rt_attr_string = hash
rt_attr_string = name
rt_field = nameIndex
rt_attr_bigint = size
rt_attr_uint = files
rt_attr_uint = piecelength
rt_attr_timestamp = added
rt_attr_string = ipv4
rt_attr_uint = port
rt_attr_string = contentType
rt_attr_string = contentCategory
rt_attr_uint = seeders
rt_attr_uint = leechers
rt_attr_uint = completed
rt_attr_timestamp = trackersChecked
rt_attr_uint = good
rt_attr_uint = bad
}
index files
{
type = rt
path = ${dbPath}/database/files
rt_attr_string = path
rt_field = pathIndex
rt_attr_string = hash
rt_attr_bigint = size
}
index version
{
type = rt
path = ${dbPath}/database/version
rt_attr_uint = version
rt_field = versionIndex
}
searchd
{
listen = 9312
listen = 9306:mysql41
read_timeout = 5
max_children = 30
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
workers = threads # for RT to work
pid_file = ${path}/searchd.pid
log = ${path}/searchd.log
query_log = ${path}/query.log
binlog_path = ${path}
}
`;
// clear dir in test env
if(env.name === 'test')
{
if (fs.existsSync(`${dbPath}/database`)) {
fs.readdirSync(`${dbPath}/database`).forEach(function(file, index){
const curPath = `${dbPath}/database` + "/" + file;
if (!fs.lstatSync(curPath).isDirectory()) {
fs.unlinkSync(curPath);
}
});
fs.readdirSync(path).forEach(function(file, index){
if(!file.startsWith('binlog'))
return;
const curPath = path + "/" + file;
if (!fs.lstatSync(curPath).isDirectory()) {
fs.unlinkSync(curPath);
}
});
}
}
if (!fs.existsSync(`${dbPath}/database`)){
fs.mkdirSync(`${dbPath}/database`);
}
if(/^win/.test(process.platform))
config = iconv.encode(config, 'win1251')
fs.writeFileSync(`${path}/sphinx.conf`, config)
console.log(`writed sphinx config to ${path}`)
console.log('db path:', dbPath)
}
const sphinxPath = path.resolve(appPath('searchd'))
console.log('Sphinx Path:', sphinxPath)
const startSphinx = (callback) => {
const sphinxConfigDirectory = app.getPath("userData")
appConfig['dbPath'] = appConfig.dbPath && appConfig.dbPath.length > 0 ? appConfig.dbPath : sphinxConfigDirectory;
// on portable dir can move database directory
if(!fs.existsSync(appConfig.dbPath) && fs.existsSync(sphinxConfigDirectory))
{
appConfig['dbPath'] = sphinxConfigDirectory
}
writeSphinxConfig(sphinxConfigDirectory, appConfig.dbPath)
const config = `${sphinxConfigDirectory}/sphinx.conf`
const options = ['--config', config]
if(!(/^win/.test(process.platform)))
{
options.push('--nodetach')
}
sphinx = spawn(sphinxPath, options)
sphinx.stdout.on('data', (data) => {
console.log(`sphinx: ${data}`)
if (data.includes('accepting connections')) {
console.log('catched sphinx start')
if(callback)
callback()
}
})
sphinx.on('close', (code, signal) => {
console.log(`sphinx closed with code ${code} and signal ${signal}`)
app.quit()
})
sphinx.stop = () => {
console.log('sphinx closing...')
exec(`"${sphinxPath}" --config "${config}" --stopwait`)
}
}
// log autoupdate
const log = require('electron-log')
log.transports.file.level = false;
log.transports.console.level = false;
log.transports.console = function(msg) {
const text = util.format.apply(util, msg.data);
console.log(text);
};
autoUpdater.logger = log;
autoUpdater.on('update-downloaded', () => {
console.log('update-downloaded lats quitAndInstall');
if (env.name === "production") {
dialog.showMessageBox({
type: 'info',
title: 'Found Updates',
message: 'Found updates, do you want update now?',
buttons: ['Sure', 'No']
}, (buttonIndex) => {
if (buttonIndex === 0) {
const isSilent = true;
const isForceRunAfter = true;
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
}
})
}
})
let tray = undefined
app.on("ready", () => {
startSphinx(() => {
mainWindow = createWindow("main", {
width: 1000,
height: 600
});
dbPatcher(() => {
setApplicationMenu();
mainWindow.loadURL(
url.format({
pathname: path.join(__dirname, "app.html"),
protocol: "file:",
slashes: true
})
);
if (env.name === "development") {
mainWindow.openDevTools();
}
if(process.platform === 'darwin')
tray = new Tray(`${resourcesPath}/icons/19x19.png`)
else
tray = new Tray(`${resourcesPath}/icons/512x512.png`)
tray.on('click', () => {
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
})
mainWindow.on('show', () => {
tray.setHighlightMode('always')
})
mainWindow.on('hide', () => {
tray.setHighlightMode('never')
})
mainWindow.on('closed', () => {
mainWindow = undefined
})
mainWindow.on('minimize', (event) => {
event.preventDefault();
mainWindow.hide();
});
var contextMenu = Menu.buildFromTemplate([
{ label: 'Show', click: function(){
mainWindow.show();
} },
{ label: 'Quit', click: function(){
app.isQuiting = true;
if (sphinx)
stop()
else
app.quit()
} }
]);
tray.setContextMenu(contextMenu)
tray.setToolTip('Rats on The Boat search')
mainWindow.webContents.on('will-navigate', e => { e.preventDefault() })
mainWindow.webContents.on('new-window', (event, url, frameName) => {
if(frameName == '_self')
{
event.preventDefault()
mainWindow.loadURL(url)
}
})
if (env.name === "production" && !portative) { autoUpdater.checkForUpdates() }
spider = spiderCall((...data) => {
if(mainWindow)
mainWindow.webContents.send(...data)
}, (message, callback) => {
ipcMain.on(message, (event, arg) => {
if(Array.isArray(arg) && typeof arg[arg.length - 1] === 'object' && arg[arg.length - 1].callback)
{
const id = arg[arg.length - 1].callback
arg[arg.length - 1] = (responce) => {
mainWindow.webContents.send('callback', id, responce)
}
}
callback.apply(null, arg)
})
}, app.getPath("userData"), app.getVersion(), env.name)
}, mainWindow)
})
});
let stopProtect = false
const stop = () => {
if(stopProtect)
return
stopProtect = true
if(tray)
tray.destroy()
if(spider)
{
spider.stop(() => sphinx.stop())
}
else
{
sphinx.stop()
}
}
app.on("window-all-closed", () => {
if (sphinx)
stop()
else
app.quit()
});
app.on('before-quit', () => {
if (sphinx)
stop()
})