Введение
Баннер технического обслуживания должен исчезать после окончания периода действия объявления. Если оставить его видимым, посетители могут подумать, что старая проблема всё ещё продолжается. Вы создадите Worker, который читает уведомление из KV и решает, следует ли его отображать.
Здесь используются два независимых срока. Срок отображения приложения сообщает вашему коду, когда нужно прекратить показ сообщения. Истечение срока действия KV сообщает службе хранения, когда нужно удалить запись. Вы намеренно оставите в хранилище старую эталонную запись, чтобы убедиться: приложение умеет скрывать просроченные данные, даже пока они существуют. Затем вы понаблюдаете, как вторая запись автоматически истечёт в облачном KV.
Сначала завершите Serve Account Preferences. На этой независимой виртуальной машине уже установлены Node.js 22.22.0 и локальный для проекта Wrangler 4.131.1 в каталоге /home/labex/project/temporary-notices. Используйте собственную учебную учётную запись и те же разрешения на чтение данных учётной записи, запись Workers и запись KV. Вы создадите один временный Worker и одно пространство имён, будете использовать только синтетические сообщения и выполните очистку перед выходом из системы. Для этого небольшого упражнения не нужны купленный домен или платное обновление. Помимо написания и тестирования обработчика, выделите около пяти минут на наблюдение за таймерами.
Подключение пространства имён для уведомлений
На этом шаге вы подключите новое пространство имён для временных уведомлений. Используйте отдельное пространство имён и уникальное имя Worker, чтобы эксперименты с истечением срока действия не удалили данные другого приложения. Привязка NOTICES подключит обработчик к этому ресурсу.
Перейдите в подготовленный проект:
cd /home/labex/project/temporary-notices
Один раз сгенерируйте уникальное имя. Команда openssl rand -hex 6 выводит случайный суффикс, а $(...) вставляет его в имя. Переменная оболочки сохранит это имя для следующих команд в данном терминале.
WORKER_NAME="labex-notices-$(openssl rand -hex 6)"
printf '%s\n' "$WORKER_NAME"
Авторизуйте эту виртуальную машину. Помимо чтения идентификатора вашей учётной записи, разрешение Workers Scripts Write позволяет развёртывать и удалять Worker, а Workers KV Write — управлять пространством имён и ключами этого задания.
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write
Откройте показанную ссылку для устройства в браузере, введите текущий код, проверьте запрошенные разрешения и учебную учётную запись, затем авторизуйте Wrangler. На странице согласия также может появиться разрешение фонового доступа. Вернитесь в терминал и дождитесь завершения входа.
Проверьте те же разрешения на запись Worker и KV, которые были представлены в задании Create a Feature Flag Store. Перед авторизацией убедитесь, что выбрана ваша учебная учётная запись.
npx wrangler whoami --json
Убедитесь, что указано loggedIn: true, а поле name содержит имя учебной учётной записи, даже если в списке присутствует только одна учётная запись. Скопируйте id этой учётной записи. Сохраните его в конфигурации ниже, заменив YOUR_ACCOUNT_ID перед выполнением команды. Здесь-document cat записывает всё между двумя строками JSON в файл, а > заменяет содержимое файла. Некавыченный разделитель позволяет оболочке подставить $WORKER_NAME.
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true
}
JSON
Создайте пространство имён в этой учётной записи. Его название содержит уникальное имя Worker, чтобы позднее вы могли легко сопоставить эти ресурсы. Параметр --update-config=false оставляет изменение привязки на ваше усмотрение и не изменяет файл автоматически.
npx wrangler kv namespace create "$WORKER_NAME-notices" --update-config=false
В выводе появится идентификатор нового пространства имён. Скопируйте его, затем замените YOUR_ACCOUNT_ID и YOUR_NAMESPACE_ID в этой полной конфигурации. Имя привязки NOTICES выбирается для вашего кода, а идентификатор указывает на реальный ресурс Cloudflare.
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true,
"kv_namespaces": [
{ "binding": "NOTICES", "id": "YOUR_NAMESPACE_ID" }
]
}
JSON
npx wrangler kv namespace list
Найдите пространство имён этого задания и сравните его идентификатор с указанным в файле. Другие пространства имён могут присутствовать — не изменяйте их. Эта конфигурация определяет, какую учётную запись и какой ресурс должны использовать последующие команды. Привязка — это ссылка на пространство имён, а не копия его данных.
Скрытие старого уведомления до удаления его данных
На этом шаге вы отделите поведение отображения от очистки хранилища. Метка времени — это число, обозначающее момент времени. Здесь displayUntil использует секунды Unix, отсчитываемые от начала 1970 года в UTC. Date.now() возвращает миллисекунды, поэтому обработчик делит результат на 1000 перед сравнением. Сравнение значений в одной единице измерения позволяет избежать распространённой ошибки со сроком действия.
Запишите обработчик с помощью этого кавычечного here-document:
cat > src/index.js <<'JS'
export default {
async fetch(request, env) {
const url = new URL(request.url);
const key = url.searchParams.get("key") ?? "notice:maintenance";
if (url.pathname !== "/notice" || !/^notice:[a-z]{1,20}$/.test(key)) {
return new Response("Not found", { status: 404 });
}
let entry;
try {
entry = await env.NOTICES.getWithMetadata(key, "text");
} catch {
return Response.json({ error: "Notice storage unavailable" }, { status: 503 });
}
if (entry.value === null) {
return Response.json({ visible: false, reason: "missing" });
}
let notice;
try {
notice = JSON.parse(entry.value);
} catch {
return Response.json({ visible: false, reason: "invalid" });
}
if (!notice || typeof notice.message !== "string" || !notice.message.trim() ||
!Number.isSafeInteger(notice.displayUntil) || notice.displayUntil <= 0) {
return Response.json({ visible: false, reason: "invalid" });
}
if (Math.floor(Date.now() / 1000) >= notice.displayUntil) {
return Response.json({ visible: false, reason: "expired" });
}
return Response.json({
visible: true, message: notice.message,
kind: entry.metadata?.kind === "maintenance" ? "maintenance" : "general"
});
}
};
JS
Параметр запроса key выбирает синтетическое уведомление. Если его не указать, обработчик использует notice:maintenance. Приложение скрывает отсутствующие, повреждённые и просроченные уведомления и возвращает поясняющий ответ JSON. Если чтение из KV завершается ошибкой, обработчик возвращает 503, а не делает вид, что уведомление отсутствует. Поле метаданных kind указывает категорию уведомления; при отсутствии метаданных или неожиданном значении используется general.
Для сравнения срока используется >=: уведомление скрывается в момент наступления срока, а не через одну секунду после него. Эта проверка выполняется при каждом запросе. Если веб-страница уже показала баннер, ей также потребуется обновить или удалить его с помощью собственного таймера: ответ Worker сам по себе не может изменить уже отображённую страницу.
Сохраните намеренно старое эталонное уведомление локально. Срок 1 — это известный момент времени в 1970 году, поэтому с точки зрения приложения эта запись уже просрочена. Мы намеренно не задаём для неё истечение срока в KV, чтобы запись оставалась доступной для проверки.
npx wrangler kv key put notice:reference '{"message":"Old maintenance notice","displayUntil":1}' --binding NOTICES --local --metadata '{"kind":"maintenance"}'
npx wrangler dev --local --ip 0.0.0.0 --port 8080 > local.log 2>&1 &
DEV_PID=$!
cat local.log
Фоновый сервер записывает вывод в local.log. Повторяйте команду просмотра журнала, пока в нём не появится сообщение о готовности на порту 8080. Теперь запросите старое эталонное уведомление:
curl -i 'http://127.0.0.1:8080/notice?key=notice:reference'
Ожидайте HTTP 200 и {"visible":false,"reason":"expired"}. Кавычки не позволяют оболочке интерпретировать вопросительный знак в URL как специальный синтаксис имени файла. Убедитесь, что запись по-прежнему существует:
npx wrangler kv key get notice:reference --binding NOTICES --local --text
JSON по-прежнему присутствует. Старое уведомление не отображается благодаря коду, а не автоматическому удалению. Отсутствующий ключ также должен обрабатываться безопасно:
curl -i 'http://127.0.0.1:8080/notice?key=notice:missing'
Ожидайте {"visible":false,"reason":"missing"}. Оставьте локальный сервер работающим до этапа очистки.
Публикация уведомления с двумя сроками
На этом шаге вы сначала опубликуете Worker, а затем запустите короткий период отображения облачного уведомления. Подготовка конечной точки до запуска таймера даст вам время проверить активный результат.
Создайте такое же эталонное уведомление без срока действия в удалённом пространстве имён:
npx wrangler kv key put notice:reference '{"message":"Old maintenance notice","displayUntil":1}' --binding NOTICES --remote --metadata '{"kind":"maintenance"}'
npx wrangler deploy
Убедитесь, что в выводе указаны правильное имя Worker и привязка NOTICES, затем сохраните фактический общедоступный URL:
WORKER_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$WORKER_URL/notice?key=notice:reference"
Ожидайте, что старое эталонное уведомление будет скрыто с причиной expired. Если имя хоста ещё не готово, подождите и повторите запрос до начала отсчитываемой части задания. Пока не запрашивайте ключ уведомления по умолчанию: чтение отсутствующих ключей KV также может кэшироваться.
Прочитайте оставшиеся инструкции перед выполнением следующих команд. date +%s возвращает текущее время виртуальной машины в секундах Unix, а $((...)) выполняет арифметические операции оболочки. Мы прекратим отображать уведомление через три минуты, а ещё через одну минуту попросим KV удалить его.
DISPLAY_UNTIL=$(($(date +%s) + 180))
KV_EXPIRES=$((DISPLAY_UNTIL + 60))
Запишите фактические данные приложения. Некавыченный разделитель JSON вставит числовой срок в файл:
cat > notice.json <<JSON
{"message":"Maintenance starts soon","displayUntil":$DISPLAY_UNTIL}
JSON
Параметр --path считывает значение из этого файла. Параметр --expiration задаёт абсолютный срок истечения KV в секундах Unix, а --metadata добавляет категорию уведомления вместе со значением.
npx wrangler kv key put notice:maintenance --path notice.json --binding NOTICES --remote --expiration "$KV_EXPIRES" --metadata '{"kind":"maintenance"}'
KV также поддерживает относительный TTL (time to live, время жизни), выраженный в секундах с момента записи. В Wrangler этот параметр называется --ttl, а в API привязки — expirationTtl. И относительный, и абсолютный срок должны наступать не ранее чем через 60 секунд. Здесь мы используем абсолютный срок, чтобы напрямую сравнить два времени окончания. См. раздел Параметры истечения срока действия KV.
npx wrangler kv key list --binding NOTICES --remote
Найдите notice:maintenance, его expiration и метаданные kind. У эталонной записи срок действия KV не задан. Теперь прочитайте активное сообщение:
curl -i "$WORKER_URL/notice"
Ожидайте HTTP 200 и {"visible":true,"message":"Maintenance starts soon","kind":"maintenance"}. Выполните проверку этого шага сейчас, пока период отображения не завершился. Она проверяет фактическое облачное значение, метаданные, срок действия KV, привязку и активный ответ. Одного сохранённого значения времени недостаточно, чтобы доказать, что уведомление было записано.
Если вы пропустили период отображения, повторите два присваивания времени, заново запишите notice.json и повторите удалённую запись с новыми сроками. Не выполняйте записи подряд без паузы. Ранее кэшированный ответ может не сразу отразить замену; учтите это и повторите активную проверку. Переходите дальше только после её успешного завершения.
После успешной активной проверки откройте Storage & databases → Workers KV в Dashboard той же учебной учётной записи и выберите пространство имён задания. Откройте KV Pairs и нажмите View рядом с notice:maintenance, чтобы увидеть сообщение и displayUntil. Срок действия KV и метаданные проверяйте по списку ключей CLI; этот экран показывает сохранённое значение. Выполняйте только чтение: время продолжает идти. Если ключ уже истёк, переходите к следующему шагу, не создавая его заново только ради просмотра. Имя и метка времени на снимке — примеры, которые не нужно копировать.

