CodeSwitch is a full-stack web application that converts source code between five programming languages using a dual-engine approach: an AI-powered backend (Groq / OpenAI / Google Gemini) with automatic key rotation and a pure rule-based fallback engine. It also includes a live code execution sandbox, shareable conversion snippets, an interactive learning platform, a personal file manager, and a full admin panel — all behind a strict security stack with httpOnly cookie auth, CSP, and brute-force lockout.
| Feature | Description |
|---|---|
| Code Converter | Convert between Python, C, C++, Java, and JavaScript |
| Dual Engine | AI-first (Groq/OpenAI/Gemini) with auto key rotation; regex rule-based fallback |
| Live Code Execution | Run converted code instantly via Wandbox sandbox |
| AI Code Explanation | Plain-English explanation of what changed between source and output |
| Shareable Snippets | Share any conversion as a permanent public UUID-slug URL |
| 5 Editor Themes | Dark, Light, High Contrast, Monokai, Dracula (Monaco Editor) |
| 13 Learning Modules | Beginner → Advanced topics with quizzes and in-lesson Monaco sandbox |
| Progress Tracking | Track completed lessons and quiz attempts per user |
| File Manager | Save, edit, and organize code files in the cloud per language |
| Dashboard | Conversion history, learning progress, and file stats in one view |
| Public Profiles | Public per-user page showing conversion count, lessons, and languages used |
| Admin Panel | Staff-only dashboard: user management, module/lesson CRUD, site stats |
| httpOnly JWT Auth | Tokens stored in httpOnly cookies — never accessible to JavaScript |
- Python 3.11 + Django 4 + Django REST Framework
- SimpleJWT — httpOnly cookie-based auth with token rotation and blacklist
- Custom
JWTCookieAuthenticationbackend (tokens never exposed to JS) - AI integration: Groq, OpenAI, or Google Gemini — configurable provider with 3-key failover
- Rule-based conversion engine (
converter/services.py) as offline fallback - Code execution proxied to Wandbox (sandboxed, no server eval)
django-axes— brute-force login lockout (5 attempts → 1-hour cooldown)django-csp— strict Content Security Policy middleware- SQLite (dev) / PostgreSQL via
DATABASE_URL(prod, Railway / Neon) - Gunicorn + Railway deployment; Railway pre-deploy handles migrations, seeding, and static collection
- React 18 + TypeScript, built with Vite
- Monaco Editor (
@monaco-editor/react) — 5 themes, lazy-loaded to cut initial bundle size React.lazy+Suspensecode splitting (defers Monaco-heavy pages)ErrorBoundaryclass component — catches render errors with a "Try Again" fallback- Axios with automatic JWT refresh interceptor and CSRF header injection
- TailwindCSS for styling
- Deployed on Vercel
codeswitch_project/
├── codeswitch/ # Django project root
│ ├── codeswitch/ # Settings, URLs, WSGI
│ │ ├── settings.py # All security, JWT, throttle, CSP config
│ │ ├── urls.py # Root url routing
│ │ └── admin_views.py # Staff-only admin API views
│ ├── users/ # Auth: register, login, logout, profile
│ │ └── authentication.py # Custom httpOnly cookie JWT backend
│ ├── converter/ # Code conversion API + AI service
│ │ ├── ai_service.py # Multi-provider AI client with key rotation
│ │ ├── services.py # Rule-based regex conversion engine (~46 KB)
│ │ ├── throttles.py # Per-user AI burst/sustained + anon run throttles
│ │ └── views.py # Convert, history, run, explain, snippet endpoints
│ ├── files/ # User file storage API
│ ├── learning/ # Modules, lessons, quizzes, progress tracking
│ │ └── management/commands/ # Seed scripts (auto-run on deploy if DB is empty)
│ ├── frontend/ # React + TypeScript app (Vite, dev port 3000)
│ │ └── src/
│ │ ├── pages/ # Landing, Login, Dashboard, Converter, FileManager,
│ │ │ # Learning, Reference, ProfilePage, AdminPanel,
│ │ │ # Playground, ShareView
│ │ ├── components/ # CodeEditor, DiffView, LanguageSelector, Logo
│ │ ├── types/index.ts # Shared TypeScript interfaces
│ │ └── api/client.ts # Axios instance with refresh interceptor + CSRF
│ ├── .env.example # Environment variable template
│ ├── requirements.txt
│ ├── Procfile # Railway web process: Gunicorn only
│ └── manage.py
└── venv/ # Not committed
- Python 3.11+
- Node.js 18+
- (Optional) PostgreSQL for production
git clone https://github.com/your-username/codeswitch.git
cd codeswitchpython -m venv venv
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activatecd codeswitch
pip install -r requirements.txtcp .env.example .envEdit .env and fill in your values:
SECRET_KEY=your-django-secret-key-min-50-chars
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
CORS_ALLOWED_ORIGINS=http://localhost:3000
CSRF_TRUSTED_ORIGINS=http://localhost:3000
# Leave DATABASE_URL blank to use SQLite (recommended for dev)
DATABASE_URL=
# AI conversion — falls back to rule-based engine if all keys are blank
AI_PROVIDER=groq # groq | openai | gemini
AI_API_KEY=your-api-key
AI_API_KEY_2= # optional failover key
AI_API_KEY_3= # optional failover key
AI_MODEL=openai/gpt-oss-20bpython manage.py migrate
python manage.py seed_all_if_empty # seeds all modules, lessons, and quizzes if DB is emptyOr individually:
python manage.py seed_learning
python manage.py seed_modules
python manage.py seed_advanced_modules
python manage.py seed_new_modules
python manage.py seed_quizzes
python manage.py seed_remaining_quizzes
python manage.py seed_new_quizzespython manage.py createsuperuserpython manage.py runservercd frontend
npm install
npm run devThe frontend runs at http://localhost:3000 The API runs at http://localhost:8000 The Django admin is at http://localhost:8000/admin
| Variable | Description | Default |
|---|---|---|
SECRET_KEY |
Django secret key (50+ chars recommended) | — |
DEBUG |
Debug mode | True |
ALLOWED_HOSTS |
Comma-separated allowed hostnames | localhost,127.0.0.1 |
DATABASE_URL |
Full DB URL (blank = SQLite) | `` |
DB_NAME |
PostgreSQL DB name (fallback if no DATABASE_URL) | codeswitch_db |
DB_USER |
PostgreSQL user | postgres |
DB_PASSWORD |
PostgreSQL password | — |
DB_HOST |
Database host | localhost |
CORS_ALLOWED_ORIGINS |
Allowed CORS origins | http://localhost:3000 |
CSRF_TRUSTED_ORIGINS |
Trusted origins for CSRF | http://localhost:3000 |
AI_PROVIDER |
AI provider (groq/openai/gemini) |
groq |
AI_API_KEY |
Primary AI API key | — |
AI_API_KEY_2 |
First failover AI key | — |
AI_API_KEY_3 |
Second failover AI key | — |
AI_MODEL |
Model name | openai/gpt-oss-20b |
JWT_ACCESS_TOKEN_LIFETIME_MINUTES |
Access token TTL | 60 |
JWT_REFRESH_TOKEN_LIFETIME_DAYS |
Refresh token TTL | 7 |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/api/register/ |
Create account | No |
POST |
/api/login/ |
Login (username or email) | No |
POST |
/api/logout/ |
Logout + blacklist refresh token + clear cookies | Yes |
POST |
/api/token/refresh/ |
Rotate access token using refresh cookie | No |
GET |
/api/me/ |
Current authenticated user | Yes |
GET/PUT |
/api/profile/ |
Get / update profile (bio, avatar) | Yes |
GET |
/api/users/<username>/ |
Public profile — stats, languages, progress | No |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/api/convert/ |
Convert code (AI + rule-based fallback) | Yes |
GET |
/api/convert/history/ |
Last 50 conversions for the current user | Yes |
POST |
/api/run/ |
Execute code via Wandbox sandbox | No |
POST |
/api/explain/ |
AI explanation of conversion differences | Yes |
POST |
/api/snippets/ |
Create a shareable snippet | Yes |
GET |
/api/snippets/<slug>/ |
Read a shared snippet (public) | No |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET/POST |
/api/files/ |
List paginated metadata / create files | Yes |
GET/PUT/DELETE |
/api/files/<id>/ |
Retrieve, update, or delete a file | Yes |
File lists return {count, next, previous, results} with 50 items by default
(page and page_size, maximum 100). List items omit code_content; retrieve
one file to load its source. Deploy the frontend and backend together when
adopting this response format.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/api/modules/ |
List all learning modules | Yes |
GET |
/api/modules/<id>/ |
Module detail + lessons | Yes |
GET |
/api/lessons/<id>/ |
Single lesson detail | Yes |
POST |
/api/progress/update/ |
Mark a lesson as complete | Yes |
GET |
/api/progress/ |
All progress for current user | Yes |
GET |
/api/quizzes/<lesson_id>/ |
Quiz for a lesson | Yes |
POST |
/api/quizzes/<lesson_id>/attempt/ |
Submit quiz answers | Yes |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/admin/stats/ |
Site-wide counts + week-over-week stats |
GET/POST |
/api/admin/users/ |
List users |
GET/PUT/DELETE |
/api/admin/users/<id>/ |
Toggle staff/active, delete user |
GET |
/api/admin/conversions/ |
Last 100 conversions across all users |
GET/POST |
/api/admin/modules/ |
List / create learning modules |
GET/PUT/DELETE |
/api/admin/modules/<id>/ |
Edit or delete a module |
GET |
/api/admin/modules/<id>/lessons/ |
All lessons for a module |
GET/PUT/DELETE |
/api/admin/lessons/<id>/ |
Edit or delete a lesson |
| Module | Level |
|---|---|
| CodeSwitch Learning Module | Beginner |
| Working with Strings | Beginner |
| Data Structures | Intermediate |
| Object-Oriented Programming | Intermediate |
| Error Handling and File I/O | Advanced |
| Algorithms and Recursion | Advanced |
| Advanced Sorting Algorithms | Intermediate |
| Dynamic Programming | Advanced |
| Graph Algorithms | Advanced |
| Pointers & Memory Management | Advanced |
| Linked Lists | Intermediate |
| Stacks & Queues | Intermediate |
| Hash Tables & Dictionaries | Intermediate |
Each module contains structured lessons with a built-in Monaco sandbox for live experimentation, followed by a quiz. Progress is tracked per user and visible on the dashboard and public profile.
- httpOnly cookie JWT — Access and refresh tokens are set as
httpOnly; Secure; SameSite=Nonecookies. JavaScript on the page can never read them, eliminating XSS-based token theft. - Custom
JWTCookieAuthentication— Django reads tokens from the cookie header, not fromAuthorization, so the frontend never handles raw tokens. - Token rotation + blacklist — Every token refresh rotates the refresh token and the old token is blacklisted. Logout explicitly blacklists the current refresh token and clears both cookies.
- Access token TTL: 60 min. Refresh token TTL: 7 days.
- Minimum 8 characters
- Must contain at least one uppercase letter, one lowercase letter, one digit, and one special character
- Enforced by
RegisterSerializer._check_password_strength()before any database write
django-axeslocks an account after 5 consecutive failed login attempts- Lockout duration: 1 hour
- Locks on the combination of username + IP address
- Resets automatically on successful login
| Scope | Limit |
|---|---|
| Authenticated users (global) | 300 requests/minute |
| Anonymous users (global) | 30 requests/minute |
| AI conversion — burst | 5 requests/minute per user |
| AI conversion — sustained | 30 requests/hour per user |
| Anonymous code execution | 10 requests/minute |
- Language allowlist — only
python,c,cpp,java,javascriptaccepted - Code body capped at 50,000 characters
- Null bytes stripped from code input before processing
- All serializer fields validated by DRF before hitting any service logic
CORS_ALLOWED_ORIGINSis loaded from.env— no wildcard origins allowed when credentials are enabledCORS_ALLOW_CREDENTIALS = True— required for cross-origin cookie delivery (Vercel → Railway)X-CSRFTokenheader injected automatically by the Axios client on every state-changing requestCSRF_TRUSTED_ORIGINSloaded from.env
| Header | Value |
|---|---|
X-Frame-Options |
DENY |
X-Content-Type-Options |
nosniff |
X-XSS-Protection |
1; mode=block |
Strict-Transport-Security |
1 year, includeSubDomains, preload (production only) |
Content-Security-Policy |
default-src 'none'; script-src 'self' 'unsafe-eval'; frame-ancestors 'none' |
django-csp enforces a strict policy:
default-src 'none'script-src 'self' 'unsafe-eval'—unsafe-evalrequired by Monaco Editor's worker modelfont-src fonts.gstatic.comconnect-src 'self' wandbox.org— allows proxied code execution callsframe-ancestors 'none'— prevents clickjacking
- Code is never executed on the CodeSwitch server
- All run requests are proxied to Wandbox, an isolated third-party sandbox
- The
RunCodeViewautomatically selects the best available Wandbox compiler for the target language
- All secrets (
SECRET_KEY, API keys, DB credentials) are loaded exclusively from.env— never hardcoded .envand.env.localare listed in.gitignore
Railway collects static files into the deploy image during an isolated
DEBUG=True build step, then runs
migrations and idempotent seeding in the pre-deploy container through
codeswitch/railway.json. The Procfile starts only Gunicorn:
The existing PostgreSQL database also stores shared rate-limit cache entries, so this deployment does not require another cache service or volume.
web: gunicorn codeswitch.wsgi --bind 0.0.0.0:$PORT --access-logfile - --error-logfile -
Set these Railway environment variables: DATABASE_URL, SECRET_KEY, ALLOWED_HOSTS, CORS_ALLOWED_ORIGINS, CSRF_TRUSTED_ORIGINS, GOOGLE_OAUTH_CLIENT_ID, AI_PROVIDER, AI_API_KEY, AI_API_KEY_2, AI_API_KEY_3, AI_MODEL, TRUSTED_PROXY_COUNT, DEBUG=False.
TRUSTED_PROXY_COUNT is required when DEBUG=False. Before deploying, verify
how many trusted proxies forward traffic into Django and how they sanitize
client-supplied X-Forwarded-For headers. Set that verified hop count; use 1
only when the actual path has one trusted proxy. Do not guess a value from the
hosting provider's name. Local development uses 0.
Recognized transient database failures return HTTP 503 with Retry-After: 2.
The backend does not replay requests or writes automatically.
Deployment uses migrate_for_deploy. If the connection drops after migrations
finish, it reconnects and retries the idempotent permission/content-type checks
at most twice. It stops if migrations remain unapplied or the error is unrelated
to a dropped connection.
External JSON requests use short-lived Python processes so the overall deadline also covers DNS lookup, response headers, and body decoding. AI attempts share a 20-second budget; Google certificate fetches have an eight-second budget. Dependency timing metrics remain enabled. Outbound trace spans record the method, host, and status without URLs or credential headers.
New avatar uploads are stored in PostgreSQL. A media volume is needed only if you retain older filesystem-backed avatars.
Build command: npm run build
Output directory: build
Node.js version: 24.x
Set VITE_API_URL to your Railway backend API URL, including /api, and VITE_GOOGLE_CLIENT_ID to the same Google OAuth Web Client ID used by Railway.
Google Cloud OAuth Web Client authorized JavaScript origins should include
http://localhost:3000 and the production Vercel URL. This ID-token flow does
not require redirect URIs.
This project is for educational purposes.