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..72da1d4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,61 +1,80 @@
-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 \
+ 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 \
+ 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 +84,37 @@ RUN composer install \
--classmap-authoritative \
&& composer clear-cache
-# Copy application files
+# 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
+# Create a non-root user for security
+RUN groupadd -g 1000 laravel \
+ && useradd -u 1000 -g laravel -m laravel
+
+# 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\
+php-fpm\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/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/BaseResponse.php b/app/Grpc/BaseResponse.php
new file mode 100644
index 0000000..668c300
--- /dev/null
+++ b/app/Grpc/BaseResponse.php
@@ -0,0 +1,29 @@
+ $this->code,
+ 'message' => $this->message,
+ 'data' => $this->data,
+ ];
+ }
+
+ protected function buildUserResponse($response, bool $success = true, string $message = '')
+ {
+ $response->setSuccess($success);
+ $response->setMessage($message);
+ return $response;
+ }
+}
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/DTOs/BaseGrpcDTO.php b/app/Grpc/DTOs/BaseGrpcDTO.php
new file mode 100644
index 0000000..8d8207d
--- /dev/null
+++ b/app/Grpc/DTOs/BaseGrpcDTO.php
@@ -0,0 +1,123 @@
+success = $success;
+ $this->message = $message;
+ }
+
+ public function isSuccess(): bool
+ {
+ return $this->success;
+ }
+
+ public function setSuccess(bool $success): self
+ {
+ $this->success = $success;
+ return $this;
+ }
+
+ public function getMessage(): string
+ {
+ return $this->message;
+ }
+
+ public function setMessage(string $message): self
+ {
+ $this->message = $message;
+ return $this;
+ }
+
+ public function getErrors(): ?array
+ {
+ return $this->errors;
+ }
+
+ public function setErrors(?array $errors): self
+ {
+ $this->errors = $errors;
+ return $this;
+ }
+
+ public function addError(string $field, string $error): self
+ {
+ if (!$this->errors) {
+ $this->errors = [];
+ }
+ $this->errors[$field] = $error;
+ return $this;
+ }
+
+ public function getMetadata(): ?array
+ {
+ return $this->metadata;
+ }
+
+ public function setMetadata(?array $metadata): self
+ {
+ $this->metadata = $metadata;
+ return $this;
+ }
+
+ public function addMetadata(string $key, $value): self
+ {
+ if (!$this->metadata) {
+ $this->metadata = [];
+ }
+ $this->metadata[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * Convert DTO to array
+ */
+ public function toArray(): array
+ {
+ $data = [
+ 'success' => $this->success,
+ 'message' => $this->message,
+ ];
+
+ if ($this->errors) {
+ $data['errors'] = $this->errors;
+ }
+
+ if ($this->metadata) {
+ $data['metadata'] = $this->metadata;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Create a failed response
+ */
+ public static function failure(string $message, ?array $errors = null): static
+ {
+ $dto = new static(false, $message);
+ if ($errors) {
+ $dto->setErrors($errors);
+ }
+ return $dto;
+ }
+
+ /**
+ * Create a successful response
+ */
+ public static function success(string $message = 'Operation completed successfully'): static
+ {
+ return new static(true, $message);
+ }
+}
diff --git a/app/Grpc/DTOs/User/AuthenticateGrpcDTO.php b/app/Grpc/DTOs/User/AuthenticateGrpcDTO.php
new file mode 100644
index 0000000..14e6a07
--- /dev/null
+++ b/app/Grpc/DTOs/User/AuthenticateGrpcDTO.php
@@ -0,0 +1,49 @@
+token = $token;
+ return $this;
+ }
+
+ public function setTokenType(string $tokenType): self
+ {
+ $this->tokenType = $tokenType;
+ return $this;
+ }
+
+ public function setExpiresAt(Carbon $expiresAt): self
+ {
+ $this->expiresAt = $expiresAt;
+ return $this;
+ }
+
+ public function toArray(): array
+ {
+ $data = parent::toArray();
+
+ if ($this->token) {
+ $data['token'] = $this->token;
+ $data['token_type'] = $this->tokenType;
+ }
+
+ if ($this->expiresAt) {
+ $data['expires_at'] = $this->expiresAt->toISOString();
+ }
+
+ return $data;
+ }
+}
diff --git a/app/Grpc/DTOs/User/RefreshTokenGrpcDTO.php b/app/Grpc/DTOs/User/RefreshTokenGrpcDTO.php
new file mode 100644
index 0000000..78471c6
--- /dev/null
+++ b/app/Grpc/DTOs/User/RefreshTokenGrpcDTO.php
@@ -0,0 +1,37 @@
+token = $token;
+ $this->expiresAt = $expiresAt;
+ $this->tokenType = $tokenType;
+ return $this;
+ }
+
+ public function toArray(): array
+ {
+ $data = parent::toArray();
+
+ if ($this->token) {
+ $data['token'] = $this->token;
+ $data['token_type'] = $this->tokenType;
+ $data['expires_at'] = $this->expiresAt?->toISOString();
+ }
+
+ return $data;
+ }
+}
diff --git a/app/Grpc/DTOs/User/SimpleResponseGrpcDTO.php b/app/Grpc/DTOs/User/SimpleResponseGrpcDTO.php
new file mode 100644
index 0000000..c7ecb1d
--- /dev/null
+++ b/app/Grpc/DTOs/User/SimpleResponseGrpcDTO.php
@@ -0,0 +1,14 @@
+user = [
+ 'id' => $user->id,
+ 'name' => $user->name,
+ 'email' => $user->email,
+ 'role' => $user->role ?? 'user',
+ 'photo_path' => $user->photo_path,
+ 'created_at' => $user->created_at?->toISOString(),
+ 'updated_at' => $user->updated_at?->toISOString(),
+ ];
+
+ // Include access token if present
+ if (isset($user->accessToken)) {
+ $this->user['access_token'] = $user->accessToken;
+ }
+
+ return $this;
+ }
+
+ public function getUser(): ?array
+ {
+ return $this->user;
+ }
+
+ public function toArray(): array
+ {
+ $data = parent::toArray();
+ if ($this->user) {
+ $data['user'] = $this->user;
+ }
+ return $data;
+ }
+}
diff --git a/app/Grpc/DTOs/User/UserListGrpcDTO.php b/app/Grpc/DTOs/User/UserListGrpcDTO.php
new file mode 100644
index 0000000..a24bb21
--- /dev/null
+++ b/app/Grpc/DTOs/User/UserListGrpcDTO.php
@@ -0,0 +1,60 @@
+users = array_map(function ($user) {
+ if ($user instanceof User) {
+ return [
+ 'id' => $user->id,
+ 'name' => $user->name,
+ 'email' => $user->email,
+ 'role' => $user->role ?? 'user',
+ 'photo_path' => $user->photo_path,
+ 'created_at' => $user->created_at?->toISOString(),
+ 'updated_at' => $user->updated_at?->toISOString(),
+ ];
+ }
+ return $user;
+ }, $users);
+
+ return $this;
+ }
+
+ public function setPagination(int $total, int $currentPage, int $perPage): self
+ {
+ $this->total = $total;
+ $this->currentPage = $currentPage;
+ $this->perPage = $perPage;
+ $this->lastPage = (int)ceil($total / $perPage);
+ return $this;
+ }
+
+ public function toArray(): array
+ {
+ $data = parent::toArray();
+ $data['users'] = $this->users;
+ $data['pagination'] = [
+ 'total' => $this->total,
+ 'current_page' => $this->currentPage,
+ 'last_page' => $this->lastPage,
+ 'per_page' => $this->perPage,
+ ];
+ return $data;
+ }
+}
diff --git a/app/Grpc/DTOs/User/UserProfileGrpcDTO.php b/app/Grpc/DTOs/User/UserProfileGrpcDTO.php
new file mode 100644
index 0000000..b5088b2
--- /dev/null
+++ b/app/Grpc/DTOs/User/UserProfileGrpcDTO.php
@@ -0,0 +1,62 @@
+id = $profileData['id'] ?? null;
+ $this->name = $profileData['name'] ?? null;
+ $this->email = $profileData['email'] ?? null;
+ $this->photoUrl = $profileData['photo_url'] ?? null;
+ $this->role = $profileData['role'] ?? null;
+
+ if (isset($profileData['cart'])) {
+ $this->setCartItems($profileData['cart']);
+ }
+
+ return $this;
+ }
+
+ public function setCartItems(array $items): self
+ {
+ $this->cartItems = $items;
+ return $this;
+ }
+
+ public function toArray(): array
+ {
+ $data = parent::toArray();
+
+ $profile = array_filter([
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'email' => $this->email,
+ 'photo_url' => $this->photoUrl,
+ 'role' => $this->role,
+ ], fn($value) => $value !== null);
+
+ if (!empty($profile)) {
+ $data['profile'] = $profile;
+ }
+
+ if (!empty($this->cartItems)) {
+ $data['cart_items'] = $this->cartItems;
+ }
+
+ return $data;
+ }
+}
diff --git a/app/Grpc/DTOs/User/ValidateTokenGrpcDTO.php b/app/Grpc/DTOs/User/ValidateTokenGrpcDTO.php
new file mode 100644
index 0000000..c5c6e7d
--- /dev/null
+++ b/app/Grpc/DTOs/User/ValidateTokenGrpcDTO.php
@@ -0,0 +1,24 @@
+valid = $valid;
+ return $this;
+ }
+
+ public function toArray(): array
+ {
+ $data = parent::toArray();
+ $data['valid'] = $this->valid;
+ return $data;
+ }
+}
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/GPBMetadata/User/UserService.php b/app/Grpc/GPBMetadata/User/UserService.php
new file mode 100644
index 0000000..b54220e
--- /dev/null
+++ b/app/Grpc/GPBMetadata/User/UserService.php
@@ -0,0 +1,24 @@
+internalAddGeneratedFile(hex2bin(
+ "0ab0130a12757365725f736572766963652e70726f746f120475736572221c0a0e4765745573657252657175657374120a0a02696418012001280322500a114372656174655573657252657175657374120c0a046e616d65180120012809120d0a05656d61696c18022001280912100a0870617373776f7264180320012809120c0a04726f6c65180420012809224e0a115570646174655573657252657175657374120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912100a0870617373776f7264180420012809221f0a1144656c6574655573657252657175657374120a0a02696418012001280322420a104c697374557365727352657175657374120c0a047061676518012001280512100a087065725f70616765180220012805120e0a0673656172636818032001280922360a1341757468656e74696361746552657175657374120d0a05656d61696c18012001280912100a0870617373776f726418022001280922240a125573657250726f66696c6552657175657374120e0a066669656c6473180220032809220f0a0d4c6f676f75745265717565737422250a1456616c6964617465546f6b656e52657175657374120d0a05746f6b656e18012001280922150a1352656672657368546f6b656e5265717565737422470a154368616e676550617373776f72645265717565737412180a1063757272656e745f70617373776f726418012001280912140a0c6e65775f70617373776f7264180220012809228f010a0455736572120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c180320012809120c0a04726f6c6518042001280912120a0a70686f746f5f7061746818052001280912140a0c6163636573735f746f6b656e18062001280912120a0a637265617465645f617418072001280912120a0a757064617465645f617418082001280922450a08436172744974656d120a0a026964180120012803120c0a047479706518022001280912100a087175616e74697479180320012805120d0a057072696365180420012801224a0a0c55736572526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120f0a0773756363657373180220012808120f0a076d65737361676518032001280922360a1244656c65746555736572526573706f6e7365120f0a0773756363657373180120012808120f0a076d6573736167651802200128092288010a114c6973745573657273526573706f6e736512190a05757365727318012003280b320a2e757365722e55736572120d0a05746f74616c18022001280512140a0c63757272656e745f7061676518032001280512110a096c6173745f70616765180420012805120f0a0773756363657373180520012808120f0a076d6573736167651806200128092289010a1441757468656e746963617465526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a05746f6b656e18022001280912120a0a746f6b656e5f7479706518032001280912120a0a657870697265735f6174180420012809120f0a0773756363657373180520012808120f0a076d65737361676518062001280922a6010a135573657250726f66696c65526573706f6e7365120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912120a0a70686f746f5f70617468180420012809120c0a04726f6c6518052001280912220a0a636172745f6974656d7318062003280b320e2e757365722e436172744974656d120f0a0773756363657373180720012808120f0a076d65737361676518082001280922320a0e4c6f676f7574526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280922620a1556616c6964617465546f6b656e526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a0576616c6964180220012808120f0a0773756363657373180320012808120f0a076d657373616765180420012809226f0a1452656672657368546f6b656e526573706f6e7365120d0a05746f6b656e18012001280912120a0a746f6b656e5f7479706518022001280912120a0a657870697265735f6174180320012809120f0a0773756363657373180420012808120f0a076d657373616765180520012809223a0a164368616e676550617373776f7264526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280932e0050a0b557365725365727669636512330a074765745573657212142e757365722e47657455736572526571756573741a122e757365722e55736572526573706f6e736512390a0a4372656174655573657212172e757365722e43726561746555736572526571756573741a122e757365722e55736572526573706f6e736512390a0a5570646174655573657212172e757365722e55706461746555736572526571756573741a122e757365722e55736572526573706f6e7365123f0a0a44656c6574655573657212172e757365722e44656c65746555736572526571756573741a182e757365722e44656c65746555736572526573706f6e7365123c0a094c697374557365727312162e757365722e4c6973745573657273526571756573741a172e757365722e4c6973745573657273526573706f6e736512490a1041757468656e7469636174655573657212192e757365722e41757468656e746963617465526571756573741a1a2e757365722e41757468656e746963617465526573706f6e736512450a0e4765745573657250726f66696c6512182e757365722e5573657250726f66696c65526571756573741a192e757365722e5573657250726f66696c65526573706f6e736512370a0a4c6f676f75745573657212132e757365722e4c6f676f7574526571756573741a142e757365722e4c6f676f7574526573706f6e736512480a0d56616c6964617465546f6b656e121a2e757365722e56616c6964617465546f6b656e526571756573741a1b2e757365722e56616c6964617465546f6b656e526573706f6e736512450a0c52656672657368546f6b656e12192e757365722e52656672657368546f6b656e526571756573741a1a2e757365722e52656672657368546f6b656e526573706f6e7365124b0a0e4368616e676550617373776f7264121b2e757365722e4368616e676550617373776f7264526571756573741a1c2e757365722e4368616e676550617373776f7264526573706f6e7365422cca020d4170705c477270635c55736572e202194170705c477270635c4750424d657461646174615c55736572620670726f746f33"
+ ), true);
+
+ static::$is_initialized = true;
+ }
+}
+
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..bd81612
--- /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/HealthCheckRequest.php b/app/Grpc/HealthCheckRequest.php
new file mode 100644
index 0000000..262db2c
--- /dev/null
+++ b/app/Grpc/HealthCheckRequest.php
@@ -0,0 +1,58 @@
+gateway.HealthCheckRequest
+ */
+class HealthCheckRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field string service = 1;
+ */
+ protected $service = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $service
+ * }
+ */
+ 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;
+ }
+
+}
+
diff --git a/app/Grpc/HealthCheckResponse.php b/app/Grpc/HealthCheckResponse.php
new file mode 100644
index 0000000..74d33a8
--- /dev/null
+++ b/app/Grpc/HealthCheckResponse.php
@@ -0,0 +1,58 @@
+gateway.HealthCheckResponse
+ */
+class HealthCheckResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field .gateway.HealthCheckResponse.ServingStatus status = 1;
+ */
+ protected $status = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $status
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\Gateway\Apigw::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field .gateway.HealthCheckResponse.ServingStatus status = 1;
+ * @return int
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ /**
+ * Generated from protobuf field .gateway.HealthCheckResponse.ServingStatus status = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setStatus($var)
+ {
+ GPBUtil::checkEnum($var, \App\Grpc\HealthCheckResponse_ServingStatus::class);
+ $this->status = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/HealthCheckResponse/ServingStatus.php b/app/Grpc/HealthCheckResponse/ServingStatus.php
new file mode 100644
index 0000000..1e1c32d
--- /dev/null
+++ b/app/Grpc/HealthCheckResponse/ServingStatus.php
@@ -0,0 +1,56 @@
+gateway.HealthCheckResponse.ServingStatus
+ */
+class ServingStatus
+{
+ /**
+ * Generated from protobuf enum UNKNOWN = 0;
+ */
+ const UNKNOWN = 0;
+ /**
+ * Generated from protobuf enum SERVING = 1;
+ */
+ const SERVING = 1;
+ /**
+ * Generated from protobuf enum NOT_SERVING = 2;
+ */
+ const NOT_SERVING = 2;
+
+ private static $valueToName = [
+ self::UNKNOWN => 'UNKNOWN',
+ self::SERVING => 'SERVING',
+ self::NOT_SERVING => 'NOT_SERVING',
+ ];
+
+ public static function name($value)
+ {
+ if (!isset(self::$valueToName[$value])) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no name defined for value %s', __CLASS__, $value));
+ }
+ return self::$valueToName[$value];
+ }
+
+
+ public static function value($name)
+ {
+ $const = __CLASS__ . '::' . strtoupper($name);
+ if (!defined($const)) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no value defined for name %s', __CLASS__, $name));
+ }
+ return constant($const);
+ }
+}
+
+// Adding a class alias for backwards compatibility with the previous class name.
+class_alias(ServingStatus::class, \App\Grpc\HealthCheckResponse_ServingStatus::class);
+
diff --git a/app/Grpc/HealthCheckResponse_ServingStatus.php b/app/Grpc/HealthCheckResponse_ServingStatus.php
new file mode 100644
index 0000000..4e1274a
--- /dev/null
+++ b/app/Grpc/HealthCheckResponse_ServingStatus.php
@@ -0,0 +1,16 @@
+_simpleRequest('/gateway.HealthService/Check',
+ $argument,
+ ['\App\Grpc\HealthCheckResponse', 'decode'],
+ $metadata, $options);
+ }
+
+}
diff --git a/app/Grpc/Middleware/GrpcAuthMiddleware.php b/app/Grpc/Middleware/GrpcAuthMiddleware.php
new file mode 100644
index 0000000..9248b23
--- /dev/null
+++ b/app/Grpc/Middleware/GrpcAuthMiddleware.php
@@ -0,0 +1,115 @@
+getTokenIdFromJWT($tokenString);
+
+ // Find the token in database
+ $token = Token::find($tokenId);
+
+ if (!$token || $token->revoked) {
+ return null;
+ }
+
+ // Check if token is expired
+ if ($token->expires_at < now()) {
+ return null;
+ }
+
+ return $token->user;
+
+ } catch (\Exception $e) {
+ return null;
+ }
+ }
+
+ /**
+ * Authenticate gRPC request using Bearer token from metadata
+ */
+ public function authenticateGrpcRequest($metadata): ?User
+ {
+ Log::info('metadata', ['metadata' => $metadata]);
+ try {
+ // Get authorization header from gRPC metadata
+ $authHeader = $metadata['authorization'][0] ?? null;
+
+ if (!$authHeader) {
+ return null;
+ }
+
+ // Extract Bearer token
+ if (!str_starts_with($authHeader, 'Bearer ')) {
+ return null;
+ }
+
+ $bearerToken = substr($authHeader, 7);
+
+
+ $user = $this->getUserByAccessToken($bearerToken);
+ Log::info('gRPC Authentication: Bearer token provided', ['user_id' => $user->id]);
+
+ if (!$user) {
+ return null;
+ }
+
+ // Set an authenticated user in Laravel Auth
+ Auth::setUser($user);
+
+ return $user;
+
+ } catch (\Exception $e) {
+ \Log::error('gRPC Authentication error: ' . $e->getMessage());
+ return null;
+ }
+ }
+
+ public function validateToken(string $token): array
+ {
+ try {
+ $user = $this->getUserByAccessToken($token);
+ if (!$user) {
+ return [
+ 'message' => 'Token is invalid or expired',
+ 'data' => null,
+ ];
+ }
+
+ return [
+ 'message' => 'Token is valid',
+ 'data' => $user,
+ ];
+ } catch (\Exception $e) {
+ \Log::error('Token validation error: ' . $e->getMessage());
+ return [
+ 'message' => 'Token validation failed',
+ 'data' => null,
+ ];
+ }
+ }
+}
diff --git a/app/Grpc/Resources/UserResource.php b/app/Grpc/Resources/UserResource.php
new file mode 100644
index 0000000..b77cde8
--- /dev/null
+++ b/app/Grpc/Resources/UserResource.php
@@ -0,0 +1,34 @@
+
+ */
+ public function toArray(Request $request): array
+ {
+ if (method_exists($this->resource, 'getUser'))
+ $user = $this->resource->getUser();
+ else
+ $user = $this->resource;
+ if (method_exists($this->resource, 'getToken'))
+ $token = $this->resource->getToken();
+
+
+ return array_filter([
+ 'id' => $user?->getId(),
+ 'name' => $user?->getName(),
+ 'email' => $user?->getEmail(),
+ 'photo_url' => $this->when($user?->getPhotoPath(), $user?->getPhotoPath()),
+ 'role_name' => $user?->getRole(),
+ 'token' => $this->whenNotNull($token ?? null),
+ ]);
+ }
+}
diff --git a/app/Grpc/Servers/UserServer.php b/app/Grpc/Servers/UserServer.php
new file mode 100644
index 0000000..ece97e4
--- /dev/null
+++ b/app/Grpc/Servers/UserServer.php
@@ -0,0 +1,603 @@
+ new MethodDescriptor(
+ $this,
+ 'GetUser',
+ GetUserRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ '/user.UserService/CreateUser' => new MethodDescriptor(
+ $this,
+ 'CreateUser',
+ CreateUserRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ '/user.UserService/UpdateUser' => new MethodDescriptor(
+ $this,
+ 'UpdateUser',
+ UpdateUserRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ '/user.UserService/DeleteUser' => new MethodDescriptor(
+ $this,
+ 'DeleteUser',
+ DeleteUserRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ '/user.UserService/ListUsers' => new MethodDescriptor(
+ $this,
+ 'ListUsers',
+ ListUsersRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ '/user.UserService/AuthenticateUser' => new MethodDescriptor(
+ $this,
+ 'AuthenticateUser',
+ AuthenticateRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ '/user.UserService/GetUserProfile' => new MethodDescriptor(
+ $this,
+ 'GetUserProfile',
+ UserProfileRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ '/user.UserService/LogoutUser' => new MethodDescriptor(
+ $this,
+ 'LogoutUser',
+ LogoutRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ '/user.UserService/ValidateToken' => new MethodDescriptor(
+ $this,
+ 'ValidateToken',
+ ValidateTokenRequest::class,
+ MethodDescriptor::UNARY_CALL,
+ ),
+ ];
+ }
+
+ public function AuthenticateUser(AuthenticateRequest $request, \Grpc\ServerContext $context): ?AuthenticateResponse
+ {
+ $response = new AuthenticateResponse();
+
+ try {
+ $credentials = [
+ 'email' => $request->getEmail(),
+ 'password' => $request->getPassword(),
+ ];
+
+ $result = $this->authService->authenticate($credentials);
+ $dto = new AuthenticateGrpcDTO($result['success'], $result['message']);
+
+ if ($result['success']) {
+ $userData = $result['data'];
+ $dto->setUser($userData['user'])
+ ->setToken($userData['token'])
+ ->setTokenType($userData['token_type'])
+ ->setExpiresAt($userData['expires_at']);
+
+ // Convert DTO to Proto response
+ $protoUser = $this->convertUserToProto($userData['user']);
+ $response->setUser($protoUser);
+ $response->setToken($userData['token']);
+ $response->setTokenType($userData['token_type']);
+ $response->setExpiresAt($userData['expires_at']->toISOString());
+ }
+
+ $response->setSuccess($dto->isSuccess());
+ $response->setMessage($dto->getMessage());
+
+ } catch (\Exception $e) {
+ Log::error('AuthenticateUser gRPC error: ' . $e->getMessage());
+ $dto = AuthenticateGrpcDTO::failure('Authentication error: ' . $e->getMessage());
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ public function ValidateToken(ValidateTokenRequest $request, \Grpc\ServerContext $context): ?ValidateTokenResponse
+ {
+ $response = new ValidateTokenResponse();
+
+ try {
+ $token = $request->getToken();
+ $user = $this->authMiddleware->validateToken($token);
+
+ if ($user) {
+ $dto = ValidateTokenGrpcDTO::success('Token is valid');
+ $dto->setUser($user)->setValid(true);
+
+ $protoUser = $this->convertUserToProto($user);
+ $response->setUser($protoUser);
+ $response->setValid(true);
+ $response->setSuccess(true);
+ $response->setMessage($dto->getMessage());
+ } else {
+ $dto = ValidateTokenGrpcDTO::failure('Invalid or expired token');
+ $response->setValid(false);
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+ } catch (\Exception $e) {
+ Log::error('ValidateToken gRPC error: ' . $e->getMessage());
+ $dto = ValidateTokenGrpcDTO::failure('Error validating token: ' . $e->getMessage());
+ $response->setValid(false);
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ public function GetUserProfile(UserProfileRequest $request, \Grpc\ServerContext $context): ?UserProfileResponse
+ {
+ $response = new UserProfileResponse();
+
+ try {
+ // Authenticate user
+ $user = $this->authMiddleware->authenticateGrpcRequest($context->clientMetadata());
+
+ if (!$user) {
+ $dto = UserProfileGrpcDTO::failure('Authentication required');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ $fields = [];
+ foreach ($request->getFields() as $field) {
+ $fields[] = $field;
+ }
+
+ $result = $this->authService->getProfile($user, $fields);
+
+ if ($result['success']) {
+ $dto = UserProfileGrpcDTO::success($result['message']);
+ $dto->setProfile($result['data']);
+
+ // Map to Proto response
+ $profileData = $result['data'];
+ $response->setId($profileData['id'] ?? 0);
+ $response->setName($profileData['name'] ?? '');
+ $response->setEmail($profileData['email'] ?? '');
+ $response->setRole($profileData['role'] ?? '');
+ $response->setPhotoPath($profileData['photo_url'] ?? '');
+
+ // Handle cart items if present
+ if (isset($profileData['cart'])) {
+ foreach ($profileData['cart'] as $item) {
+ $cartItem = new \App\Grpc\User\CartItem();
+ $cartItem->setId($item['id']);
+ $cartItem->setType($item['type']);
+ $cartItem->setQuantity($item['quantity']);
+ $cartItem->setPrice($item['price']);
+ $response->addCartItems($cartItem);
+ }
+ }
+
+ $response->setSuccess(true);
+ $response->setMessage($dto->getMessage());
+ } else {
+ $dto = UserProfileGrpcDTO::failure($result['message']);
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+ } catch (\Exception $e) {
+ Log::error('GetUserProfile gRPC error: ' . $e->getMessage());
+ $dto = UserProfileGrpcDTO::failure('Error retrieving user profile: ' . $e->getMessage());
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ public function LogoutUser(LogoutRequest $request, \Grpc\ServerContext $context): ?LogoutResponse
+ {
+ $response = new LogoutResponse();
+
+ try {
+ // Authenticate user
+ $user = $this->authMiddleware->authenticateGrpcRequest($context->clientMetadata());
+
+ if (!$user) {
+ $dto = SimpleResponseGrpcDTO::failure('Authentication required');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ $result = $this->authService->logout($user);
+ $dto = new SimpleResponseGrpcDTO($result['success'], $result['message']);
+
+ $response->setSuccess($dto->isSuccess());
+ $response->setMessage($dto->getMessage());
+
+ } catch (\Exception $e) {
+ Log::error('LogoutUser gRPC error: ' . $e->getMessage());
+ $dto = SimpleResponseGrpcDTO::failure('Error logging out user: ' . $e->getMessage());
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ public function ListUsers(ListUsersRequest $request, \Grpc\ServerContext $context): ?ListUsersResponse
+ {
+ $response = new ListUsersResponse();
+
+ try {
+ // Authenticate user
+ $user = $this->authMiddleware->authenticateGrpcRequest($context->clientMetadata());
+
+ if (!$user) {
+ $dto = UserListGrpcDTO::failure('Authentication required');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ // Check permission
+ if (!$this->authService->hasPermission($user, 'user.read')) {
+ $dto = UserListGrpcDTO::failure('Insufficient permissions');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ $result = $this->userService->index();
+ $users = collect($result['data']);
+
+ // Handle pagination and filtering
+ $page = $request->getPage() ?: 1;
+ $perPage = $request->getPerPage() ?: 15;
+ $search = $request->getSearch();
+
+ if ($search) {
+ $users = $users->filter(function ($user) use ($search) {
+ return stripos($user->name, $search) !== false ||
+ stripos($user->email, $search) !== false;
+ });
+ }
+
+ $total = $users->count();
+ $paginatedUsers = $users->forPage($page, $perPage);
+
+ // Create DTO
+ $dto = UserListGrpcDTO::success($result['message']);
+ $dto->setUsers($paginatedUsers->toArray())
+ ->setPagination($total, $page, $perPage);
+
+ // Map to Proto response
+ $protoUsers = [];
+ foreach ($paginatedUsers as $user) {
+ $protoUsers[] = $this->convertUserToProto($user);
+ }
+
+ $response->setUsers($protoUsers);
+ $response->setTotal($total);
+ $response->setCurrentPage($page);
+ $response->setLastPage(ceil($total / $perPage));
+ $response->setSuccess(true);
+ $response->setMessage($dto->getMessage());
+
+ } catch (\Exception $e) {
+ Log::error('ListUsers gRPC error: ' . $e->getMessage());
+ $dto = UserListGrpcDTO::failure('Error retrieving users: ' . $e->getMessage());
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ public function GetUser(GetUserRequest $request, \Grpc\ServerContext $context): ?UserResponse
+ {
+ $response = new UserResponse();
+
+ try {
+ // Authenticate user
+ $authenticatedUser = $this->authMiddleware->authenticateGrpcRequest($context->clientMetadata());
+
+ if (!$authenticatedUser) {
+ $dto = UserGrpcDTO::failure('Authentication required');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ // Check if user is requesting their own data or has permission
+ $requestedUserId = $request->getId();
+ if ($authenticatedUser->id !== $requestedUserId &&
+ !$this->authService->hasPermission($authenticatedUser, 'user.read')) {
+ $dto = UserGrpcDTO::failure('Insufficient permissions');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ $result = $this->userService->show($requestedUserId);
+
+ if ($result['data']) {
+ $dto = UserGrpcDTO::success($result['message']);
+ $dto->setUser($result['data']);
+
+ $protoUser = $this->convertUserToProto($result['data']);
+ $response->setUser($protoUser);
+ $response->setSuccess(true);
+ $response->setMessage($dto->getMessage());
+ } else {
+ $dto = UserGrpcDTO::failure($result['message']);
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+ } catch (\Exception $e) {
+ Log::error('GetUser gRPC error: ' . $e->getMessage());
+ $dto = UserGrpcDTO::failure('Error retrieving user: ' . $e->getMessage());
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ public function CreateUser(CreateUserRequest $request, \Grpc\ServerContext $context): ?UserResponse
+ {
+ $response = new UserResponse();
+
+ try {
+ // Authenticate user (admin only for creating users)
+ $authenticatedUser = $this->authMiddleware->authenticateGrpcRequest($context->clientMetadata());
+
+ if (!$authenticatedUser || !$this->authService->hasPermission($authenticatedUser, 'user.create')) {
+ $dto = UserGrpcDTO::failure('Authentication required or insufficient permissions');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ // Validate request data
+ if (!$request->getName() || !$request->getEmail() || !$request->getPassword()) {
+ $dto = UserGrpcDTO::failure('Name, email, and password are required');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ $userData = [
+ 'name' => $request->getName(),
+ 'email' => $request->getEmail(),
+ 'password' => bcrypt($request->getPassword()),
+ 'role' => $request->getRole() ?: 'user'
+ ];
+
+ // Use UserService to create user
+ $user = $this->userService->create($userData);
+
+ if ($user) {
+ $dto = UserGrpcDTO::success('User created successfully');
+ $dto->setUser($user);
+
+ $protoUser = $this->convertUserToProto($user);
+ $response->setUser($protoUser);
+ $response->setSuccess(true);
+ $response->setMessage($dto->getMessage());
+ } else {
+ $dto = UserGrpcDTO::failure('Failed to create user');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+ } catch (\Exception $e) {
+ Log::error('CreateUser gRPC error: ' . $e->getMessage());
+ $dto = UserGrpcDTO::failure('Error creating user: ' . $e->getMessage());
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ public function UpdateUser(UpdateUserRequest $request, \Grpc\ServerContext $context): ?UserResponse
+ {
+ $response = new UserResponse();
+
+ try {
+ // Authenticate user
+ $authenticatedUser = $this->authMiddleware->authenticateGrpcRequest($context->clientMetadata());
+
+ if (!$authenticatedUser) {
+ $dto = UserGrpcDTO::failure('Authentication required');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ $targetUserId = $request->getId();
+
+ // Check if user is updating their own data or has permission
+ if ($authenticatedUser->id !== $targetUserId &&
+ !$this->authService->hasPermission($authenticatedUser, 'user.update')) {
+ $dto = UserGrpcDTO::failure('Insufficient permissions');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ // Prepare update data
+ $updateData = [];
+
+ if ($request->getName()) {
+ $updateData['name'] = $request->getName();
+ }
+
+ if ($request->getEmail()) {
+ $updateData['email'] = $request->getEmail();
+ }
+
+ if ($request->getPassword()) {
+ // Password will be bcrypted in the service
+ $updateData['password'] = $request->getPassword();
+ }
+
+ if (empty($updateData)) {
+ $dto = UserGrpcDTO::failure('No fields to update');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ // Use UserService to update user
+ $user = $this->userService->update($targetUserId, $updateData);
+
+ if ($user) {
+ $dto = UserGrpcDTO::success('User updated successfully');
+ $dto->setUser($user);
+
+ $protoUser = $this->convertUserToProto($user);
+ $response->setUser($protoUser);
+ $response->setSuccess(true);
+ $response->setMessage($dto->getMessage());
+ } else {
+ $dto = UserGrpcDTO::failure('Failed to update user');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ } catch (\Exception $e) {
+ Log::error('UpdateUser gRPC error: ' . $e->getMessage());
+ $dto = UserGrpcDTO::failure('Error updating user: ' . $e->getMessage());
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ public function DeleteUser(DeleteUserRequest $request, \Grpc\ServerContext $context): ?DeleteUserResponse
+ {
+ $response = new DeleteUserResponse();
+
+ try {
+ // Authenticate user (admin only)
+ $authenticatedUser = $this->authMiddleware->authenticateGrpcRequest($context->clientMetadata());
+
+ if (!$authenticatedUser || !$this->authService->hasPermission($authenticatedUser, 'user.delete')) {
+ $dto = SimpleResponseGrpcDTO::failure('Authentication required or insufficient permissions');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ $targetUserId = $request->getId();
+
+ // Prevent self-deletion
+ if ($authenticatedUser->id === $targetUserId) {
+ $dto = SimpleResponseGrpcDTO::failure('Cannot delete your own account');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ // Check if user exists
+ $userResult = $this->userService->show($targetUserId);
+ if (!$userResult['data']) {
+ $dto = SimpleResponseGrpcDTO::failure('User not found');
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ return $response;
+ }
+
+ $user = $userResult['data'];
+
+ // Delete the user
+ $user->delete();
+
+ $dto = SimpleResponseGrpcDTO::success('User deleted successfully');
+ $response->setSuccess(true);
+ $response->setMessage($dto->getMessage());
+
+ } catch (\Exception $e) {
+ Log::error('DeleteUser gRPC error: ' . $e->getMessage());
+ $dto = SimpleResponseGrpcDTO::failure('Error deleting user: ' . $e->getMessage());
+ $response->setSuccess(false);
+ $response->setMessage($dto->getMessage());
+ }
+
+ return $response;
+ }
+
+ private function convertUserToProto(User $user): ProtoUser
+ {
+ $protoUser = new ProtoUser();
+ $protoUser->setId($user->id);
+ $protoUser->setName($user->name);
+ $protoUser->setEmail($user->email);
+ $protoUser->setRole($user->role ?? '');
+ $protoUser->setCreatedAt($user->created_at?->toISOString() ?? '');
+ $protoUser->setUpdatedAt($user->updated_at?->toISOString() ?? '');
+
+ // Handle photo path
+ if ($user->photo_path) {
+ $protoUser->setPhotoPath($user->photo_path);
+ }
+
+ // Handle access token if present
+ if (isset($user->accessToken)) {
+ $protoUser->setAccessToken($user->accessToken);
+ }
+
+ return $protoUser;
+ }
+}
diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php
new file mode 100644
index 0000000..c36c414
--- /dev/null
+++ b/app/Grpc/Services/GatewayService.php
@@ -0,0 +1,251 @@
+ 'user:50051', // Updated to match your service names
+ 'product' => 'product:50051',
+ 'order' => 'order:50051',
+ ];
+
+ private array $grpcClients = [];
+
+ /**
+ * Handle flexible protobuf requests
+ */
+ public function HandleRequest(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());
+ }
+
+ // 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 returned null response'
+ );
+ 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;
+ }
+
+ /**
+ * Handle raw bytes requests (maximum flexibility)
+ */
+ public function HandleBytesRequest(BytesRequest $in): BytesResponse
+ {
+ $response = new BytesResponse();
+ $response->setRequestId($in->getRequestId());
+ $response->setTimestamp($this->getCurrentTimestamp());
+
+ try {
+ $grpcClient = $this->getGrpcClient($in->getService());
+ if (!$grpcClient) {
+ return $this->createBytesErrorResponse($response, 404, 'SERVICE_NOT_FOUND', 'Service not found');
+ }
+
+ // Forward the BytesRequest to downstream service
+ list($downstreamResponse, $status) = $grpcClient->HandleBytesRequest($in)->wait();
+
+ if ($status->code === STATUS_OK) {
+ // Forward the response as-is
+ $response->setSuccess($downstreamResponse->getSuccess());
+ $response->setStatusCode($downstreamResponse->getStatusCode());
+ $response->setData($downstreamResponse->getData());
+ $response->setContentType($downstreamResponse->getContentType());
+
+ // Forward headers
+ foreach ($downstreamResponse->getHeaders() as $key => $value) {
+ $response->getHeaders()[$key] = $value;
+ }
+
+ // Forward error if exists
+ if ($downstreamResponse->hasError()) {
+ $response->setError($downstreamResponse->getError());
+ }
+
+ } else {
+ return $this->createBytesErrorResponse($response, 500, 'GRPC_ERROR', $status->details);
+ }
+
+ } catch (Exception $e) {
+ Log::error('Gateway gRPC bytes request failed', [
+ 'service' => $in->getService(),
+ 'method' => $in->getMethod(),
+ 'error' => $e->getMessage()
+ ]);
+
+ return $this->createBytesErrorResponse($response, 500, 'INTERNAL_ERROR', $e->getMessage());
+ }
+
+ return $response;
+ }
+
+ /**
+ * Handle streaming requests
+ */
+// public function HandleStreamRequest(Iterator $in): Iterator
+// {
+// foreach ($in as $request) {
+// yield $this->HandleRequest($request);
+// }
+// }
+
+ /**
+ * Get gRPC client for service
+ */
+ private function getGrpcClient(string $service): ?GatewayServiceClient
+ {
+ if (!isset($this->grpcServices[$service])) {
+ return null;
+ }
+
+ // Reuse existing client or create new one
+ if (!isset($this->grpcClients[$service])) {
+ $serviceAddress = $this->grpcServices[$service];
+
+ try {
+ // Simplified options to avoid compatibility issues
+ $options = [
+ 'credentials' => ChannelCredentials::createInsecure(),
+ 'timeout' => 30000000, // 30 seconds in microseconds
+ ];
+
+ $this->grpcClients[$service] = new GatewayServiceClient(
+ $serviceAddress,
+ $options
+ );
+
+ Log::info("Created gRPC client for service: {$service} at {$serviceAddress}");
+
+ } catch (Exception $e) {
+ Log::error("Failed to create gRPC client for service: {$service}", [
+ 'address' => $serviceAddress,
+ 'error' => $e->getMessage()
+ ]);
+ return null;
+ }
+ }
+
+ return $this->grpcClients[$service];
+ }
+
+ /**
+ * Close all gRPC connections (cleanup)
+ */
+ public function __destruct()
+ {
+ foreach ($this->grpcClients as $client) {
+ if ($client) {
+ $client->close();
+ }
+ }
+ }
+
+ /**
+ * 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;
+ }
+}
diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php
new file mode 100644
index 0000000..f381504
--- /dev/null
+++ b/app/Grpc/Services/GrpcService.php
@@ -0,0 +1,43 @@
+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
+ $gatewayService = new GatewayService();
+ $grpcResponse = $gatewayService->HandleRequest($grpcRequest);
+
+ // 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/Grpc/User/AuthenticateRequest.php b/app/Grpc/User/AuthenticateRequest.php
new file mode 100644
index 0000000..4b88e4d
--- /dev/null
+++ b/app/Grpc/User/AuthenticateRequest.php
@@ -0,0 +1,85 @@
+user.AuthenticateRequest
+ */
+class AuthenticateRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field string email = 1;
+ */
+ protected $email = '';
+ /**
+ * Generated from protobuf field string password = 2;
+ */
+ protected $password = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $email
+ * @type string $password
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field string email = 1;
+ * @return string
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * Generated from protobuf field string email = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setEmail($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->email = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string password = 2;
+ * @return string
+ */
+ public function getPassword()
+ {
+ return $this->password;
+ }
+
+ /**
+ * Generated from protobuf field string password = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setPassword($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->password = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/AuthenticateResponse.php b/app/Grpc/User/AuthenticateResponse.php
new file mode 100644
index 0000000..ce25214
--- /dev/null
+++ b/app/Grpc/User/AuthenticateResponse.php
@@ -0,0 +1,193 @@
+user.AuthenticateResponse
+ */
+class AuthenticateResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ */
+ protected $user = null;
+ /**
+ * Generated from protobuf field string token = 2;
+ */
+ protected $token = '';
+ /**
+ * Generated from protobuf field string token_type = 3;
+ */
+ protected $token_type = '';
+ /**
+ * Generated from protobuf field string expires_at = 4;
+ */
+ protected $expires_at = '';
+ /**
+ * Generated from protobuf field bool success = 5;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 6;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \App\Grpc\User\User $user
+ * @type string $token
+ * @type string $token_type
+ * @type string $expires_at
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ * @return \App\Grpc\User\User
+ */
+ public function getUser()
+ {
+ return $this->user;
+ }
+
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ * @param \App\Grpc\User\User $var
+ * @return $this
+ */
+ public function setUser($var)
+ {
+ GPBUtil::checkMessage($var, \App\Grpc\User\User::class);
+ $this->user = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string token = 2;
+ * @return string
+ */
+ public function getToken()
+ {
+ return $this->token;
+ }
+
+ /**
+ * Generated from protobuf field string token = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setToken($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->token = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string token_type = 3;
+ * @return string
+ */
+ public function getTokenType()
+ {
+ return $this->token_type;
+ }
+
+ /**
+ * Generated from protobuf field string token_type = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setTokenType($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->token_type = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string expires_at = 4;
+ * @return string
+ */
+ public function getExpiresAt()
+ {
+ return $this->expires_at;
+ }
+
+ /**
+ * Generated from protobuf field string expires_at = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setExpiresAt($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->expires_at = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 5;
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 5;
+ * @param bool $var
+ * @return $this
+ */
+ public function setSuccess($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->success = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string message = 6;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 6;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/CartItem.php b/app/Grpc/User/CartItem.php
new file mode 100644
index 0000000..b954222
--- /dev/null
+++ b/app/Grpc/User/CartItem.php
@@ -0,0 +1,139 @@
+user.CartItem
+ */
+class CartItem extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field int64 id = 1;
+ */
+ protected $id = 0;
+ /**
+ * Generated from protobuf field string type = 2;
+ */
+ protected $type = '';
+ /**
+ * Generated from protobuf field int32 quantity = 3;
+ */
+ protected $quantity = 0;
+ /**
+ * Generated from protobuf field double price = 4;
+ */
+ protected $price = 0.0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $id
+ * @type string $type
+ * @type int $quantity
+ * @type float $price
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @return int|string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setId($var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->id = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string type = 2;
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Generated from protobuf field string type = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setType($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->type = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field int32 quantity = 3;
+ * @return int
+ */
+ public function getQuantity()
+ {
+ return $this->quantity;
+ }
+
+ /**
+ * Generated from protobuf field int32 quantity = 3;
+ * @param int $var
+ * @return $this
+ */
+ public function setQuantity($var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->quantity = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field double price = 4;
+ * @return float
+ */
+ public function getPrice()
+ {
+ return $this->price;
+ }
+
+ /**
+ * Generated from protobuf field double price = 4;
+ * @param float $var
+ * @return $this
+ */
+ public function setPrice($var)
+ {
+ GPBUtil::checkDouble($var);
+ $this->price = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/ChangePasswordRequest.php b/app/Grpc/User/ChangePasswordRequest.php
new file mode 100644
index 0000000..9768234
--- /dev/null
+++ b/app/Grpc/User/ChangePasswordRequest.php
@@ -0,0 +1,85 @@
+user.ChangePasswordRequest
+ */
+class ChangePasswordRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field string current_password = 1;
+ */
+ protected $current_password = '';
+ /**
+ * Generated from protobuf field string new_password = 2;
+ */
+ protected $new_password = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $current_password
+ * @type string $new_password
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field string current_password = 1;
+ * @return string
+ */
+ public function getCurrentPassword()
+ {
+ return $this->current_password;
+ }
+
+ /**
+ * Generated from protobuf field string current_password = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setCurrentPassword($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->current_password = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string new_password = 2;
+ * @return string
+ */
+ public function getNewPassword()
+ {
+ return $this->new_password;
+ }
+
+ /**
+ * Generated from protobuf field string new_password = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setNewPassword($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->new_password = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/ChangePasswordResponse.php b/app/Grpc/User/ChangePasswordResponse.php
new file mode 100644
index 0000000..ce1ec24
--- /dev/null
+++ b/app/Grpc/User/ChangePasswordResponse.php
@@ -0,0 +1,85 @@
+user.ChangePasswordResponse
+ */
+class ChangePasswordResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field bool success = 1;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 2;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::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 string message = 2;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/CreateUserRequest.php b/app/Grpc/User/CreateUserRequest.php
new file mode 100644
index 0000000..d229886
--- /dev/null
+++ b/app/Grpc/User/CreateUserRequest.php
@@ -0,0 +1,139 @@
+user.CreateUserRequest
+ */
+class CreateUserRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field string name = 1;
+ */
+ protected $name = '';
+ /**
+ * Generated from protobuf field string email = 2;
+ */
+ protected $email = '';
+ /**
+ * Generated from protobuf field string password = 3;
+ */
+ protected $password = '';
+ /**
+ * Generated from protobuf field string role = 4;
+ */
+ protected $role = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $name
+ * @type string $email
+ * @type string $password
+ * @type string $role
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field string name = 1;
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Generated from protobuf field string name = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setName($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->name = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string email = 2;
+ * @return string
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * Generated from protobuf field string email = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setEmail($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->email = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string password = 3;
+ * @return string
+ */
+ public function getPassword()
+ {
+ return $this->password;
+ }
+
+ /**
+ * Generated from protobuf field string password = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setPassword($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->password = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string role = 4;
+ * @return string
+ */
+ public function getRole()
+ {
+ return $this->role;
+ }
+
+ /**
+ * Generated from protobuf field string role = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setRole($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->role = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/DeleteUserRequest.php b/app/Grpc/User/DeleteUserRequest.php
new file mode 100644
index 0000000..c48203b
--- /dev/null
+++ b/app/Grpc/User/DeleteUserRequest.php
@@ -0,0 +1,58 @@
+user.DeleteUserRequest
+ */
+class DeleteUserRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field int64 id = 1;
+ */
+ protected $id = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $id
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @return int|string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setId($var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->id = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/DeleteUserResponse.php b/app/Grpc/User/DeleteUserResponse.php
new file mode 100644
index 0000000..600e394
--- /dev/null
+++ b/app/Grpc/User/DeleteUserResponse.php
@@ -0,0 +1,85 @@
+user.DeleteUserResponse
+ */
+class DeleteUserResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field bool success = 1;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 2;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::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 string message = 2;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/GetUserRequest.php b/app/Grpc/User/GetUserRequest.php
new file mode 100644
index 0000000..eae8edb
--- /dev/null
+++ b/app/Grpc/User/GetUserRequest.php
@@ -0,0 +1,60 @@
+user.GetUserRequest
+ */
+class GetUserRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field int64 id = 1;
+ */
+ protected $id = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $id
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @return int|string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setId($var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->id = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/ListUsersRequest.php b/app/Grpc/User/ListUsersRequest.php
new file mode 100644
index 0000000..97d3d50
--- /dev/null
+++ b/app/Grpc/User/ListUsersRequest.php
@@ -0,0 +1,112 @@
+user.ListUsersRequest
+ */
+class ListUsersRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field int32 page = 1;
+ */
+ protected $page = 0;
+ /**
+ * Generated from protobuf field int32 per_page = 2;
+ */
+ protected $per_page = 0;
+ /**
+ * Generated from protobuf field string search = 3;
+ */
+ protected $search = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $page
+ * @type int $per_page
+ * @type string $search
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field int32 page = 1;
+ * @return int
+ */
+ public function getPage()
+ {
+ return $this->page;
+ }
+
+ /**
+ * Generated from protobuf field int32 page = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setPage($var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->page = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field int32 per_page = 2;
+ * @return int
+ */
+ public function getPerPage()
+ {
+ return $this->per_page;
+ }
+
+ /**
+ * Generated from protobuf field int32 per_page = 2;
+ * @param int $var
+ * @return $this
+ */
+ public function setPerPage($var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->per_page = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string search = 3;
+ * @return string
+ */
+ public function getSearch()
+ {
+ return $this->search;
+ }
+
+ /**
+ * Generated from protobuf field string search = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSearch($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->search = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/ListUsersResponse.php b/app/Grpc/User/ListUsersResponse.php
new file mode 100644
index 0000000..ac84a51
--- /dev/null
+++ b/app/Grpc/User/ListUsersResponse.php
@@ -0,0 +1,193 @@
+user.ListUsersResponse
+ */
+class ListUsersResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field repeated .user.User users = 1;
+ */
+ private $users;
+ /**
+ * Generated from protobuf field int32 total = 2;
+ */
+ protected $total = 0;
+ /**
+ * Generated from protobuf field int32 current_page = 3;
+ */
+ protected $current_page = 0;
+ /**
+ * Generated from protobuf field int32 last_page = 4;
+ */
+ protected $last_page = 0;
+ /**
+ * Generated from protobuf field bool success = 5;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 6;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \App\Grpc\User\User[]|\Google\Protobuf\Internal\RepeatedField $users
+ * @type int $total
+ * @type int $current_page
+ * @type int $last_page
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field repeated .user.User users = 1;
+ * @return \Google\Protobuf\Internal\RepeatedField
+ */
+ public function getUsers()
+ {
+ return $this->users;
+ }
+
+ /**
+ * Generated from protobuf field repeated .user.User users = 1;
+ * @param \App\Grpc\User\User[]|\Google\Protobuf\Internal\RepeatedField $var
+ * @return $this
+ */
+ public function setUsers($var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \App\Grpc\User\User::class);
+ $this->users = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field int32 total = 2;
+ * @return int
+ */
+ public function getTotal()
+ {
+ return $this->total;
+ }
+
+ /**
+ * Generated from protobuf field int32 total = 2;
+ * @param int $var
+ * @return $this
+ */
+ public function setTotal($var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->total = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field int32 current_page = 3;
+ * @return int
+ */
+ public function getCurrentPage()
+ {
+ return $this->current_page;
+ }
+
+ /**
+ * Generated from protobuf field int32 current_page = 3;
+ * @param int $var
+ * @return $this
+ */
+ public function setCurrentPage($var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->current_page = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field int32 last_page = 4;
+ * @return int
+ */
+ public function getLastPage()
+ {
+ return $this->last_page;
+ }
+
+ /**
+ * Generated from protobuf field int32 last_page = 4;
+ * @param int $var
+ * @return $this
+ */
+ public function setLastPage($var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->last_page = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 5;
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 5;
+ * @param bool $var
+ * @return $this
+ */
+ public function setSuccess($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->success = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string message = 6;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 6;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/LogoutRequest.php b/app/Grpc/User/LogoutRequest.php
new file mode 100644
index 0000000..9f612e2
--- /dev/null
+++ b/app/Grpc/User/LogoutRequest.php
@@ -0,0 +1,31 @@
+user.LogoutRequest
+ */
+class LogoutRequest extends \Google\Protobuf\Internal\Message
+{
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+}
+
diff --git a/app/Grpc/User/LogoutResponse.php b/app/Grpc/User/LogoutResponse.php
new file mode 100644
index 0000000..83b04b7
--- /dev/null
+++ b/app/Grpc/User/LogoutResponse.php
@@ -0,0 +1,85 @@
+user.LogoutResponse
+ */
+class LogoutResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field bool success = 1;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 2;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::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 string message = 2;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/RefreshTokenRequest.php b/app/Grpc/User/RefreshTokenRequest.php
new file mode 100644
index 0000000..1a470f9
--- /dev/null
+++ b/app/Grpc/User/RefreshTokenRequest.php
@@ -0,0 +1,31 @@
+user.RefreshTokenRequest
+ */
+class RefreshTokenRequest extends \Google\Protobuf\Internal\Message
+{
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+}
+
diff --git a/app/Grpc/User/RefreshTokenResponse.php b/app/Grpc/User/RefreshTokenResponse.php
new file mode 100644
index 0000000..8999b2b
--- /dev/null
+++ b/app/Grpc/User/RefreshTokenResponse.php
@@ -0,0 +1,166 @@
+user.RefreshTokenResponse
+ */
+class RefreshTokenResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field string token = 1;
+ */
+ protected $token = '';
+ /**
+ * Generated from protobuf field string token_type = 2;
+ */
+ protected $token_type = '';
+ /**
+ * Generated from protobuf field string expires_at = 3;
+ */
+ protected $expires_at = '';
+ /**
+ * Generated from protobuf field bool success = 4;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 5;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $token
+ * @type string $token_type
+ * @type string $expires_at
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field string token = 1;
+ * @return string
+ */
+ public function getToken()
+ {
+ return $this->token;
+ }
+
+ /**
+ * Generated from protobuf field string token = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setToken($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->token = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string token_type = 2;
+ * @return string
+ */
+ public function getTokenType()
+ {
+ return $this->token_type;
+ }
+
+ /**
+ * Generated from protobuf field string token_type = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setTokenType($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->token_type = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string expires_at = 3;
+ * @return string
+ */
+ public function getExpiresAt()
+ {
+ return $this->expires_at;
+ }
+
+ /**
+ * Generated from protobuf field string expires_at = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setExpiresAt($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->expires_at = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 4;
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 4;
+ * @param bool $var
+ * @return $this
+ */
+ public function setSuccess($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->success = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string message = 5;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 5;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/UpdateUserRequest.php b/app/Grpc/User/UpdateUserRequest.php
new file mode 100644
index 0000000..8363942
--- /dev/null
+++ b/app/Grpc/User/UpdateUserRequest.php
@@ -0,0 +1,139 @@
+user.UpdateUserRequest
+ */
+class UpdateUserRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field int64 id = 1;
+ */
+ protected $id = 0;
+ /**
+ * Generated from protobuf field string name = 2;
+ */
+ protected $name = '';
+ /**
+ * Generated from protobuf field string email = 3;
+ */
+ protected $email = '';
+ /**
+ * Generated from protobuf field string password = 4;
+ */
+ protected $password = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $id
+ * @type string $name
+ * @type string $email
+ * @type string $password
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @return int|string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setId($var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->id = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string name = 2;
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Generated from protobuf field string name = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setName($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->name = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string email = 3;
+ * @return string
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * Generated from protobuf field string email = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setEmail($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->email = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string password = 4;
+ * @return string
+ */
+ public function getPassword()
+ {
+ return $this->password;
+ }
+
+ /**
+ * Generated from protobuf field string password = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setPassword($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->password = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/User.php b/app/Grpc/User/User.php
new file mode 100644
index 0000000..e66ff64
--- /dev/null
+++ b/app/Grpc/User/User.php
@@ -0,0 +1,249 @@
+user.User
+ */
+class User extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field int64 id = 1;
+ */
+ protected $id = 0;
+ /**
+ * Generated from protobuf field string name = 2;
+ */
+ protected $name = '';
+ /**
+ * Generated from protobuf field string email = 3;
+ */
+ protected $email = '';
+ /**
+ * Generated from protobuf field string role = 4;
+ */
+ protected $role = '';
+ /**
+ * Generated from protobuf field string photo_path = 5;
+ */
+ protected $photo_path = '';
+ /**
+ * Generated from protobuf field string access_token = 6;
+ */
+ protected $access_token = '';
+ /**
+ * Generated from protobuf field string created_at = 7;
+ */
+ protected $created_at = '';
+ /**
+ * Generated from protobuf field string updated_at = 8;
+ */
+ protected $updated_at = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $id
+ * @type string $name
+ * @type string $email
+ * @type string $role
+ * @type string $photo_path
+ * @type string $access_token
+ * @type string $created_at
+ * @type string $updated_at
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @return int|string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setId($var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->id = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string name = 2;
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Generated from protobuf field string name = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setName($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->name = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string email = 3;
+ * @return string
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * Generated from protobuf field string email = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setEmail($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->email = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string role = 4;
+ * @return string
+ */
+ public function getRole()
+ {
+ return $this->role;
+ }
+
+ /**
+ * Generated from protobuf field string role = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setRole($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->role = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string photo_path = 5;
+ * @return string
+ */
+ public function getPhotoPath()
+ {
+ return $this->photo_path;
+ }
+
+ /**
+ * Generated from protobuf field string photo_path = 5;
+ * @param string $var
+ * @return $this
+ */
+ public function setPhotoPath($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->photo_path = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string access_token = 6;
+ * @return string
+ */
+ public function getAccessToken()
+ {
+ return $this->access_token;
+ }
+
+ /**
+ * Generated from protobuf field string access_token = 6;
+ * @param string $var
+ * @return $this
+ */
+ public function setAccessToken($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->access_token = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string created_at = 7;
+ * @return string
+ */
+ public function getCreatedAt()
+ {
+ return $this->created_at;
+ }
+
+ /**
+ * Generated from protobuf field string created_at = 7;
+ * @param string $var
+ * @return $this
+ */
+ public function setCreatedAt($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->created_at = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string updated_at = 8;
+ * @return string
+ */
+ public function getUpdatedAt()
+ {
+ return $this->updated_at;
+ }
+
+ /**
+ * Generated from protobuf field string updated_at = 8;
+ * @param string $var
+ * @return $this
+ */
+ public function setUpdatedAt($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->updated_at = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/UserProfileRequest.php b/app/Grpc/User/UserProfileRequest.php
new file mode 100644
index 0000000..8677915
--- /dev/null
+++ b/app/Grpc/User/UserProfileRequest.php
@@ -0,0 +1,58 @@
+user.UserProfileRequest
+ */
+class UserProfileRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field repeated string fields = 2;
+ */
+ private $fields;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string[]|\Google\Protobuf\Internal\RepeatedField $fields
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field repeated string fields = 2;
+ * @return \Google\Protobuf\Internal\RepeatedField
+ */
+ public function getFields()
+ {
+ return $this->fields;
+ }
+
+ /**
+ * Generated from protobuf field repeated string fields = 2;
+ * @param string[]|\Google\Protobuf\Internal\RepeatedField $var
+ * @return $this
+ */
+ public function setFields($var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->fields = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/UserProfileResponse.php b/app/Grpc/User/UserProfileResponse.php
new file mode 100644
index 0000000..d574745
--- /dev/null
+++ b/app/Grpc/User/UserProfileResponse.php
@@ -0,0 +1,247 @@
+user.UserProfileResponse
+ */
+class UserProfileResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field int64 id = 1;
+ */
+ protected $id = 0;
+ /**
+ * Generated from protobuf field string name = 2;
+ */
+ protected $name = '';
+ /**
+ * Generated from protobuf field string email = 3;
+ */
+ protected $email = '';
+ /**
+ * Generated from protobuf field string photo_path = 4;
+ */
+ protected $photo_path = '';
+ /**
+ * Generated from protobuf field string role = 5;
+ */
+ protected $role = '';
+ /**
+ * Generated from protobuf field repeated .user.CartItem cart_items = 6;
+ */
+ private $cart_items;
+ /**
+ * Generated from protobuf field bool success = 7;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 8;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $id
+ * @type string $name
+ * @type string $email
+ * @type string $photo_path
+ * @type string $role
+ * @type \App\Grpc\User\CartItem[]|\Google\Protobuf\Internal\RepeatedField $cart_items
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @return int|string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Generated from protobuf field int64 id = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setId($var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->id = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string name = 2;
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Generated from protobuf field string name = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setName($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->name = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string email = 3;
+ * @return string
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * Generated from protobuf field string email = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setEmail($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->email = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string photo_path = 4;
+ * @return string
+ */
+ public function getPhotoPath()
+ {
+ return $this->photo_path;
+ }
+
+ /**
+ * Generated from protobuf field string photo_path = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setPhotoPath($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->photo_path = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string role = 5;
+ * @return string
+ */
+ public function getRole()
+ {
+ return $this->role;
+ }
+
+ /**
+ * Generated from protobuf field string role = 5;
+ * @param string $var
+ * @return $this
+ */
+ public function setRole($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->role = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field repeated .user.CartItem cart_items = 6;
+ * @return \Google\Protobuf\Internal\RepeatedField
+ */
+ public function getCartItems()
+ {
+ return $this->cart_items;
+ }
+
+ /**
+ * Generated from protobuf field repeated .user.CartItem cart_items = 6;
+ * @param \App\Grpc\User\CartItem[]|\Google\Protobuf\Internal\RepeatedField $var
+ * @return $this
+ */
+ public function setCartItems($var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \App\Grpc\User\CartItem::class);
+ $this->cart_items = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 7;
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 7;
+ * @param bool $var
+ * @return $this
+ */
+ public function setSuccess($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->success = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string message = 8;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 8;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/UserResponse.php b/app/Grpc/User/UserResponse.php
new file mode 100644
index 0000000..b7ad7d2
--- /dev/null
+++ b/app/Grpc/User/UserResponse.php
@@ -0,0 +1,114 @@
+user.UserResponse
+ */
+class UserResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ */
+ protected $user = null;
+ /**
+ * Generated from protobuf field bool success = 2;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 3;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \App\Grpc\User\User $user
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ * @return \App\Grpc\User\User
+ */
+ public function getUser()
+ {
+ return $this->user;
+ }
+
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ * @param \App\Grpc\User\User $var
+ * @return $this
+ */
+ public function setUser($var)
+ {
+ GPBUtil::checkMessage($var, \App\Grpc\User\User::class);
+ $this->user = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 2;
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 2;
+ * @param bool $var
+ * @return $this
+ */
+ public function setSuccess($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->success = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string message = 3;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/UserServiceClient.php b/app/Grpc/User/UserServiceClient.php
new file mode 100644
index 0000000..cca9b6c
--- /dev/null
+++ b/app/Grpc/User/UserServiceClient.php
@@ -0,0 +1,175 @@
+_simpleRequest('/user.UserService/GetUser',
+ $argument,
+ ['\App\Grpc\User\UserResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\CreateUserRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\UserResponse
+ */
+ public function CreateUser(\App\Grpc\User\CreateUserRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/CreateUser',
+ $argument,
+ ['\App\Grpc\User\UserResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\UpdateUserRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\UserResponse
+ */
+ public function UpdateUser(\App\Grpc\User\UpdateUserRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/UpdateUser',
+ $argument,
+ ['\App\Grpc\User\UserResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\DeleteUserRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\DeleteUserResponse
+ */
+ public function DeleteUser(\App\Grpc\User\DeleteUserRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/DeleteUser',
+ $argument,
+ ['\App\Grpc\User\DeleteUserResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\ListUsersRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\ListUsersResponse
+ */
+ public function ListUsers(\App\Grpc\User\ListUsersRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/ListUsers',
+ $argument,
+ ['\App\Grpc\User\ListUsersResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\AuthenticateRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\AuthenticateResponse
+ */
+ public function AuthenticateUser(\App\Grpc\User\AuthenticateRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/AuthenticateUser',
+ $argument,
+ ['\App\Grpc\User\AuthenticateResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\UserProfileRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\UserProfileResponse
+ */
+ public function GetUserProfile(\App\Grpc\User\UserProfileRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/GetUserProfile',
+ $argument,
+ ['\App\Grpc\User\UserProfileResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\LogoutRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\LogoutResponse
+ */
+ public function LogoutUser(\App\Grpc\User\LogoutRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/LogoutUser',
+ $argument,
+ ['\App\Grpc\User\LogoutResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\ValidateTokenRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\ValidateTokenResponse
+ */
+ public function ValidateToken(\App\Grpc\User\ValidateTokenRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/ValidateToken',
+ $argument,
+ ['\App\Grpc\User\ValidateTokenResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\RefreshTokenRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\RefreshTokenResponse
+ */
+ public function RefreshToken(\App\Grpc\User\RefreshTokenRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/RefreshToken',
+ $argument,
+ ['\App\Grpc\User\RefreshTokenResponse', 'decode'],
+ $metadata, $options);
+ }
+
+ /**
+ * @param \App\Grpc\User\ChangePasswordRequest $argument input argument
+ * @param array $metadata metadata
+ * @param array $options call options
+ * @return \App\Grpc\User\ChangePasswordResponse
+ */
+ public function ChangePassword(\App\Grpc\User\ChangePasswordRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/user.UserService/ChangePassword',
+ $argument,
+ ['\App\Grpc\User\ChangePasswordResponse', 'decode'],
+ $metadata, $options);
+ }
+
+}
diff --git a/app/Grpc/User/ValidateTokenRequest.php b/app/Grpc/User/ValidateTokenRequest.php
new file mode 100644
index 0000000..cc819bf
--- /dev/null
+++ b/app/Grpc/User/ValidateTokenRequest.php
@@ -0,0 +1,58 @@
+user.ValidateTokenRequest
+ */
+class ValidateTokenRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field string token = 1;
+ */
+ protected $token = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $token
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field string token = 1;
+ * @return string
+ */
+ public function getToken()
+ {
+ return $this->token;
+ }
+
+ /**
+ * Generated from protobuf field string token = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setToken($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->token = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Grpc/User/ValidateTokenResponse.php b/app/Grpc/User/ValidateTokenResponse.php
new file mode 100644
index 0000000..cf2bddd
--- /dev/null
+++ b/app/Grpc/User/ValidateTokenResponse.php
@@ -0,0 +1,139 @@
+user.ValidateTokenResponse
+ */
+class ValidateTokenResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ */
+ protected $user = null;
+ /**
+ * Generated from protobuf field bool valid = 2;
+ */
+ protected $valid = false;
+ /**
+ * Generated from protobuf field bool success = 3;
+ */
+ protected $success = false;
+ /**
+ * Generated from protobuf field string message = 4;
+ */
+ protected $message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \App\Grpc\User\User $user
+ * @type bool $valid
+ * @type bool $success
+ * @type string $message
+ * }
+ */
+ public function __construct($data = NULL) {
+ \App\Grpc\GPBMetadata\User\UserService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ * @return \App\Grpc\User\User
+ */
+ public function getUser()
+ {
+ return $this->user;
+ }
+
+ /**
+ * Generated from protobuf field .user.User user = 1;
+ * @param \App\Grpc\User\User $var
+ * @return $this
+ */
+ public function setUser($var)
+ {
+ GPBUtil::checkMessage($var, \App\Grpc\User\User::class);
+ $this->user = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bool valid = 2;
+ * @return bool
+ */
+ public function getValid()
+ {
+ return $this->valid;
+ }
+
+ /**
+ * Generated from protobuf field bool valid = 2;
+ * @param bool $var
+ * @return $this
+ */
+ public function setValid($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->valid = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 3;
+ * @return bool
+ */
+ public function getSuccess()
+ {
+ return $this->success;
+ }
+
+ /**
+ * Generated from protobuf field bool success = 3;
+ * @param bool $var
+ * @return $this
+ */
+ public function setSuccess($var)
+ {
+ GPBUtil::checkBool($var);
+ $this->success = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field string message = 4;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Generated from protobuf field string message = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage($var)
+ {
+ GPBUtil::checkString($var, True);
+ $this->message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php
new file mode 100644
index 0000000..f4d779c
--- /dev/null
+++ b/app/Http/Controllers/ApiGatewayController.php
@@ -0,0 +1,58 @@
+bearerToken())->get(AIApi::getAIApi());
+ if ($res->failed())
+ throw new ConnectionException('Failed to connect to AI API', $res->status());
+ $recommendedProductData = $res->json('data', []);
+ $request->merge($this->productService->injectUserId());
+ $request->merge(['recommended_product_ids' => data_get($recommendedProductData, 'id')]);
+ $res = $this->grpcService->proxyRequest(
+ $request,
+ 'product',
+ 'listProducts',
+ );
+ if ($res->getStatusCode() == Response::HTTP_OK) {
+ $filteredProductData = ($res->getData(true));
+ $filteredProductData = array_map(function ($productData) {
+ $productData['percentage'] = $recommendedProductData['compatibility_score'] ?? 0;
+ return $productData;
+ }, data_get($filteredProductData, 'data', []));
+
+ return $res->setData($filteredProductData);
+ return $res;
+ } else
+ throw new ConnectionException('Failed to fetch products', $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..538f364
--- /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->proxyRequest($request,
+ 'user',
+ '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->proxyRequest($request,
+ 'user',
+ 'create'
+ );
+ } 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->proxyRequest(
+ $request,
+ 'user',
+ '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 64%
rename from app/Http/Controllers/OfferController.php
rename to app/Http/Controllers/OfferApiController.php
index 30298a0..efa460c 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->proxyRequest(
+ $request,
+ 'product',
+ '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->proxyRequest(
+ request(),
+ 'product',
+ '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..c6c645e 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->proxyRequest(
+ $request,
+ 'product',
+ '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->proxyRequest(
+ request(),
+ 'product',
+ '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..e165afa
--- /dev/null
+++ b/app/Http/Controllers/ShoppingCartApiController.php
@@ -0,0 +1,75 @@
+grpcService->proxyRequest(
+ $request,
+ 'user',
+ '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->proxyRequest(
+ $request,
+ 'user',
+ '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->proxyRequest(
+ request(),
+ 'user',
+ 'removeFromCart'
+ );
+// $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()
+ {
+ return $this->grpcService->proxyRequest(
+ request(),
+ 'user',
+ '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..83cbdd1 100644
--- a/app/Http/Controllers/UserApiController.php
+++ b/app/Http/Controllers/UserApiController.php
@@ -3,6 +3,8 @@
namespace App\Http\Controllers;
use App\External_Apis\Services\UserService;
+use App\Grpc\Services\GrpcService;
+use App\Services\UserGrpcService;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
@@ -11,36 +13,65 @@
class UserApiController extends BaseController
{
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->proxyRequest(request(),
+ 'user',
+ 'list',
+ );
}
/**
* 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->proxyRequest(request(),
+ 'user',
+ 'get',
+ );
- return $this->response($res['message'], $res['data'], $status, error: $status != Response::HTTP_OK);
}
/**
@@ -59,15 +90,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->proxyRequest($request,
+ 'user',
+ '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/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..a15586a
--- /dev/null
+++ b/app/Providers/GrpcServiceProvider.php
@@ -0,0 +1,16 @@
+app->singleton(UserGrpcService::class, function ($app) {
+ return new UserGrpcService();
+ });
+ }
+}
diff --git a/app/Services/Transformers/GrpcTransformer.php b/app/Services/Transformers/GrpcTransformer.php
new file mode 100644
index 0000000..3d45c78
--- /dev/null
+++ b/app/Services/Transformers/GrpcTransformer.php
@@ -0,0 +1,50 @@
+ $user->getId(),
+ 'name' => $user->getName(),
+ 'email' => $user->getEmail(),
+ 'role' => $user->getRole(),
+ 'photo_path' => $user->getPhotoPath(),
+ 'access_token' => $user->getAccessToken(),
+ 'created_at' => $user->getCreatedAt(),
+ 'updated_at' => $user->getUpdatedAt(),
+ ];
+ }
+
+ public static function cartItemToArray(?CartItem $cartItem): ?array
+ {
+ if (!$cartItem) {
+ return null;
+ }
+
+ return [
+ 'id' => $cartItem->getId(),
+ 'type' => $cartItem->getType(),
+ 'quantity' => $cartItem->getQuantity(),
+ 'price' => $cartItem->getPrice(),
+ ];
+ }
+
+ public static function cartItemsToArray($cartItems): array
+ {
+ $items = [];
+ foreach ($cartItems as $item) {
+ $items[] = self::cartItemToArray($item);
+ }
+ return $items;
+ }
+}
diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php
new file mode 100644
index 0000000..09f8ad3
--- /dev/null
+++ b/app/Services/UserGrpcService.php
@@ -0,0 +1,204 @@
+client = new UserServiceClient($hostname, [
+ 'credentials' => $credentials,
+ 'timeout' => 30000000,
+ ]);
+ }
+
+ public function getUser(int $userId): array
+ {
+ $request = new GetUserRequest();
+ $request->setId($userId);
+ $metadata = $this->getAuthorizationMetadata();
+ try {
+ $response = $this->client->GetUser($request, $metadata)->wait()[0];
+ return [
+ 'success' => $response->getSuccess(),
+ 'message' => $response->getMessage(),
+ 'data' => new UserResource($response)
+ ];
+ } catch (Exception $e) {
+ return [
+ 'success' => false,
+ 'message' => $e->getMessage(),
+ 'data' => null
+ ];
+ }
+ }
+
+ public function authenticateUser(string $email, string $password): array
+ {
+ $request = new AuthenticateRequest();
+ $request->setEmail($email);
+ $request->setPassword($password);
+
+ try {
+ list($response, $status) = $this->client->AuthenticateUser($request)->wait();
+
+ if ($status->code !== STATUS_OK) {
+ throw new Exception("gRPC call failed: " . $status->details);
+ }
+
+ return [
+ 'success' => $response->getSuccess(),
+ 'message' => $response->getMessage(),
+ 'data' => new UserResource($response)
+ ];
+ } catch (Exception $e) {
+ return [
+ 'success' => false,
+ 'message' => $e->getMessage(),
+ 'data' => null
+ ];
+ }
+ }
+
+ public function createUser(array $userData): array
+ {
+ $request = new CreateUserRequest();
+ $request->setName($userData['name']);
+ $request->setEmail($userData['email']);
+ $request->setPassword($userData['password']);
+
+ try {
+ list($response, $status) = $this->client->CreateUser($request)->wait();
+
+ if ($response->getSuccess() !== STATUS_OK)
+ return [
+ 'success' => false,
+ 'message' => $status->details
+ ];
+ return [
+ 'success' => $response->getSuccess(),
+ 'message' => $response->getMessage(),
+ 'data' => new UserResource($response)
+ ];
+ } catch (Exception $e) {
+ return [
+ 'success' => false,
+ 'message' => $e->getMessage()
+ ];
+ }
+ }
+
+ public function listUsers(): array
+ {
+ $request = new ListUsersRequest();
+ $metadata = [];
+ if ($bearerToken = request()->bearerToken())
+ $metadata['authorization'] = ['Bearer ' . $bearerToken];
+// try {
+ list($response, $status) = $this->client->ListUsers($request, $metadata)->wait();
+
+ if ($status->code !== STATUS_OK) {
+ throw new Exception("gRPC call failed: " . $status->details);
+ }
+
+ return [
+ 'success' => $response->getSuccess(),
+ 'message' => $response->getMessage(),
+ 'data' => UserResource::collection(collect($response->getUsers()))
+ ];
+
+// } catch (Exception $e) {
+// return [
+// 'success' => false,
+// 'message' => $e->getMessage(),
+// 'data' => []
+// ];
+// }
+ }
+
+ public function GetUserProfile(array $all): array
+ {
+ $request = new UserProfileRequest();
+ $metadata = $this->getAuthorizationMetadata();
+ try {
+ list($response, $status) = $this->client->GetUserProfile($request, $metadata)->wait()[0];
+ if (is_null($response))
+ return [
+ 'success' => false,
+ 'message' => $status->getMessage() ?: 'User not found',
+ 'data' => null
+ ];
+ return [
+ 'success' => $response->getSuccess(),
+ 'message' => $response->getMessage(),
+ 'data' => new UserResource($response)
+ ];
+ } catch (Exception $e) {
+ return [
+ 'success' => false,
+ 'message' => $e->getMessage(),
+ 'data' => null
+ ];
+ }
+ }
+
+ /**
+ * @param GetUserRequest $request
+ * @return array
+ */
+ private function getAuthorizationMetadata(): array
+ {
+ $metadata = [];
+ if ($bearerToken = request()->bearerToken())
+ $metadata['authorization'] = ['Bearer ' . $bearerToken];
+ return $metadata;
+ }
+
+ public function logout(?string $bearerToken): array
+ {
+ $metadata = [];
+ if ($bearerToken) {
+ $metadata['authorization'] = ['Bearer ' . $bearerToken];
+ }
+
+ $request = new LogoutRequest();
+ try {
+ $response = $this->client->LogoutUser($request, $metadata)->wait()[0];
+ if ($response->getSuccess() === false) {
+ return [
+ 'success' => false,
+ 'message' => $response->getMessage(),
+ 'data' => null
+ ];
+ }
+ return [
+ 'success' => $response->getSuccess(),
+ 'message' => $response->getMessage(),
+ 'data' => new UserResource($response)
+ ];
+ } catch (Exception $e) {
+ return [
+ 'success' => false,
+ 'message' => $e->getMessage(),
+ 'data' => null
+ ];
+ }
+ }
+}
diff --git a/app/Traits/InjectUser.php b/app/Traits/InjectUser.php
index b7875ab..d3682c8 100644
--- a/app/Traits/InjectUser.php
+++ b/app/Traits/InjectUser.php
@@ -4,9 +4,9 @@
trait InjectUser
{
- private function injectUserId(&$data=[]): void
+ public function injectUserId(): array
{
- $data['user_id'] = auth()->user()->id;
+ 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/grpc.php b/config/grpc.php
new file mode 100644
index 0000000..439eadd
--- /dev/null
+++ b/config/grpc.php
@@ -0,0 +1,16 @@
+ [
+ 'service' => \App\Services\UserGrpcService::class,
+ 'service_host' => env('GRPC_USER_SERVICE_HOST', 'localhost:50051'),
+ 'transformer' => \App\Services\Transformers\GrpcTransformer::class,
+ ],
+ 'cart' => [
+ 'service' => \App\Services\CartGrpcService::class,
+ 'transformer' => \App\Services\Transformers\GrpcTransformer::class,
+ ],
+ 'order' => [
+ 'service' => \App\Services\OrderGrpcService::class,
+ 'transformer' => \App\Services\Transformers\GrpcTransformer::class,
+ ],
+];
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..0cd0167 100644
--- a/config/services.php
+++ b/config/services.php
@@ -39,5 +39,6 @@
'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/'),
]
];
diff --git a/docker-compose.yml b/docker-compose.yml
index e228f14..63528bc 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.0_grpc
container_name: api-gw
restart: unless-stopped
working_dir: /var/www
@@ -31,11 +31,10 @@ 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-webserver/
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..902c222 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::post('/recommendations', [ApiGatewayController::class, 'getRecommendedProducts'])->middleware('authenticate');
diff --git a/supervisord.conf b/supervisord.conf
new file mode 100644
index 0000000..6b8f798
--- /dev/null
+++ b/supervisord.conf
@@ -0,0 +1,30 @@
+[supervisord]
+nodaemon=true
+user=root
+logfile=/var/log/supervisor/supervisord.log
+pidfile=/var/run/supervisord.pid
+
+[program:laravel-octane]
+process_name=%(program_name)s_%(process_num)02d
+command=php artisan octane:start --server=swoole --workers=2 --max-requests=50 --host=0.0.0.0 --port=8000
+directory=/var/www
+autostart=true
+autorestart=true
+user=www-data
+numprocs=1
+redirect_stderr=true
+stdout_logfile=/var/log/supervisor/laravel-octane.log
+stopwaitsecs=3600
+environment=LARAVEL_OCTANE=1
+startsecs=0
+startretries=3
+
+[unix_http_server]
+file=/var/run/supervisor.sock
+chmod=0700
+
+[supervisorctl]
+serverurl=unix:///var/run/supervisor.sock
+
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface