Running prettier
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Fabian Stamm
2020-08-07 16:16:39 +02:00
parent 77fedd2815
commit 51a8609880
87 changed files with 4000 additions and 2812 deletions

View File

@ -5,49 +5,45 @@ const {
copyFileSync,
writeFileSync,
readFileSync,
exists
} = require('fs')
const {
join,
basename,
dirname
} = require('path')
exists,
} = require("fs");
const { join, basename, dirname } = require("path");
const isDirectory = source => lstatSync(source).isDirectory()
const getDirectories = source =>
readdirSync(source).map(name => join(source, name)).filter(isDirectory)
const isDirectory = (source) => lstatSync(source).isDirectory();
const getDirectories = (source) =>
readdirSync(source)
.map((name) => join(source, name))
.filter(isDirectory);
function ensureDir(folder) {
try {
if (!isDirectory(folder)) mkdirSync(folder)
if (!isDirectory(folder)) mkdirSync(folder);
} catch (e) {
mkdirSync(folder)
mkdirSync(folder);
}
}
const fileExists = (filename) =>
new Promise((yes, no) => exists(filename, (exi) => yes(exi)));
ensureDir("./out");
const fileExists = (filename) => new Promise((yes, no) => exists(filename, (exi) => yes(exi)));
ensureDir("./out")
const sass = require('sass');
const sass = require("sass");
function findHead(elm) {
if (elm.tagName === "head") return elm;
for (let i = 0; i < elm.childNodes.length; i++) {
let res = findHead(elm.childNodes[i])
let res = findHead(elm.childNodes[i]);
if (res) return res;
}
return undefined;
}
const rollup = require("rollup")
const includepaths = require("rollup-plugin-includepaths")
const rollup = require("rollup");
const includepaths = require("rollup-plugin-includepaths");
const typescript = require("rollup-plugin-typescript2");
const resolve = require("rollup-plugin-node-resolve");
const minify = require("html-minifier").minify
const gzipSize = require('gzip-size');
const minify = require("html-minifier").minify;
const gzipSize = require("gzip-size");
async function file_name(folder, name, exts) {
for (let ext of exts) {
@ -61,58 +57,57 @@ async function buildPage(folder) {
const pagename = basename(folder);
const outpath = "./out/" + pagename;
ensureDir(outpath)
ensureDir(outpath);
const basefile = await file_name(folder, pagename, ["tsx", "ts", "js"]);
let bundle = await rollup.rollup({
input: basefile,
plugins: [
includepaths({
paths: ["shared", "node_modules"]
paths: ["shared", "node_modules"],
}),
typescript(),
resolve({
// not all files you want to resolve are .js files
extensions: ['.mjs', '.js', '.jsx', '.json'], // Default: [ '.mjs', '.js', '.json', '.node' ]
extensions: [".mjs", ".js", ".jsx", ".json"], // Default: [ '.mjs', '.js', '.json', '.node' ]
// whether to prefer built-in modules (e.g. `fs`, `path`) or
// local ones with the same names
preferBuiltins: false, // Default: true
})
preferBuiltins: false, // Default: true
}),
],
treeshake: true
})
treeshake: true,
});
let { output } = await bundle.generate({
format: "iife",
compact: true
})
compact: true,
});
let { code } = output[0];
let sass_res = sass.renderSync({
file: folder + `/${pagename}.scss`,
includePaths: ["./node_modules", folder, "./shared"],
outputStyle: "compressed"
})
outputStyle: "compressed",
});
let css = "<style>\n" + sass_res.css.toString("utf8") + "\n</style>\n";
let script = "<script>\n" + code + "\n</script>\n";
let html = readFileSync(`${folder}/${pagename}.hbs`).toString("utf8");
let idx = html.indexOf("</head>")
if (idx < 0) throw new Error("No head element found")
let idx2 = html.indexOf("</body>")
if (idx2 < 0) throw new Error("No body element found")
let idx = html.indexOf("</head>");
if (idx < 0) throw new Error("No head element found");
let idx2 = html.indexOf("</body>");
if (idx2 < 0) throw new Error("No body element found");
if (idx < idx2) {
let part1 = html.slice(0, idx)
let part1 = html.slice(0, idx);
let part2 = html.slice(idx, idx2);
let part3 = html.slice(idx2, html.length);
html = part1 + css + part2 + script + part3;
} else {
let part1 = html.slice(0, idx2)
let part1 = html.slice(0, idx2);
let part2 = html.slice(idx2, idx);
let part3 = html.slice(idx, html.length);
html = part1 + script + part2 + css + part3;
@ -126,45 +121,50 @@ async function buildPage(folder) {
minifyCSS: false,
minifyJS: false,
removeComments: true,
useShortDoctype: true
})
useShortDoctype: true,
});
let gzips = await gzipSize(result)
writeFileSync(`${outpath}/${pagename}.html`, result)
let gzips = await gzipSize(result);
writeFileSync(`${outpath}/${pagename}.html`, result);
let stats = {
sass: sass_res.stats,
js: {
chars: code.length
chars: code.length,
},
css: {
chars: css.length
chars: css.length,
},
bundle_size: result.length,
gzip_size: gzips
}
gzip_size: gzips,
};
writeFileSync(outpath + `/stats.json`, JSON.stringify(stats, null, " "))
writeFileSync(outpath + `/stats.json`, JSON.stringify(stats, null, " "));
}
async function run() {
console.log("Start compiling!");
let pages = getDirectories("./src");
await Promise.all(pages.map(async e => {
try {
await buildPage(e)
} catch (er) {
console.error("Failed compiling", basename(e))
console.log(er)
}
}))
console.log("Finished compiling!")
await Promise.all(
pages.map(async (e) => {
try {
await buildPage(e);
} catch (er) {
console.error("Failed compiling", basename(e));
console.log(er);
}
})
);
console.log("Finished compiling!");
}
const chokidar = require("chokidar");
if (process.argv.join(" ").toLowerCase().indexOf("watch") >= 0)
chokidar.watch(["./src", "./node_modules", "./package.json", "./package-lock.json"], {
ignoreInitial: true
})
chokidar
.watch(
["./src", "./node_modules", "./package.json", "./package-lock.json"],
{
ignoreInitial: true,
}
)
.on("all", () => run());
run()
run();

