mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 06:11:08 -05:00
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols. - Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage. - Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
212 lines
7.7 KiB
HTML
212 lines
7.7 KiB
HTML
{% extends "base/base.html" %}
|
|
{% load i18n %}
|
|
{% load account socialaccount %}
|
|
{% load static %}
|
|
{% load turnstile_tags %}
|
|
|
|
{% block title %}Register - ThrillWiki{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="flex items-center justify-center py-8 md:py-12">
|
|
<div class="w-full max-w-lg px-4">
|
|
<div class="auth-card">
|
|
<h1 class="auth-title">{% trans "Create Account" %}</h1>
|
|
|
|
{% get_providers as socialaccount_providers %}
|
|
{% if socialaccount_providers %}
|
|
<div class="space-y-3">
|
|
{% for provider in socialaccount_providers %}
|
|
<a
|
|
href="{% provider_login_url provider.id process='signup' %}"
|
|
class="btn-social {% if provider.id == 'discord' %}btn-discord{% elif provider.id == 'google' %}btn-google{% endif %}"
|
|
role="button"
|
|
tabindex="0"
|
|
onkeydown="if(event.key === 'Enter' || event.key === ' ') { this.click(); event.preventDefault(); }"
|
|
>
|
|
{% if provider.id == 'google' %}
|
|
<img
|
|
src="{% static 'images/google-icon.svg' %}"
|
|
alt=""
|
|
aria-hidden="true"
|
|
class="w-5 h-5 mr-3"
|
|
/>
|
|
<span>Continue with Google</span>
|
|
{% elif provider.id == 'discord' %}
|
|
<img
|
|
src="{% static 'images/discord-icon.svg' %}"
|
|
alt=""
|
|
aria-hidden="true"
|
|
class="w-5 h-5 mr-3"
|
|
/>
|
|
<span>Continue with Discord</span>
|
|
{% endif %}
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="auth-divider">
|
|
<span>Or continue with email</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<form class="space-y-6" method="POST" action="{% url 'account_signup' %}" role="form" aria-label="{% trans 'Create a new account' %}">
|
|
{% csrf_token %}
|
|
{% if form.non_field_errors %}
|
|
<div class="alert alert-error" role="alert">
|
|
<div class="text-sm">{{ form.non_field_errors }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<fieldset class="space-y-6">
|
|
<legend class="sr-only">{% trans "Account information" %}</legend>
|
|
|
|
{% include 'forms/partials/form_field.html' with field=form.username label=_("Username") %}
|
|
|
|
{% include 'forms/partials/form_field.html' with field=form.email label=_("Email") %}
|
|
</fieldset>
|
|
|
|
<fieldset class="space-y-6">
|
|
<legend class="sr-only">{% trans "Password" %}</legend>
|
|
|
|
<div>
|
|
{% include 'forms/partials/form_field.html' with field=form.password1 label=_("Password") show_help=False %}
|
|
<div class="mt-3 password-requirements" aria-live="polite">
|
|
<ul id="passwordRequirements" role="list" aria-label="{% trans 'Password requirements' %}">
|
|
<li class="invalid" id="req-length">
|
|
<i class="text-xs fas fa-circle" aria-hidden="true"></i>
|
|
<span>Must be at least 8 characters long</span>
|
|
</li>
|
|
<li class="invalid" id="req-similar">
|
|
<i class="text-xs fas fa-circle" aria-hidden="true"></i>
|
|
<span>Can't be too similar to your personal information</span>
|
|
</li>
|
|
<li class="invalid" id="req-common">
|
|
<i class="text-xs fas fa-circle" aria-hidden="true"></i>
|
|
<span>Can't be a commonly used password</span>
|
|
</li>
|
|
<li class="invalid" id="req-numeric">
|
|
<i class="text-xs fas fa-circle" aria-hidden="true"></i>
|
|
<span>Can't be entirely numeric</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
{% include 'forms/partials/form_field.html' with field=form.password2 label=_("Confirm Password") %}
|
|
</fieldset>
|
|
|
|
{% turnstile_widget %}
|
|
{% if redirect_field_value %}
|
|
<input
|
|
type="hidden"
|
|
name="{{ redirect_field_name }}"
|
|
value="{{ redirect_field_value }}"
|
|
/>
|
|
{% endif %}
|
|
|
|
<div>
|
|
<button type="submit" class="w-full btn-primary">
|
|
<i class="mr-2 fas fa-user-plus" aria-hidden="true"></i>
|
|
{% trans "Create Account" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="mt-6 text-sm text-center">
|
|
<p class="text-gray-600 dark:text-gray-400">
|
|
{% trans "Already have an account?" %}
|
|
<a
|
|
href="{% url 'account_login' %}"
|
|
class="ml-1 font-medium transition-colors text-primary hover:text-primary/80 focus:outline-hidden focus:underline"
|
|
onkeydown="if(event.key === 'Enter') { this.click(); }"
|
|
>
|
|
{% trans "Sign in" %}
|
|
</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function validatePassword(password) {
|
|
// Length requirement
|
|
const lengthReq = document.getElementById("req-length");
|
|
if (password.length >= 8) {
|
|
lengthReq.classList.remove("invalid");
|
|
lengthReq.classList.add("valid");
|
|
lengthReq.querySelector("i").classList.remove("fa-circle");
|
|
lengthReq.querySelector("i").classList.add("fa-check");
|
|
} else {
|
|
lengthReq.classList.remove("valid");
|
|
lengthReq.classList.add("invalid");
|
|
lengthReq.querySelector("i").classList.remove("fa-check");
|
|
lengthReq.querySelector("i").classList.add("fa-circle");
|
|
}
|
|
|
|
// Numeric requirement
|
|
const numericReq = document.getElementById("req-numeric");
|
|
if (!/^\d+$/.test(password)) {
|
|
numericReq.classList.remove("invalid");
|
|
numericReq.classList.add("valid");
|
|
numericReq.querySelector("i").classList.remove("fa-circle");
|
|
numericReq.querySelector("i").classList.add("fa-check");
|
|
} else {
|
|
numericReq.classList.remove("valid");
|
|
numericReq.classList.add("invalid");
|
|
numericReq.querySelector("i").classList.remove("fa-check");
|
|
numericReq.querySelector("i").classList.add("fa-circle");
|
|
}
|
|
|
|
// Common password check (basic)
|
|
const commonReq = document.getElementById("req-common");
|
|
const commonPasswords = ["password", "12345678", "qwerty", "letmein"];
|
|
if (!commonPasswords.includes(password.toLowerCase())) {
|
|
commonReq.classList.remove("invalid");
|
|
commonReq.classList.add("valid");
|
|
commonReq.querySelector("i").classList.remove("fa-circle");
|
|
commonReq.querySelector("i").classList.add("fa-check");
|
|
} else {
|
|
commonReq.classList.remove("valid");
|
|
commonReq.classList.add("invalid");
|
|
commonReq.querySelector("i").classList.remove("fa-check");
|
|
commonReq.querySelector("i").classList.add("fa-circle");
|
|
}
|
|
}
|
|
|
|
function validatePasswordMatch() {
|
|
const password1 = document.getElementById("id_password1").value;
|
|
const password2 = document.getElementById("id_password2").value;
|
|
const password2Input = document.getElementById("id_password2");
|
|
|
|
if (password2.length > 0) {
|
|
if (password1 === password2) {
|
|
password2Input.classList.remove("border-red-500");
|
|
password2Input.classList.add("border-green-500");
|
|
} else {
|
|
password2Input.classList.remove("border-green-500");
|
|
password2Input.classList.add("border-red-500");
|
|
}
|
|
} else {
|
|
password2Input.classList.remove("border-green-500", "border-red-500");
|
|
}
|
|
}
|
|
|
|
// Add event listeners after DOM is loaded
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const password1Input = document.getElementById('id_password1');
|
|
const password2Input = document.getElementById('id_password2');
|
|
|
|
if (password1Input) {
|
|
password1Input.addEventListener('input', function() {
|
|
validatePassword(this.value);
|
|
});
|
|
}
|
|
|
|
if (password2Input) {
|
|
password2Input.addEventListener('input', validatePasswordMatch);
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|