Skip to content

Commit 958507c

Browse files
committed
fixed conflicts
2 parents a9b92b0 + ccf0fd0 commit 958507c

File tree

10 files changed

+199
-30
lines changed

10 files changed

+199
-30
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DATABASE_URL=
2+
WHITELISTED_ORIGINS=
3+
TUNNEL_DOMAINS=

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ lerna-debug.log*
4141
.env.test.local
4242
.env.production.local
4343
.env.local
44+
!.env.example
4445

4546
# temp directory
4647
.temp

src/auth/auth.controller.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@ import { MiniAppWalletAuthSuccessPayload } from '@worldcoin/minikit-js';
66
interface IRequestPayload {
77
payload: MiniAppWalletAuthSuccessPayload;
88
}
9+
10+
function isHttps(req: Request) {
11+
return (
12+
req.protocol === 'https' || req.headers['x-forwarded-proto'] === 'https'
13+
);
14+
}
15+
916
@Controller('auth')
1017
export class AuthController {
1118
constructor(private readonly authService: AuthService) {}
1219

1320
@Get('nonce')
14-
async generateNonce(@Res() res: Response): Promise<any> {
21+
generateNonce(@Req() req: Request, @Res() res: Response) {
1522
const nonce = this.authService.generateNonce();
1623
res.cookie('siwe', nonce, {
1724
httpOnly: true,
18-
secure: process.env.NODE_ENV === 'production',
19-
sameSite: 'strict',
25+
secure: process.env.NODE_ENV === 'production' || isHttps(req),
26+
sameSite: 'none',
2027
maxAge: 2 * 60 * 1000, //2 minutes
2128
});
2229

@@ -45,6 +52,7 @@ export class AuthController {
4552
);
4653
return res.status(200).json({ isValid: validMessage });
4754
} catch (error: any) {
55+
console.log('Error verifying payload:', error);
4856
return res
4957
.status(400)
5058
.json({ status: 'error', isValid: false, message: error.message });

src/main.ts

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,54 @@ import { ValidationPipe } from '@nestjs/common';
66
async function bootstrap() {
77
const app = await NestFactory.create(AppModule);
88

9-
app.useGlobalPipes(
10-
new ValidationPipe({
11-
transform: true,
12-
}),
13-
);
9+
const environment = process.env.NODE_ENV || 'development';
10+
const rawOrigins = process.env.WHITELISTED_ORIGINS || '';
11+
const rawTunnelDomains = process.env.TUNNEL_DOMAINS || '';
12+
13+
const whiteListedOrigins = rawOrigins
14+
.split(',')
15+
.map((origin) => origin.trim().replace(/^https?:\/\//, ''))
16+
.filter((origin) => !!origin);
17+
18+
const tunnelDomains = rawTunnelDomains
19+
.split(',')
20+
.map((domain) => domain.trim())
21+
.filter((domain) => !!domain);
22+
23+
app.enableCors({
24+
origin: (
25+
origin: string | undefined,
26+
callback: (err: Error | null, allow?: boolean) => void,
27+
) => {
28+
if (!origin) return callback(null, true);
29+
30+
try {
31+
const { hostname } = new URL(origin);
32+
33+
const isWhitelisted = whiteListedOrigins.includes(hostname);
34+
35+
const isTunnelDomain =
36+
environment !== 'production' &&
37+
tunnelDomains.some((testDomain) => hostname.endsWith(testDomain));
38+
39+
if (isWhitelisted || isTunnelDomain) {
40+
return callback(null, true);
41+
}
42+
43+
console.error(`[CORS ERROR]: Origin ${origin} is not allowed.`);
44+
return callback(new Error(`CORS blocked for origin: ${origin}`));
45+
} catch (error) {
46+
console.error(`[CORS ERROR]: Malformed origin`, error);
47+
return callback(new Error(`Invalid origin: ${origin}`));
48+
}
49+
},
50+
credentials: true,
51+
});
52+
53+
app.useGlobalPipes(new ValidationPipe({ transform: true }));
1454
app.use(cookieParser());
1555

1656
await app.listen(process.env.PORT ?? 3000);
1757
}
58+
1859
bootstrap();

src/poll/Poll.dto.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import {
1212
} from 'class-validator';
1313

1414
export class CreatePollDto {
15+
@IsString()
16+
@IsNotEmpty()
17+
worldID: string;
18+
1519
@IsString()
1620
@IsNotEmpty()
1721
title: string;
@@ -44,6 +48,10 @@ export class CreatePollDto {
4448
}
4549

4650
export class GetPollsDto {
51+
@IsString()
52+
@IsOptional()
53+
worldID?: string;
54+
4755
@IsOptional()
4856
@Type(() => Number)
4957
@IsInt()
@@ -79,3 +87,9 @@ export class GetPollsDto {
7987
@IsEnum(['asc', 'desc'])
8088
sortOrder?: 'asc' | 'desc';
8189
}
90+
91+
export class DeletePollDto {
92+
@IsString()
93+
@IsNotEmpty()
94+
worldID: string;
95+
}

src/poll/poll.controller.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import {
1010
Delete,
1111
UsePipes,
1212
ValidationPipe,
13+
BadRequestException,
1314
} from '@nestjs/common';
1415
import { Response } from 'express';
1516
import { PollService } from './poll.service';
16-
import { CreatePollDto, GetPollsDto } from './Poll.dto';
17+
import { CreatePollDto, DeletePollDto, GetPollsDto } from './Poll.dto';
1718

1819
@Controller('poll')
1920
export class PollController {
@@ -22,15 +23,24 @@ export class PollController {
2223
@Post()
2324
@UsePipes(ValidationPipe)
2425
create(@Body() createPollDto: CreatePollDto) {
25-
const userId = 1; // need to implement Auth
26-
return this.pollService.createPoll(userId, createPollDto);
26+
return this.pollService.createPoll(createPollDto);
2727
}
2828

2929
@Get()
3030
@UsePipes(ValidationPipe)
31-
getPolls(@Req() req, @Query() query: GetPollsDto) {
32-
const userId = 1;
33-
return this.pollService.getPolls(userId, query);
31+
async getPolls(
32+
@Req() req,
33+
@Query() query: GetPollsDto,
34+
@Res() res: Response,
35+
) {
36+
try {
37+
const polls = await this.pollService.getPolls(query);
38+
return res.status(200).json(polls);
39+
} catch (error: unknown) {
40+
const errorMessage =
41+
error instanceof Error ? error.message : 'An unexpected error occurred';
42+
throw new BadRequestException(errorMessage);
43+
}
3444
}
3545

3646
@Get(':id')
@@ -50,16 +60,19 @@ export class PollController {
5060
}
5161

5262
@Delete(':id')
53-
async deletePoll(@Param('id') id: number, @Res() res: Response) {
54-
const userId = 1; // need to implement Auth
63+
async deletePoll(
64+
@Param('id') id: number,
65+
@Body() query: DeletePollDto,
66+
@Res() res: Response,
67+
) {
5568
try {
56-
const poll = await this.pollService.deletePoll(userId, Number(id));
69+
const poll = await this.pollService.deletePoll(Number(id), query);
5770

5871
return res.status(200).json({ message: 'Poll deleted', poll: poll });
59-
} catch (error) {
60-
return res
61-
.status(500)
62-
.json({ message: 'Internal server error', error: error.message });
72+
} catch (error: unknown) {
73+
const errorMessage =
74+
error instanceof Error ? error.message : 'An unexpected error occurred';
75+
throw new BadRequestException(errorMessage);
6376
}
6477
}
6578
}

src/poll/poll.service.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { BadRequestException, Injectable } from '@nestjs/common';
22
import { ActionType, Prisma } from '@prisma/client';
33
import { DatabaseService } from 'src/database/database.service';
4-
import { CreatePollDto, GetPollsDto } from './Poll.dto';
4+
import { CreatePollDto, DeletePollDto, GetPollsDto } from './Poll.dto';
55

66
@Injectable()
77
export class PollService {
88
constructor(private readonly databaseService: DatabaseService) {}
99

10-
async createPoll(userId: number, createPollDto: CreatePollDto) {
10+
async createPoll(createPollDto: CreatePollDto) {
1111
const user = await this.databaseService.user.findUnique({
12-
where: { id: userId },
12+
where: { worldID: createPollDto.worldID },
1313
});
1414
if (!user) {
1515
throw new BadRequestException('User does not exist');
@@ -30,7 +30,7 @@ export class PollService {
3030
// Create the poll
3131
const newPoll = await tx.poll.create({
3232
data: {
33-
authorUserId: userId,
33+
authorUserId: user.id,
3434
title: createPollDto.title,
3535
description: createPollDto.description,
3636
options: createPollDto.options,
@@ -45,15 +45,15 @@ export class PollService {
4545
// Create user action for CREATED
4646
await tx.userAction.create({
4747
data: {
48-
userId,
48+
userId: user.id,
4949
pollId: newPoll.pollId,
5050
type: ActionType.CREATED,
5151
},
5252
});
5353

5454
// Update user's pollsCreatedCount
5555
await tx.user.update({
56-
where: { id: userId },
56+
where: { worldID: createPollDto.worldID },
5757
data: {
5858
pollsCreatedCount: {
5959
increment: 1,
@@ -65,7 +65,7 @@ export class PollService {
6565
});
6666
}
6767

68-
async getPolls(userId: number, query: GetPollsDto) {
68+
async getPolls(query: GetPollsDto) {
6969
const {
7070
page = 1,
7171
limit = 10,
@@ -78,6 +78,8 @@ export class PollService {
7878
const skip = (page - 1) * limit;
7979
const now = new Date();
8080
const filters: any = {};
81+
let userId: number | undefined;
82+
8183
if (isActive) {
8284
filters.startDate = { lte: now };
8385
filters.endDate = { gt: now };
@@ -86,6 +88,20 @@ export class PollService {
8688
if (isActive === false) {
8789
filters.OR = [{ startDate: { gt: now } }, { endDate: { lte: now } }];
8890
}
91+
if ((userCreated || userVoted) && query.worldID) {
92+
const user = await this.databaseService.user.findUnique({
93+
where: { worldID: query.worldID },
94+
select: { id: true },
95+
});
96+
97+
if (!user) {
98+
throw new Error('User not found');
99+
}
100+
userId = user.id;
101+
} else if (userCreated || userVoted) {
102+
throw new Error('worldId Not Provided');
103+
}
104+
89105
if (userCreated) {
90106
filters.authorUserId = userId;
91107
}
@@ -139,15 +155,24 @@ export class PollService {
139155
return { user, poll, isActive };
140156
}
141157

142-
async deletePoll(userId: number, pollId: number) {
158+
async deletePoll(pollId: number, query: DeletePollDto) {
159+
const user = await this.databaseService.user.findUnique({
160+
where: { worldID: query.worldID },
161+
select: { id: true },
162+
});
163+
164+
if (!user) {
165+
throw new Error('User not found');
166+
}
167+
143168
const poll = await this.databaseService.poll.findUnique({
144169
where: { pollId },
145170
});
146171

147172
if (!poll) {
148173
throw new Error('Poll not found');
149174
}
150-
if (poll.authorUserId !== userId) {
175+
if (poll.authorUserId !== user.id) {
151176
throw new Error('User Not Authorized');
152177
}
153178

src/user/user.controller.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ import {
55
Get,
66
Post,
77
Query,
8+
UsePipes,
9+
ValidationPipe,
810
} from '@nestjs/common';
911
import { UserService } from './user.service';
1012
import {
13+
CreateUserDto,
14+
CreateUserResponseDto,
1115
EditVoteDto,
1216
EditVoteResponseDto,
1317
GetUserActivitiesDto,
@@ -84,4 +88,15 @@ export class UserController {
8488
throw new BadRequestException(errorMessage);
8589
}
8690
}
91+
92+
@Post('createUser')
93+
async createUser(@Body() dto: CreateUserDto): Promise<CreateUserResponseDto> {
94+
try {
95+
return await this.userService.createUser(dto);
96+
} catch (error: unknown) {
97+
const errorMessage =
98+
error instanceof Error ? error.message : 'An unexpected error occurred';
99+
throw new BadRequestException(errorMessage);
100+
}
101+
}
87102
}

src/user/user.dto.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
2+
13
export class GetUserDataDto {
24
worldID: string;
35
}
@@ -61,3 +63,21 @@ export class EditVoteDto {
6163
export class EditVoteResponseDto {
6264
actionId: number;
6365
}
66+
67+
export class CreateUserDto {
68+
@IsString()
69+
@IsNotEmpty()
70+
name: string;
71+
72+
@IsString()
73+
@IsNotEmpty()
74+
worldID: string;
75+
76+
@IsString()
77+
@IsOptional()
78+
profilePicture?: string;
79+
}
80+
81+
export class CreateUserResponseDto {
82+
userId: number;
83+
}

0 commit comments

Comments
 (0)