Skip to content

adding - #314

Open
aamoghS wants to merge 1 commit into
mainfrom
chore/monorepo-quality-gates
Open

adding#314
aamoghS wants to merge 1 commit into
mainfrom
chore/monorepo-quality-gates

Conversation

@aamoghS

@aamoghS aamoghS commented Aug 6, 2026

Copy link
Copy Markdown
Member

Note

High Risk
Touches authorization tiers, judging/results integrity, mass email, and destructive hackathon operations across API and schema—high blast radius even with extensive tests.

Overview
This PR is a large operational pass on hackathon admin, judging, and infrastructure so a full-venue event does not melt the API or mis-report outcomes.

Staffing and check-in: A new volunteer admin role can scan badges via isScanner while isAdmin rejects volunteers for destructive or roster-wide actions. Portal context exposes isScanner separately from isAdmin. Event staff can list and undo event check-ins; hackathon delete now requires typing the exact name, and delete can fail on FK when club memberships still reference the edition.

Participants and email: Attendee roster and analytics move to DB-side filtering, paging, and aggregates instead of shipping full PII to the browser. Mass acceptance reports approved / emailed / failedEmails, batches updates, stamps acceptanceEmailSentAt, and uses a pooled SMTP transporter. A separate announcement router emails interested/registered/approved/checked-in audiences in chunks.

Judging lifecycle: CSV bulk import and hackathon_map are removed. promoteSubmissions links judgeable projects to submissions (sourceProjectId), with idempotent table numbering. Queue assignment/rebuild and forceSkipOvertime get safer candidate selection, cross-hackathon guards, startedAt claims, and chunked queue inserts. computeRanking is shared; hackathon_result stores computed placings with publish/unpublish; public getResults reads published rows only. Admin can fix or withdraw projects; isCreateX flows from team submission.

Scale and safety: DDoS keys on user id when signed in; deploy env sets burst thresholds. Cache eviction is narrowed (no blanket hackathon* on every scan). Auth session callback drops per-request judge lookup. Schema adds indexes, hackathon_result, and drops maps; README documents conditional pre-migration for project_leader.

Reviewed by Cursor Bugbot for commit d9fd083. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file chore labels Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Visit the preview URL for this PR (updated for commit d9fd083):

https://hacklytics2027--pr-314-gccciz25.web.app

(expires Thu, 13 Aug 2026 03:26:30 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: c48ba34db61581e25fe2978355160b5eefe0e83f

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d9fd083. Configure here.

.update(hackathonParticipants)
.set({ acceptanceEmailSentAt: new Date() })
.where(eq(hackathonParticipants.id, participant.id));
emailed++;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Retry resends acceptance emails

Medium Severity

sendMassAcceptanceEmails stamps acceptanceEmailSentAt after a successful send but never skips recipients who already have that timestamp. Retrying the same batch after a timeout or partial failure sends duplicate acceptance emails to people who already received one.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d9fd083. Configure here.

// admin here would render the whole admin nav for someone every one of
// those pages rejects.
isAdmin: isStaffRole(admin?.role),
isScanner: !!admin,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Volunteers flagged as project leaders

Medium Severity

Portal context sets isProjectLeader with isProjectLeader || !!admin, so any active admin row counts—including volunteers. Volunteers are not full staff, but they still get isProjectLeader: true without a project_leader row, which drives UI such as the “My Initiatives” nav entry they cannot use.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d9fd083. Configure here.

voteCount: sql`excluded.vote_count`,
computedAt: sql`now()`,
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Recompute duplicates result rows

Medium Severity

computeResults upserts on (hackathonId, projectId, track) while always inserting track: null. On PostgreSQL, unique constraints treat each NULL in track as distinct, so a second compute before publish can insert duplicate rows instead of updating existing drafts.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d9fd083. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR substantially expands hackathon administration, judging, communications, scanner permissions, and portal UI behavior while optimizing attendee and analytics queries.

  • Adds volunteer-scoped event scanning and attendance correction.
  • Adds filtered attendee pagination/export, bulk status operations, announcements, and acceptance-delivery tracking.
  • Reworks judging results and several database constraints and indexes.
  • Updates portal pages, forms, deployment limits, tests, and documentation.

Confidence Score: 2/5

The PR is not yet safe to merge because acceptance retries can duplicate email delivery, an existing schema table is removed without data migration, and volunteer accounts cannot be provisioned through the application.

The changed acceptance workflow records but does not consult its delivery marker, the push-based schema replacement does not preserve existing map rows, and the only admin creation endpoint excludes the newly supported scanner role.

Files Needing Attention: packages/api/src/routers/hackathon/admin.ts, packages/db/src/schemas/judge.ts, packages/api/src/routers/admin.ts

Important Files Changed

Filename Overview
packages/api/src/routers/hackathon/admin.ts Adds scalable attendee administration and scanner operations, but acceptance retries ignore the newly recorded delivery marker and can resend completed emails.
packages/db/src/schemas/judge.ts Replaces map storage with judging results and adds judging constraints without preserving existing map records through a migration.
packages/api/src/routers/admin.ts Retains administrative provisioning but omits the new volunteer role from the creation contract.
packages/api/src/middleware/procedures.ts Separates volunteer scanner permissions from full staff permissions with appropriately scoped middleware.
packages/api/src/routers/hackathon/announce.ts Adds scoped, deduplicated, batched announcement delivery with escaped message content.
packages/auth/src/email.ts Adds reusable escaped HTML email templates for announcements and acceptances.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Organizer selects participants] --> B[Query participants by IDs and hackathon]
  B --> C[Approve matching rows]
  C --> D[Send acceptance email sequentially]
  D --> E[Write acceptanceEmailSentAt]
  D -->|timeout or failure| F[Organizer retries selection]
  F --> B
  B -->|marker not checked| G[Previously emailed participants selected again]
  G --> D
