Skip to content

Commit 3e99019

Browse files
committed
Add simple webpage and login test endpoint
1 parent 757d4bf commit 3e99019

File tree

5 files changed

+157
-0
lines changed

5 files changed

+157
-0
lines changed

build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ repositories {
1616
mavenCentral()
1717
}
1818

19+
sourceSets.main {
20+
resources {
21+
srcDir(file("web"))
22+
}
23+
}
24+
1925
dependencies {
2026
implementation("org.springframework.boot:spring-boot-starter-web")
2127
implementation("org.springframework.boot:spring-boot-loader")

src/main/java/io/papermc/patchroulette/controller/RESTController.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,13 @@ public ResponseEntity<String> cancelPatch(@RequestBody final PatchId input) {
115115
return ResponseEntity.ok("Patch cancelled.");
116116
}
117117

118+
@PreAuthorize("hasRole('PATCH')")
119+
@PostMapping(
120+
value = "/login",
121+
produces = "text/plain"
122+
)
123+
public ResponseEntity<String> login() {
124+
return ResponseEntity.ok("Your credentials are valid.");
125+
}
126+
118127
}

web/public/favicon.ico

66.1 KB
Binary file not shown.

web/public/index.html

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport"
6+
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
7+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
8+
<title>Patch Roulette</title>
9+
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
10+
<script src="index.js" defer></script>
11+
<link rel="icon" href="/favicon.ico">
12+
</head>
13+
<body class="bg-blue-500">
14+
<div id="login-container" class="flex items-center justify-center min-h-screen p-4">
15+
<form id="login-form" class="w-full max-w-md bg-white p-6 rounded-lg shadow-md">
16+
<div class="mb-4">
17+
<label for="username" class="block text-gray-700 text-sm font-bold mb-2">Username</label>
18+
<input type="text" id="username" name="username"
19+
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500">
20+
</div>
21+
<div class="mb-6">
22+
<label for="password" class="block text-gray-700 text-sm font-bold mb-2">Password</label>
23+
<input type="password" id="password" name="password"
24+
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500">
25+
</div>
26+
<button type="submit"
27+
class="w-full bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
28+
Login
29+
</button>
30+
</form>
31+
</div>
32+
<div id="content-container" class="flex items-center justify-center min-h-screen p-4" style="display: none;">
33+
<div class="w-full max-w-md bg-white p-6 rounded-lg shadow-md">
34+
<h2 class="text-2xl font-bold mb-4 text-gray-800">Patch Roulette</h2>
35+
<p id="user-info" class="mb-4 text-sm text-gray-600">Logged in as: <span id="logged-user" class="font-medium"></span></p>
36+
37+
<div class="mb-4">
38+
<label for="mcVersion" class="block text-gray-700 text-sm font-bold mb-2">Minecraft Version</label>
39+
<div class="flex space-x-2">
40+
<input type="text" id="mcVersion" placeholder="Enter Minecraft version"
41+
class="flex-grow px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500">
42+
<button id="select-version" type="button"
43+
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
44+
Select
45+
</button>
46+
</div>
47+
</div>
48+
49+
<div id="patches-container" class="mt-6 hidden">
50+
<h3 class="text-xl font-semibold mb-3 text-gray-800">Available Patches</h3>
51+
<div id="patches-list" class="border border-gray-200 rounded max-h-60 overflow-y-auto p-2">
52+
<!-- Patches will be loaded here -->
53+
<p class="text-gray-500 italic text-center py-4">Select a version to view available patches</p>
54+
</div>
55+
</div>
56+
57+
<div class="mt-6 flex justify-end">
58+
<button id="logout-button" type="button"
59+
class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
60+
Logout
61+
</button>
62+
</div>
63+
</div>
64+
</div>
65+
</body>
66+
</html>

web/public/index.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
let token = localStorage.getItem('token');
2+
if (token) {
3+
onLogin();
4+
}
5+
6+
function getUsername() {
7+
return atob(token).split(':')[0];
8+
}
9+
10+
const loginForm = document.getElementById('login-form');
11+
loginForm.addEventListener('submit', async (event) => {
12+
event.preventDefault();
13+
14+
const username = document.getElementById('username').value;
15+
const password = document.getElementById('password').value;
16+
const encoded = btoa(username + ":" + password);
17+
18+
const response = await fetch('/login', {
19+
method: 'POST',
20+
headers: {
21+
'Authorization': 'Basic ' + encoded
22+
}
23+
});
24+
25+
if (response.ok) {
26+
localStorage.setItem('token', encoded);
27+
await onLogin();
28+
} else {
29+
alert("Invalid login.");
30+
}
31+
});
32+
33+
async function onLogin() {
34+
document.getElementById('login-container').style.display = 'none';
35+
36+
const contentContainer = document.getElementById('content-container');
37+
contentContainer.style.display = 'flex';
38+
39+
document.getElementById('logged-user').innerText = getUsername();
40+
}
41+
42+
document.getElementById('select-version').addEventListener('click', async () => {
43+
const mcVersion = document.getElementById('mcVersion').value;
44+
if (!mcVersion) {
45+
alert("Please enter a Minecraft version.");
46+
return;
47+
}
48+
49+
const response = await fetch(`/get-available-patches?minecraftVersion=${mcVersion}`, {
50+
method: 'GET',
51+
headers: {
52+
'Authorization': 'Basic ' + localStorage.getItem('token')
53+
}
54+
});
55+
56+
if (response.ok) {
57+
const patches = await response.json();
58+
const patchesList = document.getElementById('patches-list');
59+
patchesList.innerHTML = '';
60+
61+
if (patches.length === 0) {
62+
patchesList.innerHTML = '<p class="text-gray-500 italic text-center py-4">No patches available for this version.</p>';
63+
} else {
64+
patches.forEach(patch => {
65+
const patchItem = document.createElement('div');
66+
patchItem.className = 'patch-item p-2 border-b border-gray-200';
67+
patchItem.innerText = patch;
68+
patchesList.appendChild(patchItem);
69+
});
70+
}
71+
72+
document.getElementById('patches-container').classList.remove('hidden');
73+
} else {
74+
alert("Failed to fetch patches. Please try again.");
75+
}
76+
});

0 commit comments

Comments
 (0)