-
Notifications
You must be signed in to change notification settings - Fork 18
Implement email verification and password recovery with Mailgun #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
A1L13N
merged 7 commits into
alphaonelabs:main
from
ghanshyam2005singh:feat/auth-email-verification
Jun 3, 2026
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
23642bb
feat(auth): implement email verification, password reset and Mailgun …
ghanshyam2005singh 60cbf92
- added resend-verification-email endpoint for email recovery
ghanshyam2005singh 8147cbc
- added resend-verification-email endpoint for email recovery
ghanshyam2005singh 43d3a1b
fixed
ghanshyam2005singh 5556400
fixed
ghanshyam2005singh a922fdc
test: update auth and schema initialization tests
ghanshyam2005singh bf9bf8c
Fail closed on verification email cooldown lookup errors
ghanshyam2005singh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en" class="scroll-smooth"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <meta name="description" content="Alpha One Labs - Forgot Password"> | ||
| <title>Forgot Password - Alpha One Labs</title> | ||
|
|
||
| <link rel="icon" type="image/png" href="https://alphaonelabs.com/static/images/logo.png" /> | ||
| <script src="https://cdn.tailwindcss.com"></script> | ||
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> | ||
|
|
||
| <script> | ||
| tailwind.config = { darkMode: 'class', theme: { extend: {} } } | ||
| const darkModeEnabled = localStorage.getItem('darkMode') === 'true'; | ||
| if (darkModeEnabled) document.documentElement.classList.add('dark'); | ||
| </script> | ||
|
|
||
| <style> | ||
| html, body { overflow-x: hidden; box-sizing: border-box; } | ||
| .dark { color-scheme: dark; } | ||
| :root { color-scheme: light; } | ||
| </style> | ||
| </head> | ||
|
|
||
| <body class="min-h-screen flex flex-col bg-white text-gray-900 dark:bg-black dark:text-gray-100 transition-colors duration-300"> | ||
|
|
||
| <div id="site-navbar"></div> | ||
|
|
||
| <main class="flex-1 w-full flex items-center justify-center py-12 px-4"> | ||
| <div class="w-full max-w-md"> | ||
|
|
||
| <!-- Brand Header --> | ||
| <div class="text-center mb-8"> | ||
| <a href="/" class="inline-flex items-center justify-center gap-3 mb-4 group"> | ||
| <div class="w-12 h-12 rounded-xl flex items-center justify-center shadow-lg group-hover:shadow-xl transition-shadow"> | ||
| <img src="https://alphaonelabs.com/static/images/logo.png" alt="Alpha One Labs Logo" class="w-6 h-6"> | ||
| </div> | ||
| <span class="font-bold text-2xl bg-gradient-to-r from-teal-600 to-cyan-600 dark:from-teal-400 dark:to-cyan-400 bg-clip-text text-transparent">Alpha One Labs</span> | ||
| </a> | ||
| </div> | ||
|
|
||
| <!-- Card --> | ||
| <div class="rounded-2xl shadow-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 overflow-hidden"> | ||
|
|
||
| <!-- Form panel --> | ||
| <div id="panel-form" class="p-8"> | ||
| <h2 class="text-xl font-bold text-gray-800 dark:text-gray-200 mb-2">Forgot your password?</h2> | ||
| <p class="text-gray-500 dark:text-gray-400 text-sm mb-6"> | ||
| Enter the email address associated with your account and we'll send you a reset link. | ||
| </p> | ||
|
|
||
| <form id="forgot-form" class="space-y-4" novalidate> | ||
| <div> | ||
| <label class="block text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2"> | ||
| <i class="fas fa-envelope mr-1.5 text-teal-600 dark:text-teal-400"></i>Email Address | ||
| </label> | ||
| <input id="f-email" type="email" autocomplete="email" required | ||
| class="w-full px-4 py-2.5 rounded-xl border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-teal-500 dark:focus:ring-teal-400 dark:bg-gray-700 dark:text-white text-sm transition" | ||
| placeholder="you@example.com" /> | ||
| </div> | ||
|
|
||
| <div id="forgot-err" class="hidden p-3 bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-300 text-sm flex items-start gap-2"> | ||
| <i class="fas fa-exclamation-circle mt-0.5 flex-shrink-0"></i> | ||
| <span id="forgot-err-msg"></span> | ||
| </div> | ||
|
|
||
| <button type="submit" | ||
| class="w-full bg-gradient-to-r from-teal-500 to-cyan-500 hover:from-teal-600 hover:to-cyan-600 text-white font-bold py-2.5 rounded-xl shadow-lg hover:shadow-xl transition-all duration-200 active:scale-95"> | ||
| <i class="fas fa-paper-plane mr-2"></i>Send Reset Link | ||
| </button> | ||
| </form> | ||
| </div> | ||
|
|
||
| <!-- Success panel --> | ||
| <div id="panel-success" class="p-8 hidden text-center"> | ||
| <div class="w-16 h-16 bg-teal-100 dark:bg-teal-900/40 rounded-full flex items-center justify-center mx-auto mb-4"> | ||
| <i class="fas fa-envelope-open-text text-3xl text-teal-600 dark:text-teal-400"></i> | ||
| </div> | ||
| <h2 class="text-xl font-bold text-gray-800 dark:text-gray-200 mb-2">Check your inbox</h2> | ||
| <p id="success-msg" class="text-gray-600 dark:text-gray-400 text-sm mb-6"> | ||
| If an account with that email exists, a password reset link has been sent. | ||
| </p> | ||
| <p class="text-xs text-gray-500 dark:text-gray-400"> | ||
| The link expires in 1 hour. Didn't receive it? Check your spam folder. | ||
| </p> | ||
| </div> | ||
|
|
||
| </div> | ||
|
|
||
| <p class="text-center text-gray-500 dark:text-gray-400 text-xs mt-6"> | ||
| <a href="/login" class="text-teal-600 dark:text-teal-400 hover:underline inline-flex items-center gap-1 font-semibold transition"> | ||
| <i class="fas fa-arrow-left"></i>Back to sign in | ||
| </a> | ||
| </p> | ||
| </div> | ||
| </main> | ||
|
|
||
| <div id="site-footer"></div> | ||
|
|
||
| <script src="/js/layout.js" defer></script> | ||
| <script> | ||
| const API = ''; | ||
|
|
||
| document.getElementById('forgot-form').addEventListener('submit', async e => { | ||
| e.preventDefault(); | ||
| document.getElementById('forgot-err').classList.add('hidden'); | ||
|
|
||
| const btn = e.target.querySelector('button[type=submit]'); | ||
| const originalHTML = btn.innerHTML; | ||
| btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Sending…'; | ||
| btn.disabled = true; | ||
|
|
||
| try { | ||
| const email = document.getElementById('f-email').value.trim(); | ||
| if (!email) throw new Error('Please enter your email address.'); | ||
|
|
||
| const res = await fetch(`${API}/api/forgot-password`, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ email }), | ||
| }); | ||
| const data = await res.json(); | ||
|
|
||
| if (!res.ok) throw new Error(data.error || 'Something went wrong.'); | ||
|
|
||
| document.getElementById('success-msg').textContent = | ||
| data.message || 'If an account with that email exists, a password reset link has been sent.'; | ||
| document.getElementById('panel-form').classList.add('hidden'); | ||
| document.getElementById('panel-success').classList.remove('hidden'); | ||
| } catch (err) { | ||
| const el = document.getElementById('forgot-err'); | ||
| document.getElementById('forgot-err-msg').textContent = err.message; | ||
| el.classList.remove('hidden'); | ||
| } finally { | ||
| btn.innerHTML = originalHTML; | ||
| btn.disabled = false; | ||
| } | ||
| }); | ||
| </script> | ||
| </body> | ||
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.