Files
ubicloud/views/layouts/app.erb
Enes Cakir 69e7ecfd3c Add Cloudflare Turnstile captcha to account creation form
Our account creation form is publicly accessible, and open to abuse by
spammers. The spam bots enter random email addresses and increase our
bounce rate.

We decided to add a captcha to the form to prevent this abuse. I
evaluated a few options and decided to use Cloudflare's Turnstile since
it's free and privacy-friendly. It's also easy to implement. [^1]

We add their client-side widget to our form. This widget add token input
and we use this token to verify the captcha on the server-side.

I added `cloudflare_turnstile.erb` component to add Cloudflare Turnstile
to any form easily. External Cloudflare script is loaded only when the
component is used.

There are 3 different modes for Turnstile: managed, non-interactive, and
invisible [^2]. It's configurable in the Cloudflare UI. I think we can
start with the invisible mode and see how it goes.

[^1]: https://developers.cloudflare.com/turnstile/get-started/
[^2]: https://developers.cloudflare.com/turnstile/concepts/widget/
2024-11-05 13:47:16 +03:00

89 lines
3.4 KiB
Plaintext

<!DOCTYPE html>
<html class="h-full bg-gray-50">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<title><%= ["Ubicloud", @page_title].compact.join(" - ") %></title>
<%== assets(:css) %>
<script
src="https://cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"
integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g="
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/dompurify@3.0.5/dist/purify.min.js"
integrity="sha256-QigBQMy2be3IqJD2ezKJUJ5gycSmyYlRHj2VGBuITpU="
crossorigin="anonymous"
></script>
<% if @enable_datepicker %>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css"
integrity="sha256-GzSkJVLJbxDk36qko2cnawOGiqz/Y8GsQv/jMTUrx1Q="
crossorigin="anonymous"
>
<script
src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"
integrity="sha256-Huqxy3eUcaCwqqk92RwusapTfWlvAasF6p2rxV6FJaE="
crossorigin="anonymous"
></script>
<% end %>
<% if @enable_cloudflare_turnstile %>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" defer></script>
<% end %>
</head>
<body class="h-full">
<% if rodauth.authenticated? %>
<div>
<%== render("layouts/sidebar/desktop") %>
<%== render("layouts/sidebar/mobile") %>
<div class="lg:pl-72">
<%== render("layouts/topbar") %>
<main class="py-10">
<div class="px-4 sm:px-6 lg:px-8">
<div class="px-2 sm:px-3 lg:px-4">
<%== render("components/flash_message") %>
<%== yield %>
</div>
</div>
</main>
</div>
</div>
<%== render("layouts/notifications") %>
<% elsif @error %>
<%== yield %>
<% else %>
<div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full sm:max-w-md">
<div class="flex shrink-0 items-center px-10 py-4">
<img class="" src="/logo-primary.png" alt="Ubicloud">
</div>
<h2 class="mt-6 text-center text-3xl font-bold tracking-tight text-gray-900"><%= @page_message %></h2>
</div>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div class="bg-white px-4 py-8 shadow sm:rounded-lg sm:px-10">
<div class="pt-4 empty:hidden"><%== render("components/flash_message") %></div>
<%== yield %>
</div>
</div>
<div class="mt-10 flex justify-center space-x-5">
<a href="https://github.com/ubicloud/ubicloud" target="_blank" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">GitHub</span>
<%== render("components/icon", locals: { name: "github" }) %>
</a>
<a href="mailto:support@ubicloud.com" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Support</span>
<%== render("components/icon", locals: { name: "hero-envelope" }) %>
</a>
</div>
</div>
<% end %>
<%== assets(:js) %>
</body>
</html>