View File

@ -1,15 +1,15 @@
export function setCookie(cname, cvalue, exdate) {
var expires = exdate ? `;expires=${exdate}` : "";
document.cookie = `${cname}=${cvalue}${expires}`
document.cookie = `${cname}=${cvalue}${expires}`;
}
export function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
var ca = decodedCookie.split(";");
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
while (c.charAt(0) == " ") {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
@ -17,4 +17,4 @@ export function getCookie(cname) {
}
}
return "";
}
}

View File

@ -2,12 +2,11 @@ export default function fireEvent(element, event) {
if (document.createEventObject) {
// dispatch for IE
var evt = document.createEventObject();
return element.fireEvent('on' + event, evt)
}
else {
return element.fireEvent("on" + event, evt);
} else {
// dispatch for firefox + others
var evt = document.createEvent("HTMLEvents");
evt.initEvent(event, true, true); // event type,bubbling,cancelable
return !element.dispatchEvent(evt);
}
}
}

View File

@ -1,14 +1,18 @@
export default function getFormData(element) {
let data = {};
if (element.name !== undefined && element.name !== null && element.name !== "") {
if (
element.name !== undefined &&
element.name !== null &&
element.name !== ""
) {
if (typeof element.name === "string") {
if (element.type === "checkbox") data[element.name] = element.checked;
else data[element.name] = element.value;
}
}
element.childNodes.forEach(child => {
element.childNodes.forEach((child) => {
let res = getFormData(child);
data = Object.assign(data, res);
})
});
return data;
}
}

View File

@ -1,24 +1,24 @@
(() => {
const run = () => {
document.querySelectorAll(".floating>input").forEach(e => {
document.querySelectorAll(".floating>input").forEach((e) => {
function checkState() {
if (e.value !== "") {
if (e.classList.contains("used")) return;
e.classList.add("used")
e.classList.add("used");
} else {
if (e.classList.contains("used")) e.classList.remove("used")
if (e.classList.contains("used")) e.classList.remove("used");
}
}
e.addEventListener("change", () => checkState())
checkState()
})
}
e.addEventListener("change", () => checkState());
checkState();
});
};
run();
var mutationObserver = new MutationObserver(() => {
run()
run();
});
mutationObserver.observe(document.documentElement, {
@ -28,6 +28,6 @@
subtree: true,
});
window.Mutt
window.addEventListener("DOMNodeInserted", () => run())
})();
window.Mutt;
window.addEventListener("DOMNodeInserted", () => run());
})();

