diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index eb6cb5a4877..2d4f846f5da 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -2984,9 +2984,7 @@ en: Here's the log: - ``` text %{logs} - ``` backup_failed: title: "Backup Failed" @@ -2996,9 +2994,7 @@ en: Here's the log: - ``` text %{logs} - ``` restore_succeeded: title: "Restore Succeeded" @@ -3008,9 +3004,7 @@ en: Here's the log: - ``` text %{logs} - ``` restore_failed: title: "Restore Failed" @@ -3020,9 +3014,7 @@ en: Here's the log: - ``` text %{logs} - ``` bulk_invite_succeeded: title: "Bulk Invite Succeeded" diff --git a/lib/backup_restore/backuper.rb b/lib/backup_restore/backuper.rb index dea11cda147..54c69ae2562 100644 --- a/lib/backup_restore/backuper.rb +++ b/lib/backup_restore/backuper.rb @@ -321,9 +321,8 @@ module BackupRestore log "Notifying '#{@user.username}' of the end of the backup..." status = @success ? :backup_succeeded : :backup_failed - post = SystemMessage.create_from_system_user( - @user, status, logs: Discourse::Utils.pretty_logs(@logs) - ) + logs = Discourse::Utils.logs_markdown(@logs, user: @user) + post = SystemMessage.create_from_system_user(@user, status, logs: logs) if @user.id == Discourse::SYSTEM_USER_ID post.topic.invite_group(@user, Group[:admins]) diff --git a/lib/backup_restore/restorer.rb b/lib/backup_restore/restorer.rb index 05a82338caf..00bedab2410 100644 --- a/lib/backup_restore/restorer.rb +++ b/lib/backup_restore/restorer.rb @@ -148,10 +148,8 @@ module BackupRestore log "Notifying '#{user.username}' of the end of the restore..." status = @success ? :restore_succeeded : :restore_failed - SystemMessage.create_from_system_user( - user, status, - logs: Discourse::Utils.pretty_logs(@logger.logs) - ) + logs = Discourse::Utils.logs_markdown(@logger.logs, user: user) + post = SystemMessage.create_from_system_user(user, status, logs: logs) else log "Could not send notification to '#{@user_info[:username]}' " \ "(#{@user_info[:email]}), because the user does not exist." diff --git a/lib/discourse.rb b/lib/discourse.rb index 1d203658f0d..c76f4c727b0 100644 --- a/lib/discourse.rb +++ b/lib/discourse.rb @@ -42,6 +42,49 @@ module Discourse logs.join("\n") end + def self.logs_markdown(logs, user:, filename: 'log.txt') + # Reserve 250 characters for the rest of the text + max_logs_length = SiteSetting.max_post_length - 250 + pretty_logs = Discourse::Utils.pretty_logs(logs) + + # If logs are short, try to inline them + if pretty_logs.size < max_logs_length + return <<~TEXT + ```text + #{pretty_logs} + ``` + TEXT + end + + # Try to create an upload for the logs + upload = Dir.mktmpdir do |dir| + File.write(File.join(dir, filename), pretty_logs) + zipfile = Compression::Zip.new.compress(dir, filename) + File.open(zipfile) do |file| + UploadCreator.new( + file, + File.basename(zipfile), + type: 'backup_logs', + for_export: 'true' + ).create_for(user.id) + end + end + + if upload.persisted? + return UploadMarkdown.new(upload).attachment_markdown + else + Rails.logger.warn("Failed to upload the backup logs file: #{upload.errors.full_messages}") + end + + # If logs are long and upload cannot be created, show trimmed logs + <<~TEXT + ```text + ... + #{pretty_logs.last(max_logs_length)} + ``` + TEXT + end + def self.atomic_write_file(destination, contents) begin return if File.read(destination) == contents diff --git a/spec/lib/backup_restore/backuper_spec.rb b/spec/lib/backup_restore/backuper_spec.rb index 389b8d7bca9..7cbe142d3f6 100644 --- a/spec/lib/backup_restore/backuper_spec.rb +++ b/spec/lib/backup_restore/backuper_spec.rb @@ -16,4 +16,59 @@ describe BackupRestore::Backuper do expect(backuper.send(:get_parameterized_title)).to eq("coding-horror") end + + describe '#notify_user' do + before do + freeze_time Time.zone.parse('2010-01-01 12:00') + end + + it 'includes logs if short' do + SiteSetting.max_export_file_size_kb = 1 + SiteSetting.export_authorized_extensions = "tar.gz" + + silence_stdout do + backuper = BackupRestore::Backuper.new(Discourse.system_user.id) + + expect { backuper.send(:notify_user) } + .to change { Topic.private_messages.count }.by(1) + .and change { Upload.count }.by(0) + end + + expect(Topic.last.first_post.raw).to include("```text\n[2010-01-01 12:00:00] Notifying 'system' of the end of the backup...\n```") + end + + it 'include upload if log is long' do + SiteSetting.max_post_length = 250 + + silence_stdout do + backuper = silence_stdout { BackupRestore::Backuper.new(Discourse.system_user.id) } + + expect { backuper.send(:notify_user) } + .to change { Topic.private_messages.count }.by(1) + .and change { Upload.where(original_filename: "log.txt.zip").count }.by(1) + end + + expect(Topic.last.first_post.raw).to include("[log.txt.zip|attachment]") + end + + it 'includes trimmed logs if log is long and upload cannot be saved' do + SiteSetting.max_post_length = 348 + SiteSetting.max_export_file_size_kb = 1 + SiteSetting.export_authorized_extensions = "tar.gz" + + silence_stdout do + backuper = BackupRestore::Backuper.new(Discourse.system_user.id) + + 1.upto(10).each do |i| + backuper.send(:log, "Line #{i}") + end + + expect { backuper.send(:notify_user) } + .to change { Topic.private_messages.count }.by(1) + .and change { Upload.count }.by(0) + end + + expect(Topic.last.first_post.raw).to include("```text\n...\n[2010-01-01 12:00:00] Line 10\n[2010-01-01 12:00:00] Notifying 'system' of the end of the backup...\n```") + end + end end