Общий сервис опросов под одним аккаунтом: коллеги без отдельной авторизации Apps Script #12

Open
opened 2026-11-09 17:54:43 +00:00 by valalav · 2 comments
Owner

🤖 Codex, /root · 09.11.2026

Цель

Организатор получает ссылки и настройки своего мероприятия, не копирует Apps Script и не проходит его OAuth. Единый сервис выполняется от имени владельца Google-аккаунта. Авторизация владельца остаётся необходимой; её нельзя передавать копиям других проектов. Участникам опросов также не требуется вход в Apps Script.

Работы

  1. Подготовить краткий проект решения в docs: потоки Яндекс Форма → приёмник → данные мероприятия → экран, роли владельца/организатора/зрителя, реестр мероприятий и способ хранения конфигурации. Сохранить текущие два опроса и действующие правила кодирования/средних; не расширять формат вопросов без отдельной задачи.
  2. Ввести явный идентификатор мероприятия и серверное сопоставление с его хранилищем. Клиент не должен выбирать произвольный spreadsheet ID. Проверять активность мероприятия и корректность survey. При неизвестном мероприятии/неверном ключе не читать и не писать чужие данные.
  3. Разделить ключ приёма ответов, ссылку просмотра и административные полномочия. Секрет приёма не должен попадать в HTML экрана или публичные ответы. Публичные функции google.script.run и HTTP-маршруты проверить на обход проверки доступа; создание, очистка и настройка мероприятий не должны быть доступны зрителю.
  4. Обеспечить дедупликацию в пределах мероприятия и опроса, корректность при повторной/параллельной доставке, освобождение блокировок и безопасную обработку ошибок. Один и тот же response_id в разных мероприятиях не должен теряться.
  5. Реализовать минимальный понятный путь создания мероприятия владельцем: собственные ссылки и ключ, отдельные данные, остановка приёма. Проверить фактические ограничения Apps Script/аккаунта и обосновать допустимый режим нагрузки без выдуманных квот.

Приёмка

  • Два мероприятия A/B в одном проекте: разные ответы, подписи и статистика; запись/очистка A не затрагивает B. Повторы, неверный ключ, неизвестный ID, отсутствующая конфигурация и конкурирующая доставка покрыты проверками.
  • Экран и интеграция не раскрывают секреты и не дают зрителю административных операций. Веб-контекст работает без открытой таблицы.
  • Описан и проверен на разрешённом учебном развёртывании путь коллеги без собственного Apps Script OAuth; недоступный шаг явно остаётся открытым. Реальную проверку внешнего Яндекса завершает последующая задача пилота.
  • В отчёте: SHA, команды, сценарии и свидетельства, оставшиеся ограничения. Не объявлять готовность к эксплуатации по локальному PASS.

Связанные #7/#8 исправляют текущий комплект, #9 проверяет копирование; они не реализуют общий сервис. Результаты проверки Terra/Luna — исходные свидетельства, а не доказательство многопользовательского режима.

Зависит от: нет новых задач; исходный SHA выбирается после приёмки текущих исправлений.

Порядок работы и ограничения

Постановка по поручению владельца от 09.11.2026. Сейчас разрешено открыть задачи; эта запись не является поручением автоматически начать реализацию, развернуть публичный сервис или перенести данные. Исполнителя назначает координатор отдельно.

Перед реализацией прочитать AGENTS.md, актуальный main, последние комментарии и результаты #7–#11. Не перехватывать чужие doing-задачи. Выбранный и принятый исходный SHA записать в отчёте: вариант Terra 097ca49 — кандидат, а не автоматически принятый main. Отдельный worktree и ветка codex/, коммиты со ссылкой на свою задачу; без самовольного merge.

Инструменты: Forgejo connector либо штатный LAN API из hub (токен не выводить); rg для кода; существующие Node-тесты и новые содержательные проверки; Google/Яндекс через доступный профильный инструмент или браузер. Проверить возможности инструмента, не выдумывать API. Зависимости устанавливать только при необходимости. Локальные тесты, реальный Google и настоящий Яндекс→Google отражать отдельными статусами. При отсутствии доступа фиксировать точный непройденный шаг; моки и HTTP 200 не являются приёмкой интеграции.

Боевой стенд КБР, общий Google-шаблон, чужие worktree, доступы и реальные ответы не менять. Разработка и проверки — на отдельных учебных ресурсах; публикация и миграция выполняются отдельным явно согласованным действием. В репозитории и отчётах не размещать токены, OAuth-данные, персональные ответы и действующие секреты; для примеров использовать только учебные значения.

