Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ec654a3
Link share + username share
Nov 14, 2025
482fbfc
Add dual deployment setup with separate prod/dev instances
SidmoGoesBrrr Nov 14, 2025
82f5e40
Merge branch 'dev' into share-duel
Nov 14, 2025
9982da8
akeen gay
Nov 14, 2025
3b28fde
Disable scheduler and background tasks on dev instance
SidmoGoesBrrr Nov 15, 2025
849f2ab
Add developer API info banner and endpoints
SidmoGoesBrrr Nov 15, 2025
aee1e8f
Add Learn UI feature with frontend coding challenges
SidmoGoesBrrr Nov 15, 2025
6918bcd
UI prototype
SidmoGoesBrrr Nov 16, 2025
603bcb3
Merge main into dev - resolve workflow conflicts
SidmoGoesBrrr Nov 17, 2025
ed4623d
Fix XP reset bug - prevent background task from overwriting bonus XP
SidmoGoesBrrr Nov 17, 2025
e939c13
Add /health endpoint for deployment health checks
SidmoGoesBrrr Nov 17, 2025
0c47a5c
pushing for pr
akeenkarkare Nov 17, 2025
5667d84
Merge pull request #19 from yeetcode-xyz/fix/daily-ui-update
akeenkarkare Nov 17, 2025
7ee14a9
Fix infinite loop in daily completion checking
SidmoGoesBrrr Nov 17, 2025
98b728d
Merge conflict
SidmoGoesBrrr Nov 17, 2025
e5035a7
Merge main into dev: daily completion fix + deployment improvements
SidmoGoesBrrr Nov 17, 2025
77816a8
Fix XP field always included in user data responses
SidmoGoesBrrr Nov 17, 2025
65423a6
Convert XP to integer in all responses
SidmoGoesBrrr Nov 17, 2025
23cbede
Fix background task: convert XP to int when preserving
SidmoGoesBrrr Nov 17, 2025
c5d67c2
Merge branch 'main' of https://github.com/yeetcode-xyz/yeetcode-app
SidmoGoesBrrr Nov 17, 2025
e8e56c1
Merge branch 'main' into dev
SidmoGoesBrrr Nov 17, 2025
857004b
Add reverse sort to log viewer and environment-aware log file routing
SidmoGoesBrrr Nov 17, 2025
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
65 changes: 63 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
},
"license": "MIT",
"dependencies": {
"@monaco-editor/react": "^4.7.0",
"axios": "^1.10.0",
"dotenv": "^16.3.1",
"electron-squirrel-startup": "^1.0.1",
Expand Down
1 change: 1 addition & 0 deletions scripts/fastapi/auth.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC
"""
Authentication and rate limiting for YeetCode API
"""
Expand Down
113 changes: 112 additions & 1 deletion scripts/fastapi/aws.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC
"""
AWS DynamoDB operations for YeetCode
Includes graceful error handling for AWS service disruptions
"""

import os
import time
import secrets
import boto3
from datetime import datetime, timezone
from datetime import datetime, timezone, timedelta
from typing import Dict, Optional, List, Any
from botocore.exceptions import ClientError
from dotenv import load_dotenv
Expand Down Expand Up @@ -1872,3 +1874,112 @@ async def handle_duel_timeouts() -> Dict:
if DEBUG_MODE:
print(f"[ERROR] Failed to handle duel timeouts: {error}")
return {"success": False, "error": str(error)}


class DuelInviteLinkOperations:
"""Operations for managing duel invite links"""

@staticmethod
def generate_duel_link(
challenger_username: str,
difficulty: str,
is_wager: bool = False,
wager_amount: int = None
) -> Dict:
"""Generate a shareable duel invite link"""
try:
check_aws_connection()

if not USERS_TABLE:
raise Exception("USERS_TABLE not configured")

# Generate secure token
token = secrets.token_urlsafe(32)

# Calculate expiration (7 days from now)
expires_at = datetime.now(timezone.utc) + timedelta(days=7)
ttl = int(expires_at.timestamp())

# Store invite link info in USERS_TABLE with prefix (similar to verification codes)
invite_key = f"duel_invite_{token}"

invite_data = {
'username': {'S': invite_key},
'challenger_username': {'S': challenger_username.lower()},
'difficulty': {'S': difficulty},
'is_wager': {'S': 'Yes' if is_wager else 'No'},
'wager_amount': {'N': str(wager_amount) if wager_amount else '0'},
'created_at': {'S': datetime.now(timezone.utc).isoformat()},
'expires_at': {'S': expires_at.isoformat()},
'ttl': {'N': str(ttl)}
}

ddb.put_item(
TableName=USERS_TABLE,
Item=invite_data
)

# Generate shareable link
invite_url = f"https://yeetcode.xyz/duel/{token}"

if DEBUG_MODE:
print(f"[DEBUG] Generated duel invite link: {token} for {challenger_username}")

return {
"success": True,
"token": token,
"invite_url": invite_url,
"expires_at": expires_at.isoformat()
}

except Exception as error:
if DEBUG_MODE:
print(f"[ERROR] Failed to generate duel link: {error}")
raise error

@staticmethod
def get_duel_link_info(token: str) -> Optional[Dict]:
"""Get duel invite link information by token"""
try:
check_aws_connection()

if not USERS_TABLE:
raise Exception("USERS_TABLE not configured")

invite_key = f"duel_invite_{token}"

response = ddb.get_item(
TableName=USERS_TABLE,
Key={'username': {'S': invite_key}}
)

if 'Item' not in response:
return None

item = normalize_dynamodb_item(response['Item'])

# Check if expired
expires_at_str = item.get('expires_at')
if expires_at_str:
expires_at = datetime.fromisoformat(expires_at_str.replace('Z', '+00:00'))
if datetime.now(timezone.utc) > expires_at:
# Delete expired invite
ddb.delete_item(
TableName=USERS_TABLE,
Key={'username': {'S': invite_key}}
)
return None

return {
"challenger_username": item.get('challenger_username'),
"difficulty": item.get('difficulty'),
"is_wager": item.get('is_wager') == 'Yes',
"wager_amount": int(item.get('wager_amount', 0)),
"created_at": item.get('created_at'),
"expires_at": item.get('expires_at')
}

except Exception as error:
if DEBUG_MODE:
print(f"[ERROR] Failed to get duel link info: {error}")
return None
4 changes: 3 additions & 1 deletion scripts/fastapi/background_tasks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC
"""
Background tasks for YeetCode FastAPI server
Replaces AWS Lambda functions with integrated FastAPI background jobs
Expand Down Expand Up @@ -144,7 +145,8 @@ async def process_single_user(username: str) -> bool:
current_easy = user_data.get("easy", 0)
current_medium = user_data.get("medium", 0)
current_hard = user_data.get("hard", 0)
current_xp = user_data.get("xp", 0) # Preserve existing bonus XP
# Preserve existing bonus XP (convert to int in case stored as string)
current_xp = int(user_data.get("xp", 0)) if user_data.get("xp") else 0

# Only update if changed
if (stats["easy"] != current_easy or
Expand Down
1 change: 1 addition & 0 deletions scripts/fastapi/cache_dumper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Verify the deployment timestamp accuracy.

The deployment timestamp shows 2024-12-19 00:00:00 UTC, but this PR was created on 2025-11-17. This backdated timestamp could cause confusion for deployment tracking and version history.

🤖 Prompt for AI Agents
In scripts/fastapi/cache_dumper.py around line 1 the hardcoded deployment
timestamp "2024-12-19 00:00:00 UTC" is inaccurate for this PR; replace it with
the correct deployment timestamp (e.g., 2025-11-17 with appropriate time) or
remove the static timestamp and switch to a dynamic approach (compute/inject the
build/deploy time at CI/runtime) so the file reflects accurate, automatically
maintained deployment metadata.

"""
Cache Dumper for YeetCode FastAPI

Expand Down
1 change: 1 addition & 0 deletions scripts/fastapi/cache_loader.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC
"""
Cache Loader for YeetCode FastAPI

Expand Down
1 change: 1 addition & 0 deletions scripts/fastapi/cache_manager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC
"""
Cache Manager for FastAPI - Server-side caching system
"""
Expand Down
5 changes: 3 additions & 2 deletions scripts/fastapi/cache_operations.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC
"""
Cache Operations Helper for YeetCode FastAPI

Expand Down Expand Up @@ -130,8 +131,8 @@ def award_xp_in_cache(username: str, xp_amount: int) -> bool:
if not user:
return False

# Increment XP
current_xp = user.get('xp', 0)
# Increment XP (ensure int conversion in case DB stored as string)
current_xp = int(user.get('xp', 0)) if user.get('xp') else 0
user['xp'] = current_xp + xp_amount

# Write back to cache
Expand Down
1 change: 1 addition & 0 deletions scripts/fastapi/discord_webhook.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC
"""
Discord webhook utility for sending notifications
"""
Expand Down
63 changes: 62 additions & 1 deletion scripts/fastapi/email_service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Verify the deployment timestamp.

The deployment timestamp shows 2024-12-19 00:00:00 UTC, which is in the past relative to the current date (November 2025). Ensure this timestamp reflects the actual deployment date or intended release date.

🤖 Prompt for AI Agents
scripts/fastapi/email_service.py around line 1 contains a hard-coded deployment
timestamp "2024-12-19 00:00:00 UTC" which is outdated; update this to the
correct intended deployment/release date or remove the static timestamp and
replace it with a dynamic value injected at build/deploy time (e.g., CI/CD
variable or environment variable) so it always reflects the actual deployment
time; ensure the timestamp uses a standard ISO UTC format and commit the
corrected value or implementation.

"""
Email operations for YeetCode using Resend
"""
Expand Down Expand Up @@ -70,4 +71,64 @@ def send_email_otp(email: str, code: str) -> Dict:
except Exception as error:
if DEBUG_MODE:
print(f"[ERROR] Failed to send email: {error}")
raise Exception(f"Failed to send email: {str(error)}")
raise Exception(f"Failed to send email: {str(error)}")


def send_yeetcode_invite(
recipient_email: str,
challenger_username: str,
challengee_username: str
) -> Dict:
"""Send simple YeetCode invite email"""
try:
if not resend.api_key:
if DEBUG_MODE:
print(f"[DEBUG] No Resend API key, using mock email for development")
return {"success": True, "messageId": f"mock-invite-{int(time.time())}"}

if DEBUG_MODE:
print(f"[DEBUG] Sending YeetCode invite email to {recipient_email}")

response = resend.Emails.send({
"from": "YeetCode <auth@yeetcode.xyz>",
"to": [recipient_email],
"subject": f"Join YeetCode to duel {challenger_username}",
"html": f"""
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
<div style="text-align: center; margin-bottom: 30px;">
<h1 style="color: #1a1a1a; font-size: 28px; margin: 0;">🚀 YeetCode</h1>
<p style="color: #666; font-size: 16px; margin: 10px 0 0 0;">Competitive LeetCode Platform</p>
</div>

<div style="background: #f8f9fa; border: 2px solid #000; border-radius: 12px; padding: 30px; text-align: center;">
<h2 style="color: #1a1a1a; font-size: 24px; margin: 0 0 20px 0;">You've been invited!</h2>

<p style="color: #374151; font-size: 16px; margin: 20px 0 10px 0;">
Hi {challengee_username},
</p>

<p style="color: #374151; font-size: 16px; margin: 20px 0 10px 0;">
<strong>{challenger_username}</strong> wants to duel you on YeetCode!
</p>

<p style="color: #374151; font-size: 16px; margin: 20px 0 10px 0;">
Sign up at <a href="https://yeetcode.xyz" style="color: #2563eb; text-decoration: none; font-weight: bold;">yeetcode.xyz</a> to accept the challenge.
</p>
</div>

<div style="text-align: center; margin-top: 30px; color: #9ca3af; font-size: 12px;">
<p>If you didn't expect this invite, you can safely ignore this email.</p>
<p>© 2025 YeetCode. Ready to compete?</p>
</div>
</div>
"""
})

if DEBUG_MODE:
print(f"[DEBUG] YeetCode invite email sent successfully: {response.get('id')}")
return {"success": True, "messageId": response.get("id")}

except Exception as error:
if DEBUG_MODE:
print(f"[ERROR] Failed to send YeetCode invite email: {str(error)}")
raise Exception(f"Failed to send YeetCode invite email: {str(error)}")
1 change: 1 addition & 0 deletions scripts/fastapi/logger.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Deployment timestamp: 2024-12-19 00:00:00 UTC
"""
Centralized logging system for YeetCode FastAPI backend

Expand Down
Loading