Skip to content
2 changes: 1 addition & 1 deletion backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const server = http.createServer(app); // ใช้ HTTP Server แทน Expres

// CORS Options
const corsOptions = {
origin: process.env.CORS_ORIGIN || "*",
origin: ['http://localhost:5173','http://bobby.posyayee.com:5173','http://localhost:3000','https://vote.posyayee.com'],
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
exposedHeaders: ["Content-Length", "X-Response-Time"],
Expand Down
80 changes: 25 additions & 55 deletions backend/src/controllers/poll.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ export class PollController {
private pollService: PollService;
private r2Service: R2Service;
constructor(pollService: PollService, r2Service: R2Service) {
this.pollService= pollService;

this.pollService = pollService;
this.r2Service = r2Service;
this.getPoll = this.getPoll.bind(this);
this.getPolls = this.getPolls.bind(this);
this.myPolls = this.myPolls.bind(this);
this.publicPolls = this.publicPolls.bind(this);
this.myVotedPolls = this.myVotedPolls.bind(this);
this.createPollByEventId = this.createPollByEventId.bind(this);
this.getPollResults = this.getPollResults.bind(this);
}

/**
Expand Down Expand Up @@ -265,7 +266,7 @@ export class PollController {

JSON.stringify(polls);


if (!user) {
return res.status(401).json({ success: false, message: "Unauthorized" });
}
Expand Down Expand Up @@ -296,56 +297,25 @@ export class PollController {
}
}

// public getPoll = async (req: Request, res: Response, next: NextFunction) => {
// try {
// const { pollId } = req.params;
// const poll = await this.pollService.getPoll(pollId);
// res.status(200).json({
// message: "Poll fetched successfully",
// data: poll
// });
// } catch (error) {
// next(error);
// }
// }

// public createPoll = async (req: Request, res: Response, next: NextFunction) => {
// try {
// const { poll } = req.body;
// const newPoll = await this.pollService.createPoll(poll);
// res.status(201).json({
// message: "Poll created successfully",
// data: newPoll
// });
// } catch (error) {
// next(error);
// }
// }

// public updatePoll = async (req: Request, res: Response, next: NextFunction) => {
// try {
// const { pollId } = req.params;
// const { poll } = req.body;
// const updatedPoll = await this.pollService.updatePoll(pollId, poll);
// res.status(200).json({
// message: "Poll updated successfully",
// data: updatedPoll
// });
// } catch (error) {
// next(error);
// }
// }

// public deletePoll = async (req: Request, res: Response, next: NextFunction) => {
// try {
// const { pollId } = req.params;
// await this.pollService.deletePoll(pollId);
// res.status(200).json({
// message: "Poll deleted successfully",
// data: null
// });
// } catch (error) {
// next(error);
// }
// }
public async getPollResults(req: Request, res: Response): Promise<any> {
try {
const { pollId } = req.params;
const user = req.user;

if (!user) {
return res.status(401).json({ success: false, message: "Unauthorized" });
}

const pollResults = await this.pollService.result(pollId, user.id, user.guest);

return res.status(200).json({
success: true,
message: "Poll results fetched successfully",
data: pollResults,
});
} catch (error) {
console.error("[ERROR] getPollResults:", error);
return res.status(500).json({ message: "Something went wrong", error });
}
}
}
1 change: 1 addition & 0 deletions backend/src/routes/poll.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ router.get('/public-polls', authMiddleware.validateMulti, pollController.publicP
router.get('/my-voted-polls', authMiddleware.validateMulti, pollController.myVotedPolls);
router.get('/:pollId', getPollByIdValidator(), authMiddleware.validateMulti, pollController.getPoll);
router.post('/:pollId/vote', authMiddleware.validateMulti, voteController.vote);
router.get('/:pollId/result', getAllPollValidator(), authMiddleware.validateMulti, pollController.getPollResults);

export {
router as pollRouters
Expand Down
54 changes: 53 additions & 1 deletion backend/src/services/poll.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class PollService {
public async myVotedPolls(userId: string, isGuest: boolean, logs?: boolean): Promise<{ polls: IPoll[] }> {
try {

if(isGuest){
if (isGuest) {
const rawPolls = await this.prisma.poll.findMany({
where: {
deletedAt: null,
Expand Down Expand Up @@ -436,4 +436,56 @@ export class PollService {
);
}
}

public async result(
pollId: string,
userId: string,
isGuest: boolean,
logs?: boolean
) {
try {
const poll = await this.prisma.poll.findFirst({
where: { id: pollId, deletedAt: null },
include: {
options: {
where: { deletedAt: null },
include: {
_count: {
select: { votes: true },
},
},
},
event: {
where: { deletedAt: null },
},
},
});

if (!poll) {
console.warn(`[WARN] Poll with ID ${pollId} not found.`);
return null;
}

// Count total votes
const totalVotes = poll.options.reduce((sum, option) => sum + option._count.votes, 0);

// Calculate percentage for each option
const optionsWithPercentage = poll.options.map(option => ({
optionId: option.id,
count: option._count.votes,
percentage: totalVotes > 0 ? (option._count.votes / totalVotes) * 100 : 0,
}));

// Return formatted result
const result = {
poll: this.formatPoll(poll, logs),
options: optionsWithPercentage,
};

return result;
} catch (error) {
console.error("[ERROR] result:", error);
throw new Error("Failed to fetch poll result");
}
}
}
11 changes: 7 additions & 4 deletions backend/src/services/queue.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class QueueService {
// Deduct points only for non-public polls
if (isGuest) {
const guest = await tx.guest.findUnique({
where: { id: userId },
where: { id: userId, eventId: poll.eventId },
select: { point: true },
});

Expand All @@ -105,12 +105,15 @@ class QueueService {
if (guest.point < points) throw new Error(`Guest ${userId} does not have enough points.`);

await tx.guest.updateMany({
where: { id: userId },
where: { id: userId ,eventId: poll.eventId },
data: { point: { decrement: points } },
});
} else {
const whitelistUser = await tx.whitelistUser.findFirst({
where: { userId },
where: {
userId: userId,
eventId: poll.eventId,
},
include: { user: true },
});

Expand All @@ -119,7 +122,7 @@ class QueueService {
if (whitelistUser.point < points) throw new Error(`User ${userId} does not have enough points.`);

await tx.whitelistUser.updateMany({
where: { userId: userId },
where: { userId: userId, eventId: poll.eventId },
data: { point: { decrement: points } },
});
}
Expand Down
29 changes: 22 additions & 7 deletions backend/src/services/r2.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,31 @@ export class R2Service {
* Retrieves an image by key
*/
public async getImageByKey(key: string) {
return await this.prisma.image.findUnique({
where: { key },
});
try {
return await this.prisma.image.findUnique({
where: { key },
});
}
catch (error) {
console.error("Error retrieving image by key:", error);
throw new Error("Failed to retrieve image.");
}
}

public async maskeHasUpload(key: string) {
return await this.prisma.image.update({
where: { key },
data: { hasUpload: true },
});
try {
const image = await this.prisma.image.findUnique({ where: { key } });
if (image) {
await this.prisma.image.update({
where: { key },
data: { hasUpload: true },
});
}
}
catch (error) {
console.error("Error updating image hasUpload:", error);
throw new Error("Failed to update image.");
}
}

/**
Expand Down