add imageuploader interface, server uploads expire
This commit is contained in:
parent
7ed8a3c7e3
commit
347b64cbc8
10 changed files with 275 additions and 111 deletions
|
@ -1,24 +1,27 @@
|
||||||
package eu.e99.pixelchat.fabric;
|
package eu.e99.pixelchat.fabric;
|
||||||
|
|
||||||
import eu.e99.pixelchat.fabric.image.ImageUploader;
|
import eu.e99.pixelchat.fabric.image.ImageUploader;
|
||||||
|
import eu.e99.pixelchat.fabric.image.ImageUploads;
|
||||||
|
import eu.e99.pixelchat.fabric.image.PixelChatUploader;
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
public class PixelChat implements ClientModInitializer {
|
public class PixelChat implements ClientModInitializer {
|
||||||
|
|
||||||
public static final String NAMESPACE = "pixelchat";
|
public static final String NAMESPACE = "pixelchat";
|
||||||
public static final Logger LOGGER = LoggerFactory.getLogger(NAMESPACE);
|
public static final Logger LOGGER = LoggerFactory.getLogger(NAMESPACE);
|
||||||
public static ImageUploader UPLOADER;
|
public static ImageUploads UPLOADS;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
UPLOADER = new ImageUploader(
|
UPLOADS = new ImageUploads(
|
||||||
MinecraftClient.getInstance(),
|
MinecraftClient.getInstance(),
|
||||||
URI.create("http://localhost:3000/")
|
new ImageUploader[]{
|
||||||
|
new PixelChatUploader("http", "localhost", 3001),
|
||||||
|
new PixelChatUploader("http", "localhost", 3000)
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +1,8 @@
|
||||||
package eu.e99.pixelchat.fabric.image;
|
package eu.e99.pixelchat.fabric.image;
|
||||||
|
|
||||||
import eu.e99.pixelchat.fabric.PixelChat;
|
|
||||||
import eu.e99.pixelchat.fabric.duck.BossBars;
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.client.gui.hud.ClientBossBar;
|
|
||||||
import net.minecraft.entity.boss.BossBar;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class ImageUploader {
|
public interface ImageUploader {
|
||||||
|
|
||||||
private final MinecraftClient minecraft;
|
String upload(Path path) throws Throwable;
|
||||||
private final URI endpoint;
|
|
||||||
private final HttpClient client;
|
|
||||||
|
|
||||||
public ImageUploader(MinecraftClient minecraft, URI endpoint) {
|
|
||||||
this.minecraft = minecraft;
|
|
||||||
this.endpoint = endpoint;
|
|
||||||
this.client = HttpClient.newHttpClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void uploadImage(Path path) {
|
|
||||||
Thread.ofVirtual().start(() -> this.innerUploadImage(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void innerUploadImage(Path path) {
|
|
||||||
BossBars bossBars = (BossBars) minecraft.inGameHud.getBossBarHud();
|
|
||||||
ClientBossBar bossBar = new ClientBossBar(
|
|
||||||
UUID.randomUUID(),
|
|
||||||
Text.translatable("pixelchat.uploading"),
|
|
||||||
0f,
|
|
||||||
BossBar.Color.BLUE,
|
|
||||||
BossBar.Style.PROGRESS,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.minecraft.send(() -> bossBars.add(bossBar.getUuid(), bossBar));
|
|
||||||
|
|
||||||
// TODO: Report some sort of progress?
|
|
||||||
HttpRequest req = HttpRequest.newBuilder()
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofFile(path))
|
|
||||||
.uri(this.endpoint)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
HttpResponse<String> res = this.client.send(
|
|
||||||
req,
|
|
||||||
HttpResponse.BodyHandlers.ofString()
|
|
||||||
);
|
|
||||||
if (res.statusCode() != 201) {
|
|
||||||
throw new RuntimeException(String.format("Failed to upload, expected status 201, got %d", res.statusCode()));
|
|
||||||
}
|
|
||||||
|
|
||||||
String downloadUrl = res.body();
|
|
||||||
|
|
||||||
this.minecraft.send(() -> {
|
|
||||||
// TODO: Are we actually playing?
|
|
||||||
if (this.minecraft.player == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bossBar.setName(Text.translatable("pixelchat.upload_completed"));
|
|
||||||
bossBar.setPercent(1.0f);
|
|
||||||
bossBar.setColor(BossBar.Color.GREEN);
|
|
||||||
|
|
||||||
this.minecraft.inGameHud.getChatHud().addToMessageHistory(downloadUrl);
|
|
||||||
this.minecraft.player.networkHandler.sendChatMessage(downloadUrl);
|
|
||||||
});
|
|
||||||
} catch (Throwable e) {
|
|
||||||
PixelChat.LOGGER.error("Upload failed", e);
|
|
||||||
|
|
||||||
this.minecraft.send(() -> {
|
|
||||||
bossBar.setName(Text.translatable("pixelchat.upload_failed"));
|
|
||||||
bossBar.setPercent(1.0f);
|
|
||||||
bossBar.setColor(BossBar.Color.RED);
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
Thread.sleep(5000);
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
this.minecraft.send(() -> bossBars.remove(bossBar.getUuid()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
133
mod-fabric/src/eu/e99/pixelchat/fabric/image/ImageUploads.java
Normal file
133
mod-fabric/src/eu/e99/pixelchat/fabric/image/ImageUploads.java
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
package eu.e99.pixelchat.fabric.image;
|
||||||
|
|
||||||
|
import eu.e99.pixelchat.fabric.PixelChat;
|
||||||
|
import eu.e99.pixelchat.fabric.duck.BossBars;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.gui.hud.ClientBossBar;
|
||||||
|
import net.minecraft.entity.boss.BossBar;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ImageUploads {
|
||||||
|
|
||||||
|
private final MinecraftClient client;
|
||||||
|
private final ImageUploader[] uploaders;
|
||||||
|
|
||||||
|
public ImageUploads(MinecraftClient client, ImageUploader[] uploaders) {
|
||||||
|
this.client = client;
|
||||||
|
this.uploaders = uploaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void uploadImage(Path path) {
|
||||||
|
Thread.ofVirtual().start(() -> this.innerUploadImage(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void innerUploadImage(Path path) {
|
||||||
|
BossBars bossBars = (BossBars) client.inGameHud.getBossBarHud();
|
||||||
|
ClientBossBar bossBar = new ClientBossBar(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
Text.translatable("pixelchat.uploading"),
|
||||||
|
0f,
|
||||||
|
BossBar.Color.BLUE,
|
||||||
|
BossBar.Style.PROGRESS,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
this.client.send(() -> bossBars.add(bossBar.getUuid(), bossBar));
|
||||||
|
|
||||||
|
String url = null;
|
||||||
|
for (ImageUploader uploader : this.uploaders) {
|
||||||
|
try {
|
||||||
|
url = uploader.upload(path);
|
||||||
|
break;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PixelChat.LOGGER.warn("Failed to upload", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntelliJ complains otherwise
|
||||||
|
String finalUrl = url;
|
||||||
|
this.client.send(() -> {
|
||||||
|
if (finalUrl == null) {
|
||||||
|
PixelChat.LOGGER.error("No uploader succeeded");
|
||||||
|
bossBar.setName(Text.translatable("pixelchat.upload_failed"));
|
||||||
|
bossBar.setPercent(1.0f);
|
||||||
|
bossBar.setColor(BossBar.Color.RED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bossBar.setName(Text.translatable("pixelchat.upload_completed"));
|
||||||
|
bossBar.setPercent(1.0f);
|
||||||
|
bossBar.setColor(BossBar.Color.GREEN);
|
||||||
|
|
||||||
|
// TODO: Are we actually playing?
|
||||||
|
if (this.client.player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client.inGameHud.getChatHud().addToMessageHistory(finalUrl);
|
||||||
|
this.client.player.networkHandler.sendChatMessage(finalUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client.send(() -> bossBars.remove(bossBar.getUuid()));
|
||||||
|
|
||||||
|
/*try {
|
||||||
|
this.minecraft.send(() -> bossBars.add(bossBar.getUuid(), bossBar));
|
||||||
|
|
||||||
|
// TODO: Report some sort of progress?
|
||||||
|
HttpRequest req = HttpRequest.newBuilder()
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofFile(path))
|
||||||
|
.uri(this.endpoint)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> res = this.client.send(
|
||||||
|
req,
|
||||||
|
HttpResponse.BodyHandlers.ofString()
|
||||||
|
);
|
||||||
|
if (res.statusCode() != 201) {
|
||||||
|
throw new RuntimeException(String.format("Failed to upload, expected status 201, got %d", res.statusCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.minecraft.send(() -> {
|
||||||
|
// TODO: Are we actually playing?
|
||||||
|
if (this.minecraft.player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bossBar.setName(Text.translatable("pixelchat.upload_completed"));
|
||||||
|
bossBar.setPercent(1.0f);
|
||||||
|
bossBar.setColor(BossBar.Color.GREEN);
|
||||||
|
|
||||||
|
String id = res.body();
|
||||||
|
String url = String.format("%s", id);
|
||||||
|
|
||||||
|
this.minecraft.inGameHud.getChatHud().addToMessageHistory(url);
|
||||||
|
this.minecraft.player.networkHandler.sendChatMessage(url);
|
||||||
|
});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PixelChat.LOGGER.error("Upload failed", e);
|
||||||
|
|
||||||
|
this.minecraft.send(() -> {
|
||||||
|
bossBar.setName(Text.translatable("pixelchat.upload_failed"));
|
||||||
|
bossBar.setPercent(1.0f);
|
||||||
|
bossBar.setColor(BossBar.Color.RED);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
this.minecraft.send(() -> bossBars.remove(bossBar.getUuid()));
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package eu.e99.pixelchat.fabric.image;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class PixelChatUploader implements ImageUploader {
|
||||||
|
|
||||||
|
private final HttpClient client;
|
||||||
|
private final String scheme;
|
||||||
|
private final String host;
|
||||||
|
private final int port;
|
||||||
|
|
||||||
|
public PixelChatUploader(String scheme, String host, int port) {
|
||||||
|
this.client = HttpClient.newHttpClient();
|
||||||
|
this.scheme = scheme;
|
||||||
|
this.host = host;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String upload(Path path) throws Throwable {
|
||||||
|
String uri = this.url("");
|
||||||
|
HttpRequest req = HttpRequest.newBuilder()
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofFile(path))
|
||||||
|
.uri(URI.create(uri))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> res = this.client.send(
|
||||||
|
req,
|
||||||
|
HttpResponse.BodyHandlers.ofString()
|
||||||
|
);
|
||||||
|
if (res.statusCode() != 201) {
|
||||||
|
throw new RuntimeException(String.format("Failed to upload, expected status 201, got %d", res.statusCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
String id = res.body();
|
||||||
|
return this.url(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String url(String path) {
|
||||||
|
return String.format("%s://%s:%d/%s", this.scheme, this.host, this.port, path);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ public abstract class ChatScreenMixin extends Screen {
|
||||||
@Override
|
@Override
|
||||||
public void onFilesDropped(List<Path> paths) {
|
public void onFilesDropped(List<Path> paths) {
|
||||||
for (Path path : paths) {
|
for (Path path : paths) {
|
||||||
PixelChat.UPLOADER.uploadImage(path);
|
PixelChat.UPLOADS.uploadImage(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
0
server/Dockerfile
Normal file
0
server/Dockerfile
Normal file
|
@ -3,24 +3,26 @@ package eu.e99.pixelchat.server;
|
||||||
import eu.e99.pixelchat.server.storage.Storage;
|
import eu.e99.pixelchat.server.storage.Storage;
|
||||||
import io.javalin.http.Context;
|
import io.javalin.http.Context;
|
||||||
import io.javalin.http.HttpStatus;
|
import io.javalin.http.HttpStatus;
|
||||||
import org.apache.commons.imaging.AbstractImageParser;
|
|
||||||
import org.apache.commons.imaging.ImageFormat;
|
|
||||||
import org.apache.commons.imaging.ImageFormats;
|
import org.apache.commons.imaging.ImageFormats;
|
||||||
import org.apache.commons.imaging.Imaging;
|
import org.apache.commons.imaging.Imaging;
|
||||||
import org.apache.commons.imaging.internal.ImageParserFactory;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.TemporalAmount;
|
||||||
|
|
||||||
public class ImageHandler {
|
public class ImageHandler {
|
||||||
|
|
||||||
|
private final Logger logger;
|
||||||
private final Storage storage;
|
private final Storage storage;
|
||||||
|
private final TemporalAmount expireTime;
|
||||||
|
|
||||||
public ImageHandler(Storage storage) {
|
public ImageHandler(Logger logger, Storage storage, TemporalAmount expireTime) {
|
||||||
|
this.logger = logger;
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
|
this.expireTime = expireTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void uploadImage(Context ctx) throws IOException {
|
public void uploadImage(Context ctx) throws IOException {
|
||||||
|
@ -28,11 +30,12 @@ public class ImageHandler {
|
||||||
BufferedImage image = Imaging.getBufferedImage(body);
|
BufferedImage image = Imaging.getBufferedImage(body);
|
||||||
|
|
||||||
byte[] png = Imaging.writeImageToBytes(image, ImageFormats.PNG);
|
byte[] png = Imaging.writeImageToBytes(image, ImageFormats.PNG);
|
||||||
String id = storage.put(png);
|
Instant expiresAt = Instant.now().plus(this.expireTime);
|
||||||
|
|
||||||
String url = String.format("http://localhost:3000/%s", id);
|
String id = storage.put(png, expiresAt);
|
||||||
|
logger.info("Stored {} bytes as {}", png.length, id);
|
||||||
|
|
||||||
ctx.status(HttpStatus.CREATED).result(url);
|
ctx.status(HttpStatus.CREATED).result(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void downloadImage(Context ctx) throws IOException {
|
public void downloadImage(Context ctx) throws IOException {
|
||||||
|
|
|
@ -3,16 +3,48 @@ package eu.e99.pixelchat.server;
|
||||||
import eu.e99.pixelchat.server.storage.MemoryStorage;
|
import eu.e99.pixelchat.server.storage.MemoryStorage;
|
||||||
import eu.e99.pixelchat.server.storage.Storage;
|
import eu.e99.pixelchat.server.storage.Storage;
|
||||||
import io.javalin.Javalin;
|
import io.javalin.Javalin;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.temporal.TemporalAmount;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Storage storage = new MemoryStorage();
|
run(
|
||||||
ImageHandler handler = new ImageHandler(storage);
|
LoggerFactory.getLogger("pixelchat"),
|
||||||
|
new MemoryStorage(),
|
||||||
|
Duration.ofSeconds(5),
|
||||||
|
Duration.ofMinutes(2),
|
||||||
|
3000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void run(Logger logger, Storage storage, Duration clearTime, TemporalAmount expireTime, int port) {
|
||||||
|
ImageHandler handler = new ImageHandler(
|
||||||
|
logger,
|
||||||
|
storage,
|
||||||
|
expireTime
|
||||||
|
);
|
||||||
|
|
||||||
|
Thread.ofVirtual().start(() -> {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(clearTime);
|
||||||
|
storage.clearExpired();
|
||||||
|
|
||||||
|
logger.info("Cleared expired images");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.error("Failed to clear expired images", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info("PixelChat starting on port {}", port);
|
||||||
Javalin app = Javalin.create()
|
Javalin app = Javalin.create()
|
||||||
.post("/", handler::uploadImage)
|
.post("/", handler::uploadImage)
|
||||||
.get("/{id}", handler::downloadImage)
|
.get("/{id}", handler::downloadImage)
|
||||||
.start(3000);
|
.start(port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package eu.e99.pixelchat.server.storage;
|
package eu.e99.pixelchat.server.storage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class MemoryStorage implements Storage {
|
public class MemoryStorage implements Storage {
|
||||||
|
|
||||||
private final Map<String, byte[]> storage;
|
private final Map<String, Entry> storage;
|
||||||
private int counter;
|
private int counter;
|
||||||
|
|
||||||
public MemoryStorage() {
|
public MemoryStorage() {
|
||||||
|
@ -14,14 +16,42 @@ public class MemoryStorage implements Storage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized String put(byte[] bytes) {
|
public synchronized String put(byte[] bytes, Instant expiresAt) {
|
||||||
String id = Integer.toHexString(this.counter++);
|
String id = Integer.toHexString(this.counter++);
|
||||||
this.storage.put(id, bytes);
|
this.storage.put(id, new Entry(bytes, expiresAt));
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized byte[] get(String id) {
|
public synchronized byte[] get(String id) {
|
||||||
return this.storage.get(id);
|
Entry entry = this.storage.get(id);
|
||||||
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void clearExpired() throws IOException {
|
||||||
|
Instant instant = Instant.now();
|
||||||
|
|
||||||
|
for (Map.Entry<String, Entry> mapEntry : this.storage.entrySet()) {
|
||||||
|
String id = mapEntry.getKey();
|
||||||
|
Entry entry = mapEntry.getValue();
|
||||||
|
|
||||||
|
if (instant.isBefore(entry.expiresAt)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf("Cleared %s%n", id);
|
||||||
|
|
||||||
|
this.storage.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Entry(
|
||||||
|
byte[] value,
|
||||||
|
Instant expiresAt
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package eu.e99.pixelchat.server.storage;
|
package eu.e99.pixelchat.server.storage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
public interface Storage {
|
public interface Storage {
|
||||||
|
|
||||||
String put(byte[] bytes) throws IOException;
|
String put(byte[] bytes, Instant expiresAt) throws IOException;
|
||||||
|
|
||||||
byte[] get(String id) throws IOException;
|
byte[] get(String id) throws IOException;
|
||||||
|
|
||||||
|
void clearExpired() throws IOException;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue