universo-platform-3d
437 строк · 13.3 Кб
1import {
2Body,
3Controller,
4Delete,
5Get,
6NotFoundException,
7Param,
8Patch,
9Post,
10Query,
11UnauthorizedException,
12UploadedFile,
13UseInterceptors,
14UsePipes,
15ValidationPipe
16} from '@nestjs/common'
17import {
18ApiBody,
19ApiConsumes,
20ApiCreatedResponse,
21ApiOkResponse,
22ApiParam
23} from '@nestjs/swagger'
24import { FirebaseTokenAuthGuard } from '../auth/auth.guard'
25import { User, UserPublicData } from './user.schema'
26import { Friend, UserService } from './user.service'
27import { UserToken } from '../auth/get-user.decorator'
28import { FileUploadPublicApiResponse } from '../util/file-upload/file-upload'
29import { FileInterceptor } from '@nestjs/platform-express'
30import {
31AddRpmAvatarUrlDto,
32UpdateUserAvatarDto,
33RemoveRpmAvatarUrlDto,
34UpdateUserAvatarTypeDto,
35UpdateUserDeepLinkDto,
36UpdateUserProfileDto,
37UpdateUserTermsDto,
38UpdateUserTutorialDto,
39UpsertUserEntityActionDto,
40AddUserCartItemToUserCartDto
41} from './dto/update-user.dto'
42import { PublicFirebaseAuthNotRequired } from '../auth/public.decorator'
43import { CreateUserAccessKeyDto } from './dto/create-user-access-key.dto'
44import { SubmitUserAccessKeyDto } from './dto/submit-user-access-key.dto'
45import { UserEntityActionId, UserId } from '../util/mongo-object-id-helpers'
46import { PREMIUM_ACCESS } from '../option-sets/premium-tiers'
47import { AddUserSidebarTagDto } from './dto/add-user-sidebar-tag.dto'
48import { UpdateUserSidebarTagsDto } from './dto/update-user-sidebar-tags.dto'
49import { UserRecents } from './models/user-recents.schema'
50
51@UsePipes(new ValidationPipe({ whitelist: true }))
52@Controller('user')
53@FirebaseTokenAuthGuard()
54export class UserController {
55constructor(private readonly userService: UserService) {}
56
57/*****************************
58PUBLICLY ACCESSIBLE ENDPOINTS
59****************************/
60
61/**
62* @description Retrieves a Users public data
63* id prefix added to prevent wildcard route clashes with file method order
64*/
65@PublicFirebaseAuthNotRequired()
66@Get('id/:id')
67@ApiParam({ name: 'id', type: 'string', required: true })
68public findOne(@Param('id') id: string): Promise<UserPublicData> {
69return this.userService.findPublicUser(id)
70}
71
72/** @description Retrieves a Users public profile data including populated
73* fields like public assets and public groups */
74@PublicFirebaseAuthNotRequired()
75@Get(':id/public-profile')
76@ApiParam({ name: 'id', type: 'string', required: true })
77@ApiOkResponse({ type: UserPublicData })
78public findOneWithPublicProfile(@Param('id') id: string): Promise<User> {
79return this.userService.findPublicUserFullProfile(id)
80}
81
82/** @description Retrieves a collection of Users public data */
83@PublicFirebaseAuthNotRequired()
84@Get('search')
85@ApiOkResponse({ type: [UserPublicData] })
86public search(@Query('query') query: string): Promise<UserPublicData[]> {
87return this.userService.searchForPublicUsers(query)
88}
89
90/***********************
91AUTH REQUIRED ENDPOINTS
92**********************/
93
94/** @description /me gets the current user by checking their uid on the JWT */
95@Get('me')
96@ApiOkResponse({ type: User })
97public async getCurrentUser(@UserToken('uid') id: string) {
98const user = await this.userService.findUser(id)
99if (!user) {
100throw new NotFoundException()
101}
102return user
103}
104
105@Get('recents/me')
106@ApiOkResponse({ type: UserRecents })
107public async getUserRecents(@UserToken('user_id') userId: UserId) {
108return await this.userService.getUserRecents(userId)
109}
110
111/**
112* TODO - Would be nice to have 1 file upload endpoint that can handle all entity types and paths
113* move logic to service
114*/
115@Post('/upload/public')
116@FirebaseTokenAuthGuard() // 2023-03-08 00:28:04 can this line be removed since the controller has it?
117@ApiConsumes('multipart/form-data')
118@ApiBody({ schema: { type: 'file' } })
119@ApiCreatedResponse({ type: FileUploadPublicApiResponse })
120@UseInterceptors(FileInterceptor('file'))
121public async uploadPublic(
122@UserToken('user_id') userId: string,
123@UploadedFile() file: Express.Multer.File
124) {
125return await this.userService.uploadProfileImage({ file, userId })
126}
127
128@Patch('profile')
129@ApiOkResponse({ type: User })
130public updateProfile(
131@UserToken('uid') id: string,
132@Body() dto: UpdateUserProfileDto
133) {
134return this.userService.updateUserProfileAdmin(id, dto)
135}
136
137@Patch('tutorial')
138@ApiOkResponse({ type: User })
139public updateUserTutorial(
140@UserToken('uid') id: UserId,
141@Body() dto: UpdateUserTutorialDto
142) {
143return this.userService.updateUserTutorial(id, dto)
144}
145
146@Patch('deep-link')
147public updateDeepLink(
148@UserToken('uid') id: string,
149@Body() dto: UpdateUserDeepLinkDto
150) {
151return this.userService.updateDeepLink(id, dto)
152}
153
154@Patch('avatar')
155public updateAvatar(
156@UserToken('uid') id: string,
157@Body() dto: UpdateUserAvatarDto
158) {
159return this.userService.updateUserAvatar(id, dto)
160}
161
162@Patch('terms')
163public updateTermsAgreedTo(
164@UserToken('uid') id: string,
165@Body() dto: UpdateUserTermsDto
166) {
167return this.userService.updateUserTerms(id, dto)
168}
169
170@Patch('avatar-type')
171public updateAvatarType(
172@UserToken('uid') id: string,
173@Body() dto: UpdateUserAvatarTypeDto
174) {
175return this.userService.updateUserAvatarType(id, dto)
176}
177
178/**
179* @param {string} entityId - The id of the entity that the action is for such as Space, Asset, User, etc., NOT the UserEntityAction ID
180* @date 2023-06-14 16:49
181*/
182@Get('entity-action/for-entity/:entityId')
183@ApiParam({ name: 'entityId', type: 'string', required: true })
184public getUserEntityAction(
185@UserToken('uid') id: UserId,
186@Param('entityId') entityId: string
187) {
188return this.userService.getPublicEntityActionStats(entityId)
189}
190
191/**
192* @param {string} entityId - The id of the entity that the action is for such as Space, Asset, User, etc., NOT the UserEntityAction ID
193* @date 2023-06-14 16:49
194*/
195@Get('entity-action/me/for-entity/:entityId')
196@ApiParam({ name: 'entityId', type: 'string', required: true })
197public getUserEntityActionsByMeForEntity(
198@UserToken('uid') userId: UserId,
199@Param('entityId') entityId: string
200) {
201return this.userService.findEntityActionsByUserForEntity(userId, entityId)
202}
203
204@Patch('entity-action')
205public upsertUserEntityAction(
206@UserToken('uid') id: UserId,
207@Body() dto: UpsertUserEntityActionDto
208) {
209return this.userService.upsertUserEntityAction(id, dto)
210}
211
212@Delete('entity-action/:userEntityActionId')
213@ApiParam({ name: 'userEntityActionId', type: 'string', required: true })
214public deleteUserEntityAction(
215@UserToken('uid') id: UserId,
216@Param('userEntityActionId') userEntityActionId: UserEntityActionId
217) {
218return this.userService.removeUserEntityAction(id, userEntityActionId)
219}
220
221/**
222* START Section: Friends and friend requests ------------------------------------------------------
223*/
224@Get('friends/me')
225@ApiOkResponse({
226type: [Friend],
227description: 'Gets friend requests for the current user from the token'
228})
229public getMyFriends(@UserToken('uid') userId: UserId): Promise<Friend[]> {
230return this.userService.findUserFriendsAdmin(userId)
231}
232
233@Get('friend-requests/me')
234@ApiOkResponse({ type: [Friend] })
235public getMyFriendRequests(
236@UserToken('uid') userId: UserId
237): Promise<Friend[]> {
238return this.userService.findFriendRequestsSentToMeAdmin(userId)
239}
240
241@Post('friend-requests/accept/:fromUserId')
242@ApiParam({ name: 'fromUserId', type: 'string', required: true })
243@ApiOkResponse({
244type: [Friend],
245description:
246'Accepts a friend request. This uses the current user from the token as the accepting user'
247})
248public acceptFriendRequest(
249@UserToken('uid') userId: UserId,
250@Param('fromUserId') fromUserId: UserId
251): Promise<Friend[]> {
252return this.userService.acceptFriendRequestAdmin(userId, fromUserId)
253}
254
255@Post('friend-requests/reject/:fromUserId')
256@ApiParam({ name: 'fromUserId', type: 'string', required: true })
257@ApiOkResponse({
258type: [Friend],
259description:
260'Rejects a friend request. This uses the current user from the token as the user that rejects the request'
261})
262public rejectFriendRequest(
263@UserToken('uid') userId: UserId,
264@Param('fromUserId') fromUserId: UserId
265): Promise<Friend[]> {
266return this.userService.rejectFriendRequestAdmin(userId, fromUserId)
267}
268
269@Get('friend-requests/sent')
270@ApiOkResponse({
271type: [Friend],
272description: 'Gets SENT friend requests by the current user from the token'
273})
274public getSentFriendRequests(
275@UserToken('uid') userId: UserId
276): Promise<Friend[]> {
277return this.userService.findSentFriendRequestsAdmin(userId)
278}
279
280@Post('friend-requests/:toUserId')
281@ApiParam({ name: 'toUserId', type: 'string', required: true })
282@ApiCreatedResponse({
283type: User,
284description:
285'Sends a friend request. This uses the current user from the token'
286})
287public sendFriendRequest(
288@UserToken('uid') userId: UserId,
289@Param('toUserId') toUserId: UserId
290): Promise<User> {
291return this.userService.sendFriendRequestAdmin(userId, toUserId)
292}
293
294@Delete('friends/:friendUserIdToRemove')
295@ApiParam({ name: 'friendUserIdToRemove', type: 'string', required: true })
296@ApiOkResponse({
297type: User,
298description: 'Removes a friend and returns the updated friends list'
299})
300public removeFriend(
301@UserToken('uid') userId: UserId,
302@Param('friendUserIdToRemove') friendUserIdToRemove: UserId
303): Promise<Friend[]> {
304return this.userService.removeFriendAdmin(userId, friendUserIdToRemove)
305}
306/**
307* END Section: Friends and friend requests ------------------------------------------------------
308*/
309
310/**
311* START Section: Cart ------------------------------------------------------
312*/
313@Get('cart')
314@ApiOkResponse({ type: User })
315public getUserCart(@UserToken('uid') userId: string) {
316return this.userService.getUserCartAdmin(userId)
317}
318
319@Post('cart')
320@ApiCreatedResponse({ type: User })
321public addUserCartItemToUserCart(
322@UserToken('uid') userId: string,
323@Body() dto: AddUserCartItemToUserCartDto
324) {
325return this.userService.addUserCartItemToUserCartAdmin(userId, dto)
326}
327
328@Delete('cart/all')
329@ApiOkResponse({ type: User })
330public removeAllUserItemsFromCart(@UserToken('uid') userId: string) {
331return this.userService.removeAllUserCartItemsFromUserCartAdmin(userId)
332}
333
334@Delete('cart/:cartItemId')
335@ApiParam({ name: 'cartItemId', type: 'string', required: true })
336@ApiOkResponse({ type: User })
337public removeUserCartItemFromUserCart(
338@UserToken('uid') userId: string,
339@Param('cartItemId') cartItemId: string
340) {
341return this.userService.removeUserCartItemFromUserCartAdmin(
342userId,
343cartItemId
344)
345}
346
347/**
348* END Section: Cart ------------------------------------------------------
349*/
350
351/** @description Add a url to the array of the user's RPM avatars, readyPlayerMeAvatarUrls in Mongo */
352@Post('rpm-avatar-url')
353public addRpmAvatarUrl(
354@UserToken('uid') id: string,
355@Body() dto: AddRpmAvatarUrlDto
356) {
357return this.userService.addRpmAvatarUrl(id, dto)
358}
359
360@Post('access-key')
361createSignUpKey(@Body() createSignUpKeyDto: CreateUserAccessKeyDto) {
362if (createSignUpKeyDto.token !== process.env.SIGN_UP_KEY_TOKEN) {
363throw new UnauthorizedException()
364}
365return this.userService.createUserAccessKey(createSignUpKeyDto)
366}
367
368@Post('submit-user-access-key')
369async submitUserAccessKey(
370@UserToken('uid') userId: string,
371@Body() submitUserAccessKeyDto: SubmitUserAccessKeyDto
372) {
373const key = await this.userService.checkUserAccessKeyExistence(
374submitUserAccessKeyDto.key
375)
376if (key) {
377await this.userService.addUserPremiumAccess(
378userId,
379key.premiumAccess as PREMIUM_ACCESS
380)
381await this.userService.setUserAccessKeyAsUsed(key.id, userId)
382} else {
383throw new UnauthorizedException(
384"We're sorry, but that key doesn't exist or it's been used"
385)
386}
387}
388
389/** @description Removes an RPM url from readyPlayerMeAvatarUrls in Mongo */
390@Delete('rpm-avatar-url')
391public removeRpmAvatarUrl(
392@UserToken('uid') id: string,
393@Body() dto: RemoveRpmAvatarUrlDto
394) {
395return this.userService.removeRpmAvatarUrl(id, dto)
396}
397
398@Get('sidebar-tags')
399@FirebaseTokenAuthGuard()
400public async getUserSidebarTags(@UserToken('user_id') userId: UserId) {
401return await this.userService.getUserSidebarTags(userId)
402}
403
404@Post('sidebar-tags')
405@FirebaseTokenAuthGuard()
406public async addUserSidebarTag(
407@UserToken('user_id') userId: UserId,
408@Body() addUserSidebarTagDto: AddUserSidebarTagDto
409) {
410return await this.userService.addUserSidebarTag(
411userId,
412addUserSidebarTagDto
413)
414}
415
416@Patch('sidebar-tags')
417@FirebaseTokenAuthGuard()
418public async updateUserSidebarTags(
419@UserToken('user_id') userId: UserId,
420@Body() updateUserSidebarTagsDto: UpdateUserSidebarTagsDto
421) {
422return await this.userService.updateUserSidebarTags(
423userId,
424updateUserSidebarTagsDto.sidebarTags
425)
426}
427
428@Delete('sidebar-tags/:sidebarTag')
429@ApiParam({ name: 'sidebarTag', type: 'string', required: true })
430@FirebaseTokenAuthGuard()
431public async deleteUserSidebarTag(
432@UserToken('user_id') userId: UserId,
433@Param('sidebarTag') sidebarTag: string
434) {
435return await this.userService.deleteUserSidebarTag(userId, sidebarTag)
436}
437}
438