ometra / apollo-sdk
Modular Laravel SDK for the Apollo suite APIs
Requires
- php: ^8.2
- illuminate/http: ^12.0
- illuminate/routing: ^12.0
- illuminate/support: ^12.0
- ometra/caronte-sdk: ^7.1.0
Requires (Dev)
- phpunit/phpunit: 11.5.55
README
Cliente Laravel/PHP modular para consumir Proteus, Pulse, Flare e Ignis con autenticacion compartida de Caronte.
Instalacion
composer require ometra/apollo-sdk
Publica la configuracion si necesitas sobrescribir las URLs de modulos:
php artisan vendor:publish --tag=apollo-config
El archivo publicado es config/apollo.php.
Paginas de error
Apollo registra automaticamente paginas HTML para los errores HTTP comunes de
Laravel (401, 403, 404, 419, 429, 500, 503). El SDK agrega sus
vistas como fallback, por lo que cualquier archivo local en
resources/views/errors mantiene prioridad.
Para deshabilitar este fallback:
APOLLO_ERROR_PAGES_ENABLED=false
Si necesitas personalizar las vistas dentro de la app host, publicalas con:
php artisan vendor:publish --tag=apollo-error-pages
Tambien puedes publicar configuracion y paginas de error juntas:
php artisan vendor:publish --tag=apollo
AppMenu compartido
El SDK incluye el menu compartido de la suite en resources/js/shared/AppMenu.
Publicalo en una app host con:
php artisan vendor:publish --tag=apollo-app-menu
Tambien se publica con el tag agregado apollo.
DirectoryTree compartido
El SDK incluye el arbol de directorios en resources/js/shared/DirectoryTree.
Publicalo en una app host con:
php artisan vendor:publish --tag=apollo-directory-tree
Tambien se publica con el tag agregado apollo.
Documentacion detallada de componentes UI:
docs/ui-components.md
Configuracion
Apollo solo configura URLs por modulo; la autenticacion sigue viviendo en el SDK de Caronte.
PROTEUS_BASE_URL=https://proteus.example.com/api PULSE_BASE_URL=https://pulse.example.com/api FLARE_BASE_URL=https://flare.example.com/api IGNIS_BASE_URL=https://ignis.example.com/api
Las llamadas HTTP usan el transporte de Caronte 7.1, incluyendo uploads multipart y respuestas binarias sin parsing. Segun el tipo de request agregan:
X-Group-Tokencuando existe una aplicacion grupal; en caso contrario,X-Application-TokenX-User-Tokenen llamadas de usuarioX-Tenant-IddesdeTenantContextcuando existe
Uso modular
use Ometra\Apollo\Sdk\Facades\Apollo; $directories = Apollo::proteus()->directories()->index(); $media = Apollo::proteus()->media()->upload([ 'type' => 'image', 'directory_id' => $directoryId, 'media' => [$request->file('image')], 'metadata' => [ 'source' => 'apollo', ], ]); Apollo::proteus()->media()->setMetadata($mediaId, [ 'metadata' => [ 'title' => 'Hero image', ], ]); $metadata = Apollo::proteus()->metadata()->index($mediaId, [ 'search' => 'title', ]); $images = Apollo::proteus()->media()->index(['type' => 'image']); $lightPath = Apollo::proteus()->media()->lightPathUrl($mediaId, [ 'ext' => 'mp4', 'url_ttl_seconds' => 3600, ]); $stations = Apollo::flare()->stations()->index(['country' => 'mx']); $campaigns = Apollo::ignis()->campaigns()->byGroup('group-1'); $campaign = Apollo::ignis()->campaigns()->show('group-1', 11); Apollo::ignis()->contentHits()->report([ ['content_id' => 'content-1', 'hits' => 10], ]); $groups = Apollo::pulse()->groups()->index();
URLs LightPath para media
LightPath se integra desde el recurso de media de Proteus porque Proteus es la autoridad de permisos y formatos. El SDK solo solicita una URL temporal; no valida tokens ni habla con nodos LightPath.
$response = Apollo::proteus()->media()->lightPathUrl($mediaId, [ 'ext' => 'mp4', 'url_ttl_seconds' => 3600, ]); $url = $response['data']['url'];
La respuesta incluye id_lightpath_grant (UUID), url, format,
url_expires_at, renewable_from y renewable_until.
Opciones:
ext: formato a entregar. Si se omite, Proteus usa el formato default.url_ttl_seconds: vigencia de la URL. Proteus aplica su maximo configurado.
El owner puede extender o eliminar el grant. AppToken y GroupToken validos del mismo tenant tambien pueden administrarlo, aunque no pueden crear grants:
$grantId = $response['data']['id_lightpath_grant']; Apollo::proteus()->lightPath()->extendGrant($grantId, 3600); Apollo::proteus()->lightPath()->deleteGrant($grantId); // En un proceso autenticado con AppToken: Apollo::proteus()->asApplication()->lightPath()->extendGrant($grantId, 3600);
Miniaturas de media
Para solicitar la miniatura de un recurso de media, usa MediaResource::thumbnail().
$response = Apollo::proteus()->media()->thumbnail($mediaId);
Esta llamada aprovecha el mismo endpoint de descarga y solicita el formato
thumb mediante ext=thumb.
La URL generada tiene forma https://lightpath.example.com/m/{token}. El token
opaco es la unica credencial y quien tenga la URL puede consumirla hasta
url_expires_at.
Uso en jobs y contextos sin usuario
Cuando necesites hacer llamadas desde un job, un comando o cualquier contexto donde no haya sesion de usuario activa, usa asApplication(). Esto fuerza a todas las operaciones del modulo a usar autenticacion de aplicacion (sin X-User-Token) en lugar de autenticacion de usuario:
// En un job — sin sesion HTTP, sin token de usuario $proteus = Apollo::proteus()->asApplication(); $proteus->media()->index(['type' => 'audio']); $proteus->directories()->show($directoryId);
Tambien puedes inyectar el entrypoint principal:
use Ometra\Apollo\Sdk\Apollo; public function __invoke(Apollo $apollo): array { return $apollo->proteus()->media()->index(['type' => 'image']); }
Pulse expone groups()->index() sobre el endpoint ignis/groups.
Exposicion de grupos Ignis (inbound, opt-in)
Apollo es solo outbound por defecto. Cuando lo habilitas, el SDK registra una ruta inbound GET /{prefix}/groups en la app host para que clientes externos (incluido Pulse) puedan descubrir los grupos de la app. No agrega llamadas HTTP hacia Ignis.
Habilitar la ruta
La ruta esta deshabilitada por defecto. Habilitala con una variable de entorno:
APOLLO_IGNIS_GROUPS_ENABLED=true
O publica la configuracion y edita config/apollo.php:
'ignis_groups' => [ 'enabled' => true, 'route_prefix' => 'api/ignis', 'middleware' => ['caronte.application:tenant_required'], ],
Con enabled=false (por defecto) no se registra ninguna ruta y GET /api/ignis/groups devuelve 404.
Proveer la implementacion del contrato
Para exponer grupos reales, crea una clase que implemente Ometra\Apollo\Sdk\Contracts\IgnisGroupContract:
namespace App\Services; use Ometra\Apollo\Sdk\Contracts\IgnisGroupContract; use Ometra\Apollo\Sdk\DTO\ExternalGroupDTO; final class HostGroupProvider implements IgnisGroupContract { public function getGroups(): array { return [ ExternalGroupDTO::fromArray([ 'name' => 'Mi grupo', 'external_id' => 'grupo-1', 'media_type' => ['video', 'audio'], 'play_modifiers' => ['frequency' => 2], ]), ]; } }
Registra esa clase en un service provider de la app host:
use App\Services\HostGroupProvider; use Ometra\Apollo\Sdk\Contracts\IgnisGroupContract; $this->app->bind(IgnisGroupContract::class, HostGroupProvider::class);
Si APOLLO_IGNIS_GROUPS_ENABLED=true y la app host no registra el contrato, el SDK falla al arrancar para no exponer datos dummy por accidente.
Prefijo de ruta y middleware
El prefijo por defecto es api/ignis (la ruta queda en GET /api/ignis/groups). Cambialo con route_prefix:
'ignis_groups' => [ 'enabled' => true, 'route_prefix' => 'api/custom/ignis', // GET /api/custom/ignis/groups ],
La ruta esta protegida por el middleware caronte.application:tenant_required. Las peticiones sin contexto de tenant valido son rechazadas por Caronte (401/403) antes de llegar al controlador. Puedes overridear la pila de middleware con la clave middleware.
API
El contrato completo esta en docs/api-contract.md.
Pruebas
composer test
La suite valida identidad Apollo, configuracion modular, autenticacion Caronte, rutas Proteus, ausencia de API flat y limpieza legacy.