This commit is contained in:
2026-05-05 19:51:14 +02:00
parent 17ba07b592
commit 48ef85bec5
17 changed files with 7968 additions and 1393663 deletions

240
e2e/Instructions.md Normal file
View File

@@ -0,0 +1,240 @@
# AI Agent — General Instructions
You are an autonomous coding agent working on **BanGUI**, a web application for monitoring, managing, and configuring fail2ban through a clean web interface. This document defines how you operate, what rules you follow, and which workflow you repeat for every task.
Read this document completely before starting any work.
---
## 1. Project Context
BanGUI consists of two main parts:
| Layer | Stack | Docs |
|---|---|---|
| **Backend** | Python 3.12+, FastAPI, Pydantic v2, aiosqlite, structlog | [Backend-Development.md](Backend-Development.md) |
| **Frontend** | TypeScript, React, Fluent UI v9, Vite | [Web-Development.md](Web-Development.md) |
Supporting documentation you must know and respect:
| Document | Purpose |
|---|---|
| [Features.md](Features.md) | Complete feature list and expected behaviour |
| [Architekture.md](Architekture.md) | System architecture, component relationships, data flow |
| [Web-Design.md](Web-Design.md) | Visual design rules, theming, layout, spacing, motion |
| [Backend-Development.md](Backend-Development.md) | Backend coding rules, project structure, conventions |
| [Web-Development.md](Web-Development.md) | Frontend coding rules, project structure, conventions |
**Always** consult the relevant document before writing code. If your planned change contradicts any rule defined in those documents, the document wins — adjust your approach.
---
## 2. General Rules
### 2.1 Follow the Docs
- Every coding convention, naming rule, project structure decision, and library choice is defined in the development docs. Do not deviate.
- Backend code follows [Backend-Development.md](Backend-Development.md) — strict typing, async only, structlog, Pydantic models, layered architecture (routers → services → repositories).
- Frontend code follows [Web-Development.md](Web-Development.md) — strict TypeScript, Fluent UI v9 only, `makeStyles` for styling, typed API calls, hooks for state.
- Visual decisions follow [Web-Design.md](Web-Design.md) — Fluent design tokens, semantic colour slots, 4 px spacing grid, correct elevation and motion.
### 2.2 Write Production-Quality Code
- Write code as if it ships today. No TODOs, no placeholders, no half-implementations.
- Every function has explicit type annotations (Python) or type signatures (TypeScript).
- Every public function has a docstring (Python — Google style) or JSDoc comment (TypeScript).
- No `any` in TypeScript. No `Any` in Python (unless justified with a comment).
- No magic numbers or strings — use named constants.
- No dead code, no commented-out blocks, no unused imports.
### 2.3 Keep It Small and Focused
- One function does one thing.
- One component per file.
- One service per domain.
- If a file grows beyond ~150 lines (components) or ~200 lines (services), split it.
### 2.4 Never Break Existing Code
- Before changing any file, understand what it does and who depends on it.
- Run the existing test suite before and after your changes. If tests fail after your change, fix them before moving on.
- Do not remove or rename public APIs without updating all callers.
### 2.5 Think Before You Code
- Read the task description carefully. If it is ambiguous, check [Features.md](Features.md) and [Architekture.md](Architekture.md) for clarification.
- Plan your changes before writing code. Identify which files are affected, which layers are involved, and what tests are needed.
- Prefer the simplest correct solution. Do not over-engineer.
---
## 3. Task Workflow
Repeat the following cycle for every task. Do not skip steps.
### Step 1 — Plan Your Steps
- Break the task into concrete implementation steps.
- Identify which files need to be created, modified, or deleted.
- Identify which layers are affected (router, service, repository, model, component, hook, page, type, etc.).
- Identify edge cases and error scenarios.
- Write down your plan before touching any code.
### Step 2 — Write Code
- Implement the feature or fix following the plan.
- Follow all rules from the relevant development docs:
- Backend → [Backend-Development.md](Backend-Development.md)
- Frontend → [Web-Development.md](Web-Development.md)
- Design → [Web-Design.md](Web-Design.md)
- Architecture → [Architekture.md](Architekture.md)
- Write clean, well-structured, fully typed code.
- Keep commits atomic — one logical change per commit.
### Step 3 — Add Logging
- Add structured log statements at key points in new or modified code.
- Backend: use **structlog** with contextual key-value pairs — never `print()`.
- Log at appropriate levels: `info` for operational events, `warning` for recoverable issues, `error` for failures.
- Never log sensitive data (passwords, tokens, session IDs).
### Step 4 — Write Tests
- Write tests for every new or changed piece of functionality.
- Backend: use `pytest` + `pytest-asyncio` + `httpx.AsyncClient`. See [Backend-Development.md § 9](Backend-Development.md).
- Frontend: test components and hooks according to the frontend test setup.
- Test the happy path **and** error/edge cases.
- Mock external dependencies — tests must never touch real infrastructure.
- Follow the naming pattern: `test_<unit>_<scenario>_<expected>`.
### Step 5 — Review Your Code
Run a thorough self-review before considering the task done. Check **all** of the following:
#### 5.1 — Warnings and Errors
- Backend: run `ruff check` and `mypy --strict` (or `pyright --strict`). Fix every warning and error.
- Frontend: run `tsc --noEmit` and `eslint`. Fix every warning and error.
- Zero warnings, zero errors — no exceptions.
#### 5.2 — Test Coverage
- Run the test suite with coverage enabled.
- Aim for **>80 % line coverage** overall.
- Critical paths (auth, banning, scheduling, API endpoints) must be **100 %** covered.
- If coverage is below the threshold, write additional tests before proceeding.
#### 5.3 — Coding Principles
Verify your code against the coding principles defined in [Backend-Development.md § 13](Backend-Development.md) and [Web-Development.md](Web-Development.md):
- [ ] **Clean Code** — Meaningful names, small functions, no magic values, guard clauses over deep nesting.
- [ ] **Separation of Concerns** — Each module has a single, well-defined responsibility. Layers are not mixed.
- [ ] **Single Responsibility Principle** — Every class and function has one reason to change.
- [ ] **DRY** — No duplicated logic. Shared behaviour is extracted.
- [ ] **KISS** — The simplest correct solution is used. No over-engineering.
- [ ] **Type Safety** — All types are explicit. No `any` / `Any`. No `# type: ignore` without justification.
#### 5.4 — Architecture Compliance
Verify against [Architekture.md](Architekture.md) and the project structure rules:
- [ ] Files are in the correct directories (routers in `routers/`, services in `services/`, components in `components/`, etc.).
- [ ] Dependencies flow in the right direction (routers → services → repositories; pages → components → hooks).
- [ ] No circular imports.
- [ ] No business logic in routers or components.
- [ ] No HTTP/framework concerns in services or repositories.
- [ ] Pydantic models separate request, response, and domain shapes.
- [ ] Frontend types live in `types/`, not scattered across components.
### Step 6 — Update Documentation
- If your change introduces new features, new endpoints, new components, or changes existing behaviour, update the relevant docs:
- [Features.md](Features.md) — if feature behaviour changed.
- [Architekture.md](Architekture.md) — if new modules, services, or data flows were added.
- [Backend-Development.md](Backend-Development.md) or [Web-Development.md](Web-Development.md) — if new conventions were established.
- Keep documentation accurate and in sync with the code. Outdated docs are worse than no docs.
## 5. When You Are Stuck
- Re-read the task description and the relevant docs.
- Search the existing codebase for similar patterns — follow established conventions.
- Check the fail2ban source code in `fail2ban-master/` if you need to understand how fail2ban works internally.
- If a decision is genuinely ambiguous and no document covers it, choose the simplest option that is consistent with existing code and document your reasoning in a code comment.
---
## 6. What You Must Never Do
- **Never** commit code that does not compile or has type errors.
- **Never** commit code without tests.
- **Never** use libraries that are explicitly forbidden in the development docs.
- **Never** bypass the linter or type checker with blanket ignores.
- **Never** hard-code secrets, passwords, or tokens.
- **Never** push directly to `main` — always use feature branches.
- **Never** skip the review step — sloppy code compounds over time.
- **Never** leave a task half-done — finish it or revert it.
---
## 7. First-Run Setup
### Initialize the Development Environment
Before starting the stack for the first time, set up the required environment variables:
1. **Copy the example environment file:**
```bash
cp .env.example .env
```
2. **Generate a session secret:**
```bash
python -c 'import secrets; print(secrets.token_hex(32))'
```
Copy the output and paste it as the value for `BANGUI_SESSION_SECRET` in your `.env` file.
3. **Optional: Customize other settings**
- Edit `.env` to adjust timezone, port numbers, or other settings
- Default values are sensible for development (UTC, ports 8000/5173)
4. **Start the stack:**
```bash
make up
```
**Note:** The session secret is critical for security. Do not commit `.env` to version control — it is already in `.gitignore`. Each environment (dev, staging, production) must have its own unique secret.
---
## 8. Dev Quick-Reference
### Start / stop the stack
```bash
make up # start all containers (from repo root)
make down # stop all containers
make logs # tail logs
```
Backend: `http://127.0.0.1:8000` · Frontend (Vite proxy): `http://127.0.0.1:5173`
### API login (dev)
The frontend SHA256-hashes the password before sending it to the API.
The initial setup password must be at least 8 characters long and include one uppercase letter, one number, and one special character from `!@#$%^&*()`.
The session cookie is named `bangui_session`.
```bash
# Dev master password: Hallo123!
HASHED=$(echo -n "Hallo123!" | sha256sum | awk '{print $1}')
TOKEN=$(curl -s -X POST http://127.0.0.1:8000/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d "{\"password\":\"$HASHED\"}" \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')
# Use token in subsequent requests:
curl -H "Cookie: bangui_session=$TOKEN" http://127.0.0.1:8000/api/v1/dashboard/status
```

