Partially reimplement private file API
This commit is contained in:
@@ -4,7 +4,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import io.smallrye.config.ConfigMapping;
|
||||
|
||||
@ConfigMapping(prefix = "lalafin")
|
||||
@ConfigMapping(prefix = "lalafin.core")
|
||||
public interface LalafinConfiguration {
|
||||
|
||||
public String base_url();
|
||||
|
||||
@@ -3,7 +3,7 @@ package sh.rhiobet.lalafin.advent;
|
||||
import java.util.List;
|
||||
import io.smallrye.config.ConfigMapping;
|
||||
|
||||
@ConfigMapping(prefix = "advent")
|
||||
@ConfigMapping(prefix = "lalafin.advent")
|
||||
public interface AdventConfiguration {
|
||||
public List<AdventEvent> events();
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package sh.rhiobet.lalafin.advent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.decorator.Decorator;
|
||||
import jakarta.decorator.Delegate;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import sh.rhiobet.lalafin.api.configuration.FileApiConfiguration;
|
||||
import sh.rhiobet.lalafin.path.Path;
|
||||
import sh.rhiobet.lalafin.path.PathAccessor;
|
||||
import sh.rhiobet.lalafin.path.PathPlugin;
|
||||
|
||||
@Decorator
|
||||
public class AdventThumbnailPathPlugin extends PathAccessor implements PathPlugin {
|
||||
@Inject
|
||||
FileApiConfiguration fileApiConfiguration;
|
||||
|
||||
@Inject
|
||||
@Named("file/thumbnail")
|
||||
@Delegate
|
||||
PathPlugin thumbnailPathPlugin;
|
||||
|
||||
@Inject
|
||||
AdventAccessService adventAccessService;
|
||||
|
||||
@Override
|
||||
public Optional<String> resolveURI(Path path) {
|
||||
if (this.adventAccessService.checkAccess(path)) {
|
||||
return this.thumbnailPathPlugin.resolveURI(path);
|
||||
} else {
|
||||
Optional<java.nio.file.Path> thumbnailAbsolutePath = getDefaultThumbnailPath(path);
|
||||
|
||||
if (thumbnailAbsolutePath.isPresent()) {
|
||||
java.nio.file.Path rootFolderPath = Paths.get(this.fileApiConfiguration.directory());
|
||||
return Optional.of(
|
||||
"/" + rootFolderPath.resolve("file").relativize(thumbnailAbsolutePath.get())
|
||||
.toString());
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream(Path path) throws IOException {
|
||||
if (this.adventAccessService.checkAccess(path)) {
|
||||
return this.thumbnailPathPlugin.getOutputStream(path);
|
||||
} else {
|
||||
throw new IOException("You don't yet have access to this resource.");
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<java.nio.file.Path> getDefaultThumbnailPath(Path path) {
|
||||
try {
|
||||
return Files.list(getAbsolutePath(path).getParent().resolve(".thumbnails"))
|
||||
.filter(f -> f.getFileName().toString().matches("^00(\\.[^.]+)*$"))
|
||||
.findFirst();
|
||||
} catch (IOException ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import io.smallrye.config.ConfigMapping;
|
||||
|
||||
@ConfigMapping(prefix = "file")
|
||||
@ConfigMapping(prefix = "lalafin.file")
|
||||
public interface FileConfiguration {
|
||||
public String directory();
|
||||
public Optional<List<String>> ignored();
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package sh.rhiobet.lalafin.file;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import sh.rhiobet.lalafin.LalafinConfiguration;
|
||||
import sh.rhiobet.lalafin.api.advent.AdventAccessService;
|
||||
import sh.rhiobet.lalafin.api.internal.redis.FileTokenProvider;
|
||||
import sh.rhiobet.lalafin.api.model.FileInfo;
|
||||
import sh.rhiobet.lalafin.api.model.FileInfoBase;
|
||||
import sh.rhiobet.lalafin.api.model.FolderInfo;
|
||||
import sh.rhiobet.lalafin.path.Path;
|
||||
import sh.rhiobet.lalafin.path.PathPlugin;
|
||||
|
||||
@ApplicationScoped
|
||||
public class FileMetadataService {
|
||||
@Inject
|
||||
LalafinConfiguration lalafinConfiguration;
|
||||
|
||||
@Inject
|
||||
FileConfiguration fileConfiguration;
|
||||
|
||||
@Inject
|
||||
ThumbnailService thumbnailService;
|
||||
|
||||
@Inject
|
||||
@Named("file/thumbnail")
|
||||
PathPlugin thumbnailPathPlugin;
|
||||
|
||||
@Inject
|
||||
AdventAccessService adventAccessService;
|
||||
|
||||
public FileInfoBase getInfo(Path filePath, FileTokenProvider fileTokenProvider) {
|
||||
if (filePath.exists()) {
|
||||
Optional<String> thumbUrl = this.thumbnailPathPlugin.resolveURI(filePath);
|
||||
|
||||
if (filePath.canHaveChildren()) {
|
||||
FolderInfo folderInfo = new FolderInfo(
|
||||
filePath.getFilename(),
|
||||
thumbUrl.isPresent() ? "/file" + thumbUrl.get() : "",
|
||||
"/file" + filePath.getURI(),
|
||||
""
|
||||
);
|
||||
|
||||
try {
|
||||
for (Path child : filePath.getChildren()) {
|
||||
if (child.getFilename().startsWith(".")) {
|
||||
continue;
|
||||
}
|
||||
if (fileConfiguration.ignored().isPresent()) {
|
||||
for (String ignoreString : fileConfiguration.ignored().get()) {
|
||||
if (child.getFilename().endsWith(ignoreString)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Optional<String> childThumbUrl = this.thumbnailPathPlugin.resolveURI(child);
|
||||
|
||||
FileInfoBase contentInfo;
|
||||
if (child.canHaveChildren()) {
|
||||
contentInfo = new FolderInfo(child.getFilename(), "/file" + child.getURI());
|
||||
} else {
|
||||
contentInfo = new FileInfo(child.getFilename(), "/file" + child.getURI());
|
||||
}
|
||||
|
||||
if (childThumbUrl.isPresent()) {
|
||||
contentInfo.thumbnailUrl = "/file" + childThumbUrl.get();
|
||||
}
|
||||
|
||||
folderInfo.content.add(contentInfo);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return folderInfo;
|
||||
} else {
|
||||
FileInfo fileInfo = new FileInfo(
|
||||
filePath.getFilename(),
|
||||
thumbUrl.isPresent() ? "/file" + thumbUrl.get() : "",
|
||||
"/file" + filePath.getURI(),
|
||||
""
|
||||
);
|
||||
|
||||
return fileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package sh.rhiobet.lalafin.thumbnail;
|
||||
package sh.rhiobet.lalafin.file;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -10,7 +10,6 @@ import java.util.regex.Pattern;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import sh.rhiobet.lalafin.api.configuration.FileApiConfiguration;
|
||||
import sh.rhiobet.lalafin.path.FileSystemPath;
|
||||
import sh.rhiobet.lalafin.path.Path;
|
||||
import sh.rhiobet.lalafin.path.PathAccessor;
|
||||
@@ -18,10 +17,10 @@ import sh.rhiobet.lalafin.path.PathPlugin;
|
||||
import sh.rhiobet.lalafin.path.ZipEntryPath;
|
||||
|
||||
@ApplicationScoped
|
||||
@Named("thumbnail")
|
||||
public class ThumbnailPathPlugin extends PathAccessor implements PathPlugin {
|
||||
@Named("file/thumbnail")
|
||||
public class FileThumbnailPathPlugin extends PathAccessor implements PathPlugin {
|
||||
@Inject
|
||||
FileApiConfiguration fileApiConfiguration;
|
||||
FileConfiguration fileConfiguration;
|
||||
|
||||
@Override
|
||||
public Optional<String> resolveURI(Path path) {
|
||||
@@ -30,9 +29,10 @@ public class ThumbnailPathPlugin extends PathAccessor implements PathPlugin {
|
||||
Optional<java.nio.file.Path> thumbnailAbsolutePath = getThumbnailPath(path);
|
||||
|
||||
if (thumbnailAbsolutePath.isPresent()) {
|
||||
java.nio.file.Path rootFolderPath = Paths.get(this.fileApiConfiguration.directory());
|
||||
return Optional.of(rootFolderPath.resolve("file").relativize(thumbnailAbsolutePath.get())
|
||||
.toUri().getRawPath());
|
||||
java.nio.file.Path rootFolderPath = Paths.get(this.fileConfiguration.directory());
|
||||
return Optional.of(
|
||||
"/" + rootFolderPath.resolve("file").relativize(thumbnailAbsolutePath.get())
|
||||
.toString());
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package sh.rhiobet.lalafin.file.rest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.enterprise.inject.Instance;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.PathSegment;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import sh.rhiobet.lalafin.access.AccessService;
|
||||
import sh.rhiobet.lalafin.api.model.FileInfoBase;
|
||||
import sh.rhiobet.lalafin.file.FileMetadataService;
|
||||
import sh.rhiobet.lalafin.path.InvalidPathException;
|
||||
import sh.rhiobet.lalafin.path.PathFactory;
|
||||
|
||||
@Authenticated
|
||||
@Path("/api/v1/private/file")
|
||||
public class FilePrivateAPI {
|
||||
@Inject
|
||||
FileMetadataService fileMetadataService;
|
||||
|
||||
@Inject
|
||||
Instance<AccessService> accessServices;
|
||||
|
||||
@Inject
|
||||
PathFactory pathFactory;
|
||||
|
||||
@GET
|
||||
@Path("/")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getFileInfo() {
|
||||
return this.getFileInfo(new ArrayList<>());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{names: .+}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getFileInfo(List<PathSegment> names) {
|
||||
sh.rhiobet.lalafin.path.Path path;
|
||||
try {
|
||||
path = this.pathFactory.toPath(names);
|
||||
} catch (InvalidPathException ignored) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||
}
|
||||
|
||||
if (accessServices.stream().anyMatch((a -> !a.checkAccess(path)))) {
|
||||
return Response.status(Response.Status.FORBIDDEN).build();
|
||||
}
|
||||
|
||||
FileInfoBase fileInfo = this.fileMetadataService.getInfo(path, null);
|
||||
|
||||
if (fileInfo == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
return Response.ok(fileInfo).build();
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,9 @@ public final class FileSystemPath implements Path {
|
||||
|
||||
@Override
|
||||
public String getURI() {
|
||||
if (this.segments.isEmpty()) {
|
||||
return "/";
|
||||
}
|
||||
return this.segments.stream()
|
||||
.map(s -> "/" + Encode.encodePathSegment(s))
|
||||
.reduce("", String::concat);
|
||||
@@ -38,7 +41,7 @@ public final class FileSystemPath implements Path {
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return this.segments.getLast();
|
||||
return this.segments.isEmpty() ? "/" : this.segments.getLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,7 +65,7 @@ public final class FileSystemPath implements Path {
|
||||
|
||||
@Override
|
||||
public List<Path> getChildren() throws IOException {
|
||||
if (this.segments.getLast().endsWith(".zip")) {
|
||||
if (!this.segments.isEmpty() && this.segments.getLast().endsWith(".zip")) {
|
||||
try (ZipFile zipFile = new ZipFile(this.absolutePath.toFile())) {
|
||||
return zipFile.stream()
|
||||
.map(e -> (Path) new ZipEntryPath(this, e.getName()))
|
||||
|
||||
Reference in New Issue
Block a user