forked from Qortal/qortal
Moved "directory" data uploads to new POST /site/upload API.
Directory uploads don't make much sense outside of website hosting, so it's best to make this API specific to that purpose.
This commit is contained in:
parent
f77ec1faf6
commit
39f5dce51c
@ -20,7 +20,6 @@ import org.qortal.settings.Settings;
|
|||||||
import org.qortal.storage.DataFile;
|
import org.qortal.storage.DataFile;
|
||||||
import org.qortal.storage.DataFile.ValidationResult;
|
import org.qortal.storage.DataFile.ValidationResult;
|
||||||
import org.qortal.utils.Base58;
|
import org.qortal.utils.Base58;
|
||||||
import org.qortal.utils.ZipUtils;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.*;
|
||||||
@ -71,7 +70,7 @@ public class DataResource {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||||
public String uploadDataAtPath(String path) {
|
public String uploadFileAtPath(String path) {
|
||||||
Security.checkApiCallAllowed(request);
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
// It's too dangerous to allow user-supplied filenames in weaker security contexts
|
// It's too dangerous to allow user-supplied filenames in weaker security contexts
|
||||||
@ -82,63 +81,12 @@ public class DataResource {
|
|||||||
|
|
||||||
// Check if a file or directory has been supplied
|
// Check if a file or directory has been supplied
|
||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
if (file.isFile()) {
|
if (!file.isFile()) {
|
||||||
return this.uploadFile(path);
|
LOGGER.info("Not a file: {}", path);
|
||||||
}
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
|
||||||
else if (file.isDirectory()) {
|
|
||||||
return this.uploadDirectory(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.info("No file or folder found at path: {}", path);
|
DataFile dataFile = new DataFile(path);
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
|
|
||||||
|
|
||||||
} catch (DataException e) {
|
|
||||||
LOGGER.error("Repository issue when uploading data", e);
|
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
LOGGER.error("Invalid upload data", e);
|
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String uploadFile(String filePath) {
|
|
||||||
DataFile dataFile = new DataFile(filePath);
|
|
||||||
ValidationResult validationResult = dataFile.isValid();
|
|
||||||
if (validationResult != DataFile.ValidationResult.OK) {
|
|
||||||
LOGGER.error("Invalid file: {}", validationResult);
|
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
|
||||||
}
|
|
||||||
LOGGER.info("Whole file digest: {}", dataFile.base58Digest());
|
|
||||||
|
|
||||||
int chunkCount = dataFile.split(DataFile.CHUNK_SIZE);
|
|
||||||
if (chunkCount > 0) {
|
|
||||||
LOGGER.info(String.format("Successfully split into %d chunk%s", chunkCount, (chunkCount == 1 ? "" : "s")));
|
|
||||||
return "true";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "false";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String uploadDirectory(String directoryPath) {
|
|
||||||
// Ensure temp folder exists
|
|
||||||
try {
|
|
||||||
Files.createDirectories(Paths.get("temp"));
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.error("Unable to create temp directory");
|
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Firstly zip up the directory
|
|
||||||
String outputFilePath = "temp/zipped.zip";
|
|
||||||
try {
|
|
||||||
ZipUtils.zip(directoryPath, outputFilePath, "data");
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.info("Unable to zip directory", e);
|
|
||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
DataFile dataFile = new DataFile(outputFilePath);
|
|
||||||
ValidationResult validationResult = dataFile.isValid();
|
ValidationResult validationResult = dataFile.isValid();
|
||||||
if (validationResult != DataFile.ValidationResult.OK) {
|
if (validationResult != DataFile.ValidationResult.OK) {
|
||||||
LOGGER.error("Invalid file: {}", validationResult);
|
LOGGER.error("Invalid file: {}", validationResult);
|
||||||
@ -153,16 +101,17 @@ public class DataResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return "false";
|
return "false";
|
||||||
}
|
|
||||||
finally {
|
} catch (DataException e) {
|
||||||
// Clean up by deleting the zipped file
|
LOGGER.error("Repository issue when uploading data", e);
|
||||||
File zippedFile = new File(outputFilePath);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||||
if (zippedFile.exists()) {
|
} catch (IllegalStateException e) {
|
||||||
zippedFile.delete();
|
LOGGER.error("Invalid upload data", e);
|
||||||
}
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("/file")
|
@Path("/file")
|
||||||
@Operation(
|
@Operation(
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package org.qortal.api.resource;
|
package org.qortal.api.resource;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -11,21 +15,118 @@ import java.nio.file.Paths;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.jsoup.Jsoup;
|
import org.jsoup.Jsoup;
|
||||||
import org.jsoup.nodes.Document;
|
import org.jsoup.nodes.Document;
|
||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
import org.jsoup.select.Elements;
|
import org.jsoup.select.Elements;
|
||||||
|
import org.qortal.api.ApiError;
|
||||||
|
import org.qortal.api.ApiExceptionFactory;
|
||||||
|
import org.qortal.api.Security;
|
||||||
|
import org.qortal.settings.Settings;
|
||||||
import org.qortal.storage.DataFile;
|
import org.qortal.storage.DataFile;
|
||||||
import org.qortal.utils.ZipUtils;
|
import org.qortal.utils.ZipUtils;
|
||||||
|
|
||||||
|
|
||||||
@Path("/site")
|
@Path("/site")
|
||||||
|
@Tag(name = "Website")
|
||||||
public class WebsiteResource {
|
public class WebsiteResource {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(WebsiteResource.class);
|
private static final Logger LOGGER = LogManager.getLogger(WebsiteResource.class);
|
||||||
|
|
||||||
|
@Context
|
||||||
|
HttpServletRequest request;
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/upload")
|
||||||
|
@Operation(
|
||||||
|
summary = "Build raw, unsigned, UPLOAD_DATA transaction, based on a user-supplied path to a static website",
|
||||||
|
requestBody = @RequestBody(
|
||||||
|
required = true,
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "string", example = "/Users/user/Documents/MyStaticWebsite"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
responses = {
|
||||||
|
@ApiResponse(
|
||||||
|
description = "raw, unsigned, UPLOAD_DATA transaction encoded in Base58",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = MediaType.TEXT_PLAIN,
|
||||||
|
schema = @Schema(
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public String hostWebsite(String directoryPath) {
|
||||||
|
Security.checkApiCallAllowed(request);
|
||||||
|
|
||||||
|
// It's too dangerous to allow user-supplied filenames in weaker security contexts
|
||||||
|
if (Settings.getInstance().isApiRestricted()) {
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.NON_PRODUCTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a file or directory has been supplied
|
||||||
|
File file = new File(directoryPath);
|
||||||
|
if (!file.isDirectory()) {
|
||||||
|
LOGGER.info("Not a directory: {}", directoryPath);
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure temp folder exists
|
||||||
|
try {
|
||||||
|
Files.createDirectories(Paths.get("temp"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("Unable to create temp directory");
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Firstly zip up the directory
|
||||||
|
String outputFilePath = "temp/zipped.zip";
|
||||||
|
try {
|
||||||
|
ZipUtils.zip(directoryPath, outputFilePath, "data");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.info("Unable to zip directory", e);
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DataFile dataFile = new DataFile(outputFilePath);
|
||||||
|
DataFile.ValidationResult validationResult = dataFile.isValid();
|
||||||
|
if (validationResult != DataFile.ValidationResult.OK) {
|
||||||
|
LOGGER.error("Invalid file: {}", validationResult);
|
||||||
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_DATA);
|
||||||
|
}
|
||||||
|
LOGGER.info("Whole file digest: {}", dataFile.base58Digest());
|
||||||
|
|
||||||
|
int chunkCount = dataFile.split(DataFile.CHUNK_SIZE);
|
||||||
|
if (chunkCount > 0) {
|
||||||
|
LOGGER.info(String.format("Successfully split into %d chunk%s", chunkCount, (chunkCount == 1 ? "" : "s")));
|
||||||
|
return "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "false";
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Clean up by deleting the zipped file
|
||||||
|
File zippedFile = new File(outputFilePath);
|
||||||
|
if (zippedFile.exists()) {
|
||||||
|
zippedFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{resource}")
|
@Path("{resource}")
|
||||||
public Response getResourceIndex(@PathParam("resource") String resourceId) {
|
public Response getResourceIndex(@PathParam("resource") String resourceId) {
|
||||||
|
Loading…
Reference in New Issue
Block a user