Skip to content

Commit d18b667

Browse files
committed
wip: zen
1 parent c93c0d4 commit d18b667

File tree

2 files changed

+60
-30
lines changed

2 files changed

+60
-30
lines changed

packages/console/app/src/routes/workspace/key-section.tsx

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import { createStore } from "solid-js/store"
77
import { formatDateUTC, formatDateForTable } from "./common"
88
import styles from "./key-section.module.css"
99
import { Actor } from "@opencode-ai/console-core/actor.js"
10+
import { and, Database, eq, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js"
11+
import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js"
12+
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
13+
import { AccountTable } from "@opencode-ai/console-core/schema/account.sql.js"
14+
import { User } from "@opencode-ai/console-core/user.js"
1015

1116
const removeKey = action(async (form: FormData) => {
1217
"use server"
@@ -108,11 +113,6 @@ export function KeySection() {
108113
const params = useParams()
109114
const keys = createAsync(() => listKeys(params.id))
110115

111-
function formatKey(key: string) {
112-
if (key.length <= 11) return key
113-
return `${key.slice(0, 7)}...${key.slice(-4)}`
114-
}
115-
116116
return (
117117
<section class={styles.root}>
118118
<div data-slot="section-title">
@@ -134,7 +134,8 @@ export function KeySection() {
134134
<tr>
135135
<th>Name</th>
136136
<th>Key</th>
137-
<th>Created</th>
137+
<th>Created By</th>
138+
<th>Last Used</th>
138139
<th></th>
139140
</tr>
140141
</thead>
@@ -147,24 +148,27 @@ export function KeySection() {
147148
<tr>
148149
<td data-slot="key-name">{key.name}</td>
149150
<td data-slot="key-value">
150-
<button
151-
data-color="ghost"
152-
disabled={copied()}
153-
onClick={async () => {
154-
await navigator.clipboard.writeText(key.key)
155-
setCopied(true)
156-
setTimeout(() => setCopied(false), 1000)
157-
}}
158-
title="Copy API key"
159-
>
160-
<span>{formatKey(key.key)}</span>
161-
<Show when={copied()} fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}>
162-
<IconCheck style={{ width: "14px", height: "14px" }} />
163-
</Show>
164-
</button>
151+
<Show when={key.key} fallback={<span>{key.keyDisplay}</span>}>
152+
<button
153+
data-color="ghost"
154+
disabled={copied()}
155+
onClick={async () => {
156+
await navigator.clipboard.writeText(key.key!)
157+
setCopied(true)
158+
setTimeout(() => setCopied(false), 1000)
159+
}}
160+
title="Copy API key"
161+
>
162+
<span>{key.keyDisplay}</span>
163+
<Show when={copied()} fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}>
164+
<IconCheck style={{ width: "14px", height: "14px" }} />
165+
</Show>
166+
</button>
167+
</Show>
165168
</td>
166-
<td data-slot="key-date" title={formatDateUTC(key.timeCreated)}>
167-
{formatDateForTable(key.timeCreated)}
169+
<td data-slot="key-user-email">{key.email}</td>
170+
<td data-slot="key-last-used" title={key.timeUsed ? formatDateUTC(key.timeUsed) : undefined}>
171+
{key.timeUsed ? formatDateForTable(key.timeUsed) : "-"}
168172
</td>
169173
<td data-slot="key-actions">
170174
<form action={removeKey} method="post">

packages/console/core/src/key.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,45 @@ import { Actor } from "./actor"
44
import { and, Database, eq, isNull, sql } from "./drizzle"
55
import { Identifier } from "./identifier"
66
import { KeyTable } from "./schema/key.sql"
7+
import { AccountTable } from "./schema/account.sql"
8+
import { UserTable } from "./schema/user.sql"
9+
import { User } from "./user"
710

811
export namespace Key {
9-
export const list = async () => {
10-
const workspace = Actor.workspace()
12+
export const list = fn(z.void(), async () => {
13+
const userID = Actor.assert("user").properties.userID
14+
const user = await User.fromID(userID)
1115
const keys = await Database.use((tx) =>
1216
tx
13-
.select()
17+
.select({
18+
id: KeyTable.id,
19+
name: KeyTable.name,
20+
key: KeyTable.key,
21+
timeUsed: KeyTable.timeUsed,
22+
userID: KeyTable.userID,
23+
email: AccountTable.email,
24+
})
1425
.from(KeyTable)
15-
.where(and(eq(KeyTable.workspaceID, workspace), isNull(KeyTable.timeDeleted)))
16-
.orderBy(sql`${KeyTable.timeCreated} DESC`),
26+
.innerJoin(UserTable, and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)))
27+
.innerJoin(AccountTable, eq(UserTable.accountID, AccountTable.id))
28+
.where(
29+
and(
30+
...[
31+
eq(KeyTable.workspaceID, Actor.workspace()),
32+
isNull(KeyTable.timeDeleted),
33+
...(user.role === "admin" ? [] : [eq(KeyTable.userID, userID)]),
34+
],
35+
),
36+
)
37+
.orderBy(sql`${KeyTable.name} DESC`),
1738
)
18-
return keys
19-
}
39+
// only return value for user's keys
40+
return keys.map((key) => ({
41+
...key,
42+
key: key.userID === userID ? key.key : undefined,
43+
keyDisplay: `${key.key.slice(0, 7)}...${key.key.slice(-4)}`,
44+
}))
45+
})
2046

2147
export const create = fn(
2248
z.object({

0 commit comments

Comments
 (0)