Suppression de l'envoi via l'API

J’ai cherché, mais il semble qu’un point de terminaison d’API pour supprimer un téléversement n’existe pas.

Muppet Show: Kermit's Trapeze Fall

Je publie ceci ici, car je n’ai trouvé aucune mention ou suggestion à ce sujet, le plus proche que j’ai trouvé était : API: a scope with access to /uploads

Dans les paramètres de portée granulaire de la clé API pour les téléversements, je vois que ‘create’ (créer) est disponible, mais ‘delete’ (supprimer) ne l’est pas.

Il semble qu’une solution de contournement potentielle pourrait être de créer une automatisation personnalisée et de la déclencher via l’API.

4 « J'aime »

Quel est votre cas d’utilisation ?

Selon votre configuration, les téléchargements étant une ressource partagée (référencée par des publications, le profil utilisateur, des badges, etc.), il est quelque peu risqué de les supprimer sans une diligence raisonnable appropriée.

De plus, ils sont automatiquement nettoyés après un court laps de temps s’ils ne sont référencés nulle part.

3 « J'aime »

Le cas d’utilisation concerne la suppression des téléchargements nécessitant une intervention rapide dans le cadre de flux de travail d’automatisation avec intervention humaine, en utilisant activepieces, l’explorateur de données et l’API.

L’objectif est de permettre aux modérateurs réguliers d’avoir accès à un moyen simple de supprimer complètement et en toute confiance les téléchargements immédiatement, sans accès SSH, et de couvrir de manière exhaustive tous les cas connus de rupture des références de téléchargement, ainsi que de purger automatiquement le CDN des URL spécifiques (et dans le cas des avatars proxifiés, le préfixe d’URL basé sur le nom d’utilisateur).


J’ai été soulagé en testant de voir que les références aux avatars, aux arrière-plans de profil et aux arrière-plans de carte sont gérées automatiquement lorsqu’un téléchargement est détruit.

https://github.com/discourse/discourse/commit/e1975e293f2625259e925b4a3c93d88d5acfcaa8

https://github.com/discourse/discourse/commit/38e7b1a0492dd4282c3cd3b1ddb2b3343661d31f


Les deux scénarios principaux seraient la « terre brûlée » et la « suppression chirurgicale ». Voici le travail en cours pour la terre brûlée :


Conversion en une liste de hachages de téléchargement :

  • URL d’avatar (extraire le nom d’utilisateur à l’aide d’une expression régulière) → obtenir le hachage de l’avatar via une requête d’explorateur de données (le hachage n’est pas dans l’URL)

  • URL de sujet/message → collecter tous les hachages de téléchargement utilisés dans ce message à l’aide de l’explorateur de données

  • URL de téléchargement originales/optimisées directes (y compris l’arrière-plan de profil et l’arrière-plan de carte) → extraire les hachages à l’aide d’une expression régulière


Ensuite, interroger chaque hachage pour trouver toutes les occurrences (une requête d’explorateur de données pour couvrir tous les cas, un seul hachage à la fois) :

  • liste de noms d’utilisateur/ID, pour les utilisateurs qui l’ont utilisé comme avatar

  • liste de noms d’utilisateur/ID, pour les utilisateurs qui l’ont utilisé comme arrière-plan de profil

  • liste de noms d’utilisateur/ID, pour les utilisateurs qui l’ont utilisé comme arrière-plan de carte

  • liste de tous les messages (bruts) qui utilisent ce téléchargement


Actions :

  • suspendre tous les utilisateurs qui ont utilisé le téléchargement pour un avatar, un arrière-plan de profil ou un arrière-plan de carte

  • suspendre tous les utilisateurs qui ont des messages faisant référence au téléchargement cible, mais exclure si leur référence est imbriquée dans une citation

  • supprimer tous les sujets

  • supprimer tous les messages

  • détruire le téléchargement (pas de point de terminaison API)

  • purger toutes les URL CDN (optimisées/non optimisées)

  • purger le préfixe standard pour les URL d’avatar proxifiées pour chaque nom d’utilisateur associé (pour couvrir toutes les tailles)


Le scénario de suppression chirurgicale serait essentiellement le même, mais sans suspendre les utilisateurs associés et nécessiterait quelques changements concernant la collecte de tous les hachages de téléchargement à partir des URL de messages/sujets.

Probablement toujours supprimer les messages/sujets eux-mêmes pour éviter les références cassées, mais supprimer le balisage de téléchargement pour ce téléchargement spécifique (sans toucher aux autres téléchargements) serait supérieur si possible. Comme cette automatisation si elle ne supprimait pas toutes les références de téléchargement en markdown.


Idéalement, j’aimerais pouvoir bloquer également les hachages, afin qu’après avoir parcouru ce qui précède, quelqu’un ne puisse pas simplement créer un nouveau compte et le télécharger à nouveau. :melting_face: :coffin:

