-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Cloudflare Turnstile to site
- Loading branch information
Showing
6 changed files
with
67 additions
and
21 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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 |
---|---|---|
|
@@ -14,10 +14,25 @@ | |
<script src="https://kit.fontawesome.com/15181efa86.js" crossorigin="anonymous"></script> | ||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/css/bulma.min.css" /> | ||
<script async defer data-website-id="9860fa16-de2e-4eaa-b0b4-f11c471ad4d6" src="https://umami.jerrynsh.com/umami.js"></script> | ||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" async defer></script> | ||
<script> | ||
const submitURL = () => { | ||
let statusElement = document.getElementById('status') | ||
let originalUrlElement = document.getElementById('url') | ||
let sitekey = '0x4AAAAAAAAujdqolLL946vD' | ||
let turnstileToken | ||
|
||
window.onloadTurnstileCallback = function () { | ||
const turnstileOptions = { | ||
sitekey, | ||
theme: 'light', | ||
callback: function (token) { | ||
turnstileToken = token | ||
}, | ||
} | ||
turnstile.render('#cf-turnstile-widget', turnstileOptions) | ||
} | ||
|
||
const submitURL = async () => { | ||
const statusElement = document.getElementById('status') | ||
const originalUrlElement = document.getElementById('url') | ||
|
||
if (!originalUrlElement.reportValidity()) { | ||
throw new Error('Invalid URL.') | ||
|
@@ -26,19 +41,20 @@ | |
statusElement.classList.add('is-loading') | ||
|
||
const originalUrl = originalUrlElement.value | ||
const body = JSON.stringify({ originalUrl }) | ||
const body = JSON.stringify({ originalUrl, turnstileToken }) | ||
|
||
const res = await fetch('/api/url', { method: 'POST', body }) | ||
statusElement.classList.remove('is-loading') | ||
|
||
if (res.status === 200) { | ||
const { shortUrl } = await res.json() | ||
statusElement.innerHTML = shortUrl | ||
} else { | ||
statusElement.innerHTML = `⛔ ${res.statusText}` | ||
} | ||
|
||
fetch('/api/url', { method: 'POST', body }) | ||
.then((data) => data.json()) | ||
.then((data) => { | ||
statusElement.classList.remove('is-loading') | ||
statusElement.innerHTML = data.shortUrl | ||
}) | ||
.catch((error) => { | ||
statusElement.classList.remove('is-loading') | ||
statusElement.innerHTML = '⛔ To Err is Human' | ||
}) | ||
originalUrlElement.value = '' | ||
turnstile.reset('#cf-turnstile-widget') | ||
} | ||
|
||
const copyToClipboard = (elementId) => { | ||
|
@@ -93,12 +109,17 @@ <h1 class="title is-4">Shorten A URL</h1> | |
<input | ||
class="input is-link is-primary is-medium is-rounded" | ||
type="url" | ||
placeholder="https://jerrynsh.com/" | ||
placeholder="https://jerrynsh.com" | ||
id="url" | ||
required | ||
/> | ||
</div> | ||
</div> | ||
<div class="checkbox mb-3"> | ||
<!-- The Turnstile widget will be injected in the following div. --> | ||
<div id="cf-turnstile-widget"></div> | ||
<!-- end. --> | ||
</div> | ||
<button id="submit" class="button is-block is-primary is-rounded is-fullwidth is-medium" onclick="submitURL()">Shorten</button> | ||
<br /> | ||
<button class="button is-info is-rounded is-small" onclick="copyToClipboard('status')"> | ||
|
This file contains 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
This file contains 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
This file contains 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,24 @@ | ||
const TURNSTILE_SITEVERIFY_ENDPOINT = 'https://challenges.cloudflare.com/turnstile/v0/siteverify' | ||
|
||
/* | ||
Turnstile is Cloudflare's smart CAPTCHA alternative. | ||
A middleware that calls Cloudflare's siteverify endpoint to validate the Turnstile widget response. | ||
Do note to set `TURNSTILE_SECRET` accordingly. | ||
*/ | ||
export const turnstileMiddleware = async (request) => { | ||
/* eslint-disable no-undef */ | ||
const { turnstileToken } = await request.clone().json() | ||
|
||
const response = await fetch(TURNSTILE_SITEVERIFY_ENDPOINT, { | ||
method: 'POST', | ||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | ||
body: `response=${turnstileToken}&secret=${TURNSTILE_SECRET}`, | ||
}) | ||
|
||
const verification = await response.json() | ||
if (!verification.success) { | ||
return new Response('Too Many Requests', { status: 429 }) | ||
} | ||
} |