mirror of
https://github.com/PretendoNetwork/miiverse-api.git
synced 2026-07-19 09:01:41 -05:00
This was too long in the making, and I've already made too many changes to list here. The Great rewrite begins!
This commit is contained in:
parent
fcb375fe75
commit
035c4e0a23
|
|
@ -2,19 +2,30 @@
|
|||
"http": {
|
||||
"port": 8081
|
||||
},
|
||||
"account_server": "account.miiverse.cc:8080",
|
||||
"secret": "806590af0a2bf0cf9ce54b0b5c1493a1",
|
||||
"X-Nintendo-Client-ID": "a2efa818a34fa16b8afbc8a74eba3eda",
|
||||
"X-Nintendo-Client-Secret": "c91cdb5658bd4954ade78533a339cf9a",
|
||||
"account_server": "",
|
||||
"secret": "",
|
||||
"X-Nintendo-Client-ID": "",
|
||||
"X-Nintendo-Client-Secret": "",
|
||||
"mongoose": {
|
||||
"uri": "mongodb://localhost:27017",
|
||||
"database": "Miiverse",
|
||||
"uri": "",
|
||||
"database": "",
|
||||
"options": {
|
||||
"useNewUrlParser": true,
|
||||
"useUnifiedTopology": true
|
||||
}
|
||||
},
|
||||
"authorized_PNIDs" : [
|
||||
1420636409, 1657414049, 1587572630
|
||||
]
|
||||
"account_db": {
|
||||
"uri": "",
|
||||
"database": "",
|
||||
"options": {
|
||||
"useNewUrlParser": true,
|
||||
"useUnifiedTopology": true
|
||||
}
|
||||
},
|
||||
"aws": {
|
||||
"spaces": {
|
||||
"key": "",
|
||||
"secret": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
3040
package-lock.json
generated
3040
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -11,6 +11,8 @@
|
|||
"author": "Jemma",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.1204.0",
|
||||
"bmp-js": "^0.1.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"colors": "^1.4.0",
|
||||
"express": "^4.17.1",
|
||||
|
|
|
|||
44
src/accountdb.js
Normal file
44
src/accountdb.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
const mongoose = require('mongoose');
|
||||
const { account_db: mongooseConfig } = require('../config.json');
|
||||
const { uri, database, options } = mongooseConfig;
|
||||
const logger = require('./logger');
|
||||
|
||||
let pnidConnection;
|
||||
|
||||
function connect() {
|
||||
if(!pnidConnection)
|
||||
pnidConnection = makeNewConnection(`${uri}/${database}`, options);
|
||||
}
|
||||
|
||||
function verifyConnected() {
|
||||
if (!pnidConnection) {
|
||||
throw new Error('Cannot make database requests without being connected');
|
||||
}
|
||||
}
|
||||
|
||||
function makeNewConnection(uri) {
|
||||
pnidConnection = mongoose.createConnection(uri, options);
|
||||
|
||||
pnidConnection.on('error', function (error) {
|
||||
logger.error(`MongoDB connection ${this.name} ${JSON.stringify(error)}`);
|
||||
pnidConnection.close().catch(() => logger.error(`MongoDB failed to close connection ${this.name}`));
|
||||
});
|
||||
|
||||
pnidConnection.on('connected', function () {
|
||||
logger.info(`MongoDB connected ${this.name} / ${uri}`);
|
||||
});
|
||||
|
||||
pnidConnection.on('disconnected', function () {
|
||||
logger.info(`MongoDB disconnected ${this.name}`);
|
||||
});
|
||||
|
||||
return pnidConnection;
|
||||
}
|
||||
|
||||
pnidConnection = makeNewConnection(`${uri}/${database}`, options);
|
||||
|
||||
module.exports = {
|
||||
pnidConnection,
|
||||
connect,
|
||||
verifyConnected
|
||||
};
|
||||
263
src/database.js
263
src/database.js
|
|
@ -1,12 +1,17 @@
|
|||
const mongoose = require('mongoose');
|
||||
const { mongoose: mongooseConfig } = require('../config.json');
|
||||
const { ENDPOINT } = require('./models/endpoint');
|
||||
const { COMMUNITY } = require('./models/communities');
|
||||
const { POST } = require('./models/post');
|
||||
const { USER } = require('./models/user');
|
||||
const { CONTENT } = require('./models/content');
|
||||
const { CONVERSATION } = require('./models/conversation');
|
||||
const { ENDPOINT } = require('./models/endpoint');
|
||||
const { NOTIFICATIONS } = require('./models/notifications');
|
||||
const { PNID } = require('./models/pnid');
|
||||
const { POST } = require('./models/post');
|
||||
const { SETTINGS } = require('./models/settings');
|
||||
|
||||
const { uri, database, options } = mongooseConfig;
|
||||
const logger = require('./logger');
|
||||
const accountDB = require('./accountdb');
|
||||
|
||||
let connection;
|
||||
|
||||
|
|
@ -31,19 +36,19 @@ function verifyConnected() {
|
|||
async function getCommunities(numberOfCommunities) {
|
||||
verifyConnected();
|
||||
if(numberOfCommunities === -1)
|
||||
return COMMUNITY.find({ parent: null });
|
||||
return COMMUNITY.find({ parent: null, type: 0 });
|
||||
else
|
||||
return COMMUNITY.find({ parent: null }).limit(numberOfCommunities);
|
||||
return COMMUNITY.find({ parent: null, type: 0 }).limit(numberOfCommunities);
|
||||
}
|
||||
|
||||
async function getMostPopularCommunities(numberOfCommunities) {
|
||||
verifyConnected();
|
||||
return COMMUNITY.find({ parent: null }).sort({followers: -1}).limit(numberOfCommunities);
|
||||
return COMMUNITY.find({ parent: null, type: 0 }).sort({followers: -1}).limit(numberOfCommunities);
|
||||
}
|
||||
|
||||
async function getNewCommunities(numberOfCommunities) {
|
||||
verifyConnected();
|
||||
return COMMUNITY.find({ parent: null }).sort([['created_at', -1]]).limit(numberOfCommunities);
|
||||
return COMMUNITY.find({ parent: null, type: 0 }).sort([['created_at', -1]]).limit(numberOfCommunities);
|
||||
}
|
||||
|
||||
async function getSubCommunities(communityID) {
|
||||
|
|
@ -77,7 +82,6 @@ async function getTotalPostsByCommunity(community) {
|
|||
|
||||
async function getPostByID(postID) {
|
||||
verifyConnected();
|
||||
|
||||
return POST.findOne({
|
||||
id: postID
|
||||
});
|
||||
|
|
@ -98,11 +102,23 @@ async function getPostReplies(postID, number) {
|
|||
}).limit(number);
|
||||
}
|
||||
|
||||
async function getDuplicatePosts(pid, post) {
|
||||
verifyConnected();
|
||||
return POST.findOne({
|
||||
pid: pid,
|
||||
body: post.body,
|
||||
painting: post.painting,
|
||||
screenshot: post.screenshot,
|
||||
parent: null
|
||||
});
|
||||
}
|
||||
|
||||
async function getUserPostRepliesAfterTimestamp(post, numberOfPosts) {
|
||||
verifyConnected();
|
||||
return POST.find({
|
||||
parent: post.pid,
|
||||
created_at: { $lt: post.created_at }
|
||||
created_at: { $lt: post.created_at },
|
||||
message_to_pid: null
|
||||
}).limit(numberOfPosts);
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +126,8 @@ async function getNumberUserPostsByID(userID, number) {
|
|||
verifyConnected();
|
||||
return POST.find({
|
||||
pid: userID,
|
||||
parent: null
|
||||
parent: null,
|
||||
message_to_pid: null
|
||||
}).sort({ created_at: -1}).limit(number);
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +135,8 @@ async function getTotalPostsByUserID(userID) {
|
|||
verifyConnected();
|
||||
return POST.find({
|
||||
pid: userID,
|
||||
parent: null
|
||||
parent: null,
|
||||
message_to_pid: null
|
||||
}).countDocuments();
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +176,7 @@ async function getNumberVerifiedCommunityPostsByID(community, limit, offset) {
|
|||
async function getPostsByCommunity(community, numberOfPosts) {
|
||||
verifyConnected();
|
||||
return POST.find({
|
||||
title_id: community.title_id,
|
||||
community_id: community.id,
|
||||
parent: null
|
||||
}).limit(numberOfPosts);
|
||||
}
|
||||
|
|
@ -172,6 +190,15 @@ async function getPostsByCommunityKey(community, numberOfPosts, search_key) {
|
|||
}).limit(numberOfPosts);
|
||||
}
|
||||
|
||||
async function getFriendMessages(pid, search_key, limit) {
|
||||
verifyConnected();
|
||||
return POST.find({
|
||||
message_to_pid: pid,
|
||||
search_key: search_key,
|
||||
parent: null
|
||||
}).limit(limit);
|
||||
}
|
||||
|
||||
async function getNewPostsByCommunity(community, limit, offset) {
|
||||
verifyConnected();
|
||||
return POST.find({
|
||||
|
|
@ -185,7 +212,8 @@ async function getUserPostsAfterTimestamp(post, numberOfPosts) {
|
|||
return POST.find({
|
||||
pid: post.pid,
|
||||
created_at: { $lt: post.created_at },
|
||||
parent: null
|
||||
parent: null,
|
||||
message_to_pid: null,
|
||||
}).limit(numberOfPosts);
|
||||
}
|
||||
|
||||
|
|
@ -193,8 +221,9 @@ async function getUserPostsOffset(pid, limit, offset) {
|
|||
verifyConnected();
|
||||
return POST.find({
|
||||
pid: pid,
|
||||
parent: null
|
||||
}).sort({ created_at: -1}).skip(offset).limit(limit);
|
||||
parent: null,
|
||||
message_to_pid: null
|
||||
}).skip(offset).limit(limit).sort({ created_at: -1});
|
||||
}
|
||||
|
||||
async function getCommunityPostsAfterTimestamp(post, numberOfPosts) {
|
||||
|
|
@ -206,115 +235,146 @@ async function getCommunityPostsAfterTimestamp(post, numberOfPosts) {
|
|||
}).limit(numberOfPosts);
|
||||
}
|
||||
|
||||
async function pushNewNotificationByPID(PID, content, link) {
|
||||
async function getEndpoints() {
|
||||
verifyConnected();
|
||||
return USER.update(
|
||||
{ pid: PID }, { $push: { notification_list: { content: content, link: link, read: false, created_at: Date() }}});
|
||||
return ENDPOINT.find({});
|
||||
}
|
||||
|
||||
async function pushNewNotificationToAll(content, link) {
|
||||
verifyConnected();
|
||||
return USER.updateMany(
|
||||
{}, { $push: { notification_list: { content: content, link: link, read: false, created_at: Date() }}});
|
||||
}
|
||||
|
||||
async function getDiscoveryHosts() {
|
||||
async function getEndPoint(accessLevel) {
|
||||
verifyConnected();
|
||||
return ENDPOINT.findOne({
|
||||
version: 1
|
||||
});
|
||||
server_access_level: accessLevel
|
||||
})
|
||||
}
|
||||
|
||||
async function getUsers(numberOfUsers) {
|
||||
async function getUsersSettings(numberOfUsers) {
|
||||
verifyConnected();
|
||||
if(numberOfUsers === -1)
|
||||
return USER.find({});
|
||||
return SETTINGS.find({});
|
||||
else
|
||||
return USER.find({}).limit(numberOfUsers);
|
||||
return SETTINGS.find({}).limit(numberOfUsers);
|
||||
}
|
||||
|
||||
async function getFollowingUsers(user) {
|
||||
async function getUsersContent(numberOfUsers) {
|
||||
verifyConnected();
|
||||
return USER.find({
|
||||
pid: user.following_users
|
||||
});
|
||||
if(numberOfUsers === -1)
|
||||
return SETTINGS.find({});
|
||||
else
|
||||
return SETTINGS.find({}).limit(numberOfUsers);
|
||||
}
|
||||
async function getFollowedUsers(user) {
|
||||
|
||||
async function getUserSettings(pid) {
|
||||
verifyConnected();
|
||||
return USER.find({
|
||||
pid: user.followed_users
|
||||
return SETTINGS.findOne({pid: pid});
|
||||
}
|
||||
|
||||
async function getUserContent(pid) {
|
||||
verifyConnected();
|
||||
return CONTENT.findOne({pid: pid});
|
||||
}
|
||||
|
||||
async function getFollowingUsers(content) {
|
||||
verifyConnected();
|
||||
return SETTINGS.find({
|
||||
pid: content.following_users
|
||||
});
|
||||
}
|
||||
|
||||
async function getUserByPID(PID) {
|
||||
async function getFollowedUsers(content) {
|
||||
verifyConnected();
|
||||
|
||||
return USER.findOne({
|
||||
pid: PID
|
||||
return SETTINGS.find({
|
||||
pid: content.followed_users
|
||||
});
|
||||
}
|
||||
|
||||
async function getUserByUsername(user_id) {
|
||||
verifyConnected();
|
||||
|
||||
return USER.findOne({
|
||||
pnid: new RegExp(`^${user_id}$`, 'i')
|
||||
return PNID.findOne({
|
||||
"username": new RegExp(`^${user_id}$`, 'i')
|
||||
});
|
||||
}
|
||||
|
||||
async function getServerConfig() {
|
||||
verifyConnected();
|
||||
return ENDPOINT.findOne();
|
||||
}
|
||||
|
||||
async function getNewsFeed(user, numberOfPosts) {
|
||||
async function getNewsFeed(content, numberOfPosts) {
|
||||
verifyConnected();
|
||||
return POST.find({
|
||||
$or: [
|
||||
{pid: user.followed_users},
|
||||
{pid: user.pid}
|
||||
{pid: content.followed_users},
|
||||
{pid: content.pid},
|
||||
{community_id: content.followed_communities},
|
||||
],
|
||||
parent: null
|
||||
parent: null,
|
||||
message_to_pid: null
|
||||
}).limit(numberOfPosts).sort({ created_at: -1});
|
||||
}
|
||||
|
||||
async function getNewsFeedAfterTimestamp(user, numberOfPosts, post) {
|
||||
async function getNewsFeedAfterTimestamp(content, numberOfPosts, post) {
|
||||
verifyConnected();
|
||||
return POST.find({
|
||||
$or: [
|
||||
{pid: user.followed_users},
|
||||
{pid: user.pid}
|
||||
{pid: content.followed_users},
|
||||
{pid: content.pid},
|
||||
{community_id: content.followed_communities},
|
||||
],
|
||||
created_at: { $lt: post.created_at },
|
||||
parent: null
|
||||
parent: null,
|
||||
message_to_pid: null
|
||||
}).limit(numberOfPosts).sort({ created_at: -1});
|
||||
}
|
||||
|
||||
async function getNewsFeedOffset(user, limit, offset) {
|
||||
async function getNewsFeedOffset(content, limit, offset) {
|
||||
verifyConnected();
|
||||
return POST.find({
|
||||
$or: [
|
||||
{pid: user.followed_users},
|
||||
{pid: user.pid}
|
||||
{pid: content.followed_users},
|
||||
{pid: content.pid},
|
||||
{community_id: content.followed_communities},
|
||||
],
|
||||
parent: null
|
||||
parent: null,
|
||||
message_to_pid: null
|
||||
}).skip(offset).limit(limit).sort({ created_at: -1});
|
||||
}
|
||||
|
||||
async function getConversations(pid) {
|
||||
verifyConnected();
|
||||
return CONVERSATION.find({
|
||||
pids: pid
|
||||
"users.pid": pid
|
||||
}).sort({ last_updated: -1});
|
||||
}
|
||||
|
||||
async function getUnreadConversationCount(pid) {
|
||||
verifyConnected();
|
||||
return CONVERSATION.find({
|
||||
"users": { $elemMatch: {
|
||||
'pid': pid,
|
||||
'read': false
|
||||
}}
|
||||
|
||||
}).countDocuments();
|
||||
}
|
||||
|
||||
async function getConversationByID(community_id) {
|
||||
verifyConnected();
|
||||
return CONVERSATION.findOne({
|
||||
type: 3,
|
||||
id: community_id
|
||||
});
|
||||
}
|
||||
|
||||
async function getConversation(pid, pid2) {
|
||||
async function getConversationMessages(community_id, limit, offset) {
|
||||
verifyConnected();
|
||||
return CONVERSATION.find({
|
||||
return POST.find({
|
||||
community_id: community_id,
|
||||
parent: null
|
||||
}).sort({created_at: 1}).skip(offset).limit(limit);
|
||||
}
|
||||
|
||||
async function getConversationByUsers(pids) {
|
||||
verifyConnected();
|
||||
return CONVERSATION.findOne({
|
||||
$and: [
|
||||
{pids: pid},
|
||||
{pids: pid2}
|
||||
],
|
||||
{'users.pid': pids[0]},
|
||||
{'users.pid': pids[1]}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -328,6 +388,50 @@ async function getLatestMessage(pid, pid2) {
|
|||
})
|
||||
}
|
||||
|
||||
async function getPNIDS() {
|
||||
accountDB.verifyConnected();
|
||||
return PNID.find({});
|
||||
}
|
||||
|
||||
async function getPNID(pid) {
|
||||
accountDB.verifyConnected();
|
||||
return PNID.findOne({
|
||||
pid: pid
|
||||
});
|
||||
}
|
||||
|
||||
async function getNotifications(pid, limit, offset) {
|
||||
verifyConnected();
|
||||
return NOTIFICATIONS.find({
|
||||
pid: pid,
|
||||
}).sort({created_at: 1}).skip(offset).limit(limit);
|
||||
}
|
||||
|
||||
async function getNotification(pid, type, reference_id) {
|
||||
verifyConnected();
|
||||
return NOTIFICATIONS.findOne({
|
||||
pid: pid,
|
||||
type: type,
|
||||
reference_id: reference_id
|
||||
})
|
||||
}
|
||||
|
||||
async function getLastNotification(pid) {
|
||||
verifyConnected();
|
||||
return NOTIFICATIONS.findOne({
|
||||
pid: pid
|
||||
}).sort({created_at: -1}).limit(1);
|
||||
}
|
||||
|
||||
async function getUnreadNotificationCount(pid) {
|
||||
verifyConnected();
|
||||
return NOTIFICATIONS.find({
|
||||
pid: pid,
|
||||
read: false
|
||||
}).countDocuments();
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
connect,
|
||||
getCommunities,
|
||||
|
|
@ -337,7 +441,6 @@ module.exports = {
|
|||
getCommunityByTitleID,
|
||||
getCommunityByID,
|
||||
getTotalPostsByCommunity,
|
||||
getDiscoveryHosts,
|
||||
getPostsByCommunity,
|
||||
getHotPostsByCommunity,
|
||||
getNumberNewCommunityPostsByID,
|
||||
|
|
@ -351,21 +454,33 @@ module.exports = {
|
|||
getNumberUserPostsByID,
|
||||
getTotalPostsByUserID,
|
||||
getPostByID,
|
||||
getUsers,
|
||||
getUserByPID,
|
||||
getDuplicatePosts,
|
||||
getEndpoints,
|
||||
getEndPoint,
|
||||
getUserByUsername,
|
||||
getUserPostsAfterTimestamp,
|
||||
getUserPostsOffset,
|
||||
getCommunityPostsAfterTimestamp,
|
||||
getServerConfig,
|
||||
pushNewNotificationByPID,
|
||||
pushNewNotificationToAll,
|
||||
getNewsFeed,
|
||||
getNewsFeedAfterTimestamp,
|
||||
getNewsFeedOffset,
|
||||
getFollowingUsers,
|
||||
getFollowedUsers,
|
||||
getConversations,
|
||||
getConversation,
|
||||
getConversationByID,
|
||||
getConversationByUsers,
|
||||
getConversationMessages,
|
||||
getUnreadConversationCount,
|
||||
getLatestMessage,
|
||||
getFriendMessages,
|
||||
getPNID,
|
||||
getPNIDS,
|
||||
getUsersSettings,
|
||||
getUsersContent,
|
||||
getUserSettings,
|
||||
getUserContent,
|
||||
getNotifications,
|
||||
getUnreadNotificationCount,
|
||||
getNotification,
|
||||
getLastNotification
|
||||
};
|
||||
|
|
|
|||
40
src/middleware/auth.js
Normal file
40
src/middleware/auth.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
const config = require('../../config.json');
|
||||
const util = require('../util/util');
|
||||
const xml = require("object-to-xml");
|
||||
|
||||
function auth(req, res, next) {
|
||||
if(req.path.includes('/topics'))
|
||||
return next();
|
||||
let token = req.headers["x-nintendo-servicetoken"];
|
||||
let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
|
||||
if(!token || !paramPackData)
|
||||
badAuth(res);
|
||||
else {
|
||||
let pid = util.data.processServiceToken(token);
|
||||
if(pid === null)
|
||||
badAuth(res);
|
||||
else {
|
||||
req.pid = pid;
|
||||
req.paramPackData = paramPackData;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function badAuth(res) {
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 7,
|
||||
message: "POSTING_FROM_NNID"
|
||||
}
|
||||
};
|
||||
return res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
}
|
||||
|
||||
module.exports = auth;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
const { Schema, model } = require('mongoose');
|
||||
const moment = require("moment");
|
||||
|
||||
const CommunitySchema = new Schema({
|
||||
platform_id: Number,
|
||||
|
|
@ -12,6 +13,7 @@ const CommunitySchema = new Schema({
|
|||
* 0: Main Community
|
||||
* 1: Sub-Community
|
||||
* 2: Announcement Community
|
||||
* 3: Private Community
|
||||
*/
|
||||
type: {
|
||||
type: Number,
|
||||
|
|
@ -62,7 +64,7 @@ const CommunitySchema = new Schema({
|
|||
browser_icon: String,
|
||||
browser_thumbnail: String,
|
||||
CTR_browser_header: String,
|
||||
WiiU_browser_header: String
|
||||
WiiU_browser_header: String,
|
||||
});
|
||||
|
||||
CommunitySchema.methods.upEmpathy = async function() {
|
||||
|
|
|
|||
76
src/models/content.js
Normal file
76
src/models/content.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
const { Schema, model } = require('mongoose');
|
||||
|
||||
const ContentSchema = new Schema({
|
||||
pid: String,
|
||||
likes: {
|
||||
type: [String],
|
||||
default: [0]
|
||||
},
|
||||
followed_communities: {
|
||||
type: [String],
|
||||
default: [0]
|
||||
},
|
||||
followed_users: {
|
||||
type: [String],
|
||||
default: [0]
|
||||
},
|
||||
following_users: {
|
||||
type: [String],
|
||||
default: [0]
|
||||
},
|
||||
});
|
||||
|
||||
ContentSchema.methods.addToLikes = async function(postID) {
|
||||
const likes = this.get('likes');
|
||||
likes.addToSet(postID);
|
||||
await this.save();
|
||||
}
|
||||
|
||||
ContentSchema.methods.removeFromLike = async function(postID) {
|
||||
const likes = this.get('likes');
|
||||
likes.pull(postID);
|
||||
await this.save();
|
||||
}
|
||||
|
||||
ContentSchema.methods.addToCommunities = async function(postID) {
|
||||
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();
|
||||
}
|
||||
|
||||
ContentSchema.methods.addToUsers = async function(postID) {
|
||||
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();
|
||||
}
|
||||
|
||||
ContentSchema.methods.addToFollowers = async function(postID) {
|
||||
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 CONTENT = model('CONTENT', ContentSchema);
|
||||
|
||||
module.exports = {
|
||||
ContentSchema,
|
||||
CONTENT
|
||||
};
|
||||
|
|
@ -1,6 +1,19 @@
|
|||
const { Schema, model } = require('mongoose');
|
||||
const moment = require("moment");
|
||||
const snowflake = require('node-snowflake').Snowflake;
|
||||
|
||||
const user = new Schema({
|
||||
pid: String,
|
||||
official: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
read: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const ConversationSchema = new Schema({
|
||||
id: {
|
||||
type: String,
|
||||
|
|
@ -10,7 +23,7 @@ const ConversationSchema = new Schema({
|
|||
type: Date,
|
||||
default: new Date(),
|
||||
},
|
||||
last_message_sent: {
|
||||
last_updated: {
|
||||
type: Date,
|
||||
default: new Date(),
|
||||
},
|
||||
|
|
@ -18,44 +31,35 @@ const ConversationSchema = new Schema({
|
|||
type: String,
|
||||
default: ""
|
||||
},
|
||||
pid1: {
|
||||
pid: String,
|
||||
official: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
screen_name: String,
|
||||
read: Boolean
|
||||
},
|
||||
pid2: {
|
||||
pid: String,
|
||||
official: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
screen_name: String,
|
||||
read: Boolean
|
||||
}
|
||||
users: [user]
|
||||
});
|
||||
|
||||
ConversationSchema.methods.newMessage = async function(message, fromPid) {
|
||||
const pid1 = this.get('pid1');
|
||||
const pid2 = this.get('pid2');
|
||||
if(pid1.pid === fromPid) {
|
||||
pid1.read = true
|
||||
pid2.read = false
|
||||
const users = this.get('users');
|
||||
console.log(fromPid)
|
||||
if(users[0].pid.toString() === fromPid.toString()) {;
|
||||
users[1].read = false;
|
||||
}
|
||||
else {
|
||||
pid1.read = false
|
||||
pid2.read = true
|
||||
users[0].read = false;
|
||||
}
|
||||
this.set('pid1', pid1);
|
||||
this.set('pid2', pid2);
|
||||
this.set('last_message_sent', new Date());
|
||||
this.set('users', users);
|
||||
this.set('last_updated', moment(new Date()));
|
||||
this.set('message_preview', message);
|
||||
await this.save();
|
||||
}
|
||||
|
||||
ConversationSchema.methods.markAsRead = async function(pid) {
|
||||
let users = this.get('users');
|
||||
if(users[0].pid === pid.toString())
|
||||
users[0].read = true;
|
||||
else if(users[1].pid === pid.toString())
|
||||
users[1].read = true;
|
||||
this.set('users', users)
|
||||
this.markModified('users');
|
||||
await this.save();
|
||||
}
|
||||
|
||||
const CONVERSATION = model('CONVERSATION', ConversationSchema);
|
||||
|
||||
module.exports = {
|
||||
|
|
|
|||
|
|
@ -1,30 +1,15 @@
|
|||
const { Schema, model } = require('mongoose');
|
||||
|
||||
const endpointSchema = new Schema({
|
||||
has_error: Number,
|
||||
version: Number,
|
||||
endpoint: {
|
||||
host: String,
|
||||
api_host: String,
|
||||
portal_host: String,
|
||||
n3ds_host: String
|
||||
},
|
||||
guest: Boolean,
|
||||
status: Number,
|
||||
server_access_level: String,
|
||||
guest_access: Boolean,
|
||||
host: String,
|
||||
api_host: String,
|
||||
portal_host: String,
|
||||
n3ds_host: String
|
||||
});
|
||||
|
||||
endpointSchema.methods.updateHosts = async function({host, api_host, portal_host, n3ds_host}) {
|
||||
this.set('endpoint.host', host);
|
||||
this.set('endpoint.api_host', api_host);
|
||||
this.set('endpoint.portal_host', portal_host);
|
||||
this.set('endpoint.n3ds_host', n3ds_host);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
endpointSchema.methods.updateGuest = async function(mode) {
|
||||
this.set('guest', mode);
|
||||
await this.save();
|
||||
}
|
||||
|
||||
const ENDPOINT = model('ENDPOINT', endpointSchema);
|
||||
|
||||
module.exports = {
|
||||
|
|
|
|||
37
src/models/notifications.js
Normal file
37
src/models/notifications.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
const { Schema, model } = require('mongoose');
|
||||
|
||||
const NotificationsSchema = new Schema({
|
||||
pid: String,
|
||||
/**
|
||||
* 0 like
|
||||
* 1 reply
|
||||
* 2 new follower
|
||||
* 3 other
|
||||
*/
|
||||
type: Number,
|
||||
title: String,
|
||||
content: String,
|
||||
reference_id: String,
|
||||
link: String,
|
||||
created_at: {
|
||||
type: Date,
|
||||
default: new Date()
|
||||
},
|
||||
read: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
origin_pid: String,
|
||||
});
|
||||
|
||||
NotificationsSchema.methods.markRead = async function() {
|
||||
this.set('read', true);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
const NOTIFICATIONS = model('NOTIFICATIONS', NotificationsSchema);
|
||||
|
||||
module.exports = {
|
||||
NotificationsSchema,
|
||||
NOTIFICATIONS
|
||||
};
|
||||
30
src/models/pnid.js
Normal file
30
src/models/pnid.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
const mongoose = require('mongoose');
|
||||
const {pnidConnection} = require('../accountdb');
|
||||
|
||||
const PNIDSchema = new mongoose.Schema({
|
||||
access_level: {
|
||||
type: Number,
|
||||
default: 0 // -1: banned, 0: standard, 1: tester, 2: mod, 3: dev
|
||||
},
|
||||
server_access_level: {
|
||||
type: String,
|
||||
default: 'prod' // prod, test, dev
|
||||
},
|
||||
pid: {
|
||||
type: Number,
|
||||
unique: true
|
||||
},
|
||||
username: String,
|
||||
birthdate: String,
|
||||
country: String,
|
||||
mii: {
|
||||
name: String,
|
||||
data: String,
|
||||
},
|
||||
});
|
||||
|
||||
const PNID = pnidConnection.model('PNID', PNIDSchema);
|
||||
|
||||
module.exports = {
|
||||
PNID,
|
||||
};
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
const { Schema, model } = require('mongoose');
|
||||
|
||||
const PostSchema = new Schema({
|
||||
id: String,
|
||||
title_id: String,
|
||||
screen_name: String,
|
||||
body: String,
|
||||
app_data: String,
|
||||
painting: String,
|
||||
painting_uri: String,
|
||||
screenshot: String,
|
||||
url: String,
|
||||
search_key: {
|
||||
type: [String],
|
||||
default: undefined
|
||||
|
|
@ -17,11 +16,12 @@ const PostSchema = new Schema({
|
|||
type: [String],
|
||||
default: undefined
|
||||
},
|
||||
community_id: String,
|
||||
country_id: Number,
|
||||
community_id: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
created_at: Date,
|
||||
feeling_id: Number,
|
||||
id: String,
|
||||
is_autopost: {
|
||||
type: Number,
|
||||
default: 0
|
||||
|
|
@ -48,14 +48,10 @@ const PostSchema = new Schema({
|
|||
},
|
||||
mii: String,
|
||||
mii_face_url: String,
|
||||
number: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
pid: Number,
|
||||
platform_id: Number,
|
||||
region_id: Number,
|
||||
parent_post: String,
|
||||
parent: String,
|
||||
reply_count: {
|
||||
type: Number,
|
||||
default: 0
|
||||
|
|
@ -64,17 +60,9 @@ const PostSchema = new Schema({
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
parent: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
message_to_pid: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
conversation_id: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
95
src/models/settings.js
Normal file
95
src/models/settings.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
const { Schema, model } = require('mongoose');
|
||||
|
||||
const SettingsSchema = new Schema({
|
||||
pid: String,
|
||||
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();
|
||||
};
|
||||
|
||||
SettingsSchema.methods.updateSkill = async function(skill) {
|
||||
this.set('game_skill', skill);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
SettingsSchema.methods.commentVisible = async function(active) {
|
||||
this.set('profile_comment_visibility', active);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
SettingsSchema.methods.skillVisible = async function(active) {
|
||||
this.set('game_skill_visibility', active);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
SettingsSchema.methods.birthdayVisible = async function(active) {
|
||||
this.set('birthday_visibility', active);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
SettingsSchema.methods.relationshipVisible = async function(active) {
|
||||
this.set('relationship_visibility', active);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
SettingsSchema.methods.countryVisible = async function(active) {
|
||||
this.set('country_visibility', active);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
SettingsSchema.methods.favCommunityVisible = async function(active) {
|
||||
this.set('profile_favorite_community_visibility', active);
|
||||
await this.save();
|
||||
};
|
||||
|
||||
const SETTINGS = model('SETTINGS', SettingsSchema);
|
||||
|
||||
module.exports = {
|
||||
SettingsSchema,
|
||||
SETTINGS
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@ const xmlparser = require('./middleware/xml-parser');
|
|||
const database = require('./database');
|
||||
const logger = require('./logger');
|
||||
const config = require('../config.json');
|
||||
const auth = require('./middleware/auth');
|
||||
|
||||
const { http: { port } } = config;
|
||||
const app = express();
|
||||
|
|
@ -25,6 +26,7 @@ app.use(express.urlencoded({
|
|||
parameterLimit: 100000
|
||||
}));
|
||||
app.use(xmlparser);
|
||||
app.use(auth);
|
||||
|
||||
// import the servers into one
|
||||
app.use(miiverse);
|
||||
|
|
|
|||
|
|
@ -1,198 +1,93 @@
|
|||
var express = require('express');
|
||||
var xml = require('object-to-xml');
|
||||
const database = require('../../../database');
|
||||
const util = require('../../../util/util');
|
||||
var router = express.Router();
|
||||
|
||||
/* GET discovery server. */
|
||||
router.get('/', async function (req, res) {
|
||||
const discovery = await database.getDiscoveryHosts();
|
||||
try
|
||||
{
|
||||
let pid = util.data.processServiceToken(req.headers["x-nintendo-servicetoken"]);
|
||||
let usrObj;
|
||||
if(pid == null)
|
||||
{
|
||||
throw new Error('The User token was not valid');
|
||||
}
|
||||
else
|
||||
{
|
||||
usrObj = await util.data.processUser(pid);
|
||||
if(!usrObj) {
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 2,
|
||||
message: "SETUP_NOT_COMPLETE"
|
||||
}
|
||||
};
|
||||
return res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
let user = await database.getPNID(req.pid);
|
||||
console.log(user.mii)
|
||||
if(!user) {
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 2,
|
||||
message: "SETUP_NOT_COMPLETE"
|
||||
}
|
||||
switch (usrObj.account_status) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 7,
|
||||
message: "POSTING_FROM_NNID"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
return res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.error(e);
|
||||
}
|
||||
switch(discovery.has_error)
|
||||
{
|
||||
let discovery = await database.getEndPoint(user.server_access_level);
|
||||
if(!discovery)
|
||||
discovery = await database.getEndPoint('prod');
|
||||
let message = '', error = 0;
|
||||
switch(discovery.status) {
|
||||
case 0 :
|
||||
res.set("Content-Type", "application/xml");
|
||||
response = {
|
||||
let response = {
|
||||
result: {
|
||||
has_error: 0,
|
||||
version: discovery.version,
|
||||
version: 0,
|
||||
endpoint: {
|
||||
host: discovery.endpoint.host,
|
||||
api_host: discovery.endpoint.api_host,
|
||||
portal_host: discovery.endpoint.portal_host,
|
||||
n3ds_host: discovery.endpoint.n3ds_host
|
||||
host: discovery.host,
|
||||
api_host: discovery.api_host,
|
||||
portal_host: discovery.portal_host,
|
||||
n3ds_host: discovery.n3ds_host
|
||||
}
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
break;
|
||||
return res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
case 1 :
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 1,
|
||||
message: "SYSTEM_UPDATE_REQUIRED"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
message = 'SYSTEM_UPDATE_REQUIRED';
|
||||
error = 1;
|
||||
break;
|
||||
case 2 :
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 2,
|
||||
message: "SETUP_NOT_COMPLETE"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
message = 'SETUP_NOT_COMPLETE';
|
||||
error = 2;
|
||||
break;
|
||||
case 3 :
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 3,
|
||||
message: "SERVICE_MAINTENANCE"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
message = 'SERVICE_MAINTENANCE';
|
||||
error = 3;
|
||||
break;
|
||||
case 4:
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 4,
|
||||
message: "SERVICE_CLOSED"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
message = 'SERVICE_CLOSED';
|
||||
error = 4;
|
||||
break;
|
||||
case 5 :
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 5,
|
||||
message: "PARENTAL_CONTROLS_ENABLED"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
message = 'PARENTAL_CONTROLS_ENABLED';
|
||||
error = 5;
|
||||
break;
|
||||
case 6 :
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 6,
|
||||
message: "POSTING_LIMITED_PARENTAL_CONTROLS"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
message = 'POSTING_LIMITED_PARENTAL_CONTROLS';
|
||||
error = 6;
|
||||
break;
|
||||
case 7 :
|
||||
message = 'NNID_BANNED';
|
||||
error = 7;
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 7,
|
||||
message: "PNID_BANNED"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
break;
|
||||
default :
|
||||
message = 'SERVER_ERROR';
|
||||
error = 15;
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 15,
|
||||
message: "SERVER_ERROR"
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/posts', function (req, res) {
|
||||
res.sendStatus(200);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
let response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: error,
|
||||
message: message
|
||||
}
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -10,147 +10,75 @@ var multer = require('multer');
|
|||
const snowflake = require('node-snowflake').Snowflake;
|
||||
var upload = multer();
|
||||
|
||||
/* GET post titles. */
|
||||
router.post('/', upload.none(), async function (req, res, next) {
|
||||
try
|
||||
{
|
||||
let paramPackData = util.data.decodeParamPack(req.headers["x-nintendo-parampack"]);
|
||||
let pid = util.data.processServiceToken(req.headers["x-nintendo-servicetoken"]);
|
||||
if(pid === null)
|
||||
{
|
||||
throw new Error('The User token was not valid');
|
||||
}
|
||||
else
|
||||
{
|
||||
let conversation = await database.getConversation(pid.toString(), req.body.message_to_pid.toString())
|
||||
if(!conversation) {
|
||||
let user = await database.getUserByPID(pid);
|
||||
let user2 = await database.getUserByPID(req.body.message_to_pid);
|
||||
if(!user || !user2)
|
||||
return res.sendStatus(422)
|
||||
let doc = {
|
||||
message_preview: req.body.body,
|
||||
pids: [
|
||||
{
|
||||
pid: user.pid.toString(),
|
||||
official: user.official,
|
||||
screen_name: user.user_id,
|
||||
read: true
|
||||
},
|
||||
{
|
||||
pid: user2.pid.toString(),
|
||||
official: user2.official,
|
||||
screen_name: user2.user_id,
|
||||
read: false
|
||||
}
|
||||
]
|
||||
}
|
||||
const newConversation = new CONVERSATION(doc);
|
||||
await newConversation.save();
|
||||
}
|
||||
else {
|
||||
let messageType = '';
|
||||
if(req.body.screenshot)
|
||||
messageType = '(Screenshot)';
|
||||
else if(req.body.painting)
|
||||
messageType = '(Drawing)';
|
||||
else
|
||||
messageType = req.body.body;
|
||||
await conversation.newMessage(messageType, req.pid.toString())
|
||||
}
|
||||
conversation = await database.getConversation(pid.toString(), req.body.message_to_pid.toString())
|
||||
let user = await util.data.processUser(pid);
|
||||
let appData = "";
|
||||
if (req.body.app_data) {
|
||||
appData = req.body.app_data.replace(/\0/g, "").trim();
|
||||
}
|
||||
let painting = "";
|
||||
if (req.body.painting) {
|
||||
painting = req.body.painting.replace(/\0/g, "").trim();
|
||||
}
|
||||
let paintingURI = "";
|
||||
if (req.body.painting) {
|
||||
paintingURI = await util.data.processPainting(painting);
|
||||
}
|
||||
let screenshot = "";
|
||||
if (req.body.screenshot) {
|
||||
screenshot = req.body.screenshot.replace(/\0/g, "").trim();
|
||||
}
|
||||
|
||||
let miiFace;
|
||||
console.log(parseInt(req.body.feeling_id))
|
||||
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;
|
||||
}
|
||||
|
||||
const document = {
|
||||
title_id: paramPackData.title_id,
|
||||
screen_name: user.user_id,
|
||||
body: req.body.body,
|
||||
app_data: appData,
|
||||
painting: painting,
|
||||
painting_uri: paintingURI,
|
||||
screenshot: screenshot,
|
||||
url: req.body.url,
|
||||
search_key: req.body.search_key,
|
||||
topic_tag: req.body.topic_tag,
|
||||
country_id: paramPackData.country_id,
|
||||
created_at: new Date(),
|
||||
feeling_id: req.body.feeling_id,
|
||||
id: snowflake.nextId(),
|
||||
is_autopost: req.body.is_autopost,
|
||||
is_spoiler: req.body.is_spoiler,
|
||||
is_app_jumpable: req.body.is_app_jumpable,
|
||||
language_id: req.body.language_id,
|
||||
mii: user.mii,
|
||||
mii_face_url: `http://mii.olv.pretendo.cc/mii/${user.pid}/${miiFace}`,
|
||||
pid: user.pid,
|
||||
verified: user.official,
|
||||
platform_id: paramPackData.platform_id,
|
||||
region_id: paramPackData.region_id,
|
||||
parent: null,
|
||||
message_to_pid: req.body.message_to_pid,
|
||||
conversation_id: conversation.id
|
||||
};
|
||||
const newPost = new POST(document);
|
||||
newPost.save();
|
||||
res.sendStatus(200);
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.error(e);
|
||||
res.set("Content-Type", "application/xml");
|
||||
res.statusCode = 400;
|
||||
response = {
|
||||
result: {
|
||||
has_error: 1,
|
||||
version: 1,
|
||||
code: 400,
|
||||
error_code: 7,
|
||||
message: "POSTING_FROM_NNID"
|
||||
}
|
||||
let user = await database.getPNID(req.pid);
|
||||
let user2 = await database.getPNID(req.body.message_to_pid);
|
||||
let conversation = await database.getConversationByUsers([user.pid, user2.pid]);
|
||||
if(!conversation) {
|
||||
if(!user || !user2)
|
||||
return res.sendStatus(422)
|
||||
let document = {
|
||||
id: snowflake.nextId(),
|
||||
users: [
|
||||
{
|
||||
pid: user.pid,
|
||||
official: (user.access_level === 2 || user.access_level === 3),
|
||||
read: true
|
||||
},
|
||||
{
|
||||
pid: user2.pid,
|
||||
official: (user2.access_level === 2 || user2.access_level === 3),
|
||||
read: false
|
||||
},
|
||||
]
|
||||
};
|
||||
res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
const newConversations = new CONVERSATION(document);
|
||||
await newConversations.save();
|
||||
conversation = await database.getConversationByID(document.id);
|
||||
}
|
||||
if(!conversation)
|
||||
return res.sendStatus(404);
|
||||
let document = {
|
||||
screen_name: user.mii.name,
|
||||
body: req.body.body,
|
||||
painting: req.body.raw,
|
||||
created_at: new Date(),
|
||||
id: snowflake.nextId(),
|
||||
mii: user.mii.data,
|
||||
mii_face_url: `https://mii.olv.pretendo.cc/${user.pid}/normal_face.png`,
|
||||
pid: user.pid,
|
||||
verified: (user.access_level === 2 || user.access_level === 3),
|
||||
parent: null,
|
||||
search_key: req.body.search_key,
|
||||
community_id: conversation.id,
|
||||
message_to_pid: req.body.message_to_pid
|
||||
};
|
||||
const newPost = new POST(document);
|
||||
newPost.save();
|
||||
res.sendStatus(200);
|
||||
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, document.message_to_pid);
|
||||
});
|
||||
|
||||
router.get('/', async function(req, res, next) {
|
||||
let limit = parseInt(req.query.limit), type = req.query.type, search_key = req.query.search_key, by = req.query.by;
|
||||
let posts = await database.getFriendMessages(req.pid, search_key, limit);
|
||||
|
||||
res.set("Content-Type", "application/xml");
|
||||
let response = {
|
||||
result: {
|
||||
has_error: 0,
|
||||
version: 1,
|
||||
posts: posts.length === 0 ? " " : posts
|
||||
}
|
||||
};
|
||||
return res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
|
||||
});
|
||||
|
||||
router.post('/:post_id/empathies', upload.none(), async function (req, res, next) {
|
||||
|
|
|
|||
|
|
@ -194,14 +194,17 @@ class CommunityPostGen {
|
|||
.e("expire", expirationDate.format('YYYY-MM-DD HH:MM:SS')).up()
|
||||
.e("topics");
|
||||
for (const community of communities) {
|
||||
let posts = await database.getPostsByCommunity(community, 30);
|
||||
console.log(community.name)
|
||||
let posts = await database.getNumberNewCommunityPostsByID(community, 30);
|
||||
console.log(posts);
|
||||
xml = xml.e('topic')
|
||||
.e('empathy_count', community.empathy_count).up()
|
||||
.e('has_shop_page', community.has_shop_page).up()
|
||||
.e('icon', community.icon).up()
|
||||
.e('title_ids');
|
||||
community.title_ids.forEach(function (title_id) {
|
||||
xml = xml.e('title_id', title_id).up()
|
||||
if(title_id !== '')
|
||||
xml = xml.e('title_id', title_id).up();
|
||||
})
|
||||
xml = xml.up()
|
||||
.e('title_id', community.title_ids[0]).up()
|
||||
|
|
@ -218,38 +221,38 @@ class CommunityPostGen {
|
|||
.e("post")
|
||||
.e("body", newBody).up()
|
||||
.e("community_id", community.community_id).up()
|
||||
.e("country_id", post.country_id).up()
|
||||
.e("country_id", post.country_id || 0).up()
|
||||
.e("created_at", moment(post.created_at).format('YYYY-MM-DD HH:MM:SS')).up()
|
||||
.e("feeling_id", post.feeling_id).up()
|
||||
.e("id", post.id).up()
|
||||
.e("id", '').up()
|
||||
.e("is_autopost", post.is_autopost).up()
|
||||
.e("is_community_private_autopost", post.is_community_private_autopost).up()
|
||||
.e("is_spoiler", post.is_spoiler).up()
|
||||
.e("is_app_jumpable", post.is_app_jumpable).up()
|
||||
.e("empathy_count", post.empathy_count).up()
|
||||
.e("language_id", post.language_id).up()
|
||||
.e("mii", post.mii).up()
|
||||
.e("mii_face_url", "https://s3.amazonaws.com/olv-public/pap/WVW69koebmETvBVqm1").up();
|
||||
.e("mii", post.mii.toString().replace(/[\n\r]+/gm, '')).up()
|
||||
.e("mii_face_url", post.mii_face_url).up();
|
||||
xml = xml.e("number", "0").up();
|
||||
if (post.painting) {
|
||||
xml = xml.e("painting")
|
||||
.e("format", "tga").up()
|
||||
.e("content", post.painting.replace( /[\r\n]+/gm, "" )).up()
|
||||
.e("size", post.painting.length).up()
|
||||
.e("url", "https://s3.amazonaws.com/olv-public/pap/WVW69koebmETvBVqm1").up()
|
||||
.e("url", `https://cdn.pretendo.cc/paintings/${post.pid}/${post.id}.png`).up()
|
||||
.up();
|
||||
}
|
||||
xml = xml.e("pid", post.pid).up()
|
||||
.e("platform_id", post.platform_id).up()
|
||||
.e("region_id", post.region_id).up()
|
||||
.e("reply_count", post.reply_count).up()
|
||||
.e("screen_name", post.screen_name).up()
|
||||
.e("screen_name", 'placeholder').up()
|
||||
.e("title_id", post.title_id).up()
|
||||
.up().up().up();
|
||||
}
|
||||
xml = xml.up().up()
|
||||
}
|
||||
return xml.end({ pretty: true, allowEmpty: true });
|
||||
return xml.end({ pretty: false, allowEmpty: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
289
src/util/util.js
289
src/util/util.js
|
|
@ -2,56 +2,56 @@ const crypto = require('crypto');
|
|||
const NodeRSA = require('node-rsa');
|
||||
const fs = require('fs-extra');
|
||||
const database = require('../database');
|
||||
const logger = require('../logger');
|
||||
const config = require('../../config.json');
|
||||
const xmlParser = require('xml2json');
|
||||
const request = require("request");
|
||||
const moment = require('moment');
|
||||
const { USER } = require('../models/user');
|
||||
const { SETTINGS } = require('../models/settings');
|
||||
const { CONTENT } = require('../models/content');
|
||||
const { NOTIFICATIONS } = require('../models/notifications');
|
||||
let TGA = require('tga');
|
||||
let pako = require('pako');
|
||||
let PNG = require('pngjs').PNG;
|
||||
const path = require('path')
|
||||
let bmp = require("bmp-js");
|
||||
const aws = require('aws-sdk');
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
async function saveNotification(pid, type, title, content, reference_id, link) {
|
||||
let notification = {
|
||||
pid: pid,
|
||||
type: type,
|
||||
title: title,
|
||||
content: content,
|
||||
reference_id: reference_id,
|
||||
link: link,
|
||||
}
|
||||
let newNotification = new NOTIFICATIONS(notification);
|
||||
return await newNotification.save();
|
||||
}
|
||||
|
||||
let methods = {
|
||||
processUser: function(pid) {
|
||||
return new Promise(async function(resolve, reject) {
|
||||
let userObject = await database.getUserByPID(pid);
|
||||
if(userObject != null)
|
||||
resolve(userObject);
|
||||
else
|
||||
{
|
||||
await request({
|
||||
url: "http://" + config.account_server + "/v1/api/miis?pids=" + pid,
|
||||
headers: {
|
||||
'X-Nintendo-Client-ID': 'a2efa818a34fa16b8afbc8a74eba3eda',
|
||||
'X-Nintendo-Client-Secret': 'c91cdb5658bd4954ade78533a339cf9a'
|
||||
}
|
||||
}, function (error, response, body) {
|
||||
if (!error && response.statusCode === 200) {
|
||||
let xml = xmlParser.toJson(body, {object: true});
|
||||
const newUsr = {
|
||||
pid: pid,
|
||||
created_at: moment().format('YYYY-MM-DD HH:mm:SS'),
|
||||
user_id: xml.miis.mii.user_id,
|
||||
account_status: 0,
|
||||
mii: xml.miis.mii.data,
|
||||
official: false
|
||||
};
|
||||
const newUsrObj = new USER(newUsr);
|
||||
newUsrObj.save();
|
||||
resolve(newUsr);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('fail');
|
||||
reject();
|
||||
}
|
||||
create_user: async function(pid, experience, notifications, region) {
|
||||
const pnid = await database.getPNID(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 newContentObj = new CONTENT(newContent);
|
||||
await newContentObj.save();
|
||||
},
|
||||
decodeParamPack: function (paramPack) {
|
||||
/* Decode base64 */
|
||||
|
|
@ -74,7 +74,7 @@ let methods = {
|
|||
}
|
||||
catch(e)
|
||||
{
|
||||
console.log(e)
|
||||
//console.log(e)
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ let methods = {
|
|||
// Access and refresh tokens use a different format since they must be much smaller
|
||||
// Assume a small length means access or refresh token
|
||||
if (token.length <= 32) {
|
||||
const cryptoPath = path.normalize(`${__dirname}/../certs/access`);
|
||||
const cryptoPath = `${__dirname}/../certs/access`;
|
||||
const aesKey = Buffer.from(fs.readFileSync(`${cryptoPath}/aes.key`, { encoding: 'utf8' }), 'hex');
|
||||
|
||||
const iv = Buffer.alloc(16);
|
||||
|
|
@ -96,11 +96,11 @@ let methods = {
|
|||
return decryptedBody;
|
||||
}
|
||||
|
||||
const cryptoPath = path.normalize(`${__dirname}/../certs/access`);
|
||||
const cryptoPath = `${__dirname}/../certs/access`;
|
||||
|
||||
const cryptoOptions = {
|
||||
private_key: fs.readFileSync(`${cryptoPath}/private.pem`),
|
||||
hmac_secret: config.secret
|
||||
hmac_secret: config.account_server_secret
|
||||
};
|
||||
|
||||
const privateKey = new NodeRSA(cryptoOptions.private_key, 'pkcs1-private-pem', {
|
||||
|
|
@ -123,42 +123,183 @@ let methods = {
|
|||
Buffer.from(encryptedAESKey.subarray(point2, point2 + 8))
|
||||
]);
|
||||
|
||||
const decryptedAESKey = privateKey.decrypt(encryptedAESKey);
|
||||
try {
|
||||
const decryptedAESKey = privateKey.decrypt(encryptedAESKey);
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes-128-cbc', decryptedAESKey, iv);
|
||||
const decipher = crypto.createDecipheriv('aes-128-cbc', decryptedAESKey, iv);
|
||||
|
||||
let decryptedBody = decipher.update(encryptedBody);
|
||||
decryptedBody = Buffer.concat([decryptedBody, decipher.final()]);
|
||||
let decryptedBody = decipher.update(encryptedBody);
|
||||
decryptedBody = Buffer.concat([decryptedBody, decipher.final()]);
|
||||
|
||||
const hmac = crypto.createHmac('sha1', cryptoOptions.hmac_secret).update(decryptedBody);
|
||||
const calculatedSignature = hmac.digest();
|
||||
const hmac = crypto.createHmac('sha1', cryptoOptions.hmac_secret).update(decryptedBody);
|
||||
const calculatedSignature = hmac.digest();
|
||||
|
||||
if (Buffer.compare(calculatedSignature, signature) !== 0) {
|
||||
console.log('Token signature did not match');
|
||||
if (Buffer.compare(calculatedSignature, signature) !== 0) {
|
||||
//console.log('Token signature did not match');
|
||||
return null;
|
||||
}
|
||||
|
||||
return decryptedBody;
|
||||
}
|
||||
catch (e) {
|
||||
//console.log('Failed to decrypt token. Probably a NNID from the topics request');
|
||||
return null;
|
||||
}
|
||||
},
|
||||
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 = new TGA(Buffer.from(output));
|
||||
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 decryptedBody;
|
||||
},
|
||||
processPainting: function (painting) {
|
||||
let paintingBuffer = Buffer.from(painting, 'base64');
|
||||
let output = '';
|
||||
try
|
||||
{
|
||||
output = pako.inflate(paintingBuffer);
|
||||
let output;
|
||||
try
|
||||
{
|
||||
output = pako.deflate(tga, {level: 6});
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return new Buffer(output).toString('base64')
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
console.error(err);
|
||||
}
|
||||
let tga = new TGA(Buffer.from(output));
|
||||
let png = new PNG({
|
||||
width: tga.width,
|
||||
height: tga.height
|
||||
});
|
||||
png.data = tga.pixels;
|
||||
let pngBuffer = PNG.sync.write(png);
|
||||
return `data:image/png;base64,${pngBuffer.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');
|
||||
},
|
||||
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);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
},
|
||||
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(pid, type, reference_id, origin_pid, title, content) {
|
||||
let user = await database.getUserSettings(origin_pid);
|
||||
/**
|
||||
* 0 like
|
||||
* 1 reply
|
||||
* 2 new follower
|
||||
* 3 other
|
||||
*/
|
||||
|
||||
if(type === 1)
|
||||
return await saveNotification(pid, type, `${user.screen_name} Replied to your post.`, content, reference_id, `/posts/${reference_id}`);
|
||||
else if(type === 2)
|
||||
return await saveNotification(pid, type, `${user.screen_name} Followed you!`, '', reference_id, `/users/show?pid=${origin_pid}`);
|
||||
|
||||
let lastNotification = await database.getLastNotification(pid);
|
||||
if(lastNotification && lastNotification.type === 0 && lastNotification.reference_id === reference_id) {
|
||||
let post = await database.getPostByID(reference_id);
|
||||
let newTitle = '';
|
||||
switch (post.empathy_count) {
|
||||
case 1:
|
||||
newTitle = `${user.screen_name} Yeahed your post!`;
|
||||
break;
|
||||
case 2:
|
||||
newTitle = `${user.screen_name} and 1 other Yeahed your post!`;
|
||||
break;
|
||||
default:
|
||||
newTitle = `${user.screen_name} and ${post.empathy_count - 1} others Yeahed your post!`;
|
||||
break;
|
||||
}
|
||||
lastNotification.title = newTitle;
|
||||
await lastNotification.save();
|
||||
}
|
||||
else if(type === 0) {
|
||||
let post = await database.getPostByID(reference_id);
|
||||
let newTitle = '';
|
||||
switch (post.empathy_count) {
|
||||
case 1:
|
||||
newTitle = `${user.screen_name} Yeahed your post!`;
|
||||
break;
|
||||
case 2:
|
||||
newTitle = `${user.screen_name} and 1 other Yeahed your post!`;
|
||||
break;
|
||||
default:
|
||||
newTitle = `${user.screen_name} and ${post.empathy_count - 1} others Yeahed your post!`;
|
||||
break;
|
||||
}
|
||||
let newContent;
|
||||
if(!post.body) {
|
||||
if(post.screenshot)
|
||||
newContent = 'Screenshot Post';
|
||||
else if(post.painting)
|
||||
newContent = 'Drawing Post';
|
||||
}
|
||||
else
|
||||
newContent = post.body;
|
||||
return await saveNotification(pid, type, newTitle, newContent, reference_id, `/posts/${post.id}`);
|
||||
}
|
||||
else
|
||||
return await saveNotification(pid, type, title, content, reference_id, '');
|
||||
|
||||
}
|
||||
};
|
||||
exports.data = methods;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user