приём и чтение записей сведены к одному контракту приложения

- адреса приложения переехали в своё пространство `/app/`, опрос готовности
  убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи,
  текст — отдельным адресом названного вида
- заведена единая точка отображения доменной ошибки и слой, приводящий к той же
  форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением
- у записи появились имя файла отправителя, длительность и размер своими
  колонками, а у ленты владельца — свой индекс: без него страница сканировала
  весь архив сервиса
This commit is contained in:
av
2026-08-15 13:51:23 +03:00
parent 79ff12548f
commit 3a2da3004b
55 changed files with 5506 additions and 466 deletions
+12
View File
@@ -246,6 +246,18 @@ func (h *AuthHandler) exchange(ctx context.Context, code, verifier string) (stri
return "", fmt.Errorf("failed to build exchange request: %w", err)
}
request.Header.Set("Content-Type", "application/json")
// Адрес запросу нужен, хотя запрос внутрипроцессный и наружу не идёт.
//
// Ограничитель частоты хранилища ключует клиента адресом, а у собранного
// руками запроса его нет вовсе — и вырожденное значение библиотека отдаёт не
// пустой строкой, а литералом. Её собственный страж «пустой ключ пропускаем»
// такое значение не ловит, поэтому **все** внутренние обмены кода схлопнулись
// бы в один счётчик: третий вход в пределах трёх секунд — чей угодно —
// получал бы отказ ограничителя, неотличимый от настоящего отказа провайдера.
//
// Прежде этого не случалось: ограничитель был выключен целиком. Он включается
// вместе с правилом под корнем приложения, и цена названа здесь.
request.RemoteAddr = "127.0.0.1:0"
handler, err := h.storageHandler()
if err != nil {