Browse Source

Upgraded rendering to support identifiers, as well as single file resources.

This allows any QDN resource (e.g. an IMAGE) to be linked to from a website/app and then rendered on screen. It isn't yet supported in gateway or domain map mode, as these need some more thought.
qdn-on-chain-data
CalDescent 2 years ago
parent
commit
37b20aac66
  1. 8
      src/main/java/org/qortal/api/domainmap/resource/DomainMapResource.java
  2. 14
      src/main/java/org/qortal/api/gateway/resource/GatewayResource.java
  3. 19
      src/main/java/org/qortal/api/restricted/resource/RenderResource.java
  4. 20
      src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java
  5. 3
      src/main/resources/loading/index.html
  6. 17
      src/main/resources/q-apps/q-apps.js

8
src/main/java/org/qortal/api/domainmap/resource/DomainMapResource.java

@ -42,15 +42,15 @@ public class DomainMapResource {
// Build synchronously, so that we don't need to make the summary API endpoints available over
// the domain map server. This means that there will be no loading screen, but this is potentially
// preferred in this situation anyway (e.g. to avoid confusing search engine robots).
return this.get(domainMap.get(request.getServerName()), ResourceIdType.NAME, Service.WEBSITE, inPath, null, "", false, false);
return this.get(domainMap.get(request.getServerName()), ResourceIdType.NAME, Service.WEBSITE, null, inPath, null, "", false, false);
}
return ArbitraryDataRenderer.getResponse(response, 404, "Error 404: File Not Found");
}
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String inPath,
String secret58, String prefix, boolean usePrefix, boolean async) {
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
String inPath, String secret58, String prefix, boolean usePrefix, boolean async) {
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, inPath,
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath,
secret58, prefix, usePrefix, async, "domainMap", request, response, context);
return renderer.render();
}

14
src/main/java/org/qortal/api/gateway/resource/GatewayResource.java

@ -82,7 +82,7 @@ public class GatewayResource {
@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, inPath, null, "", true, true);
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, inPath, null, "", true, true);
}
@GET
@ -91,7 +91,7 @@ public class GatewayResource {
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, "", true, true);
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, "/", null, "", true, true);
}
@ -103,7 +103,7 @@ public class GatewayResource {
@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, inPath, null, "/site", true, true);
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, inPath, null, "/site", true, true);
}
@GET
@ -111,14 +111,14 @@ public class GatewayResource {
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, "/site", true, true);
return this.get(name, ResourceIdType.NAME, Service.WEBSITE, null, "/", null, "/site", true, true);
}
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String inPath,
String secret58, String prefix, boolean usePrefix, boolean async) {
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
String inPath, String secret58, String prefix, boolean usePrefix, boolean async) {
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, inPath,
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath,
secret58, prefix, usePrefix, async, "gateway", request, response, context);
return renderer.render();
}

19
src/main/java/org/qortal/api/restricted/resource/RenderResource.java

