Feat: Add Turnstile and simplify login flow

This commit is contained in:
2025-10-02 14:41:28 -07:00
parent b2bd80943e
commit 0994c7c6e4

View File

@@ -7,6 +7,7 @@
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/scrypt.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/scrypt-js@3.0.1/scrypt.min.js"></script>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head> </head>
<body class="bg-black text-white min-h-screen flex items-center justify-center font-mono"> <body class="bg-black text-white min-h-screen flex items-center justify-center font-mono">
<div class="w-full max-w-xs" x-data="f()"> <div class="w-full max-w-xs" x-data="f()">
@@ -15,13 +16,14 @@
<input x-model="u" type="text" placeholder="username" required class="bg-gray-900 border-yellow-200/30 rounded w-full p-2"> <input x-model="u" type="text" placeholder="username" required class="bg-gray-900 border-yellow-200/30 rounded w-full p-2">
<input x-model="m" type="email" placeholder="email" required class="bg-gray-900 border-yellow-200/30 rounded w-full p-2"> <input x-model="m" type="email" placeholder="email" required class="bg-gray-900 border-yellow-200/30 rounded w-full p-2">
<input x-model="p" type="password" placeholder="password" required class="bg-gray-900 border-yellow-200/30 rounded w-full p-2"> <input x-model="p" type="password" placeholder="password" required class="bg-gray-900 border-yellow-200/30 rounded w-full p-2">
<div class="cf-turnstile" data-sitekey="0x4AAAAAAB4klN__r6wwJXs4"></div>
<button type="submit" :disabled="l" class="bg-yellow-200/80 hover:bg-yellow-200 text-black w-full p-2 rounded" x-text="l?'...':'Sign Up'"></button> <button type="submit" :disabled="l" class="bg-yellow-200/80 hover:bg-yellow-200 text-black w-full p-2 rounded" x-text="l?'...':'Sign Up'"></button>
</form> </form>
<p x-show="e" x-text="e" class="text-red-400 text-xs mt-2"></p> <p x-show="e" x-text="e" class="text-red-400 text-xs mt-2"></p>
<p class="text-xs mt-4">Have an account? <a href="/login" class="text-yellow-200/80 hover:text-yellow-200">Log in</a>.</p> <p class="text-xs mt-4">Have an account? <a href="/login" class="text-yellow-200/80 hover:text-yellow-200">Log in</a>.</p>
</div> </div>
<script> <script>
f=()=>({u:'',m:'',p:'',e:null,l:false,async s(){this.l=!0;this.e=null;try{const salt=crypto.getRandomValues(new Uint8Array(16)),N=16384,r=8,p=1,pw=(new TextEncoder()).encode(this.p);const h=await scrypt.scrypt(pw,salt,N,r,p,32),b64=a=>btoa(String.fromCharCode(...a));const h_str=`scrypt$${N}$${r}$${p}$${b64(salt)}$${b64(h)}`;const r1=await fetch('/api/signup',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:this.u,email:this.m,pass_hash:h_str})});if(!r1.ok){const d=await r1.json();throw new Error(d.error?.message||'Signup failed')}const r2=await fetch('/api/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:this.u,pass_hash:h_str})});if(!r2.ok)throw new Error('Login failed');window.location.href='/'}catch(e){this.e=e.message}finally{this.l=!1}}}) f=()=>({u:'',m:'',p:'',e:null,l:false,async s(){this.l=!0;this.e=null;try{const token=this.$el.querySelector('[name="cf-turnstile-response"]')?.value;if(!token)throw new Error('Please complete the CAPTCHA.');const salt=crypto.getRandomValues(new Uint8Array(16)),N=16384,r=8,p=1,pw=(new TextEncoder()).encode(this.p);const h=await scrypt.scrypt(pw,salt,N,r,p,32),b64=a=>btoa(String.fromCharCode(...a));const h_str=`scrypt$${N}$${r}$${p}$${b64(salt)}$${b64(h)}`;const r1=await fetch('/api/signup',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:this.u,email:this.m,pass_hash:h_str,'cf-turnstile-response':token})});if(!r1.ok){const d=await r1.json();throw new Error(d.error?.message||'Signup failed')}window.location.href='/'}catch(e){this.e=e.message}finally{this.l=!1}}})
</script> </script>
</body> </body>
</html> </html>