mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 23:58:31 -05:00
FIX: don't break the message bus when restoring a backup
This commit is contained in:
parent
2589a75c46
commit
96c23d51a2
8 changed files with 78 additions and 109 deletions
|
@ -1,90 +0,0 @@
|
||||||
/**
|
|
||||||
Data model for representing a backup
|
|
||||||
|
|
||||||
@class Backup
|
|
||||||
@extends Discourse.Model
|
|
||||||
@namespace Discourse
|
|
||||||
@module Discourse
|
|
||||||
**/
|
|
||||||
Discourse.Backup = Discourse.Model.extend({
|
|
||||||
|
|
||||||
/**
|
|
||||||
Destroys the current backup
|
|
||||||
|
|
||||||
@method destroy
|
|
||||||
@returns {Promise} a promise that resolves when the backup has been destroyed
|
|
||||||
**/
|
|
||||||
destroy: function() {
|
|
||||||
return Discourse.ajax("/admin/backups/" + this.get("filename"), { type: "DELETE" });
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
Starts the restoration of the current backup
|
|
||||||
|
|
||||||
@method restore
|
|
||||||
@returns {Promise} a promise that resolves when the backup has started being restored
|
|
||||||
**/
|
|
||||||
restore: function() {
|
|
||||||
return Discourse.ajax("/admin/backups/" + this.get("filename") + "/restore", { type: "POST" });
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Discourse.Backup.reopenClass({
|
|
||||||
|
|
||||||
/**
|
|
||||||
Finds a list of backups
|
|
||||||
|
|
||||||
@method find
|
|
||||||
@returns {Promise} a promise that resolves to the array of {Discourse.Backup} backup
|
|
||||||
**/
|
|
||||||
find: function() {
|
|
||||||
return PreloadStore.getAndRemove("backups", function() {
|
|
||||||
return Discourse.ajax("/admin/backups.json");
|
|
||||||
}).then(function(backups) {
|
|
||||||
return backups.map(function (backup) { return Discourse.Backup.create(backup); });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
Starts a backup
|
|
||||||
|
|
||||||
@method start
|
|
||||||
@returns {Promise} a promise that resolves when the backup has started
|
|
||||||
**/
|
|
||||||
start: function (withUploads) {
|
|
||||||
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); }
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
Cancels a backup
|
|
||||||
|
|
||||||
@method cancel
|
|
||||||
@returns {Promise} a promise that resolves when the backup has been cancelled
|
|
||||||
**/
|
|
||||||
cancel: function() {
|
|
||||||
return Discourse.ajax("/admin/backups/cancel.json").then(function(result) {
|
|
||||||
if (!result.success) { bootbox.alert(result.message); }
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
Rollbacks the database to the previous working state
|
|
||||||
|
|
||||||
@method rollback
|
|
||||||
@returns {Promise} a promise that resolves when the rollback is done
|
|
||||||
**/
|
|
||||||
rollback: function() {
|
|
||||||
return Discourse.ajax("/admin/backups/rollback.json").then(function(result) {
|
|
||||||
if (!result.success) {
|
|
||||||
bootbox.alert(result.message);
|
|
||||||
} else {
|
|
||||||
// redirect to homepage (session might be lost)
|
|
||||||
window.location.pathname = Discourse.getURL("/");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
56
app/assets/javascripts/admin/models/backup.js.es6
Normal file
56
app/assets/javascripts/admin/models/backup.js.es6
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
const Backup = Discourse.Model.extend({
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
return Discourse.ajax("/admin/backups/" + this.get("filename"), { type: "DELETE" });
|
||||||
|
},
|
||||||
|
|
||||||
|
restore() {
|
||||||
|
return Discourse.ajax("/admin/backups/" + this.get("filename") + "/restore", {
|
||||||
|
type: "POST",
|
||||||
|
data: { client_id: window.MessageBus.clientId }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Backup.reopenClass({
|
||||||
|
|
||||||
|
find() {
|
||||||
|
return PreloadStore.getAndRemove("backups", () => Discourse.ajax("/admin/backups.json"))
|
||||||
|
.then(backups => backups.map(backup => Backup.create(backup)));
|
||||||
|
},
|
||||||
|
|
||||||
|
start(withUploads) {
|
||||||
|
if (withUploads === undefined) { withUploads = true; }
|
||||||
|
return Discourse.ajax("/admin/backups", {
|
||||||
|
type: "POST",
|
||||||
|
data: {
|
||||||
|
with_uploads: withUploads,
|
||||||
|
client_id: window.MessageBus.clientId
|
||||||
|
}
|
||||||
|
}).then(result => {
|
||||||
|
if (!result.success) { bootbox.alert(result.message); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
return Discourse.ajax("/admin/backups/cancel.json")
|
||||||
|
.then(result => {
|
||||||
|
if (!result.success) { bootbox.alert(result.message); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
rollback() {
|
||||||
|
return Discourse.ajax("/admin/backups/rollback.json")
|
||||||
|
.then(result => {
|
||||||
|
if (!result.success) {
|
||||||
|
bootbox.alert(result.message);
|
||||||
|
} else {
|
||||||
|
// redirect to homepage (session might be lost)
|
||||||
|
window.location.pathname = Discourse.getURL("/");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Backup;
|
|
@ -9,9 +9,7 @@ function inject() {
|
||||||
name = arguments[1],
|
name = arguments[1],
|
||||||
singletonName = Ember.String.underscore(name).replace(/_/, '-') + ':main';
|
singletonName = Ember.String.underscore(name).replace(/_/, '-') + ':main';
|
||||||
|
|
||||||
Array.prototype.slice.call(arguments, 2).forEach(function(dest) {
|
Array.prototype.slice.call(arguments, 2).forEach(dest => app.inject(dest, name, singletonName));
|
||||||
app.inject(dest, name, singletonName);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectAll(app, name) {
|
function injectAll(app, name) {
|
||||||
|
@ -20,6 +18,7 @@ function injectAll(app, name) {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "inject-discourse-objects",
|
name: "inject-discourse-objects",
|
||||||
|
|
||||||
initialize(container, app) {
|
initialize(container, app) {
|
||||||
const appEvents = AppEvents.create();
|
const appEvents = AppEvents.create();
|
||||||
app.register('app-events:main', appEvents, { instantiate: false });
|
app.register('app-events:main', appEvents, { instantiate: false });
|
||||||
|
@ -29,16 +28,13 @@ export default {
|
||||||
app.register('store:main', Store);
|
app.register('store:main', Store);
|
||||||
inject(app, 'store', 'route', 'controller');
|
inject(app, 'store', 'route', 'controller');
|
||||||
|
|
||||||
// Inject Discourse.Site to avoid using Discourse.Site.current()
|
|
||||||
const site = Discourse.Site.current();
|
const site = Discourse.Site.current();
|
||||||
app.register('site:main', site, { instantiate: false });
|
app.register('site:main', site, { instantiate: false });
|
||||||
injectAll(app, 'site');
|
injectAll(app, 'site');
|
||||||
|
|
||||||
// Inject Discourse.SiteSettings to avoid using Discourse.SiteSettings globals
|
|
||||||
app.register('site-settings:main', Discourse.SiteSettings, { instantiate: false });
|
app.register('site-settings:main', Discourse.SiteSettings, { instantiate: false });
|
||||||
injectAll(app, 'siteSettings');
|
injectAll(app, 'siteSettings');
|
||||||
|
|
||||||
// Inject Session for transient data
|
|
||||||
app.register('session:main', Session.current(), { instantiate: false });
|
app.register('session:main', Session.current(), { instantiate: false });
|
||||||
injectAll(app, 'session');
|
injectAll(app, 'session');
|
||||||
|
|
||||||
|
@ -46,7 +42,7 @@ export default {
|
||||||
inject(app, 'currentUser', 'component', 'route', 'controller');
|
inject(app, 'currentUser', 'component', 'route', 'controller');
|
||||||
|
|
||||||
app.register('message-bus:main', window.MessageBus, { instantiate: false });
|
app.register('message-bus:main', window.MessageBus, { instantiate: false });
|
||||||
inject(app, 'messageBus', 'route', 'controller', 'view', 'component');
|
injectAll(app, 'messageBus');
|
||||||
|
|
||||||
app.register('location:discourse-location', DiscourseLocation);
|
app.register('location:discourse-location', DiscourseLocation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,8 @@ class Admin::BackupsController < Admin::AdminController
|
||||||
def create
|
def create
|
||||||
opts = {
|
opts = {
|
||||||
publish_to_message_bus: true,
|
publish_to_message_bus: true,
|
||||||
with_uploads: params.fetch(:with_uploads) == "true"
|
with_uploads: params.fetch(:with_uploads) == "true",
|
||||||
|
client_id: params[:client_id],
|
||||||
}
|
}
|
||||||
BackupRestore.backup!(current_user.id, opts)
|
BackupRestore.backup!(current_user.id, opts)
|
||||||
rescue BackupRestore::OperationRunningError
|
rescue BackupRestore::OperationRunningError
|
||||||
|
@ -70,8 +71,12 @@ class Admin::BackupsController < Admin::AdminController
|
||||||
end
|
end
|
||||||
|
|
||||||
def restore
|
def restore
|
||||||
filename = params.fetch(:id)
|
opts = {
|
||||||
BackupRestore.restore!(current_user.id, filename, true)
|
filename: params.fetch(:id),
|
||||||
|
client_id: params.fetch(:client_id),
|
||||||
|
publish_to_message_bus: true,
|
||||||
|
}
|
||||||
|
BackupRestore.restore!(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
|
||||||
|
|
|
@ -13,8 +13,8 @@ module BackupRestore
|
||||||
start! BackupRestore::Backuper.new(user_id, opts)
|
start! BackupRestore::Backuper.new(user_id, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.restore!(user_id, filename, publish_to_message_bus=false)
|
def self.restore!(user_id, opts={})
|
||||||
start! BackupRestore::Restorer.new(user_id, filename, publish_to_message_bus)
|
start! BackupRestore::Restorer.new(user_id, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.rollback!
|
def self.rollback!
|
||||||
|
|
|
@ -6,6 +6,7 @@ module BackupRestore
|
||||||
|
|
||||||
def initialize(user_id, opts={})
|
def initialize(user_id, opts={})
|
||||||
@user_id = user_id
|
@user_id = user_id
|
||||||
|
@client_id = opts[:client_id]
|
||||||
@publish_to_message_bus = opts[:publish_to_message_bus] || false
|
@publish_to_message_bus = opts[:publish_to_message_bus] || false
|
||||||
@with_uploads = opts[:with_uploads].nil? ? true : opts[:with_uploads]
|
@with_uploads = opts[:with_uploads].nil? ? true : opts[:with_uploads]
|
||||||
|
|
||||||
|
@ -336,7 +337,7 @@ module BackupRestore
|
||||||
def publish_log(message, timestamp)
|
def publish_log(message, timestamp)
|
||||||
return unless @publish_to_message_bus
|
return unless @publish_to_message_bus
|
||||||
data = { timestamp: timestamp, operation: "backup", message: message }
|
data = { timestamp: timestamp, operation: "backup", message: message }
|
||||||
MessageBus.publish(BackupRestore::LOGS_CHANNEL, data, user_ids: [@user_id])
|
MessageBus.publish(BackupRestore::LOGS_CHANNEL, data, user_ids: [@user_id], client_ids: [@client_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def save_log(message, timestamp)
|
def save_log(message, timestamp)
|
||||||
|
|
|
@ -7,8 +7,11 @@ module BackupRestore
|
||||||
|
|
||||||
attr_reader :success
|
attr_reader :success
|
||||||
|
|
||||||
def initialize(user_id, filename, publish_to_message_bus = false)
|
def initialize(user_id, opts={})
|
||||||
@user_id, @filename, @publish_to_message_bus = user_id, filename, publish_to_message_bus
|
@user_id = user_id
|
||||||
|
@client_id = opts[:client_id]
|
||||||
|
@filename = opts[:filename]
|
||||||
|
@publish_to_message_bus = opts[:publish_to_message_bus] || false
|
||||||
|
|
||||||
ensure_restore_is_enabled
|
ensure_restore_is_enabled
|
||||||
ensure_no_operation_is_running
|
ensure_no_operation_is_running
|
||||||
|
@ -45,8 +48,6 @@ module BackupRestore
|
||||||
|
|
||||||
switch_schema!
|
switch_schema!
|
||||||
|
|
||||||
# TOFIX: MessageBus is busted...
|
|
||||||
|
|
||||||
migrate_database
|
migrate_database
|
||||||
reconnect_database
|
reconnect_database
|
||||||
reload_site_settings
|
reload_site_settings
|
||||||
|
@ -354,7 +355,7 @@ module BackupRestore
|
||||||
def publish_log(message, timestamp)
|
def publish_log(message, timestamp)
|
||||||
return unless @publish_to_message_bus
|
return unless @publish_to_message_bus
|
||||||
data = { timestamp: timestamp, operation: "restore", message: message }
|
data = { timestamp: timestamp, operation: "restore", message: message }
|
||||||
MessageBus.publish(BackupRestore::LOGS_CHANNEL, data, user_ids: [@user_id])
|
MessageBus.publish(BackupRestore::LOGS_CHANNEL, data, user_ids: [@user_id], client_ids: [@client_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def save_log(message, timestamp)
|
def save_log(message, timestamp)
|
||||||
|
|
|
@ -63,7 +63,7 @@ class DiscourseCLI < Thor
|
||||||
|
|
||||||
begin
|
begin
|
||||||
puts "Starting restore: #{filename}"
|
puts "Starting restore: #{filename}"
|
||||||
restorer = BackupRestore::Restorer.new(Discourse.system_user.id, filename)
|
restorer = BackupRestore::Restorer.new(Discourse.system_user.id, filename: filename)
|
||||||
restorer.run
|
restorer.run
|
||||||
puts 'Restore done.'
|
puts 'Restore done.'
|
||||||
rescue BackupRestore::FilenameMissingError
|
rescue BackupRestore::FilenameMissingError
|
||||||
|
|
Loading…
Reference in a new issue