Add verified badge and page on the front
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
HttpStatus,
|
||||
ParseFilePipeBuilder,
|
||||
Response,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
@@ -41,7 +42,6 @@ import { SettingsService } from 'src/settings/settings.service';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { writeFile } from 'fs';
|
||||
import { MailerService } from '@nestjs-modules/mailer';
|
||||
|
||||
@ApiTags('auth')
|
||||
@Controller('auth')
|
||||
@@ -50,7 +50,6 @@ export class AuthController {
|
||||
private authService: AuthService,
|
||||
private usersService: UsersService,
|
||||
private settingsService: SettingsService,
|
||||
private emailService: MailerService,
|
||||
) {}
|
||||
|
||||
@Get('login/google')
|
||||
@@ -73,19 +72,29 @@ export class AuthController {
|
||||
try {
|
||||
const user = await this.usersService.createUser(registerDto);
|
||||
await this.settingsService.createUserSetting(user.id);
|
||||
await this.emailService.sendMail({
|
||||
to: user.email,
|
||||
from: "chromacase@octohub.app",
|
||||
subject: "Mail verification",
|
||||
text: "To verify your mail click here",
|
||||
html: "<b>Verify</b>",
|
||||
})
|
||||
await this.authService.sendVerifyMail(user);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new BadRequestException();
|
||||
}
|
||||
}
|
||||
|
||||
@HttpCode(200)
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('verify')
|
||||
async verify(@Request() req: any, @Query('token') token: string): Promise<void> {
|
||||
if (await this.authService.verifyMail(req.user.id, token))
|
||||
return;
|
||||
throw new BadRequestException("Invalid token. Expired or invalid.");
|
||||
}
|
||||
|
||||
@HttpCode(200)
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Put('reverify')
|
||||
async reverify(@Request() req: any): Promise<void> {
|
||||
await this.authService.sendVerifyMail(req.user);
|
||||
}
|
||||
|
||||
@ApiBody({ type: LoginDto })
|
||||
@HttpCode(200)
|
||||
@UseGuards(LocalAuthGuard)
|
||||
@@ -130,7 +139,7 @@ export class AuthController {
|
||||
)
|
||||
file: Express.Multer.File,
|
||||
) {
|
||||
const path = `/data/${req.user.id}.jpg`
|
||||
const path = `/data/${req.user.id}.jpg`;
|
||||
writeFile(path, file.buffer, (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import PayloadInterface from './interface/payload.interface';
|
||||
import { User } from 'src/models/user';
|
||||
import { MailerService } from '@nestjs-modules/mailer';
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private userService: UsersService,
|
||||
private jwtService: JwtService,
|
||||
private emailService: MailerService,
|
||||
) {}
|
||||
|
||||
async validateUser(
|
||||
@@ -31,4 +34,33 @@ export class AuthService {
|
||||
access_token,
|
||||
};
|
||||
}
|
||||
|
||||
async sendVerifyMail(user: User) {
|
||||
const token = await this.jwtService.signAsync(
|
||||
{
|
||||
userId: user.id,
|
||||
},
|
||||
{ expiresIn: '10h' },
|
||||
);
|
||||
await this.emailService.sendMail({
|
||||
to: user.email,
|
||||
from: 'chromacase@octohub.app',
|
||||
subject: 'Mail verification for Chromacase',
|
||||
html: `To verify your mail, please click on this <a href="{${process.env.PUBLIC_URL}/verify?token=${token}">link</a>.`,
|
||||
});
|
||||
}
|
||||
|
||||
async verifyMail(userId: number, token: string): Promise<boolean> {
|
||||
try {
|
||||
await this.jwtService.verifyAsync(token);
|
||||
} catch(e) {
|
||||
console.log("Verify mail token failure", e);
|
||||
return false;
|
||||
}
|
||||
await this.userService.updateUser({
|
||||
where: { id: userId },
|
||||
data: { emailVerified: true },
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import {
|
||||
Injectable,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
StreamableFile,
|
||||
} from '@nestjs/common';
|
||||
import { User, Prisma } from '@prisma/client';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
@@ -13,7 +11,9 @@ import fetch from 'node-fetch';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
) {}
|
||||
|
||||
async user(
|
||||
userWhereUniqueInput: Prisma.UserWhereUniqueInput,
|
||||
@@ -95,8 +95,7 @@ export class UsersService {
|
||||
const resp = await fetch(
|
||||
`https://www.gravatar.com/avatar/${hash}.jpg?d=404&s=200`,
|
||||
);
|
||||
for (const [k, v] of resp.headers)
|
||||
resp.headers.set(k, v);
|
||||
for (const [k, v] of resp.headers) resp.headers.set(k, v);
|
||||
resp.body!.pipe(res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import TextButton from './components/TextButton';
|
||||
import ErrorView from './views/ErrorView';
|
||||
import GenreDetailsView from './views/GenreDetailsView';
|
||||
import GoogleView from './views/GoogleView';
|
||||
import VerifiedView from './views/VerifiedView';
|
||||
|
||||
// Util function to hide route props in URL
|
||||
const removeMe = () => '';
|
||||
@@ -81,6 +82,11 @@ const protectedRoutes = () =>
|
||||
link: undefined,
|
||||
},
|
||||
User: { component: ProfileView, options: { title: translate('user') }, link: '/user' },
|
||||
Verified: {
|
||||
component: VerifiedView,
|
||||
options: { title: 'Verify email', headerShown: false },
|
||||
link: '/verify',
|
||||
},
|
||||
} as const);
|
||||
|
||||
const publicRoutes = () =>
|
||||
|
||||
@@ -8,6 +8,7 @@ export const UserValidator = yup
|
||||
username: yup.string().required(),
|
||||
password: yup.string().required().nullable(),
|
||||
email: yup.string().required(),
|
||||
emailVerified: yup.boolean().required(),
|
||||
googleID: yup.string().required().nullable(),
|
||||
isGuest: yup.boolean().required(),
|
||||
partyPlayed: yup.number().required(),
|
||||
@@ -32,6 +33,7 @@ export const UserHandler: ResponseHandler<yup.InferType<typeof UserValidator>, U
|
||||
interface User extends Model {
|
||||
name: string;
|
||||
email: string;
|
||||
emailVerified: boolean;
|
||||
googleID: string | null;
|
||||
isGuest: boolean;
|
||||
premium: boolean;
|
||||
|
||||
35
front/views/VerifiedView.tsx
Normal file
35
front/views/VerifiedView.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import API from '../API';
|
||||
import { Text } from 'native-base';
|
||||
import { useNavigation } from '../Navigation';
|
||||
import { useRoute } from '@react-navigation/native';
|
||||
|
||||
const VerifiedView = () => {
|
||||
const navigation = useNavigation();
|
||||
const route = useRoute();
|
||||
const [failed, setFailed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function run() {
|
||||
try {
|
||||
await API.fetch({
|
||||
route: `/auth/verify?token=${(route.params as any).token}`,
|
||||
method: 'PUT',
|
||||
});
|
||||
navigation.navigate('Home');
|
||||
} catch {
|
||||
setFailed(true);
|
||||
}
|
||||
}
|
||||
run();
|
||||
}, []);
|
||||
|
||||
return failed ? (
|
||||
<Text>Email verification failed. The token has expired or is invalid.</Text>
|
||||
) : (
|
||||
<Text>Loading please wait</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default VerifiedView;
|
||||
@@ -49,7 +49,7 @@ const ProfileSettings = ({ navigation }: { navigation: any }) => {
|
||||
type: 'text',
|
||||
title: translate('email'),
|
||||
data: {
|
||||
text: user.email || translate('NoAssociatedEmail'),
|
||||
text: `${user.email} ${user.emailVerified ? "verified" : "not verified"}` || translate('NoAssociatedEmail'),
|
||||
onPress: () => {
|
||||
navigation.navigate('changeEmail');
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user