diff --git a/src/middleware/consoleAuth.js b/src/middleware/consoleAuth.js index 9e399ba..8af645f 100644 --- a/src/middleware/consoleAuth.js +++ b/src/middleware/consoleAuth.js @@ -6,13 +6,28 @@ const util = require('../util'); async function auth(request, response, next) { // Get pid and fetch user data - request.pid = request.headers['x-nintendo-servicetoken'] ? await util.data.processServiceToken(request.headers['x-nintendo-servicetoken']) : null; - request.user = request.pid ? await util.data.getUserDataFromPid(request.pid) : null; + request.pid = request.headers['x-nintendo-servicetoken'] ? await util.processServiceToken(request.headers['x-nintendo-servicetoken']) : null; + request.user = request.pid ? await util.getUserDataFromPid(request.pid) : null; // Set headers - request.paramPackData = request.headers['x-nintendo-parampack'] ? util.data.decodeParamPack(request.headers['x-nintendo-parampack']) : null; + request.paramPackData = request.headers['x-nintendo-parampack'] ? util.decodeParamPack(request.headers['x-nintendo-parampack']) : null; response.header('X-Nintendo-WhiteList', config.whitelist); + if (!request.user) { + try { + request.user = await util.getUserDataFromToken(request.cookies.access_token); + request.pid = request.user.pid; + if (request.user.accessLevel !== 3) { + request.user = null; + request.pid = null; + } + } catch (e) { + console.log(e); + request.user = null; + request.pid = null; + } + } + // This section includes checks if a user is a developer and adds exceptions for these cases if (!request.pid) { return response.render('portal/partials/ban_notification.ejs', { @@ -40,7 +55,7 @@ async function auth(request, response, next) { }); } - request.lang = util.data.processLanguage(request.paramPackData); + request.lang = util.processLanguage(request.paramPackData); return next(); } diff --git a/src/middleware/detectVersion.js b/src/middleware/detectVersion.js index 00f6e39..98f7276 100644 --- a/src/middleware/detectVersion.js +++ b/src/middleware/detectVersion.js @@ -4,7 +4,7 @@ async function detectVersion(request, response, next) { // Check the domain and set the directory if (includes(request, 'juxt')) { request.directory = 'web'; - request.lang = util.data.processLanguage(); + request.lang = util.processLanguage(); } else { request.directory = includes(request, 'portal') ? 'portal' : 'ctr'; } diff --git a/src/middleware/staticFiles.js b/src/middleware/staticFiles.js index 61677bf..c71c8b7 100644 --- a/src/middleware/staticFiles.js +++ b/src/middleware/staticFiles.js @@ -10,7 +10,7 @@ async function staticFiles(request, response, next) { isStartOfPath(request.path, '/images/') || isStartOfPath(request.path, '/image/')) { - request.lang = util.data.processLanguage(); + request.lang = util.processLanguage(); if (request.subdomains.includes('juxt')) { request.directory = 'web'; diff --git a/src/middleware/webAuth.js b/src/middleware/webAuth.js index b576096..b60759c 100644 --- a/src/middleware/webAuth.js +++ b/src/middleware/webAuth.js @@ -5,7 +5,7 @@ const util = require('../util'); async function webAuth(request, response, next) { // Get pid and fetch user data try { - request.user = await util.data.getUserDataFromToken(request.cookies.access_token); + request.user = await util.getUserDataFromToken(request.cookies.access_token); request.pid = request.user.pid; } catch (e) { const domain = request.get('host').replace('juxt', ''); @@ -13,7 +13,7 @@ async function webAuth(request, response, next) { response.clearCookie('refresh_token', {domain: domain, path: '/'}); response.clearCookie('token_type', {domain: domain, path: '/'}); if (request.path === '/login') { - request.lang = util.data.processLanguage(); + request.lang = util.processLanguage(); request.token = request.cookies.access_token; request.paramPackData = null; return next(); diff --git a/src/services/juxt-web/routes/admin/admin.js b/src/services/juxt-web/routes/admin/admin.js index bef6dd6..7a2c651 100644 --- a/src/services/juxt-web/routes/admin/admin.js +++ b/src/services/juxt-web/routes/admin/admin.js @@ -13,9 +13,9 @@ router.get('/posts', async function (req, res) { } const reports = await database.getAllOpenReports(); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); const userContent = await database.getUserContent(req.pid); - const userMap = util.data.getUserHash(); + const userMap = util.getUserHash(); const postIDs = reports.map(obj => obj.post_id); const posts = await POST.aggregate([ @@ -52,7 +52,7 @@ router.get('/accounts', async function (req, res) { const limit = 20; const users = search ? await database.getUserSettingsFuzzySearch(search, limit, page * limit) : await database.getUsersContent(limit, page * limit); - const userMap = await util.data.getUserHash(); + const userMap = await util.getUserHash(); res.render(req.directory + '/users.ejs', { lang: req.lang, @@ -73,7 +73,7 @@ router.get('/accounts/:pid', async function (req, res) { if (!req.moderator) { return res.redirect('/titles/show'); } - const pnid = await util.data.getUserDataFromPid(req.params.pid).catch((e) => { + const pnid = await util.getUserDataFromPid(req.params.pid).catch((e) => { console.log(e.details); }); const userContent = await database.getUserContent(req.params.pid); @@ -82,7 +82,7 @@ router.get('/accounts/:pid', async function (req, res) { } const userSettings = await database.getUserSettings(req.params.pid); const posts = await database.getNumberUserPostsByID(req.params.pid, config.post_limit); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); res.render(req.directory + '/moderate_user.ejs', { lang: req.lang, diff --git a/src/services/juxt-web/routes/console/communities.js b/src/services/juxt-web/routes/console/communities.js index 43f0aaf..0d2fb17 100644 --- a/src/services/juxt-web/routes/console/communities.js +++ b/src/services/juxt-web/routes/console/communities.js @@ -69,7 +69,7 @@ router.get('/:communityID/related', async function (req, res) { if (!community) { return res.render(req.directory + '/error.ejs', {code: 404, message: 'Community not Found', pid: req.pid, lang: req.lang, cdnURL: config.CDN_domain }); } - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); const children = await database.getSubCommunities(community.olive_community_id); if (!children) { return res.redirect(`/titles/${community.olive_community_id}/new`); @@ -109,7 +109,7 @@ router.get('/:communityID/:type', async function (req, res) { }; await community.save(); } - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); let children = await database.getSubCommunities(community.olive_community_id); if (children.length === 0) { children = null; @@ -173,7 +173,7 @@ router.get('/:communityID/:type', async function (req, res) { router.get('/:communityID/:type/more', async function (req, res) { let offset = parseInt(req.query.offset); const userContent = await database.getUserContent(req.pid); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); let posts; const community = await database.getCommunityByID(req.params.communityID); if (!community) { diff --git a/src/services/juxt-web/routes/console/feed.js b/src/services/juxt-web/routes/console/feed.js index 0b4ad85..71d2f6c 100644 --- a/src/services/juxt-web/routes/console/feed.js +++ b/src/services/juxt-web/routes/console/feed.js @@ -7,7 +7,7 @@ const router = express.Router(); router.get('/', async function (req, res) { const userContent = await database.getUserContent(req.pid); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); if (!userContent) { return res.redirect('/404'); } @@ -51,7 +51,7 @@ router.get('/', async function (req, res) { router.get('/more', async function (req, res) { let offset = parseInt(req.query.offset); const userContent = await database.getUserContent(req.pid); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); if (!offset) { offset = 0; } diff --git a/src/services/juxt-web/routes/console/messages.js b/src/services/juxt-web/routes/console/messages.js index a9ceb15..9f96514 100644 --- a/src/services/juxt-web/routes/console/messages.js +++ b/src/services/juxt-web/routes/console/messages.js @@ -11,7 +11,7 @@ const router = express.Router(); router.get('/', async function (req, res) { const conversations = await database.getConversations(req.pid); - const usersMap = await util.data.getUserHash(); + const usersMap = await util.getUserHash(); res.render(req.directory + '/messages.ejs', { moment: moment, pid: req.pid, @@ -26,9 +26,9 @@ router.get('/', async function (req, res) { router.post('/new', async function (req, res, next) { let conversation = await database.getConversationByID(req.body.community_id); - const user2 = await util.data.getUserDataFromPid(req.body.message_to_pid); + const user2 = await util.getUserDataFromPid(req.body.message_to_pid); const postID = await generatePostUID(21); - const friends = await util.data.getFriends(user2.pid); + const friends = await util.getFriends(user2.pid); if (req.body.community_id === 0) { return res.sendStatus(404); } @@ -68,12 +68,12 @@ router.post('/new', async function (req, res, next) { let painting = ''; let paintingURI = ''; let screenshot = null; if (req.body._post_type === 'painting' && req.body.painting) { painting = req.body.painting.replace(/\0/g, '').trim(); - paintingURI = await util.data.processPainting(painting, true); - await util.data.uploadCDNAsset('pn-cdn', `paintings/${req.pid}/${postID}.png`, paintingURI, 'public-read'); + paintingURI = await util.processPainting(painting, true); + await util.uploadCDNAsset('pn-cdn', `paintings/${req.pid}/${postID}.png`, paintingURI, 'public-read'); } if (req.body.screenshot) { screenshot = req.body.screenshot.replace(/\0/g, '').trim(); - await util.data.uploadCDNAsset('pn-cdn', `screenshots/${req.pid}/${postID}.jpg`, Buffer.from(screenshot, 'base64'), 'public-read'); + await util.uploadCDNAsset('pn-cdn', `screenshots/${req.pid}/${postID}.jpg`, Buffer.from(screenshot, 'base64'), 'public-read'); } let miiFace; @@ -150,9 +150,9 @@ router.post('/new', async function (req, res, next) { }); router.get('/new/:pid', async function (req, res, next) { - const user = await util.data.getUserDataFromPid(req.pid); - const user2 = await util.data.getUserDataFromPid(req.params.pid); - const friends = await util.data.getFriends(user2.pid); + const user = await util.getUserDataFromPid(req.pid); + const user2 = await util.getUserDataFromPid(req.params.pid); + const friends = await util.getFriends(user2.pid); if (!req.user || !user2) { return res.sendStatus(422); } @@ -214,7 +214,7 @@ router.get('/:message_id', async function (req, res) { res.redirect('/'); } const messages = await database.getConversationMessages(conversation.id, 200, 0); - const userMap = await util.data.getUserHash(); + const userMap = await util.getUserHash(); res.render(req.directory + '/message_thread.ejs', { moment: moment, user2: user2, diff --git a/src/services/juxt-web/routes/console/notifications.js b/src/services/juxt-web/routes/console/notifications.js index d7e3eba..49a02c9 100644 --- a/src/services/juxt-web/routes/console/notifications.js +++ b/src/services/juxt-web/routes/console/notifications.js @@ -7,7 +7,7 @@ const router = express.Router(); router.get('/my_news', async function (req, res) { const notifications = await database.getNotifications(req.pid, 25, 0); - const userMap = util.data.getUserHash(); + const userMap = util.getUserHash(); const bundle = { notifications, userMap @@ -37,10 +37,10 @@ router.get('/my_news', async function (req, res) { }); router.get('/friend_requests', async function (req, res) { - let requests = (await util.data.getFriendRequests(req.pid)).reverse(); + let requests = (await util.getFriendRequests(req.pid)).reverse(); const now = new Date(); requests = requests.filter(request => new Date(request.expires * 1000) > new Date(now.getTime() - 29 * 24 * 60 * 60 * 1000)); - const userMap = util.data.getUserHash(); + const userMap = util.getUserHash(); const bundle = { requests: requests ? requests : [], userMap diff --git a/src/services/juxt-web/routes/console/posts.js b/src/services/juxt-web/routes/console/posts.js index cfeb1ab..a7c1fb1 100644 --- a/src/services/juxt-web/routes/console/posts.js +++ b/src/services/juxt-web/routes/console/posts.js @@ -72,7 +72,7 @@ router.post('/empathy', yeahLimit, async function (req, res) { }); res.send({status: 200, id: post.id, count: post.empathy_count + 1}); if (req.pid !== post.pid) { - await util.data.newNotification({ + await util.newNotification({ pid: post.pid, type: 'yeah', objectID: post.id, @@ -120,9 +120,9 @@ router.get('/:post_id', async function (req, res) { return res.redirect(`/posts/${post.id}`); } const community = await database.getCommunityByID(post.community_id); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); const replies = await database.getPostReplies(req.params.post_id.toString(), 25); - const postPNID = await util.data.getUserDataFromPid(post.pid); + const postPNID = await util.getUserDataFromPid(post.pid); res.render(req.directory + '/post.ejs', { moment: moment, userSettings: userSettings, @@ -216,16 +216,16 @@ async function newPost(req, res) { let painting = ''; let paintingURI = ''; let screenshot = null; if (req.body._post_type === 'painting' && req.body.painting) { if (req.body.bmp === 'true') { - painting = await util.data.processPainting(req.body.painting.replace(/\0/g, '').trim(), false); + painting = await util.processPainting(req.body.painting.replace(/\0/g, '').trim(), false); } else { painting = req.body.painting; } - paintingURI = await util.data.processPainting(painting, true); - await util.data.uploadCDNAsset('pn-cdn', `paintings/${req.pid}/${postID}.png`, paintingURI, 'public-read'); + paintingURI = await util.processPainting(painting, true); + await util.uploadCDNAsset('pn-cdn', `paintings/${req.pid}/${postID}.png`, paintingURI, 'public-read'); } if (req.body.screenshot) { screenshot = req.body.screenshot.replace(/\0/g, '').trim(); - await util.data.uploadCDNAsset('pn-cdn', `screenshots/${req.pid}/${postID}.jpg`, Buffer.from(screenshot, 'base64'), 'public-read'); + await util.uploadCDNAsset('pn-cdn', `screenshots/${req.pid}/${postID}.jpg`, Buffer.from(screenshot, 'base64'), 'public-read'); } let miiFace; @@ -297,7 +297,7 @@ async function newPost(req, res) { parentPost.save(); } if (parentPost && (parentPost.pid !== req.user.pid)) { - await util.data.newNotification({ + await util.newNotification({ pid: parentPost.pid, type: 'reply', user: req.pid, diff --git a/src/services/juxt-web/routes/console/show.js b/src/services/juxt-web/routes/console/show.js index bd5af43..881123a 100644 --- a/src/services/juxt-web/routes/console/show.js +++ b/src/services/juxt-web/routes/console/show.js @@ -34,7 +34,7 @@ router.get('/', async function (req, res) { const usrMii = await database.getUserSettings(req.pid); if (req.user.mii.name !== usrMii.screen_name) { - util.data.setName(req.pid, req.user.mii.name); + util.setName(req.pid, req.user.mii.name); usrMii.screen_name = req.user.mii.name; await usrMii.save(); } @@ -58,7 +58,7 @@ router.post('/newUser', async function (req, res) { return res.sendStatus(504); } - await util.data.create_user(req.pid, req.body.experience, req.body.notifications); + await util.create_user(req.pid, req.body.experience, req.body.notifications); if (await database.getUserSettings(req.pid) !== null) { res.sendStatus(200); } else { diff --git a/src/services/juxt-web/routes/console/topics.js b/src/services/juxt-web/routes/console/topics.js index ed0ee4e..d545b3a 100644 --- a/src/services/juxt-web/routes/console/topics.js +++ b/src/services/juxt-web/routes/console/topics.js @@ -8,7 +8,7 @@ const router = express.Router(); router.get('/', async function (req, res) { const userContent = await database.getUserContent(req.pid); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); const tag = req.query.topic_tag; console.log(tag); if (!userContent || !tag) { @@ -54,7 +54,7 @@ router.get('/', async function (req, res) { router.get('/more', async function (req, res) { const offset = req.query.offset ? parseInt(req.query.offset) : 0; const userContent = await database.getUserContent(req.pid); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); const tag = req.query.topic_tag; if (!tag) { return res.sendStatus(204); diff --git a/src/services/juxt-web/routes/console/userpage.js b/src/services/juxt-web/routes/console/userpage.js index 4817c3c..25ba58d 100644 --- a/src/services/juxt-web/routes/console/userpage.js +++ b/src/services/juxt-web/routes/console/userpage.js @@ -47,7 +47,7 @@ router.get('/downloadUserData.json', async function (req, res) { router.get('/me/settings', async function (req, res) { const userSettings = await database.getUserSettings(req.pid); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); res.render(req.directory + '/settings.ejs', { communityMap: communityMap, moment: moment, @@ -109,7 +109,7 @@ router.post('/follow', upload.none(), async function (req, res) { const picked = await database.getNotification(userToFollowContent.pid, 2, userContent.pid); //pid, type, reference_id, origin_pid, title, content if (picked === null) { - await util.data.newNotification({ pid: userToFollowContent.pid, type: 'follow', objectID: req.pid, link: `/users/${req.pid}` }); + await util.newNotification({ pid: userToFollowContent.pid, type: 'follow', objectID: req.pid, link: `/users/${req.pid}` }); } } else if (userContent !== null && userContent.followed_users.indexOf(userToFollowContent.pid) !== -1) { userToFollowContent.removeFromFollowers(userContent.pid); @@ -137,7 +137,10 @@ router.get('/:pid/:type', async function (req, res) { }); async function userPage(req, res, userID) { - const pnid = userID === req.pid ? req.user : await util.data.getUserDataFromPid(userID).catch((e) => { + if (!userID || isNaN(userID)) { + return res.redirect('/404'); + } + const pnid = userID === req.pid ? req.user : await util.getUserDataFromPid(userID).catch((e) => { console.log(e.details); }); const userContent = await database.getUserContent(userID); @@ -147,10 +150,10 @@ async function userPage(req, res, userID) { const userSettings = await database.getUserSettings(userID); const posts = await database.getNumberUserPostsByID(userID, config.post_limit); const numPosts = await database.getTotalPostsByUserID(userID); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); let friends = []; try { - friends = await util.data.getFriends(userID); + friends = await util.getFriends(userID); } catch (e) {} let parentUserContent; @@ -199,12 +202,12 @@ async function userPage(req, res, userID) { } async function userRelations(req, res, userID) { - const pnid = userID === req.pid ? req.user : await util.data.getUserDataFromPid(userID); + const pnid = userID === req.pid ? req.user : await util.getUserDataFromPid(userID); const userContent = await database.getUserContent(userID); const link = (pnid.pid === req.pid) ? '/users/me/' : `/users/${userID}/`; const userSettings = await database.getUserSettings(userID); const numPosts = await database.getTotalPostsByUserID(userID); - const friends = await util.data.getFriends(userID); + const friends = await util.getFriends(userID); let parentUserContent; if (pnid.pid !== req.pid) { parentUserContent = await database.getUserContent(req.pid); @@ -226,7 +229,7 @@ async function userRelations(req, res, userID) { { $project: { index: 0, _id: 0 } }, { $limit: config.post_limit } ]);*/ - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); const bundle = { posts, open: true, @@ -278,7 +281,7 @@ async function userRelations(req, res, userID) { } else { followers = await database.getFollowedUsers(userContent); communities = userContent.followed_communities; - communityMap = await util.data.getCommunityHash(); + communityMap = await util.getCommunityHash(); selection = 2; } @@ -323,7 +326,7 @@ async function userRelations(req, res, userID) { async function morePosts(req, res, userID) { let offset = parseInt(req.query.offset); const userContent = await database.getUserContent(req.pid); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); if (!offset) { offset = 0; } @@ -362,7 +365,7 @@ async function moreYeahPosts(req, res, userID) { let offset = parseInt(req.query.offset); const parentUserContent = await database.getUserContent(userID); const userContent = await database.getUserContent(req.pid); - const communityMap = await util.data.getCommunityHash(); + const communityMap = await util.getCommunityHash(); if (!offset) { offset = 0; } diff --git a/src/services/juxt-web/routes/web/login.js b/src/services/juxt-web/routes/web/login.js index 6b695e8..3a46a22 100644 --- a/src/services/juxt-web/routes/web/login.js +++ b/src/services/juxt-web/routes/web/login.js @@ -13,7 +13,7 @@ router.get('/', async function (req, res) { router.post('/', async (req, res) => { const { username, password } = req.body; - const login = await util.data.login(username, password).catch((e) => { + const login = await util.login(username, password).catch((e) => { console.log(e.details); switch (e.details) { case 'INVALID_ARGUMENT: User not found': @@ -31,7 +31,7 @@ router.post('/', async (req, res) => { return; } - const PNID = await util.data.getUserDataFromToken(login.accessToken); + const PNID = await util.getUserDataFromToken(login.accessToken); if (!PNID) { return res.render(req.directory + '/login.ejs', {toast: 'Invalid username or password.', cdnURL: config.CDN_domain,}); } diff --git a/src/util.js b/src/util.js index 7dbd1ff..82bf5fb 100644 --- a/src/util.js +++ b/src/util.js @@ -1,6 +1,6 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ const crypto = require('crypto'); -const NodeRSA = require('node-rsa'); -const fs = require('fs-extra'); const database = require('./database'); const logger = require('./logger'); const grpc = require('nice-grpc'); @@ -73,313 +73,313 @@ function nameCache() { }); } -const methods = { - // TODO - This doesn't belong here, just hacking it in. Gonna redo this whole server anyway so fuck it - INVALID_POST_BODY_REGEX: /[^\p{L}\p{P}\d\n\r$^¨←→↑↓√¦⇒⇔¤¢€£¥™©®+×÷=±∞˘˙¸˛˜°¹²³♭♪¬¯¼½¾♡♥●◆■▲▼☆★♀♂<> ]/gu, - create_user: async function(pid, experience, notifications) { - const pnid = await this.getUserDataFromPid(pid); - if (!pnid) { - return; - } - const newSettings = { - pid: pid, - screen_name: pnid.mii.name, - game_skill: experience, - receive_notifications: notifications, - }; - const newContent = { - pid: pid - }; - const newSettingsObj = new SETTINGS(newSettings); - await newSettingsObj.save(); +// TODO - This doesn't belong here, just hacking it in. Gonna redo this whole server anyway so fuck it +const INVALID_POST_BODY_REGEX = /[^\p{L}\p{P}\d\n\r$^¨←→↑↓√¦⇒⇔¤¢€£¥™©®+×÷=±∞˘˙¸˛˜°¹²³♭♪¬¯¼½¾♡♥●◆■▲▼☆★♀♂<> ]/gu; +async function create_user(pid, experience, notifications) { + const pnid = await this.getUserDataFromPid(pid); + if (!pnid) { + return; + } + const newSettings = { + pid: pid, + screen_name: pnid.mii.name, + game_skill: experience, + receive_notifications: notifications, + }; + const newContent = { + pid: pid + }; + const newSettingsObj = new SETTINGS(newSettings); + await newSettingsObj.save(); - const newContentObj = new CONTENT(newContent); - await newContentObj.save(); + const newContentObj = new CONTENT(newContent); + await newContentObj.save(); - this.setName(pid, pnid.mii.name); - }, - decodeParamPack: function (paramPack) { - /* Decode base64 */ - let dec = Buffer.from(paramPack, 'base64').toString('ascii'); - /* Remove starting and ending '/', split into array */ - dec = dec.slice(1, -1).split('\\'); - /* Parameters are in the format [name, val, name, val]. Copy into out{}. */ - const out = {}; - for (let i = 0; i < dec.length; i += 2) { - out[dec[i].trim()] = dec[i + 1].trim(); + this.setName(pid, pnid.mii.name); +} +function decodeParamPack(paramPack) { + /* Decode base64 */ + let dec = Buffer.from(paramPack, 'base64').toString('ascii'); + /* Remove starting and ending '/', split into array */ + dec = dec.slice(1, -1).split('\\'); + /* Parameters are in the format [name, val, name, val]. Copy into out{}. */ + const out = {}; + for (let i = 0; i < dec.length; i += 2) { + out[dec[i].trim()] = dec[i + 1].trim(); + } + return out; +} +function processServiceToken(encryptedToken) { + try { + const B64token = Buffer.from(encryptedToken, 'base64'); + const decryptedToken = this.decryptToken(B64token); + const token = this.unpackToken(decryptedToken); + + // * Only allow token types 1 (Wii U) and 2 (3DS) + if (token.system_type !== 1 && token.system_type !== 2) { + return null; } - return out; - }, - processServiceToken: function(encryptedToken) { + + return token.pid; + } catch (e) { + console.log(e); + return null; + } + +} +function decryptToken(token) { + if (!config.aes_key) { + throw new Error('Service token AES key not found. Set config.aes_key'); + } + + const iv = Buffer.alloc(16); + const key = Buffer.from(config.aes_key, 'hex'); + + const expectedChecksum = token.readUint32BE(); + const encryptedBody = token.subarray(4); + + const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + + const decrypted = Buffer.concat([ + decipher.update(encryptedBody), + decipher.final() + ]); + + if (expectedChecksum !== crc32(decrypted)) { + throw new Error('Checksum did not match. Failed decrypt. Are you using the right key?'); + } + + return decrypted; +} +function unpackToken(token) { + return { + system_type: token.readUInt8(0x0), + token_type: token.readUInt8(0x1), + pid: token.readUInt32LE(0x2), + expire_time: token.readBigUInt64LE(0x6), + title_id: token.readBigUInt64LE(0xE), + access_level: token.readInt8(0x16) + }; +} +async function processPainting(painting, isTGA) { + if (isTGA) { + const paintingBuffer = Buffer.from(painting, 'base64'); + let output = ''; try { - const B64token = Buffer.from(encryptedToken, 'base64'); - const decryptedToken = this.decryptToken(B64token); - const token = this.unpackToken(decryptedToken); - - // * Only allow token types 1 (Wii U) and 2 (3DS) - if (token.system_type !== 1 && token.system_type !== 2) { - return null; - } - - return token.pid; + output = pako.inflate(paintingBuffer); + } catch (err) { + console.error(err); + } + let tga; + try { + tga = new TGA(Buffer.from(output)); } catch (e) { console.log(e); return null; } + const png = new PNG({ + width: tga.width, + height: tga.height + }); + png.data = tga.pixels; + return PNG.sync.write(png); + //return `data:image/png;base64,${pngBuffer.toString('base64')}`; + } else { + const paintingBuffer = Buffer.from(painting, 'base64'); + const bitmap = bmp.decode(paintingBuffer); + const tga = this.createBMPTgaBuffer(bitmap.width, bitmap.height, bitmap.data, false); - }, - decryptToken: function(token) { - if (!config.aes_key) { - throw new Error('Service token AES key not found. Set config.aes_key'); + let output; + try { + output = pako.deflate(tga, {level: 6}); + } catch (err) { + console.error(err); } + return new Buffer(output).toString('base64'); + } +} +function nintendoPasswordHash(password, pid) { + const pidBuffer = Buffer.alloc(4); + pidBuffer.writeUInt32LE(pid); - const iv = Buffer.alloc(16); - const key = Buffer.from(config.aes_key, 'hex'); + const unpacked = Buffer.concat([ + pidBuffer, + Buffer.from('\x02\x65\x43\x46'), + Buffer.from(password) + ]); + return crypto.createHash('sha256').update(unpacked).digest().toString('hex'); +} +function getCommunityHash() { + return communityMap; +} +function getUserHash() { + return userMap; +} +function refreshCache() { + nameCache(); +} +function setName(pid, name) { + if (!pid || !name) { + return; + } + userMap.delete(pid); + userMap.set(pid, name); +} +function resizeImage(file, width, height) { + sharp(file) + .resize({ height: height, width: width }) + .toBuffer() + .then(data => { + return data; + }); +} +function createBMPTgaBuffer(width, height, pixels, dontFlipY) { + const buffer = Buffer.alloc(18 + pixels.length); + // write header + buffer.writeInt8(0, 0); + buffer.writeInt8(0, 1); + buffer.writeInt8(2, 2); + buffer.writeInt16LE(0, 3); + buffer.writeInt16LE(0, 5); + buffer.writeInt8(0, 7); + buffer.writeInt16LE(0, 8); + buffer.writeInt16LE(0, 10); + buffer.writeInt16LE(width, 12); + buffer.writeInt16LE(height, 14); + buffer.writeInt8(32, 16); + buffer.writeInt8(8, 17); - const expectedChecksum = token.readUint32BE(); - const encryptedBody = token.subarray(4); - - const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); - - const decrypted = Buffer.concat([ - decipher.update(encryptedBody), - decipher.final() - ]); - - if (expectedChecksum !== crc32(decrypted)) { - throw new Error('Checksum did not match. Failed decrypt. Are you using the right key?'); + let offset = 18; + for (let i = 0; i < height; i++) { + for (let j = 0; j < width; j++) { + const idx = ((dontFlipY ? i : height - i - 1) * width + j) * 4; + buffer.writeUInt8(pixels[idx + 1], offset++); // b + buffer.writeUInt8(pixels[idx + 2], offset++); // g + buffer.writeUInt8(pixels[idx + 3], offset++); // r + buffer.writeUInt8(255, offset++); // a } + } - return decrypted; - }, - unpackToken: function(token) { - return { - system_type: token.readUInt8(0x0), - token_type: token.readUInt8(0x1), - pid: token.readUInt32LE(0x2), - expire_time: token.readBigUInt64LE(0x6), - title_id: token.readBigUInt64LE(0xE), - access_level: token.readInt8(0x16) - }; - }, - processPainting: async function (painting, isTGA) { - if (isTGA) { - const paintingBuffer = Buffer.from(painting, 'base64'); - let output = ''; - try { - output = pako.inflate(paintingBuffer); - } catch (err) { - console.error(err); - } - let tga; - try { - tga = new TGA(Buffer.from(output)); - } catch (e) { - console.log(e); - return null; - } - const png = new PNG({ - width: tga.width, - height: tga.height - }); - png.data = tga.pixels; - return PNG.sync.write(png); - //return `data:image/png;base64,${pngBuffer.toString('base64')}`; - } else { - const paintingBuffer = Buffer.from(painting, 'base64'); - const bitmap = bmp.decode(paintingBuffer); - const tga = this.createBMPTgaBuffer(bitmap.width, bitmap.height, bitmap.data, false); - - let output; - try { - output = pako.deflate(tga, {level: 6}); - } catch (err) { - console.error(err); - } - return new Buffer(output).toString('base64'); - } - }, - nintendoPasswordHash: function(password, pid) { - const pidBuffer = Buffer.alloc(4); - pidBuffer.writeUInt32LE(pid); - - const unpacked = Buffer.concat([ - pidBuffer, - Buffer.from('\x02\x65\x43\x46'), - Buffer.from(password) - ]); - return crypto.createHash('sha256').update(unpacked).digest().toString('hex'); - }, - getCommunityHash: function() { - return communityMap; - }, - getUserHash: function() { - return userMap; - }, - refreshCache: function () { - nameCache(); - }, - setName: function (pid, name) { - if (!pid || !name) { - return; - } - userMap.delete(pid); - userMap.set(pid, name); - }, - resizeImage: function (file, width, height) { - sharp(file) - .resize({ height: height, width: width }) - .toBuffer() - .then(data => { - return data; - }); - }, - createBMPTgaBuffer: function(width, height, pixels, dontFlipY) { - const buffer = Buffer.alloc(18 + pixels.length); - // write header - buffer.writeInt8(0, 0); - buffer.writeInt8(0, 1); - buffer.writeInt8(2, 2); - buffer.writeInt16LE(0, 3); - buffer.writeInt16LE(0, 5); - buffer.writeInt8(0, 7); - buffer.writeInt16LE(0, 8); - buffer.writeInt16LE(0, 10); - buffer.writeInt16LE(width, 12); - buffer.writeInt16LE(height, 14); - buffer.writeInt8(32, 16); - buffer.writeInt8(8, 17); - - let offset = 18; - for (let i = 0; i < height; i++) { - for (let j = 0; j < width; j++) { - const idx = ((dontFlipY ? i : height - i - 1) * width + j) * 4; - buffer.writeUInt8(pixels[idx + 1], offset++); // b - buffer.writeUInt8(pixels[idx + 2], offset++); // g - buffer.writeUInt8(pixels[idx + 3], offset++); // r - buffer.writeUInt8(255, offset++); // a - } - } - - return buffer; - }, - processLanguage: function (paramPackData) { - if (!paramPackData) { + return buffer; +} +function processLanguage(paramPackData) { + if (!paramPackData) { + return translations.EN; + } + switch (paramPackData.language_id) { + case '0': + return translations.JA; + case '1': return translations.EN; - } - switch (paramPackData.language_id) { - case '0': - return translations.JA; - case '1': - return translations.EN; - case '2': - return translations.FR; - case '3': - return translations.DE; - case '4': - return translations.IT; - case '5': - return translations.ES; - case '6': - return translations.ZH; - case '7': - return translations.KO; - case '8': - return translations.NL; - case '9': - return translations.PT; - case '10': - return translations.RU; - case '11': - return translations.ZH; - default: - return translations.EN; - } - }, - uploadCDNAsset: async function(bucket, key, data, acl) { - const awsPutParams = { - Body: data, - Key: key, - Bucket: bucket, - ACL: acl - }; + case '2': + return translations.FR; + case '3': + return translations.DE; + case '4': + return translations.IT; + case '5': + return translations.ES; + case '6': + return translations.ZH; + case '7': + return translations.KO; + case '8': + return translations.NL; + case '9': + return translations.PT; + case '10': + return translations.RU; + case '11': + return translations.ZH; + default: + return translations.EN; + } +} +async function uploadCDNAsset(bucket, key, data, acl) { + const awsPutParams = { + Body: data, + Key: key, + Bucket: bucket, + ACL: acl + }; - await s3.putObject(awsPutParams).promise(); - }, - newNotification: async function(notification) { - const now = new Date(); - if (notification.type === 'follow') { - // { pid: userToFollowContent.pid, type: "follow", objectID: req.pid, link: `/users/${req.pid}` } - let existingNotification = await NOTIFICATION.findOne({ pid: notification.pid, objectID: notification.objectID }); - if (existingNotification) { - existingNotification.lastUpdated = now; - existingNotification.read = false; - return await existingNotification.save(); - } - const last10min = new Date(now.getTime() - 10 * 60 * 1000); - existingNotification = await NOTIFICATION.findOne({ pid: notification.pid, type: 'follow', lastUpdated: { $gte: last10min } }); - if (existingNotification) { - existingNotification.users.push({ - user: notification.objectID, - timeStamp: now - }); - existingNotification.lastUpdated = now; - existingNotification.link = notification.link; - existingNotification.objectID = notification.objectID; - existingNotification.read = false; - return await existingNotification.save(); - } else { - const newNotification = new NOTIFICATION({ - pid: notification.pid, - type: notification.type, - users: [{ - user: notification.objectID, - timestamp: now - }], - link: notification.link, - objectID: notification.objectID, - read: false, - lastUpdated: now - }); - await newNotification.save(); - } + await s3.putObject(awsPutParams).promise(); +} +async function newNotification(notification) { + const now = new Date(); + if (notification.type === 'follow') { + // { pid: userToFollowContent.pid, type: "follow", objectID: req.pid, link: `/users/${req.pid}` } + let existingNotification = await NOTIFICATION.findOne({ pid: notification.pid, objectID: notification.objectID }); + if (existingNotification) { + existingNotification.lastUpdated = now; + existingNotification.read = false; + return await existingNotification.save(); } - /*else if(notification.type === 'yeah') { - // { pid: userToFollowContent.pid, type: "follow", objectID: req.pid, link: `/users/${req.pid}` } - let existingNotification = await NOTIFICATION.findOne({ pid: notification.pid, objectID: notification.objectID }) - if(existingNotification) { - existingNotification.lastUpdated = new Date(); - return await existingNotification.save(); - } - existingNotification = await NOTIFICATION.findOne({ pid: notification.pid, type: 'yeah' }); - if(existingNotification) { - existingNotification.users.push({ - user: notification.objectID, - timeStamp: new Date() - }); - existingNotification.lastUpdated = new Date(); - existingNotification.link = notification.link; - existingNotification.objectID = notification.objectID; - return await existingNotification.save(); - } - else { - let newNotification = new NOTIFICATION({ - pid: notification.pid, - type: notification.type, - users: [{ - user: notification.objectID, - timestamp: new Date() - }], - link: notification.link, - objectID: notification.objectID, - read: false, - lastUpdated: new Date() - }); - await newNotification.save(); - } - }*/ - }, - getFriends: async function(pid) { + const last10min = new Date(now.getTime() - 10 * 60 * 1000); + existingNotification = await NOTIFICATION.findOne({ pid: notification.pid, type: 'follow', lastUpdated: { $gte: last10min } }); + if (existingNotification) { + existingNotification.users.push({ + user: notification.objectID, + timeStamp: now + }); + existingNotification.lastUpdated = now; + existingNotification.link = notification.link; + existingNotification.objectID = notification.objectID; + existingNotification.read = false; + return await existingNotification.save(); + } else { + const newNotification = new NOTIFICATION({ + pid: notification.pid, + type: notification.type, + users: [{ + user: notification.objectID, + timestamp: now + }], + link: notification.link, + objectID: notification.objectID, + read: false, + lastUpdated: now + }); + await newNotification.save(); + } + } + /*else if(notification.type === 'yeah') { + // { pid: userToFollowContent.pid, type: "follow", objectID: req.pid, link: `/users/${req.pid}` } + let existingNotification = await NOTIFICATION.findOne({ pid: notification.pid, objectID: notification.objectID }) + if(existingNotification) { + existingNotification.lastUpdated = new Date(); + return await existingNotification.save(); + } + existingNotification = await NOTIFICATION.findOne({ pid: notification.pid, type: 'yeah' }); + if(existingNotification) { + existingNotification.users.push({ + user: notification.objectID, + timeStamp: new Date() + }); + existingNotification.lastUpdated = new Date(); + existingNotification.link = notification.link; + existingNotification.objectID = notification.objectID; + return await existingNotification.save(); + } + else { + let newNotification = new NOTIFICATION({ + pid: notification.pid, + type: notification.type, + users: [{ + user: notification.objectID, + timestamp: new Date() + }], + link: notification.link, + objectID: notification.objectID, + read: false, + lastUpdated: new Date() + }); + await newNotification.save(); + } + }*/ +} +async function getFriends(pid) { + try { const pids = await friendsClient.getUserFriendPIDs({ pid: pid }, { @@ -388,8 +388,12 @@ const methods = { }) }); return pids.pids; - }, - getFriendRequests: async function(pid) { + } catch (e) { + return []; + } +} +async function getFriendRequests(pid) { + try { const requests = await friendsClient.getUserFriendRequestsIncoming({ pid: pid }, { @@ -398,47 +402,74 @@ const methods = { }) }); return requests.friendRequests; - }, - login: async function(username, password) { - return await apiClient.login({ - username: username, - password: password, - grantType: 'password' - }, { - metadata: grpc.Metadata({ - 'X-API-Key': apiKey - }) - }); - }, - refreshLogin: async function(refreshToken) { - return await apiClient.login({ - refreshToken: refreshToken - }, { - metadata: grpc.Metadata({ - 'X-API-Key': apiKey - }) - }); - }, - getUserDataFromToken: async function(token) { - return apiClient.getUserData({}, { - metadata: grpc.Metadata({ - 'X-API-Key': apiKey, - 'X-Token': token - }) - }); - }, - getUserDataFromPid: async function(pid) { - return accountClient.getUserData({ - pid: pid - }, { - metadata: grpc.Metadata({ - 'X-API-Key': apiKey - }) - }); - }, - getPid: async function(token) { - const user = await this.getUserDataFromToken(token); - return user.pid; + } catch (e) { + return []; } +} +async function login(username, password) { + return await apiClient.login({ + username: username, + password: password, + grantType: 'password' + }, { + metadata: grpc.Metadata({ + 'X-API-Key': apiKey + }) + }); +} +async function refreshLogin(refreshToken) { + return await apiClient.login({ + refreshToken: refreshToken + }, { + metadata: grpc.Metadata({ + 'X-API-Key': apiKey + }) + }); +} +async function getUserDataFromToken(token) { + return apiClient.getUserData({}, { + metadata: grpc.Metadata({ + 'X-API-Key': apiKey, + 'X-Token': token + }) + }); +} +async function getUserDataFromPid(pid) { + return accountClient.getUserData({ + pid: pid + }, { + metadata: grpc.Metadata({ + 'X-API-Key': apiKey + }) + }); +} +async function getPid(token) { + const user = await this.getUserDataFromToken(token); + return user.pid; +} +module.exports = { + decodeParamPack, + processServiceToken, + decryptToken, + unpackToken, + processPainting, + nintendoPasswordHash, + getCommunityHash, + getUserHash, + refreshCache, + setName, + resizeImage, + createBMPTgaBuffer, + processLanguage, + uploadCDNAsset, + newNotification, + getFriends, + getFriendRequests, + login, + refreshLogin, + getUserDataFromToken, + getUserDataFromPid, + getPid, + create_user, + INVALID_POST_BODY_REGEX }; -exports.data = methods;