11import { Users } from '@rocket.chat/models' ;
2- import { isLivechatUsersManagerGETProps , isPOSTLivechatUsersTypeProps } from '@rocket.chat/rest-typings' ;
3- import { check } from 'meteor/check' ;
2+ import { ajv , validateBadRequestErrorResponse , validateUnauthorizedErrorResponse , validateForbiddenErrorResponse } from '@rocket.chat/rest-typings' ;
43
54import { API } from '../../../../api/server' ;
5+ import type { ExtractRoutesFromAPI } from '../../../../api/server/ApiClass' ;
66import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems' ;
77import { hasAtLeastOnePermissionAsync } from '../../../../authorization/server/functions/hasPermission' ;
88import { findAgents , findManagers } from '../../../server/api/lib/users' ;
99import { addManager , addAgent , removeAgent , removeManager } from '../../../server/lib/omni-users' ;
1010
11+ // --- Local types and AJV schemas (moved from rest-typings) ---
12+
13+ type LivechatUsersManagerGETLocal = {
14+ text ?: string ;
15+ fields ?: string ;
16+ onlyAvailable ?: boolean ;
17+ excludeId ?: string ;
18+ showIdleAgents ?: boolean ;
19+ count ?: number ;
20+ offset ?: number ;
21+ sort ?: string ;
22+ query ?: string ;
23+ } ;
24+
25+ const LivechatUsersManagerGETLocalSchema = {
26+ type : 'object' ,
27+ properties : {
28+ text : { type : 'string' , nullable : true } ,
29+ onlyAvailable : { type : 'boolean' , nullable : true } ,
30+ excludeId : { type : 'string' , nullable : true } ,
31+ showIdleAgents : { type : 'boolean' , nullable : true } ,
32+ count : { type : 'number' , nullable : true } ,
33+ offset : { type : 'number' , nullable : true } ,
34+ sort : { type : 'string' , nullable : true } ,
35+ query : { type : 'string' , nullable : true } ,
36+ fields : { type : 'string' , nullable : true } ,
37+ } ,
38+ required : [ ] ,
39+ additionalProperties : false ,
40+ } ;
41+
42+ const isLivechatUsersManagerGETLocal = ajv . compile < LivechatUsersManagerGETLocal > ( LivechatUsersManagerGETLocalSchema ) ;
43+
44+ type POSTLivechatUsersTypeLocal = {
45+ username : string ;
46+ } ;
47+
48+ const POSTLivechatUsersTypeLocalSchema = {
49+ type : 'object' ,
50+ properties : {
51+ username : { type : 'string' } ,
52+ } ,
53+ required : [ 'username' ] ,
54+ additionalProperties : false ,
55+ } ;
56+
57+ const isPOSTLivechatUsersTypeLocal = ajv . compile < POSTLivechatUsersTypeLocal > ( POSTLivechatUsersTypeLocalSchema ) ;
58+
59+ // --- Response schemas ---
60+
61+ const paginatedUsersResponseSchema = ajv . compile < {
62+ users : object [ ] ;
63+ count : number ;
64+ offset : number ;
65+ total : number ;
66+ } > ( {
67+ type : 'object' ,
68+ properties : {
69+ // ILivechatAgent is not registered in typia (extends IUser with livechat-specific fields),
70+ // so we use { type: 'object' } as a fallback.
71+ users : { type : 'array' , items : { type : 'object' } } ,
72+ count : { type : 'number' } ,
73+ offset : { type : 'number' } ,
74+ total : { type : 'number' } ,
75+ success : { type : 'boolean' , enum : [ true ] } ,
76+ } ,
77+ required : [ 'users' , 'count' , 'offset' , 'total' , 'success' ] ,
78+ additionalProperties : false ,
79+ } ) ;
80+
81+ const postUserResponseSchema = ajv . compile < { user : object } > ( {
82+ type : 'object' ,
83+ properties : {
84+ user : { type : 'object' } ,
85+ success : { type : 'boolean' , enum : [ true ] } ,
86+ } ,
87+ required : [ 'user' , 'success' ] ,
88+ additionalProperties : false ,
89+ } ) ;
90+
91+ const successOnlyResponseSchema = ajv . compile < void > ( {
92+ type : 'object' ,
93+ properties : {
94+ success : { type : 'boolean' , enum : [ true ] } ,
95+ } ,
96+ required : [ 'success' ] ,
97+ additionalProperties : false ,
98+ } ) ;
99+
100+ const getUserByIdResponseSchema = ajv . compile < { user : object | null } > ( {
101+ type : 'object' ,
102+ properties : {
103+ user : { type : [ 'object' , 'null' ] } ,
104+ success : { type : 'boolean' , enum : [ true ] } ,
105+ } ,
106+ required : [ 'user' , 'success' ] ,
107+ additionalProperties : false ,
108+ } ) ;
109+
11110const emptyStringArray : string [ ] = [ ] ;
12111
13- API . v1 . addRoute (
14- 'livechat/users/:type' ,
15- {
16- authRequired : true ,
17- permissionsRequired : {
18- 'POST' : [ 'view-livechat-manager' ] ,
19- '*' : emptyStringArray ,
112+ const livechatUsersEndpoints = API . v1
113+ . get (
114+ 'livechat/users/:type' ,
115+ {
116+ authRequired : true ,
117+ permissionsRequired : emptyStringArray ,
118+ query : isLivechatUsersManagerGETLocal ,
119+ response : {
120+ 200 : paginatedUsersResponseSchema ,
121+ 400 : validateBadRequestErrorResponse ,
122+ 401 : validateUnauthorizedErrorResponse ,
123+ 403 : validateForbiddenErrorResponse ,
124+ } ,
20125 } ,
21- validateParams : {
22- GET : isLivechatUsersManagerGETProps ,
23- POST : isPOSTLivechatUsersTypeProps ,
24- } ,
25- } ,
26- {
27- async get ( ) {
28- check ( this . urlParams , {
29- type : String ,
30- } ) ;
126+ async function action ( ) {
31127 const { offset, count } = await getPaginationItems ( this . queryParams ) ;
32128 const { sort } = await this . parseJsonQuery ( ) ;
33129 const { text } = this . queryParams ;
34130
35131 if ( this . urlParams . type === 'agent' ) {
36132 if ( ! ( await hasAtLeastOnePermissionAsync ( this . userId , [ 'transfer-livechat-guest' , 'edit-omnichannel-contact' ] ) ) ) {
37- return API . v1 . forbidden ( ) ;
133+ return API . v1 . forbidden ( 'error-not-authorized' ) ;
38134 }
39135
40- const { onlyAvailable, excludeId, showIdleAgents } = this . queryParams ;
136+ const { onlyAvailable = false , excludeId, showIdleAgents } = this . queryParams ;
41137 return API . v1 . success (
42138 await findAgents ( {
43139 text,
@@ -54,7 +150,7 @@ API.v1.addRoute(
54150 }
55151 if ( this . urlParams . type === 'manager' ) {
56152 if ( ! ( await hasAtLeastOnePermissionAsync ( this . userId , [ 'view-livechat-manager' ] ) ) ) {
57- return API . v1 . forbidden ( ) ;
153+ return API . v1 . forbidden ( 'error-not-authorized' ) ;
58154 }
59155
60156 return API . v1 . success (
@@ -70,7 +166,21 @@ API.v1.addRoute(
70166 }
71167 throw new Error ( 'Invalid type' ) ;
72168 } ,
73- async post ( ) {
169+ )
170+ . post (
171+ 'livechat/users/:type' ,
172+ {
173+ authRequired : true ,
174+ permissionsRequired : [ 'view-livechat-manager' ] ,
175+ body : isPOSTLivechatUsersTypeLocal ,
176+ response : {
177+ 200 : postUserResponseSchema ,
178+ 400 : validateBadRequestErrorResponse ,
179+ 401 : validateUnauthorizedErrorResponse ,
180+ 403 : validateForbiddenErrorResponse ,
181+ } ,
182+ } ,
183+ async function action ( ) {
74184 if ( this . urlParams . type === 'agent' ) {
75185 const user = await addAgent ( this . bodyParams . username ) ;
76186 if ( user ) {
@@ -87,14 +197,21 @@ API.v1.addRoute(
87197
88198 return API . v1 . failure ( ) ;
89199 } ,
90- } ,
91- ) ;
92-
93- API . v1 . addRoute (
94- 'livechat/users/:type/:_id' ,
95- { authRequired : true , permissionsRequired : [ 'view-livechat-manager' ] } ,
96- {
97- async get ( ) {
200+ )
201+ . get (
202+ 'livechat/users/:type/:_id' ,
203+ {
204+ authRequired : true ,
205+ permissionsRequired : [ 'view-livechat-manager' ] ,
206+ query : undefined ,
207+ response : {
208+ 200 : getUserByIdResponseSchema ,
209+ 400 : validateBadRequestErrorResponse ,
210+ 401 : validateUnauthorizedErrorResponse ,
211+ 403 : validateForbiddenErrorResponse ,
212+ } ,
213+ } ,
214+ async function action ( ) {
98215 if ( ! [ 'agent' , 'manager' ] . includes ( this . urlParams . type ) ) {
99216 throw new Error ( 'Invalid type' ) ;
100217 }
@@ -107,7 +224,21 @@ API.v1.addRoute(
107224 // TODO: throw error instead of returning null
108225 return API . v1 . success ( { user } ) ;
109226 } ,
110- async delete ( ) {
227+ )
228+ . delete (
229+ 'livechat/users/:type/:_id' ,
230+ {
231+ authRequired : true ,
232+ permissionsRequired : [ 'view-livechat-manager' ] ,
233+ query : undefined ,
234+ response : {
235+ 200 : successOnlyResponseSchema ,
236+ 400 : validateBadRequestErrorResponse ,
237+ 401 : validateUnauthorizedErrorResponse ,
238+ 403 : validateForbiddenErrorResponse ,
239+ } ,
240+ } ,
241+ async function action ( ) {
111242 if ( this . urlParams . type === 'agent' ) {
112243 if ( await removeAgent ( this . urlParams . _id ) ) {
113244 return API . v1 . success ( ) ;
@@ -120,7 +251,15 @@ API.v1.addRoute(
120251 throw new Error ( 'Invalid type' ) ;
121252 }
122253
123- return API . v1 . failure ( ) ;
254+ return API . v1 . failure ( 'error-removing-user' ) ;
124255 } ,
125- } ,
126- ) ;
256+ ) ;
257+
258+ // --- Type augmentation ---
259+
260+ type LivechatUsersEndpoints = ExtractRoutesFromAPI < typeof livechatUsersEndpoints > ;
261+
262+ declare module '@rocket.chat/rest-typings' {
263+ // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
264+ interface Endpoints extends LivechatUsersEndpoints { }
265+ }
0 commit comments