1
0
Fork 1
mirror of https://gitlab.com/mangadex-pub/mangadex_at_home.git synced 2024-01-19 02:48:37 +00:00

Squash a lot of things

This commit is contained in:
carbotaniuman 2020-06-11 16:06:57 -05:00
parent 5928e81907
commit 83663a786b
20 changed files with 1897 additions and 30 deletions

View file

@ -1,15 +1,15 @@
package mdnet.base;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import mdnet.base.settings.ClientSettings;
import mdnet.cache.DiskLruCache;
import mdnet.webui.WebConsole;
import org.http4k.server.Http4kServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@ -171,36 +171,63 @@ public class MangaDexClient {
+ ") initializing\n");
System.out.println("Copyright (c) 2020, MangaDex Network");
String file = "settings.json";
if (args.length == 1) {
file = args[0];
} else if (args.length != 0) {
MangaDexClient.dieWithError("Expected one argument: path to config file, or nothing");
}
Gson gson = new GsonBuilder().setPrettyPrinting().create();
ClientSettings settings;
try {
String file = "settings.json";
if (args.length == 1) {
file = args[0];
} else if (args.length != 0) {
MangaDexClient.dieWithError("Expected one argument: path to config file, or nothing");
settings = gson.fromJson(new FileReader(file), ClientSettings.class);
} catch (FileNotFoundException ignored) {
settings = new ClientSettings();
LOGGER.warn("Settings file {} not found, generating file", file);
try (FileWriter writer = new FileWriter(file)) {
writer.write(gson.toJson(settings));
} catch (IOException e) {
MangaDexClient.dieWithError(e);
}
}
ClientSettings settings = new Gson().fromJson(new FileReader(file), ClientSettings.class);
if (!ClientSettings.isSecretValid(settings.getClientSecret()))
MangaDexClient.dieWithError("Config Error: API Secret is invalid, must be 52 alphanumeric characters");
if (!ClientSettings.isSecretValid(settings.getClientSecret()))
MangaDexClient.dieWithError("Config Error: API Secret is invalid, must be 52 alphanumeric characters");
if (settings.getClientPort() == 0) {
MangaDexClient.dieWithError("Config Error: Invalid port number");
}
if (settings.getClientPort() == 0) {
MangaDexClient.dieWithError("Config Error: Invalid port number");
}
if (settings.getMaxCacheSizeMib() < 1024) {
MangaDexClient.dieWithError("Config Error: Invalid max cache size, must be >= 1024 MiB (1GiB)");
}
if (settings.getMaxCacheSizeMib() < 1024) {
MangaDexClient.dieWithError("Config Error: Invalid max cache size, must be >= 1024 MiB (1GiB)");
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Client settings loaded: {}", settings);
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Client settings loaded: {}", settings);
}
MangaDexClient client = new MangaDexClient(settings);
Runtime.getRuntime().addShutdownHook(new Thread(client::shutdown));
client.runLoop();
MangaDexClient client = new MangaDexClient(settings);
Runtime.getRuntime().addShutdownHook(new Thread(client::shutdown));
client.runLoop();
} catch (FileNotFoundException e) {
MangaDexClient.dieWithError(e);
if (settings.getWebSettings() != null) {
// java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
// System.setOut(new java.io.PrintStream(out));
// TODO: system.out redirect
ClientSettings finalSettings = settings;
new Thread(() -> {
WebConsole webConsole = new WebConsole(finalSettings.getWebSettings().getClientWebsocketPort()) {
@Override
protected void parseMessage(String message) {
System.out.println(message);
// TODO: something happens here
// the message should be formatted in json
}
};
// TODO: webConsole.sendMessage(t,m) whenever system.out is written to
}).start();
}
}

View file

