Added user login and account creation

This commit is contained in:
Gnarwhal 2021-02-05 04:59:17 -05:00
parent 9ba8a99e82
commit 5a1dd33dfe
Signed by: Gnarwhal
GPG key ID: 0989A73D8C421174
19 changed files with 1276 additions and 874 deletions

View file

@ -0,0 +1,122 @@
window.addEventListener("load", (loadEvent) => {
const fields = {
email: document.querySelector("#email" ),
username: document.querySelector("#username"),
password: document.querySelector("#password"),
confirm: document.querySelector("#confirm" )
};
const createUser = document.querySelector("#create-user-button");
const login = document.querySelector("#login-button");
const header = document.querySelector("#login-header-text");
const error = document.querySelector("#error-message");
const raiseError = (errorFields, message) => {
for (const key in fields) {
if (errorFields.includes(key)) {
fields[key].classList.add("error");
} else {
fields[key].classList.remove("error");
}
}
error.style.display = "block";
error.textContent = message;
}
let frozen = false;
const switchToCreateAction = (clickEvent) => {
if (!frozen) {
fields.username.style.display = "block";
fields.confirm.style.display = "block";
login.style.display = "none";
header.textContent = "Create User";
createUser.removeEventListener("click", switchToCreateAction);
createUser.addEventListener("click", createUserAction);
}
};
const createUserAction = (clickEvent) => {
if (!frozen) {
if (fields.email.value === '') {
raiseError([ "email" ], "Email cannot be empty");
} else if (fields.username.value === '') {
raiseError([ "username" ], "Username cannot be empty");
} else if (fields.password.value !== fields.confirm.value) {
raiseError([ "password", "confirm" ], "Password fields did not match");
} else if (fields.password.value === '') {
raiseError([ "password", "confirm" ], "Password cannot be empty");
} else {
frozen = true;
fetch('https://localhost:4730/create_user', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: fields.email.value, username: fields.username.value, password: fields.password.value })
})
.then(async response => ({ status: response.status, data: await response.json() }))
.then(response => {
const data = response.data;
if (response.status === 200) {
window.sessionStorage.setItem('sessionKey', data.key);
window.location.href = "/";
} else if (response.status === 500) {
raiseError([], "Internal server error :(");
} else {
if (data.code === 1) {
raiseError([ "email" ], "A user with that email is already registered");
fields.email.value = '';
}
}
})
.catch(error => {
console.error(error);
raiseError([], "Server error :(");
}).then(() => frozen = false);
}
}
};
createUser.addEventListener("click", switchToCreateAction);
const loginAction = (clickEvent) => {
if (!frozen) {
if (fields.email.value === '') {
raiseError([ "email" ], "Email cannot be empty");
} else if (fields.password.value === '') {
raiseError([ "password" ], "Password cannot be empty");
} else {
frozen = true;
fetch('https://localhost:4730/login', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: fields.email.value, password: fields.password.value })
})
.then(async response => ({ status: response.status, data: await response.json() }))
.then(response => {
const data = response.data;
if (response.status === 200) {
window.sessionStorage.setItem('sessionKey', data.key);
window.location.href = "/";
} else if (response.status === 500) {
raiseError([], "Internal server error :(");
} else {
raiseError([ "email", "password" ], "Email or password is incorrect");
fields.password.value = '';
}
})
.catch(error => {
console.error(error);
raiseError([], "Unknown error :(");
}).then(() => frozen = false);
}
}
};
login.addEventListener("click", loginAction);
});

View file

