Files
upstream-rocketchat/apps/meteor/server/api/v1/teams.ts
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

827 lines
21 KiB
TypeScript

import { Team } from '@rocket.chat/core-services';
import type { ITeamAutocompleteResult } from '@rocket.chat/core-services';
import type { ITeam, UserStatus } from '@rocket.chat/core-typings';
import { Users, Rooms } from '@rocket.chat/models';
import {
ajv,
isTeamsAddRoomsProps,
isTeamsAutocompleteProps,
isTeamsConvertToChannelProps,
isTeamsCreateProps,
isTeamsInfoProps,
isTeamsListRoomsProps,
isTeamsListRoomsOfUserProps,
isTeamsMembersProps,
isTeamsRemoveRoomProps,
isTeamsUpdateMemberProps,
isTeamsUpdateRoomProps,
isTeamsRemoveMemberProps,
isTeamsAddMembersProps,
isTeamsDeleteProps,
isTeamsLeaveProps,
isTeamsUpdateProps,
isTeamsListChildrenProps,
validateBadRequestErrorResponse,
validateUnauthorizedErrorResponse,
validateForbiddenErrorResponse,
validateNotFoundErrorResponse,
} from '@rocket.chat/rest-typings';
import { escapeRegExp } from '@rocket.chat/tools';
import { canAccessRoomAsync } from '../../lib/authorization';
import { hasPermissionAsync, hasAtLeastOnePermissionAsync, hasAllPermissionAsync } from '../../lib/authorization/hasPermission';
import { eraseRoom } from '../../lib/eraseRoom';
import { removeUserFromRoom } from '../../lib/rooms/removeUserFromRoom';
import { effectiveStatusFilter } from '../../lib/statusVisibility/effectiveStatus';
import { getUsersHiddenFrom } from '../../lib/statusVisibility/hiddenUsers';
import { redactStatus } from '../../lib/statusVisibility/redactStatus';
import { settings } from '../../settings';
import type { ExtractRoutesFromAPI } from '../ApiClass';
import { API } from '../api';
import { eraseTeam } from '../lib/eraseTeam';
import { getPaginationItems } from '../lib/getPaginationItems';
const paginatedTeamsResponseSchema = ajv.compile<{ teams: ITeam[]; total: number; count: number; offset: number }>({
type: 'object',
properties: {
teams: { type: 'array', items: { type: 'object' } },
total: { type: 'number' },
count: { type: 'number' },
offset: { type: 'number' },
success: { type: 'boolean', enum: [true] },
},
required: ['teams', 'total', 'count', 'offset', 'success'],
additionalProperties: false,
});
const successResponseSchema = ajv.compile<void>({
type: 'object',
properties: { success: { type: 'boolean', enum: [true] } },
required: ['success'],
additionalProperties: false,
});
const teamsEndpoints = API.v1
.get(
'teams.list',
{
authRequired: true,
response: {
200: paginatedTeamsResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const { offset, count } = await getPaginationItems(this.queryParams);
const { sort, query } = await this.parseJsonQuery();
const { records, total } = await Team.list(this.userId, { offset, count }, { sort, query });
return API.v1.success({
teams: records,
total,
count: records.length,
offset,
});
},
)
.get(
'teams.listAll',
{
authRequired: true,
permissionsRequired: ['view-all-teams'],
response: {
200: paginatedTeamsResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const { offset, count } = await getPaginationItems(this.queryParams);
const { records, total } = await Team.listAll({ offset, count });
return API.v1.success({
teams: records,
total,
count: records.length,
offset,
});
},
)
.post(
'teams.create',
{
authRequired: true,
permissionsRequired: ['create-team'],
body: isTeamsCreateProps,
response: {
200: ajv.compile<{ team: ITeam }>({
type: 'object',
properties: {
team: { type: 'object' },
success: { type: 'boolean', enum: [true] },
},
required: ['team', 'success'],
additionalProperties: false,
}),
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { name, type, members, room, owner } = this.bodyParams;
if (room?.id && !(await hasAllPermissionAsync(this.user, ['create-team', 'edit-room'], room.id))) {
return API.v1.forbidden();
}
const team = await Team.create(this.userId, {
team: {
name,
type,
},
room: room as Parameters<typeof Team.create>[1]['room'],
members,
owner,
});
return API.v1.success({ team });
},
);
const getTeamByIdOrName = async (params: { teamId: string } | { teamName: string }): Promise<ITeam | null> => {
if ('teamId' in params && params.teamId) {
return Team.getOneById<ITeam>(params.teamId);
}
if ('teamName' in params && params.teamName) {
return Team.getOneByName(params.teamName);
}
return null;
};
API.v1.post(
'teams.convertToChannel',
{
authRequired: true,
body: isTeamsConvertToChannelProps,
response: {
200: successResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { roomsToRemove = [] } = this.bodyParams;
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasPermissionAsync(this.user, 'convert-team', team.roomId))) {
return API.v1.forbidden();
}
const rooms = await Team.getMatchingTeamRooms(team._id, roomsToRemove);
if (rooms.length) {
for (const room of rooms) {
await eraseRoom(room, this.user);
}
}
await Promise.all([Team.unsetTeamIdOfRooms(this.user, team), Team.removeAllMembersFromTeam(team._id)]);
await Team.deleteById(team._id);
return API.v1.success();
},
);
const roomResponseSchema = ajv.compile<{ room: object }>({
type: 'object',
properties: {
room: { type: 'object' },
success: { type: 'boolean', enum: [true] },
},
required: ['room', 'success'],
additionalProperties: false,
});
const roomsResponseSchema = ajv.compile<{ rooms: object[] }>({
type: 'object',
properties: {
rooms: { type: 'array', items: { type: 'object' } },
success: { type: 'boolean', enum: [true] },
},
required: ['rooms', 'success'],
additionalProperties: false,
});
API.v1.post(
'teams.addRooms',
{
authRequired: true,
body: isTeamsAddRoomsProps,
response: {
200: roomsResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasPermissionAsync(this.user, 'move-room-to-team', team.roomId))) {
return API.v1.forbidden('error-no-permission-team-channel');
}
const { rooms } = this.bodyParams;
const validRooms = await Team.addRooms(this.userId, rooms, team._id);
return API.v1.success({ rooms: validRooms });
},
);
API.v1.post(
'teams.removeRoom',
{
authRequired: true,
body: isTeamsRemoveRoomProps,
response: {
200: roomResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasPermissionAsync(this.user, 'remove-team-channel', team.roomId))) {
return API.v1.forbidden();
}
const canRemoveAny = !!(await hasPermissionAsync(this.user, 'view-all-team-channels', team.roomId));
const { roomId } = this.bodyParams;
const room = await Team.removeRoom(this.userId, roomId, team._id, canRemoveAny);
return API.v1.success({ room });
},
);
API.v1.post(
'teams.updateRoom',
{
authRequired: true,
body: isTeamsUpdateRoomProps,
response: {
200: roomResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { roomId, isDefault } = this.bodyParams;
const team = await Team.getOneByRoomId(roomId);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasPermissionAsync(this.user, 'edit-team-channel', team.roomId))) {
return API.v1.forbidden();
}
const canUpdateAny = !!(await hasPermissionAsync(this.user, 'view-all-team-channels', team.roomId));
if (settings.get('ABAC_Enabled') && isDefault) {
const room = await Rooms.findOneByIdAndType(roomId, 'p', { projection: { abacAttributes: 1 } });
if (room?.abacAttributes?.length) {
return API.v1.failure('error-room-is-abac-managed');
}
}
const room = await Team.updateRoom(this.userId, roomId, isDefault, canUpdateAny);
return API.v1.success({ room });
},
);
const paginatedRoomsResponseSchema = ajv.compile<{ rooms: object[]; total: number; count: number; offset: number }>({
type: 'object',
properties: {
rooms: { type: 'array', items: { type: 'object' } },
total: { type: 'number' },
count: { type: 'number' },
offset: { type: 'number' },
success: { type: 'boolean', enum: [true] },
},
required: ['rooms', 'total', 'count', 'offset', 'success'],
additionalProperties: false,
});
API.v1.get(
'teams.listRooms',
{
authRequired: true,
query: isTeamsListRoomsProps,
response: {
200: paginatedRoomsResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const { filter, type } = this.queryParams;
const { offset, count } = await getPaginationItems(this.queryParams);
const team = await getTeamByIdOrName(this.queryParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
const allowPrivateTeam: boolean = await hasPermissionAsync(this.user, 'view-all-teams', team.roomId);
const getAllRooms = await hasPermissionAsync(this.user, 'view-all-team-channels', team.roomId);
const listFilter = {
name: filter ?? undefined,
isDefault: type === 'autoJoin',
getAllRooms,
allowPrivateTeam,
};
const { records, total } = await Team.listRooms(this.userId, team._id, listFilter, {
offset,
count,
});
return API.v1.success({
rooms: records,
total,
count: records.length,
offset,
});
},
);
API.v1.get(
'teams.listRoomsOfUser',
{
authRequired: true,
query: isTeamsListRoomsOfUserProps,
response: {
200: paginatedRoomsResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { offset, count } = await getPaginationItems(this.queryParams);
const team = await getTeamByIdOrName(this.queryParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
const allowPrivateTeam = await hasPermissionAsync(this.user, 'view-all-teams', team.roomId);
const { userId, canUserDelete } = this.queryParams;
if (!(this.userId === userId || (await hasPermissionAsync(this.user, 'view-all-team-channels', team.roomId)))) {
return API.v1.forbidden();
}
const booleanCanUserDelete = canUserDelete === 'true';
const { records, total } = await Team.listRoomsOfUser(this.userId, team._id, userId, allowPrivateTeam, booleanCanUserDelete, {
offset,
count,
});
return API.v1.success({
rooms: records,
total,
count: records.length,
offset: 0,
});
},
);
const getTeamByIdOrNameOrParentRoom = async (
params: { teamId: string } | { teamName: string } | { roomId: string },
): Promise<Pick<ITeam, 'type' | 'roomId' | '_id'> | null> => {
if ('teamId' in params && params.teamId) {
return Team.getOneById<ITeam>(params.teamId, { projection: { type: 1, roomId: 1 } });
}
if ('teamName' in params && params.teamName) {
return Team.getOneByName(params.teamName, { projection: { type: 1, roomId: 1 } });
}
if ('roomId' in params && params.roomId) {
return Team.getOneByRoomId(params.roomId, { projection: { type: 1, roomId: 1 } });
}
return null;
};
// This should accept a teamId, filter (search by name on rooms collection) and sort/pagination
// should return a list of rooms/discussions from the team. the discussions will only be returned from the main room
API.v1.get(
'teams.listChildren',
{
authRequired: true,
query: isTeamsListChildrenProps,
response: {
200: ajv.compile<{ data: object[]; total: number; offset: number; count: number }>({
type: 'object',
properties: {
data: { type: 'array', items: { type: 'object' } },
total: { type: 'number' },
offset: { type: 'number' },
count: { type: 'number' },
success: { type: 'boolean', enum: [true] },
},
required: ['data', 'total', 'offset', 'count', 'success'],
additionalProperties: false,
}),
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
404: validateNotFoundErrorResponse,
},
},
async function action() {
const { offset, count } = await getPaginationItems(this.queryParams);
const { sort } = await this.parseJsonQuery();
const { filter, type } = this.queryParams;
const team = await getTeamByIdOrNameOrParentRoom(this.queryParams);
if (!team) {
return API.v1.notFound();
}
const result = await Team.listChildren(this.userId, team, filter, type, sort, offset, count);
return API.v1.success({ data: result.data, total: result.total, offset, count });
},
);
API.v1.get(
'teams.members',
{
authRequired: true,
query: isTeamsMembersProps,
response: {
200: ajv.compile<{ members: object[]; total: number; count: number; offset: number }>({
type: 'object',
properties: {
members: { type: 'array', items: { type: 'object' } },
total: { type: 'number' },
count: { type: 'number' },
offset: { type: 'number' },
success: { type: 'boolean', enum: [true] },
},
required: ['members', 'total', 'count', 'offset', 'success'],
additionalProperties: false,
}),
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const { offset, count } = await getPaginationItems(this.queryParams);
const { status, username, name } = this.queryParams;
const team = await getTeamByIdOrName(this.queryParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
const canSeeAllMembers = await hasPermissionAsync(this.user, 'view-all-teams', team.roomId);
const hidden = await getUsersHiddenFrom(this.userId);
const query: Record<string, unknown> = {};
if (username) {
query.username = new RegExp(escapeRegExp(username), 'i');
}
if (name) {
query.name = new RegExp(escapeRegExp(name), 'i');
}
if (status) {
Object.assign(query, effectiveStatusFilter(status as UserStatus[], hidden));
}
const { records, total } = await Team.members(this.userId, team._id, canSeeAllMembers, { offset, count }, query);
return API.v1.success({
members: hidden
? records.map((record) => (hidden.has(record.user._id) ? { ...record, user: redactStatus(record.user) } : record))
: records,
total,
count: records.length,
offset,
});
},
);
API.v1.post(
'teams.addMembers',
{
authRequired: true,
body: isTeamsAddMembersProps,
response: {
200: successResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { bodyParams } = this;
const { members } = bodyParams;
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasAtLeastOnePermissionAsync(this.user, ['add-team-member', 'edit-team-member'], team.roomId))) {
return API.v1.forbidden();
}
await Team.addMembers(this.userId, team._id, members);
return API.v1.success();
},
);
API.v1.post(
'teams.updateMember',
{
authRequired: true,
body: isTeamsUpdateMemberProps,
response: {
200: successResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { bodyParams } = this;
const { member } = bodyParams;
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasAtLeastOnePermissionAsync(this.user, ['edit-team-member'], team.roomId))) {
return API.v1.forbidden();
}
await Team.updateMember(team._id, member);
return API.v1.success();
},
);
API.v1.post(
'teams.removeMember',
{
authRequired: true,
body: isTeamsRemoveMemberProps,
response: {
200: successResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { bodyParams } = this;
const { userId, rooms } = bodyParams;
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasAtLeastOnePermissionAsync(this.user, ['edit-team-member'], team.roomId))) {
return API.v1.forbidden();
}
const user = await Users.findOneActiveById(userId, {});
if (!user) {
return API.v1.failure('invalid-user');
}
if (!(await Team.removeMembers(this.userId, team._id, [{ userId }]))) {
return API.v1.failure('could-not-remove-member');
}
if (rooms?.length) {
const roomsFromTeam: string[] = await Team.getMatchingTeamRooms(team._id, rooms);
await Promise.all(
roomsFromTeam.map((rid) =>
removeUserFromRoom(rid, user, {
byUser: this.user,
}),
),
);
}
return API.v1.success();
},
);
API.v1.post(
'teams.leave',
{
authRequired: true,
body: isTeamsLeaveProps,
response: {
200: successResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const { rooms = [] } = this.bodyParams;
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
await Team.removeMembers(this.userId, team._id, [
{
userId: this.userId,
},
]);
if (rooms.length) {
const roomsFromTeam: string[] = await Team.getMatchingTeamRooms(team._id, rooms);
await Promise.all(roomsFromTeam.map((rid) => removeUserFromRoom(rid, this.user)));
}
return API.v1.success();
},
);
API.v1.get(
'teams.info',
{
authRequired: true,
query: isTeamsInfoProps,
response: {
200: ajv.compile<{ teamInfo: ITeam }>({
type: 'object',
properties: {
teamInfo: { type: 'object' },
success: { type: 'boolean', enum: [true] },
},
required: ['teamInfo', 'success'],
additionalProperties: false,
}),
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const teamInfo = await getTeamByIdOrName(this.queryParams);
if (!teamInfo) {
return API.v1.failure('Team not found');
}
const room = await Rooms.findOneById(teamInfo.roomId);
if (!room) {
return API.v1.failure('Room not found');
}
const canViewInfo = (await canAccessRoomAsync(room, { _id: this.userId })) || (await hasPermissionAsync(this.user, 'view-all-teams'));
if (!canViewInfo) {
return API.v1.forbidden();
}
return API.v1.success({ teamInfo });
},
);
API.v1.post(
'teams.delete',
{
authRequired: true,
body: isTeamsDeleteProps,
response: {
200: successResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { roomsToRemove = [] } = this.bodyParams;
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasPermissionAsync(this.user, 'delete-team', team.roomId))) {
return API.v1.forbidden();
}
await eraseTeam(this.user, team, roomsToRemove);
return API.v1.success();
},
);
API.v1.get(
'teams.autocomplete',
{
authRequired: true,
query: isTeamsAutocompleteProps,
response: {
200: ajv.compile<{ teams: ITeamAutocompleteResult[] }>({
type: 'object',
properties: {
teams: { type: 'array', items: { type: 'object' } },
success: { type: 'boolean', enum: [true] },
},
required: ['teams', 'success'],
additionalProperties: false,
}),
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const { name } = this.queryParams;
const teams = await Team.autocomplete(this.userId, name);
return API.v1.success({ teams });
},
);
API.v1.post(
'teams.update',
{
authRequired: true,
body: isTeamsUpdateProps,
response: {
200: successResponseSchema,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { data } = this.bodyParams;
const team = await getTeamByIdOrName(this.bodyParams);
if (!team) {
return API.v1.failure('team-does-not-exist');
}
if (!(await hasPermissionAsync(this.user, 'edit-team', team.roomId))) {
return API.v1.forbidden();
}
await Team.update(this.userId, team._id, data);
return API.v1.success();
},
);
export type TeamsEndpoints = ExtractRoutesFromAPI<typeof teamsEndpoints>;
declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
interface Endpoints extends TeamsEndpoints {}
}