Initial commit

This commit is contained in:
2026-02-06 23:26:56 +07:00
commit d6022b9bca
92 changed files with 41386 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\V1;
use App\Events\TaskCreated;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreTaskRequest;
use App\Http\Requests\UpdateTaskRequest;
use App\Http\Resources\TaskResource;
use App\Http\Resources\TaskResourceCollection;
use App\Models\Task;
use Auth;
use Symfony\Component\HttpFoundation\Response;
final class TaskController extends Controller
{
public function __construct()
{
$this->authorizeResource(Task::class, 'task');
}
/**
* Display a listing of the resource.
*/
public function index(): TaskResourceCollection
{
$tasks = request()->user()
->tasks()
->latest()
->paginate(10);
return new TaskResourceCollection($tasks);
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreTaskRequest $request)
{
$task = Auth::user()->tasks()->create($request->validated());
event(new TaskCreated($task)); // вынести бы из контроллера
return new TaskResource($task)
->response()
->setStatusCode(Response::HTTP_CREATED)
->header('Location', route('tasks.show', $task));
}
/**
* Display the specified resource.
*/
public function show(Task $task): TaskResource
{
return new TaskResource($task);
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateTaskRequest $request, Task $task): TaskResource
{
$task->update($request->validated());
if ($task->status->isCompleted()) {
$task->notified_at = null;
$task->save();
}
return new TaskResource($task);
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Task $task)
{
$task->delete();
return response()->noContent();
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Contracts\AuthServiceContract;
use App\Data\Auth\LoginData;
use App\Http\Requests\LoginRequest;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
final class AuthController extends Controller
{
public function __construct(
private readonly AuthServiceContract $authService
) {
}
public function login(LoginRequest $request): JsonResponse
{
try {
$credentials = LoginData::fromArray($request->validated());
$result = $this->authService->attemptLogin($credentials);
if ($result->error) {
return response()->json([
'message' => $result->error->message(),
], $result->error->httpStatusCode());
}
return response()->json([
'token' => $result->token,
]);
} catch (Throwable $e) {
Log::error('AuthController.login: '.$e->getMessage(), [
'ip' => $request->ip()
]);
return response()->json([
'message' => 'Unavailable'
], Response::HTTP_SERVICE_UNAVAILABLE);
}
}
public function logout(Request $request): JsonResponse
{
try {
$request->user()->tokens()->delete();
return response()->json([
'message' => 'Logged out'
], Response::HTTP_OK);
} catch (Throwable $e) {
Log::error('AuthController.logout: '.$e->getMessage(), [
'ip' => $request->ip()
]);
return response()->json([
'message' => 'Unavailable'
], Response::HTTP_SERVICE_UNAVAILABLE);
}
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Routing\Controller as BaseController;
abstract class Controller extends BaseController
{
use AuthorizesRequests;
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
'email' => [
'required',
'string',
'email',
'max:255',
],
'password' => [
'required',
'string',
],
];
}
/**
* Get custom messages for validator errors.
*
* @return array<string, string>
*/
public function messages(): array
{
return [
'email.required' => 'Email is required',
'password.required' => 'Password is required',
'email.email' => 'Please enter a valid email address',
'email.exists' => 'Please enter a valid email address',
];
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests;
use App\Enums\TaskStatus;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreTaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'description' => ['nullable', 'string'],
'status' => ['required', Rule::in(TaskStatus::values())],
'due_date' => ['required', 'date'],
];
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests;
use App\Enums\TaskStatus;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateTaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'title' => ['sometimes', 'string', 'max:255'],
'description' => ['sometimes', 'string'],
'status' => ['sometimes', Rule::in(TaskStatus::values())],
'due_date' => ['sometimes', 'date', 'after_or_equal:today'],
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Resources;
use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @mixin Task
*/
class TaskResource extends JsonResource
{
/**
* Transform the resource collection into an array.
*
* @return array<int|string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'status' => $this->status->value,
'due_date' => $this->due_date?->toDateString(),
'created_at' => $this->created_at?->toDateTimeString(),
'updated_at' => $this->updated_at?->toDateTimeString(),
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TaskResourceCollection extends ResourceCollection
{
/**
* Указывает, что коллекция содержит ресурсы TaskResource
*
* @var string
*/
public $collects = TaskResource::class;
/**
* Transform the resource collection into an array.
*
* @return array<int|string, mixed>
*/
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
];
}
}