Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jest.mock('@fortawesome/react-fontawesome', () => ({
}) => <span data-testid={`icon-${icon.iconName}`} className={className} {...props} />,
}))

jest.mock('utils/credentials', () => ({
jest.mock('utils/env.client', () => ({
IS_PROJECT_HEALTH_ENABLED: true,
}))

Expand Down
2 changes: 1 addition & 1 deletion frontend/__tests__/unit/components/Footer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jest.mock('utils/constants', () => ({
],
}))

jest.mock('utils/credentials', () => ({
jest.mock('utils/env.client', () => ({
ENVIRONMENT: 'production',
RELEASE_VERSION: '1.2.3',
}))
Expand Down
4 changes: 2 additions & 2 deletions frontend/__tests__/unit/pages/Login.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ jest.mock('next/navigation', () => ({
jest.mock('@heroui/toast', () => ({
addToast: jest.fn(),
}))
jest.mock('utils/credentials', () => ({
isGithubAuthEnabled: jest.fn(() => true),
jest.mock('utils/env.server', () => ({
IS_GITHUB_AUTH_ENABLED: true,
}))
describe('LoginPage', () => {
const pushMock = jest.fn()
Expand Down
2 changes: 1 addition & 1 deletion frontend/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { withSentryConfig } from '@sentry/nextjs'
import type { NextConfig } from 'next'
import { SENTRY_AUTH_TOKEN } from 'utils/credentials'
import { SENTRY_AUTH_TOKEN } from 'utils/env.server'
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Avoid importing app code into next.config; read env directly to prevent loader/alias pitfalls

next.config.ts runs in Node during build. Pulling from utils/env.server can: (a) rely on TS path aliases that may not be honored here, and (b) unnecessarily pull in the server-only side-effect. Reading the env directly is simpler and more robust.

Apply this diff:

-import { SENTRY_AUTH_TOKEN } from 'utils/env.server'
+const SENTRY_AUTH_TOKEN =
+  process.env.SENTRY_AUTH_TOKEN ?? process.env.NEXT_SENTRY_AUTH_TOKEN

To verify alias and Next support in your repo, run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Next version:"
jq -r '.dependencies.next // .devDependencies.next // "unknown"' package.json 2>/dev/null || echo "package.json not found at repo root"
echo
echo "TS path alias for 'utils/*':"
rg -nC2 '"paths"\s*:\s*{[^}]*"utils/\*"' tsconfig*.json || true

Length of output: 493


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locating package.json files:"
find . -type f -name package.json

echo
echo "Inspecting Next version in each package.json:"
for pkg in $(find . -type f -name package.json); do
  echo "File: $pkg"
  jq -r '.dependencies.next // .devDependencies.next // "not specified"' "$pkg" || echo "failed to parse $pkg"
  echo
done

echo "Locating tsconfig*.json files:"
find . -type f -name 'tsconfig*.json'

echo
echo "Inspecting compilerOptions.paths in each tsconfig*.json:"
for cfg in $(find . -type f -name 'tsconfig*.json'); do
  echo "File: $cfg"
  jq -r '.compilerOptions.paths // "no paths defined"' "$cfg" || echo "failed to parse $cfg"
  echo
done

echo "Displaying first 20 lines of next.config.ts:"
find . -type f -name 'next.config.ts' -exec sed -n '1,20p' {} +

Length of output: 2467


Avoid importing application code into next.config.ts; read environment variables directly

  • Although your frontend/tsconfig.json defines a utils/* path alias, next.config.ts is loaded as a CommonJS module at build time and does not reliably honor TypeScript path mappings (nextjs.org).
  • Importing from utils/env.server inadvertently pulls in application-level, server-only logic (and any side-effects) into your build configuration.
  • A more robust pattern is to read the token directly from process.env, with a fallback:
-import { SENTRY_AUTH_TOKEN } from 'utils/env.server'
+const SENTRY_AUTH_TOKEN =
+  process.env.SENTRY_AUTH_TOKEN ?? process.env.NEXT_SENTRY_AUTH_TOKEN
📝 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
import { SENTRY_AUTH_TOKEN } from 'utils/env.server'
const SENTRY_AUTH_TOKEN =
process.env.SENTRY_AUTH_TOKEN ?? process.env.NEXT_SENTRY_AUTH_TOKEN
🤖 Prompt for AI Agents
In frontend/next.config.ts around line 3, importing SENTRY_AUTH_TOKEN from
utils/env.server pulls application/server code into the build config and may
break due to TypeScript path aliasing; remove that import and instead read the
token directly from process.env (with a safe fallback) inside next.config.ts so
the config relies only on environment variables at build time and does not
execute app-level code.


const isLocal = process.env.NEXT_PUBLIC_ENVIRONMENT === 'local'

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import GitHubProvider from 'next-auth/providers/github'
import { apolloClient } from 'server/apolloClient'
import { IS_PROJECT_LEADER_QUERY, IS_MENTOR_QUERY } from 'server/queries/mentorshipQueries'
import { ExtendedProfile, ExtendedSession } from 'types/auth'
import { isGithubAuthEnabled } from 'utils/credentials'
import { IS_GITHUB_AUTH_ENABLED } from 'utils/env.server'

async function checkIfProjectLeader(login: string): Promise<boolean> {
try {
Expand Down Expand Up @@ -35,7 +35,7 @@ async function checkIfMentor(login: string): Promise<boolean> {

const providers = []

if (isGithubAuthEnabled()) {
if (IS_GITHUB_AUTH_ENABLED) {
providers.push(
GitHubProvider({
clientId: process.env.NEXT_SERVER_GITHUB_CLIENT_ID,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/auth/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isGithubAuthEnabled } from 'utils/credentials'
import { IS_GITHUB_AUTH_ENABLED } from 'utils/env.server'
import LoginPageContent from 'components/LoginPageContent'

export default function LoginPage() {
return <LoginPageContent isGitHubAuthEnabled={isGithubAuthEnabled()} />
return <LoginPageContent isGitHubAuthEnabled={IS_GITHUB_AUTH_ENABLED} />
}
5 changes: 3 additions & 2 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import type { Metadata } from 'next'
import { Geist, Geist_Mono } from 'next/font/google'
import React from 'react'
import { Providers } from 'wrappers/provider'
import { GTM_ID, isGithubAuthEnabled } from 'utils/credentials'
import { GTM_ID } from 'utils/env.client'
import { IS_GITHUB_AUTH_ENABLED } from 'utils/env.server'
import AutoScrollToTop from 'components/AutoScrollToTop'
import BreadCrumbs from 'components/BreadCrumbs'
import Footer from 'components/Footer'
Expand Down Expand Up @@ -69,7 +70,7 @@ export default function RootLayout({
>
<Providers>
<AutoScrollToTop />
<Header isGitHubAuthEnabled={isGithubAuthEnabled()} />
<Header isGitHubAuthEnabled={IS_GITHUB_AUTH_ENABLED} />
<BreadCrumbs />
{children}
<Footer />
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/projects/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { notFound } from 'next/navigation'
import React from 'react'
import { isGithubAuthEnabled } from 'utils/credentials'
import { IS_GITHUB_AUTH_ENABLED } from 'utils/env.server'
import DashboardWrapper from 'components/DashboardWrapper'
import FontLoaderWrapper from 'components/FontLoaderWrapper'
import ProjectsDashboardNavBar from 'components/ProjectsDashboardNavBar'

const ProjectsHealthDashboardLayout: React.FC<{ readonly children: React.ReactNode }> = ({
children,
}) => {
if (!isGithubAuthEnabled()) {
if (!IS_GITHUB_AUTH_ENABLED) {
notFound()
}
return (
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/settings/api-keys/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { notFound, redirect } from 'next/navigation'
import { getServerSession } from 'next-auth'
import React from 'react'
import { isGithubAuthEnabled } from 'utils/credentials'
import { IS_GITHUB_AUTH_ENABLED } from 'utils/env.server'

export default async function ApiKeysLayout({ children }: Readonly<{ children: React.ReactNode }>) {
if (!isGithubAuthEnabled()) {
if (!IS_GITHUB_AUTH_ENABLED) {
notFound()
}
const session = await getServerSession()
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/CardDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useRouter } from 'next/navigation'
import { useSession } from 'next-auth/react'
import type { ExtendedSession } from 'types/auth'
import type { DetailsCardProps } from 'types/card'
import { IS_PROJECT_HEALTH_ENABLED } from 'utils/credentials'
import { IS_PROJECT_HEALTH_ENABLED } from 'utils/env.client'
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Importing IS_PROJECT_HEALTH_ENABLED from env.client is correct, but ensure this file is a Client Component

This component uses useSession, useRouter, and window.location, which require a Client Component boundary. If this file isn’t already under a client parent, add the directive.

Apply at the very top of this file:

+ 'use client'

If you intentionally rely on a client parent wrapper, please confirm that no server component imports this directly.


🏁 Script executed:

#!/usr/bin/env bash
set -e

FILE="frontend/src/components/CardDetailsPage.tsx"

echo "1) Checking for 'use client' directive in $FILE"
grep -n "^[\"']use client[\"']" "$FILE" || echo "=> No 'use client' directive found"

echo
echo "2) Checking for client-specific hooks and browser APIs in $FILE"
grep -E "useSession|useRouter|window\." -n "$FILE" || echo "=> No client-specific usage found"

echo
echo "3) Searching for imports of CardDetailsPage across TSX files"
rg "CardDetailsPage" -n --glob "*.tsx" .

Length of output: 12861


Add the 'use client' directive to CardDetailsPage.tsx

CardDetailsPage.tsx imports and uses next-auth’s useSession, next/navigation’s useRouter, and directly references window.location, so it must be a Client Component. There is currently no 'use client' directive at the top of the file.

Apply this change at the very top of frontend/src/components/CardDetailsPage.tsx:

+ 'use client'

This ensures Next.js treats the component as client-only and enables proper runtime for these hooks and browser APIs.

📝 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
import { IS_PROJECT_HEALTH_ENABLED } from 'utils/env.client'
'use client'
import { IS_PROJECT_HEALTH_ENABLED } from 'utils/env.client'
// … rest of CardDetailsPage.tsx …
🤖 Prompt for AI Agents
In frontend/src/components/CardDetailsPage.tsx around line 17, the file uses
next-auth's useSession, next/navigation's useRouter, and window.location but
lacks the 'use client' directive; add a single line 'use client' at the very top
of the file (above all imports) so Next.js treats this component as a Client
Component and enables client-side hooks and browser APIs.

import { scrollToAnchor } from 'utils/scrollToAnchor'
import { getSocialIcon } from 'utils/urlIconMappings'
import AnchorTitle from 'components/AnchorTitle'
Expand Down Expand Up @@ -87,7 +87,7 @@ const DetailsCard = ({
) && (
<button
type="button"
className="flex items-center justify-center gap-2 text-nowrap rounded-md border border-[#0D6EFD] bg-transparent px-2 py-2 text-[#0D6EFD] text-blue-600 transition-all hover:bg-[#0D6EFD] hover:text-white dark:border-sky-600 dark:text-sky-600 dark:hover:bg-sky-100"
className="flex items-center justify-center gap-2 text-nowrap rounded-md border border-[#0D6EFD] bg-transparent px-2 py-2 text-[#0D6EFD] transition-all hover:bg-[#0D6EFD] hover:text-white dark:border-sky-600 dark:text-sky-600 dark:hover:bg-sky-100"
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix incorrect Tailwind class and prefer theme tokens over hard-coded hex

  • text-nowrap isn’t a Tailwind class; use whitespace-nowrap.
  • Prefer Tailwind color tokens for consistency with themes and future palette changes.

Apply this diff:

-                  className="flex items-center justify-center gap-2 text-nowrap rounded-md border border-[#0D6EFD] bg-transparent px-2 py-2 text-[#0D6EFD] transition-all hover:bg-[#0D6EFD] hover:text-white dark:border-sky-600 dark:text-sky-600 dark:hover:bg-sky-100"
+                  className="flex items-center justify-center gap-2 whitespace-nowrap rounded-md border bg-transparent px-2 py-2 text-blue-600 transition-all hover:bg-blue-600 hover:text-white dark:border-sky-600 dark:text-sky-600 dark:hover:bg-sky-600/10"

If the specific brand shade (#0D6EFD) is required, consider adding it to the Tailwind theme as a custom color token instead of inlining the hex.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In frontend/src/components/CardDetailsPage.tsx around line 90, the element uses
a non-existent Tailwind class "text-nowrap" and hard-coded hex colors; replace
"text-nowrap" with Tailwind's "whitespace-nowrap" and swap hard-coded hex color
classes (border-[#0D6EFD], text-[#0D6EFD], hover:bg-[#0D6EFD],
dark:border-sky-600, dark:text-sky-600, dark:hover:bg-sky-100) with appropriate
Tailwind color tokens from the theme (or add a custom token for the brand shade
in tailwind.config and use that token like border-brand, text-brand,
hover:bg-brand) to ensure theme consistency and remove inline hex usage.

onClick={() => {
router.push(`${window.location.pathname}/edit`)
}}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useState, useCallback } from 'react'
import type { Section } from 'types/section'
import { footerIcons } from 'utils/constants'
import { footerSections } from 'utils/constants'
import { ENVIRONMENT, RELEASE_VERSION } from 'utils/credentials'
import { ENVIRONMENT, RELEASE_VERSION } from 'utils/env.client'

export default function Footer() {
// State to keep track of the open section in the footer
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/instrumentation-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/nextjs'
import { ENVIRONMENT, RELEASE_VERSION, SENTRY_DSN } from 'utils/credentials'
import { ENVIRONMENT, RELEASE_VERSION, SENTRY_DSN } from 'utils/env.client'

Sentry.init({
debug: false,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/sentry.server.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/nextjs'
import { SENTRY_DSN, ENVIRONMENT, RELEASE_VERSION } from 'utils/credentials'
import { SENTRY_DSN, ENVIRONMENT, RELEASE_VERSION } from 'utils/env.client'

Sentry.init({
dsn: SENTRY_DSN || '',
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/server/fetchAlgoliaData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AppError } from 'app/global-error'
import type { AlgoliaResponse } from 'types/algolia'
import { IDX_URL } from 'utils/credentials'
import { IDX_URL } from 'utils/env.client'
import { getCsrfToken } from 'utils/utility'

export const fetchAlgoliaData = async <T>(
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/server/fetchCsrfToken.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AppError } from 'app/global-error'
import { CSRF_URL } from 'utils/credentials'
import { CSRF_URL } from 'utils/env.client'

export const fetchCsrfToken = async (): Promise<string> => {
try {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/server/fetchMetricsPDF.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { handleAppError } from 'app/global-error'
import { API_URL } from 'utils/credentials'
import { API_URL } from 'utils/env.client'

export const fetchMetricsPDF = async (path: string, fileName: string): Promise<void> => {
const response = await fetch(`${API_URL}owasp/project-health-metrics/${path}`, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,5 @@ export const GTM_ID = process.env.NEXT_PUBLIC_GTM_ID
export const IDX_URL = process.env.NEXT_PUBLIC_IDX_URL
export const IS_PROJECT_HEALTH_ENABLED =
process.env.NEXT_PUBLIC_IS_PROJECT_HEALTH_ENABLED === 'true'
export const NEXTAUTH_URL = process.env.NEXTAUTH_URL
export const RELEASE_VERSION = process.env.NEXT_PUBLIC_RELEASE_VERSION
export const SENTRY_AUTH_TOKEN = process.env.NEXT_SENTRY_AUTH_TOKEN
export const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN

export function isGithubAuthEnabled() {
return Boolean(
process.env.NEXT_SERVER_GITHUB_CLIENT_ID && process.env.NEXT_SERVER_GITHUB_CLIENT_SECRET
)
}
6 changes: 6 additions & 0 deletions frontend/src/utils/env.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const GITHUB_CLIENT_ID = process.env.NEXT_SERVER_GITHUB_CLIENT_ID!
export const GITHUB_CLIENT_SECRET = process.env.NEXT_SERVER_GITHUB_CLIENT_SECRET!
export const NEXTAUTH_URL = process.env.NEXTAUTH_URL
export const SENTRY_AUTH_TOKEN = process.env.NEXT_SENTRY_AUTH_TOKEN

export const IS_GITHUB_AUTH_ENABLED = Boolean(GITHUB_CLIENT_ID && GITHUB_CLIENT_SECRET)
2 changes: 1 addition & 1 deletion frontend/src/utils/helpers/apolloClient.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import { AppError, handleAppError } from 'app/global-error'
import { GRAPHQL_URL } from 'utils/credentials'
import { GRAPHQL_URL } from 'utils/env.client'
import { getCsrfToken } from 'utils/utility'
const createApolloClient = () => {
if (!GRAPHQL_URL) {
Expand Down