Implement the ConfigServlet
Most of the server relevant options can now be changed in the webinterface.
This commit is contained in:
parent
44fd340323
commit
28e6313d41
|
@ -1,5 +1,7 @@
|
||||||
package ctbrec.recorder.server;
|
package ctbrec.recorder.server;
|
||||||
|
|
||||||
|
import static javax.servlet.http.HttpServletResponse.*;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
@ -7,13 +9,19 @@ import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
import ctbrec.Hmac;
|
import ctbrec.Hmac;
|
||||||
|
|
||||||
public abstract class AbstractCtbrecServlet extends HttpServlet {
|
public abstract class AbstractCtbrecServlet extends HttpServlet {
|
||||||
|
|
||||||
boolean checkAuthentication(HttpServletRequest req, String body) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException {
|
private static final Logger LOG = LoggerFactory.getLogger(AbstractCtbrecServlet.class);
|
||||||
|
|
||||||
|
boolean checkAuthentication(HttpServletRequest req, String body) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
|
||||||
boolean authenticated = false;
|
boolean authenticated = false;
|
||||||
if(Config.getInstance().getSettings().key != null) {
|
if(Config.getInstance().getSettings().key != null) {
|
||||||
String reqParamHmac = req.getParameter("hmac");
|
String reqParamHmac = req.getParameter("hmac");
|
||||||
|
@ -48,4 +56,14 @@ public abstract class AbstractCtbrecServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
return body.toString().trim();
|
return body.toString().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendResponse(HttpServletResponse resp, int httpStatus, String message) {
|
||||||
|
try {
|
||||||
|
resp.setStatus(httpStatus);
|
||||||
|
resp.getWriter().print(message);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Couldn't write response", e);
|
||||||
|
resp.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ctbrec.recorder.server;
|
||||||
import static javax.servlet.http.HttpServletResponse.*;
|
import static javax.servlet.http.HttpServletResponse.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
@ -11,6 +12,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -25,10 +27,7 @@ public class ConfigServlet extends AbstractCtbrecServlet {
|
||||||
private Settings settings;
|
private Settings settings;
|
||||||
|
|
||||||
public enum DataType {
|
public enum DataType {
|
||||||
STRING,
|
STRING, BOOLEAN, INTEGER, LONG, DOUBLE
|
||||||
BOOLEAN,
|
|
||||||
INTEGER,
|
|
||||||
DOUBLE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigServlet(Config config) {
|
public ConfigServlet(Config config) {
|
||||||
|
@ -42,7 +41,7 @@ public class ConfigServlet extends AbstractCtbrecServlet {
|
||||||
boolean authenticated = checkAuthentication(req, body(req));
|
boolean authenticated = checkAuthentication(req, body(req));
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
resp.setStatus(SC_UNAUTHORIZED);
|
resp.setStatus(SC_UNAUTHORIZED);
|
||||||
String response = "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}";
|
String response = "HMAC does not match";
|
||||||
resp.getWriter().write(response);
|
resp.getWriter().write(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -52,13 +51,13 @@ public class ConfigServlet extends AbstractCtbrecServlet {
|
||||||
addParameter("ffmpegFileSuffix", "File Suffix", DataType.STRING, settings.ffmpegFileSuffix, json);
|
addParameter("ffmpegFileSuffix", "File Suffix", DataType.STRING, settings.ffmpegFileSuffix, json);
|
||||||
addParameter("ffmpegMergedDownloadArgs", "FFmpeg Parameters", DataType.STRING, settings.ffmpegMergedDownloadArgs, json);
|
addParameter("ffmpegMergedDownloadArgs", "FFmpeg Parameters", DataType.STRING, settings.ffmpegMergedDownloadArgs, json);
|
||||||
addParameter("httpPort", "HTTP port", DataType.INTEGER, settings.httpPort, json);
|
addParameter("httpPort", "HTTP port", DataType.INTEGER, settings.httpPort, json);
|
||||||
addParameter("httpsPort", "HTTPS port", DataType.INTEGER, settings.httpSecurePort, json);
|
addParameter("httpSecurePort", "HTTPS port", DataType.INTEGER, settings.httpSecurePort, json);
|
||||||
addParameter("httpUserAgent", "User-Agent", DataType.STRING, settings.httpUserAgent, json);
|
addParameter("httpUserAgent", "User-Agent", DataType.STRING, settings.httpUserAgent, json);
|
||||||
addParameter("httpUserAgentMobile", "Mobile User-Agent", DataType.STRING, settings.httpUserAgentMobile, json);
|
addParameter("httpUserAgentMobile", "Mobile User-Agent", DataType.STRING, settings.httpUserAgentMobile, json);
|
||||||
addParameter("generatePlaylist", "Generate Playlist", DataType.BOOLEAN, settings.generatePlaylist, json);
|
addParameter("generatePlaylist", "Generate Playlist", DataType.BOOLEAN, settings.generatePlaylist, json);
|
||||||
addParameter("maximumResolution", "Maximum Resolution", DataType.INTEGER, settings.maximumResolution, json);
|
addParameter("maximumResolution", "Maximum Resolution", DataType.INTEGER, settings.maximumResolution, json);
|
||||||
addParameter("minimumLengthInSeconds", "Minimum Length (secs)", DataType.INTEGER, settings.minimumLengthInSeconds, json);
|
addParameter("minimumLengthInSeconds", "Minimum Length (secs)", DataType.INTEGER, settings.minimumLengthInSeconds, json);
|
||||||
addParameter("minimumSpaceLeftInBytes", "Leave Space On Device (GiB)", DataType.INTEGER, settings.minimumSpaceLeftInBytes, json);
|
addParameter("minimumSpaceLeftInBytes", "Leave Space On Device (GiB)", DataType.LONG, settings.minimumSpaceLeftInBytes, json);
|
||||||
addParameter("onlineCheckIntervalInSecs", "Online Check Interval (secs)", DataType.INTEGER, settings.onlineCheckIntervalInSecs, json);
|
addParameter("onlineCheckIntervalInSecs", "Online Check Interval (secs)", DataType.INTEGER, settings.onlineCheckIntervalInSecs, json);
|
||||||
addParameter("postProcessing", "Post-Processing", DataType.STRING, settings.postProcessing, json);
|
addParameter("postProcessing", "Post-Processing", DataType.STRING, settings.postProcessing, json);
|
||||||
addParameter("postProcessingThreads", "Post-Processing Threads", DataType.INTEGER, settings.postProcessingThreads, json);
|
addParameter("postProcessingThreads", "Post-Processing Threads", DataType.INTEGER, settings.postProcessingThreads, json);
|
||||||
|
@ -89,25 +88,81 @@ public class ConfigServlet extends AbstractCtbrecServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
|
||||||
try {
|
try {
|
||||||
boolean authenticated = checkAuthentication(req, body(req));
|
resp.setContentType("application/json");
|
||||||
|
String body = body(req);
|
||||||
|
boolean authenticated = checkAuthentication(req, body);
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
resp.setStatus(SC_UNAUTHORIZED);
|
String response = "HMAC does not match";
|
||||||
String response = "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}";
|
sendResponse(resp, SC_UNAUTHORIZED, response);
|
||||||
resp.getWriter().write(response);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String postBody = body(req);
|
JSONArray json = new JSONArray(body);
|
||||||
LOG.info("POST {}", postBody);
|
changeConfig(json);
|
||||||
//JSONObject json = new JSONObject(postBody);
|
sendResponse(resp, SC_OK, "{\"status\": \"success\"}");
|
||||||
resp.setStatus(SC_OK);
|
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
|
||||||
resp.setContentType("application/json");
|
LOG.error("Couldn't authenticate request", e);
|
||||||
resp.getWriter().print("{\"status\": \"success\"}");
|
sendResponse(resp, SC_INTERNAL_SERVER_ERROR, "Couldn't authenticate request");
|
||||||
} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) {
|
} catch (JSONException e) {
|
||||||
resp.setStatus(SC_INTERNAL_SERVER_ERROR);
|
LOG.error("Couldn't parse request", e);
|
||||||
|
sendResponse(resp, SC_BAD_REQUEST, "JSON array expected");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Couldn't read request", e);
|
||||||
|
sendResponse(resp, SC_INTERNAL_SERVER_ERROR, "Couldn't read request");
|
||||||
|
} catch (ConfigWriteException e) {
|
||||||
|
LOG.error("Couldn't write config", e);
|
||||||
|
sendResponse(resp, SC_INTERNAL_SERVER_ERROR, "Couldn't write config: " + e.getLocalizedMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeConfig(JSONArray json) throws ConfigWriteException {
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < json.length(); i++) {
|
||||||
|
JSONObject property = json.getJSONObject(i);
|
||||||
|
String key = property.optString("key");
|
||||||
|
DataType type = DataType.valueOf(property.optString("type"));
|
||||||
|
Object value = property.get("value");
|
||||||
|
Object typeCorrectedValue = correctType(type, value);
|
||||||
|
LOG.debug("{}: {}", key, value);
|
||||||
|
|
||||||
|
Field field = Settings.class.getField(key);
|
||||||
|
field.set(settings, typeCorrectedValue);
|
||||||
|
}
|
||||||
|
config.save();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ConfigWriteException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object correctType(DataType type, Object value) {
|
||||||
|
Object corrected = value;
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case INTEGER:
|
||||||
|
corrected = Integer.parseInt(value.toString());
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
corrected = Long.parseLong(value.toString());
|
||||||
|
break;
|
||||||
|
case BOOLEAN:
|
||||||
|
corrected = Boolean.parseBoolean(value.toString());
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
corrected = Double.parseDouble(value.toString());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return corrected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ConfigWriteException extends Exception {
|
||||||
|
public ConfigWriteException(Exception e) {
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,12 @@ function loadConfig() {
|
||||||
}
|
}
|
||||||
}).done(function(data, textStatus, jqXHR) {
|
}).done(function(data, textStatus, jqXHR) {
|
||||||
if (textStatus === 'success') {
|
if (textStatus === 'success') {
|
||||||
|
while (observableSettingsArray().length > 0) {
|
||||||
|
observableSettingsArray.pop();
|
||||||
|
}
|
||||||
for (let i = 0; i < data.length; i++) {
|
for (let i = 0; i < data.length; i++) {
|
||||||
let param = data[i];
|
let param = data[i];
|
||||||
param.ko_value = ko.observable(param.value);
|
param.ko_value = ko.observable(param.value);
|
||||||
param.ko_value.subscribe(function(newValue) {
|
|
||||||
console.log("The person's new name is " + newValue);
|
|
||||||
});
|
|
||||||
observableSettingsArray.push(param);
|
observableSettingsArray.push(param);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -36,7 +36,6 @@ function loadConfig() {
|
||||||
function saveConfig() {
|
function saveConfig() {
|
||||||
try {
|
try {
|
||||||
let msg = JSON.stringify(observableSettingsArray());
|
let msg = JSON.stringify(observableSettingsArray());
|
||||||
console.log(msg);
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : 'POST',
|
type : 'POST',
|
||||||
url : '../config',
|
url : '../config',
|
||||||
|
@ -49,17 +48,21 @@ function saveConfig() {
|
||||||
data : msg
|
data : msg
|
||||||
}).done(function(data, textStatus, jqXHR) {
|
}).done(function(data, textStatus, jqXHR) {
|
||||||
if (textStatus === 'success') {
|
if (textStatus === 'success') {
|
||||||
//$.notify('Configuration saved', 'info');
|
loadConfig();
|
||||||
|
$.notify('Configuration saved. You might have to restart the server.', 'info');
|
||||||
} else {
|
} else {
|
||||||
if (console)
|
if (console)
|
||||||
console.log('request failed', data);
|
console.log('request failed', data);
|
||||||
|
$.notify('Error: ' + jqXHR.responseText, 'error');
|
||||||
}
|
}
|
||||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||||
if (console)
|
if (console)
|
||||||
console.log(jqXHR, textStatus, errorThrown);
|
console.log(jqXHR, textStatus, errorThrown);
|
||||||
|
$.notify(errorThrown + ': ' + jqXHR.responseText, 'error');
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (console)
|
if (console)
|
||||||
console.log('Unexpected error', e);
|
console.log('Unexpected error', e);
|
||||||
|
$.notify(errorThrown + ': ' + jqXHR.responseText, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -77,7 +77,7 @@
|
||||||
<div id="alert-container"></div>
|
<div id="alert-container"></div>
|
||||||
|
|
||||||
<div class="tab-content" id="myTabContent">
|
<div class="tab-content" id="myTabContent">
|
||||||
<section id="models" class="tab-pane fade active show" role="tabpanel" aria-labelledby="models-tab">
|
<section id="models" class="tab-pane fade" role="tabpanel" aria-labelledby="models-tab">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-10 mx-auto">
|
<div class="col-lg-10 mx-auto">
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section id="configuration" class="tab-pane fade" role="tabpanel" aria-labelledby="configuration-tab">
|
<section id="configuration" class="tab-pane fade active show" role="tabpanel" aria-labelledby="configuration-tab">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<form data-bind="submit: saveConfig">
|
<form data-bind="submit: saveConfig">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -470,6 +470,7 @@
|
||||||
loadConfig();
|
loadConfig();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
loadConfig();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue