Skip to content

Conversation

@alexander-mart
Copy link
Member

@alexander-mart alexander-mart commented Jul 24, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of the base URL to better reflect the current browser location, enhancing reliability when connecting to backend services.

@netlify
Copy link

netlify bot commented Jul 24, 2025

Deploy Preview for dtp-stat failed. Why did it fail? →

Name Link
🔨 Latest commit 3ab60a4
🔍 Latest deploy log https://app.netlify.com/projects/dtp-stat/deploys/6882984c48ecf50008ef5fdb

@coderabbitai
Copy link

coderabbitai bot commented Jul 24, 2025

Walkthrough

The export of the djangoBaseUrl constant was modified to use the browser's current location (window.location.origin) instead of relying on the NEXT_PUBLIC_DJANGO_BASE_URL environment variable. The previous implementation was commented out, and the new assignment is now based on runtime context.

Changes

File Change Summary
src/shared/django-helpers.tsx Changed djangoBaseUrl export to use window.location.origin instead of environment variable

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/shared/django-helpers.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

TypeError: Cannot read properties of undefined (reading 'getAllComments')
Occurred while linting /src/shared/django-helpers.tsx:8
Rule: "unicorn/expiring-todo-comments"
at Object.Program (/node_modules/eslint/lib/rules/no-warning-comments.js:193:45)
at Program (/node_modules/eslint-plugin-unicorn/rules/expiring-todo-comments.js:511:10)
at /node_modules/eslint-plugin-unicorn/rules/utils/rule.js:39:18
at ruleErrorHandler (/node_modules/eslint/lib/linter/linter.js:1076:28)
at /node_modules/eslint/lib/linter/safe-emitter.js:45:58
at Array.forEach ()
at Object.emit (/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
at NodeEventGenerator.applySelector (/node_modules/eslint/lib/linter/node-event-generator.js:297:26)
at NodeEventGenerator.applySelectors (/node_modules/eslint/lib/linter/node-event-generator.js:326:22)
at NodeEventGenerator.enterNode (/node_modules/eslint/lib/linter/node-event-generator.js:340:14)

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch alexander-mart-patch-3

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c12f684 and 3ab60a4.

📒 Files selected for processing (1)
  • src/shared/django-helpers.tsx (1 hunks)

Comment on lines +15 to +16
// export const djangoBaseUrl = process.env.NEXT_PUBLIC_DJANGO_BASE_URL ?? "";
export const djangoBaseUrl = window.location.origin ?? "";
Copy link

@coderabbitai coderabbitai bot Jul 24, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Critical: SSR compatibility issue and security concern.

This change introduces two critical problems:

  1. SSR Error: window.location.origin will cause "ReferenceError: window is not defined" during server-side rendering in Next.js.

  2. Security Risk: Using the current window origin as the Django base URL means the application will trust whatever domain it's running on, potentially enabling malicious redirects.

Consider reverting to the environment variable approach or add proper runtime guards:

-// export const djangoBaseUrl = process.env.NEXT_PUBLIC_DJANGO_BASE_URL ?? "";
-export const djangoBaseUrl = window.location.origin ?? "";
+export const djangoBaseUrl = process.env.NEXT_PUBLIC_DJANGO_BASE_URL ?? "";

If runtime determination is required, use a function with proper guards:

-export const djangoBaseUrl = window.location.origin ?? "";
+export const getDjangoBaseUrl = (): string => {
+  if (typeof window === 'undefined') {
+    return process.env.NEXT_PUBLIC_DJANGO_BASE_URL ?? "";
+  }
+  return process.env.NEXT_PUBLIC_DJANGO_BASE_URL ?? window.location.origin ?? "";
+};

Let me verify how this constant is used across the codebase:


🏁 Script executed:

#!/bin/bash
# Description: Check usage of djangoBaseUrl to assess impact of the change
# Expected: Find all usages and determine if they're in SSR contexts

echo "=== Searching for djangoBaseUrl usage ==="
rg -A 3 -B 3 "djangoBaseUrl" --type tsx --type ts --type js --type jsx

echo -e "\n=== Checking for SSR-related files that might import this ==="
rg -l "django-helpers" --type tsx --type ts --type js --type jsx | head -10

echo -e "\n=== Checking if this file is imported in _app or layout files ==="
rg -A 2 -B 2 "django-helpers" pages/_app.* app/layout.* || echo "No _app or layout imports found"

Length of output: 820


#!/bin/bash

Search for all import sites of the shared django-helpers module

echo "=== Imports of src/shared/django-helpers.tsx ==="
rg "from .*shared/django-helpers" -n

List any pages or API routes using SSR data-fetching methods

echo -e "\n=== Files defining getServerSideProps ==="
rg "getServerSideProps" -n

echo -e "\n=== Files defining getStaticProps ==="
rg "getStaticProps" -n

Check if any SSR file imports django-helpers

echo -e "\n=== SSR files that import shared/django-helpers.tsx ==="
rg "from .*shared/django-helpers" -l | xargs -r rg -n "getServerSideProps|getStaticProps"


#!/bin/bash

Search for all usages of djangoBaseUrl in the repository

echo "=== Searching for djangoBaseUrl usages ==="
rg "djangoBaseUrl" -n .

Search for imports of the django-helpers module

echo -e "\n=== Searching for imports of shared/django-helpers.tsx ==="
rg "shared/django-helpers" -n .

List all files using Next.js SSR/SSG methods

echo -e "\n=== Files with getServerSideProps ==="
rg "getServerSideProps" -n .

echo -e "\n=== Files with getStaticProps ==="
rg "getStaticProps" -n .

echo -e "\n=== Files with getInitialProps ==="
rg "getInitialProps" -n .


🏁 Script executed:

#!/bin/bash
# Search for all references to djangoBaseUrl
echo "=== djangoBaseUrl usages ==="
rg "djangoBaseUrl" -n -g "*.{ts,tsx,js,jsx}"

# Search for all import sites of the shared helper
echo -e "\n=== Imports of shared/django-helpers.tsx ==="
rg "shared/django-helpers" -n -g "*.{ts,tsx,js,jsx}"

# Identify any SSR/SSG files that import this helper
echo -e "\n=== SSR/SSG files importing django-helpers ==="
for f in $(rg -l "shared/django-helpers" -g "*.{ts,tsx,js,jsx}"); do
  if rg -q "getServerSideProps|getStaticProps|getInitialProps" "$f"; then
    echo "$f"
  fi
done

Length of output: 3713


Fix SSR/Server-side Import of djangoBaseUrl

The switch to window.location.origin breaks any server‐side or API‐route imports of djangoBaseUrl (e.g. src/pages/api/rewrites/proxy-django-html-page.tsx), since window is undefined in Node and during SSR.

• Affected locations:

  • src/shared/django-helpers.tsx
  • src/pages/api/rewrites/proxy-django-html-page.tsx
  • (also client components sharing the same constant)

• Proposed fix: turn the constant into a guarded function that falls back to the env var on the server:

// src/shared/django-helpers.tsx
export const getDjangoBaseUrl = (): string => {
  if (typeof window !== "undefined") {
    return window.location.origin;
  }
  return process.env.NEXT_PUBLIC_DJANGO_BASE_URL ?? "";
};

Then update all imports/usages:

- import { djangoBaseUrl } from "../shared/django-helpers";
+ import { getDjangoBaseUrl } from "../shared/django-helpers";
- void router.replace(`${djangoBaseUrl}${djangoPageHref}`);
+ void router.replace(`${getDjangoBaseUrl()}${djangoPageHref}`);

And in the API route:

- import { djangoBaseUrl } from "../../../shared/django-helpers";
+ import { getDjangoBaseUrl } from "../../../shared/django-helpers";

- const upstreamUrl = new URL(req.url ?? "/", djangoBaseUrl);
+ const upstreamUrl = new URL(req.url ?? "/", getDjangoBaseUrl());

Optionally, use a non-public server‐only env var (e.g. DJANGO_BASE_URL) instead of a NEXT_PUBLIC_ one for stronger configuration control.

🤖 Prompt for AI Agents
In src/shared/django-helpers.tsx around lines 15 to 16, the use of
window.location.origin breaks server-side rendering and API route imports
because window is undefined in Node. Replace the exported constant djangoBaseUrl
with a function getDjangoBaseUrl that returns window.location.origin if window
is defined, otherwise falls back to process.env.NEXT_PUBLIC_DJANGO_BASE_URL or
an empty string. Update all imports and usages of djangoBaseUrl to call
getDjangoBaseUrl() instead. Optionally, consider using a non-public server-only
environment variable for stronger configuration control.

Copy link

Choose a reason for hiding this comment

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

@alexander-mart Для получения текущего домена в SSR и поддержки нескольких доменов, вам нужно использовать заголовки запроса. Вот несколько подходов:

1. Универсальная функция для получения домена

// src/shared/django-helpers.tsx
export const getDjangoBaseUrl = (req?: any): string => {
  // В SSR контексте используем заголовки запроса
  if (typeof window === 'undefined' && req) {
    const protocol = req.headers['x-forwarded-proto'] || 'http';
    const host = req.headers.host;
    return `${protocol}://${host}`;
  }
  
  // В браузере используем window.location
  if (typeof window !== 'undefined') {
    return window.location.origin;
  }
  
  // Fallback для случаев без контекста
  return process.env.NEXT_PUBLIC_DJANGO_BASE_URL ?? '';
};

2. В getServerSideProps

export async function getServerSideProps({ req }: GetServerSidePropsContext) {
  const baseUrl = getDjangoBaseUrl(req);
  
  // Теперь API запросы идут к тому же домену
  const response = await fetch(`${baseUrl}/api/stat`);
  const data = await response.json();
  
  return {
    props: {
      data,
      baseUrl
    }
  };
}

3. В API routes

// pages/api/proxy-stats.ts
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const protocol = req.headers['x-forwarded-proto'] || 'http';
  const host = req.headers.host;
  const baseUrl = `${protocol}://${host}`;
  
  // Проксируем запрос к Django на том же домене
  const response = await fetch(`${baseUrl}/api/stat`);
  const data = await response.json();
  
  res.json(data);
}

