Upload 7 files
Browse files- templates/auth/change_password.html +73 -0
- templates/auth/forgot_password.html +54 -0
- templates/auth/invite_invalid.html +55 -0
- templates/auth/login.html +145 -0
- templates/auth/magic_link_sent.html +127 -0
- templates/auth/reset_password.html +80 -0
- templates/auth/verify.html +26 -0
templates/auth/change_password.html
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends "base.html" %}
|
| 2 |
+
|
| 3 |
+
{% block title %}{% if has_password %}Change{% else %}Set{% endif %} Password - PyRunner{% endblock %}
|
| 4 |
+
|
| 5 |
+
{% block content %}
|
| 6 |
+
<div class="min-h-[calc(100vh-12rem)] flex items-center justify-center px-4 py-12">
|
| 7 |
+
<div class="w-full max-w-md">
|
| 8 |
+
<div class="text-center mb-10">
|
| 9 |
+
<div class="w-16 h-16 bg-code-accent/20 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
| 10 |
+
<svg class="w-8 h-8 text-code-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 11 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
| 12 |
+
</svg>
|
| 13 |
+
</div>
|
| 14 |
+
<h1 class="text-2xl font-bold text-code-text mb-2">
|
| 15 |
+
{% if has_password %}Change Password{% else %}Set Password{% endif %}
|
| 16 |
+
</h1>
|
| 17 |
+
<p class="text-code-muted">
|
| 18 |
+
{% if has_password %}
|
| 19 |
+
Update your account password
|
| 20 |
+
{% else %}
|
| 21 |
+
Create a password for your account
|
| 22 |
+
{% endif %}
|
| 23 |
+
</p>
|
| 24 |
+
</div>
|
| 25 |
+
|
| 26 |
+
<div class="bg-code-surface border border-code-border rounded-xl p-8">
|
| 27 |
+
{% if form.errors %}
|
| 28 |
+
<div class="mb-6 p-4 bg-code-red/10 border border-code-red/30 rounded-lg">
|
| 29 |
+
{% for error in form.non_field_errors %}
|
| 30 |
+
<p class="text-sm text-code-red">{{ error }}</p>
|
| 31 |
+
{% endfor %}
|
| 32 |
+
</div>
|
| 33 |
+
{% endif %}
|
| 34 |
+
|
| 35 |
+
<form method="post" class="space-y-5">
|
| 36 |
+
{% csrf_token %}
|
| 37 |
+
|
| 38 |
+
<div>
|
| 39 |
+
<label for="id_password" class="block text-sm font-medium text-code-muted mb-2">
|
| 40 |
+
New Password
|
| 41 |
+
</label>
|
| 42 |
+
{{ form.password }}
|
| 43 |
+
{% if form.password.help_text %}
|
| 44 |
+
<p class="mt-1 text-xs text-code-muted">{{ form.password.help_text }}</p>
|
| 45 |
+
{% endif %}
|
| 46 |
+
{% if form.password.errors %}
|
| 47 |
+
<p class="mt-1 text-xs text-code-red">{{ form.password.errors.0 }}</p>
|
| 48 |
+
{% endif %}
|
| 49 |
+
</div>
|
| 50 |
+
|
| 51 |
+
<div>
|
| 52 |
+
<label for="id_password_confirm" class="block text-sm font-medium text-code-muted mb-2">
|
| 53 |
+
Confirm Password
|
| 54 |
+
</label>
|
| 55 |
+
{{ form.password_confirm }}
|
| 56 |
+
{% if form.password_confirm.errors %}
|
| 57 |
+
<p class="mt-1 text-xs text-code-red">{{ form.password_confirm.errors.0 }}</p>
|
| 58 |
+
{% endif %}
|
| 59 |
+
</div>
|
| 60 |
+
|
| 61 |
+
<div class="flex space-x-4 pt-2">
|
| 62 |
+
<a href="{% url 'cpanel:settings' %}" class="flex-1 py-3 px-4 bg-code-bg hover:bg-code-bg/80 text-code-text border border-code-border font-medium rounded-lg transition-colors text-center">
|
| 63 |
+
Cancel
|
| 64 |
+
</a>
|
| 65 |
+
<button type="submit" class="flex-1 py-3 px-4 bg-code-accent hover:bg-code-accent/90 text-white font-semibold rounded-lg transition-colors">
|
| 66 |
+
{% if has_password %}Change Password{% else %}Set Password{% endif %}
|
| 67 |
+
</button>
|
| 68 |
+
</div>
|
| 69 |
+
</form>
|
| 70 |
+
</div>
|
| 71 |
+
</div>
|
| 72 |
+
</div>
|
| 73 |
+
{% endblock %}
|
templates/auth/forgot_password.html
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends "base.html" %}
|
| 2 |
+
|
| 3 |
+
{% block title %}Forgot Password - PyRunner{% endblock %}
|
| 4 |
+
|
| 5 |
+
{% block content %}
|
| 6 |
+
<div class="min-h-[calc(100vh-12rem)] flex items-center justify-center px-4 py-12">
|
| 7 |
+
<div class="w-full max-w-md">
|
| 8 |
+
<div class="text-center mb-10">
|
| 9 |
+
<div class="w-16 h-16 bg-code-accent/20 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
| 10 |
+
<svg class="w-8 h-8 text-code-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 11 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
| 12 |
+
</svg>
|
| 13 |
+
</div>
|
| 14 |
+
<h1 class="text-2xl font-bold text-code-text mb-2">Forgot Password</h1>
|
| 15 |
+
<p class="text-code-muted">Enter your email to receive a password reset link</p>
|
| 16 |
+
</div>
|
| 17 |
+
|
| 18 |
+
<div class="bg-code-surface border border-code-border rounded-xl p-8">
|
| 19 |
+
<form method="post" class="space-y-5">
|
| 20 |
+
{% csrf_token %}
|
| 21 |
+
|
| 22 |
+
<div>
|
| 23 |
+
<label for="email" class="block text-sm font-medium text-code-muted mb-2">
|
| 24 |
+
Email address
|
| 25 |
+
</label>
|
| 26 |
+
<input
|
| 27 |
+
type="email"
|
| 28 |
+
id="email"
|
| 29 |
+
name="email"
|
| 30 |
+
required
|
| 31 |
+
autocomplete="email"
|
| 32 |
+
autofocus
|
| 33 |
+
placeholder="you@example.com"
|
| 34 |
+
class="w-full px-4 py-3 bg-code-bg border border-code-border rounded-lg text-code-text placeholder-code-muted/50 focus:outline-none focus:ring-2 focus:ring-code-accent/50 focus:border-code-accent transition-all"
|
| 35 |
+
>
|
| 36 |
+
</div>
|
| 37 |
+
|
| 38 |
+
<button
|
| 39 |
+
type="submit"
|
| 40 |
+
class="w-full py-3 px-4 bg-code-accent hover:bg-code-accent/90 text-white font-semibold rounded-lg transition-colors"
|
| 41 |
+
>
|
| 42 |
+
Send Reset Link
|
| 43 |
+
</button>
|
| 44 |
+
</form>
|
| 45 |
+
|
| 46 |
+
<div class="mt-6 text-center">
|
| 47 |
+
<a href="{% url 'auth:login' %}" class="text-sm text-code-muted hover:text-code-accent transition-colors">
|
| 48 |
+
Back to sign in
|
| 49 |
+
</a>
|
| 50 |
+
</div>
|
| 51 |
+
</div>
|
| 52 |
+
</div>
|
| 53 |
+
</div>
|
| 54 |
+
{% endblock %}
|
templates/auth/invite_invalid.html
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends "base.html" %}
|
| 2 |
+
|
| 3 |
+
{% block title %}Invalid Invite - PyRunner{% endblock %}
|
| 4 |
+
|
| 5 |
+
{% block content %}
|
| 6 |
+
<div class="min-h-[calc(100vh-12rem)] flex items-center justify-center px-4 py-12">
|
| 7 |
+
<div class="w-full max-w-md text-center">
|
| 8 |
+
<div class="w-20 h-20 bg-code-red/20 rounded-full flex items-center justify-center mx-auto mb-6">
|
| 9 |
+
<svg class="w-10 h-10 text-code-red" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 10 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
| 11 |
+
</svg>
|
| 12 |
+
</div>
|
| 13 |
+
|
| 14 |
+
<h1 class="text-2xl font-bold text-code-text mb-4">{{ error }}</h1>
|
| 15 |
+
|
| 16 |
+
<p class="text-code-muted mb-8">
|
| 17 |
+
{{ message }}
|
| 18 |
+
</p>
|
| 19 |
+
|
| 20 |
+
<div class="bg-code-surface border border-code-border rounded-xl p-6 mb-8">
|
| 21 |
+
<h3 class="text-sm font-medium text-code-text mb-3">What can you do?</h3>
|
| 22 |
+
<ul class="text-sm text-code-muted space-y-2 text-left">
|
| 23 |
+
<li class="flex items-start">
|
| 24 |
+
<svg class="w-4 h-4 text-code-accent mr-2 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 25 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
| 26 |
+
</svg>
|
| 27 |
+
Ask an administrator for a new invite
|
| 28 |
+
</li>
|
| 29 |
+
<li class="flex items-start">
|
| 30 |
+
<svg class="w-4 h-4 text-code-accent mr-2 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 31 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
| 32 |
+
</svg>
|
| 33 |
+
Check if you have the correct link
|
| 34 |
+
</li>
|
| 35 |
+
<li class="flex items-start">
|
| 36 |
+
<svg class="w-4 h-4 text-code-accent mr-2 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 37 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
| 38 |
+
</svg>
|
| 39 |
+
If you already have an account, try signing in
|
| 40 |
+
</li>
|
| 41 |
+
</ul>
|
| 42 |
+
</div>
|
| 43 |
+
|
| 44 |
+
<a
|
| 45 |
+
href="{% url 'auth:login' %}"
|
| 46 |
+
class="inline-flex items-center text-code-accent hover:text-code-accent/80 transition-colors"
|
| 47 |
+
>
|
| 48 |
+
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 49 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
| 50 |
+
</svg>
|
| 51 |
+
Back to sign in
|
| 52 |
+
</a>
|
| 53 |
+
</div>
|
| 54 |
+
</div>
|
| 55 |
+
{% endblock %}
|
templates/auth/login.html
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends "base.html" %}
|
| 2 |
+
|
| 3 |
+
{% block title %}Sign In - PyRunner{% endblock %}
|
| 4 |
+
|
| 5 |
+
{% block header %}{% endblock %}
|
| 6 |
+
{% block footer %}{% endblock %}
|
| 7 |
+
|
| 8 |
+
{% block content %}
|
| 9 |
+
<div class="min-h-screen flex items-center justify-center px-4 py-12">
|
| 10 |
+
<div class="w-full max-w-md">
|
| 11 |
+
<div class="text-center mb-10">
|
| 12 |
+
<div class="w-16 h-16 bg-code-accent/20 rounded-2xl flex items-center justify-center mx-auto mb-6 glow-accent">
|
| 13 |
+
<svg class="w-8 h-8 text-code-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 14 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
| 15 |
+
</svg>
|
| 16 |
+
</div>
|
| 17 |
+
<h1 class="text-3xl font-bold gradient-text mb-6">PyRunner</h1>
|
| 18 |
+
</div>
|
| 19 |
+
|
| 20 |
+
<div class="bg-code-surface border border-code-border rounded-xl p-8">
|
| 21 |
+
<h2 class="text-xl font-semibold text-code-text mb-6 text-center">Sign In</h2>
|
| 22 |
+
|
| 23 |
+
{% if error_message %}
|
| 24 |
+
<div class="mb-6 p-4 bg-code-red/10 border border-code-red/30 rounded-lg">
|
| 25 |
+
<p class="text-sm text-code-red">{{ error_message }}</p>
|
| 26 |
+
</div>
|
| 27 |
+
{% endif %}
|
| 28 |
+
|
| 29 |
+
<!-- Password Login Form -->
|
| 30 |
+
<form method="post" class="space-y-5" id="password-form">
|
| 31 |
+
{% csrf_token %}
|
| 32 |
+
<input type="hidden" name="action" value="password">
|
| 33 |
+
|
| 34 |
+
<div>
|
| 35 |
+
<label for="id_email" class="block text-sm font-medium text-code-muted mb-2">
|
| 36 |
+
Email address
|
| 37 |
+
</label>
|
| 38 |
+
{{ password_form.email }}
|
| 39 |
+
</div>
|
| 40 |
+
|
| 41 |
+
<div>
|
| 42 |
+
<label for="id_password" class="block text-sm font-medium text-code-muted mb-2">
|
| 43 |
+
Password
|
| 44 |
+
</label>
|
| 45 |
+
{{ password_form.password }}
|
| 46 |
+
</div>
|
| 47 |
+
|
| 48 |
+
<button
|
| 49 |
+
type="submit"
|
| 50 |
+
class="w-full py-3 px-4 bg-code-accent hover:bg-code-accent/90 text-white font-semibold rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-code-accent/50 focus:ring-offset-2 focus:ring-offset-code-surface"
|
| 51 |
+
>
|
| 52 |
+
Sign In
|
| 53 |
+
</button>
|
| 54 |
+
</form>
|
| 55 |
+
|
| 56 |
+
{% if email_enabled %}
|
| 57 |
+
<!-- Forgot Password Link -->
|
| 58 |
+
<div class="mt-4 text-center">
|
| 59 |
+
<a href="{% url 'auth:forgot_password' %}" class="text-sm text-code-muted hover:text-code-accent transition-colors">
|
| 60 |
+
Forgot your password?
|
| 61 |
+
</a>
|
| 62 |
+
</div>
|
| 63 |
+
|
| 64 |
+
<!-- Divider -->
|
| 65 |
+
<div class="relative my-6">
|
| 66 |
+
<div class="absolute inset-0 flex items-center">
|
| 67 |
+
<div class="w-full border-t border-code-border"></div>
|
| 68 |
+
</div>
|
| 69 |
+
<div class="relative flex justify-center text-sm">
|
| 70 |
+
<span class="px-4 bg-code-surface text-code-muted">or</span>
|
| 71 |
+
</div>
|
| 72 |
+
</div>
|
| 73 |
+
|
| 74 |
+
<!-- Magic Link Section -->
|
| 75 |
+
<div id="magic-link-section">
|
| 76 |
+
<button
|
| 77 |
+
type="button"
|
| 78 |
+
onclick="toggleMagicLink()"
|
| 79 |
+
id="magic-link-toggle"
|
| 80 |
+
class="w-full py-3 px-4 bg-code-bg hover:bg-code-bg/80 text-code-text border border-code-border font-medium rounded-lg transition-colors flex items-center justify-center space-x-2"
|
| 81 |
+
>
|
| 82 |
+
<svg class="w-5 h-5 text-code-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 83 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
| 84 |
+
</svg>
|
| 85 |
+
<span>Use Magic Link Instead</span>
|
| 86 |
+
</button>
|
| 87 |
+
|
| 88 |
+
<form method="post" class="hidden mt-4 space-y-4" id="magic-link-form">
|
| 89 |
+
{% csrf_token %}
|
| 90 |
+
<input type="hidden" name="action" value="magic_link">
|
| 91 |
+
|
| 92 |
+
<div>
|
| 93 |
+
<label for="magic_email" class="block text-sm font-medium text-code-muted mb-2">
|
| 94 |
+
Email address
|
| 95 |
+
</label>
|
| 96 |
+
<input
|
| 97 |
+
type="email"
|
| 98 |
+
id="magic_email"
|
| 99 |
+
name="magic_email"
|
| 100 |
+
required
|
| 101 |
+
autocomplete="email"
|
| 102 |
+
placeholder="you@example.com"
|
| 103 |
+
class="w-full px-4 py-3 bg-code-bg border border-code-border rounded-lg text-code-text placeholder-code-muted/50 focus:outline-none focus:ring-2 focus:ring-code-accent/50 focus:border-code-accent transition-all"
|
| 104 |
+
>
|
| 105 |
+
</div>
|
| 106 |
+
|
| 107 |
+
<button
|
| 108 |
+
type="submit"
|
| 109 |
+
class="w-full py-3 px-4 bg-code-accent hover:bg-code-accent/90 text-white font-semibold rounded-lg transition-colors"
|
| 110 |
+
>
|
| 111 |
+
Send Magic Link
|
| 112 |
+
</button>
|
| 113 |
+
|
| 114 |
+
<button
|
| 115 |
+
type="button"
|
| 116 |
+
onclick="toggleMagicLink()"
|
| 117 |
+
class="w-full py-2 text-sm text-code-muted hover:text-code-text transition-colors"
|
| 118 |
+
>
|
| 119 |
+
Back to password login
|
| 120 |
+
</button>
|
| 121 |
+
</form>
|
| 122 |
+
</div>
|
| 123 |
+
{% endif %}
|
| 124 |
+
</div>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
|
| 128 |
+
<script>
|
| 129 |
+
function toggleMagicLink() {
|
| 130 |
+
const toggle = document.getElementById('magic-link-toggle');
|
| 131 |
+
const form = document.getElementById('magic-link-form');
|
| 132 |
+
const passwordForm = document.getElementById('password-form');
|
| 133 |
+
|
| 134 |
+
if (form.classList.contains('hidden')) {
|
| 135 |
+
form.classList.remove('hidden');
|
| 136 |
+
toggle.classList.add('hidden');
|
| 137 |
+
passwordForm.classList.add('hidden');
|
| 138 |
+
} else {
|
| 139 |
+
form.classList.add('hidden');
|
| 140 |
+
toggle.classList.remove('hidden');
|
| 141 |
+
passwordForm.classList.remove('hidden');
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
</script>
|
| 145 |
+
{% endblock %}
|
templates/auth/magic_link_sent.html
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends "base.html" %}
|
| 2 |
+
|
| 3 |
+
{% block title %}{% if show_magic_link %}Sign In{% else %}Check Your Email{% endif %} - PyRunner{% endblock %}
|
| 4 |
+
|
| 5 |
+
{% block content %}
|
| 6 |
+
<div class="min-h-[calc(100vh-12rem)] flex items-center justify-center px-4 py-12">
|
| 7 |
+
<div class="w-full max-w-md text-center">
|
| 8 |
+
{% if show_magic_link %}
|
| 9 |
+
<!-- Email not configured - show magic link with security warning -->
|
| 10 |
+
<div class="w-20 h-20 bg-red-500/20 rounded-full flex items-center justify-center mx-auto mb-6">
|
| 11 |
+
<svg class="w-10 h-10 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 12 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
| 13 |
+
</svg>
|
| 14 |
+
</div>
|
| 15 |
+
|
| 16 |
+
<h1 class="text-2xl font-bold text-code-text mb-4">Your Sign-In Link</h1>
|
| 17 |
+
|
| 18 |
+
<!-- Security Warning -->
|
| 19 |
+
<div class="bg-red-500/10 border border-red-500/30 rounded-xl p-5 mb-6 text-left">
|
| 20 |
+
<div class="flex items-start gap-3 mb-3">
|
| 21 |
+
<svg class="w-5 h-5 text-red-400 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 22 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
| 23 |
+
</svg>
|
| 24 |
+
<div>
|
| 25 |
+
<h3 class="text-red-400 font-semibold text-sm">Security Warning: Email Not Configured</h3>
|
| 26 |
+
<p class="text-xs text-code-muted mt-1">
|
| 27 |
+
Sign-in links are displayed on-screen because email is disabled.
|
| 28 |
+
<strong class="text-red-300">Anyone who sees this screen can sign in.</strong>
|
| 29 |
+
</p>
|
| 30 |
+
</div>
|
| 31 |
+
</div>
|
| 32 |
+
<p class="text-xs text-red-300/80 ml-8">
|
| 33 |
+
Configure SMTP in Settings → Email Notifications to send links securely.
|
| 34 |
+
</p>
|
| 35 |
+
</div>
|
| 36 |
+
|
| 37 |
+
<!-- Sign In Button and Link -->
|
| 38 |
+
<div class="bg-code-surface border border-code-border rounded-xl p-6 mb-6">
|
| 39 |
+
<a href="{{ magic_link_url }}"
|
| 40 |
+
class="block w-full py-3 px-4 bg-code-accent hover:bg-code-accent/90 text-white font-semibold rounded-lg transition-colors text-center mb-4">
|
| 41 |
+
Sign In Now
|
| 42 |
+
</a>
|
| 43 |
+
|
| 44 |
+
<div class="relative">
|
| 45 |
+
<input type="text"
|
| 46 |
+
value="{{ magic_link_url }}"
|
| 47 |
+
readonly
|
| 48 |
+
id="magic-link-input"
|
| 49 |
+
class="w-full px-4 py-3 pr-20 bg-code-bg border border-code-border rounded-lg text-code-text font-mono text-xs">
|
| 50 |
+
<button onclick="copyMagicLink()"
|
| 51 |
+
id="copy-btn"
|
| 52 |
+
class="absolute right-2 top-1/2 -translate-y-1/2 px-3 py-1.5 bg-code-surface border border-code-border rounded text-code-muted text-sm hover:text-code-text hover:bg-code-border transition-colors">
|
| 53 |
+
Copy
|
| 54 |
+
</button>
|
| 55 |
+
</div>
|
| 56 |
+
|
| 57 |
+
<p class="text-xs text-code-muted mt-3 text-center">
|
| 58 |
+
Expires in 15 minutes. One-time use only.
|
| 59 |
+
</p>
|
| 60 |
+
</div>
|
| 61 |
+
|
| 62 |
+
<script>
|
| 63 |
+
function copyMagicLink() {
|
| 64 |
+
const input = document.getElementById('magic-link-input');
|
| 65 |
+
const btn = document.getElementById('copy-btn');
|
| 66 |
+
navigator.clipboard.writeText(input.value).then(() => {
|
| 67 |
+
btn.textContent = 'Copied!';
|
| 68 |
+
btn.classList.add('text-code-green');
|
| 69 |
+
setTimeout(() => {
|
| 70 |
+
btn.textContent = 'Copy';
|
| 71 |
+
btn.classList.remove('text-code-green');
|
| 72 |
+
}, 2000);
|
| 73 |
+
});
|
| 74 |
+
}
|
| 75 |
+
</script>
|
| 76 |
+
|
| 77 |
+
{% else %}
|
| 78 |
+
<!-- Email configured - show check email message -->
|
| 79 |
+
<div class="w-20 h-20 bg-code-green/20 rounded-full flex items-center justify-center mx-auto mb-6">
|
| 80 |
+
<svg class="w-10 h-10 text-code-green" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 81 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
| 82 |
+
</svg>
|
| 83 |
+
</div>
|
| 84 |
+
|
| 85 |
+
<h1 class="text-2xl font-bold text-code-text mb-4">Check your email</h1>
|
| 86 |
+
|
| 87 |
+
<p class="text-code-muted mb-8">
|
| 88 |
+
We've sent you a magic link to sign in. Click the link in your email to continue.
|
| 89 |
+
</p>
|
| 90 |
+
|
| 91 |
+
<div class="bg-code-surface border border-code-border rounded-xl p-6 mb-8">
|
| 92 |
+
<h3 class="text-sm font-medium text-code-text mb-3">Didn't receive the email?</h3>
|
| 93 |
+
<ul class="text-sm text-code-muted space-y-2 text-left">
|
| 94 |
+
<li class="flex items-start">
|
| 95 |
+
<svg class="w-4 h-4 text-code-accent mr-2 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 96 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
| 97 |
+
</svg>
|
| 98 |
+
Check your spam or junk folder
|
| 99 |
+
</li>
|
| 100 |
+
<li class="flex items-start">
|
| 101 |
+
<svg class="w-4 h-4 text-code-accent mr-2 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 102 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
| 103 |
+
</svg>
|
| 104 |
+
Make sure you entered the correct email
|
| 105 |
+
</li>
|
| 106 |
+
<li class="flex items-start">
|
| 107 |
+
<svg class="w-4 h-4 text-code-accent mr-2 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 108 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
| 109 |
+
</svg>
|
| 110 |
+
The link expires in 15 minutes
|
| 111 |
+
</li>
|
| 112 |
+
</ul>
|
| 113 |
+
</div>
|
| 114 |
+
{% endif %}
|
| 115 |
+
|
| 116 |
+
<a
|
| 117 |
+
href="{% url 'auth:login' %}"
|
| 118 |
+
class="inline-flex items-center text-code-accent hover:text-code-accent/80 transition-colors"
|
| 119 |
+
>
|
| 120 |
+
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 121 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
| 122 |
+
</svg>
|
| 123 |
+
Back to sign in
|
| 124 |
+
</a>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
{% endblock %}
|
templates/auth/reset_password.html
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends "base.html" %}
|
| 2 |
+
|
| 3 |
+
{% block title %}Reset Password - PyRunner{% endblock %}
|
| 4 |
+
|
| 5 |
+
{% block content %}
|
| 6 |
+
<div class="min-h-[calc(100vh-12rem)] flex items-center justify-center px-4 py-12">
|
| 7 |
+
<div class="w-full max-w-md">
|
| 8 |
+
<div class="text-center mb-10">
|
| 9 |
+
<div class="w-16 h-16 bg-code-accent/20 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
| 10 |
+
<svg class="w-8 h-8 text-code-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 11 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
|
| 12 |
+
</svg>
|
| 13 |
+
</div>
|
| 14 |
+
<h1 class="text-2xl font-bold text-code-text mb-2">Reset Password</h1>
|
| 15 |
+
<p class="text-code-muted">Enter your new password</p>
|
| 16 |
+
</div>
|
| 17 |
+
|
| 18 |
+
<div class="bg-code-surface border border-code-border rounded-xl p-8">
|
| 19 |
+
{% if error %}
|
| 20 |
+
<!-- Error State -->
|
| 21 |
+
<div class="text-center">
|
| 22 |
+
<div class="w-16 h-16 bg-code-red/20 rounded-full flex items-center justify-center mx-auto mb-6">
|
| 23 |
+
<svg class="w-8 h-8 text-code-red" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 24 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
| 25 |
+
</svg>
|
| 26 |
+
</div>
|
| 27 |
+
<h2 class="text-xl font-semibold text-code-text mb-2">{{ error }}</h2>
|
| 28 |
+
<p class="text-code-muted mb-6">{{ message }}</p>
|
| 29 |
+
<a href="{% url 'auth:forgot_password' %}" class="inline-block py-3 px-6 bg-code-accent hover:bg-code-accent/90 text-white font-semibold rounded-lg transition-colors">
|
| 30 |
+
Request New Link
|
| 31 |
+
</a>
|
| 32 |
+
</div>
|
| 33 |
+
{% else %}
|
| 34 |
+
<!-- Reset Form -->
|
| 35 |
+
{% if form.errors %}
|
| 36 |
+
<div class="mb-6 p-4 bg-code-red/10 border border-code-red/30 rounded-lg">
|
| 37 |
+
{% for error in form.non_field_errors %}
|
| 38 |
+
<p class="text-sm text-code-red">{{ error }}</p>
|
| 39 |
+
{% endfor %}
|
| 40 |
+
</div>
|
| 41 |
+
{% endif %}
|
| 42 |
+
|
| 43 |
+
<form method="post" class="space-y-5">
|
| 44 |
+
{% csrf_token %}
|
| 45 |
+
|
| 46 |
+
<div>
|
| 47 |
+
<label for="id_password" class="block text-sm font-medium text-code-muted mb-2">
|
| 48 |
+
New Password
|
| 49 |
+
</label>
|
| 50 |
+
{{ form.password }}
|
| 51 |
+
{% if form.password.help_text %}
|
| 52 |
+
<p class="mt-1 text-xs text-code-muted">{{ form.password.help_text }}</p>
|
| 53 |
+
{% endif %}
|
| 54 |
+
{% if form.password.errors %}
|
| 55 |
+
<p class="mt-1 text-xs text-code-red">{{ form.password.errors.0 }}</p>
|
| 56 |
+
{% endif %}
|
| 57 |
+
</div>
|
| 58 |
+
|
| 59 |
+
<div>
|
| 60 |
+
<label for="id_password_confirm" class="block text-sm font-medium text-code-muted mb-2">
|
| 61 |
+
Confirm Password
|
| 62 |
+
</label>
|
| 63 |
+
{{ form.password_confirm }}
|
| 64 |
+
{% if form.password_confirm.errors %}
|
| 65 |
+
<p class="mt-1 text-xs text-code-red">{{ form.password_confirm.errors.0 }}</p>
|
| 66 |
+
{% endif %}
|
| 67 |
+
</div>
|
| 68 |
+
|
| 69 |
+
<button
|
| 70 |
+
type="submit"
|
| 71 |
+
class="w-full py-3 px-4 bg-code-accent hover:bg-code-accent/90 text-white font-semibold rounded-lg transition-colors"
|
| 72 |
+
>
|
| 73 |
+
Reset Password
|
| 74 |
+
</button>
|
| 75 |
+
</form>
|
| 76 |
+
{% endif %}
|
| 77 |
+
</div>
|
| 78 |
+
</div>
|
| 79 |
+
</div>
|
| 80 |
+
{% endblock %}
|
templates/auth/verify.html
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends "base.html" %}
|
| 2 |
+
|
| 3 |
+
{% block title %}{{ error }} - PyRunner{% endblock %}
|
| 4 |
+
|
| 5 |
+
{% block content %}
|
| 6 |
+
<div class="min-h-[calc(100vh-12rem)] flex items-center justify-center px-4 py-12">
|
| 7 |
+
<div class="w-full max-w-md text-center">
|
| 8 |
+
<div class="w-20 h-20 bg-code-red/20 rounded-full flex items-center justify-center mx-auto mb-6">
|
| 9 |
+
<svg class="w-10 h-10 text-code-red" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 10 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
| 11 |
+
</svg>
|
| 12 |
+
</div>
|
| 13 |
+
|
| 14 |
+
<h1 class="text-2xl font-bold text-code-text mb-4">{{ error }}</h1>
|
| 15 |
+
|
| 16 |
+
<p class="text-code-muted mb-8">{{ message }}</p>
|
| 17 |
+
|
| 18 |
+
<a
|
| 19 |
+
href="{% url 'auth:login' %}"
|
| 20 |
+
class="inline-flex items-center justify-center w-full py-3 px-4 bg-code-accent hover:bg-code-accent/90 text-white font-semibold rounded-lg transition-colors"
|
| 21 |
+
>
|
| 22 |
+
Request a new link
|
| 23 |
+
</a>
|
| 24 |
+
</div>
|
| 25 |
+
</div>
|
| 26 |
+
{% endblock %}
|