# o‑Byte QA – Testprotokoll‑Tool (Full README)

**Stand:** 2025-09-04  
**Stack:** PHP 8.3 ( Apache ), JS (Vanilla), MariaDB, Docker & Compose, Portainer

Dieses Repository enthält ein vollwertiges QA‑Tool mit Login (OIDC), GitLab‑Template‑Loader, DocBee‑Posting, PDF‑Erzeugung im Browser und persistenter Speicherung (DB + PDF‑Ablage). Diese README basiert ausschließlich auf dem mitgelieferten Projektstand.

---

## Inhaltsverzeichnis

1. [Überblick](#überblick)  
2. [Architektur & Verzeichnisstruktur](#architektur--verzeichnisstruktur)  
3. [Funktionen](#funktionen)  
4. [UI‑Bedienung (Kurzüberblick)](#ui-bedienung-kurzüberblick)  
5. [Datenmodell (DB)](#datenmodell-db)  
6. [API‑Endpunkte](#api-endpunkte)  
7. [Konfiguration per Umgebungsvariablen (.env)](#konfiguration-per-umgebungsvariablen-env)  
8. [Lokale Entwicklung](#lokale-entwicklung)  
9. [Deployment mit Docker Compose](#deployment-mit-docker-compose)  
10. [Deployment mit Portainer (Stack)](#deployment-mit-portainer-stack)  
11. [GitLab‑Vorlagen](#gitlab-vorlagen)  
12. [DocBee‑Integration](#docbee-integration)  
13. [PDF‑Export](#pdf-export)  
14. [Troubleshooting](#troubleshooting)  
15. [Sicherheitshinweise](#sicherheitshinweise)  
16. [Lizenz / Nutzung](#lizenz--nutzung)

---

## Überblick

- **Ziel:** Manuelle QA‑Testläufe effizient vorbereiten, durchführen und revisionssicher exportieren.  
- **Workflow:** Vorlage laden → Metadaten eintragen (inkl. **OLM‑Nummer**) → Schritte bearbeiten (Gruppen, Status, Evidenzen) → **Exportieren (DocBee, DB, PDF)**.  
- **Login:** OpenID Connect (konfigurierbar), optional **bypassbar** über `AUTH_DISABLED=true` für lokale Tests.  
- **Persistenz:** Reports (Metadaten & Steps) in MariaDB, PDFs serverseitig abgelegt.  
- **Integrationen:** GitLab (YAML‑Vorlagen), DocBee (Login + Notiz‑Erzeugung via REST).

---

## Architektur & Verzeichnisstruktur

```
o-byte-qa-tool/
├─ compose/
│  ├─ Dockerfile               # PHP 8.3 + Apache, Composer
│  └─ docker-compose.yml       # app, mariadb, schema-loader, phpmyadmin, fix-pdf-perms
└─ htdocs/                     # Webroot
   ├─ index.php                # App-Entry, Token-Bootstrap für DocBee, UI
   ├─ app.js                   # UI-Logik, Exporte, DocBee-Push, GitLab-Loader
   ├─ style.css                # Styles
   ├─ api/
   │  ├─ export.php            # Report+Steps in DB speichern, PDF entgegennehmen
   │  ├─ echo.php              # einfache Echo-Route (Debug)
   │  └─ health.php            # Health‑Check (DB/PDF‑Pfad)
   ├─ config/config.php        # ENV‑Konfiguration (OIDC, DocBee, DB, APP_BASE_URL, …)
   ├─ login.php                # OIDC‑Start
   ├─ callback.php             # OIDC‑Callback, Session‑Bootstrap
   ├─ logout.php               # Session reset (OLM beibehalten)
   ├─ logo.png / logo_light.png
   ├─ favicon.ico
   ├─ vendor/                  # Composer (z. B. jumbojett/openid-connect-php, phpseclib, …)
   └─ oidc/                    # phpseclib Assets (lokal eingebunden)
```

---

## Funktionen

### Vorlagen & Schritte
- **Vorlagen laden**: YAML/JSON (lokal per Datei **oder** aus **GitLab**‑Repo/Pfad/Ref).  
- **Gruppen**: Gruppenzeilen, ein-/ausklappbar; Schritte via Drag‑Handle verschiebbar (innerhalb und zwischen Gruppen).  
- **Automatische Neu‑Nummerierung** der Steps bei Änderungen/Verschieben.  
- **Pflichtschritte** (📌) werden geprüft – Export blockiert, wenn offen.  
- **Gruppenstatus setzen** auf *pass/fail/skip/blocked*, inkl. **Warnung** bei Überschreiben bestehender Stati.  
- **Kommentare & Evidenzen** (Freitext + URL).

### Metadaten
- **Module**, **Modul‑Version**, **PBX‑Version**, **Tester** (aus Session), **DocBee‑Ticket‑URL**, **OLM‑Nummer** (`olm_nummer`).

### Exporte
- **Exportieren (DocBee, DB, PDF)** per **einem** Button:  
  1. DocBee‑Notiz erstellen (falls Token vorhanden) → Ticket‑URL ggf. aktualisiert.  
  2. **PDF** im Browser generieren (jsPDF+AutoTable).  
  3. **/api/export.php**: JSON (Run) + PDF (multipart) an Server → DB‑Speicherung + PDF‑Ablage.  
  4. Server sendet `report_id` und `pdf_path` zurück.
- **Lauf speichern / laden** als JSON (Zwischenspeichern/Weiterarbeiten).  
- **Template als YAML exportieren** (inkl. `olm_nummer`, Gruppen & Steps).

> Hinweis: Dedizierte Buttons für CSV/MD/PDF wurden in dieser Version **konsolidiert** – CSV/MD‑Funktionen existieren noch im Code, UI‑seitig aber nicht exponiert.

---

## UI‑Bedienung (Kurzüberblick)

- **GitLab Vorlage**: Dropdown befüllen (`GitLab: …` Tag zeigt Status), Datei wählen → Vorlage wird geladen.  
- **Metadaten**: Felder ausfüllen (**OLM‑Nummer** nicht vergessen).  
- **Steps**: `+ Step` / `+ Gruppe`, per Griff `⋮⋮` ziehen, Status setzen, Kommentare/Evidenzen pflegen.  
- **Exportieren (DocBee, DB, PDF)**: Startet kompletten Lauf‑Export. Ergebnisdialog zeigt Ticket/PDF‑Pfad/Report‑ID.

---

## Datenmodell (DB)

Beim Export werden folgende Tabellen (idempotent) angelegt/aktualisiert:

**reports**
- `id` BIGINT (PK)  
- `created_at` TIMESTAMP (Default `CURRENT_TIMESTAMP`)  
- `module` VARCHAR(255)  
- `module_version` VARCHAR(100)  
- `pbx_version` VARCHAR(100)  
- `olm_nummer` VARCHAR(100)  
- `tester` VARCHAR(255)  
- `docbee_url` TEXT  
- `summary` VARCHAR(255)  → z. B. `12/15 pass, 2 fail, 1 skip`  
- `pdf_path` TEXT

**steps**
- `id` BIGINT (PK)  
- `report_id` BIGINT (FK → reports.id, ON DELETE CASCADE)  
- `step_index` INT  
- `step_id` VARCHAR(50)  
- `title` TEXT  
- `expected` TEXT  
- `status` ENUM('pass','fail','skip','na','')  
- `comment` TEXT  
- `evidence` TEXT  
- `group_title` VARCHAR(255)  
- `group_index` INT

Der **schema‑loader** Service in `docker-compose.yml` erstellt die Tabellen automatisch beim ersten Start.

---

## API‑Endpunkte

- `GET /api/health.php`  
  - Prüft PDO / MariaDB‑Erreichbarkeit, Schreibrechte des PDF‑Ordners.
- `POST /api/export.php` (multipart/form-data)  
  - **Felder:**  
    - `run` – JSON mit Metadaten (`module`, `module_version`, `pbx_version`, `olm_nummer`, `tester`, `docbee_url`, `ts`, `steps`[…])  
    - `pdf` – erzeugte PDF‑Datei (optional; Export läuft auch ohne PDF durch)  
  - **Antwort:** `{{"ok":true,"report_id":<int>,"pdf_path":"…","summary":"…"}}`
- `POST /api/echo.php`  
  - Debug/Echo.

---

## Konfiguration per Umgebungsvariablen (.env)

Beispiel **.env** (für Compose/Portainer):

```env
# App
APP_PORT=8009
APP_BASE_URL=http://localhost:8009
AUTH_DISABLED=false                 # true = Login-BYPASS (nur lokal verwenden)

# OpenID Connect (Login)
OIDC_PROVIDER=https://auth.o-byte.com/realms/o-byte.com
OIDC_CLIENT=qa-tool
OIDC_SECRET=<client-secret>

# MariaDB
DB_HOST=mariadb
DB_PORT=3306
DB_NAME=qa_tool
DB_USER=qa
DB_PASS=change_me
DB_ROOT_PASS=change_me_root

# Persistenz
PDF_STORAGE_DIR=/var/reports

# DocBee
DOCBEE_BASEURL=https://obyte.docbee.com
DOCBEE_USER=OBYTE/service
DOCBEE_PASS=<password>
DOCBEE_TIME=1440                   # Minuten Token-Gültigkeit

# GitLab Templates
GITLAB_HOST=https://git.steinert.cc
GITLAB_PROJECT_ID=qa/templates     # oder numerische ID
GITLAB_REF=main
GITLAB_PATH=templates
GITLAB_TOKEN=                      # optional (PRIVATE-TOKEN)

# Container-User (Dateirechte)
APP_UID=0
APP_GID=0

# phpMyAdmin Port (optional)
PHPMYADMIN_PORT=8010
```

> **Wichtig:** Keine Secrets in Repos commiten. Für Produktion Token/Passwörter über Portainer‑UI/Secrets setzen.

---

## Lokale Entwicklung

1. **Composer‑Abhängigkeiten** sind bereits im Repo (`vendor/`). Falls nötig:
   ```bash
   cd htdocs && composer install
   ```
2. **PHP‑Built‑in** (nur für schnellen Test, ohne DB/PDF‑Ablage):
   ```bash
   php -S 127.0.0.1:8009 -t htdocs
   ```
3. **Login‑Bypass** lokal aktivieren:
   ```bash
   export AUTH_DISABLED=true
   ```
4. Browser: `http://127.0.0.1:8009`

---

## Deployment mit Docker Compose

Im Ordner `compose/` befindet sich ein funktionsfähiges Setup mit:
- **app** (Apache+PHP 8.3), bind‑mountet `htdocs` und nutzt `PDF_STORAGE_DIR` Volume  
- **mariadb** (11.x)  
- **schema-loader** (initialisiert Tabellen)  
- **phpmyadmin** (optional)  
- **fix-pdf-perms** (setzt 0777 auf PDF‑Volume)

**Starten:**
```bash
cd compose
cp ../.env .env          # eigene Werte setzen
docker compose up -d
```

**Zugriff:** `http://<host>:${APP_PORT:-8009}`  
**phpMyAdmin:** `http://<host>:${PHPMYADMIN_PORT:-8010}`

---

## Deployment mit Portainer (Stack)

1. **Stack → Add Stack**  
2. **Environment Vars** nach obiger `.env` setzen (oder Datei hochladen).  
3. **Compose** verwenden (aus `compose/docker-compose.yml`), z. B.:

```yaml
version: "3.8"
services:
  app:
    image: thecodingmachine/php:8.3-v4-apache
    user: "${APP_UID:-0}:${APP_GID:-0}"
    ports:
      - "${APP_PORT:-8009}:80"
    environment:
      PHP_EXTENSION_PDO_MYSQL: "1"
      APP_BASE_URL: ${APP_BASE_URL}
      AUTH_DISABLED: ${AUTH_DISABLED}
      DB_HOST: ${DB_HOST}
      DB_PORT: ${DB_PORT}
      DB_NAME: ${DB_NAME}
      DB_USER: ${DB_USER}
      DB_PASS: ${DB_PASS}
      PDF_STORAGE_DIR: ${PDF_STORAGE_DIR}
      OIDC_PROVIDER: ${OIDC_PROVIDER}
      OIDC_CLIENT: ${OIDC_CLIENT}
      OIDC_SECRET: ${OIDC_SECRET}
      DOCBEE_BASEURL: ${DOCBEE_BASEURL}
      DOCBEE_USER: ${DOCBEE_USER}
      DOCBEE_PASS: ${DOCBEE_PASS}
      DOCBEE_TIME: ${DOCBEE_TIME}
      GITLAB_HOST: ${GITLAB_HOST}
      GITLAB_PROJECT_ID: ${GITLAB_PROJECT_ID}
      GITLAB_REF: ${GITLAB_REF}
      GITLAB_PATH: ${GITLAB_PATH}
      GITLAB_TOKEN: ${GITLAB_TOKEN}
    volumes:
      - ${APP_HTDOCS_HOST:-/opt/qa-tool/htdocs}:/var/www/html
      - pdf_storage:/var/reports
    depends_on:
      mariadb:
        condition: service_healthy
      fix-pdf-perms:
        condition: service_completed_successfully
    restart: unless-stopped

  mariadb:
    image: mariadb:11
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASS}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mariadb-admin", "ping", "-h", "127.0.0.1", "-p${DB_PASS}"]
      interval: 10s
      timeout: 5s
      retries: 30
      start_period: 20s
    restart: unless-stopped

  schema-loader:
    image: mariadb:11
    depends_on:
      mariadb:
        condition: service_healthy
    environment:
      DB_HOST: ${DB_HOST}
      DB_PORT: ${DB_PORT}
      DB_NAME: ${DB_NAME}
      DB_USER: ${DB_USER}
      DB_PASS: ${DB_PASS}
    command: >
      sh -lc "
        echo 'Waiting for DB…'; sleep 3;
        mysql -h $DB_HOST -P $DB_PORT -u$DB_USER -p$DB_PASS $DB_NAME <<SQL
          CREATE TABLE IF NOT EXISTS reports (
            id BIGINT AUTO_INCREMENT PRIMARY KEY,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            module VARCHAR(255),
            module_version VARCHAR(100),
            pbx_version VARCHAR(100),
            olm_nummer VARCHAR(100),
            tester VARCHAR(255),
            docbee_url TEXT,
            summary VARCHAR(255),
            pdf_path TEXT
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
          CREATE TABLE IF NOT EXISTS steps (
            id BIGINT AUTO_INCREMENT PRIMARY KEY,
            report_id BIGINT NOT NULL,
            step_index INT,
            step_id VARCHAR(50),
            title TEXT,
            expected TEXT,
            status ENUM('pass','fail','skip','na','') DEFAULT '',
            comment TEXT,
            evidence TEXT,
            group_title VARCHAR(255),
            group_index INT,
            CONSTRAINT fk_steps_report FOREIGN KEY (report_id) REFERENCES reports(id) ON DELETE CASCADE
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
        SQL
      "
    restart: "no"

  fix-pdf-perms:
    image: busybox:1.36
    volumes:
      - pdf_storage:/target
    command: |
      sh -lc '
        set +e
        mkdir -p /target
        chmod -R 0777 /target 2>/dev/null || true
        chown -R 0:0 /target 2>/dev/null || true
        exit 0
      '
    restart: "no"

  phpmyadmin:
    image: phpmyadmin:5
    ports:
      - "${PHPMYADMIN_PORT:-8010}:80"
    environment:
      PMA_HOST: mariadb
      PMA_USER: ${DB_USER}
      PMA_PASSWORD: ${DB_PASS}
    depends_on:
      mariadb:
        condition: service_healthy
    restart: unless-stopped

volumes:
  db_data:
  pdf_storage:
```

4. **Deploy Stack** → öffnen: `http://<host>:${APP_PORT}`.

---

## GitLab‑Vorlagen

- Konfiguration via ENV (`GITLAB_HOST`, `GITLAB_PROJECT_ID`, `GITLAB_REF`, `GITLAB_PATH`, `GITLAB_TOKEN`).  
- UI lädt per GitLab API die Liste der YAML‑Dateien aus `GITLAB_PATH` (nur `.yaml`/`.yml`).  
- Parser akzeptiert **YAML** und **JSON** (Fallback).

---

## DocBee‑Integration

- Beim Laden von `index.php` wird via `DOCBEE_BASEURL/restApi/login` ein Token geholt und **clientseitig** als `window.DOCBEE_TOKEN` bereitgestellt.  
- Exportablauf:
  1. **postToDocBee** erstellt eine Notiz im angegebenen **Ticket** (aus `DocBee Ticket‑URL`).  
  2. Bei Erfolg wird die URL im Lauf aktualisiert.  
  3. Danach erfolgt Server‑Export (DB + optional PDF).

**Erforderliche ENV:** `DOCBEE_BASEURL`, `DOCBEE_USER`, `DOCBEE_PASS`, `DOCBEE_TIME` (Minuten).

---

## PDF‑Export

- Erzeugung **im Browser** via **jsPDF** + **AutoTable** (CDN‑Fallbacks vorhanden).  
- **Logo** `logo_light.png` wird proportional (max 26×12 mm) **rechtsbündig** im Kopf platziert.  
- Fallback: Falls Libraries fehlen, wird ein Text‑PDF erzeugt.  
- Server speichert PDFs in `PDF_STORAGE_DIR` (Default `/var/reports`).

---

## YAML‑Vorlage (Beispiel inkl. OLM)

```yaml
name: "Modul XYZ – Basis‑Tests"
module: "Modul XYZ"
module_version: "1.2.3"
pbx_version: "8.1"
olm_nummer: "OLM-12345"
steps:
  - type: "group"
    title: "Telefonie Basis"
  - type: "step"
    id: "s1"
    title: "Anruf initiieren"
    expected: "Ruf wird erfolgreich aufgebaut"
    required: true
  - type: "step"
    id: "s2"
    title: "Rufumleitung"
    expected: "Ruf wird korrekt umgeleitet"
    required: false
```

---

## Troubleshooting

- **Login‑Loop / 401:** OIDC‑Konfiguration prüfen (`OIDC_PROVIDER`, Redirect‑URL `/callback.php`). Lokal ggf. `AUTH_DISABLED=true`.  
- **DocBee 401/403:** `DOCBEE_USER/DOCBEE_PASS/DOCBEE_BASEURL/DOCBEE_TIME` prüfen.  
- **DB‑Fehler (PDO not loaded):** Image/Extension prüfen (`PHP_EXTENSION_PDO_MYSQL=1` gesetzt? Compose).  
- **Keine Schreibrechte PDF:** Volume/`fix-pdf-perms` prüfen; `PDF_STORAGE_DIR` existiert/schreibbar? `/api/health.php` ansehen.  
- **GitLab‑Liste leer:** Pfad/Ref/Token prüfen; nur `.yaml/.yml` werden angezeigt.  
- **Export blockiert:** Pflichtschritte haben keinen Status (📌).

---

## Sicherheitshinweise

- **AUTH_DISABLED** ausschließlich in Dev/Stage verwenden.  
- DocBee‑Token wird **clientseitig** verwendet – nur in internen Netzen nutzen oder alternative Server‑Proxy‑Variante vorsehen.  
- Secrets per Portainer‑Secrets/ENV setzen, nicht im Repo.  
- DB‑Backups/Retention für `reports`/`steps` einplanen; PDF‑Ablage regelmäßig bereinigen/archivieren.

---

## Lizenz / Nutzung

Interne Nutzung innerhalb o‑Byte / Kundenprojekten. Anpassungen willkommen.