@ -3,6 +3,7 @@ package mdnet.base;
import kong.unirest.HttpResponse;
import kong.unirest.Unirest;
import kong.unirest.json.JSONObject;
import mdnet.base.settings.ClientSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -1,4 +1,4 @@
package mdnet.base;
package mdnet.base.settings;
import com.google.gson.annotations.SerializedName;
@ -18,15 +18,28 @@ public final class ClientSettings {
private final String clientSecret;
@SerializedName("threads")
private final int threads;
@SerializedName("web_settings")
private final WebSettings webSettings;
public ClientSettings() {
this.maxCacheSizeMib = 20480;
this.maxBandwidthMibPerHour = 0;
this.maxBurstRateKibPerSecond = 0;
this.clientPort = 1200;
this.clientSecret = "PASTE-YOUR-SECRET-HERE";
this.threads = 32;
this.webSettings = new WebSettings();
}
public ClientSettings(long maxCacheSizeMib, long maxBandwidthMibPerHour, long maxBurstRateKibPerSecond,
int clientPort, String clientSecret, int threads) {
int clientPort, String clientSecret, int threads, WebSettings webSettings) {
this.maxCacheSizeMib = maxCacheSizeMib;
this.maxBandwidthMibPerHour = maxBandwidthMibPerHour;
this.maxBurstRateKibPerSecond = maxBurstRateKibPerSecond;
this.clientPort = clientPort;
this.clientSecret = Objects.requireNonNull(clientSecret);
this.threads = threads;
this.webSettings = webSettings;
}
public long getMaxCacheSizeMib() {
@ -48,9 +61,12 @@ public final class ClientSettings {
public String getClientSecret() {
return clientSecret;
}
public WebSettings getWebSettings() {
return webSettings;
}
public int getThreads() {
return (threads > 0) ? threads : 16;
return threads;
}
@Override

View file

@ -0,0 +1,25 @@
package mdnet.base.settings;
import com.google.gson.annotations.SerializedName;
public final class WebSettings {
@SerializedName("client_websocket_port")
private final int clientWebsocketPort;
public WebSettings() {
this.clientWebsocketPort = 33333;
}
public WebSettings(int clientWebsocketPort) {
this.clientWebsocketPort = clientWebsocketPort;
}
public int getClientWebsocketPort() {
return clientWebsocketPort;
}
@Override
public String toString() {
return "WebSettings{" + "clientWebsocketPort=" + clientWebsocketPort + '}';
}
}

View file

@ -0,0 +1,107 @@
package mdnet.webui;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicReference;
import mdnet.base.Statistics;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class WebConsole extends WebSocketServer {
private final static Logger LOGGER = LoggerFactory.getLogger(WebConsole.class);
public WebConsole(int port) {
super(new InetSocketAddress(port));
}
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
LOGGER.info("Webclient {} connected", conn);
}
@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
LOGGER.info("Webclient {} disconnected: {} ", conn, reason);
}
@Override
public void onMessage(WebSocket conn, String message) {
parseMessage(message);
}
@Override
public void onMessage(WebSocket conn, ByteBuffer message) {
// parseMessage(message.array().toString());
}
@Override
public void onError(WebSocket conn, Exception ex) {
ex.printStackTrace();
if (conn != null) {
// some errors like port binding failed may not be assignable to a specific
// websocket
}
}
@Override
public void onStart() {
LOGGER.info("Listening for connections on port: {}", this.getPort());
setConnectionLostTimeout(0);
setConnectionLostTimeout(100);
}
protected abstract void parseMessage(String message);
// void parseCommand(String x) {
// switch (x) {
// case "help":
// this.broadcast(formatMessage("command", "Available commands:"));
// this.broadcast(formatMessage("command", "you"));
// this.broadcast(formatMessage("command", "are"));
// this.broadcast(formatMessage("command", "big"));
// this.broadcast(formatMessage("command", "gay"));
// break;
// case "stop":
// this.broadcast(formatMessage("command", "Mangadex Client has shut down,
// shutting down web client now"));
// return;
// default:
// this.broadcast(formatMessage("command", "That command was not recognized"));
// this.broadcast(formatMessage("command", "Try help for a list of available
// commands"));
// break;
// }
// }
public void sendMessage(String type, Object message) {
// JSONObject out = new JSONObject();
// switch (type) {
// case "command" :
// out.put("type", "command");
// out.put("data", message.toString());
// break;
// case "stats" :
// out.put("type", "stats");
// AtomicReference<Statistics> temp = (AtomicReference<Statistics>) message;
// out.put("hits", temp.get().getCacheHits());
// out.put("misses", temp.get().getCacheMisses());
// out.put("bytes_sent", temp.get().getBytesSent());
// out.put("req_served", temp.get().getRequestsServed());
// out.put("dataval", "empty");
// out.put("dataval", "empty");
// out.put("dataval", "empty");
// break;
// case "auth" :
// break;
// default :
// out.put("type", "command");
// out.put("data", message.toString());
// break;
// }
// broadcast(out.toString());
}
}

View file

@ -1,6 +1,7 @@
/* ktlint-disable no-wildcard-imports */
package mdnet.base
import mdnet.base.settings.ClientSettings
import mdnet.cache.DiskLruCache
import org.apache.http.client.config.CookieSpecs
import org.apache.http.client.config.RequestConfig
@ -18,8 +19,10 @@ import org.http4k.filter.CachingFilters
import org.http4k.filter.MaxAgeTtl
import org.http4k.filter.ServerFilters
import org.http4k.lens.Path
import org.http4k.routing.ResourceLoader
import org.http4k.routing.bind
import org.http4k.routing.routes
import org.http4k.routing.singlePageApp
import org.http4k.server.Http4kServer
import org.http4k.server.asServer
import org.slf4j.LoggerFactory
@ -211,7 +214,9 @@ fun getServer(cache: DiskLruCache, serverSettings: ServerSettings, clientSetting
"/data/{chapterHash}/{fileName}" bind Method.GET to app(false),
"/data-saver/{chapterHash}/{fileName}" bind Method.GET to app(true),
"/{token}/data/{chapterHash}/{fileName}" bind Method.GET to app(false),
"/{token}/data-saver/{chapterHash}/{fileName}" bind Method.GET to app(true)
"/{token}/data-saver/{chapterHash}/{fileName}" bind Method.GET to app(true),
singlePageApp(ResourceLoader.Classpath("/webui"))
)
)
.asServer(Netty(serverSettings.tls, clientSettings, statistics))

View file

