Я попытался получить к нему доступ, но, похоже, что API-эндпоинт для удаления загрузки не существует.
Публикую это здесь, так как не смог найти никаких упоминаний или предложений по этой теме. Ближайшее, что я нашёл: API: a scope with access to /uploads
В настройках детализированных прав доступа ключа API для загрузок я вижу, что доступна опция «создание», но опция «удаление» отсутствует.
В зависимости от вашей конфигурации, загрузка файлов является общим ресурсом (ссылки на него содержатся в постах, профилях пользователей, значках и т. д.), поэтому удалять их без должной проверки довольно рискованно.
Кроме того, если файлы нигде не используются, они автоматически удаляются через некоторое время.
Сценарий использования заключается в своевременном удалении загрузок в рамках автоматизированных рабочих процессов с участием человека (human-in-the-loop) с помощью activepieces, data explorer и API.
Цель — предоставить обычным модераторам простой и надёжный способ немедленного полного удаления загрузок без доступа по SSH, а также комплексно охватить все известные случаи разрыва ссылок на загрузки и автоматически очистить соответствующие URL в CDN (в случае проксируемых аватаров — префикс URL, основанный на имени пользователя).
Меня успокоило то, что при тестировании выяснилось: ссылки на аватары, фоны профиля и фоны карточек автоматически обрабатываются при удалении загрузки.
Два основных сценария: «выжженная земля» и «хирургическое удаление». Вот текущая работа над сценарием «выжженная земля»:
Преобразование в список хешей загрузок:
URL аватаров (извлечь имя пользователя с помощью регулярного выражения) → получить хеш аватара через запрос в data explorer (хеш не содержится в URL)
URL тем/сообщений → собрать все хеши загрузок, использованные в этом сообщении, с помощью data explorer
Прямые URL оригинальных/оптимизированных загрузок (включая фоны профиля и карточек) → извлечь хеши с помощью регулярного выражения
Затем выполнить запрос по каждому хешу для поиска всех вхождений (один запрос data explorer охватывает все случаи, по одному хешу за раз):
список имён пользователей/ID тех, кто использовал этот хеш для аватара
список имён пользователей/ID тех, кто использовал этот хеш для фона профиля
список имён пользователей/ID тех, кто использовал этот хеш для фона карточки
список всех сообщений (в сыром виде), использующих эту загрузку
Действия:
приостановить всех пользователей, использовавших загрузку в качестве аватара, фона профиля или фона карточки
приостановить всех пользователей, у которых есть сообщения, ссылающиеся на целевую загрузку, но исключить случаи, когда ссылка находится внутри цитаты
удалить все темы
удалить все сообщения
уничтожить загрузку (отсутствует конечная точка API)
очистить все URL CDN (оптимизированные и неоптимизированные)
очистить стандартный префикс для проксируемых URL аватаров для каждого связанного имени пользователя (чтобы охватить все размеры)
Сценарий «хирургического удаления» по сути аналогичен, но не предусматривает приостановку связанных пользователей и требует некоторых изменений в части сбора всех хешей загрузок из URL сообщений/тем.
Вероятно, всё ещё потребуется удалять сами сообщения и темы, чтобы избежать битых ссылок, но если возможно, предпочтительнее было бы просто удалить разметку Markdown для конкретной загрузки (не затрагивая другие загрузки). Как в этой автоматизации, если бы она не удаляла все ссылки на загрузку в разметке.
В идеале я хотел бы иметь возможность блокировать хеши, чтобы после выполнения описанных выше действий кто-то не мог просто создать новый аккаунт и повторно загрузить тот же файл.
На данный момент это, по-видимому, невозможно для обычного модератора, например, с помощью отслеживаемых слов. Поэтому, возможно, периодическое сканирование, аналогичное описанному выше, для списка хешей, станет способом решения этой проблемы.
Я просто делюсь этой ссылкой для пользователей, которые прочитают и согласятся с вашим постом — возможно, им будет интересно проголосовать за это (знаю, что вы уже видели это):
Да! Я чуть не расплакался от радости, увидев ваш плагин в начале этого месяца, ха-ха. Огромное спасибо за его создание и публикацию.
Однако есть сценарии, которые мне не удалось охватить с его помощью:
С использованием префикса поиска upload: не удалось найти фоны профилей или фоны карточек (если только кто-то не вставил прямую ссылку на эти изображения в пост).
Безвозвратное удаление изображений в таких случаях (изображение, не связанное ни с одним постом).
Аналогично для аватаров: получить хеш аватара (обычно он не указан в URL), затем найти все другие аккаунты, которые, возможно, использовали его, если требуется блокировка, и удалить загрузку, не связанную с постом.
Желательные функции:
Запуск вебхука при удалении для очистки CDN всех вариантов загрузок после их удаления.
Массовое/автоматическое удаление всех постов и тем, содержащих ссылку на загрузку, при удалении самой загрузки.
В веб-интерфейсе Discourse, похоже, невозможно удалить (отвязать) аватар пользователя — ни администратору/модератору, ни самому пользователю (кроме как загрузив замену). Это возможно для фона профиля и баннера. Однако в обоих случаях загрузка не уничтожается сразу, и если существует другой неизвестный пользователь, использующий ту же загрузку для части своего профиля (аватар, фон профиля, карточка профиля), она также не будет удалена позже.
В итоге я решил, что стоит попробовать создать автоматизированные рабочие процессы, способные обрабатывать большую часть или весь процесс (с человеческим контролем/одобрением и/или упрощённым вводом данных). Это позволит применять решение последовательно, охватывать крайние случаи и, в идеале, минимизировать вероятность наличия постов с неработающими ссылками на удалённые загрузки. Также необходимо автоматически блокировать пользователей при необходимости (если целевая загрузка не находится внутри цитаты) и очищать CDN.
Вот что у меня пока есть для запросов в Data Explorer (ещё не на этапе «связывания» их вместе) с целью охвата:
URL тем
URL постов
Прямые URL изображений (посты, фон профиля, фон карточки)
URL аватаров (проксируемые)
Подготовка списка хешей загрузок
username (из проксируемого URL аватара) к хешу загрузки
-- [params]
-- user_id :username
SELECT
up.sha1 AS upload_hash
FROM
users u
JOIN uploads up ON up.id = u.uploaded_avatar_id
WHERE
u.id = :username
URL поста/темы к списку хешей загрузок
-- [params]
-- post_id :post_url
SELECT
COALESCE(json_agg(up.sha1), '[]'::json) AS upload_hashes
FROM
upload_references ur
JOIN uploads up ON up.id = ur.upload_id
WHERE
ur.target_id = :post_url
AND ur.target_type = 'Post'
Другие типы URL загрузок, о которых мне известно, содержат хеш непосредственно в URL (для получения этих хешей не требуется использовать Data Explorer).
Сбор и подготовка всей доступной информации о хеше загрузки
-- [params]
-- string :upload_hash
-- string :upload_schemeless_prefix
-- string :cdn_url_prefix
-- string :app_hostname
WITH target_uploads AS (
SELECT
id,
url,
user_id
FROM
uploads
WHERE
sha1 = :upload_hash
),
all_urls AS (
SELECT
url
FROM
target_uploads
UNION
SELECT
url
FROM
optimized_images
WHERE
upload_id IN (
SELECT
id
FROM
target_uploads)
),
post_data AS (
SELECT
p.id AS post_id,
p.topic_id,
u.id AS user_id,
u.username AS author,
p.created_at,
CASE WHEN p.post_number = 1 THEN
TRUE
ELSE
FALSE
END AS is_topic_starter,
CASE WHEN t.archetype = 'private_message' THEN
TRUE
ELSE
FALSE
END AS is_dm,
p.raw
FROM
upload_references ur
JOIN posts p ON p.id = ur.target_id
AND ur.target_type = 'Post'
JOIN topics t ON t.id = p.topic_id
JOIN users u ON u.id = p.user_id
WHERE
ur.upload_id IN (
SELECT
id
FROM
target_uploads)
AND p.deleted_at IS NULL
)
SELECT
(
SELECT
COALESCE(json_agg(id), '[]'::json)
FROM
target_uploads) AS upload_ids,
(
SELECT
COALESCE(json_agg(url), '[]'::json)
FROM
all_urls) AS upload_s3_schemeless_urls,
(
SELECT
COALESCE(json_agg(
CASE WHEN url LIKE '//%' THEN
REPLACE(url, :upload_schemeless_prefix, :cdn_url_prefix)
ELSE
:cdn_url_prefix || url
END), '[]'::json)
FROM
all_urls) AS upload_cdn_urls,
(
SELECT
COALESCE(json_agg(json_build_object('user_id', id, 'username', username)), '[]'::json)
FROM
users
WHERE
uploaded_avatar_id IN (
SELECT
id
FROM
target_uploads)) AS avatar_users,
(
SELECT
COALESCE(json_agg('https://' || :app_hostname || '/user_avatar/' || :app_hostname || '/' || username || '/'), '[]'::json)
FROM
users
WHERE
uploaded_avatar_id IN (
SELECT
id
FROM
target_uploads)) AS avatar_proxied_url_prefixes,
(
SELECT
COALESCE(json_agg(json_build_object('user_id', u.id, 'username', u.username)), '[]'::json)
FROM
user_profiles up
JOIN users u ON u.id = up.user_id
WHERE
up.profile_background_upload_id IN (
SELECT
id
FROM
target_uploads)) AS profile_background_users,
(
SELECT
COALESCE(json_agg(json_build_object('user_id', u.id, 'username', u.username)), '[]'::json)
FROM
user_profiles up
JOIN users u ON u.id = up.user_id
WHERE
up.card_background_upload_id IN (
SELECT
id
FROM
target_uploads)) AS card_background_users,
(
SELECT
COALESCE(json_agg(pd.topic_id), '[]'::json)
FROM
post_data pd
WHERE
pd.is_topic_starter = TRUE) AS topic_ids,
(
SELECT
COALESCE(json_agg(pd.post_id), '[]'::json)
FROM
post_data pd
WHERE
pd.is_topic_starter = FALSE) AS post_ids,
(
SELECT
COALESCE(json_agg(json_build_object('post_id', pd.post_id, 'topic_id', pd.topic_id, 'user_id', pd.user_id, 'author', pd.author, 'created_at', pd.created_at, 'is_topic_starter', pd.is_topic_starter, 'is_dm', pd.is_dm, 'raw', pd.raw)), '[]'::json)
FROM
post_data pd) AS post_details
Не охватывает: темы/посты в очереди на проверку или черновики постов.
Вывод:
upload_ids (должен быть только один)
upload_s3_schemeless_urls
upload_cdn_urls
avatar_users
avatar_proxied_url_prefixes
profile_background_users
card_background_users
topic_ids
post_ids
post_details
Это предоставит необходимую информацию для выборочного использования следующих API:
Блокировка пользователя (для постов можно использовать поле raw из post_details, чтобы условно исключить пользователей, если разметка загрузки находится внутри цитаты)
Удаление загрузки (API на данный момент отсутствует)
А также для дальнейших действий:
Очистка всех URL CDN (все варианты: оптимизированные и оригинальные и т.д.)
Очистка URL с префиксом проксируемых аватаров (для покрытия всех размеров)
Автоматически обрабатывается Discourse:
Удаление ссылки на загрузку из всех профилей пользователей (аватар, фон профиля и фон карточки) немедленно при уничтожении/удалении загрузки.