diff options
-rw-r--r-- | src/client/DocServer.ts | 1 | ||||
-rw-r--r-- | src/client/util/ClientDiagnostics.ts | 34 | ||||
-rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 2 | ||||
-rw-r--r-- | src/client/views/Main.tsx | 2 | ||||
-rw-r--r-- | src/server/ActionUtilities.ts | 8 | ||||
-rw-r--r-- | src/server/ApiManagers/DiagnosticManager.ts | 30 | ||||
-rw-r--r-- | src/server/ApiManagers/SearchManager.ts | 49 | ||||
-rw-r--r-- | src/server/Message.ts | 1 | ||||
-rw-r--r-- | src/server/Websocket/Websocket.ts | 4 | ||||
-rw-r--r-- | src/server/index.ts | 41 |
10 files changed, 96 insertions, 76 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index e4b183715..befe9ea5c 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -82,6 +82,7 @@ export namespace DocServer { Utils.AddServerHandler(_socket, MessageStore.UpdateField, respondToUpdate); Utils.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete); Utils.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete); + _socket.on("connection_terminated", () => alert("Your connection to the server has been terminated.")); } function errorFunc(): never { diff --git a/src/client/util/ClientDiagnostics.ts b/src/client/util/ClientDiagnostics.ts deleted file mode 100644 index 0a213aa1c..000000000 --- a/src/client/util/ClientDiagnostics.ts +++ /dev/null @@ -1,34 +0,0 @@ -export namespace ClientDiagnostics { - - export async function start() { - - let serverPolls = 0; - const serverHandle = setInterval(async () => { - if (++serverPolls === 20) { - alert("Your connection to the server has been terminated."); - clearInterval(serverHandle); - } - await fetch("/serverHeartbeat"); - serverPolls--; - }, 1000 * 15); - - let executed = false; - let solrHandle: NodeJS.Timeout | undefined; - const handler = async () => { - const response = await fetch("/solrHeartbeat"); - if (!(await response.json()).running) { - if (!executed) { - alert("Looks like SOLR is not running on your machine."); - executed = true; - solrHandle && clearInterval(solrHandle); - } - } - }; - await handler(); - if (!executed) { - solrHandle = setInterval(handler, 1000 * 15); - } - - } - -}
\ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 89037dcb2..01d566831 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -1030,7 +1030,7 @@ export class TooltipTextMenu { TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMap.set(input.value, TooltipTextMenuManager.Instance._brushMarks); input.style.background = "lightGray"; } - } + }; const wrapper = document.createElement("div"); wrapper.appendChild(input); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 9e699978f..b21eb9c8f 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -5,12 +5,10 @@ import * as ReactDOM from 'react-dom'; import * as React from 'react'; import { DocServer } from "../DocServer"; import { AssignAllExtensions } from "../../extensions/General/Extensions"; -import { ClientDiagnostics } from "../util/ClientDiagnostics"; AssignAllExtensions(); (async () => { - await ClientDiagnostics.start(); const info = await CurrentUserUtils.loadCurrentUser(); DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email); await Docs.Prototypes.initialize(); diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts index 4fe7374d1..1ebeab203 100644 --- a/src/server/ActionUtilities.ts +++ b/src/server/ActionUtilities.ts @@ -9,21 +9,21 @@ export const command_line = (command: string, fromDirectory?: string) => { return new Promise<string>((resolve, reject) => { const options: ExecOptions = {}; if (fromDirectory) { - options.cwd = path.join(__dirname, fromDirectory); + options.cwd = path.resolve(__dirname, fromDirectory); } exec(command, options, (err, stdout) => err ? reject(err) : resolve(stdout)); }); }; export const read_text_file = (relativePath: string) => { - const target = path.join(__dirname, relativePath); + const target = path.resolve(__dirname, relativePath); return new Promise<string>((resolve, reject) => { fs.readFile(target, (err, data) => err ? reject(err) : resolve(data.toString())); }); }; export const write_text_file = (relativePath: string, contents: any) => { - const target = path.join(__dirname, relativePath); + const target = path.resolve(__dirname, relativePath); return new Promise<void>((resolve, reject) => { fs.writeFile(target, contents, (err) => err ? reject(err) : resolve()); }); @@ -75,5 +75,5 @@ export async function Prune(rootDirectory: string): Promise<boolean> { export const Destroy = (mediaPath: string) => new Promise<boolean>(resolve => fs.unlink(mediaPath, error => resolve(error === null))); export function addBeforeExitHandler(handler: NodeJS.BeforeExitListener) { - process.on("beforeExit", handler); + // process.on("beforeExit", handler); } diff --git a/src/server/ApiManagers/DiagnosticManager.ts b/src/server/ApiManagers/DiagnosticManager.ts deleted file mode 100644 index 104985481..000000000 --- a/src/server/ApiManagers/DiagnosticManager.ts +++ /dev/null @@ -1,30 +0,0 @@ -import ApiManager, { Registration } from "./ApiManager"; -import { Method } from "../RouteManager"; -import request = require('request-promise'); - -export default class DiagnosticManager extends ApiManager { - - protected initialize(register: Registration): void { - - register({ - method: Method.GET, - subscription: "/serverHeartbeat", - onValidation: ({ res }) => res.send(true) - }); - - register({ - method: Method.GET, - subscription: "/solrHeartbeat", - onValidation: async ({ res }) => { - try { - await request("http://localhost:8983"); - res.send({ running: true }); - } catch (e) { - res.send({ running: false }); - } - } - }); - - } - -}
\ No newline at end of file diff --git a/src/server/ApiManagers/SearchManager.ts b/src/server/ApiManagers/SearchManager.ts index 7afecbb18..0e794fed6 100644 --- a/src/server/ApiManagers/SearchManager.ts +++ b/src/server/ApiManagers/SearchManager.ts @@ -4,13 +4,28 @@ import { Search } from "../Search"; const findInFiles = require('find-in-files'); import * as path from 'path'; import { pathToDirectory, Directory } from "./UploadManager"; +import { command_line, addBeforeExitHandler } from "../ActionUtilities"; +import request = require('request-promise'); +import { red, green, yellow, cyan } from "colors"; -export default class SearchManager extends ApiManager { +export class SearchManager extends ApiManager { protected initialize(register: Registration): void { register({ method: Method.GET, + subscription: "/startSolr", + onValidation: async ({ res }) => res.send((await SolrManager.SetRunning(true)) ? "Successfully started Solr!" : "Uh oh! Check the console for the error that occurred while starting Solr") + }); + + register({ + method: Method.GET, + subscription: "/stopSolr", + onValidation: async ({ res }) => res.send((await SolrManager.SetRunning(false)) ? "Successfully stopped Solr!" : "Uh oh! Check the console for the error that occurred while stopping Solr") + }); + + register({ + method: Method.GET, subscription: "/textsearch", onValidation: async ({ req, res }) => { const q = req.query.q; @@ -46,4 +61,36 @@ export default class SearchManager extends ApiManager { } +} + +export namespace SolrManager { + + export async function initializeSolr() { + console.log(cyan("\nInspecting Solr status...")); + try { + await request("http://localhost:8983"); + console.log(green('Solr already running\n')); + } catch (e) { + console.log(cyan('Initializing Solr...')); + await SolrManager.SetRunning(true); + } finally { + addBeforeExitHandler(async () => SolrManager.SetRunning(false)); + } + } + + export async function SetRunning(status: boolean): Promise<boolean> { + const args = status ? "start" : "stop -p 8983"; + console.log(`Solr management: trying to ${args}`); + try { + console.log(await command_line(`solr.cmd ${args}`, "../../solr-8.1.1/bin")); + return true; + } catch (e) { + console.log(red(`Solr management error: unable to ${args}`)); + if (status) { + process.exit(0); + } + return false; + } + } + }
\ No newline at end of file diff --git a/src/server/Message.ts b/src/server/Message.ts index aaee143e8..621abfd1e 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -50,6 +50,7 @@ export namespace MessageStore { export const GetFields = new Message<string[]>("Get Fields"); // send string[] of 'id' get Transferable[] back export const GetDocument = new Message<string>("Get Document"); export const DeleteAll = new Message<any>("Delete All"); + export const ConnectionTerminated = new Message<string>("Connection Terminated"); export const GetRefField = new Message<string>("Get Ref Field"); export const GetRefFields = new Message<string[]>("Get Ref Fields"); diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index 60c34aa44..de1d5eb25 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -10,6 +10,7 @@ import { GoogleCredentialsLoader } from "../credentials/CredentialsLoader"; import { logPort, addBeforeExitHandler } from "../ActionUtilities"; import { timeMap } from "../ApiManagers/UserManager"; import { green } from "colors"; +import { ExitHandlers } from ".."; export namespace WebSocket { @@ -52,8 +53,9 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); + + ExitHandlers.push(() => socket.broadcast.emit("connection_terminated", Date.now())); }); - addBeforeExitHandler(async () => { await new Promise<void>(resolve => endpoint.close(resolve)); }); endpoint.listen(socketPort); logPort("websocket", socketPort); } diff --git a/src/server/index.ts b/src/server/index.ts index 551ce3898..42b4f7ff2 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -10,7 +10,7 @@ import initializeServer from './Initialization'; import RouteManager, { Method, _success, _permission_denied, _error, _invalid, OnUnauthenticated } from './RouteManager'; import * as qs from 'query-string'; import UtilManager from './ApiManagers/UtilManager'; -import SearchManager from './ApiManagers/SearchManager'; +import { SearchManager, SolrManager } from './ApiManagers/SearchManager'; import UserManager from './ApiManagers/UserManager'; import { WebSocket } from './Websocket/Websocket'; import DownloadManager from './ApiManagers/DownloadManager'; @@ -21,18 +21,48 @@ import UploadManager from "./ApiManagers/UploadManager"; import { log_execution } from "./ActionUtilities"; import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager"; import GooglePhotosManager from "./ApiManagers/GooglePhotosManager"; -import DiagnosticManager from "./ApiManagers/DiagnosticManager"; import { yellow } from "colors"; export const publicDirectory = path.resolve(__dirname, "public"); export const filesDirectory = path.resolve(publicDirectory, "files"); +export const ExitHandlers = new Array<() => void>(); + /** * These are the functions run before the server starts * listening. Anything that must be complete * before clients can access the server should be run or awaited here. */ async function preliminaryFunctions() { + process.on('SIGINT', () => { + const { stdin, stdout, stderr } = process; + stdin.resume(); + stdout.resume(); + stderr.resume(); + ExitHandlers.forEach(handler => handler()); + console.log("Okay, now we're done..."); + // process.exit(0); + }); + + (process as any).on('cleanup', () => { + console.log("CLEANING UP!"); + }); + + process.on('exit', function () { + (process.emit as Function)('cleanup'); + }); + + //catch uncaught exceptions, trace, then exit normally + process.on('uncaughtException', function (e) { + console.log('Uncaught Exception...'); + process.exit(99); + }); + process.on('unhandledRejection', function (e) { + console.log('Unhandled Rejection...'); + process.exit(99); + }); + + await SolrManager.initializeSolr(); await GoogleCredentialsLoader.loadCredentials(); GoogleApiServerUtils.processProjectCredentials(); await DashUploadUtils.buildFileDirectories(); @@ -57,7 +87,6 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: new UserManager(), new UploadManager(), new DownloadManager(), - new DiagnosticManager(), new SearchManager(), new PDFManager(), new DeleteManager(), @@ -79,6 +108,12 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: onValidation: ({ res }) => res.redirect("/home") }); + addSupervisedRoute({ + method: Method.GET, + subscription: "/serverHeartbeat", + onValidation: ({ res }) => res.send(true) + }); + const serve: OnUnauthenticated = ({ req, res }) => { const detector = new mobileDetect(req.headers['user-agent'] || ""); const filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html'; |