Updated schema. Fixed request for friends not working. Added check to discovery server on auth to reject other requests when discovery is closed.

This commit is contained in:
Jemma Poffinbarger 2023-04-16 02:36:21 -05:00
parent f50bf61abf
commit 9acc8b13f4
17 changed files with 185 additions and 372 deletions

10
package-lock.json generated
View File

@ -17,14 +17,14 @@
"express-session": "^1.17.0",
"express-subdomain": "^1.0.5",
"fs-extra": "^9.0.0",
"grpc": "github:PretendoNetwork/grpc-js",
"grpc": "github:pretendonetwork/grpc-js",
"memoizee": "^0.4.15",
"moment": "^2.24.0",
"moment-timezone": "^0.5.27",
"mongoose": "^6.10.1",
"mongoose-unique-validator": "^3.1.0",
"morgan": "^1.10.0",
"nice-grpc": "^2.1.4",
"nice-grpc": "^2.0.0",
"node-rsa": "^1.0.8",
"node-snowflake": "0.0.1",
"pako": "^1.0.11",
@ -2469,7 +2469,7 @@
},
"node_modules/grpc": {
"version": "1.0.0",
"resolved": "git+ssh://git@github.com/PretendoNetwork/grpc-js.git#b62c199fe3455a0d200a8630cd3ae3a97b1e40fc",
"resolved": "git+ssh://git@github.com/pretendonetwork/grpc-js.git#cb47c0af73a41c99efa56a24ea386537121cdd0f",
"license": "ISC",
"dependencies": {
"@grpc/grpc-js": "^1.6.9",
@ -6297,8 +6297,8 @@
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
},
"grpc": {
"version": "git+ssh://git@github.com/PretendoNetwork/grpc-js.git#b62c199fe3455a0d200a8630cd3ae3a97b1e40fc",
"from": "grpc@github:PretendoNetwork/grpc-js",
"version": "git+ssh://git@github.com/pretendonetwork/grpc-js.git#cb47c0af73a41c99efa56a24ea386537121cdd0f",
"from": "grpc@https://github.com/pretendonetwork/grpc-js",
"requires": {
"@grpc/grpc-js": "^1.6.9",
"@grpc/proto-loader": "^0.7.0",

View File

@ -19,14 +19,14 @@
"express-session": "^1.17.0",
"express-subdomain": "^1.0.5",
"fs-extra": "^9.0.0",
"grpc": "github:PretendoNetwork/grpc-js",
"grpc": "github:pretendonetwork/grpc-js",
"memoizee": "^0.4.15",
"moment": "^2.24.0",
"moment-timezone": "^0.5.27",
"mongoose": "^6.10.1",
"mongoose-unique-validator": "^3.1.0",
"morgan": "^1.10.0",
"nice-grpc": "^2.1.4",
"nice-grpc": "^2.0.0",
"node-rsa": "^1.0.8",
"node-snowflake": "0.0.1",
"pako": "^1.0.11",

View File

@ -68,7 +68,7 @@ async function getCommunityByTitleID(title_id) {
async function getCommunityByID(community_id) {
verifyConnected();
return COMMUNITY.findOne({
community_id: community_id
olive_community_id: community_id
});
}
@ -191,7 +191,7 @@ async function getNumberVerifiedCommunityPostsByID(community, limit, offset) {
async function getPostsByCommunity(community, numberOfPosts) {
verifyConnected();
return POST.find({
community_id: community.community_id,
community_id: community.olive_community_id,
parent: null,
removed: false,
app_data: { $ne: null }
@ -201,7 +201,7 @@ async function getPostsByCommunity(community, numberOfPosts) {
async function getPostsByCommunityKey(community, numberOfPosts, search_key) {
verifyConnected();
return POST.find({
community_id: community.community_id,
community_id: community.olive_community_id,
search_key: search_key,
parent: null,
removed: false,
@ -212,7 +212,7 @@ async function getPostsByCommunityKey(community, numberOfPosts, search_key) {
async function getNewPostsByCommunity(community, limit, offset) {
verifyConnected();
return POST.find({
community_id: community.community_id,
community_id: community.olive_community_id,
parent: null,
removed: false,
app_data: { $ne: null }

View File

@ -1,8 +1,9 @@
const config = require('../../config.json');
const util = require('../util/util');
const xml = require("object-to-xml");
const db = require("../database");
function auth(req, res, next) {
async function auth(req, res, next) {
if(/*req.path.includes('/topics') || */req.path.includes('/v1/status'))
return next();
const token = req.headers["x-nintendo-servicetoken"] || req.headers['olive service token'];
@ -24,6 +25,14 @@ function auth(req, res, next) {
if(pid === null)
badAuth(res);
else {
let user = await db.getPNID(pid), discovery;
if(user)
discovery = await db.getEndPoint(user.server_access_level);
else
discovery = await db.getEndPoint('prod');
if(discovery.status !== 0) return serverError(res, discovery);
req.pid = pid;
req.paramPackData = paramPackData;
return next();
@ -46,4 +55,71 @@ function badAuth(res) {
return res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
}
function serverError(res, discovery) {
let message = '', error = 0;
switch(discovery.status) {
case 0 :
res.set("Content-Type", "application/xml");
let response = {
result: {
has_error: 0,
version: 1,
endpoint: {
host: discovery.host,
api_host: discovery.api_host,
portal_host: discovery.portal_host,
n3ds_host: discovery.n3ds_host
}
}
};
return res.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml(response));
case 1 :
message = 'SYSTEM_UPDATE_REQUIRED';
error = 1;
break;
case 2 :
message = 'SETUP_NOT_COMPLETE';
error = 2;
break;
case 3 :
message = 'SERVICE_MAINTENANCE';
error = 3;
break;
case 4:
message = 'SERVICE_CLOSED';
error = 4;
break;
case 5 :
message = 'PARENTAL_CONTROLS_ENABLED';
error = 5;
break;
case 6 :
message = 'POSTING_LIMITED_PARENTAL_CONTROLS';
error = 6;
break;
case 7 :
message = 'NNID_BANNED';
error = 7;
res.set("Content-Type", "application/xml");
break;
default :
message = 'SERVER_ERROR';
error = 15;
res.set("Content-Type", "application/xml");
break;
}
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 = auth;

View File

@ -8,6 +8,10 @@ const CommunitySchema = new Schema({
type: Boolean,
default: true
},
allows_comments: {
type: Boolean,
default: true
},
/**
* 0: Main Community
* 1: Sub-Community
@ -23,7 +27,7 @@ const CommunitySchema = new Schema({
default: null
},
admins: {
type: [String],
type: [Number],
default: undefined
},
created_at: {
@ -38,10 +42,6 @@ const CommunitySchema = new Schema({
type: Number,
default: 0
},
id: {
type: Number,
default: 0
},
has_shop_page: {
type: Number,
default: 0
@ -56,16 +56,12 @@ const CommunitySchema = new Schema({
default: undefined
},
community_id: String,
olive_community_id: String,
is_recommended: {
type: Number,
default: 0
},
browser_icon: String,
browser_thumbnail: String,
CTR_browser_header: String,
WiiU_browser_header: String,
app_data: String,
app_id: String
});
CommunitySchema.methods.upEmpathy = async function() {

View File

@ -1,37 +1,21 @@
const { Schema, model } = require('mongoose');
const ContentSchema = new Schema({
pid: String,
likes: {
type: [String],
default: [0]
},
pid: Number,
followed_communities: {
type: [String],
default: [0]
},
followed_users: {
type: [String],
type: [Number],
default: [0]
},
following_users: {
type: [String],
type: [Number],
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);

View File

@ -29,9 +29,9 @@ NotificationsSchema.methods.markRead = async function() {
await this.save();
};
const NOTIFICATIONS = model('NOTIFICATIONS', NotificationsSchema);
const NOTIFICATION = model('NOTIFICATION', NotificationsSchema);
module.exports = {
NotificationsSchema,
NOTIFICATIONS
NOTIFICATION
};

View File

@ -21,6 +21,17 @@ const PNIDSchema = new mongoose.Schema({
name: String,
data: String,
},
connections: {
stripe: {
customer_id: String,
subscription_id: String,
price_id: String,
tier_level: Number,
tier_name: String,
latest_webhook_timestamp: Number
}
}
});
const PNID = pnidConnection.model('PNID', PNIDSchema);
@ -28,3 +39,4 @@ const PNID = pnidConnection.model('PNID', PNIDSchema);
module.exports = {
PNID,
};

View File

@ -41,7 +41,8 @@ const PostSchema = new Schema({
},
empathy_count: {
type: Number,
default: 0
default: 0,
min: 0
},
country_id: {
type: Number,
@ -73,7 +74,8 @@ const PostSchema = new Schema({
type: Boolean,
default: false
},
removed_reason: String
removed_reason: String,
yeahs: [Number]
});
PostSchema.methods.upReply = async function() {

18
src/models/report.js Normal file
View File

@ -0,0 +1,18 @@
const { Schema, model } = require('mongoose');
const ReportSchema = new Schema({
pid: String,
post_id: String,
reason: Number,
created_at: {
type: Date,
default: new Date()
}
});
const REPORT = model('REPORT', ReportSchema);
module.exports = {
ReportSchema,
REPORT
};

View File

@ -1,7 +1,7 @@
const { Schema, model } = require('mongoose');
const SettingsSchema = new Schema({
pid: String,
pid: Number,
screen_name: String,
account_status: {
type: Number,

View File

@ -1,278 +0,0 @@
const { Schema, model } = require('mongoose');
const notification = new Schema({
content: String,
link: String,
read: Boolean,
created_at: Date,
});
const UserSchema = new Schema({
pid: Number,
created_at: Date,
user_id: String,
pnid: String,
birthday: Date,
country: String,
pfp_uri: String,
mii: String,
mii_face_url: String,
/**
* Account Status
* 0 - Fine
* 1 - Limited from Posting
* 2 - Temporary Ban
* 3 - Forever Ban
*/
account_status: {
type: Number,
default: 0
},
ban_lift_date: Date,
ban_reason: String,
official: {
type: Boolean,
default: false
},
profile_comment: {
type: String,
default: undefined
},
game_skill: {
type: Number,
default: 0
},
game_skill_visibility: {
type: Boolean,
default: true
},
profile_comment_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
},
notifications: {
type: Boolean,
default: false
},
likes: {
type: [String],
default: [0]
},
followed_communities: {
type: [String],
default: [0]
},
followed_users: {
type: [String],
default: [0]
},
following_users: {
type: [String],
default: [0]
},
followers: {
type: Number,
default: 0
},
following: {
type: Number,
default: 0
},
notification_list: {
type: [notification],
default: [{
content: 'This is your notifications! You\'ll see more stuff here soon!',
link: '/users/me',
read: false,
created_at: new Date(),
}]
}
});
UserSchema.methods.getAccountStatus = async function() {
return this.get('account_status');
};
UserSchema.methods.setAccountStatus = async function(accountStatus) {
this.set('account_status', accountStatus);
await this.save();
};
UserSchema.methods.getBanDate = async function() {
return this.get('ban_lift_date');
};
UserSchema.methods.setBanData = async function(banDate) {
this.set('ban_lift_date', banDate);
await this.save();
};
UserSchema.methods.getProfileComment = async function() {
return this.get('profile_comment');
};
UserSchema.methods.setProfileComment = async function(profileComment) {
this.set('profile_comment', profileComment);
await this.save();
};
UserSchema.methods.getGameSkill = async function() {
return this.get('game_skill');
};
UserSchema.methods.setGameSkill = async function(gameSkill) {
this.set('game_skill', gameSkill);
await this.save();
};
UserSchema.methods.getGameSkillVisibility = async function() {
return this.get('game_skill_visibility');
};
UserSchema.methods.setGameSkillVisibility = async function(gameSkillVisibility) {
this.set('game_skill_visibility', gameSkillVisibility);
await this.save();
};
UserSchema.methods.getProfileCommentVisibility = async function() {
return this.get('profile_comment_visibility');
};
UserSchema.methods.setProfileCommentVisibility = async function(profileCommentVisibility) {
this.set('profile_comment_visibility', profileCommentVisibility);
await this.save();
};
UserSchema.methods.getBirthdayVisibility = async function() {
return this.get('birthday_visibility');
};
UserSchema.methods.setBirthdayVisibility = async function(birthdayVisibility) {
this.set('birthday_visibility', birthdayVisibility);
await this.save();
};
UserSchema.methods.getRelationshipVisibility = async function() {
return this.get('relationship_visibility');
};
UserSchema.methods.setRelationshipVisibility = async function(accountStatus) {
this.set('relationship_visibility', accountStatus);
await this.save();
};
UserSchema.methods.getFavoriteCommunityVisibility = async function() {
return this.get('profile_favorite_community_visibility');
};
UserSchema.methods.setFavoriteCommunityVisibility = async function(favoriteCommunityVisibility) {
this.set('profile_favorite_community_visibility', favoriteCommunityVisibility);
await this.save();
};
UserSchema.methods.getCountryVisibility = async function() {
return this.get('country_visibility');
};
UserSchema.methods.setCountryVisibility = async function(countryVisibility) {
this.set('country_visibility', countryVisibility);
await this.save();
};
UserSchema.methods.addToLikes = async function(postID) {
const likes = this.get('likes');
likes.addToSet(postID);
await this.save();
}
UserSchema.methods.removeFromLike = async function(postID) {
const likes = this.get('likes');
likes.pull(postID);
await this.save();
}
UserSchema.methods.addToCommunities = async function(postID) {
const communities = this.get('followed_communities');
communities.addToSet(postID);
await this.upFollowing();
await this.save();
}
UserSchema.methods.removeFromCommunities = async function(postID) {
const communities = this.get('followed_communities');
communities.pull(postID);
await this.downFollowing();
await this.save();
}
UserSchema.methods.addToUsers = async function(postID) {
const users = this.get('followed_users');
users.addToSet(postID);
await this.upFollowing();
await this.save();
}
UserSchema.methods.removeFromUsers = async function(postID) {
const users = this.get('followed_users');
users.pull(postID);
await this.downFollowing();
await this.save();
}
UserSchema.methods.addToFollowers = async function(postID) {
const users = this.get('following_users');
users.addToSet(postID);
await this.upFollower();
await this.save();
}
UserSchema.methods.removeFromFollowers = async function(postID) {
const users = this.get('following_users');
users.pull(postID);
await this.downFollower();
await this.save();
}
UserSchema.methods.upFollower = async function() {
const followers = this.get('followers');
this.set('followers', followers + 1);
await this.save();
};
UserSchema.methods.downFollower = async function() {
const followers = this.get('followers');
if(followers > 0)
this.set('followers', followers - 1);
await this.save();
};
UserSchema.methods.upFollowing = async function() {
const following = this.get('following');
this.set('following', following + 1);
await this.save();
};
UserSchema.methods.downFollowing = async function() {
const following = this.get('following');
if(following > 0)
this.set('following', following - 1);
await this.save();
};
const USER = model('USER', UserSchema);
module.exports = {
UserSchema,
USER
};

View File

@ -12,7 +12,7 @@ router.get('/', async function (req, res) {
let community = await database.getCommunityByTitleID(paramPack.title_id);
if(!community) res.sendStatus(404);
let communities = await database.getSubCommunities(community.community_id);
let communities = await database.getSubCommunities(community.olive_community_id);
if(!communities) res.sendStatus(404);
communities.unshift(community);
let response = await comPostGen.Communities(communities);
@ -38,13 +38,13 @@ router.get('/new', async function (req, res) {
router.get('/:appID/posts', async function (req, res) {
const paramPack = util.decodeParamPack(req.headers["x-nintendo-parampack"]);
let community = await COMMUNITY.findOne({ app_id: req.params.appID });
let community = await COMMUNITY.findOne({ community_id: req.params.appID });
if(!community)
community = await database.getCommunityByTitleID(paramPack.title_id);
if(!community)
res.sendStatus(404);
let query = {
community_id: community.app_id ? community.app_id : community.community_id,
community_id: community.olive_community_id,
removed: false,
app_data: { $ne: null },
message_to_pid: { $eq: null }

View File

@ -66,7 +66,7 @@ router.post('/', upload.none(), async function (req, res) {
created_at: new Date(),
id: snowflake.nextId(),
mii: user.mii.data,
mii_face_url: `http://mii.olv.pretendo.cc/mii/${PNID.pid}/${miiFace}`,
mii_face_url: `https://mii.olv.pretendo.cc/mii/${PNID.pid}/${miiFace}`,
pid: user.pid,
verified: (user.access_level === 2 || user.access_level === 3),
parent: null,

View File

@ -30,42 +30,39 @@ router.post('/:post_id.delete', async function (req, res) {
});
router.post('/:post_id/empathies', upload.none(), async function (req, res) {
let pid = util.processServiceToken(req.headers["x-nintendo-servicetoken"]);
const post = await database.getPostByID(req.params.post_id);
if(pid === null) {
res.sendStatus(403);
return;
if(!post) 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
}
});
}
let userContent = await database.getUserContent(req.pid);
if(userContent.likes.indexOf(post.id) === -1 && userContent.pid !== post.pid)
{
if(post.empathy_count < 0) {
await POST.updateOne(
{ id: post.id },
{ $set: { empathy_count: 1 } }
);
}
else {
await POST.updateOne(
{ id: post.id },
{ $inc: { empathy_count: 1 } }
);
}
userContent.addToLikes(post.id);
} else if(userContent.likes.indexOf(post.id) !== -1 && userContent.pid !== post.pid) {
if(post.empathy_count < 0) {
await POST.updateOne(
{ id: post.id },
{ $set: { empathy_count: 0 } }
);
}
else {
await POST.updateOne(
{ id: post.id },
{ $inc: { empathy_count: -1 } }
);
}
userContent.removeFromLike(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.sendStatus(200);
});
@ -115,7 +112,7 @@ async function newPost(req, res) {
let community = await database.getCommunityByID(community_id)
if(!community)
community = await COMMUNITY.findOne({app_id: community_id});
community = await COMMUNITY.findOne({olive_community_id: community_id});
if(!community)
community = await database.getCommunityByTitleID(paramPackData.title_id);
@ -125,9 +122,13 @@ async function newPost(req, res) {
parentPost = await database.getPostByID(req.params.post_id.toString());
if(!parentPost)
return res.sendStatus(403);
parentPost.reply_count = parentPost.reply_count + 1;
parentPost.save();
}
if(!(community.admins && community.admins.indexOf(req.pid) !== -1 && userSettings.account_status === 0)
&& (community.type >= 2) && !(parentPost && community.allows_comments && community.open)) {
return res.sendStatus(403);
}
let appData = "", painting = "", paintingURI = "", screenshot = null;
if (req.body.app_data)
appData = req.body.app_data.replace(/[^A-Za-z0-9+/=\s]/g, "");
@ -171,7 +172,7 @@ async function newPost(req, res) {
return res.sendStatus(400);
const document = {
title_id: paramPackData.title_id,
community_id: community.app_id ? community.app_id : community.community_id,
community_id: community.olive_community_id,
screen_name: userSettings.screen_name,
body: body,
app_data: appData,
@ -214,6 +215,10 @@ async function newPost(req, res) {
}
const newPost = new POST(document);
newPost.save();
if(parentPost) {
parentPost.reply_count = parentPost.reply_count + 1;
parentPost.save();
}
res.send(await communityPostGen.SinglePostResponse(newPost));
}

View File

@ -16,7 +16,6 @@ const PNG = require('pngjs').PNG;
const bmp = require("bmp-js");
const aws = require('aws-sdk');
const { ip, port, api_key } = config.grpc.friends;
const channel = grpc.createChannel(`${ip}:${port}`);
const client = grpc.createClient(FriendsDefinition, channel);
@ -314,7 +313,7 @@ let methods = {
metadata: grpc.Metadata({
'X-API-Key': api_key
})
});
})
}
};
module.exports = methods;

View File

@ -37,7 +37,7 @@ class XmlResponseGenerator {
.e("version", "1").up()
.e("request_name", options.name).up()
.e("topic")
.e("community_id", community.app_id ? community.app_id : community.community_id).up()
.e("community_id", community.community_id).up()
.up()
.e("posts");
for (const post of posts) {
@ -66,7 +66,6 @@ class XmlResponseGenerator {
* @constructor
*/
static async Communities(communities) {
let parent = communities[0].community_id;
let xml = xmlbuilder.create("result", { encoding: 'UTF-8' })
.e("has_error", "0").up()
.e("version", "1").up()
@ -74,8 +73,8 @@ class XmlResponseGenerator {
.e("communities");
for(let community of communities) {
xml = xml.e("community")
.e('olive_community_id', parent).up()
.e('community_id', community.app_id ? community.app_id.padStart(6, '0') : community.community_id).up()
.e('olive_community_id', community.olive_community_id).up()
.e('community_id', community.community_id).up()
.e("name", community.name).up()
.e("description", community.description).up()
.e("icon").up()