Skip to content
This repository has been archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
✨ Add session endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
AnandChowdhary committed Oct 23, 2020
1 parent 653ccfc commit 0e7c8b2
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import configuration from './config/configuration';
import { AuthModule } from './modules/auth/auth.module';
import { EmailModule } from './modules/email/email.module';
import { PrismaModule } from './modules/prisma/prisma.module';
import { SessionsModule } from './modules/sessions/sessions.module';
import { UsersModule } from './modules/user/user.module';

@Module({
Expand All @@ -23,6 +24,7 @@ import { UsersModule } from './modules/user/user.module';
duration: 60,
}),
EmailModule,
SessionsModule,
],
controllers: [AppController],
providers: [
Expand Down
5 changes: 4 additions & 1 deletion src/modules/prisma/prisma.interface.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export type Expose<T> = Omit<Omit<T, 'password'>, 'twoFactorSecret'>;
export type Expose<T> = Omit<
Omit<Omit<T, 'password'>, 'twoFactorSecret'>,
'token'
>;
4 changes: 3 additions & 1 deletion src/modules/prisma/prisma.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient, users } from '@prisma/client';
import { PrismaClient, sessions, users } from '@prisma/client';
import { Expose } from 'src/modules/prisma/prisma.interface';

@Injectable()
Expand All @@ -13,10 +13,12 @@ export class PrismaService extends PrismaClient
await this.$disconnect();
}

/** Delete sensitive keys from an object */
expose<T>(item: T): Expose<T> {
if (!item) return null;
delete ((item as any) as users).password;
delete ((item as any) as users).twoFactorSecret;
delete ((item as any) as sessions).token;
return item;
}
}
65 changes: 65 additions & 0 deletions src/modules/sessions/sessions.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Query,
UseGuards,
} from '@nestjs/common';
import { sessions } from '@prisma/client';
import { Expose } from 'src/modules/prisma/prisma.interface';
import { CursorPipe } from 'src/pipes/cursor.pipe';
import { OptionalIntPipe } from 'src/pipes/optional-int.pipe';
import { OrderByPipe } from 'src/pipes/order-by.pipe';
import { WherePipe } from 'src/pipes/where.pipe';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { Scopes } from '../auth/scope.decorator';
import { ScopesGuard } from '../auth/scope.guard';
import { SessionsService } from './sessions.service';

@Controller('users/:userId/sessions')
@UseGuards(JwtAuthGuard)
export class SessionController {
constructor(private sessionsService: SessionsService) {}

@Get()
@UseGuards(ScopesGuard)
@Scopes('user{userId}:read', 'session:read')
async getAll(
@Param('userId', ParseIntPipe) userId: number,
@Query('skip', OptionalIntPipe) skip?: number,
@Query('take', OptionalIntPipe) take?: number,
@Query('cursor', CursorPipe) cursor?: Record<string, number | string>,
@Query('where', WherePipe) where?: Record<string, number | string>,
@Query('orderBy', OrderByPipe) orderBy?: Record<string, 'asc' | 'desc'>,
): Promise<Expose<sessions>[]> {
return this.sessionsService.getSessions(userId, {
skip,
take,
orderBy,
cursor,
where,
});
}

@Get(':id')
@UseGuards(ScopesGuard)
@Scopes('user{userId}:read', 'session{id}:read')
async get(
@Param('userId', ParseIntPipe) userId: number,
@Param('id', ParseIntPipe) id: number,
): Promise<Expose<sessions>> {
return this.sessionsService.getSession(userId, { id: Number(id) });
}

@Delete(':id')
@UseGuards(ScopesGuard)
@Scopes('user{userId}:delete', 'session{id}:delete')
async remove(
@Param('userId', ParseIntPipe) userId: number,
@Param('id', ParseIntPipe) id: number,
): Promise<Expose<sessions>> {
return this.sessionsService.deleteSession(userId, { id: Number(id) });
}
}
11 changes: 11 additions & 0 deletions src/modules/sessions/sessions.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { PrismaModule } from '../prisma/prisma.module';
import { SessionController } from './sessions.controller';
import { SessionsService } from './sessions.service';

@Module({
imports: [PrismaModule],
controllers: [SessionController],
providers: [SessionsService],
})
export class SessionsModule {}
76 changes: 76 additions & 0 deletions src/modules/sessions/sessions.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {
HttpException,
HttpStatus,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import {
sessions,
sessionsCreateInput,
sessionsOrderByInput,
sessionsWhereInput,
sessionsWhereUniqueInput,
} from '@prisma/client';
import { Expose } from 'src/modules/prisma/prisma.interface';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class SessionsService {
constructor(private prisma: PrismaService) {}
async createSession(
userId: number,
data: sessionsCreateInput,
): Promise<sessions> {
return this.prisma.sessions.create({
data: { ...data, user: { connect: { id: userId } } },
});
}

async getSessions(
userId: number,
params: {
skip?: number;
take?: number;
cursor?: sessionsWhereUniqueInput;
where?: sessionsWhereInput;
orderBy?: sessionsOrderByInput;
},
): Promise<Expose<sessions>[]> {
const { skip, take, cursor, where, orderBy } = params;
const sessions = await this.prisma.sessions.findMany({
skip,
take,
cursor,
where: { ...where, user: { id: userId } },
orderBy,
});
return sessions.map(user => this.prisma.expose<sessions>(user));
}

async getSession(
userId: number,
sessionWhereUniqueInput: sessionsWhereUniqueInput,
): Promise<Expose<sessions> | null> {
const session = await this.prisma.sessions.findOne({
where: sessionWhereUniqueInput,
});
if (session.userId !== userId) throw new UnauthorizedException();
if (!session)
throw new HttpException('Session not found', HttpStatus.NOT_FOUND);
return this.prisma.expose<sessions>(session);
}

async deleteSession(
userId: number,
where: sessionsWhereUniqueInput,
): Promise<Expose<sessions>> {
const testSession = await this.prisma.sessions.findOne({
where,
});
if (testSession.userId !== userId) throw new UnauthorizedException();
const session = await this.prisma.sessions.delete({
where,
});
return this.prisma.expose<sessions>(session);
}
}

0 comments on commit 0e7c8b2

Please sign in to comment.