@ -0,0 +1,405 @@
let connection;
let theme;
let style;
let port;
let ip;
let refreshRate;
let maxConsoleLines;
let graphTimeFrame;
let showConsoleLatest;
let doAnimations;
//non-option var
let statRequest;
//stat vars
jQuery(document).ready(function () {
loadOptions();
$("#theme").attr("href", "themes/" + theme + ".css");
$("#style").attr("href", "themes/" + style + ".css");
if (doAnimations) {
$(".optionInput").addClass("smooth");
$(".slider").addClass("smoothslider").addClass("smooth");
$(".content").addClass("slide_up");
$(".sideOption").addClass("smooth");
$(".button").addClass("smooth");
}
if (showConsoleLatest)
$("#consoleLatest").attr("hidden", false);
reconnect();
$("#console_input").keyup(function (e) {
if (e.keyCode === 13) {
sendCommand($(this).text());
$(this).text("");
$('#console_text').scrollTop($("#console_text")[0].scrollHeight)
}
})
});
//site functions, no connections involved
$(window).on("click", function () {
let sideBar = $("#sideBar");
if (sideBar.hasClass("expanded")) {
sideBar.removeClass("expanded").addClass("retract").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("retract").removeClass("expanded");
}
);
}
});
function loadOptions() {
let options = JSON.parse(localStorage.getItem("options"));
if (options === null) {
options = {
refresh_rate: 5000,
theme: "lightTheme",
style: "sharpStyle",
client_port: 33333,
client_ip: "localhost",
max_console_lines: 1000,
show_console_latest: false,
graph_time_frame: 30000,
do_animations: true
}
}
theme = options.theme;
style = options.style;
port = options.client_port;
ip = options.client_ip;
refreshRate = options.refresh_rate;
maxConsoleLines = options.max_console_lines;
graphTimeFrame = options.graph_time_frame;
showConsoleLatest = options.show_console_latest;
doAnimations = options.do_animations;
$("#dataRefreshRate").val(refreshRate);
$("#port").val(port);
$("#ip").val(ip);
$("#maxConsoleLines").val(maxConsoleLines);
$("#graphTimeFrame").val(graphTimeFrame);
$("#themeIn").val(theme);
$("#styleIn").val(style);
$("#newestconsole").prop("checked", showConsoleLatest);
$("#doAnimations").prop("checked", doAnimations);
}
function resetOptions() {
if (confirm("Do you really want to reset to defaults?")) {
$("#dataRefreshRate").val(5000);
$("#port").val(33333);
$("#ip").val("localhost");
$("#maxConsoleLines").val(1000);
$("#graphTimeFrame").val(30000);
$("#themeIn").val("lightTheme");
$("#styleIn").val("sharpStyle");
$("#newestconsole").prop("checked", false);
$("#doAnimations").prop("checked", true);
applyOptions()
}
}
function applyOptions() {
let options = {
refresh_rate: parseInt($("#dataRefreshRate").val()),
theme: $("#themeIn").val(),
style: $("#styleIn").val(),
client_port: parseInt($("#port").val()),
client_ip: $("#ip").val(),
max_console_lines: parseInt($("#maxConsoleLines").val()),
show_console_latest: $("#newestconsole").prop("checked"),
graph_time_frame: parseInt($("#graphTimeFrame").val()),
do_animations: $("#doAnimations").prop("checked")
};
if (options.do_animations !== doAnimations) {
doAnimations = options.do_animations;
if (doAnimations) {
$(".optionInput").addClass("smooth");
$(".slider").addClass("smoothslider").addClass("smooth");
$(".content").addClass("slide_up");
$(".sideOption").addClass("smooth");
$(".button").addClass("smooth");
} else {
$(".optionInput").removeClass("smooth");
$(".slider").removeClass("smoothslider").removeClass("smooth");
$(".content").removeClass("slide_up");
$(".sideOption").removeClass("smooth");
$(".button").removeClass("smooth");
}
$("#doAnimationscb").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
).prop("checked", doAnimations);
}
if (options.refresh_rate !== refreshRate) {
console.log(options.refresh_rate + " " + refreshRate);
refreshRate = Math.max(options.refresh_rate, 500);
$("#dataRefreshRate").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
).val(refreshRate);
}
if (options.style !== style) {
style = options.style;
applyStyle(options.style);
$("#styleIn").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
);
}
if (options.theme !== theme) {
theme = options.theme;
applyTheme(options.theme);
$("#themeIn").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
);
}
if (options.client_port !== port) {
port = options.client_port;
$("#port").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
).val(port);
reconnect();
}
if (options.client_ip !== ip) {
ip = options.client_ip;
$("#ip").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
).val(ip);
reconnect();
}
if (options.graph_time_frame !== graphTimeFrame) {
graphTimeFrame = Math.max(options.graph_time_frame, 5000);
$("#graphTimeFrame").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
).val(graphTimeFrame);
}
if (options.max_console_lines !== maxConsoleLines) {
maxConsoleLines = Math.max(options.max_console_lines, 100);
$("#maxConsoleLines").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
).val(maxConsoleLines);
}
if (options.show_console_latest !== showConsoleLatest) {
showConsoleLatest = options.show_console_latest;
if (showConsoleLatest)
$("#consoleLatest").attr("hidden", false);
else
$("#consoleLatest").attr("hidden", true);
$("#newestconsolecb").addClass("updated").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("updated");
}
).prop("checked", showConsoleLatest);
}
localStorage.setItem("options", JSON.stringify(options));
}
function selectTab(t, l) {
let sideBar = $("#sideBar");
sideBar.children("div").each(function () {
let tmp = $(this);
if (tmp.attr("id") === t) {
tmp.addClass("sideSelected");
} else
tmp.removeClass("sideSelected");
});
$("#content").children("div").each(function () {
let tmp = $(this);
if (tmp.attr("id") === l) {
tmp.attr("hidden", false);
} else
tmp.attr("hidden", true);
});
if (sideBar.hasClass("expanded")) {
sideBar.removeClass("expanded").addClass("retract").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("retract").removeClass("expanded");
}
);
}
}
function expSide() {
let sideBar = $("#sideBar");
if (sideBar.hasClass("expanded")) {
sideBar.removeClass("expanded").addClass("retract").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).removeClass("retract").removeClass("expanded");
}
);
} else {
sideBar.addClass("expand").on(
"animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function () {
$(this).addClass("expanded").removeClass("expand");
}
);
}
}
function applyTheme(t) {
if (doAnimations)
$("*").each(function () {
if (!$(this).attr("hidden"))
$(this).addClass("tempsmooth").on(
"webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",
function () {
$(this).removeClass("tempsmooth");
}
);
});
$("#theme").attr("href", "themes/" + t + ".css");
}
function applyStyle(s) {
if (doAnimations)
$("*").each(function () {
if (!$(this).attr("hidden"))
$(this).addClass("tempsmooth").on(
"webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",
function () {
$(this).removeClass("tempsmooth");
}
);
});
$("#style").attr("href", "themes/" + s + ".css");
}
//update data functions
function updateWithMessage(m) {
//TODO: get this to talk with client
let result;
try {
result = JSON.parse(m);
switch (result.type) {
case "command":
updateConsole(result.data, 2);
break;
case "stats":
updateValues();
break;
default:
updateConsole("[WEB-INFO] The message received is improperly formatted: " + result.data, 2);
break;
}
} catch (e) {
updateConsole("[WEB-INFO] There was an error parsing the data \n" + e, 2);
}
}
function updateValues() {
//TODO: use values and update web info
}
//console functions
function updateConsole(x, status) {
let scroll = false;
let temp = $('#console_text');
let latest = $("#consoleLatest");
if (temp.scrollTop() === (temp[0].scrollHeight - temp[0].clientHeight))
scroll = true;
switch (status) {
case 1:
temp.append('<div class="consoleLine sent">' + x + '</div>');
break;
case 0:
temp.append('<div class="consoleLine unsent">' + x + '</div>');
break;
default:
temp.append('<div class="consoleLine">' + x + '</div>');
latest.html('<div class="consoleLine">' + x + '</div>');
}
let childs = temp.children();
if (childs.length > maxConsoleLines) {
let length = childs.length;
for (let i = 0; i < length - maxConsoleLines; i++) {
childs[i].remove();
}
}
if (scroll)
temp.scrollTop(temp[0].scrollHeight);
}
function sendCommand(x) {
if (x === "")
return;
if (connection.readyState === "OPEN") {
let data = {
type: "command",
data: x
};
let message = JSON.stringify(data);
connection.send(message);
} else {
updateConsole(x, 0);
}
}
//network commuication
function reconnect() {
if (connection != null)
connection.close();
updateConsole("[WEB-CONSOLE] Attempting to connect to client on " + ip + ":" + port, 2);
connection = new WebSocket("ws://" + ip + ":" + port);
$("#connection").removeClass("disconnected").removeClass("connected").addClass("connecting").text("Connecting");
addListeners(connection)
}
function addListeners(c) {
let opened = false;
c.onopen = function (event) {
$("#connection").removeClass("disconnected").removeClass("connecting").addClass("connected").text("Connected");
opened = true;
updateConsole("[WEB-CONSOLE] Successfully to connect to client on " + ip + ":" + port, 2);
statRequest = setInterval(function () {
requestStats();
}, refreshRate);
};
c.onclose = function (event) {
$("#connection").addClass("disconnected").removeClass("connecting").removeClass("connected").text("Disconnected");
if (opened)
updateConsole("[WEB-CONSOLE] Disconnected from client");
else
updateConsole("[WEB-CONSOLE] Failed to connect to client on " + ip + ":" + port, 2);
clearInterval(statRequest);
};
c.onmessage = function (event) {
updateWithMessage(event.data());
};
}
function requestStats() {
let req = {type: "stats"};
connection.send(JSON.stringify(req));
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

View file

@ -0,0 +1,149 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MD@H Client</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="dataReceive.js"></script>
<link rel="stylesheet" type="text/css" href="layout.css">
<link rel="stylesheet" type="text/css" href="" id="style">
<link rel="stylesheet" type="text/css" href="themes/darkTheme.css" id="theme">
</head>
<body>
<div id="pageBar">
<a href="https://mangadex.org/">
<img src="https://mangadex.org/images/misc/navbar.svg?3" alt="mangadex" width="65px"
height="65px" style="float: left; padding: 5px; border-radius: 50%">
</a>
<h1 style="position: absolute; left: 75px; margin-left: 10px">MangaDex@Home Client Interface</h1>
<div id="consoleLatest" hidden></div>
<button id="connection" class="connecting button" onclick="reconnect()">Disconnected</button>
</div>
<div id="sideBar">
<div id="expSide" class="sideOption" onclick="expSide()">
<img src="icons/showmore.png" alt="dash" width="30px" height="30px" style="padding: 10px" class="img">
<h2 style="position: absolute; left: 50px; top: 0; margin: calc((50px - 29px)/2)">Menu</h2>
</div>
<div id="dash" class="sideOption sideSelected" onclick="selectTab('dash','dashb')">
<img src="icons/dashboard.png" alt="dash" width="30px" height="30px" style="padding: 10px" class="img">
<h2 style="position: absolute; left: 50px; top: 50px; margin: calc((50px - 29px)/2)">Dashboard</h2>
</div>
<div id="cons" class="sideOption" onclick="selectTab('cons','console')">
<img src="icons/console.png" alt="dash" width="30px" height="30px" style="padding: 10px" class="img">
<h2 style="position: absolute; left: 50px; top: 100px; margin: calc((50px - 29px)/2)">Console</h2>
</div>
<div id="opt" class="sideOption" onclick="selectTab('opt','dashOptions')">
<img src="icons/options.png" alt="dash" width="30px" height="30px" style="padding: 10px" class="img">
<h2 style="position: absolute; left: 50px; top: 150px; margin: calc((50px - 29px)/2)">Options</h2>
</div>
<div id="inf" class="sideOption" onclick="selectTab('inf','info')">
<img src="icons/info.png" alt="dash" width="30px" height="30px" style="padding: 10px" class="img">
<h2 style="position: absolute; left: 50px; top: 200px; margin: calc((50px - 29px)/2)">Info</h2>
</div>
</div>
<div id="content">
<div id="dashb" class="content">
<div class="contentHeader">
<h1 style="margin: 0;position:absolute; top: 42px; padding-left: 30px">Dashboard</h1>
</div>
<div id="nDat">
<div id="hits" class="numerical_data"></div>
<div id="misses" class="numerical_data"></div>
<div id="hitPercent" class="numerical_data"></div>
<div id="reqServed" class="numerical_data"></div>
<div id="bytesSent" class="numerical_data"></div>
</div>
<div id="gDat">
<div id="cpuUtil" class="line_graph_data"></div>
<div id="networkUtil" class="line_graph_data"></div>
<div id="discUtil" class="line_graph_data"></div>
<div id="cacheSize" class="line_graph_data"></div>
<div id="ramUtil" class="line_graph_data"></div>
</div>
</div>
<div id="console" class="content" hidden>
<div class="contentHeader">
<h1 style="margin: 0;position:absolute; top: 42px; padding-left: 30px">Console</h1>
</div>
<div id="buttonBoard">
<!-- client control stuffs-->
<h2 style="margin-left: 40px">Client Status: Stopped</h2>
</div>
<div id="liveConsole">
<div id="console_text"></div>
<div id="console_input" contenteditable="true"></div>
</div>
</div>
<div id="dashOptions" class="content" hidden>
<div class="contentHeader">
<h1 style="margin: 0;position:absolute; top: 42px; padding-left: 30px">Options</h1>
</div>
<div id="options">
<h3>General</h3>
<div class="option">
<h4>Data Refresh Rate</h4>
<label><input id="dataRefreshRate" type="number" class="optionInput input" placeholder="5000" min="500"></label>
</div>
<div class="option">
<h4>Websocket Port</h4>
<label><input id="port" type="number" class="optionInput input" placeholder="33333"></label>
</div>
<div class="option">
<h4>Client IP</h4>
<label><input id="ip" type="text" class="optionInput input" placeholder="localhost"></label>
</div>
<div class="option">
<h4>Max Console Lines</h4>
<label><input id="maxConsoleLines" type="number" class="optionInput input" placeholder="1000" min="100"></label>
</div>
<div class="option">
<h4>Graph Time Frame</h4>
<label><input id="graphTimeFrame" type="number" class="optionInput input" placeholder="30000"
min="5000"></label>
</div>
<h3>Display</h3>
<div class="option">
<h4>Theme</h4>
<label><select id="themeIn" class="optionInput input">
<option value="lightTheme">Light</option>
<option value="darkTheme">Dark</option>
<option value="midnightTheme">Midnight</option>
<option value="eyekillerTheme">High Vibrancy</option>
</select></label>
</div>
<div class="option">
<h4>Style</h4>
<label><select id="styleIn" class="optionInput input">
<option value="sharpStyle">Sharp</option>
<option value="softStyle">Soft</option>
</select></label>
</div>
<div class="option">
<h4>Show Console Latest</h4>
<label class="switch switchInput">
<input id="newestconsole" type="checkbox">
<span id="newestconsolecb" class="slider"></span>
</label>
</div>
<div class="option">
<h4>Animations</h4>
<label class="switch switchInput">
<input id="doAnimations" type="checkbox">
<span id="doAnimationscb" class="slider"></span>
</label>
</div>
</div>
<button id="apply" class="button" onclick="applyOptions()">Apply</button>
<button id="reset" class="button" onclick="resetOptions()">Reset</button>
</div>
<div id="info" class="content" hidden>
<div class="contentHeader">
<h1 style="margin: 0;position:absolute; top: 42px; padding-left: 30px">Client Info</h1>
</div>
<div id="information">
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,372 @@
body {
margin: 0;
font-family: Calibri, serif;
overflow: hidden;
}
.smooth {
-webkit-transition: .4s;
transition: .4s;
}
.tempsmooth {
-webkit-transition: .4s;
transition: .4s;
}
/*Content holder positions*/
#pageBar {
height: 75px;
width: 100%;
position: absolute;
}
#consoleLatest {
position: absolute;
min-height: 20px;
width: calc(100% - 755px);
margin: 30px 20px 25px 20px;
left: 545px;
overflow-x: scroll;
overflow-y: hidden;
}
#consoleLatest::-webkit-scrollbar {
height: 3px;
}
#connection {
position: absolute;
right: 0;
margin: 20px 20px 20px 20px;
width: 150px;
height: 35px;
border-style: solid;
outline: none;
}
.connecting {
animation: connecting 1.5s;
animation-iteration-count: infinite;
}
@keyframes connecting {
0%, 100% {
filter: brightness(120%)
}
50% {
filter: brightness(80%)
}
}
#sideBar {
height: calc(100% - 75px);
width: 50px;
top: 75px;
position: absolute;
z-index: 10;
overflow: hidden;
user-select: none;
}
.sideOption {
width: 100%;
height: 50px;
float: left;;
}
.expand {
animation: expand 150ms ease-out;
-webkit-animation-fill-mode: forwards;
}
.expanded {
width: 200px !important;
}
.retract {
animation: expand 150ms reverse ease-in;
-webkit-animation-fill-mode: forwards;
}
@keyframes expand {
0% {
width: 50px
}
100% {
width: 200px
}
}
#content {
height: calc(100% - 75px);
width: calc(100% - 50px);
top: 75px;
left: 50px;
position: absolute;
/*overflow-y: auto;*/
}
.contentHeader {
width: calc(100% - 40px);
height: 80px;
margin: 20px;
}
/*Main dashboard positions*/
#dashb {
width: 100%;
height: 100%;
position: absolute;
}
#nDat {
width: 40%;
height: calc(100% - 140px);
margin: 20px;
top: 100px;
position: absolute;
}
.numerical_data {
height: 150px;
width: calc(50% - 20px);
margin: 10px;
float: left;
}
#gDat {
height: calc(100% - 140px);
width: calc(60% - 60px);
margin: 20px;
position: absolute;
top: 100px;
left: calc(40% + 20px);
overflow-y: scroll;
}
.line_graph_data {
height: 200px;
width: calc((100% - 20px));
margin: 10px;
float: left;
}
/*Console and options positions*/
#console {
width: 100%;
height: 100%;
position: absolute;
}
#buttonBoard {
width: calc(100% - 40px);
height: calc(40% - 20px);
margin: 20px;
position: absolute;
top: 100px;
}
#liveConsole {
width: calc(100% - 40px);
height: calc(60% - 180px);
margin: 20px;
position: absolute;
top: calc(40% + 100px);
padding-bottom: 40px;
font-family: monospace;
}
.consoleLine {
width: calc(100% - 5px);
float: left;
margin: 0 5px 0;
left: 0;
white-space: nowrap;
}
.consoleLine > p {
margin: 0;
}
#console_input {
position: absolute;
width: calc(100% - 30px);
height: 20px;
bottom: 10px;
left: 0;
margin: 0 15px;
padding-top: 10px;
padding-bottom: 10px;
white-space: nowrap;
border-width: 0;
outline: none;
background-color: inherit;
overflow: hidden;
}
#console_text {
height: calc(100% - 40px);
width: calc(100% - 20px);
position: absolute;
outline: none;
border-width: 0;
resize: none;
font-family: monospace;
padding-left: 10px;
padding-right: 10px;
padding-bottom: 40px;
background-color: rgba(0, 0, 0, 0);
overflow: scroll;
}
/*Web option positions*/
#dashOptions {
width: 100%;
height: 100%;
position: absolute;
}
#options {
width: calc(100% - 80px);
height: calc(100% - 140px);
position: absolute;
top: 100px;
margin: 20px 20px 20px 60px;
}
#apply {
position: fixed;
bottom: 20px;
right: 20px;
width: 150px;
height: 30px;
}
#reset{
position: fixed;
bottom: 20px;
right: 190px;
width: 150px;
height: 30px;
}
.option {
height: 40px;
margin: 10px;
}
.option > h4 {
margin: 0;
float: left;
font-weight: normal;
}
.optionLabel {
}
.optionInput {
left: 200px;
position: absolute;
border-style: solid;
border-width: 2px;
}
.switchInput > span {
left: 200px;
position: absolute;
border-style: solid;
}
.updated {
animation: fade 1.5s linear;
}
@keyframes fade {
0%, 100% {
filter: alpha(100%);
}
100% {
filter: alpha(0%);
}
}
/*misc modifications*/
.input {
outline: 0;
}
.button {
outline: none;
border-width: 2px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
}
.slider:before {
/*border-width: 1px;*/
position: absolute;
content: "";
}
.smoothslider:before {
position: absolute;
content: "";
-webkit-transition: .4s;
transition: .4s;
}
/*Webkit modifications*/
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-button {
width: 0;
height: 0;
}
::-webkit-scrollbar-thumb {
}
::-webkit-scrollbar-track {
}
::-webkit-scrollbar-corner {
background-color: rgba(0, 0, 0, 0);
}
/*animations*/
.slide_up {
animation: slideup .1s ease-out;
}
@keyframes slideup {
0% {
transform: translateY(50px);
opacity: 0;
}
100% {
transform: translateY(0px);
opacity: 1;
}
}

