Skip to content

added prisma db and actions and github workflow#5

Merged
Srijan76-code merged 2 commits into
mainfrom
srijan_prisma_db
Oct 4, 2025
Merged

added prisma db and actions and github workflow#5
Srijan76-code merged 2 commits into
mainfrom
srijan_prisma_db

Conversation

@Srijan76-code
Copy link
Copy Markdown
Owner

@Srijan76-code Srijan76-code commented Oct 4, 2025

Summary by CodeRabbit

  • New Features

    • GitHub webhook integration to automatically trigger rewards when merged PRs close issues.
    • Contributor reward flow: on-chain reward trigger, contribution logging, and UI refreshes for profiles and leaderboard.
    • User profile improvements: wallet management, contribution stats, and total earned display.
    • Explore page: browse repositories with open-issue counts.
    • Leaderboard: top contributors by total rewards.
    • Organization and repository registration with webhook setup; per-issue bounties.
  • Chores

    • Added DB schema and runtime dependencies; updated ignore rules.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 4, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds Prisma schema, migration, and client singleton; new server actions for user/org/repo management, GitHub webhook registration, and a reward flow; API endpoint for GitHub webhooks; Octokit dependency; .gitignore entry for generated Prisma client.

Changes

Cohort / File(s) Summary of changes
Prisma schema, migration & client
prisma/schema.prisma, prisma/migrations/20251004172052_init/migration.sql, prisma/migrations/migration_lock.toml, lib/prisma.js, .gitignore, package.json
Added Prisma schema and enums/models, initial SQL migration and migration lock, PrismaClient singleton export (lib/prisma.js), new dependencies (@prisma/client, prisma, octokit), and .gitignore entry for /lib/generated/prisma.
User / org / repo server actions
actions/userProfile.js
New server actions: upsertUser, updateUserWallet, getUserProfileData, getExplorePageRepos, getLeaderboard, createOrganization, registerRepo (calls webhook creation), setIssueBounty; uses Clerk auth, Prisma, and route revalidation.
GitHub webhook creation helper
actions/createGithubWebhook.js
New function createGithubWebhook(repoOwner, repoName, userGithubToken) that builds an Octokit client and creates a pull_request webhook using configured secret and app URL; returns success/error.
Reward orchestration for issues
actions/rewardContributorForIssue.js
New flow rewardContributorForIssue(githubIssueId, githubUsername) with internal sendSplTokenReward placeholder: validates issue/user/wallet, performs dummy on-chain transfer, records contribution and marks issue REWARDED in a transaction, revalidates caches, and returns standardized errors on failure.
GitHub webhook API endpoint
api/webhooks/github.js
New POST handler export default async function handler(req, res) that processes GitHub webhook payloads, detects merged pull_request events, extracts closing issue IDs from PR body, and invokes rewardContributorForIssue; returns 200/400/500 responses accordingly.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor GitHub as GitHub
  participant API as /api/webhooks/github
  participant Reward as rewardContributorForIssue
  participant Prisma as PrismaClient
  note right of API #D8F1E8: webhook POST (pull_request)

  GitHub->>API: POST webhook (pull_request merged)
  API->>API: parse event & extract PR author + closing issue#
  alt author & issue found
    API->>Reward: rewardContributorForIssue(issueId, username)
    Reward->>Prisma: fetch issue & user, validate state
    Reward->>Reward: sendSplTokenReward(recipient, amount, issueNumber)
    Reward->>Prisma: transaction — create Contribution, update Issue.status = REWARDED
    Reward-->>API: { success } or { error }
    API-->>GitHub: 200 OK (or 400 on known error)
  else missing data or not merged
    API-->>GitHub: 200 OK (no action)
  end
Loading
sequenceDiagram
  autonumber
  actor Admin as Org Admin
  participant Action as registerRepo (actions/userProfile)
  participant Octokit as createGithubWebhook
  participant GitHub as GitHub
  note right of Action #FFF4D9: create repo record, then webhook

  Admin->>Action: registerRepo(data, repoOwner, userToken)
  Action->>Prisma: insert repository record
  Action->>Octokit: createGithubWebhook(repoOwner, repoName, userToken)
  Octokit->>GitHub: create webhook (pull_request events)
  GitHub-->>Octokit: webhook created
  Octokit-->>Action: { success } or { error }
  Action-->>Admin: { repo } or { error }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I nibble code, twitch whiskers bright,
