feat add page to geneerate api key
CI / CI - Validation Technique (push) Successful in 37s
Publish & Deploy (Versioned) / Build and Push Docker Image (push) Successful in 29s
Publish & Deploy (Versioned) / Deploy via SSH (push) Successful in 8s

This commit is contained in:
2026-05-07 18:49:05 +02:00
parent bad3b5539c
commit 7a910b3d8f
+53 -230
View File
@@ -1,243 +1,66 @@
// 'use client' "use client";
// import { useEffect, useState } from 'react' import { useState } from "react";
import { useSession } from "next-auth/react";
// export default function AboutPage() { export default function GenerateKeyButton() {
// const [data, setData] = useState<any>(null) const [loading, setLoading] = useState(false);
// const [loading, setLoading] = useState(true) const [result, setResult] = useState<string | null>(null);
// useEffect(() => { const { data: session, status } = useSession();
// fetch((process.env.NEXT_PUBLIC_API_URL as string) + "/docs.json")
// .then((res) => res.json())
// .then((result) => {
// setData(result)
// setLoading(false)
// })
// .catch((err) => {
// console.error(err)
// setLoading(false)
// })
// }, [])
// return ( async function handleGenerate() {
// <div className="p-6"> setLoading(true);
setResult(null);
// {loading && <p>Loading...</p>} try {
const res = await fetch("http://localhost:3001/v1/keys/generate", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session?.accessToken}`,
// "x-api-key": "Q67lQp7NZY2f2Zy6DXYbFoauDMjw79FBD8LPn9ee6OM"
},
body: JSON.stringify({
name: "my-api-key",
scopes: ["read", "write"],
}),
});
// {!loading && data && ( if (!res.ok) {
// <pre>{JSON.stringify(data, null, 2)}</pre> const errText = await res.text();
// )} throw new Error(errText);
// </div> }
// )
// }
// 'use client'; const data = await res.json();
setResult(JSON.stringify(data, null, 2));
// import { useEffect, useState } from 'react'; } catch (err: any) {
// import Spinner from '@/components/spinner'; setResult(err.message);
} finally {
// export default function AboutPage() { setLoading(false);
// const [data, setData] = useState<any>(null); }
// const [loading, setLoading] = useState(true); }
// useEffect(() => {
// fetch((process.env.NEXT_PUBLIC_API_URL as string) + '/docs.json')
// .then((res) => res.json())
// .then((result) => {
// setData(result);
// setLoading(false);
// })
// .catch(() => {
// setLoading(false);
// });
// }, []);
// if (loading) {
// return (
// <div className="flex items-center justify-center min-h-screen">
// <div className="h-12 w-12 rounded-full border-4 border-gray-300 border-t-blue-600 animate-spin" />
// </div>
// );
// }
// return (
// <div className="flex items-center justify-center min-h-screen bg-primary text-primary transition-theme">
// <Spinner />
// </div>
// );
// }
'use client';
import Sidebar from '@/components/Sidebar';
import { SidebarPanel } from '@/components/Sidebar';
import {
Slider,
NumberInput,
TextInput,
Toggle,
} from '../../../components/Inputs';
type PanelProps = {
settings: Settings;
setSettings: React.Dispatch<React.SetStateAction<Settings>>;
};
export type Settings = {
temp: number;
topP: number;
topK: number | undefined;
maxTokens: number | undefined;
numCtx: number | undefined;
stop: string[] | undefined;
repeatPenalty: number;
seed: number | undefined;
stream: boolean;
keepAlive: string | undefined;
};
function SettingsPanel({ settings, setSettings }: PanelProps) {
const set =
<K extends keyof Settings>(key: K) =>
(val: Settings[K]) =>
setSettings((prev) => ({ ...prev, [key]: val }));
const sections = [
{
title: 'Sampling',
fields: (s: any, set: any) => (
<>
<Slider
label="Temperature"
value={s.temp}
setValue={set('temp')}
min={0}
max={2}
step={0.1}
/>
<Slider
label="Top P"
value={s.topP}
setValue={set('topP')}
min={0}
max={1}
step={0.05}
/>
<NumberInput label="Top K" value={s.topK} setValue={set('topK')} />
</>
),
},
{
title: 'Generation',
fields: (s: any, set: any) => (
<>
<NumberInput
label="Max Tokens"
value={s.maxTokens}
setValue={set('maxTokens')}
/>
<NumberInput
label="Context Size"
value={s.numCtx}
setValue={set('numCtx')}
/>
<TextInput
label="Stop Sequences"
value={s.stop?.join(', ')}
setValue={(val) =>
set('stop')(val ? val.split(',').map((s) => s.trim()) : undefined)
}
placeholder="Comma separated"
/>
</>
),
},
{
title: 'Control',
fields: (s: any, set: any) => (
<>
<Slider
label="Repeat Penalty"
value={s.repeatPenalty}
setValue={set('repeatPenalty')}
min={0}
max={2}
step={0.1}
/>
<NumberInput label="Seed" value={s.seed} setValue={set('seed')} />
</>
),
},
{
title: 'System',
fields: (s: any, set: any) => (
<>
<Toggle label="Streaming" value={s.stream} setValue={set('stream')} />
<TextInput
label="Keep Alive"
value={s.keepAlive}
setValue={set('keepAlive')}
placeholder="e.g. 5m"
/>
</>
),
},
];
return ( return (
<div className="space-y-6 px-1"> <div style={{ padding: 20 }}>
{sections.map((section) => ( <button
<div key={section.title} className="space-y-4"> onClick={handleGenerate}
<h3 className="text-xs uppercase text-accent tracking-widest"> disabled={loading}
{section.title} style={{
</h3> padding: "10px 16px",
background: "black",
color: "white",
borderRadius: 6,
}}
>
{loading ? "Generating..." : "Generate API Key"}
</button>
{section.fields(settings, set)} {result && (
</div> <pre style={{ marginTop: 20, background: "#eee", padding: 10 }}>
))} {result}
</div> </pre>
); )}
}
export const SIDEBAR_PANELS = ({
settings,
setSettings,
}: PanelProps): SidebarPanel[] => [
{
id: 'settings',
label: 'Settings',
icon: (
<svg viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4">
<path
fillRule="evenodd"
d="M7.84 1.804A1 1 0 018.82 1h2.36a1 1 0 01.98.804l.331 1.652a6.993 6.993 0 011.929 1.115l1.598-.54a1 1 0 011.186.447l1.18 2.044a1 1 0 01-.205 1.251l-1.267 1.113a7.047 7.047 0 010 2.228l1.267 1.113a1 1 0 01.205 1.251l-1.18 2.044a1 1 0 01-1.186.447l-1.598-.54a6.993 6.993 0 01-1.929 1.115l-.33 1.652a1 1 0 01-.98.804H8.82a1 1 0 01-.98-.804l-.331-1.652a6.993 6.993 0 01-1.929-1.115l-1.598.54a1 1 0 01-1.186-.447l-1.18-2.044a1 1 0 01.205-1.251l1.267-1.114a7.05 7.05 0 010-2.227L1.821 7.773a1 1 0 01-.206-1.25l1.18-2.045a1 1 0 011.187-.447l1.598.54A6.993 6.993 0 017.51 3.456l.33-1.652zM10 13a3 3 0 100-6 3 3 0 000 6z"
clipRule="evenodd"
/>
</svg>
),
component: <SettingsPanel settings={settings} setSettings={setSettings} />,
},
];
import { useState } from 'react';
export default function DocPage() {
const [settings, setSettings] = useState<Settings>({
temp: 0.7,
topP: 0.9,
topK: 40,
maxTokens: 1000,
numCtx: 128000,
stop: [''],
repeatPenalty: 1.1,
seed: 0,
stream: true,
keepAlive: '5m',
});
return (
<div className="flex flex-1 overflow-hidden min-h-0">
<Sidebar panels={SIDEBAR_PANELS({ settings, setSettings })} />
</div> </div>
); );
} }