mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-07 20:47:37 +00:00
config(git,claude,ssh): templated gitconfig; claude settings/commands/hooks/skills; ssh config
gitconfig: name/email from chezmoi data, gh credential helper by PATH lookup (portable to darwin). claude: no credentials, history, projects, sessions, or plugin caches. ssh config as private_ (0600). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3c691e8dc5
commit
20891f3910
476 files changed
+59086
-7
No files matched your search
@@ -0,0 +1,102 @@
|
||||
# Astro
|
||||
|
||||
For Astro projects. The form posts directly to the Worker. Astro frontmatter handles config substitution at build time.
|
||||
|
||||
```astro title="src/pages/signup.astro"
|
||||
---
|
||||
const WORKER_URL = import.meta.env.PUBLIC_TURNSTILE_WORKER_URL;
|
||||
const SITEKEY = import.meta.env.PUBLIC_TURNSTILE_SITEKEY;
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script
|
||||
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<form action={`${WORKER_URL}/`} method="POST">
|
||||
<input name="email" type="email" required />
|
||||
<div
|
||||
class="cf-turnstile"
|
||||
data-sitekey={SITEKEY}
|
||||
data-action="turnstile-spin-v1"
|
||||
/>
|
||||
<button type="submit">Sign up</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
In your `.env`:
|
||||
|
||||
```text
|
||||
PUBLIC_TURNSTILE_WORKER_URL=https://YOUR_WORKER_URL
|
||||
PUBLIC_TURNSTILE_SITEKEY=YOUR_SITEKEY
|
||||
```
|
||||
|
||||
The `PUBLIC_` prefix is mandatory for client-exposed variables in Astro.
|
||||
|
||||
## Variant: hardcoded values
|
||||
|
||||
If you do not use env vars, inline directly:
|
||||
|
||||
```astro title="src/pages/signup.astro"
|
||||
<html>
|
||||
<head>
|
||||
<script
|
||||
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<form action="https://YOUR_WORKER_URL/" method="POST">
|
||||
<div
|
||||
class="cf-turnstile"
|
||||
data-sitekey="YOUR_SITEKEY"
|
||||
data-action="turnstile-spin-v1"
|
||||
/>
|
||||
<button type="submit">Sign up</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Variant: Astro Actions
|
||||
|
||||
If the project uses Astro Actions, call siteverify from the action:
|
||||
|
||||
```ts title="src/actions/index.ts"
|
||||
import { defineAction } from "astro:actions";
|
||||
import { z } from "astro:schema";
|
||||
|
||||
export const server = {
|
||||
signup: defineAction({
|
||||
accept: "form",
|
||||
input: z.object({
|
||||
email: z.string().email(),
|
||||
"cf-turnstile-response": z.string(),
|
||||
}),
|
||||
handler: async (input) => {
|
||||
const verify = await fetch("https://YOUR_WORKER_URL/", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ token: input["cf-turnstile-response"] }),
|
||||
});
|
||||
const data = await verify.json();
|
||||
if (!data.success) throw new Error("Verification failed");
|
||||
// process signup
|
||||
},
|
||||
}),
|
||||
};
|
||||
```
|
||||
|
||||
## Substitutions
|
||||
|
||||
| Placeholder | Replace with |
|
||||
| ------------------ | ------------------------------------------- |
|
||||
| `YOUR_WORKER_URL` | Deployed Worker URL from Step 5 |
|
||||
| `YOUR_SITEKEY` | Widget site key from Step 4 |
|
||||
@@ -0,0 +1,62 @@
|
||||
# Hugo
|
||||
|
||||
For Hugo static sites. Use a partial for the widget so it can be referenced from any layout or content file.
|
||||
|
||||
```html title="layouts/partials/turnstile.html"
|
||||
<script
|
||||
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
|
||||
<form action="{{ .Site.Params.turnstileWorkerUrl }}/" method="POST">
|
||||
<input name="email" type="email" required />
|
||||
<div
|
||||
class="cf-turnstile"
|
||||
data-sitekey="{{ .Site.Params.turnstileSitekey }}"
|
||||
data-action="turnstile-spin-v1"
|
||||
></div>
|
||||
<button type="submit">Subscribe</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
Add the params to your site config:
|
||||
|
||||
```toml title="hugo.toml"
|
||||
[params]
|
||||
turnstileSitekey = "YOUR_SITEKEY"
|
||||
turnstileWorkerUrl = "https://YOUR_WORKER_URL"
|
||||
```
|
||||
|
||||
Reference the partial from any layout or content file:
|
||||
|
||||
```text
|
||||
{{ partial "turnstile.html" . }}
|
||||
```
|
||||
|
||||
## Variant: shortcode for content files
|
||||
|
||||
If you want to drop the widget into Markdown content (not just layouts), create a shortcode:
|
||||
|
||||
```html title="layouts/shortcodes/turnstile-form.html"
|
||||
{{ partial "turnstile.html" . }}
|
||||
```
|
||||
|
||||
Use in content:
|
||||
|
||||
```markdown title="content/contact.md"
|
||||
---
|
||||
title: Contact
|
||||
---
|
||||
|
||||
Contact us:
|
||||
|
||||
{{< turnstile-form >}}
|
||||
```
|
||||
|
||||
## Substitutions
|
||||
|
||||
| Placeholder | Replace with |
|
||||
| ------------------ | ------------------------------------------- |
|
||||
| `YOUR_WORKER_URL` | Deployed Worker URL from Step 5 |
|
||||
| `YOUR_SITEKEY` | Widget site key from Step 4 |
|
||||
@@ -0,0 +1,110 @@
|
||||
# Next.js (App Router)
|
||||
|
||||
For `app/`-directory Next.js projects. The widget needs to run on the client, so the page or component must be `"use client"`.
|
||||
|
||||
```tsx title="app/signup/page.tsx"
|
||||
"use client";
|
||||
import Script from "next/script";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
onTurnstileSuccess?: (token: string) => void;
|
||||
}
|
||||
}
|
||||
|
||||
export default function SignupPage() {
|
||||
const [token, setToken] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
window.onTurnstileSuccess = (t: string) => setToken(t);
|
||||
}, []);
|
||||
|
||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
const res = await fetch("https://YOUR_WORKER_URL/", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ token }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
// proceed
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input name="email" type="email" required />
|
||||
<div
|
||||
className="cf-turnstile"
|
||||
data-sitekey="YOUR_SITEKEY"
|
||||
data-action="turnstile-spin-v1"
|
||||
data-callback="onTurnstileSuccess"
|
||||
/>
|
||||
<button type="submit" disabled={!token}>
|
||||
Sign up
|
||||
</button>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
`data-callback` expects a string referencing a global function. The `useEffect` wires `window.onTurnstileSuccess` so the widget can call back into React state.
|
||||
|
||||
## Variant: Server Action
|
||||
|
||||
If you are using Server Actions, do the siteverify call from the action itself. The widget still goes in a client component, but the verify call moves server-side:
|
||||
|
||||
```tsx title="app/signup/actions.ts"
|
||||
"use server";
|
||||
|
||||
export async function submitSignup(formData: FormData) {
|
||||
const token = formData.get("cf-turnstile-response") as string;
|
||||
|
||||
const res = await fetch("https://YOUR_WORKER_URL/", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ token }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!data.success) {
|
||||
return { error: "Verification failed" };
|
||||
}
|
||||
|
||||
// process signup
|
||||
return { ok: true };
|
||||
}
|
||||
```
|
||||
|
||||
```tsx title="app/signup/page.tsx"
|
||||
"use client";
|
||||
import { submitSignup } from "./actions";
|
||||
|
||||
export default function SignupPage() {
|
||||
return (
|
||||
<form action={submitSignup}>
|
||||
<input name="email" type="email" required />
|
||||
<div
|
||||
className="cf-turnstile"
|
||||
data-sitekey="YOUR_SITEKEY"
|
||||
data-action="turnstile-spin-v1"
|
||||
/>
|
||||
<button type="submit">Sign up</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Substitutions
|
||||
|
||||
| Placeholder | Replace with |
|
||||
| ------------------ | ------------------------------------------- |
|
||||
| `YOUR_WORKER_URL` | Deployed Worker URL from Step 5 |
|
||||
| `YOUR_SITEKEY` | Widget site key from Step 4 |
|
||||
@@ -0,0 +1,59 @@
|
||||
# Next.js (Pages Router)
|
||||
|
||||
For older Next.js projects using `pages/` rather than `app/`. The form posts directly to the Worker; no React state required.
|
||||
|
||||
```tsx title="pages/signup.tsx"
|
||||
import Script from "next/script";
|
||||
|
||||
export default function SignupPage() {
|
||||
return (
|
||||
<>
|
||||
<Script src="https://challenges.cloudflare.com/turnstile/v0/api.js" />
|
||||
<form action="https://YOUR_WORKER_URL/" method="POST">
|
||||
<input name="email" type="email" required />
|
||||
<div
|
||||
className="cf-turnstile"
|
||||
data-sitekey="YOUR_SITEKEY"
|
||||
data-action="turnstile-spin-v1"
|
||||
/>
|
||||
<button type="submit">Sign up</button>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Variant: API route on your own backend
|
||||
|
||||
If you want to call siteverify from your own API route (e.g. you have application logic between siteverify and the actual signup), proxy the call through `pages/api/signup.ts`:
|
||||
|
||||
```ts title="pages/api/signup.ts"
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
) {
|
||||
const token = req.body["cf-turnstile-response"] ?? req.body.token;
|
||||
const verify = await fetch("https://YOUR_WORKER_URL/", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ token }),
|
||||
});
|
||||
const data = await verify.json();
|
||||
if (!data.success) {
|
||||
return res.status(403).json({ error: "Verification failed" });
|
||||
}
|
||||
// process signup
|
||||
return res.json({ ok: true });
|
||||
}
|
||||
```
|
||||
|
||||
Note: This pattern uses the Spin Worker as a siteverify proxy from your Node backend. It still works, just adds a hop. For pure SPA-style forms, the direct-post pattern above is simpler.
|
||||
|
||||
## Substitutions
|
||||
|
||||
| Placeholder | Replace with |
|
||||
| ------------------ | ------------------------------------------- |
|
||||
| `YOUR_WORKER_URL` | Deployed Worker URL from Step 5 |
|
||||
| `YOUR_SITEKEY` | Widget site key from Step 4 |
|
||||
@@ -0,0 +1,84 @@
|
||||
# SvelteKit
|
||||
|
||||
For SvelteKit projects. Widget renders in the page; siteverify can be called from a form action server-side, or directly against the Worker from the client.
|
||||
|
||||
```svelte title="src/routes/signup/+page.svelte"
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
let token = "";
|
||||
|
||||
onMount(() => {
|
||||
(window as any).onTurnstileSuccess = (t: string) => (token = t);
|
||||
});
|
||||
|
||||
async function handleSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
const res = await fetch("https://YOUR_WORKER_URL/", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ token }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
// proceed
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<script
|
||||
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
</svelte:head>
|
||||
|
||||
<form on:submit={handleSubmit}>
|
||||
<input name="email" type="email" required />
|
||||
<div
|
||||
class="cf-turnstile"
|
||||
data-sitekey="YOUR_SITEKEY"
|
||||
data-action="turnstile-spin-v1"
|
||||
data-callback="onTurnstileSuccess"
|
||||
></div>
|
||||
<button type="submit" disabled={!token}>Sign up</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
## Variant: Form action server-side
|
||||
|
||||
If you use SvelteKit's form actions for the signup logic, do siteverify from the action:
|
||||
|
||||
```ts title="src/routes/signup/+page.server.ts"
|
||||
import type { Actions } from "./$types";
|
||||
import { fail } from "@sveltejs/kit";
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async ({ request }) => {
|
||||
const data = await request.formData();
|
||||
const token = data.get("cf-turnstile-response") as string;
|
||||
|
||||
const verify = await fetch("https://YOUR_WORKER_URL/", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ token }),
|
||||
});
|
||||
const result = await verify.json();
|
||||
if (!result.success) {
|
||||
return fail(403, { error: "Verification failed" });
|
||||
}
|
||||
|
||||
// process signup
|
||||
return { ok: true };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
With this variant the form posts via SvelteKit's progressive enhancement; the page component drops the explicit `fetch` call and just uses a native `<form method="POST">`.
|
||||
|
||||
## Substitutions
|
||||
|
||||
| Placeholder | Replace with |
|
||||
| ------------------ | ------------------------------------------- |
|
||||
| `YOUR_WORKER_URL` | Deployed Worker URL from Step 5 |
|
||||
| `YOUR_SITEKEY` | Widget site key from Step 4 |
|
||||
@@ -0,0 +1,62 @@
|
||||
# Vanilla HTML
|
||||
|
||||
For static sites or any project without a JS framework. The form posts directly to the Worker, no client-side JavaScript required for the integration itself.
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script
|
||||
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<form action="https://YOUR_WORKER_URL/" method="POST">
|
||||
<input name="email" type="email" required />
|
||||
<div
|
||||
class="cf-turnstile"
|
||||
data-sitekey="YOUR_SITEKEY"
|
||||
data-action="turnstile-spin-v1"
|
||||
></div>
|
||||
<button type="submit">Subscribe</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
When the form is submitted, the browser includes `cf-turnstile-response` automatically. The Worker reads it (alias for `token`) and responds with siteverify JSON.
|
||||
|
||||
## Where to put the snippet
|
||||
|
||||
- Single-page site: in the `<body>` of `index.html`, wherever the form lives.
|
||||
- Multi-page site: in each page that has a form. The script tag in `<head>` can live in a shared layout.
|
||||
|
||||
## Substitutions
|
||||
|
||||
| Placeholder | Replace with |
|
||||
| ------------------ | ------------------------------------------------------- |
|
||||
| `YOUR_WORKER_URL` | The deployed Worker URL from Step 5 |
|
||||
| `YOUR_SITEKEY` | The widget site key from Step 4 |
|
||||
|
||||
## Variant: AJAX submit instead of form action
|
||||
|
||||
If the form is submitted via `fetch` instead of a native form post, the snippet still works. The widget div populates a hidden `cf-turnstile-response` input that you can read from `new FormData(form)`.
|
||||
|
||||
```html
|
||||
<script>
|
||||
document.querySelector("form").addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
const data = new FormData(e.target);
|
||||
const res = await fetch("https://YOUR_WORKER_URL/", {
|
||||
method: "POST",
|
||||
body: data,
|
||||
});
|
||||
const json = await res.json();
|
||||
if (json.success) {
|
||||
// proceed
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
Reference in new issue
Block a user