Skip to content

Added whitelist for event#609

Open
PaulicStudios wants to merge 3 commits intodevfrom
added-whitelist-for-event
Open

Added whitelist for event#609
PaulicStudios wants to merge 3 commits intodevfrom
added-whitelist-for-event

Conversation

@PaulicStudios
Copy link
Copy Markdown
Member

@PaulicStudios PaulicStudios commented Apr 4, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Event organizers can now create and manage whitelists to control event access by username and platform (GitHub or 42).
    • Private events now require whitelist approval for joining.
    • New whitelist management interface in the event dashboard with add, remove, and bulk delete capabilities.
  • Improvements

    • Enhanced authentication responses to include linked social accounts.

This commit:
- Replaces the incremental migration with a fresh table creation
  migration to avoid constraint conflicts
- Updates event service to use correct method names and improve
  whitelist checking logic
- Adds validation improvements to whitelist DTOs
- Fixes frontend whitelist component to handle null data and
  improve accessibility
- Updates user service to separate user retrieval with/without
  social accounts
- Refactors database query in removeEventAdmin to use query builder
  with INNER JOIN for pessimistic locking compatibility
@PaulicStudios PaulicStudios requested a review from Peu77 April 4, 2026 21:45
@PaulicStudios PaulicStudios self-assigned this Apr 4, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 4, 2026

📝 Walkthrough

Walkthrough

This PR introduces event whitelist functionality to control access to private events. It adds a new event_whitelists database table, entity, and service methods; new admin endpoints for managing whitelist entries; access control enforcement in the joinEvent endpoint; and a frontend UI component with corresponding server actions for managing whitelists.

Changes

Cohort / File(s) Summary
Database & Migration
api/db/migrations/1775336799887-event-whitelist.ts, api/src/event/entities/event-whitelist.entity.ts, api/src/event/entities/event.entity.ts
Adds event_whitelists table with EventWhitelistEntity ORM mapping, WhitelistPlatform enum (GITHUB, FORTYTWO), and bidirectional OneToMany/ManyToOne relationship with EventEntity.
API DTOs & Service Layer
api/src/event/dtos/whitelistDto.ts, api/src/event/event.service.ts, api/src/event/event.module.ts
Introduces WhitelistEntryDto, AddToWhitelistDto, BulkDeleteWhitelistDto DTOs; implements whitelist CRUD methods (getWhitelist, addToWhitelist, removeFromWhitelist, bulkRemoveFromWhitelist, hasWhitelist, isUserWhitelisted) in EventService; updates EventModule to register EventWhitelistEntity.
Event Controller & Endpoint
api/src/event/event.controller.ts
Adds four new admin-gated whitelist endpoints (GET/POST/DELETE/bulk-delete); enforces access control in joinEvent to check user whitelist status before allowing participation in private events.
User Service Updates
api/src/user/user.service.ts, api/src/auth/auth.controller.ts
Adds getUserWithSocialAccounts() method; updates /auth/me endpoint to return user with social account relations; refactors getUserById() to use new findOneOrFail syntax.
Frontend Server Actions
frontend/app/actions/event.ts
Adds WhitelistEntry interface and four new server actions (getEventWhitelist, addToWhitelist, removeFromWhitelist, bulkRemoveFromWhitelist); updates joinEvent to return structured response with error handling.
Frontend UI Components
frontend/components/ui/checkbox.tsx, frontend/components/event-info-notice.tsx
Adds new Radix UI-based Checkbox component; updates join-event notice to handle whitelist-related failures and display toast messages; invalidates correct query key after join.
Frontend Dashboard
frontend/app/events/[id]/dashboard/components/WhitelistManagement.tsx, frontend/app/events/[id]/dashboard/dashboard.tsx
Introduces WhitelistManagement React component with table, add/remove/bulk-delete UI, modal for batch entry, and TanStack Query integration; adds Whitelist tab to event dashboard.
Configuration & Dependencies
frontend/package.json, local-setup/local-values/api.yaml
Adds @radix-ui/react-checkbox dependency; renames OAuth environment variable from OAUTH_SUCCESS_REDIRECT_URL to OAUTH_42_SUCCESS_REDIRECT_URL.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • feat: added admin settings page #488 — Modifies event admin management logic (removeEventAdmin in EventService), which overlaps with the new pessimistic-lock query pattern and new admin whitelist endpoints.

