Package Exports
- graceful-shutdown-manager
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (graceful-shutdown-manager) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
node-graceful-shutdown-manager
All in one solution to gracefully shutdown your application through a simple delayed shutdown process. Optionally supports nodemon. (initialise, free, lock critical sections)
Use for any node.js project which might be negatively impacted by immediate termination.
Locking is a mechanism which avoids beginning the shutdown process until a block of code has finished. Wrap important code such as file writing with lock()
and unlock()
. This will ensure no corruption occurs or resources are freed when they might be required. Ensure the code within the lock is synchronous. I recommend the fs-extra
package for a drop-in fs promise replacement.
Template (example is below this template)
main.js
Program entry
async function main() {
global.gsm = require("graceful-shutdown-manager").Manager.create();
try {
global.database = require("./Database").create();
// Start
global.gsm.events.on("init", async () => {
await global.database.init();
});
// Main
global.gsm.events.on("main", async () => {
// Run code after everything has been initialised
global.database.lock();
// Write to a file or something mission critical. The program won't exit until it's unlocked. Make sure you await the result of a write.
global.database.unlock();
});
// Cleanup
global.gsm.events.on("free", async () => {
await global.database.free();
});
await global.gsm.main(); // Calls init, and then main, free will only be called when invoked
} catch (e) {
// All program errors end up here, even during the initialisation phase
console.error(e);
// await global.gsm.free(); // Use if you just want to free everything, without shutting down, useful for live reloading without nodemon (bad for runtime memory leaks)
global.gsm.exit(); // Use instead of 'process.exit()' to gracefully shut down
}
}
main();
Database.js
const Base = require('graceful-shutdown-manager').Base;
class Database extends Base {
constructor() {
super();
this.events.on("init", async () => {
await this.connect();
});
this.events.on("free", async () => {
await this.close();
});
}
connect() {
return new Promise((resolve, reject)) => {
// Connect and resolve, mysql uses callbacks
resolve();
});
}
close() {
return new Promise((resolve, reject)) => {
// End and resolve, mysql uses callbacks
resolve();
});
}
}
module.exports = Database;
Example (mysql, express, locking)
main.js
async function main() {
global.gsm = require("graceful-shutdown-manager").Manager.create();
try {
global.database = require("./Database").create();
global.express = require("./Express").create();
global.app = require("./App").create();
// Start
global.gsm.events.on("init", async () => {
await global.database.init();
await global.express.init();
await global.app.init();
});
// Main
global.gsm.events.on("main", async () => {
// Run code after everything has been initialised
});
// Cleanup
global.gsm.events.on("free", async () => {
await global.express.free();
await global.database.free();
await global.app.free();
});
await global.gsm.main(); // Calls init, and then main, free will only be called when invoked
} catch (e) {
// All program errors end up here, even during the initialisation phase
console.error(e);
// await global.gsm.free(); // Use if you just want to free everything, without shutting down, useful for live reloading without nodemon (bad for runtime memory leaks)
global.gsm.exit(); // Use instead of 'process.exit()' to gracefully shut down
}
}
main();
Database.js
const Base = require('graceful-shutdown-manager').Base;
const Mysql = require('mysql');
const connection = Mysql.createConnection({
host : 'localhost',
user : 'user',
password : 'pass',
database : 'db'
});
class Database extends Base {
constructor() {
super();
this.events.on("init", async () => {
await this.connect();
});
this.events.on("free", async () => {
await this.close();
});
}
connect() {
return new Promise((resolve, reject)) => {
connection.connect((err) => {
if (err) {
console.error('error connecting: ' + err.stack);
reject(err);
return;
}
console.log('connected as id ' + connection.threadId);
resolve();
});
});
}
close() {
return new Promise((resolve, reject)) => {
connection.end((err) => {
if (err) {
console.error('error: ' + err.stack);
reject(err);
return;
}
resolve();
});
});
}
}
module.exports = Database;
Express.js
const Base = require('graceful-shutdown-manager').Base;
const Base = require('..').Base;
const express = require('express');
const app = express();
class Express extends Base {
constructor() {
super();
// Initialise
this.events.on("init", async () => {
await this.listen();
});
// Free
this.events.on("free", async () => {
await this.close();
});
}
listen() {
return new Promise((resolve, reject)) => {
app.listen(3000, () => {
console.log(`Listening at http://localhost:3000`);
resolve();
})
});
}
close() {
return new Promise((resolve, reject)) => {
app.close(() => {
console.log(`Stopped listening`);
resolve();
})
});
}
}
module.exports = Express;
App.js
/*
This file demonstrated the locking mechanism, which
stops a program exiting while it's doing something important.
An example would be writing to a file, processing a request, etc.
*/
const Base = require("graceful-shutdown-manager").Base;
const fs = require("fs").promises;
class App extends Base {
constructor() {
super();
// Initialise
this.events.on("init", async () => {
this.logs = [];
// Store logs to an array (for file saving), by overwriting the function
this.oldConsoleLog = console.log;
console.log = (...args) => {
this.logs.push(args.join(" "));
this.oldConsoleLog(...args);
};
// Save every minute
this.saveInterval = setInterval(() => {
this.saveLog();
}, 1 * 60 * 1000);
});
// Free
this.events.on("free", async () => {
clearInterval(this.saveInterval);
await this.saveLog();
console.log = this.oldConsoleLog;
});
}
async saveLog() {
// This will cause the manager to wait until this object is unlocked before exiting, saving you a corrupted file
this.lock();
await fs.writeFile("log.txt", JSON.stringify(this.logs, NULL, 2));
this.unlock();
}
}
module.exports = App;
Game id
Visit the workshop page, in the url bar it will contain an 'appid'
The below example shows '4920' as the id, which is for Natural Selection 2: https://steamcommunity.com/workshop/browse/?appid=4920
Install
- Install git and nodejs.
- Pase in cmd/terminal:
git clone https://github.com/c-ridgway/node-workshop-downloader.git && cd node-workshop-downloader
- Add your steam API key and game id to
config.json
- Open
run.bat
for Windows andrun.sh
for Linux.