Initial commit
This commit is contained in:
3
Plugins/CloudNetWebinterface/.idea/.gitignore
generated
vendored
Normal file
3
Plugins/CloudNetWebinterface/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
13
Plugins/CloudNetWebinterface/.idea/compiler.xml
generated
Normal file
13
Plugins/CloudNetWebinterface/.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="CloudNetWebinterface" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
7
Plugins/CloudNetWebinterface/.idea/discord.xml
generated
Normal file
7
Plugins/CloudNetWebinterface/.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
||||
7
Plugins/CloudNetWebinterface/.idea/encodings.xml
generated
Normal file
7
Plugins/CloudNetWebinterface/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
25
Plugins/CloudNetWebinterface/.idea/jarRepositories.xml
generated
Normal file
25
Plugins/CloudNetWebinterface/.idea/jarRepositories.xml
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="cloudnet-releases" />
|
||||
<option name="name" value="cloudnet-releases" />
|
||||
<option name="url" value="https://repo.cloudnetservice.eu/repository/releases/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
14
Plugins/CloudNetWebinterface/.idea/misc.xml
generated
Normal file
14
Plugins/CloudNetWebinterface/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
124
Plugins/CloudNetWebinterface/.idea/uiDesigner.xml
generated
Normal file
124
Plugins/CloudNetWebinterface/.idea/uiDesigner.xml
generated
Normal file
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
||||
5
Plugins/CloudNetWebinterface/htdocs/.idea/.gitignore
generated
vendored
Normal file
5
Plugins/CloudNetWebinterface/htdocs/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
7
Plugins/CloudNetWebinterface/htdocs/.idea/discord.xml
generated
Normal file
7
Plugins/CloudNetWebinterface/htdocs/.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
||||
12
Plugins/CloudNetWebinterface/htdocs/.idea/htdocs.iml
generated
Normal file
12
Plugins/CloudNetWebinterface/htdocs/.idea/htdocs.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
Plugins/CloudNetWebinterface/htdocs/.idea/modules.xml
generated
Normal file
8
Plugins/CloudNetWebinterface/htdocs/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/htdocs.iml" filepath="$PROJECT_DIR$/.idea/htdocs.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
BIN
Plugins/CloudNetWebinterface/htdocs/assets/images/moon.jpg
Normal file
BIN
Plugins/CloudNetWebinterface/htdocs/assets/images/moon.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 488 KiB |
@@ -0,0 +1,62 @@
|
||||
<div id="navigation">
|
||||
<img src="/assets/images/server-icon.png" alt="Server Icon" id="navigation-logo" draggable="false">
|
||||
<h1 id="navigation-title">Serververwaltung</h1>
|
||||
<div id="navigation-buttons">
|
||||
<input type="button" class="navigation-button" value="Startseite" id="navigation-">
|
||||
<input type="button" class="navigation-button" value="Server" id="navigation-/server/">
|
||||
<input type="button" class="navigation-button" value="Spieler" id="navigation-/players/">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--Styling-->
|
||||
<style>
|
||||
|
||||
#navigation {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 50px;
|
||||
background-color: #4a4c4d;
|
||||
}
|
||||
|
||||
#navigation-logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 5px 5px 5px 10px;
|
||||
}
|
||||
|
||||
#navigation-title {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#navigation-buttons {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
transform: translate(-105%, -50%);
|
||||
width: 370px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.navigation-button {
|
||||
width: 100px;
|
||||
height: 30px;
|
||||
color: white;
|
||||
margin: 10px;
|
||||
background: none;
|
||||
transition: all 200ms;
|
||||
|
||||
border: 2px solid #2094c2;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.navigation-button:hover {
|
||||
background-color: #2094c2;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
33
Plugins/CloudNetWebinterface/htdocs/index.html
Normal file
33
Plugins/CloudNetWebinterface/htdocs/index.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Admin Panel</title>
|
||||
<link rel="stylesheet" type="text/css" href="/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||
<script src="/main.js"></script>
|
||||
<script src="/script.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="component unselectable" id="component-navigation"></nav>
|
||||
|
||||
<main id="container">
|
||||
<section id="left" class="unselectable">
|
||||
<div id="buttons">
|
||||
<input type="button" value="Stop" id="stop" class="buttons">
|
||||
<input type="button" value="Reload" id="reload" class="buttons">
|
||||
</div>
|
||||
<span id="playerTitle">Spieler</span>
|
||||
<div id="players"></div>
|
||||
</section>
|
||||
|
||||
<section id="right">
|
||||
<div id="console"></div>
|
||||
<div id="console-input">
|
||||
<input type="text" placeholder=">" id="command">
|
||||
<input type="button" value="Senden" id="send">
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
23
Plugins/CloudNetWebinterface/htdocs/login/index.html
Normal file
23
Plugins/CloudNetWebinterface/htdocs/login/index.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Login</title>
|
||||
<script src="../main.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<form class="login">
|
||||
<h1>Einloggen</h1>
|
||||
<div class="box">
|
||||
<input type="text" class="input" id="username" placeholder="Benutzername" autocomplete="off" required>
|
||||
<input type="password" class="input" id="password" placeholder="Passwort" required>
|
||||
<input type="button" id="submit" value="Login">
|
||||
</div>
|
||||
</form>
|
||||
<div id="snackbar">Login fehlgeschlagen!</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
21
Plugins/CloudNetWebinterface/htdocs/login/script.js
Normal file
21
Plugins/CloudNetWebinterface/htdocs/login/script.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const submit = document.getElementById("submit");
|
||||
|
||||
submit.onclick = async function() {
|
||||
const username = document.getElementById("username").value;
|
||||
const password = document.getElementById("password").value;
|
||||
|
||||
const response = await sendGetRequest(hostname + "/api/users/login?username=" + username + "&password=" + password);
|
||||
|
||||
if (response !== "false") {
|
||||
sessionStorage.setItem("sessionKey", response);
|
||||
sessionKey = response;
|
||||
location.href = hostname;
|
||||
}else {
|
||||
showSnackbar();
|
||||
}
|
||||
}
|
||||
|
||||
function showSnackbar() {
|
||||
document.getElementById('snackbar')?.classList.add('show');
|
||||
setTimeout(() => {document.getElementById('snackbar')?.classList.remove('show');}, 5000);
|
||||
}
|
||||
122
Plugins/CloudNetWebinterface/htdocs/login/style.css
Normal file
122
Plugins/CloudNetWebinterface/htdocs/login/style.css
Normal file
@@ -0,0 +1,122 @@
|
||||
* {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-image: url(../assets/images/moon.jpg);
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.login {
|
||||
width: 460px;
|
||||
background: snow;
|
||||
height: 480px;
|
||||
padding: 80px 40px;
|
||||
border-radius: 10px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.login h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 60px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.box {
|
||||
border-bottom: 2px solid #adadad;
|
||||
position: relative;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.input {
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
border: 0;
|
||||
background: none;
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
border: 2px solid #4a4c4d;
|
||||
padding: 14px 10px;
|
||||
width: 350px;
|
||||
outline: none;
|
||||
color: black;
|
||||
border-radius: 10px;
|
||||
transition: 0.25s;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
width: 380px;
|
||||
border-color: #2094c2;
|
||||
}
|
||||
|
||||
#submit {
|
||||
border: 0;
|
||||
background: none;
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
border: 2px solid #2094c2;
|
||||
padding: 14px 40px;
|
||||
outline: none;
|
||||
|
||||
border-radius: 10px;
|
||||
transition: 0.25s;
|
||||
cursor: pointer;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#submit:hover {
|
||||
background: #2094c2;
|
||||
color: snow;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#snackbar {
|
||||
visibility: hidden; /* Hidden by default. Visible on click */
|
||||
width: 290px; /* Set a default minimum width */
|
||||
margin-left: -145px; /* Hälfte von width */
|
||||
background-color:#f44336; /* Black background color */
|
||||
color: #fff; /* White text color */
|
||||
text-align: center; /* Centered text */
|
||||
border-radius: 2px; /* Rounded borders */
|
||||
padding: 20px; /* Padding */
|
||||
left: 50%;
|
||||
position: fixed; /* Sit on top of the screen */
|
||||
z-index: 1; /* Add a z-index if needed */
|
||||
bottom: 30px; /* 30px from the bottom */
|
||||
}
|
||||
|
||||
|
||||
#snackbar.show {
|
||||
visibility: visible; /* Show the snackbar */
|
||||
|
||||
-webkit-animation: fadein 0.5s, fadeout 0.5s 4.5s;
|
||||
animation: fadein 0.5s, fadeout 0.5s 4.5s;
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes fadein {
|
||||
from {bottom: 0; opacity: 0;}
|
||||
to {bottom: 30px; opacity: 1;}
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {bottom: 0; opacity: 0;}
|
||||
to {bottom: 30px; opacity: 1;}
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeout {
|
||||
from {bottom: 30px; opacity: 1;}
|
||||
to {bottom: 0; opacity: 0;}
|
||||
}
|
||||
|
||||
@keyframes fadeout {
|
||||
from {bottom: 30px; opacity: 1;}
|
||||
to {bottom: 0; opacity: 0;}
|
||||
}
|
||||
66
Plugins/CloudNetWebinterface/htdocs/main.js
Normal file
66
Plugins/CloudNetWebinterface/htdocs/main.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const hostname = location.protocol + "//" + location.host;
|
||||
if (!location.href.replace(hostname, "").includes(".") && !location.href.endsWith("/")) location.href += "/";
|
||||
|
||||
let sessionKey;
|
||||
|
||||
window.onload = async function () {
|
||||
sessionKey = sessionStorage.getItem("sessionKey");
|
||||
const valid = await sendGetRequest(hostname + "/api/users/validate");
|
||||
if (valid === "false" && !location.href.includes("login")) location.href = hostname + "/login/";
|
||||
else {
|
||||
const componentClients = document.getElementsByClassName("component");
|
||||
for (let element of componentClients) {
|
||||
const componentName = element.id.replace("component-", "");
|
||||
element.innerHTML = await sendGetRequest(hostname + "/components/" + componentName + ".html");
|
||||
}
|
||||
await initialize();
|
||||
initializeNavBar();
|
||||
}
|
||||
}
|
||||
|
||||
async function sendHttpRequest(url, body = "", method = "GET") {
|
||||
return await fetch(url, {
|
||||
method: method,
|
||||
mode: 'cors',
|
||||
cache: 'no-cache',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': sessionKey
|
||||
},
|
||||
redirect: 'follow',
|
||||
referrerPolicy: 'no-referrer',
|
||||
body: body
|
||||
});
|
||||
}
|
||||
|
||||
async function sendGetRequest(url, asJson = false) {
|
||||
const response = await sendHttpRequest(url, null, "GET");
|
||||
return asJson ? await response.json() : await response.text();
|
||||
}
|
||||
|
||||
async function sendPutRequest(url, body, asJson = false) {
|
||||
const response = await sendHttpRequest(url, body, "PUT");
|
||||
return asJson ? await response.json() : await response.text();
|
||||
}
|
||||
|
||||
async function sendPostRequest(url, body, asJson = false) {
|
||||
const response = await sendHttpRequest(url, body, "POST");
|
||||
return asJson ? await response.json() : await response.text();
|
||||
}
|
||||
|
||||
async function sendDeleteRequest(url, asJson = false) {
|
||||
const response = await sendHttpRequest(url, null, "DELETE");
|
||||
return asJson ? await response.json() : await response.text();
|
||||
}
|
||||
|
||||
function initializeNavBar() {
|
||||
const buttons = document.getElementsByClassName("navigation-button");
|
||||
for (let button of buttons) {
|
||||
button.onclick = function () {
|
||||
location.href = hostname + button.id.replace("navigation-", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let initialize = function () {}
|
||||
53
Plugins/CloudNetWebinterface/htdocs/script.js
Normal file
53
Plugins/CloudNetWebinterface/htdocs/script.js
Normal file
@@ -0,0 +1,53 @@
|
||||
initialize = async function () {
|
||||
await loadPlayers();
|
||||
|
||||
await loadConsole();
|
||||
const console = document.getElementById("console");
|
||||
console.scrollTop = console.scrollHeight;
|
||||
|
||||
document.getElementById("send").onclick = async function () {
|
||||
const cmd = document.getElementById("command");
|
||||
await sendPostRequest(hostname + "/api/console/send?command=" + cmd.value, "");
|
||||
cmd.value = "";
|
||||
await loadConsole();
|
||||
}
|
||||
|
||||
document.getElementById("stop").onclick = async function () {
|
||||
await sendGetRequest(hostname + "/api/console/stop")
|
||||
}
|
||||
document.getElementById("reload").onclick = async function () {
|
||||
await sendGetRequest(hostname + "/api/console/reload")
|
||||
}
|
||||
|
||||
setInterval(async () => {
|
||||
await loadConsole();
|
||||
await loadPlayers();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
async function loadPlayers() {
|
||||
const players = await sendGetRequest(hostname + "/api/players", true);
|
||||
const playerContainer = document.getElementById("players");
|
||||
|
||||
let html = "";
|
||||
for (const player of players) {
|
||||
const name = player["name"];
|
||||
const server = player["server"];
|
||||
html += `
|
||||
<div class="player">
|
||||
<img src="https://minotar.net/avatar/${name}/200" alt="Player Head">
|
||||
<span class="playerName">${name}</span>
|
||||
<br>
|
||||
<span class="playerServer">Server: ${server}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
playerContainer.innerHTML = html;
|
||||
}
|
||||
|
||||
async function loadConsole() {
|
||||
const consoleLog = await sendGetRequest(hostname + "/api/console");
|
||||
const console = document.getElementById("console");
|
||||
console.innerText = consoleLog;
|
||||
}
|
||||
162
Plugins/CloudNetWebinterface/htdocs/style.css
Normal file
162
Plugins/CloudNetWebinterface/htdocs/style.css
Normal file
@@ -0,0 +1,162 @@
|
||||
#container {
|
||||
position: relative;
|
||||
margin: 80px;
|
||||
height: calc(100% - 50px);
|
||||
overflow: hidden;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
#left {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 20%;
|
||||
height: calc(100% - 160px);
|
||||
background-color: #4a4a4a;
|
||||
border-bottom-left-radius: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#right {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 20%;
|
||||
width: 80%;
|
||||
height: calc(100% - 160px);
|
||||
background-color: #151515;
|
||||
border-bottom-right-radius: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#buttons {
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
#players {
|
||||
width: 100%;
|
||||
height: 87%;
|
||||
padding: 2%;
|
||||
}
|
||||
|
||||
#console {
|
||||
margin-top: 1.5%;
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
padding: 1.5%;
|
||||
overflow-y: scroll;
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#console::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
#console::-webkit-scrollbar-track {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#console::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
}
|
||||
|
||||
#console::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
#console-input {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 7%;
|
||||
padding: 1%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
width: 45%;
|
||||
height: 70%;
|
||||
border-radius: 20px;
|
||||
background: none;
|
||||
border: 2px solid #2094c2;
|
||||
color: white;
|
||||
margin: 2%;
|
||||
font-size: 150%;
|
||||
transition: all 200ms;
|
||||
}
|
||||
|
||||
.buttons:hover {
|
||||
cursor: pointer;
|
||||
background-color: #2094c2;
|
||||
}
|
||||
|
||||
.player {
|
||||
display: inline-block;
|
||||
width: 47%;
|
||||
height: 5%;
|
||||
background-color: #252525;
|
||||
margin: 1%;
|
||||
position: relative;
|
||||
padding: 2%;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.player img {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
margin-left: 3%;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
|
||||
.playerName {
|
||||
position: absolute;
|
||||
color: white;
|
||||
top: 2%;
|
||||
left: 30%;
|
||||
}
|
||||
|
||||
.playerServer {
|
||||
position: absolute;
|
||||
color: white;
|
||||
top: 55%;
|
||||
left: 30%;
|
||||
font-size: 70%;
|
||||
}
|
||||
|
||||
#playerTitle {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 3%;
|
||||
font-size: 170%;
|
||||
text-align: center;
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#command {
|
||||
top: 0;
|
||||
width: 94%;
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#send {
|
||||
width: 5%;
|
||||
background-color: #039BE5;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
transition: 200ms;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#send:hover {
|
||||
background-color: #03A9F4;
|
||||
cursor: pointer;
|
||||
}
|
||||
23
Plugins/CloudNetWebinterface/htdocs/styles.css
Normal file
23
Plugins/CloudNetWebinterface/htdocs/styles.css
Normal file
@@ -0,0 +1,23 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300&display=swap');
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
.unselectable {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body, html {
|
||||
background-color: #2a2a2a;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
103
Plugins/CloudNetWebinterface/pom.xml
Normal file
103
Plugins/CloudNetWebinterface/pom.xml
Normal file
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>de.craftix</groupId>
|
||||
<artifactId>CloudNetWebinterface</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<cloudnet.version>3.4.0-RELEASE</cloudnet.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>cloudnet-releases</id>
|
||||
<url>https://repo.cloudnetservice.eu/repository/releases/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>de.dytanic.cloudnet</groupId>
|
||||
<artifactId>cloudnet-driver</artifactId>
|
||||
<version>${cloudnet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.dytanic.cloudnet</groupId>
|
||||
<artifactId>cloudnet-bridge</artifactId>
|
||||
<version>${cloudnet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.dytanic.cloudnet</groupId>
|
||||
<artifactId>cloudnet-wrapper-jvm</artifactId>
|
||||
<version>${cloudnet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.dytanic.cloudnet</groupId>
|
||||
<artifactId>cloudnet</artifactId>
|
||||
<version>${cloudnet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.dytanic.cloudnet</groupId>
|
||||
<artifactId>cloudnet-cloudperms</artifactId>
|
||||
<version>${cloudnet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.36.0.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,43 @@
|
||||
package de.craftix.cloudnetwebinterface;
|
||||
|
||||
import de.craftix.cloudnetwebinterface.commands.MainCmd;
|
||||
import de.craftix.cloudnetwebinterface.utils.DBManager;
|
||||
import de.craftix.cloudnetwebinterface.utils.SQLite;
|
||||
import de.craftix.cloudnetwebinterface.webserver.WebServerManager;
|
||||
import de.craftix.cloudnetwebinterface.webserver.routes.ConsoleAPI;
|
||||
import de.craftix.cloudnetwebinterface.webserver.routes.PlayerAPI;
|
||||
import de.craftix.cloudnetwebinterface.webserver.routes.UserAPI;
|
||||
import de.dytanic.cloudnet.driver.module.ModuleLifeCycle;
|
||||
import de.dytanic.cloudnet.driver.module.ModuleTask;
|
||||
import de.dytanic.cloudnet.module.NodeCloudNetModule;
|
||||
|
||||
public class CloudNetWebinterface extends NodeCloudNetModule {
|
||||
private static CloudNetWebinterface instance;
|
||||
private static SQLite sqLite;
|
||||
private static WebServerManager webServerManager;
|
||||
|
||||
@ModuleTask(event = ModuleLifeCycle.STARTED)
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
sqLite = new SQLite("modules/Webinterface/data.db");
|
||||
DBManager.initialize();
|
||||
|
||||
//Start Server
|
||||
webServerManager = new WebServerManager(4041);
|
||||
webServerManager.addApi(new UserAPI());
|
||||
webServerManager.addApi(new PlayerAPI());
|
||||
webServerManager.addApi(new ConsoleAPI());
|
||||
|
||||
registerCommand(new MainCmd());
|
||||
}
|
||||
|
||||
@ModuleTask(event = ModuleLifeCycle.STOPPED)
|
||||
public void onDisable() {
|
||||
webServerManager.stop();
|
||||
}
|
||||
|
||||
public static CloudNetWebinterface getInstance() { return instance; }
|
||||
public static SQLite getSQLite() { return sqLite; }
|
||||
public static WebServerManager getWebServerManager() { return webServerManager; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package de.craftix.cloudnetwebinterface.commands;
|
||||
|
||||
import de.craftix.cloudnetwebinterface.utils.DBManager;
|
||||
import de.dytanic.cloudnet.command.DriverCommandSender;
|
||||
import de.dytanic.cloudnet.command.ICommandSender;
|
||||
import de.dytanic.cloudnet.command.sub.SubCommandHandler;
|
||||
import de.dytanic.cloudnet.common.Properties;
|
||||
import de.dytanic.cloudnet.driver.CloudNetDriver;
|
||||
import de.dytanic.cloudnet.ext.bridge.player.ICloudPlayer;
|
||||
import de.dytanic.cloudnet.ext.bridge.player.IPlayerManager;
|
||||
|
||||
public class MainCmd extends SubCommandHandler {
|
||||
|
||||
public MainCmd() {
|
||||
super("register");
|
||||
this.permission = "webinterface.admin";
|
||||
this.description = "Manage the Webinterface";
|
||||
this.usage = "/cloud register <username> <password>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ICommandSender sender, String command, String[] args, String commandLine, Properties properties) {
|
||||
if (!(sender instanceof DriverCommandSender)) sender.sendMessage("Dieser Command kann nur als Spieler ausgeführt werden!");
|
||||
else if (args.length != 2) sender.sendMessage(getUsage());
|
||||
else {
|
||||
ICloudPlayer player = CloudNetDriver.getInstance().getServicesRegistry().getFirstService(IPlayerManager.class).getFirstOnlinePlayer(args[0]);
|
||||
if (player == null) throw new NullPointerException("Player cannot be null");
|
||||
String password = args[1];
|
||||
DBManager.addUser(player.getUniqueId(), password);
|
||||
sender.sendMessage("Erfolgreich im Webinterface registriert!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package de.craftix.cloudnetwebinterface.utils;
|
||||
|
||||
import de.craftix.cloudnetwebinterface.CloudNetWebinterface;
|
||||
import de.dytanic.cloudnet.driver.CloudNetDriver;
|
||||
import de.dytanic.cloudnet.driver.permission.IPermissionGroup;
|
||||
import de.dytanic.cloudnet.driver.permission.IPermissionUser;
|
||||
import de.dytanic.cloudnet.driver.permission.PermissionUserGroupInfo;
|
||||
import de.dytanic.cloudnet.ext.bridge.player.ICloudOfflinePlayer;
|
||||
import de.dytanic.cloudnet.ext.bridge.player.IPlayerManager;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Base64;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DBManager {
|
||||
private static SQLite sqLite;
|
||||
|
||||
public static void initialize() {
|
||||
sqLite = CloudNetWebinterface.getSQLite();
|
||||
sqLite.insert("CREATE TABLE IF NOT EXISTS Users (UUID VARCHAR(100), Password VARCHAR(50))");
|
||||
sqLite.insert("CREATE TABLE IF NOT EXISTS Sessions (UUID VARCHAR(100), SessionKey VARCHAR(100), CreationDate INT(255))");
|
||||
}
|
||||
|
||||
public static void addUser(UUID uuid, String password) {
|
||||
password = Base64.getEncoder().encodeToString(password.getBytes());
|
||||
sqLite.insert("DELETE FROM Users WHERE UUID = \"" + uuid + "\"");
|
||||
sqLite.insert("INSERT INTO Users VALUES (\"" + uuid + "\", \"" + password + "\")");
|
||||
}
|
||||
|
||||
public static boolean checkLoginData(String username, String password) {
|
||||
ICloudOfflinePlayer player = CloudNetDriver.getInstance().getServicesRegistry().getFirstService(IPlayerManager.class).getFirstOfflinePlayer(username);
|
||||
if (player == null) return false;
|
||||
return password.equals(getUserPassword(player.getUniqueId()));
|
||||
}
|
||||
|
||||
private static String getUserPassword(UUID uuid) {
|
||||
try {
|
||||
ResultSet rs = sqLite.getData("SELECT Password FROM Users WHERE UUID = \"" + uuid + "\"");
|
||||
if (rs.next()) {
|
||||
return new String(Base64.getDecoder().decode(rs.getString("Password")));
|
||||
}
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String createSessionKey(UUID user) {
|
||||
String session = UUID.randomUUID().toString();
|
||||
sqLite.insert("DELETE FROM Sessions WHERE UUID = \"" + user + "\"");
|
||||
sqLite.insert("INSERT INTO Sessions VALUES (\"" + user + "\", \"" + session + "\", " + System.currentTimeMillis() + ")");
|
||||
return session;
|
||||
}
|
||||
|
||||
public static boolean validateSession(String session) {
|
||||
try {
|
||||
ResultSet rs = sqLite.getData("SELECT CreationDate FROM Sessions WHERE SessionKey = \"" + session + "\"");
|
||||
if (rs.next()) {
|
||||
long creationDate = rs.getLong("CreationDate");
|
||||
double time = System.currentTimeMillis() - creationDate;
|
||||
time = time / 1000 / 60 / 60;
|
||||
return time < 12;
|
||||
}
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean validateForPermission(String sessionKey, String permission) {
|
||||
try {
|
||||
ResultSet rs = sqLite.getData("SELECT UUID FROM Sessions WHERE SessionKey = \"" + sessionKey + "\"");
|
||||
if (rs.next()) {
|
||||
UUID uuid = UUID.fromString(rs.getString("UUID"));
|
||||
IPermissionUser player = CloudNetDriver.getInstance().getPermissionManagement().getUser(uuid);
|
||||
if (player == null) return false;
|
||||
boolean hasPermission = player.hasPermission(permission).asBoolean();
|
||||
if (!hasPermission) {
|
||||
for (IPermissionGroup group : CloudNetDriver.getInstance().getPermissionManagement().getGroups(player)) {
|
||||
if (group.hasPermission(permission).asBoolean()) return true;
|
||||
}
|
||||
}
|
||||
return hasPermission;
|
||||
}
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package de.craftix.cloudnetwebinterface.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
public class SQLite {
|
||||
|
||||
protected String path;
|
||||
protected Connection con;
|
||||
|
||||
public SQLite(String path) {
|
||||
File file = new File(path);
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
this.path = file.getAbsolutePath();
|
||||
connect();
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
if (isConnected()) return;
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
con = DriverManager.getConnection("jdbc:sqlite:" + path);
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (!isConnected()) return;
|
||||
try {
|
||||
con.close();
|
||||
con = null;
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
public boolean isConnected() { return con != null; }
|
||||
|
||||
public void insert(String qry) {
|
||||
if (!isConnected()) throw new NullPointerException("SQLite not connected");
|
||||
try {
|
||||
con.prepareStatement(qry).executeUpdate();
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
public ResultSet getData(String qry) {
|
||||
if (!isConnected()) throw new NullPointerException("SQLite not connected");
|
||||
try {
|
||||
return con.prepareStatement(qry).executeQuery();
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class WebFileManager implements HttpHandler {
|
||||
public WebFileManager(WebServerManager manager) {
|
||||
manager.getServer().createContext("/", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
String path = exchange.getRequestURI().getPath();
|
||||
byte[] response = getFileContent(path);
|
||||
|
||||
exchange.sendResponseHeaders(200, response.length);
|
||||
OutputStream out = exchange.getResponseBody();
|
||||
out.write(response);
|
||||
out.close();
|
||||
}
|
||||
|
||||
private byte[] getFileContent(String fileName) {
|
||||
try {
|
||||
File file = new File("modules/Webinterface/htdocs/" + fileName + "/");
|
||||
if (file.isDirectory()) file = new File("modules/Webinterface/htdocs/" + fileName + "/index.html");
|
||||
return Files.readAllBytes(file.toPath());
|
||||
}catch (Exception e) {
|
||||
System.err.println("Client tried to access invalid path: " + fileName);
|
||||
}
|
||||
return getFileContent("index.html");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import de.craftix.cloudnetwebinterface.utils.DBManager;
|
||||
import de.craftix.cloudnetwebinterface.webserver.api.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WebServerManager implements HttpHandler {
|
||||
private WebFileManager webFileManager;
|
||||
|
||||
private HttpServer server;
|
||||
|
||||
private final Map<String, Method> apis = new HashMap<>();
|
||||
private final Map<Method, WebApi> apiObjects = new HashMap<>();
|
||||
|
||||
public WebServerManager(int port) {
|
||||
try {
|
||||
server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||
server.createContext("/api").setHandler(this);
|
||||
webFileManager = new WebFileManager(this);
|
||||
server.start();
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() { server.stop(0); }
|
||||
|
||||
public HttpServer getServer() { return server; }
|
||||
public WebFileManager getWebFileManager() { return webFileManager; }
|
||||
|
||||
public static byte[] getApiTester() {
|
||||
return ("<!DOCTYPE html>\n" +
|
||||
"<html lang=\"en\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <title>Http Request</title>\n" +
|
||||
" <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3\" crossorigin=\"anonymous\">\n" +
|
||||
" <script src=\"https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js\" integrity=\"sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB\" crossorigin=\"anonymous\"></script>\n" +
|
||||
" <script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js\" integrity=\"sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13\" crossorigin=\"anonymous\"></script>\n" +
|
||||
"\n" +
|
||||
" <style>\n" +
|
||||
" .hover:hover {\n" +
|
||||
" cursor: pointer;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" .unselectable {\n" +
|
||||
" -webkit-touch-callout: none;\n" +
|
||||
" -webkit-user-select: none;\n" +
|
||||
" -moz-user-select: none;\n" +
|
||||
" -ms-user-select: none;\n" +
|
||||
" user-select: none;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" #url_start {\n" +
|
||||
" border-top-right-radius: 0;\n" +
|
||||
" border-bottom-right-radius: 0;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" #url, #apikey {\n" +
|
||||
" border-top-left-radius: 0;\n" +
|
||||
" border-bottom-left-radius: 0;\n" +
|
||||
" }\n" +
|
||||
" </style>\n" +
|
||||
"<body>\n" +
|
||||
"\n" +
|
||||
"<section style=\"margin-top: 20px\" id=\"options\">\n" +
|
||||
" <p class=\"text-center unselectable\" style=\"font-size: 50px\">Send Http Request</p>\n" +
|
||||
"\n" +
|
||||
" <form class=\"container\">\n" +
|
||||
" <div class=\"row\">\n" +
|
||||
" <div class=\"col\">\n" +
|
||||
" <label for=\"url\">URL</label>\n" +
|
||||
" <div class=\"input-group mb-3\">\n" +
|
||||
" <div class=\"input-group-prepend\">\n" +
|
||||
" <span class=\"input-group-text\" id=\"url_start\"></span>\n" +
|
||||
" </div>\n" +
|
||||
" <input type=\"text\" class=\"form-control\" id=\"url\" aria-describedby=\"url_start\">\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"dropdown col col-md-2\">\n" +
|
||||
" <label for=\"method\" class=\"unselectable\">Select Method</label>\n" +
|
||||
" <input class=\"btn btn-secondary dropdown-toggle form-control unselectable\" type=\"button\" id=\"method\" data-bs-toggle=\"dropdown\" aria-expanded=\"false\" value=\"GET\">\n" +
|
||||
" <ul class=\"dropdown-menu\" aria-labelledby=\"method\">\n" +
|
||||
" <li><a class=\"dropdown-item hover unselectable\" id=\"GET\">GET</a></li>\n" +
|
||||
" <li><a class=\"dropdown-item hover unselectable\" id=\"POST\">POST</a></li>\n" +
|
||||
" <li><a class=\"dropdown-item hover unselectable\" id=\"PUT\">PUT</a></li>\n" +
|
||||
" <li><a class=\"dropdown-item hover unselectable\" id=\"DELETE\">DELETE</a></li>\n" +
|
||||
" </ul>\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"row\">\n" +
|
||||
" <label for=\"apikey\">Session Key</label>\n" +
|
||||
" <input type=\"text\" class=\"form-control\" id=\"apikey\">\n" +
|
||||
" </div>\n" +
|
||||
" <button type=\"button\" class=\"btn btn-primary unselectable\" id=\"send\">Send</button>\n" +
|
||||
" </form>\n" +
|
||||
"</section>\n" +
|
||||
"\n" +
|
||||
"<br><br>\n" +
|
||||
"\n" +
|
||||
"<section id=\"response\" class=\"container\"></section>\n" +
|
||||
"\n" +
|
||||
"<script>\n" +
|
||||
" const url = new URL(window.location.href);\n" +
|
||||
" const startUrl = url.protocol + \"//\" + url.hostname + \":\" + url.port + \"/\";\n" +
|
||||
" document.getElementById(\"url_start\").innerHTML = startUrl;\n" +
|
||||
"\n" +
|
||||
" let method = \"GET\";\n" +
|
||||
"\n" +
|
||||
" const dropdowns = document.getElementsByClassName(\"dropdown-item\");\n" +
|
||||
" for (let i = 0; i < dropdowns.length; i++) {\n" +
|
||||
" const dropdown = dropdowns[i];\n" +
|
||||
" dropdown.onclick = function () {\n" +
|
||||
" method = dropdown.id;\n" +
|
||||
" document.getElementById(\"method\").value = method;\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" document.getElementById(\"send\").onclick = async function () {\n" +
|
||||
" const url = startUrl + document.getElementById(\"url\").value;\n" +
|
||||
" const sessionKey = document.getElementById(\"apikey\").value;\n" +
|
||||
" const response = await (await fetch(url, {\n" +
|
||||
" method: method,\n" +
|
||||
" mode: 'cors',\n" +
|
||||
" cache: 'no-cache',\n" +
|
||||
" credentials: 'same-origin',\n" +
|
||||
" headers: {\n" +
|
||||
" 'Content-Type': 'application/json',\n" +
|
||||
" 'Authorization': sessionKey\n" +
|
||||
" },\n" +
|
||||
" redirect: 'follow',\n" +
|
||||
" referrerPolicy: 'no-referrer',\n" +
|
||||
" })).text();\n" +
|
||||
" document.getElementById(\"response\").innerHtml = response;\n" +
|
||||
" }\n" +
|
||||
"</script>\n" +
|
||||
"\n" +
|
||||
"</body>\n" +
|
||||
"</html>").getBytes();
|
||||
}
|
||||
public static byte[] getErrorMessage() {
|
||||
return "ERROR: API-Route does not exsist".getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
String path = exchange.getRequestURI().getPath();
|
||||
if (path.equalsIgnoreCase("/api/tester")) {
|
||||
byte[] response = getApiTester();
|
||||
exchange.sendResponseHeaders(200, response.length);
|
||||
OutputStream out = exchange.getResponseBody();
|
||||
out.write(response);
|
||||
out.close();
|
||||
} else if (apis.containsKey(path)) {
|
||||
Method api = apis.get(path);
|
||||
String method = api.isAnnotationPresent(SetMethod.class) ? api.getAnnotation(SetMethod.class).value().toString() : RequestMethod.GET.toString();
|
||||
if (!exchange.getRequestMethod().equals(method)) {
|
||||
byte[] response = getErrorMessage();
|
||||
exchange.sendResponseHeaders(200, response.length);
|
||||
OutputStream out = exchange.getResponseBody();
|
||||
out.write(response);
|
||||
out.close();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean authorized = api.isAnnotationPresent(Authorized.class);
|
||||
if (authorized) {
|
||||
Authorized auth = api.getAnnotation(Authorized.class);
|
||||
String sessionKey = exchange.getRequestHeaders().getFirst("Authorization");
|
||||
if (DBManager.validateSession(sessionKey) && DBManager.validateForPermission(sessionKey, auth.permission())) {
|
||||
try {
|
||||
api.invoke(apiObjects.get(api), exchange);
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
}else {
|
||||
exchange.sendResponseHeaders(401, -1);
|
||||
}
|
||||
}else {
|
||||
try {
|
||||
api.invoke(apiObjects.get(api), exchange);
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
}else {
|
||||
byte[] response = getErrorMessage();
|
||||
exchange.sendResponseHeaders(200, response.length);
|
||||
OutputStream out = exchange.getResponseBody();
|
||||
out.write(response);
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getBytesFromStream(InputStream in) {
|
||||
try {
|
||||
if (in == null) throw new NullPointerException("InputStream cannot be null");
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
int nRead;
|
||||
byte[] data = new byte[16384];
|
||||
|
||||
while ((nRead = in.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
|
||||
return buffer.toByteArray();
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public static List<String> getFileLines(File file) {
|
||||
try {
|
||||
return Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public void addApi(WebApi api) {
|
||||
if (!api.getClass().isAnnotationPresent(Route.class)) throw new IllegalStateException("API does not have a Route");
|
||||
String route = api.getClass().getAnnotation(Route.class).value();
|
||||
|
||||
for (Method m : api.getClass().getMethods()) {
|
||||
if (m.getParameters().length == 0) continue;
|
||||
if (!m.getParameters()[0].getType().equals(HttpExchange.class)) continue;
|
||||
String r = route + (m.isAnnotationPresent(Route.class) ? m.getAnnotation(Route.class).value() : "");
|
||||
apis.put(r, m);
|
||||
apiObjects.put(m, api);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.api;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Authorized {
|
||||
String permission() default "web.use";
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.api;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpQuery {
|
||||
private final Map<String, String> variables = new HashMap<>();
|
||||
|
||||
public HttpQuery(HttpExchange exchange) {
|
||||
if (!exchange.getRequestURI().toString().contains("?")) return;
|
||||
String raw = exchange.getRequestURI().getQuery();
|
||||
String[] vars = raw.split("&");
|
||||
for (String var : vars) {
|
||||
String[] param = var.split("=");
|
||||
if (param.length == 0) return;
|
||||
if (param.length == 1) variables.put(param[0].toLowerCase(), "");
|
||||
else variables.put(param[0].toLowerCase(), param[1]);
|
||||
}
|
||||
}
|
||||
|
||||
public String getVariable(String name) { return variables.get(name.toLowerCase()) != null ? variables.get(name.toLowerCase()) : ""; }
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.api;
|
||||
|
||||
public enum LogicResult {
|
||||
|
||||
CONTINUE(100),
|
||||
SWITCHING_PROTOCOL(101),
|
||||
@Deprecated PROCESSING(102),
|
||||
EARLY_HINTS(103),
|
||||
|
||||
OK(200),
|
||||
CREATED(201),
|
||||
ACCEPTED(202),
|
||||
NOT_AUTHORIZED_CONTENT(203),
|
||||
NO_CONTENT(204),
|
||||
RESET_CONTENT(205),
|
||||
PARTIAL_CONTENT(206),
|
||||
@Deprecated ALREADY_REPORTED(208),
|
||||
@Deprecated IM_USED(226),
|
||||
|
||||
MULTIPLE_CHOICE(300),
|
||||
MOVED_PERMANENTLY(301),
|
||||
FOUND(302),
|
||||
SEE_OTHER(303),
|
||||
NOT_MODIFIED(304),
|
||||
@Deprecated USE_PROXY(305),
|
||||
@Deprecated UNUSED(306),
|
||||
TEMPORARY_REDIRECT(307),
|
||||
PERMANENT_REDIRECT(308),
|
||||
|
||||
BAD_REQUEST(400),
|
||||
UNAUTHORIZED(401),
|
||||
PAYMENT_REQUIRED(402),
|
||||
FORBIDDEN(403),
|
||||
NOT_FOUND(404),
|
||||
METHOD_NOT_ALLOWED(405),
|
||||
NOT_ACCEPTABLE(406),
|
||||
PROXY_AUTHORIZATION_REQUIRED(407),
|
||||
REQUEST_TIMEOUT(408),
|
||||
CONFLICT(409),
|
||||
GONE(410),
|
||||
LENGTH_REQUIRED(411),
|
||||
PRECONDITION_FAILED(412),
|
||||
PRECONDITION_TOO_LARGE(413),
|
||||
URI_TO_LONG(414),
|
||||
UNSUPPORTED_MEDIA_TYPE(415),
|
||||
REQUESTED_RANGE_NOT_SATISFIABLE(416),
|
||||
EXPECTATION_FAILED(417),
|
||||
@Deprecated MISDIRECTED_REQUEST(421),
|
||||
UPGRADE_REQUIRED(426),
|
||||
PRECONDITION_REQUIRED(428),
|
||||
TOO_MANY_REQUESTS(429),
|
||||
REQUEST_HEADER_FIELDS_TOO_LARGE(431),
|
||||
UNAVAILABLE_FOR_LEGAL_REASONS(451),
|
||||
|
||||
INTERNAL_SERVER_ERROR(500),
|
||||
NOT_IMPLEMENTED(501),
|
||||
BAD_GATEWAY(502),
|
||||
SERVICE_UNAVAILABLE(503),
|
||||
GATEWAY_TIMEOUT(504),
|
||||
HTTP_VERSION_NOT_SUPPORTED(505),
|
||||
VARIANT_ALSO_NEGOTIATES(506),
|
||||
INSUFFICIENT_STORAGE(507),
|
||||
LOOP_DETECTED(508),
|
||||
NOT_EXTENDED(510),
|
||||
NETWORK_AUTHENTICATION_REQUIRED(511);
|
||||
|
||||
private final int code;
|
||||
LogicResult(int code) { this.code = code; }
|
||||
public int getCode() { return code; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.api;
|
||||
|
||||
public enum RequestMethod {
|
||||
GET("GET"),
|
||||
POST("POST"),
|
||||
PUT("PUT"),
|
||||
DELETE("DELETE");
|
||||
|
||||
private final String type;
|
||||
|
||||
RequestMethod(String type) { this.type = type; }
|
||||
|
||||
public String getType() { return type; }
|
||||
|
||||
@Override
|
||||
public String toString() { return type; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.api;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface Route {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.api;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface SetMethod {
|
||||
RequestMethod value() default RequestMethod.GET;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.craftix.cloudnetwebinterface.webserver.WebServerManager;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class WebApi {
|
||||
|
||||
protected void sendDataAsJson(HttpExchange exchange, Serializable data) {
|
||||
if (data instanceof String) sendData(exchange, ((String) data).getBytes());
|
||||
else sendData(exchange, new Gson().toJson(data).getBytes());
|
||||
}
|
||||
|
||||
protected void sendData(HttpExchange exchange, byte[] data) {
|
||||
sendData(exchange, LogicResult.OK, data);
|
||||
}
|
||||
|
||||
protected <T> T getDataFromBody(HttpExchange exchange, Type type) {
|
||||
byte[] data = WebServerManager.getBytesFromStream(exchange.getRequestBody());
|
||||
Gson gson = new Gson();
|
||||
return gson.fromJson(new String(data), type);
|
||||
}
|
||||
|
||||
protected void sendData(HttpExchange exchange, LogicResult code, byte[] data) {
|
||||
try {
|
||||
if (data == null || data.length == 0) {
|
||||
exchange.sendResponseHeaders(code.getCode(), -1);
|
||||
OutputStream out = exchange.getResponseBody();
|
||||
out.write(new byte[0]);
|
||||
out.close();
|
||||
}
|
||||
exchange.sendResponseHeaders(code.getCode(), data.length);
|
||||
OutputStream out = exchange.getResponseBody();
|
||||
out.write(data);
|
||||
out.close();
|
||||
}catch (Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.routes;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.craftix.cloudnetwebinterface.webserver.WebServerManager;
|
||||
import de.craftix.cloudnetwebinterface.webserver.api.*;
|
||||
import de.dytanic.cloudnet.CloudNet;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
@Route("/api/console")
|
||||
public class ConsoleAPI extends WebApi {
|
||||
public static final int LOG_LENGTH = 500;
|
||||
|
||||
@Authorized
|
||||
public void getConsole(HttpExchange exchange) {
|
||||
String logFolder = "local/logs/";
|
||||
int logID = 0;
|
||||
while (true) {
|
||||
File log = new File(logFolder + "cloudnet." + logID + ".log");
|
||||
if (!log.exists()) {
|
||||
logID--;
|
||||
break;
|
||||
}
|
||||
logID++;
|
||||
}
|
||||
|
||||
File log = new File(logFolder + "cloudnet." + logID + ".log");
|
||||
List<String> lines = WebServerManager.getFileLines(log);
|
||||
if (lines.size() > LOG_LENGTH) lines = lines.subList(lines.size() - LOG_LENGTH, lines.size());
|
||||
StringBuilder out = new StringBuilder();
|
||||
lines.forEach((line) -> out.append("\n").append(line));
|
||||
sendData(exchange, out.toString().getBytes());
|
||||
}
|
||||
|
||||
@Authorized(permission = "web.command.main")
|
||||
@Route("/send")
|
||||
@SetMethod(RequestMethod.POST)
|
||||
public void sendConsoleCommand(HttpExchange exchange) {
|
||||
String cmd = new HttpQuery(exchange).getVariable("command");
|
||||
CloudNet.getInstance().getCommandMap().dispatchCommand(CloudNet.getInstance().getConsoleCommandSender(), cmd);
|
||||
sendData(exchange, LogicResult.OK, "Success".getBytes());
|
||||
}
|
||||
|
||||
@Authorized(permission = "web.reload")
|
||||
@Route("/reload")
|
||||
public void reload(HttpExchange exchange) {
|
||||
sendData(exchange, "Success".getBytes());
|
||||
CloudNet.getInstance().reload();
|
||||
}
|
||||
|
||||
@Authorized(permission = "web.stop")
|
||||
@Route("/stop")
|
||||
public void stop(HttpExchange exchange) {
|
||||
sendData(exchange, "Success".getBytes());
|
||||
CloudNet.getInstance().stop();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.routes;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.craftix.cloudnetwebinterface.webserver.api.*;
|
||||
import de.dytanic.cloudnet.driver.CloudNetDriver;
|
||||
import de.dytanic.cloudnet.driver.service.ServiceInfoSnapshot;
|
||||
import de.dytanic.cloudnet.ext.bridge.BridgeServiceProperty;
|
||||
import de.dytanic.cloudnet.ext.bridge.player.ServicePlayer;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
@Route("/api/players")
|
||||
public class PlayerAPI extends WebApi {
|
||||
|
||||
@Authorized
|
||||
@SetMethod(RequestMethod.GET)
|
||||
public void getPlayers(HttpExchange exchange) {
|
||||
Collection<ServiceInfoSnapshot> servers = CloudNetDriver.getInstance().getCloudServiceProvider().getCloudServices();
|
||||
List<Player> processedPlayers = new ArrayList<>();
|
||||
|
||||
for (ServiceInfoSnapshot service : servers) {
|
||||
Collection<ServicePlayer> players = service.getProperty(BridgeServiceProperty.PLAYERS).orElse(new ArrayList<>());
|
||||
for (ServicePlayer player : players) {
|
||||
if (processedPlayers.contains(new Player(player.getName(), null))) continue;
|
||||
String server = player.asBungee().getServer();
|
||||
processedPlayers.add(new Player(player.getName(), server != null ? server : "ausstehend..."));
|
||||
}
|
||||
}
|
||||
|
||||
sendDataAsJson(exchange, processedPlayers.toArray(new Player[0]));
|
||||
}
|
||||
|
||||
private static class Player implements Serializable {
|
||||
public final String name;
|
||||
public final String server;
|
||||
|
||||
public Player(String name, String server) {
|
||||
this.name = name;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Player player = (Player) o;
|
||||
return Objects.equals(name, player.name);
|
||||
}
|
||||
|
||||
@Override public int hashCode() { return Objects.hash(name, server); }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package de.craftix.cloudnetwebinterface.webserver.routes;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.craftix.cloudnetwebinterface.utils.DBManager;
|
||||
import de.craftix.cloudnetwebinterface.webserver.api.HttpQuery;
|
||||
import de.craftix.cloudnetwebinterface.webserver.api.Route;
|
||||
import de.craftix.cloudnetwebinterface.webserver.api.WebApi;
|
||||
import de.dytanic.cloudnet.driver.CloudNetDriver;
|
||||
import de.dytanic.cloudnet.ext.bridge.player.ICloudOfflinePlayer;
|
||||
import de.dytanic.cloudnet.ext.bridge.player.IPlayerManager;
|
||||
|
||||
@Route("/api/users")
|
||||
public class UserAPI extends WebApi {
|
||||
|
||||
@Route("/validate")
|
||||
public void validate(HttpExchange exchange) {
|
||||
String sessionKey = exchange.getRequestHeaders().getFirst("Authorization");
|
||||
boolean valid = DBManager.validateSession(sessionKey);
|
||||
sendData(exchange, (valid ? "true" : "false").getBytes());
|
||||
}
|
||||
|
||||
@Route("/login")
|
||||
public void login(HttpExchange exchange) {
|
||||
HttpQuery query = new HttpQuery(exchange);
|
||||
String username = query.getVariable("username");
|
||||
String password = query.getVariable("password");
|
||||
boolean valid = DBManager.checkLoginData(username, password);
|
||||
if (valid) {
|
||||
ICloudOfflinePlayer player = CloudNetDriver.getInstance().getServicesRegistry().getFirstService(IPlayerManager.class).getFirstOfflinePlayer(username);
|
||||
if (player == null) sendData(exchange, "false".getBytes());
|
||||
else sendData(exchange, DBManager.createSessionKey(player.getUniqueId()).getBytes());
|
||||
}else {
|
||||
sendData(exchange, "false".getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
10
Plugins/CloudNetWebinterface/src/main/resources/module.json
Normal file
10
Plugins/CloudNetWebinterface/src/main/resources/module.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"group": "de.craftix",
|
||||
"name": "Webinterface",
|
||||
"version": "1.0-SNAPSHOT",
|
||||
"main": "de.craftix.cloudnetwebinterface.CloudNetWebinterface",
|
||||
"description": "This is an Admin-Webinterface for CloudNet v3",
|
||||
"minimumCoreVersion": "3.4.0",
|
||||
"title": "Webinterface",
|
||||
"author": "Leon Hoppe"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
10
Plugins/CloudNetWebinterface/target/classes/module.json
Normal file
10
Plugins/CloudNetWebinterface/target/classes/module.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"group": "de.craftix",
|
||||
"name": "Webinterface",
|
||||
"version": "1.0-SNAPSHOT",
|
||||
"main": "de.craftix.cloudnetwebinterface.CloudNetWebinterface",
|
||||
"description": "This is an Admin-Webinterface for CloudNet v3",
|
||||
"minimumCoreVersion": "3.4.0",
|
||||
"title": "Webinterface",
|
||||
"author": "Leon Hoppe"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#Generated by Maven
|
||||
#Sat Dec 11 19:36:27 CET 2021
|
||||
version=1.0-SNAPSHOT
|
||||
groupId=de.craftix
|
||||
artifactId=CloudNetWebinterface
|
||||
@@ -0,0 +1,17 @@
|
||||
de\craftix\cloudnetwebinterface\webserver\routes\ConsoleAPI.class
|
||||
de\craftix\cloudnetwebinterface\webserver\api\SetMethod.class
|
||||
de\craftix\cloudnetwebinterface\webserver\WebFileManager.class
|
||||
de\craftix\cloudnetwebinterface\webserver\WebServerManager.class
|
||||
de\craftix\cloudnetwebinterface\webserver\api\HttpQuery.class
|
||||
de\craftix\cloudnetwebinterface\webserver\api\LogicResult.class
|
||||
de\craftix\cloudnetwebinterface\webserver\routes\PlayerAPI.class
|
||||
de\craftix\cloudnetwebinterface\utils\DBManager.class
|
||||
de\craftix\cloudnetwebinterface\utils\SQLite.class
|
||||
de\craftix\cloudnetwebinterface\webserver\api\RequestMethod.class
|
||||
de\craftix\cloudnetwebinterface\webserver\routes\PlayerAPI$Player.class
|
||||
de\craftix\cloudnetwebinterface\webserver\api\WebApi.class
|
||||
de\craftix\cloudnetwebinterface\CloudNetWebinterface.class
|
||||
de\craftix\cloudnetwebinterface\commands\MainCmd.class
|
||||
de\craftix\cloudnetwebinterface\webserver\routes\UserAPI.class
|
||||
de\craftix\cloudnetwebinterface\webserver\api\Authorized.class
|
||||
de\craftix\cloudnetwebinterface\webserver\api\Route.class
|
||||
@@ -0,0 +1,16 @@
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\api\Route.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\WebServerManager.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\utils\SQLite.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\WebFileManager.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\commands\MainCmd.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\api\HttpQuery.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\api\WebApi.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\routes\PlayerAPI.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\api\RequestMethod.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\api\LogicResult.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\CloudNetWebinterface.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\utils\DBManager.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\api\Authorized.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\routes\UserAPI.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\api\SetMethod.java
|
||||
D:\Programmierstuff\Plugins\CloudNetWebinterface\src\main\java\de\craftix\cloudnetwebinterface\webserver\routes\ConsoleAPI.java
|
||||
Binary file not shown.
Reference in New Issue
Block a user