mirror of
https://github.com/sakaljurgis/actions-api.git
synced 2026-07-08 22:07:40 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
// $schedule->command('inspire')->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*/
|
||||
protected function commands(): void
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* The list of the inputs that are never flashed to the session on validation exceptions.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\Google\DTO\ImageGenerationRequest;
|
||||
use App\Services\Google\DTO\ImageGenerationResponse;
|
||||
use App\Services\Google\Error\APIError;
|
||||
use App\Services\Google\ImageAspectRatios;
|
||||
use App\Services\Google\ImageGenerationService;
|
||||
use App\Services\Image\ImageService;
|
||||
use Exception;
|
||||
use Google\Exception as GoogleException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class GoogleImageGenerationController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ImageService $imageService,
|
||||
private readonly ImageGenerationService $imageGenerationService,
|
||||
) {
|
||||
}
|
||||
|
||||
public function generateImage(Request $request): JsonResponse|string
|
||||
{
|
||||
try {
|
||||
$imageGenerationRequest = $this->buildImageGenerationRequest($request);
|
||||
$imageGenerationResponse = $this->imageGenerationService->generateImage($imageGenerationRequest);
|
||||
|
||||
return new JsonResponse(
|
||||
['generations' => $this->handleGenerationResponse($imageGenerationResponse)]
|
||||
);
|
||||
} catch (GoogleException|Exception|APIError $e) {
|
||||
return new JsonResponse([
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildImageGenerationRequest(Request $request): ImageGenerationRequest
|
||||
{
|
||||
// add nulls for optional fields
|
||||
$request->merge([
|
||||
'aspect_ratio' => $request->input('aspect_ratio'),
|
||||
'negative_prompt' => $request->input('negative_prompt'),
|
||||
'sample_count' => $request->input('sample_count'),
|
||||
]);
|
||||
|
||||
$values = $request->validate([
|
||||
'prompt' => 'required|string',
|
||||
'aspect_ratio' => [Rule::enum(ImageAspectRatios::class), 'nullable'],
|
||||
'negative_prompt' => 'string|nullable',
|
||||
'sample_count' => 'integer|nullable',
|
||||
]);
|
||||
|
||||
|
||||
return new ImageGenerationRequest(
|
||||
$values['prompt'],
|
||||
$values['negative_prompt'],
|
||||
$values['sample_count'],
|
||||
$values['aspect_ratio'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function handleGenerationResponse(ImageGenerationResponse $generationResponseData): array
|
||||
{
|
||||
if (isset($generationResponseData->error)) {
|
||||
throw $generationResponseData->error;
|
||||
}
|
||||
|
||||
$generations = [];
|
||||
|
||||
foreach ($generationResponseData->predictions as $prediction) {
|
||||
$savedImage = $this->imageService->saveImage($prediction->bytesBase64Encoded, $prediction->mimeType);
|
||||
$generations[] = $savedImage;
|
||||
}
|
||||
return $generations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array<int, class-string|string>
|
||||
*/
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array<string, array<int, class-string|string>>
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's middleware aliases.
|
||||
*
|
||||
* Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
|
||||
*
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $middlewareAliases = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
|
||||
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Authenticate extends Middleware
|
||||
{
|
||||
/**
|
||||
* Get the path the user should be redirected to when they are not authenticated.
|
||||
*/
|
||||
protected function redirectTo(Request $request): ?string
|
||||
{
|
||||
return $request->expectsJson() ? null : route('login');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
|
||||
|
||||
class PreventRequestsDuringMaintenance extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be reachable while maintenance mode is enabled.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, string ...$guards): Response
|
||||
{
|
||||
$guards = empty($guards) ? [null] : $guards;
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||
|
||||
class TrustHosts extends Middleware
|
||||
{
|
||||
/**
|
||||
* Get the host patterns that should be trusted.
|
||||
*
|
||||
* @return array<int, string|null>
|
||||
*/
|
||||
public function hosts(): array
|
||||
{
|
||||
return [
|
||||
$this->allSubdomainsOfApplicationUrl(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array<int, string>|string|null
|
||||
*/
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
|
||||
|
||||
class ValidateSignature extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the query string parameters that should be ignored.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
// 'fbclid',
|
||||
// 'utm_campaign',
|
||||
// 'utm_content',
|
||||
// 'utm_medium',
|
||||
// 'utm_source',
|
||||
// 'utm_term',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasApiTokens, HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
// use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The model to policy mappings for the application.
|
||||
*
|
||||
* @var array<class-string, class-string>
|
||||
*/
|
||||
protected $policies = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class BroadcastServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Broadcast::routes();
|
||||
|
||||
require base_path('routes/channels.php');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event to listener mappings for the application.
|
||||
*
|
||||
* @var array<class-string, array<int, class-string>>
|
||||
*/
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if events and listeners should be automatically discovered.
|
||||
*/
|
||||
public function shouldDiscoverEvents(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The path to your application's "home" route.
|
||||
*
|
||||
* Typically, users are redirected here after authentication.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/home';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, and other route configuration.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||
});
|
||||
|
||||
$this->routes(function () {
|
||||
Route::middleware('api')
|
||||
->prefix('api')
|
||||
->group(base_path('routes/api.php'));
|
||||
|
||||
Route::middleware('web')
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
use Google\Client;
|
||||
|
||||
class ApiAuthService
|
||||
{
|
||||
/**
|
||||
* @throws \Google\Exception
|
||||
*/
|
||||
public function getAccessToken(): string
|
||||
{
|
||||
$client = new Client();
|
||||
$serviceAccountKeyPath = env('GOOGLE_APPLICATION_CREDENTIALS');
|
||||
$client->setAuthConfig($serviceAccountKeyPath);
|
||||
$client->addScope('https://www.googleapis.com/auth/cloud-platform');
|
||||
$client->fetchAccessTokenWithAssertion();
|
||||
|
||||
// todo - cache, refresh, etc
|
||||
$accessToken = $client->getAccessToken();
|
||||
|
||||
return $accessToken['access_token'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google\DTO;
|
||||
|
||||
class ImageGenerationPrediction
|
||||
{
|
||||
public readonly string $bytesBase64Encoded;
|
||||
public readonly string $mimeType;
|
||||
|
||||
public function __construct(string $bytesBase64Encoded, string $mimeType)
|
||||
{
|
||||
$this->bytesBase64Encoded = $bytesBase64Encoded;
|
||||
$this->mimeType = $mimeType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google\DTO;
|
||||
|
||||
class ImageGenerationRequest
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $prompt,
|
||||
public readonly ?string $negativePrompt = null,
|
||||
public readonly ?int $sampleCount = null,
|
||||
public readonly ?string $aspectRatio = null,
|
||||
public readonly ?string $safetySetting = null,
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google\DTO;
|
||||
|
||||
use App\Services\Google\Error\APIError;
|
||||
|
||||
class ImageGenerationResponse
|
||||
{
|
||||
/**
|
||||
* @var \App\Services\Google\DTO\ImageGenerationPrediction[]
|
||||
*/
|
||||
public readonly array $predictions;
|
||||
public readonly ?APIError $error;
|
||||
|
||||
public function __construct(array $rawResponse)
|
||||
{
|
||||
if (isset($rawResponse['predictions']) && count($rawResponse['predictions']) > 0) {
|
||||
$predictions = [];
|
||||
foreach ($rawResponse['predictions'] as $prediction) {
|
||||
$predictions[] = new ImageGenerationPrediction($prediction['bytesBase64Encoded'], $prediction['mimeType']);
|
||||
}
|
||||
$this->predictions = $predictions;
|
||||
$this->error = null;
|
||||
}
|
||||
|
||||
if (isset($rawResponse['error'])) {
|
||||
$error = $rawResponse['error'];
|
||||
$this->error = new APIError($error['message'], $error['code']);
|
||||
}
|
||||
|
||||
if (!isset($this->predictions) && !isset($this->error)) {
|
||||
$this->error = new APIError('An unknown error occurred.', 0,);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
class EndpointBuilderService
|
||||
{
|
||||
public function buildPredictionEndpoint(string $model): string
|
||||
{
|
||||
return $this->buildEndpoint($model, 'predict');
|
||||
}
|
||||
|
||||
public function buildGenerationEndpoint(string $model): string
|
||||
{
|
||||
return $this->buildEndpoint($model, 'generateContent');
|
||||
}
|
||||
|
||||
private function buildEndpoint(string $model, string $action): string
|
||||
{
|
||||
$projectId = env('GOOGLE_CLOUD_PROJECT');
|
||||
$location = env('GOOGLE_LOCATION');
|
||||
$modelPath = 'projects/' . $projectId .
|
||||
'/locations/' . $location .
|
||||
'/publishers/google/models/' . $model .
|
||||
':' . $action;
|
||||
|
||||
return 'https://' . $location . '-aiplatform.googleapis.com/v1/' . $modelPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google\Error;
|
||||
|
||||
use Exception;
|
||||
|
||||
class APIError extends Exception
|
||||
{
|
||||
public $code;
|
||||
public $message;
|
||||
|
||||
public function __construct(string $message, int $code)
|
||||
{
|
||||
parent::__construct($message);
|
||||
|
||||
$this->code = $code;
|
||||
$this->message = $message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
/**
|
||||
* "aspectRatio": "1:1, 9:16, 16:9, 3:4, or 4:3. default 1:1",
|
||||
*/
|
||||
enum ImageAspectRatios: string
|
||||
{
|
||||
case _1_1 = '1:1';
|
||||
case _9_16 = '9:16';
|
||||
case _16_9 = '16:9';
|
||||
case _3_4 = '3:4';
|
||||
case _4_3 = '4:3';
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
/**
|
||||
* "safetySetting": "block_none, block_only_high, block_medium_and_above, block_low_and_above. default block_medium_and_above"
|
||||
*/
|
||||
enum ImageGenerationSafetySettings: string
|
||||
{
|
||||
case BLOCK_NONE = 'block_none';
|
||||
case BLOCK_ONLY_HIGH = 'block_only_high';
|
||||
case BLOCK_MEDIUM_AND_ABOVE = 'block_medium_and_above';
|
||||
case BLOCK_LOW_AND_ABOVE = 'block_low_and_above';
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Google;
|
||||
|
||||
use App\Services\Google\DTO\ImageGenerationRequest;
|
||||
use App\Services\Google\DTO\ImageGenerationResponse;
|
||||
|
||||
/**
|
||||
* ref: https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/imagen-api
|
||||
*/
|
||||
class ImageGenerationService
|
||||
{
|
||||
private readonly string $imagenModel;
|
||||
|
||||
public function __construct(
|
||||
private readonly ApiAuthService $apiAuthService,
|
||||
private readonly EndpointBuilderService $endpointBuilderService,
|
||||
)
|
||||
{
|
||||
$this->imagenModel = env('GOOGLE_IMAGEN_MODEL');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function generateImage(ImageGenerationRequest $request): ImageGenerationResponse
|
||||
{
|
||||
$requestBody = $this->buildRequestBody($request);
|
||||
|
||||
return $this->sendGenerationRequest($requestBody);
|
||||
}
|
||||
|
||||
private function buildRequestBody(ImageGenerationRequest $request): string {
|
||||
$instance = [
|
||||
'prompt' => $request->prompt,
|
||||
];
|
||||
if ($request->negativePrompt) {
|
||||
$instance['negativePrompt'] = $request->negativePrompt;
|
||||
}
|
||||
|
||||
$parameters = [
|
||||
'sampleCount' => 1,
|
||||
];
|
||||
|
||||
if ($request->sampleCount) {
|
||||
$parameters['sampleCount'] = $request->sampleCount;
|
||||
}
|
||||
if ($request->aspectRatio) {
|
||||
$parameters['aspectRatio'] = $request->aspectRatio;
|
||||
}
|
||||
if ($request->safetySetting) {
|
||||
$parameters['safetySetting'] = $request->safetySetting;
|
||||
}
|
||||
|
||||
$requestBody = [
|
||||
'instances' => [$instance],
|
||||
'parameters' => $parameters
|
||||
];
|
||||
|
||||
return json_encode($requestBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Google\Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function sendGenerationRequest(string $requestBody): ImageGenerationResponse
|
||||
{
|
||||
$endpoint = $this->endpointBuilderService->buildPredictionEndpoint($this->imagenModel);
|
||||
$accessToken = $this->apiAuthService->getAccessToken();
|
||||
|
||||
$ch = curl_init($endpoint);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
'Content-Type: application/json',
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
if (curl_errno($ch) || $response === false) {
|
||||
throw new \Exception(curl_error($ch));
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
$responseArray = json_decode($response, true);
|
||||
|
||||
return new ImageGenerationResponse($responseArray);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Image;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ImageService
|
||||
{
|
||||
/**
|
||||
* @param string $base64ImageData
|
||||
* @param string $mimeType
|
||||
* @return array ['url', 'id']
|
||||
*/
|
||||
public function saveImage(string $base64ImageData, string $mimeType): array
|
||||
{
|
||||
$imageId = uniqid();
|
||||
$imageData = base64_decode($base64ImageData);
|
||||
$ext = explode('/', $mimeType)[1];
|
||||
|
||||
Storage::put('public/images/' . $imageId . '.' . $ext , $imageData);
|
||||
$relativePath = Storage::url('public/images/' . $imageId . '.png');
|
||||
|
||||
return [
|
||||
'url' => asset($relativePath),
|
||||
'id' => $imageId,
|
||||
// todo - size, width, height, thumb, etc
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user