#1180 Create command for performing a backup

This commit is contained in:
ljacqu 2017-04-23 20:23:40 +02:00
parent e75e22d624
commit 0e923b6a70
11 changed files with 168 additions and 90 deletions

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Fri Apr 14 18:52:29 CEST 2017. See docs/commands/commands.tpl.md -->
<!-- File auto-generated on Sun Apr 23 19:29:43 CEST 2017. See docs/commands/commands.tpl.md -->
## AuthMe Commands
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`
@ -32,8 +32,10 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
<br />Requires `authme.admin.firstspawn`
- **/authme setfirstspawn**: Change the first player's spawn to your current position.
<br />Requires `authme.admin.setfirstspawn`
- **/authme purge** &lt;days> [all]: Purge old AuthMeReloaded data longer than the specified amount of days ago.
- **/authme purge** &lt;days> [all]: Purge old AuthMeReloaded data longer than the specified number of days ago.
<br />Requires `authme.admin.purge`
- **/authme backup**: Creates a backup of the registered users.
<br />Requires `authme.admin.backup`
- **/authme resetpos** &lt;player/*>: Purge the last know position of the specified player or all of them.
<br />Requires `authme.admin.purgelastpos`
- **/authme purgebannedplayers**: Purge all AuthMeReloaded data for banned players.
@ -85,4 +87,4 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Apr 14 18:52:29 CEST 2017
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 23 19:29:43 CEST 2017

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Tue Mar 28 21:48:52 CEST 2017. See docs/config/config.tpl.md -->
<!-- File auto-generated on Sun Apr 23 19:30:08 CEST 2017. See docs/config/config.tpl.md -->
## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
@ -481,13 +481,13 @@ limbo:
# See above for a description of the values.
restoreWalkSpeed: 'MAX_RESTORE'
BackupSystem:
# Enable or disable automatic backup
# General configuration for backups: if false, no backups are possible
ActivateBackup: false
# Set backup at every start of server
# Create backup at every start of server
OnServerStart: false
# Set backup at every stop of server
# Create backup at every stop of server
OnServerStop: true
# Windows only mysql installation Path
# Windows only: MySQL installation path
MysqlWindowsPath: 'C:\Program Files\MySQL\MySQL Server 5.1\'
Converter:
Rakamak:
@ -507,4 +507,4 @@ To change settings on a running server, save your changes to config.yml and use
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Tue Mar 28 21:48:52 CEST 2017
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 23 19:30:08 CEST 2017

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Fri Apr 14 18:52:32 CEST 2017. See docs/permissions/permission_nodes.tpl.md -->
<!-- File auto-generated on Sun Apr 23 19:32:06 CEST 2017. See docs/permissions/permission_nodes.tpl.md -->
## AuthMe Permission Nodes
The following are the permission nodes that are currently supported by the latest dev builds.
@ -7,6 +7,7 @@ The following are the permission nodes that are currently supported by the lates
- **authme.admin.*** Give access to all admin commands.
- **authme.admin.accounts** Administrator command to see all accounts associated with a user.
- **authme.admin.antibotmessages** Permission to see Antibot messages.
- **authme.admin.backup** Allows to use the backup command.
- **authme.admin.changemail** Administrator command to set or change the email address of a user.
- **authme.admin.changepassword** Administrator command to change the password of a user.
- **authme.admin.converter** Administrator command to convert old or other data to AuthMe data.
@ -59,4 +60,4 @@ The following are the permission nodes that are currently supported by the lates
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Fri Apr 14 18:52:32 CEST 2017
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Apr 23 19:32:06 CEST 2017

View File

@ -73,6 +73,7 @@ public class AuthMe extends JavaPlugin {
private DataSource database;
private BukkitService bukkitService;
private Injector injector;
private BackupService backupService;
/**
* Constructor.
@ -155,7 +156,7 @@ public class AuthMe extends JavaPlugin {
}
// Do a backup on start
new BackupService(this, settings).doBackup(BackupService.BackupCause.START);
backupService.doBackup(BackupService.BackupCause.START);
// Set up Metrics
OnStartupTasks.sendMetrics(this, settings);
@ -262,6 +263,7 @@ public class AuthMe extends JavaPlugin {
permsMan = injector.getSingleton(PermissionsManager.class);
bukkitService = injector.getSingleton(BukkitService.class);
commandHandler = injector.getSingleton(CommandHandler.class);
backupService = injector.getSingleton(BackupService.class);
// Trigger construction of API classes; they will keep track of the singleton
injector.getSingleton(fr.xephi.authme.api.v3.AuthMeApi.class);
@ -357,8 +359,8 @@ public class AuthMe extends JavaPlugin {
}
// Do backup on stop if enabled
if (settings != null) {
new BackupService(this, settings).doBackup(BackupService.BackupCause.STOP);
if (backupService != null) {
backupService.doBackup(BackupService.BackupCause.STOP);
}
// Wait for tasks and close data source

View File

@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList;
import fr.xephi.authme.command.executable.HelpCommand;
import fr.xephi.authme.command.executable.authme.AccountsCommand;
import fr.xephi.authme.command.executable.authme.AuthMeCommand;
import fr.xephi.authme.command.executable.authme.BackupCommand;
import fr.xephi.authme.command.executable.authme.ChangePasswordAdminCommand;
import fr.xephi.authme.command.executable.authme.ConverterCommand;
import fr.xephi.authme.command.executable.authme.FirstSpawnCommand;
@ -226,13 +227,23 @@ public class CommandInitializer {
.parent(AUTHME_BASE)
.labels("purge", "delete")
.description("Purge old data")
.detailedDescription("Purge old AuthMeReloaded data longer than the specified amount of days ago.")
.detailedDescription("Purge old AuthMeReloaded data longer than the specified number of days ago.")
.withArgument("days", "Number of days", false)
.withArgument("all", "Add 'all' at the end to also purge players with lastlogin = 0", true)
.permission(AdminPermission.PURGE)
.executableCommand(PurgeCommand.class)
.register();
// Backup command
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("backup")
.description("Perform a backup")
.detailedDescription("Creates a backup of the registered users.")
.permission(AdminPermission.BACKUP)
.executableCommand(BackupCommand.class)
.register();
// Register the purgelastposition command
CommandDescription.builder()
.parent(AUTHME_BASE)

View File

@ -0,0 +1,23 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.service.BackupService;
import fr.xephi.authme.service.BackupService.BackupCause;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.List;
/**
* Command to perform a backup.
*/
public class BackupCommand implements ExecutableCommand {
@Inject
private BackupService backupService;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
backupService.doBackup(BackupCause.COMMAND, sender);
}
}

View File

@ -113,7 +113,12 @@ public enum AdminPermission implements PermissionNode {
/**
* Permission to see the other accounts of the players that log in.
*/
SEE_OTHER_ACCOUNTS("authme.admin.seeotheraccounts");
SEE_OTHER_ACCOUNTS("authme.admin.seeotheraccounts"),
/**
* Allows to use the backup command.
*/
BACKUP("authme.admin.backup");
/**
* The permission node.

View File

@ -1,12 +1,15 @@
package fr.xephi.authme.service;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.BackupSettings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.util.FileUtils;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@ -16,80 +19,80 @@ import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import static fr.xephi.authme.util.Utils.logAndSendMessage;
import static fr.xephi.authme.util.Utils.logAndSendWarning;
/**
* The backup management class
*
* @author stefano
* Performs a backup of the data source.
*/
public class BackupService {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm");
private final String dbName;
private final String dbUserName;
private final String dbPassword;
private final String tblname;
private final String path;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm");
private final File dataFolder;
private final File backupFolder;
private final Settings settings;
/**
* Constructor for PerformBackup.
* Constructor.
*
* @param instance AuthMe
* @param settings The plugin settings
* @param dataFolder the data folder
* @param settings the plugin settings
*/
public BackupService(AuthMe instance, Settings settings) {
this.dataFolder = instance.getDataFolder();
this.settings = settings;
this.dbName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.dbUserName = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
this.dbPassword = settings.getProperty(DatabaseSettings.MYSQL_PASSWORD);
this.tblname = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
String dateString = DATE_FORMAT.format(new Date());
this.path = String.join(File.separator,
instance.getDataFolder().getPath(), "backups", "backup" + dateString);
@Inject
public BackupService(@DataFolder File dataFolder, Settings settings) {
this.dataFolder = dataFolder;
this.backupFolder = new File(dataFolder, "backups");
this.settings = settings;
}
/**
* Perform a backup with the given reason.
* Performs a backup for the given reason.
*
* @param cause The cause of the backup.
* @param cause backup reason
*/
public void doBackup(BackupCause cause) {
doBackup(cause, null);
}
/**
* Performs a backup for the given reason.
*
* @param cause backup reason
* @param sender the command sender (nullable)
*/
public void doBackup(BackupCause cause, CommandSender sender) {
if (!settings.getProperty(BackupSettings.ENABLED)) {
// Print a warning if the backup was requested via command or by another plugin
if (cause == BackupCause.COMMAND || cause == BackupCause.OTHER) {
ConsoleLogger.warning("Can't perform a Backup: disabled in configuration. Cause of the Backup: "
+ cause.name());
logAndSendWarning(sender,
"Can't perform a backup: disabled in configuration. Cause of the backup: " + cause.name());
}
return;
}
// Check whether a backup should be made at the specified point in time
if (BackupCause.START.equals(cause) && !settings.getProperty(BackupSettings.ON_SERVER_START)
|| BackupCause.STOP.equals(cause) && !settings.getProperty(BackupSettings.ON_SERVER_STOP)) {
} else if (BackupCause.START == cause && !settings.getProperty(BackupSettings.ON_SERVER_START)
|| BackupCause.STOP == cause && !settings.getProperty(BackupSettings.ON_SERVER_STOP)) {
// Don't perform backup on start or stop if so configured
return;
}
// Do backup and check return value!
if (doBackup()) {
ConsoleLogger.info("A backup has been performed successfully. Cause of the Backup: " + cause.name());
logAndSendMessage(sender,
"A backup has been performed successfully. Cause of the backup: " + cause.name());
} else {
ConsoleLogger.warning("Error while performing a backup! Cause of the Backup: " + cause.name());
logAndSendWarning(sender, "Error while performing a backup! Cause of the backup: " + cause.name());
}
}
public boolean doBackup() {
private boolean doBackup() {
DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND);
switch (dataSourceType) {
case FILE:
return fileBackup("auths.db");
return performFileBackup("auths.db");
case MYSQL:
return mySqlBackup();
return performMySqlBackup();
case SQLITE:
return fileBackup(dbName + ".db");
String dbName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
return performFileBackup(dbName + ".db");
default:
ConsoleLogger.warning("Unknown data source type '" + dataSourceType + "' for backup");
}
@ -97,17 +100,15 @@ public class BackupService {
return false;
}
private boolean mySqlBackup() {
File dirBackup = new File(dataFolder + File.separator + "backups");
private boolean performMySqlBackup() {
FileUtils.createDirectory(backupFolder);
File sqlBackupFile = constructBackupFile("sql");
if (!dirBackup.exists()) {
dirBackup.mkdir();
}
String backupWindowsPath = settings.getProperty(BackupSettings.MYSQL_WINDOWS_PATH);
boolean isUsingWindows = checkWindows(backupWindowsPath);
boolean isUsingWindows = useWindowsCommand(backupWindowsPath);
String backupCommand = isUsingWindows
? backupWindowsPath + "\\bin\\mysqldump.exe" + buildMysqlDumpArguments()
: "mysqldump" + buildMysqlDumpArguments();
? backupWindowsPath + "\\bin\\mysqldump.exe" + buildMysqlDumpArguments(sqlBackupFile)
: "mysqldump" + buildMysqlDumpArguments(sqlBackupFile);
try {
Process runtimeProcess = Runtime.getRuntime().exec(backupCommand);
@ -124,14 +125,12 @@ public class BackupService {
return false;
}
private boolean fileBackup(String backend) {
File dirBackup = new File(dataFolder + File.separator + "backups");
if (!dirBackup.exists())
dirBackup.mkdir();
private boolean performFileBackup(String filename) {
FileUtils.createDirectory(backupFolder);
File backupFile = constructBackupFile("db");
try {
copy("plugins" + File.separator + "AuthMe" + File.separator + backend, path + ".db");
copy(new File(dataFolder, filename), backupFile);
return true;
} catch (IOException ex) {
ConsoleLogger.logException("Encountered an error during file backup:", ex);
@ -146,7 +145,7 @@ public class BackupService {
* @param windowsPath The path to check
* @return True if the path is correct, false if it is incorrect or the OS is not Windows
*/
private static boolean checkWindows(String windowsPath) {
private static boolean useWindowsCommand(String windowsPath) {
String isWin = System.getProperty("os.name").toLowerCase();
if (isWin.contains("win")) {
if (new File(windowsPath + "\\bin\\mysqldump.exe").exists()) {
@ -162,28 +161,42 @@ public class BackupService {
/**
* Builds the command line arguments to pass along when running the {@code mysqldump} command.
*
* @param sqlBackupFile the file to back up to
* @return the mysqldump command line arguments
*/
private String buildMysqlDumpArguments() {
return " -u " + dbUserName + " -p" + dbPassword + " " + dbName
+ " --tables " + tblname + " -r " + path + ".sql";
private String buildMysqlDumpArguments(File sqlBackupFile) {
String dbUsername = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
String dbPassword = settings.getProperty(DatabaseSettings.MYSQL_PASSWORD);
String dbName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
return " -u " + dbUsername + " -p" + dbPassword + " " + dbName
+ " --tables " + tableName + " -r " + sqlBackupFile.getPath() + ".sql";
}
private static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst);
/**
* Constructs the file name to back up the data source to.
*
* @param fileExtension the file extension to use (e.g. sql)
* @return the file to back up the data to
*/
private File constructBackupFile(String fileExtension) {
String dateString = dateFormat.format(new Date());
return new File(backupFolder, "backup" + dateString + "." + fileExtension);
}
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
private static void copy(File src, File dst) throws IOException {
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst)) {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
in.close();
out.close();
}
/**
* Possible backup causes.
*/

View File

@ -8,19 +8,19 @@ import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class BackupSettings implements SettingsHolder {
@Comment("Enable or disable automatic backup")
@Comment("General configuration for backups: if false, no backups are possible")
public static final Property<Boolean> ENABLED =
newProperty("BackupSystem.ActivateBackup", false);
@Comment("Set backup at every start of server")
@Comment("Create backup at every start of server")
public static final Property<Boolean> ON_SERVER_START =
newProperty("BackupSystem.OnServerStart", false);
@Comment("Set backup at every stop of server")
@Comment("Create backup at every stop of server")
public static final Property<Boolean> ON_SERVER_STOP =
newProperty("BackupSystem.OnServerStop", true);
@Comment("Windows only mysql installation Path")
@Comment("Windows only: MySQL installation path")
public static final Property<String> MYSQL_WINDOWS_PATH =
newProperty("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.util;
import fr.xephi.authme.ConsoleLogger;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
@ -69,6 +70,22 @@ public final class Utils {
}
}
/**
* Sends a warning to the given sender (null safe), and logs the warning to the console.
* This method is aware that the command sender might be the console sender and avoids
* displaying the message twice in this case.
*
* @param sender the sender to inform
* @param message the warning to log and send
*/
public static void logAndSendWarning(CommandSender sender, String message) {
ConsoleLogger.warning(message);
// Make sure sender is not console user, which will see the message from ConsoleLogger already
if (sender != null && !(sender instanceof ConsoleCommandSender)) {
sender.sendMessage(ChatColor.RED + message);
}
}
/**
* Null-safe way to check whether a collection is empty or not.
*

View File

@ -17,7 +17,7 @@ softdepend:
commands:
authme:
description: AuthMe op commands
usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|debug
usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|debug
login:
description: Login command
usage: /login <password>
@ -55,6 +55,7 @@ permissions:
children:
authme.admin.accounts: true
authme.admin.antibotmessages: true
authme.admin.backup: true
authme.admin.changemail: true
authme.admin.changepassword: true
authme.admin.converter: true
@ -81,6 +82,9 @@ permissions:
authme.admin.antibotmessages:
description: Permission to see Antibot messages.
default: op
authme.admin.backup:
description: Allows to use the backup command.
default: op
authme.admin.changemail:
description: Administrator command to set or change the email address of a user.
default: op