# Wewnętrzne SDK ATPI (Timonix\AtpiSdk)

Wewnętrzne, bezpieczne SDK przeznaczone do integracji między aplikacjami ekosystemu Timonix (np. **Aplikacja 2**, serwisy bezgłowe, mikrousługi) a centralną bramą **ATPIQuery**.

---

## Główne Możliwości

1. **Wstępna weryfikacja uprawnień i limitów (Gatekeeper Pre-Flight Check)**:
   - Sprawdzenie, czy konto użytkownika może wykonać zaplanowany upload/zapis (`canUpload()`, `canStore()`, `verifyAccess()`).
   - Weryfikacja czy rozmiar pliku mieści się w limitach pakietu (`maxPayloadKb`), czy nie przekroczono miesięcznego limitu zapytań (`Quota`), czy nie nałożono blokady częstotliwości (`Rate Limiter`) oraz czy adres IP nie jest zbanowany w bazie.
2. **Bezpieczne operacje danych w ATPI (`store`, `fetch`, `delete`)**:
   - Automatyczny pre-flight check przed wysłaniem danych.
   - Lokalna ochrona przed wstrzyknięciem SQL (SQL Injection). SDK skanuje kolekcję, klucze i cały payload JSON jeszcze przed wysłaniem żądania do sieci.
3. **Bezpieczny Handshake Przekierowań (Signed Redirect Handshake)**:
   - Gdy użytkownik jest przekierowywany z Aplikacji 2 do ATPI (lub odwrotnie), SDK generuje kryptograficznie podpisany token HMAC-SHA256 z czasem ważności (TTL) i jednorazowym noncem.
   - Po odebraniu przekierowania token jest weryfikowany, uniemożliwiając manipulację parametrami w przeglądarce użytkownika.
4. **Zgodność z architekturą Atom i PHP 8.0+**:
   - Brak przestarzałego `curl_close()`, pełna obsługa `CurlHandle` i pooling połączeń.
   - Ścisłe typowanie (`declare(strict_types=1)`), obiekty DTO i dedykowane wyjątki.

---

## 1. Instalacja i Dołączenie SDK

### Opcja A: Poprzez autoloader SDK (Standalone)
W Aplikacji 2 wystarczy dołączyć plik `autoload.php`:
```php
require_once __DIR__ . '/WEWNĘCZNE SDK/autoload.php';

use Timonix\AtpiSdk\AtpiClient;
```

### Opcja B: Poprzez Composer PSR-4
W `composer.json` Aplikacji 2 dodaj wpis:
```json
{
    "autoload": {
        "psr-4": {
            "Timonix\\AtpiSdk\\": "sciezka/do/WEWNĘCZNE SDK/src/"
        }
    }
}
```

---

## 2. Inicjalizacja Klienta

```php
use Timonix\AtpiSdk\AtpiClient;

// Sposób 1: Bezpośrednio z parametrami
$atpi = AtpiClient::create(
    baseUrl: 'https://query.timonix.pl',     // Adres serwera ATPIQuery
    apiKey: 'atpi_usr_xxxxxxxxxxxxxxxx',    // Klucz API konta lub aplikacji
    apiSecret: 'twoj_wspolny_klucz_secret', // Opcjonalny sekret do podpisów HMAC
    userUuid: 'usr_uuid_12345'              // UUID użytkownika końcowego
);

// Sposób 2: Ze zmiennych środowiskowych (.env)
// Wymaga: ATPI_BASE_URL, ATPI_API_KEY, ATPI_API_SECRET, ATPI_USER_UUID
$atpi = AtpiClient::fromEnv();
```

---

## 3. Scenariusz 1: Weryfikacja czy Upload / Zapis jest Możliwy (Pre-Flight)

Przed odebraniem dużego pliku lub rozpoczęciem przetwarzania w Aplikacji 2, sprawdzamy czy konto ma wolne limity i czy rozmiar pliku jest dozwolony:

```php
$fileSizeBytes = 2 * 1024 * 1024; // 2 MB
$clientIp = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';

$check = $atpi->canUpload($fileSizeBytes, $clientIp);

if (!$check->isAllowed()) {
    // Odpowiedź odmowna dla użytkownika zanim plik obciąży serwer
    http_response_code($check->getStatusCode());
    echo json_encode([
        'success' => false,
        'error' => $check->getError(),
        'quota_remaining' => $check->getQuotaRemaining(),
        'rate_limit_remaining' => $check->getRateLimitRemaining(),
    ]);
    exit;
}

// Zezwolono! Znamy pakiet i pozostałe limity
echo "Upload dozwolony dla pakietu: " . $check->getPackageSlug();
```