{
  "depends_on": [],
  "write_paths": [
    "src/",
    "tests/",
    "docs/SHARED_SERVICE.md"
  ],
  "files_exist": [
    "docs/SHARED_SERVICE.md"
  ],
  "commit_ref": "#12"
}
🤖 **Codex, /root** · 09.11.2026 ### Цель Организатор получает ссылки и настройки своего мероприятия, не копирует Apps Script и не проходит его OAuth. Единый сервис выполняется от имени владельца Google-аккаунта. Авторизация владельца остаётся необходимой; её нельзя передавать копиям других проектов. Участникам опросов также не требуется вход в Apps Script. ### Работы 1. Подготовить краткий проект решения в docs: потоки Яндекс Форма → приёмник → данные мероприятия → экран, роли владельца/организатора/зрителя, реестр мероприятий и способ хранения конфигурации. Сохранить текущие два опроса и действующие правила кодирования/средних; не расширять формат вопросов без отдельной задачи. 2. Ввести явный идентификатор мероприятия и серверное сопоставление с его хранилищем. Клиент не должен выбирать произвольный spreadsheet ID. Проверять активность мероприятия и корректность survey. При неизвестном мероприятии/неверном ключе не читать и не писать чужие данные. 3. Разделить ключ приёма ответов, ссылку просмотра и административные полномочия. Секрет приёма не должен попадать в HTML экрана или публичные ответы. Публичные функции google.script.run и HTTP-маршруты проверить на обход проверки доступа; создание, очистка и настройка мероприятий не должны быть доступны зрителю. 4. Обеспечить дедупликацию в пределах мероприятия и опроса, корректность при повторной/параллельной доставке, освобождение блокировок и безопасную обработку ошибок. Один и тот же response_id в разных мероприятиях не должен теряться. 5. Реализовать минимальный понятный путь создания мероприятия владельцем: собственные ссылки и ключ, отдельные данные, остановка приёма. Проверить фактические ограничения Apps Script/аккаунта и обосновать допустимый режим нагрузки без выдуманных квот. ### Приёмка - Два мероприятия A/B в одном проекте: разные ответы, подписи и статистика; запись/очистка A не затрагивает B. Повторы, неверный ключ, неизвестный ID, отсутствующая конфигурация и конкурирующая доставка покрыты проверками. - Экран и интеграция не раскрывают секреты и не дают зрителю административных операций. Веб-контекст работает без открытой таблицы. - Описан и проверен на разрешённом учебном развёртывании путь коллеги без собственного Apps Script OAuth; недоступный шаг явно остаётся открытым. Реальную проверку внешнего Яндекса завершает последующая задача пилота. - В отчёте: SHA, команды, сценарии и свидетельства, оставшиеся ограничения. Не объявлять готовность к эксплуатации по локальному PASS. Связанные #7/#8 исправляют текущий комплект, #9 проверяет копирование; они не реализуют общий сервис. Результаты проверки Terra/Luna — исходные свидетельства, а не доказательство многопользовательского режима. Зависит от: нет новых задач; исходный SHA выбирается после приёмки текущих исправлений. ### Порядок работы и ограничения Постановка по поручению владельца от 09.11.2026. Сейчас разрешено открыть задачи; эта запись не является поручением автоматически начать реализацию, развернуть публичный сервис или перенести данные. Исполнителя назначает координатор отдельно. Перед реализацией прочитать AGENTS.md, актуальный main, последние комментарии и результаты #7–#11. Не перехватывать чужие doing-задачи. Выбранный и принятый исходный SHA записать в отчёте: вариант Terra 097ca49 — кандидат, а не автоматически принятый main. Отдельный worktree и ветка codex/, коммиты со ссылкой на свою задачу; без самовольного merge. Инструменты: Forgejo connector либо штатный LAN API из hub (токен не выводить); rg для кода; существующие Node-тесты и новые содержательные проверки; Google/Яндекс через доступный профильный инструмент или браузер. Проверить возможности инструмента, не выдумывать API. Зависимости устанавливать только при необходимости. Локальные тесты, реальный Google и настоящий Яндекс→Google отражать отдельными статусами. При отсутствии доступа фиксировать точный непройденный шаг; моки и HTTP 200 не являются приёмкой интеграции. Боевой стенд КБР, общий Google-шаблон, чужие worktree, доступы и реальные ответы не менять. Разработка и проверки — на отдельных учебных ресурсах; публикация и миграция выполняются отдельным явно согласованным действием. В репозитории и отчётах не размещать токены, OAuth-данные, персональные ответы и действующие секреты; для примеров использовать только учебные значения. ```orchestrator-anchors { "depends_on": [], "write_paths": [ "src/", "tests/", "docs/SHARED_SERVICE.md" ], "files_exist": [ "docs/SHARED_SERVICE.md" ], "commit_ref": "#12" } ```
Author
Owner

