From dbdf2c6b7d3d8045212a6aed9d8cc98de6f06825 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Thu, 5 Jun 2025 18:55:53 +0300 Subject: [PATCH 01/49] Migrate User API to gRPC architecture Replaced `UserResource` with gRPC-based DTOs and responses for user-related operations. Introduced gRPC DTOs like `AuthenticateGrpcDTO`, `BaseGrpcDTO`, and others for better structure and maintainability. Enhanced user operations with protocol buffer-generated request and response classes for authentication, user management, and password handling. --- app/Grpc/BaseResponse.php | 29 + app/Grpc/DTOs/BaseGrpcDTO.php | 123 ++++ app/Grpc/DTOs/User/AuthenticateGrpcDTO.php | 49 ++ app/Grpc/DTOs/User/RefreshTokenGrpcDTO.php | 37 ++ app/Grpc/DTOs/User/SimpleResponseGrpcDTO.php | 14 + app/Grpc/DTOs/User/UserGrpcDTO.php | 48 ++ app/Grpc/DTOs/User/UserListGrpcDTO.php | 60 ++ app/Grpc/DTOs/User/UserProfileGrpcDTO.php | 62 ++ app/Grpc/DTOs/User/ValidateTokenGrpcDTO.php | 24 + app/Grpc/GPBMetadata/User/UserService.php | 24 + app/Grpc/Middleware/GrpcAuthMiddleware.php | 115 ++++ app/Grpc/Servers/UserServer.php | 603 +++++++++++++++++++ app/Grpc/User/AuthenticateRequest.php | 85 +++ app/Grpc/User/AuthenticateResponse.php | 193 ++++++ app/Grpc/User/CartItem.php | 139 +++++ app/Grpc/User/ChangePasswordRequest.php | 85 +++ app/Grpc/User/ChangePasswordResponse.php | 85 +++ app/Grpc/User/CreateUserRequest.php | 139 +++++ app/Grpc/User/DeleteUserRequest.php | 58 ++ app/Grpc/User/DeleteUserResponse.php | 85 +++ app/Grpc/User/GetUserRequest.php | 60 ++ app/Grpc/User/ListUsersRequest.php | 112 ++++ app/Grpc/User/ListUsersResponse.php | 193 ++++++ app/Grpc/User/LogoutRequest.php | 31 + app/Grpc/User/LogoutResponse.php | 85 +++ app/Grpc/User/RefreshTokenRequest.php | 31 + app/Grpc/User/RefreshTokenResponse.php | 166 +++++ app/Grpc/User/UpdateUserRequest.php | 139 +++++ app/Grpc/User/User.php | 249 ++++++++ app/Grpc/User/UserProfileRequest.php | 58 ++ app/Grpc/User/UserProfileResponse.php | 247 ++++++++ app/Grpc/User/UserResponse.php | 114 ++++ app/Grpc/User/UserServiceClient.php | 175 ++++++ app/Grpc/User/ValidateTokenRequest.php | 58 ++ app/Grpc/User/ValidateTokenResponse.php | 139 +++++ app/Http/Controllers/AuthController.php | 5 +- app/Http/Controllers/UserApiController.php | 31 +- app/Http/Resources/UserResource.php | 25 - app/Services/UserGrpcService.php | 160 +++++ composer.json | 5 +- protos/user_service.proto | 159 +++++ 41 files changed, 4265 insertions(+), 34 deletions(-) create mode 100644 app/Grpc/BaseResponse.php create mode 100644 app/Grpc/DTOs/BaseGrpcDTO.php create mode 100644 app/Grpc/DTOs/User/AuthenticateGrpcDTO.php create mode 100644 app/Grpc/DTOs/User/RefreshTokenGrpcDTO.php create mode 100644 app/Grpc/DTOs/User/SimpleResponseGrpcDTO.php create mode 100644 app/Grpc/DTOs/User/UserGrpcDTO.php create mode 100644 app/Grpc/DTOs/User/UserListGrpcDTO.php create mode 100644 app/Grpc/DTOs/User/UserProfileGrpcDTO.php create mode 100644 app/Grpc/DTOs/User/ValidateTokenGrpcDTO.php create mode 100644 app/Grpc/GPBMetadata/User/UserService.php create mode 100644 app/Grpc/Middleware/GrpcAuthMiddleware.php create mode 100644 app/Grpc/Servers/UserServer.php create mode 100644 app/Grpc/User/AuthenticateRequest.php create mode 100644 app/Grpc/User/AuthenticateResponse.php create mode 100644 app/Grpc/User/CartItem.php create mode 100644 app/Grpc/User/ChangePasswordRequest.php create mode 100644 app/Grpc/User/ChangePasswordResponse.php create mode 100644 app/Grpc/User/CreateUserRequest.php create mode 100644 app/Grpc/User/DeleteUserRequest.php create mode 100644 app/Grpc/User/DeleteUserResponse.php create mode 100644 app/Grpc/User/GetUserRequest.php create mode 100644 app/Grpc/User/ListUsersRequest.php create mode 100644 app/Grpc/User/ListUsersResponse.php create mode 100644 app/Grpc/User/LogoutRequest.php create mode 100644 app/Grpc/User/LogoutResponse.php create mode 100644 app/Grpc/User/RefreshTokenRequest.php create mode 100644 app/Grpc/User/RefreshTokenResponse.php create mode 100644 app/Grpc/User/UpdateUserRequest.php create mode 100644 app/Grpc/User/User.php create mode 100644 app/Grpc/User/UserProfileRequest.php create mode 100644 app/Grpc/User/UserProfileResponse.php create mode 100644 app/Grpc/User/UserResponse.php create mode 100644 app/Grpc/User/UserServiceClient.php create mode 100644 app/Grpc/User/ValidateTokenRequest.php create mode 100644 app/Grpc/User/ValidateTokenResponse.php delete mode 100644 app/Http/Resources/UserResource.php create mode 100644 app/Services/UserGrpcService.php create mode 100644 protos/user_service.proto 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/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/GPBMetadata/User/UserService.php b/app/Grpc/GPBMetadata/User/UserService.php new file mode 100644 index 0000000..3c8859b --- /dev/null +++ b/app/Grpc/GPBMetadata/User/UserService.php @@ -0,0 +1,24 @@ +internalAddGeneratedFile(hex2bin( + "0aaf130a12757365725f736572766963652e70726f746f120475736572221c0a0e4765745573657252657175657374120a0a02696418012001280322500a114372656174655573657252657175657374120c0a046e616d65180120012809120d0a05656d61696c18022001280912100a0870617373776f7264180320012809120c0a04726f6c65180420012809224e0a115570646174655573657252657175657374120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912100a0870617373776f7264180420012809221f0a1144656c6574655573657252657175657374120a0a02696418012001280322420a104c697374557365727352657175657374120c0a047061676518012001280512100a087065725f70616765180220012805120e0a0673656172636818032001280922360a1341757468656e74696361746552657175657374120d0a05656d61696c18012001280912100a0870617373776f726418022001280922240a125573657250726f66696c6552657175657374120e0a066669656c6473180220032809220f0a0d4c6f676f75745265717565737422250a1456616c6964617465546f6b656e52657175657374120d0a05746f6b656e18012001280922150a1352656672657368546f6b656e5265717565737422470a154368616e676550617373776f72645265717565737412180a1063757272656e745f70617373776f726418012001280912140a0c6e65775f70617373776f7264180220012809228f010a0455736572120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c180320012809120c0a04726f6c6518042001280912120a0a70686f746f5f7061746818052001280912140a0c6163636573735f746f6b656e18062001280912120a0a637265617465645f617418072001280912120a0a757064617465645f617418082001280922450a08436172744974656d120a0a026964180120012803120c0a047479706518022001280912100a087175616e74697479180320012805120d0a057072696365180420012801224a0a0c55736572526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120f0a0773756363657373180220012808120f0a076d65737361676518032001280922360a1244656c65746555736572526573706f6e7365120f0a0773756363657373180120012808120f0a076d6573736167651802200128092288010a114c6973745573657273526573706f6e736512190a05757365727318012003280b320a2e757365722e55736572120d0a05746f74616c18022001280512140a0c63757272656e745f7061676518032001280512110a096c6173745f70616765180420012805120f0a0773756363657373180520012808120f0a076d6573736167651806200128092289010a1441757468656e746963617465526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a05746f6b656e18022001280912120a0a746f6b656e5f7479706518032001280912120a0a657870697265735f6174180420012809120f0a0773756363657373180520012808120f0a076d65737361676518062001280922a5010a135573657250726f66696c65526573706f6e7365120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912110a0970686f746f5f75726c180420012809120c0a04726f6c6518052001280912220a0a636172745f6974656d7318062003280b320e2e757365722e436172744974656d120f0a0773756363657373180720012808120f0a076d65737361676518082001280922320a0e4c6f676f7574526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280922620a1556616c6964617465546f6b656e526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a0576616c6964180220012808120f0a0773756363657373180320012808120f0a076d657373616765180420012809226f0a1452656672657368546f6b656e526573706f6e7365120d0a05746f6b656e18012001280912120a0a746f6b656e5f7479706518022001280912120a0a657870697265735f6174180320012809120f0a0773756363657373180420012808120f0a076d657373616765180520012809223a0a164368616e676550617373776f7264526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280932e0050a0b557365725365727669636512330a074765745573657212142e757365722e47657455736572526571756573741a122e757365722e55736572526573706f6e736512390a0a4372656174655573657212172e757365722e43726561746555736572526571756573741a122e757365722e55736572526573706f6e736512390a0a5570646174655573657212172e757365722e55706461746555736572526571756573741a122e757365722e55736572526573706f6e7365123f0a0a44656c6574655573657212172e757365722e44656c65746555736572526571756573741a182e757365722e44656c65746555736572526573706f6e7365123c0a094c697374557365727312162e757365722e4c6973745573657273526571756573741a172e757365722e4c6973745573657273526573706f6e736512490a1041757468656e7469636174655573657212192e757365722e41757468656e746963617465526571756573741a1a2e757365722e41757468656e746963617465526573706f6e736512450a0e4765745573657250726f66696c6512182e757365722e5573657250726f66696c65526571756573741a192e757365722e5573657250726f66696c65526573706f6e736512370a0a4c6f676f75745573657212132e757365722e4c6f676f7574526571756573741a142e757365722e4c6f676f7574526573706f6e736512480a0d56616c6964617465546f6b656e121a2e757365722e56616c6964617465546f6b656e526571756573741a1b2e757365722e56616c6964617465546f6b656e526573706f6e736512450a0c52656672657368546f6b656e12192e757365722e52656672657368546f6b656e526571756573741a1a2e757365722e52656672657368546f6b656e526573706f6e7365124b0a0e4368616e676550617373776f7264121b2e757365722e4368616e676550617373776f7264526571756573741a1c2e757365722e4368616e676550617373776f7264526573706f6e7365422cca020d4170705c477270635c55736572e202194170705c477270635c4750424d657461646174615c55736572620670726f746f33" + ), true); + + static::$is_initialized = true; + } +} + 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/Servers/UserServer.php b/app/Grpc/Servers/UserServer.php new file mode 100644 index 0000000..a080cd3 --- /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->setPhotoUrl($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/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..02058b1 --- /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_url = 4; + */ + protected $photo_url = ''; + /** + * 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_url + * @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_url = 4; + * @return string + */ + public function getPhotoUrl() + { + return $this->photo_url; + } + + /** + * Generated from protobuf field string photo_url = 4; + * @param string $var + * @return $this + */ + public function setPhotoUrl($var) + { + GPBUtil::checkString($var, True); + $this->photo_url = $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/AuthController.php b/app/Http/Controllers/AuthController.php index d1a8aca..b814b59 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers; use App\External_Apis\Services\UserService; -use App\Http\Resources\UserResource; use Exception; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -17,7 +16,7 @@ public function __construct( { } - public function login(Request $request): UserResource|JsonResponse + public function login(Request $request): JsonResponse { try { [$res, $status] = $this->service->login($request->all()); @@ -30,7 +29,7 @@ public function login(Request $request): UserResource|JsonResponse /** */ - public function signup(Request $request): UserResource|JsonResponse + public function signup(Request $request): JsonResponse { try { $res = $this->service->create($request->all()); diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 9182944..70a50f8 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -3,20 +3,25 @@ namespace App\Http\Controllers; use App\External_Apis\Services\UserService; +use App\Services\UserGrpcService; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use Symfony\Component\HttpFoundation\Response; +use const Grpc\STATUS_OK; class UserApiController extends BaseController { public function __construct( - private readonly UserService $userService + private readonly UserService $userService, + private readonly UserGrpcService $userGrpcService + ) { } + /** * Display a listing of the resource. * @throws ConnectionException @@ -31,16 +36,32 @@ public function index(Request $request) * 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()); + $res = $this->userGrpcService->getUser($id); + $success = $res['success']; + $message = $res['message']; + $user = $res['user']; + + return $this->response($message, $user, $success, error: $success != STATUS_OK); - return $this->response($res['message'], $res['data'], $status, error: $status != Response::HTTP_OK); } /** 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/Services/UserGrpcService.php b/app/Services/UserGrpcService.php new file mode 100644 index 0000000..46e713f --- /dev/null +++ b/app/Services/UserGrpcService.php @@ -0,0 +1,160 @@ +client = new UserServiceClient($hostname, [ + 'credentials' => $credentials, + 'timeout' => 30000000, + ]); + } + + public function getUser(int $userId): array + { + $request = new GetUserRequest(); + $request->setId($userId); + + try { + $response = $this->client->GetUser($request); + + return [ + 'success' => $response->getSuccess(), + 'message' => $response->getMessage(), + 'user' => $response->getUser() ? [ + 'id' => $response->getUser()->getId(), + ] : null + ]; + } catch (\Exception $e) { + return [ + 'success' => false, + 'message' => $e->getMessage(), + 'user' => null + ]; + } + } + + public function authenticateUser(string $email, string $password) + { + $request = new AuthenticateRequest(); + $request->setEmail($email); + $request->setPassword($password); + + try { + list($response, $status) = $this->client->AuthenticateUser($request)->wait(); + + if ($status->code !== \Grpc\STATUS_OK) { + throw new \Exception("gRPC call failed: " . $status->details); + } + + return [ + 'success' => $response->getSuccess(), + 'message' => $response->getMessage(), + 'token' => $response->getToken(), + 'token_type' => $response->getTokenType(), + 'expires_at' => $response->getExpiresAt(), + 'user' => $response->getUser() ? [ + 'id' => $response->getUser()->getId(), + 'name' => $response->getUser()->getName(), + 'email' => $response->getUser()->getEmail(), + 'role' => $response->getUser()->getRole(), + ] : null + ]; + } catch (\Exception $e) { + return [ + 'success' => false, + 'message' => $e->getMessage() + ]; + } + } + + public function createUser(array $userData) + { + $request = new CreateUserRequest(); + $request->setName($userData['name']); + $request->setEmail($userData['email']); + $request->setPassword($userData['password']); + $request->setRole($userData['role'] ?? 'user'); + + try { + list($response, $status) = $this->client->CreateUser($request)->wait(); + + if ($status->code !== \Grpc\STATUS_OK) { + throw new \Exception("gRPC call failed: " . $status->details); + } + + return [ + 'success' => $response->getSuccess(), + 'message' => $response->getMessage(), + 'user' => $response->getUser() ? [ + 'id' => $response->getUser()->getId(), + 'name' => $response->getUser()->getName(), + 'email' => $response->getUser()->getEmail(), + 'role' => $response->getUser()->getRole(), + ] : null + ]; + } catch (\Exception $e) { + return [ + 'success' => false, + 'message' => $e->getMessage() + ]; + } + } + + public function listUsers(int $page = 1, int $perPage = 10, string $search = '') + { + $request = new ListUsersRequest(); + $request->setPage($page); + $request->setPerPage($perPage); + $request->setSearch($search); + + try { + list($response, $status) = $this->client->ListUsers($request)->wait(); + + if ($status->code !== \Grpc\STATUS_OK) { + throw new \Exception("gRPC call failed: " . $status->details); + } + + $users = []; + foreach ($response->getUsers() as $user) { + $users[] = [ + 'id' => $user->getId(), + 'name' => $user->getName(), + 'email' => $user->getEmail(), + 'role' => $user->getRole(), + 'created_at' => $user->getCreatedAt(), + 'updated_at' => $user->getUpdatedAt(), + ]; + } + + return [ + 'success' => $response->getSuccess(), + 'message' => $response->getMessage(), + 'users' => $users, + 'total' => $response->getTotal(), + 'current_page' => $response->getCurrentPage(), + 'last_page' => $response->getLastPage(), + ]; + } catch (\Exception $e) { + return [ + 'success' => false, + 'message' => $e->getMessage(), + 'users' => [] + ]; + } + } +} diff --git a/composer.json b/composer.json index 33a7091..efb102e 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,10 @@ "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/protos/user_service.proto b/protos/user_service.proto new file mode 100644 index 0000000..cba5d20 --- /dev/null +++ b/protos/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_url = 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; +} From 93942a65640de5e14731f505cde5a0564f5bf58b Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Thu, 5 Jun 2025 19:15:37 +0300 Subject: [PATCH 02/49] Add GrpcServiceProvider for gRPC service registration --- app/Providers/GrpcServiceProvider.php | 16 ++++++++++++++++ bootstrap/providers.php | 1 + 2 files changed, 17 insertions(+) create mode 100644 app/Providers/GrpcServiceProvider.php 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/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, ]; From 04590ca87c158f2980c3f801147ec7c6e2723e4e Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Thu, 5 Jun 2025 19:22:55 +0300 Subject: [PATCH 03/49] Remove gRPC support and simplify Docker setup Eliminated gRPC and protobuf dependencies, configurations, and related scripts from the Dockerfile. Streamlined PHP extension installation, reduced complexity by removing supervisor and non-essential build steps, and optimized Laravel startup for Swoole. --- php.ini | 64 ++++++++++++++++++++++++++++++++++++++++++++++-- supervisord.conf | 27 ++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 supervisord.conf diff --git a/php.ini b/php.ini index 6d05b31..cabf9fa 100644 --- a/php.ini +++ b/php.ini @@ -1,5 +1,65 @@ +[PHP] +; Basic PHP settings +memory_limit = 512M +upload_max_filesize = 100M +post_max_size = 100M +max_execution_time = 300 +max_input_time = 300 + +; Error reporting +display_errors = Off +log_errors = On +error_log = /var/log/php_errors.log + +; OPcache settings for production +[opcache] opcache.enable=1 opcache.enable_cli=1 -opcache.memory_consumption=128 -opcache.max_accelerated_files=10000 +opcache.memory_consumption=256 +opcache.interned_strings_buffer=16 +opcache.max_accelerated_files=20000 +opcache.max_wasted_percentage=5 +opcache.use_cwd=1 opcache.validate_timestamps=0 +opcache.revalidate_freq=0 +opcache.save_comments=1 +opcache.fast_shutdown=0 + +; gRPC Extension Settings +[grpc] +extension=grpc.so +; Maximum message length for gRPC calls (in bytes) +grpc.max_receive_message_length=4194304 +grpc.max_send_message_length=4194304 +; Enable gRPC keepalive +grpc.keepalive_time_ms=30000 +grpc.keepalive_timeout_ms=5000 +grpc.keepalive_permit_without_calls=true +grpc.http2_max_pings_without_data=0 +grpc.http2_min_recv_ping_interval_without_data_ms=300000 + +; Protobuf Extension Settings +[protobuf] +extension=protobuf.so + +; Swoole settings (for Laravel Octane) +[swoole] +extension=swoole.so +swoole.enable_coroutine=On +swoole.enable_library=On +swoole.enable_preemptive_scheduler=Off + +; Redis settings +[redis] +extension=redis.so + +; Socket settings (required for gRPC) +[sockets] +extension=sockets.so + +; Other extensions +extension=pdo_mysql.so +extension=pdo_pgsql.so +extension=zip.so +extension=pcntl.so +extension=bcmath.so diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..7cc3d84 --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,27 @@ +[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 /var/www/artisan octane:start --server=swoole --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 + +[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 From d77ebf6aa8bcf2eb019d6dda02ff5201d484cf4f Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Thu, 5 Jun 2025 19:35:01 +0300 Subject: [PATCH 04/49] Add gRPC and protobuf support in Docker setup Reintroduced gRPC and protobuf dependencies in Dockerfile with corresponding installations and configurations. Enhanced the build process --- Dockerfile | 135 ++++++++++++++++++++++++--- {protos => proto}/user_service.proto | 0 2 files changed, 121 insertions(+), 14 deletions(-) rename {protos => proto}/user_service.proto (100%) diff --git a/Dockerfile b/Dockerfile index b233f83..954449c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,5 @@ FROM php:8.4.4-fpm-alpine AS builder -# Install necessary build dependencies RUN apk add --no-cache \ mysql-dev \ postgresql-dev \ @@ -16,19 +15,48 @@ RUN apk add --no-cache \ g++ \ libc-dev \ pcre-dev \ + protobuf \ + protobuf-dev \ + grpc \ + grpc-dev \ + grpc-plugins \ + curl \ + wget \ + cmake \ + linux-headers \ + zlib-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 + && docker-php-ext-install opcache \ + && docker-php-ext-install sockets \ + && docker-php-ext-install bcmath -# Install Swoole via PECL +# Install PECL extensions including gRPC RUN pecl install swoole \ && docker-php-ext-enable swoole \ && pecl install redis \ - && docker-php-ext-enable redis + && docker-php-ext-enable redis \ + && pecl install grpc \ + && docker-php-ext-enable grpc \ + && pecl install protobuf \ + && docker-php-ext-enable protobuf + +# Install protoc and grpc_php_plugin if not available via apk +RUN if ! command -v protoc &> /dev/null; then \ + PROTOC_ZIP=protoc-21.12-linux-x86_64.zip && \ + curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v21.12/$PROTOC_ZIP && \ + unzip -o $PROTOC_ZIP -d /usr/local bin/protoc && \ + unzip -o $PROTOC_ZIP -d /usr/local 'include/*' && \ + rm -f $PROTOC_ZIP; \ + fi + +# Verify installations +RUN protoc --version && \ + php -m | grep -E "(grpc|protobuf)" FROM php:8.4.4-fpm-alpine AS production @@ -37,14 +65,22 @@ RUN apk add --no-cache \ mysql-client \ postgresql-client \ libzip \ - git + git \ + protobuf \ + grpc \ + supervisor \ + netcat-openbsd # Copy PHP extensions from builder stage 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 +# Copy protoc and grpc_php_plugin from builder +COPY --from=builder /usr/local/bin/protoc /usr/local/bin/protoc +COPY --from=builder /usr/local/include/google /usr/local/include/google +COPY --from=builder /usr/local/bin/grpc_php_plugin /usr/local/bin/grpc_php_plugin +# Copy php.ini with OPcache and gRPC settings +COPY php.ini /usr/local/etc/php/conf.d/custom.ini # Install Composer COPY --from=composer:latest /usr/bin/composer /usr/bin/composer @@ -52,10 +88,31 @@ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer # Set working directory WORKDIR /var/www +# Copy proto files first (for gRPC generation - changes less frequently than app code) +COPY proto/ ./proto/ + +# Generate gRPC classes from proto files (do this before copying app code) +RUN if [ -d "proto" ]; then \ + mkdir -p app/Grpc app/Grpc/GPBMetadata && \ + for proto in proto/*.proto; do \ + if [ -f "$proto" ]; then \ + echo "Generating protobuf for $proto" && \ + protoc --proto_path=proto \ + --php_out=. \ + --grpc_out=. \ + --plugin=protoc-gen-grpc=$(which grpc_php_plugin || echo "/usr/bin/grpc_php_plugin") \ + "$proto" || echo "Warning: Failed to generate protobuf for $proto"; \ + fi \ + done && \ + echo "Protobuf generation complete" && \ + if [ -d "App" ]; then \ + cp -R App/* app/ && rm -rf App; \ + fi; \ + fi + # Copy composer files first for better 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 \ @@ -68,13 +125,63 @@ RUN composer install \ # Copy application files COPY . . -# Set permissions -RUN chown -R www-data:www-data /var/www \ +# Copy supervisor configuration (if using supervisor) +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +# 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 addgroup -g 1000 laravel \ + && adduser -u 1000 -G laravel -s /bin/sh -D laravel + +# Expose ports (8000 for Octane, 50051 for gRPC if running server) +EXPOSE 8000 50051 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD nc -z localhost 8000 || exit 1 + +# Create startup script that handles both Laravel optimization and gRPC +RUN echo '#!/bin/sh\n\ +set -e\n\ +echo "Starting Laravel application with gRPC support..."\n\ +\n\ +# Laravel optimizations\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\ +\n\ +# Start the application\n\ +echo "Starting Laravel Octane with Swoole..."\n\ +exec php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000\n\ +' > /usr/local/bin/start.sh \ + && chmod +x /usr/local/bin/start.sh + +# Alternative startup script for supervisor (if you need both HTTP and gRPC servers) +RUN echo '#!/bin/sh\n\ +set -e\n\ +echo "Starting Laravel application with supervisor..."\n\ +\n\ +# Laravel optimizations\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\ +\n\ +# Start supervisor\n\ +echo "Starting supervisor..."\n\ +exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf\n\ +' > /usr/local/bin/start-supervisor.sh \ + && chmod +x /usr/local/bin/start-supervisor.sh + +# Switch to non-root user (uncomment if your setup allows) +# USER laravel -# 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" +# Default command (use start-supervisor.sh if you need both HTTP and gRPC servers) +CMD ["/usr/local/bin/start.sh"] diff --git a/protos/user_service.proto b/proto/user_service.proto similarity index 100% rename from protos/user_service.proto rename to proto/user_service.proto From b33ae9dc62da9824937c69349a66f54e07ca7981 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Thu, 5 Jun 2025 23:07:33 +0300 Subject: [PATCH 05/49] Introduce GrpcTransformer for gRPC DTO handling and enhance UserGrpcService integration Added `GrpcTransformer` to simplify and standardize transformation of gRPC DTOs to arrays. Updated `UserGrpcService` to utilize `GrpcTransformer` methods for better maintainability. Included metadata handling for gRPC requests with authorization bearer token support. Upgraded dependencies in `composer.lock` for compatibility. --- Dockerfile | 79 +- app/Http/Controllers/UserApiController.php | 4 +- app/Services/Transformers/GrpcTransformer.php | 50 ++ app/Services/UserGrpcService.php | 15 +- composer.lock | 736 ++++++++++-------- 5 files changed, 513 insertions(+), 371 deletions(-) create mode 100644 app/Services/Transformers/GrpcTransformer.php diff --git a/Dockerfile b/Dockerfile index 954449c..cc51865 100644 --- a/Dockerfile +++ b/Dockerfile @@ -75,10 +75,6 @@ RUN apk add --no-cache \ 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 protoc and grpc_php_plugin from builder -COPY --from=builder /usr/local/bin/protoc /usr/local/bin/protoc -COPY --from=builder /usr/local/include/google /usr/local/include/google -COPY --from=builder /usr/local/bin/grpc_php_plugin /usr/local/bin/grpc_php_plugin # Copy php.ini with OPcache and gRPC settings COPY php.ini /usr/local/etc/php/conf.d/custom.ini @@ -140,48 +136,47 @@ RUN addgroup -g 1000 laravel \ # Expose ports (8000 for Octane, 50051 for gRPC if running server) EXPOSE 8000 50051 -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ - CMD nc -z localhost 8000 || exit 1 - # Create startup script that handles both Laravel optimization and gRPC -RUN echo '#!/bin/sh\n\ -set -e\n\ -echo "Starting Laravel application with gRPC support..."\n\ -\n\ -# Laravel optimizations\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\ -\n\ -# Start the application\n\ -echo "Starting Laravel Octane with Swoole..."\n\ -exec php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000\n\ -' > /usr/local/bin/start.sh \ - && chmod +x /usr/local/bin/start.sh - -# Alternative startup script for supervisor (if you need both HTTP and gRPC servers) -RUN echo '#!/bin/sh\n\ -set -e\n\ -echo "Starting Laravel application with supervisor..."\n\ -\n\ -# Laravel optimizations\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\ -\n\ -# Start supervisor\n\ -echo "Starting supervisor..."\n\ -exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf\n\ -' > /usr/local/bin/start-supervisor.sh \ - && chmod +x /usr/local/bin/start-supervisor.sh +# Create startup script as a separate file +COPY < /usr/local/bin/start-supervisor.sh \ +# && chmod +x /usr/local/bin/start-supervisor.sh # Switch to non-root user (uncomment if your setup allows) # USER laravel # Default command (use start-supervisor.sh if you need both HTTP and gRPC servers) CMD ["/usr/local/bin/start.sh"] +# CMD sh -c "php artisan migrate --force && php artisan storage:link && php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000" diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 70a50f8..6193fca 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -34,7 +34,6 @@ public function index(Request $request) /** * Display the specified resource. - * @throws ConnectionException */ // public function show($user_id) // { @@ -59,8 +58,7 @@ public function show($id) $success = $res['success']; $message = $res['message']; $user = $res['user']; - - return $this->response($message, $user, $success, error: $success != STATUS_OK); + return $this->response($message, $user, Response::HTTP_OK, error: !$success); } 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 index 46e713f..0d46ebc 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -7,6 +7,7 @@ use App\Grpc\User\GetUserRequest; use App\Grpc\User\ListUsersRequest; use App\Grpc\User\UserServiceClient; +use App\Services\Transformers\GrpcTransformer; use Grpc\ChannelCredentials; class UserGrpcService @@ -15,7 +16,7 @@ class UserGrpcService public function __construct() { - $hostname = config('grpc.user_service_host', 'localhost:50051'); + $hostname = config('grpc.user_service_host', '141.94.47.127:50051'); $credentials = ChannelCredentials::createInsecure(); $this->client = new UserServiceClient($hostname, [ @@ -29,15 +30,17 @@ public function getUser(int $userId): array $request = new GetUserRequest(); $request->setId($userId); - try { - $response = $this->client->GetUser($request); + // Prepare metadata with authorization header + $metadata = []; + if ($bearerToken = request()->bearerToken()) + $metadata['authorization'] = ['Bearer ' . $bearerToken]; + try { + $response = $this->client->GetUser($request, $metadata)->wait()[0]; return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'user' => $response->getUser() ? [ - 'id' => $response->getUser()->getId(), - ] : null + 'user' => GrpcTransformer::userToArray($response->getUser()) ]; } catch (\Exception $e) { return [ diff --git a/composer.lock b/composer.lock index 95f8579..735efab 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": "9891bc824dab5d07611e542e7450e117", "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", @@ -1056,16 +1144,16 @@ }, { "name": "laminas/laminas-diactoros", - "version": "3.5.0", + "version": "3.6.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2" + "reference": "b068eac123f21c0e592de41deeb7403b88e0a89f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/143a16306602ce56b8b092a7914fef03c37f9ed2", - "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/b068eac123f21c0e592de41deeb7403b88e0a89f", + "reference": "b068eac123f21c0e592de41deeb7403b88e0a89f", "shasum": "" }, "require": { @@ -1086,7 +1174,7 @@ "ext-gd": "*", "ext-libxml": "*", "http-interop/http-factory-tests": "^2.2.0", - "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-coding-standard": "~3.0.0", "php-http/psr7-integration-tests": "^1.4.0", "phpunit/phpunit": "^10.5.36", "psalm/plugin-phpunit": "^0.19.0", @@ -1140,24 +1228,24 @@ "type": "community_bridge" } ], - "time": "2024-10-14T11:59:49+00:00" + "time": "2025-05-05T16:03:34+00:00" }, { "name": "laravel/framework", - "version": "v12.10.2", + "version": "v12.17.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "0f123cc857bc177abe4d417448d4f7164f71802a" + "reference": "8729d084510480fdeec9b6ad198180147d4a7f06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/0f123cc857bc177abe4d417448d4f7164f71802a", - "reference": "0f123cc857bc177abe4d417448d4f7164f71802a", + "url": "https://api.github.com/repos/laravel/framework/zipball/8729d084510480fdeec9b6ad198180147d4a7f06", + "reference": "8729d084510480fdeec9b6ad198180147d4a7f06", "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 +1262,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 +1354,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 +1386,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,20 +1443,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-04-24T14:11:20+00:00" + "time": "2025-06-03T14:04:18+00:00" }, { "name": "laravel/octane", - "version": "v2.9.1", + "version": "v2.9.3", "source": { "type": "git", "url": "https://github.com/laravel/octane.git", - "reference": "445002b2551c837d60cda4324259063c356dfb56" + "reference": "8278bdb3ae7a9a7380841e18e9cf52d909502647" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/octane/zipball/445002b2551c837d60cda4324259063c356dfb56", - "reference": "445002b2551c837d60cda4324259063c356dfb56", + "url": "https://api.github.com/repos/laravel/octane/zipball/8278bdb3ae7a9a7380841e18e9cf52d909502647", + "reference": "8278bdb3ae7a9a7380841e18e9cf52d909502647", "shasum": "" }, "require": { @@ -1445,7 +1533,7 @@ "issues": "https://github.com/laravel/octane/issues", "source": "https://github.com/laravel/octane" }, - "time": "2025-04-13T21:10:35+00:00" + "time": "2025-05-14T14:05:50+00:00" }, { "name": "laravel/prompts", @@ -1635,16 +1723,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 +1769,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.7-dev" + "dev-main": "2.8-dev" } }, "autoload": { @@ -1738,7 +1826,7 @@ "type": "tidelift" } ], - "time": "2025-04-18T21:09:27+00:00" + "time": "2025-05-05T12:20:28+00:00" }, { "name": "league/config", @@ -2289,16 +2377,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 +2479,7 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:57:33+00:00" + "time": "2025-05-01T19:51:51+00:00" }, { "name": "nette/schema", @@ -2457,16 +2545,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 +2625,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 +2683,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 +2756,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 +2772,7 @@ "type": "github" } ], - "time": "2024-11-21T10:39:51+00:00" + "time": "2025-05-08T08:14:37+00:00" }, { "name": "phpoption/phpoption", @@ -3374,20 +3462,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 +3484,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 +3535,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 +3595,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 +3615,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 +3689,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 +3705,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 +3754,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 +3774,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 +3796,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -3745,7 +3821,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 +3837,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 +3863,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 +3898,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 +3914,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 +3978,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 +3994,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 +4021,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -3976,7 +4054,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 +4070,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 +4118,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 +4134,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 +4164,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 +4197,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 +4213,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 +4234,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 +4311,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 +4327,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 +4391,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 +4407,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 +4475,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 +4491,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 +4554,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 +4574,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 +4632,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 +4652,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 +4715,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 +4731,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 +4796,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 +4816,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 +4877,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 +4893,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 +4957,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 +4973,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 +5033,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 +5053,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 +5112,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 +5132,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,7 +5173,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.5" + "source": "https://github.com/symfony/process/tree/v7.3.0" }, "funding": [ { @@ -5109,11 +5189,11 @@ "type": "tidelift" } ], - "time": "2025-03-13T12:21:46+00:00" + "time": "2025-04-17T09:11:12+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v7.2.0", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", @@ -5176,7 +5256,7 @@ "psr-7" ], "support": { - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.2.0" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.3.0" }, "funding": [ { @@ -5196,16 +5276,16 @@ }, { "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 +5337,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 +5353,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 +5384,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -5340,7 +5420,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 +5436,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 +5507,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 +5523,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 +5546,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 +5560,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 +5603,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 +5619,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 +5645,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -5600,7 +5681,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 +5697,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 +5755,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 +5771,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 +5839,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 +5855,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 +5914,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 +5982,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 +5994,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 +6195,16 @@ }, { "name": "filp/whoops", - "version": "2.18.0", + "version": "2.18.1", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e" + "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", - "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", + "url": "https://api.github.com/repos/filp/whoops/zipball/8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26", + "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26", "shasum": "" }, "require": { @@ -6172,7 +6254,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.1" }, "funding": [ { @@ -6180,24 +6262,24 @@ "type": "github" } ], - "time": "2025-03-15T12:00:00+00:00" + "time": "2025-06-03T18:56:14+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 +6287,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 +6311,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 +6346,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 +6382,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 +6392,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 +6417,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 +6458,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 +6521,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 +6608,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 +6656,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,7 +6664,7 @@ "type": "tidelift" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-04-29T12:36:36+00:00" }, { "name": "nunomaduro/collision", @@ -7125,16 +7208,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.18", + "version": "11.5.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fc3e887c7f3f9917e1bf61e523413d753db00a17" + "reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc3e887c7f3f9917e1bf61e523413d753db00a17", - "reference": "fc3e887c7f3f9917e1bf61e523413d753db00a17", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d565e2cdc21a7db9dc6c399c1fc2083b8010f289", + "reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289", "shasum": "" }, "require": { @@ -7144,7 +7227,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 +7240,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 +7289,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.21" }, "funding": [ { @@ -7230,7 +7313,7 @@ "type": "tidelift" } ], - "time": "2025-04-22T06:09:49+00:00" + "time": "2025-05-21T12:35:00+00:00" }, { "name": "sebastian/cli-parser", @@ -7609,23 +7692,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 +7744,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 +8307,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 +8359,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 +8375,7 @@ "type": "tidelift" } ], - "time": "2025-03-03T07:12:39+00:00" + "time": "2025-04-04T10:10:33+00:00" }, { "name": "theseer/tokenizer", @@ -8335,12 +8430,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" } From b62dacd4a0ecd7e4e055672f3238feee839a1437 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 6 Jun 2025 01:55:17 +0300 Subject: [PATCH 06/49] Refactor system to remove Redis dependencies and migrate User API to enhanced gRPC implementation Eliminated Redis-related configurations and extensions across the app, including `.env`, `php.ini`, and Laravel config files (`cache.php`, `queue.php`, `database.php`). Migrated User API operations to leverage `UserGrpcService` with support for gRPC-based DTO transformations. Updated services and controllers accordingly for consistency and maintainability. --- .env.example | 5 ---- app/Http/Controllers/UserApiController.php | 18 ++++++++++--- app/Services/UserGrpcService.php | 8 +++--- config/cache.php | 6 ----- config/database.php | 30 ---------------------- config/grpc.php | 16 ++++++++++++ config/queue.php | 9 ------- php.ini | 4 --- 8 files changed, 35 insertions(+), 61 deletions(-) create mode 100644 config/grpc.php 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/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 6193fca..ddaaf24 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -8,7 +8,6 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; use Symfony\Component\HttpFoundation\Response; -use const Grpc\STATUS_OK; class UserApiController extends BaseController { @@ -21,6 +20,16 @@ public function __construct( } +// +// /** +// * 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. @@ -28,8 +37,11 @@ public function __construct( */ 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($request->page, $request->perPage, $request->search); + $success = $res['success']; + $message = $res['message']; + $users = $res['users']; + return $this->response($message, $users, $success, error: !$success); } /** diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php index 0d46ebc..243319e 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -16,7 +16,7 @@ class UserGrpcService public function __construct() { - $hostname = config('grpc.user_service_host', '141.94.47.127:50051'); + $hostname = config('grpc.user.service_host', 'http://user-webserver:50051'); $credentials = ChannelCredentials::createInsecure(); $this->client = new UserServiceClient($hostname, [ @@ -51,7 +51,7 @@ public function getUser(int $userId): array } } - public function authenticateUser(string $email, string $password) + public function authenticateUser(string $email, string $password): array { $request = new AuthenticateRequest(); $request->setEmail($email); @@ -85,7 +85,7 @@ public function authenticateUser(string $email, string $password) } } - public function createUser(array $userData) + public function createUser(array $userData): array { $request = new CreateUserRequest(); $request->setName($userData['name']); @@ -118,7 +118,7 @@ public function createUser(array $userData) } } - public function listUsers(int $page = 1, int $perPage = 10, string $search = '') + public function listUsers(int $page = 1, int $perPage = 10, string $search = ''): array { $request = new ListUsersRequest(); $request->setPage($page); diff --git a/config/cache.php b/config/cache.php index 925f7d2..56ffb05 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'), 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/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/php.ini b/php.ini index cabf9fa..d53d8d8 100644 --- a/php.ini +++ b/php.ini @@ -49,10 +49,6 @@ swoole.enable_coroutine=On swoole.enable_library=On swoole.enable_preemptive_scheduler=Off -; Redis settings -[redis] -extension=redis.so - ; Socket settings (required for gRPC) [sockets] extension=sockets.so From d165cc3ed0e79dbc4c775e881997ece871974e7f Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 6 Jun 2025 03:45:22 +0300 Subject: [PATCH 07/49] Migrate User API to gRPC and remove Redis dependencies Replaced Redis-based configurations and dependencies with gRPC-driven `UserGrpcService` across controllers and services. Updated Docker configurations to streamline environment setup by eliminating Redis references. --- app/Http/Controllers/AuthController.php | 40 ++++--- app/Http/Controllers/UserApiController.php | 24 ++-- app/Services/UserGrpcService.php | 122 ++++++++++++++------- docker-compose.yml | 5 - docker-composeServer.yml | 5 - 5 files changed, 129 insertions(+), 67 deletions(-) diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index b814b59..f6155ee 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers; -use App\External_Apis\Services\UserService; +use App\Services\UserGrpcService; use Exception; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -11,17 +11,29 @@ class AuthController extends BaseController { public function __construct( - private readonly UserService $service + private readonly UserGrpcService $userGrpcService ) { } +// public function login(Request $request): JsonResponse +// { +// try { +// [$res, $status] = $this->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, $status] = $this->service->login($request->all()); - - return $this->response($res['message'], $res['data'], $status); + $res = $this->userGrpcService->authenticateUser($request->email, $request->password); + $success = $res['success']; + $message = $res['message']; + $data = $res['data']; + return $this->response($message, $data, Response::HTTP_OK, error: !$success); } catch (Exception $exception) { return $this->response($exception->getMessage(), status: $exception->getCode(), error: true); } @@ -32,12 +44,11 @@ public function login(Request $request): JsonResponse public function signup(Request $request): 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()); + $res = $this->userGrpcService->createUser($request->all()); + $success = $res['success']; + $message = $res['message']; + $user = $res['data']; + return $this->response($message, $user, Response::HTTP_CREATED, error: !$success); } catch (Exception $e) { return $this->response($e->getMessage(), status: $e->getCode() ?: Response::HTTP_BAD_REQUEST, error: true); } @@ -48,8 +59,11 @@ public function signup(Request $request): JsonResponse public function logout(Request $request) { try { - [$res, $status] = $this->service->logout($request->bearerToken()); - return $this->response($res['message'], $res['data'], $status); + $res = $this->userGrpcService->logout($request->bearerToken()); + $success = $res['success']; + $message = $res['message']; + $user = $res['user']; + return $this->response($message, $user, 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/UserApiController.php b/app/Http/Controllers/UserApiController.php index ddaaf24..9656a6f 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -12,7 +12,6 @@ class UserApiController extends BaseController { public function __construct( - private readonly UserService $userService, private readonly UserGrpcService $userGrpcService ) @@ -41,7 +40,7 @@ public function index(Request $request) $success = $res['success']; $message = $res['message']; $users = $res['users']; - return $this->response($message, $users, $success, error: !$success); + return $this->response($message, $users, Response::HTTP_OK, error: !$success); } /** @@ -90,15 +89,26 @@ 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); + $res = $this->userGrpcService->GetUserProfile($request->all()); + $success = $res['success']; + $message = $res['message']; + $user = $res['user']; + return $this->response($message, $user, Response::HTTP_OK, error: !$success); } } diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php index 243319e..5c8c956 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -6,6 +6,7 @@ use App\Grpc\User\CreateUserRequest; use App\Grpc\User\GetUserRequest; use App\Grpc\User\ListUsersRequest; +use App\Grpc\User\LogoutRequest; use App\Grpc\User\UserServiceClient; use App\Services\Transformers\GrpcTransformer; use Grpc\ChannelCredentials; @@ -31,24 +32,7 @@ public function getUser(int $userId): array $request->setId($userId); // Prepare metadata with authorization header - $metadata = []; - if ($bearerToken = request()->bearerToken()) - $metadata['authorization'] = ['Bearer ' . $bearerToken]; - - try { - $response = $this->client->GetUser($request, $metadata)->wait()[0]; - return [ - 'success' => $response->getSuccess(), - 'message' => $response->getMessage(), - 'user' => GrpcTransformer::userToArray($response->getUser()) - ]; - } catch (\Exception $e) { - return [ - 'success' => false, - 'message' => $e->getMessage(), - 'user' => null - ]; - } + return $this->retrieveUser($request); } public function authenticateUser(string $email, string $password): array @@ -67,15 +51,17 @@ public function authenticateUser(string $email, string $password): array return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'token' => $response->getToken(), - 'token_type' => $response->getTokenType(), - 'expires_at' => $response->getExpiresAt(), - 'user' => $response->getUser() ? [ - 'id' => $response->getUser()->getId(), - 'name' => $response->getUser()->getName(), - 'email' => $response->getUser()->getEmail(), - 'role' => $response->getUser()->getRole(), - ] : null + 'data' => [ + 'token' => $response->getToken(), + 'token_type' => $response->getTokenType(), + 'expires_at' => $response->getExpiresAt(), + 'user' => $response->getUser() ? [ + 'id' => $response->getUser()->getId(), + 'name' => $response->getUser()->getName(), + 'email' => $response->getUser()->getEmail(), + 'role' => $response->getUser()->getRole(), + ] : null + ] ]; } catch (\Exception $e) { return [ @@ -91,24 +77,22 @@ public function createUser(array $userData): array $request->setName($userData['name']); $request->setEmail($userData['email']); $request->setPassword($userData['password']); - $request->setRole($userData['role'] ?? 'user'); try { list($response, $status) = $this->client->CreateUser($request)->wait(); - if ($status->code !== \Grpc\STATUS_OK) { - throw new \Exception("gRPC call failed: " . $status->details); - } + if ($status->code !== \Grpc\STATUS_OK) + return [ + 'success' => false, + 'message' => $status->details + ]; return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'user' => $response->getUser() ? [ - 'id' => $response->getUser()->getId(), - 'name' => $response->getUser()->getName(), - 'email' => $response->getUser()->getEmail(), - 'role' => $response->getUser()->getRole(), - ] : null + 'data' => [ + 'user' => GrpcTransformer::userToArray($response->getUser()) + ] ]; } catch (\Exception $e) { return [ @@ -160,4 +144,68 @@ public function listUsers(int $page = 1, int $perPage = 10, string $search = '') ]; } } + + public function GetUserProfile(array $all): array + { + $request = new GetUserRequest(); + $request->setId($all['id']); + return $this->retrieveUser($request); + } + + /** + * @param GetUserRequest $request + * @return array + */ + private function retrieveUser(GetUserRequest $request): array + { + $metadata = []; + if ($bearerToken = request()->bearerToken()) + $metadata['authorization'] = ['Bearer ' . $bearerToken]; + + try { + $response = $this->client->GetUser($request, $metadata)->wait()[0]; + return [ + 'success' => $response->getSuccess(), + 'message' => $response->getMessage(), + 'user' => GrpcTransformer::userToArray($response->getUser()) + ]; + } catch (\Exception $e) { + return [ + 'success' => false, + 'message' => $e->getMessage(), + 'user' => null + ]; + } + } + + 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(), + 'user' => null + ]; + } + return [ + 'success' => $response->getSuccess(), + 'message' => $response->getMessage(), + 'user' => GrpcTransformer::userToArray($response->getUser()) + ]; + } catch (\Exception $e) { + return [ + 'success' => false, + 'message' => $e->getMessage(), + 'user' => null + ]; + } + } } diff --git a/docker-compose.yml b/docker-compose.yml index e228f14..bfa9589 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,11 +31,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 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: From 1878e76b52710617eaa64111f654419fa6ccda04 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 6 Jun 2025 04:57:02 +0300 Subject: [PATCH 08/49] Refactor `UserGrpcService` to leverage `UserResource` for consistent gRPC response handling and update related controllers and services accordingly --- app/Grpc/Resources/UserResource.php | 27 ++++++++ app/Http/Controllers/AuthController.php | 14 ++-- app/Http/Controllers/UserApiController.php | 12 ++-- app/Services/UserGrpcService.php | 76 ++++++++-------------- 4 files changed, 69 insertions(+), 60 deletions(-) create mode 100644 app/Grpc/Resources/UserResource.php diff --git a/app/Grpc/Resources/UserResource.php b/app/Grpc/Resources/UserResource.php new file mode 100644 index 0000000..72046a7 --- /dev/null +++ b/app/Grpc/Resources/UserResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + $user = $this->resource->getUser(); + 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($this->resource->getToken()), + ]); + } +} diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index f6155ee..7afa4ad 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\External_Apis\Services\UserService; use App\Services\UserGrpcService; use Exception; use Illuminate\Http\JsonResponse; @@ -11,7 +12,9 @@ class AuthController extends BaseController { public function __construct( - private readonly UserGrpcService $userGrpcService + private readonly UserGrpcService $userGrpcService, + private readonly UserService $service + ) { } @@ -26,7 +29,8 @@ public function __construct( // return $this->response($exception->getMessage(), status: $exception->getCode(), error: true); // } // } - public function login(Request $request): JsonResponse + public + function login(Request $request): JsonResponse { try { $res = $this->userGrpcService->authenticateUser($request->email, $request->password); @@ -41,7 +45,8 @@ public function login(Request $request): JsonResponse /** */ - public function signup(Request $request): JsonResponse + public + function signup(Request $request): JsonResponse { try { $res = $this->userGrpcService->createUser($request->all()); @@ -56,7 +61,8 @@ public function signup(Request $request): JsonResponse /** */ - public function logout(Request $request) + public + function logout(Request $request) { try { $res = $this->userGrpcService->logout($request->bearerToken()); diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 9656a6f..83c19b1 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -39,8 +39,8 @@ public function index(Request $request) $res = $this->userGrpcService->listUsers($request->page, $request->perPage, $request->search); $success = $res['success']; $message = $res['message']; - $users = $res['users']; - return $this->response($message, $users, Response::HTTP_OK, error: !$success); + $data = $res['data']; + return $this->response($message, $data, Response::HTTP_OK, error: !$success); } /** @@ -68,8 +68,8 @@ public function show($id) $res = $this->userGrpcService->getUser($id); $success = $res['success']; $message = $res['message']; - $user = $res['user']; - return $this->response($message, $user, Response::HTTP_OK, error: !$success); + $data = $res['data']; + return $this->response($message, $data, Response::HTTP_OK, error: !$success); } @@ -108,7 +108,7 @@ public function userProfile(Request $request) $res = $this->userGrpcService->GetUserProfile($request->all()); $success = $res['success']; $message = $res['message']; - $user = $res['user']; - return $this->response($message, $user, Response::HTTP_OK, error: !$success); + $data = $res['data']; + return $this->response($message, $data, Response::HTTP_OK, error: !$success); } } diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php index 5c8c956..0ab73fe 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -2,14 +2,16 @@ namespace App\Services; +use App\Grpc\Resources\UserResource; use App\Grpc\User\AuthenticateRequest; use App\Grpc\User\CreateUserRequest; use App\Grpc\User\GetUserRequest; use App\Grpc\User\ListUsersRequest; use App\Grpc\User\LogoutRequest; use App\Grpc\User\UserServiceClient; -use App\Services\Transformers\GrpcTransformer; +use Exception; use Grpc\ChannelCredentials; +use const Grpc\STATUS_OK; class UserGrpcService { @@ -17,7 +19,7 @@ class UserGrpcService public function __construct() { - $hostname = config('grpc.user.service_host', 'http://user-webserver:50051'); + $hostname = config('grpc.user.service_host', 'user:50051'); $credentials = ChannelCredentials::createInsecure(); $this->client = new UserServiceClient($hostname, [ @@ -44,29 +46,20 @@ public function authenticateUser(string $email, string $password): array try { list($response, $status) = $this->client->AuthenticateUser($request)->wait(); - if ($status->code !== \Grpc\STATUS_OK) { - throw new \Exception("gRPC call failed: " . $status->details); + if ($status->code !== STATUS_OK) { + throw new Exception("gRPC call failed: " . $status->details); } return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'data' => [ - 'token' => $response->getToken(), - 'token_type' => $response->getTokenType(), - 'expires_at' => $response->getExpiresAt(), - 'user' => $response->getUser() ? [ - 'id' => $response->getUser()->getId(), - 'name' => $response->getUser()->getName(), - 'email' => $response->getUser()->getEmail(), - 'role' => $response->getUser()->getRole(), - ] : null - ] + 'data' => new UserResource($response) ]; - } catch (\Exception $e) { + } catch (Exception $e) { return [ 'success' => false, - 'message' => $e->getMessage() + 'message' => $e->getMessage(), + 'data' => null ]; } } @@ -81,20 +74,17 @@ public function createUser(array $userData): array try { list($response, $status) = $this->client->CreateUser($request)->wait(); - if ($status->code !== \Grpc\STATUS_OK) + if ($status->code !== STATUS_OK) return [ 'success' => false, 'message' => $status->details ]; - return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'data' => [ - 'user' => GrpcTransformer::userToArray($response->getUser()) - ] + 'data' => new UserResource($response) ]; - } catch (\Exception $e) { + } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage() @@ -112,35 +102,21 @@ public function listUsers(int $page = 1, int $perPage = 10, string $search = '') try { list($response, $status) = $this->client->ListUsers($request)->wait(); - if ($status->code !== \Grpc\STATUS_OK) { - throw new \Exception("gRPC call failed: " . $status->details); - } - - $users = []; - foreach ($response->getUsers() as $user) { - $users[] = [ - 'id' => $user->getId(), - 'name' => $user->getName(), - 'email' => $user->getEmail(), - 'role' => $user->getRole(), - 'created_at' => $user->getCreatedAt(), - 'updated_at' => $user->getUpdatedAt(), - ]; + if ($status->code !== STATUS_OK) { + throw new Exception("gRPC call failed: " . $status->details); } return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'users' => $users, - 'total' => $response->getTotal(), - 'current_page' => $response->getCurrentPage(), - 'last_page' => $response->getLastPage(), + 'data' => UserResource::collection($response->getUsers()) ]; - } catch (\Exception $e) { + + } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), - 'users' => [] + 'data' => [] ]; } } @@ -167,13 +143,13 @@ private function retrieveUser(GetUserRequest $request): array return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'user' => GrpcTransformer::userToArray($response->getUser()) + 'data' => new UserResource($response) ]; - } catch (\Exception $e) { + } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), - 'user' => null + 'data' => null ]; } } @@ -192,19 +168,19 @@ public function logout(?string $bearerToken): array return [ 'success' => false, 'message' => $response->getMessage(), - 'user' => null + 'data' => null ]; } return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'user' => GrpcTransformer::userToArray($response->getUser()) + 'data' => new UserResource($response) ]; - } catch (\Exception $e) { + } catch (Exception $e) { return [ 'success' => false, 'message' => $e->getMessage(), - 'user' => null + 'data' => null ]; } } From f0e3cf416fb5ecd7f5ce064507c808dd0e1144be Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 6 Jun 2025 07:43:45 +0300 Subject: [PATCH 09/49] Refactor gRPC service methods and controllers for consistent request, response, and metadata handling --- app/Grpc/Resources/UserResource.php | 11 ++++++-- app/Http/Controllers/AuthController.php | 4 +-- app/Http/Controllers/PasswordController.php | 4 +-- app/Http/Controllers/UserApiController.php | 2 +- app/Services/UserGrpcService.php | 31 ++++++++++----------- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/app/Grpc/Resources/UserResource.php b/app/Grpc/Resources/UserResource.php index 72046a7..7eab0d4 100644 --- a/app/Grpc/Resources/UserResource.php +++ b/app/Grpc/Resources/UserResource.php @@ -14,14 +14,19 @@ class UserResource extends JsonResource */ public function toArray(Request $request): array { - $user = $this->resource->getUser(); + 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()), + 'photo_url' => $this->when($user?->getPhotoPath(), $user?->getPhotoPath()), 'role_name' => $user?->getRole(), - 'token' => $this->whenNotNull($this->resource->getToken()), + 'token' => $this->whenNotNull($token ?? null), ]); } } diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 7afa4ad..e290d3c 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -68,8 +68,8 @@ function logout(Request $request) $res = $this->userGrpcService->logout($request->bearerToken()); $success = $res['success']; $message = $res['message']; - $user = $res['user']; - return $this->response($message, $user, Response::HTTP_NO_CONTENT, error: !$success); + $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/PasswordController.php b/app/Http/Controllers/PasswordController.php index 39b53c8..881b763 100644 --- a/app/Http/Controllers/PasswordController.php +++ b/app/Http/Controllers/PasswordController.php @@ -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/UserApiController.php b/app/Http/Controllers/UserApiController.php index 83c19b1..7dbda61 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -36,7 +36,7 @@ public function __construct( */ public function index(Request $request) { - $res = $this->userGrpcService->listUsers($request->page, $request->perPage, $request->search); + $res = $this->userGrpcService->listUsers(); $success = $res['success']; $message = $res['message']; $data = $res['data']; diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php index 0ab73fe..8911547 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -74,7 +74,7 @@ public function createUser(array $userData): array try { list($response, $status) = $this->client->CreateUser($request)->wait(); - if ($status->code !== STATUS_OK) + if ($response->getSuccess() !== STATUS_OK) return [ 'success' => false, 'message' => $status->details @@ -92,15 +92,14 @@ public function createUser(array $userData): array } } - public function listUsers(int $page = 1, int $perPage = 10, string $search = ''): array + public function listUsers(): array { $request = new ListUsersRequest(); - $request->setPage($page); - $request->setPerPage($perPage); - $request->setSearch($search); - - try { - list($response, $status) = $this->client->ListUsers($request)->wait(); + $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); @@ -109,16 +108,16 @@ public function listUsers(int $page = 1, int $perPage = 10, string $search = '') return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), - 'data' => UserResource::collection($response->getUsers()) + 'data' => UserResource::collection(collect($response->getUsers())) ]; - } catch (Exception $e) { - return [ - 'success' => false, - 'message' => $e->getMessage(), - 'data' => [] - ]; - } +// } catch (Exception $e) { +// return [ +// 'success' => false, +// 'message' => $e->getMessage(), +// 'data' => [] +// ]; +// } } public function GetUserProfile(array $all): array From f865fabc02df363c0a3bb0d616ebac907771ebbf Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 6 Jun 2025 08:06:06 +0300 Subject: [PATCH 10/49] Refactor `UserGrpcService` to enhance metadata handling, improve error handling, and update method calls for `GetUserProfile` and `GetUser` operations --- app/Grpc/Servers/UserServer.php | 2 +- app/Grpc/User/UserProfileResponse.php | 10 ++-- app/Services/UserGrpcService.php | 68 ++++++++++++++++----------- proto/user_service.proto | 2 +- 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/app/Grpc/Servers/UserServer.php b/app/Grpc/Servers/UserServer.php index a080cd3..ece97e4 100644 --- a/app/Grpc/Servers/UserServer.php +++ b/app/Grpc/Servers/UserServer.php @@ -212,7 +212,7 @@ public function GetUserProfile(UserProfileRequest $request, \Grpc\ServerContext $response->setName($profileData['name'] ?? ''); $response->setEmail($profileData['email'] ?? ''); $response->setRole($profileData['role'] ?? ''); - $response->setPhotoUrl($profileData['photo_url'] ?? ''); + $response->setPhotoPath($profileData['photo_url'] ?? ''); // Handle cart items if present if (isset($profileData['cart'])) { diff --git a/app/Grpc/User/UserProfileResponse.php b/app/Grpc/User/UserProfileResponse.php index 02058b1..14b339a 100644 --- a/app/Grpc/User/UserProfileResponse.php +++ b/app/Grpc/User/UserProfileResponse.php @@ -28,7 +28,7 @@ class UserProfileResponse extends \Google\Protobuf\Internal\Message /** * Generated from protobuf field string photo_url = 4; */ - protected $photo_url = ''; + protected $photo_path = ''; /** * Generated from protobuf field string role = 5; */ @@ -137,9 +137,9 @@ public function setEmail($var) * Generated from protobuf field string photo_url = 4; * @return string */ - public function getPhotoUrl() + public function getPhotoPath() { - return $this->photo_url; + return $this->photo_path; } /** @@ -147,10 +147,10 @@ public function getPhotoUrl() * @param string $var * @return $this */ - public function setPhotoUrl($var) + public function setPhotoPath($var) { GPBUtil::checkString($var, True); - $this->photo_url = $var; + $this->photo_path = $var; return $this; } diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php index 8911547..0326533 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -8,6 +8,7 @@ use App\Grpc\User\GetUserRequest; use App\Grpc\User\ListUsersRequest; use App\Grpc\User\LogoutRequest; +use App\Grpc\User\UserProfileRequest; use App\Grpc\User\UserServiceClient; use Exception; use Grpc\ChannelCredentials; @@ -32,9 +33,21 @@ public function getUser(int $userId): array { $request = new GetUserRequest(); $request->setId($userId); - - // Prepare metadata with authorization header - return $this->retrieveUser($request); + $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 @@ -99,17 +112,17 @@ public function listUsers(): array if ($bearerToken = request()->bearerToken()) $metadata['authorization'] = ['Bearer ' . $bearerToken]; // try { - list($response, $status) = $this->client->ListUsers($request, $metadata)->wait(); + list($response, $status) = $this->client->ListUsers($request, $metadata)->wait(); - if ($status->code !== STATUS_OK) { - throw new Exception("gRPC call failed: " . $status->details); - } + 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())) - ]; + return [ + 'success' => $response->getSuccess(), + 'message' => $response->getMessage(), + 'data' => UserResource::collection(collect($response->getUsers())) + ]; // } catch (Exception $e) { // return [ @@ -122,23 +135,10 @@ public function listUsers(): array public function GetUserProfile(array $all): array { - $request = new GetUserRequest(); - $request->setId($all['id']); - return $this->retrieveUser($request); - } - - /** - * @param GetUserRequest $request - * @return array - */ - private function retrieveUser(GetUserRequest $request): array - { - $metadata = []; - if ($bearerToken = request()->bearerToken()) - $metadata['authorization'] = ['Bearer ' . $bearerToken]; - + $request = new UserProfileRequest(); + $metadata = $this->getAuthorizationMetadata(); try { - $response = $this->client->GetUser($request, $metadata)->wait()[0]; + $response = $this->client->GetUserProfile($request, $metadata)->wait()[0]; return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), @@ -153,6 +153,18 @@ private function retrieveUser(GetUserRequest $request): array } } + /** + * @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 = []; diff --git a/proto/user_service.proto b/proto/user_service.proto index cba5d20..483327b 100644 --- a/proto/user_service.proto +++ b/proto/user_service.proto @@ -126,7 +126,7 @@ message UserProfileResponse { int64 id = 1; string name = 2; string email = 3; - string photo_url = 4; + string photo_path = 4; string role = 5; repeated CartItem cart_items = 6; bool success = 7; From e6afbcc2c7c60650c2a8dd965e50703620eea9c6 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 6 Jun 2025 08:12:03 +0300 Subject: [PATCH 11/49] Update `UserProfileResponse` to rename `photo_url` to `photo_path` for consistency with protobuf field definition --- app/Grpc/User/UserProfileResponse.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Grpc/User/UserProfileResponse.php b/app/Grpc/User/UserProfileResponse.php index 14b339a..8834598 100644 --- a/app/Grpc/User/UserProfileResponse.php +++ b/app/Grpc/User/UserProfileResponse.php @@ -26,7 +26,7 @@ class UserProfileResponse extends \Google\Protobuf\Internal\Message */ protected $email = ''; /** - * Generated from protobuf field string photo_url = 4; + * Generated from protobuf field string photo_path = 4; */ protected $photo_path = ''; /** From 233838dbb5e258cb72e4ddf0ef4e594b2d5a4f12 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 6 Jun 2025 08:23:09 +0300 Subject: [PATCH 12/49] Update `UserProfileResponse` to rename `photo_url` to `photo_path` and enhance error handling in `UserGrpcService` --- app/Grpc/GPBMetadata/User/UserService.php | 2 +- app/Grpc/User/UserProfileResponse.php | 6 +++--- app/Services/UserGrpcService.php | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/Grpc/GPBMetadata/User/UserService.php b/app/Grpc/GPBMetadata/User/UserService.php index 3c8859b..b54220e 100644 --- a/app/Grpc/GPBMetadata/User/UserService.php +++ b/app/Grpc/GPBMetadata/User/UserService.php @@ -15,7 +15,7 @@ public static function initOnce() { return; } $pool->internalAddGeneratedFile(hex2bin( - "0aaf130a12757365725f736572766963652e70726f746f120475736572221c0a0e4765745573657252657175657374120a0a02696418012001280322500a114372656174655573657252657175657374120c0a046e616d65180120012809120d0a05656d61696c18022001280912100a0870617373776f7264180320012809120c0a04726f6c65180420012809224e0a115570646174655573657252657175657374120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912100a0870617373776f7264180420012809221f0a1144656c6574655573657252657175657374120a0a02696418012001280322420a104c697374557365727352657175657374120c0a047061676518012001280512100a087065725f70616765180220012805120e0a0673656172636818032001280922360a1341757468656e74696361746552657175657374120d0a05656d61696c18012001280912100a0870617373776f726418022001280922240a125573657250726f66696c6552657175657374120e0a066669656c6473180220032809220f0a0d4c6f676f75745265717565737422250a1456616c6964617465546f6b656e52657175657374120d0a05746f6b656e18012001280922150a1352656672657368546f6b656e5265717565737422470a154368616e676550617373776f72645265717565737412180a1063757272656e745f70617373776f726418012001280912140a0c6e65775f70617373776f7264180220012809228f010a0455736572120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c180320012809120c0a04726f6c6518042001280912120a0a70686f746f5f7061746818052001280912140a0c6163636573735f746f6b656e18062001280912120a0a637265617465645f617418072001280912120a0a757064617465645f617418082001280922450a08436172744974656d120a0a026964180120012803120c0a047479706518022001280912100a087175616e74697479180320012805120d0a057072696365180420012801224a0a0c55736572526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120f0a0773756363657373180220012808120f0a076d65737361676518032001280922360a1244656c65746555736572526573706f6e7365120f0a0773756363657373180120012808120f0a076d6573736167651802200128092288010a114c6973745573657273526573706f6e736512190a05757365727318012003280b320a2e757365722e55736572120d0a05746f74616c18022001280512140a0c63757272656e745f7061676518032001280512110a096c6173745f70616765180420012805120f0a0773756363657373180520012808120f0a076d6573736167651806200128092289010a1441757468656e746963617465526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a05746f6b656e18022001280912120a0a746f6b656e5f7479706518032001280912120a0a657870697265735f6174180420012809120f0a0773756363657373180520012808120f0a076d65737361676518062001280922a5010a135573657250726f66696c65526573706f6e7365120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912110a0970686f746f5f75726c180420012809120c0a04726f6c6518052001280912220a0a636172745f6974656d7318062003280b320e2e757365722e436172744974656d120f0a0773756363657373180720012808120f0a076d65737361676518082001280922320a0e4c6f676f7574526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280922620a1556616c6964617465546f6b656e526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a0576616c6964180220012808120f0a0773756363657373180320012808120f0a076d657373616765180420012809226f0a1452656672657368546f6b656e526573706f6e7365120d0a05746f6b656e18012001280912120a0a746f6b656e5f7479706518022001280912120a0a657870697265735f6174180320012809120f0a0773756363657373180420012808120f0a076d657373616765180520012809223a0a164368616e676550617373776f7264526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280932e0050a0b557365725365727669636512330a074765745573657212142e757365722e47657455736572526571756573741a122e757365722e55736572526573706f6e736512390a0a4372656174655573657212172e757365722e43726561746555736572526571756573741a122e757365722e55736572526573706f6e736512390a0a5570646174655573657212172e757365722e55706461746555736572526571756573741a122e757365722e55736572526573706f6e7365123f0a0a44656c6574655573657212172e757365722e44656c65746555736572526571756573741a182e757365722e44656c65746555736572526573706f6e7365123c0a094c697374557365727312162e757365722e4c6973745573657273526571756573741a172e757365722e4c6973745573657273526573706f6e736512490a1041757468656e7469636174655573657212192e757365722e41757468656e746963617465526571756573741a1a2e757365722e41757468656e746963617465526573706f6e736512450a0e4765745573657250726f66696c6512182e757365722e5573657250726f66696c65526571756573741a192e757365722e5573657250726f66696c65526573706f6e736512370a0a4c6f676f75745573657212132e757365722e4c6f676f7574526571756573741a142e757365722e4c6f676f7574526573706f6e736512480a0d56616c6964617465546f6b656e121a2e757365722e56616c6964617465546f6b656e526571756573741a1b2e757365722e56616c6964617465546f6b656e526573706f6e736512450a0c52656672657368546f6b656e12192e757365722e52656672657368546f6b656e526571756573741a1a2e757365722e52656672657368546f6b656e526573706f6e7365124b0a0e4368616e676550617373776f7264121b2e757365722e4368616e676550617373776f7264526571756573741a1c2e757365722e4368616e676550617373776f7264526573706f6e7365422cca020d4170705c477270635c55736572e202194170705c477270635c4750424d657461646174615c55736572620670726f746f33" + "0ab0130a12757365725f736572766963652e70726f746f120475736572221c0a0e4765745573657252657175657374120a0a02696418012001280322500a114372656174655573657252657175657374120c0a046e616d65180120012809120d0a05656d61696c18022001280912100a0870617373776f7264180320012809120c0a04726f6c65180420012809224e0a115570646174655573657252657175657374120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912100a0870617373776f7264180420012809221f0a1144656c6574655573657252657175657374120a0a02696418012001280322420a104c697374557365727352657175657374120c0a047061676518012001280512100a087065725f70616765180220012805120e0a0673656172636818032001280922360a1341757468656e74696361746552657175657374120d0a05656d61696c18012001280912100a0870617373776f726418022001280922240a125573657250726f66696c6552657175657374120e0a066669656c6473180220032809220f0a0d4c6f676f75745265717565737422250a1456616c6964617465546f6b656e52657175657374120d0a05746f6b656e18012001280922150a1352656672657368546f6b656e5265717565737422470a154368616e676550617373776f72645265717565737412180a1063757272656e745f70617373776f726418012001280912140a0c6e65775f70617373776f7264180220012809228f010a0455736572120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c180320012809120c0a04726f6c6518042001280912120a0a70686f746f5f7061746818052001280912140a0c6163636573735f746f6b656e18062001280912120a0a637265617465645f617418072001280912120a0a757064617465645f617418082001280922450a08436172744974656d120a0a026964180120012803120c0a047479706518022001280912100a087175616e74697479180320012805120d0a057072696365180420012801224a0a0c55736572526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120f0a0773756363657373180220012808120f0a076d65737361676518032001280922360a1244656c65746555736572526573706f6e7365120f0a0773756363657373180120012808120f0a076d6573736167651802200128092288010a114c6973745573657273526573706f6e736512190a05757365727318012003280b320a2e757365722e55736572120d0a05746f74616c18022001280512140a0c63757272656e745f7061676518032001280512110a096c6173745f70616765180420012805120f0a0773756363657373180520012808120f0a076d6573736167651806200128092289010a1441757468656e746963617465526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a05746f6b656e18022001280912120a0a746f6b656e5f7479706518032001280912120a0a657870697265735f6174180420012809120f0a0773756363657373180520012808120f0a076d65737361676518062001280922a6010a135573657250726f66696c65526573706f6e7365120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912120a0a70686f746f5f70617468180420012809120c0a04726f6c6518052001280912220a0a636172745f6974656d7318062003280b320e2e757365722e436172744974656d120f0a0773756363657373180720012808120f0a076d65737361676518082001280922320a0e4c6f676f7574526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280922620a1556616c6964617465546f6b656e526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a0576616c6964180220012808120f0a0773756363657373180320012808120f0a076d657373616765180420012809226f0a1452656672657368546f6b656e526573706f6e7365120d0a05746f6b656e18012001280912120a0a746f6b656e5f7479706518022001280912120a0a657870697265735f6174180320012809120f0a0773756363657373180420012808120f0a076d657373616765180520012809223a0a164368616e676550617373776f7264526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280932e0050a0b557365725365727669636512330a074765745573657212142e757365722e47657455736572526571756573741a122e757365722e55736572526573706f6e736512390a0a4372656174655573657212172e757365722e43726561746555736572526571756573741a122e757365722e55736572526573706f6e736512390a0a5570646174655573657212172e757365722e55706461746555736572526571756573741a122e757365722e55736572526573706f6e7365123f0a0a44656c6574655573657212172e757365722e44656c65746555736572526571756573741a182e757365722e44656c65746555736572526573706f6e7365123c0a094c697374557365727312162e757365722e4c6973745573657273526571756573741a172e757365722e4c6973745573657273526573706f6e736512490a1041757468656e7469636174655573657212192e757365722e41757468656e746963617465526571756573741a1a2e757365722e41757468656e746963617465526573706f6e736512450a0e4765745573657250726f66696c6512182e757365722e5573657250726f66696c65526571756573741a192e757365722e5573657250726f66696c65526573706f6e736512370a0a4c6f676f75745573657212132e757365722e4c6f676f7574526571756573741a142e757365722e4c6f676f7574526573706f6e736512480a0d56616c6964617465546f6b656e121a2e757365722e56616c6964617465546f6b656e526571756573741a1b2e757365722e56616c6964617465546f6b656e526573706f6e736512450a0c52656672657368546f6b656e12192e757365722e52656672657368546f6b656e526571756573741a1a2e757365722e52656672657368546f6b656e526573706f6e7365124b0a0e4368616e676550617373776f7264121b2e757365722e4368616e676550617373776f7264526571756573741a1c2e757365722e4368616e676550617373776f7264526573706f6e7365422cca020d4170705c477270635c55736572e202194170705c477270635c4750424d657461646174615c55736572620670726f746f33" ), true); static::$is_initialized = true; diff --git a/app/Grpc/User/UserProfileResponse.php b/app/Grpc/User/UserProfileResponse.php index 8834598..d574745 100644 --- a/app/Grpc/User/UserProfileResponse.php +++ b/app/Grpc/User/UserProfileResponse.php @@ -55,7 +55,7 @@ class UserProfileResponse extends \Google\Protobuf\Internal\Message * @type int|string $id * @type string $name * @type string $email - * @type string $photo_url + * @type string $photo_path * @type string $role * @type \App\Grpc\User\CartItem[]|\Google\Protobuf\Internal\RepeatedField $cart_items * @type bool $success @@ -134,7 +134,7 @@ public function setEmail($var) } /** - * Generated from protobuf field string photo_url = 4; + * Generated from protobuf field string photo_path = 4; * @return string */ public function getPhotoPath() @@ -143,7 +143,7 @@ public function getPhotoPath() } /** - * Generated from protobuf field string photo_url = 4; + * Generated from protobuf field string photo_path = 4; * @param string $var * @return $this */ diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php index 0326533..a36c0a6 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -139,6 +139,12 @@ public function GetUserProfile(array $all): array $metadata = $this->getAuthorizationMetadata(); try { $response = $this->client->GetUserProfile($request, $metadata)->wait()[0]; + if (is_null($response)) + return [ + 'success' => false, + 'message' => $response->getMessage() ?: 'User not found', + 'data' => null + ]; return [ 'success' => $response->getSuccess(), 'message' => $response->getMessage(), From f164cb47172fb8606088d4b1e898c72d9fee2404 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 06:45:33 +0300 Subject: [PATCH 13/49] Refactor `UserGrpcService` to improve error handling, enhance `GetUserProfile` method, clean up commented code, and adjust Dockerfile for pending gRPC setup. --- Dockerfile | 24 +++++++-------- app/Grpc/Resources/UserResource.php | 2 ++ app/Http/Controllers/UserApiController.php | 36 ++++++++++++---------- app/Services/UserGrpcService.php | 4 +-- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/Dockerfile b/Dockerfile index cc51865..5d1e85d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,18 +45,18 @@ RUN pecl install swoole \ && pecl install protobuf \ && docker-php-ext-enable protobuf -# Install protoc and grpc_php_plugin if not available via apk -RUN if ! command -v protoc &> /dev/null; then \ - PROTOC_ZIP=protoc-21.12-linux-x86_64.zip && \ - curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v21.12/$PROTOC_ZIP && \ - unzip -o $PROTOC_ZIP -d /usr/local bin/protoc && \ - unzip -o $PROTOC_ZIP -d /usr/local 'include/*' && \ - rm -f $PROTOC_ZIP; \ - fi - -# Verify installations -RUN protoc --version && \ - php -m | grep -E "(grpc|protobuf)" +## Install protoc and grpc_php_plugin if not available via apk +#RUN if ! command -v protoc &> /dev/null; then \ +# PROTOC_ZIP=protoc-21.12-linux-x86_64.zip && \ +# curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v21.12/$PROTOC_ZIP && \ +# unzip -o $PROTOC_ZIP -d /usr/local bin/protoc && \ +# unzip -o $PROTOC_ZIP -d /usr/local 'include/*' && \ +# rm -f $PROTOC_ZIP; \ +# fi + +## Verify installations +#RUN protoc --version && \ +# php -m | grep -E "(grpc|protobuf)" FROM php:8.4.4-fpm-alpine AS production diff --git a/app/Grpc/Resources/UserResource.php b/app/Grpc/Resources/UserResource.php index 7eab0d4..b77cde8 100644 --- a/app/Grpc/Resources/UserResource.php +++ b/app/Grpc/Resources/UserResource.php @@ -20,6 +20,8 @@ public function toArray(Request $request): array $user = $this->resource; if (method_exists($this->resource, 'getToken')) $token = $this->resource->getToken(); + + return array_filter([ 'id' => $user?->getId(), 'name' => $user?->getName(), diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 7dbda61..bd9c83c 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\External_Apis\Services\UserService; +use App\Grpc\User\GetUserRequest; use App\Services\UserGrpcService; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Request; @@ -12,7 +13,8 @@ class UserApiController extends BaseController { public function __construct( - private readonly UserGrpcService $userGrpcService + private readonly UserGrpcService $userGrpcService, + private readonly UserService $userService ) { @@ -89,26 +91,26 @@ 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->userGrpcService->GetUserProfile($request->all()); - $success = $res['success']; - $message = $res['message']; - $data = $res['data']; - return $this->response($message, $data, Response::HTTP_OK, error: !$success); + $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->userGrpcService->GetUserProfile($request->all()); +// $success = $res['success']; +// $message = $res['message']; +// $data = $res['data']; +// return $this->response($message, $data, Response::HTTP_OK, error: !$success); +// } } diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php index a36c0a6..09f8ad3 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -138,11 +138,11 @@ public function GetUserProfile(array $all): array $request = new UserProfileRequest(); $metadata = $this->getAuthorizationMetadata(); try { - $response = $this->client->GetUserProfile($request, $metadata)->wait()[0]; + list($response, $status) = $this->client->GetUserProfile($request, $metadata)->wait()[0]; if (is_null($response)) return [ 'success' => false, - 'message' => $response->getMessage() ?: 'User not found', + 'message' => $status->getMessage() ?: 'User not found', 'data' => null ]; return [ From e329a0e25ba548bac4642b691dc6663550ec6dd7 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 06:45:41 +0300 Subject: [PATCH 14/49] Introduce `GatewayService` with protobuf definitions and generated PHP classes Added `GatewayService` protobuf definitions in `apigw.proto`. Generated corresponding PHP classes including `FlexibleRequest`, `FlexibleResponse`, `BytesRequest`, `BytesResponse`, and `ErrorInfo`. Implemented `Apigw` initializer for descriptor pool setup. --- app/Grpc/BytesRequest.php | 202 +++++++++++++ app/Grpc/BytesResponse.php | 247 ++++++++++++++++ app/Grpc/ErrorInfo.php | 141 +++++++++ app/Grpc/FlexibleRequest.php | 203 +++++++++++++ app/Grpc/FlexibleResponse.php | 271 ++++++++++++++++++ app/Grpc/Gateway/Apigw.php | 26 ++ app/Grpc/GatewayServiceClient.php | 62 ++++ app/Grpc/HealthCheckRequest.php | 58 ++++ app/Grpc/HealthCheckResponse.php | 58 ++++ .../HealthCheckResponse/ServingStatus.php | 56 ++++ .../HealthCheckResponse_ServingStatus.php | 16 ++ app/Grpc/HealthServiceClient.php | 34 +++ app/Grpc/Services/GatewayService.php | 198 +++++++++++++ app/Grpc/Services/GrpcService.php | 44 +++ proto/apigw.proto | 110 +++++++ 15 files changed, 1726 insertions(+) create mode 100644 app/Grpc/BytesRequest.php create mode 100644 app/Grpc/BytesResponse.php create mode 100644 app/Grpc/ErrorInfo.php create mode 100644 app/Grpc/FlexibleRequest.php create mode 100644 app/Grpc/FlexibleResponse.php create mode 100644 app/Grpc/Gateway/Apigw.php create mode 100644 app/Grpc/GatewayServiceClient.php create mode 100644 app/Grpc/HealthCheckRequest.php create mode 100644 app/Grpc/HealthCheckResponse.php create mode 100644 app/Grpc/HealthCheckResponse/ServingStatus.php create mode 100644 app/Grpc/HealthCheckResponse_ServingStatus.php create mode 100644 app/Grpc/HealthServiceClient.php create mode 100644 app/Grpc/Services/GatewayService.php create mode 100644 app/Grpc/Services/GrpcService.php create mode 100644 proto/apigw.proto 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/ErrorInfo.php b/app/Grpc/ErrorInfo.php new file mode 100644 index 0000000..3b840b0 --- /dev/null +++ b/app/Grpc/ErrorInfo.php @@ -0,0 +1,141 @@ +gateway.ErrorInfo + */ +class ErrorInfo extends \Google\Protobuf\Internal\Message +{ + /** + * Generated from protobuf field string message = 1; + */ + protected $message = ''; + /** + * Generated from protobuf field string code = 2; + */ + protected $code = ''; + /** + * Generated from protobuf field repeated string details = 3; + */ + private $details; + /** + * Generated from protobuf field map metadata = 4; + */ + private $metadata; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $message + * @type string $code + * @type string[]|\Google\Protobuf\Internal\RepeatedField $details + * @type array|\Google\Protobuf\Internal\MapField $metadata + * } + */ + public function __construct($data = NULL) { + \App\Grpc\Gateway\Apigw::initOnce(); + parent::__construct($data); + } + + /** + * Generated from protobuf field string message = 1; + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Generated from protobuf field string message = 1; + * @param string $var + * @return $this + */ + public function setMessage($var) + { + GPBUtil::checkString($var, True); + $this->message = $var; + + return $this; + } + + /** + * Generated from protobuf field string code = 2; + * @return string + */ + public function getCode() + { + return $this->code; + } + + /** + * Generated from protobuf field string code = 2; + * @param string $var + * @return $this + */ + public function setCode($var) + { + GPBUtil::checkString($var, True); + $this->code = $var; + + return $this; + } + + /** + * Generated from protobuf field repeated string details = 3; + * @return \Google\Protobuf\Internal\RepeatedField + */ + public function getDetails() + { + return $this->details; + } + + /** + * Generated from protobuf field repeated string details = 3; + * @param string[]|\Google\Protobuf\Internal\RepeatedField $var + * @return $this + */ + public function setDetails($var) + { + $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING); + $this->details = $arr; + + return $this; + } + + /** + * Generated from protobuf field map metadata = 4; + * @return \Google\Protobuf\Internal\MapField + */ + public function getMetadata() + { + return $this->metadata; + } + + /** + * Generated from protobuf field map metadata = 4; + * @param array|\Google\Protobuf\Internal\MapField $var + * @return $this + */ + public function setMetadata($var) + { + $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING); + $this->metadata = $arr; + + return $this; + } + +} + diff --git a/app/Grpc/FlexibleRequest.php b/app/Grpc/FlexibleRequest.php new file mode 100644 index 0000000..f448e55 --- /dev/null +++ b/app/Grpc/FlexibleRequest.php @@ -0,0 +1,203 @@ +gateway.FlexibleRequest + */ +class FlexibleRequest extends \Google\Protobuf\Internal\Message +{ + /** + * Service identifier (user-service, product-service, etc.) + * + * Generated from protobuf field string service = 1; + */ + protected $service = ''; + /** + * Method/endpoint identifier + * + * Generated from protobuf field string method = 2; + */ + protected $method = ''; + /** + * Generic payload - can be any protobuf message + * + * Generated from protobuf field .google.protobuf.Any payload = 3; + */ + protected $payload = null; + /** + * Request metadata + * + * Generated from protobuf field map headers = 4; + */ + private $headers; + /** + * Optional request ID for tracing + * + * Generated from protobuf field string request_id = 5; + */ + protected $request_id = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type string $service + * Service identifier (user-service, product-service, etc.) + * @type string $method + * Method/endpoint identifier + * @type \Google\Protobuf\Any $payload + * Generic payload - can be any protobuf message + * @type array|\Google\Protobuf\Internal\MapField $headers + * Request metadata + * @type string $request_id + * Optional request ID for tracing + * } + */ + public function __construct($data = NULL) { + \App\Grpc\Gateway\Apigw::initOnce(); + parent::__construct($data); + } + + /** + * Service identifier (user-service, product-service, etc.) + * + * Generated from protobuf field string service = 1; + * @return string + */ + public function getService() + { + return $this->service; + } + + /** + * Service identifier (user-service, product-service, etc.) + * + * Generated from protobuf field string service = 1; + * @param string $var + * @return $this + */ + public function setService($var) + { + GPBUtil::checkString($var, True); + $this->service = $var; + + return $this; + } + + /** + * Method/endpoint identifier + * + * Generated from protobuf field string method = 2; + * @return string + */ + public function getMethod() + { + return $this->method; + } + + /** + * Method/endpoint identifier + * + * Generated from protobuf field string method = 2; + * @param string $var + * @return $this + */ + public function setMethod($var) + { + GPBUtil::checkString($var, True); + $this->method = $var; + + return $this; + } + + /** + * Generic payload - can be any protobuf message + * + * Generated from protobuf field .google.protobuf.Any payload = 3; + * @return \Google\Protobuf\Any + */ + public function getPayload() + { + return $this->payload; + } + + /** + * Generic payload - can be any protobuf message + * + * Generated from protobuf field .google.protobuf.Any payload = 3; + * @param \Google\Protobuf\Any $var + * @return $this + */ + public function setPayload($var) + { + GPBUtil::checkMessage($var, \Google\Protobuf\Any::class); + $this->payload = $var; + + return $this; + } + + /** + * Request metadata + * + * Generated from protobuf field map headers = 4; + * @return \Google\Protobuf\Internal\MapField + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Request metadata + * + * Generated from protobuf field map headers = 4; + * @param array|\Google\Protobuf\Internal\MapField $var + * @return $this + */ + public function setHeaders($var) + { + $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING); + $this->headers = $arr; + + return $this; + } + + /** + * Optional request ID for tracing + * + * Generated from protobuf field string request_id = 5; + * @return string + */ + public function getRequestId() + { + return $this->request_id; + } + + /** + * Optional request ID for tracing + * + * Generated from protobuf field string request_id = 5; + * @param string $var + * @return $this + */ + public function setRequestId($var) + { + GPBUtil::checkString($var, True); + $this->request_id = $var; + + return $this; + } + +} + diff --git a/app/Grpc/FlexibleResponse.php b/app/Grpc/FlexibleResponse.php new file mode 100644 index 0000000..3769c41 --- /dev/null +++ b/app/Grpc/FlexibleResponse.php @@ -0,0 +1,271 @@ +gateway.FlexibleResponse + */ +class FlexibleResponse extends \Google\Protobuf\Internal\Message +{ + /** + * Success indicator + * + * Generated from protobuf field bool success = 1; + */ + protected $success = false; + /** + * HTTP-like status code + * + * Generated from protobuf field int32 status_code = 2; + */ + protected $status_code = 0; + /** + * Generic response data - can be any protobuf message or raw data + * + * Generated from protobuf field .google.protobuf.Any data = 3; + */ + protected $data = null; + /** + * Error information (if any) + * + * Generated from protobuf field .gateway.ErrorInfo error = 4; + */ + protected $error = null; + /** + * Response metadata + * + * Generated from protobuf field map headers = 5; + */ + private $headers; + /** + * Request ID for tracing + * + * Generated from protobuf field string request_id = 6; + */ + protected $request_id = ''; + /** + * Response timestamp + * + * Generated from protobuf field .google.protobuf.Timestamp timestamp = 7; + */ + protected $timestamp = null; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type bool $success + * Success indicator + * @type int $status_code + * HTTP-like status code + * @type \Google\Protobuf\Any $data + * Generic response data - can be any protobuf message or raw data + * @type \App\Grpc\ErrorInfo $error + * Error information (if any) + * @type array|\Google\Protobuf\Internal\MapField $headers + * Response metadata + * @type string $request_id + * Request ID for tracing + * @type \Google\Protobuf\Timestamp $timestamp + * Response timestamp + * } + */ + public function __construct($data = NULL) { + \App\Grpc\Gateway\Apigw::initOnce(); + parent::__construct($data); + } + + /** + * Success indicator + * + * Generated from protobuf field bool success = 1; + * @return bool + */ + public function getSuccess() + { + return $this->success; + } + + /** + * Success indicator + * + * Generated from protobuf field bool success = 1; + * @param bool $var + * @return $this + */ + public function setSuccess($var) + { + GPBUtil::checkBool($var); + $this->success = $var; + + return $this; + } + + /** + * HTTP-like status code + * + * Generated from protobuf field int32 status_code = 2; + * @return int + */ + public function getStatusCode() + { + return $this->status_code; + } + + /** + * HTTP-like status code + * + * Generated from protobuf field int32 status_code = 2; + * @param int $var + * @return $this + */ + public function setStatusCode($var) + { + GPBUtil::checkInt32($var); + $this->status_code = $var; + + return $this; + } + + /** + * Generic response data - can be any protobuf message or raw data + * + * Generated from protobuf field .google.protobuf.Any data = 3; + * @return \Google\Protobuf\Any + */ + public function getData() + { + return $this->data; + } + + /** + * Generic response data - can be any protobuf message or raw data + * + * Generated from protobuf field .google.protobuf.Any data = 3; + * @param \Google\Protobuf\Any $var + * @return $this + */ + public function setData($var) + { + GPBUtil::checkMessage($var, \Google\Protobuf\Any::class); + $this->data = $var; + + return $this; + } + + /** + * Error information (if any) + * + * Generated from protobuf field .gateway.ErrorInfo error = 4; + * @return \App\Grpc\ErrorInfo + */ + public function getError() + { + return $this->error; + } + + /** + * Error information (if any) + * + * Generated from protobuf field .gateway.ErrorInfo error = 4; + * @param \App\Grpc\ErrorInfo $var + * @return $this + */ + public function setError($var) + { + GPBUtil::checkMessage($var, \App\Grpc\ErrorInfo::class); + $this->error = $var; + + return $this; + } + + /** + * Response metadata + * + * Generated from protobuf field map headers = 5; + * @return \Google\Protobuf\Internal\MapField + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Response metadata + * + * Generated from protobuf field map headers = 5; + * @param array|\Google\Protobuf\Internal\MapField $var + * @return $this + */ + public function setHeaders($var) + { + $arr = GPBUtil::checkMapField($var, \Google\Protobuf\Internal\GPBType::STRING, \Google\Protobuf\Internal\GPBType::STRING); + $this->headers = $arr; + + return $this; + } + + /** + * Request ID for tracing + * + * Generated from protobuf field string request_id = 6; + * @return string + */ + public function getRequestId() + { + return $this->request_id; + } + + /** + * Request ID for tracing + * + * Generated from protobuf field string request_id = 6; + * @param string $var + * @return $this + */ + public function setRequestId($var) + { + GPBUtil::checkString($var, True); + $this->request_id = $var; + + return $this; + } + + /** + * Response timestamp + * + * Generated from protobuf field .google.protobuf.Timestamp timestamp = 7; + * @return \Google\Protobuf\Timestamp + */ + public function getTimestamp() + { + return $this->timestamp; + } + + /** + * Response timestamp + * + * Generated from protobuf field .google.protobuf.Timestamp timestamp = 7; + * @param \Google\Protobuf\Timestamp $var + * @return $this + */ + public function setTimestamp($var) + { + GPBUtil::checkMessage($var, \Google\Protobuf\Timestamp::class); + $this->timestamp = $var; + + return $this; + } + +} + diff --git a/app/Grpc/Gateway/Apigw.php b/app/Grpc/Gateway/Apigw.php new file mode 100644 index 0000000..e9912cc --- /dev/null +++ b/app/Grpc/Gateway/Apigw.php @@ -0,0 +1,26 @@ +internalAddGeneratedFile(hex2bin( + "0afc0d0a0b61706967772e70726f746f1207676174657761791a1f676f6f676c652f70726f746f6275662f74696d657374616d702e70726f746f22d5010a0f466c657869626c6552657175657374120f0a0773657276696365180120012809120e0a066d6574686f6418022001280912250a077061796c6f616418032001280b32142e676f6f676c652e70726f746f6275662e416e7912360a076865616465727318042003280b32252e676174657761792e466c657869626c65526571756573742e48656164657273456e74727912120a0a726571756573745f69641805200128091a2e0a0c48656164657273456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122ab020a10466c657869626c65526573706f6e7365120f0a077375636365737318012001280812130a0b7374617475735f636f646518022001280512220a046461746118032001280b32142e676f6f676c652e70726f746f6275662e416e7912210a056572726f7218042001280b32122e676174657761792e4572726f72496e666f12370a076865616465727318052003280b32262e676174657761792e466c657869626c65526573706f6e73652e48656164657273456e74727912120a0a726571756573745f6964180620012809122d0a0974696d657374616d7018072001280b321a2e676f6f676c652e70726f746f6275662e54696d657374616d701a2e0a0c48656164657273456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122cf010a0c427974657352657175657374120f0a0773657276696365180120012809120e0a066d6574686f64180220012809120f0a077061796c6f616418032001280c12140a0c636f6e74656e745f7479706518042001280912330a076865616465727318052003280b32222e676174657761792e4279746573526571756573742e48656164657273456e74727912120a0a726571756573745f69641806200128091a2e0a0c48656164657273456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122a5020a0d4279746573526573706f6e7365120f0a077375636365737318012001280812130a0b7374617475735f636f6465180220012805120c0a046461746118032001280c12140a0c636f6e74656e745f7479706518042001280912210a056572726f7218052001280b32122e676174657761792e4572726f72496e666f12340a076865616465727318062003280b32232e676174657761792e4279746573526573706f6e73652e48656164657273456e74727912120a0a726571756573745f6964180720012809122d0a0974696d657374616d7018082001280b321a2e676f6f676c652e70726f746f6275662e54696d657374616d701a2e0a0c48656164657273456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122a0010a094572726f72496e666f120f0a076d657373616765180120012809120c0a04636f6465180220012809120f0a0764657461696c7318032003280912320a086d6574616461746118042003280b32202e676174657761792e4572726f72496e666f2e4d65746164617461456e7472791a2f0a0d4d65746164617461456e747279120b0a036b6579180120012809120d0a0576616c75651802200128093a02380122250a124865616c7468436865636b52657175657374120f0a0773657276696365180120012809228d010a134865616c7468436865636b526573706f6e7365123a0a0673746174757318012001280e322a2e676174657761792e4865616c7468436865636b526573706f6e73652e53657276696e67537461747573223a0a0d53657276696e67537461747573120b0a07554e4b4e4f574e1000120b0a0753455256494e471001120f0a0b4e4f545f53455256494e47100232eb010a0e476174657761795365727669636512440a0d48616e646c655265717565737412182e676174657761792e466c657869626c65526571756573741a192e676174657761792e466c657869626c65526573706f6e736512430a1248616e646c6542797465735265717565737412152e676174657761792e4279746573526571756573741a162e676174657761792e4279746573526573706f6e7365124e0a1348616e646c6553747265616d5265717565737412182e676174657761792e466c657869626c65526571756573741a192e676174657761792e466c657869626c65526573706f6e73652801300132530a0d4865616c74685365727669636512420a05436865636b121b2e676174657761792e4865616c7468436865636b526571756573741a1c2e676174657761792e4865616c7468436865636b526573706f6e7365421eca02084170705c47727063e202104170705c477270635c47617465776179620670726f746f33" + ), true); + + static::$is_initialized = true; + } +} + diff --git a/app/Grpc/GatewayServiceClient.php b/app/Grpc/GatewayServiceClient.php new file mode 100644 index 0000000..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/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php new file mode 100644 index 0000000..72ecba6 --- /dev/null +++ b/app/Grpc/Services/GatewayService.php @@ -0,0 +1,198 @@ + 'http://user-service:8080', + 'product-service' => 'http://product-service:8080', + 'order-service' => 'http://order-service:8080', + ]; + + /** + * Handle flexible protobuf requests + */ + public function HandleRequest(FlexibleRequest $in): FlexibleResponse + { + $response = new FlexibleResponse(); + $response->setRequestId($in->getRequestId()); + $response->setTimestamp($this->getCurrentTimestamp()); + + try { + // Route to the appropriate service + $serviceUrl = $this->getServiceUrl($in->getService()); + if (!$serviceUrl) { + return $this->createErrorResponse($response, 404, 'SERVICE_NOT_FOUND', 'Service not found: ' . $in->getService()); + } + + // Extract payload as raw bytes + $payload = $in->getPayload()->getValue(); + + // Forward request to target service + $httpResponse = Http::withHeaders($in->getHeaders()->getIterator()->getArrayCopy()) + ->timeout(30) + ->post($serviceUrl . '/' . $in->getMethod(), $payload); + + if ($httpResponse->successful()) { + // Create successful response with Any payload + $responseData = new Any(); + $responseData->setValue($httpResponse->body()); + + $response->setSuccess(true); + $response->setStatusCode($httpResponse->status()); + $response->setData($responseData); + + // Forward response headers + foreach ($httpResponse->headers() as $key => $value) { + $response->getHeaders()[$key] = is_array($value) ? implode(', ', $value) : $value; + } + + } else { + return $this->createErrorResponse( + $response, + $httpResponse->status(), + 'UPSTREAM_ERROR', + 'Upstream service error: ' . $httpResponse->body() + ); + } + + } catch (\Exception $e) { + Log::error('Gateway 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 { + $serviceUrl = $this->getServiceUrl($in->getService()); + if (!$serviceUrl) { + return $this->createBytesErrorResponse($response, 404, 'SERVICE_NOT_FOUND', 'Service not found'); + } + + // Forward raw bytes with content type + $httpResponse = Http::withHeaders(array_merge( + $in->getHeaders()->getIterator()->getArrayCopy(), + ['Content-Type' => $in->getContentType()] + )) + ->timeout(30) + ->withBody($in->getPayload(), $in->getContentType()) + ->post($serviceUrl . '/' . $in->getMethod()); + + if ($httpResponse->successful()) { + $response->setSuccess(true); + $response->setStatusCode($httpResponse->status()); + $response->setData($httpResponse->body()); + $response->setContentType($httpResponse->header('Content-Type', 'application/octet-stream')); + + // Forward headers + foreach ($httpResponse->headers() as $key => $value) { + $response->getHeaders()[$key] = is_array($value) ? implode(', ', $value) : $value; + } + + } else { + return $this->createBytesErrorResponse($response, $httpResponse->status(), 'UPSTREAM_ERROR', $httpResponse->body()); + } + + } catch (\Exception $e) { + Log::error('Gateway 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 service URL from configuration + */ + private function getServiceUrl(string $service): ?string + { + return $this->serviceRoutes[$service] ?? config("services.grpc.{$service}.url"); + } + + /** + * Create error response for FlexibleResponse + */ + private function createErrorResponse(FlexibleResponse $response, int $statusCode, string $code, string $message): FlexibleResponse + { + $error = new ErrorInfo(); + $error->setCode($code); + $error->setMessage($message); + + $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; + } +} + +// Example usage in a Laravel controller for HTTP-to-gRPC conversion diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php new file mode 100644 index 0000000..0d43a86 --- /dev/null +++ b/app/Grpc/Services/GrpcService.php @@ -0,0 +1,44 @@ +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); + + // Forward headers + foreach ($request->headers->all() as $key => $value) { + $grpcRequest->getHeaders()[$key] = is_array($value) ? implode(', ', $value) : $value; + } + + // Call gRPC service + $gatewayService = new GatewayService(); + $grpcResponse = $gatewayService->HandleRequest($grpcRequest); + + // Convert back to HTTP response + if ($grpcResponse->getSuccess()) { + return response($grpcResponse->getData()->getValue(), $grpcResponse->getStatusCode()) + ->withHeaders($grpcResponse->getHeaders()->getIterator()->getArrayCopy()); + } else { + return response()->json([ + 'error' => $grpcResponse->getError()->getCode(), + 'message' => $grpcResponse->getError()->getMessage() + ], $grpcResponse->getStatusCode()); + } + } +} 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; +} From 51960b8ffef370113b6e75f0472e801077949209 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 08:46:38 +0300 Subject: [PATCH 15/49] Refactor gRPC integration to replace HTTP-based logic with `GrpcService` in controllers, enhance `GatewayService` for optimized gRPC client handling, and improve error reporting for downstream services. --- app/Grpc/Services/GatewayService.php | 164 +++++++++++++-------- app/Grpc/Services/GrpcService.php | 2 +- app/Http/Controllers/AuthController.php | 9 +- app/Http/Controllers/UserApiController.php | 37 ++--- 4 files changed, 131 insertions(+), 81 deletions(-) diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php index 72ecba6..dbd61f8 100644 --- a/app/Grpc/Services/GatewayService.php +++ b/app/Grpc/Services/GatewayService.php @@ -2,24 +2,30 @@ namespace App\Grpc\Services; -use App\Grpc\FlexibleRequest; -use App\Grpc\FlexibleResponse; use App\Grpc\BytesRequest; use App\Grpc\BytesResponse; use App\Grpc\ErrorInfo; -use Google\Protobuf\Any; +use App\Grpc\FlexibleRequest; +use App\Grpc\FlexibleResponse; +use App\Grpc\GatewayServiceClient; +use DateTime; +use Exception; use Google\Protobuf\Timestamp; -use Illuminate\Support\Facades\Http; +use Grpc\ChannelCredentials; use Illuminate\Support\Facades\Log; +use Iterator; +use const Grpc\STATUS_OK; class GatewayService { - private array $serviceRoutes = [ - 'user-service' => 'http://user-service:8080', - 'product-service' => 'http://product-service:8080', - 'order-service' => 'http://order-service:8080', + private array $grpcServices = [ + 'user' => 'user:50051', + 'product' => 'product:50051', + 'order' => 'order:50051', ]; + private array $grpcClients = []; + /** * Handle flexible protobuf requests */ @@ -30,48 +36,49 @@ public function HandleRequest(FlexibleRequest $in): FlexibleResponse $response->setTimestamp($this->getCurrentTimestamp()); try { - // Route to the appropriate service - $serviceUrl = $this->getServiceUrl($in->getService()); - if (!$serviceUrl) { + // 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()); } - // Extract payload as raw bytes - $payload = $in->getPayload()->getValue(); - - // Forward request to target service - $httpResponse = Http::withHeaders($in->getHeaders()->getIterator()->getArrayCopy()) - ->timeout(30) - ->post($serviceUrl . '/' . $in->getMethod(), $payload); + // Forward the exact same FlexibleRequest to downstream service + // The downstream service implements the same flexible proto + list($downstreamResponse, $status) = $grpcClient->HandleRequest($in)->wait(); - if ($httpResponse->successful()) { - // Create successful response with Any payload - $responseData = new Any(); - $responseData->setValue($httpResponse->body()); + if ($status->code === STATUS_OK) { + // Forward the response as-is + $response->setSuccess($downstreamResponse->getSuccess()); + $response->setStatusCode($downstreamResponse->getStatusCode()); + $response->setData($downstreamResponse->getData()); - $response->setSuccess(true); - $response->setStatusCode($httpResponse->status()); - $response->setData($responseData); + // Forward headers + foreach ($downstreamResponse->getHeaders() as $key => $value) { + $response->getHeaders()[$key] = $value; + } - // Forward response headers - foreach ($httpResponse->headers() as $key => $value) { - $response->getHeaders()[$key] = is_array($value) ? implode(', ', $value) : $value; + // Forward error if exists + if ($downstreamResponse->hasError()) { + $response->setError($downstreamResponse->getError()); } } else { return $this->createErrorResponse( $response, - $httpResponse->status(), - 'UPSTREAM_ERROR', - 'Upstream service error: ' . $httpResponse->body() + 500, + 'GRPC_ERROR', + 'gRPC call failed: ' . $status->details ); } - } catch (\Exception $e) { - Log::error('Gateway request failed', [ + } catch (Exception $e) { + Log::error('Gateway gRPC request failed', [ 'service' => $in->getService(), 'method' => $in->getMethod(), - 'error' => $e->getMessage() + 'error' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTrace() ]); return $this->createErrorResponse($response, 500, 'INTERNAL_ERROR', $e->getMessage()); @@ -90,37 +97,37 @@ public function HandleBytesRequest(BytesRequest $in): BytesResponse $response->setTimestamp($this->getCurrentTimestamp()); try { - $serviceUrl = $this->getServiceUrl($in->getService()); - if (!$serviceUrl) { + $grpcClient = $this->getGrpcClient($in->getService()); + if (!$grpcClient) { return $this->createBytesErrorResponse($response, 404, 'SERVICE_NOT_FOUND', 'Service not found'); } - // Forward raw bytes with content type - $httpResponse = Http::withHeaders(array_merge( - $in->getHeaders()->getIterator()->getArrayCopy(), - ['Content-Type' => $in->getContentType()] - )) - ->timeout(30) - ->withBody($in->getPayload(), $in->getContentType()) - ->post($serviceUrl . '/' . $in->getMethod()); - - if ($httpResponse->successful()) { - $response->setSuccess(true); - $response->setStatusCode($httpResponse->status()); - $response->setData($httpResponse->body()); - $response->setContentType($httpResponse->header('Content-Type', 'application/octet-stream')); + // 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 ($httpResponse->headers() as $key => $value) { - $response->getHeaders()[$key] = is_array($value) ? implode(', ', $value) : $value; + 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, $httpResponse->status(), 'UPSTREAM_ERROR', $httpResponse->body()); + return $this->createBytesErrorResponse($response, 500, 'GRPC_ERROR', $status->details); } - } catch (\Exception $e) { - Log::error('Gateway bytes request failed', [ + } catch (Exception $e) { + Log::error('Gateway gRPC bytes request failed', [ 'service' => $in->getService(), 'method' => $in->getMethod(), 'error' => $e->getMessage() @@ -135,7 +142,7 @@ public function HandleBytesRequest(BytesRequest $in): BytesResponse /** * Handle streaming requests */ - public function HandleStreamRequest(\Iterator $in): \Iterator + public function HandleStreamRequest(Iterator $in): Iterator { foreach ($in as $request) { yield $this->HandleRequest($request); @@ -143,11 +150,46 @@ public function HandleStreamRequest(\Iterator $in): \Iterator } /** - * Get service URL from configuration + * Get gRPC client for service */ - private function getServiceUrl(string $service): ?string + private function getGrpcClient(string $service): ?GatewayServiceClient { - return $this->serviceRoutes[$service] ?? config("services.grpc.{$service}.url"); + if (!isset($this->grpcServices[$service])) { + return null; + } + + // Reuse existing client or create new one + if (!isset($this->grpcClients[$service])) { + $serviceAddress = $this->grpcServices[$service]; + + $this->grpcClients[$service] = new GatewayServiceClient( + $serviceAddress, + [ + 'credentials' => ChannelCredentials::createInsecure(), + 'timeout' => 30000000, // 30 seconds in microseconds + 'grpc.keepalive_time_ms' => 30000, + 'grpc.keepalive_timeout_ms' => 5000, + 'grpc.keepalive_permit_without_calls' => true, + 'grpc.http2.max_pings_without_data' => 0, + 'grpc.http2.min_time_between_pings_ms' => 10000, + 'grpc.http2.min_ping_interval_without_data_ms' => 300000 + ] + ); + } + + return $this->grpcClients[$service]; + } + + /** + * Close all gRPC connections (cleanup) + */ + public function __destruct() + { + foreach ($this->grpcClients as $client) { + if ($client) { + $client->close(); + } + } } /** @@ -190,9 +232,7 @@ private function createBytesErrorResponse(BytesResponse $response, int $statusCo private function getCurrentTimestamp(): Timestamp { $timestamp = new Timestamp(); - $timestamp->fromDateTime(new \DateTime()); + $timestamp->fromDateTime(new DateTime()); return $timestamp; } } - -// Example usage in a Laravel controller for HTTP-to-gRPC conversion diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index 0d43a86..b16e98a 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -4,7 +4,7 @@ use App\Grpc\FlexibleRequest; use Google\Protobuf\Any; -use Illuminate\Http\Client\Request; +use Illuminate\Http\Request; class GrpcService { diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index e290d3c..eb8d08d 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\External_Apis\Services\UserService; +use App\Grpc\Services\GrpcService; use App\Services\UserGrpcService; use Exception; use Illuminate\Http\JsonResponse; @@ -11,8 +12,10 @@ class AuthController extends BaseController { + public function __construct( private readonly UserGrpcService $userGrpcService, + private readonly GrpcService $grpcService, private readonly UserService $service ) @@ -49,7 +52,11 @@ function login(Request $request): JsonResponse function signup(Request $request): JsonResponse { try { - $res = $this->userGrpcService->createUser($request->all()); +// $res = $this->userGrpcService->createUser($request->all()); + $res = $this->grpcService->proxyRequest($request, + 'user', + 'create' + ); $success = $res['success']; $message = $res['message']; $user = $res['data']; diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index bd9c83c..d372206 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers; use App\External_Apis\Services\UserService; -use App\Grpc\User\GetUserRequest; +use App\Grpc\Services\GrpcService; use App\Services\UserGrpcService; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Request; @@ -14,6 +14,7 @@ class UserApiController extends BaseController { public function __construct( private readonly UserGrpcService $userGrpcService, + private readonly GrpcService $grpcService, private readonly UserService $userService ) @@ -91,26 +92,28 @@ 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->userGrpcService->GetUserProfile($request->all()); -// $success = $res['success']; -// $message = $res['message']; -// $data = $res['data']; -// return $this->response($message, $data, Response::HTTP_OK, error: !$success); +// $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); // } + /** + */ + public function userProfile(Request $request) + { + $res = $this->grpcService->proxyRequest($request, + 'user', + 'GetUserProfile' + ); + $success = $res['success']; + $message = $res['message']; + $data = $res['data']; + return $this->response($message, $data, Response::HTTP_OK, error: !$success); + } } From 1bc03fbfa7857e92bdad2f3e8b5a29d075a4a768 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 11:54:54 +0300 Subject: [PATCH 16/49] Refactor `AuthController` to utilize `GrpcService` for login and signup operations, simplify gRPC client creation in `GatewayService`, and enhance error handling and response formatting --- app/Grpc/Services/GatewayService.php | 46 ++++++++++++------------- app/Grpc/Services/GrpcService.php | 8 ++--- app/Http/Controllers/AuthController.php | 25 ++++++-------- 3 files changed, 36 insertions(+), 43 deletions(-) diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php index dbd61f8..d6113a6 100644 --- a/app/Grpc/Services/GatewayService.php +++ b/app/Grpc/Services/GatewayService.php @@ -19,7 +19,7 @@ class GatewayService { private array $grpcServices = [ - 'user' => 'user:50051', + 'user' => 'user:50051', // Updated to match your service names 'product' => 'product:50051', 'order' => 'order:50051', ]; @@ -45,7 +45,6 @@ public function HandleRequest(FlexibleRequest $in): FlexibleResponse // Forward the exact same FlexibleRequest to downstream service // The downstream service implements the same flexible proto list($downstreamResponse, $status) = $grpcClient->HandleRequest($in)->wait(); - if ($status->code === STATUS_OK) { // Forward the response as-is $response->setSuccess($downstreamResponse->getSuccess()); @@ -57,28 +56,21 @@ public function HandleRequest(FlexibleRequest $in): FlexibleResponse $response->getHeaders()[$key] = $value; } - // Forward error if exists - if ($downstreamResponse->hasError()) { - $response->setError($downstreamResponse->getError()); - } - } else { return $this->createErrorResponse( $response, 500, 'GRPC_ERROR', - 'gRPC call failed: ' . $status->details + $downstreamResponse->getError() ? $downstreamResponse->getError()->getMessage() : 'Unknown error' ); } } catch (Exception $e) { + Log::error('Gateway gRPC request failed', [ 'service' => $in->getService(), 'method' => $in->getMethod(), - 'error' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'trace' => $e->getTrace() + 'error' => $e->getMessage() ]); return $this->createErrorResponse($response, 500, 'INTERNAL_ERROR', $e->getMessage()); @@ -162,19 +154,27 @@ private function getGrpcClient(string $service): ?GatewayServiceClient if (!isset($this->grpcClients[$service])) { $serviceAddress = $this->grpcServices[$service]; - $this->grpcClients[$service] = new GatewayServiceClient( - $serviceAddress, - [ + try { + // Simplified options to avoid compatibility issues + $options = [ 'credentials' => ChannelCredentials::createInsecure(), 'timeout' => 30000000, // 30 seconds in microseconds - 'grpc.keepalive_time_ms' => 30000, - 'grpc.keepalive_timeout_ms' => 5000, - 'grpc.keepalive_permit_without_calls' => true, - 'grpc.http2.max_pings_without_data' => 0, - 'grpc.http2.min_time_between_pings_ms' => 10000, - 'grpc.http2.min_ping_interval_without_data_ms' => 300000 - ] - ); + ]; + + $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]; diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index b16e98a..596564d 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -8,7 +8,7 @@ class GrpcService { - public function proxyRequest(Request $request, string $service, string $method) + public function proxyRequest(Request $request, string $service, string $method): \Illuminate\Http\JsonResponse { // Convert HTTP request to gRPC $grpcRequest = new FlexibleRequest(); @@ -32,12 +32,10 @@ public function proxyRequest(Request $request, string $service, string $method) // Convert back to HTTP response if ($grpcResponse->getSuccess()) { - return response($grpcResponse->getData()->getValue(), $grpcResponse->getStatusCode()) - ->withHeaders($grpcResponse->getHeaders()->getIterator()->getArrayCopy()); + return response()->json(json_decode($grpcResponse->getData()->getValue()), $grpcResponse->getStatusCode()); } else { return response()->json([ - 'error' => $grpcResponse->getError()->getCode(), - 'message' => $grpcResponse->getError()->getMessage() + 'error' => json_decode($grpcResponse->getError()->getMessage()), ], $grpcResponse->getStatusCode()); } } diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index eb8d08d..9b8b465 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -15,7 +15,7 @@ class AuthController extends BaseController public function __construct( private readonly UserGrpcService $userGrpcService, - private readonly GrpcService $grpcService, + private readonly GrpcService $grpcService, private readonly UserService $service ) @@ -32,15 +32,15 @@ public function __construct( // return $this->response($exception->getMessage(), status: $exception->getCode(), error: true); // } // } - public - function login(Request $request): JsonResponse + public function login(Request $request): JsonResponse { try { - $res = $this->userGrpcService->authenticateUser($request->email, $request->password); - $success = $res['success']; - $message = $res['message']; - $data = $res['data']; - return $this->response($message, $data, Response::HTTP_OK, error: !$success); +// $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); } @@ -48,19 +48,14 @@ function login(Request $request): JsonResponse /** */ - public - function signup(Request $request): JsonResponse + public function signup(Request $request): Response { try { // $res = $this->userGrpcService->createUser($request->all()); - $res = $this->grpcService->proxyRequest($request, + return $this->grpcService->proxyRequest($request, 'user', 'create' ); - $success = $res['success']; - $message = $res['message']; - $user = $res['data']; - return $this->response($message, $user, Response::HTTP_CREATED, error: !$success); } catch (Exception $e) { return $this->response($e->getMessage(), status: $e->getCode() ?: Response::HTTP_BAD_REQUEST, error: true); } From 19f01900b86cf6f9813564176293f67dd1ea350b Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 12:24:23 +0300 Subject: [PATCH 17/49] Refactor `AuthController` to remove unused `UserService` dependency, update `GrpcService` error handling for consistency, and streamline `UserApiController` to proxy requests directly through `GrpcService`. --- app/Grpc/Services/GrpcService.php | 5 +++-- app/Http/Controllers/AuthController.php | 3 --- app/Http/Controllers/UserApiController.php | 10 +++++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index 596564d..3c8077c 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -32,10 +32,11 @@ public function proxyRequest(Request $request, string $service, string $method): // Convert back to HTTP response if ($grpcResponse->getSuccess()) { - return response()->json(json_decode($grpcResponse->getData()->getValue()), $grpcResponse->getStatusCode()); + return response()->json(['data' => json_decode($grpcResponse->getData()->getValue())], $grpcResponse->getStatusCode()); } else { + $error = $grpcResponse->getError()->getMessage(); return response()->json([ - 'error' => json_decode($grpcResponse->getError()->getMessage()), + 'error' => json_decode($error) ?? $error, ], $grpcResponse->getStatusCode()); } } diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 9b8b465..827bd03 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,7 +2,6 @@ namespace App\Http\Controllers; -use App\External_Apis\Services\UserService; use App\Grpc\Services\GrpcService; use App\Services\UserGrpcService; use Exception; @@ -16,8 +15,6 @@ class AuthController extends BaseController public function __construct( private readonly UserGrpcService $userGrpcService, private readonly GrpcService $grpcService, - private readonly UserService $service - ) { } diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index d372206..033506c 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -68,11 +68,11 @@ public function show($id) ['user_id' => 'required|integer'] ); - $res = $this->userGrpcService->getUser($id); - $success = $res['success']; - $message = $res['message']; - $data = $res['data']; - return $this->response($message, $data, Response::HTTP_OK, error: !$success); + request()->merge(['id' => $id]); + return $this->grpcService->proxyRequest(request(), + 'user', + 'get', + ); } From e3aa9b5f8c55f7df915bccb37e7eedf717686b06 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 14:27:22 +0300 Subject: [PATCH 18/49] Refactor `GrpcService` response formatting for JSON consistency and streamline `UserApiController` to directly proxy requests through `GrpcService`. --- app/Grpc/Services/GrpcService.php | 2 +- app/Http/Controllers/UserApiController.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index 3c8077c..6c1d67f 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -32,7 +32,7 @@ public function proxyRequest(Request $request, string $service, string $method): // Convert back to HTTP response if ($grpcResponse->getSuccess()) { - return response()->json(['data' => json_decode($grpcResponse->getData()->getValue())], $grpcResponse->getStatusCode()); + return response()->json(json_decode($grpcResponse->getData()->getValue()), $grpcResponse->getStatusCode()); } else { $error = $grpcResponse->getError()->getMessage(); return response()->json([ diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 033506c..c69cfb3 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -39,11 +39,11 @@ public function __construct( */ public function index(Request $request) { - $res = $this->userGrpcService->listUsers(); - $success = $res['success']; - $message = $res['message']; - $data = $res['data']; - return $this->response($message, $data, Response::HTTP_OK, error: !$success); +// $res = $this->userGrpcService->listUsers(); + return $this->grpcService->proxyRequest(request(), + 'user', + 'list', + ); } /** From 901d6cd16ce6f1298a2b304641fca7744cd21ca3 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 17:08:07 +0300 Subject: [PATCH 19/49] Refactor `GrpcService` and `GatewayService` for improved metadata handling, response consistency, and error reporting; streamline `UserApiController` by removing redundant logic. --- app/Grpc/Services/GatewayService.php | 69 +++++++++++++--------- app/Grpc/Services/GrpcService.php | 7 +-- app/Http/Controllers/UserApiController.php | 13 ++-- 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php index d6113a6..7c98ec3 100644 --- a/app/Grpc/Services/GatewayService.php +++ b/app/Grpc/Services/GatewayService.php @@ -35,46 +35,57 @@ public function HandleRequest(FlexibleRequest $in): 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 - list($downstreamResponse, $status) = $grpcClient->HandleRequest($in)->wait(); - if ($status->code === STATUS_OK) { - // Forward the response as-is - $response->setSuccess($downstreamResponse->getSuccess()); - $response->setStatusCode($downstreamResponse->getStatusCode()); - $response->setData($downstreamResponse->getData()); + // 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 headers - foreach ($downstreamResponse->getHeaders() as $key => $value) { - $response->getHeaders()[$key] = $value; - } + // 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 { + } 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' ); - } + } - } catch (Exception $e) { + // } catch (Exception $e) { - Log::error('Gateway gRPC request failed', [ - 'service' => $in->getService(), - 'method' => $in->getMethod(), - 'error' => $e->getMessage() - ]); + // 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 $this->createErrorResponse($response, 500, 'INTERNAL_ERROR', $e->getMessage()); + // } return $response; } diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index 6c1d67f..e510629 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -21,10 +21,6 @@ public function proxyRequest(Request $request, string $service, string $method): $payload->setValue(json_encode($request->all())); $grpcRequest->setPayload($payload); - // Forward headers - foreach ($request->headers->all() as $key => $value) { - $grpcRequest->getHeaders()[$key] = is_array($value) ? implode(', ', $value) : $value; - } // Call gRPC service $gatewayService = new GatewayService(); @@ -32,7 +28,8 @@ public function proxyRequest(Request $request, string $service, string $method): // Convert back to HTTP response if ($grpcResponse->getSuccess()) { - return response()->json(json_decode($grpcResponse->getData()->getValue()), $grpcResponse->getStatusCode()); + $data = json_decode($grpcResponse->getData()->getValue()) ?? $grpcResponse->getData()->getValue(); + return response()->json($data, $grpcResponse->getStatusCode()); } else { $error = $grpcResponse->getError()->getMessage(); return response()->json([ diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index c69cfb3..4e2e435 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -41,9 +41,9 @@ public function index(Request $request) { // $res = $this->userGrpcService->listUsers(); return $this->grpcService->proxyRequest(request(), - 'user', - 'list', - ); + 'user', + 'list', + ); } /** @@ -107,13 +107,10 @@ public function destroy(string $id) */ public function userProfile(Request $request) { - $res = $this->grpcService->proxyRequest($request, + return $this->grpcService->proxyRequest($request, 'user', 'GetUserProfile' ); - $success = $res['success']; - $message = $res['message']; - $data = $res['data']; - return $this->response($message, $data, Response::HTTP_OK, error: !$success); +// return $this->response($message, $data, Response::HTTP_OK, error: !$success); } } From 8f2d5ed738cda136566cbfa029f22152b6823629 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 21:54:26 +0300 Subject: [PATCH 20/49] Refactor `ShoppingCartController` to use `GrpcService` for `addToCart` operation, remove HTTP-based logic, and streamline `UserApiController` by eliminating `UserGrpcService` and `UserService` dependencies. --- .../Controllers/ShoppingCartController.php | 25 +++++++++++++------ app/Http/Controllers/UserApiController.php | 2 -- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/ShoppingCartController.php b/app/Http/Controllers/ShoppingCartController.php index 9c0a8be..32a26b2 100644 --- a/app/Http/Controllers/ShoppingCartController.php +++ b/app/Http/Controllers/ShoppingCartController.php @@ -3,13 +3,20 @@ namespace App\Http\Controllers; use App\External_Apis\Apis\UserApi; +use App\Grpc\Services\GrpcService; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; -use Symfony\Component\HttpFoundation\Response; class ShoppingCartController extends BaseController { + public function __construct( + private readonly GrpcService $grpcService, + + ) + { + + } /** * @throws ConnectionException @@ -18,18 +25,22 @@ public function index() { $res = http::withToken(request()->bearerToken())->get(UserApi::getBaseUrl() . 'api/carts'); return $this->response($res->json('message'), $res->json('data'), - otherData: ["total"=>$res->json('total')]); + 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()); + 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()); } /** diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 4e2e435..83cbdd1 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -13,9 +13,7 @@ class UserApiController extends BaseController { public function __construct( - private readonly UserGrpcService $userGrpcService, private readonly GrpcService $grpcService, - private readonly UserService $userService ) { From 84d1b9bfc603e021b064e409174c2cdf3568c2f9 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 22:34:52 +0300 Subject: [PATCH 21/49] Refactor `ShoppingCartController` to replace HTTP-based logic with `GrpcService` for `index`, `remove`, and `clear` operations, streamlining request handling and improving consistency. --- .../Controllers/ShoppingCartController.php | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/ShoppingCartController.php b/app/Http/Controllers/ShoppingCartController.php index 32a26b2..3691ea7 100644 --- a/app/Http/Controllers/ShoppingCartController.php +++ b/app/Http/Controllers/ShoppingCartController.php @@ -2,11 +2,9 @@ namespace App\Http\Controllers; -use App\External_Apis\Apis\UserApi; use App\Grpc\Services\GrpcService; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Http; class ShoppingCartController extends BaseController { @@ -19,13 +17,17 @@ public function __construct( } /** - * @throws ConnectionException */ - public function index() + public function index(Request $request) { - $res = http::withToken(request()->bearerToken())->get(UserApi::getBaseUrl() . 'api/carts'); - return $this->response($res->json('message'), $res->json('data'), - otherData: ["total" => $res->json('total')]); + return $this->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')]); } /** @@ -44,12 +46,17 @@ public function add(Request $request) } /** - * @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')); + \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')); } /** @@ -57,7 +64,12 @@ public function remove($item_type, $item_id) */ public function clear() { - $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts"); - return $this->response($res->json('message'), $res->json('data')); + 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')); } } From ea66519413c4762f3256fea414a400054d6d706e Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 7 Jun 2025 23:30:39 +0300 Subject: [PATCH 22/49] Refactor `AuthController` to proxy `logout` operation through `GrpcService`, removing redundant logic for improved consistency and simplicity. --- app/Http/Controllers/AuthController.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 827bd03..73aa019 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -64,11 +64,15 @@ public function signup(Request $request): Response function logout(Request $request) { try { - $res = $this->userGrpcService->logout($request->bearerToken()); - $success = $res['success']; - $message = $res['message']; - $data = $res['data']; - return $this->response($message, $data, Response::HTTP_NO_CONTENT, error: !$success); + 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); } From 988725a6ec211e3edee9065a6e12bff48e806ff6 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Mon, 9 Jun 2025 15:45:59 +0300 Subject: [PATCH 23/49] Refactor `OfferController` and `ProductController` to utilize `GrpcService` for request handling, remove redundant HTTP-based logic, streamline Laravel Octane setup with updated Swoole configuration, and adjust Docker and Supervisor integration for improved reliability. --- Dockerfile | 9 +- app/Http/Controllers/OfferController.php | 58 ++++++----- app/Http/Controllers/ProductController.php | 46 ++++++--- config/octane.php | 108 ++++++++------------- supervisord.conf | 5 +- 5 files changed, 117 insertions(+), 109 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5d1e85d..8e1a180 100644 --- a/Dockerfile +++ b/Dockerfile @@ -122,7 +122,7 @@ RUN composer install \ COPY . . # Copy supervisor configuration (if using supervisor) -COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY supervisord.conf /etc/supervisor/supervisord.conf # 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 \ @@ -151,8 +151,11 @@ php artisan migrate --force php artisan storage:link # Start the application -echo "Starting Laravel Octane with Swoole..." -exec php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000 +# echo "Starting Laravel Octane with Swoole..." +# exec php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000 +echo "Starting supervisor..." +mkdir -p /var/log/supervisor +exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf EOF RUN chmod +x /usr/local/bin/start.sh diff --git a/app/Http/Controllers/OfferController.php b/app/Http/Controllers/OfferController.php index 30298a0..4d453b0 100644 --- a/app/Http/Controllers/OfferController.php +++ b/app/Http/Controllers/OfferController.php @@ -3,6 +3,7 @@ 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; @@ -11,7 +12,9 @@ class OfferController extends BaseController implements HasMiddleware { - public function __construct(public OfferService $offerService) + public function __construct(public OfferService $offerService, + private readonly GrpcService $grpcService + ) { } @@ -20,20 +23,25 @@ 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); - } + 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 +50,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 +83,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/ProductController.php b/app/Http/Controllers/ProductController.php index 1796232..6c1b60c 100644 --- a/app/Http/Controllers/ProductController.php +++ b/app/Http/Controllers/ProductController.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; @@ -14,7 +15,8 @@ class ProductController extends BaseController implements HasMiddleware { public function __construct( - private readonly ProductService $productService + private readonly ProductService $productService, + private readonly GrpcService $grpcService ) { @@ -32,28 +34,40 @@ 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); + 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/config/octane.php b/config/octane.php index 671915a..ad71933 100644 --- a/config/octane.php +++ b/config/octane.php @@ -29,39 +29,49 @@ |-------------------------------------------------------------------------- | Octane Server |-------------------------------------------------------------------------- - | - | This value determines the default "server" that will be used by Octane - | when starting, restarting, or stopping your server via the CLI. You - | are free to change this to the supported server of your choosing. - | - | Supported: "roadrunner", "swoole", "frankenphp" - | */ - 'server' => env('OCTANE_SERVER', 'roadrunner'), + 'server' => env('OCTANE_SERVER', 'swoole'), /* |-------------------------------------------------------------------------- | 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 + | Swoole Configuration - MINIMAL SAFE SETTINGS + |-------------------------------------------------------------------------- + */ + + 'swoole' => [ + 'options' => [ + // Basic worker configuration - start conservative + 'worker_num' => env('SWOOLE_WORKER_NUM', 2), // Reduced from cpu_num + 'reactor_num' => env('SWOOLE_REACTOR_NUM', 2), // Reduced from cpu_num + 'max_request' => env('SWOOLE_MAX_REQUEST', 100), // Much lower to force frequent restarts + 'max_conn' => env('SWOOLE_MAX_CONN', 256), // Reduced from 1024 + + // Conservative buffer sizes + 'package_max_length' => env('SWOOLE_PACKAGE_MAX_LENGTH', 2 * 1024 * 1024), // 2MB instead of 10MB + 'buffer_output_size' => env('SWOOLE_BUFFER_OUTPUT_SIZE', 2 * 1024 * 1024), + 'socket_buffer_size' => env('SWOOLE_SOCKET_BUFFER_SIZE', 64 * 1024), + + // Disable coroutines temporarily to isolate issue + 'enable_coroutine' => false, + + // Basic TCP settings + 'open_tcp_nodelay' => true, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Octane Listeners - RESTORE ALL CRITICAL CLEANUP |-------------------------------------------------------------------------- - | - | 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' => [ @@ -73,7 +83,6 @@ RequestReceived::class => [ ...Octane::prepareApplicationForNextOperation(), ...Octane::prepareApplicationForNextRequest(), - // ], RequestHandled::class => [ @@ -81,12 +90,11 @@ ], RequestTerminated::class => [ - // FlushUploadedFiles::class, + FlushUploadedFiles::class, // Re-enabled ], TaskReceived::class => [ ...Octane::prepareApplicationForNextOperation(), - // ], TaskTerminated::class => [ @@ -95,7 +103,6 @@ TickReceived::class => [ ...Octane::prepareApplicationForNextOperation(), - // ], TickTerminated::class => [ @@ -105,8 +112,8 @@ OperationTerminated::class => [ FlushOnce::class, FlushTemporaryContainerInstances::class, - // DisconnectFromDatabases::class, - // CollectGarbage::class, + DisconnectFromDatabases::class, // RE-ENABLED - CRITICAL + CollectGarbage::class, // RE-ENABLED - CRITICAL ], WorkerErrorOccurred::class => [ @@ -123,15 +130,10 @@ |-------------------------------------------------------------------------- | 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(), + // Use minimal warming to avoid container issues ], 'flush' => [ @@ -140,47 +142,29 @@ /* |-------------------------------------------------------------------------- - | Octane Swoole Tables + | Octane Swoole Tables - MINIMAL |-------------------------------------------------------------------------- - | - | 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', - ], + // Remove example table that might be causing null key issues ], /* |-------------------------------------------------------------------------- - | Octane Swoole Cache Table + | Octane Swoole Cache Table - REDUCED |-------------------------------------------------------------------------- - | - | 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, + 'rows' => 100, // Reduced from 1000 + 'bytes' => 1000, // Reduced from 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' => [ @@ -197,28 +181,18 @@ /* |-------------------------------------------------------------------------- - | Garbage Collection Threshold + | Garbage Collection Threshold - LOWER FOR MORE FREQUENT CLEANUP |-------------------------------------------------------------------------- - | - | 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, + 'garbage' => 25, // Reduced from 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, + 'max_execution_time' => 30, ]; diff --git a/supervisord.conf b/supervisord.conf index 7cc3d84..6b8f798 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -6,7 +6,7 @@ pidfile=/var/run/supervisord.pid [program:laravel-octane] process_name=%(program_name)s_%(process_num)02d -command=php /var/www/artisan octane:start --server=swoole --host=0.0.0.0 --port=8000 +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 @@ -15,6 +15,9 @@ 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 From 0e861a11e789c61ad29a53d98253c700405600b6 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Mon, 9 Jun 2025 16:11:27 +0300 Subject: [PATCH 24/49] Refactor `InjectUser` trait to return `user_id` instead of modifying input by reference; update controllers and services to align with the change for improved consistency and readability. --- app/External_Apis/Services/OfferService.php | 2 +- app/External_Apis/Services/ProductService.php | 2 +- app/Http/Controllers/OfferController.php | 1 + app/Http/Controllers/ProductController.php | 1 + app/Traits/InjectUser.php | 4 ++-- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/External_Apis/Services/OfferService.php b/app/External_Apis/Services/OfferService.php index a718fc3..6a194f8 100644 --- a/app/External_Apis/Services/OfferService.php +++ b/app/External_Apis/Services/OfferService.php @@ -28,7 +28,7 @@ public function __construct(OfferRepository $OfferRepository) */ public function index(array $data): array { - $this->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/Http/Controllers/OfferController.php b/app/Http/Controllers/OfferController.php index 4d453b0..459adc4 100644 --- a/app/Http/Controllers/OfferController.php +++ b/app/Http/Controllers/OfferController.php @@ -23,6 +23,7 @@ public function __construct(public OfferService $offerService, */ public function index(Request $request) { + $request->merge($this->offerService->injectUserId()); return $this->grpcService->proxyRequest( $request, 'product', diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductController.php index 6c1b60c..f2b1eee 100644 --- a/app/Http/Controllers/ProductController.php +++ b/app/Http/Controllers/ProductController.php @@ -34,6 +34,7 @@ public static function middleware() */ public function index(Request $request) { + $request->merge($this->productService->injectUserId()); return $this->grpcService->proxyRequest( $request, 'product', diff --git a/app/Traits/InjectUser.php b/app/Traits/InjectUser.php index b7875ab..546b7dd 100644 --- a/app/Traits/InjectUser.php +++ b/app/Traits/InjectUser.php @@ -4,9 +4,9 @@ trait InjectUser { - private function injectUserId(&$data=[]): void + protected function injectUserId(): array { - $data['user_id'] = auth()->user()->id; + return ['user_id' => auth()->user()->id]; } } From 0af389e0e91c7118b5020d79608a3bd87f29d980 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Tue, 10 Jun 2025 12:24:11 +0300 Subject: [PATCH 25/49] Refactor `InjectUser` trait to make `injectUserId` method public and streamline Dockerfile by switching to Debian-based image, simplifying package installation layers, optimizing PHP extension installation, and enhancing gRPC configuration for improved maintainability. --- Dockerfile | 233 ++++++++++++++------------------------ app/Traits/InjectUser.php | 2 +- php.ini | 78 +++++-------- 3 files changed, 112 insertions(+), 201 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8e1a180..72da1d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,112 +1,78 @@ -FROM php:8.4.4-fpm-alpine AS builder +FROM php:8.4.4-fpm AS builder -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 \ - autoconf \ - automake \ - make \ - gcc \ - g++ \ - libc-dev \ - pcre-dev \ - protobuf \ - protobuf-dev \ - grpc \ - grpc-dev \ - grpc-plugins \ curl \ wget \ - cmake \ - linux-headers \ - zlib-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 \ - && docker-php-ext-install sockets \ - && docker-php-ext-install bcmath - -# Install PECL extensions including gRPC -RUN pecl install swoole \ - && docker-php-ext-enable swoole \ - && pecl install redis \ - && docker-php-ext-enable redis \ - && pecl install grpc \ - && docker-php-ext-enable grpc \ - && pecl install protobuf \ - && docker-php-ext-enable protobuf - -## Install protoc and grpc_php_plugin if not available via apk -#RUN if ! command -v protoc &> /dev/null; then \ -# PROTOC_ZIP=protoc-21.12-linux-x86_64.zip && \ -# curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v21.12/$PROTOC_ZIP && \ -# unzip -o $PROTOC_ZIP -d /usr/local bin/protoc && \ -# unzip -o $PROTOC_ZIP -d /usr/local 'include/*' && \ -# rm -f $PROTOC_ZIP; \ -# fi - -## Verify installations -#RUN protoc --version && \ -# php -m | grep -E "(grpc|protobuf)" - -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 \ + libcurl4-openssl-dev \ + libssl-dev \ + pkg-config \ + protobuf-compiler \ + protobuf-compiler-grpc \ + libprotobuf-dev \ + build-essential \ + autoconf \ + 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 \ - protobuf \ - grpc \ - supervisor \ - netcat-openbsd - -# Copy PHP extensions from builder stage + 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 and gRPC settings -COPY php.ini /usr/local/etc/php/conf.d/custom.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 proto files first (for gRPC generation - changes less frequently than app code) -COPY proto/ ./proto/ - -# Generate gRPC classes from proto files (do this before copying app code) -RUN if [ -d "proto" ]; then \ - mkdir -p app/Grpc app/Grpc/GPBMetadata && \ - for proto in proto/*.proto; do \ - if [ -f "$proto" ]; then \ - echo "Generating protobuf for $proto" && \ - protoc --proto_path=proto \ - --php_out=. \ - --grpc_out=. \ - --plugin=protoc-gen-grpc=$(which grpc_php_plugin || echo "/usr/bin/grpc_php_plugin") \ - "$proto" || echo "Warning: Failed to generate protobuf for $proto"; \ - fi \ - done && \ - echo "Protobuf generation complete" && \ - if [ -d "App" ]; then \ - cp -R App/* app/ && rm -rf App; \ - fi; \ - fi - -# 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 with production optimizations @@ -118,11 +84,9 @@ RUN composer install \ --classmap-authoritative \ && composer clear-cache -# Copy application files +# Copy application files (most frequently changing layer - put last) COPY . . -# Copy supervisor configuration (if using supervisor) -COPY supervisord.conf /etc/supervisor/supervisord.conf # 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 \ @@ -130,56 +94,27 @@ RUN mkdir -p storage/logs storage/framework/{cache,sessions,views} bootstrap/cac && chmod -R 755 /var/www/bootstrap/cache # Create a non-root user for security -RUN addgroup -g 1000 laravel \ - && adduser -u 1000 -G laravel -s /bin/sh -D laravel - -# Expose ports (8000 for Octane, 50051 for gRPC if running server) -EXPOSE 8000 50051 - -# Create startup script that handles both Laravel optimization and gRPC -# Create startup script as a separate file -COPY < /usr/local/bin/start-supervisor.sh \ -# && chmod +x /usr/local/bin/start-supervisor.sh - -# Switch to non-root user (uncomment if your setup allows) -# USER laravel - -# Default command (use start-supervisor.sh if you need both HTTP and gRPC servers) +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 + CMD ["/usr/local/bin/start.sh"] -# CMD sh -c "php artisan migrate --force && php artisan storage:link && php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000" diff --git a/app/Traits/InjectUser.php b/app/Traits/InjectUser.php index 546b7dd..d3682c8 100644 --- a/app/Traits/InjectUser.php +++ b/app/Traits/InjectUser.php @@ -4,7 +4,7 @@ trait InjectUser { - protected function injectUserId(): array + public function injectUserId(): array { return ['user_id' => auth()->user()->id]; } diff --git a/php.ini b/php.ini index d53d8d8..179b1b8 100644 --- a/php.ini +++ b/php.ini @@ -1,61 +1,37 @@ -[PHP] -; Basic PHP settings -memory_limit = 512M -upload_max_filesize = 100M -post_max_size = 100M -max_execution_time = 300 -max_input_time = 300 - -; Error reporting -display_errors = Off -log_errors = On -error_log = /var/log/php_errors.log - -; OPcache settings for production -[opcache] +; OPcache settings opcache.enable=1 opcache.enable_cli=1 -opcache.memory_consumption=256 -opcache.interned_strings_buffer=16 -opcache.max_accelerated_files=20000 -opcache.max_wasted_percentage=5 -opcache.use_cwd=1 -opcache.validate_timestamps=0 -opcache.revalidate_freq=0 +opcache.memory_consumption=128 +opcache.interned_strings_buffer=8 +opcache.max_accelerated_files=10000 +opcache.revalidate_freq=2 opcache.save_comments=1 -opcache.fast_shutdown=0 +opcache.fast_shutdown=1 -; gRPC Extension Settings -[grpc] -extension=grpc.so -; Maximum message length for gRPC calls (in bytes) +; 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 -; Enable gRPC keepalive -grpc.keepalive_time_ms=30000 -grpc.keepalive_timeout_ms=5000 -grpc.keepalive_permit_without_calls=true -grpc.http2_max_pings_without_data=0 -grpc.http2_min_recv_ping_interval_without_data_ms=300000 -; Protobuf Extension Settings -[protobuf] -extension=protobuf.so +; Error reporting +display_errors=Off +log_errors=On +error_log=/var/log/php_errors.log -; Swoole settings (for Laravel Octane) -[swoole] -extension=swoole.so -swoole.enable_coroutine=On -swoole.enable_library=On -swoole.enable_preemptive_scheduler=Off +; Process control +pcntl.async_signals=On -; Socket settings (required for gRPC) -[sockets] -extension=sockets.so +; Timezone +date.timezone=UTC -; Other extensions -extension=pdo_mysql.so -extension=pdo_pgsql.so -extension=zip.so -extension=pcntl.so -extension=bcmath.so +; Session (if needed) +session.save_handler=files +session.save_path="/tmp" From 60ae5f83c26b317435c9f421a15c13fe94f4e990 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Tue, 10 Jun 2025 17:07:33 +0300 Subject: [PATCH 26/49] Add `clear` method to `WishListController` and corresponding route for clearing all wishlist items --- app/Http/Controllers/WishListController.php | 9 +++++++++ routes/api.php | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/WishListController.php b/app/Http/Controllers/WishListController.php index 9a6ae39..e44051b 100644 --- a/app/Http/Controllers/WishListController.php +++ b/app/Http/Controllers/WishListController.php @@ -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/routes/api.php b/routes/api.php index e5d20b5..32f8a2a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -43,7 +43,8 @@ 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::delete('/clear', [WishlistController::class, 'clear']); + }); //Product Service From adb28fc873eb8c072367600cd96f602f7a670e4c Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Wed, 11 Jun 2025 19:54:04 +0300 Subject: [PATCH 27/49] Refactor `GrpcService` and `GatewayService` to improve response handling, error reporting, and data inclusion; remove unused `Iterator` dependency and deprecate streaming request logic for better maintainability. --- app/Grpc/Services/GatewayService.php | 32 +++++++++++++++------------- app/Grpc/Services/GrpcService.php | 9 +++++--- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php index 7c98ec3..c36c414 100644 --- a/app/Grpc/Services/GatewayService.php +++ b/app/Grpc/Services/GatewayService.php @@ -10,10 +10,10 @@ use App\Grpc\GatewayServiceClient; use DateTime; use Exception; +use Google\Protobuf\Any; use Google\Protobuf\Timestamp; use Grpc\ChannelCredentials; use Illuminate\Support\Facades\Log; -use Iterator; use const Grpc\STATUS_OK; class GatewayService @@ -61,19 +61,20 @@ public function HandleRequest(FlexibleRequest $in): FlexibleResponse } else { // dd($downstreamResponse->getError()); - if(is_null($downstreamResponse)) + 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' - ); + return $this->createErrorResponse( + $response, + $downstreamResponse->getStatusCode(), + 'GRPC_ERROR', + $downstreamResponse->getError() ? $downstreamResponse->getError()->getMessage() : 'Unknown error', + data: $downstreamResponse->getData() + ); } // } catch (Exception $e) { @@ -145,12 +146,12 @@ public function HandleBytesRequest(BytesRequest $in): BytesResponse /** * Handle streaming requests */ - public function HandleStreamRequest(Iterator $in): Iterator - { - foreach ($in as $request) { - yield $this->HandleRequest($request); - } - } +// public function HandleStreamRequest(Iterator $in): Iterator +// { +// foreach ($in as $request) { +// yield $this->HandleRequest($request); +// } +// } /** * Get gRPC client for service @@ -206,12 +207,13 @@ public function __destruct() /** * Create error response for FlexibleResponse */ - private function createErrorResponse(FlexibleResponse $response, int $statusCode, string $code, string $message): 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); diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index e510629..f381504 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -32,9 +32,12 @@ public function proxyRequest(Request $request, string $service, string $method): return response()->json($data, $grpcResponse->getStatusCode()); } else { $error = $grpcResponse->getError()->getMessage(); - return response()->json([ - 'error' => json_decode($error) ?? $error, - ], $grpcResponse->getStatusCode()); + $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()); } } } From 8793167cd1b2aa07348e6d919eb5d4a430ed4889 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Thu, 12 Jun 2025 03:07:32 +0300 Subject: [PATCH 28/49] Rename controllers and routes to align with `*ApiController` naming convention and add `purchaseHistory` method to `OrderApiController` for retrieving purchase history. --- ...thController.php => AuthApiController.php} | 2 +- ...Controller.php => GoogleApiController.php} | 2 +- ...rController.php => OfferApiController.php} | 2 +- app/Http/Controllers/OrderApiController.php | 11 ++++ ...ntroller.php => PasswordApiController.php} | 2 +- ...ontroller.php => ProductApiController.php} | 2 +- ...r.php => ShippingAddressApiController.php} | 2 +- ...ller.php => ShoppingCartApiController.php} | 2 +- ...ntroller.php => WishListApiController.php} | 2 +- routes/api.php | 53 ++++++++++--------- 10 files changed, 46 insertions(+), 34 deletions(-) rename app/Http/Controllers/{AuthController.php => AuthApiController.php} (98%) rename app/Http/Controllers/{GoogleController.php => GoogleApiController.php} (95%) rename app/Http/Controllers/{OfferController.php => OfferApiController.php} (98%) rename app/Http/Controllers/{PasswordController.php => PasswordApiController.php} (96%) rename app/Http/Controllers/{ProductController.php => ProductApiController.php} (98%) rename app/Http/Controllers/{ShippingAddressController.php => ShippingAddressApiController.php} (98%) rename app/Http/Controllers/{ShoppingCartController.php => ShoppingCartApiController.php} (97%) rename app/Http/Controllers/{WishListController.php => WishListApiController.php} (97%) diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthApiController.php similarity index 98% rename from app/Http/Controllers/AuthController.php rename to app/Http/Controllers/AuthApiController.php index 73aa019..538f364 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthApiController.php @@ -9,7 +9,7 @@ use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; -class AuthController extends BaseController +class AuthApiController extends BaseController { public function __construct( 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 98% rename from app/Http/Controllers/OfferController.php rename to app/Http/Controllers/OfferApiController.php index 459adc4..efa460c 100644 --- a/app/Http/Controllers/OfferController.php +++ b/app/Http/Controllers/OfferApiController.php @@ -10,7 +10,7 @@ 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, private readonly GrpcService $grpcService 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 96% rename from app/Http/Controllers/PasswordController.php rename to app/Http/Controllers/PasswordApiController.php index 881b763..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 diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductApiController.php similarity index 98% rename from app/Http/Controllers/ProductController.php rename to app/Http/Controllers/ProductApiController.php index f2b1eee..c6c645e 100644 --- a/app/Http/Controllers/ProductController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -12,7 +12,7 @@ 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, 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/ShoppingCartController.php b/app/Http/Controllers/ShoppingCartApiController.php similarity index 97% rename from app/Http/Controllers/ShoppingCartController.php rename to app/Http/Controllers/ShoppingCartApiController.php index 3691ea7..e165afa 100644 --- a/app/Http/Controllers/ShoppingCartController.php +++ b/app/Http/Controllers/ShoppingCartApiController.php @@ -6,7 +6,7 @@ use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Request; -class ShoppingCartController extends BaseController +class ShoppingCartApiController extends BaseController { public function __construct( private readonly GrpcService $grpcService, diff --git a/app/Http/Controllers/WishListController.php b/app/Http/Controllers/WishListApiController.php similarity index 97% rename from app/Http/Controllers/WishListController.php rename to app/Http/Controllers/WishListApiController.php index e44051b..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. diff --git a/routes/api.php b/routes/api.php index 32f8a2a..0371222 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,19 +1,19 @@ prefix('auth') +Route::controller(AuthApiController::class)->prefix('auth') ->group(function () { Route::post('login', 'login'); Route::post('signup', 'signup'); @@ -23,47 +23,48 @@ 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('/clear', [WishlistController::class, 'clear']); +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', [ProductController::class, 'batch']); -Route::post('/offers/batch', [OfferController::class, 'batch']); +Route::post('products/batch', [ProductApiController::class, 'batch']); +Route::post('/offers/batch', [OfferApiController::class, 'batch']); From 1c00257326a91213d4fbc94598dcd2955f35da15 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Thu, 12 Jun 2025 03:11:48 +0300 Subject: [PATCH 29/49] Remove Laravel Octane and related configurations, update `composer.json` and `composer.lock`, and clean up `cache.php` to remove Octane-specific cache driver configuration. --- composer.json | 1 - composer.lock | 329 +++++----------------------------------------- config/cache.php | 4 - config/octane.php | 198 ---------------------------- 4 files changed, 34 insertions(+), 498 deletions(-) delete mode 100644 config/octane.php diff --git a/composer.json b/composer.json index efb102e..58eab8b 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,6 @@ "require": { "php": "^8.4", "laravel/framework": "^12.0", - "laravel/octane": "^2.8", "laravel/tinker": "^2.10.1", "grpc/grpc": "^1.57", "google/protobuf": "^4.31", diff --git a/composer.lock b/composer.lock index 735efab..569419e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9891bc824dab5d07611e542e7450e117", + "content-hash": "48d551fc576552ae83ec6195033a4f26", "packages": [ { "name": "brick/math", @@ -1142,106 +1142,18 @@ ], "time": "2025-02-03T10:55:03+00:00" }, - { - "name": "laminas/laminas-diactoros", - "version": "3.6.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "b068eac123f21c0e592de41deeb7403b88e0a89f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/b068eac123f21c0e592de41deeb7403b88e0a89f", - "reference": "b068eac123f21c0e592de41deeb7403b88e0a89f", - "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": "~3.0.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": "2025-05-05T16:03:34+00:00" - }, { "name": "laravel/framework", - "version": "v12.17.0", + "version": "v12.18.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "8729d084510480fdeec9b6ad198180147d4a7f06" + "reference": "7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/8729d084510480fdeec9b6ad198180147d4a7f06", - "reference": "8729d084510480fdeec9b6ad198180147d4a7f06", + "url": "https://api.github.com/repos/laravel/framework/zipball/7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d", + "reference": "7d264a0dad2bfc5c154240b38e8ce9b2c4cdd14d", "shasum": "" }, "require": { @@ -1443,97 +1355,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-06-03T14:04:18+00:00" - }, - { - "name": "laravel/octane", - "version": "v2.9.3", - "source": { - "type": "git", - "url": "https://github.com/laravel/octane.git", - "reference": "8278bdb3ae7a9a7380841e18e9cf52d909502647" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/octane/zipball/8278bdb3ae7a9a7380841e18e9cf52d909502647", - "reference": "8278bdb3ae7a9a7380841e18e9cf52d909502647", - "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-05-14T14:05:50+00:00" + "time": "2025-06-10T14:48:34+00:00" }, { "name": "laravel/prompts", @@ -5191,89 +5013,6 @@ ], "time": "2025-04-17T09:11:12+00:00" }, - { - "name": "symfony/psr-http-message-bridge", - "version": "v7.3.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.3.0" - }, - "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": "2024-09-26T08:57:56+00:00" - }, { "name": "symfony/routing", "version": "v7.3.0", @@ -6195,16 +5934,16 @@ }, { "name": "filp/whoops", - "version": "2.18.1", + "version": "2.18.2", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26" + "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26", - "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26", + "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3", + "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3", "shasum": "" }, "require": { @@ -6254,7 +5993,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.1" + "source": "https://github.com/filp/whoops/tree/2.18.2" }, "funding": [ { @@ -6262,7 +6001,7 @@ "type": "github" } ], - "time": "2025-06-03T18:56:14+00:00" + "time": "2025-06-11T20:42:19+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -6668,23 +6407,23 @@ }, { "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", @@ -6692,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": { @@ -6763,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", @@ -7208,16 +6947,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.21", + "version": "11.5.22", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289" + "reference": "4cd72faaa8f811e4cc63040cba167757660a5538" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d565e2cdc21a7db9dc6c399c1fc2083b8010f289", - "reference": "d565e2cdc21a7db9dc6c399c1fc2083b8010f289", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4cd72faaa8f811e4cc63040cba167757660a5538", + "reference": "4cd72faaa8f811e4cc63040cba167757660a5538", "shasum": "" }, "require": { @@ -7289,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.21" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.22" }, "funding": [ { @@ -7313,7 +7052,7 @@ "type": "tidelift" } ], - "time": "2025-05-21T12:35:00+00:00" + "time": "2025-06-06T02:48:05+00:00" }, { "name": "sebastian/cli-parser", diff --git a/config/cache.php b/config/cache.php index 56ffb05..ced0f02 100644 --- a/config/cache.php +++ b/config/cache.php @@ -80,10 +80,6 @@ 'endpoint' => env('DYNAMODB_ENDPOINT'), ], - 'octane' => [ - 'driver' => 'octane', - ], - ], /* diff --git a/config/octane.php b/config/octane.php deleted file mode 100644 index ad71933..0000000 --- a/config/octane.php +++ /dev/null @@ -1,198 +0,0 @@ - env('OCTANE_SERVER', 'swoole'), - - /* - |-------------------------------------------------------------------------- - | Force HTTPS - |-------------------------------------------------------------------------- - */ - - 'https' => env('OCTANE_HTTPS', true), - - /* - |-------------------------------------------------------------------------- - | Swoole Configuration - MINIMAL SAFE SETTINGS - |-------------------------------------------------------------------------- - */ - - 'swoole' => [ - 'options' => [ - // Basic worker configuration - start conservative - 'worker_num' => env('SWOOLE_WORKER_NUM', 2), // Reduced from cpu_num - 'reactor_num' => env('SWOOLE_REACTOR_NUM', 2), // Reduced from cpu_num - 'max_request' => env('SWOOLE_MAX_REQUEST', 100), // Much lower to force frequent restarts - 'max_conn' => env('SWOOLE_MAX_CONN', 256), // Reduced from 1024 - - // Conservative buffer sizes - 'package_max_length' => env('SWOOLE_PACKAGE_MAX_LENGTH', 2 * 1024 * 1024), // 2MB instead of 10MB - 'buffer_output_size' => env('SWOOLE_BUFFER_OUTPUT_SIZE', 2 * 1024 * 1024), - 'socket_buffer_size' => env('SWOOLE_SOCKET_BUFFER_SIZE', 64 * 1024), - - // Disable coroutines temporarily to isolate issue - 'enable_coroutine' => false, - - // Basic TCP settings - 'open_tcp_nodelay' => true, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Octane Listeners - RESTORE ALL CRITICAL CLEANUP - |-------------------------------------------------------------------------- - */ - - 'listeners' => [ - WorkerStarting::class => [ - EnsureUploadedFilesAreValid::class, - EnsureUploadedFilesCanBeMoved::class, - ], - - RequestReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), - ...Octane::prepareApplicationForNextRequest(), - ], - - RequestHandled::class => [ - // - ], - - RequestTerminated::class => [ - FlushUploadedFiles::class, // Re-enabled - ], - - TaskReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), - ], - - TaskTerminated::class => [ - // - ], - - TickReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), - ], - - TickTerminated::class => [ - // - ], - - OperationTerminated::class => [ - FlushOnce::class, - FlushTemporaryContainerInstances::class, - DisconnectFromDatabases::class, // RE-ENABLED - CRITICAL - CollectGarbage::class, // RE-ENABLED - CRITICAL - ], - - WorkerErrorOccurred::class => [ - ReportException::class, - StopWorkerIfNecessary::class, - ], - - WorkerStopping::class => [ - CloseMonologHandlers::class, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Warm / Flush Bindings - |-------------------------------------------------------------------------- - */ - - 'warm' => [ - // Use minimal warming to avoid container issues - ], - - 'flush' => [ - // - ], - - /* - |-------------------------------------------------------------------------- - | Octane Swoole Tables - MINIMAL - |-------------------------------------------------------------------------- - */ - - 'tables' => [ - // Remove example table that might be causing null key issues - ], - - /* - |-------------------------------------------------------------------------- - | Octane Swoole Cache Table - REDUCED - |-------------------------------------------------------------------------- - */ - - 'cache' => [ - 'rows' => 100, // Reduced from 1000 - 'bytes' => 1000, // Reduced from 10000 - ], - - /* - |-------------------------------------------------------------------------- - | File Watching - |-------------------------------------------------------------------------- - */ - - 'watch' => [ - 'app', - 'bootstrap', - 'config/**/*.php', - 'database/**/*.php', - 'public/**/*.php', - 'resources/**/*.php', - 'routes', - 'composer.lock', - '.env', - ], - - /* - |-------------------------------------------------------------------------- - | Garbage Collection Threshold - LOWER FOR MORE FREQUENT CLEANUP - |-------------------------------------------------------------------------- - */ - - 'garbage' => 25, // Reduced from 50 - - /* - |-------------------------------------------------------------------------- - | Maximum Execution Time - |-------------------------------------------------------------------------- - */ - - 'max_execution_time' => 30, - -]; From 71bfae034dd658710921b7d46e4e9df9ae6cfcdb Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Thu, 12 Jun 2025 22:41:43 +0300 Subject: [PATCH 30/49] Add `AIApi` for AI recommendations service, implement `ApiGatewayController::getRecommendedProducts` for fetching recommended products, update routes and environment variables for AI service integration, and enhance `docker-compose.yml` configuration. --- app/External_Apis/Apis/AIApi.php | 19 ++++++ app/External_Apis/Apis/BasicApi.php | 1 - app/Http/Controllers/ApiGatewayController.php | 58 +++++++++++++++++++ config/services.php | 1 + docker-compose.yml | 6 +- routes/api.php | 4 ++ 6 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 app/External_Apis/Apis/AIApi.php create mode 100644 app/Http/Controllers/ApiGatewayController.php 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 @@ +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/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 bfa9589..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,6 +31,10 @@ services: - DB_DATABASE=api-gw_db - DB_USERNAME=laravel - DB_PASSWORD=api-gw_pass + - 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/routes/api.php b/routes/api.php index 0371222..902c222 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,5 +1,6 @@ middleware('authenticate'); From f9b8161b0fb35f72479e550b6abc7475062cc997 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 13 Jun 2025 01:17:51 +0300 Subject: [PATCH 31/49] Update `getRecommendedProducts` to use POST request for AI API, adjust response handling, switch `/recommendations` route to GET, and update AI service URL in `docker-compose.yml`. --- app/Http/Controllers/ApiGatewayController.php | 10 +++++++--- docker-compose.yml | 2 +- routes/api.php | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php index f4d779c..ee7435c 100644 --- a/app/Http/Controllers/ApiGatewayController.php +++ b/app/Http/Controllers/ApiGatewayController.php @@ -26,12 +26,17 @@ public function __construct( public function getRecommendedProducts(Request $request) { try { - $res = Http::withToken($request->bearerToken())->get(AIApi::getAIApi()); + $res = Http::post(AIApi::getAIApi(), [ + 'top_n'=> $request->input('top_n', 10), + "token"=> $request->bearerToken() + ]); + 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')]); + $request->merge(['recommended_product_ids' => array_column($recommendedProductData, 'id')]); $res = $this->grpcService->proxyRequest( $request, 'product', @@ -45,7 +50,6 @@ public function getRecommendedProducts(Request $request) }, data_get($filteredProductData, 'data', [])); return $res->setData($filteredProductData); - return $res; } else throw new ConnectionException('Failed to fetch products', $res->getStatusCode()); } catch (ConnectionException $e) { diff --git a/docker-compose.yml b/docker-compose.yml index 63528bc..916cf69 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,7 +34,7 @@ services: - PRODUCT_SERVICE_URL=http://product-webserver/ - USER_SERVICE_URL=http://user-webserver/ - ORDER_SERVICE_URL=http://order-webserver/ - - AI_SERVICE_URL=http://ai-webserver/ + - AI_SERVICE_URL=http://ai-service/ api-gw_db: image: mysql:8.0 diff --git a/routes/api.php b/routes/api.php index 902c222..39fdf87 100644 --- a/routes/api.php +++ b/routes/api.php @@ -71,4 +71,4 @@ Route::post('/offers/batch', [OfferApiController::class, 'batch']); // AI Service -Route::post('/recommendations', [ApiGatewayController::class, 'getRecommendedProducts'])->middleware('authenticate'); +Route::get('/recommendations', [ApiGatewayController::class, 'getRecommendedProducts'])->middleware('authenticate'); From bf62a916837b568a461ea598bc19fa991a5debcd Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 13 Jun 2025 02:48:34 +0300 Subject: [PATCH 32/49] setup gRPC server --- app/Console/Commands/GrpcServeCommand.php | 40 + app/Grpc/Controllers/ProductController.php | 20 + app/Grpc/Controllers/UserController.php | 20 + app/Grpc/GPBMetadata/User/UserService.php | 24 - app/Grpc/GatewayServiceClient.php | 4 +- app/Grpc/GrpcRoute.php | 11 + app/Grpc/GrpcRouteRegistry.php | 26 + app/Grpc/GrpcServiceRouter.php | 19 + app/Grpc/HealthServiceClient.php | 4 +- app/Grpc/Middleware/GrpcAuthMiddleware.php | 115 --- app/Grpc/Resources/UserResource.php | 34 - app/Grpc/Servers/BaseServer.php | 211 ++++++ app/Grpc/Servers/CartServer.php | 133 ++++ app/Grpc/Servers/UserServer.php | 808 +++++++-------------- app/Grpc/Services/GatewayService.php | 100 ++- app/Grpc/Services/GrpcService.php | 20 +- app/Grpc/User/AuthenticateRequest.php | 85 --- app/Grpc/User/AuthenticateResponse.php | 193 ----- app/Grpc/User/CartItem.php | 139 ---- app/Grpc/User/ChangePasswordRequest.php | 85 --- app/Grpc/User/ChangePasswordResponse.php | 85 --- app/Grpc/User/CreateUserRequest.php | 139 ---- app/Grpc/User/DeleteUserRequest.php | 58 -- app/Grpc/User/DeleteUserResponse.php | 85 --- app/Grpc/User/GetUserRequest.php | 60 -- app/Grpc/User/ListUsersRequest.php | 112 --- app/Grpc/User/ListUsersResponse.php | 193 ----- app/Grpc/User/LogoutRequest.php | 31 - app/Grpc/User/LogoutResponse.php | 85 --- app/Grpc/User/RefreshTokenRequest.php | 31 - app/Grpc/User/RefreshTokenResponse.php | 166 ----- app/Grpc/User/UpdateUserRequest.php | 139 ---- app/Grpc/User/User.php | 249 ------- app/Grpc/User/UserProfileRequest.php | 58 -- app/Grpc/User/UserProfileResponse.php | 247 ------- app/Grpc/User/UserResponse.php | 114 --- app/Grpc/User/UserServiceClient.php | 175 ----- app/Grpc/User/ValidateTokenRequest.php | 58 -- app/Grpc/User/ValidateTokenResponse.php | 139 ---- app/Providers/GrpcServiceProvider.php | 8 + app/Services/UserGrpcService.php | 4 +- config/services.php | 6 + routes/grpc.php | 29 + 43 files changed, 869 insertions(+), 3493 deletions(-) create mode 100644 app/Console/Commands/GrpcServeCommand.php create mode 100644 app/Grpc/Controllers/ProductController.php create mode 100644 app/Grpc/Controllers/UserController.php delete mode 100644 app/Grpc/GPBMetadata/User/UserService.php create mode 100644 app/Grpc/GrpcRoute.php create mode 100644 app/Grpc/GrpcRouteRegistry.php create mode 100644 app/Grpc/GrpcServiceRouter.php delete mode 100644 app/Grpc/Middleware/GrpcAuthMiddleware.php delete mode 100644 app/Grpc/Resources/UserResource.php create mode 100644 app/Grpc/Servers/BaseServer.php create mode 100644 app/Grpc/Servers/CartServer.php delete mode 100644 app/Grpc/User/AuthenticateRequest.php delete mode 100644 app/Grpc/User/AuthenticateResponse.php delete mode 100644 app/Grpc/User/CartItem.php delete mode 100644 app/Grpc/User/ChangePasswordRequest.php delete mode 100644 app/Grpc/User/ChangePasswordResponse.php delete mode 100644 app/Grpc/User/CreateUserRequest.php delete mode 100644 app/Grpc/User/DeleteUserRequest.php delete mode 100644 app/Grpc/User/DeleteUserResponse.php delete mode 100644 app/Grpc/User/GetUserRequest.php delete mode 100644 app/Grpc/User/ListUsersRequest.php delete mode 100644 app/Grpc/User/ListUsersResponse.php delete mode 100644 app/Grpc/User/LogoutRequest.php delete mode 100644 app/Grpc/User/LogoutResponse.php delete mode 100644 app/Grpc/User/RefreshTokenRequest.php delete mode 100644 app/Grpc/User/RefreshTokenResponse.php delete mode 100644 app/Grpc/User/UpdateUserRequest.php delete mode 100644 app/Grpc/User/User.php delete mode 100644 app/Grpc/User/UserProfileRequest.php delete mode 100644 app/Grpc/User/UserProfileResponse.php delete mode 100644 app/Grpc/User/UserResponse.php delete mode 100644 app/Grpc/User/UserServiceClient.php delete mode 100644 app/Grpc/User/ValidateTokenRequest.php delete mode 100644 app/Grpc/User/ValidateTokenResponse.php create mode 100644 routes/grpc.php diff --git a/app/Console/Commands/GrpcServeCommand.php b/app/Console/Commands/GrpcServeCommand.php new file mode 100644 index 0000000..8c733f4 --- /dev/null +++ b/app/Console/Commands/GrpcServeCommand.php @@ -0,0 +1,40 @@ +option('host'); + $port = (int)$this->option('port'); + + $this->info("Starting gRPC User Service on {$host}:{$port}"); + + try { + $server = new RpcServer([]); + $server->addHttp2Port("{$host}:{$port}"); + + // Register the User service + $server->handle(app(\App\Grpc\Services\GatewayService::class)); + + $this->info('gRPC User Service started successfully'); + $this->info('Listening for requests...'); + + // Start the server (this will block) + $server->run(); + + } catch (\Exception $e) { + $this->error('Failed to start gRPC server: ' . $e->getMessage()); + return 1; + } + return 0; + } +} diff --git a/app/Grpc/Controllers/ProductController.php b/app/Grpc/Controllers/ProductController.php new file mode 100644 index 0000000..fad7d60 --- /dev/null +++ b/app/Grpc/Controllers/ProductController.php @@ -0,0 +1,20 @@ +grpcService->forwardToGrpcService($request); + } +} diff --git a/app/Grpc/Controllers/UserController.php b/app/Grpc/Controllers/UserController.php new file mode 100644 index 0000000..fb274b9 --- /dev/null +++ b/app/Grpc/Controllers/UserController.php @@ -0,0 +1,20 @@ +grpcService->forwardToGrpcService($request); + } +} diff --git a/app/Grpc/GPBMetadata/User/UserService.php b/app/Grpc/GPBMetadata/User/UserService.php deleted file mode 100644 index b54220e..0000000 --- a/app/Grpc/GPBMetadata/User/UserService.php +++ /dev/null @@ -1,24 +0,0 @@ -internalAddGeneratedFile(hex2bin( - "0ab0130a12757365725f736572766963652e70726f746f120475736572221c0a0e4765745573657252657175657374120a0a02696418012001280322500a114372656174655573657252657175657374120c0a046e616d65180120012809120d0a05656d61696c18022001280912100a0870617373776f7264180320012809120c0a04726f6c65180420012809224e0a115570646174655573657252657175657374120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912100a0870617373776f7264180420012809221f0a1144656c6574655573657252657175657374120a0a02696418012001280322420a104c697374557365727352657175657374120c0a047061676518012001280512100a087065725f70616765180220012805120e0a0673656172636818032001280922360a1341757468656e74696361746552657175657374120d0a05656d61696c18012001280912100a0870617373776f726418022001280922240a125573657250726f66696c6552657175657374120e0a066669656c6473180220032809220f0a0d4c6f676f75745265717565737422250a1456616c6964617465546f6b656e52657175657374120d0a05746f6b656e18012001280922150a1352656672657368546f6b656e5265717565737422470a154368616e676550617373776f72645265717565737412180a1063757272656e745f70617373776f726418012001280912140a0c6e65775f70617373776f7264180220012809228f010a0455736572120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c180320012809120c0a04726f6c6518042001280912120a0a70686f746f5f7061746818052001280912140a0c6163636573735f746f6b656e18062001280912120a0a637265617465645f617418072001280912120a0a757064617465645f617418082001280922450a08436172744974656d120a0a026964180120012803120c0a047479706518022001280912100a087175616e74697479180320012805120d0a057072696365180420012801224a0a0c55736572526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120f0a0773756363657373180220012808120f0a076d65737361676518032001280922360a1244656c65746555736572526573706f6e7365120f0a0773756363657373180120012808120f0a076d6573736167651802200128092288010a114c6973745573657273526573706f6e736512190a05757365727318012003280b320a2e757365722e55736572120d0a05746f74616c18022001280512140a0c63757272656e745f7061676518032001280512110a096c6173745f70616765180420012805120f0a0773756363657373180520012808120f0a076d6573736167651806200128092289010a1441757468656e746963617465526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a05746f6b656e18022001280912120a0a746f6b656e5f7479706518032001280912120a0a657870697265735f6174180420012809120f0a0773756363657373180520012808120f0a076d65737361676518062001280922a6010a135573657250726f66696c65526573706f6e7365120a0a026964180120012803120c0a046e616d65180220012809120d0a05656d61696c18032001280912120a0a70686f746f5f70617468180420012809120c0a04726f6c6518052001280912220a0a636172745f6974656d7318062003280b320e2e757365722e436172744974656d120f0a0773756363657373180720012808120f0a076d65737361676518082001280922320a0e4c6f676f7574526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280922620a1556616c6964617465546f6b656e526573706f6e736512180a047573657218012001280b320a2e757365722e55736572120d0a0576616c6964180220012808120f0a0773756363657373180320012808120f0a076d657373616765180420012809226f0a1452656672657368546f6b656e526573706f6e7365120d0a05746f6b656e18012001280912120a0a746f6b656e5f7479706518022001280912120a0a657870697265735f6174180320012809120f0a0773756363657373180420012808120f0a076d657373616765180520012809223a0a164368616e676550617373776f7264526573706f6e7365120f0a0773756363657373180120012808120f0a076d65737361676518022001280932e0050a0b557365725365727669636512330a074765745573657212142e757365722e47657455736572526571756573741a122e757365722e55736572526573706f6e736512390a0a4372656174655573657212172e757365722e43726561746555736572526571756573741a122e757365722e55736572526573706f6e736512390a0a5570646174655573657212172e757365722e55706461746555736572526571756573741a122e757365722e55736572526573706f6e7365123f0a0a44656c6574655573657212172e757365722e44656c65746555736572526571756573741a182e757365722e44656c65746555736572526573706f6e7365123c0a094c697374557365727312162e757365722e4c6973745573657273526571756573741a172e757365722e4c6973745573657273526573706f6e736512490a1041757468656e7469636174655573657212192e757365722e41757468656e746963617465526571756573741a1a2e757365722e41757468656e746963617465526573706f6e736512450a0e4765745573657250726f66696c6512182e757365722e5573657250726f66696c65526571756573741a192e757365722e5573657250726f66696c65526573706f6e736512370a0a4c6f676f75745573657212132e757365722e4c6f676f7574526571756573741a142e757365722e4c6f676f7574526573706f6e736512480a0d56616c6964617465546f6b656e121a2e757365722e56616c6964617465546f6b656e526571756573741a1b2e757365722e56616c6964617465546f6b656e526573706f6e736512450a0c52656672657368546f6b656e12192e757365722e52656672657368546f6b656e526571756573741a1a2e757365722e52656672657368546f6b656e526573706f6e7365124b0a0e4368616e676550617373776f7264121b2e757365722e4368616e676550617373776f7264526571756573741a1c2e757365722e4368616e676550617373776f7264526573706f6e7365422cca020d4170705c477270635c55736572e202194170705c477270635c4750424d657461646174615c55736572620670726f746f33" - ), true); - - static::$is_initialized = true; - } -} - diff --git a/app/Grpc/GatewayServiceClient.php b/app/Grpc/GatewayServiceClient.php index bd81612..4da9065 100644 --- a/app/Grpc/GatewayServiceClient.php +++ b/app/Grpc/GatewayServiceClient.php @@ -6,12 +6,12 @@ /** * Gateway service definition */ -class GatewayServiceClient extends \Grpc\BaseStub { +class GatewayServiceClient extends \App\Grpc\Controllers\BaseStub { /** * @param string $hostname hostname * @param array $opts channel options - * @param \Grpc\Channel $channel (optional) re-use channel object + * @param \App\Grpc\Controllers\Channel $channel (optional) re-use channel object */ public function __construct($hostname, $opts, $channel = null) { parent::__construct($hostname, $opts, $channel); diff --git a/app/Grpc/GrpcRoute.php b/app/Grpc/GrpcRoute.php new file mode 100644 index 0000000..55d6f70 --- /dev/null +++ b/app/Grpc/GrpcRoute.php @@ -0,0 +1,11 @@ + $controller, + 'action' => $action + ]; + } + + public static function resolve(string $service, string $method): ?array + { + return self::$routes[$service][$method] ?? null; + } + + public static function getAllRoutes(): array + { + return self::$routes; + } +} diff --git a/app/Grpc/GrpcServiceRouter.php b/app/Grpc/GrpcServiceRouter.php new file mode 100644 index 0000000..3de8200 --- /dev/null +++ b/app/Grpc/GrpcServiceRouter.php @@ -0,0 +1,19 @@ +serviceName = $serviceName; + } + + public function method(string $methodName, string $controller, string $action): self + { + GrpcRouteRegistry::register($this->serviceName, $methodName, $controller, $action); + return $this; + } +} diff --git a/app/Grpc/HealthServiceClient.php b/app/Grpc/HealthServiceClient.php index 5e4f300..4d64805 100644 --- a/app/Grpc/HealthServiceClient.php +++ b/app/Grpc/HealthServiceClient.php @@ -6,12 +6,12 @@ /** * Optional: Health check service */ -class HealthServiceClient extends \Grpc\BaseStub { +class HealthServiceClient extends \App\Grpc\Controllers\BaseStub { /** * @param string $hostname hostname * @param array $opts channel options - * @param \Grpc\Channel $channel (optional) re-use channel object + * @param \App\Grpc\Controllers\Channel $channel (optional) re-use channel object */ public function __construct($hostname, $opts, $channel = null) { parent::__construct($hostname, $opts, $channel); diff --git a/app/Grpc/Middleware/GrpcAuthMiddleware.php b/app/Grpc/Middleware/GrpcAuthMiddleware.php deleted file mode 100644 index 9248b23..0000000 --- a/app/Grpc/Middleware/GrpcAuthMiddleware.php +++ /dev/null @@ -1,115 +0,0 @@ -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 deleted file mode 100644 index b77cde8..0000000 --- a/app/Grpc/Resources/UserResource.php +++ /dev/null @@ -1,34 +0,0 @@ - - */ - 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/BaseServer.php b/app/Grpc/Servers/BaseServer.php new file mode 100644 index 0000000..244bea4 --- /dev/null +++ b/app/Grpc/Servers/BaseServer.php @@ -0,0 +1,211 @@ + new MethodDescriptor( + $this, + 'HandleRequest', + FlexibleRequest::class, + MethodDescriptor::UNARY_CALL, + ), + '/gateway.GatewayService/HandleBytesRequest' => new MethodDescriptor( + $this, + 'HandleBytesRequest', + BytesRequest::class, + MethodDescriptor::UNARY_CALL, + ), + '/gateway.GatewayService/HandleStreamRequest' => new MethodDescriptor( + $this, + 'HandleStreamRequest', + FlexibleRequest::class, + MethodDescriptor::BIDI_STREAMING_CALL + ) + ]; + } + + public function HandleRequest(FlexibleRequest $in, ServerContext $context): FlexibleResponse + { + self::setContext($context); + $response = new FlexibleResponse(); + $response->setRequestId($in->getRequestId()); + $response->setTimestamp($this->getCurrentTimestamp()); + try { + return $this->manageOperation($in, $response); + + } catch (Exception $e) { + Log::error('server error', [ + 'method' => $in->getMethod(), + 'error' => $e->getMessage(), + 'trace' => $e->getTrace(), + ]); + + self::getContext()->setStatus([ + 'code' => $e->getCode() ?: STATUS_INTERNAL, + 'details' => $e->getMessage() + ]); + return $this->createErrorResponse($response, Response::HTTP_INTERNAL_SERVER_ERROR, STATUS_INTERNAL, $e->getMessage()); + } + + } + + /** + * Handle bytes requests - for JSON/REST compatibility + */ + public function HandleBytesRequest(BytesRequest $in): BytesResponse + { + $response = new BytesResponse(); + $response->setRequestId($in->getRequestId()); + $response->setTimestamp($this->getCurrentTimestamp()); + + try { + // Parse JSON payload + $data = json_decode($in->getPayload(), true); + if (json_last_error() !== JSON_ERROR_NONE) { + return $this->createBytesErrorResponse($response, 400, 'INVALID_JSON', 'Invalid JSON payload'); + } + + // Route and process + $result = $this->processUserRequest($in->getMethod(), $data); + + $response->setSuccess(true); + $response->setStatusCode(200); + $response->setData(json_encode($result)); + $response->setContentType('application/json'); + + } catch (Exception $e) { + Log::error('User service bytes request error', [ + 'method' => $in->getMethod(), + 'error' => $e->getMessage() + ]); + + return $this->createBytesErrorResponse($response, 500, 'INTERNAL_ERROR', $e->getMessage()); + } + + return $response; + } + +// /** +// * Handle streaming (not implemented for this example) +// */ +// public function HandleStreamRequest(Iterator $in, ServerContext $context): Iterator +// { +// foreach ($in as $request) { +// yield $this->HandleRequest($request, $context); +// } +// } + + /** + * Create error response + */ + public function createErrorResponse(FlexibleResponse $response, int $statusCode, $code, string $message, ?array $errorMsg = null): FlexibleResponse + { + UserServer::getContext()->setStatus([ + 'code' => $code, + 'details' => $message + ]); + $error = new ErrorInfo(); + $error->setCode($code); + $error->setMessage($message); + + if (!is_null($errorMsg)) { + $any = new Any(); + $any->setValue(json_encode($errorMsg)); + $response->setData($any); + } + + $response->setSuccess(false); + $response->setStatusCode($statusCode); + $response->setError($error); + + return $response; + } + + /** + * Create bytes error response + */ + public 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 + */ + public function getCurrentTimestamp(): Timestamp + { + $timestamp = new Timestamp(); + $timestamp->fromDateTime(new DateTime()); + return $timestamp; + } + + private function processUserRequest(string $method, array $data): array + { + switch ($method) { + case 'create': + $user = User::create($data); + return $user->toArray(); + + case 'get': + $user = User::findOrFail($data['id']); + return $user->toArray(); + + case 'list': + return User::paginate($data['per_page'] ?? 15)->toArray(); + + default: + throw new Exception('Method not supported in bytes handler'); + } + } + + /** + * @param array $data + * @param FlexibleResponse $response + */ + protected function createResponse(array $data, FlexibleResponse $response, $code = 200): FlexibleResponse + { + $responseData = new Any(); + $responseData->setValue(json_encode($data)); + $response->setData($responseData); + $response->setSuccess(true); + $response->setStatusCode($code); + return $response; + } + + /** + * @param FlexibleRequest $in + * @param FlexibleResponse $response + * @return FlexibleResponse + */ + abstract function manageOperation(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse; +} diff --git a/app/Grpc/Servers/CartServer.php b/app/Grpc/Servers/CartServer.php new file mode 100644 index 0000000..8962dd3 --- /dev/null +++ b/app/Grpc/Servers/CartServer.php @@ -0,0 +1,133 @@ +getMethod()) { + 'addToCart' => $this->addToCart($in), +// 'getCart' => $this->getCart($in, $response), +// 'removeFromCart' => $this->removeFromCart($in, $response), +// 'clearCart' => $this->clearCart($in, $response), + }; + } + + /** + * @throws \Exception + */ + private function addToCart(FlexibleRequest $in): FlexibleResponse + { + return $this->grpcService->forwardToGrpcService($in); + } + +// /** +// * @throws BindingResolutionException +// * @throws ConnectionException +// */ +// private function getCart(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse +// { +// $jsonData = $in->getPayload()->getValue(); +// $data = json_decode($jsonData, true); +// +// $cart = $this->shoppingCartGatewayService->getCart(auth()->id()); +// if (!$cart) { +// Log::error('Get cart failed', [ +// 'user_id' => auth()->id(), +// ]); +// return $this->createErrorResponse( +// $response, +// Response::HTTP_NOT_FOUND, +// STATUS_INVALID_ARGUMENT, +// 'Cart not found' +// ); +// } +// +// return $this->createResponse([ +// 'message' => 'Cart retrieved successfully', +// 'data' => $cart +// ], +// $response +// ); +// } +// +// private function removeFromCart(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse +// { +// $jsonData = $in->getPayload()->getValue(); +// $data = json_decode($jsonData, true); +// $rules = [ +// 'item_id' => 'required|integer', +// 'item_type' => 'required|string|in:products,offers', +// ]; +// $validator = Validator::make($data, $rules); +// if ($validator->fails()) { +// Log::error('Remove from cart validation error', [ +// 'errors' => $validator->errors() +// ]); +// return $this->createErrorResponse( +// $response, +// Response::HTTP_BAD_REQUEST, +// STATUS_INVALID_ARGUMENT, +// $validator->errors()->first() +// ); +// } +// try { +// $this->cartCacheService->remove( +// auth()->id(), +// data_get($data, 'item_type'), +// data_get($data, 'item_id'), +// ); +// return $this->createResponse(['message' => 'Item removed from cart successfully'], $response); +// } catch (\Exception $e) { +// Log::error('Remove from cart cache error', [ +// 'user_id' => auth()->id(), +// 'item_id' => data_get($data, 'item_id'), +// 'item_type' => data_get($data, 'item_type'), +// 'error' => $e->getMessage() +// ]); +// return $this->createErrorResponse( +// $response, +// Response::HTTP_NOT_FOUND, +// STATUS_INVALID_ARGUMENT, +// 'Item not found in cart' +// ); +// } +// } +// +// private function clearCart(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse +// { +// try { +// $this->cartCacheService->clear(auth()->id()); +// return $this->createResponse(['message' => 'Cart cleared successfully'], $response); +// } catch (\Exception $e) { +// Log::error('Clear cart cache error', [ +// 'user_id' => auth()->id(), +// 'error' => $e->getMessage() +// ]); +// return $this->createErrorResponse( +// $response, +// Response::HTTP_INTERNAL_SERVER_ERROR, +// STATUS_INVALID_ARGUMENT, +// 'Failed to clear cart' +// ); +// } +// } + +} diff --git a/app/Grpc/Servers/UserServer.php b/app/Grpc/Servers/UserServer.php index ece97e4..88f444c 100644 --- a/app/Grpc/Servers/UserServer.php +++ b/app/Grpc/Servers/UserServer.php @@ -2,602 +2,356 @@ namespace App\Grpc\Servers; -use App\Grpc\DTOs\User\AuthenticateGrpcDTO; -use App\Grpc\DTOs\User\SimpleResponseGrpcDTO; -use App\Grpc\DTOs\User\UserGrpcDTO; -use App\Grpc\DTOs\User\UserListGrpcDTO; -use App\Grpc\DTOs\User\UserProfileGrpcDTO; -use App\Grpc\DTOs\User\ValidateTokenGrpcDTO; -use App\Grpc\Middleware\GrpcAuthMiddleware; -use App\Grpc\User\AuthenticateRequest; -use App\Grpc\User\AuthenticateResponse; -use App\Grpc\User\CreateUserRequest; -use App\Grpc\User\DeleteUserRequest; -use App\Grpc\User\DeleteUserResponse; -use App\Grpc\User\GetUserRequest; -use App\Grpc\User\ListUsersRequest; -use App\Grpc\User\ListUsersResponse; -use App\Grpc\User\LogoutRequest; -use App\Grpc\User\LogoutResponse; -use App\Grpc\User\UpdateUserRequest; -use App\Grpc\User\User as ProtoUser; -use App\Grpc\User\UserProfileRequest; -use App\Grpc\User\UserProfileResponse; -use App\Grpc\User\UserResponse; -use App\Grpc\User\ValidateTokenRequest; -use App\Grpc\User\ValidateTokenResponse; +use App\External_Apis\Services\UserService; +use App\Grpc\Controllers\ServerContext; +use App\Grpc\ErrorInfo; +use App\Grpc\FlexibleRequest; +use App\Grpc\FlexibleResponse; +use App\Grpc\Services\GrpcService; use App\Models\User; -use App\Services\AuthService; -use App\Services\UserService; -use Grpc\MethodDescriptor; +use Exception; +use Google\Protobuf\Any; +use Illuminate\Contracts\Container\BindingResolutionException; +use Illuminate\Http\Client\ConnectionException; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Validator; +use Symfony\Component\HttpFoundation\Response; +use const App\Grpc\Controllers\STATUS_INVALID_ARGUMENT; +use const App\Grpc\Controllers\STATUS_NOT_FOUND; +use const App\Grpc\Controllers\STATUS_UNAUTHENTICATED; +use const App\Grpc\Controllers\STATUS_UNIMPLEMENTED; class UserServer { - public function __construct( - private readonly UserService $userService, - private readonly AuthService $authService, - private readonly GrpcAuthMiddleware $authMiddleware + use BaseServer; + + protected static ServerContext $context; + + public function __construct(protected readonly UserService $userService, + protected readonly GrpcService $grpcService ) { } - /** - * Get method descriptors for gRPC server registration - */ - public function getMethodDescriptors(): array + public + static function getContext(): ServerContext { - return [ - '/user.UserService/GetUser' => 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, - ), - ]; + return self::$context; } - public function AuthenticateUser(AuthenticateRequest $request, \Grpc\ServerContext $context): ?AuthenticateResponse + private + static function setContext(ServerContext $context): void { - $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()); - } + self::$context = $context; + } - return $response; + /** + * @throws BindingResolutionException + * @throws ConnectionException + * @throws Exception + */ + function manageOperation(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse + { + // Route based on method + $isMatched = match ($in->getMethod()) { + 'create' => $this->createUser($in), +// 'login' => $this->login($in, $response), +// 'get' => $this->getUser($in, $response), +// 'update' => $this->updateUser($in, $response), +// 'delete' => $this->deleteUser($in, $response), +// 'list' => $this->listUsers($in, $response), +// 'GetUserProfile' => $this->GetUserProfile($in, $response), +// 'logout' => $this->logout($in, $response), + default => null + }; + + if (!$isMatched) + $isMatched = app(CartServer::class)->manageOperation($in, $response); + + if ($isMatched) return $isMatched; + return $this->createErrorResponse($response, Response::HTTP_INTERNAL_SERVER_ERROR, STATUS_UNIMPLEMENTED, 'Method not found: ' . $in->getMethod()); } - public function ValidateToken(ValidateTokenRequest $request, \Grpc\ServerContext $context): ?ValidateTokenResponse + /** + * Create user + * @throws Exception + */ + private function createUser(FlexibleRequest $request): FlexibleResponse { - $response = new ValidateTokenResponse(); + return $this->grpcService->forwardToGrpcService($request); + } - 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()); + /** + * Get user by ID + * @throws Exception + */ + private + function getUser(FlexibleRequest $request, FlexibleResponse $response): FlexibleResponse + { + $jsonData = $request->getPayload()->getValue(); + $data = json_decode($jsonData, true); + + $validator = Validator::make($data, [ + 'id' => 'required|integer|min:1' + ]); + $validator->validate(); + if ($validator->fails()) { + $this->createErrorResponse( + $response, + Response::HTTP_BAD_REQUEST, + STATUS_INVALID_ARGUMENT, + $validator->errors() + ); + return $response; +// throw new Exception($validator->errors(), STATUS_INVALID_ARGUMENT); + } + $user = $this->userService->show($data['id']); + $user = $user['data']; + if (!$user) { + $this->createErrorResponse( + $response, + Response::HTTP_NOT_FOUND, + STATUS_NOT_FOUND, + 'User not found: ' . ($data['id'] ?? 'unknown') + ); + return $response; +// throw new Exception($message, STATUS_NOT_FOUND); } - return $response; - } + $responseData = new Any(); + $resource = new UserResource($user); + $responseData->setValue(json_encode([ + 'data' => $resource + ])); - public function GetUserProfile(UserProfileRequest $request, \Grpc\ServerContext $context): ?UserProfileResponse - { - $response = new UserProfileResponse(); + $response->setSuccess(true); + $response->setStatusCode(200); + $response->setData($responseData); - 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 + /** + * Update user + */ + private + function updateUser(FlexibleRequest $request, FlexibleResponse $response): FlexibleResponse { - $response = new LogoutResponse(); + $jsonData = $request->getPayload()->getValue(); + $data = json_decode($jsonData, true); - try { - // Authenticate user - $user = $this->authMiddleware->authenticateGrpcRequest($context->clientMetadata()); + $user = User::find($data['id'] ?? null); - 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']); + if (!$user) { + return $this->createErrorResponse($response, 404, 'USER_NOT_FOUND', 'User not found'); + } - $response->setSuccess($dto->isSuccess()); - $response->setMessage($dto->getMessage()); + // Validate update data + $validator = Validator::make($data, [ + 'name' => 'sometimes|string|max:255', + 'email' => 'sometimes|email|unique:users,email,' . $user->id, + ]); - } 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()); + if ($validator->fails()) { + return $this->createErrorResponse($response, 400, 'VALIDATION_ERROR', $validator->errors()->first()); } + $user->update(array_intersect_key($data, array_flip(['name', 'email']))); + + $responseData = new Any(); + $responseData->setValue(json_encode([ + 'data' => new UserResource($user) + ])); + + $response->setSuccess(true); + $response->setStatusCode(200); + $response->setData($responseData); + return $response; } - public function ListUsers(ListUsersRequest $request, \Grpc\ServerContext $context): ?ListUsersResponse + /** + * Delete user + */ + private + function deleteUser(FlexibleRequest $request, FlexibleResponse $response): FlexibleResponse { - $response = new ListUsersResponse(); + $jsonData = $request->getPayload()->getValue(); + $data = json_decode($jsonData, true); - 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()); + $user = User::find($data['id'] ?? null); - } 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()); + if (!$user) { + return $this->createErrorResponse($response, 404, 'USER_NOT_FOUND', 'User not found'); } - return $response; - } + $user->delete(); - public function GetUser(GetUserRequest $request, \Grpc\ServerContext $context): ?UserResponse - { - $response = new UserResponse(); + $response->setSuccess(true); + $response->setStatusCode(204); - 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 + /** + * List users with pagination + * @throws Exception + */ + private + function listUsers(FlexibleRequest $request, FlexibleResponse $response): FlexibleResponse { - $response = new UserResponse(); - + $jsonData = $request->getPayload()->getValue(); + $filters = json_decode($jsonData, true) ?? []; 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()); - } + $users = $this->userService->index(); + $users = data_get($users, 'data'); + $responseData = new Any(); + $resourceCollection = UserResource::collection($users); + $responseData->setValue(json_encode([ + 'data' => $resourceCollection, +// 'pagination' => [ +// 'current_page' => $users->currentPage(), +// 'total_pages' => $users->lastPage(), +// 'total_items' => $users->total(), +// 'per_page' => $users->perPage() +// ] + ])); + $response->setSuccess(true); + $response->setStatusCode(200); + \Log::info('User listing successful', [ + 'total_users' => count($users), + 'users' => $users + ]); + $response->setData($responseData); + } catch (Exception $e) { + Log::error('User listing error', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTrace() + ]); + return $this->createErrorResponse($response, Response::HTTP_UNAUTHORIZED, STATUS_UNAUTHENTICATED, 'Failed to list users: ' . $e->getMessage()); +// throw new Exception('Failed to user-service: ' . $e->getMessage(), STATUS_INTERNAL); + } return $response; } - public function UpdateUser(UpdateUserRequest $request, \Grpc\ServerContext $context): ?UserResponse + /** + * @throws Exception + */ + private + function login(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse { - $response = new UserResponse(); + $jsonData = $in->getPayload()->getValue(); + $data = json_decode($jsonData, true); + $rules = new UserReadRequest()->rules(); + $validator = Validator::make($data, $rules); + if ($validator->fails()) { + Log::error('User login validation error', [ + 'errors' => $validator->errors() + ]); + self::getContext()->setStatus([ + 'code' => STATUS_INVALID_ARGUMENT, + 'details' => $validator->errors() + ]); + $response->setStatusCode(Response::HTTP_BAD_REQUEST); + $response->setError(new ErrorInfo([ + 'message' => $validator->errors() + ])); + $response->setSuccess(false); + return $response; +// throw new Exception($validator->errors(), STATUS_INVALID_ARGUMENT); + } - 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()); + $user = $this->userService->login($data); + + if (!$user) { + self::getContext()->setStatus([ + 'code' => STATUS_UNAUTHENTICATED, + 'details' => 'Invalid credentials' + ]); + $response->setError(new ErrorInfo([ + 'message' => 'Invalid credentials' + ])); + $response->setStatusCode(Response::HTTP_UNAUTHORIZED); $response->setSuccess(false); - $response->setMessage($dto->getMessage()); + return $response; +// throw new Exception ('Invalid credentials', STATUS_UNAUTHENTICATED); } + // Pack user data into Any + $userResource = new UserResource($user); + $anyData = new Any(); + $anyData->setValue(json_encode([ + 'message' => 'Login successful', + 'data' => $userResource + ])); + + $response->setSuccess(true); + $response->setStatusCode(200); + $response->setData($anyData); + return $response; } - public function DeleteUser(DeleteUserRequest $request, \Grpc\ServerContext $context): ?DeleteUserResponse + /** + * @throws BindingResolutionException + * @throws ConnectionException + */ + private + function GetUserProfile(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse { - $response = new DeleteUserResponse(); + $jsonData = $in->getPayload()->getValue(); + $data = json_decode($jsonData, true); + $this->authMiddleware->authenticateGrpcRequest(self::$context->clientMetadata()); + $user = $this->userService->userProfile($data); + \Log::info('GetUserProfile called', [ + 'data' => $data, + 'user_id' => $data['id'] ?? null, + 'user' => $user + ]); + + if (!$user) { + $this->createErrorResponse( + $response, + Response::HTTP_NOT_FOUND, + STATUS_NOT_FOUND, + 'User not found' + ); + return $response; + } - 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()); + // Pack user data into Any + $anyData = new Any(); + $anyData->setValue(json_encode([ + 'data' => $user + ])); - } 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()); - } + $response->setSuccess(true); + $response->setStatusCode(200); + $response->setData($anyData); 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); + private + function logout(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse + { + try { + $this->authMiddleware->revokeGrpcToken(self::$context->clientMetadata()); + return $this->createResponse( + [], + $response, + Response::HTTP_NO_CONTENT, + ); + } catch (Exception $e) { + Log::error('Logout error', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTrace() + ]); + return $this->createErrorResponse($response, Response::HTTP_UNAUTHORIZED, STATUS_UNAUTHENTICATED, $isLoggedOut['error'] ?? 'Unauthenticated'); } - - return $protoUser; } } diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php index c36c414..bf64bae 100644 --- a/app/Grpc/Services/GatewayService.php +++ b/app/Grpc/Services/GatewayService.php @@ -8,28 +8,31 @@ use App\Grpc\FlexibleRequest; use App\Grpc\FlexibleResponse; use App\Grpc\GatewayServiceClient; +use App\Grpc\GrpcRouteRegistry; use DateTime; use Exception; use Google\Protobuf\Any; use Google\Protobuf\Timestamp; use Grpc\ChannelCredentials; +use Grpc\MethodDescriptor; use Illuminate\Support\Facades\Log; use const Grpc\STATUS_OK; class GatewayService { - private array $grpcServices = [ - 'user' => 'user:50051', // Updated to match your service names - 'product' => 'product:50051', - 'order' => 'order:50051', - ]; + private array $grpcServices; + + public function __construct() + { + $this->grpcServices = config('services.microservices_grpc'); + } private array $grpcClients = []; /** * Handle flexible protobuf requests */ - public function HandleRequest(FlexibleRequest $in): FlexibleResponse + public function HandleClientRequest(FlexibleRequest $in): FlexibleResponse { $response = new FlexibleResponse(); $response->setRequestId($in->getRequestId()); @@ -91,68 +94,36 @@ public function HandleRequest(FlexibleRequest $in): FlexibleResponse return $response; } - /** - * Handle raw bytes requests (maximum flexibility) - */ - public function HandleBytesRequest(BytesRequest $in): BytesResponse + public function HandleRequest($request): FlexibleResponse { - $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'); - } + // Extract service and method from the request + $serviceName = $request->getService(); // You'll need to add this to your FlexibleRequest + $methodName = $request->getMethod(); // You'll need to add this to your FlexibleRequest - // Forward the BytesRequest to downstream service - list($downstreamResponse, $status) = $grpcClient->HandleBytesRequest($in)->wait(); + // Resolve the route + $route = GrpcRouteRegistry::resolve($serviceName, $methodName); - 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; - } + if (!$route) { + throw new Exception("Route not found: {$serviceName}::{$methodName}"); + } - // Forward error if exists - if ($downstreamResponse->hasError()) { - $response->setError($downstreamResponse->getError()); - } + // Instantiate controller + $controllerClass = "App\\Grpc\\Controllers\\{$route['controller']}"; + $controller = app($controllerClass); - } else { - return $this->createBytesErrorResponse($response, 500, 'GRPC_ERROR', $status->details); - } + // Call the action + return $controller->{$route['action']}($request); } catch (Exception $e) { - Log::error('Gateway gRPC bytes request failed', [ - 'service' => $in->getService(), - 'method' => $in->getMethod(), - 'error' => $e->getMessage() + return new FlexibleResponse([ + 'status' => 'error', + 'code' => 500, + 'data' => json_encode(['message' => $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 */ @@ -187,7 +158,9 @@ private function getGrpcClient(string $service): ?GatewayServiceClient ]); return null; } - } + } else + Log::info("Reusing existing gRPC client for service: {$service}"); + return $this->grpcClients[$service]; } @@ -248,4 +221,17 @@ private function getCurrentTimestamp(): Timestamp $timestamp->fromDateTime(new DateTime()); return $timestamp; } + + public function getMethodDescriptors(): array + { + return [ + '/gateway.GatewayService/HandleRequest' => new MethodDescriptor( + $this, + 'HandleRequest', + FlexibleRequest::class, + MethodDescriptor::UNARY_CALL, + ), + ]; + } + } diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index f381504..d8cb7cd 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -3,12 +3,20 @@ namespace App\Grpc\Services; use App\Grpc\FlexibleRequest; +use App\Grpc\FlexibleResponse; use Google\Protobuf\Any; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; class GrpcService { - public function proxyRequest(Request $request, string $service, string $method): \Illuminate\Http\JsonResponse + public function __construct( + private readonly GatewayService $gatewayService + ) + { + } + + public function proxyRequest(Request $request, string $service, string $method): JsonResponse { // Convert HTTP request to gRPC $grpcRequest = new FlexibleRequest(); @@ -23,8 +31,7 @@ public function proxyRequest(Request $request, string $service, string $method): // Call gRPC service - $gatewayService = new GatewayService(); - $grpcResponse = $gatewayService->HandleRequest($grpcRequest); + $grpcResponse = $this->gatewayService->HandleClientRequest($grpcRequest); // Convert back to HTTP response if ($grpcResponse->getSuccess()) { @@ -32,7 +39,7 @@ public function proxyRequest(Request $request, string $service, string $method): return response()->json($data, $grpcResponse->getStatusCode()); } else { $error = $grpcResponse->getError()->getMessage(); - $data = $grpcResponse->getData()? json_decode($grpcResponse->getData()->getValue()) : null; + $data = $grpcResponse->getData() ? json_decode($grpcResponse->getData()->getValue()) : null; if (data_get($data, 'message')) $res['message'] = data_get($data, 'message'); @@ -40,4 +47,9 @@ public function proxyRequest(Request $request, string $service, string $method): return response()->json($res, $grpcResponse->getStatusCode()); } } + + public function forwardToGrpcService(FlexibleRequest $request): FlexibleResponse + { + return $this->gatewayService->HandleClientRequest($request); + } } diff --git a/app/Grpc/User/AuthenticateRequest.php b/app/Grpc/User/AuthenticateRequest.php deleted file mode 100644 index 4b88e4d..0000000 --- a/app/Grpc/User/AuthenticateRequest.php +++ /dev/null @@ -1,85 +0,0 @@ -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 deleted file mode 100644 index ce25214..0000000 --- a/app/Grpc/User/AuthenticateResponse.php +++ /dev/null @@ -1,193 +0,0 @@ -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 deleted file mode 100644 index b954222..0000000 --- a/app/Grpc/User/CartItem.php +++ /dev/null @@ -1,139 +0,0 @@ -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 deleted file mode 100644 index 9768234..0000000 --- a/app/Grpc/User/ChangePasswordRequest.php +++ /dev/null @@ -1,85 +0,0 @@ -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 deleted file mode 100644 index ce1ec24..0000000 --- a/app/Grpc/User/ChangePasswordResponse.php +++ /dev/null @@ -1,85 +0,0 @@ -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 deleted file mode 100644 index d229886..0000000 --- a/app/Grpc/User/CreateUserRequest.php +++ /dev/null @@ -1,139 +0,0 @@ -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 deleted file mode 100644 index c48203b..0000000 --- a/app/Grpc/User/DeleteUserRequest.php +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index 600e394..0000000 --- a/app/Grpc/User/DeleteUserResponse.php +++ /dev/null @@ -1,85 +0,0 @@ -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 deleted file mode 100644 index eae8edb..0000000 --- a/app/Grpc/User/GetUserRequest.php +++ /dev/null @@ -1,60 +0,0 @@ -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 deleted file mode 100644 index 97d3d50..0000000 --- a/app/Grpc/User/ListUsersRequest.php +++ /dev/null @@ -1,112 +0,0 @@ -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 deleted file mode 100644 index ac84a51..0000000 --- a/app/Grpc/User/ListUsersResponse.php +++ /dev/null @@ -1,193 +0,0 @@ -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 deleted file mode 100644 index 9f612e2..0000000 --- a/app/Grpc/User/LogoutRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index 83b04b7..0000000 --- a/app/Grpc/User/LogoutResponse.php +++ /dev/null @@ -1,85 +0,0 @@ -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 deleted file mode 100644 index 1a470f9..0000000 --- a/app/Grpc/User/RefreshTokenRequest.php +++ /dev/null @@ -1,31 +0,0 @@ -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 deleted file mode 100644 index 8999b2b..0000000 --- a/app/Grpc/User/RefreshTokenResponse.php +++ /dev/null @@ -1,166 +0,0 @@ -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 deleted file mode 100644 index 8363942..0000000 --- a/app/Grpc/User/UpdateUserRequest.php +++ /dev/null @@ -1,139 +0,0 @@ -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 deleted file mode 100644 index e66ff64..0000000 --- a/app/Grpc/User/User.php +++ /dev/null @@ -1,249 +0,0 @@ -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 deleted file mode 100644 index 8677915..0000000 --- a/app/Grpc/User/UserProfileRequest.php +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index d574745..0000000 --- a/app/Grpc/User/UserProfileResponse.php +++ /dev/null @@ -1,247 +0,0 @@ -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 deleted file mode 100644 index b7ad7d2..0000000 --- a/app/Grpc/User/UserResponse.php +++ /dev/null @@ -1,114 +0,0 @@ -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 deleted file mode 100644 index cca9b6c..0000000 --- a/app/Grpc/User/UserServiceClient.php +++ /dev/null @@ -1,175 +0,0 @@ -_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 deleted file mode 100644 index cc819bf..0000000 --- a/app/Grpc/User/ValidateTokenRequest.php +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index cf2bddd..0000000 --- a/app/Grpc/User/ValidateTokenResponse.php +++ /dev/null @@ -1,139 +0,0 @@ -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/Providers/GrpcServiceProvider.php b/app/Providers/GrpcServiceProvider.php index a15586a..ba79b0a 100644 --- a/app/Providers/GrpcServiceProvider.php +++ b/app/Providers/GrpcServiceProvider.php @@ -12,5 +12,13 @@ public function register(): void $this->app->singleton(UserGrpcService::class, function ($app) { return new UserGrpcService(); }); + $this->loadGrpcRoutes(); + } + + protected function loadGrpcRoutes(): void + { + if (file_exists(base_path('routes/grpc.php'))) { + require base_path('routes/grpc.php'); + } } } diff --git a/app/Services/UserGrpcService.php b/app/Services/UserGrpcService.php index 09f8ad3..239a849 100644 --- a/app/Services/UserGrpcService.php +++ b/app/Services/UserGrpcService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Grpc\Controllers\ChannelCredentials; use App\Grpc\Resources\UserResource; use App\Grpc\User\AuthenticateRequest; use App\Grpc\User\CreateUserRequest; @@ -11,8 +12,7 @@ use App\Grpc\User\UserProfileRequest; use App\Grpc\User\UserServiceClient; use Exception; -use Grpc\ChannelCredentials; -use const Grpc\STATUS_OK; +use const App\Grpc\Controllers\STATUS_OK; class UserGrpcService { diff --git a/config/services.php b/config/services.php index 0cd0167..62f3984 100644 --- a/config/services.php +++ b/config/services.php @@ -40,5 +40,11 @@ 'product_url' => env('PRODUCT_SERVICE_URL', 'http://product-webserver/'), 'order_url' => env('ORDER_SERVICE_URL', 'http://order-webserver/'), 'ai_url' => env('AI_SERVICE_URL', 'http://ai-service/'), + ], + 'microservices_grpc' => [ + 'user_url' => env('USER_SERVICE_GRPC', 'user:50051'), + 'product_url' => env('PRODUCT_SERVICE_GRPC', 'product:50051'), + 'order_url' => env('ORDER_SERVICE_GRPC', 'order:50051'), + 'ai_url' => env('AI_SERVICE_GRPC', 'ai-service:50051'), ] ]; diff --git a/routes/grpc.php b/routes/grpc.php new file mode 100644 index 0000000..34bb6b2 --- /dev/null +++ b/routes/grpc.php @@ -0,0 +1,29 @@ +method('GetUser', 'UserController', 'show') + ->method('CreateUser', 'UserController', 'store') + ->method('UpdateUser', 'UserController', 'update') + ->method('DeleteUser', 'UserController', 'destroy') + ->method('ListUsers', 'UserController', 'index'); + +// Product Service Routes +GrpcRoute::service('ProductService') + ->method('listProducts', 'ProductController', 'index'); + +// Order Service Routes +GrpcRoute::service('OrderService') + ->method('GetOrder', 'OrderController', 'show') + ->method('CreateOrder', 'OrderController', 'store') + ->method('UpdateOrderStatus', 'OrderController', 'updateStatus') + ->method('ListOrders', 'OrderController', 'index'); + +// Product Service Routes +GrpcRoute::service('ProductService') + ->method('GetProduct', 'ProductController', 'show') + ->method('CreateProduct', 'ProductController', 'store') + ->method('UpdateProduct', 'ProductController', 'update') + ->method('DeleteProduct', 'ProductController', 'destroy'); From af53eeebc9073b8fb2d412e9865ee85e2bbb2df2 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 13 Jun 2025 04:03:49 +0300 Subject: [PATCH 33/49] Remove all gRPC-related implementations, configurations, and dependencies, including `UserGrpcService`, `GrpcTransformer`, generated proto files, routes, and service definitions; clean up associated Docker and service configuration files. --- Dockerfile | 5 +- app/Grpc/BaseResponse.php | 29 --- app/Grpc/Controllers/ProductController.php | 13 +- app/Grpc/GatewayServiceClient.php | 24 +-- app/Grpc/HealthCheckRequest.php | 58 ----- app/Grpc/HealthCheckResponse.php | 58 ----- .../HealthCheckResponse/ServingStatus.php | 56 ----- .../HealthCheckResponse_ServingStatus.php | 16 -- app/Grpc/HealthServiceClient.php | 34 --- app/Http/Controllers/AuthApiController.php | 2 - app/Http/Controllers/UserApiController.php | 5 +- app/Providers/GrpcServiceProvider.php | 4 - app/Services/Transformers/GrpcTransformer.php | 50 ----- app/Services/UserGrpcService.php | 204 ------------------ config/grpc.php | 16 -- config/services.php | 8 +- docker-compose.yml | 2 +- routes/grpc.php | 7 - 18 files changed, 31 insertions(+), 560 deletions(-) delete mode 100644 app/Grpc/BaseResponse.php delete mode 100644 app/Grpc/HealthCheckRequest.php delete mode 100644 app/Grpc/HealthCheckResponse.php delete mode 100644 app/Grpc/HealthCheckResponse/ServingStatus.php delete mode 100644 app/Grpc/HealthCheckResponse_ServingStatus.php delete mode 100644 app/Grpc/HealthServiceClient.php delete mode 100644 app/Services/Transformers/GrpcTransformer.php delete mode 100644 app/Services/UserGrpcService.php delete mode 100644 config/grpc.php diff --git a/Dockerfile b/Dockerfile index 72da1d4..3fe7db3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,7 @@ FROM php:8.4.4-fpm AS builder # Install system dependencies including protobuf RUN apt-get update && apt-get install -y \ + supervisor \ libpq-dev \ libzip-dev \ unzip \ @@ -93,10 +94,6 @@ RUN mkdir -p storage/logs storage/framework/{cache,sessions,views} bootstrap/cac && chmod -R 755 /var/www/storage \ && chmod -R 755 /var/www/bootstrap/cache -# Create a non-root user for security -RUN groupadd -g 1000 laravel \ - && useradd -u 1000 -g laravel -m laravel - # Expose ports EXPOSE 50051 8080 diff --git a/app/Grpc/BaseResponse.php b/app/Grpc/BaseResponse.php deleted file mode 100644 index 668c300..0000000 --- a/app/Grpc/BaseResponse.php +++ /dev/null @@ -1,29 +0,0 @@ - $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/Controllers/ProductController.php b/app/Grpc/Controllers/ProductController.php index fad7d60..6f79250 100644 --- a/app/Grpc/Controllers/ProductController.php +++ b/app/Grpc/Controllers/ProductController.php @@ -15,6 +15,17 @@ public function __construct( public function index(FlexibleRequest $request): FlexibleResponse { - return $this->grpcService->forwardToGrpcService($request); + $res = $this->grpcService->forwardToGrpcService($request); + \Log::info('ProductController index', [ + 'res' => $res, + 'data' => $res?->getData(), + 'dataValue' => $res?->getData()?->getValue(), + 'status' => $res?->getStatusCode(), + 'message' => $res?->getData()?->getValue(), + 'msgError' => $res?->getError()->getMessage(), + 'msgCode' => $res?->getError()->getCode(), + 'msgDetails' => $res?->getError()->getDetails(), + ]); + return $res; } } diff --git a/app/Grpc/GatewayServiceClient.php b/app/Grpc/GatewayServiceClient.php index 4da9065..dc9f317 100644 --- a/app/Grpc/GatewayServiceClient.php +++ b/app/Grpc/GatewayServiceClient.php @@ -6,12 +6,12 @@ /** * Gateway service definition */ -class GatewayServiceClient extends \App\Grpc\Controllers\BaseStub { +class GatewayServiceClient extends \Grpc\BaseStub { /** * @param string $hostname hostname * @param array $opts channel options - * @param \App\Grpc\Controllers\Channel $channel (optional) re-use channel object + * @param \Grpc\Channel $channel (optional) re-use channel object */ public function __construct($hostname, $opts, $channel = null) { parent::__construct($hostname, $opts, $channel); @@ -25,11 +25,11 @@ public function __construct($hostname, $opts, $channel = null) { * @return \App\Grpc\FlexibleResponse */ public function HandleRequest(\App\Grpc\FlexibleRequest $argument, - $metadata = [], $options = []) { + $metadata = [], $options = []) { return $this->_simpleRequest('/gateway.GatewayService/HandleRequest', - $argument, - ['\App\Grpc\FlexibleResponse', 'decode'], - $metadata, $options); + $argument, + ['\App\Grpc\FlexibleResponse', 'decode'], + $metadata, $options); } /** @@ -40,11 +40,11 @@ public function HandleRequest(\App\Grpc\FlexibleRequest $argument, * @return \App\Grpc\BytesResponse */ public function HandleBytesRequest(\App\Grpc\BytesRequest $argument, - $metadata = [], $options = []) { + $metadata = [], $options = []) { return $this->_simpleRequest('/gateway.GatewayService/HandleBytesRequest', - $argument, - ['\App\Grpc\BytesResponse', 'decode'], - $metadata, $options); + $argument, + ['\App\Grpc\BytesResponse', 'decode'], + $metadata, $options); } /** @@ -55,8 +55,8 @@ public function HandleBytesRequest(\App\Grpc\BytesRequest $argument, */ public function HandleStreamRequest($metadata = [], $options = []) { return $this->_bidiRequest('/gateway.GatewayService/HandleStreamRequest', - ['\App\Grpc\FlexibleResponse','decode'], - $metadata, $options); + ['\App\Grpc\FlexibleResponse','decode'], + $metadata, $options); } } diff --git a/app/Grpc/HealthCheckRequest.php b/app/Grpc/HealthCheckRequest.php deleted file mode 100644 index 262db2c..0000000 --- a/app/Grpc/HealthCheckRequest.php +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index 74d33a8..0000000 --- a/app/Grpc/HealthCheckResponse.php +++ /dev/null @@ -1,58 +0,0 @@ -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 deleted file mode 100644 index 1e1c32d..0000000 --- a/app/Grpc/HealthCheckResponse/ServingStatus.php +++ /dev/null @@ -1,56 +0,0 @@ -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 deleted file mode 100644 index 4e1274a..0000000 --- a/app/Grpc/HealthCheckResponse_ServingStatus.php +++ /dev/null @@ -1,16 +0,0 @@ -_simpleRequest('/gateway.HealthService/Check', - $argument, - ['\App\Grpc\HealthCheckResponse', 'decode'], - $metadata, $options); - } - -} diff --git a/app/Http/Controllers/AuthApiController.php b/app/Http/Controllers/AuthApiController.php index 538f364..6ebe198 100644 --- a/app/Http/Controllers/AuthApiController.php +++ b/app/Http/Controllers/AuthApiController.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers; use App\Grpc\Services\GrpcService; -use App\Services\UserGrpcService; use Exception; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -13,7 +12,6 @@ class AuthApiController extends BaseController { public function __construct( - private readonly UserGrpcService $userGrpcService, private readonly GrpcService $grpcService, ) { diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 83cbdd1..6f3b5f5 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -2,18 +2,15 @@ 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; -use Symfony\Component\HttpFoundation\Response; class UserApiController extends BaseController { public function __construct( - private readonly GrpcService $grpcService, + private readonly GrpcService $grpcService, ) { diff --git a/app/Providers/GrpcServiceProvider.php b/app/Providers/GrpcServiceProvider.php index ba79b0a..521b147 100644 --- a/app/Providers/GrpcServiceProvider.php +++ b/app/Providers/GrpcServiceProvider.php @@ -2,16 +2,12 @@ namespace App\Providers; -use App\Services\UserGrpcService; use Illuminate\Support\ServiceProvider; class GrpcServiceProvider extends ServiceProvider { public function register(): void { - $this->app->singleton(UserGrpcService::class, function ($app) { - return new UserGrpcService(); - }); $this->loadGrpcRoutes(); } diff --git a/app/Services/Transformers/GrpcTransformer.php b/app/Services/Transformers/GrpcTransformer.php deleted file mode 100644 index 3d45c78..0000000 --- a/app/Services/Transformers/GrpcTransformer.php +++ /dev/null @@ -1,50 +0,0 @@ - $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 deleted file mode 100644 index 239a849..0000000 --- a/app/Services/UserGrpcService.php +++ /dev/null @@ -1,204 +0,0 @@ -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/config/grpc.php b/config/grpc.php deleted file mode 100644 index 439eadd..0000000 --- a/config/grpc.php +++ /dev/null @@ -1,16 +0,0 @@ - [ - '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/services.php b/config/services.php index 62f3984..782d9e4 100644 --- a/config/services.php +++ b/config/services.php @@ -42,9 +42,9 @@ 'ai_url' => env('AI_SERVICE_URL', 'http://ai-service/'), ], 'microservices_grpc' => [ - 'user_url' => env('USER_SERVICE_GRPC', 'user:50051'), - 'product_url' => env('PRODUCT_SERVICE_GRPC', 'product:50051'), - 'order_url' => env('ORDER_SERVICE_GRPC', 'order:50051'), - 'ai_url' => env('AI_SERVICE_GRPC', 'ai-service:50051'), + 'UserService' => env('USER_SERVICE_GRPC', 'user:50051'), + 'ProductService' => env('PRODUCT_SERVICE_GRPC', 'product:50051'), + 'OrderService' => env('ORDER_SERVICE_GRPC', 'order:50051'), + 'AIService' => env('AI_SERVICE_GRPC', 'ai-service:50051'), ] ]; diff --git a/docker-compose.yml b/docker-compose.yml index 916cf69..32b328e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ services: api-gw: build: context: . - image: ommrgazar315/api-gw:2.0_grpc + image: ommrgazar315/api-gw:2.5_grpc container_name: api-gw restart: unless-stopped working_dir: /var/www diff --git a/routes/grpc.php b/routes/grpc.php index 34bb6b2..7de1366 100644 --- a/routes/grpc.php +++ b/routes/grpc.php @@ -20,10 +20,3 @@ ->method('CreateOrder', 'OrderController', 'store') ->method('UpdateOrderStatus', 'OrderController', 'updateStatus') ->method('ListOrders', 'OrderController', 'index'); - -// Product Service Routes -GrpcRoute::service('ProductService') - ->method('GetProduct', 'ProductController', 'show') - ->method('CreateProduct', 'ProductController', 'store') - ->method('UpdateProduct', 'ProductController', 'update') - ->method('DeleteProduct', 'ProductController', 'destroy'); From 1445922b1997e2c012384832cb6c7bbcc3ae492a Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 13 Jun 2025 05:02:21 +0300 Subject: [PATCH 34/49] Refactor gRPC-related components: update service names in `proxyRequest` calls, comment out excessive logs in `ProductController`, add error logging in `GatewayService`, and clean up unused imports. --- app/Grpc/Controllers/ProductController.php | 20 +++++++++---------- app/Grpc/Services/GatewayService.php | 2 +- app/Http/Controllers/AuthApiController.php | 2 +- app/Http/Controllers/ProductApiController.php | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/Grpc/Controllers/ProductController.php b/app/Grpc/Controllers/ProductController.php index 6f79250..fda513f 100644 --- a/app/Grpc/Controllers/ProductController.php +++ b/app/Grpc/Controllers/ProductController.php @@ -16,16 +16,16 @@ public function __construct( public function index(FlexibleRequest $request): FlexibleResponse { $res = $this->grpcService->forwardToGrpcService($request); - \Log::info('ProductController index', [ - 'res' => $res, - 'data' => $res?->getData(), - 'dataValue' => $res?->getData()?->getValue(), - 'status' => $res?->getStatusCode(), - 'message' => $res?->getData()?->getValue(), - 'msgError' => $res?->getError()->getMessage(), - 'msgCode' => $res?->getError()->getCode(), - 'msgDetails' => $res?->getError()->getDetails(), - ]); +// \Log::info('ProductController index', [ +// 'res' => $res, +// 'data' => $res?->getData(), +// 'dataValue' => $res?->getData()?->getValue(), +// 'status' => $res?->getStatusCode(), +// 'message' => $res?->getData()?->getValue(), +// 'msgError' => $res?->getError()->getMessage(), +// 'msgCode' => $res?->getError()->getCode(), +// 'msgDetails' => $res?->getError()->getDetails(), +// ]); return $res; } } diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php index bf64bae..2fe71fd 100644 --- a/app/Grpc/Services/GatewayService.php +++ b/app/Grpc/Services/GatewayService.php @@ -2,7 +2,6 @@ namespace App\Grpc\Services; -use App\Grpc\BytesRequest; use App\Grpc\BytesResponse; use App\Grpc\ErrorInfo; use App\Grpc\FlexibleRequest; @@ -116,6 +115,7 @@ public function HandleRequest($request): FlexibleResponse return $controller->{$route['action']}($request); } catch (Exception $e) { + Log::error("Failed to handle gRPC request", [$e->getMessage()]); return new FlexibleResponse([ 'status' => 'error', 'code' => 500, diff --git a/app/Http/Controllers/AuthApiController.php b/app/Http/Controllers/AuthApiController.php index 6ebe198..cbe0506 100644 --- a/app/Http/Controllers/AuthApiController.php +++ b/app/Http/Controllers/AuthApiController.php @@ -32,7 +32,7 @@ public function login(Request $request): JsonResponse try { // $res = $this->userGrpcService->authenticateUser($request->email, $request->password); return $this->grpcService->proxyRequest($request, - 'user', + 'UserService', 'login' ); // return $this->response($message, $data, Response::HTTP_OK, error: !$success); diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index c6c645e..43ff29b 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -37,7 +37,7 @@ public function index(Request $request) $request->merge($this->productService->injectUserId()); return $this->grpcService->proxyRequest( $request, - 'product', + 'ProductService', 'listProducts', ); // [$res, $status] = $this->productService->index($request->all()); From 8afc0d424f9f49e8e9fcbd814f6a367aa1481546 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 13 Jun 2025 05:25:17 +0300 Subject: [PATCH 35/49] Update `proxyRequest` calls to use proper gRPC service names (`UserService` and `ProductService`) across controllers for consistency and clarity. --- app/Http/Controllers/AuthApiController.php | 4 ++-- app/Http/Controllers/OfferApiController.php | 4 ++-- app/Http/Controllers/ProductApiController.php | 2 +- app/Http/Controllers/ShoppingCartApiController.php | 8 ++++---- app/Http/Controllers/UserApiController.php | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/AuthApiController.php b/app/Http/Controllers/AuthApiController.php index cbe0506..b75cc6c 100644 --- a/app/Http/Controllers/AuthApiController.php +++ b/app/Http/Controllers/AuthApiController.php @@ -48,7 +48,7 @@ public function signup(Request $request): Response try { // $res = $this->userGrpcService->createUser($request->all()); return $this->grpcService->proxyRequest($request, - 'user', + 'UserService', 'create' ); } catch (Exception $e) { @@ -64,7 +64,7 @@ function logout(Request $request) try { return $this->grpcService->proxyRequest( $request, - 'user', + 'UserService', 'logout' ); // $success = $res['success']; diff --git a/app/Http/Controllers/OfferApiController.php b/app/Http/Controllers/OfferApiController.php index efa460c..a06f83a 100644 --- a/app/Http/Controllers/OfferApiController.php +++ b/app/Http/Controllers/OfferApiController.php @@ -26,7 +26,7 @@ public function index(Request $request) $request->merge($this->offerService->injectUserId()); return $this->grpcService->proxyRequest( $request, - 'product', + 'ProductService', 'listOffers', ); // try { @@ -54,7 +54,7 @@ public function show($id) request()->merge(['id' => $id]); return $this->grpcService->proxyRequest( request(), - 'product', + 'ProductService', 'getOffer', ); // [$res, $status] = $this->offerService->show($id); diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index 43ff29b..b534c5c 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -59,7 +59,7 @@ public function show(int $product_id) request()->merge(['id' => $product_id]); return $this->grpcService->proxyRequest( request(), - 'product', + 'ProductService', 'getProduct', ); // [$res, $status] = $this->productService->show($product_id); diff --git a/app/Http/Controllers/ShoppingCartApiController.php b/app/Http/Controllers/ShoppingCartApiController.php index e165afa..51fe85d 100644 --- a/app/Http/Controllers/ShoppingCartApiController.php +++ b/app/Http/Controllers/ShoppingCartApiController.php @@ -22,7 +22,7 @@ public function index(Request $request) { return $this->grpcService->proxyRequest( $request, - 'user', + 'UserService', 'getCart', ); // $res = http::withToken(request()->bearerToken())->get(UserApi::getBaseUrl() . 'api/carts'); @@ -36,7 +36,7 @@ public function add(Request $request) { return $this->grpcService->proxyRequest( $request, - 'user', + 'UserService', 'addToCart', ); // $res = http::withToken(request()->bearerToken())->post(UserApi::getBaseUrl() . "api/carts", $request->all()); @@ -52,7 +52,7 @@ public function remove($item_type, $item_id) \request()->merge(['item_type' => $item_type, 'item_id' => $item_id]); return $this->grpcService->proxyRequest( request(), - 'user', + 'UserService', 'removeFromCart' ); // $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts/$item_type/$item_id"); @@ -66,7 +66,7 @@ public function clear() { return $this->grpcService->proxyRequest( request(), - 'user', + 'UserService', 'clearCart' ); // $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts"); diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index 6f3b5f5..afd950e 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -36,7 +36,7 @@ public function index(Request $request) { // $res = $this->userGrpcService->listUsers(); return $this->grpcService->proxyRequest(request(), - 'user', + 'UserService', 'list', ); } @@ -65,7 +65,7 @@ public function show($id) request()->merge(['id' => $id]); return $this->grpcService->proxyRequest(request(), - 'user', + 'UserService', 'get', ); @@ -103,7 +103,7 @@ public function destroy(string $id) public function userProfile(Request $request) { return $this->grpcService->proxyRequest($request, - 'user', + 'UserService', 'GetUserProfile' ); // return $this->response($message, $data, Response::HTTP_OK, error: !$success); From 6fed70a7454db110760c9addcb29ccb947a0eb91 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 13 Jun 2025 14:46:29 +0300 Subject: [PATCH 36/49] Refactor ProductController and gRPC routes: update proxyRequest to use ProductService, enhance response handling in index method, and add show method for product retrieval. --- app/Grpc/Controllers/ProductController.php | 40 +- app/Grpc/Servers/BaseServer.php | 211 ----------- app/Grpc/Servers/CartServer.php | 133 ------- app/Grpc/Servers/UserServer.php | 357 ------------------ app/Http/Controllers/ApiGatewayController.php | 25 +- routes/grpc.php | 3 +- 6 files changed, 45 insertions(+), 724 deletions(-) delete mode 100644 app/Grpc/Servers/BaseServer.php delete mode 100644 app/Grpc/Servers/CartServer.php delete mode 100644 app/Grpc/Servers/UserServer.php diff --git a/app/Grpc/Controllers/ProductController.php b/app/Grpc/Controllers/ProductController.php index fda513f..898bed7 100644 --- a/app/Grpc/Controllers/ProductController.php +++ b/app/Grpc/Controllers/ProductController.php @@ -2,30 +2,42 @@ namespace App\Grpc\Controllers; +use App\External_Apis\Services\ProductService; use App\Grpc\FlexibleRequest; use App\Grpc\FlexibleResponse; +use App\Grpc\Services\GrpcService; +use App\Http\Middleware\AuthenticateMiddleware; +use Illuminate\Routing\Controllers\HasMiddleware; +use Illuminate\Routing\Controllers\Middleware; -class ProductController +class ProductController implements HasMiddleware { public function __construct( - private readonly \App\Grpc\Services\GrpcService $grpcService, + private readonly GrpcService $grpcService, + private readonly ProductService $productService ) { } + public static function middleware(): array + { + return [ + new Middleware(AuthenticateMiddleware::class), + ]; + } + public function index(FlexibleRequest $request): FlexibleResponse { - $res = $this->grpcService->forwardToGrpcService($request); -// \Log::info('ProductController index', [ -// 'res' => $res, -// 'data' => $res?->getData(), -// 'dataValue' => $res?->getData()?->getValue(), -// 'status' => $res?->getStatusCode(), -// 'message' => $res?->getData()?->getValue(), -// 'msgError' => $res?->getError()->getMessage(), -// 'msgCode' => $res?->getError()->getCode(), -// 'msgDetails' => $res?->getError()->getDetails(), -// ]); - return $res; + if ($payload = $request->getPayload()) { + $data = json_decode($payload->getValue(), true); + $data = array_merge($data, $this->productService->injectUserId()); + $request->setPayload($payload->setValue(json_encode($data))); + } + return $this->grpcService->forwardToGrpcService($request); + } + + public function show(FlexibleRequest $request): FlexibleResponse + { + return $this->grpcService->forwardToGrpcService($request); } } diff --git a/app/Grpc/Servers/BaseServer.php b/app/Grpc/Servers/BaseServer.php deleted file mode 100644 index 244bea4..0000000 --- a/app/Grpc/Servers/BaseServer.php +++ /dev/null @@ -1,211 +0,0 @@ - new MethodDescriptor( - $this, - 'HandleRequest', - FlexibleRequest::class, - MethodDescriptor::UNARY_CALL, - ), - '/gateway.GatewayService/HandleBytesRequest' => new MethodDescriptor( - $this, - 'HandleBytesRequest', - BytesRequest::class, - MethodDescriptor::UNARY_CALL, - ), - '/gateway.GatewayService/HandleStreamRequest' => new MethodDescriptor( - $this, - 'HandleStreamRequest', - FlexibleRequest::class, - MethodDescriptor::BIDI_STREAMING_CALL - ) - ]; - } - - public function HandleRequest(FlexibleRequest $in, ServerContext $context): FlexibleResponse - { - self::setContext($context); - $response = new FlexibleResponse(); - $response->setRequestId($in->getRequestId()); - $response->setTimestamp($this->getCurrentTimestamp()); - try { - return $this->manageOperation($in, $response); - - } catch (Exception $e) { - Log::error('server error', [ - 'method' => $in->getMethod(), - 'error' => $e->getMessage(), - 'trace' => $e->getTrace(), - ]); - - self::getContext()->setStatus([ - 'code' => $e->getCode() ?: STATUS_INTERNAL, - 'details' => $e->getMessage() - ]); - return $this->createErrorResponse($response, Response::HTTP_INTERNAL_SERVER_ERROR, STATUS_INTERNAL, $e->getMessage()); - } - - } - - /** - * Handle bytes requests - for JSON/REST compatibility - */ - public function HandleBytesRequest(BytesRequest $in): BytesResponse - { - $response = new BytesResponse(); - $response->setRequestId($in->getRequestId()); - $response->setTimestamp($this->getCurrentTimestamp()); - - try { - // Parse JSON payload - $data = json_decode($in->getPayload(), true); - if (json_last_error() !== JSON_ERROR_NONE) { - return $this->createBytesErrorResponse($response, 400, 'INVALID_JSON', 'Invalid JSON payload'); - } - - // Route and process - $result = $this->processUserRequest($in->getMethod(), $data); - - $response->setSuccess(true); - $response->setStatusCode(200); - $response->setData(json_encode($result)); - $response->setContentType('application/json'); - - } catch (Exception $e) { - Log::error('User service bytes request error', [ - 'method' => $in->getMethod(), - 'error' => $e->getMessage() - ]); - - return $this->createBytesErrorResponse($response, 500, 'INTERNAL_ERROR', $e->getMessage()); - } - - return $response; - } - -// /** -// * Handle streaming (not implemented for this example) -// */ -// public function HandleStreamRequest(Iterator $in, ServerContext $context): Iterator -// { -// foreach ($in as $request) { -// yield $this->HandleRequest($request, $context); -// } -// } - - /** - * Create error response - */ - public function createErrorResponse(FlexibleResponse $response, int $statusCode, $code, string $message, ?array $errorMsg = null): FlexibleResponse - { - UserServer::getContext()->setStatus([ - 'code' => $code, - 'details' => $message - ]); - $error = new ErrorInfo(); - $error->setCode($code); - $error->setMessage($message); - - if (!is_null($errorMsg)) { - $any = new Any(); - $any->setValue(json_encode($errorMsg)); - $response->setData($any); - } - - $response->setSuccess(false); - $response->setStatusCode($statusCode); - $response->setError($error); - - return $response; - } - - /** - * Create bytes error response - */ - public 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 - */ - public function getCurrentTimestamp(): Timestamp - { - $timestamp = new Timestamp(); - $timestamp->fromDateTime(new DateTime()); - return $timestamp; - } - - private function processUserRequest(string $method, array $data): array - { - switch ($method) { - case 'create': - $user = User::create($data); - return $user->toArray(); - - case 'get': - $user = User::findOrFail($data['id']); - return $user->toArray(); - - case 'list': - return User::paginate($data['per_page'] ?? 15)->toArray(); - - default: - throw new Exception('Method not supported in bytes handler'); - } - } - - /** - * @param array $data - * @param FlexibleResponse $response - */ - protected function createResponse(array $data, FlexibleResponse $response, $code = 200): FlexibleResponse - { - $responseData = new Any(); - $responseData->setValue(json_encode($data)); - $response->setData($responseData); - $response->setSuccess(true); - $response->setStatusCode($code); - return $response; - } - - /** - * @param FlexibleRequest $in - * @param FlexibleResponse $response - * @return FlexibleResponse - */ - abstract function manageOperation(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse; -} diff --git a/app/Grpc/Servers/CartServer.php b/app/Grpc/Servers/CartServer.php deleted file mode 100644 index 8962dd3..0000000 --- a/app/Grpc/Servers/CartServer.php +++ /dev/null @@ -1,133 +0,0 @@ -getMethod()) { - 'addToCart' => $this->addToCart($in), -// 'getCart' => $this->getCart($in, $response), -// 'removeFromCart' => $this->removeFromCart($in, $response), -// 'clearCart' => $this->clearCart($in, $response), - }; - } - - /** - * @throws \Exception - */ - private function addToCart(FlexibleRequest $in): FlexibleResponse - { - return $this->grpcService->forwardToGrpcService($in); - } - -// /** -// * @throws BindingResolutionException -// * @throws ConnectionException -// */ -// private function getCart(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse -// { -// $jsonData = $in->getPayload()->getValue(); -// $data = json_decode($jsonData, true); -// -// $cart = $this->shoppingCartGatewayService->getCart(auth()->id()); -// if (!$cart) { -// Log::error('Get cart failed', [ -// 'user_id' => auth()->id(), -// ]); -// return $this->createErrorResponse( -// $response, -// Response::HTTP_NOT_FOUND, -// STATUS_INVALID_ARGUMENT, -// 'Cart not found' -// ); -// } -// -// return $this->createResponse([ -// 'message' => 'Cart retrieved successfully', -// 'data' => $cart -// ], -// $response -// ); -// } -// -// private function removeFromCart(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse -// { -// $jsonData = $in->getPayload()->getValue(); -// $data = json_decode($jsonData, true); -// $rules = [ -// 'item_id' => 'required|integer', -// 'item_type' => 'required|string|in:products,offers', -// ]; -// $validator = Validator::make($data, $rules); -// if ($validator->fails()) { -// Log::error('Remove from cart validation error', [ -// 'errors' => $validator->errors() -// ]); -// return $this->createErrorResponse( -// $response, -// Response::HTTP_BAD_REQUEST, -// STATUS_INVALID_ARGUMENT, -// $validator->errors()->first() -// ); -// } -// try { -// $this->cartCacheService->remove( -// auth()->id(), -// data_get($data, 'item_type'), -// data_get($data, 'item_id'), -// ); -// return $this->createResponse(['message' => 'Item removed from cart successfully'], $response); -// } catch (\Exception $e) { -// Log::error('Remove from cart cache error', [ -// 'user_id' => auth()->id(), -// 'item_id' => data_get($data, 'item_id'), -// 'item_type' => data_get($data, 'item_type'), -// 'error' => $e->getMessage() -// ]); -// return $this->createErrorResponse( -// $response, -// Response::HTTP_NOT_FOUND, -// STATUS_INVALID_ARGUMENT, -// 'Item not found in cart' -// ); -// } -// } -// -// private function clearCart(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse -// { -// try { -// $this->cartCacheService->clear(auth()->id()); -// return $this->createResponse(['message' => 'Cart cleared successfully'], $response); -// } catch (\Exception $e) { -// Log::error('Clear cart cache error', [ -// 'user_id' => auth()->id(), -// 'error' => $e->getMessage() -// ]); -// return $this->createErrorResponse( -// $response, -// Response::HTTP_INTERNAL_SERVER_ERROR, -// STATUS_INVALID_ARGUMENT, -// 'Failed to clear cart' -// ); -// } -// } - -} diff --git a/app/Grpc/Servers/UserServer.php b/app/Grpc/Servers/UserServer.php deleted file mode 100644 index 88f444c..0000000 --- a/app/Grpc/Servers/UserServer.php +++ /dev/null @@ -1,357 +0,0 @@ -getMethod()) { - 'create' => $this->createUser($in), -// 'login' => $this->login($in, $response), -// 'get' => $this->getUser($in, $response), -// 'update' => $this->updateUser($in, $response), -// 'delete' => $this->deleteUser($in, $response), -// 'list' => $this->listUsers($in, $response), -// 'GetUserProfile' => $this->GetUserProfile($in, $response), -// 'logout' => $this->logout($in, $response), - default => null - }; - - if (!$isMatched) - $isMatched = app(CartServer::class)->manageOperation($in, $response); - - if ($isMatched) return $isMatched; - return $this->createErrorResponse($response, Response::HTTP_INTERNAL_SERVER_ERROR, STATUS_UNIMPLEMENTED, 'Method not found: ' . $in->getMethod()); - } - - /** - * Create user - * @throws Exception - */ - private function createUser(FlexibleRequest $request): FlexibleResponse - { - return $this->grpcService->forwardToGrpcService($request); - } - - /** - * Get user by ID - * @throws Exception - */ - private - function getUser(FlexibleRequest $request, FlexibleResponse $response): FlexibleResponse - { - $jsonData = $request->getPayload()->getValue(); - $data = json_decode($jsonData, true); - - $validator = Validator::make($data, [ - 'id' => 'required|integer|min:1' - ]); - $validator->validate(); - if ($validator->fails()) { - $this->createErrorResponse( - $response, - Response::HTTP_BAD_REQUEST, - STATUS_INVALID_ARGUMENT, - $validator->errors() - ); - return $response; -// throw new Exception($validator->errors(), STATUS_INVALID_ARGUMENT); - } - $user = $this->userService->show($data['id']); - $user = $user['data']; - if (!$user) { - $this->createErrorResponse( - $response, - Response::HTTP_NOT_FOUND, - STATUS_NOT_FOUND, - 'User not found: ' . ($data['id'] ?? 'unknown') - ); - return $response; -// throw new Exception($message, STATUS_NOT_FOUND); - } - - $responseData = new Any(); - $resource = new UserResource($user); - $responseData->setValue(json_encode([ - 'data' => $resource - ])); - - $response->setSuccess(true); - $response->setStatusCode(200); - $response->setData($responseData); - - - return $response; - } - - /** - * Update user - */ - private - function updateUser(FlexibleRequest $request, FlexibleResponse $response): FlexibleResponse - { - $jsonData = $request->getPayload()->getValue(); - $data = json_decode($jsonData, true); - - $user = User::find($data['id'] ?? null); - - if (!$user) { - return $this->createErrorResponse($response, 404, 'USER_NOT_FOUND', 'User not found'); - } - - // Validate update data - $validator = Validator::make($data, [ - 'name' => 'sometimes|string|max:255', - 'email' => 'sometimes|email|unique:users,email,' . $user->id, - ]); - - if ($validator->fails()) { - return $this->createErrorResponse($response, 400, 'VALIDATION_ERROR', $validator->errors()->first()); - } - - $user->update(array_intersect_key($data, array_flip(['name', 'email']))); - - $responseData = new Any(); - $responseData->setValue(json_encode([ - 'data' => new UserResource($user) - ])); - - $response->setSuccess(true); - $response->setStatusCode(200); - $response->setData($responseData); - - return $response; - } - - /** - * Delete user - */ - private - function deleteUser(FlexibleRequest $request, FlexibleResponse $response): FlexibleResponse - { - $jsonData = $request->getPayload()->getValue(); - $data = json_decode($jsonData, true); - - $user = User::find($data['id'] ?? null); - - if (!$user) { - return $this->createErrorResponse($response, 404, 'USER_NOT_FOUND', 'User not found'); - } - - $user->delete(); - - $response->setSuccess(true); - $response->setStatusCode(204); - - - return $response; - } - - /** - * List users with pagination - * @throws Exception - */ - private - function listUsers(FlexibleRequest $request, FlexibleResponse $response): FlexibleResponse - { - $jsonData = $request->getPayload()->getValue(); - $filters = json_decode($jsonData, true) ?? []; - try { - $users = $this->userService->index(); - $users = data_get($users, 'data'); - $responseData = new Any(); - $resourceCollection = UserResource::collection($users); - $responseData->setValue(json_encode([ - 'data' => $resourceCollection, -// 'pagination' => [ -// 'current_page' => $users->currentPage(), -// 'total_pages' => $users->lastPage(), -// 'total_items' => $users->total(), -// 'per_page' => $users->perPage() -// ] - ])); - - $response->setSuccess(true); - $response->setStatusCode(200); - \Log::info('User listing successful', [ - 'total_users' => count($users), - 'users' => $users - ]); - $response->setData($responseData); - } catch (Exception $e) { - Log::error('User listing error', [ - 'error' => $e->getMessage(), - 'trace' => $e->getTrace() - ]); - return $this->createErrorResponse($response, Response::HTTP_UNAUTHORIZED, STATUS_UNAUTHENTICATED, 'Failed to list users: ' . $e->getMessage()); -// throw new Exception('Failed to user-service: ' . $e->getMessage(), STATUS_INTERNAL); - } - return $response; - } - - /** - * @throws Exception - */ - private - function login(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse - { - $jsonData = $in->getPayload()->getValue(); - $data = json_decode($jsonData, true); - $rules = new UserReadRequest()->rules(); - $validator = Validator::make($data, $rules); - if ($validator->fails()) { - Log::error('User login validation error', [ - 'errors' => $validator->errors() - ]); - self::getContext()->setStatus([ - 'code' => STATUS_INVALID_ARGUMENT, - 'details' => $validator->errors() - ]); - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - $response->setError(new ErrorInfo([ - 'message' => $validator->errors() - ])); - $response->setSuccess(false); - return $response; -// throw new Exception($validator->errors(), STATUS_INVALID_ARGUMENT); - } - - $user = $this->userService->login($data); - - if (!$user) { - self::getContext()->setStatus([ - 'code' => STATUS_UNAUTHENTICATED, - 'details' => 'Invalid credentials' - ]); - $response->setError(new ErrorInfo([ - 'message' => 'Invalid credentials' - ])); - $response->setStatusCode(Response::HTTP_UNAUTHORIZED); - $response->setSuccess(false); - return $response; -// throw new Exception ('Invalid credentials', STATUS_UNAUTHENTICATED); - } - - // Pack user data into Any - $userResource = new UserResource($user); - $anyData = new Any(); - $anyData->setValue(json_encode([ - 'message' => 'Login successful', - 'data' => $userResource - ])); - - $response->setSuccess(true); - $response->setStatusCode(200); - $response->setData($anyData); - - return $response; - } - - /** - * @throws BindingResolutionException - * @throws ConnectionException - */ - private - function GetUserProfile(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse - { - $jsonData = $in->getPayload()->getValue(); - $data = json_decode($jsonData, true); - $this->authMiddleware->authenticateGrpcRequest(self::$context->clientMetadata()); - $user = $this->userService->userProfile($data); - \Log::info('GetUserProfile called', [ - 'data' => $data, - 'user_id' => $data['id'] ?? null, - 'user' => $user - ]); - - if (!$user) { - $this->createErrorResponse( - $response, - Response::HTTP_NOT_FOUND, - STATUS_NOT_FOUND, - 'User not found' - ); - return $response; - } - - // Pack user data into Any - $anyData = new Any(); - $anyData->setValue(json_encode([ - 'data' => $user - ])); - - $response->setSuccess(true); - $response->setStatusCode(200); - $response->setData($anyData); - - return $response; - } - - - private - function logout(FlexibleRequest $in, FlexibleResponse $response): FlexibleResponse - { - try { - $this->authMiddleware->revokeGrpcToken(self::$context->clientMetadata()); - return $this->createResponse( - [], - $response, - Response::HTTP_NO_CONTENT, - ); - } catch (Exception $e) { - Log::error('Logout error', [ - 'error' => $e->getMessage(), - 'trace' => $e->getTrace() - ]); - return $this->createErrorResponse($response, Response::HTTP_UNAUTHORIZED, STATUS_UNAUTHENTICATED, $isLoggedOut['error'] ?? 'Unauthenticated'); - } - } -} diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php index ee7435c..8f8b389 100644 --- a/app/Http/Controllers/ApiGatewayController.php +++ b/app/Http/Controllers/ApiGatewayController.php @@ -34,24 +34,33 @@ public function getRecommendedProducts(Request $request) if ($res->failed()) throw new ConnectionException('Failed to connect to AI API', $res->status()); - $recommendedProductData = $res->json('data', []); + $recommendedProductData = $res->json(); $request->merge($this->productService->injectUserId()); - $request->merge(['recommended_product_ids' => array_column($recommendedProductData, 'id')]); + if(!$recommendedProductIds = array_column($recommendedProductData, 'id')) + return response()->json([ + 'message' => 'No recommended products found', + 'data' => [] + ], Response::HTTP_OK); + + $request->merge(['recommended_product_ids' => $recommendedProductIds]); $res = $this->grpcService->proxyRequest( $request, - 'product', + 'ProductService', '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', [])); + $filteredProductData= data_get($filteredProductData, 'data'); + + foreach ($filteredProductData as $index => &$productData) { + $productData['percentage'] = $recommendedProductData[$index]['compatibility_score'] ?? 0; + $productData['percentage'] = round($productData['percentage'], 2)* 100; // Convert to percentage + $productData['percentage'] = number_format($productData['percentage'], 2) . '%'; + } return $res->setData($filteredProductData); } else - throw new ConnectionException('Failed to fetch products', $res->getStatusCode()); + throw new ConnectionException('Failed to fetch products', [$res]); } catch (ConnectionException $e) { return response()->json([ 'message' => 'Connection error', diff --git a/routes/grpc.php b/routes/grpc.php index 7de1366..3384b61 100644 --- a/routes/grpc.php +++ b/routes/grpc.php @@ -12,7 +12,8 @@ // Product Service Routes GrpcRoute::service('ProductService') - ->method('listProducts', 'ProductController', 'index'); + ->method('listProducts', 'ProductController', 'index') + ->method('getProduct', 'ProductController', 'show'); // Order Service Routes GrpcRoute::service('OrderService') From c584020d66fcd4f54dea94521bb23d46e89b659c Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 13 Jun 2025 16:37:36 +0300 Subject: [PATCH 37/49] Enhance error handling in ApiGatewayController: return a specific JSON response for HTTP_NOT_FOUND status when no purchased products are found. --- app/Http/Controllers/ApiGatewayController.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php index 8f8b389..35858ef 100644 --- a/app/Http/Controllers/ApiGatewayController.php +++ b/app/Http/Controllers/ApiGatewayController.php @@ -32,6 +32,11 @@ public function getRecommendedProducts(Request $request) ]); if ($res->failed()) + if($res->getStatusCode() == Response::HTTP_NOT_FOUND) + return response()->json([ + 'message' => $res->json('details', 'No purchased products found'), + ], $res->getStatusCode()); + else throw new ConnectionException('Failed to connect to AI API', $res->status()); $recommendedProductData = $res->json(); From 2c7901dc649bad3fa6553fdd49590924d2c7b892 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Fri, 13 Jun 2025 20:16:56 +0300 Subject: [PATCH 38/49] Add AuthController with gRPC methods, refactor UserController to extend BaseGrpcController, and update configurations for gRPC service integration. --- app/Grpc/Controllers/AuthController.php | 31 +++++++++++++++++++++ app/Grpc/Controllers/BaseGrpcController.php | 20 +++++++++++++ app/Grpc/Controllers/UserController.php | 5 +++- config/services.php | 1 + docker-compose.yml | 4 +++ routes/grpc.php | 7 ++++- 6 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 app/Grpc/Controllers/AuthController.php create mode 100644 app/Grpc/Controllers/BaseGrpcController.php diff --git a/app/Grpc/Controllers/AuthController.php b/app/Grpc/Controllers/AuthController.php new file mode 100644 index 0000000..d276ffc --- /dev/null +++ b/app/Grpc/Controllers/AuthController.php @@ -0,0 +1,31 @@ +setSpecifiedService($request, $this->defaultService); + return $this->grpcService->forwardToGrpcService($request); + } + + public function login(FlexibleRequest $request): FlexibleResponse + { + $this->setSpecifiedService($request, $this->defaultService); + return $this->grpcService->forwardToGrpcService($request); + } + + +} diff --git a/app/Grpc/Controllers/BaseGrpcController.php b/app/Grpc/Controllers/BaseGrpcController.php new file mode 100644 index 0000000..8a0e213 --- /dev/null +++ b/app/Grpc/Controllers/BaseGrpcController.php @@ -0,0 +1,20 @@ +getService(); + $services = explode('-', $service); + $service = $services[1] ?? $default ?? $services[0]; + \Log::info("Forwarding request to service: $service, method: {$request->getMethod()}"); + $request->setService($service); + } +} diff --git a/app/Grpc/Controllers/UserController.php b/app/Grpc/Controllers/UserController.php index fb274b9..cb542a7 100644 --- a/app/Grpc/Controllers/UserController.php +++ b/app/Grpc/Controllers/UserController.php @@ -5,7 +5,7 @@ use App\Grpc\FlexibleRequest; use App\Grpc\FlexibleResponse; -class UserController +class UserController extends BaseGrpcController { public function __construct( private readonly \App\Grpc\Services\GrpcService $grpcService, @@ -15,6 +15,9 @@ public function __construct( public function createUser(FlexibleRequest $request): FlexibleResponse { + $this->setSpecifiedService($request); return $this->grpcService->forwardToGrpcService($request); } + + } diff --git a/config/services.php b/config/services.php index 782d9e4..a671c62 100644 --- a/config/services.php +++ b/config/services.php @@ -41,6 +41,7 @@ 'order_url' => env('ORDER_SERVICE_URL', 'http://order-webserver/'), 'ai_url' => env('AI_SERVICE_URL', 'http://ai-service/'), ], + 'microservices_grpc' => [ 'UserService' => env('USER_SERVICE_GRPC', 'user:50051'), 'ProductService' => env('PRODUCT_SERVICE_GRPC', 'product:50051'), diff --git a/docker-compose.yml b/docker-compose.yml index 32b328e..e4b07ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,6 +35,10 @@ services: - USER_SERVICE_URL=http://user-webserver/ - ORDER_SERVICE_URL=http://order-webserver/ - AI_SERVICE_URL=http://ai-service/ + - USER_SERVICE_GRPC=user:50051 + - PRODUCT_SERVICE_GRPC=product:50051 + - ORDER_SERVICE_GRPC=order:50051 + - AI_SERVICE_GRPC=ai-service:50051 api-gw_db: image: mysql:8.0 diff --git a/routes/grpc.php b/routes/grpc.php index 3384b61..2eb66b8 100644 --- a/routes/grpc.php +++ b/routes/grpc.php @@ -4,8 +4,13 @@ // User Service Routes GrpcRoute::service('UserService') + //Auth Service Methods + ->method('Login', 'AuthController', 'login') + ->method('SignUp', 'AuthController', 'signup') + ->method('Logout', 'AuthController', 'logout') + ->method('ValidateToken', 'AuthController', 'validateToken') + // User Service Methods ->method('GetUser', 'UserController', 'show') - ->method('CreateUser', 'UserController', 'store') ->method('UpdateUser', 'UserController', 'update') ->method('DeleteUser', 'UserController', 'destroy') ->method('ListUsers', 'UserController', 'index'); From 8eda8162272558321904ca0766f169815a6c43b0 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 00:43:57 +0300 Subject: [PATCH 39/49] Refactor gRPC service integration: replace `proxyRequest` with `createClientRequest`, rename `forwardToGrpcService` to `forwardGrpcToService`, and update methods across controllers and services for consistency. --- app/Grpc/Controllers/AuthController.php | 9 +++++++-- app/Grpc/Controllers/ProductController.php | 4 ++-- app/Grpc/Controllers/UserController.php | 3 +-- app/Grpc/Services/GrpcService.php | 4 ++-- app/Http/Controllers/ApiGatewayController.php | 2 +- app/Http/Controllers/AuthApiController.php | 6 +++--- app/Http/Controllers/OfferApiController.php | 4 ++-- app/Http/Controllers/ProductApiController.php | 4 ++-- app/Http/Controllers/ShoppingCartApiController.php | 8 ++++---- app/Http/Controllers/UserApiController.php | 6 +++--- app/Http/Middleware/AuthenticateMiddleware.php | 14 ++++++++++++-- 11 files changed, 39 insertions(+), 25 deletions(-) diff --git a/app/Grpc/Controllers/AuthController.php b/app/Grpc/Controllers/AuthController.php index d276ffc..d14d9df 100644 --- a/app/Grpc/Controllers/AuthController.php +++ b/app/Grpc/Controllers/AuthController.php @@ -18,14 +18,19 @@ public function __construct( public function signup(FlexibleRequest $request): FlexibleResponse { $this->setSpecifiedService($request, $this->defaultService); - return $this->grpcService->forwardToGrpcService($request); + return $this->grpcService->forwardGrpcToService($request); } public function login(FlexibleRequest $request): FlexibleResponse { $this->setSpecifiedService($request, $this->defaultService); - return $this->grpcService->forwardToGrpcService($request); + return $this->grpcService->forwardGrpcToService($request); } + public function validateToken(FlexibleRequest $request): FlexibleResponse + { + $this->setSpecifiedService($request, $this->defaultService); + return $this->grpcService->forwardGrpcToService($request); + } } diff --git a/app/Grpc/Controllers/ProductController.php b/app/Grpc/Controllers/ProductController.php index 898bed7..662e738 100644 --- a/app/Grpc/Controllers/ProductController.php +++ b/app/Grpc/Controllers/ProductController.php @@ -33,11 +33,11 @@ public function index(FlexibleRequest $request): FlexibleResponse $data = array_merge($data, $this->productService->injectUserId()); $request->setPayload($payload->setValue(json_encode($data))); } - return $this->grpcService->forwardToGrpcService($request); + return $this->grpcService->forwardGrpcToService($request); } public function show(FlexibleRequest $request): FlexibleResponse { - return $this->grpcService->forwardToGrpcService($request); + return $this->grpcService->forwardGrpcToService($request); } } diff --git a/app/Grpc/Controllers/UserController.php b/app/Grpc/Controllers/UserController.php index cb542a7..c3bd06f 100644 --- a/app/Grpc/Controllers/UserController.php +++ b/app/Grpc/Controllers/UserController.php @@ -16,8 +16,7 @@ public function __construct( public function createUser(FlexibleRequest $request): FlexibleResponse { $this->setSpecifiedService($request); - return $this->grpcService->forwardToGrpcService($request); + return $this->grpcService->forwardGrpcToService($request); } - } diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index d8cb7cd..8506c48 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -16,7 +16,7 @@ public function __construct( { } - public function proxyRequest(Request $request, string $service, string $method): JsonResponse + public function createClientRequest(Request $request, string $service, string $method): JsonResponse { // Convert HTTP request to gRPC $grpcRequest = new FlexibleRequest(); @@ -48,7 +48,7 @@ public function proxyRequest(Request $request, string $service, string $method): } } - public function forwardToGrpcService(FlexibleRequest $request): FlexibleResponse + public function forwardGrpcToService(FlexibleRequest $request): FlexibleResponse { return $this->gatewayService->HandleClientRequest($request); } diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php index 35858ef..4e2020e 100644 --- a/app/Http/Controllers/ApiGatewayController.php +++ b/app/Http/Controllers/ApiGatewayController.php @@ -48,7 +48,7 @@ public function getRecommendedProducts(Request $request) ], Response::HTTP_OK); $request->merge(['recommended_product_ids' => $recommendedProductIds]); - $res = $this->grpcService->proxyRequest( + $res = $this->grpcService->createClientRequest( $request, 'ProductService', 'listProducts', diff --git a/app/Http/Controllers/AuthApiController.php b/app/Http/Controllers/AuthApiController.php index b75cc6c..094b150 100644 --- a/app/Http/Controllers/AuthApiController.php +++ b/app/Http/Controllers/AuthApiController.php @@ -31,7 +31,7 @@ public function login(Request $request): JsonResponse { try { // $res = $this->userGrpcService->authenticateUser($request->email, $request->password); - return $this->grpcService->proxyRequest($request, + return $this->grpcService->createClientRequest($request, 'UserService', 'login' ); @@ -47,7 +47,7 @@ public function signup(Request $request): Response { try { // $res = $this->userGrpcService->createUser($request->all()); - return $this->grpcService->proxyRequest($request, + return $this->grpcService->createClientRequest($request, 'UserService', 'create' ); @@ -62,7 +62,7 @@ public function signup(Request $request): Response function logout(Request $request) { try { - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( $request, 'UserService', 'logout' diff --git a/app/Http/Controllers/OfferApiController.php b/app/Http/Controllers/OfferApiController.php index a06f83a..3699123 100644 --- a/app/Http/Controllers/OfferApiController.php +++ b/app/Http/Controllers/OfferApiController.php @@ -24,7 +24,7 @@ public function __construct(public OfferService $offerService, public function index(Request $request) { $request->merge($this->offerService->injectUserId()); - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( $request, 'ProductService', 'listOffers', @@ -52,7 +52,7 @@ public function show($id) { try { request()->merge(['id' => $id]); - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( request(), 'ProductService', 'getOffer', diff --git a/app/Http/Controllers/ProductApiController.php b/app/Http/Controllers/ProductApiController.php index b534c5c..f7bfc5c 100644 --- a/app/Http/Controllers/ProductApiController.php +++ b/app/Http/Controllers/ProductApiController.php @@ -35,7 +35,7 @@ public static function middleware() public function index(Request $request) { $request->merge($this->productService->injectUserId()); - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( $request, 'ProductService', 'listProducts', @@ -57,7 +57,7 @@ public function show(int $product_id) { request()->merge(['id' => $product_id]); - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( request(), 'ProductService', 'getProduct', diff --git a/app/Http/Controllers/ShoppingCartApiController.php b/app/Http/Controllers/ShoppingCartApiController.php index 51fe85d..7bb1663 100644 --- a/app/Http/Controllers/ShoppingCartApiController.php +++ b/app/Http/Controllers/ShoppingCartApiController.php @@ -20,7 +20,7 @@ public function __construct( */ public function index(Request $request) { - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( $request, 'UserService', 'getCart', @@ -34,7 +34,7 @@ public function index(Request $request) */ public function add(Request $request) { - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( $request, 'UserService', 'addToCart', @@ -50,7 +50,7 @@ public function add(Request $request) public function remove($item_type, $item_id) { \request()->merge(['item_type' => $item_type, 'item_id' => $item_id]); - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( request(), 'UserService', 'removeFromCart' @@ -64,7 +64,7 @@ public function remove($item_type, $item_id) */ public function clear() { - return $this->grpcService->proxyRequest( + return $this->grpcService->createClientRequest( request(), 'UserService', 'clearCart' diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index afd950e..f823284 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -35,7 +35,7 @@ public function __construct( public function index(Request $request) { // $res = $this->userGrpcService->listUsers(); - return $this->grpcService->proxyRequest(request(), + return $this->grpcService->createClientRequest(request(), 'UserService', 'list', ); @@ -64,7 +64,7 @@ public function show($id) ); request()->merge(['id' => $id]); - return $this->grpcService->proxyRequest(request(), + return $this->grpcService->createClientRequest(request(), 'UserService', 'get', ); @@ -102,7 +102,7 @@ public function destroy(string $id) */ public function userProfile(Request $request) { - return $this->grpcService->proxyRequest($request, + return $this->grpcService->createClientRequest($request, 'UserService', 'GetUserProfile' ); diff --git a/app/Http/Middleware/AuthenticateMiddleware.php b/app/Http/Middleware/AuthenticateMiddleware.php index bf72614..c6c770a 100644 --- a/app/Http/Middleware/AuthenticateMiddleware.php +++ b/app/Http/Middleware/AuthenticateMiddleware.php @@ -13,6 +13,11 @@ class AuthenticateMiddleware { + public function __construct( + private readonly \App\Grpc\Services\GrpcService $grpcService, + ) + { + } /** * Handle an incoming request. * @@ -23,8 +28,13 @@ public function handle(Request $request, Closure $next): Response $token = $request->bearerToken(); $cacheKey = 'user_' . hash('sha256', $token); try { - $userModel = Cache::remember($cacheKey, 60 * 60 * 24, function () use ($token) { - $response = Http::withToken($token)->post(app('users')::getTokenApi()); + $userModel = Cache::remember($cacheKey, 60 * 60 * 24, function () use ($request, $token) { +// $response = Http::withToken($token)->post(app('users')::getTokenApi()); + $response = $this->grpcService->createClientRequest( + $request, + 'UserService', + 'validateToken', + ); if ($response->failed()) return null; From bb76a640a523ecc2d1778196207a4df2113fbb46 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 03:46:04 +0300 Subject: [PATCH 40/49] Refactor gRPC integration: update method names and service references across AuthenticateMiddleware, ShoppingCartApiController, and GatewayService; clean up unused imports and streamline gRPC client creation logic. --- app/Grpc/Controllers/BaseGrpcController.php | 6 +- app/Grpc/Services/GatewayService.php | 69 ++++++++----------- .../Controllers/ShoppingCartApiController.php | 19 ++--- .../Middleware/AuthenticateMiddleware.php | 25 ++++--- 4 files changed, 52 insertions(+), 67 deletions(-) diff --git a/app/Grpc/Controllers/BaseGrpcController.php b/app/Grpc/Controllers/BaseGrpcController.php index 8a0e213..09a4d67 100644 --- a/app/Grpc/Controllers/BaseGrpcController.php +++ b/app/Grpc/Controllers/BaseGrpcController.php @@ -6,15 +6,15 @@ class BaseGrpcController { - protected function setSpecifiedService(FlexibleRequest $request, ?string $default = null): void + public function setSpecifiedService(FlexibleRequest $request, ?string $default = null): void { // service should be 'UserService-UserService' // service can be 'UserService-AuthService' //extract second part of service name $service = $request->getService(); - $services = explode('-', $service); + $services = explode('/', $service); $service = $services[1] ?? $default ?? $services[0]; - \Log::info("Forwarding request to service: $service, method: {$request->getMethod()}"); + \Log::info("Forwarding request to service: {$services[0]}/{$service}, method: {$request->getMethod()}"); $request->setService($service); } } diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php index 2fe71fd..a990edd 100644 --- a/app/Grpc/Services/GatewayService.php +++ b/app/Grpc/Services/GatewayService.php @@ -3,6 +3,7 @@ namespace App\Grpc\Services; use App\Grpc\BytesResponse; +use App\Grpc\Controllers\BaseGrpcController; use App\Grpc\ErrorInfo; use App\Grpc\FlexibleRequest; use App\Grpc\FlexibleResponse; @@ -26,8 +27,6 @@ public function __construct() $this->grpcServices = config('services.microservices_grpc'); } - private array $grpcClients = []; - /** * Handle flexible protobuf requests */ @@ -43,6 +42,7 @@ public function HandleClientRequest(FlexibleRequest $in): FlexibleResponse if (!$grpcClient) { return $this->createErrorResponse($response, 404, 'SERVICE_NOT_FOUND', 'Service not found: ' . $in->getService()); } + app(BaseGrpcController::class)->setSpecifiedService($in); // Forward the exact same FlexibleRequest to downstream service // The downstream service implements the same flexible proto @@ -93,7 +93,7 @@ public function HandleClientRequest(FlexibleRequest $in): FlexibleResponse return $response; } - public function HandleRequest($request): FlexibleResponse + public function HandleRequest(FlexibleRequest $request): FlexibleResponse { try { // Extract service and method from the request @@ -129,51 +129,36 @@ public function HandleRequest($request): FlexibleResponse */ private function getGrpcClient(string $service): ?GatewayServiceClient { + if (str_contains($service, '/')) { + $main_sub_service = explode('/', $service); + $service = $main_sub_service[0]; + } + if (!isset($this->grpcServices[$service])) { return null; } - // 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; - } - } else - Log::info("Reusing existing gRPC client for service: {$service}"); - + $serviceAddress = $this->grpcServices[$service]; - return $this->grpcClients[$service]; - } + try { + // Simplified options to avoid compatibility issues + $options = [ + 'credentials' => ChannelCredentials::createInsecure(), + 'timeout' => 30000000, // 30 seconds in microseconds + ]; + Log::info("Created gRPC client for service: {$service} at {$serviceAddress}"); + + return new GatewayServiceClient( + $serviceAddress, + $options + ); - /** - * Close all gRPC connections (cleanup) - */ - public function __destruct() - { - foreach ($this->grpcClients as $client) { - if ($client) { - $client->close(); - } + } catch (Exception $e) { + Log::error("Failed to create gRPC client for service: {$service}", [ + 'address' => $serviceAddress, + 'error' => $e->getMessage() + ]); + return null; } } diff --git a/app/Http/Controllers/ShoppingCartApiController.php b/app/Http/Controllers/ShoppingCartApiController.php index 7bb1663..62b8cca 100644 --- a/app/Http/Controllers/ShoppingCartApiController.php +++ b/app/Http/Controllers/ShoppingCartApiController.php @@ -8,6 +8,8 @@ class ShoppingCartApiController extends BaseController { + private string $service = 'UserService/Cart'; + public function __construct( private readonly GrpcService $grpcService, @@ -22,8 +24,8 @@ public function index(Request $request) { return $this->grpcService->createClientRequest( $request, - 'UserService', - 'getCart', + $this->service, + 'GetCart', ); // $res = http::withToken(request()->bearerToken())->get(UserApi::getBaseUrl() . 'api/carts'); // return $this->response($res->json('message'), $res->json('data'), @@ -36,8 +38,8 @@ public function add(Request $request) { return $this->grpcService->createClientRequest( $request, - 'UserService', - 'addToCart', + $this->service, + 'AddToCart', ); // $res = http::withToken(request()->bearerToken())->post(UserApi::getBaseUrl() . "api/carts", $request->all()); // if ($res->status() == Response::HTTP_CREATED) @@ -52,22 +54,21 @@ public function remove($item_type, $item_id) \request()->merge(['item_type' => $item_type, 'item_id' => $item_id]); return $this->grpcService->createClientRequest( request(), - 'UserService', - 'removeFromCart' + $this->service, + 'RemoveFromCart' ); // $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts/$item_type/$item_id"); // return $this->response($res->json('message'), $res->json('data')); } /** - * @throws ConnectionException */ public function clear() { return $this->grpcService->createClientRequest( request(), - 'UserService', - 'clearCart' + $this->service, + 'ClearCart' ); // $res = http::withToken(request()->bearerToken())->delete(UserApi::getBaseUrl() . "api/carts"); // return $this->response($res->json('message'), $res->json('data')); diff --git a/app/Http/Middleware/AuthenticateMiddleware.php b/app/Http/Middleware/AuthenticateMiddleware.php index c6c770a..1ba3d38 100644 --- a/app/Http/Middleware/AuthenticateMiddleware.php +++ b/app/Http/Middleware/AuthenticateMiddleware.php @@ -8,7 +8,6 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\Http; use Symfony\Component\HttpFoundation\Response; class AuthenticateMiddleware @@ -18,6 +17,7 @@ public function __construct( ) { } + /** * Handle an incoming request. * @@ -28,18 +28,17 @@ public function handle(Request $request, Closure $next): Response $token = $request->bearerToken(); $cacheKey = 'user_' . hash('sha256', $token); try { - $userModel = Cache::remember($cacheKey, 60 * 60 * 24, function () use ($request, $token) { -// $response = Http::withToken($token)->post(app('users')::getTokenApi()); - $response = $this->grpcService->createClientRequest( - $request, - 'UserService', - 'validateToken', - ); - if ($response->failed()) - return null; - - return new User(json_decode($response->json('data'), true)); - }); + $userModel = Cache::remember($cacheKey, 60 * 60 * 24, function () use ($request, $token) { + $response = $this->grpcService->createClientRequest( + $request, + 'UserService/Auth', + 'ValidateToken', + ); + if ($response->getStatusCode() != Response::HTTP_OK) + return null; + + return new User(json_decode(data_get($response->getData(), 'data'), true)); + }); if (!$userModel) { Cache::forget($cacheKey); From 0521f1e43e303436dfb3035efe5d8b3482538282 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 04:02:17 +0300 Subject: [PATCH 41/49] Refactor UserApiController: replace hardcoded gRPC service and method names with constants for improved maintainability. --- app/Http/Controllers/UserApiController.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/UserApiController.php b/app/Http/Controllers/UserApiController.php index f823284..36e51b5 100644 --- a/app/Http/Controllers/UserApiController.php +++ b/app/Http/Controllers/UserApiController.php @@ -9,6 +9,8 @@ class UserApiController extends BaseController { + private string $serviceName='UserService'; + public function __construct( private readonly GrpcService $grpcService, @@ -36,8 +38,8 @@ public function index(Request $request) { // $res = $this->userGrpcService->listUsers(); return $this->grpcService->createClientRequest(request(), - 'UserService', - 'list', + $this->serviceName, + 'ListUsers', ); } @@ -65,8 +67,8 @@ public function show($id) request()->merge(['id' => $id]); return $this->grpcService->createClientRequest(request(), - 'UserService', - 'get', + $this->serviceName, + 'GetUser', ); } @@ -103,7 +105,7 @@ public function destroy(string $id) public function userProfile(Request $request) { return $this->grpcService->createClientRequest($request, - 'UserService', + $this->serviceName, 'GetUserProfile' ); // return $this->response($message, $data, Response::HTTP_OK, error: !$success); From 325d04bdb5563c599abcfdfe56816db60a7fb6f2 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 04:05:37 +0300 Subject: [PATCH 42/49] Remove all gRPC DTOs along with their implementations, clearing out unnecessary classes and dependencies. --- app/Grpc/DTOs/BaseGrpcDTO.php | 123 ------------------- app/Grpc/DTOs/User/AuthenticateGrpcDTO.php | 49 -------- app/Grpc/DTOs/User/RefreshTokenGrpcDTO.php | 37 ------ app/Grpc/DTOs/User/SimpleResponseGrpcDTO.php | 14 --- app/Grpc/DTOs/User/UserGrpcDTO.php | 48 -------- app/Grpc/DTOs/User/UserListGrpcDTO.php | 60 --------- app/Grpc/DTOs/User/UserProfileGrpcDTO.php | 62 ---------- app/Grpc/DTOs/User/ValidateTokenGrpcDTO.php | 24 ---- 8 files changed, 417 deletions(-) delete mode 100644 app/Grpc/DTOs/BaseGrpcDTO.php delete mode 100644 app/Grpc/DTOs/User/AuthenticateGrpcDTO.php delete mode 100644 app/Grpc/DTOs/User/RefreshTokenGrpcDTO.php delete mode 100644 app/Grpc/DTOs/User/SimpleResponseGrpcDTO.php delete mode 100644 app/Grpc/DTOs/User/UserGrpcDTO.php delete mode 100644 app/Grpc/DTOs/User/UserListGrpcDTO.php delete mode 100644 app/Grpc/DTOs/User/UserProfileGrpcDTO.php delete mode 100644 app/Grpc/DTOs/User/ValidateTokenGrpcDTO.php diff --git a/app/Grpc/DTOs/BaseGrpcDTO.php b/app/Grpc/DTOs/BaseGrpcDTO.php deleted file mode 100644 index 8d8207d..0000000 --- a/app/Grpc/DTOs/BaseGrpcDTO.php +++ /dev/null @@ -1,123 +0,0 @@ -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 deleted file mode 100644 index 14e6a07..0000000 --- a/app/Grpc/DTOs/User/AuthenticateGrpcDTO.php +++ /dev/null @@ -1,49 +0,0 @@ -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 deleted file mode 100644 index 78471c6..0000000 --- a/app/Grpc/DTOs/User/RefreshTokenGrpcDTO.php +++ /dev/null @@ -1,37 +0,0 @@ -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 deleted file mode 100644 index c7ecb1d..0000000 --- a/app/Grpc/DTOs/User/SimpleResponseGrpcDTO.php +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index a24bb21..0000000 --- a/app/Grpc/DTOs/User/UserListGrpcDTO.php +++ /dev/null @@ -1,60 +0,0 @@ -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 deleted file mode 100644 index b5088b2..0000000 --- a/app/Grpc/DTOs/User/UserProfileGrpcDTO.php +++ /dev/null @@ -1,62 +0,0 @@ -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 deleted file mode 100644 index c5c6e7d..0000000 --- a/app/Grpc/DTOs/User/ValidateTokenGrpcDTO.php +++ /dev/null @@ -1,24 +0,0 @@ -valid = $valid; - return $this; - } - - public function toArray(): array - { - $data = parent::toArray(); - $data['valid'] = $this->valid; - return $data; - } -} From 9731fcc14bfddf71a6b7902589ed5fd80180616f Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 04:06:58 +0300 Subject: [PATCH 43/49] Update service naming convention in BaseGrpcController for clarity --- app/Grpc/Controllers/BaseGrpcController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Grpc/Controllers/BaseGrpcController.php b/app/Grpc/Controllers/BaseGrpcController.php index 09a4d67..e381cd6 100644 --- a/app/Grpc/Controllers/BaseGrpcController.php +++ b/app/Grpc/Controllers/BaseGrpcController.php @@ -9,7 +9,7 @@ class BaseGrpcController public function setSpecifiedService(FlexibleRequest $request, ?string $default = null): void { // service should be 'UserService-UserService' - // service can be 'UserService-AuthService' + // service can be 'UserService/Auth' //extract second part of service name $service = $request->getService(); $services = explode('/', $service); From 686e34b4ebbb650bd9b807c2a7cb4cfbf02e5d5e Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 05:16:53 +0300 Subject: [PATCH 44/49] Add user authentication check in InjectUser trait to ensure user is logged in --- app/Traits/InjectUser.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Traits/InjectUser.php b/app/Traits/InjectUser.php index d3682c8..98ed12d 100644 --- a/app/Traits/InjectUser.php +++ b/app/Traits/InjectUser.php @@ -4,8 +4,13 @@ trait InjectUser { + /** + * @throws \Exception + */ public function injectUserId(): array { + if (is_null(auth()->user())) + throw new \Exception('User not authenticated'); return ['user_id' => auth()->user()->id]; } From b629d588bef15879dd54dd02fad5a9838c9a1a2d Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 05:18:26 +0300 Subject: [PATCH 45/49] Remove unused 'top_n' parameter from API request in ApiGatewayController --- app/Http/Controllers/ApiGatewayController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php index 4e2020e..6b24ad1 100644 --- a/app/Http/Controllers/ApiGatewayController.php +++ b/app/Http/Controllers/ApiGatewayController.php @@ -27,7 +27,6 @@ public function getRecommendedProducts(Request $request) { try { $res = Http::post(AIApi::getAIApi(), [ - 'top_n'=> $request->input('top_n', 10), "token"=> $request->bearerToken() ]); From 28617fae6163129c8e48109b707b3e1904967ea9 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 06:02:33 +0300 Subject: [PATCH 46/49] Refactor AuthApiController and GrpcService: replace hardcoded service/method names with dynamic variables, update response handling, and streamline gRPC integration logic. --- app/Grpc/Services/GrpcService.php | 21 +++++++++++++------ app/Http/Controllers/ApiGatewayController.php | 17 ++++++++------- app/Http/Controllers/AuthApiController.php | 14 +++++++------ 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/app/Grpc/Services/GrpcService.php b/app/Grpc/Services/GrpcService.php index 8506c48..cc0ad76 100644 --- a/app/Grpc/Services/GrpcService.php +++ b/app/Grpc/Services/GrpcService.php @@ -32,8 +32,22 @@ public function createClientRequest(Request $request, string $service, string $m // Call gRPC service $grpcResponse = $this->gatewayService->HandleClientRequest($grpcRequest); + return $this->convertBackToHTTPResponse($grpcResponse); - // Convert back to HTTP response + } + + public function forwardGrpcToService(FlexibleRequest $request): FlexibleResponse + { + return $this->gatewayService->HandleClientRequest($request); + } + + /** + * @param FlexibleResponse $grpcResponse + * @return JsonResponse + */ + public function convertBackToHTTPResponse(FlexibleResponse $grpcResponse): JsonResponse + { +// Convert back to HTTP response if ($grpcResponse->getSuccess()) { $data = json_decode($grpcResponse->getData()->getValue()) ?? $grpcResponse->getData()->getValue(); return response()->json($data, $grpcResponse->getStatusCode()); @@ -47,9 +61,4 @@ public function createClientRequest(Request $request, string $service, string $m return response()->json($res, $grpcResponse->getStatusCode()); } } - - public function forwardGrpcToService(FlexibleRequest $request): FlexibleResponse - { - return $this->gatewayService->HandleClientRequest($request); - } } diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php index 6b24ad1..0ceb28e 100644 --- a/app/Http/Controllers/ApiGatewayController.php +++ b/app/Http/Controllers/ApiGatewayController.php @@ -27,20 +27,20 @@ public function getRecommendedProducts(Request $request) { try { $res = Http::post(AIApi::getAIApi(), [ - "token"=> $request->bearerToken() + "token" => $request->bearerToken() ]); if ($res->failed()) - if($res->getStatusCode() == Response::HTTP_NOT_FOUND) + if ($res->getStatusCode() == Response::HTTP_NOT_FOUND) return response()->json([ 'message' => $res->json('details', 'No purchased products found'), ], $res->getStatusCode()); else - throw new ConnectionException('Failed to connect to AI API', $res->status()); + throw new ConnectionException('Failed to connect to AI API', $res->status()); $recommendedProductData = $res->json(); $request->merge($this->productService->injectUserId()); - if(!$recommendedProductIds = array_column($recommendedProductData, 'id')) + if (!$recommendedProductIds = array_column($recommendedProductData, 'id')) return response()->json([ 'message' => 'No recommended products found', 'data' => [] @@ -54,17 +54,18 @@ public function getRecommendedProducts(Request $request) ); if ($res->getStatusCode() == Response::HTTP_OK) { $filteredProductData = ($res->getData(true)); - $filteredProductData= data_get($filteredProductData, 'data'); + $filteredProductData = data_get($filteredProductData, 'data'); foreach ($filteredProductData as $index => &$productData) { $productData['percentage'] = $recommendedProductData[$index]['compatibility_score'] ?? 0; - $productData['percentage'] = round($productData['percentage'], 2)* 100; // Convert to percentage + $productData['percentage'] = round($productData['percentage'], 2) * 100; // Convert to percentage $productData['percentage'] = number_format($productData['percentage'], 2) . '%'; } - return $res->setData($filteredProductData); + $res->setData($filteredProductData); + return $this->response("Recommended products fetched successfully", $res->getData(true), Response::HTTP_OK); } else - throw new ConnectionException('Failed to fetch products', [$res]); + throw new ConnectionException($res->getData(true), $res->getStatusCode()); } catch (ConnectionException $e) { return response()->json([ 'message' => 'Connection error', diff --git a/app/Http/Controllers/AuthApiController.php b/app/Http/Controllers/AuthApiController.php index 094b150..789ccc2 100644 --- a/app/Http/Controllers/AuthApiController.php +++ b/app/Http/Controllers/AuthApiController.php @@ -11,6 +11,8 @@ class AuthApiController extends BaseController { + private string $serviceName ='UserService/Auth'; + public function __construct( private readonly GrpcService $grpcService, ) @@ -32,8 +34,8 @@ public function login(Request $request): JsonResponse try { // $res = $this->userGrpcService->authenticateUser($request->email, $request->password); return $this->grpcService->createClientRequest($request, - 'UserService', - 'login' + $this->serviceName, + 'Login' ); // return $this->response($message, $data, Response::HTTP_OK, error: !$success); } catch (Exception $exception) { @@ -48,8 +50,8 @@ public function signup(Request $request): Response try { // $res = $this->userGrpcService->createUser($request->all()); return $this->grpcService->createClientRequest($request, - 'UserService', - 'create' + $this->serviceName, + 'SignUp' ); } catch (Exception $e) { return $this->response($e->getMessage(), status: $e->getCode() ?: Response::HTTP_BAD_REQUEST, error: true); @@ -64,8 +66,8 @@ function logout(Request $request) try { return $this->grpcService->createClientRequest( $request, - 'UserService', - 'logout' + $this->serviceName, + 'Logout' ); // $success = $res['success']; // $message = $res['message']; From d19be6692f3b8b03ce4f1f2b89bcf8990684cf2a Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 06:20:47 +0300 Subject: [PATCH 47/49] Update percentage conversion logic in ApiGatewayController and improve gRPC error handling in GatewayService --- app/Grpc/Services/GatewayService.php | 2 +- app/Http/Controllers/ApiGatewayController.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Grpc/Services/GatewayService.php b/app/Grpc/Services/GatewayService.php index a990edd..ba8c4f3 100644 --- a/app/Grpc/Services/GatewayService.php +++ b/app/Grpc/Services/GatewayService.php @@ -68,7 +68,7 @@ public function HandleClientRequest(FlexibleRequest $in): FlexibleResponse $response, 500, 'GRPC_ERROR', - 'Downstream service returned null response' + 'Downstream service, code ' . $status->code . ', returned an error: ' . $status->details ); return $this->createErrorResponse( $response, diff --git a/app/Http/Controllers/ApiGatewayController.php b/app/Http/Controllers/ApiGatewayController.php index 0ceb28e..7ebec60 100644 --- a/app/Http/Controllers/ApiGatewayController.php +++ b/app/Http/Controllers/ApiGatewayController.php @@ -58,7 +58,7 @@ public function getRecommendedProducts(Request $request) foreach ($filteredProductData as $index => &$productData) { $productData['percentage'] = $recommendedProductData[$index]['compatibility_score'] ?? 0; - $productData['percentage'] = round($productData['percentage'], 2) * 100; // Convert to percentage + $productData['percentage'] = $productData['percentage'] * 100; // Convert to percentage $productData['percentage'] = number_format($productData['percentage'], 2) . '%'; } From dce6ed0bde53cda8d21b3775af1b4a68cccef39f Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 07:58:11 +0300 Subject: [PATCH 48/49] Add supervisor configuration for PHP-FPM and gRPC server in Docker setup --- Dockerfile | 4 ++++ supervisord.conf | 35 ++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3fe7db3..76294e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,6 +42,7 @@ FROM php:8.4.4-fpm AS production # Install runtime dependencies RUN apt-get update && apt-get install -y \ + supervisor \ libpq-dev \ libzip-dev \ unzip \ @@ -85,7 +86,10 @@ RUN composer install \ --classmap-authoritative \ && composer clear-cache + # Copy supervisor configuration +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf # Copy application files (most frequently changing layer - put last) + COPY . . # Create necessary directories and set permissions diff --git a/supervisord.conf b/supervisord.conf index 6b8f798..79b1312 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -1,30 +1,39 @@ [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 +[program:php-fpm] +command=/usr/local/sbin/php-fpm --nodaemonize autostart=true autorestart=true -user=www-data -numprocs=1 +stdout_logfile=/dev/stdout 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 +chown=root:root + +[inet_http_server] +port=127.0.0.1:9001 [supervisorctl] serverurl=unix:///var/run/supervisor.sock [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[program:grpc-server] +command=php /var/www/artisan grpc:serve --port=50051 --host=0.0.0.0 +directory=/var/www +autostart=true +autorestart=true +user=www-data +numprocs=1 +redirect_stderr=true +stdout_logfile=/var/log/supervisor/grpc-server.log +stderr_logfile=/var/log/supervisor/grpc-server-error.log +stdout_logfile_maxbytes=10MB +stdout_logfile_backups=3 +stopwaitsecs=10 +priority=100 From 02be0fb467409afa1990fc041be70c134f371100 Mon Sep 17 00:00:00 2001 From: OmmrGAZAR315 Date: Sat, 14 Jun 2025 12:48:38 +0300 Subject: [PATCH 49/49] Update Dockerfile: replace php-fpm with supervisor to manage processes --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 76294e4..def0b8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -114,7 +114,8 @@ php artisan route:cache\n\ php artisan view:cache\n\ php artisan migrate --force\n\ php artisan storage:link\n\ -php-fpm\n\ +echo "Starting supervisor..."\n\ +exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf\n\ ' > /usr/local/bin/start.sh \ && chmod +x /usr/local/bin/start.sh