REST API на основе Laravel для совместного управления задачами и командами с аутентификацией через Sanctum
Проект поддерживает два окружения, управляемых через Docker Compose:
- Использует
docker-compose.ymlкак базовую конфигурацию - Настроено для боевого развёртывания с оптимизированными параметрами
- Переменные окружения:
APP_ENV=prod,APP_DEBUG=false - База данных:
task_management
- Использует
docker-compose.yml+docker/config-envs/test/docker-compose.override.yml - Включает отладку, покрытие кода и подробные сообщения об ошибках
- Переменные окружения:
APP_ENV=test,APP_DEBUG=true - База данных:
task_management_test - Xdebug включён для покрытия и отладки
- Клонируйте репозиторий:
git clone <repository-url>
cd tasks-
Создайте файл
.env.prodна основе.env.exampleс боевыми настройками -
Соберите и запустите контейнеры:
docker compose build
docker compose up -d- API будет доступен по адресу
http://localhost:8000
-
Создайте
.env.testили.env.devна основе.env.exampleс параметрами для test/dev -
Соберите и поднимите окружение с тестовым override:
docker compose --env-file .env.test -f docker-compose.yml -f docker/config-envs/test/docker-compose.override.yml build
docker compose --env-file .env.test -f docker-compose.yml -f docker/config-envs/test/docker-compose.override.yml up -d- API будет доступен по адресу
http://localhost:8000
NAME IMAGE SERVICE STATUS PORTS
mysql mysql:8.0.33 mysql Up 2 hours 0.0.0.0:3306->3306/tcp, 33060/tcp
nginx nginx:latest nginx Up 2 hours 0.0.0.0:8000->80/tcp
php tasks-php php Up 2 hours 9000/tcp
redis redis:7.0.7 redis Up 2 hours 0.0.0.0:6379->6379/tcp
Route::prefix('v1')->group(function () {
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/tasks', [TaskController::class, 'index']);
Route::post('/tasks', [TaskController::class, 'store']);
Route::get('/tasks/{task}', [TaskController::class, 'show']);
Route::put('/tasks/{task}', [TaskController::class, 'update']);
Route::delete('/tasks/{task}', [TaskController::class, 'destroy']);
Route::post('/tasks/{taskId}/comments', [CommentController::class, 'store']);
Route::get('/tasks/{taskId}/comments', [CommentController::class, 'index']);
Route::delete('/comments/{id}', [CommentController::class, 'destroy']);
Route::get('/teams', [TeamController::class, 'index']);
Route::post('/teams', [TeamController::class, 'store']);
Route::get('/teams/{team}', [TeamController::class, 'show']);
Route::put('/teams/{team}', [TeamController::class, 'update']);
Route::delete('/teams/{team}', [TeamController::class, 'destroy']);
Route::post('/teams/{team}/users', [TeamController::class, 'addUser']);
Route::delete('/teams/{team}/users/{user}', [TeamController::class, 'removeUser']);
});
});В дальнейшем предполагается расширение логики использования с распределением прав и полномочий для выдаваемых токенов
Показать пример
POST http://localhost:8000/api/v1/register
Content-Type: application/json
{
"name": "MYNAME",
"email": "admin@admin.com",
"password": "content123"
}
HTTP/1.1 201 Created
Server: nginx/1.27.1
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/8.2.23
Cache-Control: no-cache, private
Date: Tue, 03 Sep 2024 16:23:34 GMT
Access-Control-Allow-Origin: *
Response {
"access_token": "2|4BrtGfUhacVxSSMFYKiaX6LMmUuRQu7pxrm8aUXY2ac15ad4",
"token_type": "Bearer"
}
Response code: 201 (Created); Time: 4942ms (4 s 942 ms); Content length: 91 bytes (91 B)Показать пример
POST http://localhost:8000/api/v1/login
Content-Type: application/json
{
"email": "admin@admin.com",
"password": "content123"
}
HTTP/1.1 200 OK
Server: nginx/1.27.1
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/8.2.23
Cache-Control: no-cache, private
Date: Tue, 03 Sep 2024 16:23:40 GMT
Access-Control-Allow-Origin: *
Response {
"access_token": "3|C2GBg1c3tAyJR9y1chCIgwT6m1CtOk33rkmJ5gmnbd5e3807",
"token_type": "Bearer"
}
Response code: 200 (OK); Time: 4533ms (4 s 533 ms); Content length: 91 bytes (91 B)Показать пример
GET http://localhost:8000/api/v1/tasks
Authorization: Bearer 3|C2GBg1c3tAyJR9y1chCIgwT6m1CtOk33rkmJ5gmnbd5e3807
HTTP/1.1 200 OK
Server: nginx/1.27.1
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/8.2.23
Cache-Control: no-cache, private
Date: Tue, 03 Sep 2024 16:23:42 GMT
Access-Control-Allow-Origin: *
Response [
{
"id": 1,
"created_at": "2024-09-03T15:33:25.000000Z",
"updated_at": "2024-09-03T15:33:25.000000Z",
"title": "Initial Task",
"description": "Initial description",
"status": "pending",
"user_id": 1,
"team_id": null
}
]
Response code: 200 (OK); Time: 2151ms (2 s 151 ms); Content length: 201 bytes (201 B)http://localhost:8000/api/v1/tasks/15
http://localhost:8000/api/v1/tasks/15/comments
http://localhost:8000/api/v1/comments/55
http://localhost:8000/api/v1/teams
http://localhost:8000/api/v1/teams/78/users
http://localhost:8000/api/v1/teams/78/users/2
Проект включает полноценное покрытие тестами на базе PHPUnit:
- Unit-тесты (
tests/Unit/) — быстрые изолированные тесты для моделей и бизнес-логики (без БД) - Feature-тесты (
tests/Feature/) — интеграционные тесты для API-эндпоинтов с БД
Тесты запускаются внутри контейнера PHP, используя окружение разработки/тестирования.
- Поднимите тестовое окружение:
docker compose --env-file .env.test -f docker-compose.yml -f docker/config-envs/test/docker-compose.override.yml up -d- Создайте тестовую базу данных:
docker compose --env-file .env.test exec mysql mysql -uroot -proot -e "CREATE DATABASE IF NOT EXISTS task_management_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"- Примените миграции для тестовой базы данных:
docker compose --env-file .env.test exec php php artisan migrate:fresh --env=test- Запустите все тесты:
docker compose --env-file .env.test exec php vendor/bin/phpunit --colors=always --testdox- Запустите тесты с покрытием:
docker compose --env-file .env.test exec php vendor/bin/phpunit --coverage-text --colors=always --testdox- Сгенерируйте HTML-отчёт покрытия:
docker compose --env-file .env.test exec php vendor/bin/phpunit --coverage-html=storage/coverage-reportОткройте storage/coverage-report/index.html в браузере, чтобы посмотреть отчёт.
Запустить один файл тестов:
docker compose --env-file .env.test exec php vendor/bin/phpunit tests/Feature/TaskTest.phpЗапустить конкретный тестовый метод:
docker compose --env-file .env.test exec php vendor/bin/phpunit --filter=testStoreTaskЗапустить только Unit-тесты:
docker compose --env-file .env.test exec php vendor/bin/phpunit tests/Unit/Запустить только Feature-тесты:
docker compose --env-file .env.test exec php vendor/bin/phpunit tests/Feature/Тесты автоматически запускаются в GitHub Actions при создании pull request в ветку dev. В пайплайне выполняется:
- Сборка Docker-контейнеров с тестовой конфигурацией
- Подготовка тестовой БД (
task_management_test) - Применение миграций
- Запуск всех тестов с покрытием
- Загрузка отчётов покрытия как артефактов
Полную конфигурацию CI/CD смотрите в .github/workflows/tests.yml.
Показать вывод
www-data@2999fbe3af46:~$ vendor/bin/phpunit --coverage-text --colors=always --testdox
PHPUnit 11.3.1 by Sebastian Bergmann and contributors.
Runtime: PHP 8.2.23 with Xdebug 3.3.2
Configuration: /var/www/phpunit.xml
....................... 23 / 23 (100%)
Time: 00:22.536, Memory: 42.50 MB
Auth (Tests\Feature\Auth)
✔ Registration
✔ Login
✔ Logout
Comment (Tests\Feature\Comment)
✔ Store comment
✔ Delete comment
Comment (Tests\Unit\Comment)
✔ Comment creation
✔ Comment relations
Task (Tests\Feature\Task)
✔ Store task
✔ Store and update task
✔ Show task
✔ Index tasks
✔ Delete task
Task (Tests\Unit\Task)
✔ Task creation
✔ Task relations
Team (Tests\Feature\Team)
✔ Store team
✔ Update team
✔ Index teams
✔ Add user to team
✔ Remove user from team
✔ Show team
✔ Delete team
Team (Tests\Unit\Team)
✔ Team creation and relations
User (Tests\Unit\User)
✔ User creation
OK (23 tests, 125 assertions)
Generating code coverage report in HTML format ... done [00:06.168]
Code Coverage Report:
2024-09-03 16:43:17
Summary:
Classes: 38.89% (7/18)
Methods: 59.30% (51/86)
Lines: 65.31% (160/245)
App\Http\Controllers\Api\AuthController
Methods: 25.00% ( 1/ 4) Lines: 68.00% ( 17/ 25)
App\Http\Controllers\Api\CommentController
Methods: 16.67% ( 1/ 6) Lines: 36.67% ( 11/ 30)
App\Http\Controllers\Api\TaskController
Methods: 16.67% ( 1/ 6) Lines: 67.44% ( 29/ 43)
App\Http\Controllers\Api\TeamController
Methods: 12.50% ( 1/ 8) Lines: 61.11% ( 22/ 36)
App\Models\Comment
Methods: 100.00% ( 2/ 2) Lines: 100.00% ( 2/ 2)
App\Models\Task
Methods: 33.33% ( 1/ 3) Lines: 33.33% ( 1/ 3)
App\Models\Team
Methods: 50.00% ( 1/ 2) Lines: 50.00% ( 1/ 2)
App\Models\User
Methods: 100.00% ( 1/ 1) Lines: 100.00% ( 3/ 3)
App\Providers\AppServiceProvider
Methods: 100.00% ( 2/ 2) Lines: 100.00% ( 21/ 21)
App\Repositories\EloquentCommentRepository
Methods: 60.00% ( 3/ 5) Lines: 50.00% ( 3/ 6)
App\Repositories\EloquentTaskRepository
Methods: 100.00% ( 5/ 5) Lines: 100.00% ( 6/ 6)
App\Repositories\EloquentTeamRepository
Methods: 100.00% ( 7/ 7) Lines: 100.00% ( 12/ 12)
App\Repositories\EloquentUserRepository
Methods: 50.00% ( 3/ 6) Lines: 38.46% ( 5/ 13)
App\Services\CommentService
Methods: 66.67% ( 4/ 6) Lines: 62.50% ( 5/ 8)
App\Services\TaskService
Methods: 100.00% ( 6/ 6) Lines: 100.00% ( 8/ 8)
App\Services\TeamService
Methods: 100.00% ( 8/ 8) Lines: 100.00% ( 10/ 10)
App\Services\UserService
Methods: 57.14% ( 4/ 7) Lines: 50.00% ( 4/ 8)
Показать структуру
├── .dockerignore
├── .editorconfig
├── .env
├── .env.example
├── .gitattributes
├── .gitignore
├── .phpunit.result.cache
├── Dockerfile
├── README.md
├── app/
│ ├── Contracts/
│ │ ├── CommentRepositoryInterface.php
│ │ ├── CommentServiceInterface.php
│ │ ├── TaskRepositoryInterface.php
│ │ ├── TaskServiceInterface.php
│ │ ├── TeamRepositoryInterface.php
│ │ ├── TeamServiceInterface.php
│ │ ├── TokenRepositoryInterface.php
│ │ ├── TokenServiceInterface.php
│ │ ├── UserRepositoryInterface.php
│ │ └── UserServiceInterface.php
│ ├── Http/
│ │ ├── Controllers/
│ │ │ ├── Api/
│ │ │ │ ├── AuthController.php
│ │ │ │ ├── CommentController.php
│ │ │ │ ├── TaskController.php
│ │ │ │ └── TeamController.php
│ │ │ └── Controller.php
│ ├── Models/
│ │ ├── Comment.php
│ │ ├── RefreshToken.php
│ │ ├── Task.php
│ │ ├── Team.php
│ │ └── User.php
│ ├── Providers/
│ │ ├── AppServiceProvider.php
│ │ └── RouteServiceProvider.php
│ ├── Repositories/
│ │ ├── EloquentCommentRepository.php
│ │ ├── EloquentTaskRepository.php
│ │ ├── EloquentTeamRepository.php
│ │ ├── EloquentTokenRepository.php
│ │ └── EloquentUserRepository.php
│ ├── Services/
│ │ ├── CommentService.php
│ │ ├── TaskService.php
│ │ ├── TeamService.php
│ │ ├── TokenService.php
│ │ └── UserService.php
├── artisan
├── bootstrap/
│ ├── app.php
│ ├── cache/
│ │ ├── .gitignore
│ │ ├── packages.php
│ │ └── services.php
│ └── providers.php
├── composer.json
├── composer.lock
├── config/
│ ├── app.php
│ ├── auth.php
│ ├── cache.php
│ ├── database.php
│ ├── filesystems.php
│ ├── logging.php
│ ├── mail.php
│ ├── queue.php
│ ├── sanctum.php
│ ├── services.php
│ └── session.php
├── database/
│ ├── .gitignore
│ ├── factories/
│ │ ├── CommentFactory.php
│ │ ├── TaskFactory.php
│ │ ├── TeamFactory.php
│ │ └── UserFactory.php
│ ├── migrations/
│ │ ├── 2024_08_30_121349_create_users_table.php
│ │ ├── 2024_08_30_121417_create_password_resets_table.php
│ │ ├── 2024_08_30_121449_create_personal_access_tokens_table.php
│ │ ├── 2024_08_30_122958_create_teams_table.php
│ │ ├── 2024_08_30_123121_create_tasks_table.php
│ │ ├── 2024_08_30_123209_create_comments_table.php
│ │ ├── 2024_08_30_123221_create_team_user_table.php
│ │ ├── 2024_08_30_125909_create_sessions_table.php
│ │ ├── 2024_08_31_074307_create_cache_table.php
│ │ └── 2024_09_04_215244_create_refresh_tokens_table.php
│ ├── seeders/
│ │ └── DatabaseSeeder.php
├── docker-compose.yml
├── nginx/
│ ├── default.conf
│ ├── php-fpm.conf
│ ├── php.ini
│ ├── snippets/
│ │ └── fastcgi-php.conf
├── package.json
├── phpunit.xml
├── public/
│ ├── .htaccess
│ ├── favicon.ico
│ ├── index.php
│ └── robots.txt
├── resources/
│ ├── css/
│ │ └── app.css
│ ├── js/
│ │ ├── app.js
│ │ └── bootstrap.js
│ ├── views/
│ │ └── welcome.blade.php
├── routes/
│ ├── api.php
│ └── console.php
├── storage/
│ ├── app/
│ │ ├── .gitignore
│ │ ├── public/
│ │ │ └── .gitignore
├── tests/
│ ├── Feature/
│ │ ├── AuthTest.php
│ │ ├── CommentTest.php
│ │ ├── TaskTest.php
│ │ └── TeamTest.php
│ ├── TestCase.php
│ ├── Unit/
│ │ ├── CommentTest.php
│ │ ├── TaskTest.php
│ │ ├── TeamTest.php
│ │ └── UserTest.php
└── vite.config.jsmain— production-ready codestage— Staging (pre-production)dev— интеграционная ветка разработки
- Фичи разрабатываются в feature-ветках и вливаются в
devчерез pull request - Тесты автоматически запускаются на каждый PR в
dev(см..github/workflows/tests.yml) - Когда
devстабилен, создайте PR изdev→stage - После валидации
stageсоздайте PR изstage→main - После мержа PR
stage→mainавтоматически создаётся новый релиз с инкрементом версии (см..github/workflows/release.yml)
- Триггерится при мерже PR из
stageвmain - Автоматически увеличивается patch-версия (например, v1.0.0 → v1.0.1)
- Создаётся релиз GitHub с changelog
- Теги в формате семантического версионирования:
vMAJOR.MINOR.PATCH
MIT License — see LICENSE for details