diff --git a/Backend/.env.example b/Backend/.env.example index 086a03ef..5aa5b0cd 100644 --- a/Backend/.env.example +++ b/Backend/.env.example @@ -1,34 +1,94 @@ +# ============================================ # Hotel Booking API - Environment Variables +# ============================================ # Copy this file to .env and fill in your actual values +# All variables are optional and have sensible defaults +# See src/config/settings.py for default values # ============================================ -# Email/SMTP Configuration +# Application Configuration # ============================================ -# SMTP Server Settings -SMTP_HOST=smtp.gmail.com -SMTP_PORT=587 -SMTP_USER=your-email@gmail.com -SMTP_PASSWORD=your-app-specific-password - -# Email Sender Information -SMTP_FROM_EMAIL=noreply@yourdomain.com -SMTP_FROM_NAME=Hotel Booking - -# Alternative: Legacy environment variable names (for backward compatibility) -# MAIL_HOST=smtp.gmail.com -# MAIL_PORT=587 -# MAIL_USER=your-email@gmail.com -# MAIL_PASS=your-app-specific-password -# MAIL_FROM=noreply@yourdomain.com -# MAIL_SECURE=false +ENVIRONMENT=development +# Options: development, staging, production +DEBUG=true +# Set to false in production +HOST=0.0.0.0 +PORT=8000 +API_V1_PREFIX=/api/v1 # ============================================ -# Other Required Variables +# Database Configuration # ============================================ -CLIENT_URL=http://localhost:5173 -DB_USER=root -DB_PASS=your_database_password -DB_NAME=hotel_db DB_HOST=localhost DB_PORT=3306 -JWT_SECRET=your-super-secret-jwt-key-change-in-production +DB_USER=root +DB_PASS= +# Leave empty if using MySQL without password +DB_NAME=hotel_db + +# ============================================ +# JWT Authentication +# ============================================ +# JWT_SECRET will be auto-generated on startup if not set +# The generated secret will be saved here automatically +JWT_ALGORITHM=HS256 +JWT_ACCESS_TOKEN_EXPIRE_MINUTES=30 +JWT_REFRESH_TOKEN_EXPIRE_DAYS=7 + +# ============================================ +# CORS & Client Configuration +# ============================================ +CLIENT_URL=http://localhost:5173 +# Frontend application URL +CORS_ORIGINS=["http://localhost:5173","http://localhost:3000","http://127.0.0.1:5173"] +# JSON array of allowed origins (only used in production) + +# ============================================ +# Email & Payment Settings +# ============================================ +# NOTE: Email (SMTP) and Payment Gateway (Stripe, PayPal) settings +# are configured in the Admin Dashboard, not via environment variables. +# Log in as admin and go to Settings page to configure these. + +# ============================================ +# Redis Configuration (Optional) +# ============================================ +REDIS_ENABLED=false +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_DB=0 +REDIS_PASSWORD= +# Leave empty if Redis has no password + +# ============================================ +# File Upload Configuration +# ============================================ +UPLOAD_DIR=uploads +MAX_UPLOAD_SIZE=5242880 +# Max upload size in bytes (5MB default) +ALLOWED_EXTENSIONS=["jpg","jpeg","png","gif","webp"] +# JSON array + +# ============================================ +# Rate Limiting +# ============================================ +RATE_LIMIT_ENABLED=true +RATE_LIMIT_PER_MINUTE=60 + +# ============================================ +# Logging Configuration +# ============================================ +LOG_LEVEL=INFO +# Options: DEBUG, INFO, WARNING, ERROR, CRITICAL +LOG_FILE=logs/app.log +LOG_MAX_BYTES=10485760 +# Max log file size in bytes (10MB) +LOG_BACKUP_COUNT=5 + +# ============================================ +# Server Configuration +# ============================================ +REQUEST_TIMEOUT=30 +# Request timeout in seconds (0 to disable) +HEALTH_CHECK_INTERVAL=30 +# Health check interval in seconds diff --git a/Backend/reset_user_passwords.py b/Backend/reset_user_passwords.py deleted file mode 100644 index aeabe583..00000000 --- a/Backend/reset_user_passwords.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import bcrypt - -sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src')) - -from sqlalchemy.orm import Session -from src.config.database import SessionLocal -from src.models.user import User -from src.config.logging_config import setup_logging - -logger = setup_logging() - - -def hash_password(password: str) -> str: - password_bytes = password.encode('utf-8') - salt = bcrypt.gensalt() - hashed = bcrypt.hashpw(password_bytes, salt) - return hashed.decode('utf-8') - - -def reset_password(db: Session, email: str, new_password: str) -> bool: - user = db.query(User).filter(User.email == email).first() - - if not user: - print(f"❌ User with email '{email}' not found") - return False - - hashed_password = hash_password(new_password) - - user.password = hashed_password - db.commit() - db.refresh(user) - - print(f"✅ Password reset for {email}") - print(f" New password: {new_password}") - print(f" Hash length: {len(user.password)} characters") - print() - - return True - - -def main(): - db = SessionLocal() - - try: - print("="*80) - print("RESETTING TEST USER PASSWORDS") - print("="*80) - print() - - - except Exception as e: - logger.error(f"Error: {e}", exc_info=True) - print(f"\n❌ Error: {e}") - db.rollback() - finally: - db.close() - - -if __name__ == "__main__": - main() diff --git a/Backend/add_accountant_role.py b/Backend/seeds_data/add_accountant_role.py similarity index 100% rename from Backend/add_accountant_role.py rename to Backend/seeds_data/add_accountant_role.py diff --git a/Backend/seed_about_page.py b/Backend/seeds_data/seed_about_page.py similarity index 100% rename from Backend/seed_about_page.py rename to Backend/seeds_data/seed_about_page.py diff --git a/Backend/seed_banners_company.py b/Backend/seeds_data/seed_banners_company.py similarity index 100% rename from Backend/seed_banners_company.py rename to Backend/seeds_data/seed_banners_company.py diff --git a/Backend/seed_homepage_footer.py b/Backend/seeds_data/seed_homepage_footer.py similarity index 100% rename from Backend/seed_homepage_footer.py rename to Backend/seeds_data/seed_homepage_footer.py diff --git a/Backend/seed_initial_data.py b/Backend/seeds_data/seed_initial_data.py similarity index 100% rename from Backend/seed_initial_data.py rename to Backend/seeds_data/seed_initial_data.py diff --git a/Backend/seed_luxury_content.py b/Backend/seeds_data/seed_luxury_content.py similarity index 100% rename from Backend/seed_luxury_content.py rename to Backend/seeds_data/seed_luxury_content.py diff --git a/Backend/seed_policy_pages.py b/Backend/seeds_data/seed_policy_pages.py similarity index 100% rename from Backend/seed_policy_pages.py rename to Backend/seeds_data/seed_policy_pages.py diff --git a/Backend/seed_rooms.py b/Backend/seeds_data/seed_rooms.py similarity index 100% rename from Backend/seed_rooms.py rename to Backend/seeds_data/seed_rooms.py diff --git a/Backend/seed_users.py b/Backend/seeds_data/seed_users.py similarity index 100% rename from Backend/seed_users.py rename to Backend/seeds_data/seed_users.py diff --git a/Backend/src/__pycache__/main.cpython-312.pyc b/Backend/src/__pycache__/main.cpython-312.pyc index 437e175d..bdb57e06 100644 Binary files a/Backend/src/__pycache__/main.cpython-312.pyc and b/Backend/src/__pycache__/main.cpython-312.pyc differ diff --git a/Backend/src/main.py b/Backend/src/main.py index 18a043f5..6ca18dbb 100644 --- a/Backend/src/main.py +++ b/Backend/src/main.py @@ -11,6 +11,9 @@ from slowapi.errors import RateLimitExceeded from pathlib import Path from datetime import datetime import sys +import secrets +import os +import re from .config.settings import settings from .config.logging_config import setup_logging, get_logger from .config.database import engine, Base, get_db @@ -151,8 +154,50 @@ app.include_router(page_content_routes.router, prefix='/api') app.include_router(page_content_routes.router, prefix=settings.API_V1_PREFIX) logger.info('All routes registered successfully') +def ensure_jwt_secret(): + """Generate and save JWT secret if it's using the default value.""" + default_secret = 'dev-secret-key-change-in-production-12345' + current_secret = settings.JWT_SECRET + + if not current_secret or current_secret == default_secret: + new_secret = secrets.token_urlsafe(64) + + os.environ['JWT_SECRET'] = new_secret + + env_file = Path(__file__).parent.parent / '.env' + if env_file.exists(): + try: + env_content = env_file.read_text(encoding='utf-8') + + jwt_pattern = re.compile(r'^JWT_SECRET=.*$', re.MULTILINE) + + if jwt_pattern.search(env_content): + env_content = jwt_pattern.sub(f'JWT_SECRET={new_secret}', env_content) + else: + jwt_section_pattern = re.compile(r'(# =+.*JWT.*=+.*\n)', re.IGNORECASE | re.MULTILINE) + match = jwt_section_pattern.search(env_content) + if match: + insert_pos = match.end() + env_content = env_content[:insert_pos] + f'JWT_SECRET={new_secret}\n' + env_content[insert_pos:] + else: + env_content += f'\nJWT_SECRET={new_secret}\n' + + env_file.write_text(env_content, encoding='utf-8') + logger.info('✓ JWT secret generated and saved to .env file') + except Exception as e: + logger.warning(f'Could not update .env file: {e}') + logger.info(f'Generated JWT secret (add to .env manually): JWT_SECRET={new_secret}') + else: + logger.info(f'Generated JWT secret (add to .env file): JWT_SECRET={new_secret}') + + logger.info('✓ Secure JWT secret generated automatically') + else: + logger.info('✓ JWT secret is configured') + @app.on_event('startup') async def startup_event(): + ensure_jwt_secret() + logger.info(f'{settings.APP_NAME} started successfully') logger.info(f'Environment: {settings.ENVIRONMENT}') logger.info(f'Debug mode: {settings.DEBUG}')