Implement token controll to access sessions
parent
337b4f43e0
commit
a95601bc34
|
|
@ -10,6 +10,8 @@ import { Purchase } from './dto/purchase';
|
||||||
import { SessionService } from './service/session.service';
|
import { SessionService } from './service/session.service';
|
||||||
import { SessionController } from './controller/session.controller';
|
import { SessionController } from './controller/session.controller';
|
||||||
import { Session } from './dto/session';
|
import { Session } from './dto/session';
|
||||||
|
import { TokensController } from './controller/tokens.controller';
|
||||||
|
import { TokenService } from './service/token.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -26,7 +28,18 @@ import { Session } from './dto/session';
|
||||||
}),
|
}),
|
||||||
TypeOrmModule.forFeature([User, Purchase, Session]),
|
TypeOrmModule.forFeature([User, Purchase, Session]),
|
||||||
],
|
],
|
||||||
controllers: [AccessController, BuyController, SessionController],
|
controllers: [
|
||||||
providers: [UserService, PurchaseService, StripeService, SessionService],
|
AccessController,
|
||||||
|
BuyController,
|
||||||
|
SessionController,
|
||||||
|
TokensController,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
UserService,
|
||||||
|
PurchaseService,
|
||||||
|
StripeService,
|
||||||
|
SessionService,
|
||||||
|
TokenService,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { UserService } from '../service/user.service';
|
||||||
import { PurchaseItem } from '../dto/purchase-item';
|
import { PurchaseItem } from '../dto/purchase-item';
|
||||||
import { StripeService } from '../service/stripe.service';
|
import { StripeService } from '../service/stripe.service';
|
||||||
import { PurchaseStatus } from 'src/dto/purchase-status';
|
import { PurchaseStatus } from 'src/dto/purchase-status';
|
||||||
|
import { TokenService } from 'src/service/token.service';
|
||||||
|
|
||||||
@Controller('/buy')
|
@Controller('/buy')
|
||||||
export class BuyController {
|
export class BuyController {
|
||||||
|
|
@ -16,6 +17,7 @@ export class BuyController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly purchaseService: PurchaseService,
|
private readonly purchaseService: PurchaseService,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
|
private readonly tokenService: TokenService,
|
||||||
private readonly stripeService: StripeService,
|
private readonly stripeService: StripeService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
@ -54,6 +56,9 @@ export class BuyController {
|
||||||
? PurchaseStatus.COMPLETED
|
? PurchaseStatus.COMPLETED
|
||||||
: PurchaseStatus.IN_PROGRESS;
|
: PurchaseStatus.IN_PROGRESS;
|
||||||
this.purchaseService.recordPurchase(purchase);
|
this.purchaseService.recordPurchase(purchase);
|
||||||
|
if (purchase.status === PurchaseStatus.COMPLETED) {
|
||||||
|
this.tokenService.addTokens(user, purchase);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { url: '/signup' };
|
return { url: '/signup' };
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Controller, Get, Query } from '@nestjs/common';
|
||||||
|
import { TokenService } from 'src/service/token.service';
|
||||||
|
import { UserService } from 'src/service/user.service';
|
||||||
|
|
||||||
|
@Controller('/tokens')
|
||||||
|
export class TokensController {
|
||||||
|
constructor(
|
||||||
|
private readonly userService: UserService,
|
||||||
|
private readonly tokenService: TokenService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
public async getTokenCount(@Query('userId') userId: string) {
|
||||||
|
const user = await this.userService.getUserById(userId);
|
||||||
|
return await this.tokenService.getAvailableTokens(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
export enum SessionStatus {
|
||||||
|
SCHEDULED = 'SCHEDULED',
|
||||||
|
OPEN = 'OPEN',
|
||||||
|
CLOSED = 'CLOSED',
|
||||||
|
CANCELLED = 'CANCELLED',
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
|
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { User } from './user';
|
import { User } from './user';
|
||||||
|
import { SessionStatus } from './session-status';
|
||||||
|
|
||||||
@Entity('SESSIONS')
|
@Entity('SESSIONS')
|
||||||
export class Session {
|
export class Session {
|
||||||
|
|
@ -14,4 +15,7 @@ export class Session {
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
date: Date;
|
date: Date;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
status: SessionStatus;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from 'typeorm';
|
||||||
|
import { User } from './user';
|
||||||
|
|
||||||
|
@Entity('TOKENS')
|
||||||
|
export class Token {
|
||||||
|
@PrimaryColumn()
|
||||||
|
userId: string;
|
||||||
|
|
||||||
|
@OneToOne(() => User, (user) => user.token)
|
||||||
|
@JoinColumn({ name: 'userId' })
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
purchasedTokens: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
consumedTokens: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
lockedTokens: number;
|
||||||
|
|
||||||
|
get availableTokens() {
|
||||||
|
return this.purchasedTokens - this.consumedTokens - this.lockedTokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,10 +4,12 @@ import {
|
||||||
JoinTable,
|
JoinTable,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
|
OneToOne,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { Purchase } from './purchase';
|
import { Purchase } from './purchase';
|
||||||
import { Session } from './session';
|
import { Session } from './session';
|
||||||
|
import { Token } from './token';
|
||||||
|
|
||||||
@Entity('USERS')
|
@Entity('USERS')
|
||||||
export class User {
|
export class User {
|
||||||
|
|
@ -26,4 +28,7 @@ export class User {
|
||||||
@ManyToMany(() => Session, (session) => session.users)
|
@ManyToMany(() => Session, (session) => session.users)
|
||||||
@JoinTable()
|
@JoinTable()
|
||||||
joinedSessions: Session[];
|
joinedSessions: Session[];
|
||||||
|
|
||||||
|
@OneToOne(() => Token, (token) => token.user)
|
||||||
|
token: Token;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Session } from 'src/dto/session';
|
import { Session } from 'src/dto/session';
|
||||||
import { User } from 'src/dto/user';
|
import { User } from 'src/dto/user';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
import { TokenService } from './token.service';
|
||||||
|
import { SessionStatus } from 'src/dto/session-status';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SessionService {
|
export class SessionService {
|
||||||
|
|
@ -10,6 +12,7 @@ export class SessionService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Session) private sessionRepo: Repository<Session>,
|
@InjectRepository(Session) private sessionRepo: Repository<Session>,
|
||||||
|
private tokenService: TokenService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public createSession(date: Date, size: number) {
|
public createSession(date: Date, size: number) {
|
||||||
|
|
@ -17,10 +20,24 @@ export class SessionService {
|
||||||
users: [],
|
users: [],
|
||||||
date,
|
date,
|
||||||
size,
|
size,
|
||||||
|
status: SessionStatus.OPEN,
|
||||||
});
|
});
|
||||||
this.sessionRepo.save(session);
|
this.sessionRepo.save(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async closeSession(sessionId: string) {
|
||||||
|
const session = await this.sessionRepo.findOneOrFail({
|
||||||
|
where: { id: sessionId },
|
||||||
|
});
|
||||||
|
Promise.all(
|
||||||
|
session.users.map(
|
||||||
|
async (user) => await this.tokenService.consumeToken(user),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
session.status = SessionStatus.CLOSED;
|
||||||
|
await this.sessionRepo.save(session);
|
||||||
|
}
|
||||||
|
|
||||||
public getAllSessions() {
|
public getAllSessions() {
|
||||||
return this.sessionRepo.find({
|
return this.sessionRepo.find({
|
||||||
relations: { users: true },
|
relations: { users: true },
|
||||||
|
|
@ -29,6 +46,7 @@ export class SessionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async joinSession(user: User, sessionId: string) {
|
public async joinSession(user: User, sessionId: string) {
|
||||||
|
this.logger.debug(`User: ${user.id} tries to join session: ${sessionId}`);
|
||||||
const session = await this.sessionRepo.findOneOrFail({
|
const session = await this.sessionRepo.findOneOrFail({
|
||||||
where: {
|
where: {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
|
|
@ -46,11 +64,13 @@ export class SessionService {
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await this.tokenService.lockToken(user);
|
||||||
session.users.push(user);
|
session.users.push(user);
|
||||||
await this.sessionRepo.save(session);
|
await this.sessionRepo.save(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async leaveSession(user: User, sessionId: string) {
|
public async leaveSession(user: User, sessionId: string) {
|
||||||
|
this.logger.debug(`User: ${user.id} tries to leave session: ${sessionId}`);
|
||||||
const session = await this.sessionRepo.findOneOrFail({
|
const session = await this.sessionRepo.findOneOrFail({
|
||||||
where: {
|
where: {
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
|
|
@ -59,9 +79,10 @@ export class SessionService {
|
||||||
users: true,
|
users: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!session) {
|
if (!session || !session.users.find((u) => u.id === user.id)) {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
this.tokenService.unlockToken(user);
|
||||||
session.users = session.users.filter((u) => u.id !== user.id);
|
session.users = session.users.filter((u) => u.id !== user.id);
|
||||||
await this.sessionRepo.save(session);
|
await this.sessionRepo.save(session);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
InternalServerErrorException,
|
||||||
|
Logger,
|
||||||
|
NotFoundException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Purchase } from 'src/dto/purchase';
|
||||||
|
import { Token } from 'src/dto/token';
|
||||||
|
import { User } from 'src/dto/user';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TokenService {
|
||||||
|
private readonly logger = new Logger(TokenService.name);
|
||||||
|
|
||||||
|
constructor(@InjectRepository(Token) private tokenRepo: Repository<Token>) {}
|
||||||
|
|
||||||
|
public async addTokens(user: User, purchase: Purchase) {
|
||||||
|
const token = await this.tokenRepo.findOneOrFail({ where: { user: user } });
|
||||||
|
token.purchasedTokens += purchase.purchasedUnits;
|
||||||
|
await this.tokenRepo.save(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAvailableTokens(user: User) {
|
||||||
|
const token = await this.tokenRepo.findOneOrFail({ where: { user: user } });
|
||||||
|
return token.availableTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async consumeToken(user: User) {
|
||||||
|
const token = await this.tokenRepo.findOneOrFail({ where: { user: user } });
|
||||||
|
if (token.lockedTokens <= 0) {
|
||||||
|
throw new InternalServerErrorException();
|
||||||
|
}
|
||||||
|
token.lockedTokens -= 1;
|
||||||
|
token.consumedTokens += 1;
|
||||||
|
await this.tokenRepo.save(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async lockToken(user: User) {
|
||||||
|
const token = await this.tokenRepo.findOneOrFail({ where: { user: user } });
|
||||||
|
if (token.availableTokens <= 0) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
token.lockedTokens += 1;
|
||||||
|
await this.tokenRepo.save(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async unlockToken(user: User) {
|
||||||
|
const token = await this.tokenRepo.findOneOrFail({ where: { user: user } });
|
||||||
|
if (token.lockedTokens <= 0) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
token.lockedTokens -= 1;
|
||||||
|
await this.tokenRepo.save(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue