958 lines · 30.6 KB · JavaScript
RawDownload
'use strict'; const electron = require('electron');const app = electron.app;  // Module to control application life.const BrowserWindow = electron.BrowserWindow;const nativeImage = electron.nativeImage; const notifier = require('node-notifier');const util = require('util');const dialog = electron.dialog;const Menu = electron.Menu;const Tray = electron.Tray;const shell = electron.shell;const os = require('os');const childProcess = require('child_process');const path = require('path');const fs = require('fs-extra');const Tail = require('always-tail');const http = require('http');const zlib = require('zlib');const tar = require('tar-fs'); const request = require('request');const progress = require('request-progress');const osHomeDir = require('os-homedir'); const updater = require('./modules/hf-updater.js'); const Config = require('./modules/config').Config; const hfprocess = require('./modules/hf-process.js'); global.log = require('electron-log'); const Process = hfprocess.Process;const ACMonitorProcess = hfprocess.ACMonitorProcess;const ProcessStates = hfprocess.ProcessStates;const ProcessGroup = hfprocess.ProcessGroup;const ProcessGroupStates = hfprocess.ProcessGroupStates; const hfApp = require('./modules/hf-app.js');const GetBuildInfo = hfApp.getBuildInfo;const StartInterface = hfApp.startInterface;const getRootHifiDataDirectory = hfApp.getRootHifiDataDirectory;const getDomainServerClientResourcesDirectory = hfApp.getDomainServerClientResourcesDirectory;const getAssignmentClientResourcesDirectory = hfApp.getAssignmentClientResourcesDirectory;const getApplicationDataDirectory = hfApp.getApplicationDataDirectory;  const osType = os.type(); const appIcon = path.join(__dirname, '../resources/console.png'); const menuNotificationIcon = path.join(__dirname, '../resources/tray-menu-notification.png'); const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Daysconst LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; const HOME_CONTENT_URL = "https://content.overte.org/DomainContent/Sandbox/Rearranged_Basic_Sandbox.tar.gz"; const buildInfo = GetBuildInfo(); const NotificationState = {    UNNOTIFIED: 'unnotified',    NOTIFIED: 'notified'}; // Update lock filepathconst UPDATER_LOCK_FILENAME = ".updating";const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_FILENAME; // Configure logconst oldLogFile = path.join(getApplicationDataDirectory(), '/log.txt');const logFile = path.join(getApplicationDataDirectory(true), '/log.txt');if (oldLogFile != logFile && fs.existsSync(oldLogFile)) {    if (!fs.existsSync(oldLogFile)) {        fs.moveSync(oldLogFile, logFile);    } else {        fs.remove(oldLogFile);    }}fs.ensureFileSync(logFile); // Ensure file existslog.transports.file.maxSize = 5 * 1024 * 1024;log.transports.file.file = logFile; log.debug("build info", buildInfo);log.debug("Root hifi directory is: ", getRootHifiDataDirectory());log.debug("App Data directory:", getApplicationDataDirectory());fs.ensureDirSync(getApplicationDataDirectory()); var oldLogPath = path.join(getApplicationDataDirectory(), '/logs');var logPath = path.join(getApplicationDataDirectory(true), '/logs');if (oldLogPath != logPath && fs.existsSync(oldLogPath)) {    if (!fs.existsSync(oldLogPath)) {        fs.moveSync(oldLogPath, logPath);    } else {        fs.remove(oldLogPath);    }}fs.ensureDirSync(logPath);log.debug("Log directory:", logPath); const configPath = path.join(getApplicationDataDirectory(), 'config.json');var userConfig = new Config();userConfig.load(configPath); const ipcMain = electron.ipcMain;  function isInterfaceInstalled () {    if (osType == "Darwin") {        // In OSX Sierra, the app translocation process moves        // the executable to a random location before starting it        // which makes finding the interface near impossible using        // relative paths.  For now, as there are no server-only        // installs, we just assume the interface is installed here        return true;    } else {        return interfacePath;    }} function isServerInstalled () {    return dsPath && acPath;} var isShuttingDown = false;function shutdown() {    log.debug("Normal shutdown (isShuttingDown: " + isShuttingDown +  ")");    if (!isShuttingDown) {        // if the home server is running, show a prompt before quit to ask if the user is sure        if (isServerInstalled() && homeServer.state == ProcessGroupStates.STARTED) {            log.debug("Showing shutdown dialog.");            dialog.showMessageBox({                type: 'question',                buttons: ['Yes', 'No'],                title: 'Stopping Overte Sandbox',                message: 'Quitting will stop your Sandbox and your Home domain will no longer be running.\nDo you wish to continue?'            }, shutdownCallback);        } else {            shutdownCallback(0);        }     }} function forcedShutdown() {    log.debug("Forced shutdown (isShuttingDown: " + isShuttingDown +  ")");    if (!isShuttingDown) {        shutdownCallback(0);    }} function shutdownCallback(idx) {    log.debug("Entering shutdown callback.");    if (idx == 0 && !isShuttingDown) {        isShuttingDown = true;         log.debug("Stop tray polling.");        trayNotifications.stopPolling();         log.debug("Saving user config");        userConfig.save(configPath);         if (logWindow) {            log.debug("Closing log window");            logWindow.close();        }         if (isServerInstalled()) {            if (homeServer) {                log.debug("Stoping home server");                homeServer.stop();                 updateTrayMenu(homeServer.state);                 if (homeServer.state == ProcessGroupStates.STOPPED) {                    // if the home server is already down, take down the server console now                    log.debug("Quitting.");                    app.exit(0);                } else {                    // if the home server is still running, wait until we get a state change or timeout                    // before quitting the app                    log.debug("Server still shutting down. Waiting");                    var timeoutID = setTimeout(function() {                        app.exit(0);                    }, 5000);                    homeServer.on('state-update', function(processGroup) {                        if (processGroup.state == ProcessGroupStates.STOPPED) {                            clearTimeout(timeoutID);                            log.debug("Quitting.");                            app.exit(0);                        }                    });                }            }        }        else {            app.exit(0);        }    }} function deleteOldFiles(directoryPath, maxAgeInSeconds, filenameRegex) {    log.debug("Deleting old log files in " + directoryPath);     var filenames = [];    try {        filenames = fs.readdirSync(directoryPath);    } catch (e) {        log.warn("Error reading contents of log file directory", e);        return;    }     for (const filename of filenames) {        log.debug("Checking", filename);        const absolutePath = path.join(directoryPath, filename);        var stat = null;        try {            stat = fs.statSync(absolutePath);        } catch (e) {            log.debug("Error stat'ing file", absolutePath, e);            continue;        }        const curTime = Date.now();        if (stat.isFile() && filename.search(filenameRegex) >= 0) {            const ageInSeconds = (curTime - stat.mtime.getTime()) / 1000.0;            if (ageInSeconds >= maxAgeInSeconds) {                log.debug("\tDeleting:", filename, ageInSeconds);                try {                    fs.unlinkSync(absolutePath);                } catch (e) {                    if (e.code != 'EBUSY') {                        log.warn("\tError deleting:", e);                    }                }            }        }    }} app.setAppUserModelId(buildInfo.appUserModelId); // print out uncaught exceptions in the consoleprocess.on('uncaughtException', function(err) {    log.error(err);    log.error(err.stack);}); const gotTheLock = app.requestSingleInstanceLock() if (!gotTheLock) {  log.warn("Another instance of the Sandbox is already running - this instance will quit.");  app.exit(0);  return;} // Check command line arguments to see how to find binariesvar argv = require('yargs').argv; var pathFinder = require('./modules/path-finder.js'); var interfacePath = null;var dsPath = null;var acPath = null; var debug = argv.debug; var binaryType = argv.binaryType; interfacePath = pathFinder.discoveredPath("interface", binaryType, buildInfo.releaseType);dsPath = pathFinder.discoveredPath("domain-server", binaryType, buildInfo.releaseType);acPath = pathFinder.discoveredPath("assignment-client", binaryType, buildInfo.releaseType); console.log("Domain Server Path: " + dsPath);console.log("Assignment Client Path: " + acPath);console.log("Interface Path: " + interfacePath); function binaryMissingMessage(displayName, executableName, required) {    var message = "The " + displayName + " executable was not found.\n";     if (required) {        message += "It is required for the Overte Sandbox to run.\n\n";    } else {        message += "\n";    }     if (debug) {        message += "Please ensure there is a compiled " + displayName + " in a folder named build in this checkout.\n\n";        message += "It was expected to be found at one of the following paths:\n";         var paths = pathFinder.searchPaths(executableName, argv.localReleaseBuilds);        message += paths.join("\n");    } else {        message += "It is expected to be found beside this executable.\n";        message += "You may need to re-install the Overte Sandbox.";    }     return message;} function openFileBrowser(path) {    // Add quotes around path    path = '"' + path + '"';    if (osType == "Windows_NT") {        childProcess.exec('start "" ' + path);    } else if (osType == "Darwin") {        childProcess.exec('open ' + path);    } else if (osType == "Linux") {        childProcess.exec('xdg-open ' + path);    }} function openLogDirectory() {    openFileBrowser(logPath);} // NOTE: this looks like it does nothing, but it's very important.// Without it the default behaviour is to quit the app once all windows closed// which is absolutely not what we want for a taskbar application.app.on('window-all-closed', function() {}); var tray = null;global.homeServer = null;global.domainServer = null;global.acMonitor = null;global.userConfig = userConfig;global.openLogDirectory = openLogDirectory; const hfNotifications = require('./modules/hf-notifications.js');const HifiNotifications = hfNotifications.HifiNotifications;const HifiNotificationType = hfNotifications.NotificationType; var pendingNotifications = {}var notificationState = NotificationState.UNNOTIFIED; function setNotificationState (notificationType, pending = undefined) {    if (pending !== undefined) {        if ((notificationType === HifiNotificationType.TRANSACTIONS ||            notificationType === HifiNotificationType.ITEMS)) {            // special case, because we want to clear the indicator light            // on INVENTORY when either Transactions or Items are            // clicked on in the notification popup, we detect that case            // here and force both to be unnotified.            pendingNotifications[HifiNotificationType.TRANSACTIONS] = pending;            pendingNotifications[HifiNotificationType.ITEMS] = pending;        } else {            pendingNotifications[notificationType] = pending;        }        notificationState = NotificationState.UNNOTIFIED;        for (var key in pendingNotifications) {            if (pendingNotifications[key]) {                notificationState = NotificationState.NOTIFIED;                break;            }        }    }    updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED);} var trayNotifications = new HifiNotifications(userConfig, setNotificationState); var LogWindow = function(ac, ds) {    this.ac = ac;    this.ds = ds;    this.window = null;    this.acMonitor = null;    this.dsMonitor = null;}LogWindow.prototype = {    open: function() {        if (this.window) {            this.window.show();            this.window.restore();            return;        }        // Create the browser window.        this.window = new BrowserWindow({ width: 700, height: 500, icon: appIcon });        this.window.loadURL('file://' + __dirname + '/log.html');         if (!debug) {            this.window.setMenu(null);        }         this.window.on('closed', function() {            this.window = null;        }.bind(this));    },    close: function() {        if (this.window) {            this.window.close();        }    }}; function visitSandboxClicked() {    if (isInterfaceInstalled()) {        StartInterface('hifi://localhost');    } else {        // show an error to say that we can't go home without an interface instance        dialog.showErrorBox("Client Not Found", binaryMissingMessage("Overte client", "Interface", false));    }} var logWindow = null; var labels = {    serverState: {        label: 'Server - Stopped',        enabled: false    },    version: {        label: 'Version - ' + buildInfo.buildIdentifier,        enabled: false    },    showNotifications: {        label: 'Show Notifications',        type: 'checkbox',        checked: true,        click: function () {            trayNotifications.enable(!trayNotifications.enabled(), setNotificationState);            userConfig.save(configPath);            updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED);        }    },    goto: {        label: 'GoTo',        click: function () {            StartInterface("hifiapp:GOTO");            setNotificationState(HifiNotificationType.GOTO, false);        }    },    people: {        label: 'People',        click: function () {            StartInterface("hifiapp:PEOPLE");            setNotificationState(HifiNotificationType.PEOPLE, false);        }    },    inventory: {        label: 'Inventory',        click: function () {            StartInterface("hifiapp:INVENTORY");            setNotificationState(HifiNotificationType.ITEMS, false);            setNotificationState(HifiNotificationType.TRANSACTIONS, false);        }    },    restart: {        label: 'Start Server',        click: function() {            homeServer.restart();        }    },    stopServer: {        label: 'Stop Server',        visible: false,        click: function() {            homeServer.stop();        }    },    goHome: {        label: 'Visit Local Server',        click: visitSandboxClicked,        enabled: false    },    quit: {        label: 'Quit',        accelerator: 'Command+Q',        click: function() {            shutdown();        }    },    settings: {        label: 'Settings',        click: function() {            shell.openExternal('http://localhost:40100/settings');        },        enabled: false    },    viewLogs: {        label: 'View Logs',        click: function() {            logWindow.open();        }    },    share: {        label: 'Share',        click: function() {            shell.openExternal('http://localhost:40100/settings/?action=share')        }    },    shuttingDown: {        label: "Shutting down...",        enabled: false    },} var separator = {    type: 'separator'

Lines 1–500 of 958.