diff --git a/src/database.js b/src/database.js index 84cbfed..ebdd39a 100644 --- a/src/database.js +++ b/src/database.js @@ -16,510 +16,513 @@ let connection; mongoose.set('strictQuery', true); async function connect() { - await mongoose.connect(`${uri}/${database}`, options); - connection = mongoose.connection; - connection.on('connected', function () { - logger.info(`MongoDB connected ${this.name}`); - }); - connection.on('error', console.error.bind(console, 'connection error:')); - connection.on('close', () => { - connection.removeAllListeners(); - }); + await mongoose.connect(`${uri}/${database}`, options); + connection = mongoose.connection; + connection.on('connected', function () { + logger.info(`MongoDB connected ${this.name}`); + }); + connection.on('error', console.error.bind(console, 'connection error:')); + connection.on('close', () => { + connection.removeAllListeners(); + }); } function verifyConnected() { - if (!connection) { - connect(); - } + if (!connection) { + connect(); + } } async function getCommunities(numberOfCommunities) { - verifyConnected(); - if(numberOfCommunities === -1) - return COMMUNITY.find({ parent: null, type: [0,2] }); - else - return COMMUNITY.find({ parent: null, type: [0,2] }).limit(numberOfCommunities); + verifyConnected(); + if (numberOfCommunities === -1) { + return COMMUNITY.find({ parent: null, type: [0,2] }); + } else { + return COMMUNITY.find({ parent: null, type: [0,2] }).limit(numberOfCommunities); + } } async function getMostPopularCommunities(numberOfCommunities) { - verifyConnected(); - return COMMUNITY.find({ parent: null, type: 0 }).sort({followers: -1}).limit(numberOfCommunities); + verifyConnected(); + return COMMUNITY.find({ parent: null, type: 0 }).sort({followers: -1}).limit(numberOfCommunities); } async function getNewCommunities(numberOfCommunities) { - verifyConnected(); - return COMMUNITY.find({ parent: null, type: 0 }).sort([['created_at', -1]]).limit(numberOfCommunities); + verifyConnected(); + return COMMUNITY.find({ parent: null, type: 0 }).sort([['created_at', -1]]).limit(numberOfCommunities); } async function getSubCommunities(communityID) { - verifyConnected(); - return COMMUNITY.find({ - parent: communityID - }); + verifyConnected(); + return COMMUNITY.find({ + parent: communityID + }); } async function getCommunityByTitleID(title_id) { - verifyConnected(); - return COMMUNITY.findOne({ - title_id: title_id - }); + verifyConnected(); + return COMMUNITY.findOne({ + title_id: title_id + }); } async function getCommunityByID(community_id) { - verifyConnected(); - return COMMUNITY.findOne({ - olive_community_id: community_id - }); + verifyConnected(); + return COMMUNITY.findOne({ + olive_community_id: community_id + }); } async function getTotalPostsByCommunity(community) { - verifyConnected(); - return POST.find({ - community_id: community.olive_community_id, - parent: null, - removed: false - }).countDocuments(); + verifyConnected(); + return POST.find({ + community_id: community.olive_community_id, + parent: null, + removed: false + }).countDocuments(); } async function getPostByID(postID) { - verifyConnected(); - return POST.findOne({ - id: postID - }); + verifyConnected(); + return POST.findOne({ + id: postID + }); } async function getPostsByUserID(userID) { - verifyConnected(); - return POST.find({ - pid: userID, - parent: null, - removed: false - }); + verifyConnected(); + return POST.find({ + pid: userID, + parent: null, + removed: false + }); } async function getPostReplies(postID, number) { - verifyConnected(); - return POST.find({ - parent: postID, - removed: false - }).limit(number); + verifyConnected(); + return POST.find({ + parent: postID, + removed: false + }).limit(number); } async function getDuplicatePosts(pid, post) { - verifyConnected(); - return POST.findOne({ - pid: pid, - body: post.body, - painting: post.painting, - screenshot: post.screenshot, - parent: null, - removed: false - }); + verifyConnected(); + return POST.findOne({ + pid: pid, + body: post.body, + painting: post.painting, + screenshot: post.screenshot, + parent: null, + removed: false + }); } async function getUserPostRepliesAfterTimestamp(post, numberOfPosts) { - verifyConnected(); - return POST.find({ - parent: post.pid, - created_at: { $lt: post.created_at }, - message_to_pid: null, - removed: false - }).limit(numberOfPosts); + verifyConnected(); + return POST.find({ + parent: post.pid, + created_at: { $lt: post.created_at }, + message_to_pid: null, + removed: false + }).limit(numberOfPosts); } async function getNumberUserPostsByID(userID, number) { - verifyConnected(); - return POST.find({ - pid: userID, - parent: null, - message_to_pid: null, - removed: false - }).sort({ created_at: -1}).limit(number); + verifyConnected(); + return POST.find({ + pid: userID, + parent: null, + message_to_pid: null, + removed: false + }).sort({ created_at: -1}).limit(number); } async function getTotalPostsByUserID(userID) { - verifyConnected(); - return POST.find({ - pid: userID, - parent: null, - message_to_pid: null, - removed: false - }).countDocuments(); + verifyConnected(); + return POST.find({ + pid: userID, + parent: null, + message_to_pid: null, + removed: false + }).countDocuments(); } async function getHotPostsByCommunity(community, numberOfPosts) { - verifyConnected(); - return POST.find({ - community_id: community.olive_community_id, - parent: null, - removed: false - }).sort({empathy_count: -1}).limit(numberOfPosts); + verifyConnected(); + return POST.find({ + community_id: community.olive_community_id, + parent: null, + removed: false + }).sort({empathy_count: -1}).limit(numberOfPosts); } async function getNumberNewCommunityPostsByID(community, number) { - verifyConnected(); - return POST.find({ - community_id: community.olive_community_id, - parent: null, - removed: false - }).sort({ created_at: -1}).limit(number); + verifyConnected(); + return POST.find({ + community_id: community.olive_community_id, + parent: null, + removed: false + }).sort({ created_at: -1}).limit(number); } async function getNumberPopularCommunityPostsByID(community, limit, offset) { - verifyConnected(); - return POST.find({ - community_id: community.olive_community_id, - parent: null, - removed: false - }).sort({ empathy_count: -1}).skip(offset).limit(limit); + verifyConnected(); + return POST.find({ + community_id: community.olive_community_id, + parent: null, + removed: false + }).sort({ empathy_count: -1}).skip(offset).limit(limit); } async function getNumberVerifiedCommunityPostsByID(community, limit, offset) { - verifyConnected(); - return POST.find({ - community_id: community.olive_community_id, - verified: true, - parent: null, - removed: false - }).sort({ created_at: -1}).skip(offset).limit(limit); + verifyConnected(); + return POST.find({ + community_id: community.olive_community_id, + verified: true, + parent: null, + removed: false + }).sort({ created_at: -1}).skip(offset).limit(limit); } async function getPostsByCommunity(community, numberOfPosts) { - verifyConnected(); - return POST.find({ - community_id: community.olive_community_id, - parent: null, - removed: false - }).limit(numberOfPosts); + verifyConnected(); + return POST.find({ + community_id: community.olive_community_id, + parent: null, + removed: false + }).limit(numberOfPosts); } async function getPostsByCommunityKey(community, numberOfPosts, search_key) { - verifyConnected(); - return POST.find({ - community_id: community.olive_community_id, - search_key: search_key, - parent: null, - removed: false - }).limit(numberOfPosts); + verifyConnected(); + return POST.find({ + community_id: community.olive_community_id, + search_key: search_key, + parent: null, + removed: false + }).limit(numberOfPosts); } async function getNewPostsByCommunity(community, limit, offset) { - verifyConnected(); - return POST.find({ - community_id: community.olive_community_id, - parent: null, - removed: false - }).sort({ created_at: -1 }).skip(offset).limit(limit); + verifyConnected(); + return POST.find({ + community_id: community.olive_community_id, + parent: null, + removed: false + }).sort({ created_at: -1 }).skip(offset).limit(limit); } async function getAllUserPosts(pid) { - verifyConnected(); - return POST.find({ - pid: pid, - message_to_pid: null - }); + verifyConnected(); + return POST.find({ + pid: pid, + message_to_pid: null + }); } async function getRemovedUserPosts(pid) { - verifyConnected(); - return POST.find({ - pid: pid, - message_to_pid: null, - removed: true - }); + verifyConnected(); + return POST.find({ + pid: pid, + message_to_pid: null, + removed: true + }); } async function getUserPostsAfterTimestamp(post, numberOfPosts) { - verifyConnected(); - return POST.find({ - pid: post.pid, - created_at: { $lt: post.created_at }, - parent: null, - message_to_pid: null, - removed: false - }).limit(numberOfPosts); + verifyConnected(); + return POST.find({ + pid: post.pid, + created_at: { $lt: post.created_at }, + parent: null, + message_to_pid: null, + removed: false + }).limit(numberOfPosts); } async function getUserPostsOffset(pid, limit, offset) { - verifyConnected(); - return POST.find({ - pid: pid, - parent: null, - message_to_pid: null, - removed: false - }).skip(offset).limit(limit).sort({ created_at: -1}); + verifyConnected(); + return POST.find({ + pid: pid, + parent: null, + message_to_pid: null, + removed: false + }).skip(offset).limit(limit).sort({ created_at: -1}); } async function getCommunityPostsAfterTimestamp(post, numberOfPosts) { - verifyConnected(); - return POST.find({ - community_id: post.community_id, - created_at: { $lt: post.created_at }, - parent: null, - removed: false - }).limit(numberOfPosts); + verifyConnected(); + return POST.find({ + community_id: post.community_id, + created_at: { $lt: post.created_at }, + parent: null, + removed: false + }).limit(numberOfPosts); } async function getEndpoints() { - verifyConnected(); - return ENDPOINT.find({}); + verifyConnected(); + return ENDPOINT.find({}); } async function getEndPoint(accessLevel) { - verifyConnected(); - return ENDPOINT.findOne({ - server_access_level: accessLevel - }) + verifyConnected(); + return ENDPOINT.findOne({ + server_access_level: accessLevel + }); } async function getUsersSettings(numberOfUsers) { - verifyConnected(); - if(numberOfUsers === -1) - return SETTINGS.find({}); - else - return SETTINGS.find({}).limit(numberOfUsers); + verifyConnected(); + if (numberOfUsers === -1) { + return SETTINGS.find({}); + } else { + return SETTINGS.find({}).limit(numberOfUsers); + } } async function getUsersContent(numberOfUsers) { - verifyConnected(); - if(numberOfUsers === -1) - return SETTINGS.find({}); - else - return SETTINGS.find({}).limit(numberOfUsers); + verifyConnected(); + if (numberOfUsers === -1) { + return SETTINGS.find({}); + } else { + return SETTINGS.find({}).limit(numberOfUsers); + } } async function getUserSettings(pid) { - verifyConnected(); - return SETTINGS.findOne({pid: pid}); + verifyConnected(); + return SETTINGS.findOne({pid: pid}); } async function getUserContent(pid) { - verifyConnected(); - return CONTENT.findOne({pid: pid}); + verifyConnected(); + return CONTENT.findOne({pid: pid}); } async function getFollowingUsers(content) { - verifyConnected(); - return SETTINGS.find({ - pid: content.following_users - }); + verifyConnected(); + return SETTINGS.find({ + pid: content.following_users + }); } async function getFollowedUsers(content) { - verifyConnected(); - return SETTINGS.find({ - pid: content.followed_users - }); + verifyConnected(); + return SETTINGS.find({ + pid: content.followed_users + }); } async function getNewsFeed(content, numberOfPosts) { - verifyConnected(); - return POST.find({ - $or: [ - {pid: content.followed_users}, - {pid: content.pid}, - {community_id: content.followed_communities}, - ], - parent: null, - message_to_pid: null, - removed: false - }).limit(numberOfPosts).sort({ created_at: -1}); + verifyConnected(); + return POST.find({ + $or: [ + {pid: content.followed_users}, + {pid: content.pid}, + {community_id: content.followed_communities}, + ], + parent: null, + message_to_pid: null, + removed: false + }).limit(numberOfPosts).sort({ created_at: -1}); } async function getNewsFeedAfterTimestamp(content, numberOfPosts, post) { - verifyConnected(); - return POST.find({ - $or: [ - {pid: content.followed_users}, - {pid: content.pid}, - {community_id: content.followed_communities}, - ], - created_at: { $lt: post.created_at }, - parent: null, - message_to_pid: null, - removed: false - }).limit(numberOfPosts).sort({ created_at: -1}); + verifyConnected(); + return POST.find({ + $or: [ + {pid: content.followed_users}, + {pid: content.pid}, + {community_id: content.followed_communities}, + ], + created_at: { $lt: post.created_at }, + parent: null, + message_to_pid: null, + removed: false + }).limit(numberOfPosts).sort({ created_at: -1}); } async function getNewsFeedOffset(content, limit, offset) { - verifyConnected(); - return POST.find({ - $or: [ - {pid: content.followed_users}, - {pid: content.pid}, - {community_id: content.followed_communities}, - ], - parent: null, - message_to_pid: null, - removed: false - }).skip(offset).limit(limit).sort({ created_at: -1}); + verifyConnected(); + return POST.find({ + $or: [ + {pid: content.followed_users}, + {pid: content.pid}, + {community_id: content.followed_communities}, + ], + parent: null, + message_to_pid: null, + removed: false + }).skip(offset).limit(limit).sort({ created_at: -1}); } async function getConversations(pid) { - verifyConnected(); - return CONVERSATION.find({ - "users.pid": pid - }).sort({ last_updated: -1}); + verifyConnected(); + return CONVERSATION.find({ + 'users.pid': pid + }).sort({ last_updated: -1}); } async function getUnreadConversationCount(pid) { - verifyConnected(); - return CONVERSATION.find({ - "users": { $elemMatch: { - 'pid': pid, - 'read': false - }} + verifyConnected(); + return CONVERSATION.find({ + 'users': { $elemMatch: { + 'pid': pid, + 'read': false + }} - }).countDocuments(); + }).countDocuments(); } async function getConversationByID(community_id) { - verifyConnected(); - return CONVERSATION.findOne({ - type: 3, - id: community_id - }); + verifyConnected(); + return CONVERSATION.findOne({ + type: 3, + id: community_id + }); } async function getConversationMessages(community_id, limit, offset) { - verifyConnected(); - return POST.find({ - community_id: community_id, - parent: null, - removed: false - }).sort({created_at: 1}).skip(offset).limit(limit); + verifyConnected(); + return POST.find({ + community_id: community_id, + parent: null, + removed: false + }).sort({created_at: 1}).skip(offset).limit(limit); } async function getConversationByUsers(pids) { - verifyConnected(); - return CONVERSATION.findOne({ - $and: [ - {'users.pid': pids[0]}, - {'users.pid': pids[1]} - ] - }); + verifyConnected(); + return CONVERSATION.findOne({ + $and: [ + {'users.pid': pids[0]}, + {'users.pid': pids[1]} + ] + }); } async function getLatestMessage(pid, pid2) { - verifyConnected(); - return POST.findOne({ - $or: [ - {pid: pid, message_to_pid: pid2}, - {pid: pid2, message_to_pid: pid} - ], - removed: false - }) + verifyConnected(); + return POST.findOne({ + $or: [ + {pid: pid, message_to_pid: pid2}, + {pid: pid2, message_to_pid: pid} + ], + removed: false + }); } async function getNotifications(pid, limit, offset) { - verifyConnected(); - return NOTIFICATION.find({ - pid: pid, - }).sort({lastUpdated: -1}).skip(offset).limit(limit); + verifyConnected(); + return NOTIFICATION.find({ + pid: pid, + }).sort({lastUpdated: -1}).skip(offset).limit(limit); } async function getNotification(pid, type, reference_id) { - verifyConnected(); - return NOTIFICATION.findOne({ - pid: pid, - type: type, - reference_id: reference_id - }) + verifyConnected(); + return NOTIFICATION.findOne({ + pid: pid, + type: type, + reference_id: reference_id + }); } async function getLastNotification(pid) { - verifyConnected(); - return NOTIFICATION.findOne({ - pid: pid - }).sort({lastUpdated: -1}).limit(1); + verifyConnected(); + return NOTIFICATION.findOne({ + pid: pid + }).sort({lastUpdated: -1}).limit(1); } async function getUnreadNotificationCount(pid) { - verifyConnected(); - return NOTIFICATION.find({ - pid: pid, - read: false - }).countDocuments(); + verifyConnected(); + return NOTIFICATION.find({ + pid: pid, + read: false + }).countDocuments(); } async function getAllReports(offset, limit) { - verifyConnected(); - return REPORT.find().sort({created_at: -1}).skip(offset).limit(limit); + verifyConnected(); + return REPORT.find().sort({created_at: -1}).skip(offset).limit(limit); } async function getAllOpenReports(offset, limit) { - verifyConnected(); - return REPORT.find({ resolved: false }).sort({created_at: -1}).skip(offset).limit(limit); + verifyConnected(); + return REPORT.find({ resolved: false }).sort({created_at: -1}).skip(offset).limit(limit); } async function getReportsByUser(pid, offset, limit) { - verifyConnected(); - return REPORT.find({ reported_by: pid }).sort({created_at: -1}).skip(offset).limit(limit); + verifyConnected(); + return REPORT.find({ reported_by: pid }).sort({created_at: -1}).skip(offset).limit(limit); } async function getReportsByPost(postID, offset, limit) { - verifyConnected(); - return REPORT.find({ post_id: postID }).sort({created_at: -1}).skip(offset).limit(limit); + verifyConnected(); + return REPORT.find({ post_id: postID }).sort({created_at: -1}).skip(offset).limit(limit); } async function getReportById(id) { - verifyConnected(); - return REPORT.findById(id); + verifyConnected(); + return REPORT.findById(id); } module.exports = { - connect, - getCommunities, - getMostPopularCommunities, - getNewCommunities, - getSubCommunities, - getCommunityByTitleID, - getCommunityByID, - getTotalPostsByCommunity, - getPostsByCommunity, - getHotPostsByCommunity, - getNumberNewCommunityPostsByID, - getNumberPopularCommunityPostsByID, - getNumberVerifiedCommunityPostsByID, - getNewPostsByCommunity, - getPostsByCommunityKey, - getPostsByUserID, - getPostReplies, - getUserPostRepliesAfterTimestamp, - getNumberUserPostsByID, - getTotalPostsByUserID, - getPostByID, - getDuplicatePosts, - getEndpoints, - getEndPoint, - getUserPostsAfterTimestamp, - getUserPostsOffset, - getCommunityPostsAfterTimestamp, - getNewsFeed, - getNewsFeedAfterTimestamp, - getNewsFeedOffset, - getFollowingUsers, - getFollowedUsers, - getConversations, - getConversationByID, - getConversationByUsers, - getConversationMessages, - getUnreadConversationCount, - getLatestMessage, - getUsersSettings, - getUsersContent, - getUserSettings, - getUserContent, - getNotifications, - getUnreadNotificationCount, - getNotification, - getLastNotification, - getAllUserPosts, - getRemovedUserPosts, - getAllReports, - getAllOpenReports, - getReportsByUser, - getReportsByPost, - getReportById + connect, + getCommunities, + getMostPopularCommunities, + getNewCommunities, + getSubCommunities, + getCommunityByTitleID, + getCommunityByID, + getTotalPostsByCommunity, + getPostsByCommunity, + getHotPostsByCommunity, + getNumberNewCommunityPostsByID, + getNumberPopularCommunityPostsByID, + getNumberVerifiedCommunityPostsByID, + getNewPostsByCommunity, + getPostsByCommunityKey, + getPostsByUserID, + getPostReplies, + getUserPostRepliesAfterTimestamp, + getNumberUserPostsByID, + getTotalPostsByUserID, + getPostByID, + getDuplicatePosts, + getEndpoints, + getEndPoint, + getUserPostsAfterTimestamp, + getUserPostsOffset, + getCommunityPostsAfterTimestamp, + getNewsFeed, + getNewsFeedAfterTimestamp, + getNewsFeedOffset, + getFollowingUsers, + getFollowedUsers, + getConversations, + getConversationByID, + getConversationByUsers, + getConversationMessages, + getUnreadConversationCount, + getLatestMessage, + getUsersSettings, + getUsersContent, + getUserSettings, + getUserContent, + getNotifications, + getUnreadNotificationCount, + getNotification, + getLastNotification, + getAllUserPosts, + getRemovedUserPosts, + getAllReports, + getAllOpenReports, + getReportsByUser, + getReportsByPost, + getReportById }; diff --git a/src/middleware/auth.js b/src/middleware/auth.js index 5925def..5df34b3 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -1,138 +1,139 @@ const config = require('../../config.json'); const util = require('../util'); -const moment = require("moment/moment"); +const moment = require('moment/moment'); const db = require('../database'); async function auth(request, response, next) { - // Web files - if(isStartOfPath(request.path, '/css/') || + // Web files + if (isStartOfPath(request.path, '/css/') || isStartOfPath(request.path, '/fonts/') || isStartOfPath(request.path, '/js/') || request.path === '/favicon.ico' || isStartOfPath(request.path, '/web/') || isStartOfPath(request.path, '/images/') || isStartOfPath(request.path, '/image/')) { - request.lang = util.data.processLanguage(); - if(includes(request, 'juxt')) - request.directory = 'web'; - else - request.directory = includes(request, 'portal') ? 'portal' : 'ctr'; - return next() - } + request.lang = util.data.processLanguage(); + if (includes(request, 'juxt')) { + request.directory = 'web'; + } else { + request.directory = includes(request, 'portal') ? 'portal' : 'ctr'; + } + return next(); + } - // Get pid and fetch user data - if(request.cookies.access_token) { - try { - request.user = await util.data.getUserDataFromToken(request.cookies.access_token); - } - catch(e) { - response.clearCookie('access_token'); - response.clearCookie('refresh_token'); - return response.render('web/login.ejs', {toast: 'Unable to reach the account server. Try again later.', cdnURL: config.CDN_domain,}); - } - request.pid = request.user ? request.user.pid : null; - } - else if(request.headers["x-nintendo-servicetoken"]) { - 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; - } + // Get pid and fetch user data + if (request.cookies.access_token) { + try { + request.user = await util.data.getUserDataFromToken(request.cookies.access_token); + } catch (e) { + response.clearCookie('access_token'); + response.clearCookie('refresh_token'); + return response.render('web/login.ejs', {toast: 'Unable to reach the account server. Try again later.', cdnURL: config.CDN_domain,}); + } + request.pid = request.user ? request.user.pid : null; + } else if (request.headers['x-nintendo-servicetoken']) { + 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; + } - // Set headers - request.paramPackData = request.headers["x-nintendo-parampack"] ? util.data.decodeParamPack(request.headers["x-nintendo-parampack"]) : null; - response.header('X-Nintendo-WhiteList', config.whitelist); + // Set headers + request.paramPackData = request.headers['x-nintendo-parampack'] ? util.data.decodeParamPack(request.headers['x-nintendo-parampack']) : null; + response.header('X-Nintendo-WhiteList', config.whitelist); - // Ban check - if(request.user) { + // Ban check + if (request.user) { if (request.user.serverAccessLevel !== 'test' && request.user.serverAccessLevel !== 'dev') { response.status(500); return response.send('No access. Must be tester or dev'); } - // Set moderator status - request.moderator = request.user.accessLevel >= 2; - const user = await db.getUserSettings(request.pid); - if(user && moment(user.ban_lift_date) <= moment() && user.account_status !== 3) { - user.account_status = 0; - await user.save() - } - // This includes ban checks for both Juxt specifically and the account server, ideally this should be squashed - // assuming we support more gradual bans on PNID's - if(user && (user.account_status < 0 || user.account_status > 1 || request.user.accessLevel < 0)) - { - response.render(request.directory + '/partials/ban_notification.ejs', { - user: user, - moment: moment, - cdnURL: config.CDN_domain, - lang: request.lang, - pid: request.pid - }); - } - } + // Set moderator status + request.moderator = request.user.accessLevel >= 2; + const user = await db.getUserSettings(request.pid); + if (user && moment(user.ban_lift_date) <= moment() && user.account_status !== 3) { + user.account_status = 0; + await user.save(); + } + // This includes ban checks for both Juxt specifically and the account server, ideally this should be squashed + // assuming we support more gradual bans on PNID's + if (user && (user.account_status < 0 || user.account_status > 1 || request.user.accessLevel < 0)) { + response.render(request.directory + '/partials/ban_notification.ejs', { + user: user, + moment: moment, + cdnURL: config.CDN_domain, + lang: request.lang, + pid: request.pid + }); + } + } - // Juxt Website - if(includes(request, 'juxt')) { - request.lang = util.data.processLanguage(); - request.token = request.cookies.access_token; - request.paramPackData = null; - request.directory = 'web'; + // Juxt Website + if (includes(request, 'juxt')) { + request.lang = util.data.processLanguage(); + request.token = request.cookies.access_token; + request.paramPackData = null; + request.directory = 'web'; - // Open access pages - if(isStartOfPath(request.path, '/users/') || + // Open access pages + if (isStartOfPath(request.path, '/users/') || (isStartOfPath(request.path, '/titles/') && request.path !== '/titles/show') || (isStartOfPath(request.path, '/posts/') && !request.path.includes('/empathy'))) { - if(!request.pid) - request.pid = 1000000000; - return next(); - } - // Login endpoint - if(request.path === '/login') { - if(request.pid) - return response.redirect('/titles/show?src=login'); - return next(); - } - if(!request.pid) - return response.redirect('/login'); + if (!request.pid) { + request.pid = 1000000000; + } + return next(); + } + // Login endpoint + if (request.path === '/login') { + if (request.pid) { + return response.redirect('/titles/show?src=login'); + } + return next(); + } + if (!request.pid) { + return response.redirect('/login'); + } - return next(); - } - else { - // 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', { - user: null, - error: "Unable to parse service token. Are you using a Nintendo Network ID?" - }); - } - if(request.user.accessLevel < 3 && !request.paramPackData) { - return response.render('portal/partials/ban_notification.ejs', { - user: null, - error: "Missing auth headers" - }); - } - if(!request.user) { - return response.render('portal/partials/ban_notification.ejs', { - user: null, - error: "Unable to fetch user data. Please try again later." - }); - } - let userAgent = request.headers['user-agent']; - if(request.user.accessLevel < 3 && (request.cookies.access_token || (!userAgent.includes('Nintendo WiiU') && !userAgent.includes('Nintendo 3DS')))) - return response.render('portal/partials/ban_notification.ejs', { - user: null, - error: "Invalid authentication method used." - }); + return next(); + } else { + // 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', { + user: null, + error: 'Unable to parse service token. Are you using a Nintendo Network ID?' + }); + } + if (request.user.accessLevel < 3 && !request.paramPackData) { + return response.render('portal/partials/ban_notification.ejs', { + user: null, + error: 'Missing auth headers' + }); + } + if (!request.user) { + return response.render('portal/partials/ban_notification.ejs', { + user: null, + error: 'Unable to fetch user data. Please try again later.' + }); + } + const userAgent = request.headers['user-agent']; + if (request.user.accessLevel < 3 && (request.cookies.access_token || (!userAgent.includes('Nintendo WiiU') && !userAgent.includes('Nintendo 3DS')))) { + return response.render('portal/partials/ban_notification.ejs', { + user: null, + error: 'Invalid authentication method used.' + }); + } - request.lang = util.data.processLanguage(request.paramPackData); - request.directory = includes(request, 'portal') ? 'portal' : 'ctr'; - return next(); - } + request.lang = util.data.processLanguage(request.paramPackData); + request.directory = includes(request, 'portal') ? 'portal' : 'ctr'; + return next(); + } } function isStartOfPath(path, value) { - return path.indexOf(value) === 0; + return path.indexOf(value) === 0; } function includes(request, domain) { - return request.subdomains.findIndex(element => element.includes(domain)) !== -1 + return request.subdomains.findIndex(element => element.includes(domain)) !== -1; } module.exports = auth; diff --git a/src/middleware/consoleAuth.js b/src/middleware/consoleAuth.js index 8baf643..5c12252 100644 --- a/src/middleware/consoleAuth.js +++ b/src/middleware/consoleAuth.js @@ -1,71 +1,71 @@ const config = require('../../config.json'); const util = require('../util'); -const moment = require("moment/moment"); +const moment = require('moment/moment'); const db = require('../database'); async function auth(request, response, next) { - // Get pid and fetch user data - if(request.headers["x-nintendo-servicetoken"]) { - 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; - } + // Get pid and fetch user data + if (request.headers['x-nintendo-servicetoken']) { + 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; + } - // Set headers - request.paramPackData = request.headers["x-nintendo-parampack"] ? util.data.decodeParamPack(request.headers["x-nintendo-parampack"]) : null; - response.header('X-Nintendo-WhiteList', config.whitelist); + // Set headers + request.paramPackData = request.headers['x-nintendo-parampack'] ? util.data.decodeParamPack(request.headers['x-nintendo-parampack']) : null; + response.header('X-Nintendo-WhiteList', config.whitelist); - // Ban check - if(request.user) { - // Set moderator status - request.moderator = request.user.accessLevel >= 2; - const user = await db.getUserSettings(request.pid); - if(user && moment(user.ban_lift_date) <= moment() && user.account_status !== 3) { - user.account_status = 0; - await user.save() - } - // This includes ban checks for both Juxt specifically and the account server, ideally this should be squashed - // assuming we support more gradual bans on PNID's - if(user && (user.account_status < 0 || user.account_status > 1 || request.user.accessLevel < 0)) - { - response.render(request.directory + '/partials/ban_notification.ejs', { - user: user, - moment: moment, - cdnURL: config.CDN_domain, - lang: request.lang, - pid: request.pid - }); - } - } + // Ban check + if (request.user) { + // Set moderator status + request.moderator = request.user.accessLevel >= 2; + const user = await db.getUserSettings(request.pid); + if (user && moment(user.ban_lift_date) <= moment() && user.account_status !== 3) { + user.account_status = 0; + await user.save(); + } + // This includes ban checks for both Juxt specifically and the account server, ideally this should be squashed + // assuming we support more gradual bans on PNID's + if (user && (user.account_status < 0 || user.account_status > 1 || request.user.accessLevel < 0)) { + response.render(request.directory + '/partials/ban_notification.ejs', { + user: user, + moment: moment, + cdnURL: config.CDN_domain, + lang: request.lang, + pid: request.pid + }); + } + } - // 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', { - user: null, - error: "Unable to parse service token. Are you using a Nintendo Network ID?" - }); - } - if(request.user.accessLevel < 3 && !request.paramPackData) { - return response.render('portal/partials/ban_notification.ejs', { - user: null, - error: "Missing auth headers" - }); - } - if(!request.user) { - return response.render('portal/partials/ban_notification.ejs', { - user: null, - error: "Unable to fetch user data. Please try again later." - }); - } - let userAgent = request.headers['user-agent']; - if(request.user.accessLevel < 3 && (request.cookies.access_token || (!userAgent.includes('Nintendo WiiU') && !userAgent.includes('Nintendo 3DS')))) - return response.render('portal/partials/ban_notification.ejs', { - user: null, - error: "Invalid authentication method used." - }); + // 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', { + user: null, + error: 'Unable to parse service token. Are you using a Nintendo Network ID?' + }); + } + if (request.user.accessLevel < 3 && !request.paramPackData) { + return response.render('portal/partials/ban_notification.ejs', { + user: null, + error: 'Missing auth headers' + }); + } + if (!request.user) { + return response.render('portal/partials/ban_notification.ejs', { + user: null, + error: 'Unable to fetch user data. Please try again later.' + }); + } + const userAgent = request.headers['user-agent']; + if (request.user.accessLevel < 3 && (request.cookies.access_token || (!userAgent.includes('Nintendo WiiU') && !userAgent.includes('Nintendo 3DS')))) { + return response.render('portal/partials/ban_notification.ejs', { + user: null, + error: 'Invalid authentication method used.' + }); + } - request.lang = util.data.processLanguage(request.paramPackData); - request.directory = request.subdomains[1]; - return next(); + request.lang = util.data.processLanguage(request.paramPackData); + request.directory = request.subdomains[1]; + return next(); } module.exports = auth; diff --git a/src/middleware/staticFiles.js b/src/middleware/staticFiles.js index 3ff02c1..61677bf 100644 --- a/src/middleware/staticFiles.js +++ b/src/middleware/staticFiles.js @@ -1,8 +1,8 @@ const util = require('../util'); async function staticFiles(request, response, next) { - // Web files - if(isStartOfPath(request.path, '/css/') || + // Web files + if (isStartOfPath(request.path, '/css/') || isStartOfPath(request.path, '/fonts/') || isStartOfPath(request.path, '/js/') || request.path === '/favicon.ico' || @@ -10,23 +10,23 @@ async function staticFiles(request, response, next) { isStartOfPath(request.path, '/images/') || isStartOfPath(request.path, '/image/')) { - request.lang = util.data.processLanguage(); + request.lang = util.data.processLanguage(); - if(request.subdomains.includes('juxt')) - request.directory = 'web'; - else - request.directory = request.subdomains[1]; - return next() - } - else if(request.path === "/") { - return response.redirect('/titles/show'); - } - else - return response.sendStatus(404); + if (request.subdomains.includes('juxt')) { + request.directory = 'web'; + } else { + request.directory = request.subdomains[1]; + } + return next(); + } else if (request.path === '/') { + return response.redirect('/titles/show'); + } else { + return response.sendStatus(404); + } } function isStartOfPath(path, value) { - return path.indexOf(value) === 0; + return path.indexOf(value) === 0; } diff --git a/src/middleware/webAuth.js b/src/middleware/webAuth.js index b861a69..6acf0ca 100644 --- a/src/middleware/webAuth.js +++ b/src/middleware/webAuth.js @@ -1,68 +1,69 @@ const config = require('../../config.json'); const util = require('../util'); -const moment = require("moment/moment"); +const moment = require('moment/moment'); const db = require('../database'); async function auth(request, response, next) { - // Get pid and fetch user data - request.lang = util.data.processLanguage(); - request.paramPackData = null; - request.directory = 'web'; - request.token = request.cookies.access_token; - if(request.cookies.access_token) { - try { - request.user = await util.data.getUserDataFromToken(request.token); - } - catch(e) { - console.log(e); - if(request.path === "/login") { - return next(); - } - return response.render('web/login.ejs', {toast: 'Unable to reach the account server. Try again later.', cdnURL: config.CDN_domain,}); - } - request.pid = request.user ? request.user.pid : null; - } + // Get pid and fetch user data + request.lang = util.data.processLanguage(); + request.paramPackData = null; + request.directory = 'web'; + request.token = request.cookies.access_token; + if (request.cookies.access_token) { + try { + request.user = await util.data.getUserDataFromToken(request.token); + } catch (e) { + console.log(e); + if (request.path === '/login') { + return next(); + } + return response.render('web/login.ejs', {toast: 'Unable to reach the account server. Try again later.', cdnURL: config.CDN_domain,}); + } + request.pid = request.user ? request.user.pid : null; + } - // Ban check - if(request.user) { - // Set moderator status - request.moderator = request.user.accessLevel >= 2; - const user = await db.getUserSettings(request.pid); - if(user && moment(user.ban_lift_date) <= moment() && user.account_status !== 3) { - user.account_status = 0; - await user.save() - } - // This includes ban checks for both Juxt specifically and the account server, ideally this should be squashed - // assuming we support more gradual bans on PNID's - if(user && (user.account_status < 0 || user.account_status > 1 || request.user.accessLevel < 0)) - { - return response.render('web/login.ejs', {toast: 'Your account has been suspended. For more information, log into https://pretendo.network/account', cdnURL: config.CDN_domain,}); - } - } + // Ban check + if (request.user) { + // Set moderator status + request.moderator = request.user.accessLevel >= 2; + const user = await db.getUserSettings(request.pid); + if (user && moment(user.ban_lift_date) <= moment() && user.account_status !== 3) { + user.account_status = 0; + await user.save(); + } + // This includes ban checks for both Juxt specifically and the account server, ideally this should be squashed + // assuming we support more gradual bans on PNID's + if (user && (user.account_status < 0 || user.account_status > 1 || request.user.accessLevel < 0)) { + return response.render('web/login.ejs', {toast: 'Your account has been suspended. For more information, log into https://pretendo.network/account', cdnURL: config.CDN_domain,}); + } + } - // Open access pages - if(isStartOfPath(request.path, '/users/') || + // Open access pages + if (isStartOfPath(request.path, '/users/') || (isStartOfPath(request.path, '/titles/') && request.path !== '/titles/show') || (isStartOfPath(request.path, '/posts/') && !request.path.includes('/empathy'))) { - if(!request.pid) - request.pid = 1000000000; - return next(); - } - // Login endpoint - if(request.path === '/login') { - if(request.pid) - return response.redirect('/titles/show?src=login'); - return next(); - } - console.log(request.route); - if(!request.user && request.path !== '/') - return response.redirect('/login'); + if (!request.pid) { + request.pid = 1000000000; + } + return next(); + } + // Login endpoint + if (request.path === '/login') { + if (request.pid) { + return response.redirect('/titles/show?src=login'); + } + return next(); + } + console.log(request.route); + if (!request.user && request.path !== '/') { + return response.redirect('/login'); + } - return next(); + return next(); } function isStartOfPath(path, value) { - return path.indexOf(value) === 0; + return path.indexOf(value) === 0; } module.exports = auth; diff --git a/src/models/communities.js b/src/models/communities.js index 865fa41..349f32e 100644 --- a/src/models/communities.js +++ b/src/models/communities.js @@ -1,100 +1,100 @@ const { Schema, model } = require('mongoose'); const CommunitySchema = new Schema({ - platform_id: Number, - name: String, - description: String, - open: { - type: Boolean, - default: true - }, - allows_comments: { - type: Boolean, - default: true - }, - /** + platform_id: Number, + name: String, + description: String, + open: { + type: Boolean, + default: true + }, + allows_comments: { + type: Boolean, + default: true + }, + /** * 0: Main Community * 1: Sub-Community * 2: Announcement Community * 3: Private Community */ - type: { - type: Number, - default: 0 - }, - parent: { - type: String, - default: null - }, - admins: { - type: [Number], - default: undefined - }, - created_at: { - type: Date, - default: new Date(), - }, - empathy_count: { - type: Number, - default: 0 - }, - followers: { - type: Number, - default: 0 - }, - has_shop_page: { - type: Number, - default: 0 - }, - icon: String, - title_ids: { - type: [String], - default: undefined - }, - title_id: { - type: [String], - default: undefined - }, - community_id: String, - olive_community_id: String, - is_recommended: { - type: Number, - default: 0 - }, - app_data: String, + type: { + type: Number, + default: 0 + }, + parent: { + type: String, + default: null + }, + admins: { + type: [Number], + default: undefined + }, + created_at: { + type: Date, + default: new Date(), + }, + empathy_count: { + type: Number, + default: 0 + }, + followers: { + type: Number, + default: 0 + }, + has_shop_page: { + type: Number, + default: 0 + }, + icon: String, + title_ids: { + type: [String], + default: undefined + }, + title_id: { + type: [String], + default: undefined + }, + community_id: String, + olive_community_id: String, + is_recommended: { + type: Number, + default: 0 + }, + app_data: String, }); CommunitySchema.methods.upEmpathy = async function() { - const empathy = this.get('empathy_count'); - this.set('empathy_count', empathy + 1); + const empathy = this.get('empathy_count'); + this.set('empathy_count', empathy + 1); - await this.save(); + await this.save(); }; CommunitySchema.methods.downEmpathy = async function() { - const empathy = this.get('empathy_count'); - this.set('empathy_count', empathy - 1); + const empathy = this.get('empathy_count'); + this.set('empathy_count', empathy - 1); - await this.save(); + await this.save(); }; CommunitySchema.methods.upFollower = async function() { - const followers = this.get('followers'); - this.set('followers', followers + 1); + const followers = this.get('followers'); + this.set('followers', followers + 1); - await this.save(); + await this.save(); }; CommunitySchema.methods.downFollower = async function() { - const followers = this.get('followers'); - this.set('followers', followers - 1); + const followers = this.get('followers'); + this.set('followers', followers - 1); - await this.save(); + await this.save(); }; const COMMUNITY = model('COMMUNITY', CommunitySchema); module.exports = { - CommunitySchema, - COMMUNITY + CommunitySchema, + COMMUNITY }; diff --git a/src/models/content.js b/src/models/content.js index cae5841..19feb85 100644 --- a/src/models/content.js +++ b/src/models/content.js @@ -1,60 +1,60 @@ const { Schema, model } = require('mongoose'); const ContentSchema = new Schema({ - pid: Number, - followed_communities: { - type: [String], - default: [0] - }, - followed_users: { - type: [Number], - default: [0] - }, - following_users: { - type: [Number], - default: [0] - }, + pid: Number, + followed_communities: { + type: [String], + default: [0] + }, + followed_users: { + type: [Number], + default: [0] + }, + following_users: { + type: [Number], + default: [0] + }, }); ContentSchema.methods.addToCommunities = async function(postID) { - const communities = this.get('followed_communities'); - communities.addToSet(postID); - await this.save(); -} + const communities = this.get('followed_communities'); + communities.addToSet(postID); + await this.save(); +}; ContentSchema.methods.removeFromCommunities = async function(postID) { - const communities = this.get('followed_communities'); - communities.pull(postID); - await this.save(); -} + const communities = this.get('followed_communities'); + communities.pull(postID); + await this.save(); +}; ContentSchema.methods.addToUsers = async function(postID) { - const users = this.get('followed_users'); - users.addToSet(postID); - await this.save(); -} + const users = this.get('followed_users'); + users.addToSet(postID); + await this.save(); +}; ContentSchema.methods.removeFromUsers = async function(postID) { - const users = this.get('followed_users'); - users.pull(postID); - await this.save(); -} + const users = this.get('followed_users'); + users.pull(postID); + await this.save(); +}; ContentSchema.methods.addToFollowers = async function(postID) { - const users = this.get('following_users'); - users.addToSet(postID); - await this.save(); -} + const users = this.get('following_users'); + users.addToSet(postID); + await this.save(); +}; ContentSchema.methods.removeFromFollowers = async function(postID) { - const users = this.get('following_users'); - users.pull(postID); - await this.save(); -} + const users = this.get('following_users'); + users.pull(postID); + await this.save(); +}; const CONTENT = model('CONTENT', ContentSchema); module.exports = { - ContentSchema, - CONTENT + ContentSchema, + CONTENT }; diff --git a/src/models/conversation.js b/src/models/conversation.js index 5b7cea9..8911ce7 100644 --- a/src/models/conversation.js +++ b/src/models/conversation.js @@ -1,60 +1,60 @@ const { Schema, model } = require('mongoose'); -const moment = require("moment"); +const moment = require('moment'); const snowflake = require('node-snowflake').Snowflake; const user = new Schema({ - pid: Number, - official: { - type: Boolean, - default: false - }, - read: { - type: Boolean, - default: true - } + pid: Number, + official: { + type: Boolean, + default: false + }, + read: { + type: Boolean, + default: true + } }); const ConversationSchema = new Schema({ - id: { - type: String, - default: snowflake.nextId() - }, - created_at: { - type: Date, - default: new Date(), - }, - last_updated: { - type: Date, - default: new Date(), - }, - message_preview: { - type: String, - default: "" - }, - users: [user] + id: { + type: String, + default: snowflake.nextId() + }, + created_at: { + type: Date, + default: new Date(), + }, + last_updated: { + type: Date, + default: new Date(), + }, + message_preview: { + type: String, + default: '' + }, + users: [user] }); ConversationSchema.methods.newMessage = async function(message, senderPID) { - this.last_updated = new Date(); + this.last_updated = new Date(); this.message_preview = message; const sender = this.users.find(user => user.pid === senderPID); if (sender) { sender.read = false; } await this.save(); -} +}; ConversationSchema.methods.markAsRead = async function(receiverPID) { - const receiver = this.users.find(user => user.pid === receiverPID); + const receiver = this.users.find(user => user.pid === receiverPID); if (receiver) { receiver.read = true; } - await this.save(); -} + await this.save(); +}; const CONVERSATION = model('CONVERSATION', ConversationSchema); module.exports = { - ConversationSchema: ConversationSchema, - CONVERSATION: CONVERSATION + ConversationSchema: ConversationSchema, + CONVERSATION: CONVERSATION }; diff --git a/src/models/endpoint.js b/src/models/endpoint.js index fe2a94b..7bfe091 100644 --- a/src/models/endpoint.js +++ b/src/models/endpoint.js @@ -1,19 +1,19 @@ const { Schema, model } = require('mongoose'); const endpointSchema = new Schema({ - status: Number, - server_access_level: String, - topics: Boolean, - guest_access: Boolean, - host: String, - api_host: String, - portal_host: String, - n3ds_host: String + status: Number, + server_access_level: String, + topics: Boolean, + guest_access: Boolean, + host: String, + api_host: String, + portal_host: String, + n3ds_host: String, }); const ENDPOINT = model('ENDPOINT', endpointSchema); module.exports = { - endpointSchema, - ENDPOINT + endpointSchema, + ENDPOINT, }; diff --git a/src/models/notifications.js b/src/models/notifications.js index 0835463..5e603fe 100644 --- a/src/models/notifications.js +++ b/src/models/notifications.js @@ -1,26 +1,26 @@ const { Schema, model } = require('mongoose'); const NotificationSchema = new Schema({ - pid: String, - type: String, - link: String, - objectID: String, - users: [{ - user: String, - timestamp: Date - }], - read: Boolean, - lastUpdated: Date + pid: String, + type: String, + link: String, + objectID: String, + users: [{ + user: String, + timestamp: Date + }], + read: Boolean, + lastUpdated: Date }); NotificationSchema.methods.markRead = async function() { - this.set('read', true); - await this.save(); + this.set('read', true); + await this.save(); }; const NOTIFICATION = model('NOTIFICATION', NotificationSchema); module.exports = { - NotificationSchema, - NOTIFICATION + NotificationSchema, + NOTIFICATION }; diff --git a/src/models/post.js b/src/models/post.js index eb511ed..f83b19f 100644 --- a/src/models/post.js +++ b/src/models/post.js @@ -1,122 +1,124 @@ const { Schema, model } = require('mongoose'); const PostSchema = new Schema({ - id: String, - title_id: String, - screen_name: String, - body: String, - app_data: String, - painting: String, - screenshot: String, - screenshot_length: Number, - search_key: { - type: [String], - default: undefined - }, - topic_tag: { - type: String, - default: undefined - }, - community_id: { - type: String, - default: undefined - }, - created_at: Date, - feeling_id: Number, - is_autopost: { - type: Number, - default: 0 - }, - is_community_private_autopost: { - type: Number, - default: 0 - }, - is_spoiler: { - type: Number, - default: 0 - }, - is_app_jumpable: { - type: Number, - default: 0 - }, - empathy_count: { - type: Number, - default: 0, - min: 0 - }, - country_id: { - type: Number, - default: 49 - }, - language_id: { - type: Number, - default: 1 - }, - mii: String, - mii_face_url: String, - pid: Number, - platform_id: Number, - region_id: Number, - parent: String, - reply_count: { - type: Number, - default: 0 - }, - verified: { - type: Boolean, - default: false - }, - message_to_pid: { - type: String, - default: null - }, - removed: { - type: Boolean, - default: false - }, - removed_reason: String, - removed_by: Number, - removed_at: Date, - yeahs: [Number] + id: String, + title_id: String, + screen_name: String, + body: String, + app_data: String, + painting: String, + screenshot: String, + screenshot_length: Number, + search_key: { + type: [String], + default: undefined + }, + topic_tag: { + type: String, + default: undefined + }, + community_id: { + type: String, + default: undefined + }, + created_at: Date, + feeling_id: Number, + is_autopost: { + type: Number, + default: 0 + }, + is_community_private_autopost: { + type: Number, + default: 0 + }, + is_spoiler: { + type: Number, + default: 0 + }, + is_app_jumpable: { + type: Number, + default: 0 + }, + empathy_count: { + type: Number, + default: 0, + min: 0 + }, + country_id: { + type: Number, + default: 49 + }, + language_id: { + type: Number, + default: 1 + }, + mii: String, + mii_face_url: String, + pid: Number, + platform_id: Number, + region_id: Number, + parent: String, + reply_count: { + type: Number, + default: 0 + }, + verified: { + type: Boolean, + default: false + }, + message_to_pid: { + type: String, + default: null + }, + removed: { + type: Boolean, + default: false + }, + removed_reason: String, + removed_by: Number, + removed_at: Date, + yeahs: [Number] }); PostSchema.methods.upReply = async function() { - const replyCount = this.get('reply_count'); - if(replyCount + 1 < 0) - this.set('reply_count', 0); - else - this.set('reply_count', replyCount + 1); + const replyCount = this.get('reply_count'); + if (replyCount + 1 < 0) { + this.set('reply_count', 0); + } else { + this.set('reply_count', replyCount + 1); + } - await this.save(); + await this.save(); }; PostSchema.methods.downReply = async function() { - const replyCount = this.get('reply_count'); - if(replyCount - 1 < 0) - this.set('reply_count', 0); - else - this.set('reply_count', replyCount - 1); + const replyCount = this.get('reply_count'); + if (replyCount - 1 < 0) { + this.set('reply_count', 0); + } else { + this.set('reply_count', replyCount - 1); + } - await this.save(); + await this.save(); }; PostSchema.methods.removePost = async function(reason, pid) { - this.set('removed', true); - this.set('removed_reason', reason); - this.set('removed_by', pid); - this.set('removed_at', new Date()) - await this.save(); + this.set('removed', true); + this.set('removed_reason', reason); + this.set('removed_by', pid); + this.set('removed_at', new Date()); + await this.save(); }; PostSchema.methods.unRemove = async function(reason) { - this.set('removed', false); - this.set('removed_reason', reason); - await this.save(); + this.set('removed', false); + this.set('removed_reason', reason); + await this.save(); }; const POST = model('POST', PostSchema); module.exports = { - PostSchema, - POST + PostSchema, + POST }; diff --git a/src/models/report.js b/src/models/report.js index 7e1ebca..9544306 100644 --- a/src/models/report.js +++ b/src/models/report.js @@ -1,33 +1,33 @@ const { Schema, model } = require('mongoose'); const ReportSchema = new Schema({ - pid: Number, - reported_by: Number, - post_id: String, - reason: Number, - message: String, - created_at: { - type: Date, - default: new Date() - }, - resolved: { - type: Boolean, - default: false - }, - resolved_by: Number, - resolved_at: Date, + pid: Number, + reported_by: Number, + post_id: String, + reason: Number, + message: String, + created_at: { + type: Date, + default: new Date() + }, + resolved: { + type: Boolean, + default: false + }, + resolved_by: Number, + resolved_at: Date, }); ReportSchema.methods.resolve = async function(pid) { - this.set('resolved', true); - this.set('resolved_by', pid); - this.set('resolved_at', new Date()) - await this.save(); + this.set('resolved', true); + this.set('resolved_by', pid); + this.set('resolved_at', new Date()); + await this.save(); }; const REPORT = model('REPORT', ReportSchema); module.exports = { - ReportSchema, - REPORT + ReportSchema, + REPORT }; diff --git a/src/models/settings.js b/src/models/settings.js index f23d101..1deea5d 100644 --- a/src/models/settings.js +++ b/src/models/settings.js @@ -1,95 +1,95 @@ const { Schema, model } = require('mongoose'); const SettingsSchema = new Schema({ - pid: Number, - screen_name: String, - account_status: { - type: Number, - default: 0 - }, - ban_lift_date: Date, - ban_reason: String, - profile_comment: { - type: String, - default: undefined - }, - profile_comment_visibility: { - type: Boolean, - default: true - }, - game_skill: { - type: Number, - default: 0 - }, - game_skill_visibility: { - type: Boolean, - default: true - }, - birthday_visibility: { - type: Boolean, - default: false - }, - relationship_visibility: { - type: Boolean, - default: false - }, - country_visibility: { - type: Boolean, - default: false - }, - profile_favorite_community_visibility: { - type: Boolean, - default: true - }, - receive_notifications: { - type: Boolean, - default: true - } + pid: Number, + screen_name: String, + account_status: { + type: Number, + default: 0 + }, + ban_lift_date: Date, + ban_reason: String, + profile_comment: { + type: String, + default: undefined + }, + profile_comment_visibility: { + type: Boolean, + default: true + }, + game_skill: { + type: Number, + default: 0 + }, + game_skill_visibility: { + type: Boolean, + default: true + }, + birthday_visibility: { + type: Boolean, + default: false + }, + relationship_visibility: { + type: Boolean, + default: false + }, + country_visibility: { + type: Boolean, + default: false + }, + profile_favorite_community_visibility: { + type: Boolean, + default: true + }, + receive_notifications: { + type: Boolean, + default: true + } }); SettingsSchema.methods.updateComment = async function(comment) { - this.set('profile_comment', comment); - await this.save(); + this.set('profile_comment', comment); + await this.save(); }; SettingsSchema.methods.updateSkill = async function(skill) { - this.set('game_skill', skill); - await this.save(); + this.set('game_skill', skill); + await this.save(); }; SettingsSchema.methods.commentVisible = async function(active) { - this.set('profile_comment_visibility', active); - await this.save(); + this.set('profile_comment_visibility', active); + await this.save(); }; SettingsSchema.methods.skillVisible = async function(active) { - this.set('game_skill_visibility', active); - await this.save(); + this.set('game_skill_visibility', active); + await this.save(); }; SettingsSchema.methods.birthdayVisible = async function(active) { - this.set('birthday_visibility', active); - await this.save(); + this.set('birthday_visibility', active); + await this.save(); }; SettingsSchema.methods.relationshipVisible = async function(active) { - this.set('relationship_visibility', active); - await this.save(); + this.set('relationship_visibility', active); + await this.save(); }; SettingsSchema.methods.countryVisible = async function(active) { - this.set('country_visibility', active); - await this.save(); + this.set('country_visibility', active); + await this.save(); }; SettingsSchema.methods.favCommunityVisible = async function(active) { - this.set('profile_favorite_community_visibility', active); - await this.save(); + this.set('profile_favorite_community_visibility', active); + await this.save(); }; const SETTINGS = model('SETTINGS', SettingsSchema); module.exports = { - SettingsSchema, - SETTINGS + SettingsSchema, + SETTINGS }; diff --git a/src/server.js b/src/server.js index c9eb41f..606da01 100644 --- a/src/server.js +++ b/src/server.js @@ -17,18 +17,18 @@ app.set('etag', false); app.disable('x-powered-by'); app.set('view engine', 'ejs'); app.set('views', __dirname + '/webfiles'); -app.set('trust proxy', 2) -app.get('/ip', (request, response) => response.send(request.ip)) +app.set('trust proxy', 2); +app.get('/ip', (request, response) => response.send(request.ip)); // Create router logger.info('Setting up Middleware'); app.use(morgan('dev')); -app.enable("trust proxy"); +app.enable('trust proxy'); app.use(express.json()); app.use(express.urlencoded({ - extended: true, - limit: '1mb', + extended: true, + limit: '1mb', })); app.use(cookieParser()); @@ -41,35 +41,35 @@ app.use(juxt_web); // 404 handler logger.info('Creating 404 status handler'); app.use((req, res) => { - logger.warn(req.protocol + '://' + req.get('host') + req.originalUrl); - res.render(req.directory + '/error.ejs', { - code: 404, - message: "Page not found", - cdnURL: config.CDN_domain, - lang: req.lang, - pid: req.pid - }); + logger.warn(req.protocol + '://' + req.get('host') + req.originalUrl); + res.render(req.directory + '/error.ejs', { + code: 404, + message: 'Page not found', + cdnURL: config.CDN_domain, + lang: req.lang, + pid: req.pid + }); }); // non-404 error handler logger.info('Creating non-404 status handler'); app.use((error, request, response) => { - const status = error.status || 500; + const status = error.status || 500; - response.status(status); + response.status(status); - response.json({ - app: 'api', - status, - error: error.message - }); + response.json({ + app: 'api', + status, + error: error.message + }); }); // Starts the server logger.info('Starting server'); database.connect().then(() => { - app.listen(port, () => { - logger.success(`Server started on port ${port}`); - }); + app.listen(port, () => { + logger.success(`Server started on port ${port}`); + }); }); diff --git a/src/services/juxt-web/routes/admin/admin.js b/src/services/juxt-web/routes/admin/admin.js index ef3b7f0..a1ea5c3 100644 --- a/src/services/juxt-web/routes/admin/admin.js +++ b/src/services/juxt-web/routes/admin/admin.js @@ -3,55 +3,62 @@ const database = require('../../../../database'); const { POST } = require('../../../../models/post'); const util = require('../../../../util'); const moment = require('moment'); -const config = require("../../../../../config.json"); +const config = require('../../../../../config.json'); const router = express.Router(); router.get('/', async function (req, res) { - if(!req.moderator) - return res.redirect('/login'); + if (!req.moderator) { + return res.redirect('/login'); + } - const reports = await database.getAllOpenReports(); - const communityMap = await util.data.getCommunityHash(); - const userContent = await database.getUserContent(req.pid); - const userMap = util.data.getUserHash(); - const postIDs = reports.map(obj => obj.post_id); + const reports = await database.getAllOpenReports(); + const communityMap = await util.data.getCommunityHash(); + const userContent = await database.getUserContent(req.pid); + const userMap = util.data.getUserHash(); + const postIDs = reports.map(obj => obj.post_id); - const posts = await POST.aggregate([ - { $match: { id: { $in: postIDs } } }, - {$addFields: { - "__order": { $indexOfArray: [ postIDs, "$id" ] } - }}, - { $sort: { "__order": 1 } }, - { $project: { index: 0, _id: 0 } } - ]); + const posts = await POST.aggregate([ + { $match: { id: { $in: postIDs } } }, + {$addFields: { + '__order': { $indexOfArray: [ postIDs, '$id' ] } + }}, + { $sort: { '__order': 1 } }, + { $project: { index: 0, _id: 0 } } + ]); - res.render(req.directory + '/reports.ejs', { - lang: req.lang, - moment: moment, - cdnURL: config.CDN_domain, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator, - userMap, - communityMap, - userContent, - reports, - posts - }); + res.render(req.directory + '/reports.ejs', { + lang: req.lang, + moment: moment, + cdnURL: config.CDN_domain, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator, + userMap, + communityMap, + userContent, + reports, + posts + }); }); router.delete('/:reportID', async function (req, res) { - let report = await database.getReportById(req.params.reportID); - if(!report) return res.sendStatus(402); - let post = await database.getPostByID(report.post_id); - if(!post) return res.sendStatus(404); + const report = await database.getReportById(req.params.reportID); + if (!report) { + return res.sendStatus(402); + } + const post = await database.getPostByID(report.post_id); + if (!post) { + return res.sendStatus(404); + } - if(!req.moderator) return res.sendStatus(401); + if (!req.moderator) { + return res.sendStatus(401); + } - await post.removePost(req.query.reason ? req.query.reason : 'Removed by moderator', req.pid); - await report.resolve(req.pid); + await post.removePost(req.query.reason ? req.query.reason : 'Removed by moderator', req.pid); + await report.resolve(req.pid); - return res.sendStatus(200); + return res.sendStatus(200); }); module.exports = router; diff --git a/src/services/juxt-web/routes/console/communities.js b/src/services/juxt-web/routes/console/communities.js index 268822a..a6dd75b 100644 --- a/src/services/juxt-web/routes/console/communities.js +++ b/src/services/juxt-web/routes/console/communities.js @@ -10,225 +10,235 @@ const { POST } = require('../../../../models/post'); const { COMMUNITY } = require('../../../../models/communities'); router.get('/', async function (req, res) { - let newCommunities = await database.getNewCommunities(6); - let last24Hours = await calculateMostPopularCommunities(); - let popularCommunities = await COMMUNITY.aggregate([ - { $match: { olive_community_id: { $in: last24Hours }, parent: null } }, - {$addFields: { - index: { $indexOfArray: [ last24Hours, "$olive_community_id" ] } - }}, - { $sort: { index: 1 } }, - { $limit : 9 }, - { $project: { index: 0, _id: 0 } } - ]); - res.render(req.directory + '/communities.ejs', { - cache: true, - popularCommunities: popularCommunities, - newCommunities: newCommunities, - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); + const newCommunities = await database.getNewCommunities(6); + const last24Hours = await calculateMostPopularCommunities(); + const popularCommunities = await COMMUNITY.aggregate([ + { $match: { olive_community_id: { $in: last24Hours }, parent: null } }, + {$addFields: { + index: { $indexOfArray: [ last24Hours, '$olive_community_id' ] } + }}, + { $sort: { index: 1 } }, + { $limit : 9 }, + { $project: { index: 0, _id: 0 } } + ]); + res.render(req.directory + '/communities.ejs', { + cache: true, + popularCommunities: popularCommunities, + newCommunities: newCommunities, + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); }); router.get('/all', async function (req, res) { - let communities = await database.getCommunities(90); - res.render(req.directory + '/all_communities.ejs', { - communities: communities, - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); + const communities = await database.getCommunities(90); + res.render(req.directory + '/all_communities.ejs', { + communities: communities, + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); }); router.get('/:communityID', async function (req, res) { - if(req.query.title_id) { - let community = await database.getCommunityByTitleID(req.query.title_id); - if(!community) return res.redirect('/404'); - return res.redirect(`/titles/${community.olive_community_id}/new`); - } - res.redirect(`/titles/${req.params.communityID}/new`); + if (req.query.title_id) { + const community = await database.getCommunityByTitleID(req.query.title_id); + if (!community) { + return res.redirect('/404'); + } + return res.redirect(`/titles/${community.olive_community_id}/new`); + } + res.redirect(`/titles/${req.params.communityID}/new`); }); router.get('/:communityID/related', async function (req, res) { - let userSettings = await database.getUserSettings(req.pid); - let userContent = await database.getUserContent(req.pid); - if(!userContent || !userSettings) - return res.redirect('/404'); - let community = await database.getCommunityByID(req.params.communityID.toString()); - 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 }); - let communityMap = await util.data.getCommunityHash(); - let children = await database.getSubCommunities(community.olive_community_id); - if(!children) - return res.redirect(`/titles/${community.olive_community_id}/new`); + const userSettings = await database.getUserSettings(req.pid); + const userContent = await database.getUserContent(req.pid); + if (!userContent || !userSettings) { + return res.redirect('/404'); + } + const community = await database.getCommunityByID(req.params.communityID.toString()); + 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 children = await database.getSubCommunities(community.olive_community_id); + if (!children) { + return res.redirect(`/titles/${community.olive_community_id}/new`); + } - res.render(req.directory + '/sub_communities.ejs', { - selection: 2, - communityMap, - community, - children, - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); + res.render(req.directory + '/sub_communities.ejs', { + selection: 2, + communityMap, + community, + children, + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); }); router.get('/:communityID/:type', async function (req, res) { - let userSettings = await database.getUserSettings(req.pid); - let userContent = await database.getUserContent(req.pid); - if(!userContent || !userSettings) - return res.redirect('/404'); - let community = await database.getCommunityByID(req.params.communityID.toString()); - 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 }); - let communityMap = await util.data.getCommunityHash(); - let children = await database.getSubCommunities(community.olive_community_id); - if(children.length === 0) - children = null; - let posts, type; + const userSettings = await database.getUserSettings(req.pid); + const userContent = await database.getUserContent(req.pid); + if (!userContent || !userSettings) { + return res.redirect('/404'); + } + const community = await database.getCommunityByID(req.params.communityID.toString()); + 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(); + let children = await database.getSubCommunities(community.olive_community_id); + if (children.length === 0) { + children = null; + } + let posts; let type; - if(req.params.type === 'hot') { - posts = await database.getNumberPopularCommunityPostsByID(community, config.post_limit); - type = 1; - } else if(req.params.type === 'verified') { - posts = await database.getNumberVerifiedCommunityPostsByID(community, config.post_limit); - type = 2; - } else { - posts = await database.getNewPostsByCommunity(community, config.post_limit); - type = 0; - } - let numPosts = await database.getTotalPostsByCommunity(community) + if (req.params.type === 'hot') { + posts = await database.getNumberPopularCommunityPostsByID(community, config.post_limit); + type = 1; + } else if (req.params.type === 'verified') { + posts = await database.getNumberVerifiedCommunityPostsByID(community, config.post_limit); + type = 2; + } else { + posts = await database.getNewPostsByCommunity(community, config.post_limit); + type = 0; + } + const numPosts = await database.getTotalPostsByCommunity(community); - let bundle = { - posts, - open: community.open, - numPosts, - communityMap, - userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/titles/${req.params.communityID}/${req.params.type}/more?offset=${posts.length}&pjax=true` - } + const bundle = { + posts, + open: community.open, + numPosts, + communityMap, + userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/titles/${req.params.communityID}/${req.params.type}/more?offset=${posts.length}&pjax=true` + }; - if(req.query.pjax) - return res.render(req.directory + '/partials/posts_list.ejs', { - bundle, - moment, - lang: req.lang - }); + if (req.query.pjax) { + return res.render(req.directory + '/partials/posts_list.ejs', { + bundle, + moment, + lang: req.lang + }); + } - res.render(req.directory + '/community.ejs', { - // EJS variable and server-side variable - moment: moment, - community: community, - communityMap: communityMap, - posts: posts, - totalNumPosts: numPosts, - userSettings: userSettings, - userContent: userContent, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - children, - type, - bundle, - template: 'posts_list', - moderator: req.moderator - }); + res.render(req.directory + '/community.ejs', { + // EJS variable and server-side variable + moment: moment, + community: community, + communityMap: communityMap, + posts: posts, + totalNumPosts: numPosts, + userSettings: userSettings, + userContent: userContent, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + children, + type, + bundle, + template: 'posts_list', + moderator: req.moderator + }); }); router.get('/:communityID/:type/more', async function (req, res) { - let offset = parseInt(req.query.offset); - let userContent = await database.getUserContent(req.pid); - let communityMap = await util.data.getCommunityHash(); - let posts; - let community = await database.getCommunityByID(req.params.communityID) - if(!community) return res.redirect('/404'); - if(!offset) - offset = 0; - switch (req.params.type) { - case 'popular': - posts = await database.getNumberPopularCommunityPostsByID(community, config.post_limit, offset); - break; - case 'verified': - posts = await database.getNumberVerifiedCommunityPostsByID(community, config.post_limit, offset); - break; - default: - posts = await database.getNewPostsByCommunity(community, config.post_limit, offset); - break; - } + let offset = parseInt(req.query.offset); + const userContent = await database.getUserContent(req.pid); + const communityMap = await util.data.getCommunityHash(); + let posts; + const community = await database.getCommunityByID(req.params.communityID); + if (!community) { + return res.redirect('/404'); + } + if (!offset) { + offset = 0; + } + switch (req.params.type) { + case 'popular': + posts = await database.getNumberPopularCommunityPostsByID(community, config.post_limit, offset); + break; + case 'verified': + posts = await database.getNumberVerifiedCommunityPostsByID(community, config.post_limit, offset); + break; + default: + posts = await database.getNewPostsByCommunity(community, config.post_limit, offset); + break; + } - let bundle = { - posts, - open: true, - numPosts: posts.length, - communityMap, - userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/titles/${req.params.communityID}/${req.params.type}/more?offset=${offset + posts.length}&pjax=true` - } + const bundle = { + posts, + open: true, + numPosts: posts.length, + communityMap, + userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/titles/${req.params.communityID}/${req.params.type}/more?offset=${offset + posts.length}&pjax=true` + }; - if(posts.length > 0) - { - res.render(req.directory + '/partials/posts_list.ejs', { - communityMap: communityMap, - moment: moment, - database: database, - bundle, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); - } - else - res.sendStatus(204); + if (posts.length > 0) { + res.render(req.directory + '/partials/posts_list.ejs', { + communityMap: communityMap, + moment: moment, + database: database, + bundle, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); + } else { + res.sendStatus(204); + } }); router.post('/follow', upload.none(), async function (req, res) { - let community = await database.getCommunityByID(req.body.id); - let userContent = await database.getUserContent(req.pid); - if(userContent !== null && userContent.followed_communities.indexOf(community.olive_community_id) === -1) - { - community.upFollower(); - userContent.addToCommunities(community.olive_community_id); - res.send({ status: 200, id: community.olive_community_id, count: community.followers }); - } - else if(userContent !== null && userContent.followed_communities.indexOf(community.olive_community_id) !== -1) - { - community.downFollower(); - userContent.removeFromCommunities(community.olive_community_id); - res.send({ status: 200, id: community.olive_community_id, count: community.followers }); - } - else - res.send({ status: 423, id: community.olive_community_id, count: community.followers }); + const community = await database.getCommunityByID(req.body.id); + const userContent = await database.getUserContent(req.pid); + if (userContent !== null && userContent.followed_communities.indexOf(community.olive_community_id) === -1) { + community.upFollower(); + userContent.addToCommunities(community.olive_community_id); + res.send({ status: 200, id: community.olive_community_id, count: community.followers }); + } else if (userContent !== null && userContent.followed_communities.indexOf(community.olive_community_id) !== -1) { + community.downFollower(); + userContent.removeFromCommunities(community.olive_community_id); + res.send({ status: 200, id: community.olive_community_id, count: community.followers }); + } else { + res.send({ status: 423, id: community.olive_community_id, count: community.followers }); + } }); async function calculateMostPopularCommunities() { - const now = new Date(); - const last24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000); + const now = new Date(); + const last24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000); - const posts = await POST.find({ created_at: { $gte: last24Hours }, message_to_pid: null }).lean(); + const posts = await POST.find({ created_at: { $gte: last24Hours }, message_to_pid: null }).lean(); - const communityIds = {}; - for (const post of posts) { - const communityId = post.community_id; - communityIds[communityId] = (communityIds[communityId] || 0) + 1; - } - return Object.entries(communityIds) - .sort((a, b) => b[1] - a[1]) - .map((entry) => entry[0]); + const communityIds = {}; + for (const post of posts) { + const communityId = post.community_id; + communityIds[communityId] = (communityIds[communityId] || 0) + 1; + } + return Object.entries(communityIds) + .sort((a, b) => b[1] - a[1]) + .map((entry) => entry[0]); } module.exports = router; diff --git a/src/services/juxt-web/routes/console/feed.js b/src/services/juxt-web/routes/console/feed.js index 0d7af69..0b4ad85 100644 --- a/src/services/juxt-web/routes/console/feed.js +++ b/src/services/juxt-web/routes/console/feed.js @@ -6,83 +6,85 @@ const moment = require('moment'); const router = express.Router(); router.get('/', async function (req, res) { - let userContent = await database.getUserContent(req.pid); - let communityMap = await util.data.getCommunityHash(); - if(!userContent) - return res.redirect('/404'); - let posts = await database.getNewsFeed(userContent, config.post_limit); + const userContent = await database.getUserContent(req.pid); + const communityMap = await util.data.getCommunityHash(); + if (!userContent) { + return res.redirect('/404'); + } + const posts = await database.getNewsFeed(userContent, config.post_limit); - let bundle = { - posts, - open: true, - communityMap, - userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/feed/more?offset=${posts.length}&pjax=true` - } + const bundle = { + posts, + open: true, + communityMap, + userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/feed/more?offset=${posts.length}&pjax=true` + }; - if(req.query.pjax) - return res.render(req.directory + '/partials/posts_list.ejs', { - bundle, - moment, - lang: req.lang - }); + if (req.query.pjax) { + return res.render(req.directory + '/partials/posts_list.ejs', { + bundle, + moment, + lang: req.lang + }); + } - res.render(req.directory + '/feed.ejs', { - moment: moment, - title: req.lang.global.activity_feed, - userContent: userContent, - posts: posts, - communityMap: communityMap, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - bundle, - template: 'posts_list', - moderator: req.moderator - }); + res.render(req.directory + '/feed.ejs', { + moment: moment, + title: req.lang.global.activity_feed, + userContent: userContent, + posts: posts, + communityMap: communityMap, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + bundle, + template: 'posts_list', + moderator: req.moderator + }); }); router.get('/more', async function (req, res) { - let offset = parseInt(req.query.offset); - let userContent = await database.getUserContent(req.pid); - let communityMap = await util.data.getCommunityHash(); - let posts; - if(!offset) offset = 0; - posts = await database.getNewsFeedOffset(userContent, config.post_limit, offset); + let offset = parseInt(req.query.offset); + const userContent = await database.getUserContent(req.pid); + const communityMap = await util.data.getCommunityHash(); + if (!offset) { + offset = 0; + } + const posts = await database.getNewsFeedOffset(userContent, config.post_limit, offset); - let bundle = { - posts, - numPosts: posts.length, - open: true, - communityMap, - userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/feed/more?offset=${offset + posts.length}&pjax=true`, - moderator: req.moderator - } + const bundle = { + posts, + numPosts: posts.length, + open: true, + communityMap, + userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/feed/more?offset=${offset + posts.length}&pjax=true`, + moderator: req.moderator + }; - if(posts.length > 0) - { - res.render(req.directory + '/partials/posts_list.ejs', { - communityMap: communityMap, - moment: moment, - database: database, - bundle, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); - } - else - res.sendStatus(204); + if (posts.length > 0) { + res.render(req.directory + '/partials/posts_list.ejs', { + communityMap: communityMap, + moment: moment, + database: database, + bundle, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); + } else { + res.sendStatus(204); + } }); module.exports = router; diff --git a/src/services/juxt-web/routes/console/messages.js b/src/services/juxt-web/routes/console/messages.js index 2b9467f..b2f5637 100644 --- a/src/services/juxt-web/routes/console/messages.js +++ b/src/services/juxt-web/routes/console/messages.js @@ -4,220 +4,233 @@ const util = require('../../../../util'); const config = require('../../../../../config.json'); const { POST } = require('../../../../models/post'); const moment = require('moment'); -const {CONVERSATION} = require("../../../../models/conversation"); -const crypto = require("crypto"); +const {CONVERSATION} = require('../../../../models/conversation'); +const crypto = require('crypto'); const snowflake = require('node-snowflake').Snowflake; const router = express.Router(); router.get('/', async function (req, res) { - let conversations = await database.getConversations(req.pid); - let usersMap = await util.data.getUserHash(); - res.render(req.directory + '/messages.ejs', { - moment: moment, - pid: req.pid, - conversations: conversations, - cdnURL: config.CDN_domain, - usersMap: usersMap, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - moderator: req.moderator - }); + const conversations = await database.getConversations(req.pid); + const usersMap = await util.data.getUserHash(); + res.render(req.directory + '/messages.ejs', { + moment: moment, + pid: req.pid, + conversations: conversations, + cdnURL: config.CDN_domain, + usersMap: usersMap, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + moderator: req.moderator + }); }); router.post('/new', async function (req, res, next) { - let conversation = await database.getConversationByID(req.body.community_id); - let user2 = await util.data.getUserDataFromPid(req.body.message_to_pid); - let postID = await generatePostUID(21); - let friends = await util.data.getFriends(user2.pid); - if(req.body.community_id === 0) - return res.sendStatus(404); - if(!conversation) { - if(!user || !user2) - return res.sendStatus(422) - let document = { - id: snowflake.nextId(), - users: [ - { - pid: req.pid, - official: (req.user.accessLevel >= 2), - read: true - }, - { - pid: user2.pid, - official: (user2.accessLevel >= 2), - read: false - }, - ] - }; - const newConversations = new CONVERSATION(document); - await newConversations.save(); - conversation = await database.getConversationByID(document.id); - } - if(!conversation) - return res.sendStatus(404); - if(!friends || friends.indexOf(req.pid) === -1) - return res.sendStatus(422); - if(req.body.body === '' && req.body.painting === '' && req.body.screenshot === '') { - res.status(422); - return res.redirect(`/friend_messages/${conversation.id}`); - } - let painting = "", paintingURI = "", 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'); - } - 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'); - } + let conversation = await database.getConversationByID(req.body.community_id); + const user2 = await util.data.getUserDataFromPid(req.body.message_to_pid); + const postID = await generatePostUID(21); + const friends = await util.data.getFriends(user2.pid); + if (req.body.community_id === 0) { + return res.sendStatus(404); + } + if (!conversation) { + if (!user || !user2) { + return res.sendStatus(422); + } + const document = { + id: snowflake.nextId(), + users: [ + { + pid: req.pid, + official: (req.user.accessLevel >= 2), + read: true + }, + { + pid: user2.pid, + official: (user2.accessLevel >= 2), + read: false + }, + ] + }; + const newConversations = new CONVERSATION(document); + await newConversations.save(); + conversation = await database.getConversationByID(document.id); + } + if (!conversation) { + return res.sendStatus(404); + } + if (!friends || friends.indexOf(req.pid) === -1) { + return res.sendStatus(422); + } + if (req.body.body === '' && req.body.painting === '' && req.body.screenshot === '') { + res.status(422); + return res.redirect(`/friend_messages/${conversation.id}`); + } + 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'); + } + 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'); + } - let miiFace; - switch (parseInt(req.body.feeling_id)) { - case 1: - miiFace = 'smile_open_mouth.png'; - break; - case 2: - miiFace = 'wink_left.png'; - break; - case 3: - miiFace = 'surprise_open_mouth.png'; - break; - case 4: - miiFace = 'frustrated.png'; - break; - case 5: - miiFace = 'sorrow.png'; - break; - default: - miiFace = 'normal_face.png'; - break; - } - let body = req.body.body; - if(body) - body = req.body.body.replace(/[^A-Za-z\d\s-_!@#$%^&*(){}‛¨ƒºª«»“”„¿¡←→↑↓√§¶†‡¦–—⇒⇔¤¢€£¥™©®+×÷=±∞ˇ˘˙¸˛˜′″µ°¹²³♭♪•…¬¯‰¼½¾♡♥●◆■▲▼☆★♀♂,./?;:'"\\<>]/g, ""); - if(body.length > 280) - body = body.substring(0,280); - const document = { - community_id: conversation.id, - screen_name: req.user.mii.name, - body: body, - painting: painting, - screenshot: screenshot ? `/screenshots/${req.pid}/${postID}.jpg`: "", - country_id: req.paramPackData ? req.paramPackData.country_id : 49, - created_at: new Date(), - feeling_id: req.body.feeling_id, - id: postID, - is_autopost: 0, - is_spoiler: (req.body.spoiler) ? 1 : 0, - is_app_jumpable: req.body.is_app_jumpable, - language_id: req.body.language_id, - mii: req.user.mii.data, - mii_face_url: `https://mii.olv.pretendo.cc/mii/${req.pid}/${miiFace}`, - pid: req.pid, - platform_id: req.paramPackData ? req.paramPackData.platform_id : 0, - region_id: req.paramPackData ? req.paramPackData.region_id : 2, - verified: (req.user.accessLevel >= 2), - message_to_pid: req.body.message_to_pid, - moderator: req.moderator - }; - let duplicatePost = await database.getDuplicatePosts(req.pid, document); - if(duplicatePost && req.params.post_id) - return res.redirect('/posts/' + req.params.post_id); - const newPost = new POST(document); - newPost.save(); - res.redirect(`/friend_messages/${conversation.id}`); - let postPreviewText; - if(document.painting) - postPreviewText = 'sent a Drawing' - else if(document.body.length > 25) - postPreviewText = document.body.substring(0, 25) + '...'; - else - postPreviewText = document.body; - await conversation.newMessage(postPreviewText, user2.pid); + let miiFace; + switch (parseInt(req.body.feeling_id)) { + case 1: + miiFace = 'smile_open_mouth.png'; + break; + case 2: + miiFace = 'wink_left.png'; + break; + case 3: + miiFace = 'surprise_open_mouth.png'; + break; + case 4: + miiFace = 'frustrated.png'; + break; + case 5: + miiFace = 'sorrow.png'; + break; + default: + miiFace = 'normal_face.png'; + break; + } + let body = req.body.body; + if (body) { + body = req.body.body.replace(/[^A-Za-z\d\s-_!@#$%^&*(){}‛¨ƒºª«»“”„¿¡←→↑↓√§¶†‡¦–—⇒⇔¤¢€£¥™©®+×÷=±∞ˇ˘˙¸˛˜′″µ°¹²³♭♪•…¬¯‰¼½¾♡♥●◆■▲▼☆★♀♂,./?;:'"\\<>]/g, ''); + } + if (body.length > 280) { + body = body.substring(0,280); + } + const document = { + community_id: conversation.id, + screen_name: req.user.mii.name, + body: body, + painting: painting, + screenshot: screenshot ? `/screenshots/${req.pid}/${postID}.jpg`: '', + country_id: req.paramPackData ? req.paramPackData.country_id : 49, + created_at: new Date(), + feeling_id: req.body.feeling_id, + id: postID, + is_autopost: 0, + is_spoiler: (req.body.spoiler) ? 1 : 0, + is_app_jumpable: req.body.is_app_jumpable, + language_id: req.body.language_id, + mii: req.user.mii.data, + mii_face_url: `https://mii.olv.pretendo.cc/mii/${req.pid}/${miiFace}`, + pid: req.pid, + platform_id: req.paramPackData ? req.paramPackData.platform_id : 0, + region_id: req.paramPackData ? req.paramPackData.region_id : 2, + verified: (req.user.accessLevel >= 2), + message_to_pid: req.body.message_to_pid, + moderator: req.moderator + }; + const duplicatePost = await database.getDuplicatePosts(req.pid, document); + if (duplicatePost && req.params.post_id) { + return res.redirect('/posts/' + req.params.post_id); + } + const newPost = new POST(document); + newPost.save(); + res.redirect(`/friend_messages/${conversation.id}`); + let postPreviewText; + if (document.painting) { + postPreviewText = 'sent a Drawing'; + } else if (document.body.length > 25) { + postPreviewText = document.body.substring(0, 25) + '...'; + } else { + postPreviewText = document.body; + } + await conversation.newMessage(postPreviewText, user2.pid); }); router.get('/new/:pid', async function (req, res, next) { - let user = await util.data.getUserDataFromPid(req.pid); - let user2 = await util.data.getUserDataFromPid(req.params.pid); - let friends = await util.data.getFriends(user2.pid); - if(!req.user || !user2) - return res.sendStatus(422) - let conversation = await database.getConversationByUsers([req.pid, user2.pid]); - if(conversation) - return res.redirect(`/friend_messages/${conversation.id}`); - if(!friends || friends.indexOf(req.pid) === -1) - return res.sendStatus(422); - let document = { - id: snowflake.nextId(), - users: [ - { - pid: req.user.pid, - official: (req.user.accessLevel >= 2), - read: true - }, - { - pid: user2.pid, - official: (user2.accessLevel >= 2), - read: false - }, - ] - }; - const newConversations = new CONVERSATION(document); - await newConversations.save(); - conversation = await database.getConversationByID(document.id); - if(!conversation) - return res.sendStatus(404); - let body = `${req.user.mii.name} started a new chat!`; - let newMessage = { - screen_name: req.user.mii.name, - body: body, - created_at: new Date(), - id: await generatePostUID(21), - mii: req.user.mii.data, - mii_face_url: `https://mii.olv.pretendo.cc/mii/${req.pid}/normal_face.png`, - pid: req.pid, - verified: (req.user.accessLevel >= 2), - parent: null, - community_id: conversation.id, - message_to_pid: user2.pid - }; - const newPost = new POST(newMessage); - newPost.save(); - await conversation.newMessage(`${req.user.mii.name} started a new chat!`, user2.pid); - res.redirect(`/friend_messages/${conversation.id}`); + 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); + if (!req.user || !user2) { + return res.sendStatus(422); + } + let conversation = await database.getConversationByUsers([req.pid, user2.pid]); + if (conversation) { + return res.redirect(`/friend_messages/${conversation.id}`); + } + if (!friends || friends.indexOf(req.pid) === -1) { + return res.sendStatus(422); + } + const document = { + id: snowflake.nextId(), + users: [ + { + pid: req.user.pid, + official: (req.user.accessLevel >= 2), + read: true + }, + { + pid: user2.pid, + official: (user2.accessLevel >= 2), + read: false + }, + ] + }; + const newConversations = new CONVERSATION(document); + await newConversations.save(); + conversation = await database.getConversationByID(document.id); + if (!conversation) { + return res.sendStatus(404); + } + const body = `${req.user.mii.name} started a new chat!`; + const newMessage = { + screen_name: req.user.mii.name, + body: body, + created_at: new Date(), + id: await generatePostUID(21), + mii: req.user.mii.data, + mii_face_url: `https://mii.olv.pretendo.cc/mii/${req.pid}/normal_face.png`, + pid: req.pid, + verified: (req.user.accessLevel >= 2), + parent: null, + community_id: conversation.id, + message_to_pid: user2.pid + }; + const newPost = new POST(newMessage); + newPost.save(); + await conversation.newMessage(`${req.user.mii.name} started a new chat!`, user2.pid); + res.redirect(`/friend_messages/${conversation.id}`); }); router.get('/:message_id', async function (req, res) { - let conversation = await database.getConversationByID(req.params.message_id.toString()); - if(!conversation) { - return res.sendStatus(404); - } - let user2 = conversation.users[0].pid === req.pid ? conversation.users[1] : conversation.users[0]; - if(req.pid !== conversation.users[0].pid && req.pid !== conversation.users[1].pid) - res.redirect('/') - let messages = await database.getConversationMessages(conversation.id, 200, 0); - let userMap = await util.data.getUserHash(); - res.render(req.directory + '/message_thread.ejs', { - moment: moment, - user2: user2, - conversation: conversation, - messages: messages, - userMap: userMap, - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); - await conversation.markAsRead(req.pid); + const conversation = await database.getConversationByID(req.params.message_id.toString()); + if (!conversation) { + return res.sendStatus(404); + } + const user2 = conversation.users[0].pid === req.pid ? conversation.users[1] : conversation.users[0]; + if (req.pid !== conversation.users[0].pid && req.pid !== conversation.users[1].pid) { + res.redirect('/'); + } + const messages = await database.getConversationMessages(conversation.id, 200, 0); + const userMap = await util.data.getUserHash(); + res.render(req.directory + '/message_thread.ejs', { + moment: moment, + user2: user2, + conversation: conversation, + messages: messages, + userMap: userMap, + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); + await conversation.markAsRead(req.pid); }); async function generatePostUID(length) { - let id = Buffer.from(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(length * 2))), 'binary').toString('base64').replace(/[+/]/g, "").substring(0, length); - const inuse = await POST.findOne({ id }); - id = (inuse ? await generatePostUID() : id); - return id; + let id = Buffer.from(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(length * 2))), 'binary').toString('base64').replace(/[+/]/g, '').substring(0, length); + const inuse = await POST.findOne({ id }); + id = (inuse ? await generatePostUID() : id); + return id; } diff --git a/src/services/juxt-web/routes/console/notifications.js b/src/services/juxt-web/routes/console/notifications.js index 2bd4ead..d7e3eba 100644 --- a/src/services/juxt-web/routes/console/notifications.js +++ b/src/services/juxt-web/routes/console/notifications.js @@ -6,62 +6,64 @@ const moment = require('moment'); const router = express.Router(); router.get('/my_news', async function (req, res) { - let notifications = await database.getNotifications(req.pid, 25, 0); - let userMap = util.data.getUserHash(); - let bundle = { - notifications, - userMap - } + const notifications = await database.getNotifications(req.pid, 25, 0); + const userMap = util.data.getUserHash(); + const bundle = { + notifications, + userMap + }; - if(req.query.pjax) - return res.render(req.directory + '/partials/notifications.ejs', { - bundle, - lang: req.lang, - moment - }); + if (req.query.pjax) { + return res.render(req.directory + '/partials/notifications.ejs', { + bundle, + lang: req.lang, + moment + }); + } - res.render(req.directory + '/notifications.ejs', { - moment, - selection: 0, - bundle, - cdnURL: config.CDN_domain, - lang: req.lang, - pid: req.pid, - template: 'notifications', - moderator: req.moderator - }); - notifications.filter(noti => noti.read === false).forEach(function(notification) { - notification.markRead(); - }); + res.render(req.directory + '/notifications.ejs', { + moment, + selection: 0, + bundle, + cdnURL: config.CDN_domain, + lang: req.lang, + pid: req.pid, + template: 'notifications', + moderator: req.moderator + }); + notifications.filter(noti => noti.read === false).forEach(function(notification) { + notification.markRead(); + }); }); router.get('/friend_requests', async function (req, res) { - let requests = (await util.data.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)) - let userMap = util.data.getUserHash(); - let bundle = { - requests: requests ? requests : [], - userMap - } + let requests = (await util.data.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 bundle = { + requests: requests ? requests : [], + userMap + }; - if(req.query.pjax) - return res.render(req.directory + '/partials/requests.ejs', { - bundle, - lang: req.lang, - moment - }); + if (req.query.pjax) { + return res.render(req.directory + '/partials/requests.ejs', { + bundle, + lang: req.lang, + moment + }); + } - res.render(req.directory + '/notifications.ejs', { - moment, - selection: 1, - bundle, - cdnURL: config.CDN_domain, - lang: req.lang, - pid: req.pid, - template: 'requests', - moderator: req.moderator - }); + res.render(req.directory + '/notifications.ejs', { + moment, + selection: 1, + bundle, + cdnURL: config.CDN_domain, + lang: req.lang, + pid: req.pid, + template: 'requests', + moderator: req.moderator + }); }); module.exports = router; diff --git a/src/services/juxt-web/routes/console/posts.js b/src/services/juxt-web/routes/console/posts.js index f1f644a..09814cd 100644 --- a/src/services/juxt-web/routes/console/posts.js +++ b/src/services/juxt-web/routes/console/posts.js @@ -5,281 +5,302 @@ const config = require('../../../../../config.json'); const {POST} = require('../../../../models/post'); const multer = require('multer'); const moment = require('moment'); -const rateLimit = require('express-rate-limit') -const {REPORT} = require("../../../../models/report"); +const rateLimit = require('express-rate-limit'); +const {REPORT} = require('../../../../models/report'); const upload = multer({dest: 'uploads/'}); -const crypto = require('crypto') +const crypto = require('crypto'); const router = express.Router(); const postLimit = rateLimit({ - windowMs: 15 * 1000, // 30 seconds - max: 10, // Limit each IP to 1 request per `window` - standardHeaders: true, - legacyHeaders: true, - message: "New post limit reached. Try again in a minute", - handler: function (req, res) { - if (req.params.post_id) - res.redirect('/posts/' + req.params.post_id.toString()); - else if (req.body.community_id) - res.redirect('/titles/' + req.body.community_id); - else { - res.render(req.directory + '/error.ejs', { - code: 429, - message: "Too many new posts have been created.", - cdnURL: config.CDN_domain, - lang: req.lang, - pid: req.pid - }); - } - }, -}) + windowMs: 15 * 1000, // 30 seconds + max: 10, // Limit each IP to 1 request per `window` + standardHeaders: true, + legacyHeaders: true, + message: 'New post limit reached. Try again in a minute', + handler: function (req, res) { + if (req.params.post_id) { + res.redirect('/posts/' + req.params.post_id.toString()); + } else if (req.body.community_id) { + res.redirect('/titles/' + req.body.community_id); + } else { + res.render(req.directory + '/error.ejs', { + code: 429, + message: 'Too many new posts have been created.', + cdnURL: config.CDN_domain, + lang: req.lang, + pid: req.pid + }); + } + }, +}); const yeahLimit = rateLimit({ - windowMs: 60 * 1000, // 1 minute - max: 10, // Limit each IP to 60 requests per `window` - standardHeaders: true, - legacyHeaders: true, -}) + windowMs: 60 * 1000, // 1 minute + max: 10, // Limit each IP to 60 requests per `window` + standardHeaders: true, + legacyHeaders: true, +}); router.post('/empathy', yeahLimit, async function (req, res) { - let post = await database.getPostByID(req.body.postID); - if (!post) - return res.sendStatus(404); - if (post.yeahs.indexOf(req.pid) === -1) { - await POST.updateOne({ - id: post.id, - yeahs: { - $ne: req.pid - } - }, - { - $inc: { - empathy_count: 1 - }, - $push: { - yeahs: req.pid - } - }); - res.send({status: 200, id: post.id, count: post.empathy_count + 1}); - if (req.pid !== post.pid) - await util.data.newNotification({ - pid: post.pid, - type: "yeah", - objectID: post.id, - userPID: req.pid, - link: `/posts/${post.id}` - }); - } else if (post.yeahs.indexOf(req.pid) !== -1) { - await POST.updateOne({ - id: post.id, - yeahs: { - $eq: req.pid - } - }, - { - $inc: { - empathy_count: -1 - }, - $pull: { - yeahs: req.pid - } - }); - res.send({status: 200, id: post.id, count: post.empathy_count - 1}); - } else - res.send({status: 423, id: post.id, count: post.empathy_count}); + const post = await database.getPostByID(req.body.postID); + if (!post) { + return res.sendStatus(404); + } + if (post.yeahs.indexOf(req.pid) === -1) { + await POST.updateOne({ + id: post.id, + yeahs: { + $ne: req.pid + } + }, + { + $inc: { + empathy_count: 1 + }, + $push: { + yeahs: req.pid + } + }); + res.send({status: 200, id: post.id, count: post.empathy_count + 1}); + if (req.pid !== post.pid) { + await util.data.newNotification({ + pid: post.pid, + type: 'yeah', + objectID: post.id, + userPID: req.pid, + link: `/posts/${post.id}` + }); + } + } else if (post.yeahs.indexOf(req.pid) !== -1) { + await POST.updateOne({ + id: post.id, + yeahs: { + $eq: req.pid + } + }, + { + $inc: { + empathy_count: -1 + }, + $pull: { + yeahs: req.pid + } + }); + res.send({status: 200, id: post.id, count: post.empathy_count - 1}); + } else { + res.send({status: 423, id: post.id, count: post.empathy_count}); + } }); router.post('/new', postLimit, upload.none(), async function (req, res) { - await newPost(req, res) + await newPost(req, res); }); router.get('/:post_id', async function (req, res) { - let userSettings = await database.getUserSettings(req.pid); - let userContent = await database.getUserContent(req.pid); - let post = await database.getPostByID(req.params.post_id.toString()); - if (post === null) return res.redirect('/404'); - if (post.parent) { - post = await database.getPostByID(post.parent); - if (post === null) - return res.sendStatus(404); - return res.redirect(`/posts/${post.id}`); - } - let community = await database.getCommunityByID(post.community_id); - let communityMap = await util.data.getCommunityHash(); - let replies = await database.getPostReplies(req.params.post_id.toString(), 25) - res.render(req.directory + '/post.ejs', { - moment: moment, - userSettings: userSettings, - userContent: userContent, - post: post, - replies: replies, - community: community, - communityMap: communityMap, - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); + const userSettings = await database.getUserSettings(req.pid); + const userContent = await database.getUserContent(req.pid); + let post = await database.getPostByID(req.params.post_id.toString()); + if (post === null) { + return res.redirect('/404'); + } + if (post.parent) { + post = await database.getPostByID(post.parent); + if (post === null) { + return res.sendStatus(404); + } + return res.redirect(`/posts/${post.id}`); + } + const community = await database.getCommunityByID(post.community_id); + const communityMap = await util.data.getCommunityHash(); + const replies = await database.getPostReplies(req.params.post_id.toString(), 25); + res.render(req.directory + '/post.ejs', { + moment: moment, + userSettings: userSettings, + userContent: userContent, + post: post, + replies: replies, + community: community, + communityMap: communityMap, + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); }); router.delete('/:post_id', async function (req, res) { - let post = await database.getPostByID(req.params.post_id); - if(!post) return res.sendStatus(404); - if(req.pid !== post.pid && !req.moderator) return res.sendStatus(401); - if(req.moderator && req.pid !== post.pid) - await post.removePost(req.query.reason ? req.query.reason : 'Removed by moderator', req.pid); - else - await post.removePost('User requested removal', req.pid); + const post = await database.getPostByID(req.params.post_id); + if (!post) { + return res.sendStatus(404); + } + if (req.pid !== post.pid && !req.moderator) { + return res.sendStatus(401); + } + if (req.moderator && req.pid !== post.pid) { + await post.removePost(req.query.reason ? req.query.reason : 'Removed by moderator', req.pid); + } else { + await post.removePost('User requested removal', req.pid); + } - res.statusCode = 200; - if(post.parent) - res.send(`/posts/${post.parent}`); - else - res.send('/users/me'); + res.statusCode = 200; + if (post.parent) { + res.send(`/posts/${post.parent}`); + } else { + res.send('/users/me'); + } }); router.post('/:post_id/new', postLimit, upload.none(), async function (req, res) { - await newPost(req, res); + await newPost(req, res); }); router.post('/:post_id/report', upload.none(), async function (req, res) { - const { reason, message, post_id } = req.body; - const post = await database.getPostByID(post_id); - if(!reason || !post_id || !post) - return res.redirect('/404'); + const { reason, message, post_id } = req.body; + const post = await database.getPostByID(post_id); + if (!reason || !post_id || !post) { + return res.redirect('/404'); + } - const reportDoc = { - pid: post.pid, - reported_by: req.pid, - post_id, - reason, - message, - created_at: new Date() - } + const reportDoc = { + pid: post.pid, + reported_by: req.pid, + post_id, + reason, + message, + created_at: new Date() + }; - const reportObj = new REPORT(reportDoc); - await reportObj.save(); + const reportObj = new REPORT(reportDoc); + await reportObj.save(); - return res.redirect(`/posts/${post.id}`); + return res.redirect(`/posts/${post.id}`); }); async function newPost(req, res) { - let userSettings = await database.getUserSettings(req.pid), parentPost = null, postID = await generatePostUID(21); - let community = await database.getCommunityByID(req.body.community_id); - if (!community || !userSettings || !req.user) { - res.status(403); - console.log('missing data') - return res.redirect(`/titles/show`); - } - if (req.params.post_id && (req.body.body === '' && req.body.painting === '' && req.body.screenshot === '')) { - res.status(422); - return res.redirect('/posts/' + req.params.post_id.toString()); - } - if (req.params.post_id) { - parentPost = await database.getPostByID(req.params.post_id.toString()); - if (!parentPost) - return res.sendStatus(403); - } - if (!(community.admins && community.admins.indexOf(req.pid) !== -1 && userSettings.account_status === 0) + const userSettings = await database.getUserSettings(req.pid); let parentPost = null; const postID = await generatePostUID(21); + const community = await database.getCommunityByID(req.body.community_id); + if (!community || !userSettings || !req.user) { + res.status(403); + console.log('missing data'); + return res.redirect('/titles/show'); + } + if (req.params.post_id && (req.body.body === '' && req.body.painting === '' && req.body.screenshot === '')) { + res.status(422); + return res.redirect('/posts/' + req.params.post_id.toString()); + } + if (req.params.post_id) { + parentPost = await database.getPostByID(req.params.post_id.toString()); + if (!parentPost) { + return res.sendStatus(403); + } + } + if (!(community.admins && community.admins.indexOf(req.pid) !== -1 && userSettings.account_status === 0) && (community.type >= 2) && !(parentPost && community.allows_comments && community.open)) { - res.status(403); - return res.redirect(`/titles/${community.olive_community_id}/new`); - } + res.status(403); + return res.redirect(`/titles/${community.olive_community_id}/new`); + } - let painting = "", paintingURI = "", 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); - 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'); - } - 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'); - } + 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); + } 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'); + } + 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'); + } - let miiFace; - switch (parseInt(req.body.feeling_id)) { - case 1: - miiFace = 'smile_open_mouth.png'; - break; - case 2: - miiFace = 'wink_left.png'; - break; - case 3: - miiFace = 'surprise_open_mouth.png'; - break; - case 4: - miiFace = 'frustrated.png'; - break; - case 5: - miiFace = 'sorrow.png'; - break; - default: - miiFace = 'normal_face.png'; - break; - } - let body = req.body.body; - if (body) - body = req.body.body.replace(/[^A-Za-z\d\s-_!@#$%^&*(){}‛¨ƒºª«»“”„¿¡←→↑↓√§¶†‡¦–—⇒⇔¤¢€£¥™©®+×÷=±∞ˇ˘˙¸˛˜′″µ°¹²³♭♪•…¬¯‰¼½¾♡♥●◆■▲▼☆★♀♂,./?;:'"\\<>]/g, ""); - if (body.length > 280) - body = body.substring(0, 280); - const document = { - title_id: community.title_id[0], - community_id: community.olive_community_id, - screen_name: userSettings.screen_name, - body: body, - painting: painting, - screenshot: screenshot ? `/screenshots/${req.pid}/${postID}.jpg` : "", - country_id: req.paramPackData ? req.paramPackData.country_id : 49, - created_at: new Date(), - feeling_id: req.body.feeling_id, - id: postID, - is_autopost: 0, - is_spoiler: (req.body.spoiler) ? 1 : 0, - is_app_jumpable: req.body.is_app_jumpable, - language_id: req.body.language_id, - mii: req.user.mii.data, - mii_face_url: `https://mii.olv.pretendo.cc/mii/${req.user.pid}/${miiFace}`, - pid: req.pid, - platform_id: req.paramPackData ? req.paramPackData.platform_id : 0, - region_id: req.paramPackData ? req.paramPackData.region_id : 2, - verified: req.moderator, - parent: parentPost ? parentPost.id : null, - moderator: req.moderator - }; - let duplicatePost = await database.getDuplicatePosts(req.pid, document); - if (duplicatePost && req.params.post_id) - return res.redirect('/posts/' + req.params.post_id.toString()); - if (document.body === '' && document.painting === '' && document.screenshot === '') - return res.redirect('/titles/' + community.olive_community_id + '/new'); - const newPost = new POST(document); - newPost.save(); - if (parentPost) { - parentPost.reply_count = parentPost.reply_count + 1; - parentPost.save(); - } - if (parentPost && (parentPost.pid !== req.user.pid)) - await util.data.newNotification({ - pid: parentPost.pid, - type: "reply", - user: req.pid, - link: `/posts/${parentPost.id}` - }); - if (parentPost) - res.redirect('/posts/' + req.params.post_id.toString()); - else - res.redirect('/titles/' + community.olive_community_id + '/new'); + let miiFace; + switch (parseInt(req.body.feeling_id)) { + case 1: + miiFace = 'smile_open_mouth.png'; + break; + case 2: + miiFace = 'wink_left.png'; + break; + case 3: + miiFace = 'surprise_open_mouth.png'; + break; + case 4: + miiFace = 'frustrated.png'; + break; + case 5: + miiFace = 'sorrow.png'; + break; + default: + miiFace = 'normal_face.png'; + break; + } + let body = req.body.body; + if (body) { + body = req.body.body.replace(/[^A-Za-z\d\s-_!@#$%^&*(){}‛¨ƒºª«»“”„¿¡←→↑↓√§¶†‡¦–—⇒⇔¤¢€£¥™©®+×÷=±∞ˇ˘˙¸˛˜′″µ°¹²³♭♪•…¬¯‰¼½¾♡♥●◆■▲▼☆★♀♂,./?;:'"\\<>]/g, ''); + } + if (body.length > 280) { + body = body.substring(0, 280); + } + const document = { + title_id: community.title_id[0], + community_id: community.olive_community_id, + screen_name: userSettings.screen_name, + body: body, + painting: painting, + screenshot: screenshot ? `/screenshots/${req.pid}/${postID}.jpg` : '', + country_id: req.paramPackData ? req.paramPackData.country_id : 49, + created_at: new Date(), + feeling_id: req.body.feeling_id, + id: postID, + is_autopost: 0, + is_spoiler: (req.body.spoiler) ? 1 : 0, + is_app_jumpable: req.body.is_app_jumpable, + language_id: req.body.language_id, + mii: req.user.mii.data, + mii_face_url: `https://mii.olv.pretendo.cc/mii/${req.user.pid}/${miiFace}`, + pid: req.pid, + platform_id: req.paramPackData ? req.paramPackData.platform_id : 0, + region_id: req.paramPackData ? req.paramPackData.region_id : 2, + verified: req.moderator, + parent: parentPost ? parentPost.id : null, + moderator: req.moderator + }; + const duplicatePost = await database.getDuplicatePosts(req.pid, document); + if (duplicatePost && req.params.post_id) { + return res.redirect('/posts/' + req.params.post_id.toString()); + } + if (document.body === '' && document.painting === '' && document.screenshot === '') { + return res.redirect('/titles/' + community.olive_community_id + '/new'); + } + const newPost = new POST(document); + newPost.save(); + if (parentPost) { + parentPost.reply_count = parentPost.reply_count + 1; + parentPost.save(); + } + if (parentPost && (parentPost.pid !== req.user.pid)) { + await util.data.newNotification({ + pid: parentPost.pid, + type: 'reply', + user: req.pid, + link: `/posts/${parentPost.id}` + }); + } + if (parentPost) { + res.redirect('/posts/' + req.params.post_id.toString()); + } else { + res.redirect('/titles/' + community.olive_community_id + '/new'); + } } async function generatePostUID(length) { - let id = Buffer.from(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(length * 2))), 'binary').toString('base64').replace(/[+/]/g, "").substring(0, length); - const inuse = await POST.findOne({id}); - id = (inuse ? await generatePostUID() : id); - return id; + let id = Buffer.from(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(length * 2))), 'binary').toString('base64').replace(/[+/]/g, '').substring(0, length); + const inuse = await POST.findOne({id}); + id = (inuse ? await generatePostUID() : id); + return id; } diff --git a/src/services/juxt-web/routes/console/show.js b/src/services/juxt-web/routes/console/show.js index 0e78ab8..0bb076c 100644 --- a/src/services/juxt-web/routes/console/show.js +++ b/src/services/juxt-web/routes/console/show.js @@ -5,63 +5,65 @@ const config = require('../../../../../config.json'); const router = express.Router(); router.get('/', async function (req, res) { - if(req.pid === 1000000000) { - return res.render(req.directory + '/guest_notice.ejs', { - cdnURL: config.CDN_domain, - lang: req.lang, - moderator: req.moderator - }); - } + if (req.pid === 1000000000) { + return res.render(req.directory + '/guest_notice.ejs', { + cdnURL: config.CDN_domain, + lang: req.lang, + moderator: req.moderator + }); + } - let user = await database.getUserSettings(req.pid); - let content = await database.getUserContent(req.pid) - if(!user || !content) { - res.render(req.directory + '/first_run.ejs', { - cdnURL: config.CDN_domain, - lang: req.lang, - pid: req.pid, - moderator: req.moderator - }); - } + const user = await database.getUserSettings(req.pid); + const content = await database.getUserContent(req.pid); + if (!user || !content) { + return res.render(req.directory + '/first_run.ejs', { + cdnURL: config.CDN_domain, + lang: req.lang, + pid: req.pid, + moderator: req.moderator + }); + } - if(req.query.topic_tag) { - res.redirect(`/topics?topic_tag=${req.query.topic_tag}`) - } - else if(req.query.pid) { - res.redirect(`/users/${req.query.pid}`) - } - else - res.redirect('/titles') + if (req.query.topic_tag) { + res.redirect(`/topics?topic_tag=${req.query.topic_tag}`); + } else if (req.query.pid) { + res.redirect(`/users/${req.query.pid}`); + } else { + res.redirect('/titles'); + } - let usrMii = await database.getUserSettings(req.pid); - if(req.user.mii.name !== usrMii.screen_name) { - util.data.setName(req.pid, req.user.mii.name); - usrMii.screen_name = req.user.mii.name; - await usrMii.save(); - } + const usrMii = await database.getUserSettings(req.pid); + if (req.user.mii.name !== usrMii.screen_name) { + util.data.setName(req.pid, req.user.mii.name); + usrMii.screen_name = req.user.mii.name; + await usrMii.save(); + } }); router.get('/first', async function (req, res) { - res.render(req.directory + '/first_run.ejs', { - cdnURL: config.CDN_domain, - lang: req.lang, - moderator: req.moderator - }); + res.render(req.directory + '/first_run.ejs', { + cdnURL: config.CDN_domain, + lang: req.lang, + moderator: req.moderator + }); }); router.post('/newUser', async function (req, res) { - if(req.pid === null) - return res.sendStatus(401); + if (req.pid === null) { + return res.sendStatus(401); + } - let user = await database.getUserSettings(req.pid); - if(user) - return res.sendStatus(504); + const user = await database.getUserSettings(req.pid); + if (user) { + return res.sendStatus(504); + } - await util.data.create_user(req.pid, req.body.experience, req.body.notifications); - if(await database.getUserSettings(req.pid) !== null) - res.sendStatus(200); - else - res.sendStatus(504); + await util.data.create_user(req.pid, req.body.experience, req.body.notifications); + if (await database.getUserSettings(req.pid) !== null) { + res.sendStatus(200); + } else { + res.sendStatus(504); + } }); diff --git a/src/services/juxt-web/routes/console/topics.js b/src/services/juxt-web/routes/console/topics.js index e4f1d48..ed0ee4e 100644 --- a/src/services/juxt-web/routes/console/topics.js +++ b/src/services/juxt-web/routes/console/topics.js @@ -7,83 +7,87 @@ const { POST } = require('../../../../models/post'); const router = express.Router(); router.get('/', async function (req, res) { - let userContent = await database.getUserContent(req.pid); - let communityMap = await util.data.getCommunityHash(); - let tag = req.query.topic_tag; - console.log(tag) - if(!userContent || !tag) - return res.redirect('/404'); - let posts = await POST.find({ topic_tag: req.query.topic_tag }).sort({ created_at: -1}).limit(parseInt(req.query.limit)); + const userContent = await database.getUserContent(req.pid); + const communityMap = await util.data.getCommunityHash(); + const tag = req.query.topic_tag; + console.log(tag); + if (!userContent || !tag) { + return res.redirect('/404'); + } + const posts = await POST.find({ topic_tag: req.query.topic_tag }).sort({ created_at: -1}).limit(parseInt(req.query.limit)); - let bundle = { - posts, - open: true, - communityMap, - userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/topics/more?tag=${tag}&offset=${posts.length}&pjax=true` - } + const bundle = { + posts, + open: true, + communityMap, + userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/topics/more?tag=${tag}&offset=${posts.length}&pjax=true` + }; - if(req.query.pjax) - return res.render(req.directory + '/partials/posts_list.ejs', { - bundle, - moment, - lang: req.lang - }); + if (req.query.pjax) { + return res.render(req.directory + '/partials/posts_list.ejs', { + bundle, + moment, + lang: req.lang + }); + } - res.render(req.directory + '/feed.ejs', { - moment: moment, - title: tag, - userContent: userContent, - posts: posts, - communityMap: communityMap, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - bundle, - template: 'posts_list', - moderator: req.moderator - }); + res.render(req.directory + '/feed.ejs', { + moment: moment, + title: tag, + userContent: userContent, + posts: posts, + communityMap: communityMap, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + bundle, + template: 'posts_list', + moderator: req.moderator + }); }); router.get('/more', async function (req, res) { - let offset = req.query.offset ? parseInt(req.query.offset) : 0; - let userContent = await database.getUserContent(req.pid); - let communityMap = await util.data.getCommunityHash(); - let tag = req.query.topic_tag; - if(!tag) return res.sendStatus(204); - let posts = await POST.find({ topic_tag: req.query.topic_tag }).sort({ created_at: -1}).limit(parseInt(req.query.limit)); + const offset = req.query.offset ? parseInt(req.query.offset) : 0; + const userContent = await database.getUserContent(req.pid); + const communityMap = await util.data.getCommunityHash(); + const tag = req.query.topic_tag; + if (!tag) { + return res.sendStatus(204); + } + const posts = await POST.find({ topic_tag: req.query.topic_tag }).sort({ created_at: -1}).limit(parseInt(req.query.limit)); - let bundle = { - posts, - numPosts: posts.length, - open: true, - communityMap, - userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/topics/more?tag=${tag}&offset=${posts.length}&pjax=true` - } + const bundle = { + posts, + numPosts: posts.length, + open: true, + communityMap, + userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/topics/more?tag=${tag}&offset=${posts.length}&pjax=true` + }; - if(posts.length > 0) { - res.render(req.directory + '/partials/posts_list.ejs', { - communityMap: communityMap, - moment: moment, - database: database, - bundle, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); - } - else - res.sendStatus(204); + if (posts.length > 0) { + res.render(req.directory + '/partials/posts_list.ejs', { + communityMap: communityMap, + moment: moment, + database: database, + bundle, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); + } else { + res.sendStatus(204); + } }); module.exports = router; diff --git a/src/services/juxt-web/routes/console/userpage.js b/src/services/juxt-web/routes/console/userpage.js index d1ea861..e151a08 100644 --- a/src/services/juxt-web/routes/console/userpage.js +++ b/src/services/juxt-web/routes/console/userpage.js @@ -6,173 +6,193 @@ const multer = require('multer'); const moment = require('moment'); const upload = multer({ dest: 'uploads/' }); const { POST } = require('../../../../models/post'); -const {SETTINGS} = require("../../../../models/settings"); +const {SETTINGS} = require('../../../../models/settings'); const router = express.Router(); router.get('/menu', async function (req, res) { - let user = await database.getUserSettings(req.pid); - res.render('ctr/user_menu.ejs', { - user: user, - }); + const user = await database.getUserSettings(req.pid); + res.render('ctr/user_menu.ejs', { + user: user, + }); }); -router.get('/me', async function (req, res) { await userPage(req, res, req.pid) }); +router.get('/me', async function (req, res) { + await userPage(req, res, req.pid); +}); router.get('/me/settings', async function (req, res) { - let userSettings = await database.getUserSettings(req.pid); - let communityMap = await util.data.getCommunityHash(); - res.render(req.directory + '/settings.ejs', { - communityMap: communityMap, - moment: moment, - userSettings: userSettings, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); + const userSettings = await database.getUserSettings(req.pid); + const communityMap = await util.data.getCommunityHash(); + res.render(req.directory + '/settings.ejs', { + communityMap: communityMap, + moment: moment, + userSettings: userSettings, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); }); -router.get('/me/:type', async function (req, res) { await userRelations(req, res, req.pid) }); +router.get('/me/:type', async function (req, res) { + await userRelations(req, res, req.pid); +}); router.post('/me/settings', upload.none(), async function (req, res) { - let userSettings = await database.getUserSettings(req.pid); + const userSettings = await database.getUserSettings(req.pid); - userSettings.country_visibility = !!req.body.country; - userSettings.birthday_visibility = !!req.body.birthday; - userSettings.game_skill_visibility = !!req.body.experience; - userSettings.profile_comment_visibility = !!req.body.comment; + userSettings.country_visibility = !!req.body.country; + userSettings.birthday_visibility = !!req.body.birthday; + userSettings.game_skill_visibility = !!req.body.experience; + userSettings.profile_comment_visibility = !!req.body.comment; - if (req.body.comment) - userSettings.updateComment(req.body.comment); - else - userSettings.updateComment(''); + if (req.body.comment) { + userSettings.updateComment(req.body.comment); + } else { + userSettings.updateComment(''); + } - res.redirect('/users/me'); + res.redirect('/users/me'); }); router.get('/show', async function (req, res) { - res.redirect(`/users/${req.query.pid}`); + res.redirect(`/users/${req.query.pid}`); }); -router.get('/:pid/more', async function (req, res) { await morePosts(req, res, req.params.pid) }); - -router.get('/:pid/yeahs/more', async function (req, res) { await moreYeahPosts(req, res, req.params.pid) }); - -router.get('/:pid/:type', async function (req, res) { await userRelations(req, res, req.params.pid) }); - -// TODO: Remove the need for a parameter to toggle the following state -router.post('/follow', upload.none(), async function (req, res) { - let userToFollowContent = await database.getUserContent(req.body.id); - let userContent = await database.getUserContent(req.pid); - if(userContent !== null && userContent.followed_users.indexOf(userToFollowContent.pid) === -1) - { - userToFollowContent.addToFollowers(userContent.pid); - userContent.addToUsers(userToFollowContent.pid); - res.send({ status: 200, id: userToFollowContent.pid, count: userToFollowContent.following_users.length - 1 }); - let 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}` }); - } - else if(userContent !== null && userContent.followed_users.indexOf(userToFollowContent.pid) !== -1) - { - userToFollowContent.removeFromFollowers(userContent.pid); - userContent.removeFromUsers(userToFollowContent.pid); - res.send({ status: 200, id: userToFollowContent.pid, count: userToFollowContent.following_users.length - 1 }); - } - else - res.send({ status: 423, id: userToFollowContent.pid, count: userToFollowContent.following_users.length - 1 }); +router.get('/:pid/more', async function (req, res) { + await morePosts(req, res, req.params.pid); }); -router.get('/:pid', async function (req, res) { - const userID = req.params.pid; - if(userID === 'me' || Number(userID) === req.pid) - return res.redirect('/users/me'); - await userPage(req, res, userID); +router.get('/:pid/yeahs/more', async function (req, res) { + await moreYeahPosts(req, res, req.params.pid); }); router.get('/:pid/:type', async function (req, res) { - const userID = req.params.pid; - if(userID === 'me' || Number(userID) === req.pid) - return res.redirect('/users/me'); - await userRelations(req, res, userID); + await userRelations(req, res, req.params.pid); +}); + +// TODO: Remove the need for a parameter to toggle the following state +router.post('/follow', upload.none(), async function (req, res) { + const userToFollowContent = await database.getUserContent(req.body.id); + const userContent = await database.getUserContent(req.pid); + if (userContent !== null && userContent.followed_users.indexOf(userToFollowContent.pid) === -1) { + userToFollowContent.addToFollowers(userContent.pid); + userContent.addToUsers(userToFollowContent.pid); + res.send({ status: 200, id: userToFollowContent.pid, count: userToFollowContent.following_users.length - 1 }); + 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}` }); + } + } else if (userContent !== null && userContent.followed_users.indexOf(userToFollowContent.pid) !== -1) { + userToFollowContent.removeFromFollowers(userContent.pid); + userContent.removeFromUsers(userToFollowContent.pid); + res.send({ status: 200, id: userToFollowContent.pid, count: userToFollowContent.following_users.length - 1 }); + } else { + res.send({ status: 423, id: userToFollowContent.pid, count: userToFollowContent.following_users.length - 1 }); + } +}); + +router.get('/:pid', async function (req, res) { + const userID = req.params.pid; + if (userID === 'me' || Number(userID) === req.pid) { + return res.redirect('/users/me'); + } + await userPage(req, res, userID); +}); + +router.get('/:pid/:type', async function (req, res) { + const userID = req.params.pid; + if (userID === 'me' || Number(userID) === req.pid) { + return res.redirect('/users/me'); + } + await userRelations(req, res, userID); }); async function userPage(req, res, userID) { - let pnid = userID === req.pid ? req.user : await util.data.getUserDataFromPid(userID).catch((e) => { - console.log(e.details); - }); - let userContent = await database.getUserContent(userID); - if(isNaN(userID) || !pnid || !userContent) - return res.redirect('/404'); - let userSettings = await database.getUserSettings(userID); - let posts = await database.getNumberUserPostsByID(userID, config.post_limit); - let numPosts = await database.getTotalPostsByUserID(userID); - let communityMap = await util.data.getCommunityHash(); - let friends = await util.data.getFriends(userID); - let parentUserContent; - if(pnid.pid !== req.pid) - parentUserContent = await database.getUserContent(req.pid); + const pnid = userID === req.pid ? req.user : await util.data.getUserDataFromPid(userID).catch((e) => { + console.log(e.details); + }); + const userContent = await database.getUserContent(userID); + if (isNaN(userID) || !pnid || !userContent) { + return res.redirect('/404'); + } + 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(); + let friends = []; + try { + friends = await util.data.getFriends(userID); + } catch (e) {} - let bundle = { - posts, - open: true, - numPosts, - communityMap, - userContent: parentUserContent ? parentUserContent : userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/users/${userID}/more?offset=${posts.length}&pjax=true` - } + let parentUserContent; + if (pnid.pid !== req.pid) { + parentUserContent = await database.getUserContent(req.pid); + } - if(req.query.pjax) - return res.render(req.directory + '/partials/posts_list.ejs', { - bundle, - lang: req.lang, - moment - }); - let link = (pnid.pid === req.pid) ? '/users/me/' : `/users/${userID}/`; - res.render(req.directory + '/user_page.ejs', { - template: 'posts_list', - selection: 0, - moment, - pnid, - numPosts, - userContent, - userSettings, - bundle, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - link, - friends, - parentUserContent, - moderator: req.moderator - }); + const bundle = { + posts, + open: true, + numPosts, + communityMap, + userContent: parentUserContent ? parentUserContent : userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/users/${userID}/more?offset=${posts.length}&pjax=true` + }; + + if (req.query.pjax) { + return res.render(req.directory + '/partials/posts_list.ejs', { + bundle, + lang: req.lang, + moment + }); + } + const link = (pnid.pid === req.pid) ? '/users/me/' : `/users/${userID}/`; + res.render(req.directory + '/user_page.ejs', { + template: 'posts_list', + selection: 0, + moment, + pnid, + numPosts, + userContent, + userSettings, + bundle, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + link, + friends, + parentUserContent, + moderator: req.moderator + }); } async function userRelations(req, res, userID) { - let pnid = userID === req.pid ? req.user : await util.data.getUserDataFromPid(userID); - let userContent = await database.getUserContent(userID); - let link = (pnid.pid === req.pid) ? '/users/me/' : `/users/${userID}/`; - let userSettings = await database.getUserSettings(userID); - let numPosts = await database.getTotalPostsByUserID(userID); - let friends = await util.data.getFriends(userID); - let parentUserContent; - if(pnid.pid !== req.pid) - parentUserContent = await database.getUserContent(req.pid); - if(isNaN(userID) || !pnid) - return res.redirect('/404'); + const pnid = userID === req.pid ? req.user : await util.data.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); + let parentUserContent; + if (pnid.pid !== req.pid) { + parentUserContent = await database.getUserContent(req.pid); + } + if (isNaN(userID) || !pnid) { + return res.redirect('/404'); + } - let followers, communities, communityMap, selection; + let followers; let communities; let communityMap; let selection; - if(req.params.type === 'yeahs') { - let posts = await POST.find({ yeahs: req.pid, removed: false }).sort({created_at: -1}); - /*let posts = await POST.aggregate([ + if (req.params.type === 'yeahs') { + const posts = await POST.find({ yeahs: req.pid, removed: false }).sort({created_at: -1}); + /*let posts = await POST.aggregate([ { $match: { id: { $in: likesArray } } }, {$addFields: { "__order": { $indexOfArray: [ likesArray, "$id" ] } @@ -181,180 +201,184 @@ async function userRelations(req, res, userID) { { $project: { index: 0, _id: 0 } }, { $limit: config.post_limit } ]);*/ - let communityMap = await util.data.getCommunityHash(); - let bundle = { - posts, - open: true, - numPosts: posts.length, - communityMap, - userContent: parentUserContent ? parentUserContent : userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/users/${userID}/yeahs/more?offset=${posts.length}&pjax=true` - } + const communityMap = await util.data.getCommunityHash(); + const bundle = { + posts, + open: true, + numPosts: posts.length, + communityMap, + userContent: parentUserContent ? parentUserContent : userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/users/${userID}/yeahs/more?offset=${posts.length}&pjax=true` + }; - if(req.query.pjax) - return res.render(req.directory + '/partials/posts_list.ejs', { - bundle, - lang: req.lang, - moment - }); - else - return res.render(req.directory + '/user_page.ejs', { - template: 'posts_list', - selection: 4, - moment, - pnid, - numPosts, - userContent, - userSettings, - bundle, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - link, - friends, - parentUserContent, - moderator: req.moderator - }); - } + if (req.query.pjax) { + return res.render(req.directory + '/partials/posts_list.ejs', { + bundle, + lang: req.lang, + moment + }); + } else { + return res.render(req.directory + '/user_page.ejs', { + template: 'posts_list', + selection: 4, + moment, + pnid, + numPosts, + userContent, + userSettings, + bundle, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + link, + friends, + parentUserContent, + moderator: req.moderator + }); + } + } - if(req.params.type === 'friends') { - followers = await SETTINGS.find({ pid: friends }); - communities = []; - selection = 2; - } - else if(req.params.type === 'followers') { - followers = await database.getFollowingUsers(userContent); - communities = []; - selection = 3; - } - else { - followers = await database.getFollowedUsers(userContent); - communities = userContent.followed_communities; - communityMap = await util.data.getCommunityHash(); - selection = 2; - } + if (req.params.type === 'friends') { + followers = await SETTINGS.find({ pid: friends }); + communities = []; + selection = 2; + } else if (req.params.type === 'followers') { + followers = await database.getFollowingUsers(userContent); + communities = []; + selection = 3; + } else { + followers = await database.getFollowedUsers(userContent); + communities = userContent.followed_communities; + communityMap = await util.data.getCommunityHash(); + selection = 2; + } - if(followers[0] === '0') - followers.splice(0, 0); - if(communities[0] === '0') - communities.splice(0, 1); + if (followers[0] === '0') { + followers.splice(0, 0); + } + if (communities[0] === '0') { + communities.splice(0, 1); + } - let bundle = { - followers: followers ? followers : [], - communities: communities, - communityMap: communityMap - } + const bundle = { + followers: followers ? followers : [], + communities: communities, + communityMap: communityMap + }; - if(req.query.pjax) - return res.render(req.directory + '/partials/following_list.ejs', { - bundle, - }); - res.render(req.directory + '/user_page.ejs', { - template: 'following_list', - selection: selection, - moment, - pnid, - numPosts, - userContent, - userSettings, - bundle, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - link, - parentUserContent, - moderator: req.moderator - }); + if (req.query.pjax) { + return res.render(req.directory + '/partials/following_list.ejs', { + bundle, + }); + } + res.render(req.directory + '/user_page.ejs', { + template: 'following_list', + selection: selection, + moment, + pnid, + numPosts, + userContent, + userSettings, + bundle, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + link, + parentUserContent, + moderator: req.moderator + }); } async function morePosts(req, res, userID) { - let offset = parseInt(req.query.offset); - let userContent = await database.getUserContent(req.pid); - let communityMap = await util.data.getCommunityHash(); - if(!offset) offset = 0; - let posts = await database.getUserPostsOffset(userID, config.post_limit, offset); + let offset = parseInt(req.query.offset); + const userContent = await database.getUserContent(req.pid); + const communityMap = await util.data.getCommunityHash(); + if (!offset) { + offset = 0; + } + const posts = await database.getUserPostsOffset(userID, config.post_limit, offset); - let bundle = { - posts, - numPosts: posts.length, - open: true, - communityMap, - userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/users/${userID}/more?offset=${offset + posts.length}&pjax=true` - } + const bundle = { + posts, + numPosts: posts.length, + open: true, + communityMap, + userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/users/${userID}/more?offset=${offset + posts.length}&pjax=true` + }; - if(posts.length > 0) - { - res.render(req.directory + '/partials/posts_list.ejs', { - communityMap: communityMap, - moment: moment, - database: database, - bundle, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); - } - else - res.sendStatus(204); + if (posts.length > 0) { + res.render(req.directory + '/partials/posts_list.ejs', { + communityMap: communityMap, + moment: moment, + database: database, + bundle, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); + } else { + res.sendStatus(204); + } } async function moreYeahPosts(req, res, userID) { - let offset = parseInt(req.query.offset); - let parentUserContent = await database.getUserContent(userID); - let userContent = await database.getUserContent(req.pid); - let communityMap = await util.data.getCommunityHash(); - if(!offset) offset = 0; - let likesArray = await userContent.likes.slice().reverse(); - let posts = await POST.aggregate([ - { $match: { id: { $in: likesArray }, removed: false } }, - {$addFields: { - "__order": { $indexOfArray: [ likesArray, "$id" ] } - }}, - { $sort: { "__order": 1 } }, - { $project: { index: 0, _id: 0 } }, - { $skip : offset }, - { $limit: config.post_limit } - ]); + 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(); + if (!offset) { + offset = 0; + } + const likesArray = await userContent.likes.slice().reverse(); + const posts = await POST.aggregate([ + { $match: { id: { $in: likesArray }, removed: false } }, + {$addFields: { + '__order': { $indexOfArray: [ likesArray, '$id' ] } + }}, + { $sort: { '__order': 1 } }, + { $project: { index: 0, _id: 0 } }, + { $skip : offset }, + { $limit: config.post_limit } + ]); - let bundle = { - posts: posts.reverse(), - numPosts: posts.length, - open: true, - communityMap, - userContent, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - link: `/users/${userID}/yeahs/more?offset=${offset + posts.length}&pjax=true` - } + const bundle = { + posts: posts.reverse(), + numPosts: posts.length, + open: true, + communityMap, + userContent, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + link: `/users/${userID}/yeahs/more?offset=${offset + posts.length}&pjax=true` + }; - if(posts.length > 0) - { - res.render(req.directory + '/partials/posts_list.ejs', { - communityMap: communityMap, - moment: moment, - database: database, - bundle, - account_server: config.account_server_domain.slice(8), - cdnURL: config.CDN_domain, - lang: req.lang, - mii_image_CDN: config.mii_image_CDN, - pid: req.pid, - moderator: req.moderator - }); - } - else - res.sendStatus(204); + if (posts.length > 0) { + res.render(req.directory + '/partials/posts_list.ejs', { + communityMap: communityMap, + moment: moment, + database: database, + bundle, + account_server: config.account_server_domain.slice(8), + cdnURL: config.CDN_domain, + lang: req.lang, + mii_image_CDN: config.mii_image_CDN, + pid: req.pid, + moderator: req.moderator + }); + } else { + res.sendStatus(204); + } } module.exports = router; diff --git a/src/services/juxt-web/routes/console/web.js b/src/services/juxt-web/routes/console/web.js index 86410d0..7b4c1df 100644 --- a/src/services/juxt-web/routes/console/web.js +++ b/src/services/juxt-web/routes/console/web.js @@ -5,146 +5,156 @@ const { POST } = require('../../../../models/post'); const path = require('path'); router.get('/', function (req, res) { - res.redirect('/titles/show') + res.redirect('/titles/show'); }); router.get('/css/:filename', function (req, res) { - res.set("Content-Type", "text/css"); - res.sendFile('/css/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); + res.set('Content-Type', 'text/css'); + res.sendFile('/css/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); }); router.get('/js/:filename', function (req, res) { - res.set("Content-Type", "application/javascript; charset=utf-8"); - res.sendFile('/js/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); + res.set('Content-Type', 'application/javascript; charset=utf-8'); + res.sendFile('/js/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); }); router.get('/images/:filename', function (req, res) { - res.set("Content-Type", "image/png"); - res.sendFile('/images/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); + res.set('Content-Type', 'image/png'); + res.sendFile('/images/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); }); router.get('/fonts/:filename', function (req, res) { - res.set("Content-Type", "font/woff"); - res.sendFile('/fonts/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); + res.set('Content-Type', 'font/woff'); + res.sendFile('/fonts/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); }); router.get('/favicon.ico', function (req, res) { - res.set("Content-Type", "image/x-icon"); - res.sendFile('/images/favicon.ico', {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); + res.set('Content-Type', 'image/x-icon'); + res.sendFile('/images/favicon.ico', {root: path.join(__dirname, '../../../../webfiles/' + req.directory)}); }); router.get('/icons/:image_id.png', async function (req, res) { - res.set("Content-Type", "image/png"); - let community = await database.getCommunityByID(req.params.image_id.toString()); - if(community !== null && community.browser_icon) { - if(community.browser_icon.indexOf('data:image/png;base64,') !== -1) - res.send(Buffer.from(community.browser_icon.replace('data:image/png;base64,',''), 'base64')); - else - res.send(Buffer.from(community.browser_icon, 'base64')); - } - else { - let user = await database.getUserSettings(req.params.image_id.toString()); - if(user !== null) - if(user.pfp_uri.indexOf('data:image/png;base64,') !== -1) - res.send(Buffer.from(user.pfp_uri.replace('data:image/png;base64,',''), 'base64')); - else - res.send(Buffer.from(user.pfp_uri, 'base64')); - else - res.sendStatus(404); - } + res.set('Content-Type', 'image/png'); + const community = await database.getCommunityByID(req.params.image_id.toString()); + if (community !== null && community.browser_icon) { + if (community.browser_icon.indexOf('data:image/png;base64,') !== -1) { + res.send(Buffer.from(community.browser_icon.replace('data:image/png;base64,',''), 'base64')); + } else { + res.send(Buffer.from(community.browser_icon, 'base64')); + } + } else { + const user = await database.getUserSettings(req.params.image_id.toString()); + if (user !== null) { + if (user.pfp_uri.indexOf('data:image/png;base64,') !== -1) { + res.send(Buffer.from(user.pfp_uri.replace('data:image/png;base64,',''), 'base64')); + } else { + res.send(Buffer.from(user.pfp_uri, 'base64')); + } + } else { + res.sendStatus(404); + } + } }); router.get('/tip/:image_id.png', async function (req, res) { - res.set("Content-Type", "image/png"); - let community = await database.getCommunityByID(req.params.image_id.toString()); - if(community !== null) { - if(community.browser_thumbnail.indexOf('data:image/png;base64,') !== -1) - res.send(Buffer.from(community.browser_thumbnail.replace('data:image/png;base64,',''), 'base64')); - else - res.send(Buffer.from(community.browser_thumbnail, 'base64')); - } - else { - let user = await database.getUserByPID(req.params.image_id.toString()); - if (user !== null) - if (user.pfp_uri.indexOf('data:image/png;base64,') !== -1) - res.send(Buffer.from(user.pfp_uri.replace('data:image/png;base64,', ''), 'base64')); - else - res.send(Buffer.from(user.pfp_uri, 'base64')); - else - res.sendStatus(404); - } + res.set('Content-Type', 'image/png'); + const community = await database.getCommunityByID(req.params.image_id.toString()); + if (community !== null) { + if (community.browser_thumbnail.indexOf('data:image/png;base64,') !== -1) { + res.send(Buffer.from(community.browser_thumbnail.replace('data:image/png;base64,',''), 'base64')); + } else { + res.send(Buffer.from(community.browser_thumbnail, 'base64')); + } + } else { + const user = await database.getUserByPID(req.params.image_id.toString()); + if (user !== null) { + if (user.pfp_uri.indexOf('data:image/png;base64,') !== -1) { + res.send(Buffer.from(user.pfp_uri.replace('data:image/png;base64,', ''), 'base64')); + } else { + res.send(Buffer.from(user.pfp_uri, 'base64')); + } + } else { + res.sendStatus(404); + } + } }); router.get('/banner/:image_id.png', async function (req, res) { - res.set("Content-Type", "image/png"); - let community = await database.getCommunityByID(req.params.image_id.toString()); - if(community !== null && community.WiiU_browser_header !== undefined) - if(community.WiiU_browser_header.indexOf('data:image/png;base64,') !== -1) - res.send(Buffer.from(community.WiiU_browser_header.replace('data:image/png;base64,',''), 'base64')); - else - res.send(Buffer.from(community.WiiU_browser_header, 'base64')); - else - res.sendStatus(404); + res.set('Content-Type', 'image/png'); + const community = await database.getCommunityByID(req.params.image_id.toString()); + if (community !== null && community.WiiU_browser_header !== undefined) { + if (community.WiiU_browser_header.indexOf('data:image/png;base64,') !== -1) { + res.send(Buffer.from(community.WiiU_browser_header.replace('data:image/png;base64,',''), 'base64')); + } else { + res.send(Buffer.from(community.WiiU_browser_header, 'base64')); + } + } else { + res.sendStatus(404); + } }); router.get('/screenshot/:image_id.png', async function (req, res) { - res.set("Content-Type", "image/png"); - let post = await database.getPostByID(req.params.image_id.toString()); - if(post !== null && post.screenshot !== '') - if(post.screenshot.indexOf('data:image/png;base64,') !== -1) - res.send(Buffer.from(post.screenshot.replace('data:image/png;base64,',''), 'base64')); - else - res.send(Buffer.from(post.screenshot, 'base64')); - else - res.sendStatus(404); + res.set('Content-Type', 'image/png'); + const post = await database.getPostByID(req.params.image_id.toString()); + if (post !== null && post.screenshot !== '') { + if (post.screenshot.indexOf('data:image/png;base64,') !== -1) { + res.send(Buffer.from(post.screenshot.replace('data:image/png;base64,',''), 'base64')); + } else { + res.send(Buffer.from(post.screenshot, 'base64')); + } + } else { + res.sendStatus(404); + } }); router.get('/drawing/:image_id.png', async function (req, res) { - res.set("Content-Type", "image/png"); - let post = await database.getPostByID(req.params.image_id.toString()); - if(post !== null && post.painting_uri !== '') - if(post.painting_uri.indexOf('data:image/png;base64,') !== -1) - res.send(Buffer.from(post.painting_uri.replace('data:image/png;base64,',''), 'base64')); - else - res.send(Buffer.from(post.painting_uri, 'base64')); - else - res.sendStatus(404); + res.set('Content-Type', 'image/png'); + const post = await database.getPostByID(req.params.image_id.toString()); + if (post !== null && post.painting_uri !== '') { + if (post.painting_uri.indexOf('data:image/png;base64,') !== -1) { + res.send(Buffer.from(post.painting_uri.replace('data:image/png;base64,',''), 'base64')); + } else { + res.send(Buffer.from(post.painting_uri, 'base64')); + } + } else { + res.sendStatus(404); + } }); router.get('/notifications.json', async function (req, res) { - let notifications = await database.getUnreadNotificationCount(req.pid); - let messagesCount = await database.getUnreadConversationCount(req.pid); - res.send( - { - message_count: messagesCount, - notification_count: notifications, - } - ) + const notifications = await database.getUnreadNotificationCount(req.pid); + const messagesCount = await database.getUnreadConversationCount(req.pid); + res.send( + { + message_count: messagesCount, + notification_count: notifications, + } + ); }); router.get('/:post_id/oembed.json', async function (req, res) { - let post = await database.getPostByID(req.params.post_id.toString()); - let user = await database.getUserByPID(post.pid); - let doc = { - "author_name": user.user_id, - "author_url": "https://portal.olv.pretendo.cc/users/show?pid=" + user.pid, - } - res.send(doc) + const post = await database.getPostByID(req.params.post_id.toString()); + const user = await database.getUserByPID(post.pid); + const doc = { + 'author_name': user.user_id, + 'author_url': 'https://portal.olv.pretendo.cc/users/show?pid=' + user.pid, + }; + res.send(doc); }); router.get('/downloadUserData.json', async function (req, res) { - res.set("Content-Type", "text/json"); - res.set('Content-Disposition', `attachment; filename="${req.pid}_user_data.json"`); - let posts = await POST.find({ pid: req.pid }) - let userContent = await database.getUserSettings(req.pid); - let userSettings = await database.getUserContent(req.pid); - let doc = { - "user_content": userContent, - "user_settings": userSettings, - "posts": posts, - } - res.send(doc) + res.set('Content-Type', 'text/json'); + res.set('Content-Disposition', `attachment; filename="${req.pid}_user_data.json"`); + const posts = await POST.find({ pid: req.pid }); + const userContent = await database.getUserSettings(req.pid); + const userSettings = await database.getUserContent(req.pid); + const doc = { + 'user_content': userContent, + 'user_settings': userSettings, + 'posts': posts, + }; + res.send(doc); }); module.exports = router; diff --git a/src/services/juxt-web/routes/web/login.js b/src/services/juxt-web/routes/web/login.js index 0474dbb..c5b387b 100644 --- a/src/services/juxt-web/routes/web/login.js +++ b/src/services/juxt-web/routes/web/login.js @@ -3,66 +3,74 @@ const router = express.Router(); const parseString = require('xml2js').parseString; const database = require('../../../../database'); const util = require('../../../../util'); -const config = require("../../../../../config.json"); -const request = require("request"); -const logger = require("../../../../logger"); +const config = require('../../../../../config.json'); +const request = require('request'); +const logger = require('../../../../logger'); router.get('/', async function (req, res) { - res.render(req.directory + '/login.ejs', {toast: null, cdnURL: config.CDN_domain,}); + res.render(req.directory + '/login.ejs', {toast: null, cdnURL: config.CDN_domain,}); }); router.post('/', async (req, res) => { - const { username, password } = req.body; - const login = await util.data.login(username, password).catch((e) => { - console.log(e.details); - switch (e.details) { - case 'INVALID_ARGUMENT: User not found': - res.render(req.directory + '/login.ejs', {toast: 'Username was invalid.', cdnURL: config.CDN_domain,}); - break; - case 'INVALID_ARGUMENT: Password is incorrect': - res.render(req.directory + '/login.ejs', {toast: 'Password was incorrect.', cdnURL: config.CDN_domain,}); - break; - default: - res.render(req.directory + '/login.ejs', {toast: 'Invalid username or password.', cdnURL: config.CDN_domain,}); - break; - } - }); - if(!login) return; + const { username, password } = req.body; + const login = await util.data.login(username, password).catch((e) => { + console.log(e.details); + switch (e.details) { + case 'INVALID_ARGUMENT: User not found': + res.render(req.directory + '/login.ejs', {toast: 'Username was invalid.', cdnURL: config.CDN_domain,}); + break; + case 'INVALID_ARGUMENT: Password is incorrect': + res.render(req.directory + '/login.ejs', {toast: 'Password was incorrect.', cdnURL: config.CDN_domain,}); + break; + default: + res.render(req.directory + '/login.ejs', {toast: 'Invalid username or password.', cdnURL: config.CDN_domain,}); + break; + } + }); + if (!login) { + return; + } - const PNID = await util.data.getUserDataFromToken(login.accessToken); - if(!PNID) - return res.render(req.directory + '/login.ejs', {toast: 'Invalid username or password.', cdnURL: config.CDN_domain,}); + const PNID = await util.data.getUserDataFromToken(login.accessToken); + if (!PNID) { + return res.render(req.directory + '/login.ejs', {toast: 'Invalid username or password.', cdnURL: config.CDN_domain,}); + } - const pid = PNID.pid; + const pid = PNID.pid; - let discovery = await database.getEndPoint(PNID.serverAccessLevel); - let message = ''; - switch (discovery.status) { - case 3: - message = "Juxt is currently undergoing maintenance. Please try again later."; - break; - case 4: - message = "Juxt is currently closed. Thank you for your interest."; - break; - default: - message = "Juxt is currently unavailable. Please try again later."; - break; - } - if(discovery.status !== 0) { - return res.render(req.directory + '/error.ejs', { - code: 504, - message: message, - cdnURL: config.CDN_domain, - lang: req.lang, - pid: pid, - moderator: req.moderator - }); - } - let cookieDomain = (req.hostname.indexOf('miiverse') !== -1) ? '.miiverse.cc' : '.pretendo.network'; - let expiration = (req.hostname.indexOf('miiverse') !== -1) ? login.expiresIn * 60 * 60 * 24 : login.expiresIn * 60 * 60 - res.cookie('access_token', login.accessToken, { domain : cookieDomain, maxAge: expiration }); - res.cookie('refresh_token', login.refreshToken, { domain : cookieDomain }); - res.redirect('/'); + let discovery = await database.getEndPoint(PNID.serverAccessLevel); + if (!discovery) { + discovery = { + status: 5 + }; + } + let message = ''; + switch (discovery.status) { + case 3: + message = 'Juxt is currently undergoing maintenance. Please try again later.'; + break; + case 4: + message = 'Juxt is currently closed. Thank you for your interest.'; + break; + default: + message = 'Juxt is currently unavailable. Please try again later.'; + break; + } + if (discovery.status !== 0) { + return res.render(req.directory + '/error.ejs', { + code: 504, + message: message, + cdnURL: config.CDN_domain, + lang: req.lang, + pid: pid, + moderator: req.moderator + }); + } + const cookieDomain = (req.hostname.indexOf('miiverse') !== -1) ? '.miiverse.cc' : '.pretendo.network'; + const expiration = (req.hostname.indexOf('miiverse') !== -1) ? login.expiresIn * 60 * 60 * 24 : login.expiresIn * 60 * 60; + res.cookie('access_token', login.accessToken, { domain : cookieDomain, maxAge: expiration }); + res.cookie('refresh_token', login.refreshToken, { domain : cookieDomain }); + res.redirect('/'); }); diff --git a/src/services/juxt-web/routes/web/pwa.js b/src/services/juxt-web/routes/web/pwa.js index 6756caf..bdcad2f 100644 --- a/src/services/juxt-web/routes/web/pwa.js +++ b/src/services/juxt-web/routes/web/pwa.js @@ -1,15 +1,15 @@ const express = require('express'); -const path = require("path"); +const path = require('path'); const router = express.Router(); router.get('/icons/:filename', function (req, res) { - res.set("Content-Type", "image/png"); - res.sendFile('/images/icons/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/web')}); + res.set('Content-Type', 'image/png'); + res.sendFile('/images/icons/' + req.params.filename, {root: path.join(__dirname, '../../../../webfiles/web')}); }); router.get('/manifest.json', function (req, res) { - res.set("Content-Type", "text/json"); - res.sendFile('manifest.json', {root: path.join(__dirname, '../../../../webfiles/web')}); + res.set('Content-Type', 'text/json'); + res.sendFile('manifest.json', {root: path.join(__dirname, '../../../../webfiles/web')}); }); module.exports = router; diff --git a/src/services/juxt-web/routes/web/robots.js b/src/services/juxt-web/routes/web/robots.js index c016aa5..859b545 100644 --- a/src/services/juxt-web/routes/web/robots.js +++ b/src/services/juxt-web/routes/web/robots.js @@ -1,10 +1,10 @@ const express = require('express'); -const path = require("path"); +const path = require('path'); const router = express.Router(); router.get('/', function (req, res) { - res.set("Content-Type", "text/css"); - res.sendFile('robots.txt', {root: path.join(__dirname, '../../../../webfiles/web')}); + res.set('Content-Type', 'text/css'); + res.sendFile('robots.txt', {root: path.join(__dirname, '../../../../webfiles/web')}); }); diff --git a/src/translations/index.js b/src/translations/index.js index 4aecb45..ced482f 100644 --- a/src/translations/index.js +++ b/src/translations/index.js @@ -1,14 +1,14 @@ module.exports = { - JA: require('./ja.json'), - EN: require('./en.json'), - FR: require('./fr.json'), - DE: require('./de.json'), - IT: require('./it.json'), - ES: require('./es.json'), - ZH: require('./zh.json'), - KO: require('./ko.json'), - NL: require('./nl.json'), - PT: require('./pt.json'), - RU: require('./ru.json'), + JA: require('./ja.json'), + EN: require('./en.json'), + FR: require('./fr.json'), + DE: require('./de.json'), + IT: require('./it.json'), + ES: require('./es.json'), + ZH: require('./zh.json'), + KO: require('./ko.json'), + NL: require('./nl.json'), + PT: require('./pt.json'), + RU: require('./ru.json'), }; \ No newline at end of file diff --git a/src/util.js b/src/util.js index b1e31f7..9d1dc82 100644 --- a/src/util.js +++ b/src/util.js @@ -12,16 +12,16 @@ const { COMMUNITY } = require('./models/communities'); const { AccountDefinition } = require('pretendo-grpc/dist/account/account_service'); const { FriendsDefinition } = require('pretendo-grpc/dist/friends/friends_service'); const { APIDefinition } = require('pretendo-grpc/dist/api/api_service'); -const translations = require('./translations') +const translations = require('./translations'); const HashMap = require('hashmap'); const TGA = require('tga'); const pako = require('pako'); const PNG = require('pngjs').PNG; -const bmp = require("bmp-js"); +const bmp = require('bmp-js'); const aws = require('aws-sdk'); const crc32 = require('crc/crc32'); -let communityMap = new HashMap(); -let userMap = new HashMap(); +const communityMap = new HashMap(); +const userMap = new HashMap(); const { ip: friendsIP, port: friendsPort, api_key: friendsKey } = config.grpc.friends; const friendsChannel = grpc.createChannel(`${friendsIP}:${friendsPort}`); @@ -36,311 +36,307 @@ const accountClient = grpc.createClient(AccountDefinition, accountChannel); const spacesEndpoint = new aws.Endpoint('nyc3.digitaloceanspaces.com'); const s3 = new aws.S3({ - endpoint: spacesEndpoint, - accessKeyId: config.aws.spaces.key, - secretAccessKey: config.aws.spaces.secret + endpoint: spacesEndpoint, + accessKeyId: config.aws.spaces.key, + secretAccessKey: config.aws.spaces.secret }); nameCache(); function nameCache() { - database.connect().then(async e => { - let communities = await COMMUNITY.find(); - if(communities !== null) { - for(let i = 0; i < communities.length; i++ ) { - if(communities[i].title_id !== null) { - for(let j = 0; j < communities[i].title_id.length; j++) { - communityMap.set(communities[i].title_id[j], communities[i].name); - communityMap.set(communities[i].title_id[j] + '-id', communities[i].olive_community_id); - } - communityMap.set(communities[i].olive_community_id, communities[i].name); - } - } - logger.success('Created community index of ' + communities.length + ' communities'); - } - let users = await database.getUsersSettings(-1); - if(users !== null) { - for(let i = 0; i < users.length; i++ ) { - if(users[i].pid !== null) { - userMap.set(users[i].pid, users[i].screen_name.replace(/[\u{0080}-\u{FFFF}]/gu,"")); - } - } - logger.success('Created user index of ' + users.length + ' users') - } + database.connect().then(async e => { + const communities = await COMMUNITY.find(); + if (communities !== null) { + for (let i = 0; i < communities.length; i++ ) { + if (communities[i].title_id !== null) { + for (let j = 0; j < communities[i].title_id.length; j++) { + communityMap.set(communities[i].title_id[j], communities[i].name); + communityMap.set(communities[i].title_id[j] + '-id', communities[i].olive_community_id); + } + communityMap.set(communities[i].olive_community_id, communities[i].name); + } + } + logger.success('Created community index of ' + communities.length + ' communities'); + } + const users = await database.getUsersSettings(-1); + if (users !== null) { + for (let i = 0; i < users.length; i++ ) { + if (users[i].pid !== null) { + userMap.set(users[i].pid, users[i].screen_name.replace(/[\u{0080}-\u{FFFF}]/gu,'')); + } + } + logger.success('Created user index of ' + users.length + ' users'); + } - }).catch(error => { - logger.error(error); - }); + }).catch(error => { + logger.error(error); + }); } -let methods = { - create_user: async function(pid, experience, notifications) { - const pnid = await this.getUserDataFromPid(pid); - if(!pnid) - return; - let newSettings = { - pid: pid, - screen_name: pnid.mii.name, - game_skill: experience, - receive_notifications: notifications, - } - let newContent = { - pid: pid - } - const newSettingsObj = new SETTINGS(newSettings); - await newSettingsObj.save(); +const methods = { + 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(); - 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(); - } - return out; - }, - processServiceToken: function(encryptedToken) { - try - { - let B64token = Buffer.from(encryptedToken, 'base64'); - let decryptedToken = this.decryptToken(B64token); - const token = this.unpackToken(decryptedToken); - return token.pid; - } - catch(e) - { - console.log(e) - return null; - } + 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(); + } + return out; + }, + processServiceToken: function(encryptedToken) { + try { + const B64token = Buffer.from(encryptedToken, 'base64'); + const decryptedToken = this.decryptToken(B64token); + const token = this.unpackToken(decryptedToken); + return token.pid; + } catch (e) { + console.log(e); + return null; + } - }, - decryptToken: function(token) { - if (!config.aes_key) { - throw new Error('Service token AES key not found. Set config.aes_key'); - } + }, + decryptToken: function(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 iv = Buffer.alloc(16); + const key = Buffer.from(config.aes_key, 'hex'); - const expectedChecksum = token.readUint32BE(); - const encryptedBody = token.subarray(4); + const expectedChecksum = token.readUint32BE(); + const encryptedBody = token.subarray(4); - const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); - const decrypted = Buffer.concat([ - decipher.update(encryptedBody), - decipher.final() - ]); + 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?'); - } + if (expectedChecksum !== crc32(decrypted)) { + throw new Error('Checksum did not match. Failed decrypt. Are you using the right key?'); + } - 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) { - let 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; - } - let 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 { - let paintingBuffer = Buffer.from(painting, 'base64'); - let bitmap = bmp.decode(paintingBuffer) - const tga = this.createBMPTgaBuffer(bitmap.width, bitmap.height, bitmap.data, false); + 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); + 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) { - var 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 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); - var offset = 18; - for (var i = 0; i < height; i++) { - for (var j = 0; j < width; j++) { - var 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 - } - } + 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 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 - }; + return buffer; + }, + processLanguage: function (paramPackData) { + if (!paramPackData) { + 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 + }; - 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 { - let 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') { + 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(); + } + } + /*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) { @@ -374,58 +370,58 @@ let methods = { await newNotification.save(); } }*/ - }, - getFriends: async function(pid) { - const pids = await friendsClient.getUserFriendPIDs({ - pid: pid - }, { - metadata: grpc.Metadata({ - 'X-API-Key': friendsKey - }) - }); - return pids.pids; - }, - getFriendRequests: async function(pid) { - const requests = await friendsClient.getUserFriendRequestsIncoming({ - pid: pid - }, { - metadata: grpc.Metadata({ - 'X-API-Key': friendsKey - }) - }); - 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 - }) - }); - }, - 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; - } + }, + getFriends: async function(pid) { + const pids = await friendsClient.getUserFriendPIDs({ + pid: pid + }, { + metadata: grpc.Metadata({ + 'X-API-Key': friendsKey + }) + }); + return pids.pids; + }, + getFriendRequests: async function(pid) { + const requests = await friendsClient.getUserFriendRequestsIncoming({ + pid: pid + }, { + metadata: grpc.Metadata({ + 'X-API-Key': friendsKey + }) + }); + 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 + }) + }); + }, + 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; + } }; exports.data = methods; diff --git a/src/webfiles/ctr/js/debug.js b/src/webfiles/ctr/js/debug.js index c28edb4..92cfcd0 100644 --- a/src/webfiles/ctr/js/debug.js +++ b/src/webfiles/ctr/js/debug.js @@ -1,277 +1,278 @@ -if (typeof cave !== "undefined") { - window.addEventListener("error", handleError, true); +if (typeof cave !== 'undefined') { + window.addEventListener('error', handleError, true); - function handleError(evt) { - if (evt.message) { // Chrome sometimes provides this - alert("error: " + evt.message + " at linenumber: " + evt.lineno + " of file: " + evt.filename); - } else { - if((evt.srcElement || evt.target).toString().indexOf('HTMLImageElement') !== -1) - return; - alert("error: " + evt.type + " from element: " + (evt.srcElement || evt.target)); - } - } + function handleError(evt) { + if (evt.message) { // Chrome sometimes provides this + alert('error: ' + evt.message + ' at linenumber: ' + evt.lineno + ' of file: ' + evt.filename); + } else { + if ((evt.srcElement || evt.target).toString().indexOf('HTMLImageElement') !== -1) { + return; + } + alert('error: ' + evt.type + ' from element: ' + (evt.srcElement || evt.target)); + } + } } -if (typeof cave === "undefined") { - window.cave = { - /** +if (typeof cave === 'undefined') { + window.cave = { + /** * Enables or disabled the back button on the toolbar * @param enable */ - toolbar_enableBackBtnFunc: function (enable) { - console.log('cave.toolbar_enableBackBtnFunc(enable = ' + enable + ')'); - }, - /** + toolbar_enableBackBtnFunc: function (enable) { + console.log('cave.toolbar_enableBackBtnFunc(enable = ' + enable + ')'); + }, + /** * Sets the callback function for a given toolbar button * @param btnType * @param callback */ - toolbar_setCallback: function (btnType, callback) { - console.log('cave.toolbar_setCallback(btnType = ' + btnType + ', callback = ' + typeof callback + ')'); - }, - /** + toolbar_setCallback: function (btnType, callback) { + console.log('cave.toolbar_setCallback(btnType = ' + btnType + ', callback = ' + typeof callback + ')'); + }, + /** * Toggles the visibility of the toolbar * @param visibility */ - toolbar_setVisible: function (visibility) { - console.log('cave.toolbar_setVisible(visibility = ' + visibility + ')'); - }, - /** + toolbar_setVisible: function (visibility) { + console.log('cave.toolbar_setVisible(visibility = ' + visibility + ')'); + }, + /** * (untested) Appears to set the "corner button" type. * @param type */ - toolbar_setButtonType: function (type) { - console.log('cave.toolbar_setButtonType(type = ' + type + ')'); - }, - /** + toolbar_setButtonType: function (type) { + console.log('cave.toolbar_setButtonType(type = ' + type + ')'); + }, + /** * Highlight a button - usually done in a button's own callback function to show that it has been activated. * @param btnType */ - toolbar_setActiveButton: function (btnType) { - console.log('cave.toolbar_setActiveButton(btnType = ' + btnType + ')'); - }, - /** + toolbar_setActiveButton: function (btnType) { + console.log('cave.toolbar_setActiveButton(btnType = ' + btnType + ')'); + }, + /** * Open the drawing tool interface. */ - memo_open: function () { - console.log('cave.memo_open()'); - }, - /** + memo_open: function () { + console.log('cave.memo_open()'); + }, + /** * Clear the current drawing. */ - memo_clear: function () { - console.log('cave.memo_clear()'); - }, - /** + memo_clear: function () { + console.log('cave.memo_clear()'); + }, + /** * Unknown * @returns {boolean} */ - memo_hasValidImage: function () { - console.log('cave.memo_hasValidImage()'); - return true; - }, - /** + memo_hasValidImage: function () { + console.log('cave.memo_hasValidImage()'); + return true; + }, + /** * Returns the drawing as base64 encoded BMP image. * @returns {string} */ - memo_getImageBmp: function () { - console.log('cave.memo_getImageBmp()'); - return 'Qk1SEwAAAAAAAJIAAAB8AAAAQAEAAHgAAAABAAEAAAAAAMASAAASCwAAEgsAAAIAAAACAAAAAAD/AAD/AAD/AAAAAAAA/0JHUnOPwvUoUbgeFR6F6wEzMzMTZmZmJmZmZgaZmZkJPQrXAyhcjzIAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAP///wD/////////////////////+P/////8f/////////////////////////////////////////////j//////H/////////////////////////////////////////////4//////x/////////////////////////////////////////////+P/////8f////////////////////////////////////////4A///h//////H////////////////////////////////////////4AH//4f/////x////////////////////////////////////////+Hx///H/////8f////////////////////////////////////////j8H//x//////H////////////////////////////////////////4wB//8f/////x////////////////////////////////////////+AAP//D//h//8f////////////////////////////////////////wPh//4//4f//H////////////////////////////////////////8H4f/+P/+H//x/////////////////////////////////////////h/D//j/////8f////////////////////////////////////////4f4f/4//////H/////////////////////////////////////////D/D/+P/////x/////////////////////////////////////////w/w//j/////8f////////////////////////////////////////+H+H/4//////H/////////////////////////////////////////w/g/+P/////x/////////////////////////////////////////8P+H/j//A//8f/////////////////////////////////////////h/h/4//wP//H/////////////////////////////////////////8f8P+P/4B//x//////////////////////////////////////////D/D/j/8Mf/8f/////////////////////////////////////////4f4f4/+HD//H/////////////////////////////////////////+D+H+P/h4//x//////////////////////////////////////////4fwfj/w+H/8f/wB//////////////////////////////////////+H+H4/8Ph//H/wAP//////////////////////////////////////w/w+H+H4P/x/wPA//////////////////////////////////////+H+Hx/h+D/8fwH4H//////////////////////////////////////x/w8f4Pgf/HwP/wf/////////////////////////////////////8P8HH8D4H/x4H/8D//////////////////////////////////////h/wx/A8A/8YP//4H/////////////////////////////////////8f8EfgPEP/EH///B//////////////////////////////////////D/gH4jxj/wH///8H/////////////////////////////////////4f8B+I8Y/8D////g//////////////////////////////////////D/gfCHGH/D/8B/8D/////////////////////////////////////4f8Hwhxx/w/+AP/gf/////////////////////////////////////D/h8cccf///Bg/+D/////////////////////////////////////4f//HHHH///g8H/wf/////////////////////////////////////D//hxhw///w/gf+D/////////////////////////////////////wf/48YeP//4P+D/4f/////////////////////////////////////D/+PCPj//4P/wf/A/////////////////////////////////////w//Dwj4//8H//B/4H////////////////////////////////////+D/x+A+P/+D//4H/h/////////////////////////////////////w/8fgPj//D///g/4D////////////////////////////////////+D/GAAAf/B///+D8AP////////////////////////////////////w/xAAAH/g////wPDD////////////////////////////////////+H8ADAB/w/////BB4f////////////////////////////////////w/Aggwf4f////4A/H////////////////////////////////////+HweA/D4P/////gfh/////////////////////////////////////w4Pgfw8H/////8Hwf///////////////////////////////////+AAAAB+EH//////wAP//////////////////////////////////+AAAAAAAAAf/////8AH//////////////////////////////////gAB+P//gAAAAH////////////////////////////////////////4B///////H4AB////////////////////////////////////////+P//////////4f////////////////////////////////////////j//////////+H////////////////////////////////////////4///////////j////////////////////////////////////////+P//////////4/////////////////////////////////////////j//////////8P////////////////////////////////////////4///////////D////////////////////////////////////////+P//////////x/////////////////////////////////////////j//////////8f////////////////////////////////////////4////wD/////H////////////////////////////////////////+P///4AP////x/////////////////////////////////////////h///wPAH///8f////////////////////////////////////////4f//4H8Af///H//////////////////////////////////////8ADH//wP/+AP//x//////////////////////////////////////8AAB//wH//4B//8YB////////////////////////////////////+D/Af/wP///8B//AAAP///////////////////////////////////j/8H/4H////gH/wHgA/////////////////////////////////8AA//x/8P/////gP8f/8H///////AAAAAAAAP////////////////+AAP/8f+H/////+A/H//x///////wAAAAAAAD////////////////+D////H+D8P///H8Bx//4f//////////////w/////////////////h////x/B/D///x/wEf/8AH///////////////////////////////4////8fB/x///8f/gH//AAP//////////////////////////////+H////HA/8f///H/8B///+B/////////////////////////////8AA////xg//H///x//4f///8H////////////////////////////8AAP///8Q//x///8f//H////h////////////////////////////8D//////Af/4f///H//x////8f///////////////////////////+D//////wP/+P///x//8f////H////////////////////////////D//////8H//j///8f//H////h////////////////////////////h///////H//4////H//x////w////////////////////////////4///////x///////x//8D///gf///////////////////////////+P/////8Af//////////AH//4D////////////////////////////j/////AAH//////////xAP/+AD////////H//////////////////4/////ADj//////////8eB///Af///////w//////////////////+P///wA/4///////////H8H///D///////+H//////////////////h///wA/8P//////////x/gf//w//////g/g//////////////////8P//AP//D//////////8f+B//+P/////4P+P//////////////////D/+AP//w/gAAAAAAAAAH/4H//j/////+H/j//////////////////4P4A///8AAAAAAAAAAAB//gf/4////j/w/////////////////////BgB////gAf///////////+D/+P///4f+P////////////////////4AH///////////////////4H/D////D/h/4D//////////////////Af////////////////////Afg/x//w/AP+A////////////////////////////////////////+Awf8f/+MAB/h/////////////////////////////////////////wAP/D//gA4f8f/////////////////////////////////////////wP/4//8D/D///////////////////////////////////////////+D/+P//A/4///////////////////////////////////////////////j//4f+H//////////////////////////////////////////////4f//H/x///////////////////////////////////////////////H//w/////////////////////////////////////////////////w//8H////////////////////////////////////////////////+P//x/////////////////////////////////////////////////h////////////////////////////////////////////////////8P////////////////////////////////////////////////////D////////////////////////////////////////////////////4////////////////////////////////////////////////////+P////////////////////////////////////////////////////j////////////////////////////////////////////////////4//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8='; - }, - /** + memo_getImageBmp: function () { + console.log('cave.memo_getImageBmp()'); + return 'Qk1SEwAAAAAAAJIAAAB8AAAAQAEAAHgAAAABAAEAAAAAAMASAAASCwAAEgsAAAIAAAACAAAAAAD/AAD/AAD/AAAAAAAA/0JHUnOPwvUoUbgeFR6F6wEzMzMTZmZmJmZmZgaZmZkJPQrXAyhcjzIAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAP///wD/////////////////////+P/////8f/////////////////////////////////////////////j//////H/////////////////////////////////////////////4//////x/////////////////////////////////////////////+P/////8f////////////////////////////////////////4A///h//////H////////////////////////////////////////4AH//4f/////x////////////////////////////////////////+Hx///H/////8f////////////////////////////////////////j8H//x//////H////////////////////////////////////////4wB//8f/////x////////////////////////////////////////+AAP//D//h//8f////////////////////////////////////////wPh//4//4f//H////////////////////////////////////////8H4f/+P/+H//x/////////////////////////////////////////h/D//j/////8f////////////////////////////////////////4f4f/4//////H/////////////////////////////////////////D/D/+P/////x/////////////////////////////////////////w/w//j/////8f////////////////////////////////////////+H+H/4//////H/////////////////////////////////////////w/g/+P/////x/////////////////////////////////////////8P+H/j//A//8f/////////////////////////////////////////h/h/4//wP//H/////////////////////////////////////////8f8P+P/4B//x//////////////////////////////////////////D/D/j/8Mf/8f/////////////////////////////////////////4f4f4/+HD//H/////////////////////////////////////////+D+H+P/h4//x//////////////////////////////////////////4fwfj/w+H/8f/wB//////////////////////////////////////+H+H4/8Ph//H/wAP//////////////////////////////////////w/w+H+H4P/x/wPA//////////////////////////////////////+H+Hx/h+D/8fwH4H//////////////////////////////////////x/w8f4Pgf/HwP/wf/////////////////////////////////////8P8HH8D4H/x4H/8D//////////////////////////////////////h/wx/A8A/8YP//4H/////////////////////////////////////8f8EfgPEP/EH///B//////////////////////////////////////D/gH4jxj/wH///8H/////////////////////////////////////4f8B+I8Y/8D////g//////////////////////////////////////D/gfCHGH/D/8B/8D/////////////////////////////////////4f8Hwhxx/w/+AP/gf/////////////////////////////////////D/h8cccf///Bg/+D/////////////////////////////////////4f//HHHH///g8H/wf/////////////////////////////////////D//hxhw///w/gf+D/////////////////////////////////////wf/48YeP//4P+D/4f/////////////////////////////////////D/+PCPj//4P/wf/A/////////////////////////////////////w//Dwj4//8H//B/4H////////////////////////////////////+D/x+A+P/+D//4H/h/////////////////////////////////////w/8fgPj//D///g/4D////////////////////////////////////+D/GAAAf/B///+D8AP////////////////////////////////////w/xAAAH/g////wPDD////////////////////////////////////+H8ADAB/w/////BB4f////////////////////////////////////w/Aggwf4f////4A/H////////////////////////////////////+HweA/D4P/////gfh/////////////////////////////////////w4Pgfw8H/////8Hwf///////////////////////////////////+AAAAB+EH//////wAP//////////////////////////////////+AAAAAAAAAf/////8AH//////////////////////////////////gAB+P//gAAAAH////////////////////////////////////////4B///////H4AB////////////////////////////////////////+P//////////4f////////////////////////////////////////j//////////+H////////////////////////////////////////4///////////j////////////////////////////////////////+P//////////4/////////////////////////////////////////j//////////8P////////////////////////////////////////4///////////D////////////////////////////////////////+P//////////x/////////////////////////////////////////j//////////8f////////////////////////////////////////4////wD/////H////////////////////////////////////////+P///4AP////x/////////////////////////////////////////h///wPAH///8f////////////////////////////////////////4f//4H8Af///H//////////////////////////////////////8ADH//wP/+AP//x//////////////////////////////////////8AAB//wH//4B//8YB////////////////////////////////////+D/Af/wP///8B//AAAP///////////////////////////////////j/8H/4H////gH/wHgA/////////////////////////////////8AA//x/8P/////gP8f/8H///////AAAAAAAAP////////////////+AAP/8f+H/////+A/H//x///////wAAAAAAAD////////////////+D////H+D8P///H8Bx//4f//////////////w/////////////////h////x/B/D///x/wEf/8AH///////////////////////////////4////8fB/x///8f/gH//AAP//////////////////////////////+H////HA/8f///H/8B///+B/////////////////////////////8AA////xg//H///x//4f///8H////////////////////////////8AAP///8Q//x///8f//H////h////////////////////////////8D//////Af/4f///H//x////8f///////////////////////////+D//////wP/+P///x//8f////H////////////////////////////D//////8H//j///8f//H////h////////////////////////////h///////H//4////H//x////w////////////////////////////4///////x///////x//8D///gf///////////////////////////+P/////8Af//////////AH//4D////////////////////////////j/////AAH//////////xAP/+AD////////H//////////////////4/////ADj//////////8eB///Af///////w//////////////////+P///wA/4///////////H8H///D///////+H//////////////////h///wA/8P//////////x/gf//w//////g/g//////////////////8P//AP//D//////////8f+B//+P/////4P+P//////////////////D/+AP//w/gAAAAAAAAAH/4H//j/////+H/j//////////////////4P4A///8AAAAAAAAAAAB//gf/4////j/w/////////////////////BgB////gAf///////////+D/+P///4f+P////////////////////4AH///////////////////4H/D////D/h/4D//////////////////Af////////////////////Afg/x//w/AP+A////////////////////////////////////////+Awf8f/+MAB/h/////////////////////////////////////////wAP/D//gA4f8f/////////////////////////////////////////wP/4//8D/D///////////////////////////////////////////+D/+P//A/4///////////////////////////////////////////////j//4f+H//////////////////////////////////////////////4f//H/x///////////////////////////////////////////////H//w/////////////////////////////////////////////////w//8H////////////////////////////////////////////////+P//x/////////////////////////////////////////////////h////////////////////////////////////////////////////8P////////////////////////////////////////////////////D////////////////////////////////////////////////////4////////////////////////////////////////////////////+P////////////////////////////////////////////////////j////////////////////////////////////////////////////4//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8='; + }, + /** * (untested) Returns the drawing as base64 encoded TGA image. * @returns {string} */ - memo_getImageRawTga: function () { - console.log('cave.memo_getImageRawTga()'); - return 'Qk1CAAAAAAAAAD4AAAAoAAAAAQAAAAEAAAABAAEAAAAAAAQAAADEDgAAxA4AAAAAAAAAAAAAAAAAAP///wCAAAAAAAAA'; - }, - /** + memo_getImageRawTga: function () { + console.log('cave.memo_getImageRawTga()'); + return 'Qk1CAAAAAAAAAD4AAAAoAAAAAQAAAAEAAAABAAEAAAAAAAQAAADEDgAAxA4AAAAAAAAAAAAAAAAAAP///wCAAAAAAAAA'; + }, + /** * Set the current notification count * @param count */ - toolbar_setNotificationCount: function (count) { - console.log('cave.toolbar_setNotificationCount(' + count + ')'); - }, - /** + toolbar_setNotificationCount: function (count) { + console.log('cave.toolbar_setNotificationCount(' + count + ')'); + }, + /** * Calls system error viewer with specified error code * @param errorCode */ - error_callErrorViewer: function (errorCode) { - var fakeMessage = 'Message for error code ' + errorCode; - alert(errorCode + '\n\n' + fakeMessage); - }, - /** + error_callErrorViewer: function (errorCode) { + const fakeMessage = 'Message for error code ' + errorCode; + alert(errorCode + '\n\n' + fakeMessage); + }, + /** * Calls system error viewer with specified error code and message * @param errorCode */ - error_callFreeErrorViewer: function (errorCode, message) { - alert(errorCode + '\n\n' + message); - }, - /** + error_callFreeErrorViewer: function (errorCode, message) { + alert(errorCode + '\n\n' + message); + }, + /** * Begins transition to new page */ - transition_begin: function () { - console.log('cave.transition_begin()'); - }, - /** + transition_begin: function () { + console.log('cave.transition_begin()'); + }, + /** * Ends transition to new page */ - transition_end: function () { - console.log('cave.transition_end()'); - }, - /** + transition_end: function () { + console.log('cave.transition_end()'); + }, + /** * Begins transition to new page without grey screen */ - transition_beginWithoutEffect: function () { - console.log('cave.transition_beginWithoutEffect()'); - }, - /** + transition_beginWithoutEffect: function () { + console.log('cave.transition_beginWithoutEffect()'); + }, + /** * Ends transition to new page without grey screen */ - transition_endWithoutEffect: function () { - console.log('cave.transition_endWithoutEffect()'); - }, - /** + transition_endWithoutEffect: function () { + console.log('cave.transition_endWithoutEffect()'); + }, + /** * Session storage of key value pair * @param key * @param value * @returns {undefined} */ - ls_setItem: function (key, value) { - console.log('cave.ls_setItem()'); - // string でないと実機でエラー出るので挙動を合わせる - // If it is not string, an error will occur on the actual machine, so match the behavior - for (var i = 0; i < 2; i++) { - if (typeof arguments[i] !== 'string') { - console.error('Argument ' + i + ' should be string'); - console.error('JavaScript Extension error. Arguments Count or Argument Type is not mutch. Or , too big arguments.'); - return undefined; - } - } - sessionStorage.setItem(key, value); - }, - /** + ls_setItem: function (key, value) { + console.log('cave.ls_setItem()'); + // string でないと実機でエラー出るので挙動を合わせる + // If it is not string, an error will occur on the actual machine, so match the behavior + for (let i = 0; i < 2; i++) { + if (typeof arguments[i] !== 'string') { + console.error('Argument ' + i + ' should be string'); + console.error('JavaScript Extension error. Arguments Count or Argument Type is not mutch. Or , too big arguments.'); + return undefined; + } + } + sessionStorage.setItem(key, value); + }, + /** * Fetches value for key from session storage * @param key * @returns {undefined|string} */ - ls_getItem: function (key) { - console.log('cave.ls_getItem()'); - if (typeof key !== 'string') { - console.error('Argument 0 should be string'); - console.error('JavaScript Extension error. Arguments Count or Argument Type is not mutch. Or , too big arguments.'); - return undefined; - } - return sessionStorage.getItem(key); - }, - /** + ls_getItem: function (key) { + console.log('cave.ls_getItem()'); + if (typeof key !== 'string') { + console.error('Argument 0 should be string'); + console.error('JavaScript Extension error. Arguments Count or Argument Type is not mutch. Or , too big arguments.'); + return undefined; + } + return sessionStorage.getItem(key); + }, + /** * Removes value for key from session storage * @param key */ - ls_removeItem: function (key) { - console.log('cave.ls_removeItem()'); - sessionStorage.removeItem(key); - }, - /** + ls_removeItem: function (key) { + console.log('cave.ls_removeItem()'); + sessionStorage.removeItem(key); + }, + /** * ゲスト初回起動済みフラグをセットする (Set guest first started flag) * Puts the applet into guest mode * @param bool */ - ls_setGuestModeLaunched: function (bool) { - console.log('cave.ls_setGuestModeLaunched( ' + bool + ' )'); - }, - /** + ls_setGuestModeLaunched: function (bool) { + console.log('cave.ls_setGuestModeLaunched( ' + bool + ' )'); + }, + /** * Sets value for specified key in local storage * @param key * @param value */ - lls_setItem: function (key, value) { - console.log('cave.lls_setItem()'); - localStorage.setItem(key, value); - }, - /** + lls_setItem: function (key, value) { + console.log('cave.lls_setItem()'); + localStorage.setItem(key, value); + }, + /** * Fetches the number of values in local storage * @returns {number} */ - lls_getCount: function () { - console.log('cave.lls_getCount()'); - return localStorage.length; - }, - /** + lls_getCount: function () { + console.log('cave.lls_getCount()'); + return localStorage.length; + }, + /** * Fetches value for key from local storage * @param key * @returns {string} */ - lls_getItem: function (key) { - console.log('cave.lls_getItem()'); - return localStorage.getItem(key); - }, - /** + lls_getItem: function (key) { + console.log('cave.lls_getItem()'); + return localStorage.getItem(key); + }, + /** * Removes value for key from local storage * @param key */ - lls_removeItem: function (key) { - console.log('cave.lls_removeItem()'); - return localStorage.removeItem(key); - }, - /** + lls_removeItem: function (key) { + console.log('cave.lls_removeItem()'); + return localStorage.removeItem(key); + }, + /** * Returns if key 'custk' has value '1' * @returns {boolean} */ - ls_canUseCachedServiceToken: function () { - console.log('cave.ls_canUseCachedServiceToken()'); - return sessionStorage.getItem('custk') === '1' ? true : false; - }, - /** + ls_canUseCachedServiceToken: function () { + console.log('cave.ls_canUseCachedServiceToken()'); + return sessionStorage.getItem('custk') === '1' ? true : false; + }, + /** * Sets 'custk' key to specified value * @param can_use */ - ls_setCanUseCachedServiceToken: function (can_use) { - console.log('cave.ls_setCanUseCachedServiceToken'); - var flag = can_use ? '1' : '0'; - sessionStorage.setItem('custk', flag); - }, - /** + ls_setCanUseCachedServiceToken: function (can_use) { + console.log('cave.ls_setCanUseCachedServiceToken'); + const flag = can_use ? '1' : '0'; + sessionStorage.setItem('custk', flag); + }, + /** * Shows a dialog box with one button * @param title * @param message * @param button_text * @returns {number} */ - dialog_oneButton: function (title, message, button_text) { - console.log('cave.dialog_oneButton()'); - alert("タイトル:" + title + "\n" + "メッセージ:" + message + "\n\n[ " + button_text + " ]"); - return 0; - }, - /** + dialog_oneButton: function (title, message, button_text) { + console.log('cave.dialog_oneButton()'); + alert('タイトル:' + title + '\n' + 'メッセージ:' + message + '\n\n[ ' + button_text + ' ]'); + return 0; + }, + /** * Returns key at specified index * @param index * @returns {string} */ - lls_getKeyAt: function (index) { - console.log('cave.lls_getKeyAt index = ' + index); - return localStorage.key(index); - }, - /** + lls_getKeyAt: function (index) { + console.log('cave.lls_getKeyAt index = ' + index); + return localStorage.key(index); + }, + /** * Returns B64 encoded user Mii image with requested expression * @param expression {number} 0-5 * @returns {string} */ - mii_getIconBase64: function (expression) { - console.log('cave.mii_getIconBase64 expression = ' + expression); - // 本来表情毎に出し分けるべきだが、とりあえず固定のものを出す - // Originally, it should be displayed separately for each expression, but for the time being, a fixed one should be displayed - return 'data:image/png;base64,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'; - }, - /** + mii_getIconBase64: function (expression) { + console.log('cave.mii_getIconBase64 expression = ' + expression); + // 本来表情毎に出し分けるべきだが、とりあえず固定のものを出す + // Originally, it should be displayed separately for each expression, but for the time being, a fixed one should be displayed + return 'data:image/png;base64,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'; + }, + /** * Returns name of user Mii * @returns {string} */ - mii_getName: function () { - console.log('cave.mii_getName'); - return 'chris4403'; - }, - /** + mii_getName: function () { + console.log('cave.mii_getName'); + return 'chris4403'; + }, + /** * Shows a dialog box with two buttons * @param title * @param message @@ -279,60 +280,60 @@ if (typeof cave === "undefined") { * @param buttonR_text * @returns {number} */ - dialog_twoButton: function (title, message, buttonL_text, buttonR_text) { - console.log('cave.dialog_twoButton()'); - var result = confirm("タイトル:" + title + "\n" + "メッセージ:" + message + "\n\n[ " + buttonL_text + " (Cancel) ] [ " + buttonR_text + " (OK) ]"); - return result ? 1 : 0; - }, + dialog_twoButton: function (title, message, buttonL_text, buttonR_text) { + console.log('cave.dialog_twoButton()'); + const result = confirm('タイトル:' + title + '\n' + 'メッセージ:' + message + '\n\n[ ' + buttonL_text + ' (Cancel) ] [ ' + buttonR_text + ' (OK) ]'); + return result ? 1 : 0; + }, - brw_scrollImmediately: function (posX, posY) { - console.log('cave.brw_scrollImmediately(posX=' + posX + ', posY=' + posY + ')'); - window.scroll(posX, posY); - }, + brw_scrollImmediately: function (posX, posY) { + console.log('cave.brw_scrollImmediately(posX=' + posX + ', posY=' + posY + ')'); + window.scroll(posX, posY); + }, - brw_getScrollTopX: function () { - console.log('cave.brw_getScrollTopX()'); - return window.pageXOffset; - }, + brw_getScrollTopX: function () { + console.log('cave.brw_getScrollTopX()'); + return window.pageXOffset; + }, - brw_getScrollTopY: function () { - console.log('cave.brw_getScrollTopY()'); - return window.pageYOffset; - }, + brw_getScrollTopY: function () { + console.log('cave.brw_getScrollTopY()'); + return window.pageYOffset; + }, - brw_notifyPageMoving: function () { - console.log('cave.brw_notifyPageMoving()'); - }, + brw_notifyPageMoving: function () { + console.log('cave.brw_notifyPageMoving()'); + }, - requestGc: function () { - console.log('requestGc'); - }, - /** + requestGc: function () { + console.log('requestGc'); + }, + /** * Exits the app */ - exitApp: function () { - console.log('cave.exitApp()'); - }, - /** + exitApp: function () { + console.log('cave.exitApp()'); + }, + /** * Returns a JSON string, containing app IDs * @param minutes * @returns {string} */ - plog_getPlayTitlesFilteredByPlayTime: function (minutes) { - console.log('cave.plog_getPlayTitlesFilteredByPlayTime(' + minutes + ')'); - return '{"IDs":["0004001000020700"]}'; // Miiスタジオ (Mii studio) - }, - /** + plog_getPlayTitlesFilteredByPlayTime: function (minutes) { + console.log('cave.plog_getPlayTitlesFilteredByPlayTime(' + minutes + ')'); + return '{"IDs":["0004001000020700"]}'; // Miiスタジオ (Mii studio) + }, + /** * Jumps to user account settings */ - jump_toAccount: function () { - console.log('cave.jump_toAccount()'); - }, - /** + jump_toAccount: function () { + console.log('cave.jump_toAccount()'); + }, + /** * play (predefined) sound effect * @param label */ - /* Values: + /* Values: SE_OLV_CANCEL SE_OLV_OK SE_OLV_OK_SUB @@ -391,14 +392,14 @@ if (typeof cave === "undefined") { SE_CTR_SPIDER_InfoOn SE_CTR_SPIDER_SEL_END */ - snd_playSe: function (label) { - console.log('cave.snd_playSe(' + label + ')'); - }, - /** + snd_playSe: function (label) { + console.log('cave.snd_playSe(' + label + ')'); + }, + /** * Play (predefined) background music * @param label */ - /* Values: + /* Values: BGM_ACCOUNT_OP BGM_CAVE_MAIN BGM_CAVE_MAIN_LOOP @@ -410,313 +411,317 @@ if (typeof cave === "undefined") { BGM_CAVE_SYOKAI BGM_CAVE_SYOKAI2 */ - snd_playBgm: function (label) { - console.log('cave.snd_playBgm(' + label + ')'); - }, - /** + snd_playBgm: function (label) { + console.log('cave.snd_playBgm(' + label + ')'); + }, + /** * Checks if suspended application is running * @returns {boolean} */ - sap_exists: function () { - console.log('cave.sap_exists()'); - return false; - }, - /** + sap_exists: function () { + console.log('cave.sap_exists()'); + return false; + }, + /** * Fetches the program ID for the suspended application * @returns {undefined} */ - sap_programId: function () { - console.log('cave.sap_programId()'); - return undefined; - }, - /** + sap_programId: function () { + console.log('cave.sap_programId()'); + return undefined; + }, + /** * Captures the lower screen of the suspended app. Returns as a base64 encoded image (todo: clarify format). */ - capture_getLowerImage: function () { - console.log('cave.capture_getLowerImage()'); - }, - /** + capture_getLowerImage: function () { + console.log('cave.capture_getLowerImage()'); + }, + /** * Captures the left-eye image from the upper screen of the suspended app. Returns as a base64 encoded image (todo: clarify format). */ - capture_getUpperImageLeft: function () { - console.log('cave.capture_getUpperImageLeft()'); - }, - /** + capture_getUpperImageLeft: function () { + console.log('cave.capture_getUpperImageLeft()'); + }, + /** * Captures the right-eye image from the upper screen of the suspended app. Returns as a base64 encoded image (todo: clarify format) */ - capture_getUpperImageRight: function () { - console.log('cave.capture_getUpperImageRight()'); - }, - /** + capture_getUpperImageRight: function () { + console.log('cave.capture_getUpperImageRight()'); + }, + /** * (untested) Presumably captures both left and right images from the top screen. */ - capture_getUpperImage3D: function () { - console.log('cave.capture_getUpperImage3D()'); - }, - /** + capture_getUpperImage3D: function () { + console.log('cave.capture_getUpperImage3D()'); + }, + /** * More advanced version of capture_getUpperImage3D that allows for customisation. * @param width * @param height * @param quality * @param format */ - capture_getLowerImageDetail: function (width, height, quality, format) { - console.log('cave.capture_getLowerImageDetail(' + + capture_getLowerImageDetail: function (width, height, quality, format) { + console.log('cave.capture_getLowerImageDetail(' + [width, height, quality, format].join(', ') + ')'); - }, - /** + }, + /** * More advanced version of capture_getUpperImageLeft that allows for customisation. * @param width * @param height * @param quality * @param format */ - capture_getUpperImageLeftDetail: function (width, height, quality, format) { - console.log('cave.capture_getUpperImageLeftDetail(' + + capture_getUpperImageLeftDetail: function (width, height, quality, format) { + console.log('cave.capture_getUpperImageLeftDetail(' + [width, height, quality, format].join(', ') + ')'); - }, - /** + }, + /** * More advanced version of capture_getUpperImageRight that allows for customisation. * @param width * @param height * @param quality * @param format */ - capture_getUpperImageRightDetail: function (width, height, quality, format) { - console.log('cave.capture_getUpperImageRightDetail(' + + capture_getUpperImageRightDetail: function (width, height, quality, format) { + console.log('cave.capture_getUpperImageRightDetail(' + [width, height, quality, format].join(', ') + ')'); - }, - /** + }, + /** * More advanced version of capture_getUpperImage3D that allows for customisation. * @param width * @param height * @param quality * @param format */ - capture_getUpperImage3DDetail: function (width, height, quality, format) { - console.log('cave.capture_getUpperImage3DDetail(' + + capture_getUpperImage3DDetail: function (width, height, quality, format) { + console.log('cave.capture_getUpperImage3DDetail(' + [width, height, quality, format].join(', ') + ')'); - }, - /** + }, + /** * Unknown */ - capture_notifyUpdatedLocalList: function () { - console.log('cave.capture_notifyUpdatedLocalList()'); - }, - /** + capture_notifyUpdatedLocalList: function () { + console.log('cave.capture_notifyUpdatedLocalList()'); + }, + /** * Check if the suspended software allows screenshots. * @returns {boolean} */ - capture_isEnabled: function () { - console.log('cave.capture_isEnabled()'); - return false; - }, - /** + capture_isEnabled: function () { + console.log('cave.capture_isEnabled()'); + return false; + }, + /** * Same as capture_isEnabled, but instead checks the capture status of a single screen. Set screenIndex to 0 to check the top screen, or 1 to check the lower screen. * @param screen * @returns {boolean|boolean} */ - capture_isEnabledEx: function (screen) { - console.log('cave.capture_isEnabledEx(' + screen + ')'); + capture_isEnabledEx: function (screen) { + console.log('cave.capture_isEnabledEx(' + screen + ')'); - // テスト等でスクショの有無を固定したい場合にクッキーで指定できるようにする, 実機ではこの挙動は存在しない - // If you want to fix the presence or absence of screenshots in tests, etc., make it possible to specify with cookies, this behavior does not exist on the actual device - var force = Olv.Cookie.get('force_screenshot_for_test'); - if (force === 'true') return true; - if (force === 'false') return false; + // テスト等でスクショの有無を固定したい場合にクッキーで指定できるようにする, 実機ではこの挙動は存在しない + // If you want to fix the presence or absence of screenshots in tests, etc., make it possible to specify with cookies, this behavior does not exist on the actual device + const force = Olv.Cookie.get('force_screenshot_for_test'); + if (force === 'true') { + return true; + } + if (force === 'false') { + return false; + } - return Math.random() >= 0.5 ? true : false; - }, + return Math.random() >= 0.5 ? true : false; + }, - boss_regist: function (interval_hour) { - console.log('cave.boss_regist(' + interval_hour + ')'); - return true; - }, - boss_registEx: function (interval_hour, count) { - console.log('cave.boss_regist(' + interval_hour + ',' + count + ')'); - return true; - }, + boss_regist: function (interval_hour) { + console.log('cave.boss_regist(' + interval_hour + ')'); + return true; + }, + boss_registEx: function (interval_hour, count) { + console.log('cave.boss_regist(' + interval_hour + ',' + count + ')'); + return true; + }, - boss_unregist: function () { - console.log('cave.boss_unregist()'); - }, + boss_unregist: function () { + console.log('cave.boss_unregist()'); + }, - boss_isRegisted: function () { - console.log('cave.boss_isRegisted()'); - return true; - }, + boss_isRegisted: function () { + console.log('cave.boss_isRegisted()'); + return true; + }, - boss_clearNewArrival: function () { - console.log('cave.boss_clearNewArrival()'); - }, - lls_clear: function () { - console.log('cave.lls_clear()'); - localStorage.clear(); - }, - lls_setCaptureImage: function (key, value) { - console.log('cave.setCaptureImage(' + [key, value].join(', ') + ')'); - localStorage.setItem(key, value); - return 0; - }, - lls_getPath: function (key) { - console.log('cave.getPath(' + key + ')'); - var value = localStorage.getItem(key); - if (value == 0) { - return '/img/dummy-image/screenshot-dummy-3ds-low.jpeg'; - } else { - return '/img/dummy-image/screenshot-dummy-3ds-upper.jpeg'; - } - }, - /** + boss_clearNewArrival: function () { + console.log('cave.boss_clearNewArrival()'); + }, + lls_clear: function () { + console.log('cave.lls_clear()'); + localStorage.clear(); + }, + lls_setCaptureImage: function (key, value) { + console.log('cave.setCaptureImage(' + [key, value].join(', ') + ')'); + localStorage.setItem(key, value); + return 0; + }, + lls_getPath: function (key) { + console.log('cave.getPath(' + key + ')'); + const value = localStorage.getItem(key); + if (value == 0) { + return '/img/dummy-image/screenshot-dummy-3ds-low.jpeg'; + } else { + return '/img/dummy-image/screenshot-dummy-3ds-upper.jpeg'; + } + }, + /** * Fetches history from previoys * @returns {string} */ - history_getPrev: function () { - console.log('cave.history_getPrev()'); - // 直前の履歴項目の URL を返す API だが、 - // とりあえずは referrer で代用しておく。 - // An API that returns the URL of the previous history item, but - // Substitute with referrer for the time being. - return document.referrer; - }, - /** + history_getPrev: function () { + console.log('cave.history_getPrev()'); + // 直前の履歴項目の URL を返す API だが、 + // とりあえずは referrer で代用しておく。 + // An API that returns the URL of the previous history item, but + // Substitute with referrer for the time being. + return document.referrer; + }, + /** * Removes previous entry from history */ - history_removePrev: function () { - console.log('cave.history_removePrev()'); - }, - /** + history_removePrev: function () { + console.log('cave.history_removePrev()'); + }, + /** * Fetches URL at provided index * @param backIndex * @returns {string} */ - history_getAt: function (backIndex) { - console.log('cave.history_getAt(' + backIndex + ')'); - return ''; - }, - /** + history_getAt: function (backIndex) { + console.log('cave.history_getAt(' + backIndex + ')'); + return ''; + }, + /** * Removes history at provided index * @param backIndex */ - history_removeAt: function (backIndex) { - console.log('cave.history_removeAt(' + backIndex + ')'); - }, - /** + history_removeAt: function (backIndex) { + console.log('cave.history_removeAt(' + backIndex + ')'); + }, + /** * Determines if browser can navigate back * @returns {number} */ - history_getBackCount: function () { - console.log('cave.history_getBackCount()'); - // 戻ることのできるページ数を返すAPIだが、 - // とりあえずは referrer で代用しておく。 - // API that returns the number of pages that can be returned, - // Substitute with referrer for the time being. - return !!document.referrer ? 1 : 0; - }, + history_getBackCount: function () { + console.log('cave.history_getBackCount()'); + // 戻ることのできるページ数を返すAPIだが、 + // とりあえずは referrer で代用しておく。 + // API that returns the number of pages that can be returned, + // Substitute with referrer for the time being. + return document.referrer ? 1 : 0; + }, - toolbar_setMode: function (mode) { - console.log('cave.toolbar_setMode(' + mode + ')'); - }, - toolbar_setWideButtonMessage: function (message) { - console.log('cave.toolbar_setWideButtonMessage(' + message + ')') - }, - jump_existsWebbrs: function () { - console.log('jump_existsWebbrs()'); - return true; - }, - jump_toSystemUpdate: function (type) { - console.log('jump_toSystemUpdate(' + type + ')') - }, - jump_toWebbrs: function (url) { - console.log('cave.jump_toWebbrs(' + url + ')'); - window.location.href = url; - return true; - }, - jump_toShop: function (dialog, titleId) { - console.log('cave.jump_toShop(' + dialog + ', ' + titleId + ')'); - return true; - }, - jump_getYoutubeVersion: function () { - console.log('cave.jump_getYoutubeVersion()'); - return -1; - }, - jump_suspendedToutube: function (query) { - console.log('cave.jump_suspendedToutube(' + query + ')'); - return 1; - }, - jump_toYoutube: function (dialog, query) { - console.log('cave.jump_toYoutube(' + dialog + ', ' + query + ')'); - return 1; - }, - jump_existsApplication: function (titleId) { - console.log('jump_existsApplication(' + titleId + ')'); - return true; - }, - jump_resetParamToApp: function () { - console.log('jump_resetParamToApp'); - }, - jump_canUseQuery: function (titleId) { - console.log('jump_canUseQuery(' + titleId + ')'); - return true; - }, - jump_setModeToApp: function (mode) { - console.log('jump_setModeToApp(' + mode + ')'); - }, - jump_setDataUTF8ToApp: function (type, data) { - console.log('jump_setDataUTF8ToApp(' + type + ', ' + data + ')'); - }, - jump_setNumberDataToApp: function (type, data) { - console.log('jump_setNumberDataToApp(' + type + ', ' + data + ')'); - }, - jump_setBase64DataToApp: function (type, data) { - console.log('jump_setBase64DataToApp(' + type + ', ' + data + ')'); - }, - jump_toApplication: function (dialog, titleId) { - console.log('cave.jump_toApplication(' + dialog + ', ' + titleId + ')'); - return true; - }, - convertTimeToString: function (unixTime) { - console.log('cave.convertTimeToString(' + unixTime + ')'); - var date = new Date(unixTime * 1000); - return date.getFullYear() + '-' + this._toDoubleDigits(date.getMonth() + 1) + '-' + this._toDoubleDigits(date.getDate()) + toolbar_setMode: function (mode) { + console.log('cave.toolbar_setMode(' + mode + ')'); + }, + toolbar_setWideButtonMessage: function (message) { + console.log('cave.toolbar_setWideButtonMessage(' + message + ')'); + }, + jump_existsWebbrs: function () { + console.log('jump_existsWebbrs()'); + return true; + }, + jump_toSystemUpdate: function (type) { + console.log('jump_toSystemUpdate(' + type + ')'); + }, + jump_toWebbrs: function (url) { + console.log('cave.jump_toWebbrs(' + url + ')'); + window.location.href = url; + return true; + }, + jump_toShop: function (dialog, titleId) { + console.log('cave.jump_toShop(' + dialog + ', ' + titleId + ')'); + return true; + }, + jump_getYoutubeVersion: function () { + console.log('cave.jump_getYoutubeVersion()'); + return -1; + }, + jump_suspendedToutube: function (query) { + console.log('cave.jump_suspendedToutube(' + query + ')'); + return 1; + }, + jump_toYoutube: function (dialog, query) { + console.log('cave.jump_toYoutube(' + dialog + ', ' + query + ')'); + return 1; + }, + jump_existsApplication: function (titleId) { + console.log('jump_existsApplication(' + titleId + ')'); + return true; + }, + jump_resetParamToApp: function () { + console.log('jump_resetParamToApp'); + }, + jump_canUseQuery: function (titleId) { + console.log('jump_canUseQuery(' + titleId + ')'); + return true; + }, + jump_setModeToApp: function (mode) { + console.log('jump_setModeToApp(' + mode + ')'); + }, + jump_setDataUTF8ToApp: function (type, data) { + console.log('jump_setDataUTF8ToApp(' + type + ', ' + data + ')'); + }, + jump_setNumberDataToApp: function (type, data) { + console.log('jump_setNumberDataToApp(' + type + ', ' + data + ')'); + }, + jump_setBase64DataToApp: function (type, data) { + console.log('jump_setBase64DataToApp(' + type + ', ' + data + ')'); + }, + jump_toApplication: function (dialog, titleId) { + console.log('cave.jump_toApplication(' + dialog + ', ' + titleId + ')'); + return true; + }, + convertTimeToString: function (unixTime) { + console.log('cave.convertTimeToString(' + unixTime + ')'); + const date = new Date(unixTime * 1000); + return date.getFullYear() + '-' + this._toDoubleDigits(date.getMonth() + 1) + '-' + this._toDoubleDigits(date.getDate()) + ' ' + this._toDoubleDigits(date.getHours()) + ':' + this._toDoubleDigits(date.getMinutes()) + ':' + this._toDoubleDigits(date.getSeconds()); - }, - _toDoubleDigits: function (num) { - num += ""; - if (num.length === 1) { - num = "0" + num; - } - return num; - }, - getLocalTimeSeconds: function () { - console.log('cave.getLocalTimeSeconds()'); - return parseInt(new Date().getTime() / 1000); - }, - effect_scrollGuide: function (flag) { - console.log('cave.effect_scrollGuide( ' + flag + ' )'); - }, - effect_setScrollGuideOffsetPos: function (x, y) { - console.log('effect_setScrollGuideOffsetPos( x = ' + x + ', y = ' + ' )'); - }, - // キーボードアプレットに入力した文字列が返り値になるので window.prompt でエミュレートする - // Since the string entered in the keyboard applet will be the return value, emulate it with window.prompt - swkbd_callFullKeyboard: function (text, maxLength, minLength, isMonospace, isMultiline, isConvertible) { - console.log('cave.callFullKeyboard( ' + Array.prototype.join.call(arguments, ', ') + ' )'); - return window.prompt('cave.callFullKeyboard', text); - }, - swkbd_callFullKeyboardWithGuide: function (text, maxLength, minLength, isMonospace, isMultiline, isConvertible, guide) { - console.log('cave.callFullKeyboardWithGuide( ' + Array.prototype.join.call(arguments, ', ') + ' )'); - return window.prompt('cave.callFullKeyboardWithGuide [' + guide + ']', text); - }, - viewer_setOnCloseCallback: function (callback) { - console.log('cave.viewer_setOnCloseCallback(callback = ' + typeof callback + ')'); - }, - home_setEnabled: function (flag) { - console.log('cave.home_setEnabled', flag); - }, - }; + }, + _toDoubleDigits: function (num) { + num += ''; + if (num.length === 1) { + num = '0' + num; + } + return num; + }, + getLocalTimeSeconds: function () { + console.log('cave.getLocalTimeSeconds()'); + return parseInt(new Date().getTime() / 1000); + }, + effect_scrollGuide: function (flag) { + console.log('cave.effect_scrollGuide( ' + flag + ' )'); + }, + effect_setScrollGuideOffsetPos: function (x, y) { + console.log('effect_setScrollGuideOffsetPos( x = ' + x + ', y = ' + ' )'); + }, + // キーボードアプレットに入力した文字列が返り値になるので window.prompt でエミュレートする + // Since the string entered in the keyboard applet will be the return value, emulate it with window.prompt + swkbd_callFullKeyboard: function (text, maxLength, minLength, isMonospace, isMultiline, isConvertible) { + console.log('cave.callFullKeyboard( ' + Array.prototype.join.call(arguments, ', ') + ' )'); + return window.prompt('cave.callFullKeyboard', text); + }, + swkbd_callFullKeyboardWithGuide: function (text, maxLength, minLength, isMonospace, isMultiline, isConvertible, guide) { + console.log('cave.callFullKeyboardWithGuide( ' + Array.prototype.join.call(arguments, ', ') + ' )'); + return window.prompt('cave.callFullKeyboardWithGuide [' + guide + ']', text); + }, + viewer_setOnCloseCallback: function (callback) { + console.log('cave.viewer_setOnCloseCallback(callback = ' + typeof callback + ')'); + }, + home_setEnabled: function (flag) { + console.log('cave.home_setEnabled', flag); + }, + }; } \ No newline at end of file diff --git a/src/webfiles/ctr/js/pjax.js b/src/webfiles/ctr/js/pjax.js index 1cfc024..4f0dec2 100644 --- a/src/webfiles/ctr/js/pjax.js +++ b/src/webfiles/ctr/js/pjax.js @@ -1,84 +1,94 @@ var Pjax = { - elements: null, - selectors: null, - href: null, - history: [], - events: { - PjaxRequest: document.createEvent('Event'), - PjaxLoaded: document.createEvent('Event'), - PjaxDone: document.createEvent('Event') - }, - init: function(init) { - this.elements = init.elements; - this.selectors = init.selectors; - this.href = document.location.href; + elements: null, + selectors: null, + href: null, + history: [], + events: { + PjaxRequest: document.createEvent('Event'), + PjaxLoaded: document.createEvent('Event'), + PjaxDone: document.createEvent('Event') + }, + init: function(init) { + this.elements = init.elements; + this.selectors = init.selectors; + this.href = document.location.href; - this.events.PjaxRequest.initEvent('PjaxRequest', true, true); - this.events.PjaxLoaded.initEvent('PjaxLoaded', true, true); - this.events.PjaxDone.initEvent('PjaxDone', true, true); + this.events.PjaxRequest.initEvent('PjaxRequest', true, true); + this.events.PjaxLoaded.initEvent('PjaxLoaded', true, true); + this.events.PjaxDone.initEvent('PjaxDone', true, true); - return this; - }, - refresh: function() { - var els = document.querySelectorAll(this.elements); - if (!els) return; - console.log(this.elements); - console.log(els); - for (var i = 0; i < els.length; i++) { - els[i].addEventListener("click", function (e) { pageWrapper(e, this) }); - } - }, - loadUrl: function (url, push) { - if(!this.elements || !this.selectors) return; - document.dispatchEvent(Pjax.events.PjaxRequest); - this.get(url, this.parseDom); - if(!push && Pjax.href.indexOf(url) === -1) - Pjax.history.push(Pjax.href); - console.log(url) - }, - get: function(url, callback) { - var xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if(this.readyState === 4) { - document.dispatchEvent(Pjax.events.PjaxLoaded); - this.responseURL = url; - return callback(this); - } - }; - xhttp.open("GET", url, true); - xhttp.send(); - }, - parseDom: function(data) { - var response = data.responseText; - if(response && data.status === 200) { - var html = document.implementation.createHTMLDocument(''); - html.documentElement.innerHTML = response; - for(var i = 0; i < Pjax.selectors.length; i++) { - var newElement = html.querySelector(Pjax.selectors[i]); - var oldElement = document.querySelector(Pjax.selectors[i]); - if(!newElement || !oldElement) continue; - oldElement.outerHTML = newElement.outerHTML; - } - console.log(data); - Pjax.refresh(); - Pjax.href = data.responseURL; - document.dispatchEvent(Pjax.events.PjaxDone); - } - }, - canGoBack: function() { - return this.history.length >= 1; - }, - back: function() { - if(!this.canGoBack()) - return; - var url = this.history.pop(); - this.loadUrl(url, true); + return this; + }, + refresh: function() { + const els = document.querySelectorAll(this.elements); + if (!els) { + return; + } + console.log(this.elements); + console.log(els); + for (let i = 0; i < els.length; i++) { + els[i].addEventListener('click', function (e) { + pageWrapper(e, this); + }); + } + }, + loadUrl: function (url, push) { + if (!this.elements || !this.selectors) { + return; + } + document.dispatchEvent(Pjax.events.PjaxRequest); + this.get(url, this.parseDom); + if (!push && Pjax.href.indexOf(url) === -1) { + Pjax.history.push(Pjax.href); + } + console.log(url); + }, + get: function(url, callback) { + const xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState === 4) { + document.dispatchEvent(Pjax.events.PjaxLoaded); + this.responseURL = url; + return callback(this); + } + }; + xhttp.open('GET', url, true); + xhttp.send(); + }, + parseDom: function(data) { + const response = data.responseText; + if (response && data.status === 200) { + const html = document.implementation.createHTMLDocument(''); + html.documentElement.innerHTML = response; + for (let i = 0; i < Pjax.selectors.length; i++) { + const newElement = html.querySelector(Pjax.selectors[i]); + const oldElement = document.querySelector(Pjax.selectors[i]); + if (!newElement || !oldElement) { + continue; + } + oldElement.outerHTML = newElement.outerHTML; + } + console.log(data); + Pjax.refresh(); + Pjax.href = data.responseURL; + document.dispatchEvent(Pjax.events.PjaxDone); + } + }, + canGoBack: function() { + return this.history.length >= 1; + }, + back: function() { + if (!this.canGoBack()) { + return; + } + const url = this.history.pop(); + this.loadUrl(url, true); - } -} + } +}; function pageWrapper(e, element) { - e.preventDefault(); - Pjax.loadUrl(element.href); - return false; + e.preventDefault(); + Pjax.loadUrl(element.href); + return false; } \ No newline at end of file diff --git a/src/webfiles/portal/js/debug.js b/src/webfiles/portal/js/debug.js index 8412231..9c1ef2a 100644 --- a/src/webfiles/portal/js/debug.js +++ b/src/webfiles/portal/js/debug.js @@ -1,311 +1,325 @@ /*Debugging*/ if (typeof wiiu === 'undefined') { - window.wiiu = { - gamepad: { - update: function () { - return true; - }, - hold: 0 - } - }; + window.wiiu = { + gamepad: { + update: function () { + return true; + }, + hold: 0 + } + }; } // ローカル開発用に 各独自 API をエミュレート // Emulate each proprietary API for local development if (typeof wiiuBrowser === 'undefined') { - window.wiiuBrowser = { - closeApplication: function () { - alert('アプリケーションは終了しました'); - }, - showLoadingIcon: function (show) { - console.debug((show ? 'Show' : 'Hide') + ' loading icon'); - }, - endStartUp: function () { - console.debug('endStartUp'); - }, - lockUserOperation: function (lock) { - console.debug((lock ? 'Lock' : 'Unlock') + ' user operations'); - }, - lockHomeButtonMenu: function (lock) { - console.debug((lock ? 'Lock' : 'Unlock') + ' home button menu'); - }, - canHistoryBack: function () { - // とりあえずトップページなら履歴を戻れないということにしておく。 - return location.pathname !== '/'; - }, - setSwkbdDictionary: function (jsonString) { - console.debug('Set SWKBD dictionary', JSON.parse(jsonString)); - return (Math.random() < 0.5) - ? {} : { error: { code: 111222, message: 'Test Error Message' } }; - }, - openTvAreaSetting: function () { - alert('Tv側の安全フレーム設定画面を開きました'); - }, - jumpToBrowser: function (url) { - console.debug(url); - }, - jumpToEshop: function (query) { - console.debug(query); - }, - jumpToApplication: function(titleId, flags, communityId, appData, postId) { - console.debug(titleId); - } - }; + window.wiiuBrowser = { + closeApplication: function () { + alert('アプリケーションは終了しました'); + }, + showLoadingIcon: function (show) { + console.debug((show ? 'Show' : 'Hide') + ' loading icon'); + }, + endStartUp: function () { + console.debug('endStartUp'); + }, + lockUserOperation: function (lock) { + console.debug((lock ? 'Lock' : 'Unlock') + ' user operations'); + }, + lockHomeButtonMenu: function (lock) { + console.debug((lock ? 'Lock' : 'Unlock') + ' home button menu'); + }, + canHistoryBack: function () { + // とりあえずトップページなら履歴を戻れないということにしておく。 + return location.pathname !== '/'; + }, + setSwkbdDictionary: function (jsonString) { + console.debug('Set SWKBD dictionary', JSON.parse(jsonString)); + return (Math.random() < 0.5) + ? {} : { error: { code: 111222, message: 'Test Error Message' } }; + }, + openTvAreaSetting: function () { + alert('Tv側の安全フレーム設定画面を開きました'); + }, + jumpToBrowser: function (url) { + console.debug(url); + }, + jumpToEshop: function (query) { + console.debug(query); + }, + jumpToApplication: function(titleId, flags, communityId, appData, postId) { + console.debug(titleId); + } + }; } if (typeof wiiuDialog === 'undefined') { - window.wiiuDialog = { - alert: function (msg, btnStr) { - alert(msg + "\n\n[ " + btnStr + " ]"); - }, - confirm: function (msg, lBtnStr, rBtnStr) { - return confirm(msg + "\n\n[ " + lBtnStr + " (Cancel) ] [ " + rBtnStr + " (OK) ]"); - } - }; + window.wiiuDialog = { + alert: function (msg, btnStr) { + alert(msg + '\n\n[ ' + btnStr + ' ]'); + }, + confirm: function (msg, lBtnStr, rBtnStr) { + return confirm(msg + '\n\n[ ' + lBtnStr + ' (Cancel) ] [ ' + rBtnStr + ' (OK) ]'); + } + }; } if (typeof wiiuSound === 'undefined') { - window.wiiuSound = { - playSound: function (soundId, device) { - this.playSoundByName('id ' + soundId, device); - }, - playSoundByName: function (name, device) { - console.debug('Play sound ' + name); - } - }; + window.wiiuSound = { + playSound: function (soundId, device) { + this.playSoundByName('id ' + soundId, device); + }, + playSoundByName: function (name, device) { + console.debug('Play sound ' + name); + } + }; } if (typeof wiiuDevice === 'undefined') { - window.wiiuDevice = { - isDrc: function () { - return true; - }, - getSKU: function () { - return Math.random() < 7 / 8 - ? { amount: 31205621760 } - : { error: { code: 1112222, message: "Test Error Message" } }; - }, - existsTitle: function () { - return Math.random() < 1 / 2; - } - }; + window.wiiuDevice = { + isDrc: function () { + return true; + }, + getSKU: function () { + return Math.random() < 7 / 8 + ? { amount: 31205621760 } + : { error: { code: 1112222, message: 'Test Error Message' } }; + }, + existsTitle: function () { + return Math.random() < 1 / 2; + } + }; } if (typeof wiiuSystemSetting === 'undefined') { - window.wiiuSystemSetting = { - checkParentalPinCode: function (code) { - console.debug(code); - return Math.random() < 0.5 ? {result: true} :{ - error : { - code : 1112222, - message : "Test Error Message" - } - }; - }, - getRegion: function () { - return Math.random() < 0.5 ? { - code: 'JPN' - } : { - error: { - code : 1112222, - message : "Test Error Message" - } - }; - }, - getCountry: function () { - return Math.random() < 0.5 ? { - code: 'JP' - } : { - error: { - code : 1112222, - message : "Test Error Message" - } - }; - }, - getLanguage: function () { - return Math.random() < 0.5 ? { - code: 'ja' - } : { - error: { - code : 1112222, - message : "Test Error Message" - } - }; - }, - getSpotPassUploadConsoleInfoState: function () { - return { enable: Math.random() < 0.5 }; - }, - }; + window.wiiuSystemSetting = { + checkParentalPinCode: function (code) { + console.debug(code); + return Math.random() < 0.5 ? {result: true} :{ + error : { + code : 1112222, + message : 'Test Error Message' + } + }; + }, + getRegion: function () { + return Math.random() < 0.5 ? { + code: 'JPN' + } : { + error: { + code : 1112222, + message : 'Test Error Message' + } + }; + }, + getCountry: function () { + return Math.random() < 0.5 ? { + code: 'JP' + } : { + error: { + code : 1112222, + message : 'Test Error Message' + } + }; + }, + getLanguage: function () { + return Math.random() < 0.5 ? { + code: 'ja' + } : { + error: { + code : 1112222, + message : 'Test Error Message' + } + }; + }, + getSpotPassUploadConsoleInfoState: function () { + return { enable: Math.random() < 0.5 }; + }, + }; } if (typeof wiiuBOSS === 'undefined') { - window.wiiuBOSS = { - isRegisteredBossTask: function () { - console.debug('wiiuBOSS.isRegisteredBossTask'); - var result = { - "isRegistered" : true - }; - return result; - }, - registerBossTask: function (languageCode) { - console.debug('wiiuBOSS.registerBossTask'); - //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; - return {}; - }, - unregisterBossTask: function () { - console.debug('wiiuBOSS.unregisterBossTask'); - //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; - return {}; - }, - isRegisteredDirectMessageTask: function () { - console.debug('wiiuBOSS.isRegisteredDirectMessageTask'); - var result = { - "isRegistered" : true - }; - return result; - }, - registerDirectMessageTask: function (languageCode) { - console.debug('wiiuBOSS.registerDirectMessageTask'); - //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; - return {}; - }, - registerDirectMessageTaskEx: function (lifeTime, interval) { - console.debug('wiiuBOSS.registerDirectMessageTaskEx'); - //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; - return {}; - }, - unregisterDirectMessageTask: function () { - console.debug('wiiuBOSS.unregisterDirectMessageTask'); - //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; - return {}; - }, - }; + window.wiiuBOSS = { + isRegisteredBossTask: function () { + console.debug('wiiuBOSS.isRegisteredBossTask'); + const result = { + 'isRegistered' : true + }; + return result; + }, + registerBossTask: function (languageCode) { + console.debug('wiiuBOSS.registerBossTask'); + //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; + return {}; + }, + unregisterBossTask: function () { + console.debug('wiiuBOSS.unregisterBossTask'); + //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; + return {}; + }, + isRegisteredDirectMessageTask: function () { + console.debug('wiiuBOSS.isRegisteredDirectMessageTask'); + const result = { + 'isRegistered' : true + }; + return result; + }, + registerDirectMessageTask: function (languageCode) { + console.debug('wiiuBOSS.registerDirectMessageTask'); + //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; + return {}; + }, + registerDirectMessageTaskEx: function (lifeTime, interval) { + console.debug('wiiuBOSS.registerDirectMessageTaskEx'); + //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; + return {}; + }, + unregisterDirectMessageTask: function () { + console.debug('wiiuBOSS.unregisterDirectMessageTask'); + //return { "error" : { "code" : 1112222, "message" : "Test Error Message"} }; + return {}; + }, + }; } if (typeof wiiuMainApplication === 'undefined') { - window.wiiuMainApplication = { - _images: [ - // JPEG, 850x450, 暖色 warm color - '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', - // JPEG, 850x450, 寒色 cool - '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', - null - ], - _getImage: function () { - var images = this._images; - var index = Math.floor(Math.random() * images.length); + window.wiiuMainApplication = { + _images: [ + // JPEG, 850x450, 暖色 warm color + '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', + // JPEG, 850x450, 寒色 cool + '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', + null + ], + _getImage: function () { + const images = this._images; + const index = Math.floor(Math.random() * images.length); - // テスト等でスクショの有無を固定したい場合にクッキーで指定できるようにする, 実機ではこの挙動は存在しない - // If you want to fix the presence or absence of screenshots in tests, etc., make it possible to specify with cookies, this behavior does not exist on the actual device - //var force = Olv.Cookie.get('force_screenshot_for_test'); - //if (force === 'true') index = 0; - //if (force === 'false') index = 2; + // テスト等でスクショの有無を固定したい場合にクッキーで指定できるようにする, 実機ではこの挙動は存在しない + // If you want to fix the presence or absence of screenshots in tests, etc., make it possible to specify with cookies, this behavior does not exist on the actual device + //var force = Olv.Cookie.get('force_screenshot_for_test'); + //if (force === 'true') index = 0; + //if (force === 'false') index = 2; - return images[index]; - }, - getScreenShot: function (isTv) { return this._getImage(); }, - getAppData: function () { return null; }, - getExternalImageData: function () { return this._getImage(); }, - getExternalBinaryData: function () { return null; } - }; + return images[index]; + }, + getScreenShot: function (isTv) { + return this._getImage(); + }, + getAppData: function () { + return null; + }, + getExternalImageData: function () { + return this._getImage(); + }, + getExternalBinaryData: function () { + return null; + } + }; } -if (typeof wiiuMemo === "undefined") { - window.wiiuMemo = { - _isFinish: true, - open: function(reset) { - console.debug('wiiuMemo.open(reset = ' + reset + ')'); - this._isFinish = false; - var self = this; - setTimeout(function () { self._isFinish = true; }, 100); - }, - isFinish: function() { - console.debug('wiiuMemo.isFinish()') - console.debug(this._isFinish); - return this._isFinish; - }, - reset: function () { - console.debug('wiiuMemo.reset()'); - }, - getImage: function (isTga) { - console.debug('wiiuMemo.getImage(isTga = ' + isTga + ')'); - if (isTga) { - return 'eJzt3FuO0zAUBuDCE8tgKSyL3YNGqFJpk9aX41v8fdK8MHXiy/HvZCR6u32/3f369vv288cfAAAAAAAAAAAAAAAAAAAAAABgK7fbbXQXALr7yr77D8BO5B+wI9kH7Ej2ATuSfcBuHnNP9gE7eM492QekOsqPFXJkxT4D83iXfTPm4ox9AtZVmoEtc2iFLAauLTobZ8tZgFwyD+CVnAMAAAAAAAAAAAAAAAAAAAAAACCH758CduQ7+ICdrZyBq/YbmMOq+ec7pIEIq+aH7AMAAAAAAAAAAAAAAAAAAAAAgFjP32929gPUs6/GS808awVx7KuxSnPPGvFFfdQzb308z21O3Vojno0+J69Uh/ZXW0dzmzrfznaOjHxfKLnu7HVrn7VxNKc12Wdd0qz6bpjSv5SxlIw7Zb5Ks2+VuV+tXmaSegZ/mt+azNxZ7jPR6LksfSdI/WzqWEvm7Yr5dzdTrdT2oVe/c2slp8ZGr8HsSvfvqHmN6lPq+ZpzFreYt1Xrd3StjDh3Sta8tE5KapT/ldRIZF1H3TsiW1plWU0fW9Zur/0wKgdr79m6lt7do2ZP1I5jFy3yq6ZtzjqenW8lfUnpQ23WlrZtVbsj9kTvDIys7dzPl/y8u04N+fcqek4i8yDlTEy5Vu0Yju7TYrwpbaKN3BM75F/q51LruYb8e9ViTqL29dF1ctvUjCt3DKXjftemdc322BMlZ1yr+/doX3Mefvp9rzHsoNdzRUpW5VwrN4+ixpFzz5Sxn7XPuXet6HukZl2v7HvuU6/2OeMrOdNzxyL/Xo04+0vuv3r+lTwHrJINR9eZKfue+9azfUnN5l6zpB1956P2+ab0LO2Vf2efi8jAHiLzYdQYUvv36ffRmX3Wtua6Jf2baT1mMOL54uh+NfkS8dnIa0XvmV5q5mu2rDuTcj61ysDce0Vf82z8O2s9Hzlrm9KXmhyqEXU2zywq/2b2Lg9aZ99RH6LrRAbmia752rV997mSGmlRVyVtVqi16H09o3c1W9q+ti8tfdqjuyudi5bn5Fnb0mtGn9lX1er5Yybq4J+rjKNW1PNf6+f453+PuObZM+antlcW+VwzaxZG1fzKrjKOWqPeDXOvG/3OkZvb6iVfq7Mxsl8j2o82yzqMtsocjMjAd23I0/I9obY/I9qPNHruZ7LSHLRat9TrrjRXM5shA3fOvy+r9z/KivOw6p7hlec/RltxLVfcM8wjMv/Uw9qs42fm6Foi1lM9XIO9ncb8XIea504tsBPvrjxSC+xGvfNIBgK7kn/AzmQgR9QDu/C3YR6pBXYjA7lTC+zI/5HmTh2wKzmItWdno7+rg/GsN7uTgcDu5CCwOxkI7MyzILA7GQjsTAYCO5N9AAAAAAAAAAAAAAAAAAAAAAAAAKzoL3j1Foo='; - } else { - return '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'; - } - } - }; +if (typeof wiiuMemo === 'undefined') { + window.wiiuMemo = { + _isFinish: true, + open: function(reset) { + console.debug('wiiuMemo.open(reset = ' + reset + ')'); + this._isFinish = false; + const self = this; + setTimeout(function () { + self._isFinish = true; + }, 100); + }, + isFinish: function() { + console.debug('wiiuMemo.isFinish()'); + console.debug(this._isFinish); + return this._isFinish; + }, + reset: function () { + console.debug('wiiuMemo.reset()'); + }, + getImage: function (isTga) { + console.debug('wiiuMemo.getImage(isTga = ' + isTga + ')'); + if (isTga) { + return 'eJzt3FuO0zAUBuDCE8tgKSyL3YNGqFJpk9aX41v8fdK8MHXiy/HvZCR6u32/3f369vv288cfAAAAAAAAAAAAAAAAAAAAAABgK7fbbXQXALr7yr77D8BO5B+wI9kH7Ej2ATuSfcBuHnNP9gE7eM492QekOsqPFXJkxT4D83iXfTPm4ox9AtZVmoEtc2iFLAauLTobZ8tZgFwyD+CVnAMAAAAAAAAAAAAAAAAAAAAAACCH758CduQ7+ICdrZyBq/YbmMOq+ec7pIEIq+aH7AMAAAAAAAAAAAAAAAAAAAAAgFjP32929gPUs6/GS808awVx7KuxSnPPGvFFfdQzb308z21O3Vojno0+J69Uh/ZXW0dzmzrfznaOjHxfKLnu7HVrn7VxNKc12Wdd0qz6bpjSv5SxlIw7Zb5Ks2+VuV+tXmaSegZ/mt+azNxZ7jPR6LksfSdI/WzqWEvm7Yr5dzdTrdT2oVe/c2slp8ZGr8HsSvfvqHmN6lPq+ZpzFreYt1Xrd3StjDh3Sta8tE5KapT/ldRIZF1H3TsiW1plWU0fW9Zur/0wKgdr79m6lt7do2ZP1I5jFy3yq6ZtzjqenW8lfUnpQ23WlrZtVbsj9kTvDIys7dzPl/y8u04N+fcqek4i8yDlTEy5Vu0Yju7TYrwpbaKN3BM75F/q51LruYb8e9ViTqL29dF1ctvUjCt3DKXjftemdc322BMlZ1yr+/doX3Mefvp9rzHsoNdzRUpW5VwrN4+ixpFzz5Sxn7XPuXet6HukZl2v7HvuU6/2OeMrOdNzxyL/Xo04+0vuv3r+lTwHrJINR9eZKfue+9azfUnN5l6zpB1956P2+ab0LO2Vf2efi8jAHiLzYdQYUvv36ffRmX3Wtua6Jf2baT1mMOL54uh+NfkS8dnIa0XvmV5q5mu2rDuTcj61ysDce0Vf82z8O2s9Hzlrm9KXmhyqEXU2zywq/2b2Lg9aZ99RH6LrRAbmia752rV997mSGmlRVyVtVqi16H09o3c1W9q+ti8tfdqjuyudi5bn5Fnb0mtGn9lX1er5Yybq4J+rjKNW1PNf6+f453+PuObZM+antlcW+VwzaxZG1fzKrjKOWqPeDXOvG/3OkZvb6iVfq7Mxsl8j2o82yzqMtsocjMjAd23I0/I9obY/I9qPNHruZ7LSHLRat9TrrjRXM5shA3fOvy+r9z/KivOw6p7hlec/RltxLVfcM8wjMv/Uw9qs42fm6Foi1lM9XIO9ncb8XIea504tsBPvrjxSC+xGvfNIBgK7kn/AzmQgR9QDu/C3YR6pBXYjA7lTC+zI/5HmTh2wKzmItWdno7+rg/GsN7uTgcDu5CCwOxkI7MyzILA7GQjsTAYCO5N9AAAAAAAAAAAAAAAAAAAAAAAAAKzoL3j1Foo='; + } else { + return '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'; + } + } + }; } -if (typeof wiiuPDM === "undefined") { - window.wiiuPDM = { - getTotalPlayTime: function(titleID) { - console.debug('wiiuPDM.getTotalPlayTime(' + titleID + ')'); - return { minutes : 0 }; - }, - getTitlesFilteredByPlayTime: function(minutes) { - console.debug('wiiuPDM.getTitlesFilteredByPlayTime(' + minutes + ')'); - return { IDs : [] }; - } - } +if (typeof wiiuPDM === 'undefined') { + window.wiiuPDM = { + getTotalPlayTime: function(titleID) { + console.debug('wiiuPDM.getTotalPlayTime(' + titleID + ')'); + return { minutes : 0 }; + }, + getTitlesFilteredByPlayTime: function(minutes) { + console.debug('wiiuPDM.getTitlesFilteredByPlayTime(' + minutes + ')'); + return { IDs : [] }; + } + }; } if (typeof wiiuErrorViewer === 'undefined') { - window.wiiuErrorViewer = { - openByCode: function (errorCode) { - var message = 'Message for error code ' + errorCode; - this.openByCodeAndMessage(errorCode, message); - }, - openByCodeAndMessage: function (errorCode, errorMessage) { - alert(errorCode + '\n\n' + errorMessage); - } - }; + window.wiiuErrorViewer = { + openByCode: function (errorCode) { + const message = 'Message for error code ' + errorCode; + this.openByCodeAndMessage(errorCode, message); + }, + openByCodeAndMessage: function (errorCode, errorMessage) { + alert(errorCode + '\n\n' + errorMessage); + } + }; } if (typeof wiiuFilter === 'undefined') { - window.wiiuFilter = { - checkWord: function (message) { - console.debug('Check words for message: ' + message); - if (/[0-9]{6}/.test(message)) return -1; - if (/ng word/i.test(message)) return -2; - return 0; - }, - }; + window.wiiuFilter = { + checkWord: function (message) { + console.debug('Check words for message: ' + message); + if (/[0-9]{6}/.test(message)) { + return -1; + } + if (/ng word/i.test(message)) { + return -2; + } + return 0; + }, + }; } if (typeof wiiuSessionStorage === 'undefined') { - window.wiiuSessionStorage = { - getItem: function (key) { - return sessionStorage.getItem(key); - }, - setItem: function (key, value) { - sessionStorage.setItem(key, value); - return true; - }, - removeItem: function (key) { - sessionStorage.removeItem(key); - }, - clear: function () { - sessionStorage.clear(); - }, - key: function (index) { - return sessionStorage.key(index); - }, - length: function () { - return sessionStorage.length; - }, - }; + window.wiiuSessionStorage = { + getItem: function (key) { + return sessionStorage.getItem(key); + }, + setItem: function (key, value) { + sessionStorage.setItem(key, value); + return true; + }, + removeItem: function (key) { + sessionStorage.removeItem(key); + }, + clear: function () { + sessionStorage.clear(); + }, + key: function (index) { + return sessionStorage.key(index); + }, + length: function () { + return sessionStorage.length; + }, + }; } if (typeof wiiuLocalStorage === 'undefined') { - window.wiiuLocalStorage = { - getItem: function (key) { - return localStorage.getItem(key); - }, - setItem: function (key, value) { - localStorage.setItem(key, value); - return true; - }, - removeItem: function (key) { - localStorage.removeItem(key); - }, - clear: function () { - localStorage.clear(); - }, - key: function (index) { - return localStorage.key(index); - }, - length: function () { - return localStorage.length; - }, - write: function () {}, - }; + window.wiiuLocalStorage = { + getItem: function (key) { + return localStorage.getItem(key); + }, + setItem: function (key, value) { + localStorage.setItem(key, value); + return true; + }, + removeItem: function (key) { + localStorage.removeItem(key); + }, + clear: function () { + localStorage.clear(); + }, + key: function (index) { + return localStorage.key(index); + }, + length: function () { + return localStorage.length; + }, + write: function () {}, + }; } \ No newline at end of file diff --git a/src/webfiles/web/css/first_run.css b/src/webfiles/web/css/first_run.css new file mode 100644 index 0000000..9353860 --- /dev/null +++ b/src/webfiles/web/css/first_run.css @@ -0,0 +1,67 @@ +.account-form-wrapper > div { + display: none; + flex-direction: column; + max-width: 700px; + padding: 1em; +} +.account-form-wrapper .logotype { + position: relative; + margin: 1em 1em 0 1em; + left: 0; +} +.account-form-wrapper > div .button-wrapper { + display: flex; +} +.button-wrapper > *:last-child, button.about-button { + background: #673DB6; +} +input[type="button"], input[type="button"], button { + display: block; + border: none; + border-radius: 4px; + font-family: Poppins, Arial, Helvetica, sans-serif; + font-size: 1rem; + color: var(--text); + padding: 12px 30px; + background: #353C6A; + margin-top: 18px; + cursor: pointer; + width: 100%; + text-decoration: none; + margin: 1em; +} +h1 { + margin: 5px 0; +} +.horizontal-list { + display: flex; + list-style: none; + justify-content: center; + flex-wrap: wrap; + background: #353C6A; + border-radius: 100px; + padding: 5px; +} +.horizontal-list > li { + min-width: 185px; + padding: 1em; + margin: 5px; + text-align: center; + border-radius: 25px; + cursor: pointer; +} +.horizontal-list > li > a { + color: white; + text-decoration: none; +} +.horizontal-list > li.selected { + background: #673DB6; +} +@media screen and (max-width:730px) { + .horizontal-list { + border-radius: 25px; + } + .horizontal-list > li { + width: 100%; + } +} \ No newline at end of file diff --git a/src/webfiles/web/first_run.ejs b/src/webfiles/web/first_run.ejs index 65e0cfb..6c52a1a 100644 --- a/src/webfiles/web/first_run.ejs +++ b/src/webfiles/web/first_run.ejs @@ -42,11 +42,49 @@ + + + + +
- - +
diff --git a/src/webfiles/web/js/web.js b/src/webfiles/web/js/web.js index 64fceef..badd5e6 100644 --- a/src/webfiles/web/js/web.js +++ b/src/webfiles/web/js/web.js @@ -3,315 +3,333 @@ setInterval(checkForUpdates, 30000); /* global Pjax */ function initNavBar() { - let els = document.querySelectorAll("#nav-menu > li"); - if (!els) return; - for (let i = 0; i < els.length; i++) { - els[i].addEventListener("click", function(e) { - let el = e.currentTarget; - for(let i = 0; i < els.length; i++) { - if(els[i].classList.contains('selected')) - els[i].classList.remove('selected'); - } - el.classList.add("selected"); - }); - } + const els = document.querySelectorAll('#nav-menu > li'); + if (!els) { + return; + } + for (let i = 0; i < els.length; i++) { + els[i].addEventListener('click', function(e) { + const el = e.currentTarget; + for (let i = 0; i < els.length; i++) { + if (els[i].classList.contains('selected')) { + els[i].classList.remove('selected'); + } + } + el.classList.add('selected'); + }); + } } function initYeah() { - let els = document.querySelectorAll("span[data-post]"); - if (!els) return; - for (let i = 0; i < els.length; i++) { - els[i].removeEventListener('click', yeah); - els[i].addEventListener("click", yeah); - } - function yeah(e) { - let el = e.currentTarget, id = el.getAttribute("data-post"); - let parent = document.getElementById(id); - let count = document.getElementById("count-" + id); - el.disabled = true; - let params = "postID=" + id; - if(el.classList.contains('selected')) { - el.classList.remove('selected'); - parent.classList.remove('yeah'); - count.innerText -= 1; + const els = document.querySelectorAll('span[data-post]'); + if (!els) { + return; + } + for (let i = 0; i < els.length; i++) { + els[i].removeEventListener('click', yeah); + els[i].addEventListener('click', yeah); + } + function yeah(e) { + const el = e.currentTarget; const id = el.getAttribute('data-post'); + const parent = document.getElementById(id); + const count = document.getElementById('count-' + id); + el.disabled = true; + const params = 'postID=' + id; + if (el.classList.contains('selected')) { + el.classList.remove('selected'); + parent.classList.remove('yeah'); + count.innerText -= 1; - } - else { - el.classList.add('selected'); - parent.classList.add('yeah'); - count.innerText = ++count.innerText; - } + } else { + el.classList.add('selected'); + parent.classList.add('yeah'); + count.innerText = ++count.innerText; + } - POST('/posts/empathy', params, function a(data) { - let post = JSON.parse(data.response); - if(!post || post.status !== 200) { - // Apparently there was an actual error code for not being able to yeah a post, who knew! - // TODO: Find more of these - Toast(1155927); - } - el.disabled = false; - count.innerText = post.count; - }); - } + POST('/posts/empathy', params, function a(data) { + const post = JSON.parse(data.response); + if (!post || post.status !== 200) { + // Apparently there was an actual error code for not being able to yeah a post, who knew! + // TODO: Find more of these + Toast(1155927); + } + el.disabled = false; + count.innerText = post.count; + }); + } } function initTabs() { - let els = document.querySelectorAll(".tab-button"); - if (!els) return; - for (let i = 0; i < els.length; i++) { - els[i].removeEventListener('click', tabs); - els[i].addEventListener("click", tabs); - } - function tabs(e) { - e.preventDefault(); - let el = e.currentTarget; - let child = el.children[0]; + const els = document.querySelectorAll('.tab-button'); + if (!els) { + return; + } + for (let i = 0; i < els.length; i++) { + els[i].removeEventListener('click', tabs); + els[i].addEventListener('click', tabs); + } + function tabs(e) { + e.preventDefault(); + const el = e.currentTarget; + const child = el.children[0]; - for(let i = 0; i < els.length; i++) { - if(els[i].classList.contains('selected')) - els[i].classList.remove('selected'); - } - el.classList.add("selected"); + for (let i = 0; i < els.length; i++) { + if (els[i].classList.contains('selected')) { + els[i].classList.remove('selected'); + } + } + el.classList.add('selected'); - GET(child.getAttribute('href') + "?pjax=true", function a(data) { - let response = data.response; - if(response && data.status === 200) { - document.getElementsByClassName("tab-body")[0].innerHTML = data.response; - window.history.pushState({ url: child.href, title: "", scrollPos: [0, 0]}, "", child.href); - initPosts(); - initMorePosts(); - } - }) + GET(child.getAttribute('href') + '?pjax=true', function a(data) { + const response = data.response; + if (response && data.status === 200) { + document.getElementsByClassName('tab-body')[0].innerHTML = data.response; + window.history.pushState({ url: child.href, title: '', scrollPos: [0, 0]}, '', child.href); + initPosts(); + initMorePosts(); + } + }); - } + } } function initPosts() { - let els = document.querySelectorAll(".post-content[data-href]"); - if (!els) return; - for (let i = 0; i < els.length; i++) { - els[i].addEventListener("click", function(e) { - pjax.loadUrl(e.currentTarget.getAttribute('data-href')); - }); - } - initYeah(); - initSpoilers(); + const els = document.querySelectorAll('.post-content[data-href]'); + if (!els) { + return; + } + for (let i = 0; i < els.length; i++) { + els[i].addEventListener('click', function(e) { + pjax.loadUrl(e.currentTarget.getAttribute('data-href')); + }); + } + initYeah(); + initSpoilers(); } function initMorePosts() { - let els = document.querySelectorAll("#load-more[data-href]"); - if (!els) return; - for (let i = 0; i < els.length; i++) { - els[i].addEventListener("click", function(e) { - let el = e.currentTarget; - GET(el.getAttribute('data-href'), function a(data) { - let response = data.response; - if(response && data.status === 200) { - el.parentElement.outerHTML = data.response; - initPosts(); - initMorePosts(); - } - else - el.parentElement.remove(); - }) + const els = document.querySelectorAll('#load-more[data-href]'); + if (!els) { + return; + } + for (let i = 0; i < els.length; i++) { + els[i].addEventListener('click', function(e) { + const el = e.currentTarget; + GET(el.getAttribute('data-href'), function a(data) { + const response = data.response; + if (response && data.status === 200) { + el.parentElement.outerHTML = data.response; + initPosts(); + initMorePosts(); + } else { + el.parentElement.remove(); + } + }); - }); - } + }); + } } function initPostModules() { - let els = document.querySelectorAll("[data-module-show]"); - console.log(els) - if (!els) return; - for (let i = 0; i < els.length; i++) { - els[i].addEventListener("click", function(e) { - let el = e.currentTarget, - show = el.getAttribute("data-module-show"), - hide = el.getAttribute("data-module-hide"), - header = el.getAttribute("data-header"), - menu = el.getAttribute("data-menu"); - if(!show || !hide) return; - document.getElementById(hide).style.display = 'none'; - document.getElementById(show).style.display = 'block'; - if(header === 'true') - document.getElementById("header").style.display = 'block'; - else - document.getElementById("header").style.display = 'none'; - if(menu === 'true') - document.getElementById("nav-menu").style.display = 'block'; - else - document.getElementById("nav-menu").style.display = 'none'; - initNewPost(); - }); - } + const els = document.querySelectorAll('[data-module-show]'); + console.log(els); + if (!els) { + return; + } + for (let i = 0; i < els.length; i++) { + els[i].addEventListener('click', function(e) { + const el = e.currentTarget; + const show = el.getAttribute('data-module-show'); + const hide = el.getAttribute('data-module-hide'); + const header = el.getAttribute('data-header'); + const menu = el.getAttribute('data-menu'); + if (!show || !hide) { + return; + } + document.getElementById(hide).style.display = 'none'; + document.getElementById(show).style.display = 'block'; + if (header === 'true') { + document.getElementById('header').style.display = 'block'; + } else { + document.getElementById('header').style.display = 'none'; + } + if (menu === 'true') { + document.getElementById('nav-menu').style.display = 'block'; + } else { + document.getElementById('nav-menu').style.display = 'none'; + } + initNewPost(); + }); + } } function initPostEmotion() { - let els = document.querySelectorAll("input[data-mii-face-url]"); - if (!els) return; - for (let i = 0; i < els.length; i++) { - els[i].addEventListener("click", function(e) { - let el = e.currentTarget; - document.getElementById("mii-face").src = el.getAttribute('data-mii-face-url'); - }); - } + const els = document.querySelectorAll('input[data-mii-face-url]'); + if (!els) { + return; + } + for (let i = 0; i < els.length; i++) { + els[i].addEventListener('click', function(e) { + const el = e.currentTarget; + document.getElementById('mii-face').src = el.getAttribute('data-mii-face-url'); + }); + } } function initNewPost() { - initPostEmotion(); - initScreenShots(); + initPostEmotion(); + initScreenShots(); } function initSpoilers() { - let els = document.querySelectorAll("button[data-post-id]"); - if (!els) return; - for (let i = 0; i < els.length; i++) { - els[i].addEventListener("click", function(e) { - let el = e.currentTarget; - document.getElementById('post-' + el.getAttribute('data-post-id')).classList.remove('spoiler'); - el.remove(); - }); - } + const els = document.querySelectorAll('button[data-post-id]'); + if (!els) { + return; + } + for (let i = 0; i < els.length; i++) { + els[i].addEventListener('click', function(e) { + const el = e.currentTarget; + document.getElementById('post-' + el.getAttribute('data-post-id')).classList.remove('spoiler'); + el.remove(); + }); + } } function initAll() { - initNavBar(); - initTabs(); - initPosts(); - initMorePosts(); - initPostModules(); - pjax.refresh(); + initNavBar(); + initTabs(); + initPosts(); + initMorePosts(); + initPostModules(); + pjax.refresh(); } -console.debug("Document initialized:" + window.location.href); -document.addEventListener("pjax:send", function() { - console.debug("Event: pjax:send", arguments); +console.debug('Document initialized:' + window.location.href); +document.addEventListener('pjax:send', function() { + console.debug('Event: pjax:send', arguments); }); -document.addEventListener("pjax:complete", function() { - console.debug("Event: pjax:complete", arguments); +document.addEventListener('pjax:complete', function() { + console.debug('Event: pjax:complete', arguments); }); -document.addEventListener("pjax:error", function(e) { - Toast('Error: Unable to load element. \nPlease send the error code and what you were doing in #support'); - console.debug(e); +document.addEventListener('pjax:error', function(e) { + Toast('Error: Unable to load element. \nPlease send the error code and what you were doing in #support'); + console.debug(e); }); -document.addEventListener("pjax:success", function() { - console.debug("Event: pjax:success", arguments); - initAll(); +document.addEventListener('pjax:success', function() { + console.debug('Event: pjax:success', arguments); + initAll(); }); -document.addEventListener("DOMContentLoaded", function() { - pjax = new Pjax({ - elements: "a[data-pjax]" + - "", - selectors: ["title", "#body"], - switches: {"#nav-menu": Pjax.switches.replaceNode, ".tab-body": Pjax.switches.replaceNode} - }) - console.debug("Pjax initialized.", pjax); - initAll(); +document.addEventListener('DOMContentLoaded', function() { + pjax = new Pjax({ + elements: 'a[data-pjax]' + + '', + selectors: ['title', '#body'], + switches: {'#nav-menu': Pjax.switches.replaceNode, '.tab-body': Pjax.switches.replaceNode} + }); + console.debug('Pjax initialized.', pjax); + initAll(); }); function follow(el) { - let id = el.getAttribute("data-community-id"); - let count = document.getElementById("followers"); - let oldtext = el.innerText, newtext = el.getAttribute("data-text"); - el.disabled = true; - let params = "id=" + id; - if(el.classList.contains('checked')) { - el.classList.remove('checked'); - } - else { - el.classList.add('checked'); - } - el.setAttribute("data-text", oldtext); - el.innerText = newtext; - POST(el.getAttribute("data-url"), params, function a(data) { - let element = JSON.parse(data.response); - if(!element || element.status !== 200) { - // Apparently there was an actual error code for not being able to yeah a post, who knew! - // TODO: Find more of these - return Toast('Unable to follow. Please try again later.'); - } - el.disabled = false; - count.innerText = element.count; - }); + const id = el.getAttribute('data-community-id'); + const count = document.getElementById('followers'); + const oldtext = el.innerText; const newtext = el.getAttribute('data-text'); + el.disabled = true; + const params = 'id=' + id; + if (el.classList.contains('checked')) { + el.classList.remove('checked'); + } else { + el.classList.add('checked'); + } + el.setAttribute('data-text', oldtext); + el.innerText = newtext; + POST(el.getAttribute('data-url'), params, function a(data) { + const element = JSON.parse(data.response); + if (!element || element.status !== 200) { + // Apparently there was an actual error code for not being able to yeah a post, who knew! + // TODO: Find more of these + return Toast('Unable to follow. Please try again later.'); + } + el.disabled = false; + count.innerText = element.count; + }); } function checkForUpdates() { - let xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if (this.readyState === 4 && this.status === 200) { - let notificationObj = JSON.parse(this.responseText); - let messages = document.getElementById("message-badge"); - let news = document.getElementById("news-badge"); - /**/ - if(notificationObj.message_count > 0 && notificationObj.message_count < 99) { - messages.innerHTML = notificationObj.message_count; - messages.style.display = "unset"; - } - else if(notificationObj.message_count >= 99) { - messages.innerHTML = "99+"; - messages.style.display = "unset"; - } - else { - messages.innerHTML = ""; - messages.style.display = "none"; - } - /*Check for Notifications*/ - if(notificationObj.notification_count > 0 && notificationObj.notification_count < 99) { - news.innerHTML = notificationObj.notification_count; - news.style.display = "unset"; - } - else if(notificationObj.notification_count >= 99) { - news.innerHTML = "99+"; - news.style.display = "unset"; - } - else { - news.innerHTML = ""; - news.style.display = "none"; - } - } - }; - xhttp.open("GET", "/notifications.json", true); - xhttp.send(); + const xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState === 4 && this.status === 200) { + const notificationObj = JSON.parse(this.responseText); + const messages = document.getElementById('message-badge'); + const news = document.getElementById('news-badge'); + /**/ + if (notificationObj.message_count > 0 && notificationObj.message_count < 99) { + messages.innerHTML = notificationObj.message_count; + messages.style.display = 'unset'; + } else if (notificationObj.message_count >= 99) { + messages.innerHTML = '99+'; + messages.style.display = 'unset'; + } else { + messages.innerHTML = ''; + messages.style.display = 'none'; + } + /*Check for Notifications*/ + if (notificationObj.notification_count > 0 && notificationObj.notification_count < 99) { + news.innerHTML = notificationObj.notification_count; + news.style.display = 'unset'; + } else if (notificationObj.notification_count >= 99) { + news.innerHTML = '99+'; + news.style.display = 'unset'; + } else { + news.innerHTML = ''; + news.style.display = 'none'; + } + } + }; + xhttp.open('GET', '/notifications.json', true); + xhttp.send(); } function POST(url, data, callback) { - let xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if(this.readyState === 4) { - return callback(this); - } - } - xhttp.open("POST", url, true); - xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - xhttp.send(data); + const xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState === 4) { + return callback(this); + } + }; + xhttp.open('POST', url, true); + xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhttp.send(data); } function GET(url, callback) { - let xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function() { - if(this.readyState === 4) { - return callback(this); - } - }; - xhttp.open("GET", url, true); - xhttp.send(); + const xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState === 4) { + return callback(this); + } + }; + xhttp.open('GET', url, true); + xhttp.send(); } window.onscroll = function(ev) { - if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) { - document.getElementById('load-more').click(); - } + if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) { + document.getElementById('load-more').click(); + } }; function copyToClipboard(text) { - let inputc = document.getElementsByTagName("header")[0].appendChild(document.createElement("input")); - inputc.value = text; - inputc.focus(); - inputc.select(); - document.execCommand('copy'); - inputc.parentNode.removeChild(inputc); - Toast("Copied to clipboard."); + const inputc = document.getElementsByTagName('header')[0].appendChild(document.createElement('input')); + inputc.value = text; + inputc.focus(); + inputc.select(); + document.execCommand('copy'); + inputc.parentNode.removeChild(inputc); + Toast('Copied to clipboard.'); } function Toast(text) { - let x = document.getElementById("toast"); - x.innerText = text; - x.className = "show"; - setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000); + const x = document.getElementById('toast'); + x.innerText = text; + x.className = 'show'; + setTimeout(function(){ + x.className = x.className.replace('show', ''); + }, 3000); } function downloadURI(uri, name) { - let link = document.createElement("a"); - link.download = name; - link.href = uri; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - delete link; + const link = document.createElement('a'); + link.download = name; + link.href = uri; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + delete link; } \ No newline at end of file diff --git a/src/webfiles/web/messages.ejs b/src/webfiles/web/messages.ejs index f3ab623..606f92b 100644 --- a/src/webfiles/web/messages.ejs +++ b/src/webfiles/web/messages.ejs @@ -9,7 +9,7 @@
<% if(conversations.length === 0) {%> -
  • <%= lang.messages.coming_soon %><

  • +
  • <%= lang.messages.coming_soon %>

  • <%} else { %>