Add option to use bytebin for exports/imports (#2432)

This commit is contained in:
Sam Goodger 2020-07-05 05:41:34 +10:00 committed by GitHub
parent 6c7c1b67b8
commit 0fd7f643a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 186 additions and 79 deletions

View File

@ -27,6 +27,7 @@ package me.lucko.luckperms.common.backup;
import com.google.gson.JsonObject;
import me.lucko.luckperms.common.command.CommandResult;
import me.lucko.luckperms.common.locale.message.Message;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.Track;
@ -40,10 +41,14 @@ import me.lucko.luckperms.common.util.ProgressLogger;
import me.lucko.luckperms.common.util.gson.GsonProvider;
import me.lucko.luckperms.common.util.gson.JArray;
import me.lucko.luckperms.common.util.gson.JObject;
import me.lucko.luckperms.common.web.AbstractHttpClient;
import me.lucko.luckperms.common.web.UnsuccessfulRequestException;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@ -78,13 +83,30 @@ public class Exporter implements Runnable {
private final Sender executor;
private final Path filePath;
private final boolean includeUsers;
private final boolean saveFile;
private final String label;
private final ProgressLogger log;
public Exporter(LuckPermsPlugin plugin, Sender executor, Path filePath, boolean includeUsers) {
public Exporter(LuckPermsPlugin plugin, Sender executor, Path filePath, boolean includeUsers, boolean saveFile) {
this.plugin = plugin;
this.executor = executor;
this.filePath = filePath;
this.includeUsers = includeUsers;
this.saveFile = saveFile;
this.label = null;
this.log = new ProgressLogger(Message.EXPORT_LOG, Message.EXPORT_LOG_PROGRESS, null);
this.log.addListener(plugin.getConsoleSender());
this.log.addListener(executor);
}
public Exporter(LuckPermsPlugin plugin, Sender executor, boolean includeUsers, boolean saveFile, String label) {
this.plugin = plugin;
this.executor = executor;
this.filePath = null;
this.includeUsers = includeUsers;
this.saveFile = saveFile;
this.label = label;
this.log = new ProgressLogger(Message.EXPORT_LOG, Message.EXPORT_LOG_PROGRESS, null);
this.log.addListener(plugin.getConsoleSender());
@ -93,32 +115,61 @@ public class Exporter implements Runnable {
@Override
public void run() {
JsonObject file = new JsonObject();
file.add("metadata", new JObject()
JsonObject json = new JsonObject();
json.add("metadata", new JObject()
.add("generatedBy", this.executor.getNameWithLocation())
.add("generatedAt", DATE_FORMAT.format(new Date(System.currentTimeMillis())))
.toJson());
this.log.log("Gathering group data...");
file.add("groups", exportGroups());
json.add("groups", exportGroups());
this.log.log("Gathering track data...");
file.add("tracks", exportTracks());
json.add("tracks", exportTracks());
if (this.includeUsers) {
this.log.log("Gathering user data...");
file.add("users", exportUsers());
json.add("users", exportUsers());
}
this.log.log("Finished gathering data, writing file...");
if (this.saveFile) {
this.log.log("Finished gathering data, writing file...");
try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(this.filePath)), StandardCharsets.UTF_8))) {
GsonProvider.prettyPrinting().toJson(file, out);
try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(this.filePath)), StandardCharsets.UTF_8))) {
GsonProvider.prettyPrinting().toJson(json, out);
} catch (IOException e) {
e.printStackTrace();
}
this.log.getListeners().forEach(l -> Message.LOG_EXPORT_SUCCESS.send(l, this.filePath.toFile().getAbsolutePath()));
} else {
post(json, this.executor, this.plugin, label);
}
}
public static CommandResult post(JsonObject payload, Sender sender, LuckPermsPlugin plugin, String label) {
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
try (Writer writer = new OutputStreamWriter(new GZIPOutputStream(bytesOut), StandardCharsets.UTF_8)) {
GsonProvider.prettyPrinting().toJson(payload, writer);
} catch (IOException e) {
e.printStackTrace();
}
this.log.getListeners().forEach(l -> Message.LOG_EXPORT_SUCCESS.send(l, this.filePath.toFile().getAbsolutePath()));
String pasteId;
try {
pasteId = plugin.getBytebin().postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key();
} catch (UnsuccessfulRequestException e) {
Message.EXPORT_HTTP_REQUEST_FAILURE.send(sender, e.getResponse().code(), e.getResponse().message());
return CommandResult.STATE_ERROR;
} catch (IOException e) {
new RuntimeException("Error uploading data to bytebin", e).printStackTrace();
Message.EXPORT_HTTP_UNKNOWN_FAILURE.send(sender);
return CommandResult.STATE_ERROR;
}
Message.EXPORT_CODE.send(sender, pasteId, label, pasteId);
return CommandResult.SUCCESS;
}
private JsonObject exportGroups() {

View File

@ -56,41 +56,52 @@ public class ExportCommand extends SingleCommand {
return CommandResult.STATE_ERROR;
}
Path dataDirectory = plugin.getBootstrap().getDataDirectory();
Path path = dataDirectory.resolve(args.get(0) + ".json.gz");
if (!path.getParent().equals(dataDirectory)) {
Message.FILE_NOT_WITHIN_DIRECTORY.send(sender, path.toString());
return CommandResult.INVALID_ARGS;
}
boolean includeUsers = !args.remove("--without-users");
boolean saveFile = !args.remove("--upload");
if (Files.exists(path)) {
Message.LOG_EXPORT_ALREADY_EXISTS.send(sender, path.toString());
return CommandResult.INVALID_ARGS;
Exporter exporter;
if (saveFile) {
Path dataDirectory = plugin.getBootstrap().getDataDirectory();
Path path = dataDirectory.resolve(args.get(0) + ".json.gz");
if (!path.getParent().equals(dataDirectory)) {
Message.FILE_NOT_WITHIN_DIRECTORY.send(sender, path.toString());
return CommandResult.INVALID_ARGS;
}
if (Files.exists(path)) {
Message.LOG_EXPORT_ALREADY_EXISTS.send(sender, path.toString());
return CommandResult.INVALID_ARGS;
}
try {
Files.createFile(path);
} catch (IOException e) {
Message.LOG_EXPORT_FAILURE.send(sender);
e.printStackTrace();
return CommandResult.FAILURE;
}
if (!Files.isWritable(path)) {
Message.LOG_EXPORT_NOT_WRITABLE.send(sender, path.toString());
return CommandResult.FAILURE;
}
if (!this.running.compareAndSet(false, true)) {
Message.EXPORT_ALREADY_RUNNING.send(sender);
return CommandResult.STATE_ERROR;
}
exporter = new Exporter(plugin, sender, path, includeUsers, saveFile);
} else {
if (!this.running.compareAndSet(false, true)) {
Message.EXPORT_ALREADY_RUNNING.send(sender);
return CommandResult.STATE_ERROR;
}
exporter = new Exporter(plugin, sender, includeUsers, saveFile, label);
}
try {
Files.createFile(path);
} catch (IOException e) {
Message.LOG_EXPORT_FAILURE.send(sender);
e.printStackTrace();
return CommandResult.FAILURE;
}
if (!Files.isWritable(path)) {
Message.LOG_EXPORT_NOT_WRITABLE.send(sender, path.toString());
return CommandResult.FAILURE;
}
if (!this.running.compareAndSet(false, true)) {
Message.EXPORT_ALREADY_RUNNING.send(sender);
return CommandResult.STATE_ERROR;
}
Exporter exporter = new Exporter(plugin, sender, path, includeUsers);
// Run the exporter in its own thread.
plugin.getBootstrap().getScheduler().executeAsync(() -> {
try {

View File

@ -39,6 +39,8 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.util.Predicates;
import me.lucko.luckperms.common.util.gson.GsonProvider;
import me.lucko.luckperms.common.web.UnsuccessfulRequestException;
import me.lucko.luckperms.common.web.WebEditor;
import java.io.BufferedReader;
import java.io.IOException;
@ -63,46 +65,73 @@ public class ImportCommand extends SingleCommand {
return CommandResult.STATE_ERROR;
}
String fileName = args.get(0);
Path dataDirectory = plugin.getBootstrap().getDataDirectory();
Path path = dataDirectory.resolve(fileName);
if (!path.getParent().equals(dataDirectory) || path.getFileName().toString().equals("config.yml")) {
Message.FILE_NOT_WITHIN_DIRECTORY.send(sender, path.toString());
return CommandResult.INVALID_ARGS;
}
// try auto adding the '.json.gz' extension
if (!Files.exists(path) && !fileName.contains(".")) {
Path pathWithDefaultExtension = path.resolveSibling(fileName + ".json.gz");
if (Files.exists(pathWithDefaultExtension)) {
path = pathWithDefaultExtension;
}
}
if (!Files.exists(path)) {
Message.IMPORT_FILE_DOESNT_EXIST.send(sender, path.toString());
return CommandResult.INVALID_ARGS;
}
if (!Files.isReadable(path)) {
Message.IMPORT_FILE_NOT_READABLE.send(sender, path.toString());
return CommandResult.FAILURE;
}
if (!this.running.compareAndSet(false, true)) {
Message.IMPORT_ALREADY_RUNNING.send(sender);
return CommandResult.STATE_ERROR;
}
boolean fromFile = !args.remove("--upload");
JsonObject data;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))) {
data = GsonProvider.normal().fromJson(reader, JsonObject.class);
} catch (IOException e) {
e.printStackTrace();
Message.IMPORT_FILE_READ_FAILURE.send(sender);
this.running.set(false);
return CommandResult.FAILURE;
if (fromFile) {
String fileName = args.get(0);
Path dataDirectory = plugin.getBootstrap().getDataDirectory();
Path path = dataDirectory.resolve(fileName);
if (!path.getParent().equals(dataDirectory) || path.getFileName().toString().equals("config.yml")) {
Message.FILE_NOT_WITHIN_DIRECTORY.send(sender, path.toString());
return CommandResult.INVALID_ARGS;
}
// try auto adding the '.json.gz' extension
if (!Files.exists(path) && !fileName.contains(".")) {
Path pathWithDefaultExtension = path.resolveSibling(fileName + ".json.gz");
if (Files.exists(pathWithDefaultExtension)) {
path = pathWithDefaultExtension;
}
}
if (!Files.exists(path)) {
Message.IMPORT_FILE_DOESNT_EXIST.send(sender, path.toString());
return CommandResult.INVALID_ARGS;
}
if (!Files.isReadable(path)) {
Message.IMPORT_FILE_NOT_READABLE.send(sender, path.toString());
return CommandResult.FAILURE;
}
if (!this.running.compareAndSet(false, true)) {
Message.IMPORT_ALREADY_RUNNING.send(sender);
return CommandResult.STATE_ERROR;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))) {
data = GsonProvider.normal().fromJson(reader, JsonObject.class);
} catch (IOException e) {
e.printStackTrace();
Message.IMPORT_FILE_READ_FAILURE.send(sender);
this.running.set(false);
return CommandResult.FAILURE;
}
} else {
String code = args.get(0);
if (code.isEmpty()) {
Message.IMPORT_INVALID_CODE.send(sender, code);
return CommandResult.INVALID_ARGS;
}
try {
data = WebEditor.readDataFromBytebin(plugin.getBytebin(), code);
} catch (UnsuccessfulRequestException e) {
Message.IMPORT_HTTP_REQUEST_FAILURE.send(sender, e.getResponse().code(), e.getResponse().message());
return CommandResult.STATE_ERROR;
} catch (IOException e) {
new RuntimeException("Error reading data to bytebin", e).printStackTrace();
Message.IMPORT_HTTP_UNKNOWN_FAILURE.send(sender);
return CommandResult.STATE_ERROR;
}
if (data == null) {
Message.IMPORT_UNABLE_TO_READ.send(sender, code);
return CommandResult.FAILURE;
}
}
Importer importer = new Importer(plugin, sender, data, args.contains("--merge"));

View File

@ -473,10 +473,26 @@ public enum Message {
IMPORT_ALREADY_RUNNING("&cAnother import process is already running. Please wait for it to finish and try again.", true),
EXPORT_ALREADY_RUNNING("&cAnother export process is already running. Please wait for it to finish and try again.", true),
FILE_NOT_WITHIN_DIRECTORY("&cError: File &4{}&c must be a direct child of the data directory.", true),
EXPORT_CODE(
"&aExport code: &7{}" + "\n" +
"&7Use the following command to import:" + "\n" +
"&a/{} import {} --upload",
true
),
EXPORT_HTTP_REQUEST_FAILURE("&cUnable to communicate with bytebin. (response code &4{}&c, message='{}')", true),
EXPORT_HTTP_UNKNOWN_FAILURE("&cUnable to communicate with bytebin. Check the console for errors.", true),
IMPORT_FILE_DOESNT_EXIST("&cError: File &4{}&c does not exist.", true),
IMPORT_FILE_NOT_READABLE("&cError: File &4{}&c is not readable.", true),
IMPORT_FILE_READ_FAILURE("&cAn unexpected error occured whilst reading from the import file. (is it the correct format?)", true),
IMPORT_INVALID_CODE("&cInvalid code. &7({})", true),
IMPORT_HTTP_REQUEST_FAILURE("&cUnable to communicate with bytebin. (response code &4{}&c, message='{}')", true),
IMPORT_HTTP_UNKNOWN_FAILURE("&cUnable to communicate with bytebin. Check the console for errors.", true),
IMPORT_UNABLE_TO_READ("&cUnable to read data using the given code. &7({})", true),
IMPORT_PROGRESS("&b(Import) &b-> &f{}&f% complete &7- &b{}&f/&b{} &foperations complete with &c{} &ferrors.", true),
IMPORT_PROGRESS_SIN("&b(Import) &b-> &f{}&f% complete &7- &b{}&f/&b{} &foperations complete with &c{} &ferror.", true),
IMPORT_START("&b(Import) &b-> &fStarting import process.", true),