index.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. 'use strict';
  2. process.env.NODE_CONFIG_DIR = `${__dirname}/config`;
  3. const async = require('async');
  4. const fs = require('fs');
  5. const Discord = require("discord.js");
  6. const client = new Discord.Client();
  7. const db = require('./logic/db');
  8. const app = require('./logic/app');
  9. const mail = require('./logic/mail');
  10. const api = require('./logic/api');
  11. const io = require('./logic/io');
  12. const stations = require('./logic/stations');
  13. const songs = require('./logic/songs');
  14. const playlists = require('./logic/playlists');
  15. const cache = require('./logic/cache');
  16. const notifications = require('./logic/notifications');
  17. const punishments = require('./logic/punishments');
  18. const logger = require('./logic/logger');
  19. const tasks = require('./logic/tasks');
  20. const i18n = require('./logic/i18n');
  21. const config = require('config');
  22. let currentComponent;
  23. let initializedComponents = [];
  24. let lockdownB = false;
  25. process.on('uncaughtException', err => {
  26. if (lockdownB || err.code === 'ECONNREFUSED' || err.code === 'UNCERTAIN_STATE') return;
  27. console.log(`UNCAUGHT EXCEPTION: ${err.stack}`);
  28. });
  29. const getError = (err) => {
  30. let error = 'An error occurred.';
  31. if (typeof err === "string") error = err;
  32. else if (err.message) {
  33. if (err.message !== 'Validation failed') error = err.message;
  34. else error = err.errors[Object.keys(err.errors)].message;
  35. }
  36. return error;
  37. };
  38. client.on('ready', () => {
  39. console.log(`Logged in to Discord as ${client.user.username}#${client.user.discriminator}`);
  40. initialize();
  41. });
  42. client.on('disconnect', (err) => {
  43. console.log(`Discord disconnected. Code: ${err.code}.`);
  44. });
  45. client.login(config.get('apis.discord.token'));
  46. const logToDiscord = (message, color, type, critical, extraFields, cb = ()=>{}) => {
  47. let richEmbed = new Discord.RichEmbed();
  48. richEmbed.setAuthor("Musare Logger", config.get("domain")+"/favicon-194x194.png", config.get("domain"));
  49. richEmbed.setColor(color);
  50. richEmbed.setDescription(message);
  51. //richEmbed.setFooter("Footer", "https://musare.com/favicon-194x194.png");
  52. //richEmbed.setImage("https://musare.com/favicon-194x194.png");
  53. //richEmbed.setThumbnail("https://musare.com/favicon-194x194.png");
  54. richEmbed.setTimestamp(new Date());
  55. richEmbed.setTitle("MUSARE ALERT");
  56. richEmbed.setURL(config.get("domain"));
  57. richEmbed.addField("Type:", type, true);
  58. richEmbed.addField("Critical:", (critical) ? 'True' : 'False', true);
  59. extraFields.forEach((extraField) => {
  60. richEmbed.addField(extraField.name, extraField.value, extraField.inline);
  61. });
  62. console.log(config.get('apis.discord.loggingChannel'));
  63. client.channels.get(config.get('apis.discord.loggingChannel')).send("", { embed: richEmbed }).then(() => {
  64. cb();
  65. }).then((reason) => {
  66. cb(reason);
  67. });
  68. };
  69. function lockdown() {
  70. if (lockdownB) return;
  71. lockdownB = true;
  72. initializedComponents.forEach((component) => {
  73. component._lockdown();
  74. });
  75. console.log("Backend locked down.");
  76. }
  77. function errorCb(message, err, component) {
  78. err = getError(err);
  79. lockdown();
  80. logToDiscord(message, "#FF0000", message, true, [{name: "Error:", value: err, inline: false}, {name: "Component:", value: component, inline: true}]);
  81. }
  82. let initialized = false;
  83. function initialize() {
  84. if (!initialized) initialized = true;
  85. else return;
  86. async.waterfall([
  87. // setup our translation
  88. (next) => {
  89. currentComponent = 'Translation';
  90. i18n.init(next);
  91. },
  92. // setup our Redis cache
  93. (next) => {
  94. currentComponent = 'Cache';
  95. cache.init(config.get('redis').url, config.get('redis').password, errorCb, () => {
  96. next();
  97. });
  98. },
  99. // setup our MongoDB database
  100. (next) => {
  101. initializedComponents.push(cache);
  102. currentComponent = 'DB';
  103. db.init(config.get("mongo").url, errorCb, next);
  104. },
  105. // setup the express server
  106. (next) => {
  107. initializedComponents.push(db);
  108. currentComponent = 'App';
  109. app.init(next);
  110. },
  111. // setup the mail
  112. (next) => {
  113. initializedComponents.push(app);
  114. currentComponent = 'Mail';
  115. mail.init(next);
  116. },
  117. // setup the socket.io server (all client / server communication is done over this)
  118. (next) => {
  119. initializedComponents.push(mail);
  120. currentComponent = 'IO';
  121. io.init(next);
  122. },
  123. // setup the punishment system
  124. (next) => {
  125. initializedComponents.push(io);
  126. currentComponent = 'Punishments';
  127. punishments.init(next);
  128. },
  129. // setup the notifications
  130. (next) => {
  131. initializedComponents.push(punishments);
  132. currentComponent = 'Notifications';
  133. notifications.init(config.get('redis').url, config.get('redis').password, errorCb, next);
  134. },
  135. // setup the stations
  136. (next) => {
  137. initializedComponents.push(notifications);
  138. currentComponent = 'Stations';
  139. stations.init(next)
  140. },
  141. // setup the songs
  142. (next) => {
  143. initializedComponents.push(stations);
  144. currentComponent = 'Songs';
  145. songs.init(next)
  146. },
  147. // setup the playlists
  148. (next) => {
  149. initializedComponents.push(songs);
  150. currentComponent = 'Playlists';
  151. playlists.init(next)
  152. },
  153. // setup the API
  154. (next) => {
  155. initializedComponents.push(playlists);
  156. currentComponent = 'API';
  157. api.init(next)
  158. },
  159. // setup the logger
  160. (next) => {
  161. initializedComponents.push(api);
  162. currentComponent = 'Logger';
  163. logger.init(next)
  164. },
  165. // setup the tasks system
  166. (next) => {
  167. initializedComponents.push(logger);
  168. currentComponent = 'Tasks';
  169. tasks.init(next)
  170. },
  171. // setup the frontend for local setups
  172. (next) => {
  173. initializedComponents.push(tasks);
  174. currentComponent = 'Windows';
  175. if (!config.get("isDocker")) {
  176. const express = require('express');
  177. const app = express();
  178. app.listen(config.get("frontendPort"));
  179. const rootDir = __dirname.substr(0, __dirname.lastIndexOf("backend")) + "frontend/dist/";
  180. const rootDirAssets = __dirname.substr(0, __dirname.lastIndexOf("backend")) + "frontend/app/";
  181. const rootDirLocales = __dirname.substr(0, __dirname.lastIndexOf("backend"));
  182. app.get("/locales/*", (req, res) => {
  183. let path = req.path;
  184. res.set('Cache-Control', 'max-age=8640000000');
  185. fs.access(rootDirLocales + path, function (err) {
  186. console.log("Error: ", !!err);
  187. if (!err) {
  188. res.sendFile(rootDirLocales + path);
  189. } else {
  190. res.redirect("/");
  191. }
  192. });
  193. });
  194. app.get("/assets/*", (req, res) => {
  195. const path = req.path;
  196. res.set('Cache-Control', 'max-age=8640000000');
  197. fs.access(rootDirAssets + path, function (err) {
  198. console.log("Error: ", !!err);
  199. if (!err) {
  200. res.sendFile(rootDirAssets + path);
  201. } else {
  202. res.redirect("/");
  203. }
  204. });
  205. });
  206. app.get("/*", (req, res) => {
  207. const path = req.path;
  208. fs.access(rootDir + path, function (err) {
  209. if (!err) {
  210. res.set('Cache-Control', 'max-age=8640000000');
  211. res.sendFile(rootDir + path);
  212. } else {
  213. res.sendFile(rootDir + "index.html");
  214. }
  215. });
  216. });
  217. }
  218. if (lockdownB) return;
  219. next();
  220. }
  221. ], (err) => {
  222. if (err && err !== true) {
  223. lockdown();
  224. logToDiscord("An error occurred while initializing the backend server.", "#FF0000", "Startup error", true, [{
  225. name: "Error:",
  226. value: err,
  227. inline: false
  228. }, {name: "Component:", value: currentComponent, inline: true}]);
  229. console.error('An error occurred while initializing the backend server');
  230. } else {
  231. logToDiscord("The backend server started successfully.", "#00AA00", "Startup", false, []);
  232. console.info('Backend server has been successfully started');
  233. }
  234. });
  235. }