View file

@ -0,0 +1,167 @@
body {
background-color: #404040;
color: #f0f0f0;
}
#pageBar {
background-color: #303030;
}
#consoleLatest{
background-color: black;
color: #f0f0f0;
}
.connected {
border-color: #0fff00 !important;
}
.disconnected {
border-color: #e50100 !important;
}
.connecting {
border-color: #e5d700 !important;
}
#sideBar {
background-color: #303030;
}
.sideOption {
background-color: #303030;
}
.sideOption:hover {
background-color: #404040;
}
.sideSelected {
background-color: #606060;
}
#content {
background-color: #404040;
}
.contentHeader {
background-color: #606060;
}
/*Main dashboard colors*/
#dashb {
}
#nDat {
}
.numerical_data {
background-color: #606060;
}
#gDat {
}
.line_graph_data {
background-color: #606060;
}
/*Console and options colors*/
#liveConsole {
background-color: black;
caret-color: #f0f0f0;
}
#console_text {
color: #f0f0f0;
}
#console_input {
color: #f0f0f0;
}
.unsent {
color: #e50100;
}
.sent {
color: #0fff00;
}
/*Web option colors*/
#dashOptions {
}
#options {
}
.option {
}
.optionLabel {
}
.optionInput {
border-color: rgba(0, 0, 0, 0);
background-color: #606060;
color: #f0f0f0;
}
/*misc*/
.button{
border-color: rgba(0, 0, 0, 0);
background-color: #606060;
color: #f0f0f0;
}
.button:hover{
background-color: #909090;
}
.img {
filter: invert(100%);
}
.updated {
border-color: #1ec70d !important;
}
.slider {
border-color: rgba(0, 0, 0, 0);
background-color: #606060;
}
.slider::before{
background-color: #f0f0f0;
}
input:checked + .slider {
background-color: #909090;
}
/*Webkit colors*/
::-webkit-scrollbar {
}
::-webkit-scrollbar-button {
}
::-webkit-scrollbar-thumb {
background-color: #555555;
}
::-webkit-scrollbar-thumb:hover {
background-color: #888888;
}
::-webkit-scrollbar-track {
}

