From ad079a088ae9262a4a40a2f0d2a2c5d948140492 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 11 Dec 2019 03:57:09 -0500 Subject: somewhat functional daemon --- logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log | 274 +++++++++++++++++++++ ...npx ts-node-20965-Wed, 11 Dec 2019 03:38:32 GMT | 0 logs/server_pids.txt | 1 + src/server/ActionUtilities.ts | 22 +- src/server/ProcessManager.ts | 49 ++-- src/server/daemon/current_daemon_pid.txt | 1 + src/server/daemon/persistence_daemon.ts | 53 ++-- .../session_crashes_@ 2019-12-11T08:31:56.281Z.log | 1 + .../session_crashes_@ 2019-12-11T08:43:46.454Z.log | 2 + 9 files changed, 357 insertions(+), 46 deletions(-) create mode 100644 logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log delete mode 100644 logs/npx ts-node-20965-Wed, 11 Dec 2019 03:38:32 GMT create mode 100644 logs/server_pids.txt create mode 100644 src/server/daemon/current_daemon_pid.txt create mode 100644 src/server/daemon/session_crashes_@ 2019-12-11T08:31:56.281Z.log create mode 100644 src/server/daemon/session_crashes_@ 2019-12-11T08:43:46.454Z.log diff --git a/logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log b/logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log new file mode 100644 index 000000000..37e232d48 --- /dev/null +++ b/logs/npm-2-Wed, 11 Dec 2019 08:44:28 GMT.log @@ -0,0 +1,274 @@ + +> dash@1.0.0 start-spawn /Users/swilkinss2012/Documents/GitHub/Dash-Web +> cross-env SPAWNED=true NODE_OPTIONS=--max_old_space_size=4096 ts-node-dev -- src/server/index.ts + +Using ts-node version 7.0.1, typescript version 3.7.2 +objc[9678]: Class GNotificationCenterDelegate is implemented in both /Users/swilkinss2012/Documents/GitHub/Dash-Web/node_modules/sharp/vendor/lib/libgio-2.0.0.dylib (0x10838d578) and /Users/swilkinss2012/Documents/GitHub/Dash-Web/node_modules/canvas/build/Release/libgio-2.0.0.dylib (0x10afd9578). One of the two will be used. Which one is undefined. + +starting execution of preliminary functions... +(node:9678) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect. +completed preliminary functions +. +Starting type checking and linting service... +Using 1 worker with 2048MB memory limit + +running server in development mode + +registering server routes... +all server routes have been successfully registered: +/ +/activity +/buxton +/delete +/deleteAll +/deleteWithAux +/deleteWithGoogleCredentials +/doc/:docId +/downloadId/:docId +/environment/:key +/getCurrentUser +/getUserDocumentId +/getUsers +/googleDocs/:sector/:action +/googlePhotosMediaDownload +/googlePhotosMediaUpload +/home +/imageHierarchyExport/:docId +/inspectImage +/persist +/pull +/readGoogleAccessToken +/search +/serializeDoc/:docId +/serverHeartbeat +/shutdown +/solr/:action +/textsearch +/thumbnail/:filename +/upload +/uploadDoc +/uploadURI +/version +/writeGoogleAccessToken + +websocket listening on port 4321 +server listening on port 1050 + +ℹ 「wdm」: wait until bundle finished: /serverHeartbeat +user samuel_wilkins@brown.edu has connected to the web socket +user samuel_wilkins@brown.edu has connected to the web socket +ℹ 「wdm」: wait until bundle finished: /serverHeartbeat +ℹ 「wdm」: wait until bundle finished: /serverHeartbeat +ℹ 「wdm」: wait until bundle finished: /serverHeartbeat +ℹ 「wdm」: wait until bundle finished: /serverHeartbeat +user samuel_wilkins@brown.edu has connected to the web socket +user samuel_wilkins@brown.edu has connected to the web socket +Type checking and linting in progress... +webpack built 8f6b743d91fd3862683b in 47419ms +⚠ 「wdm」: Hash: 8f6b743d91fd3862683b +Version: webpack 4.36.1 +Time: 47419ms +Built at: 12/11/2019 3:45:31 AM + Asset Size Chunks  Chunk Names +275711e56bd1bc79fdff544a3d7dbfae.png 289 bytes  [emitted] +32f1593298e6e7bee5673bf647328d72.png 429 bytes  [emitted] +718c914a99a2136c01c84e01f63e505a.png 829 bytes  [emitted] +906f1a1816c2a03b5c7612f6aa2ceece.png 281 bytes  [emitted] + assets/downarrow.png 3.28 KiB  [emitted] + assets/env.json 360 bytes  [emitted] + assets/google_photos.png 114 KiB  [emitted] + assets/google_tags.png 7.9 KiB  [emitted] + assets/loading.gif 112 KiB  [emitted] + assets/pdf.worker.js 1.55 MiB  [emitted] + bundle.js 20.8 MiB bundle [emitted] bundle + bundle.js.map 23.3 MiB bundle [emitted] bundle + debug/repl.html 348 bytes  [emitted] + debug/test.html 245 bytes  [emitted] + debug/viewer.html 357 bytes  [emitted] +e7a34b49f3c49ca0c25c76b30cd09e12.png 445 bytes  [emitted] + imageUpload.js 20.8 MiB imageUpload [emitted] imageUpload + imageUpload.js.map 23.3 MiB imageUpload [emitted] imageUpload + index.html 593 bytes  [emitted] + inkControls.js 116 KiB inkControls [emitted] inkControls + inkControls.js.map 122 KiB inkControls [emitted] inkControls + mobile/image.html 333 bytes  [emitted] + mobile/ink.html 252 bytes  [emitted] + repl.js 9.31 MiB repl [emitted] repl + repl.js.map 10.5 MiB repl [emitted] repl + test.js 1.2 MiB test [emitted] test + test.js.map 1.42 MiB test [emitted] test + test.pdf 53.6 KiB  [emitted] + vendors~pdfjsWorker.js 1.55 MiB vendors~pdfjsWorker [emitted] vendors~pdfjsWorker + vendors~pdfjsWorker.js.map 1.87 MiB vendors~pdfjsWorker [emitted] vendors~pdfjsWorker + viewer.js 9.47 MiB viewer [emitted] viewer + viewer.js.map 10.7 MiB viewer [emitted] viewer +Entrypoint bundle = bundle.js bundle.js.map +Entrypoint viewer = viewer.js viewer.js.map +Entrypoint repl = repl.js repl.js.map +Entrypoint test = test.js test.js.map +Entrypoint inkControls = inkControls.js inkControls.js.map +Entrypoint imageUpload = imageUpload.js imageUpload.js.map +[19] multi ./src/client/views/Main.tsx webpack-hot-middleware/client?reload=true 40 bytes {bundle} [built] +[20] multi ./src/debug/Viewer.tsx webpack-hot-middleware/client?reload=true 40 bytes {viewer} [built] +[21] multi ./src/debug/Repl.tsx webpack-hot-middleware/client?reload=true 40 bytes {repl} [built] +[22] multi ./src/debug/Test.tsx webpack-hot-middleware/client?reload=true 40 bytes {test} [built] +[23] multi ./src/mobile/InkControls.tsx webpack-hot-middleware/client?reload=true 40 bytes {inkControls} [built] +[24] multi ./src/mobile/ImageUpload.tsx webpack-hot-middleware/client?reload=true 40 bytes {imageUpload} [built] + [./node_modules/mobx-react/index.module.js] 48.8 KiB {bundle} {viewer} {repl} {imageUpload} [built] + [./node_modules/mobx/lib/mobx.module.js] 175 KiB {bundle} {viewer} {repl} {imageUpload} [built] + [./node_modules/webpack-hot-middleware/client.js?reload=true] (webpack)-hot-middleware/client.js?reload=true 7.68 KiB {bundle} {viewer} {repl} {test} {inkControls} {imageUpload} [built] + [./src/client/views/Main.tsx] 4.03 KiB {bundle} [built] + [./src/debug/Repl.tsx] 6.87 KiB {repl} [built] + [./src/debug/Test.tsx] 1.02 KiB {test} [built] + [./src/debug/Viewer.tsx] 12.1 KiB {viewer} [built] + [./src/mobile/ImageUpload.tsx] 9.97 KiB {imageUpload} [built] + [./src/mobile/InkControls.tsx] 14 bytes {inkControls} [built] + + 1494 hidden modules + +WARNING in ./node_modules/typescript/lib/typescript.js 5121:41-60 +Critical dependency: the request of a dependency is an expression + @ ./src/client/util/Scripting.ts + @ ./src/debug/Viewer.tsx + @ multi ./src/debug/Viewer.tsx webpack-hot-middleware/client?reload=true +ℹ 「wdm」: Compiled with warnings. +ℹ 「wdm」: Compiling... +webpack building... +ℹ 「wdm」: wait until bundle finished: /login +ℹ 「wdm」: wait until bundle finished: /login +ℹ 「wdm」: wait until bundle finished: /login +ℹ 「wdm」: wait until bundle finished: /login +ℹ 「wdm」: wait until bundle finished: /login +Type checking and linting in progress... +webpack built 8f6b743d91fd3862683b in 615ms +⚠ 「wdm」: Hash: 8f6b743d91fd3862683b +Version: webpack 4.36.1 +Time: 615ms +Built at: 12/11/2019 3:45:33 AM + Asset Size Chunks Chunk Names +275711e56bd1bc79fdff544a3d7dbfae.png 289 bytes   +32f1593298e6e7bee5673bf647328d72.png 429 bytes   +718c914a99a2136c01c84e01f63e505a.png 829 bytes   +906f1a1816c2a03b5c7612f6aa2ceece.png 281 bytes   + bundle.js 20.8 MiB bundle bundle + bundle.js.map 23.3 MiB bundle bundle +e7a34b49f3c49ca0c25c76b30cd09e12.png 445 bytes   + imageUpload.js 20.8 MiB imageUpload imageUpload + imageUpload.js.map 23.3 MiB imageUpload imageUpload + inkControls.js 116 KiB inkControls inkControls + inkControls.js.map 122 KiB inkControls inkControls + repl.js 9.31 MiB repl repl + repl.js.map 10.5 MiB repl repl + test.js 1.2 MiB test test + test.js.map 1.42 MiB test test + vendors~pdfjsWorker.js 1.55 MiB vendors~pdfjsWorker vendors~pdfjsWorker + vendors~pdfjsWorker.js.map 1.87 MiB vendors~pdfjsWorker vendors~pdfjsWorker + viewer.js 9.47 MiB viewer viewer + viewer.js.map 10.7 MiB viewer viewer +Entrypoint bundle = bundle.js bundle.js.map +Entrypoint viewer = viewer.js viewer.js.map +Entrypoint repl = repl.js repl.js.map +Entrypoint test = test.js test.js.map +Entrypoint inkControls = inkControls.js inkControls.js.map +Entrypoint imageUpload = imageUpload.js imageUpload.js.map +[19] multi ./src/client/views/Main.tsx webpack-hot-middleware/client?reload=true 40 bytes {bundle} +[20] multi ./src/debug/Viewer.tsx webpack-hot-middleware/client?reload=true 40 bytes {viewer} +[21] multi ./src/debug/Repl.tsx webpack-hot-middleware/client?reload=true 40 bytes {repl} +[22] multi ./src/debug/Test.tsx webpack-hot-middleware/client?reload=true 40 bytes {test} +[23] multi ./src/mobile/InkControls.tsx webpack-hot-middleware/client?reload=true 40 bytes {inkControls} +[24] multi ./src/mobile/ImageUpload.tsx webpack-hot-middleware/client?reload=true 40 bytes {imageUpload} + [./node_modules/mobx-react/index.module.js] 48.8 KiB {bundle} {viewer} {repl} {imageUpload} + [./node_modules/mobx/lib/mobx.module.js] 175 KiB {bundle} {viewer} {repl} {imageUpload} + [./node_modules/webpack-hot-middleware/client.js?reload=true] (webpack)-hot-middleware/client.js?reload=true 7.68 KiB {bundle} {viewer} {repl} {test} {inkControls} {imageUpload} + [./src/client/views/Main.tsx] 4.03 KiB {bundle} + [./src/debug/Repl.tsx] 6.87 KiB {repl} + [./src/debug/Test.tsx] 1.02 KiB {test} + [./src/debug/Viewer.tsx] 12.1 KiB {viewer} + [./src/mobile/ImageUpload.tsx] 9.97 KiB {imageUpload} + [./src/mobile/InkControls.tsx] 14 bytes {inkControls} + + 1494 hidden modules + +WARNING in ./node_modules/typescript/lib/typescript.js 5121:41-60 +Critical dependency: the request of a dependency is an expression + @ ./src/client/util/Scripting.ts + @ ./src/debug/Viewer.tsx + @ multi ./src/debug/Viewer.tsx webpack-hot-middleware/client?reload=true +ℹ 「wdm」: Compiled with warnings. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(81,21): +prefer-const: Identifier 'marks' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(83,25): +prefer-const: Identifier 'tr' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(86,21): +prefer-const: Identifier 'isValidColor' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(87,25): +prefer-const: Identifier 's' is never reassigned; use 'const' instead of 'var'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(89,36): +triple-equals: == should be === +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(90,18): +semicolon: Missing semicolon +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(99,21): +prefer-const: Identifier 'tr' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(100,21): +prefer-const: Identifier 'marks' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(126,25): +prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(141,25): +prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(154,66): +no-unnecessary-type-assertion: This assertion is unnecessary since it does not change the type of the expression. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(155,25): +prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(155,33): +no-unnecessary-type-assertion: This assertion is unnecessary since it does not change the type of the expression. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(160,25): +prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextRules.ts(192,29): +prefer-const: Identifier 'doc' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(640,134): +semicolon: Missing semicolon +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(649,21): +prefer-const: Identifier 'expand' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(650,21): +prefer-const: Identifier 'tr' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(654,138): +semicolon: Missing semicolon +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/util/RichTextSchema.tsx(658,10): +semicolon: Missing semicolon +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionView.tsx(243,13): +prefer-const: Identifier 'main' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionView.tsx(244,13): +prefer-const: Identifier 'next' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/collections/CollectionView.tsx(245,13): +prefer-const: Identifier 'prev' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/DocumentDecorations.tsx(88,21): +prefer-const: Identifier 'selectionTitleFieldKey' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/DocumentDecorations.tsx(95,8): +semicolon: Missing semicolon +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/EditableView.tsx(123,13): +prefer-const: Identifier 'wasFocused' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/MainView.tsx(289,11): +semicolon: Missing semicolon +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/DocumentView.tsx(151,25): +prefer-const: Identifier 'any' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(910,13): +prefer-const: Identifier 'prosediv' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(911,13): +prefer-const: Identifier 'keeplocation' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(913,13): +prefer-const: Identifier 'pos' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(928,17): +prefer-const: Identifier 'pcords' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(929,17): +prefer-const: Identifier 'node' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(935,21): +prefer-const: Identifier 'lastNode' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(976,71): +semicolon: Missing semicolon +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(987,17): +prefer-const: Identifier '$pos' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(1001,25): +prefer-const: Identifier '$olist_pos' is never reassigned; use 'const' instead of 'let'. +WARNING in /Users/swilkinss2012/Documents/GitHub/Dash-Web/src/client/views/nodes/FormattedTextBox.tsx(1103,17): +prefer-const: Identifier 'newHeight' is never reassigned; use 'const' instead of 'let'. +No type errors found +Version: typescript 3.7.2, tslint 5.18.0 +Time: 5473ms diff --git a/logs/npx ts-node-20965-Wed, 11 Dec 2019 03:38:32 GMT b/logs/npx ts-node-20965-Wed, 11 Dec 2019 03:38:32 GMT deleted file mode 100644 index e69de29bb..000000000 diff --git a/logs/server_pids.txt b/logs/server_pids.txt new file mode 100644 index 000000000..2aa143f24 --- /dev/null +++ b/logs/server_pids.txt @@ -0,0 +1 @@ +9675 created at Wed, 11 Dec 2019 08:44:28 GMT diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts index 2173f4369..9bdc4ed93 100644 --- a/src/server/ActionUtilities.ts +++ b/src/server/ActionUtilities.ts @@ -1,12 +1,11 @@ -import * as fs from 'fs'; +import { readFile, writeFile, exists, mkdir, unlink } from 'fs'; import { ExecOptions } from 'shelljs'; -import { exec, spawn } from 'child_process'; +import { exec } from 'child_process'; import * as path from 'path'; import * as rimraf from "rimraf"; import { yellow, Color } from 'colors'; const projectRoot = path.resolve(__dirname, "../../"); - export function pathFromRoot(relative: string) { return path.resolve(projectRoot, relative); } @@ -21,24 +20,17 @@ export const command_line = (command: string, fromDirectory?: string) => { }); }; -export async function spawn_detached_process(command: string, args?: readonly string[]) { - const out = path.resolve(projectRoot, `./logs/${command}-${process.pid}-${new Date().toUTCString()}`); - const child_out = fs.openSync(out, 'a'); - const child_error = fs.openSync(out, 'a'); - spawn(command, args, { detached: true, stdio: ["ignore", child_out, child_error] }).unref(); -} - export const read_text_file = (relativePath: string) => { const target = path.resolve(__dirname, relativePath); return new Promise((resolve, reject) => { - fs.readFile(target, (err, data) => err ? reject(err) : resolve(data.toString())); + readFile(target, (err, data) => err ? reject(err) : resolve(data.toString())); }); }; export const write_text_file = (relativePath: string, contents: any) => { const target = path.resolve(__dirname, relativePath); return new Promise((resolve, reject) => { - fs.writeFile(target, contents, (err) => err ? reject(err) : resolve()); + writeFile(target, contents, (err) => err ? reject(err) : resolve()); }); }; @@ -93,10 +85,10 @@ export function msToTime(duration: number) { } export const createIfNotExists = async (path: string) => { - if (await new Promise(resolve => fs.exists(path, resolve))) { + if (await new Promise(resolve => exists(path, resolve))) { return true; } - return new Promise(resolve => fs.mkdir(path, error => resolve(error === null))); + return new Promise(resolve => mkdir(path, error => resolve(error === null))); }; export async function Prune(rootDirectory: string): Promise { @@ -104,4 +96,4 @@ export async function Prune(rootDirectory: string): Promise { return error === null; } -export const Destroy = (mediaPath: string) => new Promise(resolve => fs.unlink(mediaPath, error => resolve(error === null))); +export const Destroy = (mediaPath: string) => new Promise(resolve => unlink(mediaPath, error => resolve(error === null))); diff --git a/src/server/ProcessManager.ts b/src/server/ProcessManager.ts index 2237f9e1b..671f0a234 100644 --- a/src/server/ProcessManager.ts +++ b/src/server/ProcessManager.ts @@ -1,7 +1,9 @@ -import { writeFileSync, unlinkSync, existsSync, mkdirSync } from "fs"; -import { pathFromRoot, log_execution, spawn_detached_process } from './ActionUtilities'; -import { resolve } from "path"; -import { red, yellow } from "colors"; +import { existsSync, mkdirSync, createWriteStream } from "fs"; +import { pathFromRoot, log_execution } from './ActionUtilities'; +import { red, green } from "colors"; +import rimraf = require("rimraf"); +import { ChildProcess, spawn } from "child_process"; +import { Stream } from "stream"; const daemonPath = pathFromRoot("./src/server/daemon/persistence_daemon.ts"); @@ -9,22 +11,33 @@ export namespace ProcessManager { export async function initialize() { const logPath = pathFromRoot("./logs"); - const filePath = resolve(logPath, "./server_pids.txt"); - const exists = existsSync(logPath); - if (exists) { - unlinkSync(filePath); - } else { - mkdirSync(logPath); + if (existsSync(logPath)) { + if (!process.env.SPAWNED) { + await new Promise(resolve => rimraf(logPath, resolve)); + } } - const { pid } = process; - if (process.env.SPAWNED === "true") { - writeFileSync(filePath, `${pid} created at ${new Date().toUTCString()}\n`); + mkdirSync(logPath); + } + + function generate_log_name(command: string, args?: readonly string[]) { + return pathFromRoot(`./logs/${command}-${args?.length}-${new Date().toUTCString()}.log`); + } + + export type Sink = "pipe" | "ipc" | "ignore" | "inherit" | Stream | number | null | undefined; + + export async function spawn_detached(command: string, args?: readonly string[], out?: Sink): Promise { + if (!out) { + const logStream = createWriteStream(generate_log_name(command, args)); + out = await new Promise(resolve => logStream.on("open", resolve)); } + const child = spawn(command, args, { detached: true, stdio: ["ignore", out, out] }); + child.unref(); + return child; } let daemonInitialized = false; export async function trySpawnDaemon() { - if (!daemonInitialized) { + if (!process.env.SPAWNED && !daemonInitialized) { daemonInitialized = true; await log_execution({ startMessage: "\ninitializing persistence daemon", @@ -32,13 +45,15 @@ export namespace ProcessManager { const success = error === null && result !== undefined; if (!success) { console.log(red("failed to initialize the persistance daemon")); + console.log(error); process.exit(0); } - return "persistence daemon process closed"; + return "failsafe daemon process successfully spawned"; }, - action: () => spawn_detached_process("npx ts-node", [daemonPath]), - color: yellow + action: () => spawn_detached('npx', ['ts-node', daemonPath], process.stdout), + color: green }); + console.log(); } } diff --git a/src/server/daemon/current_daemon_pid.txt b/src/server/daemon/current_daemon_pid.txt new file mode 100644 index 000000000..f3cd0298c --- /dev/null +++ b/src/server/daemon/current_daemon_pid.txt @@ -0,0 +1 @@ +9626 \ No newline at end of file diff --git a/src/server/daemon/persistence_daemon.ts b/src/server/daemon/persistence_daemon.ts index 3eb17a9b4..099c7898c 100644 --- a/src/server/daemon/persistence_daemon.ts +++ b/src/server/daemon/persistence_daemon.ts @@ -1,22 +1,41 @@ import * as request from "request-promise"; -import { log_execution, spawn_detached_process } from "../ActionUtilities"; -import { red, yellow, cyan, green } from "colors"; +import { log_execution, pathFromRoot } from "../ActionUtilities"; +import { red, yellow, cyan, green, Color } from "colors"; import * as nodemailer from "nodemailer"; import { MailOptions } from "nodemailer/lib/json-transport"; -import { writeFileSync } from "fs"; +import { writeFileSync, appendFileSync, createWriteStream, existsSync } from "fs"; import { resolve } from 'path'; +import { ChildProcess } from "child_process"; +import { ProcessManager } from "../ProcessManager"; + +console.log(yellow("Initializing daemon...")); + +process.on('SIGINT', () => current_backup?.kill("SIGTERM")); + +const crashLogPath = resolve(__dirname, `./session_crashes_${timestamp()}.log`); +function addLogEntry(message: string, color: Color) { + const formatted = color(`${message} ${timestamp()}.`); + console.log(formatted); + appendFileSync(crashLogPath, `${formatted}\n`); +} const LOCATION = "http://localhost"; const recipient = "samuel_wilkins@brown.edu"; let restarting = false; -writeFileSync(resolve(__dirname, "./current_pid.txt"), process.pid); +const frequency = 10; +const { pid } = process; +writeFileSync(resolve(__dirname, "./current_daemon_pid.txt"), pid); +console.log(cyan(`${pid} written to ./current_daemon_pid.txt`)); function timestamp() { return `@ ${new Date().toISOString()}`; } +let current_backup: ChildProcess | undefined = undefined; + async function listen() { + console.log(yellow(`Beginning to poll server heartbeat every ${frequency} seconds...\n`)); if (!LOCATION) { console.log(red("No location specified for persistence daemon. Please include as a command line environment variable or in a .env file.")); process.exit(0); @@ -28,34 +47,40 @@ async function listen() { let error: any; try { await request.get(heartbeat); + if (restarting) { + addLogEntry("Backup server successfully restarted", green); + } + restarting = false; } catch (e) { error = e; } finally { if (error) { if (!restarting) { restarting = true; - console.log(yellow("Detected a server crash!")); + addLogEntry("Detected a server crash", red); + current_backup?.kill(); await log_execution({ startMessage: "Sending crash notification email", endMessage: ({ error, result }) => { const success = error === null && result === true; - return (success ? `Notification successfully sent to ` : `Failed to notify `) + recipient; + return `${(success ? `Notification successfully sent to` : `Failed to notify`)} ${recipient} ${timestamp()}`; }, action: async () => notify(error || "Hmm, no error to report..."), color: cyan }); - console.log(await log_execution({ + current_backup = await log_execution({ startMessage: "Initiating server restart", - endMessage: "Server successfully restarted", - action: () => spawn_detached_process(`npm run start-spawn`), + endMessage: ({ result, error }) => { + const success = error === null && result !== undefined; + return success ? "Child process spawned.." : `An error occurred while attempting to restart the server:\n${error}`; + }, + action: () => ProcessManager.spawn_detached('npm', ['run', 'start-spawn']), color: green - })); - restarting = false; + }); + writeFileSync(pathFromRoot("./logs/current_server_pid.txt"), `${current_backup?.pid ?? -1} created ${timestamp()}\n`); } else { console.log(yellow(`Callback ignored because restarting already initiated ${timestamp()}`)); } - } else { - console.log(green(`No issues detected ${timestamp()}`)); } } }, 1000 * 10); @@ -85,7 +110,7 @@ async function notify(error: any) { text: emailText(error) } as MailOptions; return new Promise(resolve => { - smtpTransport.sendMail(mailOptions, (dispatchError: Error | null) => { console.log(dispatchError); resolve(dispatchError === null); }); + smtpTransport.sendMail(mailOptions, (dispatchError: Error | null) => resolve(dispatchError === null)); }); } diff --git a/src/server/daemon/session_crashes_@ 2019-12-11T08:31:56.281Z.log b/src/server/daemon/session_crashes_@ 2019-12-11T08:31:56.281Z.log new file mode 100644 index 000000000..32b7810ea --- /dev/null +++ b/src/server/daemon/session_crashes_@ 2019-12-11T08:31:56.281Z.log @@ -0,0 +1 @@ +Detected a server crash @ 2019-12-11T08:32:36.317Z diff --git a/src/server/daemon/session_crashes_@ 2019-12-11T08:43:46.454Z.log b/src/server/daemon/session_crashes_@ 2019-12-11T08:43:46.454Z.log new file mode 100644 index 000000000..ebb6843c2 --- /dev/null +++ b/src/server/daemon/session_crashes_@ 2019-12-11T08:43:46.454Z.log @@ -0,0 +1,2 @@ +Detected a server crash @ 2019-12-11T08:44:26.494Z. +Backup server successfully restarted @ 2019-12-11T08:45:33.644Z. -- cgit v1.2.3-70-g09d2