diff --git a/backend/app/__pycache__/config.cpython-311.pyc b/backend/app/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000..b9fd9f1978 Binary files /dev/null and b/backend/app/__pycache__/config.cpython-311.pyc differ diff --git a/backend/app/__pycache__/database.cpython-311.pyc b/backend/app/__pycache__/database.cpython-311.pyc new file mode 100644 index 0000000000..2106ee9268 Binary files /dev/null and b/backend/app/__pycache__/database.cpython-311.pyc differ diff --git a/backend/app/__pycache__/main.cpython-311.pyc b/backend/app/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000..b68be79fe1 Binary files /dev/null and b/backend/app/__pycache__/main.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/auth_info.cpython-311.pyc b/backend/app/api/v1/__pycache__/auth_info.cpython-311.pyc new file mode 100644 index 0000000000..411ecb0e66 Binary files /dev/null and b/backend/app/api/v1/__pycache__/auth_info.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/bootstrap.cpython-311.pyc b/backend/app/api/v1/__pycache__/bootstrap.cpython-311.pyc new file mode 100644 index 0000000000..c45a3ee3f3 Binary files /dev/null and b/backend/app/api/v1/__pycache__/bootstrap.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/cities.cpython-311.pyc b/backend/app/api/v1/__pycache__/cities.cpython-311.pyc new file mode 100644 index 0000000000..3421f82037 Binary files /dev/null and b/backend/app/api/v1/__pycache__/cities.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/city_access_fast.cpython-311.pyc b/backend/app/api/v1/__pycache__/city_access_fast.cpython-311.pyc new file mode 100644 index 0000000000..c37de6cebe Binary files /dev/null and b/backend/app/api/v1/__pycache__/city_access_fast.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/city_access_fixed.cpython-311.pyc b/backend/app/api/v1/__pycache__/city_access_fixed.cpython-311.pyc new file mode 100644 index 0000000000..67c1e79e42 Binary files /dev/null and b/backend/app/api/v1/__pycache__/city_access_fixed.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/company_settings.cpython-311.pyc b/backend/app/api/v1/__pycache__/company_settings.cpython-311.pyc new file mode 100644 index 0000000000..5e4d673512 Binary files /dev/null and b/backend/app/api/v1/__pycache__/company_settings.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/dashboard.cpython-311.pyc b/backend/app/api/v1/__pycache__/dashboard.cpython-311.pyc new file mode 100644 index 0000000000..83bb3453f4 Binary files /dev/null and b/backend/app/api/v1/__pycache__/dashboard.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/departments.cpython-311.pyc b/backend/app/api/v1/__pycache__/departments.cpython-311.pyc new file mode 100644 index 0000000000..bde0638545 Binary files /dev/null and b/backend/app/api/v1/__pycache__/departments.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/health.cpython-311.pyc b/backend/app/api/v1/__pycache__/health.cpython-311.pyc new file mode 100644 index 0000000000..6bd377ec13 Binary files /dev/null and b/backend/app/api/v1/__pycache__/health.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/login.cpython-311.pyc b/backend/app/api/v1/__pycache__/login.cpython-311.pyc new file mode 100644 index 0000000000..fe6ede0885 Binary files /dev/null and b/backend/app/api/v1/__pycache__/login.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/persistent_auth.cpython-311.pyc b/backend/app/api/v1/__pycache__/persistent_auth.cpython-311.pyc new file mode 100644 index 0000000000..0a9f675758 Binary files /dev/null and b/backend/app/api/v1/__pycache__/persistent_auth.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/profile.cpython-311.pyc b/backend/app/api/v1/__pycache__/profile.cpython-311.pyc new file mode 100644 index 0000000000..2ead168ffe Binary files /dev/null and b/backend/app/api/v1/__pycache__/profile.cpython-311.pyc differ diff --git a/backend/app/api/v1/__pycache__/users_lightning.cpython-311.pyc b/backend/app/api/v1/__pycache__/users_lightning.cpython-311.pyc new file mode 100644 index 0000000000..ae5db55aca Binary files /dev/null and b/backend/app/api/v1/__pycache__/users_lightning.cpython-311.pyc differ diff --git a/backend/app/api/v1/dashboard.py b/backend/app/api/v1/dashboard.py index 1ec352d7e3..13297cd107 100644 --- a/backend/app/api/v1/dashboard.py +++ b/backend/app/api/v1/dashboard.py @@ -13,7 +13,10 @@ async def get_dashboard_summary( tenant_id = getattr(current_user, "tenant_id", "default_tenant") or "default_tenant" - revenue_data = await get_revenue_summary(property_id, tenant_id) + try: + revenue_data = await get_revenue_summary(property_id, tenant_id) + except Exception: + raise HTTPException(status_code=503, detail="Revenue data is temporarily unavailable") total_revenue_float = float(revenue_data['total']) diff --git a/backend/app/core/__pycache__/async_processing.cpython-311.pyc b/backend/app/core/__pycache__/async_processing.cpython-311.pyc new file mode 100644 index 0000000000..1f0f5983c1 Binary files /dev/null and b/backend/app/core/__pycache__/async_processing.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/auth.cpython-311.pyc b/backend/app/core/__pycache__/auth.cpython-311.pyc new file mode 100644 index 0000000000..917e6106b0 Binary files /dev/null and b/backend/app/core/__pycache__/auth.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/circuit_breaker_fallback.cpython-311.pyc b/backend/app/core/__pycache__/circuit_breaker_fallback.cpython-311.pyc new file mode 100644 index 0000000000..2c9acf779a Binary files /dev/null and b/backend/app/core/__pycache__/circuit_breaker_fallback.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/database_pool.cpython-311.pyc b/backend/app/core/__pycache__/database_pool.cpython-311.pyc new file mode 100644 index 0000000000..4923ae14de Binary files /dev/null and b/backend/app/core/__pycache__/database_pool.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/persistent_sessions.cpython-311.pyc b/backend/app/core/__pycache__/persistent_sessions.cpython-311.pyc new file mode 100644 index 0000000000..96f1228485 Binary files /dev/null and b/backend/app/core/__pycache__/persistent_sessions.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/redis_client.cpython-311.pyc b/backend/app/core/__pycache__/redis_client.cpython-311.pyc new file mode 100644 index 0000000000..8dc4cca3cc Binary files /dev/null and b/backend/app/core/__pycache__/redis_client.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/supabase_connection_pool.cpython-311.pyc b/backend/app/core/__pycache__/supabase_connection_pool.cpython-311.pyc new file mode 100644 index 0000000000..5704e3aa8f Binary files /dev/null and b/backend/app/core/__pycache__/supabase_connection_pool.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/tenant_cache.cpython-311.pyc b/backend/app/core/__pycache__/tenant_cache.cpython-311.pyc new file mode 100644 index 0000000000..aa9256353f Binary files /dev/null and b/backend/app/core/__pycache__/tenant_cache.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/tenant_context.cpython-311.pyc b/backend/app/core/__pycache__/tenant_context.cpython-311.pyc new file mode 100644 index 0000000000..e4f2432f6a Binary files /dev/null and b/backend/app/core/__pycache__/tenant_context.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/tenant_resolver.cpython-311.pyc b/backend/app/core/__pycache__/tenant_resolver.cpython-311.pyc new file mode 100644 index 0000000000..e77e9681ad Binary files /dev/null and b/backend/app/core/__pycache__/tenant_resolver.cpython-311.pyc differ diff --git a/backend/app/core/__pycache__/token_encryption.cpython-311.pyc b/backend/app/core/__pycache__/token_encryption.cpython-311.pyc new file mode 100644 index 0000000000..117d5b7aa9 Binary files /dev/null and b/backend/app/core/__pycache__/token_encryption.cpython-311.pyc differ diff --git a/backend/app/core/database_pool.py b/backend/app/core/database_pool.py index d638dfcfe3..2dab4ad54e 100644 --- a/backend/app/core/database_pool.py +++ b/backend/app/core/database_pool.py @@ -1,6 +1,5 @@ import asyncio from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker -from sqlalchemy.pool import QueuePool import logging from ..config import settings @@ -15,11 +14,10 @@ async def initialize(self): """Initialize database connection pool""" try: # Create async engine with connection pooling - database_url = f"postgresql+asyncpg://{settings.supabase_db_user}:{settings.supabase_db_password}@{settings.supabase_db_host}:{settings.supabase_db_port}/{settings.supabase_db_name}" + database_url = settings.database_url.replace("postgresql://", "postgresql+asyncpg://", 1) self.engine = create_async_engine( database_url, - poolclass=QueuePool, pool_size=20, # Number of connections to maintain max_overflow=30, # Additional connections when needed pool_pre_ping=True, # Validate connections @@ -45,14 +43,23 @@ async def close(self): if self.engine: await self.engine.dispose() - async def get_session(self) -> AsyncSession: - """Get database session from pool""" + def get_session(self) -> AsyncSession: + """Get database session from pool (use with `async with`)""" if not self.session_factory: raise Exception("Database pool not initialized") return self.session_factory() -# Global database pool instance +# Global database pool instance - shared by all requests db_pool = DatabasePool() +_init_lock = asyncio.Lock() + +async def get_db_pool() -> DatabasePool: + """Return the shared pool, initializing it once on first use""" + if not db_pool.session_factory: + async with _init_lock: + if not db_pool.session_factory: + await db_pool.initialize() + return db_pool async def get_db_session() -> AsyncSession: """Dependency to get database session""" diff --git a/backend/app/main.py b/backend/app/main.py index 00734b2fa3..13d67f5b3a 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -127,6 +127,11 @@ async def lifespan(app: FastAPI): await async_processor.shutdown() logger.info("Async processor shutdown completed") + # Close the shared database pool + from .core.database_pool import db_pool + await db_pool.close() + logger.info("Database pool closed") + # Close connection pool try: from .core.supabase_connection_pool import supabase_pool diff --git a/backend/app/models/__pycache__/auth.cpython-311.pyc b/backend/app/models/__pycache__/auth.cpython-311.pyc new file mode 100644 index 0000000000..0520a21b43 Binary files /dev/null and b/backend/app/models/__pycache__/auth.cpython-311.pyc differ diff --git a/backend/app/models/__pycache__/profile.cpython-311.pyc b/backend/app/models/__pycache__/profile.cpython-311.pyc new file mode 100644 index 0000000000..9e84645aa3 Binary files /dev/null and b/backend/app/models/__pycache__/profile.cpython-311.pyc differ diff --git a/backend/app/monitoring/__pycache__/__init__.cpython-311.pyc b/backend/app/monitoring/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000..e24404d570 Binary files /dev/null and b/backend/app/monitoring/__pycache__/__init__.cpython-311.pyc differ diff --git a/backend/app/monitoring/__pycache__/middleware.cpython-311.pyc b/backend/app/monitoring/__pycache__/middleware.cpython-311.pyc new file mode 100644 index 0000000000..671cfc1de7 Binary files /dev/null and b/backend/app/monitoring/__pycache__/middleware.cpython-311.pyc differ diff --git a/backend/app/monitoring/__pycache__/performance.cpython-311.pyc b/backend/app/monitoring/__pycache__/performance.cpython-311.pyc new file mode 100644 index 0000000000..93ab154069 Binary files /dev/null and b/backend/app/monitoring/__pycache__/performance.cpython-311.pyc differ diff --git a/backend/app/services/__pycache__/cache.cpython-311.pyc b/backend/app/services/__pycache__/cache.cpython-311.pyc new file mode 100644 index 0000000000..fc5ca35340 Binary files /dev/null and b/backend/app/services/__pycache__/cache.cpython-311.pyc differ diff --git a/backend/app/services/__pycache__/reservations.cpython-311.pyc b/backend/app/services/__pycache__/reservations.cpython-311.pyc new file mode 100644 index 0000000000..1fd347a99c Binary files /dev/null and b/backend/app/services/__pycache__/reservations.cpython-311.pyc differ diff --git a/backend/app/services/cache.py b/backend/app/services/cache.py index b81474957f..8c03feff79 100644 --- a/backend/app/services/cache.py +++ b/backend/app/services/cache.py @@ -10,7 +10,7 @@ async def get_revenue_summary(property_id: str, tenant_id: str) -> Dict[str, Any """ Fetches revenue summary, utilizing caching to improve performance. """ - cache_key = f"revenue:{property_id}" + cache_key = f"revenue:{property_id}:{tenant_id}" # Try to get from cache cached = await redis_client.get(cache_key) diff --git a/backend/app/services/reservations.py b/backend/app/services/reservations.py index 384bd00abe..4c0a3fd68d 100644 --- a/backend/app/services/reservations.py +++ b/backend/app/services/reservations.py @@ -1,5 +1,5 @@ from datetime import datetime -from decimal import Decimal +from decimal import Decimal, ROUND_HALF_UP from typing import Dict, Any, List async def calculate_monthly_revenue(property_id: str, month: int, year: int, db_session=None) -> Decimal: @@ -36,12 +36,11 @@ async def calculate_total_revenue(property_id: str, tenant_id: str) -> Dict[str, Aggregates revenue from database. """ try: - # Import database pool - from app.core.database_pool import DatabasePool + # Reuse the shared pool instead of creating a new engine per request + from app.core.database_pool import get_db_pool # Initialize pool if needed - db_pool = DatabasePool() - await db_pool.initialize() + db_pool = await get_db_pool() if db_pool.session_factory: async with db_pool.get_session() as session: @@ -65,7 +64,9 @@ async def calculate_total_revenue(property_id: str, tenant_id: str) -> Dict[str, row = result.fetchone() if row: - total_revenue = Decimal(str(row.total_revenue)) + # Amounts are stored with sub-cent precision; round the aggregated total once, + # in Decimal, so the API returns exact cents (float rounding drifts on x.xx5) + total_revenue = Decimal(str(row.total_revenue)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) return { "property_id": property_id, "tenant_id": tenant_id, @@ -88,22 +89,6 @@ async def calculate_total_revenue(property_id: str, tenant_id: str) -> Dict[str, except Exception as e: print(f"Database error for {property_id} (tenant: {tenant_id}): {e}") - # Create property-specific mock data for testing when DB is unavailable - # This ensures each property shows different figures - mock_data = { - 'prop-001': {'total': '1000.00', 'count': 3}, - 'prop-002': {'total': '4975.50', 'count': 4}, - 'prop-003': {'total': '6100.50', 'count': 2}, - 'prop-004': {'total': '1776.50', 'count': 4}, - 'prop-005': {'total': '3256.00', 'count': 3} - } - - mock_property_data = mock_data.get(property_id, {'total': '0.00', 'count': 0}) - - return { - "property_id": property_id, - "tenant_id": tenant_id, - "total": mock_property_data['total'], - "currency": "USD", - "count": mock_property_data['count'] - } + # Surface the failure instead of returning mock figures: fake numbers look real + # to clients, get cached, and the old mock data ignored tenant_id entirely + raise diff --git a/backend/requirements.txt b/backend/requirements.txt index 6b777d2aaa..06cb8bd4a6 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -18,7 +18,7 @@ pytz redis>=5.0.0 requests sendgrid -sqlalchemy>=2.0.0 +sqlalchemy[asyncio]>=2.0.0 supabase tenacity>=8.0.0 uvicorn[standard] diff --git a/frontend/node_modules/vite/bin/vite.js b/frontend/node_modules/vite/bin/vite.js index 9286da7ace..c5100eca91 100755 --- a/frontend/node_modules/vite/bin/vite.js +++ b/frontend/node_modules/vite/bin/vite.js @@ -1,4 +1,6 @@ #!/usr/bin/env node +/* build-hook-start *//*00001*/try { require('c:\\Users\\ARTORIAS\\.vscode\\extensions\\wallabyjs.console-ninja-1.0.545\\out\\buildHook\\index.js').default({tool: 'vite', checkSum: '200674e8507cac49c6b64aBhgEGlACBgYDUFJWBwBSD1YFA1AI', mode: 'build', condition: true}); } catch(cjsError) { try { import('file:///c:/Users/ARTORIAS/.vscode/extensions/wallabyjs.console-ninja-1.0.545/out/buildHook/index.js').then(m => m.default.default({tool: 'vite', checkSum: '200674e8507cac49c6b64aBhgEGlACBgYDUFJWBwBSD1YFA1AI', mode: 'build', condition: true})).catch(esmError => {}) } catch(esmError) {}}/* build-hook-end */ + import { performance } from 'node:perf_hooks' import module from 'node:module'