2026.06.04 frontend építés közben
This commit is contained in:
31
backend/app/core/context.py
Normal file
31
backend/app/core/context.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/core/context.py
|
||||
"""
|
||||
Context management for request-scoped variables, particularly locale/language.
|
||||
Uses contextvars to store the current request's locale for i18n purposes.
|
||||
"""
|
||||
import contextvars
|
||||
from typing import Optional
|
||||
|
||||
# Context variable to store the current request's locale
|
||||
# Default value is "hu" (Hungarian) as per project requirements
|
||||
current_locale: contextvars.ContextVar[str] = contextvars.ContextVar(
|
||||
"current_locale", default="hu"
|
||||
)
|
||||
|
||||
def get_current_locale() -> str:
|
||||
"""
|
||||
Get the current locale from the context.
|
||||
Returns the default "hu" if not set.
|
||||
"""
|
||||
return current_locale.get()
|
||||
|
||||
def set_current_locale(locale: str) -> None:
|
||||
"""
|
||||
Set the current locale in the context.
|
||||
Should be called by middleware or other request processors.
|
||||
"""
|
||||
current_locale.set(locale)
|
||||
|
||||
# Alias for convenience
|
||||
get_locale = get_current_locale
|
||||
set_locale = set_current_locale
|
||||
108
backend/app/core/i18n_middleware.py
Normal file
108
backend/app/core/i18n_middleware.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/core/i18n_middleware.py
|
||||
"""
|
||||
FastAPI middleware for automatic locale/language detection and context management.
|
||||
Implements the priority order:
|
||||
1. ?lang= query parameter
|
||||
2. Accept-Language HTTP header
|
||||
3. User profile language (if authenticated)
|
||||
4. Default "hu" (Hungarian)
|
||||
"""
|
||||
import logging
|
||||
from typing import Optional
|
||||
from fastapi import Request, HTTPException
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from starlette.responses import Response
|
||||
|
||||
from app.core.context import set_current_locale, get_current_locale
|
||||
from app.core.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class I18nMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
Middleware for automatic locale detection and context management.
|
||||
Sets the locale in contextvars for the duration of the request.
|
||||
"""
|
||||
|
||||
async def dispatch(self, request: Request, call_next):
|
||||
# Determine locale based on priority
|
||||
locale = self._determine_locale(request)
|
||||
|
||||
# Set locale in context
|
||||
set_current_locale(locale)
|
||||
|
||||
# Add locale to request state for debugging/logging
|
||||
request.state.locale = locale
|
||||
|
||||
# Process request
|
||||
response = await call_next(request)
|
||||
|
||||
# Optionally add locale header to response
|
||||
response.headers["X-Content-Language"] = locale
|
||||
|
||||
return response
|
||||
|
||||
def _determine_locale(self, request: Request) -> str:
|
||||
"""
|
||||
Determine the locale based on the priority order.
|
||||
Returns a valid locale code (e.g., "hu", "en", "de").
|
||||
"""
|
||||
# 1. Query parameter: ?lang=
|
||||
query_lang = request.query_params.get("lang")
|
||||
if query_lang and self._is_valid_locale(query_lang):
|
||||
logger.debug(f"Locale from query param: {query_lang}")
|
||||
return query_lang
|
||||
|
||||
# 2. Accept-Language HTTP header
|
||||
accept_language = request.headers.get("accept-language")
|
||||
if accept_language:
|
||||
header_lang = self._parse_accept_language(accept_language)
|
||||
if header_lang and self._is_valid_locale(header_lang):
|
||||
logger.debug(f"Locale from Accept-Language header: {header_lang}")
|
||||
return header_lang
|
||||
|
||||
# 3. User profile language (if authenticated)
|
||||
# Note: This requires the user to be authenticated, which may not be available
|
||||
# in middleware before authentication. We'll handle this in endpoint dependencies.
|
||||
# For now, we'll skip this step in middleware and let endpoints handle it.
|
||||
|
||||
# 4. Default locale
|
||||
default_locale = getattr(settings, "DEFAULT_LOCALE", "hu")
|
||||
logger.debug(f"Using default locale: {default_locale}")
|
||||
return default_locale
|
||||
|
||||
def _parse_accept_language(self, accept_language: str) -> Optional[str]:
|
||||
"""
|
||||
Parse Accept-Language header and return the highest priority locale.
|
||||
Example: "en-US,en;q=0.9,hu;q=0.8" -> "en"
|
||||
"""
|
||||
try:
|
||||
# Split by comma and process each language range
|
||||
languages = accept_language.split(',')
|
||||
for lang_range in languages:
|
||||
# Remove quality factor if present
|
||||
lang = lang_range.split(';')[0].strip()
|
||||
# Extract primary language subtag (e.g., "en" from "en-US")
|
||||
if '-' in lang:
|
||||
lang = lang.split('-')[0]
|
||||
if self._is_valid_locale(lang):
|
||||
return lang
|
||||
except Exception as e:
|
||||
logger.debug(f"Error parsing Accept-Language header: {e}")
|
||||
|
||||
return None
|
||||
|
||||
def _is_valid_locale(self, locale: str) -> bool:
|
||||
"""
|
||||
Check if a locale code is valid/supported.
|
||||
For now, we accept any 2-5 character locale code.
|
||||
In production, you might want to check against a list of supported locales.
|
||||
"""
|
||||
if not locale or len(locale) < 2 or len(locale) > 5:
|
||||
return False
|
||||
|
||||
# Basic validation: only letters and hyphens
|
||||
return locale.replace('-', '').isalpha()
|
||||
|
||||
# Create middleware instance (commented out to avoid instantiation error during import)
|
||||
# i18n_middleware = I18nMiddleware()
|
||||
@@ -14,18 +14,27 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
def get_password_hash(password: str) -> str:
|
||||
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
|
||||
|
||||
def create_tokens(data: Dict[str, Any]) -> Tuple[str, str]:
|
||||
""" Access és Refresh token generálása UTC időzónával. """
|
||||
def create_tokens(data: Dict[str, Any], remember_me: bool = False) -> Tuple[str, str]:
|
||||
""" Access és Refresh token generálása UTC időzónával.
|
||||
|
||||
Args:
|
||||
data: Token payload adatok
|
||||
remember_me: Ha True, a refresh token lejárata 30 nap, egyébként 1 nap
|
||||
"""
|
||||
to_encode = data.copy()
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# Access Token
|
||||
# Access Token (mindig ugyanaz a lejárat)
|
||||
acc_expire = now + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_payload = {**to_encode, "exp": acc_expire, "iat": now, "type": "access"}
|
||||
access_token = jwt.encode(access_payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
|
||||
# Refresh Token
|
||||
ref_expire = now + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
|
||||
# Refresh Token lejárat a remember_me alapján
|
||||
if remember_me:
|
||||
ref_expire = now + timedelta(days=30) # 30 nap remember me esetén
|
||||
else:
|
||||
ref_expire = now + timedelta(days=1) # 1 nap alapértelmezett
|
||||
|
||||
refresh_payload = {"sub": str(to_encode.get("sub")), "exp": ref_expire, "iat": now, "type": "refresh"}
|
||||
refresh_token = jwt.encode(refresh_payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
|
||||
|
||||
23
backend/app/core/translation_helper.py
Normal file
23
backend/app/core/translation_helper.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/core/translation_helper.py
|
||||
"""
|
||||
Helper functions for easy translation usage throughout the application.
|
||||
Provides convenient access to the TranslationService with automatic locale detection.
|
||||
"""
|
||||
from typing import Optional, Dict, Any
|
||||
from app.services.translation_service import TranslationService
|
||||
|
||||
def t(key: str, variables: Optional[Dict[str, Any]] = None, lang: Optional[str] = None) -> str:
|
||||
"""
|
||||
Shortcut function for TranslationService.get_text().
|
||||
Automatically uses the current request locale from context.
|
||||
|
||||
Usage:
|
||||
t("AUTH.REGISTRATION_SUCCESS")
|
||||
t("AUTH.WELCOME", {"name": "John"})
|
||||
t("ERROR.INVALID_TOKEN", lang="en")
|
||||
"""
|
||||
return TranslationService.get_text(key, lang=lang, variables=variables)
|
||||
|
||||
# Alias for backward compatibility
|
||||
get_text = t
|
||||
translate = t
|
||||
Reference in New Issue
Block a user