API-сервіс для підписки на email-сповіщення про нові релізи GitHub-репозиторіїв.
Проєкт має:
- HTTP API на Slim 4
- gRPC API на RoadRunner
- scanner для періодичної перевірки нових релізів
- notifier для email-доставки через SMTP
Для роботи з проєктом потрібні тільки:
- Docker
- Docker Compose plugin
- GNU Make
Локально не потрібні:
- PHP
- Composer
- PostgreSQL
- Redis
- RoadRunner binary
rr
Усе піднімається та перевіряється через make.
Сервіс задеплоєний на AWS
Стек на одному інстансі через Docker Compose: app + scanner + PostgreSQL + Redis + Mailpit.
| Сервіс | URL |
|---|---|
| HTML UI / форма підписок | http://44.212.26.4:8080 |
| HTTP API | http://44.212.26.4:8080/api/subscriptions |
| Health | http://44.212.26.4:8080/health |
| Metrics | http://44.212.26.4:8080/metrics |
| Mailpit (перегляд листів) | http://44.212.26.4:8025 |
Підготувати і підняти весь стек:
make up
make migrateСервіси:
- HTTP API:
http://localhost:8080 - gRPC:
localhost:9001 - HTML UI:
http://localhost:8080/ - MailHog:
http://localhost:8025 - Metrics:
http://localhost:8080/metrics
Корисні команди:
make logs
make restart
make downmake install # зібрати Docker images для всіх workflow
make up # підняти весь application stack
make migrate # прогнати міграції в контейнері
make lint # phpcs у Docker
make stan # phpstan у Docker
make test # phpunit у Docker
make check # lint + stan + unit tests
make proto # згенерувати protobuf/gRPC класи
make behat # acceptance у Docker
make ci # повна перевірка: check + acceptance
make down # зупинити стекmake up автоматично створює .env із .env.example, якщо його ще немає.
Ключові entrypoints:
public/index.php— HTTP entrypointbin/grpc.php— gRPC worker entrypointbin/scanner.php— scanner process
Ключові модулі:
src/Service/SubscriptionService.php— бізнес-логіка підписокsrc/Grpc/ReleaseNotifierService.php— gRPC adapter над тією самою логікоюsrc/Repository/SubscriptionRepository.php— PostgreSQL repositorysrc/Service/GitHubService.php— інтеграція з GitHub APIsrc/Service/NotifierService.php— SMTP-відправка повідомлень
Нормальний флоу:
- Клієнт створює підписку через HTTP або gRPC.
- Сервіс валідовує
emailіowner/repo. - Репозиторій перевіряється через GitHub API.
- Підписка зберігається в PostgreSQL.
- Scanner перевіряє релізи пачками.
- Якщо з’явився новий реліз, notifier відправляє лист.
- Стан доставки зберігається в БД, щоб уникнути дублювання.
curl -X POST http://localhost:8080/api/subscriptions \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","repository":"golang/go"}'curl "http://localhost:8080/api/subscriptions?email=user@example.com&limit=20&offset=0"curl http://localhost:8080/api/subscriptions/1curl -X DELETE http://localhost:8080/api/subscriptions/1Якщо в .env задано API_KEY, усі запити до /api/* повинні передавати:
X-API-Key: your-api-key
Маршрути /, /health, /metrics залишаються без авторизації.
HTML-форма на / теж підтримує API key, але бере його з поля форми, а не з query string.
Proto-контракт лежить у proto/release_notifier.proto.
Generated PHP-класи лежать у generated/.
Регенерація:
make protoСервіс release_notifier.v1.ReleaseNotifierService підтримує:
HealthCreateSubscriptionListSubscriptionsGetSubscriptionDeleteSubscription
Важливий runtime-нюанс:
- логер пише в
stderr, а не вstdout - це потрібно для сумісності з RoadRunner worker protocol
- інакше gRPC-відповіді ламаються на transport layer
Якщо grpcurl встановлений локально:
grpcurl -plaintext -import-path proto -proto release_notifier.proto localhost:9001 list
grpcurl -plaintext -import-path proto -proto release_notifier.proto \
-d '{}' \
localhost:9001 release_notifier.v1.ReleaseNotifierService/HealthЯкщо локально grpcurl немає, можна використати Docker:
docker run --rm \
--network github-release-notifier_default \
-v "$PWD/proto:/proto" \
fullstorydev/grpcurl \
-plaintext \
-import-path /proto \
-proto release_notifier.proto \
grpc:9001 listmake migrateМіграції запускаються з advisory lock, тому одночасний старт кількох процесів не повинен призводити до гонок schema changes.
Усі ці команди виконуються всередині Docker:
make lint
make stan
make test
make checkПокриття:
make lint—src/,config/,bin/,tests/make stan— статичний аналізmake test— PHPUnit
Acceptance-набір запускається так:
make behatАбо поетапно:
make behat-up
docker compose -f docker-compose.yml -f docker-compose.test.yml exec -T app composer acceptance
make behat-downЩо використовується:
features/*.feature— бізнес-сценаріїtests/Acceptance/FeatureContext.php— step helpers і cleanup logicbehat.yml— конфіг Mink та OpenAPI validatordocker-compose.test.yml— test override для acceptance
Що покривають acceptance:
features/health.feature—/healthfeatures/metrics.feature—/metricsfeatures/subscription.feature— create/list/get/delete підписок- негативні кейси: невалідний email, невалідний repository format, відсутні поля,
404 - контракт HTTP API проти
swagger.yaml
Як працює cleanup:
- сценарії з тегом
@cleanupперед стартом чистять підписки через HTTP API - cleanup дозволений лише для
localhostабо127.0.0.1 - якщо задано
API_KEY, cleanup автоматично передає той самийX-API-Key
Чому є docker-compose.test.yml:
- для acceptance примусово ставиться порожній
API_KEY - scanner interval зсувається далеко вперед, щоб background job не шумів під час тестів
make cimake ci запускає:
lintstanphpunitbehat
Усе це відбувається всередині Docker.
HTTP OpenAPI схема лежить у swagger.yaml.
Вона використовується для:
- acceptance contract validation
- ручного перегляду в
https://editor.swagger.io/
Top-level security: [] у схемі залишено навмисно:
- це прибирає warnings у Behat OpenAPI validator
- і фіксує очікувану структуру документа на рівні acceptance/regression checks
- HTML-сторінка для підписки
- Redis-кешування GitHub API
- API key через
X-API-Key - Prometheus metrics
- gRPC transport
- Acceptance suite з OpenAPI-перевіркою
- Production deploy на AWS
