UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

1,585 lines (1,317 loc) • 55.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.tornadoTemplate = void 0; exports.tornadoTemplate = { id: 'tornado-py', name: 'Tornado + Python', displayName: 'Tornado + Python', description: 'High-performance Tornado server with async/await support, WebSocket connections, coroutines, and non-blocking I/O', framework: 'tornado', language: 'python', version: '1.0.0', tags: ['tornado', 'python', 'async', 'websockets', 'coroutines', 'high-performance', 'non-blocking'], dependencies: { tornado: '^6.4', 'motor': '^3.3.2', 'aiopg': '^1.4.0', 'aioredis': '^2.0.1', 'pyjwt': '^2.8.0', 'bcrypt': '^4.1.2', 'python-dotenv': '^1.0.0', 'marshmallow': '^3.20.1', 'marshmallow-sqlalchemy': '^0.29.0', 'sqlalchemy': '^2.0.23', 'alembic': '^1.13.0', 'psycopg2': '^2.9.9', 'celery': '^5.3.4', 'redis': '^5.0.1' }, devDependencies: { 'pytest': '^7.4.3', 'pytest-asyncio': '^0.21.1', 'pytest-tornado': '^0.8.1', 'pytest-cov': '^4.1.0', 'black': '^23.11.0', 'isort': '^5.12.0', 'mypy': '^1.7.1', 'flake8': '^6.1.0', 'pre-commit': '^3.6.0' }, files: { 'requirements.txt': `tornado==6.4 motor==3.3.2 aiopg==1.4.0 aioredis==2.0.1 pyjwt==2.8.0 bcrypt==4.1.2 python-dotenv==1.0.0 marshmallow==3.20.1 marshmallow-sqlalchemy==0.29.0 sqlalchemy==2.0.23 alembic==1.13.0 psycopg2==2.9.9 celery==5.3.4 redis==5.0.1`, 'requirements-dev.txt': `pytest==7.4.3 pytest-asyncio==0.21.1 pytest-tornado==0.8.1 pytest-cov==4.1.0 black==23.11.0 isort==5.12.0 mypy==1.7.1 flake8==6.1.0 pre-commit==3.6.0`, 'main.py': `#!/usr/bin/env python3 """ Tornado Application Entry Point """ import asyncio import os import signal import sys from typing import Dict, Any import tornado.ioloop import tornado.web import tornado.httpserver from tornado.options import define, options, parse_command_line from app.core.config import settings from app.core.application import create_application from app.core.database import init_database from app.core.redis_client import init_redis from app.core.logging_config import setup_logging # Command line options define("port", default=settings.PORT, help="run on the given port", type=int) define("debug", default=settings.DEBUG, help="run in debug mode") async def main(): """Main application entry point.""" parse_command_line() # Setup logging setup_logging(debug=options.debug) # Initialize database and Redis await init_database() await init_redis() # Create Tornado application app = create_application(debug=options.debug) # Create HTTP server server = tornado.httpserver.HTTPServer(app, xheaders=True) server.listen(options.port) print(f"šŸŒŖļø Tornado server started on http://localhost:{options.port}") print(f"Debug mode: {options.debug}") # Setup signal handlers for graceful shutdown def signal_handler(sig, frame): print("\\n⚔ Shutting down server...") tornado.ioloop.IOLoop.current().stop() sys.exit(0) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) # Start the IO loop tornado.ioloop.IOLoop.current().start() if __name__ == "__main__": asyncio.run(main())`, 'app/__init__.py': '', 'app/core/__init__.py': '', 'app/core/config.py': `""" Application Configuration """ import os from typing import Optional from dotenv import load_dotenv load_dotenv() class Settings: """Application settings.""" # Server Configuration HOST: str = os.getenv("HOST", "localhost") PORT: int = int(os.getenv("PORT", 8000)) DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true" # Security SECRET_KEY: str = os.getenv("SECRET_KEY", "your-secret-key-change-in-production") JWT_SECRET_KEY: str = os.getenv("JWT_SECRET_KEY", "jwt-secret-key-change-in-production") JWT_ALGORITHM: str = os.getenv("JWT_ALGORITHM", "HS256") JWT_EXPIRATION_DELTA: int = int(os.getenv("JWT_EXPIRATION_DELTA", 3600)) # Database DATABASE_URL: str = os.getenv( "DATABASE_URL", "postgresql://user:password@localhost:5432/tornado_app" ) # Redis REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379/0") # CORS CORS_ORIGINS: list = os.getenv("CORS_ORIGINS", "*").split(",") # Logging LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO") settings = Settings()`, 'app/core/application.py': `""" Tornado Application Factory """ import tornado.web from tornado.web import Application from app.handlers.auth_handlers import AuthLoginHandler, AuthLogoutHandler, AuthRegisterHandler from app.handlers.user_handlers import UserHandler, UserProfileHandler from app.handlers.websocket_handlers import ChatWebSocketHandler, NotificationWebSocketHandler from app.handlers.api_handlers import HealthCheckHandler, StatusHandler from app.core.config import settings def create_application(debug: bool = False) -> Application: """Create and configure Tornado application.""" handlers = [ # Health and Status (r"/health", HealthCheckHandler), (r"/api/v1/status", StatusHandler), # Authentication (r"/api/v1/auth/register", AuthRegisterHandler), (r"/api/v1/auth/login", AuthLoginHandler), (r"/api/v1/auth/logout", AuthLogoutHandler), # Users (r"/api/v1/users", UserHandler), (r"/api/v1/users/([0-9]+)", UserHandler), (r"/api/v1/users/profile", UserProfileHandler), # WebSocket endpoints (r"/ws/chat", ChatWebSocketHandler), (r"/ws/notifications", NotificationWebSocketHandler), # Static files (in production, use nginx) (r"/static/(.*)", tornado.web.StaticFileHandler, {"path": "static"}), ] app_settings = { "debug": debug, "autoreload": debug, "cookie_secret": settings.SECRET_KEY, "xsrf_cookies": False, # Disable for API usage "compress_response": True, "static_path": "static", "template_path": "templates", "default_handler_class": NotFoundHandler, } return Application(handlers, **app_settings) class NotFoundHandler(tornado.web.RequestHandler): """Handle 404 errors.""" def prepare(self): """Called before any HTTP method.""" self.set_status(404) self.finish({ "error": "Not Found", "message": "The requested resource was not found", "status_code": 404 })`, 'app/core/database.py': `""" Database Configuration and Connection """ import asyncio from typing import Optional import aiopg from sqlalchemy import create_engine, MetaData from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import NullPool from app.core.config import settings # Database connection pool _connection_pool: Optional[aiopg.pool.Pool] = None _engine = None _SessionLocal = None # SQLAlchemy Base Base = declarative_base() metadata = MetaData() async def init_database(): """Initialize database connection pool.""" global _connection_pool, _engine, _SessionLocal # Create async connection pool _connection_pool = await aiopg.create_pool( dsn=settings.DATABASE_URL, minsize=1, maxsize=10, loop=asyncio.get_event_loop() ) # Create SQLAlchemy engine for migrations _engine = create_engine( settings.DATABASE_URL, poolclass=NullPool, echo=settings.DEBUG ) # Create session maker _SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=_engine) print("šŸ“Š Database connection pool initialized") async def get_db_connection(): """Get database connection from pool.""" if _connection_pool is None: raise RuntimeError("Database not initialized") async with _connection_pool.acquire() as conn: yield conn def get_sync_db(): """Get synchronous database session for migrations.""" if _SessionLocal is None: raise RuntimeError("Database not initialized") db = _SessionLocal() try: yield db finally: db.close() async def close_database(): """Close database connection pool.""" global _connection_pool if _connection_pool: _connection_pool.close() await _connection_pool.wait_closed() _connection_pool = None print("šŸ“Š Database connection pool closed")`, 'app/core/redis_client.py': `""" Redis Client Configuration """ import aioredis from typing import Optional from app.core.config import settings # Redis connection _redis_client: Optional[aioredis.Redis] = None async def init_redis(): """Initialize Redis connection.""" global _redis_client _redis_client = await aioredis.from_url( settings.REDIS_URL, encoding="utf-8", decode_responses=True, max_connections=20 ) # Test connection await _redis_client.ping() print("šŸ”“ Redis connection established") async def get_redis() -> aioredis.Redis: """Get Redis client.""" if _redis_client is None: raise RuntimeError("Redis not initialized") return _redis_client async def close_redis(): """Close Redis connection.""" global _redis_client if _redis_client: await _redis_client.close() _redis_client = None print("šŸ”“ Redis connection closed")`, 'app/core/logging_config.py': `""" Logging Configuration """ import logging import sys from app.core.config import settings def setup_logging(debug: bool = False): """Setup application logging.""" log_level = logging.DEBUG if debug else getattr(logging, settings.LOG_LEVEL.upper()) # Configure root logger logging.basicConfig( level=log_level, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[ logging.StreamHandler(sys.stdout), logging.FileHandler("app.log") if not debug else logging.NullHandler() ] ) # Configure tornado access logs access_log = logging.getLogger("tornado.access") access_log.setLevel(log_level) # Configure application logger app_log = logging.getLogger("app") app_log.setLevel(log_level) if debug: app_log.info("šŸ› Debug logging enabled")`, 'app/handlers/__init__.py': '', 'app/handlers/base.py': `""" Base Request Handler """ import json import jwt import tornado.web from typing import Optional, Dict, Any from tornado.concurrent import run_on_executor from concurrent.futures import ThreadPoolExecutor import asyncio from app.core.config import settings from app.core.redis_client import get_redis class BaseHandler(tornado.web.RequestHandler): """Base request handler with common functionality.""" executor = ThreadPoolExecutor(max_workers=10) def set_default_headers(self): """Set CORS headers.""" self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With") self.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") self.set_header("Content-Type", "application/json") def options(self): """Handle preflight CORS requests.""" self.set_status(204) self.finish() def write_json(self, data: Any, status_code: int = 200): """Write JSON response.""" self.set_status(status_code) self.write(json.dumps(data, default=str)) def write_error(self, message: str, status_code: int = 400, details: Optional[Dict] = None): """Write error response.""" error_data = { "error": message, "status_code": status_code } if details: error_data.update(details) self.write_json(error_data, status_code) def get_json_body(self) -> Dict[str, Any]: """Parse JSON request body.""" try: return json.loads(self.request.body.decode('utf-8')) except (json.JSONDecodeError, UnicodeDecodeError): raise tornado.web.HTTPError(400, "Invalid JSON body") async def get_current_user_async(self) -> Optional[Dict[str, Any]]: """Get current authenticated user from JWT token.""" auth_header = self.request.headers.get("Authorization") if not auth_header or not auth_header.startswith("Bearer "): return None token = auth_header.split(" ")[1] try: # Decode JWT token payload = jwt.decode( token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM] ) # Check if token is blacklisted in Redis redis_client = await get_redis() is_blacklisted = await redis_client.get(f"blacklist:{token}") if is_blacklisted: return None return payload except jwt.ExpiredSignatureError: return None except jwt.InvalidTokenError: return None @run_on_executor def blocking_operation(self, operation, *args, **kwargs): """Run blocking operations in thread pool.""" return operation(*args, **kwargs)`, 'app/handlers/auth_handlers.py': `""" Authentication Handlers """ import bcrypt import jwt import tornado.web from datetime import datetime, timedelta from typing import Dict, Any from app.handlers.base import BaseHandler from app.core.config import settings from app.core.redis_client import get_redis from app.models.user import User, UserSchema class AuthRegisterHandler(BaseHandler): """User registration handler.""" async def post(self): """Register a new user.""" try: data = self.get_json_body() schema = UserSchema() # Validate input data try: validated_data = schema.load(data) except Exception as e: self.write_error("Validation error", 400, {"details": str(e)}) return # Check if user already exists # In a real app, you'd check the database # For this template, we'll simulate the check # Hash password hashed_password = bcrypt.hashpw( validated_data['password'].encode('utf-8'), bcrypt.gensalt() ).decode('utf-8') # Create user (simulate database operation) user_data = { "id": 1, # In real app, this would be from database "email": validated_data['email'], "username": validated_data.get('username', validated_data['email']), "is_active": True, "created_at": datetime.utcnow() } # Store in Redis as example redis_client = await get_redis() await redis_client.setex( f"user:{user_data['id']}", 3600, str(user_data) ) # Generate JWT token token_payload = { "user_id": user_data['id'], "email": user_data['email'], "exp": datetime.utcnow() + timedelta(seconds=settings.JWT_EXPIRATION_DELTA) } access_token = jwt.encode( token_payload, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM ) response_data = { "message": "User registered successfully", "user": { "id": user_data['id'], "email": user_data['email'], "username": user_data['username'] }, "access_token": access_token, "token_type": "bearer" } self.write_json(response_data, 201) except Exception as e: self.write_error(f"Registration failed: {str(e)}", 500) class AuthLoginHandler(BaseHandler): """User login handler.""" async def post(self): """Authenticate user and return JWT token.""" try: data = self.get_json_body() email = data.get('email') password = data.get('password') if not email or not password: self.write_error("Email and password required", 400) return # In a real app, you'd fetch user from database # For this template, we'll simulate authentication # Simulate user lookup and password verification user_data = { "id": 1, "email": email, "username": email.split('@')[0], "is_active": True } # Generate JWT token token_payload = { "user_id": user_data['id'], "email": user_data['email'], "exp": datetime.utcnow() + timedelta(seconds=settings.JWT_EXPIRATION_DELTA) } access_token = jwt.encode( token_payload, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM ) response_data = { "message": "Login successful", "user": user_data, "access_token": access_token, "token_type": "bearer" } self.write_json(response_data) except Exception as e: self.write_error(f"Login failed: {str(e)}", 500) class AuthLogoutHandler(BaseHandler): """User logout handler.""" async def post(self): """Logout user by blacklisting JWT token.""" try: user = await self.get_current_user_async() if not user: self.write_error("Authentication required", 401) return # Get token from header auth_header = self.request.headers.get("Authorization") token = auth_header.split(" ")[1] # Blacklist token in Redis redis_client = await get_redis() await redis_client.setex( f"blacklist:{token}", settings.JWT_EXPIRATION_DELTA, "true" ) self.write_json({"message": "Logout successful"}) except Exception as e: self.write_error(f"Logout failed: {str(e)}", 500)`, 'app/handlers/user_handlers.py': `""" User Management Handlers """ import tornado.web from typing import Dict, Any from app.handlers.base import BaseHandler class UserHandler(BaseHandler): """User CRUD operations handler.""" async def get(self, user_id: str = None): """Get user(s) information.""" try: current_user = await self.get_current_user_async() if not current_user: self.write_error("Authentication required", 401) return if user_id: # Get specific user user_data = { "id": int(user_id), "email": f"user{user_id}@example.com", "username": f"user{user_id}", "is_active": True } self.write_json({"user": user_data}) else: # Get all users (with pagination) page = int(self.get_argument("page", 1)) limit = int(self.get_argument("limit", 10)) # Simulate user list users = [ { "id": i, "email": f"user{i}@example.com", "username": f"user{i}", "is_active": True } for i in range((page-1)*limit + 1, page*limit + 1) ] response_data = { "users": users, "pagination": { "page": page, "limit": limit, "total": 100, # Simulated total "pages": 10 } } self.write_json(response_data) except Exception as e: self.write_error(f"Failed to get users: {str(e)}", 500) async def put(self, user_id: str): """Update user information.""" try: current_user = await self.get_current_user_async() if not current_user: self.write_error("Authentication required", 401) return # Check if user can update this profile if current_user['user_id'] != int(user_id): self.write_error("Permission denied", 403) return data = self.get_json_body() # Update user data (simulate database operation) updated_user = { "id": int(user_id), "email": data.get('email', current_user['email']), "username": data.get('username', f"user{user_id}"), "is_active": True, "updated_at": "2024-01-01T00:00:00Z" } self.write_json({ "message": "User updated successfully", "user": updated_user }) except Exception as e: self.write_error(f"Failed to update user: {str(e)}", 500) async def delete(self, user_id: str): """Delete user account.""" try: current_user = await self.get_current_user_async() if not current_user: self.write_error("Authentication required", 401) return # Check if user can delete this profile if current_user['user_id'] != int(user_id): self.write_error("Permission denied", 403) return # Delete user (simulate database operation) self.write_json({"message": "User deleted successfully"}) except Exception as e: self.write_error(f"Failed to delete user: {str(e)}", 500) class UserProfileHandler(BaseHandler): """User profile handler.""" async def get(self): """Get current user profile.""" try: current_user = await self.get_current_user_async() if not current_user: self.write_error("Authentication required", 401) return # Get user profile data profile_data = { "id": current_user['user_id'], "email": current_user['email'], "username": f"user{current_user['user_id']}", "is_active": True, "profile": { "bio": "Software developer passionate about async programming", "avatar_url": "https://via.placeholder.com/150", "location": "San Francisco, CA", "website": "https://example.com" } } self.write_json({"profile": profile_data}) except Exception as e: self.write_error(f"Failed to get profile: {str(e)}", 500) async def put(self): """Update current user profile.""" try: current_user = await self.get_current_user_async() if not current_user: self.write_error("Authentication required", 401) return data = self.get_json_body() # Update profile data (simulate database operation) updated_profile = { "id": current_user['user_id'], "email": current_user['email'], "username": data.get('username', f"user{current_user['user_id']}"), "profile": { "bio": data.get('bio', ''), "avatar_url": data.get('avatar_url', ''), "location": data.get('location', ''), "website": data.get('website', '') }, "updated_at": "2024-01-01T00:00:00Z" } self.write_json({ "message": "Profile updated successfully", "profile": updated_profile }) except Exception as e: self.write_error(f"Failed to update profile: {str(e)}", 500)`, 'app/handlers/websocket_handlers.py': `""" WebSocket Handlers for Real-time Communication """ import json import tornado.websocket from typing import Set, Dict, Any import asyncio from datetime import datetime from app.core.redis_client import get_redis # Store active WebSocket connections active_connections: Dict[str, Set[tornado.websocket.WebSocketHandler]] = { 'chat': set(), 'notifications': set() } class ChatWebSocketHandler(tornado.websocket.WebSocketHandler): """WebSocket handler for chat functionality.""" def check_origin(self, origin): """Allow connections from any origin (configure for production).""" return True async def open(self): """Called when WebSocket connection is opened.""" self.user_id = None self.room_id = None # Get user from query parameters or authentication user_token = self.get_argument('token', None) room_id = self.get_argument('room', 'general') if user_token: # In a real app, you'd validate the token self.user_id = 1 # Simulated user ID self.room_id = room_id # Add to active connections active_connections['chat'].add(self) # Notify room about new user await self.broadcast_to_room({ 'type': 'user_joined', 'user_id': self.user_id, 'room_id': self.room_id, 'timestamp': datetime.utcnow().isoformat(), 'message': f'User {self.user_id} joined the room' }) print(f"šŸ’¬ User {self.user_id} connected to chat room {self.room_id}") else: self.close(1000, "Authentication required") async def on_message(self, message): """Handle incoming WebSocket messages.""" try: data = json.loads(message) message_type = data.get('type', 'chat') if message_type == 'chat': await self.handle_chat_message(data) elif message_type == 'typing': await self.handle_typing_indicator(data) elif message_type == 'ping': await self.send_message({'type': 'pong', 'timestamp': datetime.utcnow().isoformat()}) except json.JSONDecodeError: await self.send_message({ 'type': 'error', 'message': 'Invalid JSON message' }) except Exception as e: await self.send_message({ 'type': 'error', 'message': f'Message handling error: {str(e)}' }) async def handle_chat_message(self, data): """Handle chat messages.""" message_data = { 'type': 'chat', 'id': f"msg_{datetime.utcnow().timestamp()}", 'user_id': self.user_id, 'room_id': self.room_id, 'content': data.get('content', ''), 'timestamp': datetime.utcnow().isoformat() } # Store message in Redis (as example) redis_client = await get_redis() await redis_client.lpush( f"chat:room:{self.room_id}", json.dumps(message_data) ) await redis_client.ltrim(f"chat:room:{self.room_id}", 0, 99) # Keep last 100 messages # Broadcast to all users in the room await self.broadcast_to_room(message_data) async def handle_typing_indicator(self, data): """Handle typing indicators.""" typing_data = { 'type': 'typing', 'user_id': self.user_id, 'room_id': self.room_id, 'is_typing': data.get('is_typing', False), 'timestamp': datetime.utcnow().isoformat() } # Broadcast to other users in the room await self.broadcast_to_room(typing_data, exclude_self=True) async def broadcast_to_room(self, message_data, exclude_self=False): """Broadcast message to all users in the same room.""" for connection in active_connections['chat'].copy(): if (exclude_self and connection == self) or connection.room_id != self.room_id: continue try: await connection.send_message(message_data) except Exception: # Remove broken connections active_connections['chat'].discard(connection) async def send_message(self, data): """Send message to this WebSocket connection.""" try: self.write_message(json.dumps(data, default=str)) except tornado.websocket.WebSocketClosedError: pass def on_close(self): """Called when WebSocket connection is closed.""" if self in active_connections['chat']: active_connections['chat'].remove(self) if self.user_id and self.room_id: # Notify room about user leaving asyncio.create_task(self.broadcast_to_room({ 'type': 'user_left', 'user_id': self.user_id, 'room_id': self.room_id, 'timestamp': datetime.utcnow().isoformat(), 'message': f'User {self.user_id} left the room' })) print(f"šŸ’¬ User {self.user_id} disconnected from chat room {self.room_id}") class NotificationWebSocketHandler(tornado.websocket.WebSocketHandler): """WebSocket handler for real-time notifications.""" def check_origin(self, origin): """Allow connections from any origin (configure for production).""" return True async def open(self): """Called when WebSocket connection is opened.""" self.user_id = None # Get user from query parameters or authentication user_token = self.get_argument('token', None) if user_token: # In a real app, you'd validate the token self.user_id = 1 # Simulated user ID # Add to active connections active_connections['notifications'].add(self) # Send welcome notification await self.send_notification({ 'type': 'welcome', 'title': 'Connected', 'message': 'You are now connected to real-time notifications', 'priority': 'info' }) print(f"šŸ”” User {self.user_id} connected to notifications") else: self.close(1000, "Authentication required") async def on_message(self, message): """Handle incoming WebSocket messages.""" try: data = json.loads(message) message_type = data.get('type', 'ping') if message_type == 'ping': await self.send_notification({ 'type': 'pong', 'timestamp': datetime.utcnow().isoformat() }) elif message_type == 'subscribe': # Handle subscription to specific notification types await self.handle_subscription(data) except json.JSONDecodeError: await self.send_notification({ 'type': 'error', 'message': 'Invalid JSON message' }) async def handle_subscription(self, data): """Handle notification subscriptions.""" # In a real app, you'd store subscription preferences await self.send_notification({ 'type': 'subscription_updated', 'message': f"Subscribed to {data.get('topics', [])}", 'priority': 'info' }) async def send_notification(self, notification_data): """Send notification to this WebSocket connection.""" try: message = { 'id': f"notif_{datetime.utcnow().timestamp()}", 'user_id': self.user_id, 'timestamp': datetime.utcnow().isoformat(), **notification_data } self.write_message(json.dumps(message, default=str)) except tornado.websocket.WebSocketClosedError: pass def on_close(self): """Called when WebSocket connection is closed.""" if self in active_connections['notifications']: active_connections['notifications'].remove(self) print(f"šŸ”” User {self.user_id} disconnected from notifications") # Helper function to broadcast notifications to all connected users async def broadcast_notification(notification_data): """Broadcast notification to all connected users.""" for connection in active_connections['notifications'].copy(): try: await connection.send_notification(notification_data) except Exception: # Remove broken connections active_connections['notifications'].discard(connection)`, 'app/handlers/api_handlers.py': `""" API Handlers for Health Check and Status """ import tornado.web import psutil import time from datetime import datetime from app.handlers.base import BaseHandler from app.core.config import settings from app.core.redis_client import get_redis class HealthCheckHandler(BaseHandler): """Health check endpoint.""" async def get(self): """Return application health status.""" try: start_time = time.time() # Check Redis connectivity redis_status = "healthy" try: redis_client = await get_redis() await redis_client.ping() except Exception: redis_status = "unhealthy" # Check database connectivity # In a real app, you'd test actual database connection database_status = "healthy" # System metrics system_metrics = { "cpu_percent": psutil.cpu_percent(), "memory_percent": psutil.virtual_memory().percent, "disk_percent": psutil.disk_usage('/').percent } response_time = (time.time() - start_time) * 1000 health_data = { "status": "healthy" if redis_status == "healthy" and database_status == "healthy" else "unhealthy", "timestamp": datetime.utcnow().isoformat(), "response_time_ms": round(response_time, 2), "services": { "redis": redis_status, "database": database_status }, "system": system_metrics, "version": "1.0.0" } status_code = 200 if health_data["status"] == "healthy" else 503 self.write_json(health_data, status_code) except Exception as e: self.write_json({ "status": "unhealthy", "error": str(e), "timestamp": datetime.utcnow().isoformat() }, 503) class StatusHandler(BaseHandler): """Detailed application status endpoint.""" async def get(self): """Return detailed application status.""" try: # WebSocket connections count from app.handlers.websocket_handlers import active_connections websocket_stats = { "chat_connections": len(active_connections.get('chat', set())), "notification_connections": len(active_connections.get('notifications', set())), "total_connections": sum(len(conns) for conns in active_connections.values()) } # Redis stats redis_stats = {} try: redis_client = await get_redis() redis_info = await redis_client.info() redis_stats = { "connected_clients": redis_info.get('connected_clients', 0), "used_memory_human": redis_info.get('used_memory_human', '0B'), "uptime_in_seconds": redis_info.get('uptime_in_seconds', 0) } except Exception: redis_stats = {"error": "Unable to fetch Redis stats"} status_data = { "application": { "name": "Tornado Microservice", "version": "1.0.0", "environment": "development" if settings.DEBUG else "production", "debug_mode": settings.DEBUG }, "server": { "host": settings.HOST, "port": settings.PORT, "uptime_seconds": time.time() # In real app, track actual uptime }, "websockets": websocket_stats, "redis": redis_stats, "system": { "python_version": f"{psutil.PYTHON_VERSION[0]}.{psutil.PYTHON_VERSION[1]}.{psutil.PYTHON_VERSION[2]}", "cpu_count": psutil.cpu_count(), "memory_total_gb": round(psutil.virtual_memory().total / (1024**3), 2) }, "timestamp": datetime.utcnow().isoformat() } self.write_json(status_data) except Exception as e: self.write_error(f"Failed to get status: {str(e)}", 500)`, 'app/models/__init__.py': '', 'app/models/user.py': `""" User Model and Schema """ from marshmallow import Schema, fields, validate from typing import Dict, Any class UserSchema(Schema): """User validation schema.""" email = fields.Email(required=True) password = fields.Str(required=True, validate=validate.Length(min=8)) username = fields.Str(validate=validate.Length(min=3, max=50)) class Meta: unknown = 'EXCLUDE' class User: """User model (simplified for template).""" def __init__(self, email: str, username: str = None, password_hash: str = None): self.email = email self.username = username or email.split('@')[0] self.password_hash = password_hash self.is_active = True self.created_at = None self.updated_at = None def to_dict(self) -> Dict[str, Any]: """Convert user to dictionary.""" return { 'email': self.email, 'username': self.username, 'is_active': self.is_active, 'created_at': self.created_at, 'updated_at': self.updated_at } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'User': """Create user from dictionary.""" user = cls( email=data['email'], username=data.get('username'), password_hash=data.get('password_hash') ) user.is_active = data.get('is_active', True) user.created_at = data.get('created_at') user.updated_at = data.get('updated_at') return user`, 'Dockerfile': `FROM python:3.11-slim WORKDIR /app # Install system dependencies RUN apt-get update && apt-get install -y \\ gcc \\ postgresql-client \\ && rm -rf /var/lib/apt/lists/* # Copy requirements COPY requirements.txt requirements-dev.txt ./ # Install Python dependencies RUN pip install --no-cache-dir -r requirements.txt # Copy application code COPY . . # Create non-root user RUN useradd -m -u 1000 tornado && chown -R tornado:tornado /app USER tornado # Expose port EXPOSE 8000 # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\ CMD python -c "import httpx; httpx.get('http://localhost:8000/health')" # Run application CMD ["python", "main.py", "--port=8000"]`, 'docker-compose.yml': `version: '3.8' services: tornado-app: build: . ports: - "8000:8000" environment: - DEBUG=true - DATABASE_URL=postgresql://postgres:password@postgres:5432/tornado_app - REDIS_URL=redis://redis:6379/0 - SECRET_KEY=your-secret-key-change-in-production - JWT_SECRET_KEY=jwt-secret-key-change-in-production depends_on: - postgres - redis volumes: - .:/app command: python main.py --port=8000 --debug postgres: image: postgres:15-alpine environment: - POSTGRES_DB=tornado_app - POSTGRES_USER=postgres - POSTGRES_PASSWORD=password ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data redis: image: redis:7-alpine ports: - "6379:6379" command: redis-server --appendonly yes volumes: - redis_data:/data volumes: postgres_data: redis_data:`, '.env.example': `# Server Configuration HOST=localhost PORT=8000 DEBUG=true # Security SECRET_KEY=your-secret-key-change-in-production JWT_SECRET_KEY=jwt-secret-key-change-in-production JWT_ALGORITHM=HS256 JWT_EXPIRATION_DELTA=3600 # Database DATABASE_URL=postgresql://user:password@localhost:5432/tornado_app # Redis REDIS_URL=redis://localhost:6379/0 # CORS CORS_ORIGINS=http://localhost:3000,http://localhost:8080 # Logging LOG_LEVEL=INFO`, 'pytest.ini': `[tool:pytest] asyncio_mode = auto testpaths = tests python_files = test_*.py python_classes = Test* python_functions = test_* addopts = --verbose --tb=short --cov=app --cov-report=term-missing --cov-report=html:htmlcov markers = unit: Unit tests integration: Integration tests websocket: WebSocket tests`, 'tests/__init__.py': '', 'tests/conftest.py': `""" Pytest configuration and fixtures. """ import pytest import asyncio from tornado.testing import AsyncHTTPTestCase from tornado.web import Application from app.core.application import create_application @pytest.fixture(scope="session") def event_loop(): """Create an instance of the default event loop for the test session.""" loop = asyncio.get_event_loop_policy().new_event_loop() yield loop loop.close() @pytest.fixture async def app(): """Create test application.""" return create_application(debug=True) class TornadoTestCase(AsyncHTTPTestCase): """Base test case for Tornado applications.""" def get_app(self) -> Application: return create_application(debug=True)`, 'tests/test_auth.py': `""" Authentication tests. """ import json import pytest from tornado.testing import AsyncHTTPTestCase from tests.conftest import TornadoTestCase class TestAuth(TornadoTestCase): def test_register(self): """Test user registration.""" body = { "email": "test@example.com", "password": "testpassword123", "username": "testuser" } response = self.fetch( '/api/v1/auth/register', method='POST', body=json.dumps(body), headers={'Content-Type': 'application/json'} ) self.assertEqual(response.code, 201) data = json.loads(response.body) self.assertIn('access_token', data) self.assertEqual(data['user']['email'], 'test@example.com') def test_login(self): """Test user login.""" body = { "email": "test@example.com", "password": "testpassword123" } response = self.fetch( '/api/v1/auth/login', method='POST', body=json.dumps(body), headers={'Content-Type': 'application/json'} ) self.assertEqual(response.code, 200) data = json.loads(response.body) self.assertIn('access_token', data) self.assertEqual(data['user']['email'], 'test@example.com') def test_login_invalid_credentials(self): """Test login with invalid credentials.""" body = { "email": "invalid@example.com", "password": "wrongpassword" } response = self.fetch( '/api/v1/auth/login', method='POST', body=json.dumps(body), headers={'Content-Type': 'application/json'} ) # In this template, all logins succeed for simplicity # In a real app, this would return 401 self.assertEqual(response.code, 200)`, 'tests/test_websockets.py': `""" WebSocket tests. """ import json import pytest from tornado.testing import AsyncHTTPTestCase, gen_test from tornado.websocket import websocket_connect from tests.conftest import TornadoTestCase class TestWebSockets(TornadoTestCase): @gen_test async def test_chat_websocket_connection(self): """Test chat WebSocket connection.""" ws_url = f"ws://localhost:{self.get_http_port()}/ws/chat?token=test_token&room=test_room" try: ws = await websocket_connect(ws_url) # Send a test message test_message = { "type": "chat", "content": "Hello, WebSocket!" } ws.write_message(json.dumps(test_message)) # Read response response = await ws.read_message() self.assertIsNotNone(response) ws.close() except Exception as e: self.fail(f"WebSocket connection failed: {e}") @gen_test async def test_notification_websocket_connection(self): """Test notification WebSocket connection.""" ws_url = f"ws://localhost:{self.get_http_port()}/ws/notifications?token=test_token" try: ws = await websocket_connect(ws_url) # Send a ping ping_message = {"type": "ping"} ws.write_message(json.dumps(ping_message)) # Read response response = await ws.read_message() self.assertIsNotNone(response) ws.close() except Exception as e: self.fail(f"WebSocket connection failed: {e}")`, 'tests/test_api.py': `""" API endpoint tests. """ import json import pytest from tornado.testing import AsyncHTTPTestCase from tests.conftest import TornadoTestCase class TestAPI(TornadoTestCase): def test_health_check(self): """Test health check endpoint.""" response = self.fetch('/health') self.assertEqual(response.code, 200) data = json.loads(response.body) self.assertIn('status', data) self.assertIn('timestamp', data) self.assertIn('services', data) def test_status_endpoint(self): """Test status endpoint.""" response = self.fetch('/api/v1/status') self.assertEqual(response.code, 200) data = json.loads(response.body) self.assertIn('application', data) self.assertIn('server', data) self.assertIn('websockets', data) def test_cors_headers(self): """Test CORS headers are set correctly.""" response = self.fetch('/health') headers = response.headers self.assertEqual(headers.get('Access-Control-Allow-Origin'), '*') self.assertIn('Content-Type', headers.get('Access-Control-Allow-Headers', ''))`, '.gitignore': `# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg # PyInstaller *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .