protected

This commit is contained in:
Michael Freno
2025-12-19 15:31:01 -05:00
parent d94184cbae
commit 8692eee0df

View File

@@ -1,6 +1,18 @@
import { createSignal, For, Show } from "solid-js"; import { createSignal, For, Show } from "solid-js";
import { query, createAsync } from "@solidjs/router";
import { getRequestEvent } from "solid-js/web";
import { getPrivilegeLevel } from "~/server/utils";
import { api } from "~/lib/api"; import { api } from "~/lib/api";
const getAuthState = query(async () => {
"use server";
const event = getRequestEvent()!;
const privilegeLevel = await getPrivilegeLevel(event.nativeEvent);
return { privilegeLevel };
}, "test-auth-state");
type EndpointTest = { type EndpointTest = {
name: string; name: string;
router: string; router: string;
@@ -846,6 +858,8 @@ const routerSections: RouterSection[] = [
]; ];
export default function TestPage() { export default function TestPage() {
const authState = createAsync(() => getAuthState());
const [expandedSections, setExpandedSections] = createSignal<Set<string>>( const [expandedSections, setExpandedSections] = createSignal<Set<string>>(
new Set() new Set()
); );
@@ -919,282 +933,297 @@ export default function TestPage() {
}; };
return ( return (
<main class="min-h-screen p-8"> <Show
<div class="mx-auto max-w-6xl"> when={authState()?.privilegeLevel === "admin"}
<div class="bg-surface0 mb-6 rounded-lg p-6 shadow-lg"> fallback={
<h1 class="mb-2 text-3xl font-bold">tRPC API Testing Dashboard</h1> <div class="w-full pt-[30vh] text-center">
<p class="text-text mb-4"> <div class="text-text text-2xl">Unauthorized</div>
Complete API coverage: Example, Auth, Database, User, Misc, and <div class="text-subtext0 mt-4">
Lineage routers You must be an admin to access this page.
</p> </div>
</div>
<div class="border-lavender bg-mauve rounded border p-4"> }
<p class="text-base text-sm"> >
<strong>Quick Start:</strong> Expand any section below to test <main class="min-h-screen p-8">
endpoints. Public endpoints work immediately. Auth-required <div class="mx-auto max-w-6xl">
endpoints need valid tokens. <div class="bg-surface0 mb-6 rounded-lg p-6 shadow-lg">
<h1 class="mb-2 text-3xl font-bold">tRPC API Testing Dashboard</h1>
<p class="text-text mb-4">
Complete API coverage: Example, Auth, Database, User, Misc, and
Lineage routers
</p> </p>
<div class="border-lavender bg-mauve rounded border p-4">
<p class="text-base text-sm">
<strong>Quick Start:</strong> Expand any section below to test
endpoints. Public endpoints work immediately. Auth-required
endpoints need valid tokens.
</p>
</div>
</div> </div>
</div>
<div class="space-y-4"> <div class="space-y-4">
<For each={routerSections}> <For each={routerSections}>
{(section) => { {(section) => {
const isExpanded = () => expandedSections().has(section.name); const isExpanded = () => expandedSections().has(section.name);
return ( return (
<div class="bg-surface0 rounded-lg shadow"> <div class="bg-surface0 rounded-lg shadow">
{/* Section Header */} {/* Section Header */}
<button <button
onClick={() => toggleSection(section.name)} onClick={() => toggleSection(section.name)}
class="flex w-full items-center justify-between px-6 py-4 transition" class="flex w-full items-center justify-between px-6 py-4 transition"
> >
<div class="text-left"> <div class="text-left">
<h2 class="text-xl font-bold">{section.name}</h2> <h2 class="text-xl font-bold">{section.name}</h2>
<p class="text-subtext0 text-sm">{section.description}</p> <p class="text-subtext0 text-sm">
<p class="text-subtext1 mt-1 text-xs"> {section.description}
{section.endpoints.length} endpoint </p>
{section.endpoints.length !== 1 ? "s" : ""} <p class="text-subtext1 mt-1 text-xs">
</p> {section.endpoints.length} endpoint
</div> {section.endpoints.length !== 1 ? "s" : ""}
<div class="text-subtext1 text-2xl"> </p>
{isExpanded() ? "" : "+"} </div>
</div> <div class="text-subtext1 text-2xl">
</button> {isExpanded() ? "" : "+"}
</div>
</button>
{/* Section Content */} {/* Section Content */}
<Show when={isExpanded()}> <Show when={isExpanded()}>
<div class="border-base space-y-4 border-t p-6"> <div class="border-base space-y-4 border-t p-6">
<For each={section.endpoints}> <For each={section.endpoints}>
{(endpoint) => { {(endpoint) => {
const key = `${endpoint.router}.${endpoint.procedure}`; const key = `${endpoint.router}.${endpoint.procedure}`;
const hasInput = endpoint.sampleInput !== undefined; const hasInput = endpoint.sampleInput !== undefined;
const displayInput = () => { const displayInput = () => {
if (inputEdits()[key]) { if (inputEdits()[key]) {
return inputEdits()[key]; return inputEdits()[key];
} }
// Handle primitive values (string, number, boolean) // Handle primitive values (string, number, boolean)
if (typeof endpoint.sampleInput === "string") { if (typeof endpoint.sampleInput === "string") {
return `"${endpoint.sampleInput}"`; return `"${endpoint.sampleInput}"`;
} }
if ( if (
typeof endpoint.sampleInput === "number" || typeof endpoint.sampleInput === "number" ||
typeof endpoint.sampleInput === "boolean" typeof endpoint.sampleInput === "boolean"
) { ) {
return String(endpoint.sampleInput); return String(endpoint.sampleInput);
} }
// Handle objects and arrays // Handle objects and arrays
return JSON.stringify( return JSON.stringify(
endpoint.sampleInput, endpoint.sampleInput,
null, null,
2 2
); );
}; };
return ( return (
<div class="bg-surface2 border-surface1 rounded-lg border p-4"> <div class="bg-surface2 border-surface1 rounded-lg border p-4">
{/* Endpoint Header */} {/* Endpoint Header */}
<div class="mb-3 flex items-start justify-between"> <div class="mb-3 flex items-start justify-between">
<div class="flex-1"> <div class="flex-1">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<h3 class="text-subtext0 text-lg font-semibold"> <h3 class="text-subtext0 text-lg font-semibold">
{endpoint.name} {endpoint.name}
</h3> </h3>
<Show when={endpoint.requiresAuth}> <Show when={endpoint.requiresAuth}>
<span class="bg-surface1 text-yellow rounded px-2 py-1 text-xs"> <span class="bg-surface1 text-yellow rounded px-2 py-1 text-xs">
🔒 Auth Required 🔒 Auth Required
</span>
</Show>
<Show when={endpoint.requiresAdmin}>
<span class="bg-maroon rounded px-2 py-1 text-base text-xs">
👑 Admin Only
</span>
</Show>
</div>
<p class="mt-1 text-sm text-gray-600">
{endpoint.description}
</p>
<div class="mt-2 flex gap-2">
<code class="bg-surface0 rounded px-2 py-1 text-xs">
{key}
</code>
<span class="bg-blue text-text rounded px-2 py-1 text-xs">
{endpoint.method === "query"
? "GET"
: "POST"}
</span> </span>
</Show> </div>
<Show when={endpoint.requiresAdmin}>
<span class="bg-maroon rounded px-2 py-1 text-base text-xs">
👑 Admin Only
</span>
</Show>
</div>
<p class="mt-1 text-sm text-gray-600">
{endpoint.description}
</p>
<div class="mt-2 flex gap-2">
<code class="bg-surface0 rounded px-2 py-1 text-xs">
{key}
</code>
<span class="bg-blue text-text rounded px-2 py-1 text-xs">
{endpoint.method === "query"
? "GET"
: "POST"}
</span>
</div> </div>
<button
onClick={() => testEndpoint(endpoint)}
disabled={loading()[key]}
class="bg-green ml-4 rounded px-4 py-2 text-base font-semibold whitespace-nowrap transition hover:brightness-125 disabled:brightness-50"
>
{loading()[key] ? "Testing..." : "Test"}
</button>
</div> </div>
<button
onClick={() => testEndpoint(endpoint)} {/* Input Editor */}
disabled={loading()[key]} <Show when={hasInput}>
class="bg-green ml-4 rounded px-4 py-2 text-base font-semibold whitespace-nowrap transition hover:brightness-125 disabled:brightness-50" <div class="mb-3">
> <label class="text-text mb-1 block text-xs font-semibold">
{loading()[key] ? "Testing..." : "Test"} Request Body (edit JSON):
</button> </label>
<textarea
value={displayInput()}
onInput={(e) =>
updateInput(key, e.currentTarget.value)
}
class="border-lavender bg-crust min-h-[100px] w-full rounded border p-2 font-mono text-xs"
spellcheck={false}
/>
</div>
</Show>
{/* Error Display */}
<Show when={errors()[key]}>
<div class="mb-3 rounded border border-red-200 bg-red-50 p-3">
<p class="text-sm font-semibold text-red-800">
Error:
</p>
<p class="font-mono text-sm text-red-600">
{errors()[key]}
</p>
</div>
</Show>
{/* Results Display */}
<Show when={results()[key]}>
<div class="rounded bg-gray-900 p-3">
<p class="mb-2 text-xs font-semibold text-green-400">
Response:
</p>
<pre class="max-h-60 overflow-auto text-xs text-green-400">
{JSON.stringify(results()[key], null, 2)}
</pre>
</div>
</Show>
</div> </div>
);
}}
</For>
</div>
</Show>
</div>
);
}}
</For>
</div>
{/* Input Editor */} {/* Footer Instructions */}
<Show when={hasInput}> <div class="bg-overlay2 mt-6 rounded-lg p-6 shadow-lg">
<div class="mb-3"> <h2 class="text-crust mb-4 text-2xl font-bold">Testing Guide</h2>
<label class="text-text mb-1 block text-xs font-semibold">
Request Body (edit JSON):
</label>
<textarea
value={displayInput()}
onInput={(e) =>
updateInput(key, e.currentTarget.value)
}
class="border-lavender bg-crust min-h-[100px] w-full rounded border p-2 font-mono text-xs"
spellcheck={false}
/>
</div>
</Show>
{/* Error Display */} <div class="space-y-4 text-base">
<Show when={errors()[key]}> <div>
<div class="mb-3 rounded border border-red-200 bg-red-50 p-3"> <h3 class="mb-2 text-lg font-semibold">🟢 No Auth Required</h3>
<p class="text-sm font-semibold text-red-800"> <ul class="ml-6 list-disc space-y-1 text-sm">
Error: <li>
</p> <strong>Example Router</strong> - Hello endpoint
<p class="font-mono text-sm text-red-600"> </li>
{errors()[key]} <li>
</p> <strong>Lineage JSON Service</strong> - All 6 endpoints work
</div> immediately
</Show> </li>
<li>
<strong>Database</strong> - All endpoints (comments, posts,
users, reactions, likes)
</li>
<li>
<strong>Misc</strong> - Downloads, S3 operations, password
utilities
</li>
<li>
<strong>Lineage Misc</strong> - Offline Secret, Get
Opponents
</li>
<li>
<strong>Lineage PvP</strong> - Get Opponents
</li>
</ul>
</div>
{/* Results Display */} <div>
<Show when={results()[key]}> <h3 class="mb-2 text-lg font-semibold">🟡 Auth Required</h3>
<div class="rounded bg-gray-900 p-3"> <p class="mb-2 text-sm">
<p class="mb-2 text-xs font-semibold text-green-400"> These need valid JWT tokens from login/registration:
Response: </p>
</p> <ul class="ml-6 list-disc space-y-1 text-sm">
<pre class="max-h-60 overflow-auto text-xs text-green-400"> <li>
{JSON.stringify(results()[key], null, 2)} <strong>Example Router</strong> - Get Profile
</pre> </li>
</div> <li>
</Show> <strong>User Router</strong> - All endpoints (profile
</div> updates, password, account deletion)
); </li>
}} <li>
</For> <strong>Lineage Auth</strong> - Email Login, Refresh Token
</div> </li>
</Show> <li>
</div> <strong>Lineage Database</strong> - Get Credentials,
); Deletion endpoints
}} </li>
</For> </ul>
</div> </div>
{/* Footer Instructions */} <div>
<div class="bg-overlay2 mt-6 rounded-lg p-6 shadow-lg"> <h3 class="mb-2 text-lg font-semibold">🔴 Admin Required</h3>
<h2 class="text-crust mb-4 text-2xl font-bold">Testing Guide</h2> <p class="mb-2 text-sm">
Maintenance endpoints require admin privileges (userIDToken
cookie with ADMIN_ID).
</p>
<ul class="ml-6 list-disc space-y-1 text-sm">
<li>
<strong>Example Router</strong> - Admin Dashboard
</li>
<li>
<strong>Lineage Maintenance</strong> - Find Loose Databases,
Cleanup Expired
</li>
</ul>
</div>
<div class="space-y-4 text-base"> <div>
<div> <h3 class="mb-2 text-lg font-semibold">📝 Typical Workflows</h3>
<h3 class="mb-2 text-lg font-semibold">🟢 No Auth Required</h3> <ol class="ml-6 list-decimal space-y-2 text-sm">
<ul class="ml-6 list-disc space-y-1 text-sm"> <li>
<li> <strong>Test public endpoints:</strong> Start with Example
<strong>Example Router</strong> - Hello endpoint Hello, Lineage JSON Service, or Database queries
</li> </li>
<li> <li>
<strong>Lineage JSON Service</strong> - All 6 endpoints work <strong>OAuth flow:</strong> Use Auth router callbacks with
immediately OAuth codes from GitHub/Google
</li> </li>
<li> <li>
<strong>Database</strong> - All endpoints (comments, posts, <strong>Email auth flow:</strong> Register verify email
users, reactions, likes) login use JWT
</li> </li>
<li> <li>
<strong>Misc</strong> - Downloads, S3 operations, password <strong>Blog/Project management:</strong> Create posts add
utilities comments/likes upload images via S3
</li> </li>
<li> <li>
<strong>Lineage Misc</strong> - Offline Secret, Get Opponents <strong>Lineage game data:</strong> Fetch JSON data
</li> register character find PvP opponents
<li> </li>
<strong>Lineage PvP</strong> - Get Opponents </ol>
</li> </div>
</ul>
</div>
<div> <div class="border-rosewater bg-rosewater mt-4 rounded border p-4">
<h3 class="mb-2 text-lg font-semibold">🟡 Auth Required</h3> <p class="text-crust text-sm">
<p class="mb-2 text-sm"> <strong>Note:</strong> Some endpoints require specific setup
These need valid JWT tokens from login/registration: (e.g., OAuth codes, existing database records, valid S3 keys).
</p> Check the sample input to understand what data each endpoint
<ul class="ml-6 list-disc space-y-1 text-sm"> expects.
<li> </p>
<strong>Example Router</strong> - Get Profile </div>
</li>
<li>
<strong>User Router</strong> - All endpoints (profile updates,
password, account deletion)
</li>
<li>
<strong>Lineage Auth</strong> - Email Login, Refresh Token
</li>
<li>
<strong>Lineage Database</strong> - Get Credentials, Deletion
endpoints
</li>
</ul>
</div>
<div>
<h3 class="mb-2 text-lg font-semibold">🔴 Admin Required</h3>
<p class="mb-2 text-sm">
Maintenance endpoints require admin privileges (userIDToken
cookie with ADMIN_ID).
</p>
<ul class="ml-6 list-disc space-y-1 text-sm">
<li>
<strong>Example Router</strong> - Admin Dashboard
</li>
<li>
<strong>Lineage Maintenance</strong> - Find Loose Databases,
Cleanup Expired
</li>
</ul>
</div>
<div>
<h3 class="mb-2 text-lg font-semibold">📝 Typical Workflows</h3>
<ol class="ml-6 list-decimal space-y-2 text-sm">
<li>
<strong>Test public endpoints:</strong> Start with Example
Hello, Lineage JSON Service, or Database queries
</li>
<li>
<strong>OAuth flow:</strong> Use Auth router callbacks with
OAuth codes from GitHub/Google
</li>
<li>
<strong>Email auth flow:</strong> Register verify email
login use JWT
</li>
<li>
<strong>Blog/Project management:</strong> Create posts add
comments/likes upload images via S3
</li>
<li>
<strong>Lineage game data:</strong> Fetch JSON data register
character find PvP opponents
</li>
</ol>
</div>
<div class="border-rosewater bg-rosewater mt-4 rounded border p-4">
<p class="text-crust text-sm">
<strong>Note:</strong> Some endpoints require specific setup
(e.g., OAuth codes, existing database records, valid S3 keys).
Check the sample input to understand what data each endpoint
expects.
</p>
</div> </div>
</div> </div>
</div> </div>
</div> </main>
</main> </Show>
); );
} }