View file

@ -0,0 +1,97 @@
body {
background-color: #ffffff;
color: #202020;
}
#pageBar {
background-color: #faff00;
}
#sideBar {
background-color: #faff00;
}
.sideOption {
}
.sideOption:hover {
background-color: #faff00;
}
.sideSelected {
background-color: #faff00;
}
#content {
background-color: #0fff00;
}
.contentHeader {
background-color: #ff00af;
}
/*Main dashboard colors*/
#dashb {
}
#nDat {
}
.numerical_data {
background-color: #00ffec;
}
#gDat {
}
.line_graph_data {
background-color: #00ffec;
}
/*Console and options colors*/
#liveConsole {
background-color: black;
caret-color: #f0f0f0;
}
#console_text {
color: #f0f0f0;
}
#console_input {
color: #f0f0f0;
}
/*misc*/
.img {
}
.updated {
border-color: #1ec70d;
}
/*Webkit colors*/
::-webkit-scrollbar {
}
::-webkit-scrollbar-button {
}
::-webkit-scrollbar-thumb {
background-color: #aaaaaa;
}
::-webkit-scrollbar-thumb:hover {
background-color: #888888;
}
::-webkit-scrollbar-track {
}

View file

@ -0,0 +1,166 @@
body {
background-color: #ffffff;
color: #202020;
}
#pageBar {
background-color: #f8f9fa;
}
#consoleLatest{
background-color: black;
color: #f0f0f0;
}
.connected {
border-color: #0fff00 !important;
}
.disconnected {
border-color: #e50100 !important;
}
.connecting {
border-color: #e5d700 !important;
}
#sideBar {
background-color: #f8f9fa;
}
.sideOption {
}
.sideOption:hover {
background-color: #eeeeee;
}
.sideSelected {
background-color: #e1e1e1;
}
#content {
background-color: #ffffff;
}
.contentHeader {
background-color: #ededed;
}
/*Main dashboard colors*/
#dashb {
}
#nDat {
}
.numerical_data {
background-color: #ededed;
}
#gDat {
}
.line_graph_data {
background-color: #ededed;
}
/*Console and options colors*/
#liveConsole {
background-color: black;
caret-color: #f0f0f0;
}
#console_text {
color: #f0f0f0;
}
#console_input {
color: #f0f0f0;
}
.unsent {
color: #cb0000;
}
.sent {
color: #1ec70d;
}
/*Web option colors*/
#dashOptions {
}
#options {
}
.option {
}
.optionLabel {
}
.optionInput {
border-color: rgba(0, 0, 0, 0);
background-color: #eaeaea;
color: #202020;
}
/*misc*/
.button{
border-color: rgba(0, 0, 0, 0);
background-color: #eaeaea;
color: #202020;
}
.button:hover{
background-color: #dadada;
}
.img {
}
.updated {
border-color: #1ec70d !important;
}
.slider {
border-color: rgba(0, 0, 0, 0);
background-color: #eaeaea;
}
.slider::before{
background-color: #202020;
}
input:checked + .slider {
background-color: #adadad;
}
/*Webkit colors*/
::-webkit-scrollbar {
}
::-webkit-scrollbar-button {
}
::-webkit-scrollbar-thumb {
background-color: #cacaca;
}
::-webkit-scrollbar-thumb:hover {
background-color: #dedede;
}
::-webkit-scrollbar-track {
}

