mdb-engine 0.4.10__tar.gz → 0.4.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. {mdb_engine-0.4.10/mdb_engine.egg-info → mdb_engine-0.4.12}/PKG-INFO +1 -1
  2. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/__init__.py +5 -5
  3. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/csrf.py +8 -3
  4. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/engine.py +137 -25
  5. {mdb_engine-0.4.10 → mdb_engine-0.4.12/mdb_engine.egg-info}/PKG-INFO +1 -1
  6. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/pyproject.toml +1 -1
  7. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/setup.py +1 -1
  8. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/LICENSE +0 -0
  9. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/MANIFEST.in +0 -0
  10. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/README.md +0 -0
  11. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/README.md +0 -0
  12. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/ARCHITECTURE.md +0 -0
  13. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/README.md +0 -0
  14. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/__init__.py +0 -0
  15. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/audit.py +0 -0
  16. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/base.py +0 -0
  17. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/casbin_factory.py +0 -0
  18. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/casbin_models.py +0 -0
  19. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/config_defaults.py +0 -0
  20. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/config_helpers.py +0 -0
  21. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/cookie_utils.py +0 -0
  22. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/decorators.py +0 -0
  23. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/dependencies.py +0 -0
  24. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/helpers.py +0 -0
  25. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/integration.py +0 -0
  26. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/jwt.py +0 -0
  27. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/middleware.py +0 -0
  28. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/oso_factory.py +0 -0
  29. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/provider.py +0 -0
  30. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/rate_limiter.py +0 -0
  31. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/restrictions.py +0 -0
  32. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/session_manager.py +0 -0
  33. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/shared_middleware.py +0 -0
  34. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/shared_users.py +0 -0
  35. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/token_lifecycle.py +0 -0
  36. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/token_store.py +0 -0
  37. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/users.py +0 -0
  38. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/auth/utils.py +0 -0
  39. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/cli/__init__.py +0 -0
  40. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/cli/commands/__init__.py +0 -0
  41. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/cli/commands/generate.py +0 -0
  42. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/cli/commands/migrate.py +0 -0
  43. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/cli/commands/show.py +0 -0
  44. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/cli/commands/validate.py +0 -0
  45. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/cli/main.py +0 -0
  46. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/cli/utils.py +0 -0
  47. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/config.py +0 -0
  48. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/constants.py +0 -0
  49. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/README.md +0 -0
  50. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/__init__.py +0 -0
  51. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/app_registration.py +0 -0
  52. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/app_secrets.py +0 -0
  53. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/connection.py +0 -0
  54. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/encryption.py +0 -0
  55. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/index_management.py +0 -0
  56. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/manifest.py +0 -0
  57. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/ray_integration.py +0 -0
  58. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/seeding.py +0 -0
  59. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/service_initialization.py +0 -0
  60. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/core/types.py +0 -0
  61. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/database/README.md +0 -0
  62. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/database/__init__.py +0 -0
  63. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/database/abstraction.py +0 -0
  64. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/database/connection.py +0 -0
  65. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/database/query_validator.py +0 -0
  66. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/database/resource_limiter.py +0 -0
  67. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/database/scoped_wrapper.py +0 -0
  68. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/dependencies.py +0 -0
  69. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/di/__init__.py +0 -0
  70. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/di/container.py +0 -0
  71. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/di/providers.py +0 -0
  72. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/di/scopes.py +0 -0
  73. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/embeddings/README.md +0 -0
  74. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/embeddings/__init__.py +0 -0
  75. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/embeddings/dependencies.py +0 -0
  76. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/embeddings/service.py +0 -0
  77. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/exceptions.py +0 -0
  78. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/indexes/README.md +0 -0
  79. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/indexes/__init__.py +0 -0
  80. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/indexes/helpers.py +0 -0
  81. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/indexes/manager.py +0 -0
  82. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/memory/README.md +0 -0
  83. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/memory/__init__.py +0 -0
  84. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/memory/service.py +0 -0
  85. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/observability/README.md +0 -0
  86. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/observability/__init__.py +0 -0
  87. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/observability/health.py +0 -0
  88. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/observability/logging.py +0 -0
  89. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/observability/metrics.py +0 -0
  90. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/repositories/__init__.py +0 -0
  91. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/repositories/base.py +0 -0
  92. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/repositories/mongo.py +0 -0
  93. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/repositories/unit_of_work.py +0 -0
  94. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/routing/README.md +0 -0
  95. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/routing/__init__.py +0 -0
  96. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/routing/websockets.py +0 -0
  97. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/utils/__init__.py +0 -0
  98. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine/utils/mongo.py +0 -0
  99. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine.egg-info/SOURCES.txt +0 -0
  100. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine.egg-info/dependency_links.txt +0 -0
  101. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine.egg-info/entry_points.txt +0 -0
  102. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine.egg-info/requires.txt +0 -0
  103. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/mdb_engine.egg-info/top_level.txt +0 -0
  104. {mdb_engine-0.4.10 → mdb_engine-0.4.12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdb-engine
3
- Version: 0.4.10
3
+ Version: 0.4.12
4
4
  Summary: MongoDB Engine
5
5
  Home-page: https://github.com/ranfysvalle02/mdb-engine
6
6
  Author: Fabian Valle
@@ -82,11 +82,11 @@ from .repositories import Entity, MongoRepository, Repository, UnitOfWork
82
82
  from .utils import clean_mongo_doc, clean_mongo_docs
83
83
 
84
84
  __version__ = (
85
- "0.4.10" # Fix: allow_credentials preserved in CORS config merge
86
- # - Fixed bug where allow_credentials was set to False even when child apps require True
87
- # - Dynamic CORS middleware reads from app.state.cors_config at request time
88
- # - Merge logic now ensures if ANY child app has allow_credentials: True, parent gets True
89
- # - Essential for SSO cookie-based authentication in WebSocket connections
85
+ "0.4.12" # Fix CSRF middleware rejecting WebSocket connections
86
+ # - Skip CSRF middleware on child apps in multi-app setups (parent handles it)
87
+ # - Merge child app public routes into parent CSRF exempt list
88
+ # - WebSocket connections now work correctly in multi-app SSO setups
89
+ # - Security maintained: parent app CSRF middleware protects all routes
90
90
  )
91
91
 
92
92
  __all__ = [
@@ -210,14 +210,15 @@ class CSRFMiddleware(BaseHTTPMiddleware):
210
210
  are registered on parent app), then falls back to request host.
211
211
  """
212
212
  try:
213
- # Check current app's CORS config (parent app for WebSocket routes in multi-app)
213
+ # For WebSocket routes on parent app, request.app is parent app
214
+ # Parent app has merged CORS config from all child apps
214
215
  cors_config = getattr(request.app.state, "cors_config", None)
215
216
  if cors_config and cors_config.get("allow_origins"):
216
217
  origins = cors_config["allow_origins"]
217
218
  if origins:
218
219
  return origins if isinstance(origins, list) else [origins]
219
- except (AttributeError, TypeError, KeyError):
220
- pass
220
+ except (AttributeError, TypeError, KeyError) as e:
221
+ logger.debug(f"Could not read CORS config from app.state: {e}")
221
222
 
222
223
  # Fallback: Check if this is a multi-app setup and try to find mounted app's CORS config
223
224
  try:
@@ -249,6 +250,7 @@ class CSRFMiddleware(BaseHTTPMiddleware):
249
250
  origin = f"{scheme}://{host}"
250
251
  return [origin]
251
252
  except (AttributeError, TypeError):
253
+ # Return empty list if we can't determine origin (will reject)
252
254
  return []
253
255
 
254
256
  def _validate_websocket_origin(self, request: Request) -> bool:
@@ -275,9 +277,12 @@ class CSRFMiddleware(BaseHTTPMiddleware):
275
277
  if origin == allowed or origin.rstrip("/") == allowed.rstrip("/"):
276
278
  return True
277
279
 
280
+ cors_config = getattr(request.app.state, "cors_config", None)
281
+ cors_enabled = cors_config.get("enabled", False) if cors_config else False
278
282
  logger.warning(
279
283
  f"WebSocket upgrade rejected - invalid Origin: {origin} "
280
284
  f"(allowed: {allowed_origins}, app: {getattr(request.app, 'title', 'unknown')}, "
285
+ f"path: {request.url.path}, CORS enabled: {cors_enabled}, "
281
286
  f"has_cors_config: {hasattr(request.app.state, 'cors_config')})"
282
287
  )
283
288
  return False
@@ -1499,8 +1499,9 @@ class MongoDBEngine:
1499
1499
 
1500
1500
  # Add CSRF middleware (after auth - auto-enabled for shared mode)
1501
1501
  # CSRF protection is enabled by default for shared auth mode
1502
+ # SKIP for sub-apps in multi-app setups - parent app handles CSRF
1502
1503
  csrf_config = auth_config.get("csrf_protection", True if auth_mode == "shared" else False)
1503
- if csrf_config:
1504
+ if csrf_config and not is_sub_app: # Don't add CSRF to child apps
1504
1505
  from ..auth.csrf import create_csrf_middleware
1505
1506
 
1506
1507
  csrf_middleware = create_csrf_middleware(
@@ -1508,6 +1509,11 @@ class MongoDBEngine:
1508
1509
  )
1509
1510
  app.add_middleware(csrf_middleware)
1510
1511
  logger.info(f"CSRFMiddleware added for '{slug}'")
1512
+ elif csrf_config and is_sub_app:
1513
+ logger.debug(
1514
+ f"CSRFMiddleware skipped for child app '{slug}' - "
1515
+ f"parent app handles CSRF protection for WebSocket routes"
1516
+ )
1511
1517
 
1512
1518
  # Add security middleware (HSTS, headers)
1513
1519
  security_config = auth_config.get("security", {})
@@ -2127,17 +2133,33 @@ class MongoDBEngine:
2127
2133
  "Path prefix validation failed:\n" + "\n".join(f" - {e}" for e in errors)
2128
2134
  )
2129
2135
 
2130
- # Check if any app uses shared auth
2136
+ # Check if any app uses shared auth and collect public routes for CSRF exemption
2131
2137
  has_shared_auth = False
2138
+ all_public_routes = [
2139
+ "/health",
2140
+ "/docs",
2141
+ "/openapi.json",
2142
+ "/_mdb/routes",
2143
+ ] # Base exempt routes
2132
2144
  for app_config in apps:
2133
2145
  try:
2134
2146
  manifest_path = app_config["manifest"]
2147
+ path_prefix = app_config.get("path_prefix", f"/{app_config.get('slug')}")
2135
2148
  with open(manifest_path) as f:
2136
2149
  app_manifest_pre = json.load(f)
2137
2150
  auth_config = app_manifest_pre.get("auth", {})
2138
2151
  if auth_config.get("mode") == "shared":
2139
2152
  has_shared_auth = True
2140
- break
2153
+ # Collect public routes with path prefix for CSRF exemption
2154
+ child_public_routes = auth_config.get("public_routes", [])
2155
+ for route in child_public_routes:
2156
+ # Add path prefix to make route absolute on parent app
2157
+ if route.startswith("/"):
2158
+ prefixed_route = f"{path_prefix.rstrip('/')}{route}"
2159
+ else:
2160
+ prefixed_route = f"{path_prefix.rstrip('/')}/{route}"
2161
+ if prefixed_route not in all_public_routes:
2162
+ all_public_routes.append(prefixed_route)
2141
2163
  except (FileNotFoundError, json.JSONDecodeError, KeyError) as e:
2142
2164
  logger.warning(f"Could not check auth mode for app '{app_config.get('slug')}': {e}")
2143
2165
 
@@ -2212,7 +2234,18 @@ class MongoDBEngine:
2212
2234
  # Merge allow_origins lists
2213
2235
  child_origins = child_cors.get("allow_origins", [])
2214
2236
  parent_origins = parent_cors.get("allow_origins", [])
2215
- merged_origins = list(set(parent_origins + child_origins))
2237
+
2238
+ # CRITICAL: Handle wildcard origins correctly
2239
+ # If any child or parent has wildcard, merged config gets wildcard
2240
+ if "*" in child_origins:
2241
+ merged_origins = ["*"] # Wildcard takes precedence
2242
+ elif "*" in parent_origins:
2243
+ merged_origins = ["*"] # Keep wildcard if already set
2244
+ else:
2245
+ # Merge unique origins
2246
+ merged_origins = list(set(parent_origins + child_origins))
2247
+ if not merged_origins:
2248
+ merged_origins = ["*"] # Default to wildcard if empty
2216
2249
 
2217
2250
  # CRITICAL: If ANY child app requires credentials, parent must allow them
2218
2251
  # This is essential for SSO cookie-based authentication
@@ -2225,16 +2258,17 @@ class MongoDBEngine:
2225
2258
  parent_app.state.cors_config = {
2226
2259
  **parent_cors,
2227
2260
  **child_cors,
2228
- "allow_origins": merged_origins if merged_origins else ["*"],
2261
+ "allow_origins": merged_origins,
2229
2262
  # If ANY child requires credentials, parent gets True (for SSO)
2230
2263
  "allow_credentials": merged_allow_credentials,
2231
2264
  }
2232
2265
  else:
2233
2266
  # Parent has no CORS config, use child's
2234
2267
  parent_app.state.cors_config = child_cors
2235
- logger.debug(
2236
- f"✅ Merged CORS config from child app '{slug}' to parent app "
2237
- f"(allow_credentials={parent_app.state.cors_config.get('allow_credentials')})"
2268
+ logger.info(
2269
+ f"✅ Merged CORS config from '{slug}': "
2270
+ f"origins={parent_app.state.cors_config.get('allow_origins')}, "
2271
+ f"credentials={parent_app.state.cors_config.get('allow_credentials')}"
2238
2272
  )
2239
2273
 
2240
2274
  async def _register_websocket_routes(
@@ -2246,6 +2280,7 @@ class MongoDBEngine:
2246
2280
  """Register WebSocket routes on parent app for a child app."""
2247
2281
  websockets_config = child_manifest.get("websockets")
2248
2282
  if not websockets_config:
2283
+ logger.debug(f"No WebSocket configuration found for app '{slug}'")
2249
2284
  return
2250
2285
 
2251
2286
  try:
@@ -2253,6 +2288,9 @@ class MongoDBEngine:
2253
2288
 
2254
2289
  from ..routing.websockets import create_websocket_endpoint
2255
2290
 
2291
+ registered_count = 0
2292
+ failed_count = 0
2293
+
2256
2294
  for endpoint_name, endpoint_config in websockets_config.items():
2257
2295
  ws_path = endpoint_config.get("path", f"/{endpoint_name}")
2258
2296
  # Combine mount prefix with WebSocket path
@@ -2273,24 +2311,65 @@ class MongoDBEngine:
2273
2311
 
2274
2312
  ping_interval = endpoint_config.get("ping_interval", 30)
2275
2313
 
2276
- # Create WebSocket handler
2277
- handler = create_websocket_endpoint(
2278
- app_slug=slug,
2279
- path=ws_path,
2280
- endpoint_name=endpoint_name,
2281
- handler=None,
2282
- require_auth=require_auth,
2283
- ping_interval=ping_interval,
2284
- )
2314
+ try:
2315
+ # Create WebSocket handler
2316
+ handler = create_websocket_endpoint(
2317
+ app_slug=slug,
2318
+ path=ws_path,
2319
+ endpoint_name=endpoint_name,
2320
+ handler=None,
2321
+ require_auth=require_auth,
2322
+ ping_interval=ping_interval,
2323
+ )
2324
+
2325
+ # Register on parent app with full path
2326
+ ws_router = APIRouter()
2327
+ ws_router.websocket(full_ws_path)(handler)
2328
+ parent_app.include_router(ws_router)
2285
2329
 
2286
- # Register on parent app with full path
2287
- ws_router = APIRouter()
2288
- ws_router.websocket(full_ws_path)(handler)
2289
- parent_app.include_router(ws_router)
2330
+ logger.info(
2331
+ f"✅ Registered WebSocket route '{full_ws_path}' "
2332
+ f"for mounted app '{slug}' (mounted at '{path_prefix}', "
2333
+ f"auth: {require_auth}, ping: {ping_interval}s)"
2334
+ )
2335
+
2336
+ # Verify route was actually registered
2337
+ registered_routes = [
2338
+ r
2339
+ for r in parent_app.routes
2340
+ if hasattr(r, "path") and full_ws_path in str(getattr(r, "path", ""))
2341
+ ]
2342
+ if registered_routes:
2343
+ registered_count += 1
2344
+ logger.debug(
2345
+ f"✅ Verified WebSocket route '{full_ws_path}' "
2346
+ f"registered for '{slug}'"
2347
+ )
2348
+ else:
2349
+ failed_count += 1
2350
+ logger.warning(
2351
+ f"⚠️ WebSocket route '{full_ws_path}' not found after registration "
2352
+ f"for '{slug}' - route may not be accessible"
2353
+ )
2354
+ except (ValueError, TypeError, AttributeError, RuntimeError) as e:
2355
+ failed_count += 1
2356
+ logger.error(
2357
+ f"Failed to register WebSocket route '{full_ws_path}' "
2358
+ f"for mounted app '{slug}': {e}",
2359
+ exc_info=True,
2360
+ )
2290
2361
 
2362
+ # Summary logging
2363
+ total_routes = len(websockets_config)
2364
+ if registered_count > 0:
2291
2365
  logger.info(
2292
- f"✅ Registered WebSocket route '{full_ws_path}' "
2293
- f"for mounted app '{slug}' (mounted at '{path_prefix}')"
2366
+ f"✅ WebSocket registration summary for '{slug}': "
2367
+ f"{registered_count}/{total_routes} routes registered successfully"
2368
+ )
2369
+ if failed_count > 0:
2370
+ logger.warning(
2371
+ f"⚠️ WebSocket registration issues for '{slug}': "
2372
+ f"{failed_count}/{total_routes} routes failed to register"
2294
2373
  )
2295
2374
  except ImportError:
2296
2375
  logger.warning(
@@ -2668,6 +2747,38 @@ class MongoDBEngine:
2668
2747
  # This ensures the state reflects the final mounted_apps list
2669
2748
  app.state.mounted_apps = mounted_apps
2670
2749
 
2750
+ # VERIFICATION: Log final configuration state
2751
+ logger.info("=" * 60)
2752
+ logger.info("MDB-Engine Multi-App Configuration Verification")
2753
+ logger.info("=" * 60)
2754
+
2755
+ # Verify CORS config
2756
+ cors_config = getattr(app.state, "cors_config", None)
2757
+ if cors_config:
2758
+ logger.info(
2759
+ f"✅ CORS Config: enabled={cors_config.get('enabled')}, "
2760
+ f"origins={cors_config.get('allow_origins')}, "
2761
+ f"credentials={cors_config.get('allow_credentials')}"
2762
+ )
2763
+ else:
2764
+ logger.warning("⚠️ No CORS config found on parent app")
2765
+
2766
+ # Verify WebSocket routes
2767
+ ws_routes = [
2768
+ r for r in app.routes if hasattr(r, "path") and "/ws" in str(getattr(r, "path", ""))
2769
+ ]
2770
+ if ws_routes:
2771
+ logger.info(f"✅ Found {len(ws_routes)} WebSocket route(s):")
2772
+ for route in ws_routes:
2773
+ route_path = getattr(route, "path", "unknown")
2774
+ logger.info(f" 🔌 {route_path}")
2775
+ else:
2776
+ logger.warning(
2777
+ "⚠️ No WebSocket routes found - check manifest.json websockets config"
2778
+ )
2779
+
2780
+ logger.info("=" * 60)
2781
+
2671
2782
  yield
2672
2783
 
2673
2784
  # Shutdown is handled by parent app
@@ -2723,10 +2834,11 @@ class MongoDBEngine:
2723
2834
  from ..auth.csrf import create_csrf_middleware
2724
2835
 
2725
2836
  # Create CSRF middleware with default config (will use parent app's CORS config)
2726
- # Exempt routes that don't need CSRF (health checks, etc.)
2837
+ # Exempt routes that don't need CSRF (health checks, public routes from child apps)
2838
+ # all_public_routes includes base routes + child app public routes with path prefixes
2727
2839
  parent_csrf_config = {
2728
2840
  "csrf_protection": True,
2729
- "public_routes": ["/health", "/docs", "/openapi.json", "/_mdb/routes"],
2841
+ "public_routes": all_public_routes,
2730
2842
  }
2731
2843
  csrf_middleware = create_csrf_middleware(parent_csrf_config)
2732
2844
  parent_app.add_middleware(csrf_middleware)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdb-engine
3
- Version: 0.4.10
3
+ Version: 0.4.12
4
4
  Summary: MongoDB Engine
5
5
  Home-page: https://github.com/ranfysvalle02/mdb-engine
6
6
  Author: Fabian Valle
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mdb-engine"
7
- version = "0.4.10"
7
+ version = "0.4.12"
8
8
  description = "MongoDB Engine"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -14,7 +14,7 @@ if readme_file.exists():
14
14
 
15
15
  setup(
16
16
  name="mdb-engine",
17
- version="0.4.8",
17
+ version="0.4.12",
18
18
  description="MongoDB Engine",
19
19
  long_description=long_description,
20
20
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes
File without changes