ГлавнаяБлогGoogle Apps Script + Chrome Extension: как я победил 401 ошибку
Python

Google Apps Script + Chrome Extension: как я победил 401 ошибку

Реальная история отладки Chrome расширения с Google Apps Script. Узнайте, как clasp спас от копирования URL и как избежать ловушки ANYONE vs ANYONE_ANONYMOUS.

Al
Редакция Algolitalgolit.ru
6 мин чтения6 июля 2026 г.

Представьте: вы пишете Chrome расширение для LinkedIn, подключаете к нему Google Таблицу через Apps Script, всё работает. Потом вы меняете одну строку кода, перезаливаете — и получаете сухую 401 ошибку. Никаких пояснений, только чувство, что Google вас обманул. Знакомо? Сегодня я расскажу, как я потратил несколько часов на отладку этой ошибки и как нашёл спасение в CLI-инструменте clasp.

Зачем расширению для LinkedIn бэкенд

Я сделал Chrome расширение ProfileKit. Оно парсит страницы профилей LinkedIn: имя, должность, компанию. Всё просто, для личного использования. Потом я захотел кнопку «Сохранить профиль»: нажал — и человек попадает в Google Таблицу, привязанный к строке своей компании. Несколько профилей на компанию, без дубликатов, автосинхронизация.

Но для этого нужен бэкенд. Поднимать сервер для пет-проекта, который используется 20 раз в день, не хотелось. Тогда Shrijith подсказал идею: превратить Google Apps Script Web App в бесплатный API, а Google Таблицу — в базу данных.

Вот как выглядит вызов из расширения:

// background.js
fetch(SHEET_SYNC_URL, {
    method: "POST",
    body: JSON.stringify(message.payload),
    redirect: "follow"
})
.then(res => res.text())
.then(text => sendResponse({ok: true, data: JSON.parse(text)}))
.catch(err => sendResponse({ok: false, error: err.message}));

А на стороне Apps Script функция doPost сопоставляет лид с компанией и добавляет строку. Бесплатный хостинг, бесплатная аутентификация (теоретически), бесплатная база данных. Что могло пойти не так?

Баг, который заставил усомниться в реальности

Сначала мелкий баг: я сопоставлял лиды с компаниями по названию. «EmpInfo» vs «EmpInfo, Inc.» — неоднозначно, хрупко. Я исправил это, используя URL компании с LinkedIn как ключ. Всё стало надёжно.

Потом я перезалил обновлённый код Apps Script — и всё сломалось. Каждый запрос возвращал 401. Не «у вас баг», не «колонка не найдена», а плоский, безликий 401, который приходил от инфраструктуры Google до того, как мой код вообще запускался.

Я проверил настройки развёртывания. Четыре раза. Там крупно написано: «Доступ: Все» (Anyone). Я смотрел на это слово, как будто оно мне должно.

Но оказалось, «Все» и «Все» — не одно и то же. В манифесте, который управляет развёртыванием, стояло:

"webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "ANYONE"
}

В API Google ANYONE означает «все, кто вошёл в аккаунт Google». Не анонимные пользователи, не curl, не мой fetch() из расширения (у которого нет кук Google). Настоящее значение «публичный доступ, без логина» — это ANYONE_ANONYMOUS.

Интерфейс говорит «Все», а API различает два вида «всех». И только один из них — действительно все. Это как объявление «можно с животными», а потом выясняется, что можно только с одной золотой рыбкой под присмотром.

Я исправил и проверил простым запросом:

curl -s "$DEPLOY_URL" -w "\n%{http_code}\n"
# {"ok":false,"error":"Use POST"}
# 200

Настоящий ответ. От незнакомца. Анонимно. Как и обещано, в конце концов.

Появляется clasp

Вот что действительно стоит описать. Каждый раз, когда я что-то правил в файле Apps Script, процесс выкладки был таким: открыть веб-редактор, вставить код вручную, нажать «Развернуть», «Управление развёртываниями», создать новую версию, скопировать новый URL /exec, вставить его в background.js расширения, обновить версию манифеста, перезагрузить расширение, обновить все открытые вкладки LinkedIn.

Для одной изменённой строки. Я делал это достаточно раз, чтобы спросить вслух: «Неужели нет пакета для этого?»

Оказалось, есть. Называется clasp — официальный CLI для Apps Script от Google. Он позволяет пушить код, создавать версии и управлять развёртываниями прямо из терминала.

npm install -g @google/clasp
clasp login  # одноразовая OAuth в браузере
clasp clone <scriptId>

Часть, которая решила проблему с URL:

clasp deploy -i <existingDeploymentId> -V <newVersionNumber>

Эта команда обновляет существующее развёртывание, указывая на новую версию. Тот же ID развёртывания, тот же URL /exec, навсегда. Больше никакого копирования нового URL каждый раз.

Автоматизация всего процесса

Я обернул push, создание версии и переразвёртывание в один скрипт и Make-цель:

make apps-script-deploy MSG="fix lead matching"

Вот и всё. Весь процесс релиза.

До: открыть редактор → вставить код → развернуть → скопировать URL → вставить в расширение → обновить версию → перезагрузить.

После: make apps-script-deploy MSG="..." → готово.

Почему этот паттерн недооценён

Chrome расширение, общающееся с Google Таблицей через Apps Script Web App — это не серьёзная архитектура. Хочу это подчеркнуть.

Но это полностью бесплатно, не требует сервера, базы данных, системы аутентификации. Я потратил вечер, чтобы всё настроить. Для личного инструмента, который один человек использует пару десятков раз в день — это фантастический компромисс.

Просто знайте об одной ловушке в манифесте и установите clasp заранее, а не после трёх часов 401 ошибок.

Практический вывод

Прямо сейчас сделайте две вещи:

  1. Проверьте настройки доступа вашего Apps Script Web App: access: "ANYONE_ANONYMOUS", а не ANYONE.
  2. Установите clasp и настройте автообновление развёртывания через clasp deploy -i <id> -V <version>.

Это сэкономит вам часы отладки и кучу нервов. А если хотите глубже изучить clasp — вот репозиторий на GitHub и руководство по Web Apps.

#Google Apps Script#Chrome расширение#отладка#clasp#Google Таблицы
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →