diff --git a/__pycache__/healthcheck.cpython-313.pyc b/__pycache__/healthcheck.cpython-313.pyc new file mode 100644 index 0000000..4e3f9a3 Binary files /dev/null and b/__pycache__/healthcheck.cpython-313.pyc differ diff --git a/__pycache__/tokenext.cpython-313.pyc b/__pycache__/tokenext.cpython-313.pyc new file mode 100644 index 0000000..1b7f171 Binary files /dev/null and b/__pycache__/tokenext.cpython-313.pyc differ 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