Added travis.yml, and ESLint rules

This commit is contained in:
RedDucks
2018-03-06 12:50:30 -05:00
parent 9a59c9f76a
commit 2f0d1de2fd
20 changed files with 2482 additions and 1519 deletions

39
.eslintrc.json Normal file
View File

@@ -0,0 +1,39 @@
{
"env": {
"node": true,
"commonjs": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 2017
},
"extends": "eslint:recommended",
"rules": {
"no-console": "off",
"prefer-const": "error",
"no-var": "error",
"one-var": [
"error",
"never"
],
"indent": [
"error",
"tab",
{
"SwitchCase": 1
}
],
"linebreak-style": [
"error",
"windows"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

10
.travis.yml Normal file
View File

@@ -0,0 +1,10 @@
language: node_js
node_js:
- "7"
- "8"
- "9"
sudo: false
script:
- "npm run lint"

View File

@@ -1,29 +1,29 @@
module.exports = {
URL_ENDPOINTS: {
mii: 'http://mii-secure.account.riiu.net/'
},
LATEST_SYSTEM_VERSION: '0230',
MINIMUN_SYSTEM_VERSION: '0209',
PID_SORT_LIST: '0123456789',
VALID_CLIENT_ID_SECRET_PAIRS: {
// 'Key' is the client ID, 'Value' is the client secret
'a2efa818a34fa16b8afbc8a74eba3eda': 'c91cdb5658bd4954ade78533a339cf9a', // Possibly WiiU exclusive?
'daf6227853bcbdce3d75baee8332b': '3eff548eac636e2bf45bb7b375e7b6b0', // Possibly 3DS exclusive?
'ea25c66c26b403376b4c5ed94ab9cdea': 'd137be62cb6a2b831cad8c013b92fb55', // Possibly 3DS exclusive?
},
REGEX: {
HEADER_FORMATS: {
DEVICE_ID: /^\d{10}$/g,
REGION: /^\d+$/g,
SYSTEM_VERSION: /^\d+$/g,
}
},
REGION_CODES_NUM: {
'4': 'EUR',
'2': 'US'
},
REGION_CODES_STR: {
EUR: 4,
US: 2
},
}
URL_ENDPOINTS: {
mii: 'http://mii-secure.account.riiu.net/'
},
LATEST_SYSTEM_VERSION: '0230',
MINIMUN_SYSTEM_VERSION: '0209',
PID_SORT_LIST: '0123456789',
VALID_CLIENT_ID_SECRET_PAIRS: {
// 'Key' is the client ID, 'Value' is the client secret
'a2efa818a34fa16b8afbc8a74eba3eda': 'c91cdb5658bd4954ade78533a339cf9a', // Possibly WiiU exclusive?
'daf6227853bcbdce3d75baee8332b': '3eff548eac636e2bf45bb7b375e7b6b0', // Possibly 3DS exclusive?
'ea25c66c26b403376b4c5ed94ab9cdea': 'd137be62cb6a2b831cad8c013b92fb55', // Possibly 3DS exclusive?
},
REGEX: {
HEADER_FORMATS: {
DEVICE_ID: /^\d{10}$/g,
REGION: /^\d+$/g,
SYSTEM_VERSION: /^\d+$/g,
}
},
REGION_CODES_NUM: {
'4': 'EUR',
'2': 'US'
},
REGION_CODES_STR: {
EUR: 4,
US: 2
},
};

24
db.js
View File

@@ -1,23 +1,23 @@
const mongoist = require('mongoist');
const config = require('./config.json');
const user_database_collection_name = 'users';
const database = config.mongo.database
const hostname = config.mongo.hostname
const port = config.mongo.port
const auth = config.mongo.authentication
const database = config.mongo.database;
const hostname = config.mongo.hostname;
const port = config.mongo.port;
const auth = config.mongo.authentication;
let connection_string = '';
if(config.mongo.use_authentication) {
connection_string = `mongodb://${auth.username}:${auth.password}@${hostname}:${port}/${database}?authSource=${auth.authentication_database}`
}else {
connection_string = `mongodb://${hostname}:${port}/${database}`
if (config.mongo.use_authentication) {
connection_string = `mongodb://${auth.username}:${auth.password}@${hostname}:${port}/${database}?authSource=${auth.authentication_database}`;
} else {
connection_string = `mongodb://${hostname}:${port}/${database}`;
}
user_database = mongoist(connection_string);
user_collection = user_database.collection(user_database_collection_name);
const user_database = mongoist(connection_string);
const user_collection = user_database.collection(user_database_collection_name);
module.exports = {
user_database: user_database,
user_collection: user_collection
user_database: user_database,
user_collection: user_collection
};

View File

@@ -1,59 +1,59 @@
let ms = require('ms'),
colors = require('colors');
const ms = require('ms');
require('colors');
class Debugger {
constructor(title) {
this.title = title;
this.previous_time = new Date();
}
constructor(title) {
this.title = title;
this.previous_time = new Date();
}
success(input) {
let current_time = new Date(),
time_taken = ms(ms(current_time - this.previous_time)),
suffix = '';
success(input) {
const current_time = new Date();
const time_taken = ms(ms(current_time - this.previous_time));
let suffix = '';
if (time_taken > 0) {
suffix = '-> '.green + (time_taken + 'ms').magenta;
}
if (time_taken > 0) {
suffix = '-> '.green + (time_taken + 'ms').magenta;
}
let message = (this.title + ':').green.bold + ' ' + input.yellow.bold + ' ' + suffix.bold;
const message = (this.title + ':').green.bold + ' ' + input.yellow.bold + ' ' + suffix.bold;
console.log(message);
console.log(message);
this.previous_time = current_time;
}
this.previous_time = current_time;
}
error(input) {
let current_time = new Date(),
time_taken = ms(ms(current_time - this.previous_time)),
suffix = '';
error(input) {
const current_time = new Date();
const time_taken = ms(ms(current_time - this.previous_time));
let suffix = '';
if (time_taken > 0) {
suffix = '-> '.green + (time_taken + 'ms').magenta;
}
if (time_taken > 0) {
suffix = '-> '.green + (time_taken + 'ms').magenta;
}
let message = (this.title + ':').red.bold + ' ' + input.yellow.bold + ' ' + suffix.bold;
const message = (this.title + ':').red.bold + ' ' + input.yellow.bold + ' ' + suffix.bold;
console.log(message);
console.log(message);
this.previous_time = current_time;
}
this.previous_time = current_time;
}
log(input) {
let current_time = new Date(),
time_taken = ms(ms(current_time - this.previous_time)),
suffix = '';
log(input) {
const current_time = new Date();
const time_taken = ms(ms(current_time - this.previous_time));
let suffix = '';
if (time_taken > 0) {
suffix = '-> '.green + (time_taken + 'ms').magenta;
}
if (time_taken > 0) {
suffix = '-> '.green + (time_taken + 'ms').magenta;
}
let message = (this.title + ':').cyan.bold + ' ' + input.yellow.bold + ' ' + suffix.bold;
const message = (this.title + ':').cyan.bold + ' ' + input.yellow.bold + ' ' + suffix.bold;
console.log(message);
console.log(message);
this.previous_time = current_time;
}
this.previous_time = current_time;
}
}
module.exports = Debugger;

View File

@@ -6,247 +6,245 @@ const constants = require('./constants');
const database = require('./db');
async function generatePID() {
// Quick, dirty fix for PIDs
const pid = Math.floor(Math.random() * (4294967295 - 1000000000) + 1000000000);
let does_pid_inuse = await database.user_collection.findOne({
pid: pid
});
// Quick, dirty fix for PIDs
const pid = Math.floor(Math.random() * (4294967295 - 1000000000) + 1000000000);
const does_pid_inuse = await database.user_collection.findOne({
pid: pid
});
if (does_pid_inuse) {
return await generatePID();
}
if (does_pid_inuse) {
return await generatePID();
}
return pid;
return pid;
}
function generateRandID(length = 10) {
let id = '';
let id = '';
for (var i=0;i<length;i++) {
id += constants.PID_SORT_LIST.charAt(Math.floor(Math.random() * constants.PID_SORT_LIST.length));
}
for (let i=0;i<length;i++) {
id += constants.PID_SORT_LIST.charAt(Math.floor(Math.random() * constants.PID_SORT_LIST.length));
}
return id;
return id;
}
async function generateEmailToken() {
const token = randtoken.generate(32);
let user = await database.user_collection.findOne({
'sensitive.email_confirms.token': token
});
if (user) {
return await generateEmailToken();
}
const token = randtoken.generate(32);
const user = await database.user_collection.findOne({
'sensitive.email_confirms.token': token
});
if (user) {
return await generateEmailToken();
}
return token;
return token;
}
async function generateEmailCode() {
const code = generateRandID(6);
const code = generateRandID(6);
const user = await database.user_collection.findOne({
'sensitive.email_confirms.code': code
});
if (user) {
return await generateEmailCode();
}
let user = await database.user_collection.findOne({
'sensitive.email_confirms.code': code
});
if (user) {
return await generateEmailCode();
}
return code;
return code;
}
function generateNintendoHashedPWrd(password, pid) {
let buff1 = pythonStruct.pack('<I', pid);
let buff2 = Buffer.from(password).toString('ascii');
const buff1 = pythonStruct.pack('<I', pid);
const buff2 = Buffer.from(password).toString('ascii');
let unpacked = new Buffer(bufferToHex(buff1) + '\x02eCF' + buff2, 'ascii'),
hashed = crypto.createHash('sha256').update(unpacked).digest().toString('hex');
const unpacked = new Buffer(bufferToHex(buff1) + '\x02eCF' + buff2, 'ascii');
const hashed = crypto.createHash('sha256').update(unpacked).digest().toString('hex');
return hashed;
return hashed;
}
function bufferToHex(buff) {
let result = '',
arr = buff.toString('hex').match(/.{1,2}/g);
for (var i=0;i<arr.length;i++) {
let char = arr[i],
char_code = char.charCodeAt();
result += String.fromCharCode(parseInt(char, 16));
}
result.replace(/\\/g, '&#92;');
return result;
let result = '';
const arr = buff.toString('hex').match(/.{1,2}/g);
for (let i=0;i<arr.length;i++) {
const char = arr[i];
result += String.fromCharCode(parseInt(char, 16));
}
result.replace(/\\/g, '&#92;');
return result;
}
async function doesUserExist(username) {
let user = await database.user_collection.findOne({
username: username.toLowerCase()
});
if (user) {
return true;
}
const user = await database.user_collection.findOne({
username: username.toLowerCase()
});
if (user) {
return true;
}
return false;
return false;
}
async function getUser(token) {
let user = await database.user_collection.findOne({
'sensitive.tokens.access.token': token
});
if (user) {
return user;
}
const user = await database.user_collection.findOne({
'sensitive.tokens.access.token': token
});
if (user) {
return user;
}
return null;
return null;
}
async function getUserBasic(token, email) {
let unpacked_token = Buffer.from(token, 'base64').toString().split(' ');
let user = await database.user_collection.findOne({
user_id_flat: unpacked_token[0].toLowerCase()
});
const unpacked_token = Buffer.from(token, 'base64').toString().split(' ');
const user = await database.user_collection.findOne({
user_id_flat: unpacked_token[0].toLowerCase()
});
if (!user) {
return null;
}
if (!user) {
return null;
}
if (user.email.address.address !== email) {
return null;
}
if (user.email.address.address !== email) {
return null;
}
let hashed_password = generateNintendoHashedPWrd(unpacked_token[1], user.pid);
const hashed_password = generateNintendoHashedPWrd(unpacked_token[1], user.pid);
if (!bcrypt.compareSync(hashed_password, user.sensitive.password)) {
return null;
}
if (!bcrypt.compareSync(hashed_password, user.sensitive.password)) {
return null;
}
return user;
return user;
}
function generateAccessToken(payload) {
let token = crypto.createHash('md5').update(JSON.stringify(payload)).digest('hex');
const token = crypto.createHash('md5').update(JSON.stringify(payload)).digest('hex');
return token;
return token;
}
function generateRefreshToken(payload) {
let token = crypto.createHash('md5').update(JSON.stringify(payload)).digest('hex');
const token = crypto.createHash('md5').update(JSON.stringify(payload)).digest('hex');
return token;
return token;
}
function mapUser(user) {
let accounts = [];
let device_attributes = [];
const accounts = [];
const device_attributes = [];
user.accounts.forEach(account => {
account = account.account
let attributes = [];
user.accounts.forEach(account => {
account = account.account;
const attributes = [];
account.attributes.forEach(attribute => {
attribute = attribute.attribute;
attributes.push({
attribute: {
id: attribute.id,
name: attribute.name,
updated_by: attribute.updated_by,
value: attribute.value,
}
});
});
account.attributes.forEach(attribute => {
attribute = attribute.attribute;
attributes.push({
attribute: {
id: attribute.id,
name: attribute.name,
updated_by: attribute.updated_by,
value: attribute.value,
}
});
});
accounts.push({
account: {
attributes: attributes,
domain: account.domain,
type: account.type,
username: account.username
}
})
});
accounts.push({
account: {
attributes: attributes,
domain: account.domain,
type: account.type,
username: account.username
}
});
});
user.device_attributes.device_attribute.forEach(device_attribute => {
let attribute = {
name: device_attribute.name,
value: device_attribute.value
};
user.device_attributes.device_attribute.forEach(device_attribute => {
const attribute = {
name: device_attribute.name,
value: device_attribute.value
};
if (device_attribute.created_date) {
attribute.created_date = device_attribute.created_date;
}
if (device_attribute.created_date) {
attribute.created_date = device_attribute.created_date;
}
device_attributes.push({
device_attribute: attribute
});
});
device_attributes.push({
device_attribute: attribute
});
});
let person = {
person: {
accounts: accounts,
active_flag: user.active_flag,
birth_date: user.birth_date,
country: user.country,
create_date: user.create_date,
device_attributes: device_attributes,
gender: user.gender,
language: user.language,
updated: user.updated,
marketing_flag: user.marketing_flag,
off_device_flag: user.off_device_flag,
pid: user.pid,
email: {
address: user.email.address.address,
id: user.email.id,
parent: user.email.address.parent,
primary: user.email.address.primary,
reachable: user.email.reachable,
type: user.email.address.type,
updated_by: user.email.updated_by,
validated: user.email.address.validated,
validated_date: user.updated
},
mii: {
status: user.mii.status,
data: user.mii.data.replace('\r\n', ''),
id: user.mii.id,
mii_hash: user.mii.mii_hash,
mii_images: [
{
mii_image: {
cached_url: user.mii.mii_images[0].cached_url,
id: user.mii.mii_images[0].id,
url: user.mii.mii_images[0].url,
type: user.mii.mii_images[0].type
}
}
],
name: user.mii.name,
primary: user.mii.primary
},
region: user.region,
tz_name: user.tz_name,
user_id: user.user_id,
utc_offset: user.utc_offset
}
}
const person = {
person: {
accounts: accounts,
active_flag: user.active_flag,
birth_date: user.birth_date,
country: user.country,
create_date: user.create_date,
device_attributes: device_attributes,
gender: user.gender,
language: user.language,
updated: user.updated,
marketing_flag: user.marketing_flag,
off_device_flag: user.off_device_flag,
pid: user.pid,
email: {
address: user.email.address.address,
id: user.email.id,
parent: user.email.address.parent,
primary: user.email.address.primary,
reachable: user.email.reachable,
type: user.email.address.type,
updated_by: user.email.updated_by,
validated: user.email.address.validated,
validated_date: user.updated
},
mii: {
status: user.mii.status,
data: user.mii.data.replace('\r\n', ''),
id: user.mii.id,
mii_hash: user.mii.mii_hash,
mii_images: [
{
mii_image: {
cached_url: user.mii.mii_images[0].cached_url,
id: user.mii.mii_images[0].id,
url: user.mii.mii_images[0].url,
type: user.mii.mii_images[0].type
}
}
],
name: user.mii.name,
primary: user.mii.primary
},
region: user.region,
tz_name: user.tz_name,
user_id: user.user_id,
utc_offset: user.utc_offset
}
};
return person;
return person;
}
module.exports = {
generatePID: generatePID,
generateRandID: generateRandID,
generateNintendoHashedPWrd: generateNintendoHashedPWrd,
doesUserExist: doesUserExist,
generateAccessToken: generateAccessToken,
generateRefreshToken: generateRefreshToken,
getUser: getUser,
getUserBasic: getUserBasic,
mapUser: mapUser,
generateEmailToken: generateEmailToken,
generateEmailCode: generateEmailCode,
}
generatePID: generatePID,
generateRandID: generateRandID,
generateNintendoHashedPWrd: generateNintendoHashedPWrd,
doesUserExist: doesUserExist,
generateAccessToken: generateAccessToken,
generateRefreshToken: generateRefreshToken,
getUser: getUser,
getUserBasic: getUserBasic,
mapUser: mapUser,
generateEmailToken: generateEmailToken,
generateEmailCode: generateEmailCode,
};

View File

@@ -2,11 +2,11 @@ const nodemailer = require('nodemailer');
const config = require('./config.json');
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: config.email.address,
pass: config.email.password
}
service: 'gmail',
auth: {
user: config.email.address,
pass: config.email.password
}
});
/**
@@ -16,22 +16,22 @@ const transporter = nodemailer.createTransport({
* @param {String} message The body of the email
*/
function send(email, subject = 'No email subject provided', message = 'No email body provided') {
let options = {
from: config.email.address,
to: email,
subject: subject,
html: message
}
const options = {
from: config.email.address,
to: email,
subject: subject,
html: message
};
transporter.sendMail(options, (error, info) => {
if (error) {
console.log(error);
} else {
console.log(info);
}
});
transporter.sendMail(options, (error, info) => {
if (error) {
console.log(error);
} else {
console.log(info);
}
});
}
module.exports = {
send: send
}
send: send
};

