fix fix fix

use amazon s3
This commit is contained in:
iceBear67 committed 2025-09-22 17:59:01 +08:00
1 parent fb42be8155
commit 048451543b
7 files changed
+79 -35

No files matched your search

@@ -7,8 +7,10 @@ import net.minecraft.text.ClickEvent;
import net.minecraft.text.Text;
import java.awt.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
public class BackupCommands {
protected final JustBackupMod mod;
@@ -81,12 +83,24 @@ public class BackupCommands {
s.sendMessage(Text.literal("Please restart your server to take changes"));
restoreIssued = () -> {
System.out.println("Recovering backup... ");
try{
try {
var toOverride = Path.of(backup.from());
var old = Path.of(backup.from()+"_old");
var old = Path.of(backup.from() + "_old");
if(Files.exists(old)){
try (var f = Files.walk(old)) {
f.sorted(Comparator.reverseOrder())
.forEach(it -> {
try {
Files.deleteIfExists(it);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
Files.deleteIfExists(old);
Files.move(toOverride, old);
System.out.println("Moved your old save to "+old);
System.out.println("Moved your old save to " + old);
mod.tracker.recoverBackup(backup, toOverride);
System.out.println("Recover successfully!");
} catch (Exception e) {
@@ -145,12 +145,19 @@ public class JustBackupMod implements ModInitializer {
.then(literal("create").executes(commandHandler::cmdIssueBackup))
.then(literal("suspend").executes(commandHandler::cmdSuspend))
);
ServerLifecycleEvents.SERVER_STOPPED.register(server -> {
scheduledBackupExecutor.shutdown();
if (commandHandler.restoreIssued != null) {
commandHandler.restoreIssued.run();
}
tracker.close();
});
}
private BackupStrategy createStrategy() {
return switch (config.option()) {
case StorageOption.Local local -> new LocalBackupStrategy(local);
case StorageOption.S3 s3 -> new S3BackupStrategy(s3);
case StorageOption.S3 s3 -> new S3BackupStrategy(s3, Path.of(config.temporaryBackupDir()));
};
}
@@ -18,6 +18,7 @@ public class SerialBundlerReader implements Closeable {
protected final EntryInputStream metadataIn;
protected final byte[] buffer = new byte[4096];
protected final RandomAccessFile raf;
protected boolean entryRead;
protected int entries;
@@ -28,7 +29,7 @@ public class SerialBundlerReader implements Closeable {
// }
public SerialBundlerReader(Path path) throws IOException {
var raf = new RandomAccessFile(path.toFile(), "r");
raf = new RandomAccessFile(path.toFile(), "r");
var totalLen = Files.size(path);
var magic = raf.readInt();
var compressed = magic == Bundle.COMPRESSED_MAGIC;
@@ -96,5 +97,6 @@ public class SerialBundlerReader implements Closeable {
public void close() throws IOException {
in.close();
metadataIn.close();
raf.close();
}
}
@@ -18,4 +18,6 @@ public interface BackupStrategy {
void deleteBackup(Backup backup);
boolean isAvailable();
void close();
}
@@ -41,4 +41,9 @@ public class BackupTracker implements BackupStrategy {
public boolean isAvailable() {
return underlying.isAvailable();
}
@Override
public void close() {
underlying.close();
}
}
@@ -80,4 +80,9 @@ public class LocalBackupStrategy implements BackupStrategy {
}
return true;
}
@Override
public void close() {
}
}
@@ -1,48 +1,46 @@
package io.ib67.sfcraft.strategy;
import io.ib67.sfcraft.Backup;
import io.ib67.sfcraft.WorldDir;
import io.ib67.sfcraft.bundler.Bundle;
import io.ib67.sfcraft.config.StorageOption;
import io.minio.BucketExistsArgs;
import io.minio.MinioClient;
import io.minio.RemoveObjectArgs;
import io.minio.UploadObjectArgs;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import java.nio.file.StandardOpenOption;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@Log4j2
public class S3BackupStrategy implements BackupStrategy {
protected final StorageOption.S3 option;
protected final MinioClient minio;
protected final AtomicBoolean uploading = new AtomicBoolean(false);
protected final StorageOption.S3 option;
protected final S3Client s3;
protected final Path temporaryDownloadPath;
@SneakyThrows
public S3BackupStrategy(StorageOption.S3 option) {
public S3BackupStrategy(StorageOption.S3 option, Path tempDownloadPath) {
this.option = option;
this.minio = MinioClient.builder()
.endpoint(URI.create(option.endpoint()).toURL())
.credentials(option.accessKey(), option.secretKey())
.region(option.region())
var credential = AwsBasicCredentials.create(option.accessKey(), option.secretKey());
this.s3 = S3Client.builder()
.region(Region.of(option.region()))
.forcePathStyle(option.enforcePathStyle())
.endpointOverride(URI.create(option.endpoint()))
.credentialsProvider(StaticCredentialsProvider.create(credential))
.build();
if (option.enforcePathStyle()) {
minio.disableVirtualStyleEndpoint();
} else {
minio.enableVirtualStyleEndpoint();
}
log.info("Testing S3 credentials");
var exists = minio.bucketExists(BucketExistsArgs.builder().bucket(option.bucket()).build());
if (!exists) throw new IllegalStateException("Bucket '" + option.bucket() + "' does not exist");
// throws exception if the bucket neither inaccessible nor not exist
s3.headBucket(b -> b.bucket(option.bucket()));
this.temporaryDownloadPath = Objects.requireNonNull(tempDownloadPath);
if (Files.notExists(temporaryDownloadPath))
Files.createDirectories(temporaryDownloadPath);
}
@Override
@@ -55,11 +53,10 @@ public class S3BackupStrategy implements BackupStrategy {
do {
try {
var object = option.prefix() + "/" + pathToBundle.getFileName().toString();
minio.uploadObject(UploadObjectArgs.builder()
s3.putObject(o -> o.bucket(option.bucket())
.contentType("application/octet-stream")
.object(object)
.filename(pathToBundle.toString())
.build());
.ifNoneMatch("*") // to avoid content overriding
.key(object).build(), pathToBundle);
//todo also calculate sha?
return new Backup(pathToBundle.getFileName().toString(),
object, "s3",
@@ -82,15 +79,22 @@ public class S3BackupStrategy implements BackupStrategy {
}
@Override
@SneakyThrows
public void recoverBackup(Backup backup, Path restorePath) {
//todo http range
var resp = s3.getObject(b -> b.bucket(option.bucket()).key(backup.backupKey()));
var target = temporaryDownloadPath.resolve("s3_" + System.currentTimeMillis());
try (var fs = Files.newOutputStream(target, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
resp.transferTo(fs);
Bundle.unbundleFile(target, restorePath);
} finally {
Files.deleteIfExists(target);
}
}
@Override
@SneakyThrows
public void deleteBackup(Backup backup) {
minio.removeObject(RemoveObjectArgs.builder()
.bucket(backup.backupKey()).build());
s3.deleteObject(b -> b.bucket(option.bucket()).key(backup.backupKey()).build());
}
@Override
@@ -101,4 +105,9 @@ public class S3BackupStrategy implements BackupStrategy {
}
return true;
}
@Override
public void close() {
s3.close();
}
}