Wiederherstellen fehlender Uploads in der Datenbank

Ich habe eine Seite, bei der anscheinend eine Reihe von Uploads aus der uploads-Tabelle in der Datenbank entfernt wurden, aber im Dateisystem noch existieren. Dadurch sind die Links zu diesen Dateien defekt. Ich habe einige behoben, indem ich die Dateien erneut in ein neues Thema hochgeladen habe, um den Datensatz in der DB zu erstellen (zuerst dachte ich, die Dateien seien ebenfalls verschwunden, aber @angus hat mir freundlicherweise erklärt, warum ich sie nicht finden konnte – eines Tages werde ich fragen: Warum haben wir für all diese Assets sowohl sha1- als auch base62-Namen?) und anschließend die Beiträge, die diese Uploads enthalten, neu gerendert (rebaked), wie folgt:

def rebake_posts_with_uploads(topic_id, post_number)
  p = Post.find_by(topic_id: topic_id, post_number: post_number)
  exit unless p
  re = /upload:\/\/(.+?)\)/
  shas= p.raw.scan(re)

  shas.each do |sha|
    posts = Post.where("raw like '%#{sha[0]}%'")
    next unless posts
    puts "Found #{posts.count - 1} #{sha[0]}"
    posts.each do |post|
      next if post.id == p.id
      puts "rebake #{post.id}--#{BASE_URL}/t/-/#{post.topic_id}/#{post.post_number}"
      post.rebake!
    end
  end
end

Mein neuer Plan, da die Dateien scheinbar im Dateisystem, aber nicht in der Datenbank vorhanden sind, ist, etwas Ähnliches zu tun:

for (all files in /shared/uploads/default/original/1x) do |file|
  unless file is in uploads table
     create upload record 
     for each post that includes that upload record
       rebake

Klingt das vernünftig? Ich schaue mir uploads.rake an und finde nichts, das dies bereits erledigt. Das ist sozusagen das Gegenteil von

aber anstatt FileUtils.rm(file_path) würde ich stattdessen Upload.create verwenden, denke ich.

Wenn das wirklich dumm klingt oder es eine viel bessere Lösung gibt, würde ich mich freuen, das vorab zu hören, bevor ich mich in dieses kleine Kaninchenloch stürze.

Vielen Dank.

Ich weiß nicht, wie das passiert ist. Ich hatte gehofft, die Schuld einem benutzerdefinierten Plugin zuschieben zu können, aber ich fürchte, das ist nicht der Fall. Es könnte mit einer anderen Diskussion zusammenhängen, in der jemand sagte:

3 „Gefällt mir“

Ja, unsere Historie hier beim Hochladen ist ziemlich lückenhaft, und das liegt an uns. @sam, kannst du jemanden empfehlen, der einen kurzen Rat gibt?

2 „Gefällt mir“

Das kann funktionieren, man muss nur sorgfältig auf dem lokalen System testen … Schauen Sie sich auch die 2x / 3x-Verzeichnisse an, es gibt überall Uploads.

3 „Gefällt mir“

Ich versuche so etwas wie das:

def add_missing_files_to_uploads
  public_directory = Rails.root.join("public").to_s
  db = RailsMultisite::ConnectionManagement.current_db
  uploads_directory = File.join(public_directory, 'uploads', db).to_s
  # Uploads und optimierte Bilder
  missing = 0
  matched = 0
  Dir.glob("#{uploads_directory}/**/*.pdf").each do |file_path|
    sha1 = Upload.generate_digest(file_path)
    url = file_path.split(public_directory, 2)[1]
    if (Upload.where(sha1: sha1).empty? &&
        Upload.where(url: url).empty?)
      puts "FEHLT #{file_path}" if DEBUG
      missing += 1
    else
      matched += 1
    end
  end
  puts "FEHLT: #{missing}. Übereinstimmung #{matched}"
end

Sieht das irgendwie ähnlich aus? Mein erster Test ist scheinbar fehlgeschlagen, aber ich habe vielleicht etwas falsch gemacht.

Hallo @pfaffman. Ich habe diesen Beitrag gelesen, da er eine vielversprechende Lösung für mein Problem hier zu sein scheint. Bist du am Ende zu einem guten Ergebnis gekommen?

Tut mir leid, ich kann mich nicht erinnern.

Für Sam und mich sieht es so aus, als würde es funktionieren.

Ich habe ein Simulator-Problem gelöst, indem ich einen Beitrag erstellt und darin einen Link zu allen Bildern eingefügt habe, und dann alle Beiträge, die transparent.gif enthalten, neu gerendert habe.

Das sieht nach einer etwas eleganteren Lösung aus.