View file

@ -0,0 +1,163 @@
body {
background-color: #101010;
color: #bfbfbf;
}
#pageBar {
background-color: #202020;
}
#consoleLatest {
background-color: black;
color: #f0f0f0;
}
.connected {
border-color: #0fff00 !important;
}
.disconnected {
border-color: #e50100 !important;
}
.connecting {
border-color: #e5d700 !important;
}
#sideBar {
background-color: #202020;
}
.sideOption:hover {
background-color: #404040;
}
.sideSelected {
background-color: #505050;
}
#content {
background-color: #101010;
}
.contentHeader {
background-color: #404040;
}
/*Main dashboard colors*/
#dashb {
}
#nDat {
}
.numerical_data {
background-color: #404040;
}
#gDat {
}
.line_graph_data {
background-color: #404040;
}
/*Console and options colors*/
#liveConsole {
background-color: black;
caret-color: #f0f0f0;
}
#console_text {
color: #f0f0f0;
}
#console_input {
color: #f0f0f0;
}
.unsent {
color: #e50100;
}
.sent {
color: #0fff00;
}
/*Web option colors*/
#dashOptions {
}
#options {
}
.option {
}
.optionLabel {
}
.optionInput {
border-color: rgba(0, 0, 0, 0);
background-color: #404040;
color: #bfbfbf;
}
/*misc*/
.button{
border-color: rgba(0, 0, 0, 0);
background-color: #404040;
color: #bfbfbf;
}
.button:hover{
background-color: #797979;
}
.img {
filter: invert(100%);
}
.updated {
border-color: #1ec70d !important;
}
.slider {
border-color: rgba(0, 0, 0, 0);
background-color: #404040;
}
.slider::before {
background-color: #bfbfbf;
}
input:checked + .slider {
background-color: #757575;
}
/*Webkit colors*/
::-webkit-scrollbar {
}
::-webkit-scrollbar-button {
}
::-webkit-scrollbar-thumb {
background-color: #555555;
}
::-webkit-scrollbar-thumb:hover {
background-color: #888888;
}
::-webkit-scrollbar-track {
}

