diff --git a/modules/app_alerts.py b/modules/app_alerts.py index 875b159..365094f 100644 --- a/modules/app_alerts.py +++ b/modules/app_alerts.py @@ -231,7 +231,7 @@ def _power_condition(throttling, now=None): def collect_conditions(config): health = app_health.collect_health() videos = app_video.list_local_videos() - pending_videos = sum(1 for video in videos["files"] if not video["uploaded"]) + pending_videos = sum(1 for video in (videos or {}).get("files", []) if not video["uploaded"]) pending_tracking = app_sql.count_unsent_tracking_rows() speed = app_obd.get_fresh_speed() fix_age = app_gps.get_fix_age() diff --git a/modules/app_cellular.py b/modules/app_cellular.py index ee492d3..a766dbd 100644 --- a/modules/app_cellular.py +++ b/modules/app_cellular.py @@ -229,7 +229,7 @@ def http_post( break read_resp = _at(ser, "AT+HTTPREAD", terminators=("OK", "ERROR"), timeout=15) - body = read_resp.encode(errors="replace") + body = read_resp return status_code, body finally: diff --git a/modules/app_settings.py b/modules/app_settings.py index 0799db5..dc99473 100644 --- a/modules/app_settings.py +++ b/modules/app_settings.py @@ -121,6 +121,7 @@ def update_settings(body): apn = body.get("cellular_apn") if not isinstance(apn, str): errors["cellular_apn"] = "Must be text." + apn = "" else: apn = apn.strip() if len(apn) > 100 or not _APN_PATTERN.fullmatch(apn): diff --git a/modules/app_upload.py b/modules/app_upload.py index a5a61bf..f5c11ed 100644 --- a/modules/app_upload.py +++ b/modules/app_upload.py @@ -35,6 +35,7 @@ never commit real values - mirrors the db/creds.env pattern already used by app_db.py for MongoDB). """ import hashlib +import json import os import time from threading import Thread @@ -178,9 +179,7 @@ def _upload_tracking_batch(): if not app_cellular.is_configured(): raise print(f"WiFi/Ethernet upload unreachable ({wifi_err}), trying cellular fallback...") - import json as _json - - body_bytes = _json.dumps({"points": points}).encode() + body_bytes = json.dumps({"points": points}).encode() try: status_code, _body = app_cellular.http_post_json( f"{SERVER_URL}/api/v1/tracking/batch", body_bytes, _headers() diff --git a/server/app.py b/server/app.py index e452fb6..eb3650c 100644 --- a/server/app.py +++ b/server/app.py @@ -430,7 +430,10 @@ def portal_trips(): device_id = _portal_device_id() if device_id is None: return jsonify(error="device not configured"), 404 - limit = min(int(request.args.get("limit", 200)), 1000) + try: + limit = min(int(request.args.get("limit", 200)), 1000) + except (ValueError, TypeError): + limit = 200 return jsonify(db.list_trips(device_id, limit=limit)) @@ -452,7 +455,10 @@ def portal_dashcam_list(): device_id = _portal_device_id() if device_id is None: return jsonify(error="device not configured"), 404 - limit = min(int(request.args.get("limit", 500)), 2000) + try: + limit = min(int(request.args.get("limit", 500)), 2000) + except (ValueError, TypeError): + limit = 500 return jsonify(db.list_dashcam_files(device_id, limit=limit)) @@ -468,7 +474,11 @@ def portal_dashcam_stream(file_id): abort(404) storage_path = file_row["storage_path"] # Safety check: must still be under STORAGE_DIR - if not os.path.commonpath([STORAGE_DIR, os.path.normpath(storage_path)]) == os.path.normpath(STORAGE_DIR): + try: + safe = os.path.commonpath([STORAGE_DIR, os.path.normpath(storage_path)]) == os.path.normpath(STORAGE_DIR) + except ValueError: + safe = False + if not safe: abort(403) if not os.path.isfile(storage_path): abort(404) diff --git a/tests/test_app_settings.py b/tests/test_app_settings.py index a8a5cc5..50b2f91 100644 --- a/tests/test_app_settings.py +++ b/tests/test_app_settings.py @@ -8,6 +8,8 @@ from modules import app_cellular, app_settings def valid_payload(): return { "dashcam_upload_enabled": False, + "trips_upload_enabled": True, + "video_convert_enabled": True, "local_video_retention_days": 14, "cellular_enabled": True, "cellular_apn": "carrier.apn",