935
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@
"description": "",
"main": "server.js",
"scripts": {
"lint": "./node_modules/.bin/eslint .",
"test": "echo \"Error: no test specified\" && exit 1",
"start": "NODE_ENV=production node server.js",
"start:dev": "NODE_ENV=development node server.js"
@@ -14,6 +15,7 @@
"bcryptjs": "^2.4.3",
"cli-table": "^0.3.1",
"colors": "^1.1.2",
"eslint": "^4.18.2",
"express": "^4.16.2",
"express-rate-limit": "^2.11.0",
"express-subdomain": "^1.0.5",

View File

@@ -10,61 +10,43 @@ route_debugger.success('Loading \'account\' API routes');
* Description: Verifies a user email via token
*/
routes.get('/email-confirmation', async (request, response) => {
let _GET = request.query;
const _GET = request.query;
console.log(_GET)
if (!_GET.token) {
return response.send('ERROR: Invalid token');
}
if (!_GET.token) {
return response.send('ERROR: Invalid token');
}
const user = await database.user_collection.findOne({
'sensitive.email_confirms.token': _GET.token
});
console.log(2)
if (!user) {
return response.send('ERROR: Invalid token');
}
let user = await database.user_collection.findOne({
'sensitive.email_confirms.token': _GET.token
});
user.email.reachable = 'Y';
user.email.validated = 'Y';
console.log(3)
await database.user_collection.update({
pid: user.pid
}, {
$set: {
email: user.email
}
});
if (!user) {
return response.send('ERROR: Invalid token');
}
user.sensitive.email_confirms.token = null;
user.sensitive.email_confirms.code = null;
console.log(4)
await database.user_collection.update({
pid: user.pid
}, {
$set: {
sensitive: user.sensitive
}
});
user.email.reachable = 'Y';
user.email.validated = 'Y';
console.log(5)
await database.user_collection.update({
pid: user.pid
}, {
$set: {
email: user.email
}
});
console.log(6)
user.sensitive.email_confirms.token = null;
user.sensitive.email_confirms.code = null;
console.log(7)
await database.user_collection.update({
pid: user.pid
}, {
$set: {
sensitive: user.sensitive
}
});
console.log(8)
response.send('It has been confirmed that you can receive e-mails from Pretendo. The confirmation process is now complete.');
console.log(9)
response.send('It has been confirmed that you can receive e-mails from Pretendo. The confirmation process is now complete.');
});
module.exports = routes;

View File

@@ -13,97 +13,97 @@ route_debugger.success('Loading \'admin\' API routes');
* Description: Maps given input to expected output
*/
routes.all('/mapped_ids', async (request, response) => {
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
let headers = request.headers,
_GET = request.query;
const headers = request.headers;
const _GET = request.query;
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
let error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
}
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
const error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
if (
!_GET.output_type ||
!_GET.input_type ||
!_GET.input ||
_GET.input.trim() == ''
) {
let error = {
errors: {
error: {
cause: 'Bad Request',
code: '1600',
message: 'Unable to process request'
}
}
}
if (
!_GET.output_type ||
!_GET.input_type ||
!_GET.input ||
_GET.input.trim() == ''
) {
const error = {
errors: {
error: {
cause: 'Bad Request',
code: '1600',
message: 'Unable to process request'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
let output = {
mapped_ids: [
]
}
const output = {
mapped_ids: [
]
};
_GET.input = _GET.input.split(',');
_GET.input = _GET.input.split(',');
switch (true) {
case (_GET.input_type === 'user_id' && _GET.output_type === 'pid'):
await _GET.input.asyncForEach(async user_id => {
let user = await database.user_collection.findOne({
user_id_flat: user_id.toLowerCase(),
}),
out_id = null;
switch (true) {
case (_GET.input_type === 'user_id' && _GET.output_type === 'pid'):
await _GET.input.asyncForEach(async user_id => {
const user = await database.user_collection.findOne({
user_id_flat: user_id.toLowerCase(),
});
let out_id = null;
if (user) {
out_id = user.pid;
}
if (user) {
out_id = user.pid;
}
output.mapped_ids.push({
mapped_id: {
in_id: user_id,
out_id: out_id
}
});
});
break;
default:
_GET.input.forEach(input => {
output.mapped_ids.push({
mapped_id: {
in_id: input,
out_id: null
}
});
});
}
output.mapped_ids.push({
mapped_id: {
in_id: user_id,
out_id: out_id
}
});
});
break;
default:
_GET.input.forEach(input => {
output.mapped_ids.push({
mapped_id: {
in_id: input,
out_id: null
}
});
});
}
response.send(json2xml(output));
})
response.send(json2xml(output));
});
module.exports = routes;
Array.prototype.asyncForEach = async function (cb) {
for (let i=0;i<this.length;i++) {
await cb(this[i], i, this);
}
}
for (let i=0;i<this.length;i++) {
await cb(this[i], i, this);
}
};

View File

@@ -14,47 +14,47 @@ route_debugger.success('Loading \'content\' API routes');
* Description: Sends the client an XML list of timezones
*/
routes.get('/time_zones/:region/:language', (request, response) => {
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
let headers = request.headers,
region = request.params.region,
language = request.params.language;
const headers = request.headers;
const region = request.params.region;
const language = request.params.language;
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
let error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
}
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
const error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
if (!fs.pathExistsSync(path.join(__dirname, 'storage', 'timezones', region, language + '.xml'))) {
let error = {
errors: {
error: {
code: '0000',
message: 'Unknown error'
}
}
}
if (!fs.pathExistsSync(path.join(__dirname, 'storage', 'timezones', region, language + '.xml'))) {
const error = {
errors: {
error: {
code: '0000',
message: 'Unknown error'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
response.sendFile(path.join(__dirname, 'storage', 'timezones', region, language + '.xml'));
response.sendFile(path.join(__dirname, 'storage', 'timezones', region, language + '.xml'));
});
@@ -64,73 +64,72 @@ routes.get('/time_zones/:region/:language', (request, response) => {
* Description: Sends the client requested agreement
*/
routes.get('/agreements/:type/:region/:version', (request, response) => {
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
let headers = request.headers,
type = request.params.type,
region = request.params.region,
version = request.params.version,
_GET = request.query;
const headers = request.headers;
const type = request.params.type;
const region = request.params.region;
const version = request.params.version;
// for some reason, the 3ds won't work if you have this.
// TODO: fix this for the 3ds
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
let error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
}
// for some reason, the 3ds won't work if you have this.
// TODO: fix this for the 3ds
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
const error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
if (!fs.pathExistsSync(path.join(__dirname, 'storage', 'agreements', type, region, version + '.xml'))) {
let error = {
errors: {
error: {
code: '1101',
message: 'No stored agreement found for this country: ' + region + ' type: ' + type + ' and version: ' + version
}
}
}
if (!fs.pathExistsSync(path.join(__dirname, 'storage', 'agreements', type, region, version + '.xml'))) {
const error = {
errors: {
error: {
code: '1101',
message: 'No stored agreement found for this country: ' + region + ' type: ' + type + ' and version: ' + version
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
/*
// ALSO NEED TO FIGURE OUT WHEN TO THROW ERROR 1102:
let error = {
errors: {
error: {
code: '1102',
message: 'No privacy policy found for this country ' + region + ' version ' + version
}
}
}
/*
// ALSO NEED TO FIGURE OUT WHEN TO THROW ERROR 1102:
let error = {
errors: {
error: {
code: '1102',
message: 'No privacy policy found for this country ' + region + ' version ' + version
}
}
}
return response.send(json2xml(error));
return response.send(json2xml(error));
// SEEMS TO BE RELATED TO THE `X-Nintendo-Region` HEADER.
// SETTING `X-Nintendo-Region` TO 4 (and `:region` is `US`) WHEN `:type` IS `Nintendo-Network-EULA` THROWS ERROR 1102
*/
// SEEMS TO BE RELATED TO THE `X-Nintendo-Region` HEADER.
// SETTING `X-Nintendo-Region` TO 4 (and `:region` is `US`) WHEN `:type` IS `Nintendo-Network-EULA` THROWS ERROR 1102
*/
response.sendFile(path.join(__dirname, 'storage', 'agreements', type, region, version + '.xml'));
response.sendFile(path.join(__dirname, 'storage', 'agreements', type, region, version + '.xml'));
});
module.exports = routes;
Array.prototype.contains = function(el) { // polyfill custom Array method
return this.indexOf(el) > -1;
}
return this.indexOf(el) > -1;
};

View File

@@ -1,6 +1,4 @@
const routes = require('express').Router();
const path = require('path');
const fs = require('fs-extra');
const json2xml = require('json2xml');
const debug = require('../../debugger');
const constants = require('../../constants');
@@ -14,32 +12,32 @@ route_debugger.success('Loading \'devices\' API routes');
* Description: Unknown use
*/
routes.get('/@current/status', (request, response) => {
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
let headers = request.headers;
const headers = request.headers;
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
let error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
}
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
const error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
return response.send(json2xml({device: null}));
return response.send(json2xml({device: null}));
});
module.exports = routes;

View File

@@ -11,184 +11,183 @@ route_debugger.log('Loading \'oauth20\' API routes');
/**
* [GET]
* Replacement for: https://account.nintendo.net/v1/api/oauth20/access_token/generate
* Description: Unknown use
* Description: Generates user access tokens
*/
routes.all('/access_token/generate', async (request, response) => {
let POST = request.body;
if (
!POST ||
!POST.grant_type ||
!['password', 'refresh_token'].includes(POST.grant_type)
) {
let error = {
errors: {
error: {
cause: 'grant_type',
code: '0004',
message: 'Invalid Grant Type'
}
}
}
const POST = request.body;
return response.send(json2xml(error));
}
if (
!POST ||
!POST.grant_type ||
!['password', 'refresh_token'].includes(POST.grant_type)
) {
const error = {
errors: {
error: {
cause: 'grant_type',
code: '0004',
message: 'Invalid Grant Type'
}
}
};
if (POST.grant_type == 'password') {
if (!POST.user_id || !POST.password) {
let error = {
errors: {
error: {
cause: 'grant_type',
code: '0004',
message: 'Invalid Grant Type'
}
}
}
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
let user = await database.user_collection.findOne({
user_id: POST.user_id
});
if (POST.grant_type == 'password') {
if (!POST.user_id || !POST.password) {
const error = {
errors: {
error: {
cause: 'grant_type',
code: '0004',
message: 'Invalid Grant Type'
}
}
};
return response.send(json2xml(error));
}
if (!user) {
let error = {
errors: {
error: {
code: '0113',
message: 'Unauthorized device'
}
}
}
const user = await database.user_collection.findOne({
user_id: POST.user_id
});
return response.send(json2xml(error));
}
if (!user) {
const error = {
errors: {
error: {
code: '0113',
message: 'Unauthorized device'
}
}
};
if (!POST.password_type || POST.password_type.toLowerCase() !== 'hash') {
POST.password = helpers.generateNintendoHashedPWrd(POST.password, user.pid);
}
return response.send(json2xml(error));
}
if (!bcrypt.compareSync(POST.password, user.sensitive.password)) {
let error = {
errors: {
error: {
code: '0106',
message: 'Invalid account ID or password.'
}
}
}
if (!POST.password_type || POST.password_type.toLowerCase() !== 'hash') {
POST.password = helpers.generateNintendoHashedPWrd(POST.password, user.pid);
}
return response.send(json2xml(error));
}
let access_token = helpers.generateAccessToken({
pid: user.pid,
token_salt: helpers.generateRandID(100)
});
//let access_token = user.pid
if (!bcrypt.compareSync(POST.password, user.sensitive.password)) {
const error = {
errors: {
error: {
code: '0106',
message: 'Invalid account ID or password.'
}
}
};
let refresh_token = helpers.generateRefreshToken({
pid: user.pid,
token_salt: helpers.generateRandID(100)
});
return response.send(json2xml(error));
}
const access_token = helpers.generateAccessToken({
pid: user.pid,
token_salt: helpers.generateRandID(100)
});
user.sensitive.tokens.refresh = refresh_token;
user.sensitive.tokens.access.token = access_token;
user.sensitive.tokens.access.ttl = Math.floor((Date.now() / 1000) + 3600);
const refresh_token = helpers.generateRefreshToken({
pid: user.pid,
token_salt: helpers.generateRandID(100)
});
await database.user_collection.update({
pid: user.pid
}, {
$set: {
sensitive: user.sensitive
}
});
response.send(json2xml({
OAuth20: {
access_token: {
token: access_token,
refresh_token: refresh_token,
expires_in: 3600,
}
}
}));
} else if (POST.grant_type == 'refresh_token') {
if (!POST.refresh_token) {
let error = {
errors: {
error: {
cause: 'refresh_token',
code: '0106',
message: 'Invalid Refresh Token'
}
}
}
user.sensitive.tokens.refresh = refresh_token;
user.sensitive.tokens.access.token = access_token;
user.sensitive.tokens.access.ttl = Math.floor((Date.now() / 1000) + 3600);
return response.send(json2xml(error));
}
await database.user_collection.update({
pid: user.pid
}, {
$set: {
sensitive: user.sensitive
}
});
response.send(json2xml({
OAuth20: {
access_token: {
token: access_token,
refresh_token: refresh_token,
expires_in: 3600,
}
}
}));
} else if (POST.grant_type == 'refresh_token') {
if (!POST.refresh_token) {
const error = {
errors: {
error: {
cause: 'refresh_token',
code: '0106',
message: 'Invalid Refresh Token'
}
}
};
let user = database.user_collection.findOne({
sensitive: {
tokens: {
refresh: POST.refresh_token
}
}
});
return response.send(json2xml(error));
}
if (!user || user.sensitive.tokens.refresh !== POST.refresh_token) {
let error = {
errors: {
error: {
cause: 'refresh_token',
code: '0106',
message: 'Invalid Refresh Token'
}
}
}
const user = database.user_collection.findOne({
sensitive: {
tokens: {
refresh: POST.refresh_token
}
}
});
return response.send(json2xml(error));
}
if (!user || user.sensitive.tokens.refresh !== POST.refresh_token) {
const error = {
errors: {
error: {
cause: 'refresh_token',
code: '0106',
message: 'Invalid Refresh Token'
}
}
};
let access_token = helpers.generateAccessToken({
pid: user.pid,
token_salt: helpers.generateRandID(100)
});
return response.send(json2xml(error));
}
let refresh_token = helpers.generateRefreshToken({
pid: user.pid,
token_salt: helpers.generateRandID(100)
});
const access_token = helpers.generateAccessToken({
pid: user.pid,
token_salt: helpers.generateRandID(100)
});
user.sensitive.tokens.refresh = refresh_token;
user.sensitive.tokens.access.token = access_token;
user.sensitive.tokens.access.ttl = Math.floor((Date.now() / 1000) + 3600);
const refresh_token = helpers.generateRefreshToken({
pid: user.pid,
token_salt: helpers.generateRandID(100)
});
await database.user_collection.update({
sensitive: {
tokens: {
refresh: POST.refresh_token
}
}
}, {
$set: {
sensitive: user.sensitive
}
});
user.sensitive.tokens.refresh = refresh_token;
user.sensitive.tokens.access.token = access_token;
user.sensitive.tokens.access.ttl = Math.floor((Date.now() / 1000) + 3600);
response.send(json2xml({
OAuth20: {
access_token: {
token: access_token,
refresh_token: refresh_token,
expires_in: 3600,
}
}
}));
}
await database.user_collection.update({
sensitive: {
tokens: {
refresh: POST.refresh_token
}
}
}, {
$set: {
sensitive: user.sensitive
}
});
response.send(json2xml({
OAuth20: {
access_token: {
token: access_token,
refresh_token: refresh_token,
expires_in: 3600,
}
}
}));
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
const routes = require('express').Router();
const RateLimit = require('express-rate-limit');
const json2xml = require('json2xml');
const debug = require('../../debugger');
const route_debugger = new debug('Provider Route'.green);
@@ -7,7 +6,7 @@ const route_debugger = new debug('Provider Route'.green);
route_debugger.log('Loading \'provider\' API routes');
//The game ips and ports are stored here. When the game tries to access its specific server, it will be given the respecive ip and port.
let gamePort = {
const gamePort = {
friends: {
ip: '10.0.0.225',
port: '1300'
@@ -16,7 +15,7 @@ let gamePort = {
ip: '10.0.0.225',
port: '1301'
}
}
};
/**
* [GET]
@@ -24,15 +23,15 @@ let gamePort = {
* Description: Gets service token
*/
routes.get('/service_token/@me', async (request, response) => {
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
let token = {
const token = {
service_token: {
token: 'pretendo_test'
}
}
};
return response.send(json2xml(token));
});
@@ -43,8 +42,8 @@ routes.get('/service_token/@me', async (request, response) => {
* Description: Gets nex token
*/
routes.get('/nex_token/@me', async (request, response) => {
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
let ip = null;
@@ -67,21 +66,21 @@ routes.get('/nex_token/@me', async (request, response) => {
break;
}
if(ip==null || port==null){
let error = {
errors: {
error: {
cause: 'No game server',
code: '9999',
message: 'No server found'
}
}
}
if (ip==null || port==null){
const error = {
errors: {
error: {
cause: 'No game server',
code: '9999',
message: 'No server found'
}
}
};
return response.send(json2xml(error));
}
let token = {
const token = {
nex_token: {
host: ip,
nex_password: 'pretendo',
@@ -89,7 +88,8 @@ routes.get('/nex_token/@me', async (request, response) => {
port: port,
token: 'pretendo_test'
}
}
};
return response.send(json2xml(token));
});

View File

@@ -10,7 +10,7 @@ route_debugger.log('Loading \'root/misc\' API routes');
* Description: tests the validity of this server by returning a predefined message
*/
routes.get('/isthisworking', async (req, res) => {
return res.send('{"server": "account.nintendo.net"}')
return res.send('{"server": "account.nintendo.net"}');
});
module.exports = routes
module.exports = routes;

View File

@@ -1,6 +1,5 @@
const dns = require('dns');
const routes = require('express').Router();
const helpers = require('../../helpers');
const constants = require('../../constants');
const database = require('../../db');
const json2xml = require('json2xml');
@@ -15,82 +14,82 @@ route_debugger.log('Loading \'support\' API routes');
* Description: Validates an email by attempting to ping it's mail server
*/
routes.post('/validate/email', async (request, response) => {
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
let POST = request.body,
headers = request.headers
email = POST.email;
const POST = request.body;
const headers = request.headers;
const email = POST.email;
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
let error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
}
if (
!headers['x-nintendo-client-id'] ||
!headers['x-nintendo-client-secret'] ||
!constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']] ||
headers['x-nintendo-client-secret'] !== constants.VALID_CLIENT_ID_SECRET_PAIRS[headers['x-nintendo-client-id']]
) {
const error = {
errors: {
error: {
cause: 'client_id',
code: '0004',
message: 'API application invalid or incorrect application credentials'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
if (
!headers['content-type'] ||
headers['content-type'].toLowerCase() !== 'application/x-www-form-urlencoded'
) {
let error = {
errors: {
error: {
code: '2001',
message: 'Internal server error'
}
}
}
if (
!headers['content-type'] ||
headers['content-type'].toLowerCase() !== 'application/x-www-form-urlencoded'
) {
const error = {
errors: {
error: {
code: '2001',
message: 'Internal server error'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
if (!email) {
let error = {
errors: {
error: {
cause: 'email',
code: '0103',
message: 'Email format is invalid'
}
}
}
if (!email) {
const error = {
errors: {
error: {
cause: 'email',
code: '0103',
message: 'Email format is invalid'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
let domain = email.split('@')[1];
const domain = email.split('@')[1];
dns.resolveMx(domain, (error, addr) => {
if (error) {
let error = {
errors: {
error: {
code: '1126',
message: 'The domain "' + domain + '" is not accessible.'
}
}
}
dns.resolveMx(domain, (error) => {
if (error) {
const error = {
errors: {
error: {
code: '1126',
message: 'The domain "' + domain + '" is not accessible.'
}
}
};
return response.send(json2xml(error));
}
return response.send(json2xml(error));
}
response.status(200);
response.end();
});
response.status(200);
response.end();
});
});
@@ -100,64 +99,65 @@ routes.post('/validate/email', async (request, response) => {
* Description: Confirms an email
*/
routes.put('/email_confirmation/:pid/:code', async (request, response) => {
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
response.set('Content-Type', 'text/xml');
response.set('Server', 'Nintendo 3DS (http)');
response.set('X-Nintendo-Date', new Date().getTime());
let user = await database.user_collection.findOne({
pid: Number(request.params.pid)
});
const user = await database.user_collection.findOne({
pid: Number(request.params.pid)
});
if (!user) {
let error = {
errors: {
error: {
code: '0130',
message: 'PID has not been registered yet'
}
}
}
return response.send(json2xml(error));
}
if (user.sensitive.email_confirms.code !== request.params.code) {
let error = {
errors: {
error: {
code: '0116',
message: 'Missing or invalid verification code'
}
}
}
const error = {
errors: {
error: {
code: '0130',
message: 'PID has not been registered yet'
}
}
};
return response.send(json2xml(error));
}
user.email.reachable = 'Y';
user.email.validated = 'Y';
if (user.sensitive.email_confirms.code !== request.params.code) {
const error = {
errors: {
error: {
code: '0116',
message: 'Missing or invalid verification code'
}
}
};
await database.user_collection.update({
pid: user.pid
}, {
$set: {
email: user.email
}
});
return response.send(json2xml(error));
}
user.email.reachable = 'Y';
user.email.validated = 'Y';
user.sensitive.email_confirms.token = null;
user.sensitive.email_confirms.code = null;
await database.user_collection.update({
pid: user.pid
}, {
$set: {
email: user.email
}
});
await database.user_collection.update({
pid: user.pid
}, {
$set: {
sensitive: user.sensitive
}
});
user.sensitive.email_confirms.token = null;
user.sensitive.email_confirms.code = null;
response.send(json2xml({
email: null
}));
await database.user_collection.update({
pid: user.pid
}, {
$set: {
sensitive: user.sensitive
}
});
response.send(json2xml({
email: null
}));
});

131
server.js
View File

@@ -1,31 +1,30 @@
const path = require('path');
const express = require('express');
const subdomain = require('express-subdomain');
const table = require('cli-table');
const colors = require('colors');
const morgan = require('morgan');
const XMLMiddleware = require('./xml-middleware');
const debug = require('./debugger');
const config = require('./config.json')
const config = require('./config.json');
const app = express();
const router = express.Router();
const testing_env = process.env.NODE_ENV !== "production"
const testing_env = process.env.NODE_ENV !== 'production';
require('colors');
const server_debugger = new debug('Server');
server_debugger.log('Importing routes');
// API routes
const ROUTES = {
ACCOUNT: require('./routes/account'),
ADMIN: require('./routes/admin'),
CONTENT: require('./routes/content'),
DEVICES: require('./routes/devices'),
OAUTH20: require('./routes/oauth20'),
PEOPLE: require('./routes/people'),
PROVIDER: require('./routes/provider'),
SUPPORT: require('./routes/support'),
ROOT: require('./routes/root')
}
ACCOUNT: require('./routes/account'),
ADMIN: require('./routes/admin'),
CONTENT: require('./routes/content'),
DEVICES: require('./routes/devices'),
OAUTH20: require('./routes/oauth20'),
PEOPLE: require('./routes/people'),
PROVIDER: require('./routes/provider'),
SUPPORT: require('./routes/support'),
ROOT: require('./routes/root')
};
// START APPLICATION
app.set('etag', false);
@@ -36,76 +35,78 @@ app.use(morgan('dev'));
router.use(express.json());
router.use(XMLMiddleware());
router.use(express.urlencoded({
extended: true
extended: true
}));
server_debugger.log('Creating \'account\' subdomain');
// Create subdomain
if (testing_env === false) {
app.use(subdomain('account', router));
app.use(subdomain('account', router));
} else {
app.use(router)
app.use(router);
}
server_debugger.log('Applying imported routes');
// Setup routes
router.use('/account', ROUTES.ACCOUNT); // Account API routes (only used for email confirmation)
router.use('/v1/api/admin', ROUTES.ADMIN); // Admin API routes
router.use('/account', ROUTES.ACCOUNT); // Account API routes (only used for email confirmation)
router.use('/v1/api/admin', ROUTES.ADMIN); // Admin API routes
router.use('/v1/api/content', ROUTES.CONTENT); // Content API routes
router.use('/v1/api/devices', ROUTES.DEVICES); // Device API routes
router.use('/v1/api/oauth20', ROUTES.OAUTH20); // oAuth API routes
router.use('/v1/api/people', ROUTES.PEOPLE); // People API routes
router.use('/v1/api/people', ROUTES.PEOPLE); // People API routes
router.use('/v1/api/provider', ROUTES.PROVIDER); // Provider API routes
router.use('/v1/api/support', ROUTES.SUPPORT); // Support API routes
router.use('/', ROUTES.ROOT); // Root/Misc routes
router.use('/v1/api/support', ROUTES.SUPPORT); // Support API routes
router.use('/', ROUTES.ROOT); // Root/Misc routes
function getRoutes(_router) {
let routes, ret = [];
_router.forEach(layer => {
if (layer.name == 'router') {
routes = layer;
}
});
const ret = [];
let routes;
let stack = routes.handle.stack;
_router.forEach(layer => {
if (layer.name == 'router') {
routes = layer;
}
});
stack.forEach(layer => {
if (layer.handle.stack) {
let base = layer.regexp.toString()
.replace('\\/?', '')
.replace('(?=\\/|$)', '$')
.match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//)[1]
.replace(/\\(.)/g, '$1');
const stack = routes.handle.stack;
layer.handle.stack.forEach(mini_layer => {
ret.push({
method: Object.keys(mini_layer.route.methods)[0].toUpperCase().replace('_', ''),
path: base + mini_layer.route.path
})
})
}
});
stack.forEach(layer => {
if (layer.handle.stack) {
const base = layer.regexp.toString()
.replace('\\/?', '')
.replace('(?=\\/|$)', '$')
.match(/^\/\^((?:\\[.*+?^${}()|[\]\\/]|[^.*+?^${}()|[\]\\/])*)\$\//)[1]
.replace(/\\(.)/g, '$1');
return ret;
layer.handle.stack.forEach(mini_layer => {
ret.push({
method: Object.keys(mini_layer.route.methods)[0].toUpperCase().replace('_', ''),
path: base + mini_layer.route.path
});
});
}
});
return ret;
}
server_debugger.log('Creating 404 status handler');
// 404 handler
router.use((request, response) => {
response.status(404);
response.send();
response.status(404);
response.send();
});
server_debugger.log('Creating non-404 status handler');
// non-404 error handler
router.use((error, request, response) => {
let status = error.status || 500;
response.status(status);
response.json({
app: 'api',
status: status,
error: error.message
});
const status = error.status || 500;
response.status(status);
response.json({
app: 'api',
status: status,
error: error.message
});
});
@@ -113,19 +114,19 @@ router.use((error, request, response) => {
server_debugger.log('Starting server');
// Starts the server
app.listen(config.http.port, () => {
let route_table = new table({
head: ["Method", "Path"]
}),
routes = getRoutes(app._router.stack);
const route_table = new table({
head: ['Method', 'Path']
});
const routes = getRoutes(app._router.stack);
routes.forEach(route => {
let obj = {};
obj[route.method] = route.path;
routes.forEach(route => {
const obj = {};
obj[route.method] = route.path;
route_table.push(obj)
});
route_table.push(obj);
});
console.log(route_table.toString());
console.log(route_table.toString());
server_debugger.log('Started '.green + 'on port '.blue + new String(config.http.port).yellow);
server_debugger.log('Started '.green + 'on port '.blue + new String(config.http.port).yellow);
});

View File

@@ -3,36 +3,36 @@ const XML = require('pixl-xml');
module.exports = middleware;
function middleware() {
return XMLMiddleware;
return XMLMiddleware;
function XMLMiddleware(request, response, next) {
if (request.method == 'POST' || request.method == 'PUT') {
let headers = request.headers,
body = '';
if (
!headers['content-type'] ||
headers['content-type'].toLowerCase() !== 'application/xml'
) {
return next();
}
function XMLMiddleware(request, response, next) {
if (request.method == 'POST' || request.method == 'PUT') {
const headers = request.headers;
let body = '';
if (
!headers['content-type'] ||
headers['content-type'].toLowerCase() !== 'application/xml'
) {
return next();
}
request.setEncoding('utf-8');
request.on('data', (chunk) => {
body += chunk;
});
request.setEncoding('utf-8');
request.on('data', (chunk) => {
body += chunk;
});
request.on('end', () => {
try {
request.body = XML.parse(body);
} catch (error) {
return next();
}
request.on('end', () => {
try {
request.body = XML.parse(body);
} catch (error) {
return next();
}
next();
});
} else {
next();
}
}
next();
});
} else {
next();
}
}
}