diff --git a/.env.example b/.env.example
index 4d3d446..b2a9f63 100644
--- a/.env.example
+++ b/.env.example
@@ -42,11 +42,6 @@ CACHE_STORE=database
MEMCACHED_HOST=127.0.0.1
-REDIS_CLIENT=phpredis
-REDIS_HOST=127.0.0.1
-REDIS_PASSWORD=null
-REDIS_PORT=6379
-
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
diff --git a/Dockerfile b/Dockerfile
index b233f83..def0b8f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,61 +1,82 @@
-FROM php:8.4.4-fpm-alpine AS builder
+FROM php:8.4.4-fpm AS builder
-# Install necessary build dependencies
-RUN apk add --no-cache \
- mysql-dev \
- postgresql-dev \
+# Install system dependencies including protobuf
+RUN apt-get update && apt-get install -y \
+ supervisor \
+ libpq-dev \
libzip-dev \
unzip \
git \
- openssl-dev \
- brotli-dev \
+ curl \
+ wget \
+ libcurl4-openssl-dev \
+ libssl-dev \
+ pkg-config \
+ protobuf-compiler \
+ protobuf-compiler-grpc \
+ libprotobuf-dev \
+ build-essential \
autoconf \
- automake \
- make \
- gcc \
- g++ \
- libc-dev \
- pcre-dev \
- $PHPIZE_DEPS
-
-# Install PHP extensions
-RUN docker-php-ext-install pdo_mysql pdo_pgsql zip \
- && docker-php-ext-configure pcntl --enable-pcntl \
- && docker-php-ext-install pcntl \
- && docker-php-ext-install opcache
-
-# Install Swoole via PECL
-RUN pecl install swoole \
- && docker-php-ext-enable swoole \
- && pecl install redis \
- && docker-php-ext-enable redis
-
-FROM php:8.4.4-fpm-alpine AS production
-
-# Install runtime dependencies (required for the extensions to work)
-RUN apk add --no-cache \
- mysql-client \
- postgresql-client \
- libzip \
- git
-
-# Copy PHP extensions from builder stage
+ libtool \
+ netcat-openbsd \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get clean
+
+# Install PHP extensions (separate layer for better caching)
+RUN docker-php-ext-install \
+ pdo_mysql \
+ zip \
+ sockets \
+ pcntl \
+ bcmath \
+ opcache
+
+# Copy backed up gRPC extension files
+COPY grpc-backup/extensions/ /usr/local/lib/php/extensions/
+COPY grpc-backup/conf.d/ /usr/local/etc/php/conf.d/
+
+# Install remaining PECL extensions
+# docke
+
+FROM php:8.4.4-fpm AS production
+
+# Install runtime dependencies
+RUN apt-get update && apt-get install -y \
+ supervisor \
+ libpq-dev \
+ libzip-dev \
+ unzip \
+ git \
+ curl \
+ libcurl4-openssl-dev \
+ libssl-dev \
+ pkg-config \
+ protobuf-compiler \
+ protobuf-compiler-grpc \
+ libprotobuf-dev \
+ netcat-openbsd \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get clean
+
+# Copy PHP extensions from builder
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
-# Copy php.ini with OPcache settings
-COPY php.ini /usr/local/etc/php/conf.d/opcache.ini
+# Clean up PECL cache to reduce image size
+RUN rm -rf /tmp/pear
-# Install Composer
-COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
+# Install Composer (separate layer)
+COPY --from=composer:2.8 /usr/bin/composer /usr/bin/composer
# Set working directory
WORKDIR /var/www
-# Copy composer files first for better caching
+# Copy PHP configuration early (rarely changes)
+COPY php.ini /usr/local/etc/php/conf.d/custom.ini
+
+# Copy composer files (for dependency caching)
COPY composer.json composer.lock ./
-# Install dependencies (this layer will be cached if composer files don't change)
# Install dependencies with production optimizations
RUN composer install \
--no-dev \
@@ -65,16 +86,37 @@ RUN composer install \
--classmap-authoritative \
&& composer clear-cache
-# Copy application files
+ # Copy supervisor configuration
+COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
+# Copy application files (most frequently changing layer - put last)
+
COPY . .
-# Set permissions
-RUN chown -R www-data:www-data /var/www \
+# Create necessary directories and set permissions
+RUN mkdir -p storage/logs storage/framework/{cache,sessions,views} bootstrap/cache \
+ && chown -R www-data:www-data /var/www \
&& chmod -R 755 /var/www/storage \
&& chmod -R 755 /var/www/bootstrap/cache
-# Expose Port
-EXPOSE 8000
+# Expose ports
+EXPOSE 50051 8080
+
+# Health check for gRPC service
+HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
+ CMD nc -z localhost 50051 || exit 1
+
+# Create startup script
+RUN echo '#!/bin/bash\n\
+set -e\n\
+echo "Starting Laravel gRPC application..."\n\
+php artisan config:cache\n\
+php artisan route:cache\n\
+php artisan view:cache\n\
+php artisan migrate --force\n\
+php artisan storage:link\n\
+echo "Starting supervisor..."\n\
+exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf\n\
+' > /usr/local/bin/start.sh \
+ && chmod +x /usr/local/bin/start.sh
-# Start Laravel Octane with Swoole
-CMD sh -c "php artisan migrate --force && php artisan storage:link && php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000"
+CMD ["/usr/local/bin/start.sh"]
diff --git a/app/Console/Commands/GrpcServeCommand.php b/app/Console/Commands/GrpcServeCommand.php
new file mode 100644
index 0000000..8c733f4
--- /dev/null
+++ b/app/Console/Commands/GrpcServeCommand.php
@@ -0,0 +1,40 @@
+option('host');
+ $port = (int)$this->option('port');
+
+ $this->info("Starting gRPC User Service on {$host}:{$port}");
+
+ try {
+ $server = new RpcServer([]);
+ $server->addHttp2Port("{$host}:{$port}");
+
+ // Register the User service
+ $server->handle(app(\App\Grpc\Services\GatewayService::class));
+
+ $this->info('gRPC User Service started successfully');
+ $this->info('Listening for requests...');
+
+ // Start the server (this will block)
+ $server->run();
+
+ } catch (\Exception $e) {
+ $this->error('Failed to start gRPC server: ' . $e->getMessage());
+ return 1;
+ }
+ return 0;
+ }
+}
diff --git a/app/External_Apis/Apis/AIApi.php b/app/External_Apis/Apis/AIApi.php
new file mode 100644
index 0000000..33d4c42
--- /dev/null
+++ b/app/External_Apis/Apis/AIApi.php
@@ -0,0 +1,19 @@
+injectUserId($data);
+ $data[] = $this->injectUserId();
$res = $this->OfferRepository->getAll($data);
$data = $this->handlingResErrorAndMessage($res);
if ($res->successful())
diff --git a/app/External_Apis/Services/ProductService.php b/app/External_Apis/Services/ProductService.php
index 19fbf2a..58513d2 100644
--- a/app/External_Apis/Services/ProductService.php
+++ b/app/External_Apis/Services/ProductService.php
@@ -30,7 +30,7 @@ public function __construct(
*/
public function index(array $data): array
{
- $this->injectUserId($data);
+ $data[] = $this->injectUserId();
$res = $this->productRepository->getAll($data);
$data = $this->handlingResErrorAndMessage($res);
if ($res->successful())
diff --git a/app/Grpc/BytesRequest.php b/app/Grpc/BytesRequest.php
new file mode 100644
index 0000000..17e9989
--- /dev/null
+++ b/app/Grpc/BytesRequest.php
@@ -0,0 +1,202 @@
+gateway.BytesRequest
+ */
+class BytesRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field string service = 1;
+ */
+ protected $service = '';
+ /**
+ * Generated from protobuf field string method = 2;
+ */
+ protected $method = '';
+ /**
+ * Generated from protobuf field bytes payload = 3;
+ */
+ protected $payload = '';
+ /**
+ * "application/json", "application/protobuf", etc.
+ *
+ * Generated from protobuf field string content_type = 4;
+ */
+ protected $content_type = '';
+ /**
+ * Generated from protobuf field map headers = 5;
+ */
+ private $headers;
+ /**
+ * Generated from protobuf field string request_id = 6;
+ */
+ protected $request_id = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $service
+ * @type string $method
+ * @type string $payload
+ * @type string $content_type
+ * "application/json", "application/protobuf", etc.
+ * @type array|\Google\Protobuf\Internal\MapField $headers
+ * @type string $request_id
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\Gateway\Apigw::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field string service = 1;
+ * @return string
+ */
+ public function getService()
+ {
+ return $this->service;
+ }
+
+ /**
+ * Generated from protobuf field string service = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setService($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->service = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string method = 2;
+ * @return string
+ */
+ public function getMethod()
+ {
+ return $this->method;
+ }
+
+ /**
+ * Generated from protobuf field string method = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setMethod($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->method = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bytes payload = 3;
+ * @return string
+ */
+ public function getPayload()
+ {
+ return $this->payload;
+ }
+
+ /**
+ * Generated from protobuf field bytes payload = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setPayload($var)
+ {
+ GPBUtil::checkString($var, False);
+ $this->payload = $var;
+
+ return $this;
+ }
+
+ /**
+ * "application/json", "application/protobuf", etc.
+ *
+ * Generated from protobuf field string content_type = 4;
+ * @return string
+ */
+ public function getContentType()
+ {
+ return $this->content_type;
+ }
+
+ /**
+ * "application/json", "application/protobuf", etc.
+ *
+ * Generated from protobuf field string content_type = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setContentType($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->content_type = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field map headers = 5;
+ * @return \Google\Protobuf\Internal\MapField
+ */
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ /**
+ * Generated from protobuf field map headers = 5;
+ * @param array|\Google\Protobuf\Internal\MapField $var
+ * @return $this
+ */
+ public function setHeaders($var)
+ {
+ $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->headers = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string request_id = 6;
+ * @return string
+ */
+ public function getRequestId()
+ {
+ return $this->request_id;
+ }
+
+ /**
+ * Generated from protobuf field string request_id = 6;
+ * @param string $var
+ * @return $this
+ */
+ public function setRequestId($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->request_id = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/BytesResponse.php b/app/Grpc/BytesResponse.php
new file mode 100644
index 0000000..b6e6228
--- /dev/null
+++ b/app/Grpc/BytesResponse.php
@@ -0,0 +1,247 @@
+gateway.BytesResponse
+ */
+class BytesResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field bool success = 1;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field int32 status_code = 2;
+ */
+ protected $status_code = 0;
+ /**
+ * Generated from protobuf field bytes data = 3;
+ */
+ protected $data = '';
+ /**
+ * Generated from protobuf field string content_type = 4;
+ */
+ protected $content_type = '';
+ /**
+ * Generated from protobuf field .gateway.ErrorInfo error = 5;
+ */
+ protected $error = null;
+ /**
+ * Generated from protobuf field map headers = 6;
+ */
+ private $headers;
+ /**
+ * Generated from protobuf field string request_id = 7;
+ */
+ protected $request_id = '';
+ /**
+ * Generated from protobuf field .google.protobuf.Timestamp timestamp = 8;
+ */
+ protected $timestamp = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type bool $success
+ * @type int $status_code
+ * @type string $data
+ * @type string $content_type
+ * @type \App\Grpc\ErrorInfo $error
+ * @type array|\Google\Protobuf\Internal\MapField $headers
+ * @type string $request_id
+ * @type \Google\Protobuf\Timestamp $timestamp
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\Gateway\Apigw::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field bool success = 1;
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 1;
+ * @param bool $var
+ * @return $this
+ */
+ public function setSuccess($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->success = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field int32 status_code = 2;
+ * @return int
+ */
+ public function getStatusCode()
+ {
+ return $this->status_code;
+ }
+
+ /**
+ * Generated from protobuf field int32 status_code = 2;
+ * @param int $var
+ * @return $this
+ */
+ public function setStatusCode($var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->status_code = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bytes data = 3;
+ * @return string
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Generated from protobuf field bytes data = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setData($var)
+ {
+ GPBUtil::checkString($var, False);
+ $this->data = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string content_type = 4;
+ * @return string
+ */
+ public function getContentType()
+ {
+ return $this->content_type;
+ }
+
+ /**
+ * Generated from protobuf field string content_type = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setContentType($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->content_type = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .gateway.ErrorInfo error = 5;
+ * @return \App\Grpc\ErrorInfo
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * Generated from protobuf field .gateway.ErrorInfo error = 5;
+ * @param \App\Grpc\ErrorInfo $var
+ * @return $this
+ */
+ public function setError($var)
+ {
+ GPBUtil::checkMessage($var, \App\Grpc\ErrorInfo::class);
+ $this->error = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field map headers = 6;
+ * @return \Google\Protobuf\Internal\MapField
+ */
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ /**
+ * Generated from protobuf field map headers = 6;
+ * @param array|\Google\Protobuf\Internal\MapField $var
+ * @return $this
+ */
+ public function setHeaders($var)
+ {
+ $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->headers = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string request_id = 7;
+ * @return string
+ */
+ public function getRequestId()
+ {
+ return $this->request_id;
+ }
+
+ /**
+ * Generated from protobuf field string request_id = 7;
+ * @param string $var
+ * @return $this
+ */
+ public function setRequestId($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->request_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .google.protobuf.Timestamp timestamp = 8;
+ * @return \Google\Protobuf\Timestamp
+ */
+ public function getTimestamp()
+ {
+ return $this->timestamp;
+ }
+
+ /**
+ * Generated from protobuf field .google.protobuf.Timestamp timestamp = 8;
+ * @param \Google\Protobuf\Timestamp $var
+ * @return $this
+ */
+ public function setTimestamp($var)
+ {
+ GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+ $this->timestamp = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/Controllers/AuthController.php b/app/Grpc/Controllers/AuthController.php
new file mode 100644
index 0000000..d14d9df
--- /dev/null
+++ b/app/Grpc/Controllers/AuthController.php
@@ -0,0 +1,36 @@
+setSpecifiedService($request, $this->defaultService);
+ return $this->grpcService->forwardGrpcToService($request);
+ }
+
+ public function login(FlexibleRequest $request): FlexibleResponse
+ {
+ $this->setSpecifiedService($request, $this->defaultService);
+ return $this->grpcService->forwardGrpcToService($request);
+ }
+
+ public function validateToken(FlexibleRequest $request): FlexibleResponse
+ {
+ $this->setSpecifiedService($request, $this->defaultService);
+ return $this->grpcService->forwardGrpcToService($request);
+ }
+
+}
diff --git a/app/Grpc/Controllers/BaseGrpcController.php b/app/Grpc/Controllers/BaseGrpcController.php
new file mode 100644
index 0000000..e381cd6
--- /dev/null
+++ b/app/Grpc/Controllers/BaseGrpcController.php
@@ -0,0 +1,20 @@
+getService();
+ $services = explode('/', $service);
+ $service = $services[1] ?? $default ?? $services[0];
+ \Log::info("Forwarding request to service: {$services[0]}/{$service}, method: {$request->getMethod()}");
+ $request->setService($service);
+ }
+}
diff --git a/app/Grpc/Controllers/ProductController.php b/app/Grpc/Controllers/ProductController.php
new file mode 100644
index 0000000..662e738
--- /dev/null
+++ b/app/Grpc/Controllers/ProductController.php
@@ -0,0 +1,43 @@
+getPayload()) {
+ $data = json_decode($payload->getValue(), true);
+ $data = array_merge($data, $this->productService->injectUserId());
+ $request->setPayload($payload->setValue(json_encode($data)));
+ }
+ return $this->grpcService->forwardGrpcToService($request);
+ }
+
+ public function show(FlexibleRequest $request): FlexibleResponse
+ {
+ return $this->grpcService->forwardGrpcToService($request);
+ }
+}
diff --git a/app/Grpc/Controllers/UserController.php b/app/Grpc/Controllers/UserController.php
new file mode 100644
index 0000000..c3bd06f
--- /dev/null
+++ b/app/Grpc/Controllers/UserController.php
@@ -0,0 +1,22 @@
+setSpecifiedService($request);
+ return $this->grpcService->forwardGrpcToService($request);
+ }
+
+}
diff --git a/app/Grpc/ErrorInfo.php b/app/Grpc/ErrorInfo.php
new file mode 100644
index 0000000..3b840b0
--- /dev/null
+++ b/app/Grpc/ErrorInfo.php
@@ -0,0 +1,141 @@
+gateway.ErrorInfo
+ */
+class ErrorInfo extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field string message = 1;
+ */
+ protected $message = '';
+ /**
+ * Generated from protobuf field string code = 2;
+ */
+ protected $code = '';
+ /**
+ * Generated from protobuf field repeated string details = 3;
+ */
+ private $details;
+ /**
+ * Generated from protobuf field map metadata = 4;
+ */
+ private $metadata;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $message
+ * @type string $code
+ * @type string[]|\Google\Protobuf\Internal\RepeatedField $details
+ * @type array|\Google\Protobuf\Internal\MapField $metadata
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\Gateway\Apigw::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field string message = 1;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string code = 2;
+ * @return string
+ */
+ public function getCode()
+ {
+ return $this->code;
+ }
+
+ /**
+ * Generated from protobuf field string code = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setCode($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->code = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field repeated string details = 3;
+ * @return \Google\Protobuf\Internal\RepeatedField
+ */
+ public function getDetails()
+ {
+ return $this->details;
+ }
+
+ /**
+ * Generated from protobuf field repeated string details = 3;
+ * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+ * @return $this
+ */
+ public function setDetails($var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->details = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field map metadata = 4;
+ * @return \Google\Protobuf\Internal\MapField
+ */
+ public function getMetadata()
+ {
+ return $this->metadata;
+ }
+
+ /**
+ * Generated from protobuf field map metadata = 4;
+ * @param array|\Google\Protobuf\Internal\MapField $var
+ * @return $this
+ */
+ public function setMetadata($var)
+ {
+ $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->metadata = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/FlexibleRequest.php b/app/Grpc/FlexibleRequest.php
new file mode 100644
index 0000000..f448e55
--- /dev/null
+++ b/app/Grpc/FlexibleRequest.php
@@ -0,0 +1,203 @@
+gateway.FlexibleRequest
+ */
+class FlexibleRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Service identifier (user-service, product-service, etc.)
+ *
+ * Generated from protobuf field string service = 1;
+ */
+ protected $service = '';
+ /**
+ * Method/endpoint identifier
+ *
+ * Generated from protobuf field string method = 2;
+ */
+ protected $method = '';
+ /**
+ * Generic payload - can be any protobuf message
+ *
+ * Generated from protobuf field .google.protobuf.Any payload = 3;
+ */
+ protected $payload = null;
+ /**
+ * Request metadata
+ *
+ * Generated from protobuf field map headers = 4;
+ */
+ private $headers;
+ /**
+ * Optional request ID for tracing
+ *
+ * Generated from protobuf field string request_id = 5;
+ */
+ protected $request_id = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $service
+ * Service identifier (user-service, product-service, etc.)
+ * @type string $method
+ * Method/endpoint identifier
+ * @type \Google\Protobuf\Any $payload
+ * Generic payload - can be any protobuf message
+ * @type array|\Google\Protobuf\Internal\MapField $headers
+ * Request metadata
+ * @type string $request_id
+ * Optional request ID for tracing
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\Gateway\Apigw::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Service identifier (user-service, product-service, etc.)
+ *
+ * Generated from protobuf field string service = 1;
+ * @return string
+ */
+ public function getService()
+ {
+ return $this->service;
+ }
+
+ /**
+ * Service identifier (user-service, product-service, etc.)
+ *
+ * Generated from protobuf field string service = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setService($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->service = $var;
+
+ return $this;
+ }
+
+ /**
+ * Method/endpoint identifier
+ *
+ * Generated from protobuf field string method = 2;
+ * @return string
+ */
+ public function getMethod()
+ {
+ return $this->method;
+ }
+
+ /**
+ * Method/endpoint identifier
+ *
+ * Generated from protobuf field string method = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setMethod($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->method = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generic payload - can be any protobuf message
+ *
+ * Generated from protobuf field .google.protobuf.Any payload = 3;
+ * @return \Google\Protobuf\Any
+ */
+ public function getPayload()
+ {
+ return $this->payload;
+ }
+
+ /**
+ * Generic payload - can be any protobuf message
+ *
+ * Generated from protobuf field .google.protobuf.Any payload = 3;
+ * @param \Google\Protobuf\Any $var
+ * @return $this
+ */
+ public function setPayload($var)
+ {
+ GPBUtil::checkMessage($var, \Google\Protobuf\Any::class);
+ $this->payload = $var;
+
+ return $this;
+ }
+
+ /**
+ * Request metadata
+ *
+ * Generated from protobuf field map headers = 4;
+ * @return \Google\Protobuf\Internal\MapField
+ */
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ /**
+ * Request metadata
+ *
+ * Generated from protobuf field map headers = 4;
+ * @param array|\Google\Protobuf\Internal\MapField $var
+ * @return $this
+ */
+ public function setHeaders($var)
+ {
+ $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->headers = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Optional request ID for tracing
+ *
+ * Generated from protobuf field string request_id = 5;
+ * @return string
+ */
+ public function getRequestId()
+ {
+ return $this->request_id;
+ }
+
+ /**
+ * Optional request ID for tracing
+ *
+ * Generated from protobuf field string request_id = 5;
+ * @param string $var
+ * @return $this
+ */
+ public function setRequestId($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->request_id = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/FlexibleResponse.php b/app/Grpc/FlexibleResponse.php
new file mode 100644
index 0000000..3769c41
--- /dev/null
+++ b/app/Grpc/FlexibleResponse.php
@@ -0,0 +1,271 @@
+gateway.FlexibleResponse
+ */
+class FlexibleResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Success indicator
+ *
+ * Generated from protobuf field bool success = 1;
+ */
+ protected $success = false;
+ /**
+ * HTTP-like status code
+ *
+ * Generated from protobuf field int32 status_code = 2;
+ */
+ protected $status_code = 0;
+ /**
+ * Generic response data - can be any protobuf message or raw data
+ *
+ * Generated from protobuf field .google.protobuf.Any data = 3;
+ */
+ protected $data = null;
+ /**
+ * Error information (if any)
+ *
+ * Generated from protobuf field .gateway.ErrorInfo error = 4;
+ */
+ protected $error = null;
+ /**
+ * Response metadata
+ *
+ * Generated from protobuf field map headers = 5;
+ */
+ private $headers;
+ /**
+ * Request ID for tracing
+ *
+ * Generated from protobuf field string request_id = 6;
+ */
+ protected $request_id = '';
+ /**
+ * Response timestamp
+ *
+ * Generated from protobuf field .google.protobuf.Timestamp timestamp = 7;
+ */
+ protected $timestamp = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type bool $success
+ * Success indicator
+ * @type int $status_code
+ * HTTP-like status code
+ * @type \Google\Protobuf\Any $data
+ * Generic response data - can be any protobuf message or raw data
+ * @type \App\Grpc\ErrorInfo $error
+ * Error information (if any)
+ * @type array|\Google\Protobuf\Internal\MapField $headers
+ * Response metadata
+ * @type string $request_id
+ * Request ID for tracing
+ * @type \Google\Protobuf\Timestamp $timestamp
+ * Response timestamp
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\Gateway\Apigw::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Success indicator
+ *
+ * Generated from protobuf field bool success = 1;
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Success indicator
+ *
+ * Generated from protobuf field bool success = 1;
+ * @param bool $var
+ * @return $this
+ */
+ public function setSuccess($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->success = $var;
+
+ return $this;
+ }
+
+ /**
+ * HTTP-like status code
+ *
+ * Generated from protobuf field int32 status_code = 2;
+ * @return int
+ */
+ public function getStatusCode()
+ {
+ return $this->status_code;
+ }
+
+ /**
+ * HTTP-like status code
+ *
+ * Generated from protobuf field int32 status_code = 2;
+ * @param int $var
+ * @return $this
+ */
+ public function setStatusCode($var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->status_code = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generic response data - can be any protobuf message or raw data
+ *
+ * Generated from protobuf field .google.protobuf.Any data = 3;
+ * @return \Google\Protobuf\Any
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Generic response data - can be any protobuf message or raw data
+ *
+ * Generated from protobuf field .google.protobuf.Any data = 3;
+ * @param \Google\Protobuf\Any $var
+ * @return $this
+ */
+ public function setData($var)
+ {
+ GPBUtil::checkMessage($var, \Google\Protobuf\Any::class);
+ $this->data = $var;
+
+ return $this;
+ }
+
+ /**
+ * Error information (if any)
+ *
+ * Generated from protobuf field .gateway.ErrorInfo error = 4;
+ * @return \App\Grpc\ErrorInfo
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * Error information (if any)
+ *
+ * Generated from protobuf field .gateway.ErrorInfo error = 4;
+ * @param \App\Grpc\ErrorInfo $var
+ * @return $this
+ */
+ public function setError($var)
+ {
+ GPBUtil::checkMessage($var, \App\Grpc\ErrorInfo::class);
+ $this->error = $var;
+
+ return $this;
+ }
+
+ /**
+ * Response metadata
+ *
+ * Generated from protobuf field map headers = 5;
+ * @return \Google\Protobuf\Internal\MapField
+ */
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ /**
+ * Response metadata
+ *
+ * Generated from protobuf field map headers = 5;
+ * @param array|\Google\Protobuf\Internal\MapField $var
+ * @return $this
+ */
+ public function setHeaders($var)
+ {
+ $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->headers = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Request ID for tracing
+ *
+ * Generated from protobuf field string request_id = 6;
+ * @return string
+ */
+ public function getRequestId()
+ {
+ return $this->request_id;
+ }
+
+ /**
+ * Request ID for tracing
+ *
+ * Generated from protobuf field string request_id = 6;
+ * @param string $var
+ * @return $this
+ */
+ public function setRequestId($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->request_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * Response timestamp
+ *
+ * Generated from protobuf field .google.protobuf.Timestamp timestamp = 7;
+ * @return \Google\Protobuf\Timestamp
+ */
+ public function getTimestamp()
+ {
+ return $this->timestamp;
+ }
+
+ /**
+ * Response timestamp
+ *
+ * Generated from protobuf field .google.protobuf.Timestamp timestamp = 7;
+ * @param \Google\Protobuf\Timestamp $var
+ * @return $this
+ */
+ public function setTimestamp($var)
+ {
+ GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class);
+ $this->timestamp = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/Gateway/Apigw.php b/app/Grpc/Gateway/Apigw.php
new file mode 100644
index 0000000..e9912cc
--- /dev/null
+++ b/app/Grpc/Gateway/Apigw.php
@@ -0,0 +1,26 @@
+internalAddGeneratedFile(hex2bin(
+ "0afc0d0a0b61706967772e70726f746f1207676174657761791a1f676f6f676c652f70726f746f6275662f74696d657374616d702e70726f746f22d5010a0f466c657869626c6552657175657374120f0a0773657276696365180120012809120e0a066d6574686f6418022001280912250a077061796c6f616418032001280b32142e676f6f676c652e70726f746f6275662e416e7912360a076865616465727318042003280b32252e676174657761792e466c657869626c65526571756573742e48656164657273456e74727912120a0a726571756573745f69641805200128091a2e0a0c48656164657273456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122ab020a10466c657869626c65526573706f6e7365120f0a077375636365737318012001280812130a0b7374617475735f636f646518022001280512220a046461746118032001280b32142e676f6f676c652e70726f746f6275662e416e7912210a056572726f7218042001280b32122e676174657761792e4572726f72496e666f12370a076865616465727318052003280b32262e676174657761792e466c657869626c65526573706f6e73652e48656164657273456e74727912120a0a726571756573745f6964180620012809122d0a0974696d657374616d7018072001280b321a2e676f6f676c652e70726f746f6275662e54696d657374616d701a2e0a0c48656164657273456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122cf010a0c427974657352657175657374120f0a0773657276696365180120012809120e0a066d6574686f64180220012809120f0a077061796c6f616418032001280c12140a0c636f6e74656e745f7479706518042001280912330a076865616465727318052003280b32222e676174657761792e4279746573526571756573742e48656164657273456e74727912120a0a726571756573745f69641806200128091a2e0a0c48656164657273456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122a5020a0d4279746573526573706f6e7365120f0a077375636365737318012001280812130a0b7374617475735f636f6465180220012805120c0a046461746118032001280c12140a0c636f6e74656e745f7479706518042001280912210a056572726f7218052001280b32122e676174657761792e4572726f72496e666f12340a076865616465727318062003280b32232e676174657761792e4279746573526573706f6e73652e48656164657273456e74727912120a0a726571756573745f6964180720012809122d0a0974696d657374616d7018082001280b321a2e676f6f676c652e70726f746f6275662e54696d657374616d701a2e0a0c48656164657273456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122a0010a094572726f72496e666f120f0a076d657373616765180120012809120c0a04636f6465180220012809120f0a0764657461696c7318032003280912320a086d6574616461746118042003280b32202e676174657761792e4572726f72496e666f2e4d65746164617461456e7472791a2f0a0d4d65746164617461456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122250a124865616c7468436865636b52657175657374120f0a0773657276696365180120012809228d010a134865616c7468436865636b526573706f6e7365123a0a0673746174757318012001280e322a2e676174657761792e4865616c7468436865636b526573706f6e73652e53657276696e67537461747573223a0a0d53657276696e67537461747573120b0a07554e4b4e4f574e1000120b0a0753455256494e471001120f0a0b4e4f545f53455256494e47100232eb010a0e476174657761795365727669636512440a0d48616e646c655265717565737412182e676174657761792e466c657869626c65526571756573741a192e676174657761792e466c657869626c65526573706f6e736512430a1248616e646c6542797465735265717565737412152e676174657761792e4279746573526571756573741a162e676174657761792e4279746573526573706f6e7365124e0a1348616e646c6553747265616d5265717565737412182e676174657761792e466c657869626c65526571756573741a192e676174657761792e466c657869626c65526573706f6e73652801300132530a0d4865616c74685365727669636512420a05436865636b121b2e676174657761792e4865616c7468436865636b526571756573741a1c2e676174657761792e4865616c7468436865636b526573706f6e7365421eca02084170705c47727063e202104170705c477270635c47617465776179620670726f746f33"
+ ), true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/app/Grpc/GatewayServiceClient.php b/app/Grpc/GatewayServiceClient.php
new file mode 100644
index 0000000..dc9f317
--- /dev/null
+++ b/app/Grpc/GatewayServiceClient.php
@@ -0,0 +1,62 @@
+_simpleRequest('/gateway.GatewayService/HandleRequest',
+ $argument,
+ ['\App\Grpc\FlexibleResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * Handle any request with raw bytes (maximum flexibility)
+ * @param \App\Grpc\BytesRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\BytesResponse
+ */
+ public function HandleBytesRequest(\App\Grpc\BytesRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/gateway.GatewayService/HandleBytesRequest',
+ $argument,
+ ['\App\Grpc\BytesResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * Streaming support for real-time data
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\FlexibleResponse
+ */
+ public function HandleStreamRequest($metadata = [], $options = []) {
+ return $this->_bidiRequest('/gateway.GatewayService/HandleStreamRequest',
+ ['\App\Grpc\FlexibleResponse','decode'],
+ $metadata, $options);
+ }
+
+}
diff --git a/app/Grpc/GrpcRoute.php b/app/Grpc/GrpcRoute.php
new file mode 100644
index 0000000..55d6f70
--- /dev/null
+++ b/app/Grpc/GrpcRoute.php
@@ -0,0 +1,11 @@
+ $controller,
+ 'action' => $action
+ ];
+ }
+
+ public static function resolve(string $service, string $method): ?array
+ {
+ return self::$routes[$service][$method] ?? null;
+ }
+
+ public static function getAllRoutes(): array
+ {
+ return self::$routes;
+ }
+}
diff --git a/app/Grpc/GrpcServiceRouter.php b/app/Grpc/GrpcServiceRouter.php
new file mode 100644
index 0000000..3de8200
--- /dev/null
+++ b/app/Grpc/GrpcServiceRouter.php
@@ -0,0 +1,19 @@
+serviceName = $serviceName;
+ }
+
+ public function method(string $methodName, string $controller, string $action): self
+ {
+ GrpcRouteRegistry::register($this->serviceName, $methodName, $controller, $action);
+ return $this;
+ }
+}
diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php
new file mode 100644
index 0000000..ba8c4f3
--- /dev/null
+++ b/app/Grpc/Services/GatewayService.php
@@ -0,0 +1,222 @@
+grpcServices = config('services.microservices_grpc');
+ }
+
+ /**
+ * Handle flexible protobuf requests
+ */
+ public function HandleClientRequest(FlexibleRequest $in): FlexibleResponse
+ {
+ $response = new FlexibleResponse();
+ $response->setRequestId($in->getRequestId());
+ $response->setTimestamp($this->getCurrentTimestamp());
+
+ // try {
+ // Get a gRPC client for the target service
+ $grpcClient = $this->getGrpcClient($in->getService());
+ if (!$grpcClient) {
+ return $this->createErrorResponse($response, 404, 'SERVICE_NOT_FOUND', 'Service not found: ' . $in->getService());
+ }
+ app(BaseGrpcController::class)->setSpecifiedService($in);
+
+ // Forward the exact same FlexibleRequest to downstream service
+ // The downstream service implements the same flexible proto
+ $metadata = [];
+ if ($bearerToken = request()->bearerToken())
+ $metadata['authorization'] = ['Bearer ' . $bearerToken];
+ list($downstreamResponse, $status) = $grpcClient->HandleRequest($in, $metadata)->wait();
+ if ($status->code === STATUS_OK) {
+ // Forward the response as-is
+ $response->setSuccess($downstreamResponse->getSuccess());
+ $response->setStatusCode($downstreamResponse->getStatusCode());
+ $response->setData($downstreamResponse->getData());
+
+ // Forward headers
+ foreach ($downstreamResponse->getHeaders() as $key => $value) {
+ $response->getHeaders()[$key] = $value;
+ }
+
+ } else {
+ // dd($downstreamResponse->getError());
+ if (is_null($downstreamResponse))
+ return $this->createErrorResponse(
+ $response,
+ 500,
+ 'GRPC_ERROR',
+ 'Downstream service, code ' . $status->code . ', returned an error: ' . $status->details
+ );
+ return $this->createErrorResponse(
+ $response,
+ $downstreamResponse->getStatusCode(),
+ 'GRPC_ERROR',
+ $downstreamResponse->getError() ? $downstreamResponse->getError()->getMessage() : 'Unknown error',
+ data: $downstreamResponse->getData()
+ );
+ }
+
+ // } catch (Exception $e) {
+
+ // Log::error('Gateway gRPC request failed', [
+ // 'service' => $in->getService(),
+ // 'method' => $in->getMethod(),
+ // 'error' => $e->getMessage()
+ // ]);
+
+ // return $this->createErrorResponse($response, 500, 'INTERNAL_ERROR', $e->getMessage());
+ // }
+
+ return $response;
+ }
+
+ public function HandleRequest(FlexibleRequest $request): FlexibleResponse
+ {
+ try {
+ // Extract service and method from the request
+ $serviceName = $request->getService(); // You'll need to add this to your FlexibleRequest
+ $methodName = $request->getMethod(); // You'll need to add this to your FlexibleRequest
+
+ // Resolve the route
+ $route = GrpcRouteRegistry::resolve($serviceName, $methodName);
+
+ if (!$route) {
+ throw new Exception("Route not found: {$serviceName}::{$methodName}");
+ }
+
+ // Instantiate controller
+ $controllerClass = "App\\Grpc\\Controllers\\{$route['controller']}";
+ $controller = app($controllerClass);
+
+ // Call the action
+ return $controller->{$route['action']}($request);
+
+ } catch (Exception $e) {
+ Log::error("Failed to handle gRPC request", [$e->getMessage()]);
+ return new FlexibleResponse([
+ 'status' => 'error',
+ 'code' => 500,
+ 'data' => json_encode(['message' => $e->getMessage()])
+ ]);
+ }
+ }
+
+ /**
+ * Get gRPC client for service
+ */
+ private function getGrpcClient(string $service): ?GatewayServiceClient
+ {
+ if (str_contains($service, '/')) {
+ $main_sub_service = explode('/', $service);
+ $service = $main_sub_service[0];
+ }
+
+ if (!isset($this->grpcServices[$service])) {
+ return null;
+ }
+
+ $serviceAddress = $this->grpcServices[$service];
+
+ try {
+ // Simplified options to avoid compatibility issues
+ $options = [
+ 'credentials' => ChannelCredentials::createInsecure(),
+ 'timeout' => 30000000, // 30 seconds in microseconds
+ ];
+ Log::info("Created gRPC client for service: {$service} at {$serviceAddress}");
+
+ return new GatewayServiceClient(
+ $serviceAddress,
+ $options
+ );
+
+ } catch (Exception $e) {
+ Log::error("Failed to create gRPC client for service: {$service}", [
+ 'address' => $serviceAddress,
+ 'error' => $e->getMessage()
+ ]);
+ return null;
+ }
+ }
+
+ /**
+ * Create error response for FlexibleResponse
+ */
+ private function createErrorResponse(FlexibleResponse $response, int $statusCode, string $code, string $message, ?Any $data = null): FlexibleResponse
+ {
+ $error = new ErrorInfo();
+ $error->setCode($code);
+ $error->setMessage($message);
+
+ $response->setData($data);
+ $response->setSuccess(false);
+ $response->setStatusCode($statusCode);
+ $response->setError($error);
+
+ return $response;
+ }
+
+ /**
+ * Create error response for BytesResponse
+ */
+ private function createBytesErrorResponse(BytesResponse $response, int $statusCode, string $code, string $message): BytesResponse
+ {
+ $error = new ErrorInfo();
+ $error->setCode($code);
+ $error->setMessage($message);
+
+ $response->setSuccess(false);
+ $response->setStatusCode($statusCode);
+ $response->setError($error);
+ $response->setContentType('application/json');
+ $response->setData(json_encode(['error' => $code, 'message' => $message]));
+
+ return $response;
+ }
+
+ /**
+ * Get current timestamp
+ */
+ private function getCurrentTimestamp(): Timestamp
+ {
+ $timestamp = new Timestamp();
+ $timestamp->fromDateTime(new DateTime());
+ return $timestamp;
+ }
+
+ public function getMethodDescriptors(): array
+ {
+ return [
+ '/gateway.GatewayService/HandleRequest' => new MethodDescriptor(
+ $this,
+ 'HandleRequest',
+ FlexibleRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ ];
+ }
+
+}
diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php
new file mode 100644
index 0000000..cc0ad76
--- /dev/null
+++ b/app/Grpc/Services/GrpcService.php
@@ -0,0 +1,64 @@
+setService($service);
+ $grpcRequest->setMethod($method);
+ $grpcRequest->setRequestId(uniqid());
+
+ // Pack request data as Any
+ $payload = new Any();
+ $payload->setValue(json_encode($request->all()));
+ $grpcRequest->setPayload($payload);
+
+
+ // Call gRPC service
+ $grpcResponse = $this->gatewayService->HandleClientRequest($grpcRequest);
+ return $this->convertBackToHTTPResponse($grpcResponse);
+
+ }
+
+ public function forwardGrpcToService(FlexibleRequest $request): FlexibleResponse
+ {
+ return $this->gatewayService->HandleClientRequest($request);
+ }
+
+ /**
+ * @param FlexibleResponse $grpcResponse
+ * @return JsonResponse
+ */
+ public function convertBackToHTTPResponse(FlexibleResponse $grpcResponse): JsonResponse
+ {
+// Convert back to HTTP response
+ if ($grpcResponse->getSuccess()) {
+ $data = json_decode($grpcResponse->getData()->getValue()) ?? $grpcResponse->getData()->getValue();
+ return response()->json($data, $grpcResponse->getStatusCode());
+ } else {
+ $error = $grpcResponse->getError()->getMessage();
+ $data = $grpcResponse->getData() ? json_decode($grpcResponse->getData()->getValue()) : null;
+ if (data_get($data, 'message'))
+ $res['message'] = data_get($data, 'message');
+
+ $res['error'] = json_decode($error) ?? $error;
+ return response()->json($res, $grpcResponse->getStatusCode());
+ }
+ }
+}
diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php
new file mode 100644
index 0000000..7ebec60
--- /dev/null
+++ b/app/Http/Controllers/ApiGatewayController.php
@@ -0,0 +1,76 @@
+ $request->bearerToken()
+ ]);
+
+ if ($res->failed())
+ if ($res->getStatusCode() == Response::HTTP_NOT_FOUND)
+ return response()->json([
+ 'message' => $res->json('details', 'No purchased products found'),
+ ], $res->getStatusCode());
+ else
+ throw new ConnectionException('Failed to connect to AI API', $res->status());
+
+ $recommendedProductData = $res->json();
+ $request->merge($this->productService->injectUserId());
+ if (!$recommendedProductIds = array_column($recommendedProductData, 'id'))
+ return response()->json([
+ 'message' => 'No recommended products found',
+ 'data' => []
+ ], Response::HTTP_OK);
+
+ $request->merge(['recommended_product_ids' => $recommendedProductIds]);
+ $res = $this->grpcService->createClientRequest(
+ $request,
+ 'ProductService',
+ 'listProducts',
+ );
+ if ($res->getStatusCode() == Response::HTTP_OK) {
+ $filteredProductData = ($res->getData(true));
+ $filteredProductData = data_get($filteredProductData, 'data');
+
+ foreach ($filteredProductData as $index => &$productData) {
+ $productData['percentage'] = $recommendedProductData[$index]['compatibility_score'] ?? 0;
+ $productData['percentage'] = $productData['percentage'] * 100; // Convert to percentage
+ $productData['percentage'] = number_format($productData['percentage'], 2) . '%';
+ }
+
+ $res->setData($filteredProductData);
+ return $this->response("Recommended products fetched successfully", $res->getData(true), Response::HTTP_OK);
+ } else
+ throw new ConnectionException($res->getData(true), $res->getStatusCode());
+ } catch (ConnectionException $e) {
+ return response()->json([
+ 'message' => 'Connection error',
+ 'error' => $e->getMessage()
+ ], 500);
+ }
+ }
+}
diff --git a/app/Http/Controllers/AuthApiController.php b/app/Http/Controllers/AuthApiController.php
new file mode 100644
index 0000000..789ccc2
--- /dev/null
+++ b/app/Http/Controllers/AuthApiController.php
@@ -0,0 +1,80 @@
+service->login($request->all());
+//
+// return $this->response($res['message'], $res['data'], $status);
+// } catch (Exception $exception) {
+// return $this->response($exception->getMessage(), status: $exception->getCode(), error: true);
+// }
+// }
+ public function login(Request $request): JsonResponse
+ {
+ try {
+// $res = $this->userGrpcService->authenticateUser($request->email, $request->password);
+ return $this->grpcService->createClientRequest($request,
+ $this->serviceName,
+ 'Login'
+ );
+// return $this->response($message, $data, Response::HTTP_OK, error: !$success);
+ } catch (Exception $exception) {
+ return $this->response($exception->getMessage(), status: $exception->getCode(), error: true);
+ }
+ }
+
+ /**
+ */
+ public function signup(Request $request): Response
+ {
+ try {
+// $res = $this->userGrpcService->createUser($request->all());
+ return $this->grpcService->createClientRequest($request,
+ $this->serviceName,
+ 'SignUp'
+ );
+ } catch (Exception $e) {
+ return $this->response($e->getMessage(), status: $e->getCode() ?: Response::HTTP_BAD_REQUEST, error: true);
+ }
+ }
+
+ /**
+ */
+ public
+ function logout(Request $request)
+ {
+ try {
+ return $this->grpcService->createClientRequest(
+ $request,
+ $this->serviceName,
+ 'Logout'
+ );
+// $success = $res['success'];
+// $message = $res['message'];
+// $data = $res['data'];
+// return $this->response($message, $data, Response::HTTP_NO_CONTENT, error: !$success);
+ } catch (Exception $exception) {
+ return $this->response($exception->getMessage(), status: $exception->getCode(), error: true);
+ }
+ }
+}
diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php
deleted file mode 100644
index d1a8aca..0000000
--- a/app/Http/Controllers/AuthController.php
+++ /dev/null
@@ -1,58 +0,0 @@
-service->login($request->all());
-
- return $this->response($res['message'], $res['data'], $status);
- } catch (Exception $exception) {
- return $this->response($exception->getMessage(), status: $exception->getCode(), error: true);
- }
- }
-
- /**
- */
- public function signup(Request $request): UserResource|JsonResponse
- {
- try {
- $res = $this->service->create($request->all());
-
- if ($res->status() == Response::HTTP_CREATED)
- return $this->response($res->json('message'), $res->json('data'), $res->status());
- else
- return $this->response($res->json('message'), $res->json('errors'), $res->status());
- } catch (Exception $e) {
- return $this->response($e->getMessage(), status: $e->getCode() ?: Response::HTTP_BAD_REQUEST, error: true);
- }
- }
-
- /**
- */
- public function logout(Request $request)
- {
- try {
- [$res, $status] = $this->service->logout($request->bearerToken());
- return $this->response($res['message'], $res['data'], $status);
- } catch (Exception $exception) {
- return $this->response($exception->getMessage(), status: $exception->getCode(), error: true);
- }
- }
-}
diff --git a/app/Http/Controllers/GoogleController.php b/app/Http/Controllers/GoogleApiController.php
similarity index 95%
rename from app/Http/Controllers/GoogleController.php
rename to app/Http/Controllers/GoogleApiController.php
index 1c0a0c1..ec2da71 100644
--- a/app/Http/Controllers/GoogleController.php
+++ b/app/Http/Controllers/GoogleApiController.php
@@ -6,7 +6,7 @@
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
-class GoogleController extends BaseController
+class GoogleApiController extends BaseController
{
public function __construct(
private UserService $userService,
diff --git a/app/Http/Controllers/OfferController.php b/app/Http/Controllers/OfferApiController.php
similarity index 63%
rename from app/Http/Controllers/OfferController.php
rename to app/Http/Controllers/OfferApiController.php
index 30298a0..3699123 100644
--- a/app/Http/Controllers/OfferController.php
+++ b/app/Http/Controllers/OfferApiController.php
@@ -3,15 +3,18 @@
namespace App\Http\Controllers;
use App\External_Apis\Services\OfferService;
+use App\Grpc\Services\GrpcService;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Request;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
use Symfony\Component\HttpFoundation\Response;
-class OfferController extends BaseController implements HasMiddleware
+class OfferApiController extends BaseController implements HasMiddleware
{
- public function __construct(public OfferService $offerService)
+ public function __construct(public OfferService $offerService,
+ private readonly GrpcService $grpcService
+ )
{
}
@@ -20,20 +23,26 @@ public function __construct(public OfferService $offerService)
*/
public function index(Request $request)
{
- try {
- [$res, $status] = $this->offerService->index($request->all());
- if ($status != Response::HTTP_OK && isset($res['errors']))
- $mes = $res['errors'];
- else
- $mes = $res['message'];
-
- return $this->response($mes, $res['data'], $status, error: $status != Response::HTTP_OK);
- } catch (\Exception $e) {
- return $this->response($e->getMessage(), [
- 'file' => $e->getFile(),
- 'line' => $e->getLine()
- ], $e->getCode(), error: true);
- }
+ $request->merge($this->offerService->injectUserId());
+ return $this->grpcService->createClientRequest(
+ $request,
+ 'ProductService',
+ 'listOffers',
+ );
+// try {
+// [$res, $status] = $this->offerService->index($request->all());
+// if ($status != Response::HTTP_OK && isset($res['errors']))
+// $mes = $res['errors'];
+// else
+// $mes = $res['message'];
+//
+// return $this->response($mes, $res['data'], $status, error: $status != Response::HTTP_OK);
+// } catch (\Exception $e) {
+// return $this->response($e->getMessage(), [
+// 'file' => $e->getFile(),
+// 'line' => $e->getLine()
+// ], $e->getCode(), error: true);
+// }
}
/**
@@ -42,13 +51,19 @@ public function index(Request $request)
public function show($id)
{
try {
- [$res, $status] = $this->offerService->show($id);
- if ($status != Response::HTTP_OK && isset($res['errors']))
- $mes = $res['errors'];
- else
- $mes = $res['message'];
+ request()->merge(['id' => $id]);
+ return $this->grpcService->createClientRequest(
+ request(),
+ 'ProductService',
+ 'getOffer',
+ );
+// [$res, $status] = $this->offerService->show($id);
+// if ($status != Response::HTTP_OK && isset($res['errors']))
+// $mes = $res['errors'];
+// else
+// $mes = $res['message'];
- return $this->response($mes, $res['data'], $status, error: $status != Response::HTTP_OK);
+// return $this->response($mes, $res['data'], $status, error: $status != Response::HTTP_OK);
} catch (\Exception $e) {
return $this->response($e->getMessage(), [$e->getFile(), $e->getLine()], $e->getCode(), error: true);
}
@@ -69,7 +84,7 @@ public function store(Request $request)
error: $status != Response::HTTP_CREATED
);
} catch (\Exception $e) {
- return $this->response('service '.$e->getMessage(),
+ return $this->response('service ' . $e->getMessage(),
[$e->getFile(),
$e->getLine(),
$e->getTrace()
diff --git a/app/Http/Controllers/OrderApiController.php b/app/Http/Controllers/OrderApiController.php
index d802d68..51931de 100644
--- a/app/Http/Controllers/OrderApiController.php
+++ b/app/Http/Controllers/OrderApiController.php
@@ -50,4 +50,15 @@ public function update(Request $request, $id)
return $this->response($res->json('message'), $res->json('data'), status: $res->status());
return $this->response($res->json('error'), $res->json('data'), status: $res->status(), error: true);
}
+
+ /**
+ * @throws ConnectionException
+ */
+ public function purchaseHistory()
+ {
+ $res = http::withToken(request()->bearerToken())->get(OrderApi::getBaseUrl() . 'api/purchase-history');
+ if ($res->status() == Response::HTTP_OK)
+ return $this->response($res->json('message'), $res->json('data'), status: $res->status());
+ return $this->response($res->json('error'), $res->json('data'), status: $res->status(), error: true);
+ }
}
diff --git a/app/Http/Controllers/PasswordController.php b/app/Http/Controllers/PasswordApiController.php
similarity index 93%
rename from app/Http/Controllers/PasswordController.php
rename to app/Http/Controllers/PasswordApiController.php
index 39b53c8..f8560da 100644
--- a/app/Http/Controllers/PasswordController.php
+++ b/app/Http/Controllers/PasswordApiController.php
@@ -6,7 +6,7 @@
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
-class PasswordController extends BaseController
+class PasswordApiController extends BaseController
{
public function __construct(
private readonly UserService $userService
@@ -18,8 +18,8 @@ public function sendResetLink(Request $request)
{
try {
[$res, $status] = $this->userService->sendResetLink($request->all());
- if ($status != Response::HTTP_OK && isset($res['errors']))
- $mes = $res['errors'];
+ if ($status != Response::HTTP_OK && isset($res['error']))
+ $mes = $res['error'];
else
$mes = $res['message'];
diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductApiController.php
similarity index 66%
rename from app/Http/Controllers/ProductController.php
rename to app/Http/Controllers/ProductApiController.php
index 1796232..f7bfc5c 100644
--- a/app/Http/Controllers/ProductController.php
+++ b/app/Http/Controllers/ProductApiController.php
@@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\External_Apis\Services\ProductService;
+use App\Grpc\Services\GrpcService;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\RequestException;
use Illuminate\Http\Request;
@@ -11,10 +12,11 @@
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpFoundation\Response;
-class ProductController extends BaseController implements HasMiddleware
+class ProductApiController extends BaseController implements HasMiddleware
{
public function __construct(
- private readonly ProductService $productService
+ private readonly ProductService $productService,
+ private readonly GrpcService $grpcService
)
{
@@ -32,28 +34,41 @@ public static function middleware()
*/
public function index(Request $request)
{
- [$res, $status] = $this->productService->index($request->all());
- $isError = $status != Response::HTTP_OK;
- if ($isError && isset($res['errors']))
- $msg = $res['errors'];
- else
- $msg = $res['message'];
-
- return $this->response($msg, $res['data'], $status, error: $isError);
+ $request->merge($this->productService->injectUserId());
+ return $this->grpcService->createClientRequest(
+ $request,
+ 'ProductService',
+ 'listProducts',
+ );
+// [$res, $status] = $this->productService->index($request->all());
+// $isError = $status != Response::HTTP_OK;
+// if ($isError && isset($res['errors']))
+// $msg = $res['errors'];
+// else
+// $msg = $res['message'];
+//
+// return $this->response($msg, $res['data'], $status, error: $isError);
}
/**
* @throws ConnectionException
*/
public function show(int $product_id)
+
{
- [$res, $status] = $this->productService->show($product_id);
- $isError = $status != Response::HTTP_OK;
- if ($isError && isset($res['errors']))
- $msg = $res['errors'];
- else
- $msg = $res['message'];
- return $this->response($msg, $res['data'], $status, error: $isError);
+ request()->merge(['id' => $product_id]);
+ return $this->grpcService->createClientRequest(
+ request(),
+ 'ProductService',
+ 'getProduct',
+ );
+// [$res, $status] = $this->productService->show($product_id);
+// $isError = $status != Response::HTTP_OK;
+// if ($isError && isset($res['errors']))
+// $msg = $res['errors'];
+// else
+// $msg = $res['message'];
+// return $this->response($msg, $res['data'], $status, error: $isError);
}
/**
diff --git a/app/Http/Controllers/ShippingAddressController.php b/app/Http/Controllers/ShippingAddressApiController.php
similarity index 98%
rename from app/Http/Controllers/ShippingAddressController.php
rename to app/Http/Controllers/ShippingAddressApiController.php
index 94ae6fb..9e87a69 100644
--- a/app/Http/Controllers/ShippingAddressController.php
+++ b/app/Http/Controllers/ShippingAddressApiController.php
@@ -7,7 +7,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Symfony\Component\HttpFoundation\Response;
-class ShippingAddressController extends BaseController
+class ShippingAddressApiController extends BaseController
{
/**
* Display a listing of the resource.
diff --git a/app/Http/Controllers/ShoppingCartApiController.php b/app/Http/Controllers/ShoppingCartApiController.php
new file mode 100644
index 0000000..62b8cca
--- /dev/null
+++ b/app/Http/Controllers/ShoppingCartApiController.php
@@ -0,0 +1,76 @@
+grpcService->createClientRequest(
+ $request,
+ $this->service,
+ 'GetCart',
+ );
+// $res = http::withToken(request()->bearerToken())->get(UserApi::getBaseUrl() . 'api/carts');
+// return $this->response($res->json('message'), $res->json('data'),
+// otherData: ["total" => $res->json('total')]);
+ }
+
+ /**
+ */
+ public function add(Request $request)
+ {
+ return $this->grpcService->createClientRequest(
+ $request,
+ $this->service,
+ 'AddToCart',
+ );
+// $res = http::withToken(request()->bearerToken())->post(UserApi::getBaseUrl() . "api/carts", $request->all());
+// if ($res->status() == Response::HTTP_CREATED)
+// return $this->response($res->json('message'), $res->json('data'), status: $res->status());
+// return $this->response($res->json('error'), $res->json('data'), status: $res->status());
+ }
+
+ /**
+ */
+ public function remove($item_type, $item_id)
+ {
+ \request()->merge(['item_type' => $item_type, 'item_id' => $item_id]);
+ return $this->grpcService->createClientRequest(
+ request(),
+ $this->service,
+ 'RemoveFromCart'
+ );
+// $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts/$item_type/$item_id");
+// return $this->response($res->json('message'), $res->json('data'));
+ }
+
+ /**
+ */
+ public function clear()
+ {
+ return $this->grpcService->createClientRequest(
+ request(),
+ $this->service,
+ 'ClearCart'
+ );
+// $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts");
+// return $this->response($res->json('message'), $res->json('data'));
+ }
+}
diff --git a/app/Http/Controllers/ShoppingCartController.php b/app/Http/Controllers/ShoppingCartController.php
deleted file mode 100644
index 9c0a8be..0000000
--- a/app/Http/Controllers/ShoppingCartController.php
+++ /dev/null
@@ -1,52 +0,0 @@
-bearerToken())->get(UserApi::getBaseUrl() . 'api/carts');
- return $this->response($res->json('message'), $res->json('data'),
- otherData: ["total"=>$res->json('total')]);
- }
-
- /**
- * @throws ConnectionException
- */
- public function add(Request $request)
- {
- $res = http::withToken(request()->bearerToken())->post(UserApi::getBaseUrl() . "api/carts", $request->all());
- if ($res->status() == Response::HTTP_CREATED)
- return $this->response($res->json('message'), $res->json('data'), status: $res->status());
- return $this->response($res->json('error'), $res->json('data'), status: $res->status());
- }
-
- /**
- * @throws ConnectionException
- */
- public function remove($item_type, $item_id)
- {
- $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts/$item_type/$item_id");
- return $this->response($res->json('message'), $res->json('data'));
- }
-
- /**
- * @throws ConnectionException
- */
- public function clear()
- {
- $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts");
- return $this->response($res->json('message'), $res->json('data'));
- }
-}
diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php
index 9182944..36e51b5 100644
--- a/app/Http/Controllers/UserApiController.php
+++ b/app/Http/Controllers/UserApiController.php
@@ -2,45 +2,75 @@
namespace App\Http\Controllers;
-use App\External_Apis\Services\UserService;
+use App\Grpc\Services\GrpcService;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
-use Symfony\Component\HttpFoundation\Response;
class UserApiController extends BaseController
{
+ private string $serviceName='UserService';
+
public function __construct(
- private readonly UserService $userService
+ private readonly GrpcService $grpcService,
+
)
{
}
+//
+// /**
+// * Display a listing of the resource.
+// * @throws ConnectionException
+// */
+// public function index(Request $request)
+// {
+// [$res, $status] = $this->userService->index($request, $request->bearerToken());
+// return $this->response($res['message'], $res['data'], $status, error: $status != Response::HTTP_OK);
+// }
+
/**
* Display a listing of the resource.
* @throws ConnectionException
*/
public function index(Request $request)
{
- [$res, $status] = $this->userService->index($request, $request->bearerToken());
- return $this->response($res['message'], $res['data'], $status, error: $status != Response::HTTP_OK);
+// $res = $this->userGrpcService->listUsers();
+ return $this->grpcService->createClientRequest(request(),
+ $this->serviceName,
+ 'ListUsers',
+ );
}
/**
* Display the specified resource.
- * @throws ConnectionException
*/
- public function show($user_id)
+// public function show($user_id)
+// {
+// Validator::validate(
+// ['user_id' => $user_id],
+// ['user_id' => 'required|integer']
+// );
+//
+// [$res, $status] = $this->userService->show($user_id, request()->bearerToken());
+//
+// return $this->response($res['message'], $res['data'], $status, error: $status != Response::HTTP_OK);
+// }
+
+ public function show($id)
{
Validator::validate(
- ['user_id' => $user_id],
+ ['user_id' => $id],
['user_id' => 'required|integer']
);
- [$res, $status] = $this->userService->show($user_id, request()->bearerToken());
+ request()->merge(['id' => $id]);
+ return $this->grpcService->createClientRequest(request(),
+ $this->serviceName,
+ 'GetUser',
+ );
- return $this->response($res['message'], $res['data'], $status, error: $status != Response::HTTP_OK);
}
/**
@@ -59,15 +89,25 @@ public function destroy(string $id)
//
}
+// /**
+// * @throws ConnectionException
+// */
+// public function userProfile(Request $request)
+// {
+// $res = $this->userService->userProfile($request->all());
+// if ($res['status'] == Response::HTTP_OK)
+// return $this->response($res['message'], $res['data'], $res['status']);
+// else
+// return $this->response($res['message'], status: $res['status'], error: true);
+// }
/**
- * @throws ConnectionException
*/
public function userProfile(Request $request)
{
- $res = $this->userService->userProfile($request->all());
- if ($res['status'] == Response::HTTP_OK)
- return $this->response($res['message'], $res['data'], $res['status']);
- else
- return $this->response($res['message'], status: $res['status'], error: true);
+ return $this->grpcService->createClientRequest($request,
+ $this->serviceName,
+ 'GetUserProfile'
+ );
+// return $this->response($message, $data, Response::HTTP_OK, error: !$success);
}
}
diff --git a/app/Http/Controllers/WishListController.php b/app/Http/Controllers/WishListApiController.php
similarity index 78%
rename from app/Http/Controllers/WishListController.php
rename to app/Http/Controllers/WishListApiController.php
index 9a6ae39..efd5853 100644
--- a/app/Http/Controllers/WishListController.php
+++ b/app/Http/Controllers/WishListApiController.php
@@ -6,7 +6,7 @@
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Http;
-class WishListController extends BaseController
+class WishListApiController extends BaseController
{
/**
* Display a listing of the resource.
@@ -40,4 +40,13 @@ public function destroy(string $item_type, int $item_id)
$res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/wishlists/$item_type/$item_id");
return $this->response($res->json('message') ?? $res->json('error'), $res->json('errors'), status: $res->status());
}
+
+ /**
+ * @throws ConnectionException
+ */
+ public function clear()
+ {
+ $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . 'api/wishlists/clear');
+ return $this->response($res->json('message') ?? $res->json('error'), $res->json('errors'), status: $res->status());
+ }
}
diff --git a/app/Http/Middleware/AuthenticateMiddleware.php b/app/Http/Middleware/AuthenticateMiddleware.php
index bf72614..1ba3d38 100644
--- a/app/Http/Middleware/AuthenticateMiddleware.php
+++ b/app/Http/Middleware/AuthenticateMiddleware.php
@@ -8,11 +8,16 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
-use Illuminate\Support\Facades\Http;
use Symfony\Component\HttpFoundation\Response;
class AuthenticateMiddleware
{
+ public function __construct(
+ private readonly \App\Grpc\Services\GrpcService $grpcService,
+ )
+ {
+ }
+
/**
* Handle an incoming request.
*
@@ -23,13 +28,17 @@ public function handle(Request $request, Closure $next): Response
$token = $request->bearerToken();
$cacheKey = 'user_' . hash('sha256', $token);
try {
- $userModel = Cache::remember($cacheKey, 60 * 60 * 24, function () use ($token) {
- $response = Http::withToken($token)->post(app('users')::getTokenApi());
- if ($response->failed())
- return null;
-
- return new User(json_decode($response->json('data'), true));
- });
+ $userModel = Cache::remember($cacheKey, 60 * 60 * 24, function () use ($request, $token) {
+ $response = $this->grpcService->createClientRequest(
+ $request,
+ 'UserService/Auth',
+ 'ValidateToken',
+ );
+ if ($response->getStatusCode() != Response::HTTP_OK)
+ return null;
+
+ return new User(json_decode(data_get($response->getData(), 'data'), true));
+ });
if (!$userModel) {
Cache::forget($cacheKey);
diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php
deleted file mode 100644
index afb4db9..0000000
--- a/app/Http/Resources/UserResource.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- */
- public function toArray(Request $request): array
- {
- return [
- 'id' => $this->id,
- 'name' => $this->name,
- 'username' => $this->username,
- 'email' => $this->whenNotNull($this->email),
- 'token' => $this->token,
- ];
- }
-}
diff --git a/app/Providers/GrpcServiceProvider.php b/app/Providers/GrpcServiceProvider.php
new file mode 100644
index 0000000..521b147
--- /dev/null
+++ b/app/Providers/GrpcServiceProvider.php
@@ -0,0 +1,20 @@
+loadGrpcRoutes();
+ }
+
+ protected function loadGrpcRoutes(): void
+ {
+ if (file_exists(base_path('routes/grpc.php'))) {
+ require base_path('routes/grpc.php');
+ }
+ }
+}
diff --git a/app/Traits/InjectUser.php b/app/Traits/InjectUser.php
index b7875ab..98ed12d 100644
--- a/app/Traits/InjectUser.php
+++ b/app/Traits/InjectUser.php
@@ -4,9 +4,14 @@
trait InjectUser
{
- private function injectUserId(&$data=[]): void
+ /**
+ * @throws \Exception
+ */
+ public function injectUserId(): array
{
- $data['user_id'] = auth()->user()->id;
+ if (is_null(auth()->user()))
+ throw new \Exception('User not authenticated');
+ return ['user_id' => auth()->user()->id];
}
}
diff --git a/bootstrap/providers.php b/bootstrap/providers.php
index a693850..a060716 100644
--- a/bootstrap/providers.php
+++ b/bootstrap/providers.php
@@ -3,4 +3,5 @@
return [
App\Providers\AppServiceProvider::class,
App\Providers\ExternalApiServiceProvider::class,
+ App\Providers\GrpcServiceProvider::class,
];
diff --git a/composer.json b/composer.json
index 33a7091..58eab8b 100644
--- a/composer.json
+++ b/composer.json
@@ -8,8 +8,10 @@
"require": {
"php": "^8.4",
"laravel/framework": "^12.0",
- "laravel/octane": "^2.8",
- "laravel/tinker": "^2.10.1"
+ "laravel/tinker": "^2.10.1",
+ "grpc/grpc": "^1.57",
+ "google/protobuf": "^4.31",
+ "ext-grpc": "*"
},
"require-dev": {
"fakerphp/faker": "^1.24",
diff --git a/composer.lock b/composer.lock
index 95f8579..569419e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "df1def02b3fd57624f7afc0a1ad360d8",
+ "content-hash": "48d551fc576552ae83ec6195033a4f26",
"packages": [
{
"name": "brick/math",
- "version": "0.12.3",
+ "version": "0.13.1",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
- "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
+ "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
- "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
+ "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
+ "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
"shasum": ""
},
"require": {
@@ -56,7 +56,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
- "source": "https://github.com/brick/math/tree/0.12.3"
+ "source": "https://github.com/brick/math/tree/0.13.1"
},
"funding": [
{
@@ -64,7 +64,7 @@
"type": "github"
}
],
- "time": "2025-02-28T13:11:00+00:00"
+ "time": "2025-03-29T13:50:30+00:00"
},
{
"name": "carbonphp/carbon-doctrine-types",
@@ -581,6 +581,50 @@
],
"time": "2023-10-12T05:21:21+00:00"
},
+ {
+ "name": "google/protobuf",
+ "version": "v4.31.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/protocolbuffers/protobuf-php.git",
+ "reference": "2b028ce8876254e2acbeceea7d9b573faad41864"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/2b028ce8876254e2acbeceea7d9b573faad41864",
+ "reference": "2b028ce8876254e2acbeceea7d9b573faad41864",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=5.0.0"
+ },
+ "suggest": {
+ "ext-bcmath": "Need to support JSON deserialization"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\Protobuf\\": "src/Google/Protobuf",
+ "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "proto library for PHP",
+ "homepage": "https://developers.google.com/protocol-buffers/",
+ "keywords": [
+ "proto"
+ ],
+ "support": {
+ "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.31.1"
+ },
+ "time": "2025-05-28T18:52:35+00:00"
+ },
{
"name": "graham-campbell/result-type",
"version": "v1.1.3",
@@ -643,6 +687,50 @@
],
"time": "2024-07-20T21:45:45+00:00"
},
+ {
+ "name": "grpc/grpc",
+ "version": "1.57.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/grpc/grpc-php.git",
+ "reference": "b610c42022ed3a22f831439cb93802f2a4502fdf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/grpc/grpc-php/zipball/b610c42022ed3a22f831439cb93802f2a4502fdf",
+ "reference": "b610c42022ed3a22f831439cb93802f2a4502fdf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "google/auth": "^v1.3.0"
+ },
+ "suggest": {
+ "ext-protobuf": "For better performance, install the protobuf C extension.",
+ "google/protobuf": "To get started using grpc quickly, install the native protobuf library."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Grpc\\": "src/lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "gRPC library for PHP",
+ "homepage": "https://grpc.io",
+ "keywords": [
+ "rpc"
+ ],
+ "support": {
+ "source": "https://github.com/grpc/grpc-php/tree/v1.57.0"
+ },
+ "time": "2023-08-14T23:57:54+00:00"
+ },
{
"name": "guzzlehttp/guzzle",
"version": "7.9.3",
@@ -1054,110 +1142,22 @@
],
"time": "2025-02-03T10:55:03+00:00"
},
- {
- "name": "laminas/laminas-diactoros",
- "version": "3.5.0",
- "source": {
- "type": "git",
- "url": "https://github.com/laminas/laminas-diactoros.git",
- "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/143a16306602ce56b8b092a7914fef03c37f9ed2",
- "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2",
- "shasum": ""
- },
- "require": {
- "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
- "psr/http-factory": "^1.1",
- "psr/http-message": "^1.1 || ^2.0"
- },
- "conflict": {
- "amphp/amp": "<2.6.4"
- },
- "provide": {
- "psr/http-factory-implementation": "^1.0",
- "psr/http-message-implementation": "^1.1 || ^2.0"
- },
- "require-dev": {
- "ext-curl": "*",
- "ext-dom": "*",
- "ext-gd": "*",
- "ext-libxml": "*",
- "http-interop/http-factory-tests": "^2.2.0",
- "laminas/laminas-coding-standard": "~2.5.0",
- "php-http/psr7-integration-tests": "^1.4.0",
- "phpunit/phpunit": "^10.5.36",
- "psalm/plugin-phpunit": "^0.19.0",
- "vimeo/psalm": "^5.26.1"
- },
- "type": "library",
- "extra": {
- "laminas": {
- "module": "Laminas\\Diactoros",
- "config-provider": "Laminas\\Diactoros\\ConfigProvider"
- }
- },
- "autoload": {
- "files": [
- "src/functions/create_uploaded_file.php",
- "src/functions/marshal_headers_from_sapi.php",
- "src/functions/marshal_method_from_sapi.php",
- "src/functions/marshal_protocol_version_from_sapi.php",
- "src/functions/normalize_server.php",
- "src/functions/normalize_uploaded_files.php",
- "src/functions/parse_cookie_header.php"
- ],
- "psr-4": {
- "Laminas\\Diactoros\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "description": "PSR HTTP Message implementations",
- "homepage": "https://laminas.dev",
- "keywords": [
- "http",
- "laminas",
- "psr",
- "psr-17",
- "psr-7"
- ],
- "support": {
- "chat": "https://laminas.dev/chat",
- "docs": "https://docs.laminas.dev/laminas-diactoros/",
- "forum": "https://discourse.laminas.dev",
- "issues": "https://github.com/laminas/laminas-diactoros/issues",
- "rss": "https://github.com/laminas/laminas-diactoros/releases.atom",
- "source": "https://github.com/laminas/laminas-diactoros"
- },
- "funding": [
- {
- "url": "https://funding.communitybridge.org/projects/laminas-project",
- "type": "community_bridge"
- }
- ],
- "time": "2024-10-14T11:59:49+00:00"
- },
{
"name": "laravel/framework",
- "version": "v12.10.2",
+ "version": "v12.18.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "0f123cc857bc177abe4d417448d4f7164f71802a"
+ "reference": "7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/0f123cc857bc177abe4d417448d4f7164f71802a",
- "reference": "0f123cc857bc177abe4d417448d4f7164f71802a",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d",
+ "reference": "7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d",
"shasum": ""
},
"require": {
- "brick/math": "^0.11|^0.12",
+ "brick/math": "^0.11|^0.12|^0.13",
"composer-runtime-api": "^2.2",
"doctrine/inflector": "^2.0.5",
"dragonmantank/cron-expression": "^3.4",
@@ -1174,7 +1174,7 @@
"guzzlehttp/uri-template": "^1.0",
"laravel/prompts": "^0.3.0",
"laravel/serializable-closure": "^1.3|^2.0",
- "league/commonmark": "^2.6",
+ "league/commonmark": "^2.7",
"league/flysystem": "^3.25.1",
"league/flysystem-local": "^3.25.1",
"league/uri": "^7.5.1",
@@ -1266,7 +1266,7 @@
"php-http/discovery": "^1.15",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1",
- "predis/predis": "^2.3",
+ "predis/predis": "^2.3|^3.0",
"resend/resend-php": "^0.10.0",
"symfony/cache": "^7.2.0",
"symfony/http-client": "^7.2.0",
@@ -1298,7 +1298,7 @@
"pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).",
"php-http/discovery": "Required to use PSR-7 bridging features (^1.15).",
"phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).",
- "predis/predis": "Required to use the predis connector (^2.3).",
+ "predis/predis": "Required to use the predis connector (^2.3|^3.0).",
"psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).",
"resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).",
@@ -1355,97 +1355,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2025-04-24T14:11:20+00:00"
- },
- {
- "name": "laravel/octane",
- "version": "v2.9.1",
- "source": {
- "type": "git",
- "url": "https://github.com/laravel/octane.git",
- "reference": "445002b2551c837d60cda4324259063c356dfb56"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/laravel/octane/zipball/445002b2551c837d60cda4324259063c356dfb56",
- "reference": "445002b2551c837d60cda4324259063c356dfb56",
- "shasum": ""
- },
- "require": {
- "laminas/laminas-diactoros": "^3.0",
- "laravel/framework": "^10.10.1|^11.0|^12.0",
- "laravel/prompts": "^0.1.24|^0.2.0|^0.3.0",
- "laravel/serializable-closure": "^1.3|^2.0",
- "nesbot/carbon": "^2.66.0|^3.0",
- "php": "^8.1.0",
- "symfony/console": "^6.0|^7.0",
- "symfony/psr-http-message-bridge": "^2.2.0|^6.4|^7.0"
- },
- "conflict": {
- "spiral/roadrunner": "<2023.1.0",
- "spiral/roadrunner-cli": "<2.6.0",
- "spiral/roadrunner-http": "<3.3.0"
- },
- "require-dev": {
- "guzzlehttp/guzzle": "^7.6.1",
- "inertiajs/inertia-laravel": "^1.3.2|^2.0",
- "laravel/scout": "^10.2.1",
- "laravel/socialite": "^5.6.1",
- "livewire/livewire": "^2.12.3|^3.0",
- "mockery/mockery": "^1.5.1",
- "nunomaduro/collision": "^6.4.0|^7.5.2|^8.0",
- "orchestra/testbench": "^8.21|^9.0|^10.0",
- "phpstan/phpstan": "^2.1.7",
- "phpunit/phpunit": "^10.4|^11.5",
- "spiral/roadrunner-cli": "^2.6.0",
- "spiral/roadrunner-http": "^3.3.0"
- },
- "bin": [
- "bin/roadrunner-worker",
- "bin/swoole-server"
- ],
- "type": "library",
- "extra": {
- "laravel": {
- "aliases": {
- "Octane": "Laravel\\Octane\\Facades\\Octane"
- },
- "providers": [
- "Laravel\\Octane\\OctaneServiceProvider"
- ]
- },
- "branch-alias": {
- "dev-master": "2.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Laravel\\Octane\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Taylor Otwell",
- "email": "taylor@laravel.com"
- }
- ],
- "description": "Supercharge your Laravel application's performance.",
- "keywords": [
- "frankenphp",
- "laravel",
- "octane",
- "roadrunner",
- "swoole"
- ],
- "support": {
- "issues": "https://github.com/laravel/octane/issues",
- "source": "https://github.com/laravel/octane"
- },
- "time": "2025-04-13T21:10:35+00:00"
+ "time": "2025-06-10T14:48:34+00:00"
},
{
"name": "laravel/prompts",
@@ -1635,16 +1545,16 @@
},
{
"name": "league/commonmark",
- "version": "2.6.2",
+ "version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
- "reference": "06c3b0bf2540338094575612f4a1778d0d2d5e94"
+ "reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/06c3b0bf2540338094575612f4a1778d0d2d5e94",
- "reference": "06c3b0bf2540338094575612f4a1778d0d2d5e94",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
+ "reference": "6fbb36d44824ed4091adbcf4c7d4a3923cdb3405",
"shasum": ""
},
"require": {
@@ -1681,7 +1591,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "2.7-dev"
+ "dev-main": "2.8-dev"
}
},
"autoload": {
@@ -1738,7 +1648,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-18T21:09:27+00:00"
+ "time": "2025-05-05T12:20:28+00:00"
},
{
"name": "league/config",
@@ -2289,16 +2199,16 @@
},
{
"name": "nesbot/carbon",
- "version": "3.9.0",
+ "version": "3.9.1",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
- "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d"
+ "reference": "ced71f79398ece168e24f7f7710462f462310d4d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6d16a8a015166fe54e22c042e0805c5363aef50d",
- "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d",
+ "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ced71f79398ece168e24f7f7710462f462310d4d",
+ "reference": "ced71f79398ece168e24f7f7710462f462310d4d",
"shasum": ""
},
"require": {
@@ -2391,7 +2301,7 @@
"type": "tidelift"
}
],
- "time": "2025-03-27T12:57:33+00:00"
+ "time": "2025-05-01T19:51:51+00:00"
},
{
"name": "nette/schema",
@@ -2457,16 +2367,16 @@
},
{
"name": "nette/utils",
- "version": "v4.0.6",
+ "version": "v4.0.7",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
- "reference": "ce708655043c7050eb050df361c5e313cf708309"
+ "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309",
- "reference": "ce708655043c7050eb050df361c5e313cf708309",
+ "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2",
+ "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2",
"shasum": ""
},
"require": {
@@ -2537,22 +2447,22 @@
],
"support": {
"issues": "https://github.com/nette/utils/issues",
- "source": "https://github.com/nette/utils/tree/v4.0.6"
+ "source": "https://github.com/nette/utils/tree/v4.0.7"
},
- "time": "2025-03-30T21:06:30+00:00"
+ "time": "2025-06-03T04:55:08+00:00"
},
{
"name": "nikic/php-parser",
- "version": "v5.4.0",
+ "version": "v5.5.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "447a020a1f875a434d62f2a401f53b82a396e494"
+ "reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
- "reference": "447a020a1f875a434d62f2a401f53b82a396e494",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
+ "reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
"shasum": ""
},
"require": {
@@ -2595,37 +2505,37 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
},
- "time": "2024-12-30T11:07:19+00:00"
+ "time": "2025-05-31T08:24:38+00:00"
},
{
"name": "nunomaduro/termwind",
- "version": "v2.3.0",
+ "version": "v2.3.1",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/termwind.git",
- "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda"
+ "reference": "dfa08f390e509967a15c22493dc0bac5733d9123"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda",
- "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda",
+ "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dfa08f390e509967a15c22493dc0bac5733d9123",
+ "reference": "dfa08f390e509967a15c22493dc0bac5733d9123",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^8.2",
- "symfony/console": "^7.1.8"
+ "symfony/console": "^7.2.6"
},
"require-dev": {
- "illuminate/console": "^11.33.2",
- "laravel/pint": "^1.18.2",
+ "illuminate/console": "^11.44.7",
+ "laravel/pint": "^1.22.0",
"mockery/mockery": "^1.6.12",
- "pestphp/pest": "^2.36.0",
- "phpstan/phpstan": "^1.12.11",
- "phpstan/phpstan-strict-rules": "^1.6.1",
- "symfony/var-dumper": "^7.1.8",
+ "pestphp/pest": "^2.36.0 || ^3.8.2",
+ "phpstan/phpstan": "^1.12.25",
+ "phpstan/phpstan-strict-rules": "^1.6.2",
+ "symfony/var-dumper": "^7.2.6",
"thecodingmachine/phpstan-strict-rules": "^1.0.0"
},
"type": "library",
@@ -2668,7 +2578,7 @@
],
"support": {
"issues": "https://github.com/nunomaduro/termwind/issues",
- "source": "https://github.com/nunomaduro/termwind/tree/v2.3.0"
+ "source": "https://github.com/nunomaduro/termwind/tree/v2.3.1"
},
"funding": [
{
@@ -2684,7 +2594,7 @@
"type": "github"
}
],
- "time": "2024-11-21T10:39:51+00:00"
+ "time": "2025-05-08T08:14:37+00:00"
},
{
"name": "phpoption/phpoption",
@@ -3374,20 +3284,20 @@
},
{
"name": "ramsey/uuid",
- "version": "4.7.6",
+ "version": "4.8.1",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
- "reference": "91039bc1faa45ba123c4328958e620d382ec7088"
+ "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088",
- "reference": "91039bc1faa45ba123c4328958e620d382ec7088",
+ "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
+ "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
"shasum": ""
},
"require": {
- "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12",
+ "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
@@ -3396,26 +3306,23 @@
"rhumsaa/uuid": "self.version"
},
"require-dev": {
- "captainhook/captainhook": "^5.10",
+ "captainhook/captainhook": "^5.25",
"captainhook/plugin-composer": "^5.3",
- "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
- "doctrine/annotations": "^1.8",
- "ergebnis/composer-normalize": "^2.15",
- "mockery/mockery": "^1.3",
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "ergebnis/composer-normalize": "^2.47",
+ "mockery/mockery": "^1.6",
"paragonie/random-lib": "^2",
- "php-mock/php-mock": "^2.2",
- "php-mock/php-mock-mockery": "^1.3",
- "php-parallel-lint/php-parallel-lint": "^1.1",
- "phpbench/phpbench": "^1.0",
- "phpstan/extension-installer": "^1.1",
- "phpstan/phpstan": "^1.8",
- "phpstan/phpstan-mockery": "^1.1",
- "phpstan/phpstan-phpunit": "^1.1",
- "phpunit/phpunit": "^8.5 || ^9",
- "ramsey/composer-repl": "^1.4",
- "slevomat/coding-standard": "^8.4",
- "squizlabs/php_codesniffer": "^3.5",
- "vimeo/psalm": "^4.9"
+ "php-mock/php-mock": "^2.6",
+ "php-mock/php-mock-mockery": "^1.5",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpbench/phpbench": "^1.2.14",
+ "phpstan/extension-installer": "^1.4",
+ "phpstan/phpstan": "^2.1",
+ "phpstan/phpstan-mockery": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "slevomat/coding-standard": "^8.18",
+ "squizlabs/php_codesniffer": "^3.13"
},
"suggest": {
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
@@ -3450,23 +3357,13 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
- "source": "https://github.com/ramsey/uuid/tree/4.7.6"
+ "source": "https://github.com/ramsey/uuid/tree/4.8.1"
},
- "funding": [
- {
- "url": "https://github.com/ramsey",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
- "type": "tidelift"
- }
- ],
- "time": "2024-04-27T21:32:50+00:00"
+ "time": "2025-06-01T06:28:46+00:00"
},
{
"name": "symfony/clock",
- "version": "v7.2.0",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/clock.git",
@@ -3520,7 +3417,7 @@
"time"
],
"support": {
- "source": "https://github.com/symfony/clock/tree/v7.2.0"
+ "source": "https://github.com/symfony/clock/tree/v7.3.0"
},
"funding": [
{
@@ -3540,23 +3437,24 @@
},
{
"name": "symfony/console",
- "version": "v7.2.5",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "e51498ea18570c062e7df29d05a7003585b19b88"
+ "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/e51498ea18570c062e7df29d05a7003585b19b88",
- "reference": "e51498ea18570c062e7df29d05a7003585b19b88",
+ "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44",
+ "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44",
"shasum": ""
},
"require": {
"php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^2.5|^3",
- "symfony/string": "^6.4|^7.0"
+ "symfony/string": "^7.2"
},
"conflict": {
"symfony/dependency-injection": "<6.4",
@@ -3613,7 +3511,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v7.2.5"
+ "source": "https://github.com/symfony/console/tree/v7.3.0"
},
"funding": [
{
@@ -3629,11 +3527,11 @@
"type": "tidelift"
}
],
- "time": "2025-03-12T08:11:12+00:00"
+ "time": "2025-05-24T10:34:04+00:00"
},
{
"name": "symfony/css-selector",
- "version": "v7.2.0",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
@@ -3678,7 +3576,7 @@
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/css-selector/tree/v7.2.0"
+ "source": "https://github.com/symfony/css-selector/tree/v7.3.0"
},
"funding": [
{
@@ -3698,16 +3596,16 @@
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
- "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
"shasum": ""
},
"require": {
@@ -3720,7 +3618,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -3745,7 +3643,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -3761,20 +3659,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2024-09-25T14:21:43+00:00"
},
{
"name": "symfony/error-handler",
- "version": "v7.2.5",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b"
+ "reference": "cf68d225bc43629de4ff54778029aee6dc191b83"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b",
- "reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/cf68d225bc43629de4ff54778029aee6dc191b83",
+ "reference": "cf68d225bc43629de4ff54778029aee6dc191b83",
"shasum": ""
},
"require": {
@@ -3787,9 +3685,11 @@
"symfony/http-kernel": "<6.4"
},
"require-dev": {
+ "symfony/console": "^6.4|^7.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/http-kernel": "^6.4|^7.0",
- "symfony/serializer": "^6.4|^7.0"
+ "symfony/serializer": "^6.4|^7.0",
+ "symfony/webpack-encore-bundle": "^1.0|^2.0"
},
"bin": [
"Resources/bin/patch-type-declarations"
@@ -3820,7 +3720,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/error-handler/tree/v7.2.5"
+ "source": "https://github.com/symfony/error-handler/tree/v7.3.0"
},
"funding": [
{
@@ -3836,20 +3736,20 @@
"type": "tidelift"
}
],
- "time": "2025-03-03T07:12:39+00:00"
+ "time": "2025-05-29T07:19:49+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v7.2.0",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1"
+ "reference": "497f73ac996a598c92409b44ac43b6690c4f666d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1",
- "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/497f73ac996a598c92409b44ac43b6690c4f666d",
+ "reference": "497f73ac996a598c92409b44ac43b6690c4f666d",
"shasum": ""
},
"require": {
@@ -3900,7 +3800,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0"
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.0"
},
"funding": [
{
@@ -3916,20 +3816,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:21:43+00:00"
+ "time": "2025-04-22T09:11:45+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
- "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f"
+ "reference": "59eb412e93815df44f05f342958efa9f46b1e586"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f",
- "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586",
+ "reference": "59eb412e93815df44f05f342958efa9f46b1e586",
"shasum": ""
},
"require": {
@@ -3943,7 +3843,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -3976,7 +3876,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -3992,20 +3892,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2024-09-25T14:21:43+00:00"
},
{
"name": "symfony/finder",
- "version": "v7.2.2",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "87a71856f2f56e4100373e92529eed3171695cfb"
+ "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb",
- "reference": "87a71856f2f56e4100373e92529eed3171695cfb",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d",
+ "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d",
"shasum": ""
},
"require": {
@@ -4040,7 +3940,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v7.2.2"
+ "source": "https://github.com/symfony/finder/tree/v7.3.0"
},
"funding": [
{
@@ -4056,20 +3956,20 @@
"type": "tidelift"
}
],
- "time": "2024-12-30T19:00:17+00:00"
+ "time": "2024-12-30T19:00:26+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v7.2.5",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "371272aeb6286f8135e028ca535f8e4d6f114126"
+ "reference": "4236baf01609667d53b20371486228231eb135fd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/371272aeb6286f8135e028ca535f8e4d6f114126",
- "reference": "371272aeb6286f8135e028ca535f8e4d6f114126",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4236baf01609667d53b20371486228231eb135fd",
+ "reference": "4236baf01609667d53b20371486228231eb135fd",
"shasum": ""
},
"require": {
@@ -4086,6 +3986,7 @@
"doctrine/dbal": "^3.6|^4",
"predis/predis": "^1.1|^2.0",
"symfony/cache": "^6.4.12|^7.1.5",
+ "symfony/clock": "^6.4|^7.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/expression-language": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
@@ -4118,7 +4019,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v7.2.5"
+ "source": "https://github.com/symfony/http-foundation/tree/v7.3.0"
},
"funding": [
{
@@ -4134,20 +4035,20 @@
"type": "tidelift"
}
],
- "time": "2025-03-25T15:54:33+00:00"
+ "time": "2025-05-12T14:48:23+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v7.2.5",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "b1fe91bc1fa454a806d3f98db4ba826eb9941a54"
+ "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b1fe91bc1fa454a806d3f98db4ba826eb9941a54",
- "reference": "b1fe91bc1fa454a806d3f98db4ba826eb9941a54",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/ac7b8e163e8c83dce3abcc055a502d4486051a9f",
+ "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f",
"shasum": ""
},
"require": {
@@ -4155,8 +4056,8 @@
"psr/log": "^1|^2|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/error-handler": "^6.4|^7.0",
- "symfony/event-dispatcher": "^6.4|^7.0",
- "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^7.3",
+ "symfony/http-foundation": "^7.3",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
@@ -4232,7 +4133,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v7.2.5"
+ "source": "https://github.com/symfony/http-kernel/tree/v7.3.0"
},
"funding": [
{
@@ -4248,20 +4149,20 @@
"type": "tidelift"
}
],
- "time": "2025-03-28T13:32:50+00:00"
+ "time": "2025-05-29T07:47:32+00:00"
},
{
"name": "symfony/mailer",
- "version": "v7.2.3",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailer.git",
- "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3"
+ "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mailer/zipball/f3871b182c44997cf039f3b462af4a48fb85f9d3",
- "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/0f375bbbde96ae8c78e4aa3e63aabd486e33364c",
+ "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c",
"shasum": ""
},
"require": {
@@ -4312,7 +4213,7 @@
"description": "Helps sending emails",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/mailer/tree/v7.2.3"
+ "source": "https://github.com/symfony/mailer/tree/v7.3.0"
},
"funding": [
{
@@ -4328,20 +4229,20 @@
"type": "tidelift"
}
],
- "time": "2025-01-27T11:08:17+00:00"
+ "time": "2025-04-04T09:51:09+00:00"
},
{
"name": "symfony/mime",
- "version": "v7.2.4",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
- "reference": "87ca22046b78c3feaff04b337f33b38510fd686b"
+ "reference": "0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mime/zipball/87ca22046b78c3feaff04b337f33b38510fd686b",
- "reference": "87ca22046b78c3feaff04b337f33b38510fd686b",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9",
+ "reference": "0e7b19b2f399c31df0cdbe5d8cbf53f02f6cfcd9",
"shasum": ""
},
"require": {
@@ -4396,7 +4297,7 @@
"mime-type"
],
"support": {
- "source": "https://github.com/symfony/mime/tree/v7.2.4"
+ "source": "https://github.com/symfony/mime/tree/v7.3.0"
},
"funding": [
{
@@ -4412,11 +4313,11 @@
"type": "tidelift"
}
],
- "time": "2025-02-19T08:51:20+00:00"
+ "time": "2025-02-19T08:51:26+00:00"
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.31.0",
+ "version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
@@ -4475,7 +4376,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
},
"funding": [
{
@@ -4495,7 +4396,7 @@
},
{
"name": "symfony/polyfill-intl-grapheme",
- "version": "v1.31.0",
+ "version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
@@ -4553,7 +4454,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0"
},
"funding": [
{
@@ -4573,16 +4474,16 @@
},
{
"name": "symfony/polyfill-intl-idn",
- "version": "v1.31.0",
+ "version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
- "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773"
+ "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773",
- "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3",
+ "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3",
"shasum": ""
},
"require": {
@@ -4636,7 +4537,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0"
},
"funding": [
{
@@ -4652,11 +4553,11 @@
"type": "tidelift"
}
],
- "time": "2024-09-09T11:45:10+00:00"
+ "time": "2024-09-10T14:38:51+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.31.0",
+ "version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
@@ -4717,7 +4618,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0"
},
"funding": [
{
@@ -4737,19 +4638,20 @@
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.31.0",
+ "version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
- "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"shasum": ""
},
"require": {
+ "ext-iconv": "*",
"php": ">=7.2"
},
"provide": {
@@ -4797,7 +4699,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
},
"funding": [
{
@@ -4813,20 +4715,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-09T11:45:10+00:00"
+ "time": "2024-12-23T08:48:59+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.31.0",
+ "version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
- "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"shasum": ""
},
"require": {
@@ -4877,7 +4779,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
},
"funding": [
{
@@ -4893,11 +4795,11 @@
"type": "tidelift"
}
],
- "time": "2024-09-09T11:45:10+00:00"
+ "time": "2025-01-02T08:10:11+00:00"
},
{
"name": "symfony/polyfill-php83",
- "version": "v1.31.0",
+ "version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php83.git",
@@ -4953,7 +4855,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0"
},
"funding": [
{
@@ -4973,7 +4875,7 @@
},
{
"name": "symfony/polyfill-uuid",
- "version": "v1.31.0",
+ "version": "v1.32.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-uuid.git",
@@ -5032,7 +4934,7 @@
"uuid"
],
"support": {
- "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0"
+ "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0"
},
"funding": [
{
@@ -5052,16 +4954,16 @@
},
{
"name": "symfony/process",
- "version": "v7.2.5",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "87b7c93e57df9d8e39a093d32587702380ff045d"
+ "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d",
- "reference": "87b7c93e57df9d8e39a093d32587702380ff045d",
+ "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
+ "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af",
"shasum": ""
},
"require": {
@@ -5093,90 +4995,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/process/tree/v7.2.5"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2025-03-13T12:21:46+00:00"
- },
- {
- "name": "symfony/psr-http-message-bridge",
- "version": "v7.2.0",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/psr-http-message-bridge.git",
- "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/03f2f72319e7acaf2a9f6fcbe30ef17eec51594f",
- "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f",
- "shasum": ""
- },
- "require": {
- "php": ">=8.2",
- "psr/http-message": "^1.0|^2.0",
- "symfony/http-foundation": "^6.4|^7.0"
- },
- "conflict": {
- "php-http/discovery": "<1.15",
- "symfony/http-kernel": "<6.4"
- },
- "require-dev": {
- "nyholm/psr7": "^1.1",
- "php-http/discovery": "^1.15",
- "psr/log": "^1.1.4|^2|^3",
- "symfony/browser-kit": "^6.4|^7.0",
- "symfony/config": "^6.4|^7.0",
- "symfony/event-dispatcher": "^6.4|^7.0",
- "symfony/framework-bundle": "^6.4|^7.0",
- "symfony/http-kernel": "^6.4|^7.0"
- },
- "type": "symfony-bridge",
- "autoload": {
- "psr-4": {
- "Symfony\\Bridge\\PsrHttpMessage\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "PSR HTTP message bridge",
- "homepage": "https://symfony.com",
- "keywords": [
- "http",
- "http-message",
- "psr-17",
- "psr-7"
- ],
- "support": {
- "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.2.0"
+ "source": "https://github.com/symfony/process/tree/v7.3.0"
},
"funding": [
{
@@ -5192,20 +5011,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-26T08:57:56+00:00"
+ "time": "2025-04-17T09:11:12+00:00"
},
{
"name": "symfony/routing",
- "version": "v7.2.3",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996"
+ "reference": "8e213820c5fea844ecea29203d2a308019007c15"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/ee9a67edc6baa33e5fae662f94f91fd262930996",
- "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/8e213820c5fea844ecea29203d2a308019007c15",
+ "reference": "8e213820c5fea844ecea29203d2a308019007c15",
"shasum": ""
},
"require": {
@@ -5257,7 +5076,7 @@
"url"
],
"support": {
- "source": "https://github.com/symfony/routing/tree/v7.2.3"
+ "source": "https://github.com/symfony/routing/tree/v7.3.0"
},
"funding": [
{
@@ -5273,20 +5092,20 @@
"type": "tidelift"
}
],
- "time": "2025-01-17T10:56:55+00:00"
+ "time": "2025-05-24T20:43:28+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0"
+ "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
- "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
+ "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
"shasum": ""
},
"require": {
@@ -5304,7 +5123,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -5340,7 +5159,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -5356,20 +5175,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2025-04-25T09:37:31+00:00"
},
{
"name": "symfony/string",
- "version": "v7.2.0",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82"
+ "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82",
- "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82",
+ "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125",
+ "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125",
"shasum": ""
},
"require": {
@@ -5427,7 +5246,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v7.2.0"
+ "source": "https://github.com/symfony/string/tree/v7.3.0"
},
"funding": [
{
@@ -5443,20 +5262,20 @@
"type": "tidelift"
}
],
- "time": "2024-11-13T13:31:26+00:00"
+ "time": "2025-04-20T20:19:01+00:00"
},
{
"name": "symfony/translation",
- "version": "v7.2.4",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "283856e6981286cc0d800b53bd5703e8e363f05a"
+ "reference": "4aba29076a29a3aa667e09b791e5f868973a8667"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/283856e6981286cc0d800b53bd5703e8e363f05a",
- "reference": "283856e6981286cc0d800b53bd5703e8e363f05a",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/4aba29076a29a3aa667e09b791e5f868973a8667",
+ "reference": "4aba29076a29a3aa667e09b791e5f868973a8667",
"shasum": ""
},
"require": {
@@ -5466,6 +5285,7 @@
"symfony/translation-contracts": "^2.5|^3.0"
},
"conflict": {
+ "nikic/php-parser": "<5.0",
"symfony/config": "<6.4",
"symfony/console": "<6.4",
"symfony/dependency-injection": "<6.4",
@@ -5479,7 +5299,7 @@
"symfony/translation-implementation": "2.3|3.0"
},
"require-dev": {
- "nikic/php-parser": "^4.18|^5.0",
+ "nikic/php-parser": "^5.0",
"psr/log": "^1|^2|^3",
"symfony/config": "^6.4|^7.0",
"symfony/console": "^6.4|^7.0",
@@ -5522,7 +5342,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/translation/tree/v7.2.4"
+ "source": "https://github.com/symfony/translation/tree/v7.3.0"
},
"funding": [
{
@@ -5538,20 +5358,20 @@
"type": "tidelift"
}
],
- "time": "2025-02-13T10:27:23+00:00"
+ "time": "2025-05-29T07:19:49+00:00"
},
{
"name": "symfony/translation-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation-contracts.git",
- "reference": "4667ff3bd513750603a09c8dedbea942487fb07c"
+ "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c",
- "reference": "4667ff3bd513750603a09c8dedbea942487fb07c",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
+ "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
"shasum": ""
},
"require": {
@@ -5564,7 +5384,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -5600,7 +5420,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -5616,20 +5436,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2024-09-27T08:32:26+00:00"
},
{
"name": "symfony/uid",
- "version": "v7.2.0",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/uid.git",
- "reference": "2d294d0c48df244c71c105a169d0190bfb080426"
+ "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426",
- "reference": "2d294d0c48df244c71c105a169d0190bfb080426",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/7beeb2b885cd584cd01e126c5777206ae4c3c6a3",
+ "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3",
"shasum": ""
},
"require": {
@@ -5674,7 +5494,7 @@
"uuid"
],
"support": {
- "source": "https://github.com/symfony/uid/tree/v7.2.0"
+ "source": "https://github.com/symfony/uid/tree/v7.3.0"
},
"funding": [
{
@@ -5690,24 +5510,25 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:21:43+00:00"
+ "time": "2025-05-24T14:28:13+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v7.2.3",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "82b478c69745d8878eb60f9a049a4d584996f73a"
+ "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a",
- "reference": "82b478c69745d8878eb60f9a049a4d584996f73a",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/548f6760c54197b1084e1e5c71f6d9d523f2f78e",
+ "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e",
"shasum": ""
},
"require": {
"php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
@@ -5757,7 +5578,7 @@
"dump"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v7.2.3"
+ "source": "https://github.com/symfony/var-dumper/tree/v7.3.0"
},
"funding": [
{
@@ -5773,7 +5594,7 @@
"type": "tidelift"
}
],
- "time": "2025-01-17T11:39:41+00:00"
+ "time": "2025-04-27T18:39:23+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@@ -5832,16 +5653,16 @@
},
{
"name": "vlucas/phpdotenv",
- "version": "v5.6.1",
+ "version": "v5.6.2",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
- "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2"
+ "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2",
- "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
+ "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af",
"shasum": ""
},
"require": {
@@ -5900,7 +5721,7 @@
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
- "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1"
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2"
},
"funding": [
{
@@ -5912,7 +5733,7 @@
"type": "tidelift"
}
],
- "time": "2024-07-20T21:52:34+00:00"
+ "time": "2025-04-30T23:37:27+00:00"
},
{
"name": "voku/portable-ascii",
@@ -6113,16 +5934,16 @@
},
{
"name": "filp/whoops",
- "version": "2.18.0",
+ "version": "2.18.2",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
- "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e"
+ "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
- "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
+ "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3",
+ "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3",
"shasum": ""
},
"require": {
@@ -6172,7 +5993,7 @@
],
"support": {
"issues": "https://github.com/filp/whoops/issues",
- "source": "https://github.com/filp/whoops/tree/2.18.0"
+ "source": "https://github.com/filp/whoops/tree/2.18.2"
},
"funding": [
{
@@ -6180,24 +6001,24 @@
"type": "github"
}
],
- "time": "2025-03-15T12:00:00+00:00"
+ "time": "2025-06-11T20:42:19+00:00"
},
{
"name": "hamcrest/hamcrest-php",
- "version": "v2.0.1",
+ "version": "v2.1.1",
"source": {
"type": "git",
"url": "https://github.com/hamcrest/hamcrest-php.git",
- "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
+ "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
- "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
+ "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487",
"shasum": ""
},
"require": {
- "php": "^5.3|^7.0|^8.0"
+ "php": "^7.4|^8.0"
},
"replace": {
"cordoval/hamcrest-php": "*",
@@ -6205,8 +6026,8 @@
"kodova/hamcrest-php": "*"
},
"require-dev": {
- "phpunit/php-file-iterator": "^1.4 || ^2.0",
- "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
+ "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0"
},
"type": "library",
"extra": {
@@ -6229,22 +6050,22 @@
],
"support": {
"issues": "https://github.com/hamcrest/hamcrest-php/issues",
- "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1"
},
- "time": "2020-07-09T08:09:16+00:00"
+ "time": "2025-04-30T06:54:44+00:00"
},
{
"name": "laravel/pail",
- "version": "v1.2.2",
+ "version": "v1.2.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/pail.git",
- "reference": "f31f4980f52be17c4667f3eafe034e6826787db2"
+ "reference": "8cc3d575c1f0e57eeb923f366a37528c50d2385a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/pail/zipball/f31f4980f52be17c4667f3eafe034e6826787db2",
- "reference": "f31f4980f52be17c4667f3eafe034e6826787db2",
+ "url": "https://api.github.com/repos/laravel/pail/zipball/8cc3d575c1f0e57eeb923f366a37528c50d2385a",
+ "reference": "8cc3d575c1f0e57eeb923f366a37528c50d2385a",
"shasum": ""
},
"require": {
@@ -6264,7 +6085,7 @@
"orchestra/testbench-core": "^8.13|^9.0|^10.0",
"pestphp/pest": "^2.20|^3.0",
"pestphp/pest-plugin-type-coverage": "^2.3|^3.0",
- "phpstan/phpstan": "^1.10",
+ "phpstan/phpstan": "^1.12.27",
"symfony/var-dumper": "^6.3|^7.0"
},
"type": "library",
@@ -6300,6 +6121,7 @@
"description": "Easily delve into your Laravel application's log files directly from the command line.",
"homepage": "https://github.com/laravel/pail",
"keywords": [
+ "dev",
"laravel",
"logs",
"php",
@@ -6309,20 +6131,20 @@
"issues": "https://github.com/laravel/pail/issues",
"source": "https://github.com/laravel/pail"
},
- "time": "2025-01-28T15:15:15+00:00"
+ "time": "2025-06-05T13:55:57+00:00"
},
{
"name": "laravel/pint",
- "version": "v1.22.0",
+ "version": "v1.22.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
- "reference": "7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36"
+ "reference": "941d1927c5ca420c22710e98420287169c7bcaf7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/pint/zipball/7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36",
- "reference": "7ddfaa6523a675fae5c4123ee38fc6bfb8ee4f36",
+ "url": "https://api.github.com/repos/laravel/pint/zipball/941d1927c5ca420c22710e98420287169c7bcaf7",
+ "reference": "941d1927c5ca420c22710e98420287169c7bcaf7",
"shasum": ""
},
"require": {
@@ -6334,11 +6156,11 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.75.0",
- "illuminate/view": "^11.44.2",
- "larastan/larastan": "^3.3.1",
+ "illuminate/view": "^11.44.7",
+ "larastan/larastan": "^3.4.0",
"laravel-zero/framework": "^11.36.1",
"mockery/mockery": "^1.6.12",
- "nunomaduro/termwind": "^2.3",
+ "nunomaduro/termwind": "^2.3.1",
"pestphp/pest": "^2.36.0"
},
"bin": [
@@ -6375,20 +6197,20 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
- "time": "2025-04-08T22:11:45+00:00"
+ "time": "2025-05-08T08:38:12+00:00"
},
{
"name": "laravel/sail",
- "version": "v1.41.1",
+ "version": "v1.43.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/sail.git",
- "reference": "e5692510f1ef8e0f5096cde2b885d558f8d86592"
+ "reference": "3e7d899232a8c5e3ea4fc6dee7525ad583887e72"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/sail/zipball/e5692510f1ef8e0f5096cde2b885d558f8d86592",
- "reference": "e5692510f1ef8e0f5096cde2b885d558f8d86592",
+ "url": "https://api.github.com/repos/laravel/sail/zipball/3e7d899232a8c5e3ea4fc6dee7525ad583887e72",
+ "reference": "3e7d899232a8c5e3ea4fc6dee7525ad583887e72",
"shasum": ""
},
"require": {
@@ -6438,7 +6260,7 @@
"issues": "https://github.com/laravel/sail/issues",
"source": "https://github.com/laravel/sail"
},
- "time": "2025-04-22T13:39:39+00:00"
+ "time": "2025-05-19T13:19:21+00:00"
},
{
"name": "mockery/mockery",
@@ -6525,16 +6347,16 @@
},
{
"name": "myclabs/deep-copy",
- "version": "1.13.0",
+ "version": "1.13.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "024473a478be9df5fdaca2c793f2232fe788e414"
+ "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
- "reference": "024473a478be9df5fdaca2c793f2232fe788e414",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
+ "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
"shasum": ""
},
"require": {
@@ -6573,7 +6395,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
},
"funding": [
{
@@ -6581,27 +6403,27 @@
"type": "tidelift"
}
],
- "time": "2025-02-12T12:17:51+00:00"
+ "time": "2025-04-29T12:36:36+00:00"
},
{
"name": "nunomaduro/collision",
- "version": "v8.8.0",
+ "version": "v8.8.1",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/collision.git",
- "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8"
+ "reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nunomaduro/collision/zipball/4cf9f3b47afff38b139fb79ce54fc71799022ce8",
- "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8",
+ "url": "https://api.github.com/repos/nunomaduro/collision/zipball/44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
+ "reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
"shasum": ""
},
"require": {
- "filp/whoops": "^2.18.0",
- "nunomaduro/termwind": "^2.3.0",
+ "filp/whoops": "^2.18.1",
+ "nunomaduro/termwind": "^2.3.1",
"php": "^8.2.0",
- "symfony/console": "^7.2.5"
+ "symfony/console": "^7.3.0"
},
"conflict": {
"laravel/framework": "<11.44.2 || >=13.0.0",
@@ -6609,15 +6431,15 @@
},
"require-dev": {
"brianium/paratest": "^7.8.3",
- "larastan/larastan": "^3.2",
- "laravel/framework": "^11.44.2 || ^12.6",
- "laravel/pint": "^1.21.2",
- "laravel/sail": "^1.41.0",
- "laravel/sanctum": "^4.0.8",
+ "larastan/larastan": "^3.4.2",
+ "laravel/framework": "^11.44.2 || ^12.18",
+ "laravel/pint": "^1.22.1",
+ "laravel/sail": "^1.43.1",
+ "laravel/sanctum": "^4.1.1",
"laravel/tinker": "^2.10.1",
- "orchestra/testbench-core": "^9.12.0 || ^10.1",
- "pestphp/pest": "^3.8.0",
- "sebastian/environment": "^7.2.0 || ^8.0"
+ "orchestra/testbench-core": "^9.12.0 || ^10.4",
+ "pestphp/pest": "^3.8.2",
+ "sebastian/environment": "^7.2.1 || ^8.0"
},
"type": "library",
"extra": {
@@ -6680,7 +6502,7 @@
"type": "patreon"
}
],
- "time": "2025-04-03T14:33:09+00:00"
+ "time": "2025-06-11T01:04:21+00:00"
},
{
"name": "phar-io/manifest",
@@ -7125,16 +6947,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "11.5.18",
+ "version": "11.5.22",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "fc3e887c7f3f9917e1bf61e523413d753db00a17"
+ "reference": "4cd72faaa8f811e4cc63040cba167757660a5538"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc3e887c7f3f9917e1bf61e523413d753db00a17",
- "reference": "fc3e887c7f3f9917e1bf61e523413d753db00a17",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4cd72faaa8f811e4cc63040cba167757660a5538",
+ "reference": "4cd72faaa8f811e4cc63040cba167757660a5538",
"shasum": ""
},
"require": {
@@ -7144,7 +6966,7 @@
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
- "myclabs/deep-copy": "^1.13.0",
+ "myclabs/deep-copy": "^1.13.1",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.2",
@@ -7157,7 +6979,7 @@
"sebastian/code-unit": "^3.0.3",
"sebastian/comparator": "^6.3.1",
"sebastian/diff": "^6.0.2",
- "sebastian/environment": "^7.2.0",
+ "sebastian/environment": "^7.2.1",
"sebastian/exporter": "^6.3.0",
"sebastian/global-state": "^7.0.2",
"sebastian/object-enumerator": "^6.0.1",
@@ -7206,7 +7028,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.18"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.22"
},
"funding": [
{
@@ -7230,7 +7052,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-22T06:09:49+00:00"
+ "time": "2025-06-06T02:48:05+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -7609,23 +7431,23 @@
},
{
"name": "sebastian/environment",
- "version": "7.2.0",
+ "version": "7.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5"
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5",
- "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4",
"shasum": ""
},
"require": {
"php": ">=8.2"
},
"require-dev": {
- "phpunit/phpunit": "^11.0"
+ "phpunit/phpunit": "^11.3"
},
"suggest": {
"ext-posix": "*"
@@ -7661,15 +7483,27 @@
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
"security": "https://github.com/sebastianbergmann/environment/security/policy",
- "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0"
+ "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
"type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
+ "type": "tidelift"
}
],
- "time": "2024-07-03T04:54:44+00:00"
+ "time": "2025-05-21T11:55:47+00:00"
},
{
"name": "sebastian/exporter",
@@ -8212,16 +8046,16 @@
},
{
"name": "symfony/yaml",
- "version": "v7.2.5",
+ "version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912"
+ "reference": "cea40a48279d58dc3efee8112634cb90141156c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912",
- "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/cea40a48279d58dc3efee8112634cb90141156c2",
+ "reference": "cea40a48279d58dc3efee8112634cb90141156c2",
"shasum": ""
},
"require": {
@@ -8264,7 +8098,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/yaml/tree/v7.2.5"
+ "source": "https://github.com/symfony/yaml/tree/v7.3.0"
},
"funding": [
{
@@ -8280,7 +8114,7 @@
"type": "tidelift"
}
],
- "time": "2025-03-03T07:12:39+00:00"
+ "time": "2025-04-04T10:10:33+00:00"
},
{
"name": "theseer/tokenizer",
@@ -8335,12 +8169,13 @@
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
- "php": "^8.4"
+ "php": "^8.4",
+ "ext-grpc": "*"
},
- "platform-dev": [],
- "plugin-api-version": "2.2.0"
+ "platform-dev": {},
+ "plugin-api-version": "2.6.0"
}
diff --git a/config/cache.php b/config/cache.php
index 925f7d2..ced0f02 100644
--- a/config/cache.php
+++ b/config/cache.php
@@ -71,12 +71,6 @@
],
],
- 'redis' => [
- 'driver' => 'redis',
- 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
- 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
- ],
-
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
@@ -86,10 +80,6 @@
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
- 'octane' => [
- 'driver' => 'octane',
- ],
-
],
/*
diff --git a/config/database.php b/config/database.php
index 8910562..af51ab5 100644
--- a/config/database.php
+++ b/config/database.php
@@ -141,34 +141,4 @@
|
*/
- 'redis' => [
-
- 'client' => env('REDIS_CLIENT', 'phpredis'),
-
- 'options' => [
- 'cluster' => env('REDIS_CLUSTER', 'redis'),
- 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
- 'persistent' => env('REDIS_PERSISTENT', false),
- ],
-
- 'default' => [
- 'url' => env('REDIS_URL'),
- 'host' => env('REDIS_HOST', '127.0.0.1'),
- 'username' => env('REDIS_USERNAME'),
- 'password' => env('REDIS_PASSWORD'),
- 'port' => env('REDIS_PORT', '6379'),
- 'database' => env('REDIS_DB', '0'),
- ],
-
- 'cache' => [
- 'url' => env('REDIS_URL'),
- 'host' => env('REDIS_HOST', '127.0.0.1'),
- 'username' => env('REDIS_USERNAME'),
- 'password' => env('REDIS_PASSWORD'),
- 'port' => env('REDIS_PORT', '6379'),
- 'database' => env('REDIS_CACHE_DB', '1'),
- ],
-
- ],
-
];
diff --git a/config/octane.php b/config/octane.php
deleted file mode 100644
index 671915a..0000000
--- a/config/octane.php
+++ /dev/null
@@ -1,224 +0,0 @@
- env('OCTANE_SERVER', 'roadrunner'),
-
- /*
- |--------------------------------------------------------------------------
- | Force HTTPS
- |--------------------------------------------------------------------------
- |
- | When this configuration value is set to "true", Octane will inform the
- | framework that all absolute links must be generated using the HTTPS
- | protocol. Otherwise your links may be generated using plain HTTP.
- |
- */
-
- 'https' => env('OCTANE_HTTPS', true),
-
- /*
- |--------------------------------------------------------------------------
- | Octane Listeners
- |--------------------------------------------------------------------------
- |
- | All of the event listeners for Octane's events are defined below. These
- | listeners are responsible for resetting your application's state for
- | the next request. You may even add your own listeners to the list.
- |
- */
-
- 'listeners' => [
- WorkerStarting::class => [
- EnsureUploadedFilesAreValid::class,
- EnsureUploadedFilesCanBeMoved::class,
- ],
-
- RequestReceived::class => [
- ...Octane::prepareApplicationForNextOperation(),
- ...Octane::prepareApplicationForNextRequest(),
- //
- ],
-
- RequestHandled::class => [
- //
- ],
-
- RequestTerminated::class => [
- // FlushUploadedFiles::class,
- ],
-
- TaskReceived::class => [
- ...Octane::prepareApplicationForNextOperation(),
- //
- ],
-
- TaskTerminated::class => [
- //
- ],
-
- TickReceived::class => [
- ...Octane::prepareApplicationForNextOperation(),
- //
- ],
-
- TickTerminated::class => [
- //
- ],
-
- OperationTerminated::class => [
- FlushOnce::class,
- FlushTemporaryContainerInstances::class,
- // DisconnectFromDatabases::class,
- // CollectGarbage::class,
- ],
-
- WorkerErrorOccurred::class => [
- ReportException::class,
- StopWorkerIfNecessary::class,
- ],
-
- WorkerStopping::class => [
- CloseMonologHandlers::class,
- ],
- ],
-
- /*
- |--------------------------------------------------------------------------
- | Warm / Flush Bindings
- |--------------------------------------------------------------------------
- |
- | The bindings listed below will either be pre-warmed when a worker boots
- | or they will be flushed before every new request. Flushing a binding
- | will force the container to resolve that binding again when asked.
- |
- */
-
- 'warm' => [
- ...Octane::defaultServicesToWarm(),
- ],
-
- 'flush' => [
- //
- ],
-
- /*
- |--------------------------------------------------------------------------
- | Octane Swoole Tables
- |--------------------------------------------------------------------------
- |
- | While using Swoole, you may define additional tables as required by the
- | application. These tables can be used to store data that needs to be
- | quickly accessed by other workers on the particular Swoole server.
- |
- */
-
- 'tables' => [
- 'example:1000' => [
- 'name' => 'string:1000',
- 'votes' => 'int',
- ],
- ],
-
- /*
- |--------------------------------------------------------------------------
- | Octane Swoole Cache Table
- |--------------------------------------------------------------------------
- |
- | While using Swoole, you may leverage the Octane cache, which is powered
- | by a Swoole table. You may set the maximum number of rows as well as
- | the number of bytes per row using the configuration options below.
- |
- */
-
- 'cache' => [
- 'rows' => 1000,
- 'bytes' => 10000,
- ],
-
- /*
- |--------------------------------------------------------------------------
- | File Watching
- |--------------------------------------------------------------------------
- |
- | The following list of files and directories will be watched when using
- | the --watch option offered by Octane. If any of the directories and
- | files are changed, Octane will automatically reload your workers.
- |
- */
-
- 'watch' => [
- 'app',
- 'bootstrap',
- 'config/**/*.php',
- 'database/**/*.php',
- 'public/**/*.php',
- 'resources/**/*.php',
- 'routes',
- 'composer.lock',
- '.env',
- ],
-
- /*
- |--------------------------------------------------------------------------
- | Garbage Collection Threshold
- |--------------------------------------------------------------------------
- |
- | When executing long-lived PHP scripts such as Octane, memory can build
- | up before being cleared by PHP. You can force Octane to run garbage
- | collection if your application consumes this amount of megabytes.
- |
- */
-
- 'garbage' => 50,
-
- /*
- |--------------------------------------------------------------------------
- | Maximum Execution Time
- |--------------------------------------------------------------------------
- |
- | The following setting configures the maximum execution time for requests
- | being handled by Octane. You may set this value to 0 to indicate that
- | there isn't a specific time limit on Octane request execution time.
- |
- */
-
- 'max_execution_time' => 60,
-
-];
diff --git a/config/queue.php b/config/queue.php
index 116bd8d..fc82a42 100644
--- a/config/queue.php
+++ b/config/queue.php
@@ -63,15 +63,6 @@
'after_commit' => false,
],
- 'redis' => [
- 'driver' => 'redis',
- 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
- 'queue' => env('REDIS_QUEUE', 'default'),
- 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
- 'block_for' => null,
- 'after_commit' => false,
- ],
-
],
/*
diff --git a/config/services.php b/config/services.php
index 85c8b10..a671c62 100644
--- a/config/services.php
+++ b/config/services.php
@@ -39,5 +39,13 @@
'user_url' => env('USER_SERVICE_URL', 'http://user-webserver/'),
'product_url' => env('PRODUCT_SERVICE_URL', 'http://product-webserver/'),
'order_url' => env('ORDER_SERVICE_URL', 'http://order-webserver/'),
+ 'ai_url' => env('AI_SERVICE_URL', 'http://ai-service/'),
+ ],
+
+ 'microservices_grpc' => [
+ 'UserService' => env('USER_SERVICE_GRPC', 'user:50051'),
+ 'ProductService' => env('PRODUCT_SERVICE_GRPC', 'product:50051'),
+ 'OrderService' => env('ORDER_SERVICE_GRPC', 'order:50051'),
+ 'AIService' => env('AI_SERVICE_GRPC', 'ai-service:50051'),
]
];
diff --git a/docker-compose.yml b/docker-compose.yml
index e228f14..e4b07ee 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,7 +2,7 @@ services:
api-gw:
build:
context: .
- image: ommrgazar315/api-gw:2.0
+ image: ommrgazar315/api-gw:2.5_grpc
container_name: api-gw
restart: unless-stopped
working_dir: /var/www
@@ -31,11 +31,14 @@ services:
- DB_DATABASE=api-gw_db
- DB_USERNAME=laravel
- DB_PASSWORD=api-gw_pass
- - BROADCAST_DRIVER=redis
- - CACHE_DRIVER=redis
- - QUEUE_CONNECTION=redis
- - SESSION_DRIVER=redis
- - REDIS_HOST=redis
+ - PRODUCT_SERVICE_URL=http://product-webserver/
+ - USER_SERVICE_URL=http://user-webserver/
+ - ORDER_SERVICE_URL=http://order-webserver/
+ - AI_SERVICE_URL=http://ai-service/
+ - USER_SERVICE_GRPC=user:50051
+ - PRODUCT_SERVICE_GRPC=product:50051
+ - ORDER_SERVICE_GRPC=order:50051
+ - AI_SERVICE_GRPC=ai-service:50051
api-gw_db:
image: mysql:8.0
diff --git a/docker-composeServer.yml b/docker-composeServer.yml
index e13daf2..a32509f 100644
--- a/docker-composeServer.yml
+++ b/docker-composeServer.yml
@@ -12,11 +12,6 @@ services:
- DB_DATABASE=api-gw_db
- DB_USERNAME=laravel
- DB_PASSWORD=api-gw_pass
- - BROADCAST_DRIVER=redis
- - CACHE_DRIVER=redis
- - QUEUE_CONNECTION=redis
- - SESSION_DRIVER=redis
- - REDIS_HOST=redis
volumes:
api-gw-db:
diff --git a/php.ini b/php.ini
index 6d05b31..179b1b8 100644
--- a/php.ini
+++ b/php.ini
@@ -1,5 +1,37 @@
+; OPcache settings
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
+opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
-opcache.validate_timestamps=0
+opcache.revalidate_freq=2
+opcache.save_comments=1
+opcache.fast_shutdown=1
+
+; PHP settings
+memory_limit=256M
+max_execution_time=300
+max_input_time=300
+post_max_size=50M
+upload_max_filesize=50M
+
+; gRPC settings
+grpc.enable_fork_support=1
+grpc.poll_strategy=epoll1
+grpc.max_receive_message_length=4194304
+grpc.max_send_message_length=4194304
+
+; Error reporting
+display_errors=Off
+log_errors=On
+error_log=/var/log/php_errors.log
+
+; Process control
+pcntl.async_signals=On
+
+; Timezone
+date.timezone=UTC
+
+; Session (if needed)
+session.save_handler=files
+session.save_path="/tmp"
diff --git a/proto/apigw.proto b/proto/apigw.proto
new file mode 100644
index 0000000..d8b2606
--- /dev/null
+++ b/proto/apigw.proto
@@ -0,0 +1,110 @@
+syntax = "proto3";
+
+package gateway;
+
+import "google/protobuf/any.proto";
+import "google/protobuf/timestamp.proto";
+
+option php_namespace = "App\\Grpc";
+option php_metadata_namespace = "App\\Grpc\\Gateway";
+
+// Generic request that can handle any payload
+message FlexibleRequest {
+ // Service identifier (user-service, product-service, etc.)
+ string service = 1;
+
+ // Method/endpoint identifier
+ string method = 2;
+
+ // Generic payload - can be any protobuf message
+ google.protobuf.Any payload = 3;
+
+ // Request metadata
+ map headers = 4;
+
+ // Optional request ID for tracing
+ string request_id = 5;
+}
+
+// Generic response that can handle any payload
+message FlexibleResponse {
+ // Success indicator
+ bool success = 1;
+
+ // HTTP-like status code
+ int32 status_code = 2;
+
+ // Generic response data - can be any protobuf message or raw data
+ google.protobuf.Any data = 3;
+
+ // Error information (if any)
+ ErrorInfo error = 4;
+
+ // Response metadata
+ map headers = 5;
+
+ // Request ID for tracing
+ string request_id = 6;
+
+ // Response timestamp
+ google.protobuf.Timestamp timestamp = 7;
+}
+
+// Alternative: Pure bytes approach (even more flexible)
+message BytesRequest {
+ string service = 1;
+ string method = 2;
+ bytes payload = 3;
+ string content_type = 4; // "application/json", "application/protobuf", etc.
+ map headers = 5;
+ string request_id = 6;
+}
+
+message BytesResponse {
+ bool success = 1;
+ int32 status_code = 2;
+ bytes data = 3;
+ string content_type = 4;
+ ErrorInfo error = 5;
+ map headers = 6;
+ string request_id = 7;
+ google.protobuf.Timestamp timestamp = 8;
+}
+
+// Error structure
+message ErrorInfo {
+ string message = 1;
+ string code = 2;
+ repeated string details = 3;
+ map metadata = 4;
+}
+
+// Gateway service definition
+service GatewayService {
+ // Handle any request with protobuf Any
+ rpc HandleRequest(FlexibleRequest) returns (FlexibleResponse);
+
+ // Handle any request with raw bytes (maximum flexibility)
+ rpc HandleBytesRequest(BytesRequest) returns (BytesResponse);
+
+ // Streaming support for real-time data
+ rpc HandleStreamRequest(stream FlexibleRequest) returns (stream FlexibleResponse);
+}
+
+// Optional: Health check service
+service HealthService {
+ rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
+}
+
+message HealthCheckRequest {
+ string service = 1;
+}
+
+message HealthCheckResponse {
+ enum ServingStatus {
+ UNKNOWN = 0;
+ SERVING = 1;
+ NOT_SERVING = 2;
+ }
+ ServingStatus status = 1;
+}
diff --git a/proto/user_service.proto b/proto/user_service.proto
new file mode 100644
index 0000000..483327b
--- /dev/null
+++ b/proto/user_service.proto
@@ -0,0 +1,159 @@
+// proto/user_service.proto
+syntax = "proto3";
+
+package user;
+
+option php_namespace = "App\\Grpc\\User";
+option php_metadata_namespace = "App\\Grpc\\GPBMetadata\\User";
+
+service UserService {
+ rpc GetUser(GetUserRequest) returns (UserResponse);
+ rpc CreateUser(CreateUserRequest) returns (UserResponse);
+ rpc UpdateUser(UpdateUserRequest) returns (UserResponse);
+ rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
+ rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
+ rpc AuthenticateUser(AuthenticateRequest) returns (AuthenticateResponse);
+ rpc GetUserProfile(UserProfileRequest) returns (UserProfileResponse);
+ rpc LogoutUser(LogoutRequest) returns (LogoutResponse);
+ rpc ValidateToken(ValidateTokenRequest) returns (ValidateTokenResponse);
+ rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
+ rpc ChangePassword(ChangePasswordRequest) returns (ChangePasswordResponse);
+}
+
+// Request Messages
+message GetUserRequest {
+ int64 id = 1;
+}
+
+message CreateUserRequest {
+ string name = 1;
+ string email = 2;
+ string password = 3;
+ string role = 4;
+}
+
+message UpdateUserRequest {
+ int64 id = 1;
+ string name = 2;
+ string email = 3;
+ string password = 4;
+}
+
+message DeleteUserRequest {
+ int64 id = 1;
+}
+
+message ListUsersRequest {
+ int32 page = 1;
+ int32 per_page = 2;
+ string search = 3;
+}
+
+message AuthenticateRequest {
+ string email = 1;
+ string password = 2;
+}
+
+message UserProfileRequest {
+ repeated string fields = 2;
+}
+
+message LogoutRequest {
+}
+
+message ValidateTokenRequest {
+ string token = 1;
+}
+
+message RefreshTokenRequest {
+}
+
+message ChangePasswordRequest {
+ string current_password = 1;
+ string new_password = 2;
+}
+
+// Data Messages
+message User {
+ int64 id = 1;
+ string name = 2;
+ string email = 3;
+ string role = 4;
+ string photo_path = 5;
+ string access_token = 6;
+ string created_at = 7;
+ string updated_at = 8;
+}
+
+message CartItem {
+ int64 id = 1;
+ string type = 2;
+ int32 quantity = 3;
+ double price = 4;
+}
+
+// Response Messages
+message UserResponse {
+ User user = 1;
+ bool success = 2;
+ string message = 3;
+}
+
+message DeleteUserResponse {
+ bool success = 1;
+ string message = 2;
+}
+
+message ListUsersResponse {
+ repeated User users = 1;
+ int32 total = 2;
+ int32 current_page = 3;
+ int32 last_page = 4;
+ bool success = 5;
+ string message = 6;
+}
+
+message AuthenticateResponse {
+ User user = 1;
+ string token = 2;
+ string token_type = 3;
+ string expires_at = 4;
+ bool success = 5;
+ string message = 6;
+}
+
+message UserProfileResponse {
+ int64 id = 1;
+ string name = 2;
+ string email = 3;
+ string photo_path = 4;
+ string role = 5;
+ repeated CartItem cart_items = 6;
+ bool success = 7;
+ string message = 8;
+}
+
+message LogoutResponse {
+ bool success = 1;
+ string message = 2;
+}
+
+message ValidateTokenResponse {
+ User user = 1;
+ bool valid = 2;
+ bool success = 3;
+ string message = 4;
+}
+
+message RefreshTokenResponse {
+ string token = 1;
+ string token_type = 2;
+ string expires_at = 3;
+ bool success = 4;
+ string message = 5;
+}
+
+message ChangePasswordResponse {
+ bool success = 1;
+ string message = 2;
+}
diff --git a/routes/api.php b/routes/api.php
index e5d20b5..39fdf87 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -1,19 +1,20 @@
prefix('auth')
+Route::controller(AuthApiController::class)->prefix('auth')
->group(function () {
Route::post('login', 'login');
Route::post('signup', 'signup');
@@ -23,46 +24,51 @@
Route::resource('users', UserApiController::class)->except(['create', 'edit'])->middleware('authenticate');
Route::get('/userProfile', [UserApiController::class, 'userProfile'])->middleware('authenticate');
-Route::apiResource('/user/shipping-addresses', ShippingAddressController::class)->middleware('authenticate');
-Route::get('/user/current-shipping-address', [ShippingAddressController::class, 'getCurrent'])->middleware('authenticate');
+Route::apiResource('/user/shipping-addresses', ShippingAddressApiController::class)->middleware('authenticate');
+Route::get('/user/current-shipping-address', [ShippingAddressApiController::class, 'getCurrent'])->middleware('authenticate');
-Route::controller(PasswordController::class)->middleware('guest')->group(function () {
+Route::controller(PasswordApiController::class)->middleware('guest')->group(function () {
Route::post('/forgot-password', 'sendResetLink');
Route::post('/reset-password', 'resetPassword');
});
-Route::post('auth/google/callback', [GoogleController::class, 'handleGoogleCallback']);
+Route::post('auth/google/callback', [GoogleApiController::class, 'handleGoogleCallback']);
Route::group(['middleware' => 'authenticate', 'prefix' => 'carts'], function () {
- Route::get('/', [ShoppingCartController::class, 'index']);
- Route::post('/', [ShoppingCartController::class, 'add']);
- Route::delete('/{item_type}/{item_id}', [ShoppingCartController::class, 'remove']);
- Route::delete('/', [ShoppingCartController::class, 'clear']);
+ Route::get('/', [ShoppingCartApiController::class, 'index']);
+ Route::post('/', [ShoppingCartApiController::class, 'add']);
+ Route::delete('/{item_type}/{item_id}', [ShoppingCartApiController::class, 'remove']);
+ Route::delete('/', [ShoppingCartApiController::class, 'clear']);
});
-Route::controller(WishListController::class)->prefix('wishlists')->middleware('authenticate')->group(function () {
- Route::get('/', [WishListController::class, 'index']);
- Route::post('/{item_type}/{item_id}', [WishListController::class, 'store']);
- Route::delete('/{item_type}/{item_id}', [WishListController::class, 'destroy']);
+Route::controller(WishListApiController::class)->prefix('wishlists')->middleware('authenticate')->group(function () {
+ Route::get('/', [WishListApiController::class, 'index']);
+ Route::post('/{item_type}/{item_id}', [WishListApiController::class, 'store']);
+ Route::delete('/clear', [WishListApiController::class, 'clear']);
+
});
//Product Service
-Route::resource('products', ProductController::class)
+Route::resource('products', ProductApiController::class)
->except(['update', 'create', 'edit'])
->middleware('authenticate')
->where(['product' => '[0-9]+']);
-Route::post('products/{product}', [ProductController::class, 'update'])
+Route::post('products/{product}', [ProductApiController::class, 'update'])
->middleware('authenticate')
->where('product', '[0-9]+');
// Offer Service
-Route::resource('offers', OfferController::class)
+Route::resource('offers', OfferApiController::class)
->except(['create', 'edit'])
->middleware('authenticate')
->where(['offer' => '[0-9]+']);
Route::apiResource('/orders', OrderApiController::class)->middleware('authenticate');
+Route::get('/purchase-history', [OrderApiController::class, 'purchaseHistory'])->middleware('authenticate');
+
+Route::post('products/batch', [ProductApiController::class, 'batch']);
+Route::post('/offers/batch', [OfferApiController::class, 'batch']);
-Route::post('products/batch', [ProductController::class, 'batch']);
-Route::post('/offers/batch', [OfferController::class, 'batch']);
+// AI Service
+Route::get('/recommendations', [ApiGatewayController::class, 'getRecommendedProducts'])->middleware('authenticate');
diff --git a/routes/grpc.php b/routes/grpc.php
new file mode 100644
index 0000000..2eb66b8
--- /dev/null
+++ b/routes/grpc.php
@@ -0,0 +1,28 @@
+method('Login', 'AuthController', 'login')
+ ->method('SignUp', 'AuthController', 'signup')
+ ->method('Logout', 'AuthController', 'logout')
+ ->method('ValidateToken', 'AuthController', 'validateToken')
+ // User Service Methods
+ ->method('GetUser', 'UserController', 'show')
+ ->method('UpdateUser', 'UserController', 'update')
+ ->method('DeleteUser', 'UserController', 'destroy')
+ ->method('ListUsers', 'UserController', 'index');
+
+// Product Service Routes
+GrpcRoute::service('ProductService')
+ ->method('listProducts', 'ProductController', 'index')
+ ->method('getProduct', 'ProductController', 'show');
+
+// Order Service Routes
+GrpcRoute::service('OrderService')
+ ->method('GetOrder', 'OrderController', 'show')
+ ->method('CreateOrder', 'OrderController', 'store')
+ ->method('UpdateOrderStatus', 'OrderController', 'updateStatus')
+ ->method('ListOrders', 'OrderController', 'index');
diff --git a/supervisord.conf b/supervisord.conf
new file mode 100644
index 0000000..79b1312
--- /dev/null
+++ b/supervisord.conf
@@ -0,0 +1,39 @@
+[supervisord]
+nodaemon=true
+user=root
+
+[program:php-fpm]
+command=/usr/local/sbin/php-fpm --nodaemonize
+autostart=true
+autorestart=true
+stdout_logfile=/dev/stdout
+redirect_stderr=true
+
+[unix_http_server]
+file=/var/run/supervisor.sock
+chmod=0700
+chown=root:root
+
+[inet_http_server]
+port=127.0.0.1:9001
+
+[supervisorctl]
+serverurl=unix:///var/run/supervisor.sock
+
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+
+[program:grpc-server]
+command=php /var/www/artisan grpc:serve --port=50051 --host=0.0.0.0
+directory=/var/www
+autostart=true
+autorestart=true
+user=www-data
+numprocs=1
+redirect_stderr=true
+stdout_logfile=/var/log/supervisor/grpc-server.log
+stderr_logfile=/var/log/supervisor/grpc-server-error.log
+stdout_logfile_maxbytes=10MB
+stdout_logfile_backups=3
+stopwaitsecs=10
+priority=100