Skip to content

Commit

Permalink
feat: more improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisgera committed Oct 31, 2024
1 parent 97157c4 commit c3de4fe
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 89 deletions.
2 changes: 1 addition & 1 deletion fly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ primary_region = 'gru'
timeout = "10s"
grace_period = "60s"
method = "GET"
path = "/health"
path = "/api/health"
protocol = "http"

[env]
Expand Down
133 changes: 78 additions & 55 deletions frontend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,94 +10,117 @@
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

class APIError(Exception):
pass

def get_api_url() -> str:
"""Get the API URL based on the environment"""
environment = os.getenv("ENVIRONMENT", "development")
logger.info(f"Current environment: {environment}")

if environment == "production":
# Always use the /api prefix in production
api_host = os.getenv("API_HOST", "https://stackr-cold-violet-2349.fly.dev")
base_url = f"{api_host}/api"
host = os.getenv("API_HOST", "stackr-cold-violet-2349.fly.dev").rstrip('/')
if not host.startswith('http'):
host = f"https://{host}"
base_url = f"{host}/api"
logger.info(f"Using production API URL: {base_url}")
return base_url
else:
# Development environment
host = os.getenv("API_HOST", "http://backend")
port = os.getenv("API_PORT", "8000")
base_url = f"{host}:{port}"
logger.info(f"Using development API URL: {base_url}")
return base_url


def make_request(
method: str, endpoint: str, max_retries: int = 3, retry_delay: int = 1, **kwargs
method: str,
endpoint: str,
max_retries: int = 3,
retry_delay: int = 1,
**kwargs
) -> Optional[requests.Response]:
"""
Make a request to the API with enhanced error handling
"""
base_url = get_api_url()

# Ensure endpoint doesn't start with slash
endpoint = endpoint.lstrip("/")

# Construct full URL
# Ensure consistent path handling
endpoint = endpoint.strip('/')
url = f"{base_url}/{endpoint}"

logger.info(f"Making {method.upper()} request to: {url}")


session = requests.Session()

# Set default headers
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
# Update with any custom headers
if 'headers' in kwargs:
headers.update(kwargs.pop('headers'))

for attempt in range(max_retries):
try:
logger.info(f"Attempt {attempt + 1}/{max_retries}")
response = requests.request(method, url, timeout=30, **kwargs)

# Log response details

response = session.request(
method,
url,
headers=headers,
timeout=30,
allow_redirects=True, # Follow redirects
**kwargs
)

logger.info(f"Response status code: {response.status_code}")
logger.info(f"Response headers: {dict(response.headers)}")

try:
# If response is JSON, log it
if "application/json" in response.headers.get("content-type", ""):
logger.info(f"JSON Response: {response.json()}")
else:
logger.warning(
f"Non-JSON response received: {response.content[:200]}"
)
except Exception as e:
logger.error(f"Error parsing response: {str(e)}")

# Handle redirect

if response.history:
logger.info(
f"Request was redirected: {[r.status_code for r in response.history]}"
)

# Check for successful response
if response.status_code in (200, 201):
if not response.content:
logger.warning("Empty response content")
return response

try:
response.json() # Validate JSON
return response
except json.JSONDecodeError as e:
logger.error(f"Invalid JSON response: {str(e)}")
if attempt < max_retries - 1:
time.sleep(retry_delay * (attempt + 1))
continue
else:
logger.error(f"Request failed with status code: {response.status_code}")
logger.info(f"Request was redirected: {' -> '.join(str(r.url) for r in response.history)}")
logger.info(f"Final URL: {response.url}")

# Check content type
content_type = response.headers.get('content-type', '')
if 'json' not in content_type:
logger.error(f"Unexpected content type: {content_type}")
logger.error(f"Response content: {response.content[:200]}")
st.error(f"API request failed: {response.status_code}")
return None

if attempt < max_retries - 1:
time.sleep(retry_delay * (attempt + 1))
continue
raise APIError(f"Expected JSON response, got {content_type}")

# Try to parse response
try:
response.json()
return response
except json.JSONDecodeError as e:
logger.error(f"Failed to parse JSON: {str(e)}")
logger.error(f"Response content: {response.content[:200]}")
if attempt < max_retries - 1:
time.sleep(retry_delay * (attempt + 1))
continue
raise APIError("Invalid JSON response from server")

except requests.exceptions.RequestException as e:
logger.error(f"Request failed: {str(e)}")
if attempt < max_retries - 1:
time.sleep(retry_delay * (attempt + 1))
continue
st.error("Failed to connect to the server")
st.error("Failed to connect to the server. Please try again later.")
return None

return None

except APIError as e:
logger.error(f"API Error: {str(e)}")
st.error(str(e))
return None

except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
if attempt < max_retries - 1:
time.sleep(retry_delay * (attempt + 1))
continue
st.error(f"An unexpected error occurred: {str(e)}")
return None

return None
53 changes: 20 additions & 33 deletions nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ http {
sendfile on;
keepalive_timeout 65;

# Enhanced logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
Expand All @@ -21,12 +20,6 @@ http {
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log debug;

# Better buffer settings
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_temp_file_write_size 256k;

upstream backend {
server 127.0.0.1:8000;
keepalive 32;
Expand All @@ -37,44 +30,33 @@ http {
keepalive 32;
}

# Remove trailing slashes
server {
listen 8080;
listen 8080 default_server;
server_name _;

# Redirect requests with trailing slashes
rewrite ^/(.*)/$ /$1 permanent;

location = / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_buffering off;
}
client_max_body_size 100M;

# Handle API requests
# API endpoints
location /api/ {
proxy_pass http://backend/; # Note: removed the /api/ suffix
rewrite ^/api/(.*) /$1 break;
proxy_pass http://backend/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";

# Add headers for better error tracking
add_header X-Upstream $upstream_addr;
proxy_intercept_errors on;
# Prevent redirects
proxy_redirect off;

# Remove /api prefix when proxying to backend
rewrite ^/api/(.*) /$1 break;
# Better error handling
proxy_intercept_errors on;
error_page 404 = @notfound;
}

# Streamlit specific locations
location /_stcore {
proxy_pass http://frontend/_stcore;
# Streamlit static files
location /_stcore/ {
proxy_pass http://frontend/_stcore/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Expand All @@ -93,8 +75,13 @@ http {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_buffering off;
}

# 404 handler
location @notfound {
return 404 '{"error": "Not Found"}';
add_header Content-Type application/json;
}
}
}

0 comments on commit c3de4fe

Please sign in to comment.