Register done

This commit is contained in:
Dita Aji Pratama 2024-06-17 17:33:29 +07:00
parent 60b23d05f5
commit 89d0a555bf
9 changed files with 225 additions and 36 deletions

View File

@ -1,7 +1,8 @@
from bottle import Bottle, get, static_file from bottle import Bottle, get, static_file
page = { page = {
'public' :'pages/public' 'public' :'pages/public',
'email' :'pages/email'
} }
app = Bottle() app = Bottle()
@ -12,6 +13,10 @@ app = Bottle()
def static(filepath): def static(filepath):
return static_file(filepath, root="./static/css") return static_file(filepath, root="./static/css")
@app.get("/js/<filepath:re:.*\.(js)>")
def static(filepath):
return static_file(filepath, root="./static/js")
# Template staticdir: plain # Template staticdir: plain
@app.get("/templates/plain/css/<filepath:re:.*\.(css|sass|css.map)>") @app.get("/templates/plain/css/<filepath:re:.*\.(css|sass|css.map)>")

View File

@ -5,22 +5,51 @@
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/. # You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.
from bottle import Bottle, route, request from bottle import Bottle, route, request, response
import json
from config import directory from config import directory
import templates.plain.main as template_public import templates.plain.main as template_public
import templates.postcard.main as template_email
import modules.public.home as public_home import modules.public.home as public_home
import modules.public.register as public_register
import modules.api.auth as api_auth
app = Bottle() app = Bottle()
@app.route('/') @app.route('/')
def index(): def index():
params = { params = {
"mako":{ "mako":{
"website" : template_public.main(directory.page["public"], "home") "website" : template_public.main(directory.page["public"], "home")
} }
} }
return public_home.main().html(params) return public_home.home().html(params)
@app.route('/register/<roles>')
def index(roles):
params = {
"roles" :roles,
"mako" :{
"website" : template_public.main(directory.page["public"], "register")
}
}
return public_register.register().html(params)
@app.route('/api/auth/registration/register/<roles>', method='POST')
def index(roles):
try:
params = request.json
params["roles" ] = roles
params["mako" ] = {
"email" : template_email.main(directory.page["email"], "verification")
}
response.content_type = 'application/json'
return json.dumps(api_auth.auth().register(params), indent = 2).encode()
except Exception as e:
print(str(e))
return json.dumps({}, indent = 2).encode()

View File

