1
0
mirror of https://github.com/immich-app/immich.git synced 2024-12-25 10:43:13 +02:00

chore(server): cleanup controllers (#2065)

This commit is contained in:
Jason Rasmussen 2023-03-24 00:53:56 -04:00 committed by GitHub
parent 6745826f35
commit 54f98053a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 93 additions and 84 deletions

View File

@ -22,7 +22,7 @@ import { Authenticated } from '../decorators/authenticated.decorator';
@ApiTags('Authentication')
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
constructor(private readonly service: AuthService) {}
@Post('login')
async login(
@ -31,8 +31,8 @@ export class AuthController {
@Req() req: Request,
@Res({ passthrough: true }) res: Response,
): Promise<LoginResponseDto> {
const { response, cookie } = await this.authService.login(loginCredential, clientIp, req.secure);
res.setHeader('Set-Cookie', cookie);
const { response, cookie } = await this.service.login(loginCredential, clientIp, req.secure);
res.header('Set-Cookie', cookie);
return response;
}
@ -41,25 +41,24 @@ export class AuthController {
adminSignUp(
@Body(new ValidationPipe({ transform: true })) signUpCredential: SignUpDto,
): Promise<AdminSignupResponseDto> {
return this.authService.adminSignUp(signUpCredential);
return this.service.adminSignUp(signUpCredential);
}
@Authenticated()
@Post('validateToken')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
validateAccessToken(@GetAuthUser() authUser: AuthUserDto): ValidateAccessTokenResponseDto {
validateAccessToken(): ValidateAccessTokenResponseDto {
return { authStatus: true };
}
@Authenticated()
@Post('change-password')
async changePassword(@GetAuthUser() authUser: AuthUserDto, @Body() dto: ChangePasswordDto): Promise<UserResponseDto> {
return this.authService.changePassword(authUser, dto);
changePassword(@GetAuthUser() authUser: AuthUserDto, @Body() dto: ChangePasswordDto): Promise<UserResponseDto> {
return this.service.changePassword(authUser, dto);
}
@Authenticated()
@Post('logout')
async logout(
logout(
@Req() req: Request,
@Res({ passthrough: true }) res: Response,
@GetAuthUser() authUser: AuthUserDto,
@ -69,6 +68,6 @@ export class AuthController {
res.clearCookie(IMMICH_ACCESS_COOKIE);
res.clearCookie(IMMICH_AUTH_TYPE_COOKIE);
return this.authService.logout(authUser, authType);
return this.service.logout(authUser, authType);
}
}

View File

@ -9,9 +9,9 @@ import { ApiTags } from '@nestjs/swagger';
import { GetAuthUser } from '../decorators/auth-user.decorator';
import { Authenticated } from '../decorators/authenticated.decorator';
@Authenticated()
@ApiTags('Device Info')
@Controller('device-info')
@Authenticated()
export class DeviceInfoController {
constructor(private readonly service: DeviceInfoService) {}

View File

@ -3,19 +3,19 @@ import { Body, Controller, Get, Param, Put, ValidationPipe } from '@nestjs/commo
import { ApiTags } from '@nestjs/swagger';
import { Authenticated } from '../decorators/authenticated.decorator';
@Authenticated({ admin: true })
@ApiTags('Job')
@Controller('jobs')
@Authenticated({ admin: true })
export class JobController {
constructor(private readonly jobService: JobService) {}
constructor(private service: JobService) {}
@Get()
getAllJobsStatus(): Promise<AllJobStatusResponseDto> {
return this.jobService.getAllJobsStatus();
return this.service.getAllJobsStatus();
}
@Put('/:jobId')
sendJobCommand(@Param(ValidationPipe) { jobId }: JobIdDto, @Body(ValidationPipe) dto: JobCommandDto): Promise<void> {
return this.jobService.handleCommand(jobId, dto);
return this.service.handleCommand(jobId, dto);
}
}

View File

@ -1,7 +1,6 @@
import {
AuthUserDto,
LoginResponseDto,
MOBILE_REDIRECT,
OAuthCallbackDto,
OAuthConfigDto,
OAuthConfigResponseDto,
@ -17,43 +16,42 @@ import { Authenticated } from '../decorators/authenticated.decorator';
@ApiTags('OAuth')
@Controller('oauth')
export class OAuthController {
constructor(private readonly oauthService: OAuthService) {}
constructor(private service: OAuthService) {}
@Get('mobile-redirect')
@Redirect()
public mobileRedirect(@Req() req: Request) {
const url = `${MOBILE_REDIRECT}?${req.url.split('?')[1] || ''}`;
return { url, statusCode: HttpStatus.TEMPORARY_REDIRECT };
mobileRedirect(@Req() req: Request) {
return {
url: this.service.getMobileRedirect(req.url),
statusCode: HttpStatus.TEMPORARY_REDIRECT,
};
}
@Post('config')
public generateConfig(@Body(ValidationPipe) dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> {
return this.oauthService.generateConfig(dto);
generateConfig(@Body(ValidationPipe) dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> {
return this.service.generateConfig(dto);
}
@Post('callback')
public async callback(
async callback(
@Res({ passthrough: true }) res: Response,
@Body(ValidationPipe) dto: OAuthCallbackDto,
@Req() req: Request,
): Promise<LoginResponseDto> {
const { response, cookie } = await this.oauthService.login(dto, req.secure);
res.setHeader('Set-Cookie', cookie);
const { response, cookie } = await this.service.login(dto, req.secure);
res.header('Set-Cookie', cookie);
return response;
}
@Authenticated()
@Post('link')
public async link(
@GetAuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) dto: OAuthCallbackDto,
): Promise<UserResponseDto> {
return this.oauthService.link(authUser, dto);
link(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) dto: OAuthCallbackDto): Promise<UserResponseDto> {
return this.service.link(authUser, dto);
}
@Authenticated()
@Post('unlink')
public async unlink(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
return this.oauthService.unlink(authUser);
unlink(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
return this.service.unlink(authUser);
}
}

View File

@ -12,26 +12,26 @@ import { GetAuthUser } from '../decorators/auth-user.decorator';
import { Authenticated } from '../decorators/authenticated.decorator';
@ApiTags('Search')
@Authenticated()
@Controller('search')
@Authenticated()
export class SearchController {
constructor(private readonly searchService: SearchService) {}
constructor(private service: SearchService) {}
@Get()
async search(
search(
@GetAuthUser() authUser: AuthUserDto,
@Query(new ValidationPipe({ transform: true })) dto: SearchDto,
): Promise<SearchResponseDto> {
return this.searchService.search(authUser, dto);
return this.service.search(authUser, dto);
}
@Get('config')
getSearchConfig(): SearchConfigResponseDto {
return this.searchService.getConfig();
return this.service.getConfig();
}
@Get('explore')
getExploreData(@GetAuthUser() authUser: AuthUserDto): Promise<SearchExploreResponseDto[]> {
return this.searchService.getExploreData(authUser) as Promise<SearchExploreResponseDto[]>;
return this.service.getExploreData(authUser) as Promise<SearchExploreResponseDto[]>;
}
}

View File

@ -12,7 +12,7 @@ import { Authenticated } from '../decorators/authenticated.decorator';
@ApiTags('Server Info')
@Controller('server-info')
export class ServerInfoController {
constructor(private readonly service: ServerInfoService) {}
constructor(private service: ServerInfoService) {}
@Get()
getServerInfo(): Promise<ServerInfoResponseDto> {

View File

@ -1,35 +1,36 @@
import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, ShareService } from '@app/domain';
import { Body, Controller, Delete, Get, Param, Patch, ValidationPipe } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { GetAuthUser } from '../decorators/auth-user.decorator';
import { Authenticated } from '../decorators/authenticated.decorator';
import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, ShareService } from '@app/domain';
@ApiTags('share')
@Controller('share')
export class ShareController {
constructor(private readonly shareService: ShareService) {}
constructor(private readonly service: ShareService) {}
@Authenticated()
@Get()
getAllSharedLinks(@GetAuthUser() authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> {
return this.shareService.getAll(authUser);
return this.service.getAll(authUser);
}
@Authenticated({ isShared: true })
@Get('me')
getMySharedLink(@GetAuthUser() authUser: AuthUserDto): Promise<SharedLinkResponseDto> {
return this.shareService.getMine(authUser);
return this.service.getMine(authUser);
}
@Authenticated()
@Get(':id')
getSharedLinkById(@GetAuthUser() authUser: AuthUserDto, @Param('id') id: string): Promise<SharedLinkResponseDto> {
return this.shareService.getById(authUser, id, true);
return this.service.getById(authUser, id, true);
}
@Authenticated()
@Delete(':id')
removeSharedLink(@GetAuthUser() authUser: AuthUserDto, @Param('id') id: string): Promise<void> {
return this.shareService.remove(authUser, id);
return this.service.remove(authUser, id);
}
@Authenticated()
@ -37,8 +38,8 @@ export class ShareController {
editSharedLink(
@GetAuthUser() authUser: AuthUserDto,
@Param('id') id: string,
@Body(new ValidationPipe()) dto: EditSharedLinkDto,
@Body(ValidationPipe) dto: EditSharedLinkDto,
): Promise<SharedLinkResponseDto> {
return this.shareService.edit(authUser, id, dto);
return this.service.edit(authUser, id, dto);
}
}

View File

@ -4,28 +4,28 @@ import { ApiTags } from '@nestjs/swagger';
import { Authenticated } from '../decorators/authenticated.decorator';
@ApiTags('System Config')
@Authenticated({ admin: true })
@Controller('system-config')
@Authenticated({ admin: true })
export class SystemConfigController {
constructor(private readonly systemConfigService: SystemConfigService) {}
constructor(private readonly service: SystemConfigService) {}
@Get()
public getConfig(): Promise<SystemConfigDto> {
return this.systemConfigService.getConfig();
getConfig(): Promise<SystemConfigDto> {
return this.service.getConfig();
}
@Get('defaults')
public getDefaults(): SystemConfigDto {
return this.systemConfigService.getDefaults();
getDefaults(): SystemConfigDto {
return this.service.getDefaults();
}
@Put()
public updateConfig(@Body(ValidationPipe) dto: SystemConfigDto): Promise<SystemConfigDto> {
return this.systemConfigService.updateConfig(dto);
updateConfig(@Body(ValidationPipe) dto: SystemConfigDto): Promise<SystemConfigDto> {
return this.service.updateConfig(dto);
}
@Get('storage-template-options')
public getStorageTemplateOptions(): SystemConfigTemplateStorageOptionDto {
return this.systemConfigService.getStorageTemplateOptions();
getStorageTemplateOptions(): SystemConfigTemplateStorageOptionDto {
return this.service.getStorageTemplateOptions();
}
}

View File

@ -33,60 +33,58 @@ import { UserCountDto } from '@app/domain';
@ApiTags('User')
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
constructor(private service: UserService) {}
@Authenticated()
@Get()
async getAllUsers(
getAllUsers(
@GetAuthUser() authUser: AuthUserDto,
@Query('isAll', ParseBoolPipe) isAll: boolean,
): Promise<UserResponseDto[]> {
return await this.userService.getAllUsers(authUser, isAll);
return this.service.getAllUsers(authUser, isAll);
}
@Get('/info/:userId')
async getUserById(@Param('userId') userId: string): Promise<UserResponseDto> {
return await this.userService.getUserById(userId);
getUserById(@Param('userId') userId: string): Promise<UserResponseDto> {
return this.service.getUserById(userId);
}
@Authenticated()
@Get('me')
async getMyUserInfo(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
return await this.userService.getUserInfo(authUser);
getMyUserInfo(@GetAuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
return this.service.getUserInfo(authUser);
}
@Authenticated({ admin: true })
@Post()
async createUser(
@Body(new ValidationPipe({ transform: true })) createUserDto: CreateUserDto,
): Promise<UserResponseDto> {
return await this.userService.createUser(createUserDto);
createUser(@Body(new ValidationPipe({ transform: true })) createUserDto: CreateUserDto): Promise<UserResponseDto> {
return this.service.createUser(createUserDto);
}
@Get('/count')
async getUserCount(@Query(new ValidationPipe({ transform: true })) dto: UserCountDto): Promise<UserCountResponseDto> {
return await this.userService.getUserCount(dto);
getUserCount(@Query(new ValidationPipe({ transform: true })) dto: UserCountDto): Promise<UserCountResponseDto> {
return this.service.getUserCount(dto);
}
@Authenticated({ admin: true })
@Delete('/:userId')
async deleteUser(@GetAuthUser() authUser: AuthUserDto, @Param('userId') userId: string): Promise<UserResponseDto> {
return await this.userService.deleteUser(authUser, userId);
deleteUser(@GetAuthUser() authUser: AuthUserDto, @Param('userId') userId: string): Promise<UserResponseDto> {
return this.service.deleteUser(authUser, userId);
}
@Authenticated({ admin: true })
@Post('/:userId/restore')
async restoreUser(@GetAuthUser() authUser: AuthUserDto, @Param('userId') userId: string): Promise<UserResponseDto> {
return await this.userService.restoreUser(authUser, userId);
restoreUser(@GetAuthUser() authUser: AuthUserDto, @Param('userId') userId: string): Promise<UserResponseDto> {
return this.service.restoreUser(authUser, userId);
}
@Authenticated()
@Put()
async updateUser(
updateUser(
@GetAuthUser() authUser: AuthUserDto,
@Body(ValidationPipe) updateUserDto: UpdateUserDto,
): Promise<UserResponseDto> {
return await this.userService.updateUser(authUser, updateUserDto);
return this.service.updateUser(authUser, updateUserDto);
}
@UseInterceptors(FileInterceptor('file', profileImageUploadOption))
@ -97,20 +95,18 @@ export class UserController {
type: CreateProfileImageDto,
})
@Post('/profile-image')
async createProfileImage(
createProfileImage(
@GetAuthUser() authUser: AuthUserDto,
@UploadedFile() fileInfo: Express.Multer.File,
): Promise<CreateProfileImageResponseDto> {
return await this.userService.createProfileImage(authUser, fileInfo);
return this.service.createProfileImage(authUser, fileInfo);
}
@Get('/profile-image/:userId')
@Header('Cache-Control', 'max-age=600')
async getProfileImage(@Param('userId') userId: string, @Response({ passthrough: true }) res: Res): Promise<any> {
const readableStream = await this.userService.getUserProfileImage(userId);
res.set({
'Content-Type': 'image/jpeg',
});
const readableStream = await this.service.getUserProfileImage(userId);
res.header('Content-Type', 'image/jpeg');
return new StreamableFile(readableStream);
}
}

View File

@ -63,6 +63,16 @@ describe('OAuthService', () => {
expect(sut).toBeDefined();
});
describe('getMobileRedirect', () => {
it('should pass along the query params', () => {
expect(sut.getMobileRedirect('http://immich.app?code=123&state=456')).toEqual('app.immich:/?code=123&state=456');
});
it('should work if called without query params', () => {
expect(sut.getMobileRedirect('http://immich.app')).toEqual('app.immich:/?');
});
});
describe('generateConfig', () => {
it('should work when oauth is not configured', async () => {
await expect(sut.generateConfig({ redirectUri: 'http://callback' })).resolves.toEqual({

View File

@ -1,14 +1,15 @@
import { SystemConfig } from '@app/infra/db/entities';
import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common';
import { AuthType, AuthUserDto, LoginResponseDto } from '../auth';
import { ICryptoRepository } from '../crypto';
import { AuthCore } from '../auth/auth.core';
import { ICryptoRepository } from '../crypto';
import { INITIAL_SYSTEM_CONFIG, ISystemConfigRepository } from '../system-config';
import { IUserRepository, UserCore, UserResponseDto } from '../user';
import { IUserTokenRepository } from '../user-token';
import { OAuthCallbackDto, OAuthConfigDto } from './dto';
import { MOBILE_REDIRECT } from './oauth.constants';
import { OAuthCore } from './oauth.core';
import { OAuthConfigResponseDto } from './response-dto';
import { IUserTokenRepository } from '../user-token';
@Injectable()
export class OAuthService {
@ -30,6 +31,10 @@ export class OAuthService {
this.oauthCore = new OAuthCore(configRepository, initialConfig);
}
getMobileRedirect(url: string) {
return `${MOBILE_REDIRECT}?${url.split('?')[1] || ''}`;
}
generateConfig(dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> {
return this.oauthCore.generateConfig(dto);
}