Webhooks set and ledgers light.
Tables planted, migrations sowed,
Rewards hop home down rabbit road. 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title mentions Prisma, actions, and a GitHub workflow, which are all touched on in the pull request, but it is overly broad and inaccurately suggests a GitHub Actions workflow file was added. It fails to clearly convey that the main change is initializing the Prisma database schema alongside new server-side actions and webhook handling. Although related, the phrasing lacks precision and focus on the primary update.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdbfba5 and 4eafeb2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • package.json (2 hunks)

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.

@Srijan76-code Srijan76-code merged commit 3b019bc into main Oct 4, 2025
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: 9

🧹 Nitpick comments (5)
actions/createGithubWebhook.js (2)

19-29: Consider checking for existing webhooks to prevent duplicates.

If registerRepo is called multiple times for the same repository, this will create duplicate webhooks, which could lead to duplicate reward processing and race conditions.

Add a check before creating the webhook:

  try {
+    // Check if webhook already exists
+    const { data: hooks } = await octokit.rest.repos.listWebhooks({
+      owner: repoOwner,
+      repo: repoName,
+    });
+    
+    const existingHook = hooks.find(hook => 
+      hook.config.url === webhookUrl && 
+      hook.events.includes('pull_request')
+    );
+    
+    if (existingHook) {
+      console.log(`Webhook already exists for ${repoOwner}/${repoName}`);
+      return { success: true };
+    }
+
    await octokit.rest.repos.createWebhook({

32-34: Return error details for better debugging.

The catch block logs the error but returns a generic message, making it difficult to diagnose webhook creation failures in production.

Include error details in the response:

  } catch (error) {
    console.error("Failed to create webhook:", error);
-    return { error: "Could not create the GitHub webhook." };
+    return { error: `Could not create the GitHub webhook: ${error.message}` };
  }
lib/prisma.js (1)

4-4: Minor formatting inconsistencies.

Missing spaces around operators reduce readability.

Apply consistent spacing:

-export const prisma= globalThis.prisma || new PrismaClient()
+export const prisma = globalThis.prisma || new PrismaClient()

-if (process.env.NODE_ENV!=="production"){
-    globalThis.prisma=prisma
+if (process.env.NODE_ENV !== "production") {
+    globalThis.prisma = prisma
}

Also applies to: 6-6

api/webhooks/github.js (1)

26-43: Add input validation for webhook payload.

The handler assumes the payload structure is valid but doesn't validate required fields, which could cause runtime errors.

Add validation before processing:

   if (event === 'pull_request' && payload.action === 'closed' && payload.pull_request?.merged) {
     const pr = payload.pull_request;
+    
+    if (!pr.user?.login) {
+      console.error('PR payload missing user.login');
+      return res.status(400).json({ message: 'Invalid payload structure' });
+    }
+    
+    if (!pr.body) {
+      console.log('PR has no body, skipping issue resolution check');
+      return res.status(200).json({ message: 'PR merged but no issue reference found' });
+    }
     
     const githubUsername = pr.user.login;
prisma/migrations/20251004172052_init/migration.sql (1)

10-10: Consider making email non-nullable.

If users are created via Clerk (as suggested by the upsertUser function), email should always be available. Making it nullable could complicate user lookups and validations.

If email is always provided by Clerk, update the schema:

-    "email" TEXT,
+    "email" TEXT NOT NULL,

Then generate a new migration. If email truly can be null in some scenarios, document why in a comment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98d9433 and bdbfba5.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • actions/createGithubWebhook.js (1 hunks)
  • actions/rewardContributorForIssue.js (1 hunks)
  • actions/userProfile.js (1 hunks)
  • api/webhooks/github.js (1 hunks)
  • lib/prisma.js (1 hunks)
  • package.json (1 hunks)
  • prisma/migrations/20251004172052_init/migration.sql (1 hunks)
  • prisma/migrations/migration_lock.toml (1 hunks)
  • prisma/schema.prisma (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
actions/rewardContributorForIssue.js (3)
api/webhooks/github.js (2)
  • githubIssueId (30-30)
  • githubUsername (29-29)
actions/userProfile.js (3)
  • issue (204-214)
  • user (15-28)
  • user (60-60)
lib/prisma.js (2)
  • prisma (4-4)
  • prisma (4-4)
actions/userProfile.js (3)
actions/rewardContributorForIssue.js (2)
  • user (24-26)
  • issue (20-22)
lib/prisma.js (2)
  • prisma (4-4)
  • prisma (4-4)
actions/createGithubWebhook.js (1)
  • createGithubWebhook (8-36)
api/webhooks/github.js (1)
actions/rewardContributorForIssue.js (1)
  • rewardContributorForIssue (17-73)
🔇 Additional comments (9)
package.json (1)

13-13: LGTM!

The dependency versions are current and appropriate for this PR's Prisma ORM integration and GitHub webhook functionality. Based on learnings, these are stable releases:

  • Prisma 6.16.3: Latest patch with bug fixes
  • Octokit 5.0.3: Current "all-batteries" SDK package

Also applies to: 18-19

actions/userProfile.js (3)

114-131: LGTM with proper BigInt handling.

The raw SQL query correctly computes leaderboard rankings and handles BigInt serialization for JSON. The query is safe from SQL injection since it contains no user input interpolation.


147-159: LGTM! Proper use of transactions.

The transactional creation of organization and admin membership ensures data consistency, preventing orphaned organizations without members.


204-214: LGTM! Proper upsert pattern.

The issue bounty logic correctly uses githubIssueId as the unique identifier, allowing both creation and updates of bounties.

prisma/migrations/migration_lock.toml (1)

1-3: LGTM!

Standard Prisma migration lock file correctly specifying PostgreSQL as the database provider.

lib/prisma.js (1)

1-8: LGTM! Proper Prisma singleton pattern.

The implementation correctly uses globalThis to prevent multiple PrismaClient instances in development with hot module reloading. Based on learnings, this is the recommended pattern for Next.js with Prisma 6.16.3.

Based on learnings

prisma/migrations/20251004172052_init/migration.sql (2)

1-5: LGTM! Well-defined enums.

The enum types provide type safety and clear semantics for role-based access control and issue lifecycle tracking.


101-101: LGTM! One reward per issue constraint.

The unique constraint on issueId enforces that each issue can only have one rewarded contribution. This prevents double-rewarding but assumes only one PR closes each issue. Document this business rule in your schema or code comments if this is intentional behavior.

.gitignore (1)

42-43: Remove redundant .gitignore entry (lines 42–43): prisma/schema.prisma uses the default client output (node_modules/.prisma/client), so /lib/generated/prisma is never populated—delete that entry.

if (!userGithubToken) return { error: "User does not have a GitHub token." };

const octokit = new Octokit({ auth: userGithubToken });
const webhookUrl = `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/github`;
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

Security risk: Use server-side environment variable.

NEXT_PUBLIC_APP_URL is exposed to the client and should not be used for server-side webhook configuration. This creates a security vulnerability where the webhook URL could be manipulated.

Use a server-only environment variable:

-  const webhookUrl = `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/github`;
+  const webhookUrl = `${process.env.APP_URL}/api/webhooks/github`;

Add APP_URL to your .env file (not .env.local with NEXT_PUBLIC_ prefix).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const webhookUrl = `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/github`;
const webhookUrl = `${process.env.APP_URL}/api/webhooks/github`;
🤖 Prompt for AI Agents
In actions/createGithubWebhook.js around line 12, the code uses
NEXT_PUBLIC_APP_URL (a client-exposed env var) to build the webhook URL; replace
it with a server-only environment variable (e.g., process.env.APP_URL) set in
your server .env (not NEXT_PUBLIC_ prefixed) and remove any client exposure.
Update the code to read process.env.APP_URL, validate it exists (throw or log
and abort if missing), and ensure APP_URL is documented/added to your deployment
env vars so the webhook URL is configured server-side only.

Comment on lines +17 to +72
export async function rewardContributorForIssue(githubIssueId, githubUsername) {
try {
// 1. Find the issue and the user in your database
const issue = await prisma.issue.findUnique({
where: { githubIssueId },
});

const user = await prisma.user.findFirst({
where: { githubUsername },
});

// 2. Validate everything before proceeding
if (!issue) return { error: `Issue #${githubIssueId} not found in our database.` };
if (!user) return { error: `User '${githubUsername}' is not registered in our app.` };
if (issue.status !== 'OPEN') return { error: 'Issue is not open for rewards.' };
if (!user.walletAddress) return { error: `User ${githubUsername} has no wallet address.` };
if (issue.tokenReward <= 0) return { error: 'Issue has no token reward set.' };

// 3. Perform the on-chain Solana transaction
const { signature, error } = await sendSplTokenReward(
user.walletAddress,
issue.tokenReward,
issue.number
);

if (error) throw new Error(error);

// 4. Update your database in a single transaction
const contribution = await prisma.$transaction(async (tx) => {
// Create the contribution record as a log
const newContribution = await tx.contribution.create({
data: {
issueId: issue.id,
contributorId: user.id,
transactionSignature: signature,
},
});

// Update the issue status to REWARDED
await tx.issue.update({
where: { id: issue.id },
data: { status: 'REWARDED' },
});

return newContribution;
});

// 5. Revalidate paths to update the UI for users
revalidatePath(`/profile/${user.githubUsername}`);
revalidatePath("/leaderboard");

return { success: true, contribution };
} catch (error) {
console.error("Failed to reward contributor:", error);
return { error: error.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 | 🔴 Critical

Import the dependencies before using them.

prisma and revalidatePath are referenced without any imports, so this module will throw a ReferenceError the first time rewardContributorForIssue runs. Add the missing imports at the top of the file.

+import { prisma } from "@/lib/prisma";
+import { revalidatePath } from "next/cache";
+
 // --- REAL SOLANA LOGIC GOES HERE ---
 // 1. Initialize connection to Solana cluster.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function rewardContributorForIssue(githubIssueId, githubUsername) {
try {
// 1. Find the issue and the user in your database
const issue = await prisma.issue.findUnique({
where: { githubIssueId },
});
const user = await prisma.user.findFirst({
where: { githubUsername },
});
// 2. Validate everything before proceeding
if (!issue) return { error: `Issue #${githubIssueId} not found in our database.` };
if (!user) return { error: `User '${githubUsername}' is not registered in our app.` };
if (issue.status !== 'OPEN') return { error: 'Issue is not open for rewards.' };
if (!user.walletAddress) return { error: `User ${githubUsername} has no wallet address.` };
if (issue.tokenReward <= 0) return { error: 'Issue has no token reward set.' };
// 3. Perform the on-chain Solana transaction
const { signature, error } = await sendSplTokenReward(
user.walletAddress,
issue.tokenReward,
issue.number
);
if (error) throw new Error(error);
// 4. Update your database in a single transaction
const contribution = await prisma.$transaction(async (tx) => {
// Create the contribution record as a log
const newContribution = await tx.contribution.create({
data: {
issueId: issue.id,
contributorId: user.id,
transactionSignature: signature,
},
});
// Update the issue status to REWARDED
await tx.issue.update({
where: { id: issue.id },
data: { status: 'REWARDED' },
});
return newContribution;
});
// 5. Revalidate paths to update the UI for users
revalidatePath(`/profile/${user.githubUsername}`);
revalidatePath("/leaderboard");
return { success: true, contribution };
} catch (error) {
console.error("Failed to reward contributor:", error);
return { error: error.message };
}
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
// --- REAL SOLANA LOGIC GOES HERE ---
// 1. Initialize connection to Solana cluster.
export async function rewardContributorForIssue(githubIssueId, githubUsername) {
try {
// 1. Find the issue and the user in your database
const issue = await prisma.issue.findUnique({
where: { githubIssueId },
});
const user = await prisma.user.findFirst({
where: { githubUsername },
});
// 2. Validate everything before proceeding
if (!issue) return { error: `Issue #${githubIssueId} not found in our database.` };
if (!user) return { error: `User '${githubUsername}' is not registered in our app.` };
if (issue.status !== 'OPEN') return { error: 'Issue is not open for rewards.' };
if (!user.walletAddress) return { error: `User ${githubUsername} has no wallet address.` };
if (issue.tokenReward <= 0) return { error: 'Issue has no token reward set.' };
// 3. Perform the on-chain Solana transaction
const { signature, error } = await sendSplTokenReward(
user.walletAddress,
issue.tokenReward,
issue.number
);
if (error) throw new Error(error);
// 4. Update your database in a single transaction
const contribution = await prisma.$transaction(async (tx) => {
// Create the contribution record as a log
const newContribution = await tx.contribution.create({
data: {
issueId: issue.id,
contributorId: user.id,
transactionSignature: signature,
},
});
// Update the issue status to REWARDED
await tx.issue.update({
where: { id: issue.id },
data: { status: 'REWARDED' },
});
return newContribution;
});
// 5. Revalidate paths to update the UI for users
revalidatePath(`/profile/${user.githubUsername}`);
revalidatePath("/leaderboard");
return { success: true, contribution };
} catch (error) {
console.error("Failed to reward contributor:", error);
return { error: error.message };
}
}
🤖 Prompt for AI Agents
In actions/rewardContributorForIssue.js around lines 17-72, the function uses
prisma and revalidatePath without importing them; add the missing imports at the
top of the file (e.g., import your Prisma client instance as prisma from your
project path such as '../lib/prisma' or wherever it lives, and import {
revalidatePath } from 'next/cache' or the appropriate framework module) so the
function can access the database client and revalidation helper; ensure the
import paths match your project structure and remove any unused imports.

Comment thread actions/userProfile.js

// Updates the wallet address for the current user.
export async function updateUserWallet(walletAddress) {
const { userId } = auth();
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

Use async auth() for Next.js 15 compatibility.

The Clerk SDK version 6.33.2 used in this project requires await auth() when called in server actions with Next.js 15.

Based on learnings

Apply this diff:

-  const { userId } = auth();
+  const { userId } = await auth();

This pattern must be applied to all auth() calls in this file (lines 38, 56, 143, 170, 200).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { userId } = auth();
const { userId } = await auth();
🤖 Prompt for AI Agents
In actions/userProfile.js around lines 38, 56, 143, 170 and 200, calls to auth()
must be awaited for Next.js 15 compatibility with Clerk SDK v6.33.2; replace
each occurrence like const { userId } = auth(); with const { userId } = await
auth(); (or await auth() destructured appropriately) and ensure the surrounding
function is declared async so awaiting is valid; apply this change at all listed
line locations.

Comment thread actions/userProfile.js
where: { contributorId: userId },
include: { issue: { select: { tokenReward: true } } },
});
const totalEarned = contributions.reduce((sum, c) => sum + Number(c.issue.tokenReward), 0);
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

Potential precision loss converting Decimal to Number.

The tokenReward field is defined as DECIMAL(18,9) in the database, which can represent values larger than JavaScript's Number.MAX_SAFE_INTEGER (2^53 - 1). Converting to Number may cause precision loss for large token amounts.

Use string arithmetic or a decimal library:

-    const totalEarned = contributions.reduce((sum, c) => sum + Number(c.issue.tokenReward), 0);
+    const totalEarned = contributions.reduce((sum, c) => sum + parseFloat(c.issue.tokenReward.toString()), 0);

Consider using a decimal library like decimal.js for financial calculations if precision is critical.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const totalEarned = contributions.reduce((sum, c) => sum + Number(c.issue.tokenReward), 0);
const totalEarned = contributions.reduce((sum, c) => sum + parseFloat(c.issue.tokenReward.toString()), 0);
🤖 Prompt for AI Agents
In actions/userProfile.js around line 67, the reduction converts
issue.tokenReward to Number causing potential precision loss for DECIMAL(18,9)
values; change the summation to use a decimal library (e.g., decimal.js):
import/require Decimal, convert each c.issue.tokenReward to a string then to new
Decimal(...) and accumulate using Decimal.add, and finally convert the
totalEarned to the required output type (string or Decimal.toFixed) instead of a
Number; ensure any downstream uses expect the string/Decimal representation.

Comment thread actions/userProfile.js
Comment on lines +184 to +186
if (webhookResult.error) {
console.error("Webhook setup failed:", webhookResult.error);
}
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

Webhook failures are silently ignored.

If webhook creation fails, the repository is still registered but won't receive PR events, breaking the reward flow. Users won't know the webhook setup failed.

Consider these approaches:

Option 1: Fail the entire operation (stricter)

      if (webhookResult.error) {
-        console.error("Webhook setup failed:", webhookResult.error);
+        // Rollback repo creation
+        await prisma.repository.delete({ where: { id: repo.id } });
+        return { error: `Repository registered but webhook failed: ${webhookResult.error}` };
      }

Option 2: Track webhook status (more resilient)
Add a webhookConfigured boolean field to the Repository model and allow manual retry:

      if (webhookResult.error) {
        console.error("Webhook setup failed:", webhookResult.error);
+        await prisma.repository.update({
+          where: { id: repo.id },
+          data: { webhookConfigured: false }
+        });
+        return { repo, warning: "Repository registered but webhook setup failed. Please retry." };
      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (webhookResult.error) {
console.error("Webhook setup failed:", webhookResult.error);
}
if (webhookResult.error) {
// Rollback repo creation
await prisma.repository.delete({ where: { id: repo.id } });
return { error: `Repository registered but webhook failed: ${webhookResult.error}` };
}
Suggested change
if (webhookResult.error) {
console.error("Webhook setup failed:", webhookResult.error);
}
if (webhookResult.error) {
console.error("Webhook setup failed:", webhookResult.error);
await prisma.repository.update({
where: { id: repo.id },
data: { webhookConfigured: false }
});
return { repo, warning: "Repository registered but webhook setup failed. Please retry." };
}

Comment thread api/webhooks/github.js
Comment on lines +6 to +10
function getClosingIssueId(body) {
if (!body) return null;
const match = body.match(/(?:closes|fixes|resolves)\s#(\d+)/i);
return match ? BigInt(match[1]) : null;
}
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

Avoid BigInt for compatibility.

Line 9 returns a BigInt which can cause JSON serialization errors and compatibility issues with the reward function.

Convert to Number or String:

 function getClosingIssueId(body) {
   if (!body) return null;
   const match = body.match(/(?:closes|fixes|resolves)\s#(\d+)/i);
-  return match ? BigInt(match[1]) : null;
+  return match ? parseInt(match[1], 10) : null;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function getClosingIssueId(body) {
if (!body) return null;
const match = body.match(/(?:closes|fixes|resolves)\s#(\d+)/i);
return match ? BigInt(match[1]) : null;
}
function getClosingIssueId(body) {
if (!body) return null;
const match = body.match(/(?:closes|fixes|resolves)\s#(\d+)/i);
return match ? parseInt(match[1], 10) : null;
}
🤖 Prompt for AI Agents
In api/webhooks/github.js around lines 6 to 10, the function currently returns a
BigInt which can break JSON serialization and downstream compatibility; replace
the BigInt return with a JSON-serializable Number (e.g., parseInt/Number on
match[1]) or a String per caller needs — convert match[1] to Number, guard
against NaN and return null if conversion fails, so the function returns either
a Number or null instead of BigInt.

Comment thread api/webhooks/github.js
Comment on lines +13 to +15
// IMPORTANT: You must verify the webhook signature in a real app!
// This ensures the request is actually from GitHub.
// For the hackathon, we might skip this, but it's critical for production.
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

Critical: Implement webhook signature verification.

The webhook endpoint accepts any POST request without verifying it came from GitHub. This is a critical security vulnerability that allows attackers to trigger fake reward payouts.

Implement HMAC signature verification:

+import crypto from 'crypto';
+
+function verifyGitHubSignature(payload, signature, secret) {
+  const hmac = crypto.createHmac('sha256', secret);
+  const digest = 'sha256=' + hmac.update(payload).digest('hex');
+  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
+}
+
 export default async function handler(req, res) {
-  // IMPORTANT: You must verify the webhook signature in a real app!
-  // This ensures the request is actually from GitHub.
-  // For the hackathon, we might skip this, but it's critical for production.
+  const signature = req.headers['x-hub-signature-256'];
+  const webhookSecret = process.env.GITHUB_WEBHOOK_SECRET;
+  
+  if (!signature || !webhookSecret) {
+    return res.status(401).json({ message: 'Unauthorized' });
+  }
+  
+  const payload = JSON.stringify(req.body);
+  if (!verifyGitHubSignature(payload, signature, webhookSecret)) {
+    return res.status(401).json({ message: 'Invalid signature' });
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// IMPORTANT: You must verify the webhook signature in a real app!
// This ensures the request is actually from GitHub.
// For the hackathon, we might skip this, but it's critical for production.
import crypto from 'crypto';
function verifyGitHubSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
export default async function handler(req, res) {
const signature = req.headers['x-hub-signature-256'];
const webhookSecret = process.env.GITHUB_WEBHOOK_SECRET;
if (!signature || !webhookSecret) {
return res.status(401).json({ message: 'Unauthorized' });
}
const payload = JSON.stringify(req.body);
if (!verifyGitHubSignature(payload, signature, webhookSecret)) {
return res.status(401).json({ message: 'Invalid signature' });
}
// ... existing handler logic continues here ...
}

Comment on lines +107 to +122
ALTER TABLE "org_members" ADD CONSTRAINT "org_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "org_members" ADD CONSTRAINT "org_members_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "repositories" ADD CONSTRAINT "repositories_orgId_fkey" FOREIGN KEY ("orgId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "issues" ADD CONSTRAINT "issues_repoId_fkey" FOREIGN KEY ("repoId") REFERENCES "repositories"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "contributions" ADD CONSTRAINT "contributions_issueId_fkey" FOREIGN KEY ("issueId") REFERENCES "issues"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "contributions" ADD CONSTRAINT "contributions_contributorId_fkey" FOREIGN KEY ("contributorId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Review CASCADE delete implications for data integrity.

All foreign keys use ON DELETE CASCADE, which will propagate deletions through the entire relationship hierarchy. Consider the impact:

  • Deleting a user removes all their contributions and org memberships
  • Deleting an organization removes all repositories, issues, and contributions
  • Deleting a repository removes all issues and contributions

For a reward/bounty system, you may want to preserve historical contribution records even after user or org deletion. Consider using ON DELETE RESTRICT or ON DELETE SET NULL for certain relationships, or implement soft deletes.

Example for preserving contribution history:

 ALTER TABLE "contributions" ADD CONSTRAINT "contributions_contributorId_fkey" 
-  FOREIGN KEY ("contributorId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+  FOREIGN KEY ("contributorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

This would prevent user deletion if they have contributions, or you could change the field to nullable and use SET NULL to mark them as deleted.

🤖 Prompt for AI Agents
In prisma/migrations/20251004172052_init/migration.sql around lines 107 to 122,
all foreign keys currently use ON DELETE CASCADE which will cascade removals
across orgs, repos, issues and contributions and can erase historical
contribution data; update the migration to preserve history by replacing cascade
on the contribution and membership relationships: change the
contributions.contributorId FK to allow NULL and use ON DELETE SET NULL (or
change to ON DELETE RESTRICT if you want to block deletion while contributions
exist), change org_members.userId to ON DELETE RESTRICT (or implement
soft-delete on users and orgs instead), and change repositories/orgs or issues
FKs as appropriate (use RESTRICT or SET NULL) to avoid unintentional mass
deletions; ensure schema changes include ALTER COLUMN ... DROP NOT NULL when
making contributorId nullable and add corresponding constraint updates or
soft-delete flags before running the migration.

Comment thread prisma/schema.prisma
Comment on lines +27 to +33
model User {
id String @id @unique // Clerk User ID
email String? @unique
githubUsername String?
githubAvatarUrl String?
walletAddress String? @unique
createdAt DateTime @default(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.

⚠️ Potential issue | 🔴 Critical

Drop @unique from the primary key field.

Prisma rejects schemas that annotate the same field with both @id and @unique. This causes prisma generate/migrate to fail, blocking the entire rollout. Remove the redundant @unique on the id field.

-model User {
-  id              String    @id @unique // Clerk User ID
+model User {
+  id              String    @id // Clerk User ID
🤖 Prompt for AI Agents
In prisma/schema.prisma around lines 27 to 33, the User model's id field is
annotated with both @id and @unique which Prisma rejects; remove the redundant
@unique from the id field so it is annotated only with @id (leave other fields
unchanged), then run prisma generate/migrate to confirm the schema validates.

This was referenced Oct 4, 2025
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