From ac92687af45dcdbd76e07c5f2e1711436e9c3bba Mon Sep 17 00:00:00 2001 From: Rambo6Glaz <39063367+EpicUsername12@users.noreply.github.com> Date: Tue, 4 Jul 2023 09:22:07 +0200 Subject: [PATCH 1/5] Add sub community limits and checks --- src/services/api/routes/communities.ts | 143 +++++++++++------- src/types/common/create-new-community-body.ts | 4 +- src/util.ts | 6 +- 3 files changed, 95 insertions(+), 58 deletions(-) diff --git a/src/services/api/routes/communities.ts b/src/services/api/routes/communities.ts index ef6f8b2..819025f 100644 --- a/src/services/api/routes/communities.ts +++ b/src/services/api/routes/communities.ts @@ -22,27 +22,31 @@ import { ParamPack } from '@/types/common/param-pack'; const createNewCommunitySchema = z.object({ name: z.string(), - description: z.string(), + description: z.string().optional(), icon: z.string(), - app_data: z.string() + app_data: z.string().optional() }); const router: express.Router = express.Router(); -function respondCommunityNotFound(response: express.Response) : void { - response.status(404); - response.send(xmlbuilder.create({ +function respondCommunityError(response: express.Response, http_code: number, error_code: number): void { + response.status(http_code).send(xmlbuilder.create({ result: { has_error: 1, version: 1, - code: 404, - error_code: 919, - message: 'COMMUNITY_NOT_FOUND' + code: http_code, + error_code: error_code, + message: 'COMMUNITY_ERROR' // This field is unused by the entire nn_olv.rpl } }).end({ pretty: true })); } -async function commonGetSubCommunity(paramPack: ParamPack, communityID: string | undefined) : Promise { +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); if (!parentCommunity) { @@ -75,17 +79,17 @@ router.get('/', async function (request: express.Request, response: express.Resp const type: string | undefined = getValueFromQueryString(request.query, 'type'); const limitString: string | undefined = getValueFromQueryString(request.query, 'limit'); - let limit: number = 8; + let limit: number = 4; if (limitString) { limit = parseInt(limitString); } if (isNaN(limit)) { - limit = 8; + limit = 4; } - if (limit > 32) { - limit = 32; + if (limit > 16) { + limit = 16; } const query: SubCommunityQuery = { @@ -94,7 +98,7 @@ router.get('/', async function (request: express.Request, response: express.Resp if (type === 'my') { query.owner = request.pid; - } else if (type ==='favorite') { + } else if (type === 'favorite') { query.user_favorites = request.pid; } @@ -144,17 +148,7 @@ router.get('/:communityID/posts', async function (request: express.Request, resp } if (!community) { - response.status(404); - response.send(xmlbuilder.create({ - result: { - has_error: 1, - version: 1, - code: 404, - error_code: 919, - message: 'COMMUNITY_NOT_FOUND' - } - }).end({ pretty: true })); - return; + return respondCommunityNotFound(response); } const query: CommunityPostsQuery = { @@ -253,23 +247,46 @@ router.post('/', multer().none(), async function (request: express.Request, resp const parentCommunity: HydratedCommunityDocument | null = await getCommunityByTitleID(request.paramPack.title_id); if (!parentCommunity) { - respondCommunityNotFound(response); - return; + return respondCommunityNotFound(response); } // TODO - Better error codes, maybe do defaults? const bodyCheck: z.SafeParseReturnType = createNewCommunitySchema.safeParse(request.body); if (!bodyCheck.success) { - response.send(xmlbuilder.create({ - result: { - has_error: 1, - version: 1, - code: 404, - error_code: 20, - message: 'BAD_COMMUNITY_DATA' - } - }).end({ pretty: true })); - return; + return respondCommunityError(response, 400, 20); + } + + // Name must be at least 4 character long + if (request.body.name.length < 4) { + return respondCommunityError(response, 400, 20); + } + + // Name must contain less than 5 numbers + const digitCount = (request.body.name.match(/\d/g) || []).length; + if (digitCount > 5) { + return respondCommunityError(response, 400, 20); + } + + // Each user can only have 4 subcommunities per title + const ownedQuery: SubCommunityQuery = { + parent: parentCommunity.olive_community_id, + owner: request.pid + }; + + const ownedSubcommunityCount: number = 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 = { + parent: parentCommunity.olive_community_id, + user_favorites: request.pid + }; + + const ownedFavoriteCount: number = await Community.countDocuments(favoriteQuery); + if (ownedFavoriteCount >= 16) { + return respondCommunityError(response, 401, 912); } const communitiesCount: number = await Community.count(); @@ -277,7 +294,7 @@ router.post('/', multer().none(), async function (request: express.Request, resp const community: HydratedCommunityDocument = await Community.create({ platform_id: 0, // WiiU name: request.body.name, - description: request.body.description, + description: request.body.description || '', open: true, allows_comments: true, type: 1, @@ -288,7 +305,8 @@ router.post('/', multer().none(), async function (request: express.Request, resp title_id: request.paramPack.title_id, community_id: communityId.toString(), olive_community_id: communityId.toString(), - app_data: request.body.app_data, + app_data: request.body.app_data || '', + user_favorites: [request.pid] }); response.send(xmlbuilder.create({ @@ -336,6 +354,17 @@ router.post('/:community_id.favorite', multer().none(), async function (request: return; } + // Each user can only have 16 favorite subcommunities per title + const favoriteQuery: SubCommunityQuery = { + parent: community.parent, + user_favorites: request.pid + }; + + const ownedFavoriteCount: number = await Community.countDocuments(favoriteQuery); + if (ownedFavoriteCount >= 16) { + return respondCommunityError(response, 401, 914); + } + await community.addUserFavorite(request.pid); response.send(xmlbuilder.create({ @@ -357,6 +386,11 @@ router.post('/:community_id.unfavorite', multer().none(), async function (reques return; } + // You can't remove from your favorites a community you own + if (community.owner === request.pid) { + return respondCommunityError(response, 401, 916); + } + await community.delUserFavorite(request.pid); response.send(xmlbuilder.create({ @@ -386,22 +420,25 @@ router.post('/:community_id', multer().none(), async function (request: express. const bodyCheck: z.SafeParseReturnType = createNewCommunitySchema.safeParse(request.body); if (!bodyCheck.success) { - response.send(xmlbuilder.create({ - result: { - has_error: 1, - version: 1, - code: 404, - error_code: 20, - message: 'BAD_COMMUNITY_DATA' - } - }).end({ pretty: true })); - return; + return respondCommunityError(response, 400, 20); + } + + if (request.body.name) { + community.name = request.body.name; + } + + if (request.body.description) { + community.description = request.body.description; + } + + if (request.body.icon) { + community.icon = request.body.icon; + } + + if (request.body.app_data) { + community.app_data = request.body.app_data; } - community.name = request.body.name; - community.description = request.body.description; - community.icon = request.body.icon; - community.app_data = request.body.app_data; await community.save(); response.send(xmlbuilder.create({ diff --git a/src/types/common/create-new-community-body.ts b/src/types/common/create-new-community-body.ts index 24aa2dc..a4ad82d 100644 --- a/src/types/common/create-new-community-body.ts +++ b/src/types/common/create-new-community-body.ts @@ -1,6 +1,6 @@ export interface CreateNewCommunityBody { name: string; - description: string; + description?: string; icon: string; - app_data: string; + app_data?: string; } \ No newline at end of file diff --git a/src/util.ts b/src/util.ts index 4c3bb96..9260e91 100644 --- a/src/util.ts +++ b/src/util.ts @@ -37,7 +37,7 @@ 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) => { if (0 === index % 2) { - entries.push([ value ]); + entries.push([value]); } else { entries[Math.ceil(index / 2 - 1)].push(value); } @@ -60,7 +60,7 @@ export function getPIDFromServiceToken(token: string): number { return unpackedToken.pid; } catch (e) { - // TODO - Log this + console.error(e); return 0; } } @@ -215,5 +215,5 @@ export function getValueFromHeaders(headers: IncomingHttpHeaders, key: string): } export function mapToObject(map: Map): object { - return Object.fromEntries(Array.from(map.entries(), ([ k, v ]) => v instanceof Map ? [ k, mapToObject(v) ] : [ k, v ])); + return Object.fromEntries(Array.from(map.entries(), ([k, v]) => v instanceof Map ? [k, mapToObject(v)] : [k, v])); } \ No newline at end of file From 44a338590ba3257b56fa64129b8d1888e05b237e Mon Sep 17 00:00:00 2001 From: Rambo6Glaz <39063367+EpicUsername12@users.noreply.github.com> Date: Tue, 4 Jul 2023 23:48:59 +0200 Subject: [PATCH 2/5] Fix snake_case arguments --- src/services/api/routes/communities.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/api/routes/communities.ts b/src/services/api/routes/communities.ts index 819025f..4d37c6a 100644 --- a/src/services/api/routes/communities.ts +++ b/src/services/api/routes/communities.ts @@ -29,13 +29,13 @@ const createNewCommunitySchema = z.object({ const router: express.Router = express.Router(); -function respondCommunityError(response: express.Response, http_code: number, error_code: number): void { - response.status(http_code).send(xmlbuilder.create({ +function respondCommunityError(response: express.Response, httpStatusCode: number, errorCode: number): void { + response.status(httpStatusCode).send(xmlbuilder.create({ result: { has_error: 1, version: 1, - code: http_code, - error_code: error_code, + code: httpStatusCode, + error_code: errorCode, message: 'COMMUNITY_ERROR' // This field is unused by the entire nn_olv.rpl } }).end({ pretty: true })); From e95b10981684e4878bbb43d05361d612fee587d6 Mon Sep 17 00:00:00 2001 From: Rambo6Glaz <39063367+EpicUsername12@users.noreply.github.com> Date: Wed, 5 Jul 2023 00:27:42 +0200 Subject: [PATCH 3/5] - Removed digit count condition - Added "Cannot start with a whitespace" condition --- src/services/api/routes/communities.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/services/api/routes/communities.ts b/src/services/api/routes/communities.ts index 4d37c6a..eb7f717 100644 --- a/src/services/api/routes/communities.ts +++ b/src/services/api/routes/communities.ts @@ -261,9 +261,8 @@ router.post('/', multer().none(), async function (request: express.Request, resp return respondCommunityError(response, 400, 20); } - // Name must contain less than 5 numbers - const digitCount = (request.body.name.match(/\d/g) || []).length; - if (digitCount > 5) { + // Name must not start with whitespace + if (/^\s/.test(request.body.name)) { return respondCommunityError(response, 400, 20); } @@ -418,12 +417,7 @@ router.post('/:community_id', multer().none(), async function (request: express. return; } - const bodyCheck: z.SafeParseReturnType = createNewCommunitySchema.safeParse(request.body); - if (!bodyCheck.success) { - return respondCommunityError(response, 400, 20); - } - - if (request.body.name) { + if (request.body.name && !(/^\s/.test(request.body.name))) { community.name = request.body.name; } From 792fdf9f409939016a58d86d7233d18bf29741e7 Mon Sep 17 00:00:00 2001 From: Rambo6Glaz <39063367+EpicUsername12@users.noreply.github.com> Date: Wed, 5 Jul 2023 00:32:50 +0200 Subject: [PATCH 4/5] Removed regex to 'trim()' --- src/services/api/routes/communities.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/services/api/routes/communities.ts b/src/services/api/routes/communities.ts index eb7f717..adc9094 100644 --- a/src/services/api/routes/communities.ts +++ b/src/services/api/routes/communities.ts @@ -256,13 +256,14 @@ router.post('/', multer().none(), async function (request: express.Request, resp return respondCommunityError(response, 400, 20); } - // Name must be at least 4 character long - if (request.body.name.length < 4) { - return respondCommunityError(response, 400, 20); + request.body.name = request.body.name.trim(); + + if (request.body.description) { + request.body.description = request.body.description.trim(); } - // Name must not start with whitespace - if (/^\s/.test(request.body.name)) { + // Name must be at least 4 character long + if (request.body.name.length < 4) { return respondCommunityError(response, 400, 20); } @@ -417,12 +418,12 @@ router.post('/:community_id', multer().none(), async function (request: express. return; } - if (request.body.name && !(/^\s/.test(request.body.name))) { - community.name = request.body.name; + if (request.body.name) { + community.name = request.body.name.trim(); } if (request.body.description) { - community.description = request.body.description; + community.description = request.body.description.trim(); } if (request.body.icon) { From 919b74d64e7d374689d835247ea295d78d7615cf Mon Sep 17 00:00:00 2001 From: Rambo6Glaz <39063367+EpicUsername12@users.noreply.github.com> Date: Wed, 5 Jul 2023 00:46:38 +0200 Subject: [PATCH 5/5] =?UTF-8?q?More=20=E2=9C=82=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/api/routes/communities.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/services/api/routes/communities.ts b/src/services/api/routes/communities.ts index adc9094..a06fa0e 100644 --- a/src/services/api/routes/communities.ts +++ b/src/services/api/routes/communities.ts @@ -257,11 +257,16 @@ router.post('/', multer().none(), async function (request: express.Request, resp } request.body.name = request.body.name.trim(); + request.body.icon = request.body.icon.trim(); if (request.body.description) { request.body.description = request.body.description.trim(); } + if (request.body.app_data) { + request.body.app_data = request.body.app_data.trim(); + } + // Name must be at least 4 character long if (request.body.name.length < 4) { return respondCommunityError(response, 400, 20); @@ -427,11 +432,11 @@ router.post('/:community_id', multer().none(), async function (request: express. } if (request.body.icon) { - community.icon = request.body.icon; + community.icon = request.body.icon.trim(); } if (request.body.app_data) { - community.app_data = request.body.app_data; + community.app_data = request.body.app_data.trim(); } await community.save();