From 3be3c22be9777a3f6584b0fa84c7d3f757e13436 Mon Sep 17 00:00:00 2001 From: gulimabr Date: Sun, 18 Jan 2026 22:06:02 -0300 Subject: [PATCH] general fixes --- .env.example | 8 + backend/src/config.py | 6 + backend/src/main.py | 10 ++ frontend/src/components/LanguageSelector.tsx | 8 +- frontend/src/i18n/locales/en/dashboard.json | 5 +- .../i18n/locales/en/requirementDetail.json | 1 + frontend/src/i18n/locales/pt/dashboard.json | 5 +- .../i18n/locales/pt/requirementDetail.json | 1 + frontend/src/pages/AdminPage.tsx | 14 +- frontend/src/pages/DashboardPage.tsx | 148 +----------------- frontend/src/pages/RequirementDetailPage.tsx | 31 ++-- k8s/prod.yaml | 28 ++-- 12 files changed, 87 insertions(+), 178 deletions(-) diff --git a/.env.example b/.env.example index 0fd42e4..778c39f 100644 --- a/.env.example +++ b/.env.example @@ -41,6 +41,14 @@ COOKIE_DOMAIN= # Cookie max age in seconds (default: 3600 = 1 hour) COOKIE_MAX_AGE=3600 +# ------------------------------------------- +# Reverse Proxy / TLS Termination +# ------------------------------------------- +# Enable to trust X-Forwarded-* headers (set true behind ingress/nginx) +PROXY_HEADERS=false +# Comma-separated trusted proxy hosts/IPs (use "*" to trust all) +TRUSTED_PROXY_HOSTS=127.0.0.1,::1 + # ------------------------------------------- # Database Configuration (PostgreSQL) # ------------------------------------------- diff --git a/backend/src/config.py b/backend/src/config.py index 35a8bfb..af9d7d0 100644 --- a/backend/src/config.py +++ b/backend/src/config.py @@ -25,6 +25,12 @@ class Settings(BaseSettings): cookie_max_age: int = Field(default=28800, env="COOKIE_MAX_AGE") # 8 hours cookie_name: str = Field(default="access_token", env="COOKIE_NAME") + # Proxy / TLS termination settings + # Enable to honor X-Forwarded-Proto when behind a reverse proxy (ingress/nginx) + proxy_headers: bool = Field(default=False, env="PROXY_HEADERS") + # Comma-separated list of trusted proxy hosts/IPs. Use "*" to trust all. + trusted_proxy_hosts: str = Field(default="127.0.0.1,::1", env="TRUSTED_PROXY_HOSTS") + # Database settings database_host: str = Field(default="postgres", env="DATABASE_HOST") database_port: int = Field(default=5432, env="DATABASE_PORT") diff --git a/backend/src/main.py b/backend/src/main.py index e3613aa..0286177 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -2,6 +2,7 @@ from contextlib import asynccontextmanager from typing import List, Optional from fastapi import FastAPI, Depends, Request, HTTPException, status from fastapi.middleware.cors import CORSMiddleware +from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from fastapi.responses import RedirectResponse from sqlalchemy.ext.asyncio import AsyncSession @@ -139,6 +140,15 @@ app = FastAPI( lifespan=lifespan ) +# Respect X-Forwarded-Proto/For headers when behind a reverse proxy +if settings.proxy_headers: + trusted_hosts = [ + host.strip() + for host in settings.trusted_proxy_hosts.split(",") + if host.strip() + ] + app.add_middleware(ProxyHeadersMiddleware, trusted_hosts=trusted_hosts) + # Configure CORS app.add_middleware( CORSMiddleware, diff --git a/frontend/src/components/LanguageSelector.tsx b/frontend/src/components/LanguageSelector.tsx index 42fab85..17de897 100644 --- a/frontend/src/components/LanguageSelector.tsx +++ b/frontend/src/components/LanguageSelector.tsx @@ -15,6 +15,12 @@ interface LanguageSelectorProps { export default function LanguageSelector({ className = '', compact = false }: LanguageSelectorProps) { const { i18n, t } = useTranslation('common') + const resolvedLanguage = i18n.resolvedLanguage || i18n.language + const normalizedLanguage = (resolvedLanguage?.split('-')[0] || 'en') as LanguageCode + const selectedLanguage = LANGUAGES.some((lang) => lang.code === normalizedLanguage) + ? normalizedLanguage + : 'en' + const handleLanguageChange = (e: React.ChangeEvent) => { const newLang = e.target.value as LanguageCode i18n.changeLanguage(newLang) @@ -23,7 +29,7 @@ export default function LanguageSelector({ className = '', compact = false }: La return (
setNewProjectName(e.target.value)} - placeholder={t('createProject.projectNamePlaceholder')} - className="w-full px-3 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent" - required - /> -
- - {/* Project Description */} -
- -