This commit is contained in:
@ -14,7 +14,7 @@ Handlebars.registerHelper("humangender", function (value, options) {
|
||||
}
|
||||
});
|
||||
|
||||
// Deprecated since version 0.8.0
|
||||
// Deprecated since version 0.8.0
|
||||
Handlebars.registerHelper("formatDate", function (datetime, format) {
|
||||
return new Date(datetime).toLocaleString();
|
||||
});
|
||||
@ -26,9 +26,8 @@ Handlebars.registerHelper("formatDate", function (datetime, format) {
|
||||
document.getElementById("sitename").innerText = title;
|
||||
}
|
||||
|
||||
|
||||
const cc = document.getElementById("custom_data")
|
||||
const ccc = document.getElementById("custom_data_cont")
|
||||
const cc = document.getElementById("custom_data");
|
||||
const ccc = document.getElementById("custom_data_cont");
|
||||
|
||||
function setCustomCard(content) {
|
||||
if (!content) {
|
||||
@ -40,8 +39,8 @@ Handlebars.registerHelper("formatDate", function (datetime, format) {
|
||||
}
|
||||
}
|
||||
|
||||
const error_cont = document.getElementById("error_cont")
|
||||
const error_msg = document.getElementById("error_msg")
|
||||
const error_cont = document.getElementById("error_cont");
|
||||
const error_msg = document.getElementById("error_msg");
|
||||
|
||||
function catchError(error) {
|
||||
error_cont.style.display = "";
|
||||
@ -50,40 +49,53 @@ Handlebars.registerHelper("formatDate", function (datetime, format) {
|
||||
}
|
||||
|
||||
async function renderUser() {
|
||||
console.log("Rendering User")
|
||||
setTitle("User")
|
||||
const listt = Handlebars.compile(document.getElementById("template-user-list").innerText)
|
||||
console.log("Rendering User");
|
||||
setTitle("User");
|
||||
const listt = Handlebars.compile(
|
||||
document.getElementById("template-user-list").innerText
|
||||
);
|
||||
|
||||
async function loadList() {
|
||||
let data = await request("/api/admin/user", "GET");
|
||||
tableb.innerHTML = listt({
|
||||
users: data
|
||||
})
|
||||
users: data,
|
||||
});
|
||||
}
|
||||
|
||||
window.userOnChangeType = (id) => {
|
||||
request("/api/admin/user?id=" + id, "PUT").then(() => loadList()).catch(catchError)
|
||||
}
|
||||
request("/api/admin/user?id=" + id, "PUT")
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
};
|
||||
|
||||
window.deleteUser = (id) => {
|
||||
request("/api/admin/user?id=" + id, "DELETE").then(() => loadList()).catch(catchError)
|
||||
}
|
||||
request("/api/admin/user?id=" + id, "DELETE")
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
};
|
||||
|
||||
await loadList();
|
||||
}
|
||||
|
||||
async function renderPermissions(client_id, client_name) {
|
||||
const listt = Handlebars.compile(document.getElementById("template-permission-list").innerText);
|
||||
const formt = Handlebars.compile(document.getElementById("template-permission-form").innerText);
|
||||
const listt = Handlebars.compile(
|
||||
document.getElementById("template-permission-list").innerText
|
||||
);
|
||||
const formt = Handlebars.compile(
|
||||
document.getElementById("template-permission-form").innerText
|
||||
);
|
||||
setCustomCard();
|
||||
async function loadList() {
|
||||
try {
|
||||
let data = await request("/api/admin/permission?client=" + client_id, "GET");
|
||||
let data = await request(
|
||||
"/api/admin/permission?client=" + client_id,
|
||||
"GET"
|
||||
);
|
||||
tableb.innerHTML = listt({
|
||||
client_id: client_id,
|
||||
client_name: client_name,
|
||||
permissions: data
|
||||
})
|
||||
permissions: data,
|
||||
});
|
||||
} catch (err) {
|
||||
catchError(err);
|
||||
}
|
||||
@ -91,11 +103,13 @@ Handlebars.registerHelper("formatDate", function (datetime, format) {
|
||||
|
||||
window.gotoClients = () => {
|
||||
renderClient();
|
||||
}
|
||||
};
|
||||
|
||||
window.deletePermission = (id) => {
|
||||
request("/api/admin/permission?id=" + id, "DELETE").then(() => loadList()).catch(catchError)
|
||||
}
|
||||
request("/api/admin/permission?id=" + id, "DELETE")
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
};
|
||||
|
||||
window.createPermission = () => {
|
||||
try {
|
||||
@ -103,41 +117,49 @@ Handlebars.registerHelper("formatDate", function (datetime, format) {
|
||||
} catch (err) {
|
||||
console.log("Err", err);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
window.createPermissionSubmit = (elm) => {
|
||||
console.log(elm);
|
||||
let data = getFormData(elm);
|
||||
console.log(data);
|
||||
request("/api/admin/permission", "POST", data).then(() => setCustomCard()).then(() => loadList()).catch(catchError)
|
||||
}
|
||||
await loadList()
|
||||
request("/api/admin/permission", "POST", data)
|
||||
.then(() => setCustomCard())
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
};
|
||||
await loadList();
|
||||
}
|
||||
|
||||
async function renderClient() {
|
||||
console.log("Rendering Client")
|
||||
setTitle("Client")
|
||||
console.log("Rendering Client");
|
||||
setTitle("Client");
|
||||
|
||||
const listt = Handlebars.compile(document.getElementById("template-client-list").innerText)
|
||||
const formt = Handlebars.compile(document.getElementById("template-client-form").innerText)
|
||||
const listt = Handlebars.compile(
|
||||
document.getElementById("template-client-list").innerText
|
||||
);
|
||||
const formt = Handlebars.compile(
|
||||
document.getElementById("template-client-form").innerText
|
||||
);
|
||||
|
||||
let clients = [];
|
||||
async function loadList() {
|
||||
let data = await request("/api/admin/client", "GET");
|
||||
clients = data;
|
||||
tableb.innerHTML = listt({
|
||||
clients: data
|
||||
})
|
||||
clients: data,
|
||||
});
|
||||
}
|
||||
|
||||
window.permissionsClient = (id) => {
|
||||
renderPermissions(id, clients.find(e => e._id === id).name);
|
||||
}
|
||||
renderPermissions(id, clients.find((e) => e._id === id).name);
|
||||
};
|
||||
|
||||
window.deleteClient = (id) => {
|
||||
request("/api/admin/client/id=" + id, "DELETE").then(() => loadList()).catch(catchError)
|
||||
}
|
||||
request("/api/admin/client/id=" + id, "DELETE")
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
};
|
||||
|
||||
window.createClientSubmit = (elm) => {
|
||||
console.log(elm);
|
||||
@ -146,45 +168,57 @@ Handlebars.registerHelper("formatDate", function (datetime, format) {
|
||||
let id = data.id;
|
||||
delete data.id;
|
||||
if (id && id !== "") {
|
||||
request("/api/admin/client?id=" + id, "PUT", data).then(() => setCustomCard()).then(() => loadList()).catch(catchError)
|
||||
request("/api/admin/client?id=" + id, "PUT", data)
|
||||
.then(() => setCustomCard())
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
} else {
|
||||
request("/api/admin/client", "POST", data).then(() => setCustomCard()).then(() => loadList()).catch(catchError)
|
||||
request("/api/admin/client", "POST", data)
|
||||
.then(() => setCustomCard())
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.createClient = () => {
|
||||
setCustomCard(formt());
|
||||
}
|
||||
};
|
||||
|
||||
window.editClient = (id) => {
|
||||
let client = clients.find(e => e._id === id);
|
||||
if (!client) return catchError(new Error("Client does not exist!!"))
|
||||
let client = clients.find((e) => e._id === id);
|
||||
if (!client) return catchError(new Error("Client does not exist!!"));
|
||||
setCustomCard(formt(client));
|
||||
}
|
||||
};
|
||||
|
||||
await loadList().catch(catchError);
|
||||
}
|
||||
|
||||
async function renderRegCode() {
|
||||
console.log("Rendering RegCode")
|
||||
setTitle("RegCode")
|
||||
console.log("Rendering RegCode");
|
||||
setTitle("RegCode");
|
||||
|
||||
const listt = Handlebars.compile(document.getElementById("template-regcode-list").innerText)
|
||||
const listt = Handlebars.compile(
|
||||
document.getElementById("template-regcode-list").innerText
|
||||
);
|
||||
|
||||
async function loadList() {
|
||||
let data = await request("/api/admin/regcode", "GET");
|
||||
tableb.innerHTML = listt({
|
||||
regcodes: data
|
||||
})
|
||||
regcodes: data,
|
||||
});
|
||||
}
|
||||
|
||||
window.deleteRegcode = (id) => {
|
||||
request("/api/admin/regcode?id=" + id, "DELETE").then(() => loadList()).catch(catchError)
|
||||
}
|
||||
request("/api/admin/regcode?id=" + id, "DELETE")
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
};
|
||||
|
||||
window.createRegcode = () => {
|
||||
request("/api/admin/regcode", "POST").then(() => loadList()).catch(catchError);
|
||||
}
|
||||
request("/api/admin/regcode", "POST")
|
||||
.then(() => loadList())
|
||||
.catch(catchError);
|
||||
};
|
||||
|
||||
await loadList().catch(catchError);
|
||||
}
|
||||
@ -192,14 +226,14 @@ Handlebars.registerHelper("formatDate", function (datetime, format) {
|
||||
const type = new URL(window.location.href).searchParams.get("type");
|
||||
switch (type) {
|
||||
case "client":
|
||||
renderClient().catch(catchError)
|
||||
break
|
||||
renderClient().catch(catchError);
|
||||
break;
|
||||
case "regcode":
|
||||
renderRegCode().catch(catchError)
|
||||
renderRegCode().catch(catchError);
|
||||
break;
|
||||
case "user":
|
||||
default:
|
||||
renderUser().catch(catchError);
|
||||
break;
|
||||
}
|
||||
})()
|
||||
})();
|
||||
|
@ -100,4 +100,4 @@ table td {
|
||||
.col.form-group {
|
||||
padding-left: 0 !important;
|
||||
margin-left: 5px !important;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
document.getElementById("hidden_form").action += window.location.href.split("?")[1];
|
||||
document.getElementById("hidden_form").action += window.location.href.split(
|
||||
"?"
|
||||
)[1];
|
||||
|
||||
function submit() {
|
||||
document.getElementById("hidden_form").submit();
|
||||
@ -10,9 +12,10 @@ document.getElementById("cancel").onclick = () => {
|
||||
if (uri === "$local") {
|
||||
uri = "/code";
|
||||
}
|
||||
window.location.href = uri + "?error=access_denied&state=" + u.searchParams.get("state");
|
||||
}
|
||||
window.location.href =
|
||||
uri + "?error=access_denied&state=" + u.searchParams.get("state");
|
||||
};
|
||||
|
||||
document.getElementById("allow").onclick = () => {
|
||||
submit()
|
||||
}
|
||||
submit();
|
||||
};
|
||||
|
@ -10,29 +10,34 @@
|
||||
hr {
|
||||
// display: block;
|
||||
// height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #b8b8b8;
|
||||
border: 0;
|
||||
border-top: 1px solid #b8b8b8;
|
||||
// margin: 1em 0;
|
||||
// padding: 0;
|
||||
// padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
background: #eee;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: Helvetica;
|
||||
background: #eee;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align:center;
|
||||
.title {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1, h3 { font-weight: 300; }
|
||||
h1,
|
||||
h3 {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
h1 { color: #636363; }
|
||||
h1 {
|
||||
color: #636363;
|
||||
}
|
||||
|
||||
ul {
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.permission {
|
||||
display: flex;
|
||||
@ -70,10 +75,11 @@ ul {
|
||||
}
|
||||
|
||||
.card {
|
||||
max-width: 480px;
|
||||
margin: 4em auto;
|
||||
padding: 3em 2em 2em 2em;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ebebeb;
|
||||
box-shadow: rgba(0,0,0,0.14902) 0px 1px 1px 0px,rgba(0,0,0,0.09804) 0px 1px 2px 0px;
|
||||
}
|
||||
max-width: 480px;
|
||||
margin: 4em auto;
|
||||
padding: 3em 2em 2em 2em;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ebebeb;
|
||||
box-shadow: rgba(0, 0, 0, 0.14902) 0px 1px 1px 0px,
|
||||
rgba(0, 0, 0, 0.09804) 0px 1px 2px 0px;
|
||||
}
|
||||
|
@ -1,114 +1,123 @@
|
||||
import sha from "sha512";
|
||||
import {
|
||||
setCookie,
|
||||
getCookie
|
||||
} from "cookie"
|
||||
import "inputs"
|
||||
import { setCookie, getCookie } from "cookie";
|
||||
import "inputs";
|
||||
|
||||
const loader = document.getElementById("loader")
|
||||
const container = document.getElementById("container")
|
||||
const usernameinput = document.getElementById("username")
|
||||
const usernamegroup = document.getElementById("usernamegroup")
|
||||
const uerrorfield = document.getElementById("uerrorfield")
|
||||
const passwordinput = document.getElementById("password")
|
||||
const passwordgroup = document.getElementById("passwordgroup")
|
||||
const perrorfield = document.getElementById("perrorfield")
|
||||
const nextbutton = document.getElementById("nextbutton")
|
||||
const loginbutton = document.getElementById("loginbutton")
|
||||
const loader = document.getElementById("loader");
|
||||
const container = document.getElementById("container");
|
||||
const usernameinput = document.getElementById("username");
|
||||
const usernamegroup = document.getElementById("usernamegroup");
|
||||
const uerrorfield = document.getElementById("uerrorfield");
|
||||
const passwordinput = document.getElementById("password");
|
||||
const passwordgroup = document.getElementById("passwordgroup");
|
||||
const perrorfield = document.getElementById("perrorfield");
|
||||
const nextbutton = document.getElementById("nextbutton");
|
||||
const loginbutton = document.getElementById("loginbutton");
|
||||
|
||||
let username;
|
||||
let salt;
|
||||
|
||||
usernameinput.focus()
|
||||
usernameinput.focus();
|
||||
|
||||
const loading = () => {
|
||||
container.style.filter = "blur(2px)";
|
||||
loader.style.display = "";
|
||||
}
|
||||
};
|
||||
|
||||
const loading_fin = () => {
|
||||
container.style.filter = ""
|
||||
container.style.filter = "";
|
||||
loader.style.display = "none";
|
||||
}
|
||||
};
|
||||
loading_fin();
|
||||
|
||||
usernameinput.onkeydown = (e) => {
|
||||
var keycode = e.keyCode ? e.keyCode : e.which;
|
||||
if (keycode === 13) nextbutton.click();
|
||||
clearError(uerrorfield);
|
||||
}
|
||||
};
|
||||
|
||||
nextbutton.onclick = async () => {
|
||||
loading();
|
||||
username = usernameinput.value;
|
||||
try {
|
||||
let res = await fetch("/api/user/login?type=username&username=" + username, {
|
||||
method: "POST"
|
||||
}).then(e => {
|
||||
if (e.status !== 200) throw new Error(e.statusText)
|
||||
return e.json()
|
||||
}).then(data => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error))
|
||||
let res = await fetch(
|
||||
"/api/user/login?type=username&username=" + username,
|
||||
{
|
||||
method: "POST",
|
||||
}
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.then((e) => {
|
||||
if (e.status !== 200) throw new Error(e.statusText);
|
||||
return e.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error));
|
||||
}
|
||||
return data;
|
||||
});
|
||||
salt = res.salt;
|
||||
usernamegroup.classList.add("invisible")
|
||||
passwordgroup.classList.remove("invisible")
|
||||
passwordinput.focus()
|
||||
usernamegroup.classList.add("invisible");
|
||||
passwordgroup.classList.remove("invisible");
|
||||
passwordinput.focus();
|
||||
} catch (e) {
|
||||
showError(uerrorfield, e.message)
|
||||
showError(uerrorfield, e.message);
|
||||
}
|
||||
loading_fin()
|
||||
}
|
||||
loading_fin();
|
||||
};
|
||||
|
||||
passwordinput.onkeydown = (e) => {
|
||||
var keycode = e.keyCode ? e.keyCode : e.which;
|
||||
if (keycode === 13) loginbutton.click();
|
||||
clearError(perrorfield);
|
||||
}
|
||||
};
|
||||
|
||||
loginbutton.onclick = async () => {
|
||||
loading();
|
||||
let pw = sha(salt + passwordinput.value);
|
||||
try {
|
||||
let { login, special, tfa } = await fetch("/api/user/login?type=password", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
username: usernameinput.value,
|
||||
password: pw
|
||||
}),
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}).then(e => {
|
||||
if (e.status !== 200) throw new Error(e.statusText)
|
||||
return e.json()
|
||||
}).then(data => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error))
|
||||
let { login, special, tfa } = await fetch(
|
||||
"/api/user/login?type=password",
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
username: usernameinput.value,
|
||||
password: pw,
|
||||
}),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
}
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.then((e) => {
|
||||
if (e.status !== 200) throw new Error(e.statusText);
|
||||
return e.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error));
|
||||
}
|
||||
return data;
|
||||
});
|
||||
|
||||
setCookie("login", login.token, new Date(login.expires).toUTCString());
|
||||
setCookie("special", special.token, new Date(special.expires).toUTCString());
|
||||
let d = new Date()
|
||||
d.setTime(d.getTime() + (30 * 24 * 60 * 60 * 1000)); //Keep the username 30 days
|
||||
setCookie(
|
||||
"special",
|
||||
special.token,
|
||||
new Date(special.expires).toUTCString()
|
||||
);
|
||||
let d = new Date();
|
||||
d.setTime(d.getTime() + 30 * 24 * 60 * 60 * 1000); //Keep the username 30 days
|
||||
setCookie("username", username, d.toUTCString());
|
||||
let url = new URL(window.location.href);
|
||||
let state = url.searchParams.get("state")
|
||||
let red = "/"
|
||||
let state = url.searchParams.get("state");
|
||||
let red = "/";
|
||||
|
||||
if (tfa) twofactor(tfa);
|
||||
else {
|
||||
if (state) {
|
||||
let base64 = url.searchParams.get("base64")
|
||||
if (base64)
|
||||
red = atob(state)
|
||||
else
|
||||
red = state
|
||||
let base64 = url.searchParams.get("base64");
|
||||
if (base64) red = atob(state);
|
||||
else red = state;
|
||||
}
|
||||
window.location.href = red;
|
||||
}
|
||||
@ -117,19 +126,19 @@ loginbutton.onclick = async () => {
|
||||
showError(perrorfield, e.message);
|
||||
}
|
||||
loading_fin();
|
||||
}
|
||||
};
|
||||
|
||||
function clearError(field) {
|
||||
field.innerText = "";
|
||||
field.classList.add("invisible")
|
||||
field.classList.add("invisible");
|
||||
}
|
||||
|
||||
function showError(field, error) {
|
||||
field.innerText = error;
|
||||
field.classList.remove("invisible")
|
||||
field.classList.remove("invisible");
|
||||
}
|
||||
|
||||
username = getCookie("username")
|
||||
username = getCookie("username");
|
||||
if (username) {
|
||||
usernameinput.value = username;
|
||||
|
||||
@ -138,10 +147,9 @@ if (username) {
|
||||
usernameinput.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
|
||||
function twofactor(tfa) {
|
||||
let list = tfa
|
||||
.map(entry => {
|
||||
.map((entry) => {
|
||||
switch (entry) {
|
||||
case 0: // OTC
|
||||
return "Authenticator App";
|
||||
@ -150,9 +158,9 @@ function twofactor(tfa) {
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
.filter(e => e !== undefined)
|
||||
.filter((e) => e !== undefined)
|
||||
.reduce((p, c) => p + `<li>${c}</li>`, "");
|
||||
|
||||
let tfl = document.getElementById("tflist");
|
||||
tfl.innerHTML = list;
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ form {
|
||||
padding: 3em 2em 2em 2em;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ebebeb;
|
||||
box-shadow: rgba(0, 0, 0, 0.14902) 0px 1px 1px 0px, rgba(0, 0, 0, 0.09804) 0px 1px 2px 0px;
|
||||
box-shadow: rgba(0, 0, 0, 0.14902) 0px 1px 1px 0px,
|
||||
rgba(0, 0, 0, 0.09804) 0px 1px 2px 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -50,35 +51,37 @@ form {
|
||||
height: 64px;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
top: 0; left: 0; bottom: 0; right: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.loader{
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
.loader {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
.loader:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
margin: 1px;
|
||||
border-radius: 50%;
|
||||
border: 5px solid #000000;
|
||||
border-color: #000000 transparent #000000 transparent;
|
||||
animation: loader 1.2s linear infinite;
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
margin: 1px;
|
||||
border-radius: 50%;
|
||||
border: 5px solid #000000;
|
||||
border-color: #000000 transparent #000000 transparent;
|
||||
animation: loader 1.2s linear infinite;
|
||||
}
|
||||
@keyframes loader {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
}
|
||||
@ -86,13 +89,13 @@ footer {
|
||||
footer p {
|
||||
color: #888;
|
||||
font-size: 13px;
|
||||
letter-spacing: .4px;
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: $primary;
|
||||
text-decoration: none;
|
||||
transition: all .2s ease;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
@ -102,11 +105,11 @@ footer a:hover {
|
||||
|
||||
footer img {
|
||||
width: 80px;
|
||||
transition: all .2s ease;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
footer img:hover {
|
||||
opacity: .83;
|
||||
opacity: 0.83;
|
||||
}
|
||||
|
||||
footer img:focus,
|
||||
|
@ -1,316 +1,434 @@
|
||||
import { h, Component, render } from "preact"
|
||||
import "inputs"
|
||||
import "./u2f-api-polyfill"
|
||||
import { h, Component, render } from "preact";
|
||||
import "inputs";
|
||||
import "./u2f-api-polyfill";
|
||||
|
||||
import sha from "sha512";
|
||||
import {
|
||||
setCookie,
|
||||
getCookie
|
||||
} from "cookie"
|
||||
import { setCookie, getCookie } from "cookie";
|
||||
|
||||
let appname = "test";
|
||||
|
||||
function Loader() {
|
||||
return <div class="loader_box" id="loader">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
return (
|
||||
<div class="loader_box" id="loader">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
class Username extends Component<{ username: string, onNext: (username: string, salt: string) => void }, { error: string, loading: boolean }> {
|
||||
username_input: HTMLInputElement;
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { error: undefined, loading: false }
|
||||
}
|
||||
class Username extends Component<
|
||||
{ username: string; onNext: (username: string, salt: string) => void },
|
||||
{ error: string; loading: boolean }
|
||||
> {
|
||||
username_input: HTMLInputElement;
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { error: undefined, loading: false };
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
this.setState({ loading: true });
|
||||
try {
|
||||
let res = await fetch("/api/user/login?type=username&username=" + this.username_input.value, {
|
||||
method: "POST"
|
||||
}).then(e => {
|
||||
if (e.status !== 200) throw new Error(e.statusText)
|
||||
return e.json()
|
||||
}).then(data => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error))
|
||||
}
|
||||
return data;
|
||||
async onClick() {
|
||||
this.setState({ loading: true });
|
||||
try {
|
||||
let res = await fetch(
|
||||
"/api/user/login?type=username&username=" +
|
||||
this.username_input.value,
|
||||
{
|
||||
method: "POST",
|
||||
}
|
||||
)
|
||||
.then((e) => {
|
||||
if (e.status !== 200) throw new Error(e.statusText);
|
||||
return e.json();
|
||||
})
|
||||
let salt = res.salt;
|
||||
this.props.onNext(this.username_input.value, salt);
|
||||
} catch (err) {
|
||||
this.setState({
|
||||
error: err.message
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error));
|
||||
}
|
||||
return data;
|
||||
});
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
let salt = res.salt;
|
||||
this.props.onNext(this.username_input.value, salt);
|
||||
} catch (err) {
|
||||
this.setState({
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.loading) return <Loader />
|
||||
return <div>
|
||||
render() {
|
||||
if (this.state.loading) return <Loader />;
|
||||
return (
|
||||
<div>
|
||||
<div class="floating group">
|
||||
<input onKeyDown={e => {
|
||||
let k = e.keyCode | e.which;
|
||||
if (k === 13) this.onClick();
|
||||
this.setState({ error: undefined })
|
||||
}} type="text" value={this.username_input ? this.username_input.value : this.props.username} autofocus ref={elm => elm ? this.username_input = elm : undefined} />
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Username or Email</label>
|
||||
{this.state.error ? <div class="error"> {this.state.error}</div> : undefined}
|
||||
<input
|
||||
onKeyDown={(e) => {
|
||||
let k = e.keyCode | e.which;
|
||||
if (k === 13) this.onClick();
|
||||
this.setState({ error: undefined });
|
||||
}}
|
||||
type="text"
|
||||
value={
|
||||
this.username_input
|
||||
? this.username_input.value
|
||||
: this.props.username
|
||||
}
|
||||
autofocus
|
||||
ref={(elm) => (elm ? (this.username_input = elm) : undefined)}
|
||||
/>
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Username or Email</label>
|
||||
{this.state.error ? (
|
||||
<div class="error"> {this.state.error}</div>
|
||||
) : undefined}
|
||||
</div>
|
||||
<button type="button" class="mdc-button mdc-button--raised spanned-btn" onClick={() => this.onClick()}>Next</button>
|
||||
</div>
|
||||
}
|
||||
<button
|
||||
type="button"
|
||||
class="mdc-button mdc-button--raised spanned-btn"
|
||||
onClick={() => this.onClick()}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum TFATypes {
|
||||
OTC,
|
||||
BACKUP_CODE,
|
||||
YUBI_KEY,
|
||||
APP_ALLOW
|
||||
OTC,
|
||||
BACKUP_CODE,
|
||||
YUBI_KEY,
|
||||
APP_ALLOW,
|
||||
}
|
||||
|
||||
interface TwoFactors {
|
||||
id: string;
|
||||
name: string;
|
||||
type: TFATypes;
|
||||
id: string;
|
||||
name: string;
|
||||
type: TFATypes;
|
||||
}
|
||||
|
||||
class Password extends Component<{ username: string, salt: string, onNext: (login: Token, special: Token, tfa: TwoFactors[]) => void }, { error: string, loading: boolean }> {
|
||||
password_input: HTMLInputElement;
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { error: undefined, loading: false }
|
||||
}
|
||||
class Password extends Component<
|
||||
{
|
||||
username: string;
|
||||
salt: string;
|
||||
onNext: (login: Token, special: Token, tfa: TwoFactors[]) => void;
|
||||
},
|
||||
{ error: string; loading: boolean }
|
||||
> {
|
||||
password_input: HTMLInputElement;
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { error: undefined, loading: false };
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
this.setState({
|
||||
loading: true
|
||||
});
|
||||
async onClick() {
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
|
||||
try {
|
||||
let pw = sha(this.props.salt + this.password_input.value);
|
||||
let { login, special, tfa } = await fetch("/api/user/login?type=password", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
username: this.props.username,
|
||||
password: pw
|
||||
}),
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}).then(e => {
|
||||
if (e.status !== 200) throw new Error(e.statusText)
|
||||
return e.json()
|
||||
}).then(data => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error))
|
||||
}
|
||||
return data;
|
||||
})
|
||||
this.props.onNext(login, special, tfa);
|
||||
} catch (err) {
|
||||
this.setState({ error: err.messagae });
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.loading) return <Loader />
|
||||
return <div>
|
||||
<div class="floating group" >
|
||||
<input onKeyDown={e => {
|
||||
let k = e.keyCode | e.which;
|
||||
if (k === 13) this.onClick();
|
||||
this.setState({ error: undefined })
|
||||
}} type="password" ref={(elm: HTMLInputElement) => {
|
||||
if (elm) {
|
||||
this.password_input = elm
|
||||
setTimeout(() => elm.focus(), 200)
|
||||
// elm.focus();
|
||||
}
|
||||
}
|
||||
} />
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Password</label>
|
||||
{this.state.error ? <div class="error"> {this.state.error}</div> : undefined}
|
||||
</div>
|
||||
<button type="button" class="mdc-button mdc-button--raised spanned-btn" onClick={() => this.onClick()}>Login</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
class TwoFactor extends Component<{ twofactors: TwoFactors[], next: (id: string, type: TFATypes) => void }, {}> {
|
||||
render() {
|
||||
let tfs = this.props.twofactors.map(fac => {
|
||||
let name: string;
|
||||
switch (fac.type) {
|
||||
case TFATypes.OTC:
|
||||
name = "Authenticator"
|
||||
break;
|
||||
case TFATypes.BACKUP_CODE:
|
||||
name = "Backup code";
|
||||
break;
|
||||
case TFATypes.APP_ALLOW:
|
||||
name = "Use App: %s"
|
||||
break;
|
||||
case TFATypes.YUBI_KEY:
|
||||
name = "Use Yubikey: %s"
|
||||
break;
|
||||
try {
|
||||
let pw = sha(this.props.salt + this.password_input.value);
|
||||
let { login, special, tfa } = await fetch(
|
||||
"/api/user/login?type=password",
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
username: this.props.username,
|
||||
password: pw,
|
||||
}),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((e) => {
|
||||
if (e.status !== 200) throw new Error(e.statusText);
|
||||
return e.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error));
|
||||
}
|
||||
return data;
|
||||
});
|
||||
this.props.onNext(login, special, tfa);
|
||||
} catch (err) {
|
||||
this.setState({ error: err.messagae });
|
||||
}
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
|
||||
name = name.replace("%s", fac.name ? fac.name : "");
|
||||
render() {
|
||||
if (this.state.loading) return <Loader />;
|
||||
return (
|
||||
<div>
|
||||
<div class="floating group">
|
||||
<input
|
||||
onKeyDown={(e) => {
|
||||
let k = e.keyCode | e.which;
|
||||
if (k === 13) this.onClick();
|
||||
this.setState({ error: undefined });
|
||||
}}
|
||||
type="password"
|
||||
ref={(elm: HTMLInputElement) => {
|
||||
if (elm) {
|
||||
this.password_input = elm;
|
||||
setTimeout(() => elm.focus(), 200);
|
||||
// elm.focus();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>Password</label>
|
||||
{this.state.error ? (
|
||||
<div class="error"> {this.state.error}</div>
|
||||
) : undefined}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="mdc-button mdc-button--raised spanned-btn"
|
||||
onClick={() => this.onClick()}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return <li onClick={() => {
|
||||
console.log("Click on Solution")
|
||||
this.props.next(fac.id, fac.type)
|
||||
}}>
|
||||
{name}
|
||||
class TwoFactor extends Component<
|
||||
{ twofactors: TwoFactors[]; next: (id: string, type: TFATypes) => void },
|
||||
{}
|
||||
> {
|
||||
render() {
|
||||
let tfs = this.props.twofactors.map((fac) => {
|
||||
let name: string;
|
||||
switch (fac.type) {
|
||||
case TFATypes.OTC:
|
||||
name = "Authenticator";
|
||||
break;
|
||||
case TFATypes.BACKUP_CODE:
|
||||
name = "Backup code";
|
||||
break;
|
||||
case TFATypes.APP_ALLOW:
|
||||
name = "Use App: %s";
|
||||
break;
|
||||
case TFATypes.YUBI_KEY:
|
||||
name = "Use Yubikey: %s";
|
||||
break;
|
||||
}
|
||||
|
||||
name = name.replace("%s", fac.name ? fac.name : "");
|
||||
|
||||
return (
|
||||
<li
|
||||
onClick={() => {
|
||||
console.log("Click on Solution");
|
||||
this.props.next(fac.id, fac.type);
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</li>
|
||||
})
|
||||
return <div>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<h1>Select one</h1>
|
||||
<ul>
|
||||
{tfs}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
<ul>{tfs}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class TFA_YubiKey extends Component<{ id: string, login: Token, special: Token, next: (login: Token, special: Token) => void }, {}> {
|
||||
// render() {
|
||||
// render() {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
enum Page {
|
||||
username,
|
||||
password,
|
||||
twofactor,
|
||||
yubikey
|
||||
username,
|
||||
password,
|
||||
twofactor,
|
||||
yubikey,
|
||||
}
|
||||
|
||||
interface Token {
|
||||
token: string;
|
||||
expires: string;
|
||||
token: string;
|
||||
expires: string;
|
||||
}
|
||||
|
||||
async function apiRequest(endpoint: string, method: "GET" | "POST" | "DELETE" | "PUT" = "GET", body: string = undefined) {
|
||||
return fetch(endpoint, {
|
||||
method,
|
||||
body,
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
}).then(e => {
|
||||
if (e.status !== 200) throw new Error(e.statusText)
|
||||
return e.json()
|
||||
}).then(data => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error))
|
||||
}
|
||||
return data;
|
||||
})
|
||||
async function apiRequest(
|
||||
endpoint: string,
|
||||
method: "GET" | "POST" | "DELETE" | "PUT" = "GET",
|
||||
body: string = undefined
|
||||
) {
|
||||
return fetch(endpoint, {
|
||||
method,
|
||||
body,
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
})
|
||||
.then((e) => {
|
||||
if (e.status !== 200) throw new Error(e.statusText);
|
||||
return e.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
return Promise.reject(new Error(data.error));
|
||||
}
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
class App extends Component<
|
||||
{},
|
||||
{
|
||||
page: Page;
|
||||
username: string;
|
||||
salt: string;
|
||||
twofactor: TwoFactors[];
|
||||
twofactor_id: string;
|
||||
}
|
||||
> {
|
||||
login: Token;
|
||||
special: Token;
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
page: Page.username,
|
||||
username: getCookie("username"),
|
||||
salt: undefined,
|
||||
twofactor: [],
|
||||
twofactor_id: null,
|
||||
};
|
||||
}
|
||||
|
||||
class App extends Component<{}, { page: Page, username: string, salt: string, twofactor: TwoFactors[], twofactor_id: string }> {
|
||||
login: Token;
|
||||
special: Token;
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { page: Page.username, username: getCookie("username"), salt: undefined, twofactor: [], twofactor_id: null }
|
||||
}
|
||||
setCookies() {
|
||||
setCookie(
|
||||
"login",
|
||||
this.login.token,
|
||||
new Date(this.login.expires).toUTCString()
|
||||
);
|
||||
setCookie(
|
||||
"special",
|
||||
this.special.token,
|
||||
new Date(this.special.expires).toUTCString()
|
||||
);
|
||||
}
|
||||
|
||||
setCookies() {
|
||||
setCookie("login", this.login.token, new Date(this.login.expires).toUTCString());
|
||||
setCookie("special", this.special.token, new Date(this.special.expires).toUTCString());
|
||||
}
|
||||
finish() {
|
||||
this.setCookies();
|
||||
let d = new Date();
|
||||
d.setTime(d.getTime() + 30 * 24 * 60 * 60 * 1000); //Keep the username 30 days
|
||||
setCookie("username", this.state.username, d.toUTCString());
|
||||
let url = new URL(window.location.href);
|
||||
let state = url.searchParams.get("state");
|
||||
let red = "/";
|
||||
|
||||
finish() {
|
||||
this.setCookies();
|
||||
let d = new Date()
|
||||
d.setTime(d.getTime() + (30 * 24 * 60 * 60 * 1000)); //Keep the username 30 days
|
||||
setCookie("username", this.state.username, d.toUTCString());
|
||||
let url = new URL(window.location.href);
|
||||
let state = url.searchParams.get("state")
|
||||
let red = "/"
|
||||
if (state) {
|
||||
let base64 = url.searchParams.get("base64");
|
||||
if (base64) red = atob(state);
|
||||
else red = state;
|
||||
}
|
||||
window.location.href = red;
|
||||
}
|
||||
|
||||
if (state) {
|
||||
let base64 = url.searchParams.get("base64")
|
||||
if (base64)
|
||||
red = atob(state)
|
||||
else
|
||||
red = state
|
||||
}
|
||||
window.location.href = red;
|
||||
}
|
||||
render() {
|
||||
let cont;
|
||||
switch (this.state.page) {
|
||||
case Page.username:
|
||||
cont = (
|
||||
<Username
|
||||
username={this.state.username}
|
||||
onNext={(username, salt) => {
|
||||
this.setState({ username, salt, page: Page.password });
|
||||
localStorage.setItem("username", username);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case Page.password:
|
||||
cont = (
|
||||
<Password
|
||||
username={this.state.username}
|
||||
salt={this.state.salt}
|
||||
onNext={(login, special, twofactor) => {
|
||||
this.login = login;
|
||||
this.special = special;
|
||||
this.setCookies();
|
||||
|
||||
render() {
|
||||
let cont;
|
||||
switch (this.state.page) {
|
||||
case Page.username:
|
||||
cont = <Username username={this.state.username} onNext={(username, salt) => {
|
||||
this.setState({ username, salt, page: Page.password })
|
||||
localStorage.setItem("username", username);
|
||||
}} />
|
||||
break;
|
||||
case Page.password:
|
||||
cont = <Password username={this.state.username} salt={this.state.salt} onNext={(login, special, twofactor) => {
|
||||
this.login = login;
|
||||
this.special = special;
|
||||
this.setCookies();
|
||||
|
||||
if (!twofactor) {
|
||||
if (!twofactor) {
|
||||
this.finish();
|
||||
} else {
|
||||
} else {
|
||||
this.setState({ twofactor, page: Page.twofactor });
|
||||
}
|
||||
}} />
|
||||
break;
|
||||
case Page.twofactor:
|
||||
cont = <TwoFactor twofactors={this.state.twofactor} next={async (id, type) => {
|
||||
if (type === TFATypes.YUBI_KEY) {
|
||||
let { request } = await apiRequest("/api/user/twofactor/yubikey", "GET");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case Page.twofactor:
|
||||
cont = (
|
||||
<TwoFactor
|
||||
twofactors={this.state.twofactor}
|
||||
next={async (id, type) => {
|
||||
if (type === TFATypes.YUBI_KEY) {
|
||||
let { request } = await apiRequest(
|
||||
"/api/user/twofactor/yubikey",
|
||||
"GET"
|
||||
);
|
||||
console.log(request);
|
||||
(window as any).u2f.sign(request.appId, [request.challenge], [request], async (response) => {
|
||||
let res = await apiRequest("/api/user/twofactor/yubikey", "PUT", JSON.stringify({ response }));
|
||||
if (res.success) {
|
||||
this.login.expires = res.login_exp;
|
||||
this.special.expires = res.special_exp;
|
||||
this.finish();
|
||||
}
|
||||
})
|
||||
}
|
||||
}} />
|
||||
break;
|
||||
// case Page.yubikey:
|
||||
// cont = <TFA_YubiKey id={this.state.twofactor_id} login={this.login} special={this.special} next={(login, special) => {
|
||||
// this.login = login;
|
||||
// this.special = special;
|
||||
// this.finish()
|
||||
// }} />
|
||||
// break;
|
||||
}
|
||||
return <div>
|
||||
(window as any).u2f.sign(
|
||||
request.appId,
|
||||
[request.challenge],
|
||||
[request],
|
||||
async (response) => {
|
||||
let res = await apiRequest(
|
||||
"/api/user/twofactor/yubikey",
|
||||
"PUT",
|
||||
JSON.stringify({ response })
|
||||
);
|
||||
if (res.success) {
|
||||
this.login.expires = res.login_exp;
|
||||
this.special.expires = res.special_exp;
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
// case Page.yubikey:
|
||||
// cont = <TFA_YubiKey id={this.state.twofactor_id} login={this.login} special={this.special} next={(login, special) => {
|
||||
// this.login = login;
|
||||
// this.special = special;
|
||||
// this.finish()
|
||||
// }} />
|
||||
// break;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<header>
|
||||
<h1>Login</h1>
|
||||
<h1>Login</h1>
|
||||
</header>
|
||||
<form action="JavaScript:void(0)">
|
||||
{cont}
|
||||
</form>
|
||||
<form action="JavaScript:void(0)">{cont}</form>
|
||||
<footer>
|
||||
<p>Powered by {appname}</p>
|
||||
<p>Powered by {appname}</p>
|
||||
</footer>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
render(<App />, document.body.querySelector("#content"))
|
||||
}, false)
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
function () {
|
||||
render(<App />, document.body.querySelector("#content"));
|
||||
},
|
||||
false
|
||||
);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
console.log("Hello World")
|
||||
console.log("Hello World");
|
||||
|
@ -1,23 +1,25 @@
|
||||
import "inputs";
|
||||
import sha from "sha512";
|
||||
import fireEvent from "event"
|
||||
import fireEvent from "event";
|
||||
|
||||
(() => {
|
||||
const translations = JSON.parse(document.getElementById("error_codes").innerText)
|
||||
const translations = JSON.parse(
|
||||
document.getElementById("error_codes").innerText
|
||||
);
|
||||
|
||||
const regcode = document.getElementById("regcode")
|
||||
regcode.value = new URL(window.location.href).searchParams.get("regcode")
|
||||
const regcode = document.getElementById("regcode");
|
||||
regcode.value = new URL(window.location.href).searchParams.get("regcode");
|
||||
fireEvent(regcode, "change");
|
||||
|
||||
function showError(element, message) {
|
||||
if (typeof element === "string")
|
||||
element = document.getElementById(element)
|
||||
element = document.getElementById(element);
|
||||
|
||||
if (!element) console.error("Element not found,", element)
|
||||
if (!element) console.error("Element not found,", element);
|
||||
element.innerText = message;
|
||||
if (!message) {
|
||||
if (!element.classList.contains("invisible"))
|
||||
element.classList.add("invisible")
|
||||
element.classList.add("invisible");
|
||||
} else {
|
||||
element.classList.remove("invisible");
|
||||
}
|
||||
@ -25,7 +27,8 @@ import fireEvent from "event"
|
||||
|
||||
function makeid(length) {
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var possible =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
@ -33,61 +36,61 @@ import fireEvent from "event"
|
||||
return text;
|
||||
}
|
||||
|
||||
const username = document.getElementById("username")
|
||||
const name = document.getElementById("name")
|
||||
const mail = document.getElementById("mail")
|
||||
const password = document.getElementById("password")
|
||||
const passwordrep = document.getElementById("passwordrep")
|
||||
const username = document.getElementById("username");
|
||||
const name = document.getElementById("name");
|
||||
const mail = document.getElementById("mail");
|
||||
const password = document.getElementById("password");
|
||||
const passwordrep = document.getElementById("passwordrep");
|
||||
|
||||
const radio_male = document.getElementById("radio-male")
|
||||
const radio_female = document.getElementById("radio-female")
|
||||
const radio_other = document.getElementById("radio-other")
|
||||
const radio_male = document.getElementById("radio-male");
|
||||
const radio_female = document.getElementById("radio-female");
|
||||
const radio_other = document.getElementById("radio-other");
|
||||
|
||||
const registerButton = document.getElementById("registerbutton")
|
||||
const registerButton = document.getElementById("registerbutton");
|
||||
registerButton.onclick = () => {
|
||||
console.log("Register")
|
||||
console.log("Register");
|
||||
showError("error");
|
||||
let error = false;
|
||||
if (!regcode.value) {
|
||||
showError("err_regcode", translations["noregcode"])
|
||||
showError("err_regcode", translations["noregcode"]);
|
||||
error = true;
|
||||
} else {
|
||||
showError("err_regcode")
|
||||
showError("err_regcode");
|
||||
}
|
||||
|
||||
if (!username.value) {
|
||||
showError("err_username", translations["nousername"])
|
||||
showError("err_username", translations["nousername"]);
|
||||
error = true;
|
||||
} else {
|
||||
showError("err_username")
|
||||
showError("err_username");
|
||||
}
|
||||
|
||||
if (!name.value) {
|
||||
showError("err_name", translations["noname"])
|
||||
showError("err_name", translations["noname"]);
|
||||
error = true;
|
||||
} else {
|
||||
showError("err_name")
|
||||
showError("err_name");
|
||||
}
|
||||
|
||||
if (!mail.value) {
|
||||
showError("err_mail", translations["nomail"])
|
||||
showError("err_mail", translations["nomail"]);
|
||||
error = true;
|
||||
} else {
|
||||
showError("err_mail")
|
||||
showError("err_mail");
|
||||
}
|
||||
|
||||
if (!password.value) {
|
||||
showError("err_password", translations["nopassword"])
|
||||
showError("err_password", translations["nopassword"]);
|
||||
error = true;
|
||||
} else {
|
||||
showError("err_password")
|
||||
showError("err_password");
|
||||
}
|
||||
|
||||
if (password.value !== passwordrep.value) {
|
||||
showError("err_passwordrep", translations["nomatch"])
|
||||
showError("err_passwordrep", translations["nomatch"]);
|
||||
error = true;
|
||||
} else {
|
||||
showError("err_passwordrep")
|
||||
showError("err_passwordrep");
|
||||
}
|
||||
|
||||
if (error) return;
|
||||
@ -95,14 +98,14 @@ import fireEvent from "event"
|
||||
let gender;
|
||||
|
||||
if (radio_male.checked) {
|
||||
gender = "male"
|
||||
gender = "male";
|
||||
} else if (radio_female.checked) {
|
||||
gender = "female"
|
||||
gender = "female";
|
||||
} else {
|
||||
gender = "other"
|
||||
gender = "other";
|
||||
}
|
||||
|
||||
let salt = makeid(10)
|
||||
let salt = makeid(10);
|
||||
|
||||
//username, password, salt, mail, gender, name, birthday, regcode
|
||||
|
||||
@ -113,37 +116,44 @@ import fireEvent from "event"
|
||||
name: name.value,
|
||||
regcode: regcode.value,
|
||||
salt: salt,
|
||||
password: sha(salt + password.value)
|
||||
}
|
||||
password: sha(salt + password.value),
|
||||
};
|
||||
|
||||
fetch("/api/user/register", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
"content-type": "application/json",
|
||||
},
|
||||
}).then(async e => {
|
||||
if (e.status !== 200) return Promise.reject(new Error(await e.text() || e.statusText));
|
||||
return e.json()
|
||||
}).then(data => {
|
||||
if (data.error) {
|
||||
if (!Array.isArray(data.error)) return Promise.reject(new Error(data.error));
|
||||
let ce = [];
|
||||
data.error.forEach(e => {
|
||||
let ef = document.getElementById("err_" + e.field);
|
||||
if (!ef) ce.push(e);
|
||||
else {
|
||||
showError(ef, e.message);
|
||||
}
|
||||
})
|
||||
if (ce.length > 0) {
|
||||
showError("error", ce.join("<br>"));
|
||||
}
|
||||
} else {
|
||||
window.location.href = "/login";
|
||||
}
|
||||
}).catch(e => {
|
||||
showError("error", e.message);
|
||||
})
|
||||
}
|
||||
})()
|
||||
.then(async (e) => {
|
||||
if (e.status !== 200)
|
||||
return Promise.reject(
|
||||
new Error((await e.text()) || e.statusText)
|
||||
);
|
||||
return e.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
if (!Array.isArray(data.error))
|
||||
return Promise.reject(new Error(data.error));
|
||||
let ce = [];
|
||||
data.error.forEach((e) => {
|
||||
let ef = document.getElementById("err_" + e.field);
|
||||
if (!ef) ce.push(e);
|
||||
else {
|
||||
showError(ef, e.message);
|
||||
}
|
||||
});
|
||||
if (ce.length > 0) {
|
||||
showError("error", ce.join("<br>"));
|
||||
}
|
||||
} else {
|
||||
window.location.href = "/login";
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
showError("error", e.message);
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
@ -36,13 +36,14 @@ form {
|
||||
padding: 3em 2em 2em 2em;
|
||||
background: #fafafa;
|
||||
border: 1px solid #ebebeb;
|
||||
box-shadow: rgba(0, 0, 0, 0.14902) 0px 1px 1px 0px, rgba(0, 0, 0, 0.09804) 0px 1px 2px 0px;
|
||||
box-shadow: rgba(0, 0, 0, 0.14902) 0px 1px 1px 0px,
|
||||
rgba(0, 0, 0, 0.09804) 0px 1px 2px 0px;
|
||||
}
|
||||
|
||||
#registerbutton {
|
||||
width: 100%;
|
||||
background: $primary;
|
||||
text-shadow: 1px 1px 0 rgba(39, 110, 204, .5);
|
||||
text-shadow: 1px 1px 0 rgba(39, 110, 204, 0.5);
|
||||
}
|
||||
|
||||
footer {
|
||||
@ -52,13 +53,13 @@ footer {
|
||||
footer p {
|
||||
color: #888;
|
||||
font-size: 13px;
|
||||
letter-spacing: .4px;
|
||||
letter-spacing: 0.4px;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: $primary;
|
||||
text-decoration: none;
|
||||
transition: all .2s ease;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
@ -68,11 +69,11 @@ footer a:hover {
|
||||
|
||||
footer img {
|
||||
width: 80px;
|
||||
transition: all .2s ease;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
footer img:hover {
|
||||
opacity: .83;
|
||||
opacity: 0.83;
|
||||
}
|
||||
|
||||
footer img:focus,
|
||||
@ -92,4 +93,4 @@ footer a:focus {
|
||||
color: $error;
|
||||
margin-top: 5px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user