44from flask_cors import CORS
55import json , logging , os , re
66from werkzeug .exceptions import HTTPException
7+ from config import LOG_LEVEL , LOG_EXCLUDE_LEVELS , LOG_FORMAT , LOG_DATE_FORMAT
8+
9+ class ConfigurableLogFilter (logging .Filter ):
10+ """Filter out specific log levels based on configuration."""
11+ def __init__ (self , exclude_levels_str ):
12+ super ().__init__ ()
13+ # Parse excluded levels from config (e.g., "WARNING,DEBUG" -> [30, 10])
14+ self .excluded_levels = set ()
15+ if exclude_levels_str :
16+ for level_name in exclude_levels_str .split (',' ):
17+ level_name = level_name .strip ().upper ()
18+ if level_name and hasattr (logging , level_name ):
19+ self .excluded_levels .add (getattr (logging , level_name ))
20+
21+ def filter (self , record ):
22+ return record .levelno not in self .excluded_levels
23+
24+ logging .basicConfig (
25+ level = getattr (logging , LOG_LEVEL , logging .INFO ),
26+ format = LOG_FORMAT ,
27+ datefmt = LOG_DATE_FORMAT
28+ )
29+
30+ log_filter = ConfigurableLogFilter (LOG_EXCLUDE_LEVELS )
31+ for handler in logging .root .handlers :
32+ handler .addFilter (log_filter )
733
834from services .db import redis_client
935from services .canvas_counter import get_canvas_draw_count
1036from services .graphql_service import commit_transaction_via_graphql
37+ from services .graphql_retry_worker import start_retry_worker , stop_retry_worker
1138from config import *
1239
1340app = Flask (__name__ )
@@ -78,7 +105,17 @@ def handle_rate_limit_exception(e):
78105local_regexes = [r"^https?://localhost(:\d+)?$" , r"^https?://127\.0\.0\.1(:\d+)?$" ]
79106
80107cors_origins = explicit_allowed + local_regexes
81- CORS (app , supports_credentials = True , origins = cors_origins )
108+
109+ CORS (app ,
110+ resources = {r"/*" : {
111+ "origins" : cors_origins ,
112+ "methods" : ["GET" , "POST" , "PUT" , "PATCH" , "DELETE" , "OPTIONS" ],
113+ "allow_headers" : ["Content-Type" , "Authorization" , "X-Requested-With" , "Accept" ],
114+ "expose_headers" : ["Content-Type" , "Authorization" ],
115+ "supports_credentials" : True ,
116+ "max_age" : 3600
117+ }}
118+ )
82119
83120def origin_allowed (origin ):
84121 """Return True if the provided origin string is allowed by explicit allowed
@@ -103,17 +140,17 @@ def add_cors_headers(response):
103140 This complements flask-cors and guards against cases where exception paths
104141 or other middleware may return responses without the proper headers.
105142 """
143+ if response .headers .get ("Access-Control-Allow-Origin" ):
144+ return response
145+
106146 try :
107147 origin = request .headers .get ("Origin" )
108148 if origin and origin_allowed (origin ):
109149 response .headers ["Access-Control-Allow-Origin" ] = origin
110150 response .headers ["Access-Control-Allow-Credentials" ] = "true"
111- else :
112- fallback = explicit_allowed [0 ] if explicit_allowed else "http://localhost:10008"
113- response .headers .setdefault ("Access-Control-Allow-Origin" , fallback )
114- response .headers .setdefault ("Access-Control-Allow-Credentials" , "true" )
115- response .headers .setdefault ("Access-Control-Allow-Headers" , "Content-Type,Authorization" )
116- response .headers .setdefault ("Access-Control-Allow-Methods" , "GET,POST,PUT,PATCH,DELETE,OPTIONS" )
151+ response .headers ["Access-Control-Allow-Headers" ] = "Content-Type,Authorization,X-Requested-With,Accept"
152+ response .headers ["Access-Control-Allow-Methods" ] = "GET,POST,PUT,PATCH,DELETE,OPTIONS"
153+ response .headers ["Access-Control-Expose-Headers" ] = "Content-Type,Authorization"
117154 except Exception :
118155 pass
119156 return response
@@ -142,28 +179,32 @@ def handle_all_exceptions(e):
142179 if origin and origin_allowed (origin ):
143180 resp .headers ["Access-Control-Allow-Origin" ] = origin
144181 resp .headers ["Access-Control-Allow-Credentials" ] = "true"
182+ resp .headers ["Access-Control-Allow-Headers" ] = "Content-Type,Authorization,X-Requested-With,Accept"
183+ resp .headers ["Access-Control-Allow-Methods" ] = "GET,POST,PUT,PATCH,DELETE,OPTIONS"
184+ resp .headers ["Access-Control-Expose-Headers" ] = "Content-Type,Authorization"
145185 else :
146186 fallback = explicit_allowed [0 ] if explicit_allowed else "http://localhost:10008"
147187 resp .headers .setdefault ("Access-Control-Allow-Origin" , fallback )
148188 resp .headers .setdefault ("Access-Control-Allow-Credentials" , "true" )
149- resp .headers .setdefault ("Access-Control-Allow-Headers" , "Content-Type,Authorization" )
150- resp .headers .setdefault ("Access-Control-Allow-Methods" , "GET,POST,PUT,PATCH,DELETE,OPTIONS" )
189+ resp .headers .setdefault ("Access-Control-Allow-Headers" , "Content-Type,Authorization,X-Requested-With,Accept" )
190+ resp .headers .setdefault ("Access-Control-Allow-Methods" , "GET,POST,PUT,PATCH,DELETE,OPTIONS" )
191+ resp .headers .setdefault ("Access-Control-Expose-Headers" , "Content-Type,Authorization" )
151192 return resp
152193 except Exception :
153194 # If even the error handler fails, return a minimal JSON response
154195 logger .exception ("Error while handling exception" )
155196 out = make_response (json .dumps ({"status" : "error" , "message" : "Fatal error" }), 500 )
156197 out .headers .setdefault ("Access-Control-Allow-Origin" , "http://localhost:10008" )
157198 out .headers .setdefault ("Access-Control-Allow-Credentials" , "true" )
158- out .headers .setdefault ("Access-Control-Allow-Headers" , "Content-Type,Authorization" )
199+ out .headers .setdefault ("Access-Control-Allow-Headers" , "Content-Type,Authorization,X-Requested-With,Accept " )
159200 out .headers .setdefault ("Access-Control-Allow-Methods" , "GET,POST,PUT,PATCH,DELETE,OPTIONS" )
160201 out .headers ["Content-Type" ] = "application/json"
161202 return out
162203
163204
164205from flask_socketio import SocketIO
165206import services .socketio_service as socketio_service
166- socketio = SocketIO (app , cors_allowed_origins = "*" , async_mode = "threading" )
207+ socketio = SocketIO (app , cors_allowed_origins = cors_origins , async_mode = "threading" )
167208socketio_service .socketio = socketio
168209socketio_service .register_socketio_handlers ()
169210
@@ -201,6 +242,14 @@ def handle_all_exceptions(e):
201242app .register_blueprint (frontend_bp )
202243app .register_blueprint (analytics_bp )
203244
245+ # Start the GraphQL retry worker in background thread
246+ # Worker sleeps 2 seconds on startup to ensure Redis/MongoDB are ready
247+ start_retry_worker ()
248+
249+ # Register cleanup on shutdown
250+ import atexit
251+ atexit .register (stop_retry_worker )
252+
204253if __name__ == '__main__' :
205254 if not redis_client .exists ('res-canvas-draw-count' ):
206255 init_count = {"id" : "res-canvas-draw-count" , "value" : 0 }
0 commit comments