frontend templating

This commit is contained in:
realaravinth
2021-04-03 17:18:18 +05:30
parent ecc0076cdd
commit a78b1eb25d
27 changed files with 189 additions and 107 deletions

View File

@@ -1,47 +0,0 @@
const ROUTES = {
registerUser: '/api/v1/signup',
loginUser: '/api/v1/signin',
signoutUser: '/api/v1/signout',
deleteAccount: '/api/v1/account/delete',
usernameExists: '/api/v1/account/username/exists',
emailExists: '/api/v1/account/email/exists',
healthCheck: '/api/v1/meta/health',
buildDetails: '/api/v1/meta/build',
addDomain: '/api/v1/mcaptcha/domain/add',
challengeDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/get',
proveDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/prove',
deleteDomain: '/api/v1/mcaptcha/domain/delete',
addToken: '/api/v1/mcaptcha/domain/token/add',
updateTokenKey: '/api/v1/mcaptcha/domain/token/update',
getTokenKey: '/api/v1/mcaptcha/domain/token/get',
deleteToken: '/api/v1/mcaptcha/domain/token/delete',
addTokenLevels: '/api/v1/mcaptcha/domain/token/levels/add',
updateTokenLevels: '/api/v1/mcaptcha/domain/token/levels/update',
deleteTokenLevels: '/api/v1/mcaptcha/domain/token/levels/delete',
getTokenLevels: '/api/v1/mcaptcha/domain/token/levels/get',
getTokenDuration: '/api/v1/mcaptcha/domain/token/token/get',
updateTokenDuration: '/api/v1/mcaptcha/domain/token/token/update',
};
export default ROUTES;

View File

@@ -1,43 +0,0 @@
import ROUTES from '../api/v1/routes';
import genJsonPayload from '../utils/genJsonPayload';
const checkEmailExists = async () => {
let email = document.getElementById('email');
let val = email.value;
let payload = {
val,
};
// return fetch(ROUTES.emailExists, genJsonPayload(payload)).then(res => {
// if (res.ok) {
// res.json().then(data => {
// if (data.exists) {
// console.log(email.className);
// email.className += ' form__in-field--warn';
// alert('Email taken');
// }
//
// return data.exists;
// });
// } else {
// res.json().then(err => alert(`error: ${err.error}`));
// }
// });
//
let res = await fetch(ROUTES.emailExists, genJsonPayload(payload));
if (res.ok) {
let data = await res.json();
if (data.exists) {
email.className += ' form__in-field--warn';
alert('Email taken');
}
return data.exists;
} else {
let err = await res.json();
alert(`error: ${err.error}`);
}
};
export {checkEmailExists};

View File

@@ -1,49 +0,0 @@
import ROUTES from '../api/v1/routes';
import isBlankString from '../utils/isBlankString';
import genJsonPayload from '../utils/genJsonPayload';
import {checkUsernameExists} from './userExists';
import {checkEmailExists} from './emailExists';
const registerUser = async e => {
e.preventDefault();
let username = document.getElementById('username').value;
isBlankString(e, username, 'username');
let password = document.getElementById('password').value;
let passwordCheck = document.getElementById('password-check').value;
if (password != passwordCheck) {
return alert("passwords don't match, check again!");
}
let email = document.getElementById('email').value;
isBlankString(e, email, 'email');
let exists = await checkUsernameExists();
if (exists) {
return;
}
exists = await checkEmailExists();
if (exists) {
return;
}
let payload = {
username,
password,
email,
};
let res = await fetch(ROUTES.registerUser, genJsonPayload(payload));
if (res.ok) {
alert('success');
} else {
let err = await res.json();
alert(`error: ${err.error}`);
}
};
export default registerUser;

View File

@@ -1,26 +0,0 @@
import isBlankString from '../utils/isBlankString';
import genJsonPayload from '../utils/genJsonPayload';
import ROUTES from '../api/v1/routes';
const signin = e => {
e.preventDefault();
let username = document.getElementById('username').value;
isBlankString(e, username, 'username');
let password = document.getElementById('password').value;
let payload = {
username,
password,
};
fetch(ROUTES.loginUser, genJsonPayload(payload)).then(res => {
if (res.ok) {
alert('success');
} else {
res.json().then(err => alert(`error: ${err.error}`));
}
});
};
export default signin;

View File

@@ -1,47 +0,0 @@
import ROUTES from '../api/v1/routes';
import genJsonPayload from '../utils/genJsonPayload';
const checkUsernameEventHandler = _e => {
checkUsernameExists();
};
//export const checkUsernameExists = async () => {
async function checkUsernameExists() {
let username = document.getElementById('username');
let val = username.value;
let payload = {
val,
};
// return fetch(ROUTES.usernameExists, genJsonPayload(payload)).then(res => {
// if (res.ok) {
// res.json().then(data => {
// if (data.exists) {
// username.className += ' form__in-field--warn';
// alert('Username taken');
// }
// return data.exists;
// });
// } else {
// res.json().then(err => alert(`error: ${err.error}`));
// }
// });
//
let res = await fetch(ROUTES.usernameExists, genJsonPayload(payload));
if (res.ok) {
let data = await res.json();
if (data.exists) {
username.className += ' form__in-field--warn';
alert('Username taken');
}
return data.exists;
} else {
let err = await res.json();
alert(`error: ${err.error}`);
}
return false;
};
export {checkUsernameExists, checkUsernameEventHandler};