Loading

Comments Outside Diff (2)

  1. packages/api/src/routers/hackathon/admin.ts, line 281-289 (link)

    P1 Acceptance retries resend emails

    When an organizer retries a partially completed mass-accept operation, this query selects participants whose acceptanceEmailSentAt is already set, and the send loop emails them again before overwriting the marker, causing duplicate acceptance messages.

  2. packages/api/src/routers/admin.ts, line 148 (link)

    P1 Volunteer role cannot be provisioned

    When a super administrator attempts to provision a volunteer scanner, admin.create rejects the new volunteer role even though scanner access requires an active admin row, forcing volunteer accounts to be created through direct database changes.

Reviews (1): Last reviewed commit: "adding" | Re-trigger Greptile

Comment on lines 156 to +200
],
);

// Map images for hackathon venues
export const hackathonMaps = pgTable(
"hackathon_map",
/**
* A frozen placing, computed once when judging closes.
*
* getRankings recomputes the whole ordering on every call, and its z-score
* normalisation runs over the entire vote set — so one late vote silently
* changes every project's score, including ones already announced. The
* ordering existed only inside an HTTP response; nothing in the product could
* say who won yesterday.
*
* A snapshot instead: computed deliberately, reviewable while unpublished, and
* unchanged by anything that happens to the votes afterwards.
*/
export const hackathonResults = pgTable(
"hackathon_result",
{
id: uuid("id").defaultRandom().primaryKey(),
hackathonId: uuid("hackathon_id")
.notNull()
.references(() => hackathons.id, { onDelete: "cascade" }),
imageUrl: text("image_url").notNull(),
name: text("name"),
order: integer("order").notNull().default(0),
createdAt: timestamp("created_at").defaultNow().notNull(),
projectId: uuid("project_id")
.notNull()
.references(() => judgingProjects.id, { onDelete: "cascade" }),
/** Carried across at compute time so results survive the judging tables
* and can name the team that actually built the thing. */
sourceProjectId: uuid("source_project_id").references(
() => hackathonProjects.id,
{ onDelete: "set null" },
),
/** Which prize this placing is for. Null is the overall ranking. */
track: text("track"),
placement: integer("placement").notNull(),
/** The blended score at the moment of computation. `numeric` because the
* pipeline produces a float — hackathon_project.score is an integer and
* could never have held this value. */
weightedScore: numeric("weighted_score", { precision: 6, scale: 2 }),
voteCount: integer("vote_count").notNull().default(0),
/** Null while the snapshot is a draft. Set on publish; cleared on
* unpublish, which is what makes publishing reversible. */
publishedAt: timestamp("published_at"),
computedAt: timestamp("computed_at").defaultNow().notNull(),
},
(table) => [index("map_hackathon_id_idx").on(table.hackathonId)],
(table) => [

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Map records lack migration

If an existing database contains hackathon_map rows, applying this push-based schema replacement removes the table without migrating its records, causing persisted venue-map names and image URLs to be lost.

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

Labels

chore dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant