Ultimo refactor, espero
This commit is contained in:
parent
964f6da880
commit
7be72d03e1
89
.env.example
Normal file
89
.env.example
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# Copia este archivo a .env y configura tus ajustes
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Configuración del Servidor
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Puerto en el que escuchará el servidor
|
||||||
|
# Por defecto: 6942
|
||||||
|
PORT=6942
|
||||||
|
|
||||||
|
# Token de autenticación para proteger la API
|
||||||
|
# Si está vacío, la autenticación estará deshabilitada
|
||||||
|
# Los clientes deben enviar este valor en el header "Authorization"
|
||||||
|
PASSWORD=clave_ultra_segura
|
||||||
|
|
||||||
|
# Ruta base de la API (sin barra final)
|
||||||
|
# Todas las rutas de la API comenzarán con este prefijo
|
||||||
|
# Ejemplo: /smn/v1/weather/location/10821
|
||||||
|
BASE_PATH=/smn
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Configuración de la API del SMN
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# URL base de la API del Servicio Meteorológico Nacional
|
||||||
|
# Esta es la URL upstream a la que se reenviarán las peticiones
|
||||||
|
# Se declara por un motivo de mantener el proyecto en un sistema orientado a objetos, pero no debe cambiarse
|
||||||
|
BASE_API=https://ws1.smn.gob.ar
|
||||||
|
|
||||||
|
# URL del sitio web del SMN para extraer el token de autenticación
|
||||||
|
# Se usa con Selenium para obtener el token JWT automáticamente
|
||||||
|
# Se declara por un motivo de mantener el proyecto en un sistema orientado a objetos, pero no debe cambiarse
|
||||||
|
SMN_URL=https://www.smn.gob.ar/
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Configuración de Caché
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Directorio donde se almacenarán los archivos de caché
|
||||||
|
# Se creará automáticamente si no existe
|
||||||
|
CACHE_DIR=cache
|
||||||
|
|
||||||
|
# Tiempo de vida del caché en minutos
|
||||||
|
# Las respuestas cacheadas se considerarán válidas durante este tiempo
|
||||||
|
# Después de este período, se realizará una nueva petición al SMN
|
||||||
|
CACHE_TTL_MINUTES=60
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Configuración del Token
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Archivo donde se guardará el token JWT del SMN
|
||||||
|
# Este token se renueva automáticamente cuando expira
|
||||||
|
SMN_TOKEN_FILE=token
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Configuración de Logging
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Ruta al archivo de logs (opcional)
|
||||||
|
# Si está vacío, los logs solo se mostrarán en la consola
|
||||||
|
# Ejemplo: LOG_FILE=logs/opensmn.log
|
||||||
|
LOG_FILE=
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Configuración de Selenium
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Ejecutar Selenium en modo headless (sin interfaz gráfica)
|
||||||
|
# Valores: true o false
|
||||||
|
# Recomendado: true para servidores sin entorno gráfico, realmente solo se utilizaria false en desarollo
|
||||||
|
SELENIUM_HEADLESS=true
|
||||||
|
|
||||||
|
# Segundos de espera para que la página del SMN cargue completamente
|
||||||
|
# Aumenta este valor si tenes una conexión lenta
|
||||||
|
SELENIUM_WAIT_SECONDS=8
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Configuración de Uvicorn (Servidor ASGI)
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Cantidad de workers para el servidor
|
||||||
|
# Se recomienda mantener en 1 para evitar problemas de sincronización, por ahora no esta preparado para escalar verticalmente
|
||||||
|
WORKERS=1
|
||||||
|
|
||||||
|
# Dirección IP en la que escuchará el servidor
|
||||||
|
# 0.0.0.0 = escucha en todas las interfaces de red
|
||||||
|
# 127.0.0.1 = solo escucha en localhost (conexiones locales)
|
||||||
|
HOST=0.0.0.0
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,6 +1,4 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
cache
|
cache
|
||||||
config.ini
|
|
||||||
token
|
token
|
||||||
opensmn.egg-info
|
.env
|
||||||
build
|
|
||||||
103
README.md
103
README.md
@ -1,35 +1,78 @@
|
|||||||
# OpenSMN
|
# OpenSMN
|
||||||
|
|
||||||
OpenSMN es una API opensource que actúa como proxy del Servicio Meteorológico Nacional (SMN) de Argentina, ofreciendo una forma sencilla, y privada de acceder a sus datos públicos.
|
OpenSMN es una API opensource que actúa como proxy del Servicio Meteorológico Nacional (SMN) de Argentina, ofreciendo una forma sencilla y privada de acceder a sus datos públicos.
|
||||||
|
|
||||||
## Setup
|
## Requisitos
|
||||||
|
|
||||||
Clona la repo
|
- Python 3.7+
|
||||||
|
|
||||||
~~~
|
|
||||||
git clone https://github.com/nixietab/OpenSMN
|
|
||||||
|
|
||||||
cd OpenSMN
|
|
||||||
|
|
||||||
~~~
|
|
||||||
|
|
||||||
Instala los requerimientos
|
|
||||||
|
|
||||||
~~~
|
|
||||||
pip install -r requirements.txt
|
|
||||||
~~~
|
|
||||||
|
|
||||||
y ejecuta el servicio!
|
|
||||||
|
|
||||||
~~~
|
|
||||||
python3 main.py
|
|
||||||
~~~
|
|
||||||
|
|
||||||
Despues de la primera ejecucion se va a crear un config.ini
|
|
||||||
|
|
||||||
Una vez se tenga configurado y corriendo es altamente recomendado correrlo en un reverse proxy como nginx
|
|
||||||
|
|
||||||
## Requierimientos
|
|
||||||
|
|
||||||
- Python 3
|
|
||||||
- ChromeDriver
|
- ChromeDriver
|
||||||
|
|
||||||
|
## Instalación
|
||||||
|
|
||||||
|
1. Clona el repositorio:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/nixietab/OpenSMN
|
||||||
|
cd OpenSMN
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Instala las dependencias:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Configura las variables de entorno:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Edita el archivo `.env` con tu configuración, puedes ver todas las variables en el archivo `.env.example`.
|
||||||
|
|
||||||
|
## Uso
|
||||||
|
|
||||||
|
### Desarrollo
|
||||||
|
|
||||||
|
Para ejecutar el servidor en modo desarrollo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn server:app --reload --port 6942
|
||||||
|
```
|
||||||
|
|
||||||
|
### Producción
|
||||||
|
|
||||||
|
Para ejecutar el servidor en producción, usa el script `start.sh`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
El script carga automáticamente las variables de entorno desde `.env` y ejecuta la api.
|
||||||
|
|
||||||
|
## Autenticación
|
||||||
|
|
||||||
|
Si configuras un `PASSWORD` en tu `.env`, todas las peticiones deben incluir el header:
|
||||||
|
|
||||||
|
```
|
||||||
|
Authorization: clave_ultra_segura
|
||||||
|
```
|
||||||
|
|
||||||
|
## Despliegue con Reverse Proxy
|
||||||
|
|
||||||
|
Para producción, es altamente recomendado usar un reverse proxy como nginx:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /smn/ {
|
||||||
|
proxy_pass http://localhost:6942/smn/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Licencia
|
||||||
|
|
||||||
|
El proyecto de openSMN es de código abierto y está licenciado bajo la licencia MIT. no obstante, interacciona con el Servicio Meteorológico Nacional (SMN) de Argentina, servicio de codigo privativo.
|
||||||
|
|
||||||
|
No hay asociacion con este proyecto con el SMN de ninguna manera
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
import os
|
|
||||||
import configparser
|
|
||||||
|
|
||||||
CONFIG_FILE = "config.ini"
|
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
|
||||||
"server": {
|
|
||||||
"port": "6942",
|
|
||||||
"password": "debug",
|
|
||||||
"cache_dir": "cache",
|
|
||||||
"base_api": "https://ws1.smn.gob.ar",
|
|
||||||
"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)
|
|
||||||
with open(CONFIG_FILE, "w") as f:
|
|
||||||
config.write(f)
|
|
||||||
else:
|
|
||||||
config.read(CONFIG_FILE)
|
|
||||||
changed = False
|
|
||||||
for section, values in DEFAULT_CONFIG.items():
|
|
||||||
if section not in config:
|
|
||||||
config[section] = values
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
for key, val in values.items():
|
|
||||||
if key not in config[section]:
|
|
||||||
config[section][key] = val
|
|
||||||
changed = True
|
|
||||||
if changed:
|
|
||||||
with open(CONFIG_FILE, "w") as f:
|
|
||||||
config.write(f)
|
|
||||||
print("[CONFIG] Missing keys added to config.ini")
|
|
||||||
|
|
||||||
return config
|
|
||||||
103
main.py
103
main.py
@ -1,103 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
from configparser import ConfigParser
|
|
||||||
from healthcheck import ensure_config_exists
|
|
||||||
|
|
||||||
|
|
||||||
def configparser_to_dict(cp: ConfigParser) -> dict:
|
|
||||||
d = {}
|
|
||||||
for section in cp.sections():
|
|
||||||
d[section] = {}
|
|
||||||
for key, val in cp.items(section):
|
|
||||||
d[section][key] = val
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def load_config(config_path=None):
|
|
||||||
if config_path:
|
|
||||||
if not os.path.exists(config_path):
|
|
||||||
print(f"[ERROR] Config file not found: {config_path}")
|
|
||||||
sys.exit(1)
|
|
||||||
try:
|
|
||||||
with open(config_path, "r", encoding="utf-8") as f:
|
|
||||||
data = json.load(f)
|
|
||||||
return data
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
cp = ConfigParser()
|
|
||||||
cp.read(config_path)
|
|
||||||
return configparser_to_dict(cp)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[ERROR] Failed to read config file: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
cfg = ensure_config_exists()
|
|
||||||
if isinstance(cfg, ConfigParser):
|
|
||||||
return configparser_to_dict(cfg)
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
prog="OpenSMN",
|
|
||||||
description="Run the SMN API with custom configuration."
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-c", "--config",
|
|
||||||
type=str,
|
|
||||||
help="Path to a custom configuration file (INI or JSON)."
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-p", "--port",
|
|
||||||
type=int,
|
|
||||||
help="Override the server port defined in config."
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--no-auth",
|
|
||||||
action="store_true",
|
|
||||||
help="Disable authentication even if password is defined in config."
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--show-config",
|
|
||||||
action="store_true",
|
|
||||||
help="Print the loaded configuration and exit."
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--base-path",
|
|
||||||
type=str,
|
|
||||||
help="Override the base API path."
|
|
||||||
)
|
|
||||||
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
args = parse_args()
|
|
||||||
config = load_config(args.config)
|
|
||||||
server_cfg = config.get("server", {})
|
|
||||||
|
|
||||||
# apply cli overrides
|
|
||||||
if args.port:
|
|
||||||
server_cfg["port"] = str(args.port)
|
|
||||||
if args.base_path:
|
|
||||||
server_cfg["base_path"] = args.base_path
|
|
||||||
if args.no_auth:
|
|
||||||
server_cfg["password"] = ""
|
|
||||||
|
|
||||||
if args.show_config:
|
|
||||||
print(json.dumps(config, indent=2, ensure_ascii=False))
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# run the server
|
|
||||||
import server
|
|
||||||
server.run_from_cli(config)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -1,3 +1,6 @@
|
|||||||
requests
|
requests
|
||||||
selenium
|
selenium
|
||||||
flask
|
flask
|
||||||
|
uvicorn[standard]
|
||||||
|
python-dotenv
|
||||||
|
asgiref
|
||||||
|
|||||||
83
server.py
83
server.py
@ -7,21 +7,30 @@ import sys
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from flask import Flask, jsonify, Response, request
|
from flask import Flask, jsonify, Response, request
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
from dotenv import load_dotenv
|
||||||
import tokenext
|
import tokenext
|
||||||
from healthcheck import ensure_config_exists
|
|
||||||
|
# Load environment variables
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# Global Data
|
# Configuration from environment variables
|
||||||
PORT = 6942
|
PORT = int(os.getenv("PORT", "6942"))
|
||||||
PASSWORD = ""
|
PASSWORD = os.getenv("PASSWORD", "").strip()
|
||||||
CACHE_DIR = "cache"
|
CACHE_DIR = os.getenv("CACHE_DIR", "cache")
|
||||||
BASE_API = "https://ws1.smn.gob.ar"
|
BASE_API = os.getenv("BASE_API", "https://ws1.smn.gob.ar")
|
||||||
LOG_FILE = ""
|
LOG_FILE = os.getenv("LOG_FILE", "").strip()
|
||||||
BASE_PATH = "/smn"
|
BASE_PATH = os.getenv("BASE_PATH", "/smn").strip()
|
||||||
AUTH_ENABLED = False
|
AUTH_ENABLED = PASSWORD != ""
|
||||||
CACHE_TTL = timedelta(minutes=60)
|
CACHE_TTL = timedelta(minutes=int(os.getenv("CACHE_TTL_MINUTES", "60")))
|
||||||
SMN_TOKEN_FILE = "token"
|
SMN_TOKEN_FILE = os.getenv("SMN_TOKEN_FILE", "token")
|
||||||
|
|
||||||
|
# Ensure BASE_PATH is properly formatted
|
||||||
|
if not BASE_PATH.startswith("/"):
|
||||||
|
BASE_PATH = "/" + BASE_PATH
|
||||||
|
if BASE_PATH.endswith("/"):
|
||||||
|
BASE_PATH = BASE_PATH[:-1]
|
||||||
|
|
||||||
def log(msg: str):
|
def log(msg: str):
|
||||||
if LOG_FILE:
|
if LOG_FILE:
|
||||||
@ -63,7 +72,9 @@ def load_smn_token():
|
|||||||
|
|
||||||
def refresh_smn_token():
|
def refresh_smn_token():
|
||||||
log("[TOKEN] Refreshing SMN token...")
|
log("[TOKEN] Refreshing SMN token...")
|
||||||
ok = tokenext.refresh_token(output_file=SMN_TOKEN_FILE, headless=True, wait_seconds=8)
|
headless = os.getenv("SELENIUM_HEADLESS", "true").lower() == "true"
|
||||||
|
wait_seconds = int(os.getenv("SELENIUM_WAIT_SECONDS", "8"))
|
||||||
|
ok = tokenext.refresh_token(output_file=SMN_TOKEN_FILE, headless=headless, wait_seconds=wait_seconds)
|
||||||
if ok:
|
if ok:
|
||||||
log("[TOKEN] Token refreshed successfully.")
|
log("[TOKEN] Token refreshed successfully.")
|
||||||
else:
|
else:
|
||||||
@ -157,52 +168,16 @@ def handle_sigint(signum, frame):
|
|||||||
|
|
||||||
signal.signal(signal.SIGINT, handle_sigint)
|
signal.signal(signal.SIGINT, handle_sigint)
|
||||||
|
|
||||||
|
# Initialize on startup
|
||||||
def run_from_cli(config=None):
|
|
||||||
global PORT, PASSWORD, CACHE_DIR, BASE_API, LOG_FILE, BASE_PATH, AUTH_ENABLED
|
|
||||||
|
|
||||||
if config is None:
|
|
||||||
config = ensure_config_exists()
|
|
||||||
|
|
||||||
server_cfg = config.get("server", {})
|
|
||||||
PORT = int(server_cfg.get("port", 6942))
|
|
||||||
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]
|
|
||||||
|
|
||||||
AUTH_ENABLED = PASSWORD != ""
|
|
||||||
|
|
||||||
os.makedirs(CACHE_DIR, exist_ok=True)
|
os.makedirs(CACHE_DIR, exist_ok=True)
|
||||||
if not os.path.exists(SMN_TOKEN_FILE):
|
if not os.path.exists(SMN_TOKEN_FILE):
|
||||||
log("[STARTUP] No token file found, generating a new one.")
|
log("[STARTUP] No token file found, generating a new one.")
|
||||||
refresh_smn_token()
|
refresh_smn_token()
|
||||||
|
|
||||||
log(f"[STARTUP] Server starting on port {PORT}")
|
log(f"[STARTUP] Server configured on port {PORT}")
|
||||||
log(f"[STARTUP] Base path set to '{BASE_PATH}/<path>'")
|
log(f"[STARTUP] Base path set to '{BASE_PATH}/<path>'")
|
||||||
|
log(f"[STARTUP] Authentication: {'Enabled' if AUTH_ENABLED else 'Disabled'}")
|
||||||
|
|
||||||
try:
|
# Wrap Flask app with ASGI adapter for uvicorn
|
||||||
app.run(host="0.0.0.0", port=PORT)
|
from asgiref.wsgi import WsgiToAsgi
|
||||||
except KeyboardInterrupt:
|
app = WsgiToAsgi(app)
|
||||||
log("[SHUTDOWN] Interrupted. server stopped cleanly.")
|
|
||||||
except Exception as e:
|
|
||||||
log(f"[ERROR] Unhandled exception: {e}")
|
|
||||||
finally:
|
|
||||||
log("[SHUTDOWN] Goodbye.")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import signal
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def handle_sigint(signum, frame):
|
|
||||||
log("[SHUTDOWN] shutting down gracefully...")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, handle_sigint)
|
|
||||||
run_from_cli()
|
|
||||||
36
setup.py
36
setup.py
@ -1,36 +0,0 @@
|
|||||||
from setuptools import setup
|
|
||||||
|
|
||||||
with open("README.md", "r", encoding="utf-8") as f:
|
|
||||||
long_description = f.read()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="opensmn",
|
|
||||||
version="1.0.0",
|
|
||||||
author="nix",
|
|
||||||
description="OpenSMN: Proxy API for Argentina's Servicio Meteorológico Nacional (SMN)",
|
|
||||||
url="https://github.com/nixietab/OpenSMN",
|
|
||||||
license="MIT",
|
|
||||||
py_modules=[
|
|
||||||
"main",
|
|
||||||
"server",
|
|
||||||
"healthcheck",
|
|
||||||
"tokenext",
|
|
||||||
],
|
|
||||||
install_requires=[
|
|
||||||
"flask",
|
|
||||||
"requests",
|
|
||||||
"selenium",
|
|
||||||
],
|
|
||||||
entry_points={
|
|
||||||
"console_scripts": [
|
|
||||||
"opensmn=main:main",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
classifiers=[
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"License :: OSI Approved :: MIT License",
|
|
||||||
"Operating System :: OS Independent",
|
|
||||||
"Framework :: Flask",
|
|
||||||
],
|
|
||||||
python_requires=">=3.8",
|
|
||||||
)
|
|
||||||
32
start.sh
Executable file
32
start.sh
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Production startup script for OpenSMN
|
||||||
|
|
||||||
|
# Load environment variables from .env file
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
echo "Error: .env file not found!"
|
||||||
|
echo "Please copy .env.example to .env and configure your settings."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Export environment variables
|
||||||
|
set -a
|
||||||
|
source .env
|
||||||
|
set +a
|
||||||
|
|
||||||
|
# Default values if not set in .env
|
||||||
|
PORT=${PORT:-6942}
|
||||||
|
WORKERS=${WORKERS:-1} # This can change on the future, but for now, 1 is enough
|
||||||
|
HOST=${HOST:-0.0.0.0}
|
||||||
|
|
||||||
|
echo "Starting OpenSMN..."
|
||||||
|
echo "Host: $HOST"
|
||||||
|
echo "Port: $PORT"
|
||||||
|
echo "Workers: $WORKERS"
|
||||||
|
|
||||||
|
# Start uvicorn with production settings
|
||||||
|
exec uvicorn server:app \
|
||||||
|
--host "$HOST" \
|
||||||
|
--port "$PORT" \
|
||||||
|
--workers "$WORKERS" \
|
||||||
|
--log-level info \
|
||||||
|
--access-log
|
||||||
@ -1,4 +1,4 @@
|
|||||||
# tokenext.py
|
import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@ -8,7 +8,7 @@ from selenium.webdriver.common.by import By
|
|||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
DEFAULT_URL = "https://www.smn.gob.ar/"
|
DEFAULT_URL = os.getenv("SMN_URL", "https://www.smn.gob.ar/")
|
||||||
DEFAULT_OUTPUT_FILE = "token"
|
DEFAULT_OUTPUT_FILE = "token"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user