mirror of
https://github.com/Xevion/xevion.dev.git
synced 2026-01-31 04:26:43 -06:00
feat: add PGP public key page with multiple access endpoints
- Add dedicated /pgp page with key viewer and download options - Support CLI-friendly endpoints (/publickey.asc, /pgp.asc, /.well-known/pgpkey.asc) - Detect user-agent to serve raw key to curl/wget or HTML to browsers - Add modal component for quick key access from homepage - Embed static key file in Rust assets for efficient serving
This commit is contained in:
@@ -21,12 +21,12 @@
|
||||
|
||||
<div class={cn("pointer-events-none fixed inset-0 -z-20 bg-white dark:bg-black transition-colors duration-300", bgColor)}></div>
|
||||
<Dots class={[backgroundClass]} />
|
||||
{#if showThemeToggle}
|
||||
<div class="fixed top-5 right-6 z-50">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
{/if}
|
||||
<main class={cn("relative min-h-screen text-zinc-900 dark:text-zinc-50 transition-colors duration-300", className)}>
|
||||
{#if showThemeToggle}
|
||||
<div class="absolute top-5 right-6 z-50">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
{/if}
|
||||
{#if children}
|
||||
{@render children()}
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
<script lang="ts">
|
||||
import { PGP_KEY_METADATA } from "$lib/pgp/key-info";
|
||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-svelte";
|
||||
import "overlayscrollbars/overlayscrollbars.css";
|
||||
import IconDownload from "~icons/material-symbols/download-rounded";
|
||||
import IconCopy from "~icons/material-symbols/content-copy-rounded";
|
||||
import { fade, scale } from "svelte/transition";
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
let { open = $bindable(false) }: Props = $props();
|
||||
|
||||
let copySuccess = $state(false);
|
||||
let keyContent = $state<string>("");
|
||||
let loading = $state(false);
|
||||
|
||||
// Fetch key content when modal opens
|
||||
$effect(() => {
|
||||
if (open && !keyContent && !loading) {
|
||||
loading = true;
|
||||
fetch("/publickey.asc")
|
||||
.then((res) => res.text())
|
||||
.then((text) => {
|
||||
keyContent = text;
|
||||
loading = false;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to fetch PGP key:", err);
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function handleBackdropClick(e: MouseEvent) {
|
||||
if (e.target === e.currentTarget) {
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
open = false;
|
||||
}
|
||||
|
||||
async function copyToClipboard() {
|
||||
if (!keyContent) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(keyContent);
|
||||
copySuccess = true;
|
||||
setTimeout(() => {
|
||||
copySuccess = false;
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error("Failed to copy:", err);
|
||||
}
|
||||
}
|
||||
|
||||
function downloadKey() {
|
||||
const a = document.createElement("a");
|
||||
a.href = "/publickey.asc";
|
||||
a.download = "publickey.asc";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-[2px] p-4"
|
||||
onclick={handleBackdropClick}
|
||||
onkeydown={(e) => e.key === "Escape" && handleClose()}
|
||||
role="presentation"
|
||||
tabindex="-1"
|
||||
transition:fade={{ duration: 200 }}
|
||||
>
|
||||
<div
|
||||
class="relative w-full max-w-2xl rounded-lg bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 p-6 shadow-xl"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="pgp-modal-title"
|
||||
transition:scale={{ duration: 200, start: 0.95 }}
|
||||
>
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<h2 id="pgp-modal-title" class="text-xl font-semibold text-zinc-900 dark:text-white">
|
||||
PGP Public Key
|
||||
</h2>
|
||||
<button
|
||||
onclick={handleClose}
|
||||
class="text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200 transition-colors"
|
||||
aria-label="Close modal"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Fingerprint -->
|
||||
<div class="mb-4 p-3 bg-zinc-100 dark:bg-zinc-800 rounded border border-zinc-200 dark:border-zinc-700">
|
||||
<div class="text-xs font-medium text-zinc-600 dark:text-zinc-400 mb-1">
|
||||
Fingerprint
|
||||
</div>
|
||||
<div class="font-mono text-sm text-zinc-900 dark:text-zinc-100 break-all">
|
||||
{PGP_KEY_METADATA.fingerprint}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Key Content -->
|
||||
<div class="mb-4 border border-zinc-200 dark:border-zinc-700 rounded overflow-hidden">
|
||||
{#if loading}
|
||||
<div class="p-4 text-center text-zinc-600 dark:text-zinc-400">
|
||||
Loading key...
|
||||
</div>
|
||||
{:else if keyContent}
|
||||
<OverlayScrollbarsComponent
|
||||
options={{
|
||||
scrollbars: { autoHide: "leave", autoHideDelay: 800 },
|
||||
}}
|
||||
defer
|
||||
style="max-height: 400px"
|
||||
>
|
||||
<pre
|
||||
class="p-4 text-xs font-mono text-zinc-800 dark:text-zinc-200 bg-zinc-50 dark:bg-zinc-900/50 overflow-x-auto">{keyContent}</pre>
|
||||
</OverlayScrollbarsComponent>
|
||||
{:else}
|
||||
<div class="p-4 text-center text-zinc-600 dark:text-zinc-400">
|
||||
Failed to load key
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex gap-3 justify-end">
|
||||
<button
|
||||
onclick={downloadKey}
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-sm bg-zinc-100 dark:bg-zinc-800 text-zinc-800 dark:text-zinc-100 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors"
|
||||
>
|
||||
<IconDownload class="size-4" />
|
||||
<span class="text-sm font-medium">Download</span>
|
||||
</button>
|
||||
<button
|
||||
onclick={copyToClipboard}
|
||||
class="flex items-center gap-2 px-4 py-2 rounded-sm bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900 hover:bg-zinc-800 dark:hover:bg-zinc-200 transition-colors"
|
||||
>
|
||||
<IconCopy class="size-4" />
|
||||
<span class="text-sm font-medium">{copySuccess ? "Copied!" : "Copy to Clipboard"}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,6 @@
|
||||
export const PGP_KEY_METADATA = {
|
||||
fingerprint: '211D 7157 249B F07D 81C8 B9DE C217 005C F3C0 0672',
|
||||
keyId: 'C217005CF3C00672',
|
||||
email: 'xevion@xevion.dev',
|
||||
name: 'Ryan Walters',
|
||||
} as const;
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import AppWrapper from "$lib/components/AppWrapper.svelte";
|
||||
import ProjectCard from "$lib/components/ProjectCard.svelte";
|
||||
import PgpKeyModal from "$lib/components/PgpKeyModal.svelte";
|
||||
import type { PageData } from "./$types";
|
||||
import IconSimpleIconsGithub from "~icons/simple-icons/github";
|
||||
import IconSimpleIconsLinkedin from "~icons/simple-icons/linkedin";
|
||||
@@ -10,6 +11,7 @@
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
const projects = data.projects;
|
||||
let pgpModalOpen = $state(false);
|
||||
</script>
|
||||
|
||||
<AppWrapper class="overflow-x-hidden font-schibsted">
|
||||
@@ -70,6 +72,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center gap-x-1.5 px-1.5 py-1 rounded-sm bg-zinc-100 dark:bg-zinc-900 shadow-sm hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors"
|
||||
onclick={() => (pgpModalOpen = true)}
|
||||
>
|
||||
<MaterialSymbolsVpnKey class="size-4.5 text-zinc-600 dark:text-zinc-300" />
|
||||
<span class="whitespace-nowrap text-sm text-zinc-800 dark:text-zinc-100">PGP Key</span>
|
||||
@@ -87,3 +90,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</AppWrapper>
|
||||
|
||||
<PgpKeyModal bind:open={pgpModalOpen} />
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { PGP_KEY_METADATA } from '$lib/pgp/key-info';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const load = () => {
|
||||
// Read the PGP key from static directory at build time
|
||||
const keyPath = join(process.cwd(), 'static', 'publickey.asc');
|
||||
const content = readFileSync(keyPath, 'utf-8');
|
||||
|
||||
return {
|
||||
key: {
|
||||
...PGP_KEY_METADATA,
|
||||
content,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
<script lang="ts">
|
||||
import AppWrapper from "$lib/components/AppWrapper.svelte";
|
||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-svelte";
|
||||
import "overlayscrollbars/overlayscrollbars.css";
|
||||
import IconDownload from "~icons/material-symbols/download-rounded";
|
||||
import IconCopy from "~icons/material-symbols/content-copy-rounded";
|
||||
import IconCheck from "~icons/material-symbols/check-rounded";
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
let copySuccess = $state(false);
|
||||
let copyCommandSuccess = $state(false);
|
||||
|
||||
async function copyToClipboard() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(data.key.content);
|
||||
copySuccess = true;
|
||||
setTimeout(() => {
|
||||
copySuccess = false;
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error("Failed to copy:", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function copyCommand() {
|
||||
try {
|
||||
await navigator.clipboard.writeText("curl https://xevion.dev/pgp | gpg --import");
|
||||
copyCommandSuccess = true;
|
||||
setTimeout(() => {
|
||||
copyCommandSuccess = false;
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error("Failed to copy command:", err);
|
||||
}
|
||||
}
|
||||
|
||||
function downloadKey() {
|
||||
const a = document.createElement("a");
|
||||
a.href = "/publickey.asc";
|
||||
a.download = "publickey.asc";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>PGP Public Key - Ryan Walters</title>
|
||||
<meta name="description" content="Download or copy Ryan Walters' PGP public key" />
|
||||
</svelte:head>
|
||||
|
||||
<AppWrapper class="overflow-x-hidden font-schibsted">
|
||||
<div class="flex items-center flex-col pt-14 pb-20 px-4 sm:px-6">
|
||||
<div class="max-w-2xl w-full">
|
||||
<!-- Header -->
|
||||
<div class="mb-6">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-zinc-900 dark:text-white mb-2">
|
||||
PGP Public Key
|
||||
</h1>
|
||||
<p class="text-sm sm:text-base text-zinc-600 dark:text-zinc-400">
|
||||
Use this key to send me encrypted messages or verify my signed content.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Fingerprint -->
|
||||
<div
|
||||
class="mb-6 p-3 sm:p-4 bg-zinc-100 dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700"
|
||||
>
|
||||
<div class="text-xs sm:text-sm font-semibold text-zinc-700 dark:text-zinc-300 mb-2">
|
||||
Key Fingerprint
|
||||
</div>
|
||||
<div class="font-mono text-sm sm:text-base text-zinc-900 dark:text-zinc-100 break-all">
|
||||
{data.key.fingerprint}
|
||||
</div>
|
||||
<div class="mt-3 pt-3 border-t border-zinc-200 dark:border-zinc-700 space-y-1">
|
||||
<div class="text-xs sm:text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<span class="font-medium">Key ID:</span>
|
||||
<span class="font-mono ml-2">{data.key.keyId}</span>
|
||||
</div>
|
||||
<div class="text-xs sm:text-sm text-zinc-600 dark:text-zinc-400">
|
||||
<span class="font-medium">Email:</span>
|
||||
<span class="ml-2">{data.key.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Key Content Card -->
|
||||
<div
|
||||
class="mb-6 border border-zinc-200 dark:border-zinc-700 rounded-lg overflow-hidden bg-white dark:bg-zinc-900"
|
||||
>
|
||||
<div class="px-3 sm:px-4 py-2 sm:py-3 bg-zinc-50 dark:bg-zinc-800 border-b border-zinc-200 dark:border-zinc-700">
|
||||
<div class="text-xs sm:text-sm font-semibold text-zinc-700 dark:text-zinc-300">
|
||||
Public Key
|
||||
</div>
|
||||
</div>
|
||||
<OverlayScrollbarsComponent
|
||||
options={{
|
||||
scrollbars: { autoHide: "leave", autoHideDelay: 800 },
|
||||
}}
|
||||
defer
|
||||
style="max-height: 400px"
|
||||
>
|
||||
<pre
|
||||
class="p-3 sm:p-4 text-xs font-mono text-zinc-800 dark:text-zinc-200 bg-zinc-50 dark:bg-zinc-900/50 overflow-x-auto">{data.key.content}</pre>
|
||||
</OverlayScrollbarsComponent>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex flex-col sm:flex-row gap-2 sm:gap-3">
|
||||
<button
|
||||
onclick={copyToClipboard}
|
||||
class="flex items-center justify-center gap-2 px-3 sm:px-4 py-2 sm:py-2.5 rounded-sm bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900 hover:bg-zinc-800 dark:hover:bg-zinc-200 transition-colors shadow-sm"
|
||||
>
|
||||
<IconCopy class="size-4 sm:size-5" />
|
||||
<span class="text-sm sm:text-base font-medium">{copySuccess ? "Copied!" : "Copy to Clipboard"}</span>
|
||||
</button>
|
||||
<button
|
||||
onclick={downloadKey}
|
||||
class="flex items-center justify-center gap-2 px-3 sm:px-4 py-2 sm:py-2.5 rounded-sm bg-zinc-100 dark:bg-zinc-800 text-zinc-800 dark:text-zinc-100 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors"
|
||||
>
|
||||
<IconDownload class="size-4 sm:size-5" />
|
||||
<span class="text-sm sm:text-base font-medium">Download</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Additional Info -->
|
||||
<div class="mt-8 p-3 sm:p-4 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
||||
<h2 class="text-xs sm:text-sm font-semibold text-zinc-700 dark:text-zinc-300 mb-2">
|
||||
How to use this key
|
||||
</h2>
|
||||
<div class="text-xs sm:text-sm text-zinc-600 dark:text-zinc-400 space-y-2">
|
||||
<p>
|
||||
Import this key into your GPG keyring to encrypt messages for me or verify my signatures:
|
||||
</p>
|
||||
<div class="relative">
|
||||
<pre class="p-2 sm:p-3 pr-12 bg-white dark:bg-zinc-900 rounded border border-zinc-200 dark:border-zinc-700 font-mono text-xs overflow-x-auto">curl https://xevion.dev/pgp | gpg --import</pre>
|
||||
<button
|
||||
onclick={copyCommand}
|
||||
disabled={copyCommandSuccess}
|
||||
class="absolute top-1/2 -translate-y-1/2 right-2 p-1 rounded border border-zinc-300 dark:border-zinc-600 bg-zinc-50 dark:bg-zinc-800 hover:bg-zinc-100 dark:hover:bg-zinc-700 hover:border-zinc-400 dark:hover:border-zinc-500 transition-all {copyCommandSuccess ? 'cursor-default' : 'cursor-pointer'}"
|
||||
title={copyCommandSuccess ? "Copied!" : "Copy command"}
|
||||
>
|
||||
{#if copyCommandSuccess}
|
||||
<IconCheck class="size-3.5 text-green-600 dark:text-green-500" />
|
||||
{:else}
|
||||
<IconCopy class="size-3.5 text-zinc-600 dark:text-zinc-400" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-xs text-zinc-500 dark:text-zinc-500">
|
||||
You can also find this key on public keyservers by searching for the fingerprint above.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AppWrapper>
|
||||
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
JUK4SSLSxYQ=
|
||||
=yHBH
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
Reference in New Issue
Block a user