Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
CHANGED
|
@@ -192,7 +192,8 @@ def start_url_monitoring_thread(target_url_id):
|
|
| 192 |
# Stop existing thread if it's alive
|
| 193 |
if "_thread" in url_data_entry and url_data_entry["_thread"].is_alive():
|
| 194 |
print(f"Monitor for URL ID {target_url_id} already running. Attempting to restart.")
|
| 195 |
-
url_data_entry["_stop_event"]
|
|
|
|
| 196 |
url_data_entry["_thread"].join(timeout=3) # Wait for thread to stop
|
| 197 |
|
| 198 |
new_stop_event = threading.Event()
|
|
@@ -211,7 +212,8 @@ def stop_url_monitoring_thread(target_url_id):
|
|
| 211 |
url_data_entry = monitored_urls_store[target_url_id]
|
| 212 |
if "_thread" in url_data_entry and url_data_entry["_thread"].is_alive():
|
| 213 |
print(f"Signaling stop for monitor thread of URL ID {target_url_id}")
|
| 214 |
-
url_data_entry["_stop_event"]
|
|
|
|
| 215 |
# Not joining here to keep API responsive, daemon thread will exit.
|
| 216 |
url_data_entry.pop("_thread", None)
|
| 217 |
url_data_entry.pop("_stop_event", None)
|
|
@@ -255,8 +257,9 @@ def add_new_url():
|
|
| 255 |
with lock:
|
| 256 |
# Check for duplicates (case-insensitive, ignoring trailing slashes)
|
| 257 |
normalized_new_url = input_url.rstrip('/').lower()
|
| 258 |
-
for
|
| 259 |
-
|
|
|
|
| 260 |
return jsonify({"error": "URL already monitored"}), 409 # Conflict
|
| 261 |
|
| 262 |
new_url_id = str(uuid.uuid4())
|
|
@@ -266,13 +269,18 @@ def add_new_url():
|
|
| 266 |
"id": new_url_id, "url": input_url, "status": 'pending',
|
| 267 |
"ip": resolved_ip, "responseTime": None, "lastChecked": None, "history": []
|
| 268 |
}
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
save_data_to_json()
|
| 271 |
|
| 272 |
-
start_url_monitoring_thread(new_url_id)
|
| 273 |
|
| 274 |
-
# Return the
|
| 275 |
-
return jsonify(
|
| 276 |
|
| 277 |
|
| 278 |
@app.route('/api/urls/<string:target_url_id>', methods=['DELETE'])
|
|
@@ -284,7 +292,7 @@ def delete_existing_url(target_url_id):
|
|
| 284 |
save_data_to_json()
|
| 285 |
|
| 286 |
# Prepare data for response (without thread objects)
|
| 287 |
-
response_data = removed_url_entry.copy()
|
| 288 |
response_data.pop("_thread", None)
|
| 289 |
response_data.pop("_stop_event", None)
|
| 290 |
print(f"Deleted URL ID {target_url_id}")
|
|
@@ -301,7 +309,12 @@ if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': # Avoids double load in Flask
|
|
| 301 |
if __name__ == '__main__':
|
| 302 |
# This block is for local development (e.g., `python app.py`)
|
| 303 |
# `load_data_from_json()` is called above unless Werkzeug reloader is active.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
app.run(debug=True, host='0.0.0.0', port=7860)
|
| 305 |
|
| 306 |
# When run with Gunicorn, Gunicorn imports `app` from this `app.py` file.
|
| 307 |
-
# `load_data_from_json()` will have been called during that import.
|
|
|
|
| 192 |
# Stop existing thread if it's alive
|
| 193 |
if "_thread" in url_data_entry and url_data_entry["_thread"].is_alive():
|
| 194 |
print(f"Monitor for URL ID {target_url_id} already running. Attempting to restart.")
|
| 195 |
+
if "_stop_event" in url_data_entry and url_data_entry["_stop_event"]: # Check if _stop_event exists
|
| 196 |
+
url_data_entry["_stop_event"].set()
|
| 197 |
url_data_entry["_thread"].join(timeout=3) # Wait for thread to stop
|
| 198 |
|
| 199 |
new_stop_event = threading.Event()
|
|
|
|
| 212 |
url_data_entry = monitored_urls_store[target_url_id]
|
| 213 |
if "_thread" in url_data_entry and url_data_entry["_thread"].is_alive():
|
| 214 |
print(f"Signaling stop for monitor thread of URL ID {target_url_id}")
|
| 215 |
+
if "_stop_event" in url_data_entry and url_data_entry["_stop_event"]: # Check if _stop_event exists
|
| 216 |
+
url_data_entry["_stop_event"].set()
|
| 217 |
# Not joining here to keep API responsive, daemon thread will exit.
|
| 218 |
url_data_entry.pop("_thread", None)
|
| 219 |
url_data_entry.pop("_stop_event", None)
|
|
|
|
| 257 |
with lock:
|
| 258 |
# Check for duplicates (case-insensitive, ignoring trailing slashes)
|
| 259 |
normalized_new_url = input_url.rstrip('/').lower()
|
| 260 |
+
for existing_url_id in list(monitored_urls_store.keys()): # Iterate over keys to avoid issues if store is modified
|
| 261 |
+
existing_url_data = monitored_urls_store.get(existing_url_id)
|
| 262 |
+
if existing_url_data and existing_url_data['url'].rstrip('/').lower() == normalized_new_url:
|
| 263 |
return jsonify({"error": "URL already monitored"}), 409 # Conflict
|
| 264 |
|
| 265 |
new_url_id = str(uuid.uuid4())
|
|
|
|
| 269 |
"id": new_url_id, "url": input_url, "status": 'pending',
|
| 270 |
"ip": resolved_ip, "responseTime": None, "lastChecked": None, "history": []
|
| 271 |
}
|
| 272 |
+
|
| 273 |
+
# Make a copy of the entry for the response *before* it's potentially modified
|
| 274 |
+
# by start_url_monitoring_thread with non-serializable objects.
|
| 275 |
+
response_payload = url_entry_to_add.copy()
|
| 276 |
+
|
| 277 |
+
monitored_urls_store[new_url_id] = url_entry_to_add # url_entry_to_add will be modified by start_url_monitoring_thread
|
| 278 |
save_data_to_json()
|
| 279 |
|
| 280 |
+
start_url_monitoring_thread(new_url_id) # This will add _thread and _stop_event to monitored_urls_store[new_url_id]
|
| 281 |
|
| 282 |
+
# Return the clean response_payload, which does not have _thread or _stop_event
|
| 283 |
+
return jsonify(response_payload), 201
|
| 284 |
|
| 285 |
|
| 286 |
@app.route('/api/urls/<string:target_url_id>', methods=['DELETE'])
|
|
|
|
| 292 |
save_data_to_json()
|
| 293 |
|
| 294 |
# Prepare data for response (without thread objects)
|
| 295 |
+
response_data = removed_url_entry.copy() # Copy before potential modification if stop_url_monitoring_thread didn't pop everything
|
| 296 |
response_data.pop("_thread", None)
|
| 297 |
response_data.pop("_stop_event", None)
|
| 298 |
print(f"Deleted URL ID {target_url_id}")
|
|
|
|
| 309 |
if __name__ == '__main__':
|
| 310 |
# This block is for local development (e.g., `python app.py`)
|
| 311 |
# `load_data_from_json()` is called above unless Werkzeug reloader is active.
|
| 312 |
+
# If using Flask's reloader, load_data_from_json will be called twice:
|
| 313 |
+
# once by the main process, once by the reloader's child process.
|
| 314 |
+
# The check for WERKZEUG_RUN_MAIN ensures it only loads in the main one or the child.
|
| 315 |
+
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': # Ensure data is loaded in the reloaded process too
|
| 316 |
+
load_data_from_json()
|
| 317 |
app.run(debug=True, host='0.0.0.0', port=7860)
|
| 318 |
|
| 319 |
# When run with Gunicorn, Gunicorn imports `app` from this `app.py` file.
|
| 320 |
+
# `load_data_from_json()` will have been called during that import (due to the WERKZEUG_RUN_MAIN check).
|