@ -146,7 +146,7 @@ public class RenderResource {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null);
return this.get(signature, ResourceIdType.SIGNATURE, null, "/", null, "/render/signature", true, true, theme);
return this.get(signature, ResourceIdType.SIGNATURE, null, null, "/", null, "/render/signature", true, true, theme);
}
@GET
@ -157,7 +157,7 @@ public class RenderResource {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, signature, Service.WEBSITE, null);
return this.get(signature, ResourceIdType.SIGNATURE, null, inPath,null, "/render/signature", true, true, theme);
return this.get(signature, ResourceIdType.SIGNATURE, null, null, inPath,null, "/render/signature", true, true, theme);
}
@GET
@ -168,7 +168,7 @@ public class RenderResource {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null);
return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, "/", secret58, "/render/hash", true, false, theme);
return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, null, "/", secret58, "/render/hash", true, false, theme);
}
@GET
@ -180,7 +180,7 @@ public class RenderResource {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, hash58, Service.WEBSITE, null);
return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, inPath, secret58, "/render/hash", true, false, theme);
return this.get(hash58, ResourceIdType.FILE_HASH, Service.WEBSITE, null, inPath, secret58, "/render/hash", true, false, theme);
}
@GET
@ -189,12 +189,13 @@ public class RenderResource {
public HttpServletResponse getPathByName(@PathParam("service") Service service,
@PathParam("name") String name,
@PathParam("path") String inPath,
@QueryParam("identifier") String identifier,
@QueryParam("theme") String theme) {
if (!Settings.getInstance().isQDNAuthBypassEnabled())
Security.requirePriorAuthorization(request, name, service, null);
String prefix = String.format("/render/%s", service);
return this.get(name, ResourceIdType.NAME, service, inPath, null, prefix, true, true, theme);
return this.get(name, ResourceIdType.NAME, service, identifier, inPath, null, prefix, true, true, theme);
}
@GET
@ -207,15 +208,15 @@ public class RenderResource {
Security.requirePriorAuthorization(request, name, service, null);
String prefix = String.format("/render/%s", service);
return this.get(name, ResourceIdType.NAME, service, "/", null, prefix, true, true, theme);
return this.get(name, ResourceIdType.NAME, service, null, "/", null, prefix, true, true, theme);
}
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String inPath,
String secret58, String prefix, boolean usePrefix, boolean async, String theme) {
private HttpServletResponse get(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
String inPath, String secret58, String prefix, boolean usePrefix, boolean async, String theme) {
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, inPath,
ArbitraryDataRenderer renderer = new ArbitraryDataRenderer(resourceId, resourceIdType, service, identifier, inPath,
secret58, prefix, usePrefix, async, "render", request, response, context);
if (theme != null) {

20
src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java

@ -2,6 +2,7 @@ package org.qortal.arbitrary;
import com.google.common.io.Resources;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.api.HTMLParser;
@ -34,6 +35,7 @@ public class ArbitraryDataRenderer {
private final String resourceId;
private final ResourceIdType resourceIdType;
private final Service service;
private final String identifier;
private String theme = "light";
private String inPath;
private final String secret58;
@ -45,13 +47,14 @@ public class ArbitraryDataRenderer {
private final HttpServletResponse response;
private final ServletContext context;
public ArbitraryDataRenderer(String resourceId, ResourceIdType resourceIdType, Service service, String inPath,
String secret58, String prefix, boolean usePrefix, boolean async, String qdnContext,
public ArbitraryDataRenderer(String resourceId, ResourceIdType resourceIdType, Service service, String identifier,
String inPath, String secret58, String prefix, boolean usePrefix, boolean async, String qdnContext,
HttpServletRequest request, HttpServletResponse response, ServletContext context) {
this.resourceId = resourceId;
this.resourceIdType = resourceIdType;
this.service = service;
this.identifier = identifier != null ? identifier : "default";
this.inPath = inPath;
this.secret58 = secret58;
this.prefix = prefix;
@ -73,14 +76,14 @@ public class ArbitraryDataRenderer {
return ArbitraryDataRenderer.getResponse(response, 500, "QDN is disabled in settings");
}
ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(resourceId, resourceIdType, service, null);
ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(resourceId, resourceIdType, service, identifier);
arbitraryDataReader.setSecret58(secret58); // Optional, used for loading encrypted file hashes only
try {
if (!arbitraryDataReader.isCachedDataAvailable()) {
// If async is requested, show a loading screen whilst build is in progress
if (async) {
arbitraryDataReader.loadAsynchronously(false, 10);
return this.getLoadingResponse(service, resourceId, theme);
return this.getLoadingResponse(service, resourceId, identifier, theme);
}
// Otherwise, loop until we have data
@ -113,6 +116,12 @@ public class ArbitraryDataRenderer {
}
String unzippedPath = path.toString();
String[] files = ArrayUtils.removeElement(new File(unzippedPath).list(), ".qortal");
if (files.length == 1) {
// This is a single file resource
inPath = files[0];
}
try {
String filename = this.getFilename(unzippedPath, inPath);
String filePath = Paths.get(unzippedPath, filename).toString();
@ -174,7 +183,7 @@ public class ArbitraryDataRenderer {
return userPath;
}
private HttpServletResponse getLoadingResponse(Service service, String name, String theme) {
private HttpServletResponse getLoadingResponse(Service service, String name, String identifier, String theme) {
String responseString = "";
URL url = Resources.getResource("loading/index.html");
try {
@ -183,6 +192,7 @@ public class ArbitraryDataRenderer {
// Replace vars
responseString = responseString.replace("%%SERVICE%%", service.toString());
responseString = responseString.replace("%%NAME%%", name);
responseString = responseString.replace("%%IDENTIFIER%%", identifier);
responseString = responseString.replace("%%THEME%%", theme);
} catch (IOException e) {

3
src/main/resources/loading/index.html

@ -43,8 +43,9 @@
var host = location.protocol + '//' + location.host;
var service = "%%SERVICE%%"
var name = "%%NAME%%"
var identifier = "%%IDENTIFIER%%"
var url = host + '/arbitrary/resource/status/' + service + '/' + name + '?build=true';
var url = host + '/arbitrary/resource/status/' + service + '/' + name + '/' + identifier + '?build=true';
var textStatus = "Loading...";
var textProgress = "";
var retryInterval = 2500;

17
src/main/resources/q-apps/q-apps.js

@ -81,6 +81,8 @@ window.addEventListener("message", (event) => {
url = "/" + data.name;
}
if (data.path != null) url = url.concat((data.path.startsWith("/") ? "" : "/") + data.path);
if (data.identifier != null) url = url.concat("?identifier=" + data.identifier);
window.location = url;
response = true;
break;
@ -228,11 +230,26 @@ function interceptClickEvent(e) {
let parts = href.split("/");
const service = parts[0].toUpperCase(); parts.shift();
const name = parts[0]; parts.shift();
let identifier;
if (parts.length > 0) {
identifier = parts[0]; // Do not shift yet
// Check if a resource exists with this service, name and identifier combination
const url = "/arbitrary/resource/status/" + service + "/" + name + "/" + identifier;
const response = httpGet(url);
const responseObj = JSON.parse(response);
if (responseObj.totalChunkCount > 0) {
// Identifier exists, so don't include it in the path
parts.shift();
}
}
const path = parts.join("/");
qortalRequest({
action: "LINK_TO_QDN_RESOURCE",
service: service,
name: name,
identifier: identifier,
path: path
});
}

Loading…
Cancel
Save