update stuff, add fallbacks, more order to the api
This commit is contained in:
parent
e777ed476d
commit
ad9fab907c
BIN
__pycache__/healthcheck.cpython-313.pyc
Normal file
BIN
__pycache__/healthcheck.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/tokenext.cpython-313.pyc
Normal file
BIN
__pycache__/tokenext.cpython-313.pyc
Normal file
Binary file not shown.
49
berretin.py
49
berretin.py
@ -8,6 +8,7 @@ from urllib.parse import urljoin
|
|||||||
import tokenext
|
import tokenext
|
||||||
from healthcheck import ensure_config_exists
|
from healthcheck import ensure_config_exists
|
||||||
|
|
||||||
|
# load config
|
||||||
config = ensure_config_exists()
|
config = ensure_config_exists()
|
||||||
server_cfg = config["server"]
|
server_cfg = config["server"]
|
||||||
|
|
||||||
@ -16,6 +17,13 @@ PASSWORD = server_cfg.get("password", "").strip()
|
|||||||
CACHE_DIR = server_cfg.get("cache_dir", "cache")
|
CACHE_DIR = server_cfg.get("cache_dir", "cache")
|
||||||
BASE_API = server_cfg.get("base_api", "https://ws1.smn.gob.ar")
|
BASE_API = server_cfg.get("base_api", "https://ws1.smn.gob.ar")
|
||||||
LOG_FILE = server_cfg.get("log_file", "").strip()
|
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"
|
SMN_TOKEN_FILE = "token"
|
||||||
CACHE_TTL = timedelta(minutes=60)
|
CACHE_TTL = timedelta(minutes=60)
|
||||||
AUTH_ENABLED = PASSWORD != ""
|
AUTH_ENABLED = PASSWORD != ""
|
||||||
@ -28,6 +36,7 @@ def log(msg: str):
|
|||||||
f.write(f"[{datetime.now().isoformat()}] {msg}\n")
|
f.write(f"[{datetime.now().isoformat()}] {msg}\n")
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
|
# cache handling
|
||||||
def get_cache_filename(url: str) -> str:
|
def get_cache_filename(url: str) -> str:
|
||||||
h = hashlib.sha256(url.encode()).hexdigest()
|
h = hashlib.sha256(url.encode()).hexdigest()
|
||||||
return os.path.join(CACHE_DIR, f"{h}.json")
|
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:
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
# token handling
|
||||||
def load_smn_token():
|
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:
|
with open(SMN_TOKEN_FILE, "r") as f:
|
||||||
return f.read().strip()
|
return f.read().strip()
|
||||||
|
|
||||||
@ -69,6 +84,7 @@ def check_access_token():
|
|||||||
header_token = request.headers.get("Authorization", "").strip()
|
header_token = request.headers.get("Authorization", "").strip()
|
||||||
return header_token == PASSWORD
|
return header_token == PASSWORD
|
||||||
|
|
||||||
|
# upstream the request
|
||||||
def fetch_from_smn(url: str, retry: bool = True):
|
def fetch_from_smn(url: str, retry: bool = True):
|
||||||
token = load_smn_token()
|
token = load_smn_token()
|
||||||
headers = {
|
headers = {
|
||||||
@ -89,7 +105,8 @@ def fetch_from_smn(url: str, retry: bool = True):
|
|||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
@app.route("/smn/<path:subpath>")
|
# the proxy stuff
|
||||||
|
@app.route(f"{BASE_PATH}/<path:subpath>")
|
||||||
def smn_proxy(subpath):
|
def smn_proxy(subpath):
|
||||||
if not check_access_token():
|
if not check_access_token():
|
||||||
return jsonify({"error": "Unauthorized"}), 401
|
return jsonify({"error": "Unauthorized"}), 401
|
||||||
@ -121,7 +138,37 @@ def smn_proxy(subpath):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return Response("Invalid JSON from SMN", status=502)
|
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__":
|
if __name__ == "__main__":
|
||||||
os.makedirs(CACHE_DIR, exist_ok=True)
|
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] Server starting on port {PORT}")
|
||||||
|
log(f"[STARTUP] Base path set to '{BASE_PATH}/<path>'")
|
||||||
app.run(host="0.0.0.0", port=PORT)
|
app.run(host="0.0.0.0", port=PORT)
|
||||||
|
|||||||
30
cache/47172bb6d3ba5ce5b090e419551d930d7d438dc6256e8ed03c5832b6bc7d8fa3.json
vendored
Normal file
30
cache/47172bb6d3ba5ce5b090e419551d930d7d438dc6256e8ed03c5832b6bc7d8fa3.json
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,12 +9,14 @@ DEFAULT_CONFIG = {
|
|||||||
"password": "debug",
|
"password": "debug",
|
||||||
"cache_dir": "cache",
|
"cache_dir": "cache",
|
||||||
"base_api": "https://ws1.smn.gob.ar",
|
"base_api": "https://ws1.smn.gob.ar",
|
||||||
"log_file": ""
|
"log_file": "",
|
||||||
|
"base_path": "/smn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def ensure_config_exists():
|
def ensure_config_exists():
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
|
|
||||||
if not os.path.exists(CONFIG_FILE):
|
if not os.path.exists(CONFIG_FILE):
|
||||||
print("[CONFIG] config.ini not found - creating with default values")
|
print("[CONFIG] config.ini not found - creating with default values")
|
||||||
config.read_dict(DEFAULT_CONFIG)
|
config.read_dict(DEFAULT_CONFIG)
|
||||||
@ -36,4 +38,5 @@ def ensure_config_exists():
|
|||||||
with open(CONFIG_FILE, "w") as f:
|
with open(CONFIG_FILE, "w") as f:
|
||||||
config.write(f)
|
config.write(f)
|
||||||
print("[CONFIG] Missing keys added to config.ini")
|
print("[CONFIG] Missing keys added to config.ini")
|
||||||
return config
|
|
||||||
|
return config
|
||||||
Loading…
Reference in New Issue
Block a user