summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorAlessio Vanni <vannilla@firemail.cc>2021-05-02 19:02:26 +0200
committerAlessio Vanni <vannilla@firemail.cc>2021-05-07 22:38:02 +0200
commit2bca4006c49c7b3875034f6fd6530cbcfe20bcb1 (patch)
tree391f0b63a81385b8ad7fabd7cb120c4de9a86ef6 /contrib
parenta5082240f035f1851715771b0265e25088bb687c (diff)
[FCFSD] Provide a better user experience
The motivations behind these changes are as following. To begin with, at the most superficial level, the form is given a better appearance, instead of some plain XHTML. Additionally, the served pages can be substituted with something else by using an entry in the configuration value, altough with some limitations. The page listing all the registered zones has been removed in favour of a search function. A configuration entry could've been used to let service operators choose between showing the full listing or not, but at the same time, being presented with a (possibly) giant list of names is not that great from a usability point of view. Having a search function is, at the very least, faster than having to wait for the full list to be displayed before being able to use the user agent's page search feature. Other than the above, people registering names with the service might not want to be known by everyone. Even though checking if a certain name or key was registered already can be known simply by querying the service, it's not straightforward to associate a name with a specific key (or viceversa). Last but not least, the service was restructured to be more "route-oriented" instead of the traditional (X)HTML document format. The main purpose of this change is to decouple usage of the service from the tools used to access it. With a traditional document, users are pretty much forced to use a web browsers as data submission is carried through the standard HTML form handling. Now, it is possible to access the service using any tool capable of speaking HTTP, regardless of wether it's a web browser, cURL or even a custom tool specific for this service. Another advantage of this approach is that it allows adding "layers" to the service, for example an authentication check before letting users register a name. As long as the layer immediately on top of the service is able to send some JSON using HTTP, there is no need to have users access the service itself: just put a "proxy" inbetween and run the service locally, while the proxy handles other administrative tasks before a name can be registered. By using layers, the service can keep being small feature-wise (i.e. provide only searching and registering), while everything else is provided by other applications, including access through protocols other than HTTP.
Diffstat (limited to 'contrib')
-rw-r--r--contrib/Makefile.am3
-rw-r--r--contrib/fcfsd/fcfsd-forbidden.html11
-rw-r--r--contrib/fcfsd/fcfsd-index.html345
-rw-r--r--contrib/fcfsd/fcfsd-notfound.html11
4 files changed, 370 insertions, 0 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index 331620586..f42fb684d 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -11,6 +11,9 @@ dist_pkgdata_DATA = \
gns/def.tex \
gns/gns-form-fields.xml \
gns/gns-form.xslt \
+ fcfsd/fcfsd-index.html \
+ fcfsd/fcfsd-notfound.html \
+ fcfsd/fcfsd-forbidden.html \
branding/logo/gnunet-logo.pdf \
branding/logo/gnunet-logo.png \
branding/logo/gnunet-logo-color.png \
diff --git a/contrib/fcfsd/fcfsd-forbidden.html b/contrib/fcfsd/fcfsd-forbidden.html
new file mode 100644
index 000000000..57ebb4c61
--- /dev/null
+++ b/contrib/fcfsd/fcfsd-forbidden.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Forbidden - GNUnet FCFS Authority Name Registration Service</title>
+ </head>
+ <body>
+ <h1>You can not access this resource.</h1>
+ </body>
+</html>
diff --git a/contrib/fcfsd/fcfsd-index.html b/contrib/fcfsd/fcfsd-index.html
new file mode 100644
index 000000000..3fa71d7c8
--- /dev/null
+++ b/contrib/fcfsd/fcfsd-index.html
@@ -0,0 +1,345 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>GNUnet FCFS Authority Name Registration Service</title>
+ <style>
+ html {
+ box-sizing: border-box;
+ font-family: sans-serif;
+ }
+
+ *, *:before, *:after {
+ box-sizing: inherit;
+ }
+
+ header {
+ width: 800px;
+ margin: 0 auto;
+ }
+
+ main {
+ width: 800px;
+ margin: 0 auto;
+ }
+
+ section h4 {
+ text-align: center;
+ width: 100%;
+ }
+
+ section input {
+ width: 100%;
+ padding: 8px 17px;
+ font-size: 1rem;
+ border: 1px solid #aaa;
+ border-radius: 7px;
+ background-color: white;
+ margin-bottom: 7px;
+ }
+
+ section input:focus {
+ box-shadow: 0px 0px 5px 3px lightblue;
+ }
+
+ section button {
+ font-size: 1rem;
+ font-weight: bold;
+ background-color: #8b008b;
+ color: white;
+ border: none;
+ padding: 7px;
+ }
+
+ section button:hover {
+ background-color: #bf00bf;
+ }
+
+ section button:disabled {
+ background-color: gray;
+ }
+
+ section h3 {
+ text-align: center;
+ width: 100%;
+ }
+
+ section small {
+ display: block;
+ margin-bottom: 5px;
+ }
+
+ .error-message {
+ color: red;
+ }
+
+ .success-message {
+ color: green;
+ }
+
+ @media screen and (max-width: 991px) {
+ header, main {
+ width: 100%;
+ }
+ }
+
+ footer {
+ margin-top: 30px;
+ text-align: center;
+ }
+
+ nav {
+ border-bottom: 1px solid black;
+ }
+
+ nav button {
+ font-size: 1rem;
+ font-weight: bold;
+ background-color: #ccc;
+ border: 1px solid black;
+ border-bottom: none;
+ border-top-right-radius: 7px;
+ border-top-left-radius: 7px;
+ padding: 7px;
+ }
+
+ nav button:hover {
+ background-color: #f0f0f0;
+ cursor: pointer;
+ }
+
+ nav button.selected {
+ background-color: #f0f0f0;
+ }
+ </style>
+ </head>
+ <body>
+ <header>
+ <h1>Name Registration Service</h1>
+ <p>Here you can register a name for your zone as part of this service's
+ delegated names.</p>
+ <p>The registration is based on a <em>First Come First Served</em>
+ policy, meaning a name is given to the first user requesting it.</p>
+ <p>Use the search bar below to see if your desired name is available and
+ then use the form to submit your registration request.</p>
+ </header>
+ <main>
+ <div class="form-container">
+ <nav>
+ <button id="tab-search">Search</button>
+ <button id="tab-register">Register</button>
+ </nav>
+ <section id="search-form">
+ <h4>Is your name available?</h4>
+ <h3 id="search-result-message"></h3>
+ <input id="search-name"
+ name="search-name"
+ type="text"
+ placeholder="Your name..."
+ autocomplete="name"
+ maxlength="63"
+ minlength="1">
+ <small class="error-message" id="search-name-error"></small>
+ <button>Search</button>
+ </section>
+ <section id="submit-form">
+ <h4>Submit a registration request</h4>
+ <h3 id="submit-result-message"></h3>
+ <input id="register-name"
+ name="register-name"
+ type="text"
+ placeholder="Your name..."
+ autocomplete="off"
+ maxlength="63"
+ minlength="1">
+ <input id="register-value"
+ name="register-value"
+ type="text"
+ placeholder="Your zone key..."
+ autocomplete="off"
+ minlength="1">
+ <small class="error-message" id="submit-error"></small>
+ <button>Submit</button>
+ </section>
+ </div>
+ </main>
+ <footer>
+ <a href="https://gnunet.org">GNUnet homepage</a>
+ </footer>
+ <script>
+ const buttons = document.querySelectorAll('nav button');
+ for (let i=0; i<buttons.length; ++i) {
+ buttons[i].onclick = function (e) {
+ let selected = document.querySelector('nav button.selected');
+ if (selected) {
+ selected.classList.toggle('selected');
+ }
+ e.target.classList.toggle('selected');
+
+ let show = '';
+ let hide = '';
+ if (e.target.id === 'tab-search') {
+ show = 'search-form';
+ hide = 'submit-form';
+ } else {
+ show = 'submit-form';
+ hide = 'search-form'
+ }
+
+ document.getElementById(hide).style.display = 'none';
+ document.getElementById(show).style.display = 'block';
+ };
+ }
+
+ buttons[0].click({target: buttons[0]});
+
+ const searchbutton = document.querySelector('#search-form button');
+ const submitbutton = document.querySelector('#submit-form button');
+
+ document.getElementById('search-name').onkeydown = function (e) {
+ if (e.key !== 'Enter') {
+ return;
+ }
+
+ searchbutton.click();
+ };
+
+ for (let n of ['register-name', 'register-value']) {
+ document.getElementById(n).onkeydown = function (e) {
+ if (e.key !== 'Enter') {
+ return;
+ }
+
+ submitbutton.click();
+ };
+ }
+
+ searchbutton.onclick = function (e) {
+ const searchname = document.getElementById('search-name');
+ const errormsg = document.getElementById('search-name-error');
+ const resultmsg = document.getElementById('search-result-message');
+
+ if (0 === searchname.value.length) {
+ errormsg.innerText = 'The field can not be empty';
+ searchname.setCustomValidity('The field can not be empty');
+ return;
+ }
+
+ if (-1 !== searchname.value.indexOf('.')) {
+ errormsg.innerText = 'The name can not contain dots';
+ searchname.setCustomValidity('The name can not contain dots');
+ return;
+ }
+
+ searchname.setCustomValidity('');
+ errormsg.innerText = '';
+
+ const name = searchname.value.toLowerCase();
+
+ searchbutton.disabled = true;
+ submitbutton.disabled = true;
+
+ fetch(`/search?name=${name}`)
+ .then(function (response) {
+ if (!response.ok) {
+ throw 'error';
+ }
+
+ return response.json()
+ })
+ .then(function (data) {
+ if ("true" === data.free) {
+ resultmsg.innerText = `'${name}' is available!`;
+ resultmsg.classList.add('success-message');
+ resultmsg.classList.remove('error-message');
+ } else {
+ resultmsg.innerText = `'${name}' is not available`;
+ resultmsg.classList.remove('success-message');
+ resultmsg.classList.add('error-message');
+ }
+ searchbutton.disabled = false;
+ submitbutton.disabled = false;
+ })
+ .catch(function (error) {
+ resultmsg.innerText = 'An error occurred while processing your query';
+ resultmsg.classList.remove('success-message');
+ resultmsg.classList.add('error-message');
+ console.error(error);
+ searchbutton.disabled = false;
+ submitbutton.disabled = false;
+ });
+ };
+
+ submitbutton.onclick = function (e) {
+ const registername = document.getElementById('register-name');
+ const registervalue = document.getElementById('register-value');
+ const errormsg = document.getElementById('submit-error');
+ const resultmsg = document.getElementById('submit-result-message');
+
+ let errors = 0;
+ let errs = [];
+
+ if (0 === registername.value.length) {
+ errs.push('The name field can not be empty');
+ registername.setCustomValidity('The name field can not be empty');
+ ++errors;
+ }
+ if (-1 !== registername.value.indexOf('.')) {
+ errs.push('The name can not contain dots');
+ registername.setCustomValidity('The name can not contain dots');
+ ++errors;
+ }
+ if (0 === registervalue.value.length) {
+ errs.push('The value field can not be empty');
+ registervalue.setCustomValidity('The value field can not be empty');
+ ++errors;
+ }
+
+ if (0 < errors) {
+ errormsg.innerHTML = 'The form contains invalid values:';
+ for (let e of errs) {
+ errormsg.innerHTML += '<br/>' + e;
+ }
+ return;
+ }
+
+ searchbutton.disabled = true;
+ submitbutton.disabled = true;
+
+ fetch('/register', {
+ method: 'POST',
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ name: registername.value,
+ key: registervalue.value,
+ }),
+ }).then(function (response) {
+ return response.json();
+ }).then(function (data) {
+ if (data.error === "false") {
+ resultmsg.innerText = `'${registername.value}' was registered successfully!`;
+ resultmsg.classList.add('success-message');
+ resultmsg.classList.remove('error-message');
+ } else {
+ resultmsg.innerText = `'${registername.value}' could not be registered! (${data.message})`;
+ resultmsg.classList.remove('success-message');
+ resultmsg.classList.add('error-message');
+ }
+ searchbutton.disabled = false;
+ submitbutton.disabled = false;
+ }).catch(function (error) {
+ resultmsg.innerText = 'An error occurred while processing your query';
+ resultmsg.classList.remove('success-message');
+ resultmsg.classList.add('error-message');
+ console.error(error);
+ searchbutton.disabled = false;
+ submitbutton.disabled = false;
+ });
+ };
+ </script>
+ </body>
+</html>
diff --git a/contrib/fcfsd/fcfsd-notfound.html b/contrib/fcfsd/fcfsd-notfound.html
new file mode 100644
index 000000000..676bf4a9a
--- /dev/null
+++ b/contrib/fcfsd/fcfsd-notfound.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Not Found - GNUnet FCFS Authority Name Registration Service</title>
+ </head>
+ <body>
+ <h1>The requested resource could not be found</h1>
+ </body>
+</html>