add invite code support
This commit is contained in:
+2
-4
@@ -3,9 +3,7 @@ import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { VoteController } from './vote/vote.controller';
|
||||
import { VoteService } from './vote/vote.service';
|
||||
import { Answer, Vote } from './vote/vote.entity';
|
||||
import { Answer, InviteCode, Vote } from './vote/vote.entity';
|
||||
import { VoteModule } from './vote/vote.module';
|
||||
|
||||
void ConfigModule.forRoot({
|
||||
@@ -24,7 +22,7 @@ const configService = new ConfigService();
|
||||
username: configService.get('PG_USER'),
|
||||
database: configService.get('PG_DATABASE'),
|
||||
synchronize: configService.get('DEV_MODE') == 'true',
|
||||
entities: [Vote, Answer],
|
||||
entities: [Vote, Answer, InviteCode],
|
||||
}),
|
||||
VoteModule,
|
||||
],
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseUUIDPipe,
|
||||
Post,
|
||||
} from '@nestjs/common';
|
||||
import { VoteCreateDTO } from './vote.dto';
|
||||
import { VoteService } from './vote.service';
|
||||
import { ApiOperation } from '@nestjs/swagger';
|
||||
@@ -7,18 +16,38 @@ import { ApiOperation } from '@nestjs/swagger';
|
||||
export class VoteController {
|
||||
constructor(private readonly voteService: VoteService) {}
|
||||
|
||||
@Get('check/:accessCode')
|
||||
@ApiOperation({ summary: 'Checks if a code has been used' })
|
||||
async check(@Param('accessCode', new ParseUUIDPipe()) accessCode: string) {
|
||||
const value = await this.voteService.check(accessCode);
|
||||
if (value === null) {
|
||||
throw new HttpException('Invalid accessCode', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
if (value === false) {
|
||||
throw new HttpException('AccessCode expired', HttpStatus.FORBIDDEN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@Post('vote/:accessCode')
|
||||
@ApiOperation({ summary: 'Vote for a poll' })
|
||||
async vote(
|
||||
@Param('accessCode') accessCode: string,
|
||||
@Param('accessCode', new ParseUUIDPipe()) accessCode: string,
|
||||
@Body() answer: VoteCreateDTO,
|
||||
) {
|
||||
await this.voteService.create(answer);
|
||||
const code = await this.voteService.check(accessCode);
|
||||
if (code === null) {
|
||||
throw new HttpException('Invalid accessCode', HttpStatus.NOT_FOUND);
|
||||
}
|
||||
if (code === false) {
|
||||
throw new HttpException('AccessCode expired', HttpStatus.FORBIDDEN);
|
||||
}
|
||||
await this.voteService.create(answer, code);
|
||||
}
|
||||
|
||||
@Get('results/:accessCode')
|
||||
@ApiOperation({ summary: 'Get results' })
|
||||
async results(@Param('accessCode') accessCode: string) {
|
||||
async results(@Param('accessCode', new ParseUUIDPipe()) accessCode: string) {
|
||||
return await this.voteService.getResults();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
OneToMany,
|
||||
ManyToOne,
|
||||
Column,
|
||||
OneToOne,
|
||||
Generated,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
@@ -16,6 +18,13 @@ export class Vote {
|
||||
eager: true,
|
||||
})
|
||||
answers: Answer[];
|
||||
|
||||
@OneToOne(() => InviteCode, (code) => code.vote, {
|
||||
eager: false,
|
||||
onDelete: 'CASCADE',
|
||||
nullable: false,
|
||||
})
|
||||
code: Promise<InviteCode>;
|
||||
}
|
||||
|
||||
@Entity()
|
||||
@@ -35,3 +44,22 @@ export class Answer {
|
||||
@Column()
|
||||
answer: string;
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class InviteCode {
|
||||
@PrimaryGeneratedColumn({})
|
||||
id: number;
|
||||
|
||||
@OneToOne(() => Vote, (vote) => vote.code, {
|
||||
eager: false,
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
vote: Promise<Vote>;
|
||||
|
||||
@Column({ default: 1 })
|
||||
usages_left: number;
|
||||
|
||||
@Column()
|
||||
@Generated('uuid')
|
||||
value: string;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { Answer, Vote } from './vote.entity';
|
||||
import { InviteCode, Vote } from './vote.entity';
|
||||
import { VoteController } from './vote.controller';
|
||||
import { VoteService } from './vote.service';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Vote, Answer])],
|
||||
imports: [TypeOrmModule.forFeature([Vote, InviteCode])],
|
||||
controllers: [VoteController],
|
||||
providers: [VoteService],
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { VoteCreateDTO } from './vote.dto';
|
||||
import { Vote } from './vote.entity';
|
||||
import { InviteCode, Vote } from './vote.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
@@ -17,8 +17,13 @@ interface IVote {
|
||||
export class VoteService {
|
||||
constructor(
|
||||
@InjectRepository(Vote) private voteRepository: Repository<Vote>,
|
||||
@InjectRepository(InviteCode)
|
||||
private inviteCodeRepository: Repository<InviteCode>,
|
||||
) {}
|
||||
async create(value: VoteCreateDTO) {
|
||||
async create(value: VoteCreateDTO, accessCode: InviteCode) {
|
||||
accessCode.usages_left--;
|
||||
await this.inviteCodeRepository.save(accessCode);
|
||||
|
||||
const vote = Object.entries(value).reduce<IVote>(
|
||||
(acc, [question_id, a]) => {
|
||||
const answer = String(a);
|
||||
@@ -51,4 +56,17 @@ export class VoteService {
|
||||
}
|
||||
return compiled_results;
|
||||
}
|
||||
|
||||
async check(inviteCode: string) {
|
||||
const code = await this.inviteCodeRepository.findOneBy({
|
||||
value: inviteCode,
|
||||
});
|
||||
if (!code) {
|
||||
return null;
|
||||
}
|
||||
if (code.usages_left > 0) {
|
||||
return code;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user