Перейти к содержанию

Аккаунт, локальная база и прекращение сессии

До этого момента мы использовали Alice как постоянного автора. Теперь добавим Bob и рассмотрим переключение сессии. У каждого владельца должны быть отдельные notes, outbox и cursor, а сервер должен определять область данных независимо от клиентского имени базы. Результат урока — граница аккаунта, которая сохраняет локальные правки при отказе входа и не отправляет прежнюю очередь под новым владельцем.

Лаборатория использует известные строки classroom-alice и classroom-bob, а не реальные пароли. Автор не запускал вход, не подключал внешний поставщик идентичности и не выполнял проверки безопасности сервиса. Конечные исходники демонстрируют структуру ответственности; производственная аутентификация требует отдельного решения и не возникает из названия учебного токена.

Имя браузерной базы не предоставляет полномочий

OpenDB формирует имя bloknot:${account}. Это отделяет Alice от Bob в одном профиле и предотвращает случайное объединение их local notes. Однако любой исполняемый код того же origin способен обратиться к известному имени базы. Поэтому такое разделение — прикладная организация, а не криптографическая или браузерная защита каждого аккаунта друг от друга.

Полномочия сервера определяются bearer строкой. Push не принимает account из тела операции; обработчик сначала получает владельца из токена, затем выбирает server state. Клиентское указание имени не может изменить это решение. Так контракт остаётся понятным: идентичность подтверждается на сервере, а локальная база открывается для уже выбранной сессии.

В реальном приложении нельзя полагаться на токен, встроенный в JavaScript и одинаковый у всех читателей. Он публичен, поэтому не отличает конкретного человека. Учебные строки позволяют наблюдать две области данных без регистрационного сервиса. Их использование ограничено локальным loopback процессом и не должно переноситься в публичный проект без новой системы сессий.

Открываем базу после подтверждения аккаунта

Функция start сначала останавливает прежний sync, закрывает db и channel, затем получает /api/me с выбранным токеном. Ответ должен быть успешным и содержать ожидаемый account. Только после этого открывается локальная база. Это предотвращает случайное продолжение работы под неподтверждённой областью, когда селектор интерфейса уже сменился, а серверная сессия ещё нет.

Существенный фрагмент app.js показан ниже. Остальные действия start создают канал, планировщик и новую пустую форму.

sync?.stop(); db?.close(); channel?.close();
account = $('account').value;
const token = `classroom-${account}`;
const response = await fetch('/api/me', {
  headers: { authorization: `Bearer ${token}` }
});
if (!response.ok || (await response.json()).account !== account) {
  throw new Error('Сессия не подтверждена');
}
db = await openDB(account);

Первый вход офлайн здесь не реализован. Чтобы впервые выбрать аккаунт, нужен ответ локального сервера. После успешного start открытую сессию можно продолжать редактировать при отключённых запросах: saveNote не обращается к /api/me при каждом нажатии. Это точная граница лаборатории, а не обещание автономного входа после любой перезагрузки.

Если продукт должен разрешать повторное локальное открытие без сети, понадобятся политика сохранённой идентичности, правила срока сессии и понятный режим локального доступа. Эти правила отличаются от получения права отправлять на сервер. Включение «последнего account» в localStorage само по себе не доказывает действующие полномочия и не должно автоматически запускать push.

Прежняя очередь остаётся в прежней области

Пусть Alice офлайн изменила «План урока» и переключилась на Bob. Stop запрещает новые проходы Alice, а закрытие соединения освобождает её ресурсы. Прежняя notes/outbox не удаляется. Bob получает другую базу и server account; его список может быть пустым. После возвращения в Alice неотправленная работа должна остаться доступной для дальнейшего согласования.

Такой результат особенно важен на общем компьютере. Очистка базы при каждом выходе действительно уменьшает доступность локальных данных следующему участнику, но уничтожает неотправленные изменения. Нужна явная продуктовая политика: сохранить защищённую локальную работу, экспортировать её либо удалить по понятному действию. Конечная лаборатория выбирает сохранение и прямо объясняет, что оно не обеспечивает приватность между пользователями одного origin.

Уже отправленный запрос прежней сессии может завершиться после переключения. Его callback связан с прежними db и token, а не получает текущий глобальный аккаунт для применения ответа. Закрытая база может привести к отказу локального подтверждения; очередь тогда останется для безопасного повтора позднее. Это лучше, чем применить accepted Alice к outbox Bob.

Stop не означает, что сервер обязательно отменил ранее принятый запрос. То же правило мы использовали для timeout. При следующем открытии Alice стабильный opId позволит получить прежний ledger ответ. Владелец server results входит в область аккаунта, поэтому одинаковые UUID двух владельцев не должны смешивать их истории.

Отказ авторизации сохраняет локальную работу

Если API отвечает 401, createSync устанавливает stopped true и сообщает, что сессия закончилась. Timer не назначается вновь. Notes и outbox не очищаются, поскольку прекращение полномочий не делает введённый текст ненужным. Человек может экспортировать доступные записи или заново открыть подходящую сессию после восстановления доступа.

Нельзя автоматически переключиться на другого пользователя и отправить ему старую очередь. NoteId принадлежит документу внутри конкретной области, revision относится к её серверной истории, cursor — к её журналу. Перенос body в другой аккаунт возможен как новая явно выбранная операция импорта, но сохранение прежних идентификаторов и ревизий нарушило бы модель.

В рабочем сервисе 403 может обозначать другую причину: сессия известна, но конкретное изменение запрещено. Тогда интерфейсу полезно предложить сохранить личную копию или обратиться к доступному владельцу. Лаборатория не реализует роли заметок, поэтому нельзя приписывать ей эти возможности. Общая граница остаётся: сервер проверяет разрешения на каждый запрос, клиент сохраняет локальный результат и объясняет отказ.

Данные на общем устройстве требуют отдельного решения

Разные имена IndexedDB не скрывают заметки от человека, имеющего доступ к браузерному профилю и инструментам разработчика. Хранение локально также не означает шифрование. Для чувствительных документов нужны модель угроз, защита устройства и выбранная политика локального хранения; интерфейс выхода должен честно описывать, какие данные остаются на этом компьютере.

Экспорт содержит account для пояснения происхождения, но импорт создаёт новые UUID в выбранной текущей базе. Он не доверяет имени владельца из файла как разрешению открыть чужой server state. Если Bob импортирует Alice файл, появляются новые Bob заметки из доступного текста. Это действие требует осознанного выбора файла человеком и не выполняется при переключении сессии автоматически.

Теперь у синхронизации есть область ответственности: конкретный owner, его локальная база, его server journal и его действующая сессия. Переключение не стирает работу и не меняет владельца прежних намерений; отказ авторизации прекращает запросы. Последняя глава объединит все переходы в регламент передачи, чтобы следующий разработчик получил полную модель и список ещё не выполненных сценариев.

Оглавление серии