Removed JSON config, moved to env vars

This commit is contained in:
Jonathan Barrow 2023-04-22 11:03:51 -04:00
parent f70cd34406
commit 93008c5a1a
No known key found for this signature in database
GPG Key ID: E86E9FE9049C741F
15 changed files with 402 additions and 244 deletions

3
.gitignore vendored
View File

@ -131,6 +131,5 @@ dist
# custom
certs
config.json
src/logs
dist
dist

View File

@ -1,38 +0,0 @@
{
"http": {
"port": 8080
},
"account_server": "localhost",
"account_server_secret": "",
"X-Nintendo-Client-ID": "",
"X-Nintendo-Client-Secret": "",
"mongoose": {
"uri": "mongodb://localhost:27017",
"database": "Juxt",
"options": {
"useNewUrlParser": true,
"useUnifiedTopology": true
}
},
"account_db": {
"uri": "mongodb://localhost:27017",
"database": "pretendo",
"options": {
"useNewUrlParser": true,
"useUnifiedTopology": true
}
},
"aws": {
"spaces": {
"key": "",
"secret": ""
}
},
"grpc": {
"friends": {
"ip": "localhost",
"port": 50051,
"api_key": ""
}
}
}

80
package-lock.json generated
View File

@ -13,11 +13,11 @@
"bmp-js": "^0.1.0",
"body-parser": "^1.20.2",
"colors": "^1.4.0",
"dotenv": "^16.0.3",
"express": "^4.17.1",
"express-session": "^1.17.0",
"express-subdomain": "^1.0.5",
"fs-extra": "^9.0.0",
"grpc": "github:pretendonetwork/grpc-js",
"memoizee": "^0.4.15",
"moment": "^2.24.0",
"moment-timezone": "^0.5.27",
@ -37,8 +37,13 @@
"xmlbuilder2": "0.0.4"
},
"devDependencies": {
"@types/bmp-js": "^0.1.0",
"@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.1",
"@types/morgan": "^1.9.4",
"@types/node-rsa": "^1.1.1",
"@types/pako": "^2.0.0",
"@types/pngjs": "^6.0.1",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"eslint": "^8.38.0",
@ -1445,6 +1450,15 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"node_modules/@types/bmp-js": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@types/bmp-js/-/bmp-js-0.1.0.tgz",
"integrity": "sha512-uMU85ROcmlY1f4mVPTlNodRXa6Z5f0AIxvv5b0pvjty3KNg7ljf5lNSspHgaF6iFDCiGpLQmJna+VwEpUC9TyA==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/body-parser": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
@ -1487,12 +1501,31 @@
"@types/range-parser": "*"
}
},
"node_modules/@types/fs-extra": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.1.tgz",
"integrity": "sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==",
"dev": true,
"dependencies": {
"@types/jsonfile": "*",
"@types/node": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
"node_modules/@types/jsonfile": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz",
"integrity": "sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/long": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
@ -1518,6 +1551,30 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz",
"integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q=="
},
"node_modules/@types/node-rsa": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/node-rsa/-/node-rsa-1.1.1.tgz",
"integrity": "sha512-itzxtaBgk4OMbrCawVCvas934waMZWjW17v7EYgFVlfYS/cl0/P7KZdojWCq9SDJMI5cnLQLUP8ayhVCTY8TEg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/pako": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.0.tgz",
"integrity": "sha512-10+iaz93qR5WYxTo+PMifD5TSxiOtdRaxBf7INGGXMQgTCu8Z/7GYWYFUOS3q/G0nE5boj1r4FEB+WSy7s5gbA==",
"dev": true
},
"node_modules/@types/pngjs": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.1.tgz",
"integrity": "sha512-J39njbdW1U/6YyVXvC9+1iflZghP8jgRf2ndYghdJb5xL49LYDB+1EuAxfbuJ2IBbWIL3AjHPQhgaTxT3YaYeg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@ -2497,6 +2554,14 @@
"node": ">=6.0.0"
}
},
"node_modules/dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@ -3337,19 +3402,6 @@
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true
},
"node_modules/grpc": {
"name": "pretendo-grpc",
"version": "1.0.0",
"resolved": "git+ssh://git@github.com/pretendonetwork/grpc-js.git#ea933171998bd8093a6a8ba1d57691af85a36cc0",
"license": "ISC",
"dependencies": {
"@grpc/grpc-js": "^1.6.9",
"@grpc/proto-loader": "^0.7.0",
"long": "^5.2.0",
"nice-grpc": "^2.0.0",
"protobufjs": "^7.0.0"
}
},
"node_modules/har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",

