Skip to main content

server-api

The optional FastAPI server (--server): every REST endpoint, HTTP Basic Auth on /admin and the mutating endpoints, log linking (/add-log, /log), auto-update behaviour, and the admin page bundle. Use when adding or changing endpoints, debugging uploads from the listener, or touching server.py / templates/admin.html.

Source facts

Repository
MarketSquare/robotframework-dashboard
Last source activity
October 18, 2026 at 20:49
Detected SKILL.md language
English
Stars
89
Forks
21

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
server-api
description
The optional FastAPI server (--server): every REST endpoint, HTTP Basic Auth on /admin and the mutating endpoints, log linking (/add-log, /log), auto-update behaviour, and the admin page bundle. Use when adding or changing endpoints, debugging uploads from the listener, or touching server.py / templates/admin.html.
# Server API ## Overview The server is an optional FastAPI application started with `robotdashboard --server` (or `-s host:port:user:pass`). It wraps a persistent `RobotDashboard` instance and exposes REST endpoints for managing outputs and serving the dashboard. Implementation is split across four modules: | File | Holds | |---|---| | `server.py` | `ApiServer`: app + auth setup, the HTML serving routes (`/`, `/admin`, `/log`, `/refresh-dashboard`, catch-all), `run()` | | `server_models.py` | The Pydantic models (`AddOutput`, `RemoveOutputs`, `AddLog`, …), their `*_model_config` OpenAPI examples and `model_examples()` | | `server_routes_outputs.py` | `register_output_routes(server, authenticate)` — `/get-outputs`, `/add-outputs`, `/add-output-file`, `/remove-outputs` | | `server_routes_logs.py` | `register_log_routes(server, authenticate)` — `/get-logs`, `/add-log`, `/add-log-file`, `/remove-log` | The route modules are plain functions that take the `ApiServer` instance (they read `server.robotdashboard`, `server.log_dir`, `server.no_autoupdate`) and the `authenticate` dependency built in `_setup_routes`. A new endpoint goes in the matching route module; a new model goes in `server_models.py`. `server.py` re-exports the models, so `from robotframework_dashboard.server import ResponseMessage` keeps working. --- ## Starting the Server ```bash robotdashboard --server # localhost:8000, no auth robotdashboard -s 0.0.0.0:9000:admin:secret # custom host:port:user:pass ``` Served with **uvicorn** via `fastapi_offline.FastAPIOffline`. --- ## Authentication - HTTP Basic Auth is implemented using `HTTPBasic` + `secrets.compare_digest` (constant-time comparison) - **`/admin` and all mutation endpoints require credentials** when `server_user` and `server_pass` are set - Read-only endpoints (`/`, `/get-outputs`, `/get-logs`, `/log`, `/{full_path}`) are **unauthenticated** - If no credentials are configured, all endpoints are open (the `authenticate` dependency returns `"anonymous"`) --- ## All Endpoints | Method | Path | Auth | Description | |---|---|---|---| | `GET` | `/` | None | Serves `robot_dashboard.html` from the working directory | | `GET` | `/admin` | Basic (if configured) | Serves the admin page HTML (generated from `templates/admin.html`) | | `POST` | `/refresh-dashboard` | Basic (if configured) | Manually triggers `robotdashboard.create_dashboard()` without adding data | | `GET` | `/get-outputs` | None | Returns list of `{run_start, name, alias, tags}` for all stored runs | | `POST` | `/add-outputs` | Basic (if configured) | Add output(s) from a path, raw XML string, or folder. See body fields below. | | `POST` | `/add-output-file` | Basic (if configured) | Multipart upload of `output.xml` (or `.gz`/`.gzip`). Form fields: `tags` (colon-separated), `version`, `custom_filters`, `log_url`. | | `DELETE` | `/remove-outputs` | Basic (if configured) | Remove runs by various selectors. See body fields below. | | `GET` | `/get-logs` | None | Lists filenames in `robot_logs/` | | `POST` | `/add-log` | Basic (if configured) | Saves HTML log content to `robot_logs/<log_name>` and links it to the matching run in the DB | | `POST` | `/add-log-file` | Basic (if configured) | Same as `/add-log` but via multipart file upload (supports `.gz`/`.gzip`). Filenames containing `report` are saved as-is with no DB matching — see Report Handling below. | | `DELETE` | `/remove-log` | Basic (if configured) | Removes one log by `log_name`, or all logs with `all: True` | | `GET` | `/log` | None | Serves a log HTML file by `?path=` query param, only if it is inside `log_dir` or is the log of a stored run path (`server_paths.log_path_from_run_path`); otherwise 404 with the path HTML-escaped. Stores parent dir for subsequent resource requests | | `GET` | `/{full_path:path}` | None | Catch-all: serves static resources (screenshots, etc.) relative to the last served log's directory. Path-traversal protected (`server_paths.is_within`) and limited to log resource types (`server_paths.is_log_resource`: images, videos, HTML). | **Path safety (#374):** every file name taken from a request (`log_name`, upload `filename`, `output_alias`) goes through `server_paths.safe_file_name`, which rejects anything with a folder part. Output uploads are written to a `TemporaryDirectory`, never the working directory. Keep both rules for any new endpoint that touches files. --- ## Request Body Fields ### `POST /add-outputs` Exactly one input source must be provided (mutually exclusive): | Field | Type | Description | |---|---|---| | `output_path` | `str` | Absolute path to an `output.xml` file | | `output_data` | `str` | Raw XML content (written to a temp file) | | `output_folder_path` | `str` | Folder scanned recursively for `*output*.xml` files | | `output_tags` | `List[str]` | Optional tags to attach to all added runs | | `output_alias` | `str` | Optional alias override | | `output_version` | `str` | Optional project version string | | `output_log_url` | `str` | Optional externally-hosted log URL, mirrors CLI `--logurl`. Requires a `{run_alias}` placeholder when `output_folder_path` is used (potentially multiple runs); rejected without one. `/add-output-file`'s multipart equivalent is the unprefixed `log_url` form field (no placeholder requirement there since it always processes a single file). | ### `DELETE /remove-outputs` Any combination of the following: | Field | Type | Description | |---|---|---| | `run_starts` | `List[str]` | Run start timestamps (exact match) | | `indexes` | `List[str]` | Positional indexes; supports negatives and range syntax (e.g. `"0:5"`) | | `aliases` | `List[str]` | Run aliases | | `tags` | `List[str]` | Run tags | | `limit` | `int` | Keep only the N most recent runs, remove the rest | | `all` | `bool` | Remove all runs | --- ## Response Model All mutation endpoints return `ResponseMessage`: ```json { "success": "1", "message": "...", "console": "..." } ``` - `success`: `"1"` on success, `"0"` on error - `message`: human-readable summary - `console`: raw stdout-style log from the `RobotDashboard` processor --- ## Auto-Update Behavior After any mutation (add/remove outputs, add/remove logs): - If `no_autoupdate` is `False` (default): `robotdashboard.create_dashboard()` is called automatically to regenerate the HTML - If `no_autoupdate` is `True` (set via `--no-autoupdate` flag): regeneration is skipped; call `POST /refresh-dashboard` manually --- ## Log File Naming Convention Log files linked to runs **must** follow this naming pattern: ``` output-XYZ.xml ←→ log-XYZ.html ``` The suffix `XYZ` must match between the output file and its corresponding log. The server's `/add-log` and `/add-log-file` endpoints enforce this convention when calling `update_output_path()` to link the log to the correct run in the DB. --- ## Report Handling (`/add-log-file`) Reports are not tracked in the database — Robot Framework's own `log.html` already links to `report.html` (top-right corner), so the dashboard just needs the report saved next to its log with a matching name (`log_XYZ.html` ↔ `report_XYZ.html`). `/add-log-file` detects a `report` substring in the uploaded filename and takes a different path than for `log` files: - The file is saved to `robot_logs/` as-is; `update_output_path()` (DB matching) is **not** called — a report filename would never match a stored output/log path anyway. - It checks whether a `log` file exists with the same name (`report` → `log`) in `robot_logs/`. If found: `SUCCESS` with a console note that the report is reachable from its log. If not found: still `SUCCESS`, but the console carries a `WARNING` that the report was saved but has no matching log yet (upload order matters — upload the log first, or the report before it just produces a harmless warning). - This intentionally returns `success: "1"` either way; before this behavior, a `report*.html` upload always returned `success: "0"` with a misleading DB-matching error even though the file was saved correctly (#308). ## Server State The `ApiServer` instance holds: | Attribute | Description | |---|---| | `self.robotdashboard` | The `RobotDashboard` instance (set via `set_robotdashboard()`) | | `self.server_user` / `self.server_pass` | Basic auth credentials (empty = no auth) | | `self.no_autoupdate` | Skip HTML regeneration after mutations | | `self.log_dir` | Directory for log HTML files (default: `robot_logs/`) | | `self.latest_log_dir` | Parent dir of the last served log file (used by catch-all route) |
View on GitHub