diff --git a/app/Http/Controllers/DefaultController.php b/app/Http/Controllers/DefaultController.php new file mode 100644 index 0000000..9febe26 --- /dev/null +++ b/app/Http/Controllers/DefaultController.php @@ -0,0 +1,317 @@ +checkAuth($request)) { + return response()->json(json_decode(self::FORBIDDEN)); + } + $responseValue = '{ + "data": [ + { + "mission": { + "name": "Восток 1", + "launch_details": { + "launch_date": "1961-04-12", + "launch_site": { + "name": "Космодром Байконур", + "location": { + "latitude": "45.9650000", + "longitude": "63.3050000" + } + } + }, + "flight_duration": { + "hours": 1, + "minutes": 48 + }, + "spacecraft": { + "name": "Восток 3KA", + "manufacturer": "OKB-1", + "crew_capacity": 1 + } + }, + "landing": { + "date": "1961-04-12", + "site": { + "name": "Смеловка", + "country": "СССР", + "coordinates": { + "latitude": "51.2700000", + "longitude": "45.9970000" + } + }, + "details": { + "parachute_landing": true, + "impact_velocity_mps": 7 + } + }, + "cosmonaut": { + "name": "Юрий Гагарин", + "birthdate": "1934-03-09", + "rank": "Старший лейтенант", + "bio": { + "early_life": "Родился в Клушино, Россия.", + "career": "Отобран в отряд космонавтов в 1960 году...", + "post_flight": "Стал международным героем." + } + } + } + ] +}'; + + return response()->json(json_decode($responseValue)); + } + + public function getFlight() + { + $responseValue = '{ + "data": { + "name": "Аполлон-11", + "crew_capacity": 3, + "cosmonaut": [ + { + "name": "Нил Армстронг", + "role": "Командир" + }, + { + "name": "Базз Олдрин", + "role": "Пилот лунного модуля" + }, + { + "name": "Майкл Коллинз", + "role": "Пилот командного модуля" + } + ], + "launch_details": { + "launch_date": "1969-07-16", + "launch_site": { + "name": "Космический центр имени Кеннеди", + "latitude": "28.5721000", + "longitude": "-80.6480000" + } + }, + "landing_details": { + "landing_date": "1969-07-20", + "landing_site": { + "name": "Море спокойствия", + "latitude": "0.6740000", + "longitude": "23.4720000" + } + } + } +}'; + + return response()->json(json_decode($responseValue)); + } + + public function addLunarMission(Request $request) + { + $validated = Validator::make($request->all(), [ + 'mission' => 'required', + 'mission.name' => 'required|regex:/^[A-ZА-ЯЁ].*$/u', + 'mission.launch_details' => 'required', + 'mission.launch_details.launch_date' => ['required', Rule::date()->format('Y-m-d')], + 'mission.launch_details.launch_site' => 'required', + 'mission.launch_details.launch_site.name' => 'required', + 'mission.launch_details.launch_site.location' => 'required', + 'mission.launch_details.launch_site.location.latitude' => 'required|decimal:1,10', + 'mission.launch_details.launch_site.location.longitude' => 'required|decimal:1,10', + 'mission.landing_details' => 'required', + 'mission.landing_details.landing_date' => ['required', Rule::date()->format('Y-m-d')], + 'mission.landing_details.landing_site' => 'required', + 'mission.landing_details.landing_site.name' => 'required', + 'mission.landing_details.landing_site.coordinates' => 'required', + 'mission.landing_details.landing_site.coordinates.latitude' => 'required|decimal:1,10', + 'mission.landing_details.landing_site.coordinates.longitude' => 'required|decimal:1,10', + 'mission.spacecraft' => 'required', + 'mission.spacecraft.command_module' => 'required', + 'mission.spacecraft.lunar_module' => 'required', + 'mission.spacecraft.crew' => 'required|array', + 'mission.spacecraft.crew.*.name' => 'required', + 'mission.spacecraft.crew.*.role' => 'required', + ], + [ + 'name.regex' => 'Поле :attribute должно начинаться с большой буквы.', + ] + ); + + if ($validated->errors()->isNotEmpty()) { + $error = '{ + "error": { + "code": 422, + "message": "Not valid" + } +}'; + $error = json_decode($error); + $error->error->errors = $validated->errors()->getMessages(); + + return response()->json($error); + } + $mission = new LunarMission(); + $mission->missions = $request->all(); + $mission->save(); + $response = '{ +"data": { +"code": 201, +"message": "Миссия добавлена" +} +}'; + + return response()->json(json_decode($response), 201); + } + + public function getLunarMissions() + { + $missions = LunarMission::all('missions')->map(fn(LunarMission $mission) => $mission->missions); + + return response()->json($missions->all()); + } + + public function deleteLunarMission(Request $request, int $id) + { + $lm = LunarMission::find($id); + + if (!$lm) { + return response()->json(json_decode(self::NOT_FOUND)); + } + + return response()->json(null, 204); + } + + public function editLunarMission(Request $request, int $id) + { + $mission = LunarMission::find($id); + + if (!$mission) { + return response()->json(json_decode(self::NOT_FOUND)); + } + + $mission->missions = $request->all(); + $mission->save(); + $response = '{ +"data": { +"code": 200, +"message": "Миссия обновлена" +} +}'; + + return response()->json(json_decode($response)); + } + + public function addSpaceFlight(Request $request) + { + $mission = new SpaceFlight(); + $mission->flight = $request->all(); + $mission->save(); + $response = '{ +"data": { +"code": 201, +"message": "Космический полет создан" +} +}'; + + return response()->json(json_decode($response), 201); + } + + public function getSpaceFlight() + { + $flights = SpaceFlight::query()->where('flight->seats_available', '>', 0)->get(); + $flights = $flights->map(fn(SpaceFlight $flight) => $flight->flight); + + return response()->json($flights->all()); + } + + public function bookFlight(Request $request) + { + $number = $request->get('flight_number'); + $flight = SpaceFlight::query()->where('flight->flight_number', $number)->where('flight->seats_available', '> 0')->first(); + $response = '{ +"data": { +"code": 404, +"message": "Полет не найден" +} +}'; + if (!$flight) { + return response()->json(json_decode($response), 404); + } + + $flightInfo = $flight->flight; + $flightInfo['seats_available'] = $flightInfo['seats_available'] - 1; + $flight->flight = $flightInfo; + $flight->save(); + + $response = '{ +"data": { +"code": 201, +"message": "Рейс забронирован" +} +}'; + + return response()->json(json_decode($response), 201); + } + + public function search(Request $request) + { + $query = $request->get('query'); + + // для поиска по массиву crew, находящимся в объекте spacecraft + // который в свою очередь находится в объекте mission и в папке missions используется следующий запрос + $result = LunarMission::query() + ->orWhereRaw("missions->'$.mission.spacecraft.crew[*].name' LIKE '%$query%'") + ->get(); + // 'это костыль, для того чтобы подсветить, что найден пилот и чтоб не возиться с джойнами в SQL запросах + $result2 = LunarMission::query() + ->orWhere('missions->mission->name', 'LIKE', "%$query%") + ->get(); + $result = $result->map(function (LunarMission $lunarMission) use ($query) { + $mission = $lunarMission->missions; + return [ + "type" => 'Миссия', + "name" => $mission['mission']['name'], + "launch_date" => $mission['mission']['launch_details']['launch_date'], + "landing_date" => $mission['mission']['landing_details']['landing_date'], + 'crew' => $mission['mission']['spacecraft']['crew'], + 'landing_site' => $mission['mission']['landing_details']['landing_site']['name'] + ]; + }); + $result2 = $result2->map(function (LunarMission $lunarMission) use ($query) { + $mission = $lunarMission->missions; + return [ + "type" => "Пилот", + "name" => $mission['mission']['name'], + "launch_date" => $mission['mission']['launch_details']['launch_date'], + "landing_date" => $mission['mission']['landing_details']['landing_date'], + 'crew' => $mission['mission']['spacecraft']['crew'], + 'landing_site' => $mission['mission']['landing_details']['landing_site']['name'] + ]; + }); + + return response()->json(['data' => $result->merge($result2)->all()]); + } + + private function checkAuth(Request $request) + { + $token = str_replace('Bearer ', '', $request->header('Authorization')); + return User::where('token', $token)->first(); + } +} diff --git a/app/Models/LunarMission.php b/app/Models/LunarMission.php new file mode 100644 index 0000000..285ea18 --- /dev/null +++ b/app/Models/LunarMission.php @@ -0,0 +1,12 @@ + 'array' + ]; +} diff --git a/app/Models/SpaceFlight.php b/app/Models/SpaceFlight.php new file mode 100644 index 0000000..272d989 --- /dev/null +++ b/app/Models/SpaceFlight.php @@ -0,0 +1,12 @@ + 'array' + ]; +} diff --git a/bootstrap/app.php b/bootstrap/app.php index 7b162da..d329c82 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -7,8 +7,10 @@ use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', + apiPrefix: 'api-kosmos' ) ->withMiddleware(function (Middleware $middleware) { // diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore old mode 100644 new mode 100755 diff --git a/composer.json b/composer.json index 11437f5..f2efa91 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,7 @@ "php": "^8.2", "guzzlehttp/guzzle": "^7.2", "laravel/framework": "^11.0", + "laravel/sanctum": "^4.0", "laravel/tinker": "^2.9" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 52d1c8c..bdb962d 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": "f2046e54e0e5a393b33d4a440a1b68d5", + "content-hash": "8fa2d0dd5b833adf5981da170259333f", "packages": [ { "name": "brick/math", @@ -1328,6 +1328,70 @@ }, "time": "2025-01-24T15:41:01+00:00" }, + { + "name": "laravel/sanctum", + "version": "v4.0.8", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "ec1dd9ddb2ab370f79dfe724a101856e0963f43c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/ec1dd9ddb2ab370f79dfe724a101856e0963f43c", + "reference": "ec1dd9ddb2ab370f79dfe724a101856e0963f43c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^11.0|^12.0", + "illuminate/contracts": "^11.0|^12.0", + "illuminate/database": "^11.0|^12.0", + "illuminate/support": "^11.0|^12.0", + "php": "^8.2", + "symfony/console": "^7.0" + }, + "require-dev": { + "mockery/mockery": "^1.6", + "orchestra/testbench": "^9.0|^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2025-01-26T19:34:36+00:00" + }, { "name": "laravel/serializable-closure", "version": "v2.0.2", @@ -8532,12 +8596,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": "^8.2" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..764a82f --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,83 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, + 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + ], + +]; diff --git a/database/migrations/2025_02_22_142649_create_lunar_missions_table.php b/database/migrations/2025_02_22_142649_create_lunar_missions_table.php new file mode 100644 index 0000000..2bfd44c --- /dev/null +++ b/database/migrations/2025_02_22_142649_create_lunar_missions_table.php @@ -0,0 +1,28 @@ +id(); + $table->jsonb('missions')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('lunar_missions'); + } +}; diff --git a/database/migrations/2025_02_22_145307_create_space_flights_table.php b/database/migrations/2025_02_22_145307_create_space_flights_table.php new file mode 100644 index 0000000..e51a5c1 --- /dev/null +++ b/database/migrations/2025_02_22_145307_create_space_flights_table.php @@ -0,0 +1,28 @@ +id(); + $table->timestamps(); + $table->jsonb('flight')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('space_flights'); + } +}; diff --git a/database/migrations/2025_02_22_180001_create_personal_access_tokens_table.php b/database/migrations/2025_02_22_180001_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/database/migrations/2025_02_22_180001_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/migrations/2025_02_22_180747_add_token_to_users_table.php b/database/migrations/2025_02_22_180747_add_token_to_users_table.php new file mode 100644 index 0000000..4873f26 --- /dev/null +++ b/database/migrations/2025_02_22_180747_add_token_to_users_table.php @@ -0,0 +1,28 @@ +string('token')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + // + }); + } +}; diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..bdce876 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,15 @@ +