View File

@ -18,6 +18,7 @@
"bmp-js": "^0.1.0",
"body-parser": "^1.20.2",
"colors": "^1.4.0",
"dotenv": "^16.0.3",
"express": "^4.17.1",
"express-session": "^1.17.0",
"express-subdomain": "^1.0.5",
@ -41,8 +42,13 @@
"xmlbuilder2": "0.0.4"
},
"devDependencies": {
"@types/bmp-js": "^0.1.0",
"@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.1",
"@types/morgan": "^1.9.4",
"@types/node-rsa": "^1.1.1",
"@types/pako": "^2.0.0",
"@types/pngjs": "^6.0.1",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"eslint": "^8.38.0",

View File

@ -1,14 +1,14 @@
import mongoose from 'mongoose';
import { LOG_INFO, LOG_ERROR } from '@/logger';
import { account_db as mongooseConfig } from '../config.json';
import { config } from '@/config-manager';
const { uri, database, options } = mongooseConfig;
const { account_db: mongooseConfig } = config;
export let pnidConnection: mongoose.Connection;
export function connect() {
if(!pnidConnection)
pnidConnection = makeNewConnection(`${uri}/${database}`);
pnidConnection = makeNewConnection(mongooseConfig.connection_string);
}
export function verifyConnected() {
@ -18,7 +18,7 @@ export function verifyConnected() {
}
export function makeNewConnection(uri) {
pnidConnection = mongoose.createConnection(uri, options as mongoose.ConnectOptions);
pnidConnection = mongoose.createConnection(uri, mongooseConfig.options);
pnidConnection.on('error', function (error) {
LOG_ERROR(`MongoDB connection ${this.name} ${JSON.stringify(error)}`);
@ -36,4 +36,4 @@ export function makeNewConnection(uri) {
return pnidConnection;
}
pnidConnection = makeNewConnection(`${uri}/${database}`);
pnidConnection = makeNewConnection(mongooseConfig.connection_string);

111
src/config-manager.ts Normal file
View File

@ -0,0 +1,111 @@
import fs from 'fs-extra';
import mongoose from 'mongoose';
import dotenv from 'dotenv';
import { LOG_INFO, LOG_WARN, LOG_ERROR } from '@/logger';
import { Config } from '@/types/common/config';
dotenv.config();
LOG_INFO('Loading config');
let mongooseConnectOptionsMain: mongoose.ConnectOptions = {};
if (process.env.PN_MIIVERSE_API_CONFIG_MONGOOSE_CONNECT_OPTIONS_PATH) {
mongooseConnectOptionsMain = fs.readJSONSync(process.env.PN_MIIVERSE_API_CONFIG_MONGOOSE_CONNECT_OPTIONS_PATH);
} else {
LOG_WARN('No Mongoose connection options found for main connection. To add connection options, set PN_MIIVERSE_API_CONFIG_MONGOOSE_CONNECT_OPTIONS_PATH to the path of your options JSON file');
}
let mongooseConnectOptionsAccount: mongoose.ConnectOptions = {};
if (process.env.PN_MIIVERSE_API_CONFIG_MONGOOSE_ACCOUNT_SERVER_CONNECT_OPTIONS_PATH) {
mongooseConnectOptionsAccount = fs.readJSONSync(process.env.PN_MIIVERSE_API_CONFIG_MONGOOSE_ACCOUNT_SERVER_CONNECT_OPTIONS_PATH);
} else {
LOG_WARN('No Mongoose connection options found for main connection. To add connection options, set PN_MIIVERSE_API_CONFIG_MONGOOSE_ACCOUNT_SERVER_CONNECT_OPTIONS_PATH to the path of your options JSON file');
}
export const config: Config = {
http: {
port: Number(process.env.PN_MIIVERSE_API_CONFIG_HTTP_PORT || '')
},
account_server_address: process.env.PN_MIIVERSE_API_CONFIG_ACCOUNT_SERVER_ADDRESS || '',
account_server_secret: process.env.PN_MIIVERSE_API_CONFIG_ACCOUNT_SERVER_SECRET || '',
mongoose: {
connection_string: process.env.PN_MIIVERSE_API_CONFIG_MONGO_CONNECTION_STRING || '',
options: mongooseConnectOptionsMain
},
account_db: {
connection_string: process.env.PN_MIIVERSE_API_CONFIG_MONGO_ACCOUNT_SERVER_CONNECTION_STRING || '',
options: mongooseConnectOptionsAccount
},
s3: {
endpoint: process.env.PN_MIIVERSE_API_CONFIG_S3_ENDPOINT || '',
key: process.env.PN_MIIVERSE_API_CONFIG_S3_ACCESS_KEY || '',
secret: process.env.PN_MIIVERSE_API_CONFIG_S3_ACCESS_SECRET || ''
},
grpc: {
friends: {
ip: process.env.PN_MIIVERSE_API_CONFIG_GRPC_FRIENDS_IP || '',
port: Number(process.env.PN_MIIVERSE_API_CONFIG_GRPC_FRIENDS_PORT || ''),
api_key: process.env.PN_MIIVERSE_API_CONFIG_GRPC_FRIENDS_API_KEY || ''
}
}
};
LOG_INFO('Config loaded, checking integrity');
if (!config.http.port) {
LOG_ERROR('Failed to find HTTP port. Set the PN_MIIVERSE_API_CONFIG_HTTP_PORT environment variable');
process.exit(0);
}
if (!config.account_server_address) {
LOG_ERROR('Failed to find account server address. Set the PN_MIIVERSE_API_CONFIG_ACCOUNT_SERVER_ADDRESS environment variable');
process.exit(0);
}
if (!config.account_server_secret) {
LOG_ERROR('Failed to find account server secret. Set the PN_MIIVERSE_API_CONFIG_ACCOUNT_SERVER_SECRET environment variable');
process.exit(0);
}
if (!config.mongoose.connection_string) {
LOG_ERROR('Failed to find MongoDB connection string. Set the PN_MIIVERSE_API_CONFIG_MONGO_CONNECTION_STRING environment variable');
process.exit(0);
}
if (!config.account_db.connection_string) {
LOG_ERROR('Failed to find MongoDB Account Server connection string. Set the PN_MIIVERSE_API_CONFIG_MONGO_ACCOUNT_SERVER_CONNECTION_STRING environment variable');
process.exit(0);
}
if (!config.s3.endpoint) {
LOG_ERROR('Failed to find s3 endpoint. Set the PN_MIIVERSE_API_CONFIG_S3_ENDPOINT environment variable');
process.exit(0);
}
if (!config.s3.key) {
LOG_ERROR('Failed to find s3 key. Set the PN_MIIVERSE_API_CONFIG_S3_ACCESS_KEY environment variable');
process.exit(0);
}
if (!config.s3.secret) {
LOG_ERROR('Failed to find s3 secret. Set the PN_MIIVERSE_API_CONFIG_S3_ACCESS_SECRET environment variable');
process.exit(0);
}
if (!config.grpc.friends.ip) {
LOG_ERROR('Failed to find NEX Friends gRPC ip. Set the PN_MIIVERSE_API_CONFIG_GRPC_FRIENDS_IP environment variable');
process.exit(0);
}
if (!config.grpc.friends.port) {
LOG_ERROR('Failed to find NEX Friends gRPC port. Set the PN_MIIVERSE_API_CONFIG_GRPC_FRIENDS_PORT environment variable');
process.exit(0);
}
if (!config.grpc.friends.api_key) {
LOG_ERROR('Failed to find NEX Friends gRPC API key. Set the PN_MIIVERSE_API_CONFIG_GRPC_FRIENDS_API_KEY environment variable');
process.exit(0);
}

View File

@ -9,15 +9,14 @@ import { Notification } from '@/models/notification';
import { PNID } from '@/models/pnid';
import { Post } from '@/models/post';
import { Settings } from '@/models/settings';
import { config } from '@/config-manager';
import { mongoose as mongooseConfig } from '../config.json';
const { uri, database, options } = mongooseConfig;
const { mongoose: mongooseConfig } = config;
let connection;
export async function connect() {
await mongoose.connect(`${uri}/${database}`, options as mongoose.ConnectOptions || {});
await mongoose.connect(mongooseConfig.connection_string, mongooseConfig.options);
connection = mongoose.connection;
connection.on('connected', function () {
LOG_INFO(`MongoDB connected ${this.name}`);

View File

@ -5,12 +5,13 @@ import morgan from 'morgan';
import xml from 'object-to-xml';
import { connect as connectDatabase } from '@/database';
import { LOG_INFO, LOG_SUCCESS } from '@/logger';
import config from '../config.json';
import xmlparser from '@/middleware/xml-parser';
import auth from '@/middleware/auth';
import miiverse from '@/services/miiverse-api';
import { config } from '@/config-manager';
const { http: { port } } = config;
const app = express();

View File

@ -50,7 +50,7 @@ router.post('/', upload.none(), async function (req, res) {
return res.redirect(`/friend_messages/${conversation.id}`);
}
let paramPackData = decodeParamPack(req.headers["x-nintendo-parampack"]);
let appData = "", painting = "", paintingURI = "", screenshot = null;
let appData = "", painting = "", paintingURI, screenshot = null;
if (req.body.app_data)
appData = req.body.app_data.replace(/[^A-Za-z0-9+/=\s]/g, "");
if (req.body.painting) {

View File

@ -139,7 +139,7 @@ async function newPost(req, res) {
return res.sendStatus(403);
}
let appData = "", painting = "", paintingURI = "", screenshot = null;
let appData = "", painting = "", paintingURI, screenshot = null;
if (req.body.app_data)
appData = req.body.app_data.replace(/[^A-Za-z0-9+/=\s]/g, "");
if (req.body.painting) {

View File

@ -0,0 +1,29 @@
import mongoose from 'mongoose';
export interface Config {
http: {
port: number;
};
account_server_address: string;
account_server_secret: string;
mongoose: {
connection_string: string;
options: mongoose.ConnectOptions;
};
account_db: {
connection_string: string;
options: mongoose.ConnectOptions;
};
s3: {
endpoint: string;
key: string;
secret: string;
};
grpc: {
friends: {
ip: string;
port: number;
api_key: string;
};
};
}

View File

@ -1,5 +1,5 @@
// to make the file a module and avoid the TypeScript error
export {}
export {};
declare global {
namespace Express {

3
src/types/object-to-xml.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
declare module 'object-to-xml';
// TODO - Add proper types

3
src/types/tga.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
declare module 'tga';
// TODO - Add proper types

View File

@ -5,7 +5,7 @@ import fs from 'fs-extra';
import TGA from 'tga';
import pako from 'pako';
import { PNG } from 'pngjs';
import bmp from "bmp-js";
import bmp from 'bmp-js';
import aws from 'aws-sdk';
import { createChannel, createClient, Metadata } from 'nice-grpc';
import { FriendsDefinition } from 'pretendo-grpc-ts/src/friends/friends_service';
@ -17,241 +17,234 @@ import { Content } from '@/models/content';
import { SafeQs } from '@/types/common/safe-qs';
import { ParamPack } from '@/types/common/param-pack';
const config = require('../../config.json');
import { config } from '@/config-manager';
const { ip, port, api_key } = config.grpc.friends;
const channel = createChannel(`${ip}:${port}`);
const client = createClient(FriendsDefinition, channel);
const spacesEndpoint = new aws.Endpoint('nyc3.digitaloceanspaces.com');
const s3 = new aws.S3({
endpoint: spacesEndpoint,
accessKeyId: config.aws.spaces.key,
secretAccessKey: config.aws.spaces.secret
endpoint: new aws.Endpoint(config.s3.endpoint),
accessKeyId: config.s3.key,
secretAccessKey: config.s3.secret
});
export async function create_user(pid, experience, notifications, region) {
const pnid = await 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 pnid = await getPNID(pid);
if (!pnid) {
return;
}
const newSettings = {
pid: pid,
screen_name: pnid.mii.name,
game_skill: experience,
receive_notifications: notifications,
};
const newContent = {
pid: pid
};
const newSettingsObj = new Settings(newSettings);
await newSettingsObj.save();
const newContentObj = new Content(newContent);
await newContentObj.save();
const newContentObj = new Content(newContent);
await newContentObj.save();
}
export function decodeParamPack(paramPack): ParamPack {
/* Decode base64 */
let dec = Buffer.from(paramPack, "base64").toString("ascii").slice(1, -1).split("\\");
/* Remove starting and ending '/', split into array */
/* Parameters are in the format [name, val, name, val]. Copy into out{}. */
const out = {};
for (let i = 0; i < dec.length; i += 2) {
out[dec[i].trim()] = dec[i + 1].trim();
}
return out as ParamPack;
/* Decode base64 */
const dec = Buffer.from(paramPack, 'base64').toString('ascii').slice(1, -1).split('\\');
/* Remove starting and ending '/', split into array */
/* Parameters are in the format [name, val, name, val]. Copy into out{}. */
const out = {};
for (let i = 0; i < dec.length; i += 2) {
out[dec[i].trim()] = dec[i + 1].trim();
}
return out as ParamPack;
}
export function processServiceToken(token) {
try
{
let B64token = Buffer.from(token, 'base64');
let decryptedToken = this.decryptToken(B64token);
return decryptedToken.readUInt32LE(0x2);
}
catch(e)
{
return null;
}
try {
const B64token = Buffer.from(token, 'base64');
const decryptedToken = this.decryptToken(B64token);
return decryptedToken.readUInt32LE(0x2);
} catch (e) {
return null;
}
}
export function decryptToken(token) {
// 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 = `${__dirname}/../certs/access`;
const aesKey = Buffer.from(fs.readFileSync(`${cryptoPath}/aes.key`, { encoding: 'utf8' }), 'hex');
// 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 = `${__dirname}/../certs/access`;
const aesKey = Buffer.from(fs.readFileSync(`${cryptoPath}/aes.key`, { encoding: 'utf8' }), 'hex');
const iv = Buffer.alloc(16);
const iv = Buffer.alloc(16);
const decipher = crypto.createDecipheriv('aes-128-cbc', aesKey, iv);
const decipher = crypto.createDecipheriv('aes-128-cbc', aesKey, iv);
let decryptedBody = decipher.update(token);
decryptedBody = Buffer.concat([decryptedBody, decipher.final()]);
let decryptedBody = decipher.update(token);
decryptedBody = Buffer.concat([decryptedBody, decipher.final()]);
return decryptedBody;
}
return decryptedBody;
}
const cryptoPath = `${__dirname}/../certs/access`;
const cryptoPath = `${__dirname}/../certs/access`;
const cryptoOptions = {
private_key: fs.readFileSync(`${cryptoPath}/private.pem`),
hmac_secret: config.account_server_secret
};
const cryptoOptions = {
private_key: fs.readFileSync(`${cryptoPath}/private.pem`),
hmac_secret: config.account_server_secret
};
const privateKey = new NodeRSA(cryptoOptions.private_key, 'pkcs1-private-pem', {
environment: 'browser',
encryptionScheme: {
'hash': 'sha256',
}
});
const privateKey = new NodeRSA(cryptoOptions.private_key, 'pkcs1-private-pem', {
environment: 'browser',
encryptionScheme: {
scheme: 'pkcs1_oaep',
hash: 'sha256'
}
});
const cryptoConfig = token.subarray(0, 0x82);
const signature = token.subarray(0x82, 0x96);
const encryptedBody = token.subarray(0x96);
const cryptoConfig = token.subarray(0, 0x82);
const signature = token.subarray(0x82, 0x96);
const encryptedBody = token.subarray(0x96);
const encryptedAESKey = cryptoConfig.subarray(0, 128);
const point1 = cryptoConfig.readInt8(0x80);
const point2 = cryptoConfig.readInt8(0x81);
const encryptedAESKey = cryptoConfig.subarray(0, 128);
const point1 = cryptoConfig.readInt8(0x80);
const point2 = cryptoConfig.readInt8(0x81);
const iv = Buffer.concat([
Buffer.from(encryptedAESKey.subarray(point1, point1 + 8)),
Buffer.from(encryptedAESKey.subarray(point2, point2 + 8))
]);
const iv = Buffer.concat([
Buffer.from(encryptedAESKey.subarray(point1, point1 + 8)),
Buffer.from(encryptedAESKey.subarray(point2, point2 + 8))
]);
try {
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) {
LOG_ERROR('Token signature did not match');
return null;
}
if (Buffer.compare(calculatedSignature, signature) !== 0) {
LOG_ERROR('Token signature did not match');
return null;
}
return decryptedBody;
}
catch (e) {
LOG_ERROR('Failed to decrypt token. Probably a NNID from the topics request');
return null;
}
return decryptedBody;
} catch (e) {
LOG_ERROR('Failed to decrypt token. Probably a NNID from the topics request');
return null;
}
}
export async function processPainting(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);
if (isTGA) {
const paintingBuffer = Buffer.from(painting, 'base64');
let output;
try {
output = pako.inflate(paintingBuffer);
} catch (err) {
console.error(err);
}
const tga = new TGA(Buffer.from(output));
const png = new PNG({
width: tga.width,
height: tga.height
});
png.data = tga.pixels;
return PNG.sync.write(png);
//return `data:image/png;base64,${pngBuffer.toString('base64')}`;
} else {
const paintingBuffer = Buffer.from(painting, 'base64');
const bitmap = bmp.decode(paintingBuffer);
const tga = this.createBMPTgaBuffer(bitmap.width, bitmap.height, bitmap.data, false);
let output;
try
{
output = pako.deflate(tga, {level: 6});
}
catch (err)
{
console.error(err);
}
let output;
try {
output = pako.deflate(tga, {level: 6});
} catch (err) {
console.error(err);
}
return new Buffer(output).toString('base64')
}
return new Buffer(output).toString('base64');
}
}
export function nintendoPasswordHash(password, pid) {
const pidBuffer = Buffer.alloc(4);
pidBuffer.writeUInt32LE(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');
const unpacked = Buffer.concat([
pidBuffer,
Buffer.from('\x02\x65\x43\x46'),
Buffer.from(password)
]);
return crypto.createHash('sha256').update(unpacked).digest().toString('hex');
}
export function createBMPTgaBuffer(width, height, pixels, dontFlipY) {
var buffer = Buffer.alloc(18 + pixels.length);
// write header
buffer.writeInt8(0, 0);
buffer.writeInt8(0, 1);
buffer.writeInt8(2, 2);
buffer.writeInt16LE(0, 3);
buffer.writeInt16LE(0, 5);
buffer.writeInt8(0, 7);
buffer.writeInt16LE(0, 8);
buffer.writeInt16LE(0, 10);
buffer.writeInt16LE(width, 12);
buffer.writeInt16LE(height, 14);
buffer.writeInt8(32, 16);
buffer.writeInt8(8, 17);
const buffer = Buffer.alloc(18 + pixels.length);
// write header
buffer.writeInt8(0, 0);
buffer.writeInt8(0, 1);
buffer.writeInt8(2, 2);
buffer.writeInt16LE(0, 3);
buffer.writeInt16LE(0, 5);
buffer.writeInt8(0, 7);
buffer.writeInt16LE(0, 8);
buffer.writeInt16LE(0, 10);
buffer.writeInt16LE(width, 12);
buffer.writeInt16LE(height, 14);
buffer.writeInt8(32, 16);
buffer.writeInt8(8, 17);
var offset = 18;
for (var i = 0; i < height; i++) {
for (var j = 0; j < width; j++) {
var idx = ((dontFlipY ? i : height - i - 1) * width + j) * 4;
buffer.writeUInt8(pixels[idx + 1], offset++); // b
buffer.writeUInt8(pixels[idx + 2], offset++); // g
buffer.writeUInt8(pixels[idx + 3], offset++); // r
buffer.writeUInt8(255, offset++); // a
}
}
let offset = 18;
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
const idx = ((dontFlipY ? i : height - i - 1) * width + j) * 4;
buffer.writeUInt8(pixels[idx + 1], offset++); // b
buffer.writeUInt8(pixels[idx + 2], offset++); // g
buffer.writeUInt8(pixels[idx + 3], offset++); // r
buffer.writeUInt8(255, offset++); // a
}
}
return buffer;
return buffer;
}
export async function uploadCDNAsset(bucket, key, data, acl) {
const awsPutParams = {
Body: data,
Key: key,
Bucket: bucket,
ACL: acl
};
const awsPutParams = {
Body: data,
Key: key,
Bucket: bucket,
ACL: acl
};
await s3.putObject(awsPutParams).promise();
await s3.putObject(awsPutParams).promise();
}
export async function getFriends(pid) {
return await client.getUserFriendPIDs({
pid: pid
}, {
metadata: Metadata({
'X-API-Key': api_key
})
})
return await client.getUserFriendPIDs({
pid: pid
}, {
metadata: Metadata({
'X-API-Key': api_key
})
});
}
export async function getFriendRequests(pid) {
const requests = await client.getUserFriendRequestsIncoming({
pid: pid
}, {
metadata: Metadata({
'X-API-Key': api_key
})
});
return requests.friendRequests;
const requests = await client.getUserFriendRequestsIncoming({
pid: pid
}, {
metadata: Metadata({
'X-API-Key': api_key
})
});
return requests.friendRequests;
}
export function makeSafeQs(query: ParsedQs): SafeQs {