Add get profile route that supports gravatar

This commit is contained in:
2023-07-23 15:56:48 +09:00
parent 20eb62d19b
commit 04487c9b24
6 changed files with 69 additions and 18 deletions
+26 -14
View File
@@ -44,16 +44,16 @@ export class AuthController {
private settingsService: SettingsService, private settingsService: SettingsService,
) {} ) {}
@Get("login/google") @Get('login/google')
@UseGuards(AuthGuard('google')) @UseGuards(AuthGuard('google'))
googleLogin() { } googleLogin() {}
@Get("logged/google") @Get('logged/google')
@UseGuards(AuthGuard('google')) @UseGuards(AuthGuard('google'))
async googleLoginCallbakc(@Req() req: any) { async googleLoginCallbakc(@Req() req: any) {
let user = await this.usersService.user({googleID: req.user.googleID}); let user = await this.usersService.user({ googleID: req.user.googleID });
if (!user) { if (!user) {
user = await this.usersService.createUser(req.user) user = await this.usersService.createUser(req.user);
await this.settingsService.createUserSetting(user.id); await this.settingsService.createUserSetting(user.id);
} }
return this.authService.login(user); return this.authService.login(user);
@@ -62,9 +62,9 @@ export class AuthController {
@Post('register') @Post('register')
async register(@Body() registerDto: RegisterDto): Promise<void> { async register(@Body() registerDto: RegisterDto): Promise<void> {
try { try {
const user = await this.usersService.createUser(registerDto) const user = await this.usersService.createUser(registerDto);
await this.settingsService.createUserSetting(user.id); await this.settingsService.createUserSetting(user.id);
} catch(e) { } catch (e) {
console.error(e); console.error(e);
throw new BadRequestException(); throw new BadRequestException();
} }
@@ -86,6 +86,15 @@ export class AuthController {
return this.authService.login(user); return this.authService.login(user);
} }
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOkResponse({ description: 'The user profile picture' })
@ApiUnauthorizedResponse({ description: 'Invalid token' })
@Get('me/picture')
async getProfilePicture(@Request() req: any) {
return await this.usersService.getProfilePicture(req.user.id);
}
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@ApiOkResponse({ description: 'Successfully logged in', type: User }) @ApiOkResponse({ description: 'Successfully logged in', type: User })
@@ -133,25 +142,28 @@ export class AuthController {
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@ApiOkResponse({description: 'Successfully edited settings', type: Setting}) @ApiOkResponse({ description: 'Successfully edited settings', type: Setting })
@ApiUnauthorizedResponse({description: 'Invalid token'}) @ApiUnauthorizedResponse({ description: 'Invalid token' })
@Patch('me/settings') @Patch('me/settings')
udpateSettings( udpateSettings(
@Request() req: any, @Request() req: any,
@Body() settingUserDto: UpdateSettingDto): Promise<Setting> { @Body() settingUserDto: UpdateSettingDto,
): Promise<Setting> {
return this.settingsService.updateUserSettings({ return this.settingsService.updateUserSettings({
where: { userId: +req.user.id}, where: { userId: +req.user.id },
data: settingUserDto, data: settingUserDto,
}); });
} }
@UseGuards(JwtAuthGuard) @UseGuards(JwtAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@ApiOkResponse({description: 'Successfully edited settings', type: Setting}) @ApiOkResponse({ description: 'Successfully edited settings', type: Setting })
@ApiUnauthorizedResponse({description: 'Invalid token'}) @ApiUnauthorizedResponse({ description: 'Invalid token' })
@Get('me/settings') @Get('me/settings')
async getSettings(@Request() req: any): Promise<Setting> { async getSettings(@Request() req: any): Promise<Setting> {
const result = await this.settingsService.getUserSetting({ userId: +req.user.id }); const result = await this.settingsService.getUserSetting({
userId: +req.user.id,
});
if (!result) throw new NotFoundException(); if (!result) throw new NotFoundException();
return result; return result;
} }
+5
View File
@@ -20,4 +20,9 @@ export class UsersController {
if (!ret) throw new NotFoundException(); if (!ret) throw new NotFoundException();
return ret; return ret;
} }
@Get(':id/picture')
async getPicture(@Param('id') id: number) {
return await this.usersService.getProfilePicture(+id);
}
} }
+28 -4
View File
@@ -1,8 +1,14 @@
import { Injectable } from '@nestjs/common'; import {
Injectable,
InternalServerErrorException,
NotFoundException,
StreamableFile,
} from '@nestjs/common';
import { User, Prisma } from '@prisma/client'; import { User, Prisma } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import * as bcrypt from 'bcryptjs'; import * as bcrypt from 'bcryptjs';
import { randomUUID } from 'crypto'; import { createHash, randomUUID } from 'crypto';
import { createReadStream, existsSync } from 'fs';
@Injectable() @Injectable()
export class UsersService { export class UsersService {
@@ -34,8 +40,7 @@ export class UsersService {
} }
async createUser(data: Prisma.UserCreateInput): Promise<User> { async createUser(data: Prisma.UserCreateInput): Promise<User> {
if (data.password) if (data.password) data.password = await bcrypt.hash(data.password, 8);
data.password = await bcrypt.hash(data.password, 8);
return this.prisma.user.create({ return this.prisma.user.create({
data, data,
}); });
@@ -73,4 +78,23 @@ export class UsersService {
where, where,
}); });
} }
async getProfilePicture(userId: number) {
const path = `/data/${userId}.png`;
if (existsSync(path)) {
const file = createReadStream(path);
return new StreamableFile(file);
}
// We could not find a profile icon locally, using gravatar instead.
const user = await this.user({ id: userId });
if (!user) throw new InternalServerErrorException();
const hash = createHash('md5')
.update(user.email.trim().toLowerCase())
.digest('hex');
const resp = await fetch(
`https://www.gravatar.com/avatar/${hash}.jpg?d=404`,
);
if (!resp.ok) throw new NotFoundException('No image found for user');
return resp.arrayBuffer();
}
} }
+4
View File
@@ -9,6 +9,7 @@ services:
volumes: volumes:
- ./back:/app - ./back:/app
- ./assets:/assets - ./assets:/assets
- data:/data
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
@@ -54,3 +55,6 @@ services:
- "back" - "back"
env_file: env_file:
- .env - .env
volumes:
data:
+4
View File
@@ -10,6 +10,7 @@ services:
- .env - .env
volumes: volumes:
- ./assets:/assets - ./assets:/assets
- data:/data
scorometer: scorometer:
image: ghcr.io/chroma-case/scorometer:main image: ghcr.io/chroma-case/scorometer:main
ports: ports:
@@ -43,3 +44,6 @@ services:
- "back" - "back"
env_file: env_file:
- .env - .env
volumes:
data:
+2
View File
@@ -10,6 +10,7 @@ services:
- .env - .env
volumes: volumes:
- ./assets:/assets - ./assets:/assets
- data:/data
scorometer: scorometer:
build: ./scorometer build: ./scorometer
ports: ports:
@@ -52,3 +53,4 @@ services:
volumes: volumes:
db: db:
data: