Skip to content

Feat/all features v2#20

Merged
senutpal merged 20 commits intomainfrom
feat/all-features-v2
Mar 12, 2026
Merged

Feat/all features v2#20
senutpal merged 20 commits intomainfrom
feat/all-features-v2

Conversation

@senutpal
Copy link
Owner

No description provided.

@vercel
Copy link

vercel bot commented Mar 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dev-radar-server Ready Ready Preview, Comment Mar 12, 2026 7:25pm

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

Warning

Rate limit exceeded

@senutpal has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 57 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 250d5a41-78d3-4b18-a5d2-24fd0c160179

📥 Commits

Reviewing files that changed from the base of the PR and between 02bbba7 and 796f242.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • apps/server/package.json
  • apps/server/prisma/schema.prisma
  • apps/server/src/config.ts
  • apps/server/src/routes/auth.ts
  • apps/server/src/routes/leaderboards.ts
  • apps/server/src/routes/stats.ts
  • apps/server/src/routes/teams.ts
  • apps/server/src/routes/users.ts
  • apps/server/src/services/auth0.ts
  • apps/server/src/services/email.ts
  • apps/web/src/app/dashboard/dashboard-shell.tsx
  • apps/web/src/app/dashboard/settings/page.tsx
  • apps/web/src/app/dashboard/stats/page.tsx
  • apps/web/src/app/dashboard/teams/[id]/page.tsx
  • apps/web/src/app/layout.tsx
  • apps/web/src/components/dashboard/conflict-toast.tsx
  • apps/web/src/components/dashboard/friend-card.tsx
  • apps/web/src/components/dashboard/index.ts
  • apps/web/src/components/layout/header.tsx
  • apps/web/src/lib/api/index.ts
  • apps/web/src/lib/api/types.ts
  • apps/web/src/lib/auth/auth-context.tsx
  • apps/web/src/lib/theme-context.tsx
  • apps/web/src/lib/themes.ts
  • packages/shared/src/types.ts
  • packages/shared/src/validators.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/all-features-v2
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Comment on lines +226 to +253
app.get('/sso', async (request: FastifyRequest, reply: FastifyReply) => {
if (!isAuth0Configured()) {
return reply.status(501).send({
error: { code: 'SSO_NOT_CONFIGURED', message: 'SSO is not configured on this server' },
});
}

const querySchema = z.object({
connection: z.string().optional(),
});
const query = querySchema.safeParse(request.query);
const connection = query.success ? query.data.connection : undefined;

const state = crypto.randomUUID();

reply.setCookie('sso_state', state, {
httpOnly: true,
secure: isProduction,
sameSite: 'lax',
maxAge: 600,
path: '/',
});

const authUrl = getAuth0AuthorizeUrl(state, connection);
logger.debug({ connection }, 'Redirecting to Auth0 SSO');

return reply.redirect(authUrl);
});

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI 1 day ago

To fix the problem, the /sso route should be configured with rate limiting using the same mechanism already used for /sso/callback. Fastify’s rate limit plugin supports per-route configuration via a config.rateLimit object in the route options. The best low-impact fix is therefore to wrap the /sso handler in a route definition that includes config: { rateLimit: { ... } }, mirroring the existing callback configuration but possibly with slightly different limits appropriate for an initiation endpoint.

Concretely, in apps/server/src/routes/auth.ts, edit the app.get('/sso', ...) declaration (around lines 225–253) to add a second argument: a route options object with a config.rateLimit property, before the async handler function. For example, configure a modest per-IP rate limit such as max: 30, timeWindow: '1 minute'. No new imports are needed because Fastify picks up these options via the already-registered rate-limit plugin (as evidenced by the /sso/callback route using the same pattern). The rest of the handler body (Auth0 configuration check, cookie setting, redirect) remains unchanged.

Suggested changeset 1
apps/server/src/routes/auth.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/server/src/routes/auth.ts b/apps/server/src/routes/auth.ts
--- a/apps/server/src/routes/auth.ts
+++ b/apps/server/src/routes/auth.ts
@@ -223,35 +223,43 @@
   // ─── Auth0 SSO Routes ───────────────────────────────────────────────
 
   // GET /sso - Redirect to Auth0 for SSO login
