|
1 | 1 | import { MeteorError, Team, api, Calendar } from '@rocket.chat/core-services'; |
2 | | -import type { IExportOperation, ILoginToken, IPersonalAccessToken, IUser, UserStatus } from '@rocket.chat/core-typings'; |
| 2 | +import type { IExportOperation, ILoginToken, ISubscription, IPersonalAccessToken, IUser, UserStatus } from '@rocket.chat/core-typings'; |
3 | 3 | import { Users, Subscriptions, Sessions } from '@rocket.chat/models'; |
4 | 4 | import { |
5 | 5 | isUserCreateParamsPOST, |
@@ -425,92 +425,6 @@ API.v1.addRoute( |
425 | 425 | }, |
426 | 426 | ); |
427 | 427 |
|
428 | | -const usersInfoEndpoint = API.v1.get( |
429 | | - 'users.info', |
430 | | - { |
431 | | - authRequired: true, |
432 | | - query: ajv.compile< |
433 | | - | { userId: string; username?: never; importId?: never; includeUserRooms?: string } |
434 | | - | { username: string; userId?: never; importId?: never; includeUserRooms?: string } |
435 | | - | { importId: string; userId?: never; username?: never; includeUserRooms?: string } |
436 | | - >({ |
437 | | - anyOf: [ |
438 | | - { |
439 | | - type: 'object', |
440 | | - properties: { |
441 | | - userId: { type: 'string' }, |
442 | | - includeUserRooms: { type: 'string' }, |
443 | | - }, |
444 | | - required: ['userId'], |
445 | | - additionalProperties: false, |
446 | | - }, |
447 | | - { |
448 | | - type: 'object', |
449 | | - properties: { |
450 | | - username: { type: 'string' }, |
451 | | - includeUserRooms: { type: 'string' }, |
452 | | - }, |
453 | | - required: ['username'], |
454 | | - additionalProperties: false, |
455 | | - }, |
456 | | - { |
457 | | - type: 'object', |
458 | | - properties: { |
459 | | - importId: { type: 'string' }, |
460 | | - includeUserRooms: { type: 'string' }, |
461 | | - }, |
462 | | - required: ['importId'], |
463 | | - additionalProperties: false, |
464 | | - }, |
465 | | - ], |
466 | | - }), |
467 | | - response: { |
468 | | - 400: validateBadRequestErrorResponse, |
469 | | - 401: validateUnauthorizedErrorResponse, |
470 | | - 200: ajv.compile<{ user: IUser; success: true }>({ |
471 | | - type: 'object', |
472 | | - properties: { |
473 | | - user: { type: 'object' }, |
474 | | - success: { type: 'boolean', enum: [true] }, |
475 | | - }, |
476 | | - required: ['user', 'success'], |
477 | | - additionalProperties: false, |
478 | | - }), |
479 | | - }, |
480 | | - }, |
481 | | - async function action() { |
482 | | - const searchTerms: [string, 'id' | 'username' | 'importId'] | false = |
483 | | - ('userId' in this.queryParams && !!this.queryParams.userId && [this.queryParams.userId, 'id']) || |
484 | | - ('username' in this.queryParams && !!this.queryParams.username && [this.queryParams.username, 'username']) || |
485 | | - ('importId' in this.queryParams && !!this.queryParams.importId && [this.queryParams.importId, 'importId']); |
486 | | - |
487 | | - if (!searchTerms) { |
488 | | - return API.v1.failure('Invalid search query.'); |
489 | | - } |
490 | | - |
491 | | - const user = await getFullUserDataByIdOrUsernameOrImportId(this.userId, ...searchTerms); |
492 | | - |
493 | | - if (!user) { |
494 | | - return API.v1.failure('User not found.'); |
495 | | - } |
496 | | - |
497 | | - const myself = user._id === this.userId; |
498 | | - if (this.queryParams.includeUserRooms === 'true' && (myself || (await hasPermissionAsync(this.userId, 'view-other-user-channels')))) { |
499 | | - return API.v1.success({ |
500 | | - user: { |
501 | | - ...user, |
502 | | - rooms: await Subscriptions.findByUserId(user._id, { |
503 | | - projection: { rid: 1, name: 1, t: 1, roles: 1, unread: 1, federated: 1 }, |
504 | | - sort: { t: 1, name: 1 }, |
505 | | - }).toArray(), |
506 | | - }, |
507 | | - }); |
508 | | - } |
509 | | - |
510 | | - return API.v1.success({ user }); |
511 | | - }, |
512 | | -); |
513 | | - |
514 | 428 | API.v1.addRoute( |
515 | 429 | 'users.list', |
516 | 430 | { |
@@ -788,6 +702,69 @@ API.v1.addRoute( |
788 | 702 | }, |
789 | 703 | ); |
790 | 704 |
|
| 705 | +type UsersInfoParamsGet = ({ userId: string } | { username: string } | { importId: string }) & { |
| 706 | + fields?: string; |
| 707 | + includeUserRooms?: string; |
| 708 | +}; |
| 709 | + |
| 710 | +const UsersInfoParamsGetSchema = { |
| 711 | + anyOf: [ |
| 712 | + { |
| 713 | + type: 'object', |
| 714 | + properties: { |
| 715 | + userId: { |
| 716 | + type: 'string', |
| 717 | + }, |
| 718 | + includeUserRooms: { |
| 719 | + type: 'string', |
| 720 | + }, |
| 721 | + fields: { |
| 722 | + type: 'string', |
| 723 | + nullable: true, |
| 724 | + }, |
| 725 | + }, |
| 726 | + required: ['userId'], |
| 727 | + additionalProperties: false, |
| 728 | + }, |
| 729 | + { |
| 730 | + type: 'object', |
| 731 | + properties: { |
| 732 | + username: { |
| 733 | + type: 'string', |
| 734 | + }, |
| 735 | + includeUserRooms: { |
| 736 | + type: 'string', |
| 737 | + }, |
| 738 | + fields: { |
| 739 | + type: 'string', |
| 740 | + nullable: true, |
| 741 | + }, |
| 742 | + }, |
| 743 | + required: ['username'], |
| 744 | + additionalProperties: false, |
| 745 | + }, |
| 746 | + { |
| 747 | + type: 'object', |
| 748 | + properties: { |
| 749 | + importId: { |
| 750 | + type: 'string', |
| 751 | + }, |
| 752 | + includeUserRooms: { |
| 753 | + type: 'string', |
| 754 | + }, |
| 755 | + fields: { |
| 756 | + type: 'string', |
| 757 | + nullable: true, |
| 758 | + }, |
| 759 | + }, |
| 760 | + required: ['importId'], |
| 761 | + additionalProperties: false, |
| 762 | + }, |
| 763 | + ], |
| 764 | +}; |
| 765 | + |
| 766 | +const isUsersInfoParamsGetProps = ajv.compile<UsersInfoParamsGet>(UsersInfoParamsGetSchema); |
| 767 | + |
791 | 768 | const usersEndpoints = API.v1 |
792 | 769 | .post( |
793 | 770 | 'users.createToken', |
@@ -913,7 +890,99 @@ const usersEndpoints = API.v1 |
913 | 890 |
|
914 | 891 | return API.v1.success({ suggestions }); |
915 | 892 | }, |
916 | | - ); |
| 893 | + ) |
| 894 | + .get( |
| 895 | + 'users.info', |
| 896 | + { |
| 897 | + authRequired: true, |
| 898 | + query: isUsersInfoParamsGetProps, |
| 899 | + response: { |
| 900 | + 400: validateBadRequestErrorResponse, |
| 901 | + 401: validateUnauthorizedErrorResponse, |
| 902 | + |
| 903 | + 200: ajv.compile<{ |
| 904 | + user: IUser & { rooms?: Pick<ISubscription, 'rid' | 'name' | 't' | 'roles' | 'unread'> & { federated?: boolean }[] }; |
| 905 | + success: true; |
| 906 | + }>({ |
| 907 | + type: 'object', |
| 908 | + properties: { |
| 909 | + user: { |
| 910 | + allOf: [ |
| 911 | + { $ref: '#/components/schemas/IUser' }, |
| 912 | + { |
| 913 | + type: 'object', |
| 914 | + properties: { |
| 915 | + rooms: { |
| 916 | + type: 'array', |
| 917 | + items: { |
| 918 | + type: 'object', |
| 919 | + properties: { |
| 920 | + rid: { type: 'string' }, |
| 921 | + name: { type: 'string' }, |
| 922 | + t: { type: 'string' }, |
| 923 | + roles: { type: 'array', items: { type: 'string' } }, |
| 924 | + unread: { type: 'number' }, |
| 925 | + federated: { type: 'boolean' }, |
| 926 | + }, |
| 927 | + required: ['rid', 't', 'unread', 'name'], |
| 928 | + additionalProperties: false, |
| 929 | + }, |
| 930 | + }, |
| 931 | + }, |
| 932 | + additionalProperties: true, |
| 933 | + }, |
| 934 | + ], |
| 935 | + }, |
| 936 | + success: { type: 'boolean', enum: [true] }, |
| 937 | + }, |
| 938 | + required: ['user', 'success'], |
| 939 | + additionalProperties: false, |
| 940 | + }), |
| 941 | + }, |
| 942 | + }, |
| 943 | + async function action() { |
| 944 | + const searchTerms: [string, 'id' | 'username' | 'importId'] | false = |
| 945 | + ('userId' in this.queryParams && !!this.queryParams.userId && [this.queryParams.userId, 'id']) || |
| 946 | + ('username' in this.queryParams && !!this.queryParams.username && [this.queryParams.username, 'username']) || |
| 947 | + ('importId' in this.queryParams && !!this.queryParams.importId && [this.queryParams.importId, 'importId']); |
| 948 | + |
| 949 | + if (!searchTerms) { |
| 950 | + return API.v1.failure('Invalid search query.'); |
| 951 | + } |
| 952 | + |
| 953 | + const user = await getFullUserDataByIdOrUsernameOrImportId(this.userId, ...searchTerms); |
| 954 | + |
| 955 | + if (!user) { |
| 956 | + return API.v1.failure('User not found.'); |
| 957 | + } |
| 958 | + const myself = user._id === this.userId; |
| 959 | + if (this.queryParams.includeUserRooms === 'true' && (myself || (await hasPermissionAsync(this.userId, 'view-other-user-channels')))) { |
| 960 | + return API.v1.success({ |
| 961 | + user: { |
| 962 | + ...user, |
| 963 | + rooms: await Subscriptions.findByUserId(user._id, { |
| 964 | + projection: { |
| 965 | + rid: 1, |
| 966 | + name: 1, |
| 967 | + t: 1, |
| 968 | + roles: 1, |
| 969 | + unread: 1, |
| 970 | + federated: 1, |
| 971 | + }, |
| 972 | + sort: { |
| 973 | + t: 1, |
| 974 | + name: 1, |
| 975 | + }, |
| 976 | + }).toArray(), |
| 977 | + }, |
| 978 | + }); |
| 979 | + } |
| 980 | + |
| 981 | + return API.v1.success({ |
| 982 | + user, |
| 983 | + }); |
| 984 | + }, |
| 985 | +); |
917 | 986 |
|
918 | 987 | API.v1.addRoute( |
919 | 988 | 'users.getPreferences', |
@@ -1591,8 +1660,7 @@ settings.watch<number>('Rate_Limiter_Limit_RegisterUser', (value) => { |
1591 | 1660 | }); |
1592 | 1661 |
|
1593 | 1662 | type UsersEndpoints = ExtractRoutesFromAPI<typeof usersEndpoints>; |
1594 | | -type UsersInfoEndpoint = ExtractRoutesFromAPI<typeof usersInfoEndpoint>; |
1595 | 1663 | declare module '@rocket.chat/rest-typings' { |
1596 | 1664 | // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface |
1597 | | - interface Endpoints extends UsersEndpoints, UsersInfoEndpoint {} |
| 1665 | + interface Endpoints extends UsersEndpoints {} |
1598 | 1666 | } |
0 commit comments