Re-adding missing uploads to the database

De acuerdo, lo resolví con Claude y muchos elogios. Comparto lo que hice para ayudar a cualquiera que tenga un problema similar o el mismo.

No estoy seguro de si este es el método más ingenioso y óptimo, solo el que funcionó para mí.

Por favor, tenga cuidado y tenga en cuenta que no soy un experto, sino un novato que siempre está aprendiendo.

El problema (S3 → sistema de archivos local)

Después de migrar de AWS S3 a un sistema de archivos local, muchas imágenes se mostraban como transparent.png. Los archivos siempre estaban en disco, pero Discourse no podía resolverlos.

La causa raíz fue una cadena rota:

  1. Publicaciones con URL cortas upload:// (SHA1 codificado en base62).
  2. Base de datos uploads mapeando SHA1 → ruta de archivo local.
  3. Sistema de archivos almacenando archivos nombrados por su hash SHA1,

La migración movió los archivos al disco correctamente, pero no existían registros de DB uploads. Sin un registro coincidente, Discourse recurre a transparent.png.

La solución (crear registros y volver a hornear)

Crear registros de carga faltantes a partir de archivos huérfanos:

dir = Rails.root.join("public", "uploads", "default", "original")
created = 0

Dir.glob(dir.join("**", "*")).select { |f| File.file?(f) }.each do |path|
  sha = File.basename(path, File.extname(path))
  next if Upload.find_by(sha1: sha)

  ext = File.extname(path).delete(".")
  relative = path.sub("#{Rails.root}/public", "")

  u = Upload.new
  u.sha1 = sha
  u.url = relative
  u.original_filename = File.basename(path)
  u.filesize = File.size(path)
  u.extension = ext
  u.user_id = -1
  u.save!(validate: false)

  created += 1
  puts "Created upload #{u.id}: #{sha}"
end

puts "Total created: #{created}"

Volver a hornear las publicaciones que hacen referencia a las cargas restauradas:

fixed_posts = 0

Upload.where(user_id: -1).find_each do |u|
  short = u.short_url
  next unless short

  Post.where("raw LIKE '%upload://%'").find_each do |p|
    urls = p.raw.scan(/upload:\/\/+[^\\s\\]\\)]+/)
    urls.each do |url|
      decoded = Upload.sha1_from_short_url(url)
      if decoded == u.sha1
        p.rebake!
        fixed_posts += 1
        puts "Rebaked post #{p.id}"
        break
      end
    end
  end
end

puts "Total rebaked: #{fixed_posts}"

Regenerar los optimizados faltantes:

Después de arreglar los archivos originales, necesitamos poblar los archivos optimizados (1X, 2X, etc.).

rake uploads:regenerate_missing_optimized

Reversión segura (por si acaso)

Todos los registros creados usan user_id: -1. Para deshacer:

Upload.where(user_id: -1).delete_all

delete_all omite los callbacks por lo que los archivos del sistema de archivos no se tocan.

Anteriormente usé destroy_all por error y activó callbacks que movieron archivos a la papelera.

Recuperé uno individual que usé para probar y reformulé mi enfoque.

3 Me gusta