Skip to content

Commit

Permalink
fix: once invites are accepted the page is stuck
Browse files Browse the repository at this point in the history
  • Loading branch information
harshithmullapudi committed Aug 29, 2024
1 parent 15d8e34 commit 91db4dd
Show file tree
Hide file tree
Showing 13 changed files with 29 additions and 185 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION=0.3.0-alpha
VERSION=0.3.1-alpha
NODE_ENV=development


Expand Down
2 changes: 1 addition & 1 deletion apps/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "server",
"version": "0.3.0",
"version": "0.3.1",
"description": "Tegon server",
"author": "Tegon",
"private": true,
Expand Down
10 changes: 10 additions & 0 deletions apps/server/src/common/utils/login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Passwordless from 'supertokens-node/recipe/passwordless';

export async function createMagicLink(email: string) {
const magicLink = await Passwordless.createMagicLink({
email,
tenantId: 'public',
});

return magicLink;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Injectable } from '@nestjs/common';
import { MailerService } from '@nestjs-modules/mailer';
import supertokens, { deleteUser } from 'supertokens-node';
import EmailPassword from 'supertokens-node/recipe/emailpassword';

import { UsersService } from 'modules/users/users.service';

Expand All @@ -28,10 +27,6 @@ export class SupertokensService {
});
}

getEmailPasswordRecipe() {
return EmailPassword;
}

async deleteUserForId(userId: string) {
await deleteUser(userId);
}
Expand Down
41 changes: 1 addition & 40 deletions apps/server/src/modules/users/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { SessionContainer } from 'supertokens-node/recipe/session';

import { AuthGuard } from 'modules/auth/auth.guard';
import { Session as SessionDecorator } from 'modules/auth/session.decorator';
import { SupertokensService } from 'modules/auth/supertokens/supertokens.service';