View File

@@ -1,104 +0,0 @@
* {
padding: 0;
margin: 0;
}
.form__logo {
width: 110px;
padding-top: 50px;
display: block;
margin: auto;
position: relative;
top: 20%;
transform: translate(0%, -40.9%);
}
.form__brand {
padding: 10px 0;
text-align: center;
position: relative;
top: 20%;
transform: translate(0%, -90.9%);
}
.form-container {
max-width: 40%;
min-width: 20%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -49.9%);
box-sizing: border-box;
margin: auto;
padding: 20px 0;
}
.form__box {
border: 1px solid #eaecef;
background-color: #f6f8fa;
border-radius: 5px;
padding: 20px 0;
}
.form__in-group {
display: block;
position: relative;
margin: auto;
max-width: 80%;
padding: 10px 0px;
box-sizing: content-box;
align-items: center;
align-content: center;
}
.form__in-field {
display: block;
box-sizing: border-box;
margin: 10px 0;
padding: 10px 0;
width: 100%;
}
.form__in-field--warn {
border: solid 1px red;
}
.form__in-field--success {
border: solid 1px #2ea44f;
}
.form__pw-recovery {
text-decoration: none;
color: rgb(3, 102, 214);
font-size: 0.8rem;
}
.form__submit-button {
display: block;
border: 1px solid skyblue;
background: #2ea44f;
color: white;
height: 40px;
border-radius: 5px;
width: 80%;
margin: auto;
}
.form__secondary-action {
display: block;
margin-top: 10px;
}
.form__secondary-action__banner {
display: block;
margin: auto;
max-width: 80%;
text-align: center;
}
.form__secondary-action__link {
text-decoration: none;
color: rgb(3, 102, 214);
}

View File

@@ -1,19 +0,0 @@
import './css/forms.scss';
import signin from './auth/signin';
import registerUser from './auth/register';
import {checkUsernameEventHandler} from './auth/userExists';
if (window.location.pathname == '/') {
let form = document.getElementById('form');
form.addEventListener('submit', signin, true);
} else if (window.location.pathname == '/signup') {
let form = document.getElementById('form');
form.addEventListener('submit', registerUser, true);
let username = document.getElementById('username');
username.addEventListener('input', checkUsernameEventHandler, false);
} else {
}
//export default signin;

70
frontend/src/main.rs Normal file
View File

@@ -0,0 +1,70 @@
use log::{debug, info};
use sailfish::TemplateOnce;
use tokio::fs;
use tokio::io::{Error, ErrorKind};
#[derive(TemplateOnce)] // automatically implement `TemplateOnce` trait
#[template(path = "index.stpl")] // specify the path to template
struct IndexPage {
// data to be passed to the template
name: String,
title: String,
}
const BASE_DIR: &str = "./output";
#[tokio::main]
async fn main() {
pretty_env_logger::init();
match fs::create_dir(BASE_DIR).await {
Err(e) => {
if e.kind() == ErrorKind::AlreadyExists {
info!("cleaning up old assetes");
fs::remove_dir_all(BASE_DIR).await.unwrap();
debug!("creating target location");
fs::create_dir(BASE_DIR).await.unwrap();
}
}
_ => (),
};
let ctx = IndexPage {
name: "mCaptcha".into(),
title: "Login".into(),
};
// Now render templates with given data
info!("rendering {}", path("index.html"));
let index = ctx.render_once().unwrap();
fs::write(path("index.html"), index).await.unwrap();
info!("wrote {}", path("index.html"));
let ctx = signup::IndexPage {
name: "mCaptcha".into(),
title: "Register".into(),
};
// Now render templates with given data
info!("rendering {}", path("signup/index.html"));
let index = ctx.render_once().unwrap();
fs::create_dir(path("signup")).await.unwrap();
info!("creating dir {}", path("signup/"));
fs::write(path("signup/index.html"), index).await.unwrap();
info!("wrote {}", path("signup/index.html"));
}
fn path(rel: &str) -> String {
format!("{}/{}", BASE_DIR, rel)
}
mod signup {
use super::*;
#[derive(TemplateOnce)] // automatically implement `TemplateOnce` trait
#[template(path = "signup/index.stpl", escape = false)] // specify the path to template
pub struct IndexPage {
// data to be passed to the template
pub name: String,
pub title: String,
}
}

View File

@@ -1,8 +0,0 @@
import './css/forms.scss';
import registerUser from './auth/register';
let form = document.getElementById('form');
form.addEventListener('submit', registerUser, true);
let username = document.getElementById('username');
username.addEventListener('input', checkUsernameEventHandler, false);

View File

@@ -1,12 +0,0 @@
const genJsonPayload = payload => {
let value = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
};
return value;
};
export default genJsonPayload;

View File

@@ -1,8 +0,0 @@
const isBlankString = (event, value, field) => {
if (!value.replace(/\s/g, '').length) {
event.preventDefault();
alert(`${field} can't be empty`);
}
};
export default isBlankString;