-
Notifications
You must be signed in to change notification settings - Fork 9
voting system,crud operation with integration testing and hash function to generate anonymous id updated #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Hrusi01
wants to merge
17
commits into
p-society:main
Choose a base branch
from
Hrusi01:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
e5f4181
anonymous hash function
Hrusi01 393caa8
updated hash
Hrusi01 e859a32
finalHash function
Hrusi01 c297efa
old hash file deleted
Hrusi01 827bf99
vote schema added
Hrusi01 18e58f3
index.ts updated
Hrusi01 22eaec2
vote.dto.ts and votes.ts created
Hrusi01 f76c2c5
update
Hrusi01 71238a1
biome fix
Hrusi01 0f55cd9
ci fix
Hrusi01 5222634
ci fix
Hrusi01 13ec4a7
updated the anonymousname
Hrusi01 b89bb5d
Merge branch 'main' into main
Hrusi01 3206e48
sync posts.dto.ts from upstream
Hrusi01 981c6a0
upstream sync lock posts topic pkgJson
Hrusi01 da8b3dd
new vote schema update
Hrusi01 ec51644
updated dto
Hrusi01 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { pgTable, uniqueIndex, uuid,timestamp,boolean, } from "drizzle-orm/pg-core"; | ||
| import { posts } from "./post.schema"; | ||
| import { users } from "./user.schema"; | ||
| export const votes = pgTable( | ||
| "vote", | ||
| { | ||
| //composite Key:userId, postId | ||
| userId: uuid("user_id") | ||
| .references(() => users.id, { onDelete: "restrict" }) | ||
| .notNull(), | ||
|
|
||
| postId: uuid("post_id") | ||
| .references(() => posts.id, { onDelete: "cascade" }) | ||
| .notNull(), | ||
|
|
||
| isUpvote: boolean("is_upvote").notNull().default(false), | ||
| isDownvote: boolean("is_downvote").notNull().default(false), | ||
| deletedAt: timestamp("deleted_at", { mode: "string" }), | ||
|
|
||
| }, | ||
| (table) => ({ | ||
| pk: uniqueIndex("vote_pk").on(table.userId, table.postId), | ||
| }) | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { z } from "zod"; | ||
|
|
||
| export const postIdParamsSchema = z.object({ | ||
| postId: z.string().uuid(), | ||
| }); | ||
| export const votePayloadSchema = z.object({ | ||
| voteValue: z.union([z.literal(1),z.literal(-1),z.literal(0), | ||
| ]), | ||
| }); | ||
| export type VotePayloadInput = z.infer<typeof votePayloadSchema>; | ||
| export type PostIdParams = z.infer<typeof postIdParamsSchema>; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import type { FastifyInstance } from "fastify"; | ||
| import crypto from "node:crypto"; | ||
| import { z } from "zod"; | ||
|
|
||
| export async function anonymousRoutes(fastify: FastifyInstance) { | ||
|
|
||
| const bodySchema = z.object({ | ||
| userId: z.string().uuid(), | ||
| threadId: z.string().uuid(), | ||
| password: z.string().min(1), | ||
| }); | ||
|
|
||
|
|
||
| const animals = [ | ||
| "Tiger", "Wolf", "Falcon", "Panther", "Phoenix", "Leopard", "Eagle", "Raven", | ||
| "Cobra", "Viper", "Hawk", "Dragon", "Lynx", "Jaguar", "Shark", "Stallion", | ||
| "Owl", "Rhino", "Bear", "Panda", "Griffin", "Kraken", "Hydra", "Fox", | ||
| "Bison", "Cheetah", "Gorilla", "Turtle", "Scorpion", "Mantis", | ||
| ]; | ||
|
|
||
| const adjectives = [ | ||
| "Shadow", "Silent", "Ghost", "Crimson", "Blue", "Iron", "Night", "Frost", | ||
| "Storm", "Electric", "Golden", "Cyber", "Wild", "Atomic", "Nebula", "Lunar", | ||
| "Solar", "Thunder", "Noble", "Brave", "Swift", "Hidden", "Obsidian", "Phantom", | ||
| "Radiant", "Mystic", "Silver", "Scarlet", "Feral", "Arcane", | ||
| ]; | ||
|
|
||
| fastify.post("/anon-name", async (request, reply) => { | ||
| const parsed = bodySchema.safeParse(request.body); | ||
|
|
||
| if (!parsed.success) { | ||
| return reply.status(400).send({ | ||
| success: false, | ||
| error: "Invalid userId, threadId, or password", | ||
| }); | ||
| } | ||
|
|
||
| const { userId, threadId, password } = parsed.data; | ||
|
|
||
| try { | ||
| const seed = `${userId}-${threadId}`; | ||
|
|
||
| const hash = crypto | ||
| .createHmac("sha256", password) | ||
| .update(seed) | ||
| .digest("hex"); | ||
|
|
||
| const adjIndex = parseInt(hash.substring(0, 8), 16) % adjectives.length; | ||
| const animalIndex = parseInt(hash.substring(8, 16), 16) % animals.length; | ||
| const num = parseInt(hash.substring(16, 20), 16) % 100; | ||
|
|
||
| const anonName = `${adjectives[adjIndex]}${animals[animalIndex]}${num}`; | ||
|
|
||
| return reply.status(200).send({ | ||
| success: true, | ||
| anonName, | ||
| }); | ||
| } catch (err) { | ||
| fastify.log.error(err); | ||
| return reply.status(500).send({ | ||
| success: false, | ||
| error: "Server error generating anonymous name", | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import type { FastifyInstance } from "fastify"; | ||
| import { z } from "zod"; | ||
| import { handlePostVote } from "@/service/vote.service"; | ||
| import { attachUser, authenticateUser } from "./auth"; | ||
|
|
||
| type VoteParams = { | ||
| postId: string; | ||
| }; | ||
|
|
||
| type VoteBody = { | ||
| voteValue: 1 | -1 | 0; | ||
| }; | ||
|
|
||
| export async function voteRoutes(fastify: FastifyInstance) { | ||
| fastify.post<{ | ||
| Params: VoteParams; | ||
| Body: VoteBody; | ||
| }>( | ||
| "/posts/:postId/vote", | ||
| { | ||
| preHandler: [authenticateUser, attachUser], | ||
| schema: { | ||
| params: z.object({ | ||
| postId: z.string().uuid(), | ||
| }), | ||
| body: z.object({ | ||
| voteValue: z.union([ | ||
| z.literal(1), | ||
| z.literal(-1), | ||
| z.literal(0), | ||
| ]), | ||
| }), | ||
| }, | ||
| }, | ||
| async (request, reply) => { | ||
| const { postId } = request.params; | ||
| const { voteValue } = request.body; | ||
| const userId = request.userId; | ||
|
|
||
| if (!userId) { | ||
| return reply.code(401).send({ error: "Unauthorized" }); | ||
| } | ||
| const updatedPost = await handlePostVote( | ||
| postId, | ||
| userId, | ||
| voteValue | ||
| ); | ||
|
|
||
| return reply.send({ | ||
| success: true, | ||
| data: updatedPost, | ||
| }); | ||
| } | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| import { and, eq, isNull, sql } from "drizzle-orm"; | ||
| import { DrizzleClient as db } from "@/db"; | ||
| import { votes } from "@/db/schema/votes.schema"; | ||
| import { posts } from "@/db/schema/post.schema"; | ||
|
|
||
| export async function handlePostVote( | ||
| postId: string, | ||
| userId: string, | ||
| voteValue: 1 | -1 | 0, | ||
| ) { | ||
| return db.transaction(async (tx) => { | ||
| // fetch existing active vote | ||
| const [existingVote] = await tx | ||
| .select() | ||
| .from(votes) | ||
| .where( | ||
| and( | ||
| eq(votes.postId, postId), | ||
| eq(votes.userId, userId), | ||
| isNull(votes.deletedAt), | ||
| ), | ||
| ) | ||
| .limit(1); | ||
|
|
||
| const oldVoteValue = existingVote | ||
| ? existingVote.isUpvote? 1:-1: 0; | ||
| const delta = voteValue - oldVoteValue; | ||
|
|
||
| // remove vote (soft delete) | ||
| if (voteValue === 0) { | ||
| if (existingVote) { | ||
| await tx | ||
| .update(votes) | ||
| .set({ deletedAt: new Date().toISOString() }) | ||
| .where( | ||
| and( | ||
| eq(votes.userId, userId), | ||
| eq(votes.postId, postId), | ||
| ), | ||
| ); | ||
| } | ||
| } else { | ||
| // insert or update vote | ||
| await tx | ||
| .insert(votes) | ||
| .values({ | ||
| userId, | ||
| postId, | ||
| isUpvote: voteValue === 1, | ||
| isDownvote: voteValue === -1, | ||
| deletedAt: null, | ||
| }) | ||
| .onConflictDoUpdate({ | ||
| target: [votes.userId, votes.postId], | ||
| set: { | ||
| isUpvote: voteValue === 1, | ||
| isDownvote: voteValue === -1, | ||
| deletedAt: sql`NULL`, | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| // update post vote count | ||
| const [updatedPost] = await tx | ||
| .update(posts) | ||
| .set({ | ||
| vote: sql`${posts.vote} + ${delta}`, | ||
| }) | ||
| .where(eq(posts.id, postId)) | ||
| .returning(); | ||
|
|
||
| if (!updatedPost) { | ||
| tx.rollback(); | ||
| throw new Error("Post not found"); | ||
| } | ||
|
|
||
| return updatedPost; | ||
| }); | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think auth checks are needed here.