Files
opencode/packages/console/core/src/key.ts
2025-10-16 22:28:34 -04:00

97 lines
2.9 KiB
TypeScript

import { z } from "zod"
import { fn } from "./util/fn"
import { Actor } from "./actor"
import { and, Database, eq, isNull, sql } from "./drizzle"
import { Identifier } from "./identifier"
import { KeyTable } from "./schema/key.sql"
import { UserTable } from "./schema/user.sql"
import { AuthTable } from "./schema/auth.sql"
export namespace Key {
export const list = fn(z.void(), async () => {
const keys = await Database.use((tx) =>
tx
.select({
id: KeyTable.id,
name: KeyTable.name,
key: KeyTable.key,
timeUsed: KeyTable.timeUsed,
userID: KeyTable.userID,
email: AuthTable.subject,
})
.from(KeyTable)
.innerJoin(UserTable, and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)))
.innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")))
.where(
and(
...[
eq(KeyTable.workspaceID, Actor.workspace()),
isNull(KeyTable.timeDeleted),
...(Actor.userRole() === "admin" ? [] : [eq(KeyTable.userID, Actor.userID())]),
],
),
)
.orderBy(sql`${KeyTable.name} DESC`),
)
// only return value for user's keys
return keys.map((key) => ({
...key,
key: key.userID === Actor.userID() ? key.key : undefined,
keyDisplay: `${key.key.slice(0, 7)}...${key.key.slice(-4)}`,
}))
})
export const create = fn(
z.object({
userID: z.string(),
name: z.string().min(1).max(255),
}),
async (input) => {
const { name } = input
// Generate secret key: sk- + 64 random characters (upper, lower, numbers)
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
let secretKey = "sk-"
const array = new Uint32Array(64)
crypto.getRandomValues(array)
for (let i = 0, l = array.length; i < l; i++) {
secretKey += chars[array[i] % chars.length]
}
const keyID = Identifier.create("key")
await Database.use((tx) =>
tx.insert(KeyTable).values({
id: keyID,
workspaceID: Actor.workspace(),
userID: input.userID,
name,
key: secretKey,
timeUsed: null,
}),
)
return keyID
},
)
export const remove = fn(z.object({ id: z.string() }), async (input) => {
// only admin can remove other user's keys
await Database.use((tx) =>
tx
.update(KeyTable)
.set({
timeDeleted: sql`now()`,
})
.where(
and(
...[
eq(KeyTable.id, input.id),
eq(KeyTable.workspaceID, Actor.workspace()),
...(Actor.userRole() === "admin" ? [] : [eq(KeyTable.userID, Actor.userID())]),
],
),
),
)
})
}