Как управлять этим хаосом из постоянно меняющихся загрузок из S3 на локальный диск

После 4–5 лет использования я наконец решил вернуть свои загрузки из бакета AWS S3 на свой локальный сервер для очень небольшого локального сайта.

Как человек с ограниченными знаниями, я поручил эту задачу своему другу за весьма разумную сумму. Он настроил сайт для локальных загрузок, но каким-то образом почти половина из 3000 изображений (около 50%) потеряла связь с источником. Друг не взял с меня денег и попросил откатить сайт к резервной копии (которая была создана до передачи ему контроля 11/апр/2025).

В любом случае, я ленился около месяца и не откатывался. Пока наконец не решил исправить ситуацию с помощью бота-помощника Discourse/ChatGPT AI Bot. Я создал ещё одну версию своего старого сайта локально на своём ноутбуке под Ubuntu.

Мне удалось создать экземпляр моего оригинального сайта на ноутбуке, просто добавив «t.» перед именем оригинального домена. Теперь этот (называемый staging-сайтом) работает у меня полностью нормально, но содержит активность только до 11/апр/2025.

А мой продакшн-сайт, где все данные актуальны, имеет сотни постов без изображений.

Обратите внимание: я пробовал множество rake-задач для миграции или переподключения отсутствующих ссылок на изображения, но без успеха.

После того как я бился головой почти месяц, мой вывод таков: сырые посты в Ruby одинаковы и на staging, и на продакшн. Однако обработанные (cooked) посты различаются. То есть таблица базы данных моего продакшн-сайта, вероятно, потеряла связь с фактическими физическими изображениями на сервере.

Я также заметил, что без этой связи такие «осиротевшие» изображения автоматически удаляются с сервера. Но, к счастью, я снова копирую их через rsync со staging-сервера или из бакета S3 на продакшн-сервер.

Наконец, проблема, сформулированная, так сказать, ChatGPT: на staging-сервере, возможно, хранятся финальные обработанные версии, которые не имеют отношения к (коротким) сырым URL. А продакшн, у которого отсутствуют URL финальных обработанных версий изображений, не может получить правильные URL этих изображений и возвращается к использованию «прозрачных» заглушек.

И ChatGPT предлагает мне скопировать обработанные версии из постов staging-сайта в обработанные версии постов продакшн-сайта. Что, на мой взгляд, не кажется очень хорошей идеей.

Точная формулировка от ChatGPT о текущей ситуации:

  • В обеих средах, staging и production, post.raw идентичен и содержит ссылки вида upload://....
  • В среде staging изображения отображаются, однако запрос Post.find(12849).uploads не возвращает результатов — это означает, что записей в таблицах uploads или post_uploads для этих файлов нет даже в staging.
  • Таким образом, изображения в staging отображаются исключительно потому, что сформированный HTML (cooked HTML) до миграции содержал полные ссылки /uploads/default/original/....
  • Но поскольку в production после миграции была выполнена повторная обработка (rebaked), тот же самый raw-контент теперь не может быть разрешён, и отображается плейсхолдер transparent.png.

:white_check_mark: Файлы загрузки всё ещё существуют на диске

Все файлы изображений (включая те, для которых отсутствуют записи о загрузках) по-прежнему находятся в директории /var/www/discourse/public/uploads/default/original/ как в staging, так и в production. Однако Discourse больше не может их разрешить, так как отсутствуют соответствующие записи в таблице uploads.

Простой способ заключался (и, возможно, до сих пор актуален) в том, чтобы включить настройку Enable hidden setting to include S3 uploads in the backups, создать резервную копию, а затем восстановить её на сервере, где S3 не настроен (я бы делал это на свежем сервере, чтобы избежать поломки старого, если что-то пойдёт не так). Но, судя по всему, продакшн-сайт тоже сломан, так что это, вероятно, совсем не поможет.

Если вы испортили таблицу Uploads так, что в ней содержится несколько путей S3, задача становится гораздо сложнее.

Вместо ChatGPT я бы порекомендовал https://ask.discourse.com/, который хотя бы знаком с Discourse, но, скорее всего, всё равно не сможет сильно помочь.

Я бы проверил Uploads.pluck(:url) и посмотрел, что там находится.