---

## 4. Scenariusz 2: Zapis i Odczyt Danych z Magazynu ATPI

SDK automatycznie weryfikuje bezpieczeństwo danych wejściowych pod kątem ataków SQL Injection i wykonuje pre-flight check uprawnień:

### Zapis rekordu (`store`):
```php
try {
    $result = $atpi->store(
        collection: 'user_profiles',
        key: 'profile_usr_99',
        payload: [
            'theme' => 'dark_purple',
            'notifications' => true,
            'updated_at' => date('c'),
        ],
        clientIp: $_SERVER['REMOTE_ADDR'] ?? null,
        preVerify: true // Domyślnie true: najpierw weryfikuje uprawnienia
    );

    if ($result->isSuccess()) {
        echo "Pomyślnie zapisano w ATPI! Latency: {$result->getLatencyMs()} ms";
    }
} catch (\Timonix\AtpiSdk\Exceptions\AtpiSecurityException $e) {
    // Zablokowano próbę wstrzyknięcia kodu lokalnie
    error_log("Security alert: " . $e->getMessage());
} catch (\Timonix\AtpiSdk\Exceptions\AtpiVerificationException $e) {
    // Przekroczono quota lub konto zablokowane
    echo "Limit przekroczony: " . $e->getMessage();
}
```

### Odczyt rekordu (`fetch`):
```php
$result = $atpi->fetch(
    collection: 'user_profiles',
    key: 'profile_usr_99'
);

if ($result->isSuccess()) {
    $data = $result->getData();
    var_dump($data);
}
```

---

## 5. Scenariusz 3: Przekierowanie Użytkownika z Bezpiecznym Handshake (Redirect Flow)

Realizuje wymóg: *„jak puszcze dodawanie po przekierowaniu z tamtej naszej aplikacji to żeby najpierw zostało wszystko zweryfikowane a puźniej zapisane/ odczytane itp.”*

### Krok 1: W Aplikacji 2 (Inicjacja przekierowania)
Przed przekierowaniem SDK weryfikuje użytkownika i generuje jednorazowy podpisany token HMAC:

```php
// W Aplikacji 2:
$targetUrl = 'https://query.timonix.pl/upload-handler';

// Metoda weryfikuje konto i jeśli wszystko OK, zwraca podpisany URL:
$redirectUrl = $atpi->generateSignedRedirectUrl(
    targetUrl: $targetUrl,
    action: 'upload',
    extraData: [
        'file_size_bytes' => 1024 * 500, // 500 KB
        'collection' => 'documents',
    ],
    ttlSeconds: 300 // Ważny przez 5 minut
);

// Bezpieczne przekierowanie przeglądarki użytkownika:
header("Location: " . $redirectUrl);
exit;
```

### Krok 2: Po odebraniu przekierowania
W skrypcie docelowym odbieramy parametr `atpi_handshake` i weryfikujemy integralność podpisu oraz ważność tokena:

```php
$token = $_GET['atpi_handshake'] ?? '';

$handshake = $atpi->verifyRedirectHandshake($token);

if (!$handshake['valid']) {
    http_response_code(403);
    die("Odrzucono przekierowanie: " . $handshake['error']);
}

// Dane są zweryfikowane i autentyczne!
$userUuid = $handshake['user_uuid'];
$action = $handshake['action'];
$metadata = $handshake['data'];

// Możemy bezpiecznie zapisać dane:
$clientForUser = $atpi->withUser($userUuid);
$clientForUser->store($metadata['collection'], 'doc_' . time(), $uploadedData);
```

---

## 6. Hierarchia Wyjątków

Wszystkie błędy dziedziczą z `Timonix\AtpiSdk\Exceptions\AtpiException`:

- `AtpiSecurityException` – wykryto próbę ataku SQL Injection, niedozwolone znaki w kolekcji/kluczu lub naruszenie integralności podpisu.
- `AtpiVerificationException` – odrzucono zapytanie przez serwer ATPI (wyczerpany pakiet, blokada częstotliwości, ban na IP).
- `AtpiHttpException` – błędy warstwy sieciowej cURL lub brak odpowiedzi z serwera.
