FEATURE: backup without uploads

This commit is contained in:
Régis Hanol 2014-08-20 18:48:56 +02:00
parent 7a621d97b9
commit 8a20d05ba5
12 changed files with 82 additions and 35 deletions

View file

@ -1,5 +1,4 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ModalFunctionality from 'discourse/mixins/modal-functionality';
import ObjectController from 'discourse/controllers/object'; import ObjectController from 'discourse/controllers/object';
export default ObjectController.extend(ModalFunctionality, { export default ObjectController.extend(ModalFunctionality, {

View file

@ -0,0 +1,33 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import Controller from 'discourse/controllers/controller';
export default Controller.extend(ModalFunctionality, {
needs: ["adminBackupsLogs"],
_startBackup: function (withUploads) {
var self = this;
Discourse.User.currentProp("hideReadOnlyAlert", true);
Discourse.Backup.start(withUploads).then(function() {
self.get("controllers.adminBackupsLogs").clear();
self.send("backupStarted");
});
},
actions: {
startBackup: function () {
return this._startBackup();
},
startBackupWithoutUpload: function () {
return this._startBackup(false);
},
cancel: function () {
this.send("closeModal");
}
}
});

View file

@ -52,8 +52,9 @@ Discourse.Backup.reopenClass({
@method start @method start
@returns {Promise} a promise that resolves when the backup has started @returns {Promise} a promise that resolves when the backup has started
**/ **/
start: function() { start: function (withUploads) {
return Discourse.ajax("/admin/backups", { type: "POST" }).then(function(result) { if (withUploads === undefined) { withUploads = true; }
return Discourse.ajax("/admin/backups", { type: "POST", data: { with_uploads: withUploads } }).then(function(result) {
if (!result.success) { bootbox.alert(result.message); } if (!result.success) { bootbox.alert(result.message); }
}); });
}, },

View file

@ -47,22 +47,14 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
@method startBackup @method startBackup
**/ **/
startBackup: function() { startBackup: function() {
var self = this; Discourse.Route.showModal(this, 'admin_start_backup');
bootbox.confirm( this.controllerFor('modal').set('modalClass', 'start-backup-modal');
I18n.t("admin.backups.operations.backup.confirm"), },
I18n.t("no_value"),
I18n.t("yes_value"), backupStarted: function () {
function(confirmed) { this.modelFor("adminBackups").set("isOperationRunning", true);
if (confirmed) { this.transitionTo("admin.backups.logs");
Discourse.User.currentProp("hideReadOnlyAlert", true); this.send("closeModal");
Discourse.Backup.start().then(function() {
self.controllerFor("adminBackupsLogs").clear();
self.modelFor("adminBackups").set("isOperationRunning", true);
self.transitionTo("admin.backups.logs");
});
}
}
);
}, },
/** /**

View file

@ -0,0 +1,3 @@
<button {{action startBackup}} class="btn btn-primary">{{i18n yes_value}}</button>
<button {{action startBackupWithoutUpload}} class="btn">{{i18n admin.backups.operations.backup.without_uploads}}</button>
<button {{action cancel}} class="btn">{{i18n no_value}}</button>

View file

@ -0,0 +1,4 @@
Discourse.AdminStartBackupView = Discourse.ModalBodyView.extend({
templateName: 'admin/templates/modal/admin_start_backup',
title: I18n.t('admin.backups.operations.backup.confirm')
});

View file

@ -1164,6 +1164,15 @@ button.ru {
} }
} }
.start-backup-modal {
.btn {
margin: 10px 0 10px 5px;
}
.btn:first-of-type {
margin-left: 10px;
}
}
@media all @media all
and (max-width : 850px) { and (max-width : 850px) {
.nav-stacked { .nav-stacked {

View file

@ -23,7 +23,11 @@ class Admin::BackupsController < Admin::AdminController
end end
def create def create
BackupRestore.backup!(current_user.id, true) opts = {
publish_to_message_bus: true,
with_uploads: params.fetch(:with_uploads) == "true"
}
BackupRestore.backup!(current_user.id, opts)
rescue BackupRestore::OperationRunningError rescue BackupRestore::OperationRunningError
render json: failed_json.merge(message: I18n.t("backup.operation_already_running")) render json: failed_json.merge(message: I18n.t("backup.operation_already_running"))
else else

View file

@ -7,7 +7,7 @@ module Jobs
def execute(args) def execute(args)
return unless SiteSetting.backup_daily? return unless SiteSetting.backup_daily?
BackupRestore.backup!(Discourse.system_user.id, false) BackupRestore.backup!(Discourse.system_user.id, publish_to_message_bus: false)
end end
end end
end end

View file

@ -1600,7 +1600,8 @@ en:
backup: backup:
text: "Backup" text: "Backup"
title: "Create a backup" title: "Create a backup"
confirm: "Are you sure you want to start a new backup?" confirm: "Do you want to start a new backup?"
without_uploads: "Yes (without upload)"
download: download:
text: "Download" text: "Download"
title: "Download the backup" title: "Download the backup"

View file

@ -9,14 +9,12 @@ module BackupRestore
METADATA_FILE = "meta.json" METADATA_FILE = "meta.json"
LOGS_CHANNEL = "/admin/backups/logs" LOGS_CHANNEL = "/admin/backups/logs"
def self.backup!(user_id, publish_to_message_bus = false) def self.backup!(user_id, opts={})
exporter = Export::Exporter.new(user_id, publish_to_message_bus) start! Export::Exporter.new(user_id, opts)
start! exporter
end end
def self.restore!(user_id, filename, publish_to_message_bus = false) def self.restore!(user_id, filename, publish_to_message_bus=false)
importer = Import::Importer.new(user_id, filename, publish_to_message_bus) start! Import::Importer.new(user_id, filename, publish_to_message_bus)
start! importer
end end
def self.rollback! def self.rollback!
@ -33,7 +31,6 @@ module BackupRestore
end end
def self.mark_as_running! def self.mark_as_running!
# TODO: for extra safety, it should acquire a lock and raise an exception if already running
$redis.setex(running_key, 60, "1") $redis.setex(running_key, 60, "1")
save_start_logs_message_id save_start_logs_message_id
keep_it_running keep_it_running

View file

@ -4,8 +4,10 @@ module Export
attr_reader :success attr_reader :success
def initialize(user_id, publish_to_message_bus = false) def initialize(user_id, opts={})
@user_id, @publish_to_message_bus = user_id, publish_to_message_bus @user_id = user_id
@publish_to_message_bus = opts[:publish_to_message_bus] || false
@with_uploads = opts[:with_uploads].nil? ? true : opts[:with_uploads]
ensure_no_operation_is_running ensure_no_operation_is_running
ensure_we_have_a_user ensure_we_have_a_user
@ -246,11 +248,13 @@ module Export
`tar --append --dereference --file #{tar_filename} #{File.basename(@dump_filename)}` `tar --append --dereference --file #{tar_filename} #{File.basename(@dump_filename)}`
end end
upload_directory = "uploads/" + @current_db if @with_uploads
upload_directory = "uploads/" + @current_db
log "Archiving uploads..." log "Archiving uploads..."
FileUtils.cd(File.join(Rails.root, "public")) do FileUtils.cd(File.join(Rails.root, "public")) do
`tar --append --dereference --file #{tar_filename} #{upload_directory}` `tar --append --dereference --file #{tar_filename} #{upload_directory}`
end
end end
log "Gzipping archive..." log "Gzipping archive..."