From 78c61249c51511fde28634ab2277be0816e7b998 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Mon, 2 Oct 2023 14:55:52 -0400 Subject: [PATCH] removed unnecessary type defs. prefer inference --- .eslintrc.json | 2 +- src/logger.ts | 10 +- src/middleware/auth.ts | 14 +- src/middleware/client-header.ts | 10 +- src/models/post.ts | 6 +- src/server.ts | 2 +- src/services/api/routes/communities.ts | 124 +++++++++++------- src/services/api/routes/friend_messages.ts | 42 +++--- src/services/api/routes/people.ts | 38 +++--- src/services/api/routes/posts.ts | 68 +++++----- src/services/api/routes/status.ts | 5 +- src/services/api/routes/topics.ts | 37 +++--- src/services/api/routes/users.ts | 8 +- src/services/discovery/routes/discovery.ts | 22 ++-- src/types/common/create-new-community-body.ts | 6 - src/types/common/crypto-options.ts | 4 - src/types/common/send-message-body.ts | 7 - src/util.ts | 46 ++++--- test/test.ts | 48 +++---- tsconfig.json | 2 + 20 files changed, 254 insertions(+), 247 deletions(-) delete mode 100644 src/types/common/create-new-community-body.ts delete mode 100644 src/types/common/crypto-options.ts delete mode 100644 src/types/common/send-message-body.ts diff --git a/.eslintrc.json b/.eslintrc.json index b038534..fcfc1a5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -29,7 +29,7 @@ "no-extra-semi": "off", "@typescript-eslint/no-extra-semi": "error", "@typescript-eslint/no-empty-interface": "warn", - "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-inferrable-types": "error", "@typescript-eslint/typedef": "error", "@typescript-eslint/explicit-function-return-type": "error", "keyword-spacing": "off", diff --git a/src/logger.ts b/src/logger.ts index 4d200b9..21bb483 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -3,7 +3,7 @@ import colors from 'colors'; colors.enable(); -const root: string = process.env.PN_MIIVERSE_API_LOGGER_PATH ? process.env.PN_MIIVERSE_API_LOGGER_PATH : `${__dirname}/..`; +const root = process.env.PN_MIIVERSE_API_LOGGER_PATH ? process.env.PN_MIIVERSE_API_LOGGER_PATH : `${__dirname}/..`; fs.ensureDirSync(`${root}/logs`); const streams = { @@ -15,7 +15,7 @@ const streams = { } as const; export function LOG_SUCCESS(input: string): void { - const time: Date = new Date(); + const time = new Date(); input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [SUCCESS]: ${input}`; streams.success.write(`${input}\n`); @@ -23,7 +23,7 @@ export function LOG_SUCCESS(input: string): void { } export function LOG_ERROR(input: string): void { - const time: Date = new Date(); + const time = new Date(); input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [ERROR]: ${input}`; streams.error.write(`${input}\n`); @@ -31,7 +31,7 @@ export function LOG_ERROR(input: string): void { } export function LOG_WARN(input: string): void { - const time: Date = new Date(); + const time = new Date(); input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [WARN]: ${input}`; streams.warn.write(`${input}\n`); @@ -39,7 +39,7 @@ export function LOG_WARN(input: string): void { } export function LOG_INFO(input: string): void { - const time: Date = new Date(); + const time = new Date(); input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [INFO]: ${input}`; streams.info.write(`${input}\n`); diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index fb7f563..877ad7f 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -4,7 +4,6 @@ import { z } from 'zod'; import { GetUserDataResponse } from 'pretendo-grpc-ts/dist/account/get_user_data_rpc'; import { getEndpoint } from '@/database'; import { getUserAccountData, getValueFromHeaders, decodeParamPack, getPIDFromServiceToken } from '@/util'; -import { ParamPack } from '@/types/common/param-pack'; import { HydratedEndpointDocument } from '@/types/mongoose/endpoint'; const ParamPackSchema = z.object({ @@ -30,7 +29,7 @@ async function auth(request: express.Request, response: express.Response, next: return next(); } - let encryptedToken: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-servicetoken'); + let encryptedToken = getValueFromHeaders(request.headers, 'x-nintendo-servicetoken'); if (!encryptedToken) { encryptedToken = getValueFromHeaders(request.headers, 'olive service token'); } @@ -44,13 +43,13 @@ async function auth(request: express.Request, response: express.Response, next: return badAuth(response, 16, 'BAD_TOKEN'); } - const paramPack: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-parampack'); + const paramPack = getValueFromHeaders(request.headers, 'x-nintendo-parampack'); if (!paramPack) { return badAuth(response, 17, 'NO_PARAM'); } - const paramPackData: ParamPack = decodeParamPack(paramPack); - const paramPackCheck: z.SafeParseReturnType = ParamPackSchema.safeParse(paramPackData); + const paramPackData = decodeParamPack(paramPack); + const paramPackCheck = ParamPackSchema.safeParse(paramPackData); if (!paramPackCheck.success) { console.log(paramPackCheck.error); return badAuth(response, 18, 'BAD_PARAM'); @@ -67,6 +66,7 @@ async function auth(request: express.Request, response: express.Response, next: } let discovery: HydratedEndpointDocument | null; + if (user) { discovery = await getEndpoint(user.serverAccessLevel); } else { @@ -103,8 +103,8 @@ function badAuth(response: express.Response, errorCode: number, message: string) } function serverError(response: express.Response, discovery: HydratedEndpointDocument): void { - let message: string = ''; - let error: number = 0; + let message = ''; + let error = 0; switch (discovery.status) { case 1 : diff --git a/src/middleware/client-header.ts b/src/middleware/client-header.ts index 6480dac..7b40938 100644 --- a/src/middleware/client-header.ts +++ b/src/middleware/client-header.ts @@ -15,14 +15,14 @@ function nintendoClientHeaderCheck(request: express.Request, response: express.R response.set('Server', 'Nintendo 3DS (http)'); response.set('X-Nintendo-Date', new Date().getTime().toString()); - const clientId: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-client-id'); - const clientSecret: string | undefined = getValueFromHeaders(request.headers, 'x-nintendo-client-secret'); + const clientID = getValueFromHeaders(request.headers, 'x-nintendo-client-id'); + const clientSecret = getValueFromHeaders(request.headers, 'x-nintendo-client-secret'); if ( - !clientId || + !clientID || !clientSecret || - !VALID_CLIENT_ID_SECRET_PAIRS[clientId] || - clientSecret !== VALID_CLIENT_ID_SECRET_PAIRS[clientId] + !VALID_CLIENT_ID_SECRET_PAIRS[clientID] || + clientSecret !== VALID_CLIENT_ID_SECRET_PAIRS[clientID] ) { response.type('application/xml'); response.send(xmlbuilder.create({ diff --git a/src/models/post.ts b/src/models/post.ts index ead834f..61b89d3 100644 --- a/src/models/post.ts +++ b/src/models/post.ts @@ -1,7 +1,7 @@ import crypto from 'node:crypto'; import moment from 'moment'; import { Schema, model } from 'mongoose'; -import { HydratedPostDocument, IPost, IPostMethods, PostModel } from '@/types/mongoose/post'; +import { IPost, IPostMethods, PostModel } from '@/types/mongoose/post'; import { HydratedCommunityDocument } from '@/types/mongoose/community'; import { PostToJSONOptions } from '@/types/mongoose/post-to-json-options'; import { PostPainting, PostScreenshot } from '@/types/common/post'; @@ -122,9 +122,9 @@ PostSchema.method('unRemove', async function unRemove(reason) { }); PostSchema.method('generatePostUID', async function generatePostUID(length: number) { - const id: string = Buffer.from(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(length * 2))), 'binary').toString('base64').replace(/[+/]/g, '').substring(0, length); + const id = Buffer.from(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(length * 2))), 'binary').toString('base64').replace(/[+/]/g, '').substring(0, length); - const inuse: HydratedPostDocument | null = await Post.findOne({ id }); + const inuse = await Post.findOne({ id }); if (inuse) { await this.generatePostUID(length); diff --git a/src/server.ts b/src/server.ts index 6d42575..f39aa7d 100644 --- a/src/server.ts +++ b/src/server.ts @@ -53,7 +53,7 @@ app.use((_request: express.Request, response: express.Response) => { // non-404 error handler LOG_INFO('Creating non-404 status handler'); app.use((error: any, _request: express.Request, response: express.Response, _next: express.NextFunction) => { - const status: number = error.status || 500; + const status = error.status || 500; response.type('application/xml'); response.status(404); diff --git a/src/services/api/routes/communities.ts b/src/services/api/routes/communities.ts index 2c4dfd1..25f99a8 100644 --- a/src/services/api/routes/communities.ts +++ b/src/services/api/routes/communities.ts @@ -12,11 +12,9 @@ import { getValueFromQueryString } from '@/util'; import { LOG_WARN } from '@/logger'; import { Community } from '@/models/community'; import { Post } from '@/models/post'; -import { CreateNewCommunityBody } from '@/types/common/create-new-community-body'; import { HydratedCommunityDocument } from '@/types/mongoose/community'; import { SubCommunityQuery } from '@/types/mongoose/subcommunity-query'; import { CommunityPostsQuery } from '@/types/mongoose/community-posts-query'; -import { HydratedContentDocument } from '@/types/mongoose/content'; import { HydratedPostDocument, IPost } from '@/types/mongoose/post'; import { ParamPack } from '@/types/common/param-pack'; @@ -27,7 +25,7 @@ const createNewCommunitySchema = z.object({ app_data: z.string().optional() }); -const router: express.Router = express.Router(); +const router = express.Router(); function respondCommunityError(response: express.Response, httpStatusCode: number, errorCode: number): void { response.status(httpStatusCode).send(xmlbuilder.create({ @@ -45,20 +43,21 @@ function respondCommunityNotFound(response: express.Response): void { respondCommunityError(response, 404, 919); } - async function commonGetSubCommunity(paramPack: ParamPack, communityID: string | undefined): Promise { - const parentCommunity: HydratedCommunityDocument | null = await getCommunityByTitleID(paramPack.title_id); + const parentCommunity = await getCommunityByTitleID(paramPack.title_id); + if (!parentCommunity) { return null; } - const query: SubCommunityQuery = { + const query = { parent: parentCommunity.olive_community_id, community_id: communityID }; - const community: HydratedCommunityDocument | null = await Community.findOne(query); + const community = await Community.findOne(query); + if (!community) { return null; } @@ -70,16 +69,17 @@ async function commonGetSubCommunity(paramPack: ParamPack, communityID: string | router.get('/', async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const parentCommunity: HydratedCommunityDocument | null = await getCommunityByTitleID(request.paramPack.title_id); + const parentCommunity = await getCommunityByTitleID(request.paramPack.title_id); if (!parentCommunity) { respondCommunityNotFound(response); return; } - const type: string | undefined = getValueFromQueryString(request.query, 'type')[0]; - const limitString: string | undefined = getValueFromQueryString(request.query, 'limit')[0]; + const type = getValueFromQueryString(request.query, 'type')[0]; + const limitString = getValueFromQueryString(request.query, 'limit')[0]; + + let limit = 4; - let limit: number = 4; if (limitString) { limit = parseInt(limitString); } @@ -102,7 +102,7 @@ router.get('/', async function (request: express.Request, response: express.Resp query.user_favorites = request.pid; } - const communities: HydratedCommunityDocument[] = await Community.find(query).limit(limit); + const communities = await Community.find(query).limit(limit); const json: Record = { result: { @@ -119,18 +119,23 @@ router.get('/', async function (request: express.Request, response: express.Resp }); } - response.send(xmlbuilder.create(json, { separateArrayItems: true }).end({ pretty: true, allowEmpty: true })); + response.send(xmlbuilder.create(json, { + separateArrayItems: true + }).end({ + pretty: true, + allowEmpty: true + })); }); router.get('/popular', async function (_request: express.Request, response: express.Response): Promise { - const popularCommunities: HydratedCommunityDocument[] = await getMostPopularCommunities(100); + const popularCommunities = await getMostPopularCommunities(100); response.type('application/json'); response.send(popularCommunities); }); router.get('/new', async function (_request: express.Request, response: express.Response): Promise { - const newCommunities: HydratedCommunityDocument[] = await getNewCommunities(100); + const newCommunities = await getNewCommunities(100); response.type('application/json'); response.send(newCommunities); @@ -139,7 +144,7 @@ router.get('/new', async function (_request: express.Request, response: express. router.get('/:communityID/posts', async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - let community: HydratedCommunityDocument | null = await Community.findOne({ + let community = await Community.findOne({ community_id: request.params.communityID }); @@ -158,15 +163,15 @@ router.get('/:communityID/posts', async function (request: express.Request, resp message_to_pid: { $eq: null } }; - const searchKey: string | undefined = getValueFromQueryString(request.query, 'search_key')[0]; - const allowSpoiler: string | undefined = getValueFromQueryString(request.query, 'allow_spoiler')[0]; - const postType: string | undefined = getValueFromQueryString(request.query, 'type')[0]; - const queryBy: string | undefined = getValueFromQueryString(request.query, 'by')[0]; - const distinctPID: string | undefined = getValueFromQueryString(request.query, 'distinct_pid')[0]; - const limitString: string | undefined = getValueFromQueryString(request.query, 'limit')[0]; - const withMii: string | undefined = getValueFromQueryString(request.query, 'with_mii')[0]; + const searchKey = getValueFromQueryString(request.query, 'search_key')[0]; + const allowSpoiler = getValueFromQueryString(request.query, 'allow_spoiler')[0]; + const postType = getValueFromQueryString(request.query, 'type')[0]; + const queryBy = getValueFromQueryString(request.query, 'by')[0]; + const distinctPID = getValueFromQueryString(request.query, 'distinct_pid')[0]; + const limitString = getValueFromQueryString(request.query, 'limit')[0]; + const withMii = getValueFromQueryString(request.query, 'with_mii')[0]; - let limit: number = 10; + let limit = 10; if (limitString) { limit = parseInt(limitString); @@ -190,7 +195,7 @@ router.get('/:communityID/posts', async function (request: express.Request, resp } if (queryBy === 'followings') { - const userContent: HydratedContentDocument | null = await getUserContent(request.pid); + const userContent = await getUserContent(request.pid); if (!userContent) { LOG_WARN(`USER PID ${request.pid} HAS NO USER CONTENT`); @@ -203,15 +208,16 @@ router.get('/:communityID/posts', async function (request: express.Request, resp } let posts: HydratedPostDocument[]; + if (distinctPID && distinctPID === '1') { - posts = await Post.aggregate([ + const unhydratedPosts = await Post.aggregate([ { $match: query }, // filter based on input query { $sort: { created_at: -1 } }, // sort by 'created_at' in descending order { $group: { _id: '$pid', doc: { $first: '$$ROOT' } } }, // remove any duplicate 'pid' elements { $replaceRoot: { newRoot: '$doc' } }, // replace the root with the 'doc' field { $limit: limit } // only return the top 10 results ]); - posts = posts.map((post: IPost) => Post.hydrate(post)); + posts = unhydratedPosts.map((post: IPost) => Post.hydrate(post)); } else { posts = await Post.find(query).sort({ created_at: -1 }).limit(limit); } @@ -238,20 +244,25 @@ router.get('/:communityID/posts', async function (request: express.Request, resp }); } - response.send(xmlbuilder.create(json, { separateArrayItems: true }).end({ pretty: true, allowEmpty: true })); + response.send(xmlbuilder.create(json, { + separateArrayItems: true + }).end({ + pretty: true, + allowEmpty: true + })); }); // Handler for POST on '/v1/communities' router.post('/', multer().none(), async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const parentCommunity: HydratedCommunityDocument | null = await getCommunityByTitleID(request.paramPack.title_id); + const parentCommunity = await getCommunityByTitleID(request.paramPack.title_id); if (!parentCommunity) { return respondCommunityNotFound(response); } // TODO - Better error codes, maybe do defaults? - const bodyCheck: z.SafeParseReturnType = createNewCommunitySchema.safeParse(request.body); + const bodyCheck = createNewCommunitySchema.safeParse(request.body); if (!bodyCheck.success) { return respondCommunityError(response, 400, 20); } @@ -273,30 +284,30 @@ router.post('/', multer().none(), async function (request: express.Request, resp } // Each user can only have 4 subcommunities per title - const ownedQuery: SubCommunityQuery = { + const ownedQuery = { parent: parentCommunity.olive_community_id, owner: request.pid }; - const ownedSubcommunityCount: number = await Community.countDocuments(ownedQuery); + const ownedSubcommunityCount = await Community.countDocuments(ownedQuery); if (ownedSubcommunityCount >= 4) { return respondCommunityError(response, 401, 911); } // Each user can only have 16 favorite subcommunities per title - const favoriteQuery: SubCommunityQuery = { + const favoriteQuery = { parent: parentCommunity.olive_community_id, user_favorites: request.pid }; - const ownedFavoriteCount: number = await Community.countDocuments(favoriteQuery); + const ownedFavoriteCount = await Community.countDocuments(favoriteQuery); if (ownedFavoriteCount >= 16) { return respondCommunityError(response, 401, 912); } - const communitiesCount: number = await Community.count(); - const communityId: number = (parseInt(parentCommunity.community_id) + (5000 * communitiesCount)); // Change this to auto increment - const community: HydratedCommunityDocument = await Community.create({ + const communitiesCount = await Community.count(); + const communityID = (parseInt(parentCommunity.community_id) + (5000 * communitiesCount)); // Change this to auto increment + const community = await Community.create({ platform_id: 0, // WiiU name: request.body.name, description: request.body.description || '', @@ -308,8 +319,8 @@ router.post('/', multer().none(), async function (request: express.Request, resp owner: request.pid, icon: request.body.icon, title_id: request.paramPack.title_id, - community_id: communityId.toString(), - olive_community_id: communityId.toString(), + community_id: communityID.toString(), + olive_community_id: communityID.toString(), app_data: request.body.app_data || '', user_favorites: [request.pid] }); @@ -321,13 +332,17 @@ router.post('/', multer().none(), async function (request: express.Request, resp request_name: 'community', community: community.json() } - }).end({ pretty: true, allowEmpty: true })); + }).end({ + pretty: true, + allowEmpty: true + })); }); router.post('/:community_id.delete', multer().none(), async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const community: HydratedCommunityDocument | null = await commonGetSubCommunity(request.paramPack, request.params.community_id); + const community = await commonGetSubCommunity(request.paramPack, request.params.community_id); + if (!community) { respondCommunityNotFound(response); return; @@ -353,19 +368,20 @@ router.post('/:community_id.delete', multer().none(), async function (request: e router.post('/:community_id.favorite', multer().none(), async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const community: HydratedCommunityDocument | null = await commonGetSubCommunity(request.paramPack, request.params.community_id); + const community = await commonGetSubCommunity(request.paramPack, request.params.community_id); + if (!community) { respondCommunityNotFound(response); return; } // Each user can only have 16 favorite subcommunities per title - const favoriteQuery: SubCommunityQuery = { + const favoriteQuery = { parent: community.parent, user_favorites: request.pid }; - const ownedFavoriteCount: number = await Community.countDocuments(favoriteQuery); + const ownedFavoriteCount = await Community.countDocuments(favoriteQuery); if (ownedFavoriteCount >= 16) { return respondCommunityError(response, 401, 914); } @@ -379,13 +395,16 @@ router.post('/:community_id.favorite', multer().none(), async function (request: request_name: 'community', community: community.json() } - }).end({ pretty: true, allowEmpty: true })); + }).end({ + pretty: true, + allowEmpty: true + })); }); router.post('/:community_id.unfavorite', multer().none(), async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const community: HydratedCommunityDocument | null = await commonGetSubCommunity(request.paramPack, request.params.community_id); + const community = await commonGetSubCommunity(request.paramPack, request.params.community_id); if (!community) { respondCommunityNotFound(response); return; @@ -405,14 +424,18 @@ router.post('/:community_id.unfavorite', multer().none(), async function (reques request_name: 'community', community: community.json() } - }).end({ pretty: true, allowEmpty: true })); + }).end({ + pretty: true, + allowEmpty: true + })); }); router.post('/:community_id', multer().none(), async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const community: HydratedCommunityDocument | null = await commonGetSubCommunity(request.paramPack, request.params.community_id); + const community = await commonGetSubCommunity(request.paramPack, request.params.community_id); + if (!community) { respondCommunityNotFound(response); return; @@ -448,7 +471,10 @@ router.post('/:community_id', multer().none(), async function (request: express. request_name: 'community', community: community.json() } - }).end({ pretty: true, allowEmpty: true })); + }).end({ + pretty: true, + allowEmpty: true + })); }); export default router; diff --git a/src/services/api/routes/friend_messages.ts b/src/services/api/routes/friend_messages.ts index 9dc846f..e25ba1f 100644 --- a/src/services/api/routes/friend_messages.ts +++ b/src/services/api/routes/friend_messages.ts @@ -10,11 +10,7 @@ import { getConversationByUsers, getUserSettings, getFriendMessages } from '@/da import { LOG_WARN } from '@/logger'; import { Post } from '@/models/post'; import { Conversation } from '@/models/conversation'; -import { SendMessageBody } from '@/types/common/send-message-body'; import { FormattedMessage } from '@/types/common/formatted-message'; -import { HydratedConversationDocument } from '@/types/mongoose/conversation'; -import { HydratedSettingsDocument } from '@/types/mongoose/settings'; -import { HydratedPostDocument } from '@/types/mongoose/post'; const sendMessageSchema = z.object({ message_to_pid: z.string().transform(Number), @@ -24,25 +20,25 @@ const sendMessageSchema = z.object({ app_data: z.string().optional() }); -const router: express.Router = express.Router(); -const upload: multer.Multer = multer(); +const router = express.Router(); +const upload = multer(); router.post('/', upload.none(), async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); // TODO - Better error codes, maybe do defaults? - const bodyCheck: z.SafeParseReturnType = sendMessageSchema.safeParse(request.body); + const bodyCheck = sendMessageSchema.safeParse(request.body); if (!bodyCheck.success) { response.status(422); return; } - const recipientPID: number = bodyCheck.data.message_to_pid; - let messageBody: string = bodyCheck.data.body; - const painting: string = bodyCheck.data.painting?.replace(/\0/g, '').trim() || ''; - const screenshot: string = bodyCheck.data.screenshot?.trim().replace(/\0/g, '').trim() || ''; - const appData: string = bodyCheck.data.app_data?.replace(/[^A-Za-z0-9+/=\s]/g, '').trim() || ''; + const recipientPID = bodyCheck.data.message_to_pid; + let messageBody = bodyCheck.data.body; + const painting = bodyCheck.data.painting?.replace(/\0/g, '').trim() || ''; + const screenshot = bodyCheck.data.screenshot?.trim().replace(/\0/g, '').trim() || ''; + const appData = bodyCheck.data.app_data?.replace(/[^A-Za-z0-9+/=\s]/g, '').trim() || ''; if (isNaN(recipientPID)) { response.status(422); @@ -76,11 +72,11 @@ router.post('/', upload.none(), async function (request: express.Request, respon return; } - let conversation: HydratedConversationDocument | null = await getConversationByUsers([sender.pid, recipient.pid]); + let conversation = await getConversationByUsers([sender.pid, recipient.pid]); if (!conversation) { - const userSettings: HydratedSettingsDocument | null = await getUserSettings(request.pid); - const user2Settings: HydratedSettingsDocument | null = await getUserSettings(recipient.pid); + const userSettings = await getUserSettings(request.pid); + const user2Settings = await getUserSettings(recipient.pid); if (!sender || !recipient || userSettings || user2Settings) { response.sendStatus(422); @@ -109,14 +105,14 @@ router.post('/', upload.none(), async function (request: express.Request, respon return; } - const friendPIDs: number[] = await getUserFriendPIDs(recipient.pid); + const friendPIDs = await getUserFriendPIDs(recipient.pid); if (friendPIDs.indexOf(request.pid) === -1) { response.sendStatus(422); return; } - let miiFace: string = 'normal_face.png'; + let miiFace = 'normal_face.png'; switch (parseInt(request.body.feeling_id)) { case 1: miiFace = 'smile_open_mouth.png'; @@ -179,7 +175,7 @@ router.post('/', upload.none(), async function (request: express.Request, respon }); if (painting) { - const paintingBuffer: Buffer | null = await processPainting(painting); + const paintingBuffer = await processPainting(painting); if (paintingBuffer) { await uploadCDNAsset('pn-cdn', `paintings/${request.pid}/${post.id}.png`, paintingBuffer, 'public-read'); @@ -189,7 +185,7 @@ router.post('/', upload.none(), async function (request: express.Request, respon } if (screenshot) { - const screenshotBuffer: Buffer = Buffer.from(screenshot, 'base64'); + const screenshotBuffer = Buffer.from(screenshot, 'base64'); await uploadCDNAsset('pn-cdn', `screenshots/${request.pid}/${post.id}.jpg`, screenshotBuffer, 'public-read'); @@ -214,10 +210,10 @@ router.post('/', upload.none(), async function (request: express.Request, respon router.get('/', async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const limitString: string | undefined = getValueFromQueryString(request.query, 'limit')[0]; + const limitString = getValueFromQueryString(request.query, 'limit')[0]; // TODO - Is this the limit? - let limit: number = 10; + let limit = 10; if (limitString) { limit = parseInt(limitString); @@ -232,9 +228,9 @@ router.get('/', async function (request: express.Request, response: express.Resp return; } - const searchKey: string[] = getValueFromQueryString(request.query, 'search_key'); + const searchKey = getValueFromQueryString(request.query, 'search_key'); - const messages: HydratedPostDocument[] = await getFriendMessages(request.pid.toString(), searchKey, limit); + const messages = await getFriendMessages(request.pid.toString(), searchKey, limit); const postBody: FormattedMessage[] = []; for (const message of messages) { diff --git a/src/services/api/routes/people.ts b/src/services/api/routes/people.ts index 662787f..4e1f2f6 100644 --- a/src/services/api/routes/people.ts +++ b/src/services/api/routes/people.ts @@ -4,18 +4,16 @@ import moment from 'moment'; import { getUserContent, getFollowedUsers } from '@/database'; import { getValueFromQueryString, getUserFriendPIDs } from '@/util'; import { Post } from '@/models/post'; -import { HydratedContentDocument } from '@/types/mongoose/content'; import { CommunityPostsQuery } from '@/types/mongoose/community-posts-query'; import { HydratedPostDocument, IPost } from '@/types/mongoose/post'; -import { HydratedSettingsDocument } from '@/types/mongoose/settings'; -const router: express.Router = express.Router(); +const router = express.Router(); /* GET post titles. */ router.get('/', async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const userContent: HydratedContentDocument | null = await getUserContent(request.pid); + const userContent = await getUserContent(request.pid); if (!userContent) { response.sendStatus(404); @@ -30,12 +28,12 @@ router.get('/', async function (request: express.Request, response: express.Resp message_to_pid: { $eq: null } }; - const relation: string | undefined = getValueFromQueryString(request.query, 'relation')[0]; - const distinctPID: string | undefined = getValueFromQueryString(request.query, 'distinct_pid')[0]; - const limitString: string | undefined = getValueFromQueryString(request.query, 'limit')[0]; - const withMii: string | undefined = getValueFromQueryString(request.query, 'with_mii')[0]; + const relation = getValueFromQueryString(request.query, 'relation')[0]; + const distinctPID = getValueFromQueryString(request.query, 'distinct_pid')[0]; + const limitString = getValueFromQueryString(request.query, 'limit')[0]; + const withMii = getValueFromQueryString(request.query, 'with_mii')[0]; - let limit: number = 10; + let limit = 10; if (limitString) { limit = parseInt(limitString); @@ -50,15 +48,16 @@ router.get('/', async function (request: express.Request, response: express.Resp } else if (relation === 'following') { query.pid = { $in: userContent.followed_users }; } else if (request.query.pid) { - const pidInputs: string[] = getValueFromQueryString(request.query, 'pid'); - const pids: number[] = pidInputs.map(pid => Number(pid)).filter(pid => !isNaN(pid)); + const pidInputs = getValueFromQueryString(request.query, 'pid'); + const pids = pidInputs.map(pid => Number(pid)).filter(pid => !isNaN(pid)); query.pid = { $in: pids }; } let posts: HydratedPostDocument[]; + if (distinctPID === '1') { - posts = await Post.aggregate([ + const unhydratedPosts = await Post.aggregate([ { $match: query }, // filter based on input query { $sort: { created_at: -1 } }, // sort by 'created_at' in descending order { $group: { _id: '$pid', doc: { $first: '$$ROOT' } } }, // remove any duplicate 'pid' elements @@ -66,7 +65,7 @@ router.get('/', async function (request: express.Request, response: express.Resp { $limit: limit } // only return the top 10 results ]); - posts = posts.map((post: IPost) => Post.hydrate(post)); + posts = unhydratedPosts.map((post: IPost) => Post.hydrate(post)); } else if (request.query.is_hot === '1') { posts = await Post.find(query).sort({ empathy_count: -1}).limit(limit); } else { @@ -98,27 +97,32 @@ router.get('/', async function (request: express.Request, response: express.Resp }); } - response.send(xmlbuilder.create(json, { separateArrayItems: true }).end({ pretty: true, allowEmpty: true })); + response.send(xmlbuilder.create(json, { + separateArrayItems: true + }).end({ + pretty: true, + allowEmpty: true + })); }); router.get('/:pid/following', async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const pid: number = parseInt(request.params.pid); + const pid = parseInt(request.params.pid); if (isNaN(pid)) { response.sendStatus(404); return; } - const userContent: HydratedContentDocument | null = await getUserContent(pid); + const userContent = await getUserContent(pid); if (!userContent) { response.sendStatus(404); return; } - const people: HydratedSettingsDocument[] = await getFollowedUsers(userContent); + const people = await getFollowedUsers(userContent); const json: Record = { result: { diff --git a/src/services/api/routes/posts.ts b/src/services/api/routes/posts.ts index e1d55e6..c8ffb2b 100644 --- a/src/services/api/routes/posts.ts +++ b/src/services/api/routes/posts.ts @@ -16,9 +16,7 @@ import { import { LOG_WARN } from '@/logger'; import { Post } from '@/models/post'; import { Community } from '@/models/community'; -import { HydratedPostDocument, IPost } from '@/types/mongoose/post'; -import { HydratedContentDocument } from '@/types/mongoose/content'; -import { HydratedSettingsDocument } from '@/types/mongoose/settings'; +import { HydratedPostDocument } from '@/types/mongoose/post'; const newPostSchema = z.object({ community_id: z.string().optional(), @@ -35,8 +33,8 @@ const newPostSchema = z.object({ language_id: z.string() }); -const router: express.Router = express.Router(); -const upload: multer.Multer = multer(); +const router = express.Router(); +const upload = multer(); /* GET post titles. */ router.post('/', upload.none(), newPost); @@ -46,8 +44,8 @@ router.post('/:post_id/replies', upload.none(), newPost); router.post('/:post_id.delete', async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const post: HydratedPostDocument | null = await getPostByID(request.params.post_id); - const userContent: HydratedContentDocument | null = await getUserContent(request.pid); + const post = await getPostByID(request.params.post_id); + const userContent = await getUserContent(request.pid); if (!post || !userContent) { response.sendStatus(504); @@ -65,7 +63,7 @@ router.post('/:post_id.delete', async function (request: express.Request, respon router.post('/:post_id/empathies', upload.none(), async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const post: HydratedPostDocument | null = await getPostByID(request.params.post_id); + const post = await getPostByID(request.params.post_id); if (!post) { response.sendStatus(404); @@ -110,9 +108,9 @@ router.post('/:post_id/empathies', upload.none(), async function (request: expre router.get('/:post_id/replies', async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const limitString: string | undefined = getValueFromQueryString(request.query, 'limit')[0]; + const limitString = getValueFromQueryString(request.query, 'limit')[0]; - let limit: number = 10; // TODO - Is there a real limit? + let limit = 10; // TODO - Is there a real limit? if (limitString) { limit = parseInt(limitString); @@ -122,14 +120,14 @@ router.get('/:post_id/replies', async function (request: express.Request, respon limit = 10; } - const post: HydratedPostDocument | null = await getPostByID(request.params.post_id); + const post = await getPostByID(request.params.post_id); if (!post) { response.sendStatus(404); return; } - const posts: HydratedPostDocument[] = await getPostReplies(post.id, limit); + const posts = await getPostReplies(post.id, limit); if (posts.length === 0) { response.sendStatus(404); return; @@ -159,7 +157,7 @@ router.get('/:post_id/replies', async function (request: express.Request, respon router.get('/', async function (request: express.Request, response: express.Response): Promise { response.type('application/xml'); - const postID: string | undefined = getValueFromQueryString(request.query, 'post_id')[0]; + const postID = getValueFromQueryString(request.query, 'post_id')[0]; if (!postID) { response.type('application/xml'); @@ -175,7 +173,7 @@ router.get('/', async function (request: express.Request, response: express.Resp return; } - const post: HydratedPostDocument | null = await getPostByID(postID); + const post = await getPostByID(postID); if (!post) { response.status(404); @@ -222,7 +220,7 @@ async function newPost(request: express.Request, response: express.Response): Pr return; } - const userSettings: HydratedSettingsDocument | null = await getUserSettings(request.pid); + const userSettings = await getUserSettings(request.pid); const bodyCheck = newPostSchema.safeParse(request.body); if (!userSettings || !bodyCheck.success) { @@ -230,21 +228,21 @@ async function newPost(request: express.Request, response: express.Response): Pr return; } - const communityID: string | undefined = bodyCheck.data.community_id || ''; - let messageBody: string | undefined = bodyCheck.data.body; - const painting: string = bodyCheck.data.painting?.replace(/\0/g, '').trim() || ''; - const screenshot: string = bodyCheck.data.screenshot?.replace(/\0/g, '').trim() || ''; - const appData: string = bodyCheck.data.app_data?.replace(/[^A-Za-z0-9+/=\s]/g, '').trim() || ''; - const feelingID: number = parseInt(bodyCheck.data.feeling_id); - let searchKey: string | string[] = bodyCheck.data.search_key || []; - const topicTag: string | undefined = bodyCheck.data.topic_tag || ''; - const autopost: string = bodyCheck.data.is_autopost; - const spoiler: string | undefined = bodyCheck.data.is_spoiler; - const jumpable: string | undefined = bodyCheck.data.is_app_jumpable; - const languageID: number = parseInt(bodyCheck.data.language_id); - const countryID: number = parseInt(request.paramPack.country_id); - const platformID: number = parseInt(request.paramPack.platform_id); - const regionID: number = parseInt(request.paramPack.region_id); + const communityID = bodyCheck.data.community_id || ''; + let messageBody = bodyCheck.data.body; + const painting = bodyCheck.data.painting?.replace(/\0/g, '').trim() || ''; + const screenshot = bodyCheck.data.screenshot?.replace(/\0/g, '').trim() || ''; + const appData = bodyCheck.data.app_data?.replace(/[^A-Za-z0-9+/=\s]/g, '').trim() || ''; + const feelingID = parseInt(bodyCheck.data.feeling_id); + let searchKey = bodyCheck.data.search_key || []; + const topicTag = bodyCheck.data.topic_tag || ''; + const autopost = bodyCheck.data.is_autopost; + const spoiler = bodyCheck.data.is_spoiler; + const jumpable = bodyCheck.data.is_app_jumpable; + const languageID = parseInt(bodyCheck.data.language_id); + const countryID = parseInt(request.paramPack.country_id); + const platformID = parseInt(request.paramPack.platform_id); + const regionID = parseInt(request.paramPack.region_id); if ( isNaN(feelingID) || @@ -294,7 +292,7 @@ async function newPost(request: express.Request, response: express.Response): Pr } } - let miiFace: string = 'normal_face.png'; + let miiFace = 'normal_face.png'; switch (parseInt(request.body.feeling_id)) { case 1: miiFace = 'smile_open_mouth.png'; @@ -330,7 +328,7 @@ async function newPost(request: express.Request, response: express.Response): Pr searchKey = [searchKey]; } - const document: IPost = { + const document = { id: '', // * This gets changed when saving the document for the first time title_id: request.paramPack.title_id, community_id: community.olive_community_id, @@ -375,10 +373,10 @@ async function newPost(request: express.Request, response: express.Response): Pr return; } - const post: HydratedPostDocument = await Post.create(document); + const post = await Post.create(document); if (painting) { - const paintingBuffer: Buffer | null = await processPainting(painting); + const paintingBuffer = await processPainting(painting); if (paintingBuffer) { await uploadCDNAsset('pn-cdn', `paintings/${request.pid}/${post.id}.png`, paintingBuffer, 'public-read'); @@ -388,7 +386,7 @@ async function newPost(request: express.Request, response: express.Response): Pr } if (screenshot) { - const screenshotBuffer: Buffer = Buffer.from(screenshot, 'base64'); + const screenshotBuffer = Buffer.from(screenshot, 'base64'); await uploadCDNAsset('pn-cdn', `screenshots/${request.pid}/${post.id}.jpg`, screenshotBuffer, 'public-read'); diff --git a/src/services/api/routes/status.ts b/src/services/api/routes/status.ts index 6262c81..d77ec3a 100644 --- a/src/services/api/routes/status.ts +++ b/src/services/api/routes/status.ts @@ -1,15 +1,14 @@ import express from 'express'; import { getEndpoints } from '@/database'; -import { HydratedEndpointDocument } from '@/types/mongoose/endpoint'; -const router: express.Router = express.Router(); +const router = express.Router(); router.get('/', function(_request: express.Request, response: express.Response): void { response.send('Pong!'); }); router.get('/database', async function(_request: express.Request, response: express.Response): Promise { - const endpoints: HydratedEndpointDocument[] = await getEndpoints(); + const endpoints = await getEndpoints(); if (endpoints && endpoints.length <= 0) { response.send('DB Connection Working! :D'); diff --git a/src/services/api/routes/topics.ts b/src/services/api/routes/topics.ts index c5d8ce2..a0e5dc1 100644 --- a/src/services/api/routes/topics.ts +++ b/src/services/api/routes/topics.ts @@ -7,6 +7,7 @@ import Cache from '@/cache'; import { getEndpoint } from '@/database'; import { Post } from '@/models/post'; import { Community } from '@/models/community'; +import { IPost } from '@/types/mongoose/post'; import { HydratedEndpointDocument } from '@/types/mongoose/endpoint'; import { HydratedCommunityDocument } from '@/types/mongoose/community'; import { WWPData, WWPPost, WWPTopic } from '@/types/common/wara-wara-plaza'; @@ -43,7 +44,7 @@ router.get('/', async function (request: express.Request, response: express.Resp } if (!WARA_WARA_PLAZA_CACHE.valid()) { - const communities: HydratedCommunityDocument[] = await calculateMostPopularCommunities(24, 10); + const communities = await calculateMostPopularCommunities(24, 10); if (communities.length < 10) { response.sendStatus(404); @@ -54,8 +55,14 @@ router.get('/', async function (request: express.Request, response: express.Resp } const data = WARA_WARA_PLAZA_CACHE.get() || {}; + const xml = xmlbuilder.create(data, { + separateArrayItems: true + }).end({ + pretty: true, + allowEmpty: true + }); - response.send(xmlbuilder.create(data, { separateArrayItems: true }).end({ pretty: true, allowEmpty: true })); + response.send(xml); }); async function generateTopicsData(communities: HydratedCommunityDocument[]): Promise { @@ -66,17 +73,17 @@ async function generateTopicsData(communities: HydratedCommunityDocument[]): Pro for (let i = 0; i < communities.length; i++) { const community = communities[i]; - const empathies = await Post.aggregate([ + const empathies = await Post.aggregate<{ _id: null; total: number; }>([ { $match: { community_id: community.olive_community_id } }, { - $group : { - _id : null, - total : { - $sum : '$empathy_count' + $group: { + _id: null, + total: { + $sum: '$empathy_count' } } }, @@ -163,10 +170,10 @@ async function generateTopicsData(communities: HydratedCommunityDocument[]): Pro }; } -async function getCommunityPeople(community: HydratedCommunityDocument, hours = 24): Promise { +async function getCommunityPeople(community: HydratedCommunityDocument, hours = 24): Promise<{ _id: number; post: IPost }[]> { const now = new Date(); const last24Hours = new Date(now.getTime() - hours * 60 * 60 * 1000); - const people = await Post.aggregate([ + const people = await Post.aggregate<{ _id: number; post: IPost }>([ { $match: { title_id: { @@ -220,10 +227,7 @@ async function calculateMostPopularCommunities(hours: number, limit: number): Pr throw new Error('Invalid date'); } - const validCommunities: { - _id: null; - communities: [string]; - }[] = await Community.aggregate([ + const validCommunities = await Community.aggregate<{ _id: null; communities: string[]; }>([ { $match: { type: 0, @@ -240,16 +244,13 @@ async function calculateMostPopularCommunities(hours: number, limit: number): Pr } ]); - const communityIDs: [string] = validCommunities[0].communities; + const communityIDs = validCommunities[0].communities; if (!communityIDs) { throw new Error('No communities found'); } - const popularCommunities: { - _id: string; - count: number; - }[] = await Post.aggregate([ + const popularCommunities = await Post.aggregate<{ _id: null; count: number; }>([ { $match: { created_at: { diff --git a/src/services/api/routes/users.ts b/src/services/api/routes/users.ts index 3d9da83..912cc99 100644 --- a/src/services/api/routes/users.ts +++ b/src/services/api/routes/users.ts @@ -2,12 +2,12 @@ import express from 'express'; import xmlbuilder from 'xmlbuilder'; import { getValueFromQueryString } from '@/util'; -const router: express.Router = express.Router(); +const router = express.Router(); router.get('/:pid/notifications', function(request: express.Request, response: express.Response): void { - const type: string | undefined = getValueFromQueryString(request.query, 'type')[0]; - const titleID: string | undefined = getValueFromQueryString(request.query, 'title_id')[0]; - const pid: string | undefined = getValueFromQueryString(request.query, 'pid')[0]; + const type = getValueFromQueryString(request.query, 'type')[0]; + const titleID = getValueFromQueryString(request.query, 'title_id')[0]; + const pid = getValueFromQueryString(request.query, 'pid')[0]; console.log(type); console.log(titleID); diff --git a/src/services/discovery/routes/discovery.ts b/src/services/discovery/routes/discovery.ts index 337331f..5512469 100644 --- a/src/services/discovery/routes/discovery.ts +++ b/src/services/discovery/routes/discovery.ts @@ -5,7 +5,7 @@ import { getUserAccountData } from '@/util'; import { getEndpoint } from '@/database'; import { HydratedEndpointDocument } from '@/types/mongoose/endpoint'; -const router: express.Router = express.Router(); +const router = express.Router(); /* GET discovery server. */ router.get('/', async function (request: express.Request, response: express.Response): Promise { @@ -35,10 +35,10 @@ router.get('/', async function (request: express.Request, response: express.Resp return; } - let message: string = ''; - let errorCode: number = 0; + let message = ''; + let errorCode = 0; switch (discovery.status) { - case 0 : + case 0: response.send(xmlbuilder.create({ result: { has_error: 0, @@ -53,15 +53,15 @@ router.get('/', async function (request: express.Request, response: express.Resp }).end({ pretty: true })); return ; - case 1 : + case 1: message = 'SYSTEM_UPDATE_REQUIRED'; errorCode = 1; break; - case 2 : + case 2: message = 'SETUP_NOT_COMPLETE'; errorCode = 2; break; - case 3 : + case 3: message = 'SERVICE_MAINTENANCE'; errorCode = 3; break; @@ -69,20 +69,20 @@ router.get('/', async function (request: express.Request, response: express.Resp message = 'SERVICE_CLOSED'; errorCode = 4; break; - case 5 : + case 5: message = 'PARENTAL_CONTROLS_ENABLED'; errorCode = 5; break; - case 6 : + case 6: message = 'POSTING_LIMITED_PARENTAL_CONTROLS'; errorCode = 6; break; - case 7 : + case 7: message = 'NNID_BANNED'; errorCode = 7; response.type('application/xml'); break; - default : + default: message = 'SERVER_ERROR'; errorCode = 15; response.type('application/xml'); diff --git a/src/types/common/create-new-community-body.ts b/src/types/common/create-new-community-body.ts deleted file mode 100644 index a4ad82d..0000000 --- a/src/types/common/create-new-community-body.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface CreateNewCommunityBody { - name: string; - description?: string; - icon: string; - app_data?: string; -} \ No newline at end of file diff --git a/src/types/common/crypto-options.ts b/src/types/common/crypto-options.ts deleted file mode 100644 index 170a6fe..0000000 --- a/src/types/common/crypto-options.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface CryptoOptions { - private_key: Buffer; - hmac_secret: string; -} \ No newline at end of file diff --git a/src/types/common/send-message-body.ts b/src/types/common/send-message-body.ts deleted file mode 100644 index d54f6e5..0000000 --- a/src/types/common/send-message-body.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface SendMessageBody { - message_to_pid: number; - body: string; - painting?: string; - screenshot?: string; - app_data?: string; -} \ No newline at end of file diff --git a/src/util.ts b/src/util.ts index 279f09b..97c23ba 100644 --- a/src/util.ts +++ b/src/util.ts @@ -11,30 +11,28 @@ import { ParamPack } from '@/types/common/param-pack'; import { config } from '@/config-manager'; import { Token } from '@/types/common/token'; -import { FriendsClient, FriendsDefinition } from 'pretendo-grpc-ts/dist/friends/friends_service'; -import { GetUserFriendPIDsResponse } from 'pretendo-grpc-ts/dist/friends/get_user_friend_pids_rpc'; -import { GetUserFriendRequestsIncomingResponse } from 'pretendo-grpc-ts/dist/friends/get_user_friend_requests_incoming_rpc'; +import { FriendsDefinition } from 'pretendo-grpc-ts/dist/friends/friends_service'; import { FriendRequest } from 'pretendo-grpc-ts/dist/friends/friend_request'; -import { AccountClient, AccountDefinition } from 'pretendo-grpc-ts/dist/account/account_service'; +import { AccountDefinition } from 'pretendo-grpc-ts/dist/account/account_service'; import { GetUserDataResponse } from 'pretendo-grpc-ts/dist/account/get_user_data_rpc'; // * nice-grpc doesn't export ChannelImplementation so this can't be typed const gRPCFriendsChannel = createChannel(`${config.grpc.friends.ip}:${config.grpc.friends.port}`); -const gRPCFriendsClient: FriendsClient = createClient(FriendsDefinition, gRPCFriendsChannel); +const gRPCFriendsClient = createClient(FriendsDefinition, gRPCFriendsChannel); const gRPCAccountChannel = createChannel(`${config.grpc.account.ip}:${config.grpc.account.port}`); -const gRPCAccountClient: AccountClient = createClient(AccountDefinition, gRPCAccountChannel); +const gRPCAccountClient = createClient(AccountDefinition, gRPCAccountChannel); -const s3: aws.S3 = new aws.S3({ +const s3 = new aws.S3({ endpoint: new aws.Endpoint(config.s3.endpoint), accessKeyId: config.s3.key, secretAccessKey: config.s3.secret }); export function decodeParamPack(paramPack: string): ParamPack { - const values: string[] = Buffer.from(paramPack, 'base64').toString().split('\\'); - const entries: string[][] = values.filter(value => value).reduce((entries: string[][], value: string, index: number) => { + const values = Buffer.from(paramPack, 'base64').toString().split('\\'); + const entries = values.filter(value => value).reduce((entries: string[][], value: string, index: number) => { if (0 === index % 2) { entries.push([value]); } else { @@ -49,13 +47,13 @@ export function decodeParamPack(paramPack: string): ParamPack { export function getPIDFromServiceToken(token: string): number { try { - const decryptedToken: Buffer = decryptToken(Buffer.from(token, 'base64')); + const decryptedToken = decryptToken(Buffer.from(token, 'base64')); if (!decryptedToken) { return 0; } - const unpackedToken: Token = unpackToken(decryptedToken); + const unpackedToken = unpackToken(decryptedToken); return unpackedToken.pid; } catch (e) { @@ -65,14 +63,14 @@ export function getPIDFromServiceToken(token: string): number { } export function decryptToken(token: Buffer): Buffer { - const iv: Buffer = Buffer.alloc(16); + const iv = Buffer.alloc(16); - const expectedChecksum: number = token.readUint32BE(); - const encryptedBody: Buffer = token.subarray(4); + const expectedChecksum = token.readUint32BE(); + const encryptedBody = token.subarray(4); - const decipher: crypto.Decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(config.aes_key, 'hex'), iv); + const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(config.aes_key, 'hex'), iv); - const decrypted: Buffer = Buffer.concat([ + const decrypted = Buffer.concat([ decipher.update(encryptedBody), decipher.final() ]); @@ -96,7 +94,7 @@ export function unpackToken(token: Buffer): Token { } export function processPainting(painting: string): Buffer | null { - const paintingBuffer: Buffer = Buffer.from(painting, 'base64'); + const paintingBuffer = Buffer.from(painting, 'base64'); let output: Uint8Array; try { @@ -106,8 +104,8 @@ export function processPainting(painting: string): Buffer | null { return null; } - const tga: TGA = new TGA(Buffer.from(output)); - const png: PNG = new PNG({ + const tga = new TGA(Buffer.from(output)); + const png = new PNG({ width: tga.width, height: tga.height }); @@ -118,7 +116,7 @@ export function processPainting(painting: string): Buffer | null { } export async function uploadCDNAsset(bucket: string, key: string, data: Buffer, acl: string): Promise { - const awsPutParams: aws.S3.PutObjectRequest = { + const awsPutParams = { Body: data, Key: key, Bucket: bucket, @@ -129,7 +127,7 @@ export async function uploadCDNAsset(bucket: string, key: string, data: Buffer, } export async function getUserFriendPIDs(pid: number): Promise { - const response: GetUserFriendPIDsResponse = await gRPCFriendsClient.getUserFriendPIDs({ + const response = await gRPCFriendsClient.getUserFriendPIDs({ pid: pid }, { metadata: Metadata({ @@ -141,7 +139,7 @@ export async function getUserFriendPIDs(pid: number): Promise { } export async function getUserFriendRequestsIncoming(pid: number): Promise { - const response: GetUserFriendRequestsIncomingResponse = await gRPCFriendsClient.getUserFriendRequestsIncoming({ + const response = await gRPCFriendsClient.getUserFriendRequestsIncoming({ pid: pid }, { metadata: Metadata({ @@ -163,7 +161,7 @@ export function getUserAccountData(pid: number): Promise { } export function getValueFromQueryString(qs: ParsedQs, key: string): string[] { - const property: string | string[] | undefined = qs[key] as string | string[]; + const property = qs[key] as string | string[]; if (property) { if (Array.isArray(property)) { @@ -177,7 +175,7 @@ export function getValueFromQueryString(qs: ParsedQs, key: string): string[] { } export function getValueFromHeaders(headers: IncomingHttpHeaders, key: string): string | undefined { - let header: string | string[] | undefined = headers[key]; + let header = headers[key]; let value: string | undefined; if (header) { diff --git a/test/test.ts b/test/test.ts index 0695544..2b7233c 100644 --- a/test/test.ts +++ b/test/test.ts @@ -4,7 +4,7 @@ import crypto from 'node:crypto'; import newman from 'newman'; import { Collection, CollectionDefinition } from 'postman-collection'; import qs from 'qs'; -import axios, { AxiosResponse } from 'axios'; +import axios from 'axios'; import { create as parseXML } from 'xmlbuilder2'; import { table } from 'table'; import ora from 'ora'; @@ -29,11 +29,11 @@ interface TestResult { error?: string } -const USERNAME: string = process.env.PN_MIIVERSE_API_TESTING_USERNAME?.trim() || ''; -const PASSWORD: string = process.env.PN_MIIVERSE_API_TESTING_PASSWORD?.trim() || ''; -const DEVICE_ID: string = process.env.PN_MIIVERSE_API_TESTING_DEVICE_ID?.trim() || ''; -const SERIAL_NUMBER: string = process.env.PN_MIIVERSE_API_TESTING_SERIAL_NUMBER?.trim() || ''; -const CERTIFICATE: string = process.env.PN_MIIVERSE_API_TESTING_CONSOLE_CERT?.trim() || ''; +const USERNAME = process.env.PN_MIIVERSE_API_TESTING_USERNAME?.trim() || ''; +const PASSWORD = process.env.PN_MIIVERSE_API_TESTING_PASSWORD?.trim() || ''; +const DEVICE_ID = process.env.PN_MIIVERSE_API_TESTING_DEVICE_ID?.trim() || ''; +const SERIAL_NUMBER = process.env.PN_MIIVERSE_API_TESTING_SERIAL_NUMBER?.trim() || ''; +const CERTIFICATE = process.env.PN_MIIVERSE_API_TESTING_CONSOLE_CERT?.trim() || ''; if (!USERNAME) { throw new Error('PNID username missing. Required for requesting service tokens. Set PN_MIIVERSE_API_TESTING_USERNAME'); @@ -55,13 +55,13 @@ if (!CERTIFICATE) { throw new Error('Console certificate missing. Required for requesting service tokens. Set PN_MIIVERSE_API_TESTING_CONSOLE_CERT'); } -const BASE_URL: string = 'https://account.pretendo.cc'; -const API_URL: string = `${BASE_URL}/v1/api`; -const MAPPED_IDS_URL: string = `${API_URL}/admin/mapped_ids`; -const ACCESS_TOKEN_URL: string = `${API_URL}/oauth20/access_token/generate`; -const SERVICE_TOKEN_URL: string = `${API_URL}/provider/service_token/@me?client_id=87cd32617f1985439ea608c2746e4610`; +const BASE_URL = 'https://account.pretendo.cc'; +const API_URL = `${BASE_URL}/v1/api`; +const MAPPED_IDS_URL = `${API_URL}/admin/mapped_ids`; +const ACCESS_TOKEN_URL = `${API_URL}/oauth20/access_token/generate`; +const SERVICE_TOKEN_URL = `${API_URL}/provider/service_token/@me?client_id=87cd32617f1985439ea608c2746e4610`; -const DEFAULT_HEADERS: Record = { +const DEFAULT_HEADERS = { 'X-Nintendo-Client-ID': 'a2efa818a34fa16b8afbc8a74eba3eda', 'X-Nintendo-Client-Secret': 'c91cdb5658bd4954ade78533a339cf9a', 'X-Nintendo-Device-ID': DEVICE_ID, @@ -70,10 +70,10 @@ const DEFAULT_HEADERS: Record = { }; export function nintendoPasswordHash(password: string, pid: number): string { - const pidBuffer: Buffer = Buffer.alloc(4); + const pidBuffer = Buffer.alloc(4); pidBuffer.writeUInt32LE(pid); - const unpacked: Buffer = Buffer.concat([ + const unpacked = Buffer.concat([ pidBuffer, Buffer.from('\x02\x65\x43\x46'), Buffer.from(password) @@ -83,7 +83,7 @@ export function nintendoPasswordHash(password: string, pid: number): string { } async function apiGetRequest(url: string, headers = {}): Promise> { - const response: AxiosResponse = await axios.get(url, { + const response = await axios.get(url, { headers: Object.assign(headers, DEFAULT_HEADERS), validateStatus: () => true }); @@ -102,7 +102,7 @@ async function apiGetRequest(url: string, headers = {}): Promise> { - const response: AxiosResponse = await axios.post(url, body, { + const response = await axios.post(url, body, { headers: DEFAULT_HEADERS, validateStatus: () => true, }); @@ -121,26 +121,26 @@ async function apiPostRequest(url: string, body: string): Promise { - const response: Record = await apiGetRequest(`${MAPPED_IDS_URL}?input_type=user_id&output_type=pid&input=${username}`); + const response = await apiGetRequest(`${MAPPED_IDS_URL}?input_type=user_id&output_type=pid&input=${username}`); return Number(response.mapped_ids.mapped_id.out_id); } async function getAccessToken(username: string, passwordHash: string): Promise { - const data: string = qs.stringify({ + const data = qs.stringify({ grant_type: 'password', user_id: username, password: passwordHash, password_type: 'hash', }); - const response: Record = await apiPostRequest(ACCESS_TOKEN_URL, data); + const response = await apiPostRequest(ACCESS_TOKEN_URL, data); return response.OAuth20.access_token.token; } async function getMiiverseServiceToken(accessToken: string): Promise { - const response: Record = await apiGetRequest(SERVICE_TOKEN_URL, { + const response = await apiGetRequest(SERVICE_TOKEN_URL, { 'X-Nintendo-Title-ID': '0005001010040100', Authorization: `Bearer ${accessToken}` }); @@ -193,10 +193,10 @@ function peopleRoutesTest(serviceToken: string): Promise { async function main(): Promise { const tokensSpinner = ora('Acquiring account tokens').start(); - const pid: number = await getPID(USERNAME); - const passwordHash: string = nintendoPasswordHash(PASSWORD, pid); - const accessToken: string = await getAccessToken(USERNAME, passwordHash); - const serviceToken: string = await getMiiverseServiceToken(accessToken); + const pid = await getPID(USERNAME); + const passwordHash = nintendoPasswordHash(PASSWORD, pid); + const accessToken = await getAccessToken(USERNAME, passwordHash); + const serviceToken = await getMiiverseServiceToken(accessToken); tokensSpinner.succeed(); diff --git a/tsconfig.json b/tsconfig.json index 5fc610d..b332a95 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,8 @@ "allowJs": true, "target": "es2022", "noEmitOnError": true, + "noImplicitAny": true, + "strictPropertyInitialization": true, "paths": { "@/*": ["./*"] }