From 04f248bcdd3386a7e4546141facbfa7e3664b77a Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sat, 28 Jan 2023 17:56:24 +0000 Subject: [PATCH] Upgraded gateway to support service and identifier. The URL used to access the gateway is now interpreted, and the most appropriate resource is served. This means it can be used in different ways to retrieve any type of content from QDN. For example: /QortalDemo /QortalDemo/minting-leveling/index.html /WEBSITE/QortalDemo /WEBSITE/QortalDemo/minting-leveling/index.html /APP/QortalDemo /THUMBNAIL/QortalDemo/qortal_avatar /QCHAT_IMAGE/birtydasterd/qchat_BfBeCz /ARBITRARY_DATA/PirateChainWallet/LiteWalletJNI/coinparams.json --- .../api/gateway/resource/GatewayResource.java | 94 ++++++++++++------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java b/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java index 354631c0..091a0f19 100644 --- a/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java +++ b/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java @@ -16,6 +16,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.*; import javax.ws.rs.core.Context; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; @Path("/") @@ -76,50 +79,75 @@ public class GatewayResource { @GET - @Path("{name}/{path:.*}") + @Path("{path:.*}") @SecurityRequirement(name = "apiKey") - public HttpServletResponse getPathByName(@PathParam("name") String name, - @PathParam("path") String inPath) { + public HttpServletResponse getPath(@PathParam("path") String inPath) { // Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data Security.disallowLoopbackRequests(request); - return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, inPath, null, "", true, true); + return this.parsePath(inPath, "gateway", null, "", true, true); } - @GET - @Path("{name}") - @SecurityRequirement(name = "apiKey") - public HttpServletResponse getIndexByName(@PathParam("name") String name) { - // Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data - Security.disallowLoopbackRequests(request); - return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, "/", null, "", true, true); - } + + private HttpServletResponse parsePath(String inPath, String qdnContext, String secret58, String prefix, boolean usePrefix, boolean async) { + if (inPath == null || inPath.equals("")) { + // Assume not a real file + return ArbitraryDataRenderer.getResponse(response, 404, "Error 404: File Not Found"); + } - // Optional /site alternative for backwards support + // Default service is WEBSITE + Service service = Service.WEBSITE; + String name = null; + String identifier = null; + String outPath = ""; - @GET - @Path("/site/{name}/{path:.*}") - public HttpServletResponse getSitePathByName(@PathParam("name") String name, - @PathParam("path") String inPath) { - // Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data - Security.disallowLoopbackRequests(request); - return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, inPath, null, "/site", true, true); - } + if (!inPath.contains("/")) { + // Assume entire inPath is a registered name + name = inPath; + } + else { + // Parse the path to determine what we need to load + List parts = new LinkedList<>(Arrays.asList(inPath.split("/"))); - @GET - @Path("/site/{name}") - public HttpServletResponse getSiteIndexByName(@PathParam("name") String name) { - // Block requests from localhost, to prevent websites/apps from running javascript that fetches unvetted data - Security.disallowLoopbackRequests(request); - return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, "/", null, "/site", true, true); - } + // Check if the first element is a service + try { + Service parsedService = Service.valueOf(parts.get(0).toUpperCase()); + if (parsedService != null) { + // First element matches a service, so we can assume it is one + service = parsedService; + parts.remove(0); + } + } catch (IllegalArgumentException e) { + // Not a service + } - - private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier, - String inPath, String secret58, String prefix, boolean usePrefix, boolean async) { + if (parts.isEmpty()) { + // We need more than just a service + return ArbitraryDataRenderer.getResponse(response, 404, "Error 404: File Not Found"); + } + + // Service is removed, so assume first element is now a registered name + name = parts.get(0); + parts.remove(0); + + if (!parts.isEmpty()) { + // Name is removed, so check if the first element is now an identifier + ArbitraryResourceStatus status = this.getStatus(service, name, parts.get(0), false); + if (status.getTotalChunkCount() > 0) { + // Matched service, name and identifier combination - so assume this is an identifier and can be removed + identifier = parts.get(0); + parts.remove(0); + } + } + + if (!parts.isEmpty()) { + // outPath can be built by combining any remaining parts + outPath = String.join("/", parts); + } + } - ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath, - secret58, prefix, usePrefix, async, "gateway", request, response, context); + ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(name, ResourceIdType.NAME, service, identifier, outPath, + secret58, prefix, usePrefix, async, qdnContext, request, response, context); return renderer.render(); }