immich/server/src/controllers/user.controller.ts
Brandon Wees 74438f5bd8
feat(web): improved user onboarding ()
* wip

* added user metadata key

* wip

* restructure onboarding system and add initial locale

* update language card and fix translation updating

* remove prints

* new card formattings

* fix cursed unmount effect

* add OAuth route onboarding

* remove required admin auth for onboarding

* delete the hotwire button

* update open-api files

* delete import

* fix failing oauth onboarding fields

* fix e2e test

* fix web e2e test

* add onboarding to user registration e2e test

* remove todo

this was a holdover during dev and didn't get deleted

* fix server small tests

* use onDestroy to save settings rather than a bind:this

* change to false for isOnboarded

* fix other auth small test

* provide type annotation in user factory metadata field

* remove onboardingCompelted from UserDto

* move translations to onboarding steps array and mark as derived so they update

* break language selector out into its own component as per @danieldietzler suggestion

* remove hello header on card

* fix flixkering on server privacy card

* label/id fixes

* openapi

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2025-06-02 21:09:13 +00:00

140 lines
4.2 KiB
TypeScript

import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Next,
Param,
Post,
Put,
Res,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
import { NextFunction, Response } from 'express';
import { AuthDto } from 'src/dtos/auth.dto';
import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto';
import { OnboardingDto, OnboardingResponseDto } from 'src/dtos/onboarding.dto';
import { UserPreferencesResponseDto, UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto';
import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto';
import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto } from 'src/dtos/user.dto';
import { RouteKey } from 'src/enum';
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
import { LoggingRepository } from 'src/repositories/logging.repository';
import { UserService } from 'src/services/user.service';
import { sendFile } from 'src/utils/file';
import { UUIDParamDto } from 'src/validation';
@ApiTags('Users')
@Controller(RouteKey.USER)
export class UserController {
constructor(
private service: UserService,
private logger: LoggingRepository,
) {}
@Get()
@Authenticated()
searchUsers(@Auth() auth: AuthDto): Promise<UserResponseDto[]> {
return this.service.search(auth);
}
@Get('me')
@Authenticated()
getMyUser(@Auth() auth: AuthDto): Promise<UserAdminResponseDto> {
return this.service.getMe(auth);
}
@Put('me')
@Authenticated()
updateMyUser(@Auth() auth: AuthDto, @Body() dto: UserUpdateMeDto): Promise<UserAdminResponseDto> {
return this.service.updateMe(auth, dto);
}
@Get('me/preferences')
@Authenticated()
getMyPreferences(@Auth() auth: AuthDto): Promise<UserPreferencesResponseDto> {
return this.service.getMyPreferences(auth);
}
@Put('me/preferences')
@Authenticated()
updateMyPreferences(
@Auth() auth: AuthDto,
@Body() dto: UserPreferencesUpdateDto,
): Promise<UserPreferencesResponseDto> {
return this.service.updateMyPreferences(auth, dto);
}
@Get('me/license')
@Authenticated()
getUserLicense(@Auth() auth: AuthDto): Promise<LicenseResponseDto> {
return this.service.getLicense(auth);
}
@Put('me/license')
@Authenticated()
async setUserLicense(@Auth() auth: AuthDto, @Body() license: LicenseKeyDto): Promise<LicenseResponseDto> {
return this.service.setLicense(auth, license);
}
@Delete('me/license')
@Authenticated()
async deleteUserLicense(@Auth() auth: AuthDto): Promise<void> {
await this.service.deleteLicense(auth);
}
@Get('me/onboarding')
@Authenticated()
getUserOnboarding(@Auth() auth: AuthDto): Promise<OnboardingResponseDto> {
return this.service.getOnboarding(auth);
}
@Put('me/onboarding')
@Authenticated()
async setUserOnboarding(@Auth() auth: AuthDto, @Body() Onboarding: OnboardingDto): Promise<OnboardingResponseDto> {
return this.service.setOnboarding(auth, Onboarding);
}
@Delete('me/onboarding')
@Authenticated()
async deleteUserOnboarding(@Auth() auth: AuthDto): Promise<void> {
await this.service.deleteOnboarding(auth);
}
@Get(':id')
@Authenticated()
getUser(@Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
return this.service.get(id);
}
@UseInterceptors(FileUploadInterceptor)
@ApiConsumes('multipart/form-data')
@ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto })
@Post('profile-image')
@Authenticated()
createProfileImage(
@Auth() auth: AuthDto,
@UploadedFile() fileInfo: Express.Multer.File,
): Promise<CreateProfileImageResponseDto> {
return this.service.createProfileImage(auth, fileInfo);
}
@Delete('profile-image')
@HttpCode(HttpStatus.NO_CONTENT)
@Authenticated()
deleteProfileImage(@Auth() auth: AuthDto): Promise<void> {
return this.service.deleteProfileImage(auth);
}
@Get(':id/profile-image')
@FileResponse()
@Authenticated()
async getProfileImage(@Res() res: Response, @Next() next: NextFunction, @Param() { id }: UUIDParamDto) {
await sendFile(res, next, () => this.service.getProfileImage(id), this.logger);
}
}