diff options
| author | Frank <[email protected]> | 2025-08-28 16:44:55 -0400 |
|---|---|---|
| committer | Frank <[email protected]> | 2025-08-28 16:44:55 -0400 |
| commit | c6ef92634d0ae026a59e023e69847b481975462b (patch) | |
| tree | c88bfbbabf1f46ca922e3212dd929b34d6229a1a /cloud/app/src | |
| parent | f97fdceb01c69ca563e755c6d50312ef7352f663 (diff) | |
| download | opencode-c6ef92634d0ae026a59e023e69847b481975462b.tar.gz opencode-c6ef92634d0ae026a59e023e69847b481975462b.zip | |
wip cloud
Diffstat (limited to 'cloud/app/src')
| -rw-r--r-- | cloud/app/src/routes/[workspaceID].tsx | 186 | ||||
| -rw-r--r-- | cloud/app/src/style/token/color.css | 4 |
2 files changed, 181 insertions, 9 deletions
diff --git a/cloud/app/src/routes/[workspaceID].tsx b/cloud/app/src/routes/[workspaceID].tsx index 706a64323..98d03753f 100644 --- a/cloud/app/src/routes/[workspaceID].tsx +++ b/cloud/app/src/routes/[workspaceID].tsx @@ -1,15 +1,187 @@ -import { createAsync, query } from "@solidjs/router" +import { Billing } from "@opencode/cloud-core/billing.js" +import { Key } from "@opencode/cloud-core/key.js" +import { action, createAsync, revalidate, query, useAction, useSubmission } from "@solidjs/router" +import { createSignal, For, Show } from "solid-js" import { getActor, withActor } from "~/context/auth" -const getPosts = query(async () => { +///////////////////////////////////// +// Keys related queries and actions +///////////////////////////////////// + +const listKeys = query(async () => { + "use server" + return withActor(() => Key.list()) +}, "keys") + +const createKey = action(async (name: string) => { + "use server" + return withActor(() => Key.create({ name })) +}, "createKey") + +const removeKey = action(async (id: string) => { "use server" - return withActor(() => { - return "ok" + return withActor(() => Key.remove({ id })) +}, "removeKey") + +///////////////////////////////////// +// Billing related queries and actions +///////////////////////////////////// + +const getBillingInfo = query(async () => { + "use server" + return withActor(async () => { + const billing = await Billing.get() + const payments = await Billing.payments() + const usage = await Billing.usages() + return { billing, payments, usage } }) -}, "posts") +}, "billingInfo") + +const createCheckoutUrl = action(async (successUrl: string, cancelUrl: string) => { + "use server" + return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl })) +}, "checkoutUrl") +const createPortalUrl = action(async (returnUrl: string) => { + "use server" + return withActor(() => Billing.generatePortalUrl({ returnUrl })) +}, "portalUrl") + +//export const route = { +// preload: () => listKeys(), +//} export default function () { - const actor = createAsync(async () => getActor()) - return <div>{JSON.stringify(actor())}</div> + const actor = createAsync(() => getActor()) + const keys = createAsync(() => listKeys()) + const createKeyAction = useAction(createKey) + const removeKeyAction = useAction(removeKey) + const createKeySubmission = useSubmission(createKey) + const [showCreateForm, setShowCreateForm] = createSignal(false) + const [keyName, setKeyName] = createSignal("") + + const formatDate = (date: Date) => { + return date.toLocaleDateString() + } + + const formatKey = (key: string) => { + if (key.length <= 11) return key + return `${key.slice(0, 7)}...${key.slice(-4)}` + } + + const copyToClipboard = async (text: string) => { + try { + await navigator.clipboard.writeText(text) + } catch (error) { + console.error("Failed to copy to clipboard:", error) + } + } + + const handleCreateKey = async () => { + if (!keyName().trim()) return + + try { + await createKeyAction(keyName().trim()) + revalidate("keys") + setKeyName("") + setShowCreateForm(false) + } catch (error) { + console.error("Failed to create API key:", error) + } + } + + const handleDeleteKey = async (keyId: string) => { + if (!confirm("Are you sure you want to delete this API key? This action cannot be undone.")) { + return + } + + try { + await removeKeyAction(keyId) + revalidate("keys") + } catch (error) { + console.error("Failed to delete API key:", error) + } + } + + return ( + <div> + <h1>Actor</h1> + <div>{JSON.stringify(actor())}</div> + <h1>API Keys</h1> + <Show + when={!showCreateForm()} + fallback={ + <div data-slot="create-form"> + <input + data-component="input" + type="text" + placeholder="Enter key name" + value={keyName()} + onInput={(e) => setKeyName(e.currentTarget.value)} + onKeyPress={(e) => e.key === "Enter" && handleCreateKey()} + /> + <div data-slot="form-actions"> + <button + color="primary" + disabled={createKeySubmission.pending || !keyName().trim()} + onClick={handleCreateKey} + > + {createKeySubmission.pending ? "Creating..." : "Create"} + </button> + <button + color="ghost" + onClick={() => { + setShowCreateForm(false) + setKeyName("") + }} + > + Cancel + </button> + </div> + </div> + } + > + <button + color="primary" + onClick={() => { + console.log("clicked") + setShowCreateForm(true) + }} + > + Create API Key + </button> + </Show> + <div data-slot="key-list"> + <For + each={keys()} + fallback={ + <div data-slot="empty-state"> + <p>Create an API key to access opencode gateway</p> + </div> + } + > + {(key) => ( + <div data-slot="key-item"> + <div data-slot="key-info"> + <div data-slot="key-name">{key.name}</div> + <div data-slot="key-value">{formatKey(key.key)}</div> + <div data-slot="key-meta"> + Created: {formatDate(key.timeCreated)} + {key.timeUsed && ` • Last used: ${formatDate(key.timeUsed)}`} + </div> + </div> + <div data-slot="key-actions"> + <button color="ghost" onClick={() => copyToClipboard(key.key)} title="Copy API key"> + Copy + </button> + <button color="ghost" onClick={() => handleDeleteKey(key.id)} title="Delete API key"> + Delete + </button> + </div> + </div> + )} + </For> + </div> + </div> + ) } diff --git a/cloud/app/src/style/token/color.css b/cloud/app/src/style/token/color.css index 35846acd9..31d11e2dd 100644 --- a/cloud/app/src/style/token/color.css +++ b/cloud/app/src/style/token/color.css @@ -47,7 +47,7 @@ @media (prefers-color-scheme: dark) { :root { /* OpenCode dark theme colors */ - --color-bg: #0c0c0e; + /*--color-bg: #0c0c0e;*/ --color-bg-surface: #161618; --color-bg-elevated: #1c1c1f; @@ -87,4 +87,4 @@ --color-surface-hover: var(--color-bg-elevated); --color-border: var(--color-border); } -} +}
\ No newline at end of file |