4. Для клиентских запросов без CORS

// src/hooks/useApi.ts
import { getDjangoBaseUrl } from '../shared/django-helpers';

export const useApi = () => {
  const makeRequest = async (endpoint: string) => {
    const baseUrl = getDjangoBaseUrl();
    // Запрос идёт к тому же домену - нет CORS проблем
    const response = await fetch(`${baseUrl}${endpoint}`);
    return response.json();
  };
  
  return { makeRequest };
};

5. С использованием Next.js middleware (рекомендуется)

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';

export function middleware(request: NextRequest) {
  const response = NextResponse.next();
  
  // Добавляем текущий домен в заголовки для использования в компонентах
  response.headers.set('x-current-domain', request.nextUrl.origin);
  
  return response;
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

Затем в компонентах:

// src/components/MyComponent.tsx
import { headers } from 'next/headers';

export default function MyComponent() {
  const headersList = headers();
  const currentDomain = headersList.get('x-current-domain') || '';
  
  // Используем текущий домен для API запросов
  const apiUrl = `${currentDomain}/api/stat`;
  
  return <div>{/* ваш компонент */}</div>;
}

Обновлённая версия django-helpers.tsx

// src/shared/django-helpers.tsx
export const getDjangoBaseUrl = (req?: any): string => {
  // SSR с объектом запроса
  if (typeof window === 'undefined' && req) {
    const protocol = req.headers['x-forwarded-proto'] || 
                    req.connection?.encrypted ? 'https' : 'http';
    const host = req.headers.host;
    return `${protocol}://${host}`;
  }
  
  // Браузер
  if (typeof window !== 'undefined') {
    return window.location.origin;
  }
  
  // Fallback
  return process.env.NEXT_PUBLIC_DJANGO_BASE_URL ?? '';
};

Такой подход:

  • ✅ Работает в SSR и клиенте
  • ✅ Поддерживает множественные домены автоматически
  • ✅ Избегает CORS ошибок (запросы к тому же домену)
  • ✅ Не ломает сборку Next.js

✏️ Learnings added
Learnt from: alexander-mart
PR: dtpstat/website#120
File: src/shared/django-helpers.tsx:15-16
Timestamp: 2025-07-24T20:42:58.703Z
Learning: В Next.js для получения текущего домена в SSR используется req.headers.host из объекта запроса. Для избежания CORS ошибок при работе с несколькими доменами, API запросы должны идти к тому же домену, что и текущая страница.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@coderabbitai coderabbitai bot mentioned this pull request Jul 24, 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.

2 participants