import config from "config"; import async from "async"; import crypto from "crypto"; import CoreClass from "../core"; import { hasPermission } from "./hooks/hasPermission"; let AppModule; let DBModule; let PlaylistsModule; let UtilsModule; let PunishmentsModule; let CacheModule; let NotificationsModule; class _APIModule extends CoreClass { // eslint-disable-next-line require-jsdoc constructor() { super("api"); } /** * Initialises the api module * * @returns {Promise} - returns promise (reject, resolve) */ initialize() { return new Promise((resolve, reject) => { AppModule = this.moduleManager.modules.app; DBModule = this.moduleManager.modules.db; PlaylistsModule = this.moduleManager.modules.playlists; UtilsModule = this.moduleManager.modules.utils; PunishmentsModule = this.moduleManager.modules.punishments; CacheModule = this.moduleManager.modules.cache; NotificationsModule = this.moduleManager.modules.notifications; const SIDname = config.get("cookie.SIDname"); const isLoggedIn = (req, res, next) => { let SID; async.waterfall( [ next => { UtilsModule.runJob("PARSE_COOKIES", { cookieString: req.headers.cookie }) .then(res => { SID = res[SIDname]; next(null); }) .catch(next); }, next => { if (!SID) return next("No SID."); return next(); }, next => { CacheModule.runJob("HGET", { table: "sessions", key: SID }).then(session => next(null, session) ); }, (session, next) => { if (!session) return next("No session found."); session.refreshDate = Date.now(); req.session = session; return CacheModule.runJob("HSET", { table: "sessions", key: SID, value: session }).then(session => { next(null, session); }); }, (res, next) => { // check if a session's user / IP is banned PunishmentsModule.runJob("GET_PUNISHMENTS", {}) .then(punishments => { const isLoggedIn = !!(req.session && req.session.refreshDate); const userId = isLoggedIn ? req.session.userId : null; const banishment = { banned: false, ban: 0 }; punishments.forEach(punishment => { if (punishment.expiresAt > banishment.ban) banishment.ban = punishment; if ( punishment.type === "banUserId" && isLoggedIn && punishment.value === userId ) banishment.banned = true; if (punishment.type === "banUserIp" && punishment.value === req.ip) banishment.banned = true; }); req.banishment = banishment; next(); }) .catch(() => { next(); }); } ], err => { if (err) return res.json({ status: "error", message: "You are not logged in" }); return next(); } ); }; AppModule.runJob("GET_APP", {}) .then(response => { response.app.get("/", (req, res) => { res.json({ status: "success", message: "Coming Soon" }); }); response.app.get("/export/playlist/:playlistId", async (req, res) => { const { playlistId } = req.params; PlaylistsModule.runJob("GET_PLAYLIST", { playlistId }) .then(playlist => { if (!playlist) res.json({ status: "error", message: "Playlist not found." }); else if (playlist.privacy === "public") res.json({ status: "success", playlist }); else isLoggedIn(req, res, () => { if (playlist.createdBy === req.session.userId) res.json({ status: "success", playlist }); else hasPermission("playlists.get", req.session.userId) .then(() => res.json({ status: "success", playlist })) .catch(() => res.json({ status: "error", message: "You're not allowed to download this playlist." }) ); }); }) .catch(err => { res.json({ status: "error", message: err.message }); }); }); if (config.get("debug.stationIssue")) { response.app.get("/debug_station", async (req, res) => { const responseObject = {}; const stationModel = await DBModule.runJob("GET_MODEL", { modelName: "station" }); async.waterfall( [ next => { stationModel.find({}, next); }, (stations, next) => { responseObject.mongo = { stations }; next(); }, next => { CacheModule.runJob("HGETALL", { table: "stations" }) .then(stations => { next(null, stations); }) .catch(err => { console.log(err); next(err); }); }, (stations, next) => { responseObject.redis = { stations }; next(); }, next => { responseObject.cryptoExamples = {}; responseObject.mongo.stations.forEach(station => { const payloadName = `stations.nextSong?id=${station._id}`; responseObject.cryptoExamples[station._id] = crypto .createHash("md5") .update(`_notification:${payloadName}_`) .digest("hex"); }); next(); }, next => { NotificationsModule.pub .KEYS("*") .then(redisKeys => next(null, redisKeys)) .catch(next); }, (redisKeys, next) => { responseObject.redis = { ...redisKeys, ttl: {} }; async.eachLimit( redisKeys, 1, (redisKey, next) => { NotificationsModule.pub .TTL(redisKey) .then(ttl => { responseObject.redis.ttl[redisKey] = ttl; next(); }) .catch(next); }, next ); }, next => { responseObject.debugLogs = this.moduleManager.debugLogs.stationIssue; next(); }, next => { responseObject.debugJobs = this.moduleManager.debugJobs; next(); } ], err => { if (err) { console.log(err); return res.json({ error: err, objectSoFar: responseObject }); } const responseJson = JSON.stringify(responseObject, (key, value) => { if ( key === "module" || key === "task" || key === "onFinish" || key === "server" || key === "nsp" || key === "socket" || key === "res" || key === "client" || key === "_idleNext" || key === "_idlePrev" ) { return undefined; } if (key === "parentJob" && value) return value.toString(); return value; }); return res.end(responseJson); } ); }); } resolve(); }) .catch(err => { reject(err); }); }); } } export default new _APIModule();