forked from Qortal/qortal
Use bindAddress from Settings for UI, API and P2P.
This commit is contained in:
parent
8a0d93f304
commit
e631e69fa1
@ -2,6 +2,9 @@ package org.qora.api;
|
|||||||
|
|
||||||
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
|
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
|
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
|
||||||
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
||||||
import org.eclipse.jetty.server.CustomRequestLog;
|
import org.eclipse.jetty.server.CustomRequestLog;
|
||||||
@ -23,93 +26,93 @@ import org.qora.settings.Settings;
|
|||||||
|
|
||||||
public class ApiService {
|
public class ApiService {
|
||||||
|
|
||||||
private final Server server;
|
|
||||||
private final ResourceConfig config;
|
|
||||||
|
|
||||||
public ApiService() {
|
|
||||||
config = new ResourceConfig();
|
|
||||||
config.packages("org.qora.api.resource");
|
|
||||||
config.register(OpenApiResource.class);
|
|
||||||
config.register(ApiDefinition.class);
|
|
||||||
config.register(AnnotationPostProcessor.class);
|
|
||||||
|
|
||||||
// Create RPC server
|
|
||||||
this.server = new Server(Settings.getInstance().getApiPort());
|
|
||||||
|
|
||||||
// Error handler
|
|
||||||
ErrorHandler errorHandler = new ApiErrorHandler();
|
|
||||||
this.server.setErrorHandler(errorHandler);
|
|
||||||
|
|
||||||
// Request logging
|
|
||||||
if (Settings.getInstance().isApiLoggingEnabled()) {
|
|
||||||
RequestLogWriter logWriter = new RequestLogWriter("API-requests.log");
|
|
||||||
logWriter.setAppend(true);
|
|
||||||
logWriter.setTimeZone("UTC");
|
|
||||||
RequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.EXTENDED_NCSA_FORMAT);
|
|
||||||
server.setRequestLog(requestLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IP address based access control
|
|
||||||
InetAccessHandler accessHandler = new InetAccessHandler();
|
|
||||||
for (String pattern : Settings.getInstance().getApiWhitelist()) {
|
|
||||||
accessHandler.include(pattern);
|
|
||||||
}
|
|
||||||
this.server.setHandler(accessHandler);
|
|
||||||
|
|
||||||
// URL rewriting
|
|
||||||
RewriteHandler rewriteHandler = new RewriteHandler();
|
|
||||||
accessHandler.setHandler(rewriteHandler);
|
|
||||||
|
|
||||||
// Context
|
|
||||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
|
||||||
context.setContextPath("/");
|
|
||||||
rewriteHandler.setHandler(context);
|
|
||||||
|
|
||||||
// Cross-origin resource sharing
|
|
||||||
FilterHolder corsFilterHolder = new FilterHolder(CrossOriginFilter.class);
|
|
||||||
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
|
|
||||||
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET, POST, DELETE");
|
|
||||||
corsFilterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
|
|
||||||
context.addFilter(corsFilterHolder, "/*", null);
|
|
||||||
|
|
||||||
// API servlet
|
|
||||||
ServletContainer container = new ServletContainer(config);
|
|
||||||
ServletHolder apiServlet = new ServletHolder(container);
|
|
||||||
apiServlet.setInitOrder(1);
|
|
||||||
context.addServlet(apiServlet, "/*");
|
|
||||||
|
|
||||||
// Swagger-UI static content
|
|
||||||
ClassLoader loader = this.getClass().getClassLoader();
|
|
||||||
ServletHolder swaggerUIServlet = new ServletHolder("static-swagger-ui", DefaultServlet.class);
|
|
||||||
swaggerUIServlet.setInitParameter("resourceBase", loader.getResource("resources/swagger-ui/").toString());
|
|
||||||
swaggerUIServlet.setInitParameter("dirAllowed", "true");
|
|
||||||
swaggerUIServlet.setInitParameter("pathInfoOnly", "true");
|
|
||||||
context.addServlet(swaggerUIServlet, "/api-documentation/*");
|
|
||||||
|
|
||||||
rewriteHandler.addRule(new RedirectPatternRule("", "/api-documentation/")); // redirect to Swagger UI start page
|
|
||||||
rewriteHandler.addRule(new RedirectPatternRule("/api-documentation", "/api-documentation/")); // redirect to Swagger UI start page
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: replace singleton pattern by dependency injection?
|
|
||||||
private static ApiService instance;
|
private static ApiService instance;
|
||||||
|
|
||||||
|
private final ResourceConfig config;
|
||||||
|
private Server server;
|
||||||
|
|
||||||
|
private ApiService() {
|
||||||
|
this.config = new ResourceConfig();
|
||||||
|
this.config.packages("org.qora.api.resource");
|
||||||
|
this.config.register(OpenApiResource.class);
|
||||||
|
this.config.register(ApiDefinition.class);
|
||||||
|
this.config.register(AnnotationPostProcessor.class);
|
||||||
|
}
|
||||||
|
|
||||||
public static ApiService getInstance() {
|
public static ApiService getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null)
|
||||||
instance = new ApiService();
|
instance = new ApiService();
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<Class<?>> getResources() {
|
public Iterable<Class<?>> getResources() {
|
||||||
// return resources;
|
// return resources;
|
||||||
return config.getClasses();
|
return this.config.getClasses();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
try {
|
try {
|
||||||
|
// Create API server
|
||||||
|
InetAddress bindAddr = InetAddress.getByName(Settings.getInstance().getBindAddress());
|
||||||
|
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, Settings.getInstance().getApiPort());
|
||||||
|
this.server = new Server(endpoint);
|
||||||
|
|
||||||
|
// Error handler
|
||||||
|
ErrorHandler errorHandler = new ApiErrorHandler();
|
||||||
|
this.server.setErrorHandler(errorHandler);
|
||||||
|
|
||||||
|
// Request logging
|
||||||
|
if (Settings.getInstance().isApiLoggingEnabled()) {
|
||||||
|
RequestLogWriter logWriter = new RequestLogWriter("API-requests.log");
|
||||||
|
logWriter.setAppend(true);
|
||||||
|
logWriter.setTimeZone("UTC");
|
||||||
|
RequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.EXTENDED_NCSA_FORMAT);
|
||||||
|
this.server.setRequestLog(requestLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP address based access control
|
||||||
|
InetAccessHandler accessHandler = new InetAccessHandler();
|
||||||
|
for (String pattern : Settings.getInstance().getApiWhitelist()) {
|
||||||
|
accessHandler.include(pattern);
|
||||||
|
}
|
||||||
|
this.server.setHandler(accessHandler);
|
||||||
|
|
||||||
|
// URL rewriting
|
||||||
|
RewriteHandler rewriteHandler = new RewriteHandler();
|
||||||
|
accessHandler.setHandler(rewriteHandler);
|
||||||
|
|
||||||
|
// Context
|
||||||
|
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
||||||
|
context.setContextPath("/");
|
||||||
|
rewriteHandler.setHandler(context);
|
||||||
|
|
||||||
|
// Cross-origin resource sharing
|
||||||
|
FilterHolder corsFilterHolder = new FilterHolder(CrossOriginFilter.class);
|
||||||
|
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
|
||||||
|
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET, POST, DELETE");
|
||||||
|
corsFilterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
|
||||||
|
context.addFilter(corsFilterHolder, "/*", null);
|
||||||
|
|
||||||
|
// API servlet
|
||||||
|
ServletContainer container = new ServletContainer(this.config);
|
||||||
|
ServletHolder apiServlet = new ServletHolder(container);
|
||||||
|
apiServlet.setInitOrder(1);
|
||||||
|
context.addServlet(apiServlet, "/*");
|
||||||
|
|
||||||
|
// Swagger-UI static content
|
||||||
|
ClassLoader loader = this.getClass().getClassLoader();
|
||||||
|
ServletHolder swaggerUIServlet = new ServletHolder("static-swagger-ui", DefaultServlet.class);
|
||||||
|
swaggerUIServlet.setInitParameter("resourceBase", loader.getResource("resources/swagger-ui/").toString());
|
||||||
|
swaggerUIServlet.setInitParameter("dirAllowed", "true");
|
||||||
|
swaggerUIServlet.setInitParameter("pathInfoOnly", "true");
|
||||||
|
context.addServlet(swaggerUIServlet, "/api-documentation/*");
|
||||||
|
|
||||||
|
rewriteHandler.addRule(new RedirectPatternRule("", "/api-documentation/")); // redirect to Swagger UI start page
|
||||||
|
rewriteHandler.addRule(new RedirectPatternRule("/api-documentation", "/api-documentation/")); // redirect to Swagger UI start page
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
server.start();
|
this.server.start();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Failed to start
|
// Failed to start
|
||||||
throw new RuntimeException("Failed to start API", e);
|
throw new RuntimeException("Failed to start API", e);
|
||||||
@ -119,9 +122,12 @@ public class ApiService {
|
|||||||
public void stop() {
|
public void stop() {
|
||||||
try {
|
try {
|
||||||
// Stop server
|
// Stop server
|
||||||
server.stop();
|
this.server.stop();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Failed to stop
|
// Failed to stop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.server = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,9 @@ public class Settings {
|
|||||||
// Settings, and other config files
|
// Settings, and other config files
|
||||||
private String userPath;
|
private String userPath;
|
||||||
|
|
||||||
|
// Common to all networking (UI/API/P2P)
|
||||||
|
private String bindAddress = "::"; // Use IPv6 wildcard to listen on all local addresses
|
||||||
|
|
||||||
// Node management UI
|
// Node management UI
|
||||||
private boolean uiEnabled = true;
|
private boolean uiEnabled = true;
|
||||||
private Integer uiPort;
|
private Integer uiPort;
|
||||||
@ -71,7 +74,6 @@ public class Settings {
|
|||||||
// Peer-to-peer related
|
// Peer-to-peer related
|
||||||
private boolean isTestNet = false;
|
private boolean isTestNet = false;
|
||||||
private Integer listenPort;
|
private Integer listenPort;
|
||||||
private String bindAddress = "::"; // Use IPv6 wildcard to listen on all local addresses
|
|
||||||
/** Minimum number of peers to allow block generation / synchronization. */
|
/** Minimum number of peers to allow block generation / synchronization. */
|
||||||
private int minBlockchainPeers = 3;
|
private int minBlockchainPeers = 3;
|
||||||
/** Target number of outbound connections to peers we should make. */
|
/** Target number of outbound connections to peers we should make. */
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package org.qora.ui;
|
package org.qora.ui;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
|
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
|
||||||
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
@ -14,69 +17,70 @@ import org.qora.settings.Settings;
|
|||||||
public class UiService {
|
public class UiService {
|
||||||
|
|
||||||
public static final String DOWNLOADS_RESOURCE_PATH = "node-ui-downloads";
|
public static final String DOWNLOADS_RESOURCE_PATH = "node-ui-downloads";
|
||||||
|
|
||||||
private final Server server;
|
|
||||||
|
|
||||||
public UiService() {
|
|
||||||
// Create node management UI server
|
|
||||||
this.server = new Server(Settings.getInstance().getUiPort());
|
|
||||||
|
|
||||||
// IP address based access control
|
|
||||||
InetAccessHandler accessHandler = new InetAccessHandler();
|
|
||||||
for (String pattern : Settings.getInstance().getUiWhitelist()) {
|
|
||||||
accessHandler.include(pattern);
|
|
||||||
}
|
|
||||||
this.server.setHandler(accessHandler);
|
|
||||||
|
|
||||||
// URL rewriting
|
|
||||||
RewriteHandler rewriteHandler = new RewriteHandler();
|
|
||||||
accessHandler.setHandler(rewriteHandler);
|
|
||||||
|
|
||||||
// Context
|
|
||||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
|
||||||
context.setContextPath("/");
|
|
||||||
rewriteHandler.setHandler(context);
|
|
||||||
|
|
||||||
// Cross-origin resource sharing
|
|
||||||
FilterHolder corsFilterHolder = new FilterHolder(CrossOriginFilter.class);
|
|
||||||
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
|
|
||||||
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET, POST, DELETE");
|
|
||||||
corsFilterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
|
|
||||||
context.addFilter(corsFilterHolder, "/*", null);
|
|
||||||
|
|
||||||
ClassLoader loader = this.getClass().getClassLoader();
|
|
||||||
|
|
||||||
// Node management UI download servlet
|
|
||||||
ServletHolder uiDownloadServlet = new ServletHolder("node-ui-download", new DefaultServlet(new DownloadResourceService()));
|
|
||||||
uiDownloadServlet.setInitParameter("resourceBase", loader.getResource(DOWNLOADS_RESOURCE_PATH + "/").toString());
|
|
||||||
uiDownloadServlet.setInitParameter("dirAllowed", "true");
|
|
||||||
uiDownloadServlet.setInitParameter("pathInfoOnly", "true");
|
|
||||||
context.addServlet(uiDownloadServlet, "/downloads/*");
|
|
||||||
|
|
||||||
// Node management UI static content servlet
|
|
||||||
ServletHolder uiServlet = new ServletHolder("node-management-ui", DefaultServlet.class);
|
|
||||||
uiServlet.setInitParameter("resourceBase", loader.getResource("node-management-ui/").toString());
|
|
||||||
uiServlet.setInitParameter("dirAllowed", "true");
|
|
||||||
uiServlet.setInitParameter("pathInfoOnly", "true");
|
|
||||||
context.addServlet(uiServlet, "/*");
|
|
||||||
|
|
||||||
rewriteHandler.addRule(new RedirectPatternRule("", "/index.html")); // node management UI start page
|
|
||||||
}
|
|
||||||
|
|
||||||
private static UiService instance;
|
private static UiService instance;
|
||||||
|
|
||||||
|
private Server server;
|
||||||
|
|
||||||
|
private UiService() {
|
||||||
|
}
|
||||||
|
|
||||||
public static UiService getInstance() {
|
public static UiService getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null)
|
||||||
instance = new UiService();
|
instance = new UiService();
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
try {
|
try {
|
||||||
|
// Create node management UI server
|
||||||
|
InetAddress bindAddr = InetAddress.getByName(Settings.getInstance().getBindAddress());
|
||||||
|
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, Settings.getInstance().getUiPort());
|
||||||
|
this.server = new Server(endpoint);
|
||||||
|
|
||||||
|
// IP address based access control
|
||||||
|
InetAccessHandler accessHandler = new InetAccessHandler();
|
||||||
|
for (String pattern : Settings.getInstance().getUiWhitelist()) {
|
||||||
|
accessHandler.include(pattern);
|
||||||
|
}
|
||||||
|
this.server.setHandler(accessHandler);
|
||||||
|
|
||||||
|
// URL rewriting
|
||||||
|
RewriteHandler rewriteHandler = new RewriteHandler();
|
||||||
|
accessHandler.setHandler(rewriteHandler);
|
||||||
|
|
||||||
|
// Context
|
||||||
|
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
||||||
|
context.setContextPath("/");
|
||||||
|
rewriteHandler.setHandler(context);
|
||||||
|
|
||||||
|
// Cross-origin resource sharing
|
||||||
|
FilterHolder corsFilterHolder = new FilterHolder(CrossOriginFilter.class);
|
||||||
|
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
|
||||||
|
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET, POST, DELETE");
|
||||||
|
corsFilterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
|
||||||
|
context.addFilter(corsFilterHolder, "/*", null);
|
||||||
|
|
||||||
|
ClassLoader loader = this.getClass().getClassLoader();
|
||||||
|
|
||||||
|
// Node management UI download servlet
|
||||||
|
ServletHolder uiDownloadServlet = new ServletHolder("node-ui-download", new DefaultServlet(new DownloadResourceService()));
|
||||||
|
uiDownloadServlet.setInitParameter("resourceBase", loader.getResource(DOWNLOADS_RESOURCE_PATH + "/").toString());
|
||||||
|
uiDownloadServlet.setInitParameter("dirAllowed", "true");
|
||||||
|
uiDownloadServlet.setInitParameter("pathInfoOnly", "true");
|
||||||
|
context.addServlet(uiDownloadServlet, "/downloads/*");
|
||||||
|
|
||||||
|
// Node management UI static content servlet
|
||||||
|
ServletHolder uiServlet = new ServletHolder("node-management-ui", DefaultServlet.class);
|
||||||
|
uiServlet.setInitParameter("resourceBase", loader.getResource("node-management-ui/").toString());
|
||||||
|
uiServlet.setInitParameter("dirAllowed", "true");
|
||||||
|
uiServlet.setInitParameter("pathInfoOnly", "true");
|
||||||
|
context.addServlet(uiServlet, "/*");
|
||||||
|
|
||||||
|
rewriteHandler.addRule(new RedirectPatternRule("", "/index.html")); // node management UI start page
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
server.start();
|
this.server.start();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Failed to start
|
// Failed to start
|
||||||
throw new RuntimeException("Failed to start node management UI", e);
|
throw new RuntimeException("Failed to start node management UI", e);
|
||||||
@ -86,10 +90,12 @@ public class UiService {
|
|||||||
public void stop() {
|
public void stop() {
|
||||||
try {
|
try {
|
||||||
// Stop server
|
// Stop server
|
||||||
server.stop();
|
this.server.stop();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Failed to stop
|
// Failed to stop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.server = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user