Развести номера ADR: 064/065/066 этой ветки → 073/074/075
Ветка отведена от 522ba89, где последним был ADR-063, и заняла
064–072. Тем временем в main через #3 и #4 пришли свои 064
(строка ввода — div вместо form), 065 (стороны сообщений)
и 066 (серверного перца нет).
Переезжают три ADR этой ветки, 067–072 остаются на месте:
их номера свободны, а внутренние ссылки менять незачем.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QR8uS3zGkybzWRW4GEY1oz
This commit is contained in:
+1
-1
@@ -162,7 +162,7 @@ func vapid() error {
|
||||
// version печатает, какой код собран в этот бинарь: короткую ревизию
|
||||
// и время коммита. Время коммита, а не компиляции: штамп момента сборки
|
||||
// делал бы каждую пересборку одного коммита новым файлом, и сверка хеша
|
||||
// со сборкой из тега перестала бы что-либо значить (ADR-022, ADR-065).
|
||||
// со сборкой из тега перестала бы что-либо значить (ADR-022, ADR-074).
|
||||
// Ревизию, которой нет, и время, которого нет, бинарь не выдумывает
|
||||
// (ADR-057).
|
||||
func version() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-022: Деплой — nginx, systemd, кросс-сборка
|
||||
|
||||
Уточнён [ADR-032](032-state-permissions.md) (`StateDirectoryMode` и `UMask` в юните), [ADR-056](056-nginx-access-log-off.md) (`access_log off`), [ADR-057](057-version-marks-dirty-tree.md) (`bare version` помечает сборку из изменённого дерева) и [ADR-065](065-version-and-commit-time.md) (версия и время коммита в `GET /api/config`, времени компиляции в бинаре нет).
|
||||
Уточнён [ADR-032](032-state-permissions.md) (`StateDirectoryMode` и `UMask` в юните), [ADR-056](056-nginx-access-log-off.md) (`access_log off`), [ADR-057](057-version-marks-dirty-tree.md) (`bare version` помечает сборку из изменённого дерева) и [ADR-074](074-version-and-commit-time.md) (версия и время коммита в `GET /api/config`, времени компиляции в бинаре нет).
|
||||
|
||||
## Контекст
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-024: Айдентика «Скобы», интерфейс и язык
|
||||
|
||||
Уточнён [ADR-066](066-no-zoom-app-feel.md) (масштабирование запрещено, размеры шрифтов прежние), [ADR-067](067-version-in-sidebar-foot.md) (версия и время коммита в подвале сайдбара) и [ADR-069](069-message-input-is-editable-block.md) (строка сообщения — редактируемый блок).
|
||||
Уточнён [ADR-075](075-no-zoom-app-feel.md) (масштабирование запрещено, размеры шрифтов прежние), [ADR-067](067-version-in-sidebar-foot.md) (версия и время коммита в подвале сайдбара) и [ADR-069](069-message-input-is-editable-block.md) (строка сообщения — редактируемый блок).
|
||||
|
||||
## Контекст
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-047: Исходящий запрос к push-сервису
|
||||
|
||||
Уточняет [ADR-011](011-web-push.md) и [ADR-023](023-push-and-service-worker.md). Код причины из ответа push-сервиса в журнале — [ADR-064](064-push-failure-reason-in-log.md).
|
||||
Уточняет [ADR-011](011-web-push.md) и [ADR-023](023-push-and-service-worker.md). Код причины из ответа push-сервиса в журнале — [ADR-073](073-push-failure-reason-in-log.md).
|
||||
|
||||
## Контекст
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Уточняет [ADR-022](022-deploy-nginx-systemd.md): проверка подлинности бинаря опирается на ревизию, значит ревизия обязана быть честной.
|
||||
|
||||
Уточнён [ADR-065](065-version-and-commit-time.md): чтение build info переехало в `internal/build`, ревизия печатается короткой и рядом с временем коммита; `+dirty` и `unknown` — как здесь. Формат стал проверяемым тестом — разбор build info отделён от `debug.ReadBuildInfo`; сама простановка `vcs.*` по-прежнему проверяется руками.
|
||||
Уточнён [ADR-074](074-version-and-commit-time.md): чтение build info переехало в `internal/build`, ревизия печатается короткой и рядом с временем коммита; `+dirty` и `unknown` — как здесь. Формат стал проверяемым тестом — разбор build info отделён от `debug.ReadBuildInfo`; сама простановка `vcs.*` по-прежнему проверяется руками.
|
||||
|
||||
## Контекст
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# ADR-067: Версия и время коммита в подвале сайдбара
|
||||
|
||||
Доводит до интерфейса [ADR-065](065-version-and-commit-time.md): сервер отдаёт `version` и `commitAt`, а где их показывать, там не решено.
|
||||
Доводит до интерфейса [ADR-074](074-version-and-commit-time.md): сервер отдаёт `version` и `commitAt`, а где их показывать, там не решено.
|
||||
|
||||
## Контекст
|
||||
|
||||
`GET /api/config` называет работающую версию и время коммита (ADR-065). Клиент читает конфигурацию при запуске и так — ради `kdfIterations` и `vapidPublicKey`. Показать их негде: `docs/ui.md` такого элемента не знает.
|
||||
`GET /api/config` называет работающую версию и время коммита (ADR-074). Клиент читает конфигурацию при запуске и так — ради `kdfIterations` и `vapidPublicKey`. Показать их негде: `docs/ui.md` такого элемента не знает.
|
||||
|
||||
Спрашивают об этом при каждой поломке: обновилось ли то, что человек видит. В установленном на «Домой» приложении вопрос острее — там нет ни адресной строки, ни консоли, и другого способа узнать версию у человека нет.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
- Версия стоит в подвале сайдбара, рядом с «ты: @nick»: это единственное постоянное место интерфейса, где уже написано, кто и где мы.
|
||||
- Текст — `версия · дд.мм чч:мм`, время коммита в местной зоне. Год не показывается: вопрос «что сейчас работает», а не летопись. Времени нет (`commitAt = 0`) — остаётся одна версия; конфигурации нет вовсе — строки нет.
|
||||
- Цвет `stone`, размер подписи 11 px, без рамок и фона. Акцентом версия не бывает: `mark` — один смысловой элемент на экран (ADR-024), и это непрочитанные, а не служебная строка.
|
||||
- Не кнопка и не ссылка: нажимать в ней нечего. Стоит рядом с кнопкой «ты: @nick», а не внутри неё — иначе экранный диктор зачитывал бы хеш как часть названия кнопки. Выделяется, в отличие от остального сайдбара (ADR-066): на iPhone скопировать её иначе нечем.
|
||||
- Не кнопка и не ссылка: нажимать в ней нечего. Стоит рядом с кнопкой «ты: @nick», а не внутри неё — иначе экранный диктор зачитывал бы хеш как часть названия кнопки. Выделяется, в отличие от остального сайдбара (ADR-075): на iPhone скопировать её иначе нечем.
|
||||
- В сайдбаре 224 px строка и версия рядом не помещаются никогда: `версия · дд.мм чч:мм` — это 179 px при внутренней ширине 184, а «ты: @nick» просит ещё восемьдесят с лишним. Значит, на десктопе версия всегда занимает вторую строку и стоит справа; на мобильном, где сайдбар во всю ширину, обе стоят в одной строке. Ник, которому не хватило и целой строки, обрезается многоточием; версия — никогда: половина хеша бесполезна.
|
||||
- Текст ради одной строки не режется: вариант «в подвале ревизия, время в `title`» отклонён — `title` не показывается на телефоне, а телефон и есть место, ради которого строка заведена.
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
- Разговор о поломке начинается с версии, а не с «попробуйте обновиться».
|
||||
- Подвал сайдбара стал двухстрочным и вырос с 47 px мока до 76. Это принятое отклонение от `docs/identity/screens.html`, блок 2a: там подвал — одна строка «ты: @nick» без версии и без 44 px цели нажатия. Мок остаётся снимком айдентики на момент ADR-024 и за интерфейсом не идёт: в нём нет и строки `+ новый чат`, и баннера установки.
|
||||
- Названа версия сервера, а не оболочки, которую исполняет браузер: `version` приезжает с `GET /api/config`, а страница и её модули — из кэша service worker. После релиза первое открытие показывает новую версию под старой оболочкой; расходятся они до применения обновления (ADR-068), а с недописанным сообщением в поле — сколь угодно долго. Показывать рядом версию кэша — отдельное решение, здесь его нет.
|
||||
- Записано в `docs/ui.md`, «Список чатов (сайдбар)». Отдельного запроса ради этого нет: поля приезжают с конфигурацией (ADR-065).
|
||||
- Записано в `docs/ui.md`, «Список чатов (сайдбар)». Отдельного запроса ради этого нет: поля приезжают с конфигурацией (ADR-074).
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# ADR-069: Строка сообщения — редактируемый блок, а не поле формы
|
||||
|
||||
Уточняет [ADR-024](024-identity-and-ui.md) («Чат» и «Доступность» в `docs/ui.md`) и [ADR-066](066-no-zoom-app-feel.md): в перечне элементов управления, по которым второй тап не гасится, прибавился редактируемый блок.
|
||||
Уточняет [ADR-024](024-identity-and-ui.md) («Чат» и «Доступность» в `docs/ui.md`) и [ADR-075](075-no-zoom-app-feel.md): в перечне элементов управления, по которым второй тап не гасится, прибавился редактируемый блок.
|
||||
|
||||
Уточнён [ADR-071](071-input-limit-cuts-what-arrives.md): предел держится до вставки и режет приходящее, а не хвост блока.
|
||||
|
||||
## Контекст
|
||||
|
||||
На iPhone при фокусе в строке сообщения над клавиатурой висит системная полоса помощника форм: две стрелки перехода между полями и «готово». Стрелки неактивны — поле на экране одно, переходить некуда, — а полоса занимает место и ломает ощущение приложения, ради которого принят ADR-066.
|
||||
На iPhone при фокусе в строке сообщения над клавиатурой висит системная полоса помощника форм: две стрелки перехода между полями и «готово». Стрелки неактивны — поле на экране одно, переходить некуда, — а полоса занимает место и ломает ощущение приложения, ради которого принят ADR-075.
|
||||
|
||||
Убрать её со страницы нечем: полосу рисует система, и для `input` и `textarea` она бывает всегда. Единственный работающий приём — перестать быть полем формы: у редактируемого блока (`contenteditable`) iOS полосы не показывает.
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# ADR-064: Код причины отказа push-сервиса в журнале
|
||||
# ADR-073: Код причины отказа push-сервиса в журнале
|
||||
|
||||
Уточняет [ADR-047](047-push-endpoint.md).
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# ADR-065: Версия — короткая ревизия, «время сборки» — время коммита
|
||||
# ADR-074: Версия — короткая ревизия, «время сборки» — время коммита
|
||||
|
||||
Уточняет [ADR-022](022-deploy-nginx-systemd.md) (подлинность бинаря проверяется сверкой хеша со сборкой из тега) и [ADR-057](057-version-marks-dirty-tree.md) (`+dirty` у сборки из изменённого дерева).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ADR-066: Масштабирования нет, размеры шрифтов прежние
|
||||
# ADR-075: Масштабирования нет, размеры шрифтов прежние
|
||||
|
||||
Уточняет [ADR-024](024-identity-and-ui.md): типографика из `docs/identity/brief.md` остаётся как записана, а страница перестаёт масштабироваться.
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o bare ./cmd/bare
|
||||
```
|
||||
|
||||
Версия бинаря — `vcs.revision` и `vcs.time` из `debug.ReadBuildInfo()`. `bare version` печатает семь символов ревизии и время коммита в UTC: `cfd0ec0 2026-08-23T04:54:16Z`. У сборки из изменённого рабочего дерева (`vcs.modified`) к ревизии дописывается `+dirty` — сверка со сборкой из тега не должна проходить молча (ADR-057); у сборки не из git печатается `unknown`. Времени компиляции в бинаре нет: оно делало бы каждую пересборку одного коммита новым файлом (ADR-065). Те же значения отдаёт `GET /api/config` полями `version` и `commitAt`. `/healthz` отвечает только `ok`.
|
||||
Версия бинаря — `vcs.revision` и `vcs.time` из `debug.ReadBuildInfo()`. `bare version` печатает семь символов ревизии и время коммита в UTC: `cfd0ec0 2026-08-23T04:54:16Z`. У сборки из изменённого рабочего дерева (`vcs.modified`) к ревизии дописывается `+dirty` — сверка со сборкой из тега не должна проходить молча (ADR-057); у сборки не из git печатается `unknown`. Времени компиляции в бинаре нет: оно делало бы каждую пересборку одного коммита новым файлом (ADR-074). Те же значения отдаёт `GET /api/config` полями `version` и `commitAt`. `/healthz` отвечает только `ok`.
|
||||
|
||||
## Первичная настройка сервера (один раз)
|
||||
|
||||
@@ -147,6 +147,6 @@ ssh xmatic 'sudo install -m 0755 -o root -g root /tmp/bare /opt/bare/bare && sud
|
||||
|
||||
## Логи
|
||||
|
||||
Сервер пишет в stdout: время, метод, путь, статус, длительность; для маршрутов `/api/` вместо пути пишется шаблон (`/api/users/{nick}`), чтобы ник не попадал в журнал, а если отказ случился до маршрутизации (`Origin`, предел тела) и шаблона ещё нет — просто `/api/`; ника в журнале нет вовсе, включая отказы по лимитам (ADR-055); IP не пишется. Причины ответов `500 internal` (ADR-027) пишутся отдельной строкой, без данных запроса. Отправитель пушей пишет класс отказа — «таймаут», «имя не разрешилось», «отправка не удалась» — без адреса подписки и идентификатора устройства (ADR-047). Ответ push-сервиса пишется статусом и коротким кодом причины из тела: «пуш: push-сервис ответил 403 (BadJwtToken)». Код — диагностика вендора; всё, что на короткий код не похоже, отбрасывается целиком, и остаётся один статус (ADR-064). journald хранит по своим правилам.
|
||||
Сервер пишет в stdout: время, метод, путь, статус, длительность; для маршрутов `/api/` вместо пути пишется шаблон (`/api/users/{nick}`), чтобы ник не попадал в журнал, а если отказ случился до маршрутизации (`Origin`, предел тела) и шаблона ещё нет — просто `/api/`; ника в журнале нет вовсе, включая отказы по лимитам (ADR-055); IP не пишется. Причины ответов `500 internal` (ADR-027) пишутся отдельной строкой, без данных запроса. Отправитель пушей пишет класс отказа — «таймаут», «имя не разрешилось», «отправка не удалась» — без адреса подписки и идентификатора устройства (ADR-047). Ответ push-сервиса пишется статусом и коротким кодом причины из тела: «пуш: push-сервис ответил 403 (BadJwtToken)». Код — диагностика вендора; всё, что на короткий код не похоже, отбрасывается целиком, и остаётся один статус (ADR-073). journald хранит по своим правилам.
|
||||
|
||||
nginx журнал запросов не ведёт: `access_log off` в обоих server-блоках (ADR-056). Без этой строки он унаследовал бы `access.log` формата `combined` из `/etc/nginx/nginx.conf` — с адресом клиента и полным URI, то есть с ником и социальным графом. `error_log` остаётся: это журнал сбоев, а не запросов, и при отказе он записывает адрес клиента.
|
||||
|
||||
@@ -37,12 +37,12 @@ font-family: ui-monospace, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monos
|
||||
|
||||
Размеры: текст сообщений 14 px / 1.55; автор и время 12 px; заголовки секций 10 px, разрядка 0.14em, uppercase; подписи 11 px; имя чата в шапке 15 px. Шрифты не загружаются.
|
||||
|
||||
Поля ввода — те же 14 px, исключений нет: масштабирование в клиенте запрещено, и растить шрифт ради обхода фокус-зума iOS не приходится (ADR-066).
|
||||
Поля ввода — те же 14 px, исключений нет: масштабирование в клиенте запрещено, и растить шрифт ради обхода фокус-зума iOS не приходится (ADR-075).
|
||||
|
||||
## Компоновка
|
||||
|
||||
- Десктоп: сайдбар 224 px с правой границей `line`, шапка 64 px, отступы контента 32 px; сообщения — сетка `132px 1fr`, column-gap 20, row-gap 6.
|
||||
- Мобильный: шапка 56 px, отступы 20 px, ввод с min-height 44 px. Нижнее поле подвала и строки ввода считается с `env(safe-area-inset-bottom)`: под системную полосу iPhone они не заходят (ADR-066).
|
||||
- Мобильный: шапка 56 px, отступы 20 px, ввод с min-height 44 px. Нижнее поле подвала и строки ввода считается с `env(safe-area-inset-bottom)`: под системную полосу iPhone они не заходят (ADR-075).
|
||||
- Ввод — рамка 1 px ink, без скруглений, `>` цветом mark слева.
|
||||
- Активный элемент списка — инверсия: фон ink, текст bone.
|
||||
- Разделители — 1 px `line`; разделитель «новые» — 1 px mark.
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
```
|
||||
embed.go //go:embed web в корне модуля (ADR-025)
|
||||
cmd/bare/main.go подкоманды: serve, vapid, version
|
||||
internal/build/ ревизия и время коммита из build info (ADR-065)
|
||||
internal/build/ ревизия и время коммита из build info (ADR-074)
|
||||
internal/config/ переменные BARE_*
|
||||
internal/store/ SQLite, migrations/*.sql (embed), запросы
|
||||
internal/auth/ argon2id, сессии, cookie
|
||||
@@ -36,7 +36,7 @@ web/
|
||||
js/sync.js устройство, поток событий, приём и отправка
|
||||
js/pwa.js service worker, самообновление (ADR-068), пуши, установка
|
||||
js/ulid.js ULID
|
||||
js/zoom.js запрет масштабирования (ADR-066)
|
||||
js/zoom.js запрет масштабирования (ADR-075)
|
||||
js/ui/*.js экраны из ui.md
|
||||
js/export.js .bare
|
||||
scripts/deploy.sh
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ WrappedKey { to: nick, iv: string, ct: string }
|
||||
|
||||
`GET /api/config` → `200 {inviteRequired: bool, vapidPublicKey: string, kdfIterations: number, maxMessageChars: 4000, version: string, commitAt: number}`
|
||||
|
||||
`version` — короткая ревизия сборки: семь символов хеша коммита, с суффиксом `+dirty` у бинаря из изменённого дерева (ADR-057) и `unknown` у сборки не из git. `commitAt` — время коммита в миллисекундах Unix, `0` если оно неизвестно. Это время коммита, а не момент компиляции: штамп времени сборки лишил бы смысла сверку хеша бинаря со сборкой из тега (ADR-022, ADR-065). Те же значения печатает `bare version`. Отдельного эндпоинта у них нет.
|
||||
`version` — короткая ревизия сборки: семь символов хеша коммита, с суффиксом `+dirty` у бинаря из изменённого дерева (ADR-057) и `unknown` у сборки не из git. `commitAt` — время коммита в миллисекундах Unix, `0` если оно неизвестно. Это время коммита, а не момент компиляции: штамп времени сборки лишил бы смысла сверку хеша бинаря со сборкой из тега (ADR-022, ADR-074). Те же значения печатает `bare version`. Отдельного эндпоинта у них нет.
|
||||
|
||||
`GET /api/kdf?nick=<nick>` → `200 {iterations}`. Для неизвестного ника — `kdfIterations` из конфигурации, тем же статусом. Скрытием существования ника ответ не занимается: у аккаунта, не входившего после повышения цели, число итераций своё (ADR-062), а сам факт, что ник существует, публичен (ADR-019).
|
||||
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@
|
||||
|
||||
Без inline-стилей и inline-скриптов (CSP). Рендер — `document.createElement` и `textContent`; `innerHTML` не используется нигде: сообщения — пользовательские данные.
|
||||
|
||||
Страница ведёт себя как приложение, а не как документ (ADR-066): масштабирования нет, резинового отскока нет, выделение снято с шапки, сайдбара, кнопок и разделителей — но не с текста сообщений, отпечатков, полей ввода и версии в подвале сайдбара: их копируют. Установленное на «Домой» приложение объявлено мета-строками, имя на экране — `bare`, системная полоса остаётся над страницей. Подвал сайдбара и строка ввода отступают от нижней системной полосы iPhone.
|
||||
Страница ведёт себя как приложение, а не как документ (ADR-075): масштабирования нет, резинового отскока нет, выделение снято с шапки, сайдбара, кнопок и разделителей — но не с текста сообщений, отпечатков, полей ввода и версии в подвале сайдбара: их копируют. Установленное на «Домой» приложение объявлено мета-строками, имя на экране — `bare`, системная полоса остаётся над страницей. Подвал сайдбара и строка ввода отступают от нижней системной полосы iPhone.
|
||||
|
||||
## Вход и регистрация
|
||||
|
||||
@@ -120,4 +120,4 @@
|
||||
|
||||
Строка сообщения — редактируемый блок (ADR-069), и семантику ему задают руками: `role="textbox"`, `aria-multiline="true"`, подпись `aria-label` тем же текстом, что подсказка. Заблокированный ввод перестаёт быть редактируемым и говорит об этом `aria-disabled`.
|
||||
|
||||
Масштабирование запрещено — и щипок, и двойной тап (ADR-066). Это сознательный размен: интерфейс ведёт себя как приложение, а человек, которому нужно увеличить мелкое, средства лишается. Размеры шрифтов от запрета не меняются: поля ввода остаются 14 px, как в `docs/identity/brief.md`. Браузер вправе запрет проигнорировать.
|
||||
Масштабирование запрещено — и щипок, и двойной тап (ADR-075). Это сознательный размен: интерфейс ведёт себя как приложение, а человек, которому нужно увеличить мелкое, средства лишается. Размеры шрифтов от запрета не меняются: поля ввода остаются 14 px, как в `docs/identity/brief.md`. Браузер вправе запрет проигнорировать.
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
//
|
||||
// version и commitAt отвечают на вопрос «какой код сейчас работает»:
|
||||
// короткая ревизия сборки и время коммита, а не момент компиляции
|
||||
// (ADR-065). Отдельного эндпоинта им не заводится — конфигурацию клиент
|
||||
// (ADR-074). Отдельного эндпоинта им не заводится — конфигурацию клиент
|
||||
// читает до входа и так.
|
||||
func (s *server) config(w http.ResponseWriter, r *http.Request) {
|
||||
info := build.Current()
|
||||
|
||||
@@ -112,7 +112,7 @@ func TestConfig(t *testing.T) {
|
||||
t.Errorf("maxMessageChars: получено %d, ожидалось 4000", got.MaxMessageChars)
|
||||
}
|
||||
// Версия — то же самое, что печатает `bare version`: одно место,
|
||||
// один формат (ADR-065). Тестовому бинарю vcs.* не проставляются,
|
||||
// один формат (ADR-074). Тестовому бинарю vcs.* не проставляются,
|
||||
// поэтому здесь проверяется в том числе поведение без build info —
|
||||
// «unknown» и 0.
|
||||
if !validVersion(got.Version) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Package build отвечает на вопрос «какой код сейчас работает»: ревизия
|
||||
// и время коммита, которые git оставляет в бинаре при сборке (ADR-065).
|
||||
// и время коммита, которые git оставляет в бинаре при сборке (ADR-074).
|
||||
// Момента компиляции здесь нет и не будет: штамп времени сборки делал бы
|
||||
// каждую пересборку одного коммита новым файлом, а подлинность бинаря
|
||||
// проверяется сравнением хеша со сборкой из тега (ADR-022).
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
const hash = "9f2c1ab7d3e4c5061728394a5b6c7d8e9f001122"
|
||||
|
||||
// Формат версии: семь символов ревизии, «+dirty» у изменённого дерева,
|
||||
// «unknown» без ревизии (ADR-057, ADR-065).
|
||||
// «unknown» без ревизии (ADR-057, ADR-074).
|
||||
func TestVersion(t *testing.T) {
|
||||
cases := []struct {
|
||||
info Info
|
||||
|
||||
@@ -70,7 +70,7 @@ const (
|
||||
dropEvery = time.Minute
|
||||
)
|
||||
|
||||
// Чтение тела ответа push-сервиса ради кода причины (ADR-064).
|
||||
// Чтение тела ответа push-сервиса ради кода причины (ADR-073).
|
||||
const (
|
||||
// maxReasonBody — сколько байт тела читаем. Код причины стоит в начале
|
||||
// ответа; остальное дочитывается в никуда, ради переиспользования
|
||||
@@ -331,7 +331,7 @@ func (s *Sender) deliver(j job) {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// Начало тела нужно ради кода причины (ADR-064), остаток дочитывается
|
||||
// Начало тела нужно ради кода причины (ADR-073), остаток дочитывается
|
||||
// в никуда: иначе соединение не переиспользуется.
|
||||
head, _ := io.ReadAll(io.LimitReader(resp.Body, maxReasonBody))
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
@@ -432,7 +432,7 @@ func (s *Sender) report(format string, args ...any) {
|
||||
}
|
||||
|
||||
// status — ответ push-сервиса для журнала: код и, если он разобран,
|
||||
// короткий код причины из тела (ADR-064).
|
||||
// короткий код причины из тела (ADR-073).
|
||||
func status(code int, body []byte) string {
|
||||
if r := serviceReason(body); r != "" {
|
||||
return fmt.Sprintf("%d (%s)", code, r)
|
||||
@@ -443,7 +443,7 @@ func status(code int, body []byte) string {
|
||||
// serviceReason достаёт из тела ответа короткий код причины: APNs отвечает
|
||||
// {"reason":"BadJwtToken"}, Mozilla — {"errno":…,"error":"Not Found"}.
|
||||
// Код — диагностика вендора, а не данные пользователя, и без него отказ
|
||||
// не читается: голый «403» сутки выглядел как «что-то с пушами» (ADR-064).
|
||||
// не читается: голый «403» сутки выглядел как «что-то с пушами» (ADR-073).
|
||||
//
|
||||
// Тело всё же приходит снаружи, поэтому в журнал идёт не оно, а то, что
|
||||
// прошло safeReason.
|
||||
|
||||
@@ -193,7 +193,7 @@ func TestVAPIDSubjectInToken(t *testing.T) {
|
||||
}
|
||||
|
||||
// Отказ push-сервиса читается по журналу: статус и код причины из тела
|
||||
// (ADR-064). Без кода 403 от APNs неотличим от любого другого отказа.
|
||||
// (ADR-073). Без кода 403 от APNs неотличим от любого другого отказа.
|
||||
func TestServiceStatusInLog(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
io.Copy(io.Discard, r.Body)
|
||||
@@ -225,7 +225,7 @@ func TestServiceStatusInLog(t *testing.T) {
|
||||
|
||||
// Код причины берётся из тела ответа, но телом распоряжается чужая
|
||||
// сторона: всё, что на короткий код не похоже, в журнал не идёт вовсе
|
||||
// (ADR-064, docs/deploy.md, «Логи»).
|
||||
// (ADR-073, docs/deploy.md, «Логи»).
|
||||
func TestServiceReason(t *testing.T) {
|
||||
const endpoint = "web.push.apple.com"
|
||||
cases := []struct{ body, want string }{
|
||||
|
||||
+5
-5
@@ -25,7 +25,7 @@ body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* масштабирования нет (ADR-066). manipulation снимает зум по двойному тапу
|
||||
/* масштабирования нет (ADR-075). manipulation снимает зум по двойному тапу
|
||||
и задержку 300 мс, которую браузер держит, ожидая второго касания;
|
||||
прокрутку и щипок правило не трогает — щипок запрещён в viewport.
|
||||
Правило стоит на html: двойной тап — жест документа, и браузер берёт
|
||||
@@ -41,7 +41,7 @@ body {
|
||||
/* виден вырез: viewport-fit=cover пускает страницу под скруглённые углы,
|
||||
под боковой вырез в альбомной ориентации и под верхнюю системную
|
||||
полосу. Полосу над страницей держит мета-строка о стиле полосы
|
||||
(ADR-066), поэтому верхняя вставка обычно нулевая и не делает ничего;
|
||||
(ADR-075), поэтому верхняя вставка обычно нулевая и не делает ничего;
|
||||
там, где она не нулевая, шапка не уезжает под часы */
|
||||
padding-top: env(safe-area-inset-top);
|
||||
padding-left: env(safe-area-inset-left);
|
||||
@@ -58,7 +58,7 @@ body {
|
||||
/* интерфейс не выделяется: долгое нажатие по шапке, списку и кнопкам
|
||||
вызывает лупу и «копировать», а копировать там нечего. Текст сообщений,
|
||||
отпечатки и поля ввода выделяются как обычно — их именно копируют
|
||||
(ADR-066) */
|
||||
(ADR-075) */
|
||||
|
||||
.side,
|
||||
.head,
|
||||
@@ -421,7 +421,7 @@ input[type="password"] {
|
||||
gap: 2px 8px;
|
||||
margin-top: auto;
|
||||
/* нижняя системная полоса iPhone проходит поверх страницы: подвал
|
||||
отступает от неё, а не прячется под ней (ADR-066) */
|
||||
отступает от неё, а не прячется под ней (ADR-075) */
|
||||
padding: 6px 20px calc(6px + env(safe-area-inset-bottom));
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
@@ -463,7 +463,7 @@ input[type="password"] {
|
||||
/* версия выделяется: с неё начинается разговор о поломке (ADR-067),
|
||||
а на iPhone скопировать её иначе нечем — ни строки адреса, ни консоли
|
||||
там нет. Общий запрет выделения снят с неё так же, как с текста
|
||||
сообщений и отпечатков (ADR-066) */
|
||||
сообщений и отпечатков (ADR-075) */
|
||||
|
||||
.build {
|
||||
flex: none;
|
||||
|
||||
+1
-1
@@ -483,7 +483,7 @@ function errorText(err) {
|
||||
|
||||
async function boot() {
|
||||
// Масштабирования нет: интерфейс ведёт себя как приложение, а не как
|
||||
// страница (ADR-066). Строке viewport Safari верит не всегда, поэтому
|
||||
// страница (ADR-075). Строке viewport Safari верит не всегда, поэтому
|
||||
// страховка ставится до первого экрана.
|
||||
lock();
|
||||
db.persist();
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ function side(ctx, active) {
|
||||
return { nav, dispose: mount(items, ctx, active) };
|
||||
}
|
||||
|
||||
// build — версия и время коммита из GET /api/config (ADR-065, ADR-067).
|
||||
// build — версия и время коммита из GET /api/config (ADR-074, ADR-067).
|
||||
// Конфигурации нет — офлайн-старт до первого ответа сервера — значит,
|
||||
// и строки нет: выдумывать версию не из чего. Время без версии не бывает:
|
||||
// её сервер отдаёт всегда, хотя бы как «unknown».
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Запрет масштабирования — страховка к строке viewport (ADR-066).
|
||||
// Запрет масштабирования — страховка к строке viewport (ADR-075).
|
||||
//
|
||||
// Основное средство — `maximum-scale=1, user-scalable=no` в index.html
|
||||
// и `touch-action: manipulation` в app.css. Safari вправе не послушаться:
|
||||
|
||||
Reference in New Issue
Block a user