From c02f5d5bf8260518d860ae2ec093a6319bd7f2ce Mon Sep 17 00:00:00 2001 From: Devit Date: Sat, 28 Mar 2026 23:15:36 +0500 Subject: [PATCH] https and sms job add --- app/Jobs/SendSmsJob.php | 174 ++++++++++++++++++ app/Models/EskizToken.php | 25 +++ app/Providers/AppServiceProvider.php | 2 +- ...03_28_180022_create_eskiz_tokens_table.php | 29 +++ docker-compose.yml | 36 ++++ routes/api.php | 1 + 6 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 app/Jobs/SendSmsJob.php create mode 100644 app/Models/EskizToken.php create mode 100644 database/migrations/2026_03_28_180022_create_eskiz_tokens_table.php diff --git a/app/Jobs/SendSmsJob.php b/app/Jobs/SendSmsJob.php new file mode 100644 index 0000000..21136b7 --- /dev/null +++ b/app/Jobs/SendSmsJob.php @@ -0,0 +1,174 @@ +phoneNumber = $phoneNumber; + $this->code = $code; + } + + public function handle(): void + { + $phoneNumber = $this->normalizePhoneNumber($this->phoneNumber); + + // Bazadagi tokenni olish + $token = $this->getTokenFromDb(); + + // Token yo'q yoki expired bo'lsa yangilash + if (!$token) { + $token = $this->refreshToken(); + if (!$token) { + Log::error('Eskiz token olish imkonsiz', ['phone' => $phoneNumber]); + return; + } + } + + $postData = [ + 'mobile_phone' => $phoneNumber, + 'message' => "DELGO hisobiga kirish uchun kod: {$this->code} Ushbu kodni ulashmang! #WBcz5ARFKcp", +// 'message' => "DELGO hisobiga kirish uchun kod: {$this->code} Ushbu kodni ulashmang! #WBcz5ARFKcp", + 'from' => "4546" + ]; + + $response = $this->sendSmsRequest($token, $postData, $phoneNumber); + + // Token eskirgan bo‘lsa qayta olish + if ($response['http_code'] === 401) { + Log::warning("Eskiz token muddati tugagan, yangidan olinmoqda..."); + + $token = $this->refreshToken(); + if ($token) { + $this->sendSmsRequest($token, $postData, $phoneNumber); + } + } + } + + private function sendSmsRequest(string $token, array $postData, string $phoneNumber): array + { + $curl = curl_init(); + curl_setopt_array($curl, [ + CURLOPT_URL => "https://notify.eskiz.uz/api/message/sms/send", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => http_build_query($postData), + CURLOPT_HTTPHEADER => [ + "Authorization: Bearer $token", + "Content-Type: application/x-www-form-urlencoded" + ], + ]); + + $response = curl_exec($curl); + $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $err = curl_error($curl); + curl_close($curl); + + if ($err) { + Log::error("Eskiz SMS cURL Error: " . $err, [ + 'phone' => $phoneNumber, + 'http_code' => $httpCode + ]); + return ['http_code' => $httpCode]; + } + + Log::debug("Eskiz SMS Response", [ + 'phone' => $phoneNumber, + 'http_code' => $httpCode, + 'response' => $response + ]); + + return ['http_code' => $httpCode, 'body' => $response]; + } + + private function normalizePhoneNumber(string $phoneNumber): string + { + $phoneNumber = preg_replace('/[^0-9]/', '', $phoneNumber); + + if (strlen($phoneNumber) === 9 && in_array(substr($phoneNumber, 0, 2), [ + '33', '77', '88', '90', '91', '93', '94', '95', '97', '98', '99' + ])) { + $phoneNumber = '998' . $phoneNumber; + } elseif (str_starts_with($phoneNumber, '+998')) { + $phoneNumber = substr($phoneNumber, 1); + } + + return $phoneNumber; + } + + private function getTokenFromDb(): ?string + { + $record = EskizToken::latest()->first(); + + if (!$record) return null; + + // Token muddati tugagan bo'lsa + if ($record->expires_at && now()->greaterThan($record->expires_at)) { + return null; + } + + return $record->token; + } + + private function refreshToken(): ?string + { + $email = config('services.eskiz.email'); + $password = config('services.eskiz.password'); + + $postData = ['email' => $email, 'password' => $password]; + + $curl = curl_init(); + curl_setopt_array($curl, [ + CURLOPT_URL => "https://notify.eskiz.uz/api/auth/login", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => http_build_query($postData), + CURLOPT_HTTPHEADER => ["Content-Type: application/x-www-form-urlencoded"], + ]); + + $response = curl_exec($curl); + $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); + $err = curl_error($curl); + curl_close($curl); + + if ($err) { + Log::error("Eskiz Auth Error: " . $err, ['http_code' => $httpCode]); + return null; + } + + $data = json_decode($response, true); + + if (isset($data['message'], $data['data']['token']) && $data['message'] === 'token_generated') { + $token = $data['data']['token']; + $expiresAt = now()->addHour(); // Token 1 soatga amal qiladi + + // Tokenni bazaga saqlash (update yoki create) + EskizToken::updateOrCreate( + ['id' => 1], // har doim 1-row ishlatiladi + [ + 'token' => $token, + 'expires_at' => $expiresAt + ] + ); + + return $token; + } + + Log::error("Eskiz Auth Response Invalid", ['response' => $response]); + return null; + } +} diff --git a/app/Models/EskizToken.php b/app/Models/EskizToken.php new file mode 100644 index 0000000..e14e973 --- /dev/null +++ b/app/Models/EskizToken.php @@ -0,0 +1,25 @@ +expires_at && Carbon::now()->lt($this->expires_at); + } + +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index cd35da8..cf52611 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -27,6 +27,6 @@ class AppServiceProvider extends ServiceProvider public function boot() { Schema::defaultStringLength(191); - URL::forceScheme('http'); + URL::forceScheme('https'); } } diff --git a/database/migrations/2026_03_28_180022_create_eskiz_tokens_table.php b/database/migrations/2026_03_28_180022_create_eskiz_tokens_table.php new file mode 100644 index 0000000..c999fb3 --- /dev/null +++ b/database/migrations/2026_03_28_180022_create_eskiz_tokens_table.php @@ -0,0 +1,29 @@ +id(); + $table->text('token'); + $table->timestamp('expires_at'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('eskiz_tokens'); + } +}; diff --git a/docker-compose.yml b/docker-compose.yml index 8e53183..e500edd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,42 @@ services: condition: service_healthy restart: unless-stopped + horizon: + build: + context: . + dockerfile: Dockerfile + args: + WWWGROUP: '${WWWGROUP:-1000}' + container_name: ${DOCKER_CONTAINER_NAME}_horizon + command: php artisan horizon + volumes: + - .:/var/www/html + networks: + - devituz + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + restart: unless-stopped + + scheduler: + build: + context: . + dockerfile: Dockerfile + container_name: ${DOCKER_CONTAINER_NAME}_scheduler + command: sh -c "while [ true ]; do php artisan schedule:run --verbose --no-interaction; sleep 60; done" + volumes: + - .:/var/www/html + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + networks: + - devituz + restart: unless-stopped + nginx: image: nginx:1.25-alpine container_name: ${DOCKER_CONTAINER_NAME}_nginx diff --git a/routes/api.php b/routes/api.php index 38f1581..cea8c0a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -71,6 +71,7 @@ Route::group(['middleware' => ['auth:sanctum']], static function () { Route::post('logout', [ApiController::class, 'logout']); }); + /* Non Authenticated Routes */ Route::get('user-exists', [ApiController::class, 'userExists']); Route::get('get-currencies', [ApiController::class, 'getCurrencies']);