-  app.get('/sso', async (request: FastifyRequest, reply: FastifyReply) => {
-    if (!isAuth0Configured()) {
-      return reply.status(501).send({
-        error: { code: 'SSO_NOT_CONFIGURED', message: 'SSO is not configured on this server' },
+  app.get(
+    '/sso',
+    {
+      config: {
+        rateLimit: { max: 30, timeWindow: '1 minute' },
+      },
+    },
+    async (request: FastifyRequest, reply: FastifyReply) => {
+      if (!isAuth0Configured()) {
+        return reply.status(501).send({
+          error: { code: 'SSO_NOT_CONFIGURED', message: 'SSO is not configured on this server' },
+        });
+      }
+
+      const querySchema = z.object({
+        connection: z.string().optional(),
       });
-    }
+      const query = querySchema.safeParse(request.query);
+      const connection = query.success ? query.data.connection : undefined;
 
-    const querySchema = z.object({
-      connection: z.string().optional(),
-    });
-    const query = querySchema.safeParse(request.query);
-    const connection = query.success ? query.data.connection : undefined;
+      const state = crypto.randomUUID();
 
-    const state = crypto.randomUUID();
+      reply.setCookie('sso_state', state, {
+        httpOnly: true,
+        secure: isProduction,
+        sameSite: 'lax',
+        maxAge: 600,
+        path: '/',
+      });
 
-    reply.setCookie('sso_state', state, {
-      httpOnly: true,
-      secure: isProduction,
-      sameSite: 'lax',
-      maxAge: 600,
-      path: '/',
-    });
+      const authUrl = getAuth0AuthorizeUrl(state, connection);
+      logger.debug({ connection }, 'Redirecting to Auth0 SSO');
 
-    const authUrl = getAuth0AuthorizeUrl(state, connection);
-    logger.debug({ connection }, 'Redirecting to Auth0 SSO');
+      return reply.redirect(authUrl);
+    }
+  );
 
-    return reply.redirect(authUrl);
-  });
-
   // GET /sso/callback - Auth0 SSO callback
   app.get(
     '/sso/callback',
EOF
@@ -223,35 +223,43 @@
// ─── Auth0 SSO Routes ───────────────────────────────────────────────

// GET /sso - Redirect to Auth0 for SSO login
app.get('/sso', async (request: FastifyRequest, reply: FastifyReply) => {
if (!isAuth0Configured()) {
return reply.status(501).send({
error: { code: 'SSO_NOT_CONFIGURED', message: 'SSO is not configured on this server' },
app.get(
'/sso',
{
config: {
rateLimit: { max: 30, timeWindow: '1 minute' },
},
},
async (request: FastifyRequest, reply: FastifyReply) => {
if (!isAuth0Configured()) {
return reply.status(501).send({
error: { code: 'SSO_NOT_CONFIGURED', message: 'SSO is not configured on this server' },
});
}

const querySchema = z.object({
connection: z.string().optional(),
});
}
const query = querySchema.safeParse(request.query);
const connection = query.success ? query.data.connection : undefined;

const querySchema = z.object({
connection: z.string().optional(),
});
const query = querySchema.safeParse(request.query);
const connection = query.success ? query.data.connection : undefined;
const state = crypto.randomUUID();

const state = crypto.randomUUID();
reply.setCookie('sso_state', state, {
httpOnly: true,
secure: isProduction,
sameSite: 'lax',
maxAge: 600,
path: '/',
});

reply.setCookie('sso_state', state, {
httpOnly: true,
secure: isProduction,
sameSite: 'lax',
maxAge: 600,
path: '/',
});
const authUrl = getAuth0AuthorizeUrl(state, connection);
logger.debug({ connection }, 'Redirecting to Auth0 SSO');

const authUrl = getAuth0AuthorizeUrl(state, connection);
logger.debug({ connection }, 'Redirecting to Auth0 SSO');
return reply.redirect(authUrl);
}
);

return reply.redirect(authUrl);
});

// GET /sso/callback - Auth0 SSO callback
app.get(
'/sso/callback',
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +263 to +343
async (request: FastifyRequest, reply: FastifyReply) => {
if (!isAuth0Configured()) {
return reply.status(501).send({
error: { code: 'SSO_NOT_CONFIGURED', message: 'SSO is not configured' },
});
}

const callbackSchema = z.object({
code: z.string().min(1),
state: z.string().min(1),
error: z.string().optional(),
error_description: z.string().optional(),
});

const result = callbackSchema.safeParse(request.query);
if (!result.success) {
throw new ValidationError('Invalid SSO callback parameters');
}

const { code, state, error, error_description } = result.data;

/* Validate CSRF state */
const storedState = request.cookies.sso_state;
if (!state || !storedState || state !== storedState) {
logger.warn('SSO state mismatch');
return reply.status(400).send({
error: { code: 'INVALID_STATE', message: 'Invalid SSO state parameter' },
});
}
reply.clearCookie('sso_state');

if (error) {
logger.warn({ error, error_description }, 'Auth0 SSO error');
return reply.status(400).send({
error: { code: 'SSO_ERROR', message: error_description ?? error },
});
}

/* Exchange code for tokens and fetch user info */
const tokens = await exchangeAuth0Code(code);
const userInfo = await getAuth0UserInfo(tokens.access_token);

const db = getDb();

/* Find existing user by email or create a new one */
let user = await db.user.findFirst({
where: { email: userInfo.email },
});

if (!user) {
user = await db.user.create({
data: {
githubId: `auth0_${userInfo.sub}`,
username: userInfo.nickname ?? userInfo.email.split('@')[0] ?? 'user',
displayName: userInfo.name || null,
avatarUrl: userInfo.picture || null,
email: userInfo.email,
tier: 'TEAM',
},
});

logger.info({ userId: user.id, email: userInfo.email }, 'New SSO user created');
}

/* Generate JWT */
const token = app.jwt.sign(
{
userId: user.id,
username: user.username,
tier: user.tier,
},
{ expiresIn: '7d' }
);

logger.info({ userId: user.id }, 'SSO authentication successful');

/* Redirect to web app with token */
const webUrl = new URL('/dashboard', env.WEB_APP_URL);
webUrl.searchParams.set('token', token);
return reply.redirect(webUrl.toString());
}

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI 1 day ago

Generally, the fix is to ensure that this authorization route is explicitly rate-limited at the Fastify route level, using the standard rateLimit option that works with the @fastify/rate-limit plugin. This avoids reliance on non-standard configuration keys that static analysis might miss and guarantees that the handler cannot be abused for denial-of-service via excessive Auth0 callbacks.

Concretely, in apps/server/src/routes/auth.ts, modify the /sso/callback route definition to move the existing config.rateLimit configuration into the top-level rateLimit property of the route options object:

app.get(
  '/sso/callback',
  {
    rateLimit: {
      max: 10,
      timeWindow: '1 minute',
    },
  },
  async (request, reply) => { ... }
);

This keeps the same limits (10 requests per minute per IP, assuming default plugin behavior) while using the canonical configuration shape recognized by @fastify/rate-limit. No other logic changes are needed. Because we must not change imports beyond well-known libraries, and we cannot see plugin registration in this file, we limit ourselves to changing only the route options object; the actual plugin registration (e.g., app.register(require('@fastify/rate-limit'), ...)) would remain elsewhere in the codebase.

Suggested changeset 1
apps/server/src/routes/auth.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/server/src/routes/auth.ts b/apps/server/src/routes/auth.ts
--- a/apps/server/src/routes/auth.ts
+++ b/apps/server/src/routes/auth.ts
@@ -256,8 +256,9 @@
   app.get(
     '/sso/callback',
     {
-      config: {
-        rateLimit: { max: 10, timeWindow: '1 minute' },
+      rateLimit: {
+        max: 10,
+        timeWindow: '1 minute',
       },
     },
     async (request: FastifyRequest, reply: FastifyReply) => {
EOF
@@ -256,8 +256,9 @@
app.get(
'/sso/callback',
{
config: {
rateLimit: { max: 10, timeWindow: '1 minute' },
rateLimit: {
max: 10,
timeWindow: '1 minute',
},
},
async (request: FastifyRequest, reply: FastifyReply) => {
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +347 to +351
app.get('/sso/status', async (_request: FastifyRequest, reply: FastifyReply) => {
return reply.send({
enabled: isAuth0Configured(),
});
});

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix

AI 1 day ago

To fix the problem, the /sso/status route should be explicitly rate-limited, just like /sso/callback. In Fastify, this is typically done via the config.rateLimit option on the route definition (assuming the fastify-rate-limit plugin is registered at the app level). We will wrap the handler in the object-based app.get(path, options, handler) form and add a conservative rate limit configuration.

Concretely, in apps/server/src/routes/auth.ts, locate the app.get('/sso/status', async (_request: FastifyRequest, reply: FastifyReply) => { ... }); definition near lines 346–351. Replace it with a three-argument call: the same path, an options object containing config: { rateLimit: { max: 60, timeWindow: '1 minute' } } (or similar reasonable values), and the existing async handler function unchanged. No new imports or helper methods are required, because the file already uses config.rateLimit for /sso/callback, implying the necessary plugin is configured elsewhere.

Suggested changeset 1
apps/server/src/routes/auth.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/server/src/routes/auth.ts b/apps/server/src/routes/auth.ts
--- a/apps/server/src/routes/auth.ts
+++ b/apps/server/src/routes/auth.ts
@@ -344,9 +344,17 @@
   );
 
   // GET /sso/status - Check if SSO is available
-  app.get('/sso/status', async (_request: FastifyRequest, reply: FastifyReply) => {
-    return reply.send({
-      enabled: isAuth0Configured(),
-    });
-  });
+  app.get(
+    '/sso/status',
+    {
+      config: {
+        rateLimit: { max: 60, timeWindow: '1 minute' },
+      },
+    },
+    async (_request: FastifyRequest, reply: FastifyReply) => {
+      return reply.send({
+        enabled: isAuth0Configured(),
+      });
+    }
+  );
 }
EOF
@@ -344,9 +344,17 @@
);

// GET /sso/status - Check if SSO is available
app.get('/sso/status', async (_request: FastifyRequest, reply: FastifyReply) => {
return reply.send({
enabled: isAuth0Configured(),
});
});
app.get(
'/sso/status',
{
config: {
rateLimit: { max: 60, timeWindow: '1 minute' },
},
},
async (_request: FastifyRequest, reply: FastifyReply) => {
return reply.send({
enabled: isAuth0Configured(),
});
}
);
}
Copilot is powered by AI and may make mistakes. Always verify output.
@senutpal senutpal merged commit 4e28ed3 into main Mar 12, 2026
4 of 6 checks passed
@senutpal senutpal deleted the feat/all-features-v2 branch March 12, 2026 19:29
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 796f2423cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// TODO: Send invitation email with token
// Send invitation email (non-blocking)
const joinUrl = `${env.WEB_APP_URL}/dashboard/teams/${teamId}/join?token=${token}`;

Choose a reason for hiding this comment

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

P1 Badge Point invite emails to an existing join route

This link targets /dashboard/teams/${teamId}/join, but the web app only has apps/web/src/app/dashboard/teams/page.tsx and apps/web/src/app/dashboard/teams/[id]/page.tsx, so recipients who click the email land on a 404 and cannot redeem the invitation token. The new email invitation flow is therefore broken for real users.

Useful? React with 👍 / 👎.

<div className="flex min-h-screen bg-background">
<Sidebar isConnected={isConnected} />
<main className="flex-1 min-w-0">{children}</main>
<ConflictToastListener />

Choose a reason for hiding this comment

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

P1 Badge Avoid opening a second dashboard WebSocket connection

Adding this listener mounts another useWebSocket instance while DashboardShell already keeps one active, so one page now creates two simultaneous sockets for the same user. The server side WebSocket handler keeps a single connection per user and closes the previous one when a new one appears, which makes these two clients repeatedly disconnect/reconnect each other and causes unstable realtime behavior.

Useful? React with 👍 / 👎.


/* Redirect to web app with token */
const webUrl = new URL('/dashboard', env.WEB_APP_URL);
webUrl.searchParams.set('token', token);

Choose a reason for hiding this comment

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

P1 Badge Stop placing JWTs in the SSO redirect query string

Putting the signed session token into ?token= exposes a bearer credential in URL logs, browser history, and potential Referer leakage before client code strips it. This creates a concrete token disclosure path for every SSO login and should be replaced with a safer handoff (for example, HttpOnly cookie or one-time code exchange).

Useful? React with 👍 / 👎.

Comment on lines +270 to +272
const callbackSchema = z.object({
code: z.string().min(1),
state: z.string().min(1),

Choose a reason for hiding this comment

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

P2 Badge Accept Auth0 error callbacks without requiring code

The callback schema requires code even when Auth0 returns an OAuth error response (error/error_description with no code), so those valid failure callbacks are rejected as Invalid SSO callback parameters before your explicit if (error) handling runs. Users then get the wrong failure path instead of the intended SSO error response.

Useful? React with 👍 / 👎.

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