🤖 gemini-4.0-argon · DESKTOP-6KC7IA7

Взял в работу.

🤖 **gemini-4.0-argon** · DESKTOP-6KC7IA7 **Взял в работу.**
Author
Owner

🤖 gemini-4.0-argon · DESKTOP-6KC7IA7

Готово, на проверку. Выполнено в изолированной ветке codex/shared-service-it (коммит 5674a82, база main 733734b, с учётом свидетельств #10/#11 097ca49/b88c4fe).

1. Реализованная архитектура общего сервиса ([извлечено])

  • Документация и ролевая модель (docs/SHARED_SERVICE.md): описаны потоки Яндекс Формы → единый doPost → таблица мероприятия → экран LiveWidget, роли владельца сервиса, регионального организатора, ведущего/зрителя и участника, а также квоты Google Apps Script (https://developers.google.com/apps-script/guides/services/quotas и https://developers.google.com/apps-script/guides/web#permissions). Организатор и зритель не копируют Apps Script и не проходят OAuth.
  • Серверный реестр и маршрутизация (src/Code.gs, EVENTS_REGISTRY_JSON):
    • Введён явный идентификатор мероприятия event (/^[a-zA-Z0-9_-]{2,64}$/) и серверное разрешение resolveEventConfig_(eventId, mode, providedKey).
    • Попытка передать sheet_id, sheetId, spreadsheet_id, spreadsheetId, book_id, bookId от клиента в URL или JSON немедленно отклоняется (Client-supplied spreadsheet ID is forbidden) до обращения к SpreadsheetApp.
    • При неизвестном/пустом event, неактивном мероприятии (active: false) или неверном ключе никакие таблицы не открываются (openByIdCalls === 0).
  • Разделение ключей и защита google.script.run (requireAdminAccess_):
    • Разделены ingestKey (приём POST от Яндекс Форм), viewKey (чтение статистики экрана getLiveStats(eventId, viewKey)) и ADMIN_MASTER_KEY (административные операции владельца).
    • Ни ingestKey, ни sheetId не попадают в HTML экрана (doGet) или публичные ответы.
    • Все административные функции (setupTemplate, clearAllTestResponses, bindActiveSpreadsheet, registerEvent, setEventActive, clearEventResponses, validateServiceConfig, exportPublicConfig, importPublicConfig) защищены requireAdminAccess_: вызов зрителем через google.script.run (getActiveSpreadsheet() === null без ADMIN_MASTER_KEY) блокируется с Access denied.
  • Дедупликация и конкурентный доступ: дедупликация response_id изолирована в пределах (event, survey) под LockService.getScriptLock().waitLock(20000) с гарантированным finally { lock.releaseLock(); }.

2. Доказательства и проверки ([извлечено] / [не проверено: INFRA])

  • Локальные тесты (PASS [извлечено]): node tests/test_code_gs.mjs (exit 0, тесты 13a–13g): проверены изоляция мероприятий A и B, кастомные шкалы, совпадение response_id между A и B, отказ на повтор внутри A, запрет клиентского sheetId, блокировка админ-вызовов от зрителя, остановка приёма (setEventActive) и изолированная очистка (clearEventResponses).
  • Браузерная проверка экрана (PASS [извлечено]): headless Chromium (Playwright, 1366×768) — проверена отрисовка двух мероприятий it-pilot-a и it-pilot-b через window.__KS_SURVEY_CTX__ (docs/deployment/evidence/shared_screen_event_a_1366x768.png, docs/deployment/evidence/shared_screen_event_b_1366x768.png).
  • Живое развёртывание на целевом аккаунте ИТ ([не проверено: INFRA]): ожидает назначения владельцем целевого аккаунта ИТ (см. #14).

3. Failure Mode Analysis

  1. Общий ScriptLock и лимит 30 параллельных вызовов на аккаунт владельца: при одновременном проведении нескольких крупных сессий в одну минуту пиковые запросы встают в общую очередь waitLock(20000).
  2. Ограничения Google Workspace домена: если на корпоративном аккаунте запрещён доступ Anyone для Web App, внешние вебхуки Яндекс Форм получат 302 редирект на страницу входа.
🤖 **gemini-4.0-argon** · DESKTOP-6KC7IA7 **Готово, на проверку.** Выполнено в изолированной ветке `codex/shared-service-it` (коммит `5674a82`, база `main` `733734b`, с учётом свидетельств `#10`/`#11` `097ca49`/`b88c4fe`). ### 1. Реализованная архитектура общего сервиса (`[извлечено]`) - **Документация и ролевая модель (`docs/SHARED_SERVICE.md`):** описаны потоки `Яндекс Формы → единый doPost → таблица мероприятия → экран LiveWidget`, роли владельца сервиса, регионального организатора, ведущего/зрителя и участника, а также квоты Google Apps Script (`https://developers.google.com/apps-script/guides/services/quotas` и `https://developers.google.com/apps-script/guides/web#permissions`). Организатор и зритель не копируют Apps Script и не проходят OAuth. - **Серверный реестр и маршрутизация (`src/Code.gs`, `EVENTS_REGISTRY_JSON`):** - Введён явный идентификатор мероприятия `event` (`/^[a-zA-Z0-9_-]{2,64}$/`) и серверное разрешение `resolveEventConfig_(eventId, mode, providedKey)`. - Попытка передать `sheet_id`, `sheetId`, `spreadsheet_id`, `spreadsheetId`, `book_id`, `bookId` от клиента в URL или JSON немедленно отклоняется (`Client-supplied spreadsheet ID is forbidden`) до обращения к `SpreadsheetApp`. - При неизвестном/пустом `event`, неактивном мероприятии (`active: false`) или неверном ключе никакие таблицы не открываются (`openByIdCalls === 0`). - **Разделение ключей и защита `google.script.run` (`requireAdminAccess_`):** - Разделены `ingestKey` (приём POST от Яндекс Форм), `viewKey` (чтение статистики экрана `getLiveStats(eventId, viewKey)`) и `ADMIN_MASTER_KEY` (административные операции владельца). - Ни `ingestKey`, ни `sheetId` не попадают в HTML экрана (`doGet`) или публичные ответы. - Все административные функции (`setupTemplate`, `clearAllTestResponses`, `bindActiveSpreadsheet`, `registerEvent`, `setEventActive`, `clearEventResponses`, `validateServiceConfig`, `exportPublicConfig`, `importPublicConfig`) защищены `requireAdminAccess_`: вызов зрителем через `google.script.run` (`getActiveSpreadsheet() === null` без `ADMIN_MASTER_KEY`) блокируется с `Access denied`. - **Дедупликация и конкурентный доступ:** дедупликация `response_id` изолирована в пределах `(event, survey)` под `LockService.getScriptLock().waitLock(20000)` с гарантированным `finally { lock.releaseLock(); }`. ### 2. Доказательства и проверки (`[извлечено]` / `[не проверено: INFRA]`) - **Локальные тесты (`PASS [извлечено]`):** `node tests/test_code_gs.mjs` (`exit 0`, тесты `13a`–`13g`): проверены изоляция мероприятий A и B, кастомные шкалы, совпадение `response_id` между A и B, отказ на повтор внутри A, запрет клиентского `sheetId`, блокировка админ-вызовов от зрителя, остановка приёма (`setEventActive`) и изолированная очистка (`clearEventResponses`). - **Браузерная проверка экрана (`PASS [извлечено]`):** headless Chromium (`Playwright`, `1366×768`) — проверена отрисовка двух мероприятий `it-pilot-a` и `it-pilot-b` через `window.__KS_SURVEY_CTX__` (`docs/deployment/evidence/shared_screen_event_a_1366x768.png`, `docs/deployment/evidence/shared_screen_event_b_1366x768.png`). - **Живое развёртывание на целевом аккаунте ИТ (`[не проверено: INFRA]`):** ожидает назначения владельцем целевого аккаунта ИТ (см. `#14`). ### 3. Failure Mode Analysis 1. **Общий `ScriptLock` и лимит 30 параллельных вызовов на аккаунт владельца:** при одновременном проведении нескольких крупных сессий в одну минуту пиковые запросы встают в общую очередь `waitLock(20000)`. 2. **Ограничения Google Workspace домена:** если на корпоративном аккаунте запрещён доступ `Anyone` для Web App, внешние вебхуки Яндекс Форм получат `302` редирект на страницу входа.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
valalav/ks-live-survey#12
No description provided.