Ich würde sagen, mache ein rein datenbankbasiertes Backup und probiere es aus. Ich bin gerade etwas im Urlaub, aber wenn du ein Budget hast, kann ich sehen, was ich tun kann.

Und ich habe ebenfalls einen wichtigen Kunden in ams3; ich habe sie noch nicht zu AWS verlegt, aber ich denke, das wird bald passieren. Ein Freund von mir, der bei Digital Ocean gearbeitet hat, hat ams3 empfohlen, weil es ihr bestes Rechenzentrum war und weitgehend ungenutzt war (das ist schon lange her). Das ist nicht so gelaufen, wie ich gehofft hatte.

1 „Gefällt mir“

Ich frage mich, ob dies heutzutage die beste Methode ist?

Ich versuche, Uploads wiederherzustellen, die sich auf dem Dateisystem befinden, aber nach einer fehlerhaften S3-Migration (mit dem alten rake:s3_migrate) nicht in der Datenbank sind.

Es gibt eigentlich keine gute Methode. :crying_cat:

Aber ja. Das ist immer noch die Idee. Ich glaube nicht, dass sich seitdem etwas an Discourse geändert hat. Normalerweise würde ich ein paar manuell durchführen, um sicherzustellen, dass es das tut, was erwartet wird. Erstellen Sie außerdem zuerst ein Backup und versetzen Sie die Seite möglicherweise in den schreibgeschützten Modus, damit Sie das Backup nicht wegwerfen müssen, falls Sie es wiederherstellen müssen, falls während Ihrer Basteleien Beiträge erstellt wurden.

Ohne viel mehr zu wissen, als leicht in einem Forum kommuniziert werden kann, kann ich nicht sagen, ob das wirklich der beste Weg ist oder ob es etwas Einfacheres gibt. Sie könnten zum Beispiel einfach die Pfade der URLs mit gsub ersetzen. Wenn Sie ein Budget haben, können Sie mich kontaktieren oder in Marketplace fragen.

1 „Gefällt mir“

Okay, ich habe es mit Claude und viel Lob gelöst. Ich teile, was ich getan habe, um jedem mit einem ähnlichen oder demselben Problem zu helfen.

Ich bin mir nicht sicher, ob dies die cleverste und optimalste Methode ist, nur die, die für mich funktioniert hat.

Bitte seien Sie vorsichtig und denken Sie daran, dass ich kein Experte, sondern ein Anfänger bin, der immer lernt.

Das Problem (S3 → lokales Dateisystem)

Nach der Migration von AWS S3 auf das lokale Dateisystem wurden viele Bilder als transparent.png angezeigt. Die Dateien waren immer auf der Festplatte vorhanden, aber Discourse konnte sie nicht auflösen.

Die eigentliche Ursache war eine fehlerhafte Kette:

  1. Beiträge mit upload:// Kurz-URLs (base62-kodiertes SHA1).
  2. Datenbank uploads-Zuordnung SHA1 → lokaler Dateipfad.
  3. Dateisystem, das Dateien speichert, die mit ihrem SHA1-Hash benannt sind.

Die Migration hat die Dateien korrekt auf die Festplatte verschoben, aber es gab keine uploads-DB-Einträge. Ohne einen passenden Eintrag fällt Discourse auf transparent.png zurück.

Die Lösung (Datensätze erstellen und erneut backen)

Fehlende Upload-Datensätze aus verwaisten Dateien erstellen:

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}"

Beiträge, die wiederhergestellte Uploads referenzieren, erneut backen:

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\s]+?(?=[ \]]|$)/) # Angepasst, um besser mit verschiedenen Trennzeichen umzugehen
    urls.each do |url|
      decoded = Upload.sha1_from_short_url(url.first) # scan gibt eine Array von Matches zurück
      if decoded == u.sha1
        p.rebake!
        fixed_posts += 1
        puts "Rebaked post #{p.id}"
        break
      end
    end
  end
end

puts "Total rebaked: #{fixed_posts}"

Fehlende optimierte Dateien neu generieren:

Nachdem die ursprünglichen Dateien korrigiert wurden, müssen wir die optimierten Dateien (1X, 2X usw.) auffüllen.

rake uploads:regenerate_missing_optimized

Sicheres Rollback (nur für den Fall)

Alle erstellten Datensätze verwenden user_id: -1. Zum Rückgängigmachen:

Upload.where(user_id: -1).delete_all

delete_all überspringt Rückrufe, sodass die Dateien im Dateisystem unberührt bleiben.

Zuvor wurde versehentlich destroy_all verwendet, was Rückrufe auslöste, die Dateien in den Papierkorb verschoben haben.

Ich habe eine einzelne Datei, die ich zum Testen verwendet habe, wiederhergestellt und meinen Ansatz neu formuliert.

3 „Gefällt mir“