View file

@ -0,0 +1,22 @@
.input {
padding-left: 2px;
}
.slider {
width: 60px;
height: 12px;
margin-top: 2px;
}
.slider:before {
height: 12px;
width: 30px;
left: 0;
bottom: 0;
}
input:checked + .slider:before {
-webkit-transform: translateX(30px);
-ms-transform: translateX(30px);
transform: translateX(30px);
}

View file

@ -0,0 +1,145 @@
/*Content holder positions*/
#pageBar {
}
#connection {
border-radius: 10px;
}
#consoleLatest {
border-radius: 10px;
}
#sideBar {
}
.sideSelected {
border-radius: 10px;
}
.sideOption:hover {
border-radius: 10px;
}
#expSide {
}
#dash {
}
#cons {
}
#opt {
}
#content {
}
.contentHeader {
border-radius: 10px;
}
/*Main dashboard positions*/
#dashb {
}
#nDat {
}
.numerical_data {
border-radius: 10px;
}
#gDat {
border-radius: 10px;
}
.line_graph_data {
border-radius: 10px;
}
/*Console and options positions*/
#console {
}
#buttonBoard {
}
#liveConsole {
border-radius: 10px;
}
#console_input {
}
#console_text {
border-radius: 10px;
}
/*Web option positions*/
#dashOptions {
}
#options {
}
#apply {
border-radius: 10px;
}
.option {
}
.optionLabel {
}
.optionInput {
}
/*misc modifications*/
.input {
padding-left: 5px;
border-radius: 10px;
}
.slider {
border-radius: 30px;
width: 60px;
height: 16px;
}
.slider:before {
border-radius: 7px;
height: 14px;
width: 28px;
left: 3px;
bottom: 1px;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/*Webkit modifications*/
::-webkit-scrollbar {
}
::-webkit-scrollbar-button {
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
}
::-webkit-scrollbar-track {
}