Je ne pense pas que ce soit possible actuellement pour un modérateur ordinaire, par exemple en utilisant des mots surveillés. Donc, peut-être qu’un balayage périodique comme celui ci-dessus pour une liste de hachages serait un moyen de gérer cela.

1 « J'aime »

Avez-vous consulté notre Legal Compliance Plugin ?

Il dispose de points de terminaison pour obtenir les téléchargements d’une publication et pour supprimer un téléchargement étant donné les identifiants de téléchargement et de publication.

Il étend également la fonctionnalité de recherche pour obtenir toutes les publications contenant un téléchargement spécifique, étant donné le hachage.

2 « J'aime »

Je mets juste ce lien pour les utilisateurs qui liront et seront d’accord avec votre publication, ils pourraient être intéressés par voter pour ceci (je sais que vous l’avez déjà lu) :

2 « J'aime »

Oui ! J’ai pratiquement versé des larmes de joie en voyant votre plugin plus tôt ce mois-ci haha. Un immense merci de l’avoir créé et partagé. :heart:

Quelques scénarios que je n’ai pas réussi à couvrir avec :

  • Utilisation du téléchargement : préfixe de recherche, il ne semblait pas possible de trouver les arrière-plans de profil ou les arrière-plans de carte (sauf si quelqu’un liait directement ces images dans une publication également).
  • Suppression définitive des images dans ce scénario (image sans association avec des publications).
  • Similaire pour l’Avatar, par exemple obtenir le hachage de l’avatar (il n’est généralement pas dans l’URL), puis trouver tout autre compte qui l’a peut-être également utilisé, si une suspension est nécessaire + supprimer le téléchargement sans association de publication.

Agréable à avoir :

  • Déclencher un webhook lors de la suppression, afin de déclencher des purges CDN après la suppression pour toutes les variantes des téléchargements.
  • Suppression en masse/automatique de toutes les publications/sujets qui font référence au téléchargement lorsque celui-ci est supprimé.

Dans Discourse (interface web), il semble impossible de supprimer (délier) un avatar d’utilisateur, que ce soit par un administrateur/modérateur ou par l’utilisateur lui-même (autre que télécharger un remplacement). C’est possible pour l’arrière-plan du profil et la bannière. Mais les deux ne détruisent pas immédiatement le téléchargement et si un autre utilisateur inconnu existe qui utilise le même téléchargement pour une partie de son profil (avatar, arrière-plan de profil, carte de profil), il ne sera pas purgé plus tard non plus.


J’ai fini par penser qu’il pourrait être utile d’essayer de créer des flux de travail automatisés capables de gérer la majorité ou la totalité du processus (avec examen/approbation humaine et/ou entrée humaine simplifiée). Pour faciliter une application cohérente, couvrir les cas limites et idéalement minimiser la possibilité d’avoir des publications non supprimées avec des références de téléchargement mortes. Également suspension automatique si nécessaire (sauf si le téléchargement cible se trouve à l’intérieur d’une citation) et purge CDN.


Voici ce que j’avais jusqu’à présent pour les requêtes d’explorateur de données (pas encore à l’étape de les « coller ensemble »), avec l’objectif de couvrir :

  • URL de sujet
  • URL de publication
  • URL d’image directe (publications, arrière-plan de profil, arrière-plan de carte)
  • URL d’avatar (proxifiées)

Préparer la liste des hachages de téléchargement

nom_utilisateur (à partir de l’URL d’avatar proxifiée) vers hachage de téléchargement

-- [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 de publication/sujet vers liste de hachages de téléchargement

-- [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'

Les autres types d’URL de téléchargement dont je suis au courant ont le hachage dans l’URL elle-même (il n’est pas nécessaire d’utiliser l’explorateur de données pour obtenir ces hachages).


Rassembler et préparer toutes les informations exploitables sur un hachage de téléchargement

exemples de paramètres :

upload_schemeless_prefix:
//my-s3-bucket.somehost.example.com

cdn_url_prefix:
https://cdn.example.com

app_hostname:
www.example.com

upload_hash:
0000000000000000000000000000000000000000

Recherche de hachage et résumé

-- [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

Ne couvre pas : les sujets en file d’attente d’examen en attente ou les brouillons de publications

Sorties :

upload_ids (ne devrait en contenir qu'un seul)
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

Cela fournira les informations nécessaires pour utiliser sélectivement ces API :

Et pour des actions supplémentaires :

  • Purger toutes les URL CDN (toutes les variantes, optimisées et originales, etc.)
  • Purger les URL de préfixe des avatars proxifiés (pour couvrir toutes les tailles)

Géré automatiquement par Discourse :

  • Suppression de la référence de téléchargement de tous les profils utilisateur (avatar, arrière-plan de profil et arrière-plan de carte) immédiatement lorsqu’un téléchargement est détruit/supprimé.
1 « J'aime »