Suggested reviewers

  • Peu77

Poem

🐰 A whitelist grows, secure and bright,
Gatekeeping access—who joins the night?
Platform checks for GitHub friends,
Or 42 crew—the story bends!
Private events now have their say,
Keep the riffraff out of play! 🎪

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Added whitelist for event' accurately summarizes the primary change: implementation of a whitelist feature for events, including database migration, entity, DTOs, service methods, controller endpoints, and UI components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch added-whitelist-for-event

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
api/src/event/dtos/whitelistDto.ts (1)

5-11: Consider adding @IsNotEmpty() to prevent empty username strings.

The @IsString() decorator allows empty strings. Adding @IsNotEmpty() ensures usernames are meaningful.

Proposed fix
+import { ArrayNotEmpty, IsArray, IsEnum, IsNotEmpty, IsString, IsUUID, ValidateNested } from "class-validator";
-import { ArrayNotEmpty, IsArray, IsEnum, IsString, IsUUID, ValidateNested } from "class-validator";
 import { Type } from "class-transformer";
 import { WhitelistPlatform } from "../entities/event-whitelist.entity";

 export class WhitelistEntryDto {
   `@IsString`()
+  `@IsNotEmpty`()
   username: string;

   `@IsEnum`(WhitelistPlatform)
   platform: WhitelistPlatform;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/src/event/dtos/whitelistDto.ts` around lines 5 - 11, Add a non-empty
validation to the username field on WhitelistEntryDto: update the
WhitelistEntryDto class so the username property is decorated with `@IsNotEmpty`()
in addition to `@IsString`(), ensuring empty strings are rejected by validation;
keep the existing `@IsEnum`(WhitelistPlatform) on platform unchanged.
frontend/app/events/[id]/dashboard/components/WhitelistManagement.tsx (1)

77-119: Don't widen mutation errors to any.

These three onError handlers throw away the type information coming from useMutation and make .message unchecked again. Please verify this against the installed @tanstack/react-query typings and keep the inferred type, or narrow before reading the message.

Type-safe error handling
-    onError: (e: any) => toast.error(e.message),
+    onError: (error) =>
+      toast.error(
+        error instanceof Error ? error.message : "An unexpected error occurred",
+      ),

Apply the same change to the other two mutations.

As per coding guidelines, `**/*.{ts,tsx}`: Use strict TypeScript typing and avoid any type where possible, using interfaces/types for DTOs and props instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/events/`[id]/dashboard/components/WhitelistManagement.tsx around
lines 77 - 119, The onError handlers for addMutation, removeMutation, and
bulkRemoveMutation currently accept e: any and lose type information; change
each onError to accept e: unknown (or the specific error type from
`@tanstack/react-query` if available) and narrow it before reading message — e.g.,
check if e is an instance of Error and use e.message, otherwise render a safe
fallback like String(e) or a generic error string; update the onError signatures
for useMutation calls in WhitelistManagement.tsx (the addMutation,
removeMutation, and bulkRemoveMutation handlers) accordingly.
frontend/app/actions/event.ts (1)

253-284: Parameterize the whitelist Axios calls.

handleError infers its generic from the Axios promise, and these helpers currently pass untyped get/post/delete calls, so the new ServerActionResponse<WhitelistEntry[]> / ServerActionResponse<void> contracts collapse to any. Please verify the payloads against the backend routes and set the Axios generics here so contract drift is caught by TypeScript.

Preserve the declared response types
 export async function getEventWhitelist(
   eventId: string,
 ): Promise<ServerActionResponse<WhitelistEntry[]>> {
-  return await handleError(axiosInstance.get(`event/${eventId}/whitelist`));
+  return await handleError(
+    axiosInstance.get<WhitelistEntry[]>(`event/${eventId}/whitelist`),
+  );
 }
 
 export async function addToWhitelist(
   eventId: string,
   entries: { username: string; platform: "GITHUB" | "FORTYTWO" }[],
 ): Promise<ServerActionResponse<WhitelistEntry[]>> {
   return await handleError(
-    axiosInstance.post(`event/${eventId}/whitelist`, { entries }),
+    axiosInstance.post<WhitelistEntry[]>(`event/${eventId}/whitelist`, { entries }),
   );
 }
 
 export async function removeFromWhitelist(
   eventId: string,
   whitelistId: string,
 ): Promise<ServerActionResponse<void>> {
   return await handleError(
-    axiosInstance.delete(`event/${eventId}/whitelist/${whitelistId}`),
+    axiosInstance.delete<void>(`event/${eventId}/whitelist/${whitelistId}`),
   );
 }
 
 export async function bulkRemoveFromWhitelist(
   eventId: string,
   ids: string[],
 ): Promise<ServerActionResponse<void>> {
   return await handleError(
-    axiosInstance.post(`event/${eventId}/whitelist/bulk-delete`, { ids }),
+    axiosInstance.post<void>(`event/${eventId}/whitelist/bulk-delete`, { ids }),
   );
 }
As per coding guidelines, `**/*.{ts,tsx}`: Use strict TypeScript typing and avoid any type where possible, using interfaces/types for DTOs and props instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/actions/event.ts` around lines 253 - 284, The axios calls in
getEventWhitelist, addToWhitelist, removeFromWhitelist, and
bulkRemoveFromWhitelist are untyped so handleError can't infer the declared
ServerActionResponse<...> results; update each axiosInstance call to include the
correct response generics (e.g.
axiosInstance.get<ServerActionResponse<WhitelistEntry[]>>(...) and
axiosInstance.post<ServerActionResponse<WhitelistEntry[]>>(...) /
post<ServerActionResponse<void>> / delete<ServerActionResponse<void>>), and also
type the request payloads for the POSTs (ensure the entries and ids match the
backend DTO types) so TypeScript will catch contract drift when compiling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/src/event/event.controller.ts`:
- Around line 176-194: When handling private events in the event join flow, gate
the whitelist check behind a presence check so an empty whitelist does not block
joins: call this.eventService.hasWhitelist(eventId) first and only call
this.eventService.isUserWhitelisted(...) (after fetching the user via
this.userService.getUserWithSocialAccounts(userId) and extracting
fortyTwoAccount) if hasWhitelist returns true; if hasWhitelist is false, allow
link-only access and do not throw the UnauthorizedException for an empty
whitelist.

In `@api/src/event/event.service.ts`:
- Around line 697-723: The current read-then-save flow around
existingEntries/newEntries races the DB unique constraint (existingSet check
then whitelistRepository.save) so concurrent requests can still cause a
duplicate-key error; fix by performing an insert that ignores conflicts or by
catching the duplicate-key error and treating it as idempotent. Replace the save
path for whitelistEntries (where newEntries are created) with a single bulk
insert that uses your ORM's conflict-ignore/upsert behavior (e.g.,
whitelistRepository.createQueryBuilder().insert().values(...).orIgnore() or
equivalent) so duplicates are handled at insert time, or wrap
whitelistRepository.save(...) in a try/catch that detects the DB
unique-constraint error (e.g., Postgres 23505) and returns as if no-op; refer to
whitelistRepository.save, whitelistRepository.create, newEntries,
uniqueInputEntries and existingSet to locate and update the code.

In `@frontend/app/events/`[id]/dashboard/components/WhitelistManagement.tsx:
- Around line 92-104: The onSuccess handlers for whitelist mutations (e.g.,
removeMutation and the bulk delete mutation referenced around lines 151-168)
only invalidate the query but do not update the local selectedIds state, leaving
deleted IDs in selectedIds and causing stale bulk UI and failed subsequent
deletes; after a successful mutation, update selectedIds by removing the deleted
id(s) (for removeMutation drop the single whitelistId; for bulk delete drop all
returned/removed ids or intersect selectedIds with the refreshed list), or
compute selectedIds = selectedIds.filter(id => refreshedList.includes(id))
before or after invalidation so the selection reflects the new list and the bulk
toolbar/count disappears appropriately.

In `@local-setup/local-values/api.yaml`:
- Line 30: The GitHub OAuth callback in auth.controller.ts references the old
env key OAUTH_SUCCESS_REDIRECT_URL which no longer exists; update the GitHub
callback handler (in api/src/auth/auth.controller.ts, the code that calls
getOrThrow for the redirect URL) to use the renamed
OAUTH_42_SUCCESS_REDIRECT_URL or alternatively add and use a clearer
OAUTH_GITHUB_SUCCESS_REDIRECT_URL; ensure the getOrThrow call and any variable
names in the GitHub callback method are updated to match the chosen env key so
the value resolves at runtime.

---

Nitpick comments:
In `@api/src/event/dtos/whitelistDto.ts`:
- Around line 5-11: Add a non-empty validation to the username field on
WhitelistEntryDto: update the WhitelistEntryDto class so the username property
is decorated with `@IsNotEmpty`() in addition to `@IsString`(), ensuring empty
strings are rejected by validation; keep the existing `@IsEnum`(WhitelistPlatform)
on platform unchanged.

In `@frontend/app/actions/event.ts`:
- Around line 253-284: The axios calls in getEventWhitelist, addToWhitelist,
removeFromWhitelist, and bulkRemoveFromWhitelist are untyped so handleError
can't infer the declared ServerActionResponse<...> results; update each
axiosInstance call to include the correct response generics (e.g.
axiosInstance.get<ServerActionResponse<WhitelistEntry[]>>(...) and
axiosInstance.post<ServerActionResponse<WhitelistEntry[]>>(...) /
post<ServerActionResponse<void>> / delete<ServerActionResponse<void>>), and also
type the request payloads for the POSTs (ensure the entries and ids match the
backend DTO types) so TypeScript will catch contract drift when compiling.

In `@frontend/app/events/`[id]/dashboard/components/WhitelistManagement.tsx:
- Around line 77-119: The onError handlers for addMutation, removeMutation, and
bulkRemoveMutation currently accept e: any and lose type information; change
each onError to accept e: unknown (or the specific error type from
`@tanstack/react-query` if available) and narrow it before reading message — e.g.,
check if e is an instance of Error and use e.message, otherwise render a safe
fallback like String(e) or a generic error string; update the onError signatures
for useMutation calls in WhitelistManagement.tsx (the addMutation,
removeMutation, and bulkRemoveMutation handlers) accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d17d4ed1-5fc5-476b-8acd-d34d0f62554c

📥 Commits

Reviewing files that changed from the base of the PR and between d821193 and 0e47e21.

⛔ Files ignored due to path filters (1)
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • api/db/migrations/1775336799887-event-whitelist.ts
  • api/src/auth/auth.controller.ts
  • api/src/event/dtos/whitelistDto.ts
  • api/src/event/entities/event-whitelist.entity.ts
  • api/src/event/entities/event.entity.ts
  • api/src/event/event.controller.ts
  • api/src/event/event.module.ts
  • api/src/event/event.service.ts
  • api/src/user/user.service.ts
  • frontend/app/actions/event.ts
  • frontend/app/events/[id]/dashboard/components/WhitelistManagement.tsx
  • frontend/app/events/[id]/dashboard/dashboard.tsx
  • frontend/components/event-info-notice.tsx
  • frontend/components/ui/checkbox.tsx
  • frontend/package.json
  • local-setup/local-values/api.yaml

Comment on lines +697 to +723
const existingEntries = await this.whitelistRepository.find({
where: {
event: { id: eventId },
},
});

const existingSet = new Set(
existingEntries.map((e) => `${e.username}:${e.platform}`),
);

const newEntries = uniqueInputEntries.filter(
(entry) => !existingSet.has(`${entry.username}:${entry.platform}`),
);

if (newEntries.length === 0) {
return [];
}

const whitelistEntries = newEntries.map((entry) =>
this.whitelistRepository.create({
event,
username: entry.username,
platform: entry.platform,
}),
);

return this.whitelistRepository.save(whitelistEntries);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The duplicate filter still races the unique constraint.

This path does a read of existing rows and then a separate save(). Two concurrent adds of the same username can both pass the existingSet filter, and one request will then fail on the unique key from api/db/migrations/1775336799887-event-whitelist.ts Line 7 instead of behaving like a no-op. Please handle conflicts at insert time, or catch the duplicate-key case and treat it as idempotent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/src/event/event.service.ts` around lines 697 - 723, The current
read-then-save flow around existingEntries/newEntries races the DB unique
constraint (existingSet check then whitelistRepository.save) so concurrent
requests can still cause a duplicate-key error; fix by performing an insert that
ignores conflicts or by catching the duplicate-key error and treating it as
idempotent. Replace the save path for whitelistEntries (where newEntries are
created) with a single bulk insert that uses your ORM's conflict-ignore/upsert
behavior (e.g.,
whitelistRepository.createQueryBuilder().insert().values(...).orIgnore() or
equivalent) so duplicates are handled at insert time, or wrap
whitelistRepository.save(...) in a try/catch that detects the DB
unique-constraint error (e.g., Postgres 23505) and returns as if no-op; refer to
whitelistRepository.save, whitelistRepository.create, newEntries,
uniqueInputEntries and existingSet to locate and update the code.

Comment on lines +92 to +104
const removeMutation = useMutation({
mutationFn: async (whitelistId: string) => {
const result = await removeFromWhitelist(eventId, whitelistId);
if (isActionError(result))
throw new Error(result.error);
return result;
},
onSuccess: () => {
toast.success("User removed from whitelist");
queryClient.invalidateQueries({ queryKey: ["event", eventId, "whitelist"] });
},
onError: (e: any) => toast.error(e.message),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prune selectedIds after whitelist mutations.

removeMutation.onSuccess only invalidates the query. If the deleted row was selected, selectedIds still contains it after the refetch, so the bulk toolbar/count stay visible and the next bulk delete resends a dead id. Intersect the local selection with the refreshed list, or drop the removed id in onSuccess.

Also applies to: 151-168

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/events/`[id]/dashboard/components/WhitelistManagement.tsx around
lines 92 - 104, The onSuccess handlers for whitelist mutations (e.g.,
removeMutation and the bulk delete mutation referenced around lines 151-168)
only invalidate the query but do not update the local selectedIds state, leaving
deleted IDs in selectedIds and causing stale bulk UI and failed subsequent
deletes; after a successful mutation, update selectedIds by removing the deleted
id(s) (for removeMutation drop the single whitelistId; for bulk delete drop all
returned/removed ids or intersect selectedIds with the refreshed list), or
compute selectedIds = selectedIds.filter(id => refreshedList.includes(id))
before or after invalidation so the selection reflects the new list and the bulk
toolbar/count disappears appropriately.

AUTH_COOKIE_DOMAIN: localhost
CALLBACK_GITHUB_URL: http://localhost:4000/auth/github/callback
OAUTH_SUCCESS_REDIRECT_URL: http://localhost:3000/auth/sso
OAUTH_42_SUCCESS_REDIRECT_URL: http://localhost:3000/auth/sso
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify which OAuth handlers reference which environment variables
rg -n "OAUTH.*SUCCESS_REDIRECT" --type ts

Repository: 42core-team/website

Length of output: 210


Update the GitHub OAuth handler to use the renamed variable.

The GitHub callback handler in api/src/auth/auth.controller.ts (line 42) references OAUTH_SUCCESS_REDIRECT_URL, but the config file now only defines OAUTH_42_SUCCESS_REDIRECT_URL. GitHub login will fail at runtime when getOrThrow cannot find the variable. Update line 42 to use OAUTH_42_SUCCESS_REDIRECT_URL or create a separate OAUTH_GITHUB_SUCCESS_REDIRECT_URL variable for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@local-setup/local-values/api.yaml` at line 30, The GitHub OAuth callback in
auth.controller.ts references the old env key OAUTH_SUCCESS_REDIRECT_URL which
no longer exists; update the GitHub callback handler (in
api/src/auth/auth.controller.ts, the code that calls getOrThrow for the redirect
URL) to use the renamed OAUTH_42_SUCCESS_REDIRECT_URL or alternatively add and
use a clearer OAUTH_GITHUB_SUCCESS_REDIRECT_URL; ensure the getOrThrow call and
any variable names in the GitHub callback method are updated to match the chosen
env key so the value resolves at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant