我试着去寻找,但似乎没有用于删除上传的 API 端点 不存在。

我把它发在这里,因为我没有找到任何关于此的提及或建议,我找到的最接近的是:API: a scope with access to /uploads
在上传的 API 密钥粒度范围设置中,我看到“创建”可用,但“删除”不可用。
似乎一个可能的变通方法是创建一个自定义自动化并通过 API 触发它。
我试着去寻找,但似乎没有用于删除上传的 API 端点 不存在。

我把它发在这里,因为我没有找到任何关于此的提及或建议,我找到的最接近的是:API: a scope with access to /uploads
在上传的 API 密钥粒度范围设置中,我看到“创建”可用,但“删除”不可用。
似乎一个可能的变通方法是创建一个自定义自动化并通过 API 触发它。
您的使用场景是什么?
根据您的配置,上传文件作为共享资源(被帖子、用户资料、徽章等引用),在没有进行适当尽职调查的情况下删除它们是有些风险的。
此外,如果上传文件没有被任何地方引用,它们会在一段时间后被自动清理掉。
该用例是使用 activepieces、data explorer 和 API 在“人在回路”自动化工作流程中,对上传内容进行时间敏感性的移除。
目标是让普通版主能够有一种简单的方式,自信地立即完全移除上传内容,同时全面覆盖所有已知的破坏上传引用的情况,并自动清除特定 URL 的 CDN(对于代理头像的情况,则清除基于用户名的 URL 前缀)。
在测试时,我很高兴地看到,当销毁上传内容时,头像、个人资料背景和卡片背景引用会自动处理。
https://github.com/discourse/discourse/commit/e1975e293f2625259e925b4a3c93d88d5acfcaa8
https://github.com/discourse/discourse/commit/38e7b1a0492dd4282c3cd3b1ddb2b3343661d31f
主要有两种场景:“焦土政策”和“外科手术式移除”。以下是“焦土政策”的进行中工作:
转换为上传哈希列表:
头像 URL(使用正则表达式提取用户名)→ 通过数据探索器查询获取头像哈希(哈希不在 URL 中)
主题/帖子 URL → 使用数据探索器收集该帖子中使用的所有上传哈希
直接原始/优化上传 URL(包括个人资料背景和卡片背景)→ 使用正则表达式提取哈希
然后对每个哈希进行查询,以查找所有出现的地方(一次一个数据探索器查询,涵盖所有情况):
用户名/ID 列表,用于曾将其用作头像的用户
用户名/ID 列表,用于曾将其用作个人资料背景的用户
用户名/ID 列表,用于曾将其用作卡片背景的用户
使用此上传的所有帖子(原始)列表
操作:
暂停所有曾将该上传用作头像、个人资料背景或卡片背景的用户
暂停所有引用目标上传的用户帖子,但如果其引用嵌套在引用中则排除
删除所有主题
删除所有帖子
销毁上传(没有 API 端点)
清除所有 CDN URL(优化/未优化)
清除代理头像 URL 的标准前缀,针对每个相关用户名(以覆盖所有尺寸)
“外科手术式移除”场景基本上是相同的,但不暂停相关用户,并且在从帖子/主题 URL 收集所有上传哈希方面需要进行一些更改。
可能仍然需要删除帖子/主题本身以避免引用断裂,但如果可能,剥离该特定上传的上传 Markdown(不触及其他上传)会更好。类似于这个自动化,但它会剥离所有上传的 Markdown 引用。
理想情况下,我希望能够阻止哈希,这样在完成上述操作后,某人就不能简单地创建一个新帐户并重新上传。
![]()
我不认为普通版主目前可以做到,比如通过使用监视词。所以,对哈希列表进行上述那样的定期扫描可能是处理这种情况的一种方法。
我只是为会阅读并同意您的帖子的用户链接此内容,他们可能对投票支持此内容感兴趣(我知道您已经阅读过了):
是的!看到您的插件时,我简直喜极而泣哈哈。非常感谢您制作并分享它。![]()
我没有找到用它来覆盖的一些场景:
“有更好”的功能:
在 Discourse(Web UI)中,似乎管理员/版主或用户本人都无法删除(取消链接)用户头像(除了上传一个替换项)。个人资料背景和横幅是可以的。但这两种情况都不会立即销毁上传项,如果存在另一个未知用户将其上传项用于其个人资料的某个部分(头像、个人资料背景、个人资料卡),它也不会在之后被清除。
我最终认为,值得尝试创建一些可以处理大部分或全部流程的自动化工作流程(带有人工审核/批准和/或简化的用户输入)。以便更容易地保持一致性、覆盖边缘情况,并理想情况下最大限度地减少存在指向已删除上传项的未删除帖子的可能性。如果需要,还可以自动停用并清除 CDN。
这是我目前为数据资源管理器查询所做的(尚未达到“将它们粘合在一起”的阶段),目标是覆盖:
-- [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
-- [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 本身中(无需使用数据资源管理器来获取这些哈希值)。
upload_schemeless_prefix:
//my-s3-bucket.somehost.example.com
cdn_url_prefix:
https://cdn.example.com
app_hostname:
www.example.com
upload_hash:
0000000000000000000000000000000000000000
-- [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