@ -2,27 +2,19 @@ import mysql.connector as mariadb
from mako.template import Template from mako.template import Template
from bottle import request from bottle import request
import config.database as database from config import database, globalvar
import config.globalvar as globalvar
import bcrypt import bcrypt
import re import re
import datetime import datetime
import scripts.googly as googly from scripts import loggorilla, saltedkey, googly, tokenguard, sendwave
import scripts.saltedkey as saltedkey
import scripts.loggorilla as loggorilla
import scripts.sendwave as sendwave
import scripts.tokenguard as tokenguard
import scripts.paperplease as paperplease
class auth: class auth:
def __init__(self): def __init__(self):
# TODO: set database
self.db_main = mariadb.connect(**database.db_main) self.db_main = mariadb.connect(**database.db_main)
self.cursor = self.db_main.cursor(dictionary=True) self.cursor = self.db_main.cursor(dictionary=True)
# TODO: Config your SMTP
self.smtpconfig = globalvar.smtpconfig self.smtpconfig = globalvar.smtpconfig
def register(self, params): def register(self, params):
@ -39,13 +31,10 @@ class auth:
loggorilla.prcss(APIADDR, "Process parameters") loggorilla.prcss(APIADDR, "Process parameters")
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
token = saltedkey.token(username, hashed) token = saltedkey.token(username, hashed)
# TODO: set production/development version
if globalvar.production == True: if globalvar.production == True:
# TODO: set reCAPTCHA['server']
captcha_r = googly.recaptcha(captcha, globalvar.reCAPTCHA['server']) captcha_r = googly.recaptcha(captcha, globalvar.reCAPTCHA['server'])
score = captcha_r["score"] score = captcha_r["score"]
else: else:
# For localhost reCAPTCHA['server']
captcha_r = 'dev mode' captcha_r = 'dev mode'
score = 0.9 score = 0.9
loggorilla.prcss(APIADDR, "Get dependency data") loggorilla.prcss(APIADDR, "Get dependency data")
@ -55,7 +44,6 @@ class auth:
result_username = self.cursor.fetchone() result_username = self.cursor.fetchone()
self.cursor.execute(f"SELECT COUNT(*) AS `count` FROM auth_profile_verification INNER JOIN auth_profile ON auth_profile.id = auth_profile_verification.auth_profile WHERE auth_profile.email = %s AND auth_profile_verification.type = 'email' AND auth_profile_verification.verified = 0 ; ", (email,) ) self.cursor.execute(f"SELECT COUNT(*) AS `count` FROM auth_profile_verification INNER JOIN auth_profile ON auth_profile.id = auth_profile_verification.auth_profile WHERE auth_profile.email = %s AND auth_profile_verification.type = 'email' AND auth_profile_verification.verified = 0 ; ", (email,) )
result_unverified = self.cursor.fetchone() result_unverified = self.cursor.fetchone()
# TODO: set roles
self.cursor.execute(f"SELECT id, name FROM `auth_roles` WHERE auth_roles.name = %s ; ", (roles,) ) self.cursor.execute(f"SELECT id, name FROM `auth_roles` WHERE auth_roles.name = %s ; ", (roles,) )
result_roles = self.cursor.fetchone() result_roles = self.cursor.fetchone()
loggorilla.prcss(APIADDR, "Validation") loggorilla.prcss(APIADDR, "Validation")
@ -123,14 +111,12 @@ class auth:
self.cursor.execute("INSERT INTO `auth_profile_verification` VALUES (DEFAULT, %s, 'email', 0, NOW(), NULL);", (auth_profile_lastrowid,) ) self.cursor.execute("INSERT INTO `auth_profile_verification` VALUES (DEFAULT, %s, 'email', 0, NOW(), NULL);", (auth_profile_lastrowid,) )
self.cursor.execute("INSERT INTO `auth_profile_roles` VALUES (DEFAULT, %s, %s, NOW(), NULL);", (auth_profile_lastrowid, result_roles['id']) ) self.cursor.execute("INSERT INTO `auth_profile_roles` VALUES (DEFAULT, %s, %s, NOW(), NULL);", (auth_profile_lastrowid, result_roles['id']) )
loggorilla.prcss(APIADDR, "Generate URL") loggorilla.prcss(APIADDR, "Generate URL")
# TODO: set expired time
expired = datetime.datetime.now() + datetime.timedelta(minutes=30) # Can be hours or minutes expired = datetime.datetime.now() + datetime.timedelta(minutes=30) # Can be hours or minutes
expired_isoformat = expired.isoformat() expired_isoformat = expired.isoformat()
payload = { payload = {
"token" : token, "token" : token,
"expired": expired_isoformat "expired": expired_isoformat
} }
# TODO: Config SSH key for tokenguard and set verification URL
token_encrypt = tokenguard.encode(payload, globalvar.ssh['key']['private'], globalvar.ssh['passphrase']) token_encrypt = tokenguard.encode(payload, globalvar.ssh['key']['private'], globalvar.ssh['passphrase'])
verification_url = globalvar.verification_url(token_encrypt) verification_url = globalvar.verification_url(token_encrypt)
notme_url = globalvar.notme_url(token_encrypt) notme_url = globalvar.notme_url(token_encrypt)
@ -138,12 +124,12 @@ class auth:
self.smtpconfig['subject' ] = f"{globalvar.title} email verification" self.smtpconfig['subject' ] = f"{globalvar.title} email verification"
self.smtpconfig['to' ] = email self.smtpconfig['to' ] = email
self.smtpconfig['text' ] = f"Please visit this link to complete the registration: {verification_url}. You are not registering this? report on this: {notme_url}." self.smtpconfig['text' ] = f"Please visit this link to complete the registration: {verification_url}. You are not registering this? report on this: {notme_url}."
self.smtpconfig['html' ] = Template(params["mako"]["email"]['template']).render( self.smtpconfig['html' ] = Template(params["mako"]["email"]['index']).render(
title = globalvar.title, title = globalvar.title,
heading = self.smtpconfig['subject'], header = globalvar.title,
image = "https://colorlib.com/etc/email-template/10/images/email.png", copyright = globalvar.copyright,
unsubscribe = "#",
container = Template(params["mako"]["email"]['container']).render( container = Template(params["mako"]["email"]['container']).render(
header = self.smtpconfig['subject'],
verify = verification_url, verify = verification_url,
notme = notme_url notme = notme_url
) )
@ -199,7 +185,7 @@ class auth:
self.smtpconfig['subject' ] = f"{globalvar.title} email verification" self.smtpconfig['subject' ] = f"{globalvar.title} email verification"
self.smtpconfig['to' ] = email self.smtpconfig['to' ] = email
self.smtpconfig['text' ] = f"Please visit this link to complete the registration: {verification_url}. You are not registering this? report on this: {notme_url}." self.smtpconfig['text' ] = f"Please visit this link to complete the registration: {verification_url}. You are not registering this? report on this: {notme_url}."
self.smtpconfig['html' ] = Template(params["mako"]["email"]['template']).render( self.smtpconfig['html' ] = Template(params["mako"]["email"]['index']).render(
title = globalvar.title, title = globalvar.title,
heading = self.smtpconfig['subject'], heading = self.smtpconfig['subject'],
image = "https://colorlib.com/etc/email-template/10/images/email.png", image = "https://colorlib.com/etc/email-template/10/images/email.png",

View File

@ -1,19 +1,25 @@
import mysql.connector as mariadb
from mako.template import Template from mako.template import Template
from config import globalvar from config import globalvar, database
class main: class home:
def __init__(self): def __init__(self):
pass self.db_main = mariadb.connect(**database.db_main)
self.cursor = self.db_main.cursor(dictionary=True)
self.user_roles = [0] # Cari user roles disini
def html(self, params): def html(self, params):
active_page = "Home"
return Template(params["mako"]["website"]['index']).render( return Template(params["mako"]["website"]['index']).render(
title = globalvar.title, title = globalvar.title,
header = "Welcome to CostaPy", header = globalvar.header,
navbar = Template(params["mako"]["website"]['navbar']).render( navbar = Template(params["mako"]["website"]['navbar']).render(
menu = globalvar.menu['public']['navbar'], menu = globalvar.menu['public']['navbar'],
user_roles = [0], user_roles = self.user_roles,
active_page = "Home" active_page = active_page
), ),
footer = Template(params["mako"]["website"]['footer']).render( footer = Template(params["mako"]["website"]['footer']).render(
copyright = globalvar.copyright, copyright = globalvar.copyright,

View File

@ -0,0 +1,34 @@
import mysql.connector as mariadb
from mako.template import Template
from config import globalvar, database
class register:
def __init__(self):
self.db_main = mariadb.connect(**database.db_main)
self.cursor = self.db_main.cursor(dictionary=True)
self.user_roles = [0] # Cari user roles disini
def html(self, params):
roles = params["roles"]
active_page = "Register"
return Template(params["mako"]["website"]['index']).render(
title = globalvar.title,
header = globalvar.header,
navbar = Template(params["mako"]["website"]['navbar']).render(
menu = globalvar.menu['public']['navbar'],
user_roles = self.user_roles,
active_page = active_page
),
footer = Template(params["mako"]["website"]['footer']).render(
copyright = globalvar.copyright,
),
container = Template(params["mako"]["website"]['container']).render(
title = globalvar.title,
reCAPTCHA_client = globalvar.reCAPTCHA['client'],
roles = roles,
production = globalvar.production
)
)

View File

@ -0,0 +1,11 @@
<h2>${header}</h2>
<p>Thanks for signing up! This is the start of an exciting journey and youre just one step away from completing your account setup to start using your profile.</p>
<p>Click the button below to verify your account and get started.</p>
<a href="${verify}" class="button">
Verify email address
</a>
<br>
<br>
<p>You are not registering this? <a href="${notme}">I'm not registering this</a></p>
<br>

View File

@ -0,0 +1,32 @@
<h1>Register</h1>
% if production:
<script type="text/javascript" src="https://www.google.com/recaptcha/api.js"></script>
% endif
<script type="text/javascript" src="/js/carrack.js"></script>
<!-- FORM -->
<input type="hidden" id="roles" value="${roles}">
<input required type="email" id="form-email" placeholder="Email" > <br>
<input required type="text" id="form-username" placeholder="Username" > <br>
<input required type="password" id="form-password" placeholder="Password" > <br>
% if production:
<button class="g-recaptcha" data-sitekey="${reCAPTCHA_client}" data-callback='onSubmit' data-action='submit'>Register</button>
% else:
<button onclick="onSubmit('dev')">Register</button>
% endif
<!-- RESPONSE -->
<div id="alert-response" role="alert">
<b id="alert-status">Loading...</b> <span id="alert-desc">Please wait...</span>
</div>
<!-- RESEND FORM -->
<div id="resend-div">
<input type="hidden" id="resend-email" value="">
<button id="resend-link" onclick="resending()">Resend verification</button>
<p id="resend-message">Message here</p>
</div>
<script type="text/javascript" src="/js/auth/register.js"></script>

View File

@ -0,0 +1,68 @@
function flushResponse() {
document.getElementById("alert-response" ).style.display = 'none';
document.getElementById("resend-div" ).style.display = 'none';
document.getElementById("alert-response" ).classList.remove('alert-success' );
document.getElementById("alert-response" ).classList.remove('alert-danger' );
document.getElementById("alert-response" ).classList.remove('alert-primary' );
}
function loadingResponse() {
flushResponse();
document.getElementById("alert-status" ).innerHTML = "Loading...";
document.getElementById("alert-desc" ).innerHTML = "Please wait...";
document.getElementById("alert-response").classList.add('alert-primary');
document.getElementById("alert-response").style.display = 'block';
}
function responseAlert(response) {
flushResponse();
const obj = JSON.parse(response);
if (obj.status == "success" ) document.getElementById("alert-response").classList.add('alert-success' );
if (obj.status == "failed" ) document.getElementById("alert-response").classList.add('alert-danger' );
if (obj.desc == "check email for verification") {
document.getElementById("resend-email" ).value = document.getElementById("form-email").value;
document.getElementById("resend-message" ).innerHTML = obj.data.message;
document.getElementById("resend-link" ).setAttribute('href', obj.data.resend);
document.getElementById("resend-div" ).style.display = 'block';
}
document.getElementById("alert-status" ).innerHTML = obj.status;
document.getElementById("alert-desc" ).innerHTML = obj.desc;
document.getElementById("alert-response").style.display = 'block';
}
function onSubmit(token) {
loadingResponse();
var email = document.getElementById("form-email" ).value;
var username = document.getElementById("form-username" ).value;
var password = document.getElementById("form-password" ).value;
var roles = document.getElementById("roles" ).value;
var url = "/api/auth/registration/register/"+roles;
var payload = {
"email" : email,
"username" : username,
"password" : password
};
payload.captcha = token; // Add response from reCAPTCHA
sendHttpRequest(url, "POST", payload, function (error, response) {
if (error) console.error("Error:", error);
else {
console.log("JSON Response:", response);
responseAlert(response);
}
}, "application/json");
}
function resending() {
loadingResponse();
var email = document.getElementById("resend-email").value;
var url = "/api/auth/registration/resend?email="+email;
sendHttpRequest(url, "GET", null, function (error, response) {
if (error) console.error("Error:", error);
else {
console.log("JSON Response:", response);
responseAlert(response);
}
}, "multipart/form-data");
}
flushResponse();

18
app/static/js/carrack.js Normal file
View File

@ -0,0 +1,18 @@
function sendHttpRequest(url, method, data, callback, contentType = "multipart/form-data") {
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.setRequestHeader("Content-Type", contentType);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var response = xhr.responseText;
callback(null, response);
}
else callback(xhr.status, null);
}
};
var requestData;
if (contentType === "application/json") requestData = JSON.stringify(data);
else requestData = data;
xhr.send(requestData);
}