first commit
Made-with: Cursor
This commit is contained in:
147
frontend/app/admin/nostr/page.tsx
Normal file
147
frontend/app/admin/nostr/page.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { api } from "@/lib/api";
|
||||
import { Search, RefreshCw, Bug } from "lucide-react";
|
||||
|
||||
export default function NostrToolsPage() {
|
||||
const [fetchInput, setFetchInput] = useState("");
|
||||
const [fetchResult, setFetchResult] = useState<any>(null);
|
||||
const [fetching, setFetching] = useState(false);
|
||||
|
||||
const [cacheStatus, setCacheStatus] = useState("");
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
const [debugInput, setDebugInput] = useState("");
|
||||
const [debugResult, setDebugResult] = useState<any>(null);
|
||||
const [debugging, setDebugging] = useState(false);
|
||||
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const handleFetch = async () => {
|
||||
if (!fetchInput.trim()) return;
|
||||
setFetching(true);
|
||||
setError("");
|
||||
setFetchResult(null);
|
||||
try {
|
||||
const isNaddr = fetchInput.startsWith("naddr");
|
||||
const data = await api.fetchNostrEvent(
|
||||
isNaddr ? { naddr: fetchInput } : { eventId: fetchInput }
|
||||
);
|
||||
setFetchResult(data);
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setFetching(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRefreshCache = async () => {
|
||||
setRefreshing(true);
|
||||
setCacheStatus("");
|
||||
setError("");
|
||||
try {
|
||||
const result = await api.refreshCache();
|
||||
setCacheStatus(result.message || "Cache refreshed successfully.");
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDebug = async () => {
|
||||
if (!debugInput.trim()) return;
|
||||
setDebugging(true);
|
||||
setError("");
|
||||
setDebugResult(null);
|
||||
try {
|
||||
const data = await api.debugEvent(debugInput);
|
||||
setDebugResult(data);
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setDebugging(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h1 className="text-2xl font-bold text-on-surface">Nostr Tools</h1>
|
||||
|
||||
{error && <p className="text-error text-sm">{error}</p>}
|
||||
|
||||
<div className="bg-surface-container-low rounded-xl p-6">
|
||||
<h2 className="text-lg font-semibold text-on-surface mb-4 flex items-center gap-2">
|
||||
<Search size={18} />
|
||||
Manual Fetch
|
||||
</h2>
|
||||
<div className="flex gap-3">
|
||||
<input
|
||||
placeholder="Event ID or naddr..."
|
||||
value={fetchInput}
|
||||
onChange={(e) => setFetchInput(e.target.value)}
|
||||
className="bg-surface-container-highest text-on-surface rounded-lg px-4 py-3 w-full focus:outline-none focus:ring-1 focus:ring-primary/40 flex-1"
|
||||
/>
|
||||
<button
|
||||
onClick={handleFetch}
|
||||
disabled={fetching || !fetchInput.trim()}
|
||||
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-gradient-to-r from-primary to-primary-container text-on-primary font-semibold text-sm hover:opacity-90 transition-opacity disabled:opacity-50 whitespace-nowrap"
|
||||
>
|
||||
{fetching ? "Fetching..." : "Fetch"}
|
||||
</button>
|
||||
</div>
|
||||
{fetchResult && (
|
||||
<pre className="mt-4 bg-surface-container rounded-lg p-4 text-on-surface/80 text-xs overflow-x-auto max-h-96 overflow-y-auto font-mono">
|
||||
{JSON.stringify(fetchResult, null, 2)}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bg-surface-container-low rounded-xl p-6">
|
||||
<h2 className="text-lg font-semibold text-on-surface mb-4 flex items-center gap-2">
|
||||
<RefreshCw size={18} />
|
||||
Cache Management
|
||||
</h2>
|
||||
<button
|
||||
onClick={handleRefreshCache}
|
||||
disabled={refreshing}
|
||||
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-gradient-to-r from-primary to-primary-container text-on-primary font-semibold text-sm hover:opacity-90 transition-opacity disabled:opacity-50"
|
||||
>
|
||||
<RefreshCw size={16} className={refreshing ? "animate-spin" : ""} />
|
||||
{refreshing ? "Refreshing..." : "Refresh Cache"}
|
||||
</button>
|
||||
{cacheStatus && (
|
||||
<p className="mt-3 text-green-400 text-sm">{cacheStatus}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bg-surface-container-low rounded-xl p-6">
|
||||
<h2 className="text-lg font-semibold text-on-surface mb-4 flex items-center gap-2">
|
||||
<Bug size={18} />
|
||||
Debug Event
|
||||
</h2>
|
||||
<div className="flex gap-3">
|
||||
<input
|
||||
placeholder="Event ID..."
|
||||
value={debugInput}
|
||||
onChange={(e) => setDebugInput(e.target.value)}
|
||||
className="bg-surface-container-highest text-on-surface rounded-lg px-4 py-3 w-full focus:outline-none focus:ring-1 focus:ring-primary/40 flex-1"
|
||||
/>
|
||||
<button
|
||||
onClick={handleDebug}
|
||||
disabled={debugging || !debugInput.trim()}
|
||||
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-gradient-to-r from-primary to-primary-container text-on-primary font-semibold text-sm hover:opacity-90 transition-opacity disabled:opacity-50 whitespace-nowrap"
|
||||
>
|
||||
{debugging ? "Debugging..." : "Debug"}
|
||||
</button>
|
||||
</div>
|
||||
{debugResult && (
|
||||
<pre className="mt-4 bg-surface-container rounded-lg p-4 text-on-surface/80 text-xs overflow-x-auto max-h-96 overflow-y-auto font-mono">
|
||||
{JSON.stringify(debugResult, null, 2)}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user