# Admin Tools

Reachable from `index.php`'s **Admin** navbar dropdown, and directly at the paths below.

## ⚠️ Security status: no authentication

None of these pages currently check who's accessing them. Each one has a visible warning banner as a reminder. This was a deliberate choice to get the tools built and the deployment pipeline verified first — **auth needs to be added before this app sits on a publicly reachable URL**, since:

- `config-check.php` reveals absolute server file paths, PHP config, and whether shell execution works — useful recon for an attacker.
- `test-quote.php` and `docs-viewer.php` are lower risk individually, but shouldn't be open by default either.

**Recommended before going live:** a lightweight session-based password gate in front of `admin/` (a single shared secret is enough for a personal tool — no need for the full multi-group `user_groups` auth pattern from the larger project ecosystem unless this tool grows beyond single-user use). In the meantime, restrict access via server config (e.g. an IP allowlist in your host's control panel, or HTTP Basic Auth at the web server level) if this is deployed anywhere reachable from outside your own network.

## `admin/config-check.php` — Validate Config

Automates the exact manual SSH checks used during initial deployment:

| Check | What it verifies |
|---|---|
| PHP version | Running PHP ≥ 7.4 |
| shell_exec enabled | Not present in `disable_functions` |
| shell_exec actually runs | A probe command executes and returns the expected output (catches cases where it's enabled but silently broken) |
| max_execution_time reasonable | Long enough for multi-ticker requests (flags anything under 30s) |
| Python interpreter found | Resolves the same candidate list `api.php` uses |
| Using project .venv (not a fallback) | Confirms it's not silently falling back to system/Homebrew Python |
| yfinance importable | Runs `import yfinance; print(yfinance.__version__)` in the resolved interpreter |
| Outbound network to Yahoo Finance | Attempts a real `yf.Ticker('AAPL').history()` call |
| stocks.json exists / writable | Watchlist persistence will work |
| Project directory writable | `stocks.json` can be created if it doesn't exist yet |
| scraper.py present | Sanity check the deploy included every file |

Run this after any deploy, server migration, or Python/venv change — it catches the exact class of problem (wrong Python picked up silently) that was hardest to debug manually.

## `admin/test-quote.php` — Manual API Test

A thin form over `api.php` itself: pick `search` or `load_json`, enter tickers (for `search`), and see the exact raw JSON response with HTTP status and response time. Useful for:

- Confirming a new/index symbol resolves correctly without going through the full UI.
- Checking response shape after a `scraper.py` change.
- Quick timing checks if requests start feeling slow.

## `admin/docs-viewer.php` — Doc Viewer

Lists every `.md` file in the project root and `docs/`, and renders the selected one client-side via `marked.js`. Discovery uses `glob()` server-side; the `?file=` parameter is matched back against that discovered list by basename only — it's never used to build a filesystem path directly, so there's no path-traversal risk even without auth in front of it yet.
