Architecture ============ High-Level Flow --------------- :: +------------------+ HTTPS +------------------+ | Web Browser | <-----------> | Caddy (Host) | | or API Consumer | | 70.88.205.138 | +------------------+ +------------------+ | | reverse proxy v +------------------+ HTTP +------------------+ | CT 400 | <-----------> | Flask Server | | evercycle-api | port 5000 | api_server.py | | 10.1.10.173 | +------------------+ +------------------+ | | requests v +------------------+ HTTPS +------------------+ | Auth cache | | Evercycle Cloud | | auth.properties | | api.evercycle.io | +------------------+ +------------------+ Component Responsibilities -------------------------- **api_server.py (Flask REST Server)** * Exposes HTTP endpoints for health checks, auth, listing, and dynamic API proxying. * Manages token lifecycle (acquire, refresh, expiry check) via a background lock. * Patches ``EvercycleApiClient.call_api`` at runtime for certain endpoints (e.g., ``get-asset-id``) to inject path parameters into the URL. **evercycle_api.py (CLI Client)** * Command-line tool for auth, listing, and calling APIs. * Reads/writes ``auth.properties`` and JSON configs in ``evercycle-api/config/``. **api_client.py (Generic HTTP Client)** * Low-level ``requests.Session`` wrapper. * Supports endpoint discovery, interactive mode, and request/response history. **api_runner.py (Config Generator & Runner)** * Parses markdown API specs to generate JSON configs. * Runs individual or all APIs in sequence. * Auto-injects tokens from ``auth.properties`` into config headers after sign-in. **evercycle_api_runner.py (Interactive TUI)** * Menu-driven interactive tool for calling endpoints. * Prompts for headers, path params, query params, and body fields at runtime. Data Flow for an Authenticated Request -------------------------------------- 1. Client calls ``POST /api/auth`` with Evercycle credentials. 2. ``api_server.py`` forwards the request to ``https://api.evercycle.io/v1/auth/signin``. 3. Tokens (``AccessToken``, ``IdToken``, ``RefreshToken``) are stored in ``auth.properties`` inside the ``evercycle-api`` directory. 4. Subsequent calls to ``/api/`` trigger ``refresh_auth_token()``. 5. If the token is expired, the server attempts a refresh using the Evercycle refresh endpoint; if that fails, it re-authenticates using ``config/auth-signin.json``. 6. The API call is executed by ``EvercycleApiClient.call_api``, which: * Loads the JSON config for the requested API * Substitutes path parameters * Injects auth headers * Dispatches the HTTP request * Returns the JSON response CORS Architecture ----------------- * ``flask_cors.CORS(app)`` is applied globally. * ``ALLOWED_ORIGINS`` restricts actual ``Access-Control-Allow-Origin`` to: * ``http://localhost:3000`` * ``http://127.0.0.1:3000`` * ``https://ui.veraetime.net`` * ``https://*.ngrok.io`` * ``https://*.ngrok-free.app`` * Preflight ``OPTIONS`` requests return permissive headers for any origin to avoid preflight failures, but credentialed requests still require a matching origin on the actual call.