Cancellazione caricamento tramite API

L’ho cercato, ma sembra che un endpoint API per eliminare un caricamento non esista.

Muppet Show: Kermit's Trapeze Fall

Lo pubblico qui, poiché non sono riuscito a trovare menzioni o suggerimenti a riguardo; il più vicino che ho trovato è: API: a scope with access to /uploads

Nelle impostazioni di ambito granulare della chiave API per i caricamenti, vedo che ‘create’ (crea) è disponibile, ma ‘delete’ (elimina) no.

Sembra che una potenziale soluzione alternativa potrebbe essere quella di creare un automatismo personalizzato e attivarlo tramite API.

4 Mi Piace

Qual è il tuo caso d’uso?

A seconda della configurazione, il caricamento di file come risorsa condivisa (a cui si fa riferimento tramite post, profilo utente, badge, ecc.) comporta un certo rischio se vengono eliminati senza la dovuta diligenza.

Inoltre, vengono automaticamente eliminati dopo un po’ di tempo se non sono più referenziati da nessuna parte.

3 Mi Piace

Il caso d’uso è per la rimozione di upload sensibile al tempo come parte dei flussi di lavoro di automazione human-in-the-loop utilizzando activepieces, data explorer e l’API.

L’obiettivo è consentire ai moderatori regolari di avere accesso a un modo semplice per rimuovere in modo sicuro e completo gli upload immediatamente senza accesso SSH e coprire in modo esaustivo tutti i casi noti per la rottura dei riferimenti agli upload, oltre a eseguire il purge automatico tramite CDN degli URL specifici (e nel caso di avatar con proxy, il prefisso URL basato sul nome utente).


Sono stato sollevato quando, testando, ho visto che i riferimenti ad avatar, sfondo del profilo e sfondo della card vengono gestiti automaticamente quando un upload viene distrutto.

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

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


I due scenari principali sarebbero “terra bruciata” e “rimozione chirurgica”. Questo è il lavoro in corso per la terra bruciata:


Conversione in una lista di hash degli upload:

  • URL degli avatar (estrai il nome utente usando regex) → ottieni l’hash dell’avatar tramite query data explorer (l’hash non è nell’URL)

  • URL di topic/post → raccogli tutti gli hash degli upload utilizzati in quel post usando data explorer

  • URL di upload originali/ottimizzati diretti (inclusi sfondo del profilo e sfondo della card) → estrai gli hash usando regex


Quindi esegui una query su ciascun hash per trovare tutte le occorrenze (una query data explorer per coprire tutti i casi, un hash alla volta):

  • lista nome utente/ID, per gli utenti che lo hanno utilizzato per un avatar

  • lista nome utente/ID, per gli utenti che lo hanno utilizzato per uno sfondo del profilo

  • lista nome utente/ID, per gli utenti che lo hanno utilizzato per uno sfondo della card

  • lista di tutti i post (raw) che utilizzano questo upload


Azioni:

  • sospendi tutti gli utenti che hanno utilizzato l’upload per un avatar, sfondo del profilo o sfondo della card

  • sospendi tutti gli utenti che hanno post che fanno riferimento all’upload di destinazione, ma escludi se il loro riferimento è annidato all’interno di una citazione

  • elimina tutti i topic

  • elimina tutti i post

  • distruggi l’upload (nessun endpoint API)

  • esegui il purge di tutti gli URL CDN (ottimizzati/non ottimizzati)

  • esegui il purge del prefisso standard per gli URL degli avatar con proxy per ciascun nome utente associato (per coprire tutte le dimensioni)


Lo scenario di rimozione chirurgica sarebbe essenzialmente lo stesso, ma senza sospendere gli utenti associati e richiederebbe alcune modifiche per quanto riguarda la raccolta di tutti gli hash degli upload dagli URL dei post/topic.

Probabilmente eliminare comunque i post/topic stessi per evitare riferimenti interrotti, ma rimuovere il markdown dell’upload per quell’upload specifico (senza toccare altri upload) sarebbe superiore se possibile. Come questa automazione se non rimuovesse tutti i riferimenti agli upload nel markdown.


Idealmente vorrei essere in grado di bloccare anche gli hash, in modo che dopo aver eseguito quanto sopra, qualcuno non possa semplicemente creare un nuovo account e ricaricare. :melting_face: :coffin:

Non penso sia attualmente possibile per un mod regolare, come usando le parole osservate (watched words). Quindi forse eseguire una scansione periodica come quella sopra per una lista di hash sarebbe un modo per gestirlo.

