Skip to content

Commit

Permalink
Add easier to use form for PIN verification.
Browse files Browse the repository at this point in the history
  • Loading branch information
hgaiser committed Apr 22, 2024
1 parent 3ed1031 commit c1459d0
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

167 changes: 167 additions & 0 deletions assets/pin.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<!DOCTYPE html>
<html lang="en">

<head>
<title>Moonshine PIN</title>
<style>
/* Reset some default styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: Arial, sans-serif;
background-color: #1a1a1a;
color: #f2f2f2;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

#container {
background-color: #2b2b2b;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
}

#pin-instructions {
text-align: center;
margin-bottom: 1rem;
font-size: 1.1rem;
}

#pin-fields {
display: flex;
justify-content: center;
margin-bottom: 1.5rem;
}

.pin-field {
width: 3rem;
height: 3rem;
font-size: 2rem;
text-align: center;
margin: 0 0.5rem;
padding: 0.5rem;
border: none;
border-radius: 4px;
background-color: #3b3b3b;
color: #f2f2f2;
caret-color: transparent;
}

button {
display: block;
width: 100%;
padding: 0.75rem 1.5rem;
font-size: 1rem;
background-color: #2166b5;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}

button:hover {
background-color: #3d7fca;
}


button:disabled {
background-color: #ccc;
color: #515151;
cursor: default;
}

#error-message {
color: #ff6b6b;
text-align: center;
margin-top: 1rem;
display: none;
}

#success-message {
color: #60ed7a;
text-align: center;
margin-top: 1rem;
display: none;
}

</style>
</head>

<body>
<div id="container">
<div id="pin-instructions">Please fill in the PIN from Moonlight</div>

<form autocomplete="off" id="pin-form" action="pin" method="post">
<div id="pin-fields">
<input name="pin1" type="text" oninput="this.value=this.value.replace(/[^0-9]/g, '');" maxlength="1" class="pin-field" autofocus>
<input name="pin2" type="text" oninput="this.value=this.value.replace(/[^0-9]/g, '');" maxlength="1" class="pin-field">
<input name="pin3" type="text" oninput="this.value=this.value.replace(/[^0-9]/g, '');" maxlength="1" class="pin-field">
<input name="pin4" type="text" oninput="this.value=this.value.replace(/[^0-9]/g, '');" maxlength="1" class="pin-field">
</div>
<button id="submit" type="submit" disabled>Submit</button>
</form>

<div id="error-message">Error submitting PIN. Please try again or check the server logs.</div>
<div id="success-message">Successfully paired.</div>
</div>

<script>
const pin_instructions = document.getElementById("pin-instructions");
const pin_fields = document.querySelectorAll(".pin-field");
const pin_form = document.getElementById("pin-form");
const submit_button = document.getElementById("submit");
const error_message = document.getElementById("error-message");
const success_message = document.getElementById("success-message");

pin_form.addEventListener("submit", async (event) => {
event.preventDefault();

let pin = "";
for (const field of pin_fields) {
pin += field.value;
}

const response = await fetch(`/submit-pin?uniqueid=0123456789ABCDEF&pin=${pin}`, { method: 'GET' });

if (response.ok) {
error_message.style.display = "none";
success_message.style.display = "block";
} else {
error_message.style.display = "block";
success_message.style.display = "none";
}
});

for (const [index, field] of pin_fields.entries()) {
// Select the content when receiving focus.
field.addEventListener("focus", () => field.select());

// Move focus to next input when a number is filled in.
field.addEventListener("input", (e) => {
e.target.value = e.target.value.slice(0, 1);
if (e.target.value) {
if (index < pin_fields.length - 1) {
pin_fields[index + 1].focus();
}
}

let disabled = false;
for (const field of pin_fields) {
if (field.value === "") {
disabled = true;
}
}
submit_button.disabled = disabled;
});
}
</script>
</body>

</html>
15 changes: 13 additions & 2 deletions src/webserver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{net::{ToSocketAddrs, IpAddr}, collections::HashMap, convert::Infallibl

use async_shutdown::ShutdownManager;
use http_body_util::Full;
use hyper::{service::service_fn, Response, Request, body::Bytes, StatusCode, header::{self, HeaderValue}, Method};
use hyper::{body::Bytes, header::{self, HeaderValue}, service::service_fn, Method, Request, Response, StatusCode};
use hyper_util::rt::tokio::TokioIo;
use image::ImageFormat;
use network_interface::NetworkInterfaceConfig;
Expand Down Expand Up @@ -184,7 +184,8 @@ impl Webserver {
match (request.method(), request.uri().path()) {
(&Method::GET, "/serverinfo") => self.server_info(params, mac_address, https).await,
(&Method::GET, "/pair") => handle_pair_request(params, &self.server_certs, &self.client_manager).await,
(&Method::GET, "/pin") => self.pin(params).await,
(&Method::GET, "/pin") => self.pin().await,
(&Method::GET, "/submit-pin") => self.submit_pin(params).await,
(method, uri) => {
log::warn!("Unhandled {method} request with URI '{uri}'");
not_found()
Expand Down Expand Up @@ -347,6 +348,16 @@ impl Webserver {

async fn pin(
&self,
) -> Response<Full<Bytes>> {
let content = include_bytes!("../../assets/pin.html");
let mut response = Response::new(Full::new(Bytes::from_static(content)));
response.headers_mut().insert(header::CONTENT_TYPE, HeaderValue::from_static("text/html; charset=UTF-8"));

response
}

async fn submit_pin(
&self,
params: HashMap<String, String>,
) -> Response<Full<Bytes>> {
let unique_id = match params.get("uniqueid") {
Expand Down

0 comments on commit c1459d0

Please sign in to comment.