mirror of
https://github.com/PretendoNetwork/account.git
synced 2026-04-25 07:22:47 -05:00
Enable strict mode in TS and fix most issues with it
This commit is contained in:
parent
e508dd95d4
commit
dd952954fd
|
|
@ -1 +1,2 @@
|
|||
dist
|
||||
dist
|
||||
*.js
|
||||
3083
package-lock.json
generated
3083
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -33,7 +33,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"email-validator": "^2.0.4",
|
||||
"express": "^4.17.1",
|
||||
"express-rate-limit": "^5.3.0",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"express-subdomain": "^1.0.5",
|
||||
"fs-extra": "^8.1.0",
|
||||
"got": "^11.8.2",
|
||||
|
|
@ -55,13 +55,17 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/dicer": "^0.2.2",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/mongoose-unique-validator": "^1.0.7",
|
||||
"@types/morgan": "^1.9.4",
|
||||
"@types/node": "^18.14.4",
|
||||
"@types/node-rsa": "^1.1.1",
|
||||
"@types/nodemailer": "^6.4.7",
|
||||
"@types/validator": "^13.7.14",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.1",
|
||||
"@typescript-eslint/parser": "^5.54.1",
|
||||
"eslint": "^8.35.0",
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ export async function setCachedFile(fileName: string, value: Buffer): Promise<vo
|
|||
}
|
||||
|
||||
export async function getCachedFile(fileName: string, encoding?: BufferEncoding): Promise<Buffer> {
|
||||
let cachedFile: Buffer;
|
||||
let cachedFile: Buffer = Buffer.alloc(0);
|
||||
|
||||
if (disabledFeatures.redis) {
|
||||
cachedFile = memoryCache[fileName] || null;
|
||||
} else {
|
||||
const redisValue: string = await client.get(fileName);
|
||||
const redisValue: string | null = await client.get(fileName);
|
||||
if (redisValue) {
|
||||
cachedFile = Buffer.from(redisValue, encoding);
|
||||
}
|
||||
|
|
@ -177,7 +177,7 @@ export async function getLocalCDNFile(name: string, encoding?: BufferEncoding):
|
|||
|
||||
if (file === null) {
|
||||
if (await fs.pathExists(`${LOCAL_CDN_BASE}/${name}`)) {
|
||||
const fileBuffer: string = await fs.readFile(`${LOCAL_CDN_BASE}/${name}`, { encoding });
|
||||
const fileBuffer: string | Buffer = await fs.readFile(`${LOCAL_CDN_BASE}/${name}`, { encoding });
|
||||
file = Buffer.from(fileBuffer);
|
||||
await setLocalCDNFile(name, file);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export const disabledFeatures: DisabledFeatures = {
|
|||
|
||||
LOG_INFO('Loading config');
|
||||
|
||||
let mongooseConnectOptions: mongoose.ConnectOptions;
|
||||
let mongooseConnectOptions: mongoose.ConnectOptions = {};
|
||||
|
||||
if (process.env.PN_ACT_CONFIG_MONGOOSE_CONNECT_OPTIONS_PATH) {
|
||||
mongooseConnectOptions = fs.readJSONSync(process.env.PN_ACT_CONFIG_MONGOOSE_CONNECT_OPTIONS_PATH);
|
||||
|
|
@ -23,41 +23,41 @@ if (process.env.PN_ACT_CONFIG_MONGOOSE_CONNECT_OPTIONS_PATH) {
|
|||
|
||||
export const config: Config = {
|
||||
http: {
|
||||
port: Number(process.env.PN_ACT_CONFIG_HTTP_PORT)
|
||||
port: Number(process.env.PN_ACT_CONFIG_HTTP_PORT || '')
|
||||
},
|
||||
mongoose: {
|
||||
connection_string: process.env.PN_ACT_CONFIG_MONGO_CONNECTION_STRING,
|
||||
connection_string: process.env.PN_ACT_CONFIG_MONGO_CONNECTION_STRING || '',
|
||||
options: mongooseConnectOptions
|
||||
},
|
||||
redis: {
|
||||
client: {
|
||||
url: process.env.PN_ACT_CONFIG_REDIS_URL
|
||||
url: process.env.PN_ACT_CONFIG_REDIS_URL || ''
|
||||
}
|
||||
},
|
||||
email: {
|
||||
host: process.env.PN_ACT_CONFIG_EMAIL_HOST,
|
||||
port: Number(process.env.PN_ACT_CONFIG_EMAIL_PORT),
|
||||
secure: Boolean(process.env.PN_ACT_CONFIG_EMAIL_SECURE),
|
||||
host: process.env.PN_ACT_CONFIG_EMAIL_HOST || '',
|
||||
port: Number(process.env.PN_ACT_CONFIG_EMAIL_PORT || ''),
|
||||
secure: Boolean(process.env.PN_ACT_CONFIG_EMAIL_SECURE || ''),
|
||||
auth: {
|
||||
user: process.env.PN_ACT_CONFIG_EMAIL_USERNAME,
|
||||
pass: process.env.PN_ACT_CONFIG_EMAIL_PASSWORD
|
||||
user: process.env.PN_ACT_CONFIG_EMAIL_USERNAME || '',
|
||||
pass: process.env.PN_ACT_CONFIG_EMAIL_PASSWORD || ''
|
||||
},
|
||||
from: process.env.PN_ACT_CONFIG_EMAIL_FROM
|
||||
from: process.env.PN_ACT_CONFIG_EMAIL_FROM || ''
|
||||
},
|
||||
s3: {
|
||||
endpoint: process.env.PN_ACT_CONFIG_S3_ENDPOINT,
|
||||
key: process.env.PN_ACT_CONFIG_S3_ACCESS_KEY,
|
||||
secret: process.env.PN_ACT_CONFIG_S3_ACCESS_SECRET
|
||||
endpoint: process.env.PN_ACT_CONFIG_S3_ENDPOINT || '',
|
||||
key: process.env.PN_ACT_CONFIG_S3_ACCESS_KEY || '',
|
||||
secret: process.env.PN_ACT_CONFIG_S3_ACCESS_SECRET || ''
|
||||
},
|
||||
hcaptcha: {
|
||||
secret: process.env.PN_ACT_CONFIG_HCAPTCHA_SECRET
|
||||
secret: process.env.PN_ACT_CONFIG_HCAPTCHA_SECRET || ''
|
||||
},
|
||||
cdn: {
|
||||
subdomain: process.env.PN_ACT_CONFIG_CDN_SUBDOMAIN,
|
||||
disk_path: process.env.PN_ACT_CONFIG_CDN_DISK_PATH,
|
||||
base_url: process.env.PN_ACT_CONFIG_CDN_BASE_URL
|
||||
subdomain: process.env.PN_ACT_CONFIG_CDN_SUBDOMAIN || '',
|
||||
disk_path: process.env.PN_ACT_CONFIG_CDN_DISK_PATH || '',
|
||||
base_url: process.env.PN_ACT_CONFIG_CDN_BASE_URL || ''
|
||||
},
|
||||
website_base: process.env.PN_ACT_CONFIG_WEBSITE_BASE
|
||||
website_base: process.env.PN_ACT_CONFIG_WEBSITE_BASE || ''
|
||||
};
|
||||
|
||||
LOG_INFO('Config loaded, checking integrity');
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export function verifyConnected(): void {
|
|||
}
|
||||
}
|
||||
|
||||
export async function getUserByUsername(username: string): Promise<HydratedPNIDDocument> {
|
||||
export async function getUserByUsername(username: string): Promise<HydratedPNIDDocument | null> {
|
||||
verifyConnected();
|
||||
|
||||
return await PNID.findOne<HydratedPNIDDocument>({
|
||||
|
|
@ -50,7 +50,7 @@ export async function getUserByUsername(username: string): Promise<HydratedPNIDD
|
|||
});
|
||||
}
|
||||
|
||||
export async function getUserByPID(pid: number): Promise<HydratedPNIDDocument> {
|
||||
export async function getUserByPID(pid: number): Promise<HydratedPNIDDocument | null> {
|
||||
verifyConnected();
|
||||
|
||||
return await PNID.findOne<HydratedPNIDDocument>({
|
||||
|
|
@ -58,7 +58,7 @@ export async function getUserByPID(pid: number): Promise<HydratedPNIDDocument> {
|
|||
});
|
||||
}
|
||||
|
||||
export async function getUserByEmailAddress(email: string): Promise<HydratedPNIDDocument> {
|
||||
export async function getUserByEmailAddress(email: string): Promise<HydratedPNIDDocument | null> {
|
||||
verifyConnected();
|
||||
|
||||
// TODO - Update documents to store email normalized
|
||||
|
|
@ -73,7 +73,7 @@ export async function doesUserExist(username: string): Promise<boolean> {
|
|||
return !!await getUserByUsername(username);
|
||||
}
|
||||
|
||||
export async function getUserBasic(token: string): Promise<HydratedPNIDDocument> {
|
||||
export async function getUserBasic(token: string): Promise<HydratedPNIDDocument | null> {
|
||||
verifyConnected();
|
||||
|
||||
// * Wii U sends Basic auth as `username password`, where the password may not have spaces
|
||||
|
|
@ -84,7 +84,7 @@ export async function getUserBasic(token: string): Promise<HydratedPNIDDocument>
|
|||
const username: string = parts[0];
|
||||
const password: string = parts[1];
|
||||
|
||||
const user: HydratedPNIDDocument = await getUserByUsername(username);
|
||||
const user: HydratedPNIDDocument | null = await getUserByUsername(username);
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
|
|
@ -99,14 +99,14 @@ export async function getUserBasic(token: string): Promise<HydratedPNIDDocument>
|
|||
return user;
|
||||
}
|
||||
|
||||
export async function getUserBearer(token: string): Promise<HydratedPNIDDocument> {
|
||||
export async function getUserBearer(token: string): Promise<HydratedPNIDDocument | null> {
|
||||
verifyConnected();
|
||||
|
||||
try {
|
||||
const decryptedToken: Buffer = await decryptToken(Buffer.from(token, 'base64'));
|
||||
const unpackedToken: Token = unpackToken(decryptedToken);
|
||||
|
||||
const user: HydratedPNIDDocument = await getUserByPID(unpackedToken.pid);
|
||||
const user: HydratedPNIDDocument | null = await getUserByPID(unpackedToken.pid);
|
||||
|
||||
if (user) {
|
||||
const expireTime: number = Math.floor((Number(unpackedToken.expire_time) / 1000));
|
||||
|
|
@ -124,27 +124,38 @@ export async function getUserBearer(token: string): Promise<HydratedPNIDDocument
|
|||
}
|
||||
}
|
||||
|
||||
export async function getUserProfileJSONByPID(pid: number): Promise<PNIDProfile> {
|
||||
export async function getUserProfileJSONByPID(pid: number): Promise<PNIDProfile | null> {
|
||||
verifyConnected();
|
||||
|
||||
const user: HydratedPNIDDocument = await getUserByPID(pid);
|
||||
const user: HydratedPNIDDocument | null = await getUserByPID(pid);
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const device: HydratedDeviceDocument = user.get('devices')[0]; // * Just grab the first device
|
||||
let device_attributes: [{
|
||||
let device_attributes: {
|
||||
device_attribute: {
|
||||
name: string;
|
||||
value: string;
|
||||
created_date: string;
|
||||
};
|
||||
}];
|
||||
}[] = [];
|
||||
|
||||
if (device) {
|
||||
device_attributes = device.get('device_attributes').map(({name, value, created_date}) => ({
|
||||
device_attribute: {
|
||||
name,
|
||||
value,
|
||||
created_date: created_date ? created_date : ''
|
||||
}
|
||||
}));
|
||||
device_attributes = device.get('device_attributes').map((attribute: { name: string; value: string; created_date: string; }) => {
|
||||
const name: string = attribute.name;
|
||||
const value: string = attribute.value;
|
||||
const created_date: string = attribute.created_date;
|
||||
|
||||
return {
|
||||
device_attribute: {
|
||||
name,
|
||||
value,
|
||||
created_date: created_date ? created_date : ''
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -196,21 +207,21 @@ export async function getUserProfileJSONByPID(pid: number): Promise<PNIDProfile>
|
|||
};
|
||||
}
|
||||
|
||||
export async function getServer(gameServerId: string, accessMode: string): Promise<HydratedServerDocument> {
|
||||
export async function getServer(gameServerId: string, accessMode: string): Promise<HydratedServerDocument | null> {
|
||||
return await Server.findOne({
|
||||
game_server_id: gameServerId,
|
||||
access_mode: accessMode
|
||||
});
|
||||
}
|
||||
|
||||
export async function getServerByTitleId(titleId: string, accessMode: string): Promise<HydratedServerDocument> {
|
||||
export async function getServerByTitleId(titleId: string, accessMode: string): Promise<HydratedServerDocument | null> {
|
||||
return await Server.findOne({
|
||||
title_ids: titleId,
|
||||
access_mode: accessMode
|
||||
});
|
||||
}
|
||||
|
||||
export async function addUserConnection(pnid: HydratedPNIDDocument, data: ConnectionData, type: string): Promise<ConnectionResponse> {
|
||||
export async function addUserConnection(pnid: HydratedPNIDDocument, data: ConnectionData, type: string): Promise<ConnectionResponse | undefined> {
|
||||
if (type === 'discord') {
|
||||
return await addUserConnectionDiscord(pnid, data);
|
||||
}
|
||||
|
|
@ -239,7 +250,7 @@ export async function addUserConnectionDiscord(pnid: HydratedPNIDDocument, data:
|
|||
};
|
||||
}
|
||||
|
||||
export async function removeUserConnection(pnid: HydratedPNIDDocument, type: string): Promise<ConnectionResponse> {
|
||||
export async function removeUserConnection(pnid: HydratedPNIDDocument, type: string): Promise<ConnectionResponse | undefined> {
|
||||
// * Add more connections later?
|
||||
if (type === 'discord') {
|
||||
return await removeUserConnectionDiscord(pnid);
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ export async function sendMail(options: MailerOptions): Promise<void> {
|
|||
let html: string = confirmation ? confirmationEmailTemplate : genericEmailTemplate;
|
||||
|
||||
html = html.replace(/{{username}}/g, username);
|
||||
html = html.replace(/{{paragraph}}/g, paragraph);
|
||||
html = html.replace(/{{preview}}/g, (preview || ''));
|
||||
html = html.replace(/{{confirmation-href}}/g, (confirmation?.href || ''));
|
||||
html = html.replace(/{{confirmation-code}}/g, (confirmation?.code || ''));
|
||||
html = html.replace(/{{paragraph}}/g, paragraph || '');
|
||||
html = html.replace(/{{preview}}/g, preview || '');
|
||||
html = html.replace(/{{confirmation-href}}/g, confirmation?.href || '');
|
||||
html = html.replace(/{{confirmation-code}}/g, confirmation?.code || '');
|
||||
|
||||
if (link) {
|
||||
const { href, text } = link;
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ import { getUserBearer } from '@/database';
|
|||
import { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
|
||||
async function APIMiddleware(request: express.Request, _response: express.Response, next: express.NextFunction): Promise<void> {
|
||||
const authHeader: string = request.headers.authorization;
|
||||
const authHeader: string | undefined = request.headers.authorization;
|
||||
|
||||
if (!authHeader || !(authHeader.startsWith('Bearer'))) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const token: string = authHeader.split(' ')[1];
|
||||
const user: HydratedPNIDDocument = await getUserBearer(token);
|
||||
const user: HydratedPNIDDocument | null = await getUserBearer(token);
|
||||
|
||||
request.pnid = user;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ async function NASCMiddleware(request: express.Request, response: express.Respon
|
|||
const macAddressHash: string = crypto.createHash('sha256').update(macAddress).digest('base64');
|
||||
const fcdcertHash: string = crypto.createHash('sha256').update(fcdcert).digest('base64');
|
||||
|
||||
let pid: number;
|
||||
let pidHmac: string;
|
||||
let password: string;
|
||||
let pid: number = 0; // * Real PIDs are always positive and non-zero
|
||||
let pidHmac: string = '';
|
||||
let password: string = '';
|
||||
|
||||
if (requestParams.userid) {
|
||||
pid = Number(nintendoBase64Decode(requestParams.userid).toString());
|
||||
|
|
@ -68,7 +68,7 @@ async function NASCMiddleware(request: express.Request, response: express.Respon
|
|||
return;
|
||||
}
|
||||
|
||||
let model: string;
|
||||
let model: string = '';
|
||||
switch (serialNumber[0]) {
|
||||
case 'C':
|
||||
model = 'ctr';
|
||||
|
|
@ -95,7 +95,7 @@ async function NASCMiddleware(request: express.Request, response: express.Respon
|
|||
return;
|
||||
}
|
||||
|
||||
let device: HydratedDeviceDocument = await Device.findOne({
|
||||
let device: HydratedDeviceDocument | null = await Device.findOne({
|
||||
model,
|
||||
serial: serialNumber,
|
||||
environment,
|
||||
|
|
@ -174,7 +174,7 @@ async function NASCMiddleware(request: express.Request, response: express.Respon
|
|||
}
|
||||
}
|
||||
|
||||
const nexUser: HydratedNEXAccountDocument = await NEXAccount.findOne({ pid });
|
||||
const nexUser: HydratedNEXAccountDocument | null = await NEXAccount.findOne({ pid });
|
||||
|
||||
if (!nexUser || nexUser.get('access_level') < 0) {
|
||||
response.status(200).send(nascError('102'));
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { getUserBasic, getUserBearer } from '@/database';
|
|||
import { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
|
||||
async function PNIDMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): Promise<void> {
|
||||
const authHeader: string = request.headers.authorization;
|
||||
const authHeader: string | undefined = request.headers.authorization;
|
||||
|
||||
if (!authHeader || !(authHeader.startsWith('Bearer') || authHeader.startsWith('Basic'))) {
|
||||
return next();
|
||||
|
|
@ -13,7 +13,7 @@ async function PNIDMiddleware(request: express.Request, response: express.Respon
|
|||
const parts: string[] = authHeader.split(' ');
|
||||
const type: string = parts[0];
|
||||
let token: string = parts[1];
|
||||
let user: HydratedPNIDDocument;
|
||||
let user: HydratedPNIDDocument | null;
|
||||
|
||||
if (request.isCemu) {
|
||||
token = Buffer.from(token, 'hex').toString('base64');
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { document as xmlParser } from 'xmlbuilder2';
|
|||
|
||||
function XMLMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): void {
|
||||
if (request.method == 'POST' || request.method == 'PUT') {
|
||||
const contentType: string = request.headers['content-type'];
|
||||
const contentLength: string = request.headers['content-length'];
|
||||
const contentType: string | undefined = request.headers['content-type'];
|
||||
const contentLength: string | undefined = request.headers['content-length'];
|
||||
let body: string = '';
|
||||
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ PNIDSchema.method('generatePID', async function generatePID(): Promise<void> {
|
|||
|
||||
const pid: number = Math.floor(Math.random() * (max - min + 1) + min);
|
||||
|
||||
const inuse: HydratedPNIDDocument = await PNID.findOne({
|
||||
const inuse: HydratedPNIDDocument | null = await PNID.findOne({
|
||||
pid
|
||||
});
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ PNIDSchema.method('generateEmailValidationCode', async function generateEmailVal
|
|||
PNIDSchema.method('generateEmailValidationToken', async function generateEmailValidationToken(): Promise<void> {
|
||||
const token: string = crypto.randomBytes(32).toString('hex');
|
||||
|
||||
const inuse: HydratedPNIDDocument = await PNID.findOne({
|
||||
const inuse: HydratedPNIDDocument | null = await PNID.findOne({
|
||||
'identification.email_token': token
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const VALID_CONNECTION_TYPES: string[] = [
|
|||
*/
|
||||
router.post('/add/:type', async (request: express.Request, response: express.Response) => {
|
||||
const data: ConnectionData = request.body?.data;
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
const type: string = request.params.type;
|
||||
|
||||
if (!pnid) {
|
||||
|
|
@ -44,9 +44,17 @@ router.post('/add/:type', async (request: express.Request, response: express.Res
|
|||
});
|
||||
}
|
||||
|
||||
const result: ConnectionResponse = await addUserConnection(pnid, data, type);
|
||||
let result: ConnectionResponse | undefined = await addUserConnection(pnid, data, type);
|
||||
|
||||
response.status(result.status).json(result);
|
||||
if (!result) {
|
||||
result = {
|
||||
app: 'api',
|
||||
status: 500,
|
||||
error: 'Unknown server error'
|
||||
};
|
||||
}
|
||||
|
||||
response.status(result.status || 500).json(result);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -55,7 +63,7 @@ router.post('/add/:type', async (request: express.Request, response: express.Res
|
|||
* Description: Removes an account connection from the users PNID
|
||||
*/
|
||||
router.delete('/remove/:type', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
const type: string = request.params.type;
|
||||
|
||||
if (!pnid) {
|
||||
|
|
@ -74,7 +82,15 @@ router.delete('/remove/:type', async (request: express.Request, response: expres
|
|||
});
|
||||
}
|
||||
|
||||
const result: ConnectionResponse = await removeUserConnection(pnid, type);
|
||||
let result: ConnectionResponse | undefined = await removeUserConnection(pnid, type);
|
||||
|
||||
if (!result) {
|
||||
result = {
|
||||
app: 'api',
|
||||
status: 500,
|
||||
error: 'Unknown server error'
|
||||
};
|
||||
}
|
||||
|
||||
response.status(result.status).json(result);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
|||
const router: express.Router = express.Router();
|
||||
|
||||
router.get('/verify', async (request: express.Request, response: express.Response) => {
|
||||
let token: string = request.query.token as string;
|
||||
const token: string = request.query.token as string;
|
||||
|
||||
if (!token || token.trim() == '') {
|
||||
return response.status(400).json({
|
||||
|
|
@ -17,7 +17,7 @@ router.get('/verify', async (request: express.Request, response: express.Respons
|
|||
});
|
||||
}
|
||||
|
||||
const pnid: HydratedPNIDDocument = await PNID.findOne({
|
||||
const pnid: HydratedPNIDDocument | null = await PNID.findOne({
|
||||
'identification.email_token': token
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ router.post('/', async (request: express.Request, response: express.Response) =>
|
|||
});
|
||||
}
|
||||
|
||||
let pnid: HydratedPNIDDocument;
|
||||
let pnid: HydratedPNIDDocument | null;
|
||||
|
||||
if (validator.isEmail(input)) {
|
||||
pnid = await getUserByEmailAddress(input);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ router.post('/', async (request: express.Request, response: express.Response) =>
|
|||
});
|
||||
}
|
||||
|
||||
let pnid: HydratedPNIDDocument;
|
||||
let pnid: HydratedPNIDDocument | null;
|
||||
|
||||
if (grantType === 'password') {
|
||||
pnid = await getUserByUsername(username);
|
||||
|
|
@ -125,8 +125,10 @@ router.post('/', async (request: express.Request, response: express.Response) =>
|
|||
expire_time: BigInt(Date.now() + (3600 * 1000))
|
||||
};
|
||||
|
||||
const accessToken: string = await generateToken(cryptoOptions, accessTokenOptions);
|
||||
const newRefreshToken: string = await generateToken(cryptoOptions, refreshTokenOptions);
|
||||
const accessToken: string | null = await generateToken(cryptoOptions, accessTokenOptions);
|
||||
const newRefreshToken: string | null = await generateToken(cryptoOptions, refreshTokenOptions);
|
||||
|
||||
// TODO - Handle null tokens
|
||||
|
||||
response.json({
|
||||
access_token: accessToken,
|
||||
|
|
|
|||
|
|
@ -363,8 +363,10 @@ router.post('/', async (request: express.Request, response: express.Response) =>
|
|||
expire_time: BigInt(Date.now() + (3600 * 1000))
|
||||
};
|
||||
|
||||
const accessToken: string = await generateToken(cryptoOptions, accessTokenOptions);
|
||||
const refreshToken: string = await generateToken(cryptoOptions, refreshTokenOptions);
|
||||
const accessToken: string | null = await generateToken(cryptoOptions, accessTokenOptions);
|
||||
const refreshToken: string | null = await generateToken(cryptoOptions, refreshTokenOptions);
|
||||
|
||||
// TODO - Handle null tokens
|
||||
|
||||
response.json({
|
||||
access_token: accessToken,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ router.post('/', async (request: express.Request, response: express.Response) =>
|
|||
});
|
||||
}
|
||||
|
||||
const pnid: HydratedPNIDDocument = await PNID.findOne({ pid: unpackedToken.pid });
|
||||
const pnid: HydratedPNIDDocument | null = await PNID.findOne({ pid: unpackedToken.pid });
|
||||
|
||||
if (!pnid) {
|
||||
return response.status(400).json({
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const userSchema: joi.ObjectSchema = joi.object({
|
|||
* Description: Gets PNID details about the current user
|
||||
*/
|
||||
router.get('/', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
if (!pnid) {
|
||||
return response.status(400).json({
|
||||
|
|
@ -70,7 +70,7 @@ router.get('/', async (request: express.Request, response: express.Response) =>
|
|||
* Description: Updates PNID certain details about the current user
|
||||
*/
|
||||
router.post('/', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
const updateUserRequest: UpdateUserRequest = request.body;
|
||||
|
||||
if (!pnid) {
|
||||
|
|
|
|||
|
|
@ -8,11 +8,22 @@ const router: express.Router = express.Router();
|
|||
|
||||
const signatureSecret: Buffer = fs.readFileSync(`${__dirname}/../../../../certs/nex/datastore/secret.key`);
|
||||
|
||||
function multipartParser(request: express.Request, response: express.Response, next: express.NextFunction) {
|
||||
function multipartParser(request: express.Request, response: express.Response, next: express.NextFunction): void {
|
||||
const RE_BOUNDARY: RegExp = /^multipart\/.+?(?:; boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i;
|
||||
const RE_FILE_NAME: RegExp = /name="(.*)"/;
|
||||
|
||||
const boundary: RegExpExecArray = RE_BOUNDARY.exec(request.header('content-type'));
|
||||
const contentType: string | undefined = request.header('content-type');
|
||||
|
||||
if (!contentType) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const boundary: RegExpExecArray | null = RE_BOUNDARY.exec(contentType);
|
||||
|
||||
if (!boundary) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const dicer: Dicer = new Dicer({ boundary: boundary[1] || boundary[2] });
|
||||
const files: { [key: string]: Buffer } = {};
|
||||
|
||||
|
|
@ -21,6 +32,7 @@ function multipartParser(request: express.Request, response: express.Response, n
|
|||
let fileName: string = '';
|
||||
|
||||
part.on('header', header => {
|
||||
// TODO - strict mode yells here
|
||||
fileName = RE_FILE_NAME.exec(header['content-disposition'][0])[1];
|
||||
});
|
||||
|
||||
|
|
@ -46,6 +58,10 @@ function multipartParser(request: express.Request, response: express.Response, n
|
|||
}
|
||||
|
||||
router.post('/upload', multipartParser, async (request: express.Request, response: express.Response) => {
|
||||
if (!request.files) {
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
|
||||
const bucket: string = request.files.bucket.toString();
|
||||
const key: string = request.files.key.toString();
|
||||
const file: Buffer = request.files.file;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const router: express.Router = express.Router();
|
|||
router.post('/', async (request: express.Request, response: express.Response) => {
|
||||
const requestParams: NASCRequestParams = request.body;
|
||||
const action: string = nintendoBase64Decode(requestParams.action).toString();
|
||||
let responseData: URLSearchParams;
|
||||
let responseData: URLSearchParams = nascError('null');
|
||||
|
||||
switch (action) {
|
||||
case 'LOGIN':
|
||||
|
|
@ -35,7 +35,12 @@ router.post('/', async (request: express.Request, response: express.Response) =>
|
|||
async function processLoginRequest(request: express.Request): Promise<URLSearchParams> {
|
||||
const requestParams: NASCRequestParams = request.body;
|
||||
const titleID: string = nintendoBase64Decode(requestParams.titleid).toString();
|
||||
const nexUser: HydratedNEXAccountDocument = request.nexUser;
|
||||
const nexUser: HydratedNEXAccountDocument | null = request.nexUser;
|
||||
|
||||
if (!nexUser) {
|
||||
// TODO - Research this error more
|
||||
return nascError('null');
|
||||
}
|
||||
|
||||
// TODO: REMOVE AFTER PUBLIC LAUNCH
|
||||
// LET EVERYONE IN THE `test` FRIENDS SERVER
|
||||
|
|
@ -45,7 +50,7 @@ async function processLoginRequest(request: express.Request): Promise<URLSearchP
|
|||
serverAccessLevel = nexUser.get('server_access_level');
|
||||
}
|
||||
|
||||
const server: HydratedServerDocument = await getServerByTitleId(titleID, serverAccessLevel);
|
||||
const server: HydratedServerDocument | null = await getServerByTitleId(titleID, serverAccessLevel);
|
||||
|
||||
if (!server || !server.service_name || !server.ip || !server.port) {
|
||||
return nascError('110');
|
||||
|
|
@ -72,8 +77,10 @@ async function processLoginRequest(request: express.Request): Promise<URLSearchP
|
|||
expire_time: BigInt(Date.now() + (3600 * 1000))
|
||||
};
|
||||
|
||||
let nexToken: string = await generateToken(cryptoOptions, tokenOptions);
|
||||
nexToken = nintendoBase64Encode(Buffer.from(nexToken, 'base64'));
|
||||
// TODO - Handle null tokens
|
||||
|
||||
let nexToken: string | null = await generateToken(cryptoOptions, tokenOptions);
|
||||
nexToken = nintendoBase64Encode(Buffer.from(nexToken || '', 'base64'));
|
||||
|
||||
return new URLSearchParams({
|
||||
locator: nintendoBase64Encode(`${ip}:${port}`),
|
||||
|
|
|
|||
|
|
@ -64,9 +64,10 @@ router.get('/mapped_ids', async (request: express.Request, response: express.Res
|
|||
pid?: number;
|
||||
} = {};
|
||||
|
||||
// TODO - TS strict mode...what?
|
||||
query[queryInput] = input;
|
||||
|
||||
const searchResult: HydratedPNIDDocument = await PNID.findOne(query);
|
||||
const searchResult: HydratedPNIDDocument | null = await PNID.findOne(query);
|
||||
|
||||
if (searchResult) {
|
||||
result.out_id = searchResult.get(queryOutput);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ router.post('/access_token/generate', async (request: express.Request, response:
|
|||
}).end());
|
||||
}
|
||||
|
||||
const pnid: HydratedPNIDDocument = await getUserByUsername(username);
|
||||
const pnid: HydratedPNIDDocument | null = await getUserByUsername(username);
|
||||
|
||||
if (!pnid || !await bcrypt.compare(password, pnid.password)) {
|
||||
response.status(400);
|
||||
|
|
@ -103,12 +103,14 @@ router.post('/access_token/generate', async (request: express.Request, response:
|
|||
expire_time: BigInt(Date.now() + (3600 * 1000))
|
||||
};
|
||||
|
||||
let accessToken: string = await generateToken(null, accessTokenOptions);
|
||||
let refreshToken: string = await generateToken(null, refreshTokenOptions);
|
||||
let accessToken: string | null = await generateToken(null, accessTokenOptions);
|
||||
let refreshToken: string | null = await generateToken(null, refreshTokenOptions);
|
||||
|
||||
// TODO - Handle null tokens
|
||||
|
||||
if (request.isCemu) {
|
||||
accessToken = Buffer.from(accessToken, 'base64').toString('hex');
|
||||
refreshToken = Buffer.from(refreshToken, 'base64').toString('hex');
|
||||
accessToken = Buffer.from(accessToken || '', 'base64').toString('hex');
|
||||
refreshToken = Buffer.from(refreshToken || '', 'base64').toString('hex');
|
||||
}
|
||||
|
||||
response.send(xmlbuilder.create({
|
||||
|
|
|
|||
|
|
@ -212,9 +212,21 @@ router.get('/@me/profile', async (request: express.Request, response: express.Re
|
|||
response.set('Server', 'Nintendo 3DS (http)');
|
||||
response.set('X-Nintendo-Date', new Date().getTime().toString());
|
||||
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
const person: PNIDProfile = await getUserProfileJSONByPID(pnid.get('pid'));
|
||||
if (!pnid) {
|
||||
// TODO - Research this error more
|
||||
response.status(404);
|
||||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
const person: PNIDProfile | null = await getUserProfileJSONByPID(pnid.get('pid'));
|
||||
|
||||
if (!person) {
|
||||
// TODO - Research this error more
|
||||
response.status(404);
|
||||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
response.send(xmlbuilder.create({
|
||||
person
|
||||
|
|
@ -237,9 +249,21 @@ router.post('/@me/devices', async (request: express.Request, response: express.R
|
|||
|
||||
// TODO - CHANGE THIS. WE NEED TO SAVE CONSOLE DETAILS !!!
|
||||
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
const person: PNIDProfile = await getUserProfileJSONByPID(pnid.pid);
|
||||
if (!pnid) {
|
||||
// TODO - Research this error more
|
||||
response.status(404);
|
||||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
const person: PNIDProfile | null = await getUserProfileJSONByPID(pnid.get('pid'));
|
||||
|
||||
if (!person) {
|
||||
// TODO - Research this error more
|
||||
response.status(404);
|
||||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
response.send(xmlbuilder.create({
|
||||
person
|
||||
|
|
@ -256,7 +280,7 @@ router.get('/@me/devices', async (request: express.Request, response: express.Re
|
|||
response.set('Server', 'Nintendo 3DS (http)');
|
||||
response.set('X-Nintendo-Date', new Date().getTime().toString());
|
||||
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
const deviceId: string = request.headers['x-nintendo-device-id'] as string;
|
||||
const acceptLanguage: string = request.headers['accept-language'] as string;
|
||||
const platformId: string = request.headers['x-nintendo-platform-id'] as string;
|
||||
|
|
@ -264,6 +288,12 @@ router.get('/@me/devices', async (request: express.Request, response: express.Re
|
|||
const serialNumber: string = request.headers['x-nintendo-serial-number'] as string;
|
||||
const systemVersion: string = request.headers['x-nintendo-system-version'] as string;
|
||||
|
||||
if (!pnid) {
|
||||
// TODO - Research this error more
|
||||
response.status(404);
|
||||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
response.send(xmlbuilder.create({
|
||||
devices: [
|
||||
{
|
||||
|
|
@ -295,9 +325,21 @@ router.get('/@me/devices/owner', async (request: express.Request, response: expr
|
|||
response.set('Server', 'Nintendo 3DS (http)');
|
||||
response.set('X-Nintendo-Date', moment().add(5, 'h').toString());
|
||||
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
const person: PNIDProfile = await getUserProfileJSONByPID(pnid.get('pid'));
|
||||
if (!pnid) {
|
||||
// TODO - Research this error more
|
||||
response.status(404);
|
||||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
const person: PNIDProfile | null = await getUserProfileJSONByPID(pnid.get('pid'));
|
||||
|
||||
if (!person) {
|
||||
// TODO - Research this error more
|
||||
response.status(404);
|
||||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
response.send(xmlbuilder.create({
|
||||
person
|
||||
|
|
@ -326,14 +368,22 @@ router.get('/@me/devices/status', async (_request: express.Request, response: ex
|
|||
* Description: Updates a users Mii
|
||||
*/
|
||||
router.put('/@me/miis/@primary', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
if (!pnid) {
|
||||
// TODO - Research this error more
|
||||
response.status(404);
|
||||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
// TODO - Make this more strictly typed?
|
||||
const mii: Map<string, string> = request.body.get('mii');
|
||||
|
||||
const name: string = mii.get('name');
|
||||
const primary: string = mii.get('primary');
|
||||
const data: string = mii.get('data');
|
||||
// TODO - Better checks
|
||||
|
||||
const name: string | undefined = mii.get('name') || '';
|
||||
const primary: string | undefined = mii.get('primary') || '';
|
||||
const data: string | undefined = mii.get('data') || '';
|
||||
|
||||
await pnid.updateMii({ name, primary, data });
|
||||
|
||||
|
|
@ -349,7 +399,7 @@ router.put('/@me/devices/@current/inactivate', async (request: express.Request,
|
|||
response.set('Server', 'Nintendo 3DS (http)');
|
||||
response.set('X-Nintendo-Date', new Date().getTime().toString());
|
||||
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
if (!pnid) {
|
||||
response.status(400);
|
||||
|
|
@ -375,7 +425,7 @@ router.put('/@me/devices/@current/inactivate', async (request: express.Request,
|
|||
* Description: Deletes a NNID
|
||||
*/
|
||||
router.put('/@me/deletion', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
if (!pnid) {
|
||||
response.status(400);
|
||||
|
|
@ -400,7 +450,7 @@ router.put('/@me/deletion', async (request: express.Request, response: express.R
|
|||
* Description: Updates a PNIDs account details
|
||||
*/
|
||||
router.put('/@me', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
const person: Person = request.body.get('person');
|
||||
|
||||
if (!pnid) {
|
||||
|
|
@ -454,7 +504,7 @@ router.put('/@me', async (request: express.Request, response: express.Response)
|
|||
* Description: Gets a list (why?) of PNID emails
|
||||
*/
|
||||
router.get('/@me/emails', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
if (!pnid) {
|
||||
response.status(400);
|
||||
|
|
@ -495,12 +545,12 @@ router.get('/@me/emails', async (request: express.Request, response: express.Res
|
|||
* Description: Updates a users email address
|
||||
*/
|
||||
router.put('/@me/emails/@primary', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
// TODO - Make this more strictly typed?
|
||||
const email: Map<string, string> = request.body.get('email');
|
||||
|
||||
if (!pnid) {
|
||||
if (!pnid || !email) {
|
||||
response.status(400);
|
||||
|
||||
return response.end(xmlbuilder.create({
|
||||
|
|
@ -514,7 +564,8 @@ router.put('/@me/emails/@primary', async (request: express.Request, response: ex
|
|||
}).end());
|
||||
}
|
||||
|
||||
pnid.set('email.address', email.get('address').toLowerCase());
|
||||
// TODO - Better email check
|
||||
pnid.set('email.address', (email.get('address') || '').toLowerCase());
|
||||
pnid.set('email.reachable', false);
|
||||
pnid.set('email.validated', false);
|
||||
pnid.set('email.validated_date', '');
|
||||
|
|
|
|||
|
|
@ -19,11 +19,25 @@ const router: express.Router = express.Router();
|
|||
* Description: Gets a service token
|
||||
*/
|
||||
router.get('/service_token/@me', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
if (!pnid) {
|
||||
response.status(400);
|
||||
|
||||
return response.end(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'access_token',
|
||||
code: '0002',
|
||||
message: 'Invalid access token'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
const titleId: string = request.headers['x-nintendo-title-id'] as string;
|
||||
const serverAccessLevel: string = pnid.get('server_access_level');
|
||||
const server: HydratedServerDocument = await getServerByTitleId(titleId, serverAccessLevel);
|
||||
const server: HydratedServerDocument | null = await getServerByTitleId(titleId, serverAccessLevel);
|
||||
|
||||
if (!server) {
|
||||
return response.send(xmlbuilder.create({
|
||||
|
|
@ -70,10 +84,12 @@ router.get('/service_token/@me', async (request: express.Request, response: expr
|
|||
expire_time: BigInt(Date.now() + (3600 * 1000))
|
||||
};
|
||||
|
||||
let serviceToken: string = await generateToken(cryptoOptions, tokenOptions);
|
||||
let serviceToken: string | null = await generateToken(cryptoOptions, tokenOptions);
|
||||
|
||||
// TODO - Handle null tokens
|
||||
|
||||
if (request.isCemu) {
|
||||
serviceToken = Buffer.from(serviceToken, 'base64').toString('hex');
|
||||
serviceToken = Buffer.from(serviceToken || '', 'base64').toString('hex');
|
||||
}
|
||||
|
||||
response.send(xmlbuilder.create({
|
||||
|
|
@ -89,7 +105,22 @@ router.get('/service_token/@me', async (request: express.Request, response: expr
|
|||
* Description: Gets a NEX server address and token
|
||||
*/
|
||||
router.get('/nex_token/@me', async (request: express.Request, response: express.Response) => {
|
||||
const pnid: HydratedPNIDDocument = request.pnid;
|
||||
const pnid: HydratedPNIDDocument | null = request.pnid;
|
||||
|
||||
if (!pnid) {
|
||||
response.status(400);
|
||||
|
||||
return response.end(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'access_token',
|
||||
code: '0002',
|
||||
message: 'Invalid access token'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
}
|
||||
|
||||
const gameServerID: string = request.query.game_server_id as string;
|
||||
|
||||
if (!gameServerID) {
|
||||
|
|
@ -104,7 +135,7 @@ router.get('/nex_token/@me', async (request: express.Request, response: express.
|
|||
}
|
||||
|
||||
const serverAccessLevel: string = pnid.get('server_access_level');
|
||||
const server: HydratedServerDocument = await getServer(gameServerID, serverAccessLevel);
|
||||
const server: HydratedServerDocument | null = await getServer(gameServerID, serverAccessLevel);
|
||||
|
||||
if (!server) {
|
||||
return response.send(xmlbuilder.create({
|
||||
|
|
@ -154,7 +185,7 @@ router.get('/nex_token/@me', async (request: express.Request, response: express.
|
|||
expire_time: BigInt(Date.now() + (3600 * 1000))
|
||||
};
|
||||
|
||||
const nexUser: HydratedNEXAccountDocument = await NEXAccount.findOne({
|
||||
const nexUser: HydratedNEXAccountDocument | null = await NEXAccount.findOne({
|
||||
owning_pid: pnid.get('pid')
|
||||
});
|
||||
|
||||
|
|
@ -163,10 +194,12 @@ router.get('/nex_token/@me', async (request: express.Request, response: express.
|
|||
return response.send('<errors><error><cause/><code>0008</code><message>Not Found</message></error></errors>');
|
||||
}
|
||||
|
||||
let nexToken: string = await generateToken(cryptoOptions, tokenOptions);
|
||||
let nexToken: string | null = await generateToken(cryptoOptions, tokenOptions);
|
||||
|
||||
// TODO = Handle null tokens
|
||||
|
||||
if (request.isCemu) {
|
||||
nexToken = Buffer.from(nexToken, 'base64').toString('hex');
|
||||
nexToken = Buffer.from(nexToken || '', 'base64').toString('hex');
|
||||
}
|
||||
|
||||
response.send(xmlbuilder.create({
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ router.post('/validate/email', async (request: express.Request, response: expres
|
|||
|
||||
const domain: string = email.split('@')[1];
|
||||
|
||||
dns.resolveMx(domain, (error: NodeJS.ErrnoException) => {
|
||||
dns.resolveMx(domain, (error: NodeJS.ErrnoException | null) => {
|
||||
if (error) {
|
||||
return response.send(xmlbuilder.create({
|
||||
errors: {
|
||||
|
|
@ -56,7 +56,7 @@ router.put('/email_confirmation/:pid/:code', async (request: express.Request, re
|
|||
const code: string = request.params.code;
|
||||
const pid: number = Number(request.params.pid);
|
||||
|
||||
const pnid: HydratedPNIDDocument = await getUserByPID(pid);
|
||||
const pnid: HydratedPNIDDocument | null = await getUserByPID(pid);
|
||||
|
||||
if (!pnid) {
|
||||
return response.status(400).send(xmlbuilder.create({
|
||||
|
|
@ -101,7 +101,7 @@ router.put('/email_confirmation/:pid/:code', async (request: express.Request, re
|
|||
router.get('/resend_confirmation', async (request: express.Request, response: express.Response) => {
|
||||
const pid: number = Number(request.headers['x-nintendo-pid']);
|
||||
|
||||
const pnid: HydratedPNIDDocument = await getUserByPID(pid);
|
||||
const pnid: HydratedPNIDDocument | null = await getUserByPID(pid);
|
||||
|
||||
if (!pnid) {
|
||||
// TODO - Unsure if this is the right error
|
||||
|
|
@ -129,7 +129,7 @@ router.get('/resend_confirmation', async (request: express.Request, response: ex
|
|||
router.get('/forgotten_password/:pid', async (request: express.Request, response: express.Response) => {
|
||||
const pid: number = Number(request.params.pid);
|
||||
|
||||
const pnid: HydratedPNIDDocument = await getUserByPID(pid);
|
||||
const pnid: HydratedPNIDDocument | null = await getUserByPID(pid);
|
||||
|
||||
if (!pnid) {
|
||||
// TODO - Better errors
|
||||
|
|
|
|||
4
src/types/express.d.ts
vendored
4
src/types/express.d.ts
vendored
|
|
@ -5,8 +5,8 @@ import { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account';
|
|||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
pnid?: HydratedPNIDDocument;
|
||||
nexUser?: HydratedNEXAccountDocument;
|
||||
pnid: HydratedPNIDDocument | null;
|
||||
nexUser: HydratedNEXAccountDocument | null;
|
||||
isCemu?: boolean;
|
||||
files?: Record<string, any>;
|
||||
certificate?: NintendoCertificate;
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ export interface PNIDProfile {
|
|||
birth_date: string;
|
||||
country: string;
|
||||
create_date: string;
|
||||
device_attributes: [{
|
||||
device_attributes: {
|
||||
device_attribute: {
|
||||
name: string;
|
||||
value: string;
|
||||
created_date: string;
|
||||
};
|
||||
}];
|
||||
}[];
|
||||
gender: string;
|
||||
language: string;
|
||||
updated: string;
|
||||
|
|
|
|||
15
src/util.ts
15
src/util.ts
|
|
@ -47,7 +47,7 @@ export function nintendoBase64Encode(decoded: string | Buffer): string {
|
|||
return encoded.replaceAll('+', '.').replaceAll('/', '-').replaceAll('=', '*');
|
||||
}
|
||||
|
||||
export async function generateToken(cryptoOptions: CryptoOptions | null, tokenOptions: TokenOptions): Promise<string> {
|
||||
export async function generateToken(cryptoOptions: CryptoOptions | null, tokenOptions: TokenOptions): Promise<string | null> {
|
||||
// Access and refresh tokens use a different format since they must be much smaller
|
||||
// They take no extra crypto options
|
||||
if (!cryptoOptions) {
|
||||
|
|
@ -67,6 +67,8 @@ export async function generateToken(cryptoOptions: CryptoOptions | null, tokenOp
|
|||
encryptedBody = Buffer.concat([encryptedBody, cipher.final()]);
|
||||
|
||||
return encryptedBody.toString('base64');
|
||||
} else if (!tokenOptions.access_level || !tokenOptions.title_id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const publicKey: NodeRSA = new NodeRSA(cryptoOptions.public_key, 'pkcs8-public-pem', {
|
||||
|
|
@ -191,8 +193,9 @@ export async function decryptToken(token: Buffer): Promise<Buffer> {
|
|||
const calculatedSignature: Buffer = hmac.digest();
|
||||
|
||||
if (!signature.equals(calculatedSignature)) {
|
||||
// TODO - FIX THIS. ONLY DONE SO STRICT MODE DOESN'T YELL
|
||||
console.log('Token signature did not match');
|
||||
return null;
|
||||
return Buffer.alloc(0);
|
||||
}
|
||||
|
||||
return decryptedBody;
|
||||
|
|
@ -300,7 +303,9 @@ export async function sendForgotPasswordEmail(pnid: mongoose.HydratedDocument<IP
|
|||
expire_time: BigInt(Date.now() + (24 * 60 * 60 * 1000)) // Only valid for 24 hours
|
||||
};
|
||||
|
||||
const passwordResetToken: string = await generateToken(cryptoOptions, tokenOptions);
|
||||
const passwordResetToken: string | null = await generateToken(cryptoOptions, tokenOptions);
|
||||
|
||||
// TODO - Handle null token
|
||||
|
||||
const mailerOptions: MailerOptions = {
|
||||
to: pnid.get('email.address'),
|
||||
|
|
@ -309,9 +314,9 @@ export async function sendForgotPasswordEmail(pnid: mongoose.HydratedDocument<IP
|
|||
paragraph: 'a password reset has been requested from this account. If you did not request the password reset, please ignore this email. If you did request this password reset, please click the link below to reset your password.',
|
||||
link: {
|
||||
text: 'Reset password',
|
||||
href: `${config.website_base}/account/reset-password?token=${encodeURIComponent(passwordResetToken)}`
|
||||
href: `${config.website_base}/account/reset-password?token=${encodeURIComponent(passwordResetToken || '')}`
|
||||
},
|
||||
text: `Dear ${pnid.get('username')}, a password reset has been requested from this account. \r\n\r\nIf you did not request the password reset, please ignore this email. \r\nIf you did request this password reset, please click the link to reset your password: ${config.website_base}/account/reset-password?token=${encodeURIComponent(passwordResetToken)}`
|
||||
text: `Dear ${pnid.get('username')}, a password reset has been requested from this account. \r\n\r\nIf you did not request the password reset, please ignore this email. \r\nIf you did request this password reset, please click the link to reset your password: ${config.website_base}/account/reset-password?token=${encodeURIComponent(passwordResetToken || '')}`
|
||||
};
|
||||
|
||||
await sendMail(mailerOptions);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"resolveJsonModule": true,
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user