diff --git a/app/controllers/admin/backups_controller.rb b/app/controllers/admin/backups_controller.rb new file mode 100644 index 00000000000..dae617b0426 --- /dev/null +++ b/app/controllers/admin/backups_controller.rb @@ -0,0 +1,84 @@ +require_dependency "backup_restore" + +class Admin::BackupsController < Admin::AdminController + + skip_before_filter :check_xhr, only: [:index, :show] + + def index + respond_to do |format| + format.html do + store_preloaded("backups", MultiJson.dump(serialize_data(Backup.all, BackupSerializer))) + store_preloaded("operations_status", MultiJson.dump(BackupRestore.operations_status)) + render "default/empty" + end + format.json do + render_serialized(Backup.all, BackupSerializer) + end + end + end + + def status + render_json_dump(BackupRestore.operations_status) + end + + def create + BackupRestore.backup!(current_user.id, true) + rescue BackupRestore::OperationRunningError + render json: failed_json.merge(message: I18n.t("backup.operation_already_running")) + else + render json: success_json + end + + def cancel + BackupRestore.cancel! + rescue BackupRestore::OperationRunningError + render json: failed_json.merge(message: I18n.t("backup.operation_already_running")) + else + render json: success_json + end + + # download + def show + filename = params.fetch(:id) + if backup = Backup[filename] + send_file backup.path + else + render nothing: true, status: 404 + end + end + + def destroy + filename = params.fetch(:id) + Backup.remove(filename) + render nothing: true + end + + def logs + store_preloaded("operations_status", MultiJson.dump(BackupRestore.operations_status)) + render "default/empty" + end + + def restore + filename = params.fetch(:id) + BackupRestore.restore!(current_user.id, filename, true) + rescue BackupRestore::OperationRunningError + render json: failed_json.merge(message: I18n.t("backup.operation_already_running")) + else + render json: success_json + end + + def rollback + BackupRestore.rollback! + rescue BackupRestore::OperationRunningError + render json: failed_json.merge(message: I18n.t("backup.operation_already_running")) + else + render json: success_json + end + + def readonly + enable = params.fetch(:enable).to_s == "true" + enable ? Discourse.enable_readonly_mode : Discourse.disable_readonly_mode + render nothing: true + end + +end diff --git a/spec/controllers/admin/backups_controller_spec.rb b/spec/controllers/admin/backups_controller_spec.rb new file mode 100644 index 00000000000..936fb9756db --- /dev/null +++ b/spec/controllers/admin/backups_controller_spec.rb @@ -0,0 +1,194 @@ +require "spec_helper" + +describe Admin::BackupsController do + + it "is a subclass of AdminController" do + (Admin::BackupsController < Admin::AdminController).should be_true + end + + let(:backup_filename) { "2014-02-10-065935.tar.gz" } + + context "while logged in as an admin" do + + before { @admin = log_in(:admin) } + + describe ".index" do + + context "html format" do + + it "preloads both backups and operations_status" do + Backup.expects(:all).returns([]) + subject.expects(:store_preloaded).with("backups", "[]") + + BackupRestore.expects(:operations_status).returns({}) + subject.expects(:store_preloaded).with("operations_status", "{}") + + xhr :get, :index, format: :html + + response.should be_success + end + + end + + context "json format" do + + it "returns a list of all the backups" do + File.stubs(:size).returns(42) + Backup.expects(:all).returns([Backup.new("backup1"), Backup.new("backup2")]) + + xhr :get, :index, format: :json + + response.should be_success + + json = JSON.parse(response.body) + json[0]["filename"].should == "backup1" + json[1]["filename"].should == "backup2" + end + + end + + end + + describe ".status" do + + it "returns the current backups status" do + BackupRestore.expects(:operations_status) + + xhr :get, :status + + response.should be_success + end + + end + + describe ".create" do + + it "starts a backup" do + BackupRestore.expects(:backup!).with(@admin.id, true) + + xhr :post, :create + + response.should be_success + end + + # it "catches OperationRunningError exception" do + # BackupRestore.expects(:is_operation_running?).returns(true) + + # xhr :post, :create + + # response.should be_success + + # json = JSON.parse(response.body) + # json["message"].should_not be_nil + # end + + end + + describe ".cancel" do + + it "cancels an export" do + BackupRestore.expects(:cancel!) + + xhr :delete, :cancel + + response.should be_success + end + + end + + describe ".show" do + + it "uses send_file to transmit the backup" do + controller.stubs(:render) # we need this since we're stubing send_file + + File.stubs(:size).returns(42) + backup = Backup.new("backup42") + + Backup.expects(:[]).with(backup_filename).returns(backup) + subject.expects(:send_file).with(backup.path) + + get :show, id: backup_filename + end + + it "returns 404 when the backup does not exist" do + Backup.expects(:[]).returns(nil) + + get :show, id: backup_filename + + response.should be_not_found + end + + end + + describe ".destroy" do + + it "removes the backup" do + Backup.expects(:remove).with(backup_filename) + + xhr :delete, :destroy, id: backup_filename + + response.should be_success + end + + end + + describe ".logs" do + + it "preloads operations_status" do + BackupRestore.expects(:operations_status).returns({}) + subject.expects(:store_preloaded).with("operations_status", "{}") + + xhr :get, :logs, format: :html + + response.should be_success + end + + end + + describe ".restore" do + + it "starts a restore" do + BackupRestore.expects(:restore!).with(@admin.id, backup_filename, true) + + xhr :post, :restore, id: backup_filename + + response.should be_success + end + + end + + describe ".rollback" do + + it "rolls back to previous working state" do + BackupRestore.expects(:rollback!) + + xhr :get, :rollback + + response.should be_success + end + + end + + describe ".readonly" do + + it "enables readonly mode" do + Discourse.expects(:enable_readonly_mode) + + xhr :put, :readonly, enable: true + + response.should be_success + end + + it "disables readonly mode" do + Discourse.expects(:disable_readonly_mode) + + xhr :put, :readonly, enable: false + + response.should be_success + end + + end + + end + +end