tillio-crm / oauth-resources
Official PHP SDK for Tillio app resources: per-app document storage and file storage proxy.
Requires
- php: ^8.3
- ext-curl: *
- ext-json: *
- guzzlehttp/guzzle: ^7.8
Requires (Dev)
- phpunit/phpunit: ^11.0
README
Oficjalne SDK PHP do zasobów aplikacji Tillio: prywatny magazyn dokumentów i plików per (aplikacja × workspace), udostępniany przez storage proxy serwera Tillio.
Twoja aplikacja nigdy nie dotyka Firestore/GCS bezpośrednio — wszystko przechodzi przez API Tillio z tokenem OAuth. Przestrzeń danych jest wyznaczana po stronie serwera z tokenu, więc aplikacja widzi wyłącznie własne dane w obrębie workspace'u, który ją zainstalował.
Uzupełnia tillio-crm/oauth-client
(logowanie OAuth 2.0 + PKCE) — razem stanowią komplet do budowy aplikacji Tillio.
Spis treści
- Wymagania
- Instalacja
- Szybki start
- Dokumenty
- Pliki
- API
- Bezpieczeństwo
- Testy
- Wersjonowanie
- Zgłaszanie błędów / pull requesty
- Licencja
Wymagania
- PHP
^8.3 - rozszerzenia:
ext-curl,ext-json - Composer
- Aplikacja zarejestrowana w Tillio (zainstalowana w workspace) z tokenem OAuth
zawierającym scope
app_storage
Instalacja
composer require tillio-crm/oauth-resources
Szybki start
Najwygodniej w parze z tillio-crm/oauth-client — callable z tokenem
załatwia auto-refresh:
use TillioCrm\OAuth\Client\Client; use TillioCrm\OAuth\Resources\ResourcesClient; $oauth = new Client([ 'clientId' => '...', 'clientSecret' => '...', 'redirectUri' => 'https://twoja-apka.pl/callback', 'scopes' => ['profile', 'email', 'offline_access', 'app_storage'], ]); $resources = new ResourcesClient([ 'accessToken' => fn () => $oauth->accessToken(), // 'server' => 'https://auth.tillio.app', // default ]);
Token można też podać wprost: new ResourcesClient(['accessToken' => $jwt]).
Dokumenty
Dokument to tablica JSON-owalna (max 256 KB). Nazwy kolekcji i ID:
[a-zA-Z0-9_-], max 64 znaki.
$storage = $resources->storage(); // Zapis (upsert — pełne nadpisanie) $storage->put('sync_state', 'wfirma', [ 'last_run_at' => '2026-07-24T03:00:00Z', 'cursor' => 18234, 'status' => 'ok', ]); // Odczyt $doc = $storage->get('sync_state', 'wfirma'); // NotFoundException gdy brak $doc = $storage->find('sync_state', 'wfirma'); // null gdy brak echo $doc['data']['status']; // 'ok' // Lista z filtrem i paginacją $page = $storage->list('invoices', [ 'limit' => 100, 'filter' => ['status', '==', 'pending'], ]); $next = $storage->list('invoices', ['after' => $page['next_cursor']]); // Iteracja po całej kolekcji (paginacja automatyczna) foreach ($storage->all('invoices') as $invoice) { // ... } // Usunięcie $storage->delete('sync_state', 'wfirma');
Operatory filtra: ==, !=, >, >=, <, <=, in, array-contains.
Wartości filtra zachowują typ (string "5" ≠ liczba 5).
Pliki
Pliki trafiają bezpośrednio do Google Cloud Storage przez krótkoterminowe signed URLs (15 min) — nie przechodzą przez serwer Tillio. SDK ukrywa cały flow:
$files = $resources->files(); // Upload / download w jednej linii $files->upload('invoices/2026/07/faktura-123.pdf', $pdfBytes, 'application/pdf'); $pdf = $files->download('invoices/2026/07/faktura-123.pdf'); // Signed URL np. dla przeglądarki użytkownika (ważny 15 minut) $grant = $files->downloadUrl('invoices/2026/07/faktura-123.pdf'); header('Location: ' . $grant['url']); // Listing i kasowanie $list = $files->list('invoices/2026/07/'); $files->delete('invoices/2026/07/faktura-123.pdf');
Ścieżki są względne w obrębie przestrzeni aplikacji; segmenty [a-zA-Z0-9._-],
bez .. i bez segmentów zaczynających się kropką. Limit uploadu: 100 MB
(egzekwowany kryptograficznie w podpisie URL).
API
Klasa ResourcesClient
| Metoda / opcja | Opis |
|---|---|
new ResourcesClient(array $config) |
accessToken (string | callable, wymagane), server (default https://auth.tillio.app), timeout (default 30.0), handler (Guzzle HandlerStack, do testów) |
storage(): Storage |
dokumentowy magazyn danych |
files(): Files |
magazyn plików |
Klasa Storage
| Metoda | Opis |
|---|---|
put(string $collection, string $docId, array $data): array |
upsert; zwraca {id, data, created_at, updated_at} |
get(string $collection, string $docId): array |
odczyt; NotFoundException gdy brak |
find(string $collection, string $docId): ?array |
jak get(), ale null zamiast wyjątku |
delete(string $collection, string $docId): void |
usunięcie dokumentu |
list(string $collection, array $options = []): array |
limit (≤500), after (cursor), filter ([pole, operator, wartość]); zwraca {documents, next_cursor} |
all(string $collection, array $options = []): Generator |
iterator po całej kolekcji (auto-paginacja) |
Klasa Files
| Metoda | Opis |
|---|---|
upload(string $path, mixed $contents, string $contentType): void |
string albo stream |
download(string $path): string |
NotFoundException gdy brak |
uploadUrl(string $path, string $contentType, ?int $maxBytes): array |
{url, method, headers, expires_in} — nagłówki są wymagane przy PUT |
downloadUrl(string $path): array |
{url, method, expires_in} |
list(string $prefix = ''): array |
[{path, size, content_type, updated_at}] |
delete(string $path): void |
NotFoundException gdy brak |
Wyjątki
Wszystkie dziedziczą po ResourcesException (\RuntimeException);
kod wyjątku = status HTTP.
| Wyjątek | Kiedy |
|---|---|
AuthenticationException |
401/403 — zły/pusty token, brak scope app_storage, apka niezainstalowana w workspace |
NotFoundException |
404 — dokument/plik nie istnieje |
ValidationException |
pozostałe 4xx — zły input (nazwa kolekcji, limit 256 KB itd.) |
ServerException |
5xx — błąd serwera/magazynu |
use TillioCrm\OAuth\Resources\Exception\NotFoundException; try { $doc = $resources->storage()->get('sync_state', 'wfirma'); } catch (NotFoundException) { // pierwsza synchronizacja — brak stanu }
Bezpieczeństwo
- Namespace z tokenu, nie z inputu — prefiks przestrzeni danych (workspace × aplikacja) serwer wyprowadza z claimów tokenu OAuth. Aplikacja nie zna i nie podaje pełnych ścieżek, więc nie może odczytać danych innej aplikacji ani innego workspace'u.
- Token Tillio nigdy nie trafia do GCS — komunikacja z signed URLs odbywa się
osobnym klientem HTTP bez nagłówka
Authorization(podpis jest w samym URL-u). - Ścieżki walidowane po stronie serwera —
..,//, ukryte segmenty i ścieżki absolutne są odrzucane (ValidationException). - Rewokacja tokenu / odinstalowanie aplikacji odcina dostęp natychmiast — serwer sprawdza ważność tokenu przy każdym wywołaniu.
Testy
composer install vendor/bin/phpunit
Testy nie wymagają sieci (Guzzle MockHandler przez opcję handler).
Wersjonowanie
SemVer. Do wersji 1.0.0 drobne zmiany łamiące mogą
pojawić się w wydaniach minor.
Zgłaszanie błędów / pull requesty
Issues i PR-y: github.com/tillio-crm/oauth-resources. Zgłoszenia bezpieczeństwa — nie przez publiczne issues; napisz na adres wsparcia Tillio.
Licencja
MIT — patrz LICENSE.