@ -1,142 +1,141 @@
var template = template || {};
template.type = {};
template.type._entryMap = new Map();
template.type.register = (type, callback) => {
if (typeof type !== 'string') {
console.error(`'type' must be a string, recieved: `, type);
} else {
const TYPE_REGEX = /^(\w+)\s*(<\s*\?(?:\s*,\s*\?)*\s*>)?\s*$/;
const result = type.match(TYPE_REGEX);
if (result === null) {
console.error(`'${type}' is not a valid type id`);
} else {
if (result[2] === undefined) {
result[2] = 0;
} else {
result[2] = result[2].split(/\s*,\s*/).length;
}
const completeType = result[1] + ':' + result[2];
if (template.type._entryMap.get(completeType) === undefined) {
template.type._entryMap.set(completeType, async function() {
await callback.apply(null, Array.from(arguments));
});
} else {
console.error(`${type} is already a registered template!`);
}
}
}
};
// Courtesy of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|\[\]\\]/g, '\\$&'); // $& means the whole matched string
}
/* Intrinsic Templates */
// Basic - Simple search and replace
template.type.register('Basic', (element, map) => {
let html = element.innerHTML;
function applyObject(object, path) {
for (const key in object) {
const regexKey = escapeRegExp(path + key);
html = html.replace(new RegExp(`(?:(?<!\\\\)\\\${${regexKey}})`, 'gm'), object[key]);
if (typeof object[key] === 'object') {
applyObject(object[key], path + key + '.');
}
}
}
applyObject(map, '');
html = html.replace('\\&', '&');
element.outerHTML = html.trim();
});
// Extern - Retrieve template from webserver
template.type.register('Extern', (element, name) => {
return fetch(`templates/${name}.html.template`, {
method: 'GET',
mode: 'no-cors',
headers: {
'Content-Type': 'text/plain'
}
}).then(response => response.text().then((data) => {
element.outerHTML = data;
})).catch(error => {
console.error(`failed to retrieve template '${name}': `, error);
});
});
// List - Iterate over list and emit copy of child for each iteration
template.type.register('List<?>', async (element, subtype, arrayMap) => {
let cumulative = '';
const temp = document.createElement('template');
for (const obj of arrayMap) {
temp.innerHTML = `<template></template>`;
const child = temp.content.children[0];
child.innerHTML = element.innerHTML;
const callback = template.type._entryMap.get(subtype.type);
if (callback === undefined) {
cumulative = '';
console.error(`'${subtype.type}' is not a registered template type`);
} else {
await callback.apply(null, [ child, obj ]);
}
cumulative = cumulative + temp.innerHTML.trim();
}
element.outerHTML = cumulative;
});
template._entryMap = new Map();
template.apply = function(pattern, promise) {
if (typeof pattern !== 'string') {
console.error('pattern must be a string, received: ', pattern);
} else {
return new template.apply.applicators(pattern);
}
};
template.apply.applicators = class {
constructor(pattern) {
this._pattern = pattern;
}
_apply(asyncArgs) {
template._entryMap.set(RegExp('^' + this._pattern + '$'), asyncArgs);
}
values(...args) {
this._apply(async () => Array.from(args));
}
promise(promise) {
let args = null;
promise = promise.then(data => args = [ data ]);
this._apply(async () => args || promise);
}
fetch(dataProcessor, url, options) {
if (typeof dataProcessor === 'string') {
const path = dataProcessor;
dataProcessor = data => {
for (const id of path.split(/\./)) {
data = data[id];
if (data === undefined) {
throw `invalid path '${path}'`;
}
}
return data;
};
};
this.promise(
fetch(url, options || { method: 'GET', mode: 'cors' })
.then(response => response.json())
.then(data => dataProcessor(data))
);
}
};
(() => {
templateTypeEntryMap = new Map();
template.register = (type, callback) => {
if (typeof type !== 'string') {
console.error(`'type' must be a string, recieved: `, type);
} else {
const TYPE_REGEX = /^(\w+)\s*(<\s*\?(?:\s*,\s*\?)*\s*>)?\s*$/;
const result = type.match(TYPE_REGEX);
if (result === null) {
console.error(`'${type}' is not a valid type id`);
} else {
if (result[2] === undefined) {
result[2] = 0;
} else {
result[2] = result[2].split(/\s*,\s*/).length;
}
const completeType = result[1] + ':' + result[2];
if (templateTypeEntryMap.get(completeType) === undefined) {
templateTypeEntryMap.set(completeType, async function() {
await callback.apply(null, Array.from(arguments));
});
} else {
console.error(`${type} is already a registered template!`);
}
}
}
};
// Courtesy of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|\[\]\\]/g, '\\$&'); // $& means the whole matched string
}
/* Intrinsic Templates */
// Basic - Simple search and replace
template.register('Basic', (element, map) => {
let html = element.innerHTML;
function applyObject(object, path) {
for (const key in object) {
const regexKey = escapeRegExp(path + key);
html = html.replace(new RegExp(`(?:(?<!\\\\)\\\${${regexKey}})`, 'gm'), object[key]);
if (typeof object[key] === 'object') {
applyObject(object[key], path + key + '.');
}
}
}
applyObject(map, '');
html = html.replace('\\&', '&');
element.outerHTML = html.trim();
});
// Extern - Retrieve template from webserver
template.register('Extern', (element, name) => {
return fetch(`templates/${name}.html.template`, {
method: 'GET',
mode: 'no-cors',
headers: {
'Content-Type': 'text/plain'
}
}).then(response => response.text().then((data) => {
element.outerHTML = data;
})).catch(error => {
console.error(`failed to retrieve template '${name}': `, error);
});
});
// List - Iterate over list and emit copy of child for each iteration
template.register('List<?>', async (element, subtype, arrayMap) => {
let cumulative = '';
const temp = document.createElement('template');
for (const obj of arrayMap) {
temp.innerHTML = `<template></template>`;
const child = temp.content.children[0];
child.innerHTML = element.innerHTML;
const callback = templateTypeEntryMap.get(subtype.type);
if (callback === undefined) {
cumulative = '';
console.error(`'${subtype.type}' is not a registered template`);
} else {
await callback.apply(null, [ child, obj ]);
}
cumulative = cumulative + temp.innerHTML.trim();
}
element.outerHTML = cumulative;
});
templateEntryMap = new Map();
template.apply = function(pattern, promise) {
if (typeof pattern !== 'string') {
console.error('pattern must be a string, received: ', pattern);
} else {
return new template.apply.applicators(pattern);
}
};
template.apply.applicators = class {
constructor(pattern) {
this._pattern = pattern;
}
_apply(asyncArgs) {
templateEntryMap.set(RegExp('^' + this._pattern + '$'), asyncArgs);
}
values(...args) {
this._apply(async () => Array.from(args));
}
promise(promise) {
let args = null;
promise = promise.then(data => args = [ data ]);
this._apply(async () => args || promise);
}
fetch(dataProcessor, url, options) {
if (typeof dataProcessor === 'string') {
const path = dataProcessor;
dataProcessor = data => {
for (const id of path.split(/\./)) {
data = data[id];
if (data === undefined) {
throw `invalid path '${path}'`;
}
}
return data;
};
};
this.promise(
fetch(url, options || { method: 'GET', mode: 'cors' })
.then(response => response.json())
.then(data => dataProcessor(data))
);
}
};
const parseType = (type) => {
let result = type.match(/^\s*(\w+)\s*(?:<(.*)>)?\s*$/);
let id = result[1];
@ -162,12 +161,12 @@ template.apply.applicators = class {
let promises = [];
let parents = new Set();
for (const child of children) {
for (const [pattern, argsCallback] of template._entryMap) {
for (const [pattern, argsCallback] of templateEntryMap) {
await argsCallback().then(args => {
if (pattern.test(child.id)) {
const callback = template.type._entryMap.get(child.typeCapture.type);
const callback = templateTypeEntryMap.get(child.typeCapture.type);
if (typeof callback !== 'function') {
console.error(`'${child.typeCapture.type}' is not a registered template type`);
console.error(`'${child.typeCapture.type}' is not a registered template`);
} else {
let params = Array.from(args)
for (const subtype of child.typeCapture.params) {