Merge branch 'phase/38-session'
# Conflicts: # docs/phases/README.md # tests/HSchool.AppHost.Tests/PortraitApiTests.cs
This commit is contained in:
+13
-13
@@ -13,28 +13,28 @@
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] `HSchool:AlphaPassword` в конфиге; пустая строка — процесс не стартует. В `appsettings.json`
|
||||
- [x] `HSchool:AlphaPassword` в конфиге; пустая строка — процесс не стартует. В `appsettings.json`
|
||||
заглушка для локальной игры; headless AppHost задаёт тестовый пароль окружением
|
||||
- [ ] `POST /api/session` `{ password, userName }`: неверный пароль — `401` `bad-password`;
|
||||
- [x] `POST /api/session` `{ password, userName }`: неверный пароль — `401` `bad-password`;
|
||||
кривое имя — `400` `invalid-name` (те же правила, что у школы); имя с живым сокетом —
|
||||
`409` `name-online`. Успех ставит HttpOnly-куку и при первом разе пишет имя в
|
||||
`saves/users.json` (занятость без учёта регистра)
|
||||
- [ ] `GET /api/session` — текущее имя или `401`. `DELETE /api/session` снимает куку
|
||||
- [ ] Все игровые HTTP без куки — `401`. Живут без неё: `/health` и три ручки сессии
|
||||
- [ ] WebSocket без куки не шлёт Welcome, закрывается. Hello по-прежнему версия + локаль
|
||||
- [ ] Клиент: шаг пароля, шаг имени; при живой куке оба пропускаются. Сокет — после сессии.
|
||||
- [x] `GET /api/session` — текущее имя или `401`. `DELETE /api/session` снимает куку
|
||||
- [x] Все игровые HTTP без куки — `401`. Живут без неё: `/health` и три ручки сессии
|
||||
- [x] WebSocket без куки не шлёт Welcome, закрывается. Hello по-прежнему версия + локаль
|
||||
- [x] Клиент: шаг пароля, шаг имени; при живой куке оба пропускаются. Сокет — после сессии.
|
||||
Кнопка выхода на меню
|
||||
- [ ] Хостовые тесты логинятся в общем месте (`ResetAsync` или рядом), не в каждом факте
|
||||
- [ ] `docs/protocol.md` — HTTP сессии в том же коммите. Раскладку сокета и версию не трогать
|
||||
- [x] Хостовые тесты логинятся в общем месте (`ResetAsync` или рядом), не в каждом факте
|
||||
- [x] `docs/protocol.md` — HTTP сессии в том же коммите. Раскладку сокета и версию не трогать
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] Без сессии `GET /api/schools` — `401`; после `POST /api/session` — `200`
|
||||
- [ ] Верный пароль и новое имя пишут `users.json`; повтор с тем же именем (другой регистр) —
|
||||
- [x] Без сессии `GET /api/schools` — `401`; после `POST /api/session` — `200`
|
||||
- [x] Верный пароль и новое имя пишут `users.json`; повтор с тем же именем (другой регистр) —
|
||||
тот же человек, не вторая запись
|
||||
- [ ] Пока сокет имени жив, второй `POST /api/session` с ним — `409` `name-online`
|
||||
- [ ] Сокет без куки закрывается, Welcome не приходит
|
||||
- [ ] Клиентский тест: без куки виден ввод пароля, не сетка школ
|
||||
- [x] Пока сокет имени жив, второй `POST /api/session` с ним — `409` `name-online`
|
||||
- [x] Сокет без куки закрывается, Welcome не приходит
|
||||
- [x] Клиентский тест: без куки виден ввод пароля, не сетка школ
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
|
||||
| Фаза | Статус | Зачем |
|
||||
| --- | --- | --- |
|
||||
| [38. Сессия](38-session.md) | 🔄 | Пароль альфы, имя, кука, сокет после входа |
|
||||
| [38. Сессия](38-session.md) | ✅ | Пароль альфы, имя, кука, сокет после входа |
|
||||
|
||||
**Этап B — чьи школы.** Хозяин, слоты, гость только смотрит.
|
||||
|
||||
|
||||
@@ -25,9 +25,48 @@ closes connections whose hello carries a different version with `1002 ProtocolEr
|
||||
|
||||
## HTTP API
|
||||
|
||||
Most routes require a signed session cookie set by `POST /api/session`. Without it the server
|
||||
returns `401`. Public exceptions: `GET /health` and the three `/api/session` routes.
|
||||
|
||||
Game dates are ISO-8601 UTC instants. The in-game calendar has no time zone — UTC is only used so
|
||||
the wire format is unambiguous, and the client formats it back in UTC.
|
||||
|
||||
### `POST /api/session`
|
||||
|
||||
Alpha login. Body:
|
||||
|
||||
```json
|
||||
{ "password": "alpha", "userName": "Leo" }
|
||||
```
|
||||
|
||||
Success (`200`) sets an HttpOnly cookie (`SameSite=Lax`, `Path=/`) and returns:
|
||||
|
||||
```json
|
||||
{ "userName": "Leo" }
|
||||
```
|
||||
|
||||
The name is normalized like a school name (trim, no control characters, 1–40 chars). Occupancy is
|
||||
case-insensitive: `Leo` and `leo` are the same person; the first spelling is kept in
|
||||
`saves/users.json`.
|
||||
|
||||
| Status | `code` | When |
|
||||
| --- | --- | --- |
|
||||
| `401` | `bad-password` | Wrong alpha password |
|
||||
| `400` | `invalid-name` | Name fails normalization |
|
||||
| `409` | `name-online` | A live WebSocket already uses that name |
|
||||
|
||||
### `GET /api/session`
|
||||
|
||||
Returns `{ "userName": "Leo" }` when the cookie is valid, otherwise `401`.
|
||||
|
||||
### `DELETE /api/session`
|
||||
|
||||
Clears the session cookie. `204`.
|
||||
|
||||
The WebSocket at `/ws/game` uses the same cookie on upgrade. Without a valid cookie the server
|
||||
closes the connection with a policy violation and never sends Welcome. Hello is unchanged
|
||||
(version + locale only).
|
||||
|
||||
### `GET /api/schools`
|
||||
|
||||
Everything the main menu needs in one request. `schoolWeekDays` is 5–7 working days counted
|
||||
|
||||
Reference in New Issue
Block a user