2467
e2e/log.html Normal file

File diff suppressed because one or more lines are too long

1520
e2e/output.xml Normal file

File diff suppressed because it is too large Load Diff

93
e2e/playwright-log.txt Normal file
View File

@@ -0,0 +1,93 @@
{"level":30,"time":"2026-05-05T17:39:03.866Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Listening on 127.0.0.1:59711"}
{"level":30,"time":"2026-05-05T17:39:03.908Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method newBrowser"}
{"level":30,"time":"2026-05-05T17:39:03.955Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Adding browser to stack: chromium, version: 147.0.7727.15"}
{"level":30,"time":"2026-05-05T17:39:03.955Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Adding 0 contexts to browser"}
{"level":30,"time":"2026-05-05T17:39:03.955Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method newBrowser"}
{"level":30,"time":"2026-05-05T17:39:03.961Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method newPage"}
{"level":30,"time":"2026-05-05T17:39:03.961Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"currentBrowser: {\"_contextStack\":[],\"browser\":{\"_type\":\"Browser\",\"_guid\":\"browser@55901c3a866b7fa3f570ea6e32bf6b10\"},\"name\":\"chromium\",\"id\":\"browser=247dd9e8-ea2c-4d1d-8907-8af4f70dce6e\",\"headless\":true}"}
{"level":30,"time":"2026-05-05T17:39:03.969Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Setting default timeout for context context=238efdc3-cf83-4059-8956-d047f1446895 to 10000"}
{"level":30,"time":"2026-05-05T17:39:03.969Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Changed active context: context=238efdc3-cf83-4059-8956-d047f1446895"}
{"level":30,"time":"2026-05-05T17:39:04.009Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Video path: undefined"}
{"level":30,"time":"2026-05-05T17:39:04.010Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Changed active page"}
{"level":30,"time":"2026-05-05T17:39:04.016Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method newPage"}
{"level":30,"time":"2026-05-05T17:39:04.020Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method goTo"}
{"level":30,"time":"2026-05-05T17:39:04.515Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method goTo"}
{"level":30,"time":"2026-05-05T17:39:04.520Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method waitForElementState"}
{"level":30,"time":"2026-05-05T17:39:04.520Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Strict mode is enabled, find Locator with css=form in page."}
{"level":30,"time":"2026-05-05T17:39:04.633Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method waitForElementState"}
{"level":30,"time":"2026-05-05T17:39:04.636Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getText"}
{"level":30,"time":"2026-05-05T17:39:04.636Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Strict mode is enabled, find Locator with css=body in page."}
{"level":30,"time":"2026-05-05T17:39:04.658Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Retrieved text for element css=body containing BanGUI\nEnter your master password to continue.\nPassword*\nSign in"}
{"level":30,"time":"2026-05-05T17:39:04.658Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getText"}
{"level":30,"time":"2026-05-05T17:39:04.663Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method closeBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.667Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Removed page=fb0bbd95-3fca-4460-9169-e7cffa907f78 from context=238efdc3-cf83-4059-8956-d047f1446895 page stack"}
{"level":30,"time":"2026-05-05T17:39:04.687Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method closeBrowser"}
================= Original suppressed error =================
Error: Browser has been closed.
at PlaywrightState.getActiveBrowser (/home/lukas/Volume/repo/BanGUI/.venv/lib/python3.12/site-packages/Browser/wrapper/index.js:8777:13)
at PlaywrightServer.getActiveBrowser (/home/lukas/Volume/repo/BanGUI/.venv/lib/python3.12/site-packages/Browser/wrapper/index.js:9689:52)
at PlaywrightServer.setTimeout (/home/lukas/Volume/repo/BanGUI/.venv/lib/python3.12/site-packages/Browser/wrapper/index.js:9887:56)
at Object.onReceiveHalfClose (/home/lukas/Volume/repo/BanGUI/.venv/lib/python3.12/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server.js:1464:25)
at BaseServerInterceptingCall.maybePushNextMessage (/home/lukas/Volume/repo/BanGUI/.venv/lib/python3.12/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:595:31)
at BaseServerInterceptingCall.handleEndEvent (/home/lukas/Volume/repo/BanGUI/.venv/lib/python3.12/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:635:14)
at ServerHttp2Stream.<anonymous> (/home/lukas/Volume/repo/BanGUI/.venv/lib/python3.12/site-packages/Browser/wrapper/node_modules/@grpc/grpc-js/build/src/server-interceptors.js:394:18)
at ServerHttp2Stream.emit (node:events:531:35)
at endReadableNT (node:internal/streams/readable:1698:12)
at process.processTicksAndRejections (node:internal/process/task_queues:89:21)
=============================================================
{"level":30,"time":"2026-05-05T17:39:04.692Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.692Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.694Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.694Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.697Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method newBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.749Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Adding browser to stack: chromium, version: 147.0.7727.15"}
{"level":30,"time":"2026-05-05T17:39:04.749Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Adding 0 contexts to browser"}
{"level":30,"time":"2026-05-05T17:39:04.749Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method newBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.785Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.785Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.787Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.788Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.791Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.791Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.814Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.814Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.816Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.816Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.818Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.818Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.839Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.839Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.840Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.841Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.843Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.843Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.866Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.866Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.868Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.869Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.871Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.871Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.891Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.891Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.892Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.892Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.896Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.896Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.912Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.912Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.914Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.914Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.916Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.916Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.933Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.933Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.934Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.935Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.936Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.936Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method switchBrowser"}
{"level":30,"time":"2026-05-05T17:39:04.955Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.955Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.957Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:04.957Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method getBrowserCatalog"}
{"level":30,"time":"2026-05-05T17:39:05.067Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"Start of node method closeAllBrowsers"}
{"level":30,"time":"2026-05-05T17:39:05.079Z","pid":252953,"hostname":"lukas-20tdcto1ww","msg":"End of node method closeAllBrowsers"}

2729
e2e/report.html Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,31 +1,32 @@
*** Settings ***
Resource ${CURDIR}/common.resource
Library Collections
*** Keywords ***
Login As Admin
# Check setup status.
${response}= GET ${BACKEND_URL}/api/setup/status
${response}= GET ${BACKEND_URL}/api/v1/setup
${body}= Set Variable ${response.json()}
IF ${body}[setup_complete] == ${false}
IF ${body}[completed] == ${false}
# Complete the setup wizard with the dev master password ("Hallo123!").
${password}= Set Variable Hallo123!
${hashed}= Evaluate "sha256('${password}'.encode()).hexdigest()" modules=hashlib
${setup_payload}= Create Dictionary password=${hashed}
POST ${BACKEND_URL}/api/setup/complete json=${setup_payload}
${setup_payload}= Create Dictionary
... master_password=Hallo123!
... database_path=bangui.db
... fail2ban_socket=/var/run/fail2ban/fail2ban.sock
... timezone=UTC
... session_duration_minutes=60
POST ${BACKEND_URL}/api/v1/setup json=${setup_payload}
# Retry login after setup.
${response}= GET ${BACKEND_URL}/api/auth/login
${response}= POST ${BACKEND_URL}/api/v1/auth/login
END
# Perform login.
${password}= Set Variable Hallo123!
${hashed}= Evaluate "sha256('${password}'.encode()).hexdigest()" modules=hashlib
${login_payload}= Create Dictionary password=${hashed}
${response}= POST ${BACKEND_URL}/api/auth/login json=${login_payload}
${login_payload}= Create Dictionary password=Hallo123!
${response}= POST ${BACKEND_URL}/api/v1/auth/login json=${login_payload}
# Store session cookie for subsequent requests.
${cookies}= Get Cookies
${session_cookie}= Get Cookie Value bangui_session
Set Browser Variables session_cookie=${session_cookie}
${session_cookie}= Get Cookie bangui_session
Set Suite Variable ${session_cookie} ${session_cookie}
Log Logged in as admin.

View File

@@ -15,7 +15,7 @@ Wait For Backend Health
WHILE True
${now}= Evaluate time.time()
IF ${now} >= ${deadline} FAIL Backend did not become healthy within ${timeout} seconds
${response}= GET ${BACKEND_URL}/api/health expected_status=200
${response}= GET ${BACKEND_URL}/api/v1/health expected_status=200
IF ${response.status} == 200 BREAK
Sleep ${interval}
END

View File

@@ -52,7 +52,7 @@ Jail Detail Page Loads Without Error
Login As Admin
# Guard: find an active jail before navigating to /jails/:name
${response}= GET ${BACKEND_URL}/api/jails
${response}= GET ${BACKEND_URL}/api/v1/jails
${jails}= Set Variable ${response.json()}
${count}= Get Length ${jails}