From f227c8164be33ec58927c38592ce740b3b283727 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Sun, 1 Mar 2026 19:58:13 +0530 Subject: [PATCH 01/13] feat(rest-typings): migrate four user endpoints to Ajv --- apps/meteor/app/api/server/v1/users.ts | 53 ++++++++------ packages/rest-typings/src/v1/users.ts | 72 ++++++++++++++----- .../users/UsersDeleteOwnAccountParamsPOST.ts | 23 ++++++ .../v1/users/UsersForgotPasswordParamsPOST.ts | 18 +++++ .../src/v1/users/UsersGetAvatarParamsGET.ts | 31 ++++++++ 5 files changed, 157 insertions(+), 40 deletions(-) create mode 100644 packages/rest-typings/src/v1/users/UsersDeleteOwnAccountParamsPOST.ts create mode 100644 packages/rest-typings/src/v1/users/UsersForgotPasswordParamsPOST.ts create mode 100644 packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index d9e37b1f7b8b2..b3975da5fccdc 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -21,6 +21,10 @@ import { ajv, validateBadRequestErrorResponse, validateUnauthorizedErrorResponse, + isUsersGetAvatarProps, + isUsersDeleteOwnAccountProps, + isUsersResetAvatarProps, + isUsersForgotPasswordProps, } from '@rocket.chat/rest-typings'; import { getLoginExpirationInMs, wrapExceptions } from '@rocket.chat/tools'; import { Accounts } from 'meteor/accounts-base'; @@ -83,7 +87,10 @@ import { findPaginatedUsersByStatus, findUsersToAutocomplete, getInclusiveFields API.v1.addRoute( 'users.getAvatar', - { authRequired: true }, + { + authRequired: true, + validateParams: isUsersGetAvatarProps, + }, { async get() { const user = await getUserFromParams(this.queryParams); @@ -171,9 +178,9 @@ API.v1.addRoute( const twoFactorOptions = !userData.typedPassword ? null : { - twoFactorCode: userData.typedPassword, - twoFactorMethod: 'password', - }; + twoFactorCode: userData.typedPassword, + twoFactorMethod: 'password', + }; await executeSaveUserProfile.call(this, this.user, userData, this.bodyParams.customFields, twoFactorOptions); @@ -361,20 +368,19 @@ API.v1.addRoute( API.v1.addRoute( 'users.deleteOwnAccount', - { authRequired: true }, + { + authRequired: true, + validateParams: isUsersDeleteOwnAccountProps, + }, { async post() { - const { password } = this.bodyParams; - if (!password) { - return API.v1.failure('Body parameter "password" is required.'); - } + const { password, confirmRelinquish = false } = this.bodyParams; + if (!settings.get('Accounts_AllowDeleteOwnAccount')) { throw new Meteor.Error('error-not-allowed', 'Not allowed'); } - const { confirmRelinquish = false } = this.bodyParams; - - await deleteUserOwnAccount(this.userId, password, confirmRelinquish); + const result = await deleteUserOwnAccount(this.userId, password, confirmRelinquish); return API.v1.success(); }, @@ -536,10 +542,10 @@ API.v1.addRoute( const limit = count !== 0 ? [ - { - $limit: count, - }, - ] + { + $limit: count, + }, + ] : []; const result = await Users.col @@ -729,7 +735,10 @@ API.v1.addRoute( API.v1.addRoute( 'users.resetAvatar', - { authRequired: true }, + { + authRequired: true, + validateParams: isUsersResetAvatarProps, + }, { async post() { const user = await getUserFromParams(this.bodyParams); @@ -900,7 +909,10 @@ API.v1.addRoute( API.v1.addRoute( 'users.forgotPassword', - { authRequired: false }, + { + authRequired: false, + validateParams: isUsersForgotPasswordProps, + }, { async post() { const isPasswordResetEnabled = settings.get('Accounts_PasswordReset'); @@ -910,9 +922,6 @@ API.v1.addRoute( } const { email } = this.bodyParams; - if (!email) { - return API.v1.failure("The 'email' param is required"); - } await sendForgotPasswordEmail(email.toLowerCase()); return API.v1.success(); @@ -1558,5 +1567,5 @@ type UsersEndpoints = ExtractRoutesFromAPI; declare module '@rocket.chat/rest-typings' { // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface - interface Endpoints extends UsersEndpoints {} + interface Endpoints extends UsersEndpoints { } } diff --git a/packages/rest-typings/src/v1/users.ts b/packages/rest-typings/src/v1/users.ts index 565620d31ba5e..8955f32bc346f 100644 --- a/packages/rest-typings/src/v1/users.ts +++ b/packages/rest-typings/src/v1/users.ts @@ -17,6 +17,39 @@ import type { UsersSendWelcomeEmailParamsPOST } from './users/UsersSendWelcomeEm import type { UsersSetPreferencesParamsPOST } from './users/UsersSetPreferenceParamsPOST'; import type { UsersUpdateOwnBasicInfoParamsPOST } from './users/UsersUpdateOwnBasicInfoParamsPOST'; import type { UsersUpdateParamsPOST } from './users/UsersUpdateParamsPOST'; +import type { UsersGetAvatarParamsGET } from './users/UsersGetAvatarParamsGET'; +import type { UsersDeleteOwnAccountParamsPOST } from './users/UsersDeleteOwnAccountParamsPOST'; +import type { UsersForgotPasswordParamsPOST } from './users/UsersForgotPasswordParamsPOST'; + +export const isUsersGetAvatarProps = ajv.compile({ + type: 'object', + properties: { + userId: { type: 'string', nullable: true }, + username: { type: 'string', nullable: true }, + user: { type: 'string', nullable: true }, + }, + required: [], + additionalProperties: false, +}); + +export const isUsersDeleteOwnAccountProps = ajv.compile({ + type: 'object', + properties: { + password: { type: 'string' }, + confirmRelinquish: { type: 'boolean', nullable: true }, + }, + required: ['password'], + additionalProperties: false, +}); + +export const isUsersForgotPasswordProps = ajv.compile({ + type: 'object', + properties: { + email: { type: 'string' }, + }, + required: ['email'], + additionalProperties: false, +}); type UsersInfo = { userId?: IUser['_id']; username?: IUser['username'] }; @@ -197,14 +230,14 @@ export type UsersEndpoints = { POST: ( params: | { - userId: string; - } + userId: string; + } | { - username: string; - } + username: string; + } | { - user: string; - }, + user: string; + }, ) => void; }; @@ -212,14 +245,14 @@ export type UsersEndpoints = { POST: ( params: | { - userId: string; - } + userId: string; + } | { - username: string; - } + username: string; + } | { - user: string; - }, + user: string; + }, ) => void; }; @@ -299,14 +332,14 @@ export type UsersEndpoints = { GET: ( params: | { - userId: string; - } + userId: string; + } | { - username: string; - } + username: string; + } | { - user: string; - }, + user: string; + }, ) => { presence: UserStatus; connectionStatus?: 'online' | 'offline' | 'away' | 'busy'; @@ -380,3 +413,6 @@ export * from './users/UserRegisterParamsPOST'; export * from './users/UserLogoutParamsPOST'; export * from './users/UsersListTeamsParamsGET'; export * from './users/UsersAutocompleteParamsGET'; +export * from './users/UsersGetAvatarParamsGET'; +export * from './users/UsersDeleteOwnAccountParamsPOST'; +export * from './users/UsersForgotPasswordParamsPOST'; diff --git a/packages/rest-typings/src/v1/users/UsersDeleteOwnAccountParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersDeleteOwnAccountParamsPOST.ts new file mode 100644 index 0000000000000..2c5e3c67c4576 --- /dev/null +++ b/packages/rest-typings/src/v1/users/UsersDeleteOwnAccountParamsPOST.ts @@ -0,0 +1,23 @@ +import { ajv } from '../Ajv'; + +export type UsersDeleteOwnAccountParamsPOST = { + password: string; + confirmRelinquish?: boolean; +}; + +const UsersDeleteOwnAccountParamsPostSchema = { + type: 'object', + properties: { + password: { + type: 'string', + }, + confirmRelinquish: { + type: 'boolean', + nullable: true, + } + }, + required: ['password'], + additionalProperties: false, +}; + +export const isUsersDeleteOwnAccountProps = ajv.compile(UsersDeleteOwnAccountParamsPostSchema); diff --git a/packages/rest-typings/src/v1/users/UsersForgotPasswordParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersForgotPasswordParamsPOST.ts new file mode 100644 index 0000000000000..34a0a12d70d76 --- /dev/null +++ b/packages/rest-typings/src/v1/users/UsersForgotPasswordParamsPOST.ts @@ -0,0 +1,18 @@ +import { ajv } from '../Ajv'; + +export type UsersForgotPasswordParamsPOST = { + email: string; +}; + +const UsersForgotPasswordParamsPostSchema = { + type: 'object', + properties: { + email: { + type: 'string', + } + }, + required: ['email'], + additionalProperties: false, +}; + +export const isUsersForgotPasswordProps = ajv.compile(UsersForgotPasswordParamsPostSchema); diff --git a/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts b/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts new file mode 100644 index 0000000000000..b456b1e6d90eb --- /dev/null +++ b/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts @@ -0,0 +1,31 @@ +import type { IUser } from '@rocket.chat/core-typings'; + +import { ajv } from '../Ajv'; + +export type UsersGetAvatarParamsGET = { + userId?: IUser['_id']; + username?: IUser['username']; + user?: string; +}; + +const UsersGetAvatarParamsGetSchema = { + type: 'object', + properties: { + userId: { + type: 'string', + nullable: true, + }, + username: { + type: 'string', + nullable: true, + }, + user: { + type: 'string', + nullable: true, + } + }, + required: [], + additionalProperties: false, +}; + +export const isUsersGetAvatarProps = ajv.compile(UsersGetAvatarParamsGetSchema); From a64fa3435688c49f20d50c418369b0c8f95617af Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Sun, 1 Mar 2026 20:06:51 +0530 Subject: [PATCH 02/13] chore: add changeset for users REST param validations migrations --- .changeset/add-users-rest-param-validations.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/add-users-rest-param-validations.md diff --git a/.changeset/add-users-rest-param-validations.md b/.changeset/add-users-rest-param-validations.md new file mode 100644 index 0000000000000..812f502e0a530 --- /dev/null +++ b/.changeset/add-users-rest-param-validations.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +Migrated `users.getAvatar`, `users.deleteOwnAccount`, `users.resetAvatar`, and `users.forgotPassword` to explicit `Ajv` schema validation. From da0ff364f9583c63d1a286bf65125fae49b8ec4b Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Sun, 1 Mar 2026 20:27:16 +0530 Subject: [PATCH 03/13] refactor(rest-typings): remove duplicate ajv validator exports --- .../users/UsersDeleteOwnAccountParamsPOST.ts | 19 --------------- .../v1/users/UsersForgotPasswordParamsPOST.ts | 15 ------------ .../src/v1/users/UsersGetAvatarParamsGET.ts | 24 ------------------- 3 files changed, 58 deletions(-) diff --git a/packages/rest-typings/src/v1/users/UsersDeleteOwnAccountParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersDeleteOwnAccountParamsPOST.ts index 2c5e3c67c4576..ab8bc6077e4d6 100644 --- a/packages/rest-typings/src/v1/users/UsersDeleteOwnAccountParamsPOST.ts +++ b/packages/rest-typings/src/v1/users/UsersDeleteOwnAccountParamsPOST.ts @@ -1,23 +1,4 @@ -import { ajv } from '../Ajv'; - export type UsersDeleteOwnAccountParamsPOST = { password: string; confirmRelinquish?: boolean; }; - -const UsersDeleteOwnAccountParamsPostSchema = { - type: 'object', - properties: { - password: { - type: 'string', - }, - confirmRelinquish: { - type: 'boolean', - nullable: true, - } - }, - required: ['password'], - additionalProperties: false, -}; - -export const isUsersDeleteOwnAccountProps = ajv.compile(UsersDeleteOwnAccountParamsPostSchema); diff --git a/packages/rest-typings/src/v1/users/UsersForgotPasswordParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersForgotPasswordParamsPOST.ts index 34a0a12d70d76..4d489327da555 100644 --- a/packages/rest-typings/src/v1/users/UsersForgotPasswordParamsPOST.ts +++ b/packages/rest-typings/src/v1/users/UsersForgotPasswordParamsPOST.ts @@ -1,18 +1,3 @@ -import { ajv } from '../Ajv'; - export type UsersForgotPasswordParamsPOST = { email: string; }; - -const UsersForgotPasswordParamsPostSchema = { - type: 'object', - properties: { - email: { - type: 'string', - } - }, - required: ['email'], - additionalProperties: false, -}; - -export const isUsersForgotPasswordProps = ajv.compile(UsersForgotPasswordParamsPostSchema); diff --git a/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts b/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts index b456b1e6d90eb..8f5c79dfdf284 100644 --- a/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts +++ b/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts @@ -1,31 +1,7 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { ajv } from '../Ajv'; - export type UsersGetAvatarParamsGET = { userId?: IUser['_id']; username?: IUser['username']; user?: string; }; - -const UsersGetAvatarParamsGetSchema = { - type: 'object', - properties: { - userId: { - type: 'string', - nullable: true, - }, - username: { - type: 'string', - nullable: true, - }, - user: { - type: 'string', - nullable: true, - } - }, - required: [], - additionalProperties: false, -}; - -export const isUsersGetAvatarProps = ajv.compile(UsersGetAvatarParamsGetSchema); From 57f23271037c27dafbea9d74616759fe1ef5eb4a Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Sun, 1 Mar 2026 20:31:12 +0530 Subject: [PATCH 04/13] fix(rest-typings): address validation schema gaps in users REST API Adds minLength constraints to users.forgotPassword and users.deleteOwnAccount to replicate legacy truthiness checks. Removes unaligned nullable fields from users.getAvatar definition. --- packages/rest-typings/src/v1/users.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/rest-typings/src/v1/users.ts b/packages/rest-typings/src/v1/users.ts index 8955f32bc346f..da67d39b918d9 100644 --- a/packages/rest-typings/src/v1/users.ts +++ b/packages/rest-typings/src/v1/users.ts @@ -24,9 +24,9 @@ import type { UsersForgotPasswordParamsPOST } from './users/UsersForgotPasswordP export const isUsersGetAvatarProps = ajv.compile({ type: 'object', properties: { - userId: { type: 'string', nullable: true }, - username: { type: 'string', nullable: true }, - user: { type: 'string', nullable: true }, + userId: { type: 'string' }, + username: { type: 'string' }, + user: { type: 'string' }, }, required: [], additionalProperties: false, @@ -35,8 +35,8 @@ export const isUsersGetAvatarProps = ajv.compile({ export const isUsersDeleteOwnAccountProps = ajv.compile({ type: 'object', properties: { - password: { type: 'string' }, - confirmRelinquish: { type: 'boolean', nullable: true }, + password: { type: 'string', minLength: 1 }, + confirmRelinquish: { type: 'boolean' }, }, required: ['password'], additionalProperties: false, @@ -45,7 +45,7 @@ export const isUsersDeleteOwnAccountProps = ajv.compile({ type: 'object', properties: { - email: { type: 'string' }, + email: { type: 'string', minLength: 1 }, }, required: ['email'], additionalProperties: false, From 075af9c1600b0b449c906d1838a7b06c33095675 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Sun, 1 Mar 2026 23:40:57 +0530 Subject: [PATCH 05/13] feat(api): Enforce Schema Validation on groups.list and groups.listAll --- apps/meteor/app/api/server/v1/groups.ts | 18 +++++------ .../src/v1/groups/GroupsListProps.ts | 32 +++++++++++++++++-- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/apps/meteor/app/api/server/v1/groups.ts b/apps/meteor/app/api/server/v1/groups.ts index 2437813860836..75dd5dd530c16 100644 --- a/apps/meteor/app/api/server/v1/groups.ts +++ b/apps/meteor/app/api/server/v1/groups.ts @@ -1,7 +1,7 @@ import { Team, isMeteorError } from '@rocket.chat/core-services'; import type { IIntegration, IUser, IRoom, RoomType, UserStatus } from '@rocket.chat/core-typings'; import { Integrations, Messages, Rooms, Subscriptions, Uploads, Users } from '@rocket.chat/models'; -import { isGroupsOnlineProps, isGroupsMessagesProps, isGroupsFilesProps } from '@rocket.chat/rest-typings'; +import { isGroupsOnlineProps, isGroupsMessagesProps, isGroupsFilesProps, isGroupsListProps } from '@rocket.chat/rest-typings'; import { isTruthy } from '@rocket.chat/tools'; import { check, Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; @@ -81,12 +81,12 @@ async function findPrivateGroupByIdOrName({ userId, }: { params: - | { - roomId?: string; - } - | { - roomName?: string; - }; + | { + roomId?: string; + } + | { + roomName?: string; + }; userId: string; checkedArchived?: boolean; }): Promise<{ @@ -653,7 +653,7 @@ API.v1.addRoute( // List Private Groups a user has access to API.v1.addRoute( 'groups.list', - { authRequired: true }, + { authRequired: true, validateParams: isGroupsListProps }, { async get() { const { offset, count } = await getPaginationItems(this.queryParams); @@ -692,7 +692,7 @@ API.v1.addRoute( API.v1.addRoute( 'groups.listAll', - { authRequired: true, permissionsRequired: ['view-room-administration'] }, + { authRequired: true, permissionsRequired: ['view-room-administration'], validateParams: isGroupsListProps }, { async get() { const { offset, count } = await getPaginationItems(this.queryParams); diff --git a/packages/rest-typings/src/v1/groups/GroupsListProps.ts b/packages/rest-typings/src/v1/groups/GroupsListProps.ts index 10bc322f8e0be..3142563d4c506 100644 --- a/packages/rest-typings/src/v1/groups/GroupsListProps.ts +++ b/packages/rest-typings/src/v1/groups/GroupsListProps.ts @@ -1,6 +1,34 @@ import type { PaginatedRequest } from '../../helpers/PaginatedRequest'; import { ajv } from '../Ajv'; -export type GroupsListProps = PaginatedRequest; -const groupsListPropsSchema = {}; +export type GroupsListProps = PaginatedRequest<{ name?: string }>; + +const groupsListPropsSchema = { + type: 'object', + properties: { + count: { + type: 'number', + nullable: true, + }, + offset: { + type: 'number', + nullable: true, + }, + sort: { + type: 'string', + nullable: true, + }, + fields: { + type: 'string', + nullable: true, + }, + query: { + type: 'string', + nullable: true, + }, + }, + required: [], + additionalProperties: false, +}; + export const isGroupsListProps = ajv.compile(groupsListPropsSchema); From e77c2b18a3ae59a4af1e60ac97cce15248a9c481 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Mon, 2 Mar 2026 01:33:08 +0530 Subject: [PATCH 06/13] fix(groups): allow ame filter inside GroupsListProps schema validation --- packages/rest-typings/src/v1/groups/GroupsListProps.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/rest-typings/src/v1/groups/GroupsListProps.ts b/packages/rest-typings/src/v1/groups/GroupsListProps.ts index 3142563d4c506..8eb811f647ed0 100644 --- a/packages/rest-typings/src/v1/groups/GroupsListProps.ts +++ b/packages/rest-typings/src/v1/groups/GroupsListProps.ts @@ -26,6 +26,10 @@ const groupsListPropsSchema = { type: 'string', nullable: true, }, + name: { + type: 'string', + nullable: true, + }, }, required: [], additionalProperties: false, From 163b46314dafe24c8327e1ae8b6ea14c9fe6fd49 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Mon, 2 Mar 2026 19:18:31 +0530 Subject: [PATCH 07/13] refactor(rest-typings): enforce strict mutual exclusivity for user avatar selectors Updates UsersGetAvatarParamsGET to a union type and uses oneOf in the Ajv schema. Also updates changeset to include groups endpoints. --- .../add-users-rest-param-validations.md | 2 +- packages/rest-typings/src/v1/users.ts | 28 +++++++++++++------ .../src/v1/users/UsersGetAvatarParamsGET.ts | 9 +++--- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/.changeset/add-users-rest-param-validations.md b/.changeset/add-users-rest-param-validations.md index 812f502e0a530..76739971782aa 100644 --- a/.changeset/add-users-rest-param-validations.md +++ b/.changeset/add-users-rest-param-validations.md @@ -3,4 +3,4 @@ '@rocket.chat/meteor': minor --- -Migrated `users.getAvatar`, `users.deleteOwnAccount`, `users.resetAvatar`, and `users.forgotPassword` to explicit `Ajv` schema validation. +Migrated `users.getAvatar`, `users.deleteOwnAccount`, `users.resetAvatar`, and `users.forgotPassword` to explicit `Ajv` schema validation. Similarly, `groups.list` and `groups.listAll` were migrated to ensure strict parameter validation. diff --git a/packages/rest-typings/src/v1/users.ts b/packages/rest-typings/src/v1/users.ts index da67d39b918d9..9ef59199a1494 100644 --- a/packages/rest-typings/src/v1/users.ts +++ b/packages/rest-typings/src/v1/users.ts @@ -22,14 +22,26 @@ import type { UsersDeleteOwnAccountParamsPOST } from './users/UsersDeleteOwnAcco import type { UsersForgotPasswordParamsPOST } from './users/UsersForgotPasswordParamsPOST'; export const isUsersGetAvatarProps = ajv.compile({ - type: 'object', - properties: { - userId: { type: 'string' }, - username: { type: 'string' }, - user: { type: 'string' }, - }, - required: [], - additionalProperties: false, + oneOf: [ + { + type: 'object', + properties: { userId: { type: 'string' } }, + required: ['userId'], + additionalProperties: false, + }, + { + type: 'object', + properties: { username: { type: 'string' } }, + required: ['username'], + additionalProperties: false, + }, + { + type: 'object', + properties: { user: { type: 'string' } }, + required: ['user'], + additionalProperties: false, + }, + ], }); export const isUsersDeleteOwnAccountProps = ajv.compile({ diff --git a/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts b/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts index 8f5c79dfdf284..195500169c9a6 100644 --- a/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts +++ b/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts @@ -1,7 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; -export type UsersGetAvatarParamsGET = { - userId?: IUser['_id']; - username?: IUser['username']; - user?: string; -}; +export type UsersGetAvatarParamsGET = + | { userId: string; username?: never; user?: never } + | { username: string; userId?: never; user?: never } + | { user: string; userId?: never; username?: never }; From f90fe50f492c40970b8777e10364a7c0ee54570a Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Mon, 2 Mar 2026 19:44:33 +0530 Subject: [PATCH 08/13] refactor(rest-typings): align users.getAvatar signature with strict union type Updates UsersEndpoints to use UsersGetAvatarParamsGET instead of a loose object, ensuring TypeScript enforcement of the unique selector requirement. --- packages/rest-typings/src/v1/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rest-typings/src/v1/users.ts b/packages/rest-typings/src/v1/users.ts index 9ef59199a1494..1b7ab60849b55 100644 --- a/packages/rest-typings/src/v1/users.ts +++ b/packages/rest-typings/src/v1/users.ts @@ -401,7 +401,7 @@ export type UsersEndpoints = { }; '/v1/users.getAvatar': { - GET: (params: { userId?: string; username?: string; user?: string }) => void; + GET: (params: UsersGetAvatarParamsGET) => void; }; '/v1/users.updateOwnBasicInfo': { From d9fc7e12498bdbf44ff0e8c7afc8044b2edccb73 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Mon, 2 Mar 2026 19:48:49 +0530 Subject: [PATCH 09/13] fix(rest-typings): remove unused IUser import in UsersGetAvatarParamsGET --- packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts b/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts index 195500169c9a6..bd2a2227609da 100644 --- a/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts +++ b/packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts @@ -1,5 +1,3 @@ -import type { IUser } from '@rocket.chat/core-typings'; - export type UsersGetAvatarParamsGET = | { userId: string; username?: never; user?: never } | { username: string; userId?: never; user?: never } From 82cf45abddc51a43b9a9c7239bcc69e018660976 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Tue, 3 Mar 2026 00:28:15 +0530 Subject: [PATCH 10/13] fix: resolve CI failures and fix indentation in groups REST API --- apps/meteor/app/api/server/v1/users.ts | 14 ++--- .../src/v1/groups/GroupsListProps.ts | 58 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index b3975da5fccdc..2130870223308 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -380,7 +380,7 @@ API.v1.addRoute( throw new Meteor.Error('error-not-allowed', 'Not allowed'); } - const result = await deleteUserOwnAccount(this.userId, password, confirmRelinquish); + await deleteUserOwnAccount(this.userId, password, confirmRelinquish); return API.v1.success(); }, @@ -548,7 +548,12 @@ API.v1.addRoute( ] : []; - const result = await Users.col + const [ + { + sortedResults: users, + totalCount: [{ total } = { total: 0 }], + } = { sortedResults: [], totalCount: [] }, + ] = await Users.col .aggregate<{ sortedResults: IUser[]; totalCount: { total: number }[] }>([ { $match: nonEmptyQuery, @@ -580,11 +585,6 @@ API.v1.addRoute( ]) .toArray(); - const { - sortedResults: users, - totalCount: [{ total } = { total: 0 }], - } = result[0]; - return API.v1.success({ users, count: users.length, diff --git a/packages/rest-typings/src/v1/groups/GroupsListProps.ts b/packages/rest-typings/src/v1/groups/GroupsListProps.ts index 8eb811f647ed0..f80f1c7d00af1 100644 --- a/packages/rest-typings/src/v1/groups/GroupsListProps.ts +++ b/packages/rest-typings/src/v1/groups/GroupsListProps.ts @@ -4,35 +4,35 @@ import { ajv } from '../Ajv'; export type GroupsListProps = PaginatedRequest<{ name?: string }>; const groupsListPropsSchema = { - type: 'object', - properties: { - count: { - type: 'number', - nullable: true, - }, - offset: { - type: 'number', - nullable: true, - }, - sort: { - type: 'string', - nullable: true, - }, - fields: { - type: 'string', - nullable: true, - }, - query: { - type: 'string', - nullable: true, - }, - name: { - type: 'string', - nullable: true, - }, - }, - required: [], - additionalProperties: false, + type: 'object', + properties: { + count: { + type: 'number', + nullable: true, + }, + offset: { + type: 'number', + nullable: true, + }, + sort: { + type: 'string', + nullable: true, + }, + fields: { + type: 'string', + nullable: true, + }, + query: { + type: 'string', + nullable: true, + }, + name: { + type: 'string', + nullable: true, + }, + }, + required: [], + additionalProperties: false, }; export const isGroupsListProps = ajv.compile(groupsListPropsSchema); From d853e09dd55331aef9d5ca97c24efa0d3cd51f0e Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Tue, 3 Mar 2026 00:45:35 +0530 Subject: [PATCH 11/13] chore: ignore .agent and .optimization folders local to development --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 8ee032f2d8c32..7ba2202078894 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,7 @@ storybook-static development/tempo-data/ .env + +# Antigravity local folders +.agent/ +.optimization/ \ No newline at end of file From ac9cfed103066f549e59f99ae83a7cdf8185f837 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Tue, 3 Mar 2026 01:36:44 +0530 Subject: [PATCH 12/13] fix(api): resolve lint failures in REST typings and ignore local meta-folders --- packages/rest-typings/src/v1/Ajv.ts | 2 +- packages/rest-typings/src/v1/users.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/rest-typings/src/v1/Ajv.ts b/packages/rest-typings/src/v1/Ajv.ts index 0c2def3db310f..c79ba17edf417 100644 --- a/packages/rest-typings/src/v1/Ajv.ts +++ b/packages/rest-typings/src/v1/Ajv.ts @@ -14,7 +14,7 @@ addFormats(ajv); ajv.addFormat('basic_email', /^[^@]+@[^@]+$/); ajv.addFormat( 'rfc_email', - /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/, + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/, ); ajv.addKeyword({ keyword: 'isNotEmpty', diff --git a/packages/rest-typings/src/v1/users.ts b/packages/rest-typings/src/v1/users.ts index 1b7ab60849b55..f65276105f61d 100644 --- a/packages/rest-typings/src/v1/users.ts +++ b/packages/rest-typings/src/v1/users.ts @@ -9,6 +9,9 @@ import type { UserLogoutParamsPOST } from './users/UserLogoutParamsPOST'; import type { UserRegisterParamsPOST } from './users/UserRegisterParamsPOST'; import type { UserSetActiveStatusParamsPOST } from './users/UserSetActiveStatusParamsPOST'; import type { UsersAutocompleteParamsGET } from './users/UsersAutocompleteParamsGET'; +import type { UsersDeleteOwnAccountParamsPOST } from './users/UsersDeleteOwnAccountParamsPOST'; +import type { UsersForgotPasswordParamsPOST } from './users/UsersForgotPasswordParamsPOST'; +import type { UsersGetAvatarParamsGET } from './users/UsersGetAvatarParamsGET'; import type { UsersInfoParamsGet } from './users/UsersInfoParamsGet'; import type { UsersListStatusParamsGET } from './users/UsersListStatusParamsGET'; import type { UsersListTeamsParamsGET } from './users/UsersListTeamsParamsGET'; @@ -17,9 +20,6 @@ import type { UsersSendWelcomeEmailParamsPOST } from './users/UsersSendWelcomeEm import type { UsersSetPreferencesParamsPOST } from './users/UsersSetPreferenceParamsPOST'; import type { UsersUpdateOwnBasicInfoParamsPOST } from './users/UsersUpdateOwnBasicInfoParamsPOST'; import type { UsersUpdateParamsPOST } from './users/UsersUpdateParamsPOST'; -import type { UsersGetAvatarParamsGET } from './users/UsersGetAvatarParamsGET'; -import type { UsersDeleteOwnAccountParamsPOST } from './users/UsersDeleteOwnAccountParamsPOST'; -import type { UsersForgotPasswordParamsPOST } from './users/UsersForgotPasswordParamsPOST'; export const isUsersGetAvatarProps = ajv.compile({ oneOf: [ From 30e898c5d4142dff135dbd79c58ff904196bc8e8 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Tue, 3 Mar 2026 04:45:25 +0530 Subject: [PATCH 13/13] fix(rest-typings): fix Prettier formatting in users.ts --- packages/rest-typings/src/v1/users.ts | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/rest-typings/src/v1/users.ts b/packages/rest-typings/src/v1/users.ts index f65276105f61d..a636b5e3e230d 100644 --- a/packages/rest-typings/src/v1/users.ts +++ b/packages/rest-typings/src/v1/users.ts @@ -242,14 +242,14 @@ export type UsersEndpoints = { POST: ( params: | { - userId: string; - } + userId: string; + } | { - username: string; - } + username: string; + } | { - user: string; - }, + user: string; + }, ) => void; }; @@ -257,14 +257,14 @@ export type UsersEndpoints = { POST: ( params: | { - userId: string; - } + userId: string; + } | { - username: string; - } + username: string; + } | { - user: string; - }, + user: string; + }, ) => void; }; @@ -344,14 +344,14 @@ export type UsersEndpoints = { GET: ( params: | { - userId: string; - } + userId: string; + } | { - username: string; - } + username: string; + } | { - user: string; - }, + user: string; + }, ) => { presence: UserStatus; connectionStatus?: 'online' | 'offline' | 'away' | 'busy';