View File

@ -5,7 +5,7 @@
min-height: 45px;
}
.floating>input {
.floating > input {
font-size: 18px;
padding: 10px 10px 10px 5px;
appearance: none;
@ -19,13 +19,13 @@
border-bottom: 1px solid #757575;
}
.floating>input:focus {
.floating > input:focus {
outline: none;
}
/* Label */
.floating>label {
.floating > label {
color: #999;
font-size: 18px;
font-weight: normal;
@ -38,10 +38,10 @@
/* active */
.floating>input:focus~label,
.floating>input.used~label {
top: -.75em;
transform: scale(.75);
.floating > input:focus ~ label,
.floating > input.used ~ label {
top: -0.75em;
transform: scale(0.75);
left: -2px;
/* font-size: 14px; */
color: $primary;
@ -58,7 +58,7 @@
.bar:before,
.bar:after {
content: '';
content: "";
height: 2px;
width: 0;
bottom: 1px;
@ -77,8 +77,8 @@
/* active */
.floating>input:focus~.bar:before,
.floating>input:focus~.bar:after {
.floating > input:focus ~ .bar:before,
.floating > input:focus ~ .bar:after {
width: 50%;
}
@ -96,7 +96,7 @@
/* active */
.floating>input:focus~.highlight {
.floating > input:focus ~ .highlight {
animation: inputHighlighter 0.3s ease;
}
@ -110,4 +110,4 @@
width: 0;
background: transparent;
}
}
}

View File

@ -1,2 +1,2 @@
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons");
@import url("https://unpkg.com/bootstrap-material-design@4.1.1/dist/css/bootstrap-material-design.min.css");
@import url("https://unpkg.com/bootstrap-material-design@4.1.1/dist/css/bootstrap-material-design.min.css");

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,26 @@
export default function request(endpoint, method, data) {
var headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.set("Content-Type", "application/json");
return fetch(endpoint, {
method: method,
body: JSON.stringify(data),
headers: headers,
credentials: "include"
}).then(async e => {
if (e.status !== 200) throw new Error(await e.text() || e.statusText);
return e.json()
}).then(e => {
if (e.error) return Promise.reject(new Error(typeof e.error === "string" ? e.error : JSON.stringify(e.error)));
return e;
credentials: "include",
})
}
.then(async (e) => {
if (e.status !== 200)
throw new Error((await e.text()) || e.statusText);
return e.json();
})
.then((e) => {
if (e.error)
return Promise.reject(
new Error(
typeof e.error === "string"
? e.error
: JSON.stringify(e.error)
)
);
return e;
});
}

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
// $primary: #4a89dc;
$primary: #1E88E5;
$primary: #1e88e5;
$error: #ff2f00;
.btn-primary {
color: white !important;
background-color: $primary !important;
}
}

View File

@ -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;
}
})()
})();

View File

@ -100,4 +100,4 @@ table td {
.col.form-group {
padding-left: 0 !important;
margin-left: 5px !important;
}
}

View File

@ -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();
};

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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

View File

@ -1 +1 @@
console.log("Hello World")
console.log("Hello World");

View File

@ -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);
});
};
})();

View File

@ -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;
}
}

View File

@ -1,18 +1,9 @@
{
"compilerOptions": {
"lib": [
"dom",
"es2015",
"es6",
"es7",
"es2018",
"esnext"
],
"jsxFactory": "h",
"jsx": "react",
"module": "esnext"
},
"include": [
"./types.d.ts"
]
}
"compilerOptions": {
"lib": ["dom", "es2015", "es6", "es7", "es2018", "esnext"],
"jsxFactory": "h",
"jsx": "react",
"module": "esnext"
},
"include": ["./types.d.ts"]
}

10
views/types.d.ts vendored
View File

@ -1,9 +1,9 @@
declare module "sha512" {
const val: any;
export default val;
const val: any;
export default val;
}
declare module "cookie" {
export function getCookie(name: string): string | undefined;
export function setCookie(name: string, value: string, exp?: string): void;
}
export function getCookie(name: string): string | undefined;
export function setCookie(name: string, value: string, exp?: string): void;
}