From ad9fab907cca62a16b74603050a3c349337f2881 Mon Sep 17 00:00:00 2001 From: Nixietab <75538775+nixietab@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:53:52 -0300 Subject: [PATCH] update stuff, add fallbacks, more order to the api --- __pycache__/healthcheck.cpython-313.pyc | Bin 0 -> 1769 bytes __pycache__/tokenext.cpython-313.pyc | Bin 0 -> 5567 bytes berretin.py | 49 +++++++++++++++++- ...d930d7d438dc6256e8ed03c5832b6bc7d8fa3.json | 30 +++++++++++ healthcheck.py | 7 ++- 5 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 __pycache__/healthcheck.cpython-313.pyc create mode 100644 __pycache__/tokenext.cpython-313.pyc create mode 100644 cache/47172bb6d3ba5ce5b090e419551d930d7d438dc6256e8ed03c5832b6bc7d8fa3.json diff --git a/__pycache__/healthcheck.cpython-313.pyc b/__pycache__/healthcheck.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e3f9a3565728a71a6c35cef62d896dddce1c33c GIT binary patch literal 1769 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 literal 0 HcmV?d00001 diff --git a/berretin.py b/berretin.py index f86ada3..75b581a 100644 --- a/berretin.py +++ b/berretin.py @@ -8,6 +8,7 @@ from urllib.parse import urljoin import tokenext from healthcheck import ensure_config_exists +# load config config = ensure_config_exists() server_cfg = config["server"] @@ -16,6 +17,13 @@ PASSWORD = server_cfg.get("password", "").strip() CACHE_DIR = server_cfg.get("cache_dir", "cache") BASE_API = server_cfg.get("base_api", "https://ws1.smn.gob.ar") LOG_FILE = server_cfg.get("log_file", "").strip() + +BASE_PATH = server_cfg.get("base_path", "/smn").strip() +if not BASE_PATH.startswith("/"): + BASE_PATH = "/" + BASE_PATH +if BASE_PATH.endswith("/"): + BASE_PATH = BASE_PATH[:-1] + SMN_TOKEN_FILE = "token" CACHE_TTL = timedelta(minutes=60) AUTH_ENABLED = PASSWORD != "" @@ -28,6 +36,7 @@ def log(msg: str): f.write(f"[{datetime.now().isoformat()}] {msg}\n") print(msg) +# cache handling def get_cache_filename(url: str) -> str: h = hashlib.sha256(url.encode()).hexdigest() return os.path.join(CACHE_DIR, f"{h}.json") @@ -51,7 +60,13 @@ def save_cache(url: str, data: dict): with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) + # token handling def load_smn_token(): + if not os.path.exists(SMN_TOKEN_FILE): + log("[TOKEN] Token file not found — refreshing token...") + refresh_smn_token() + if not os.path.exists(SMN_TOKEN_FILE): + raise FileNotFoundError("Token file could not be created.") with open(SMN_TOKEN_FILE, "r") as f: return f.read().strip() @@ -69,6 +84,7 @@ def check_access_token(): header_token = request.headers.get("Authorization", "").strip() return header_token == PASSWORD +# upstream the request def fetch_from_smn(url: str, retry: bool = True): token = load_smn_token() headers = { @@ -89,7 +105,8 @@ def fetch_from_smn(url: str, retry: bool = True): return resp -@app.route("/smn/") +# the proxy stuff +@app.route(f"{BASE_PATH}/") def smn_proxy(subpath): if not check_access_token(): return jsonify({"error": "Unauthorized"}), 401 @@ -121,7 +138,37 @@ def smn_proxy(subpath): except Exception: return Response("Invalid JSON from SMN", status=502) +@app.errorhandler(404) +def handle_not_found(e): + return jsonify({ + "error": "Endpoint not found", + "message": f"The requested URL '{request.path}' is not a valid API endpoint.", + }), 200 + +@app.errorhandler(405) +def handle_method_not_allowed(e): + return jsonify({ + "error": "Method not allowed", + "allowed": ["GET"], + "path": request.path + }), 200 + +@app.errorhandler(Exception) +def handle_general_error(e): + log(f"[ERROR] Unexpected exception: {e}") + return jsonify({ + "error": "Internal error", + "message": str(e), + "path": request.path + }), 200 + + +# === Startup === if __name__ == "__main__": os.makedirs(CACHE_DIR, exist_ok=True) + if not os.path.exists(SMN_TOKEN_FILE): + log("[STARTUP] No token file found — generating a new one.") + refresh_smn_token() log(f"[STARTUP] Server starting on port {PORT}") + log(f"[STARTUP] Base path set to '{BASE_PATH}/'") app.run(host="0.0.0.0", port=PORT) diff --git a/cache/47172bb6d3ba5ce5b090e419551d930d7d438dc6256e8ed03c5832b6bc7d8fa3.json b/cache/47172bb6d3ba5ce5b090e419551d930d7d438dc6256e8ed03c5832b6bc7d8fa3.json new file mode 100644 index 0000000..d03c5ad --- /dev/null +++ b/cache/47172bb6d3ba5ce5b090e419551d930d7d438dc6256e8ed03c5832b6bc7d8fa3.json @@ -0,0 +1,30 @@ +{ + "date": "2025-11-10T13:00:00-03:00", + "humidity": 55.0, + "pressure": 1020.8, + "feels_like": null, + "temperature": 24.1, + "visibility": 10.0, + "weather": { + "description": "Ligeramente nublado", + "id": 13 + }, + "wind": { + "direction": "Noreste", + "deg": 45.0, + "speed": 15.0 + }, + "station_id": 87582, + "location": { + "id": 10821, + "name": "Aeroparque Buenos Aires", + "department": "CABA", + "province": "CABA", + "type": "", + "coord": { + "lon": -58.4187, + "lat": -34.5619 + }, + "distance": 0.35 + } +} \ No newline at end of file diff --git a/healthcheck.py b/healthcheck.py index 52c9783..5a52835 100644 --- a/healthcheck.py +++ b/healthcheck.py @@ -9,12 +9,14 @@ DEFAULT_CONFIG = { "password": "debug", "cache_dir": "cache", "base_api": "https://ws1.smn.gob.ar", - "log_file": "" + "log_file": "", + "base_path": "/smn" } } def ensure_config_exists(): config = configparser.ConfigParser() + if not os.path.exists(CONFIG_FILE): print("[CONFIG] config.ini not found - creating with default values") config.read_dict(DEFAULT_CONFIG) @@ -36,4 +38,5 @@ def ensure_config_exists(): with open(CONFIG_FILE, "w") as f: config.write(f) print("[CONFIG] Missing keys added to config.ini") - return config + + return config \ No newline at end of file