mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2025-11-23 22:15:46 +00:00
frontend toolchain config and registration
This commit is contained in:
parent
584d43c34a
commit
ecc0076cdd
2
frontend/.gitignore
vendored
Normal file
2
frontend/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
33
frontend/package.json
Normal file
33
frontend/package.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "mCaptcha/guard frontend",
|
||||||
|
"main": "index.js",
|
||||||
|
"repository": "https://github.com/mCaptcha/guard",
|
||||||
|
"author": "Aravinth Manivannan <realaravinth@batsense.net>",
|
||||||
|
"license": "AGPLv3 or above",
|
||||||
|
"scripts": {
|
||||||
|
"start": "webpack-dev-server --config webpack.dev.js --open",
|
||||||
|
"build": "webpack --config webpack.prod.js"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
|
||||||
|
"devDependencies": {
|
||||||
|
"css-loader": "^2.1.0",
|
||||||
|
"file-loader": "^3.0.1",
|
||||||
|
"html-loader": "^0.5.5",
|
||||||
|
"html-webpack-plugin": "^3.2.0",
|
||||||
|
"mini-css-extract-plugin": "^0.5.0",
|
||||||
|
"node-sass": "^4.11.0",
|
||||||
|
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||||
|
"sass-loader": "^7.1.0",
|
||||||
|
"style-loader": "^0.23.1",
|
||||||
|
"webpack": "^4.29.6",
|
||||||
|
"webpack-cli": "^3.2.3",
|
||||||
|
"webpack-dev-server": "^3.2.1",
|
||||||
|
"webpack-merge": "^4.2.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"clean-webpack-plugin": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 455 KiB After Width: | Height: | Size: 455 KiB |
38
frontend/public/index.html
Normal file
38
frontend/public/index.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<html lang="en"><head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login | mCaptcha</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="form-container">
|
||||||
|
<img src="/img/icon-trans.png" class="form__logo" alt="">
|
||||||
|
<h2 class="form__brand">Sign in to mCaptcha</h2>
|
||||||
|
|
||||||
|
<form class="form__box" id="form">
|
||||||
|
<label class="form__in-group" for="username">Username
|
||||||
|
<input class="form__in-field" id="username" type="text" name="username" required="">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="password" class="form__in-group">Password
|
||||||
|
<input class="form__in-field" type="password" id="password" name="password" required="">
|
||||||
|
<!--
|
||||||
|
<a class="form__pw-recovery" -href="/recovert/password"
|
||||||
|
>Forgot password?</a
|
||||||
|
>
|
||||||
|
-->
|
||||||
|
</label>
|
||||||
|
<button class="form__submit-button" type="submit">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<div class="form__secondary-action">
|
||||||
|
<p class="form__secondary-action__banner">
|
||||||
|
New to mCaptcha?
|
||||||
|
<a href="/signup" class="form__secondary-action__link">Create account</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/forms.css">
|
||||||
|
|
||||||
|
</body></html>
|
||||||
75
frontend/public/signup/index.html
Normal file
75
frontend/public/signup/index.html
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Login | mCaptcha</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="form-container">
|
||||||
|
<img src="/img/icon-trans.png" class="form__logo" alt="" />
|
||||||
|
<h2 class="form__brand">Join mCaptcha</h2>
|
||||||
|
|
||||||
|
<form class="form__box" id="form">
|
||||||
|
<label class="form__in-group" for="username"
|
||||||
|
>Username
|
||||||
|
<input
|
||||||
|
class="form__in-field"
|
||||||
|
id="username"
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
id="username"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="form__in-group" for="username"
|
||||||
|
>Email
|
||||||
|
<input
|
||||||
|
class="form__in-field"
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
id="email"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="password" class="form__in-group"
|
||||||
|
>Password
|
||||||
|
<input
|
||||||
|
class="form__in-field"
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
id="password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="password" class="form__in-group"
|
||||||
|
>Re-enter Password
|
||||||
|
<input
|
||||||
|
class="form__in-field"
|
||||||
|
type="password"
|
||||||
|
id="password-check"
|
||||||
|
name="password-check"
|
||||||
|
id="password-check"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<button class="form__submit-button" type="submit">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<div class="form__secondary-action">
|
||||||
|
<p class="form__secondary-action__banner">
|
||||||
|
Already have an account?
|
||||||
|
<a href="/" class="form__secondary-action__link"
|
||||||
|
>Click here to login</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
47
frontend/src/api/v1/routes.js
Normal file
47
frontend/src/api/v1/routes.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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;
|
||||||
43
frontend/src/auth/emailExists.js
Normal file
43
frontend/src/auth/emailExists.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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};
|
||||||
49
frontend/src/auth/register.js
Normal file
49
frontend/src/auth/register.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
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;
|
||||||
26
frontend/src/auth/signin.js
Normal file
26
frontend/src/auth/signin.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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;
|
||||||
47
frontend/src/auth/userExists.js
Normal file
47
frontend/src/auth/userExists.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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};
|
||||||
104
frontend/src/css/forms.scss
Normal file
104
frontend/src/css/forms.scss
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
* {
|
||||||
|
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);
|
||||||
|
}
|
||||||
19
frontend/src/index.js
Normal file
19
frontend/src/index.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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;
|
||||||
8
frontend/src/signup/index.js
Normal file
8
frontend/src/signup/index.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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);
|
||||||
12
frontend/src/utils/genJsonPayload.js
Normal file
12
frontend/src/utils/genJsonPayload.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const genJsonPayload = payload => {
|
||||||
|
let value = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
};
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default genJsonPayload;
|
||||||
8
frontend/src/utils/isBlankString.js
Normal file
8
frontend/src/utils/isBlankString.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const isBlankString = (event, value, field) => {
|
||||||
|
if (!value.replace(/\s/g, '').length) {
|
||||||
|
event.preventDefault();
|
||||||
|
alert(`${field} can't be empty`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isBlankString;
|
||||||
25
frontend/webpack.common.js
Normal file
25
frontend/webpack.common.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
main: './src/index.js',
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.html$/,
|
||||||
|
use: ['html-loader'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(svg|png|jpg|gif)$/,
|
||||||
|
use: {
|
||||||
|
loader: 'file-loader',
|
||||||
|
options: {
|
||||||
|
name: '[name].[hash].[ext]',
|
||||||
|
outputPath: 'imgs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
34
frontend/webpack.dev.js
Normal file
34
frontend/webpack.dev.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const common = require("./webpack.common");
|
||||||
|
const merge = require("webpack-merge");
|
||||||
|
var HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: "development",
|
||||||
|
output: {
|
||||||
|
filename: "[name].bundle.js",
|
||||||
|
path: path.resolve(__dirname, "dist")
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: "./public/index.html"
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
filename: "signup/index.html",
|
||||||
|
template: "./public/signup/index.html"
|
||||||
|
})
|
||||||
|
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [
|
||||||
|
"style-loader", //3. Inject styles into DOM
|
||||||
|
"css-loader", //2. Turns css into commonjs
|
||||||
|
"sass-loader" //1. Turns sass into css
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
55
frontend/webpack.prod.js
Normal file
55
frontend/webpack.prod.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const common = require('./webpack.common');
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'production',
|
||||||
|
output: {
|
||||||
|
filename: '[name].[contentHash].bundle.js',
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimizer: [
|
||||||
|
new OptimizeCssAssetsPlugin(),
|
||||||
|
new TerserPlugin(),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: path.resolve(__dirname, 'public/', 'index.html'),
|
||||||
|
minify: {
|
||||||
|
removeAttributeQuotes: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeComments: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
filename: 'signup/index.html',
|
||||||
|
template: path.resolve(__dirname, 'public/signup/', 'index.html'),
|
||||||
|
minify: {
|
||||||
|
removeAttributeQuotes: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeComments: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new MiniCssExtractPlugin({filename: '[name].[contentHash].css'}),
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader, //3. Extract css into files
|
||||||
|
'css-loader', //2. Turns css into commonjs
|
||||||
|
'sass-loader', //1. Turns sass into css
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
2756
frontend/yarn-error.log
Normal file
2756
frontend/yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
5932
frontend/yarn.lock
Normal file
5932
frontend/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
11
src/main.rs
11
src/main.rs
@ -42,11 +42,14 @@ pub use settings::Settings;
|
|||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref SETTINGS: Settings = Settings::new().unwrap();
|
pub static ref SETTINGS: Settings = Settings::new().unwrap();
|
||||||
pub static ref GIT_COMMIT_HASH: String = env::var("GIT_HASH").unwrap();
|
// pub static ref GIT_COMMIT_HASH: String = env::var("GIT_HASH").unwrap();
|
||||||
pub static ref OPEN_API_DOC: String = env::var("OPEN_API_DOCS").unwrap();
|
|
||||||
|
// pub static ref OPEN_API_DOC: String = env::var("OPEN_API_DOCS").unwrap();
|
||||||
pub static ref S: String = env::var("S").unwrap();
|
pub static ref S: String = env::var("S").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub static OPEN_API_DOC: &str = env!("OPEN_API_DOCS");
|
||||||
|
pub static GIT_COMMIT_HASH: &str = env!("GIT_HASH");
|
||||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
pub static PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
pub static PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
pub static PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
pub static PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
||||||
@ -62,7 +65,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
info!(
|
info!(
|
||||||
"{}: {}.\nFor more information, see: {}\nBuild info:\nVersion: {} commit: {}",
|
"{}: {}.\nFor more information, see: {}\nBuild info:\nVersion: {} commit: {}",
|
||||||
PKG_NAME, PKG_DESCRIPTION, PKG_HOMEPAGE, VERSION, *GIT_COMMIT_HASH
|
PKG_NAME, PKG_DESCRIPTION, PKG_HOMEPAGE, VERSION, GIT_COMMIT_HASH
|
||||||
);
|
);
|
||||||
|
|
||||||
let data = Data::new().await;
|
let data = Data::new().await;
|
||||||
@ -82,7 +85,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.configure(v1::services)
|
.configure(v1::services)
|
||||||
.configure(docs::services)
|
.configure(docs::services)
|
||||||
.app_data(get_json_err())
|
.app_data(get_json_err())
|
||||||
.service(Files::new("/", "./static"))
|
.service(Files::new("/", "./frontend/dist").index_file("index.html"))
|
||||||
})
|
})
|
||||||
.bind(SETTINGS.server.get_ip())
|
.bind(SETTINGS.server.get_ip())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|||||||
@ -1,193 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Login | mCaptcha</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<img src="/img/icon-trans.png" class="form__logo" alt="" />
|
|
||||||
<h2 class="form__brand">Sign in to mCaptcha</h2>
|
|
||||||
|
|
||||||
<div class="form-container">
|
|
||||||
<form class="form__box">
|
|
||||||
<label class="form__in-group" for="username"
|
|
||||||
>Username
|
|
||||||
<input
|
|
||||||
class="form__in-field"
|
|
||||||
id="username"
|
|
||||||
type="text"
|
|
||||||
name="username"
|
|
||||||
id="username"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label for="password" class="form__in-group"
|
|
||||||
>Password
|
|
||||||
<input
|
|
||||||
class="form__in-field"
|
|
||||||
type="password"
|
|
||||||
id="password"
|
|
||||||
name="password"
|
|
||||||
id="password"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<!--
|
|
||||||
<a class="form__pw-recovery" -href="/recovert/password"
|
|
||||||
>Forgot password?</a
|
|
||||||
>
|
|
||||||
-->
|
|
||||||
</label>
|
|
||||||
<button class="form__submit-button" type="submit">
|
|
||||||
Submit
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<div class="form__secondary-action">
|
|
||||||
<p class="form__secondary-action__banner">New to mCaptcha? <a href="/signup" class="form__secondary-action__link">Create account</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
export const SUBMIT = '/api/v1/signup';
|
|
||||||
|
|
||||||
export const isBlankString = (event, value, field) => {
|
|
||||||
if (!value.replace(/\s/g, '').length) {
|
|
||||||
event.preventDefautl()
|
|
||||||
alert(`${field} can't be empty`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const genJsonPayload = payload => {
|
|
||||||
let value = {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(payload),
|
|
||||||
};
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const submit = async e => {
|
|
||||||
let name = document.getElementById('username').value;
|
|
||||||
isBlankString(e, name, 'username');
|
|
||||||
|
|
||||||
let password = document.getElementById('password').value;
|
|
||||||
isBlankString(e, email, 'email');
|
|
||||||
|
|
||||||
let payload = {
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(SUBMIT, genJsonPayload(payload)).then(resp => {
|
|
||||||
if (resp.ok) {
|
|
||||||
aslert('signed in');
|
|
||||||
} else {
|
|
||||||
resp.json().then(err => alert(`Error ${resp.error}`));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let form = document.getElementById('form');
|
|
||||||
|
|
||||||
form.addEventListener('submit', submit, true);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form__logo {
|
|
||||||
width: 120px;
|
|
||||||
padding-top: 50px;
|
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form__brand {
|
|
||||||
padding: 10px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.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__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);
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</html>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user