key.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import { z } from "zod"
  2. import { fn } from "./util/fn"
  3. import { Actor } from "./actor"
  4. import { and, Database, eq, isNull, sql } from "./drizzle"
  5. import { Identifier } from "./identifier"
  6. import { KeyTable } from "./schema/key.sql"
  7. export namespace Key {
  8. export const list = async () => {
  9. const workspace = Actor.workspace()
  10. const keys = await Database.use((tx) =>
  11. tx
  12. .select()
  13. .from(KeyTable)
  14. .where(and(eq(KeyTable.workspaceID, workspace), isNull(KeyTable.timeDeleted)))
  15. .orderBy(sql`${KeyTable.timeCreated} DESC`),
  16. )
  17. return keys
  18. }
  19. export const create = fn(z.object({ name: z.string().min(1).max(255) }), async (input) => {
  20. const workspaceID = Actor.workspace()
  21. const { name } = input
  22. // Generate secret key: sk- + 64 random characters (upper, lower, numbers)
  23. const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  24. let secretKey = "sk-"
  25. const array = new Uint32Array(64)
  26. crypto.getRandomValues(array)
  27. for (let i = 0, l = array.length; i < l; i++) {
  28. secretKey += chars[array[i] % chars.length]
  29. }
  30. const keyID = Identifier.create("key")
  31. await Database.use((tx) =>
  32. tx.insert(KeyTable).values({
  33. id: keyID,
  34. workspaceID,
  35. actor: Actor.use(),
  36. name,
  37. key: secretKey,
  38. timeUsed: null,
  39. }),
  40. ).catch((e: any) => {
  41. if (e.message.match(/Duplicate entry '.*' for key 'key.name'/))
  42. throw new Error("A key with this name already exists. Please choose a different name.")
  43. throw e
  44. })
  45. return keyID
  46. })
  47. export const remove = fn(z.object({ id: z.string() }), async (input) => {
  48. const workspace = Actor.workspace()
  49. await Database.transaction(async (tx) => {
  50. const row = await tx
  51. .select({
  52. name: KeyTable.name,
  53. })
  54. .from(KeyTable)
  55. .where(and(eq(KeyTable.id, input.id), eq(KeyTable.workspaceID, workspace)))
  56. .then((rows) => rows[0])
  57. if (!row) return
  58. await tx
  59. .update(KeyTable)
  60. .set({
  61. timeDeleted: sql`now()`,
  62. oldName: row.name,
  63. name: input.id, // Use the key ID as the name
  64. })
  65. .where(and(eq(KeyTable.id, input.id), eq(KeyTable.workspaceID, workspace)))
  66. })
  67. })
  68. }