feat: HADO_DEV_USER substitutes X-Remote-User in local env

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
This commit is contained in:
2026-09-04 09:49:32 -03:00
parent cbb43714ae
commit edc08ff43e
5 changed files with 26 additions and 2 deletions

View File

@@ -14,8 +14,9 @@ docker compose exec app php artisan migrate
docker compose exec app php artisan hado:source:create docs # токен печатается один раз docker compose exec app php artisan hado:source:create docs # токен печатается один раз
``` ```
- http://localhost:8080/ — инбокс (нужен заголовок `X-Remote-User`; локально: расширение - http://localhost:8080/ — инбокс. В проде пользователь приходит в заголовке `X-Remote-User`
браузера для заголовков или `curl -H 'X-Remote-User: nikita' localhost:8080/me`) от хаба; локально поставь `HADO_DEV_USER=nikita` в `core/.env` (работает только при
`APP_ENV=local`) и открывай в браузере без заголовков.
- `PUT /api/events` с `Authorization: Bearer <токен>` — см. спеку §5 - `PUT /api/events` с `Authorization: Bearer <токен>` — см. спеку §5
Тесты локально: `cd core && php artisan test` (SQLite in-memory, нужны расширения Тесты локально: `cd core && php artisan test` (SQLite in-memory, нужны расширения

View File

@@ -41,6 +41,8 @@ CACHE_STORE=database
# CACHE_PREFIX= # CACHE_PREFIX=
HADO_DEFAULT_TZ=America/Montevideo HADO_DEFAULT_TZ=America/Montevideo
# Только dev (APP_ENV=local): логин вместо X-Remote-User, чтобы открыть инбокс без хаба
HADO_DEV_USER=
HADO_PRESENCE_TIMEOUT=2 HADO_PRESENCE_TIMEOUT=2
HADO_FALLBACK_DAY=30 HADO_FALLBACK_DAY=30
HADO_FALLBACK_HOUR=10 HADO_FALLBACK_HOUR=10

View File

@@ -18,6 +18,10 @@ class RemoteUser
public function handle(Request $request, Closure $next): Response public function handle(Request $request, Closure $next): Response
{ {
$login = trim((string) $request->header('X-Remote-User', '')); $login = trim((string) $request->header('X-Remote-User', ''));
if ($login === '' && app()->environment('local')) {
// Dev без хаба: HADO_DEV_USER подставляет пользователя. В проде не работает.
$login = trim((string) config('hado.dev_user'));
}
if ($login === '') { if ($login === '') {
return response()->json(['message' => 'Нет X-Remote-User: запрос должен идти через хаб'], 401); return response()->json(['message' => 'Нет X-Remote-User: запрос должен идти через хаб'], 401);
} }

View File

@@ -2,6 +2,8 @@
return [ return [
'default_tz' => env('HADO_DEFAULT_TZ', 'UTC'), 'default_tz' => env('HADO_DEFAULT_TZ', 'UTC'),
// Только для APP_ENV=local: логин, который подставляется вместо X-Remote-User, если заголовка нет.
'dev_user' => env('HADO_DEV_USER', ''),
'presence_timeout' => (int) env('HADO_PRESENCE_TIMEOUT', 2), 'presence_timeout' => (int) env('HADO_PRESENCE_TIMEOUT', 2),
'presence_ttl' => 60, 'presence_ttl' => 60,
'fallback_day_minutes' => (int) env('HADO_FALLBACK_DAY', 30), 'fallback_day_minutes' => (int) env('HADO_FALLBACK_DAY', 30),

View File

@@ -52,6 +52,21 @@ class MeApiTest extends TestCase
public function test_requires_remote_user_header(): void public function test_requires_remote_user_header(): void
{ {
$this->getJson('/me')->assertStatus(401); $this->getJson('/me')->assertStatus(401);
// dev_user срабатывает только в local
config(['hado.dev_user' => 'dev']);
$this->getJson('/me')->assertStatus(401);
}
public function test_dev_user_substitutes_header_in_local_env_only(): void
{
config(['hado.dev_user' => 'dev']);
$this->app->detectEnvironment(fn () => 'local');
$this->getJson('/me')->assertOk()->assertJsonPath('login', 'dev');
config(['hado.dev_user' => '']);
$this->getJson('/me')->assertStatus(401);
} }
public function test_first_visit_creates_user_with_web_channel(): void public function test_first_visit_creates_user_with_web_channel(): void