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).
|