Наблюдение за скрытием содержимого и автоматическим истечением срока
На этом шаге вы понаблюдаете за двумя сроками, не удаляя ключ уведомления о техническом обслуживании вручную. Не изменяйте notice.json, чтобы сравнить исходные данные с результатом.
Выведите оба запланированных времени и текущее время:
printf 'displayUntil=%s
KV expiration=%s
now=%s
' "$DISPLAY_UNTIL" "$KV_EXPIRES" "$(date +%s)"
Подождите, пока текущее время не достигнет displayUntil. Эти команды вычисляют только оставшуюся задержку. Если срок уже прошёл, условие пропустит ожидание. sleep принимает секунды, а if не позволяет передать ему отрицательную задержку.
WAIT_SECONDS=$((DISPLAY_UNTIL - $(date +%s) + 1))
if [ "$WAIT_SECONDS" -gt 0 ]; then sleep "$WAIT_SECONDS"; fi
curl -i "$WORKER_URL/notice"
Сообщение больше не должно отображаться. До истечения срока в KV ожидайте {"visible":false,"reason":"expired"}. Если вы вернулись уже после истечения срока ключа в KV, поле reason может иметь значение missing; оба результата предотвращают отображение. Сохранённая эталонная запись по-прежнему позволяет напрямую проверить поведение срока приложения:
curl -i "$WORKER_URL/notice?key=notice:reference"
npx wrangler kv key get notice:reference --binding NOTICES --remote --text
Конечная точка скрывает эталонную запись с причиной expired, тогда как чтение из KV всё ещё возвращает её старый JSON. Это показывает, зачем нужен срок приложения, даже когда сохранённые данные остаются доступными.
Теперь дождитесь времени истечения срока KV:
WAIT_SECONDS=$((KV_EXPIRES - $(date +%s) + 1))
if [ "$WAIT_SECONDS" -gt 0 ]; then sleep "$WAIT_SECONDS"; fi
npx wrangler kv key list --binding NOTICES --remote
curl -i "$WORKER_URL/notice"
В списке должна остаться только notice:reference, а конечная точка по умолчанию должна вернуть {"visible":false,"reason":"missing"}. Не выполняйте команду удаления для ключа уведомления о техническом обслуживании: это наблюдение посвящено автоматическому истечению срока. Если запись всё ещё отображается, повторяйте проверки только для чтения с интервалом 15 секунд в течение максимум двух минут. Это окно наблюдения в рамках задания, а не гарантия точного времени удаления. Если состояние не изменилось, сообщите о неокончательном результате, а не заявляйте об успехе. Ошибка авторизации или сети не доказывает отсутствие записи.
Истечение срока KV и кэш чтения — разные понятия. Истечение срока применяется даже при заданной более длительной продолжительности кэша чтения. Однако изменения сохранённой конфигурации могут распространяться с задержкой, поэтому новый срок, записанный после предыдущего чтения, не является немедленной глобальной гарантией планирования. В этом задании проверяется срок, содержащийся в записи, которую фактически прочитал обработчик.
Удаление временных облачных ресурсов
На этом шаге вы удалите оба ресурса, пока Wrangler всё ещё авторизован. Пространство имён может существовать дольше Worker, поэтому удаление приложения само по себе не очищает его данные.
Остановите локальный процесс разработки, запущенный в этом терминале:
kill "$DEV_PID"
Перед удалением проверьте сохранённые ссылки на ресурсы:
cat wrangler.jsonc
Убедитесь, что указаны имя Worker labex-notices-... и идентификатор пространства имён NOTICES. Удалите Worker, выбранный этой конфигурацией:
npx wrangler delete
Если появится запрос подтверждения, проверьте, что отображаемое имя соответствует этому заданию, и подтвердите удаление клавишей y. Затем удалите только пространство имён, на которое ссылается NOTICES. Это также удалит сохранённую эталонную запись:
npx wrangler kv namespace delete --binding NOTICES
Перед подтверждением проверьте пространство имён в соответствующем запросе. Не изменяйте wrangler.jsonc, чтобы независимая проверка могла определить, какие ресурсы должны отсутствовать.
npx wrangler kv namespace list
Пространство имён этого задания должно отсутствовать, а несвязанные пространства имён должны сохраниться. Обновите списки в Dashboard и убедитесь, что Worker и пространство имён этого задания исчезли. Неудачный запрос или истёкшая сессия входа не доказывают удаление. Выполните проверку этого шага до выхода из системы, чтобы она могла просмотреть авторизованный список ресурсов.
Завершение авторизации виртуальной машины
На этом шаге вы отключите Wrangler после успешного прохождения проверки очистки. Выход из системы завершает сохранённую авторизацию Wrangler на этой виртуальной машине; он не удаляет облачные ресурсы и не завершает обычную сессию Dashboard в браузере.
npx wrangler logout
npx wrangler whoami --json
Убедитесь, что структурированный результат содержит "loggedIn": false. Эта команда без авторизации может завершиться с ненулевым кодом выхода — в данном случае это ожидаемо. Если вывод содержит только ошибку подключения и не содержит явного состояния аутентификации, повторите команду после восстановления подключения.
Оставшиеся локальные файлы и локальное состояние KV принадлежат этой временной виртуальной машине. Они не связаны с облачными ресурсами, которые вы уже удалили. Теперь можно завершить задание.
Итоги
Вы создали средство чтения уведомлений, которое проверяет срок отображения при каждом запросе, безопасно обрабатывает отсутствующие и некорректные данные и считывает категорию из метаданных KV. Сохранённая старая запись показала, что для скрытия содержимого не обязательно сначала удалять его. Вторая запись продемонстрировала истечение срока KV по абсолютной метке времени и отдельную проверку автоматического удаления.
Вы различили срок отображения, срок хранения и поведение кэша чтения, затем удалили временные ресурсы и вышли из системы. Далее вы импортируете и будете поддерживать небольшой каталог перенаправлений в KV.