import {
UpdateUserBody,
Expand All @@ -33,10 +32,7 @@ import { UsersService } from './users.service';
path: 'users',
})
export class UsersController {
constructor(
private usersService: UsersService,
private supertokensService: SupertokensService,
) {}
constructor(private usersService: UsersService) {}

@Get()
@UseGuards(AuthGuard)
Expand All @@ -62,41 +58,6 @@ export class UsersController {
return user;
}

@Post('change_password')
@UseGuards(AuthGuard)
async changePassword(
@SessionDecorator() session: SessionContainer,
@Body() passwordBody: Record<string, string>,
) {
const userId = session.getUserId();
return await this.usersService.changePassword(
this.supertokensService,
userId,
session,
passwordBody,
);
}

@Post('forgot_password')
async sendPasswordResetEmail(@Body() forgotBody: Record<string, string>) {
return await this.usersService.sendPasswordResetEmail(
this.supertokensService,
forgotBody.email,
);
}

@Post('forgot_password/:token')
async resetPassword(
@Param('token') token: string,
@Body('newPassword') newPassword: string,
) {
return this.usersService.resetPassword(
this.supertokensService,
token,
newPassword,
);
}

@Post('pat')
@UseGuards(AuthGuard)
async createPersonalAccessToken(
Expand Down
132 changes: 2 additions & 130 deletions apps/server/src/modules/users/users.service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { MailerService } from '@nestjs-modules/mailer';
import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { GetUsersDto, PublicUser, User } from '@tegonhq/types';
import { PrismaService } from 'nestjs-prisma';
import supertokens from 'supertokens-node';
import { SessionContainer } from 'supertokens-node/recipe/session';

import {
generateKeyForUserId,
generatePersonalAccessToken,
} from 'common/authentication';

import { SupertokensService } from 'modules/auth/supertokens/supertokens.service';

import {
UpdateUserBody,
userSerializer,
Expand All @@ -21,11 +16,7 @@ import {

@Injectable()
export class UsersService {
private readonly logger: Logger = new Logger('UserService');
constructor(
private prisma: PrismaService,
private mailerService: MailerService,
) {}
constructor(private prisma: PrismaService) {}

async upsertUser(
id: string,
Expand Down Expand Up @@ -127,125 +118,6 @@ export class UsersService {
return userSerializer(user);
}

async changePassword(
supertokensService: SupertokensService,
userId: string,
session: SessionContainer,
passwordBody: Record<string, string>,
) {
const oldPassword = passwordBody.oldPassword;
const newPassword = passwordBody.newPassword;

const userInfo = await supertokens.getUser(userId);

if (userInfo === undefined) {
throw new BadRequestException('User not found');
}

const loginMethod = userInfo.loginMethods.find(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(lM: any) =>
lM.recipeUserId.getAsString() ===
session.getRecipeUserId().getAsString() &&
lM.recipeId === 'emailpassword',
);

if (loginMethod === undefined) {
throw new BadRequestException(`You've signed up with different method`);
}
const email = loginMethod.email;

const EmailPassword = supertokensService.getEmailPasswordRecipe();

const isPasswordValid = await EmailPassword.signIn(
session!.getTenantId(),
email,
oldPassword,
);

if (isPasswordValid.status !== 'OK') {
throw new BadRequestException(`Your current password didn't match`);
}

// update the user's password using updateEmailOrPassword
const response = await EmailPassword.updateEmailOrPassword({
recipeUserId: session.getRecipeUserId(),
password: newPassword,
tenantIdForPasswordPolicy: session.getTenantId(),
});

if (response.status === 'PASSWORD_POLICY_VIOLATED_ERROR') {
// TODO: handle incorrect password error
throw new BadRequestException(
`Your new password didn't match with the policy`,
);
}

return { message: 'Successful' };
}

async sendPasswordResetEmail(
supertokensService: SupertokensService,
email: string,
) {
const EmailPassword = supertokensService.getEmailPasswordRecipe();

const user = await this.prisma.user.findUnique({
where: { email },
});

if (!user) {
throw new BadRequestException('User not found');
}

const response = await EmailPassword.createResetPasswordLink(
undefined,
user.id,
email,
);

if (response.status === 'OK') {
await this.mailerService.sendMail({
to: user.email,
subject: 'Password Reset',
template: 'resetPassword',
context: {
username: user.fullname,
resetUrl: response.link,
},
});
this.logger.log('Reset Email sent to user');
}

return response;
}

async resetPassword(
supertokensService: SupertokensService,
token: string,
newPassword: string,
) {
const EmailPassword = supertokensService.getEmailPasswordRecipe();

const response = await EmailPassword.resetPasswordUsingToken(
undefined,
token,
newPassword,
);

if (response.status === 'PASSWORD_POLICY_VIOLATED_ERROR') {
throw new BadRequestException(
`Your new password didn't match with the policy`,
);
} else if (response.status === 'RESET_PASSWORD_INVALID_TOKEN_ERROR') {
throw new BadRequestException(`Invalid reset password token`);
} else if (response.status === 'OK') {
return { message: 'Successful' };
}

throw new BadRequestException(response.status);
}

async getInvitesForUser(email: string) {
const invites = await this.prisma.invite.findMany({
where: { emailId: email, deleted: null },
Expand Down
6 changes: 5 additions & 1 deletion apps/server/src/modules/workspaces/workspaces.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
import { PrismaService } from 'nestjs-prisma';
import { SessionContainer } from 'supertokens-node/recipe/session';

import { createMagicLink } from 'common/utils/login';

import { workflowSeedData } from 'modules/teams/teams.interface';
import { UsersService } from 'modules/users/users.service';

Expand Down Expand Up @@ -229,14 +231,16 @@ export default class WorkspacesService {
},
});

const magicLink = await createMagicLink(email);

await this.mailerService.sendMail({
to: email,
subject: `Invite to ${workspace.name}`,
template: 'inviteUser',
context: {
workspaceName: workspace.name,
inviterName: iniviter.fullname,
invitationUrl: `${process.env.FRONTEND_HOST}/auth`,
invitationUrl: magicLink,
},
});
this.logger.log('Invite Email sent to user');
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/.prod.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
NEXT_PUBLIC_VERSION=0.3.0-alpha
NEXT_PUBLIC_VERSION=0.3.1-alpha
NEXT_PUBLIC_BASE_HOST=PROD_NEXT_PUBLIC_BASE_HOST
NEXT_PUBLIC_BACKEND_HOST=PROD_NEXT_PUBLIC_BACKEND_HOST
NEXT_PUBLIC_SENTRY_DSN=PROD_NEXT_PUBLIC_SENTRY_DSN
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "webapp",
"version": "0.3.0",
"version": "0.3.1",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
4 changes: 3 additions & 1 deletion apps/webapp/src/modules/invites/invites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ export function Invites() {
const context = React.useContext(UserContext);
const { toast } = useToast();
const router = useRouter();

const { mutate: inviteAction, isLoading } = useInviteActionMutation({
onSuccess: (data: Invite) => {
if (data.status === 'ACCEPTED') {
router.replace('/');
toast({
title: 'Invitation accepted',
description: 'Current invitation for the workspace has been accepted',
});

window.location.reload();
}
},
});
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
build:
context: .
dockerfile: ./apps/webapp/Dockerfile
image: tegonhq/tegon-webapp:0.3.0-alpha
image: tegonhq/tegon-webapp:0.3.1-alpha
restart: always
ports:
- 3000:3000
Expand All @@ -18,7 +18,7 @@ services:
build:
context: .
dockerfile: ./apps/server/Dockerfile
image: tegonhq/tegon-server:0.3.0-alpha
image: tegonhq/tegon-server:0.3.1-alpha
restart: always
ports:
- 3001:3001
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tegon",
"version": "0.3.0",
"version": "0.3.1",
"description": "Tegon is an open-source, AI-first alternative to Jira, Linear",
"main": "index.js",
"repository": "https://github.com/tegonhq/tegon.git",
Expand Down
2 changes: 1 addition & 1 deletion packages/services/apiSpec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ openapi: 3.0.0
info:
title: Tegon
description: AI First Project Management Tool
version: 0.3.0
version: 0.3.1

servers:
- url: https://app.tegon.ai
Expand Down

0 comments on commit 91db4dd

Please sign in to comment.