1 Mi Piace

Hai dato un’occhiata al nostro Legal Compliance Plugin ?

Ha endpoint per ottenere i caricamenti per un post e per rimuovere un caricamento dati l’ID del caricamento e del post.

Estende inoltre la funzionalità di ricerca per ottenere tutti i post che contengono un caricamento specifico, dato l’hash.

2 Mi Piace

Sto solo linkando questo per gli utenti che leggeranno e saranno d’accordo con il tuo post, potrebbero essere interessati a votare per questo (so che l’hai già letto):

2 Mi Piace

Sì! Ho pianto lacrime di gioia quando ho visto il tuo plugin all’inizio di questo mese haha. Un enorme grazie per averlo creato e condiviso. :heart:

Alcuni scenari che non sono riuscito a coprire con esso:

  • Utilizzo del prefisso di ricerca del caricamento: non sembrava possibile trovare gli sfondi del profilo o gli sfondi delle schede (a meno che qualcuno non avesse inserito direttamente il collegamento a quelle immagini in un post).
  • Eliminazione forzata delle immagini in quello scenario (immagine senza associazione a post).
  • Simile per l’Avatar, ad esempio ottenere l’hash dell’avatar (di solito non è nell’URL), quindi trovare eventuali altri account che potrebbero averlo utilizzato, se è necessaria una sospensione + eliminare il caricamento senza associazione a post.

Funzionalità utili:

  • Attivare un webhook alla cancellazione, per attivare la pulizia della CDN dopo la cancellazione per tutte le varianti dei caricamenti.
  • Eliminazione in blocco/automatica di tutti i post/argomenti che fanno riferimento al caricamento quando il caricamento viene eliminato.

In Discourse (interfaccia web), non sembra possibile eliminare (scollegare) l’avatar di un utente, né da parte di un amministratore/moderatore né da parte dell’utente stesso (oltre a caricare una sostituzione). È possibile per lo sfondo del profilo e il banner. Ma entrambi questi non distruggono immediatamente il caricamento e se esiste un altro utente sconosciuto che utilizza lo stesso caricamento per una parte del proprio profilo (avatar, sfondo del profilo, scheda del profilo) non verrà eliminato in seguito.


Ho finito per pensare che potrebbe valere la pena provare a creare alcuni flussi di lavoro automatizzati in grado di gestire la maggior parte o la totalità del processo (con revisione/approvazione umana e/o input umano semplificato). Per facilitare l’applicazione coerente, coprire i casi limite e idealmente ridurre al minimo la possibilità di avere post non eliminati con riferimenti a caricamenti non validi. Inoltre, sospendere automaticamente se necessario (a meno che il caricamento di destinazione non sia all’interno di una citazione) e pulire la CDN.


Questo è ciò che ho finora per le query di esplorazione dati (non ancora alla parte di ‘collegarle’), con l’obiettivo di coprire:

  • URL degli argomenti
  • URL dei post
  • URL delle immagini dirette (post, sfondo del profilo, sfondo della scheda)
  • URL degli avatar (con proxy)

Prepara l’elenco degli hash di caricamento

nomeutente (da URL avatar con proxy) all’hash di caricamento

-- [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 post/argomento all’elenco degli hash di caricamento

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

Gli altri tipi di URL di caricamento di cui sono a conoscenza, hanno l’hash nell’URL stesso (non è necessario utilizzare l’esploratore dati per ottenere tali hash).


Raccogli e prepara tutte le informazioni attuabili su un hash di caricamento

parametri di esempio:

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

cdn_url_prefix:
https://cdn.example.com

app_hostname:
www.example.com

upload_hash:
0000000000000000000000000000000000000000

ricerca hash e riepilogo

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

Non copre: argomenti in coda in attesa di revisione o post in bozza

Output:

upload_ids (dovrebbe essere solo uno)
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

Ciò fornirà le informazioni necessarie per utilizzare selettivamente queste API:

E per ulteriori azioni:

  • Pulire tutti gli URL CDN (tutte le varianti, ottimizzate e originali, ecc.)
  • Pulire gli URL prefisso degli avatar con proxy (per coprire tutte le dimensioni)

Gestito automaticamente da Discourse:

  • Rimuove immediatamente il riferimento al caricamento da tutti i profili utente (avatar, sfondo del profilo e sfondo della scheda) quando un caricamento viene distrutto/eliminato.
1 Mi Piace