key.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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. import { AccountTable } from "./schema/account.sql"
  8. import { UserTable } from "./schema/user.sql"
  9. import { User } from "./user"
  10. export namespace Key {
  11. export const list = fn(z.void(), async () => {
  12. const keys = await Database.use((tx) =>
  13. tx
  14. .select({
  15. id: KeyTable.id,
  16. name: KeyTable.name,
  17. key: KeyTable.key,
  18. timeUsed: KeyTable.timeUsed,
  19. userID: KeyTable.userID,
  20. email: AccountTable.email,
  21. })
  22. .from(KeyTable)
  23. .innerJoin(UserTable, and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)))
  24. .innerJoin(AccountTable, eq(UserTable.accountID, AccountTable.id))
  25. .where(
  26. and(
  27. ...[
  28. eq(KeyTable.workspaceID, Actor.workspace()),
  29. isNull(KeyTable.timeDeleted),
  30. ...(Actor.userRole() === "admin" ? [] : [eq(KeyTable.userID, Actor.userID())]),
  31. ],
  32. ),
  33. )
  34. .orderBy(sql`${KeyTable.name} DESC`),
  35. )
  36. // only return value for user's keys
  37. return keys.map((key) => ({
  38. ...key,
  39. key: key.userID === Actor.userID() ? key.key : undefined,
  40. keyDisplay: `${key.key.slice(0, 7)}...${key.key.slice(-4)}`,
  41. }))
  42. })
  43. export const create = fn(
  44. z.object({
  45. userID: z.string(),
  46. name: z.string().min(1).max(255),
  47. }),
  48. async (input) => {
  49. const { name } = input
  50. // Generate secret key: sk- + 64 random characters (upper, lower, numbers)
  51. const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  52. let secretKey = "sk-"
  53. const array = new Uint32Array(64)
  54. crypto.getRandomValues(array)
  55. for (let i = 0, l = array.length; i < l; i++) {
  56. secretKey += chars[array[i] % chars.length]
  57. }
  58. const keyID = Identifier.create("key")
  59. await Database.use((tx) =>
  60. tx.insert(KeyTable).values({
  61. id: keyID,
  62. workspaceID: Actor.workspace(),
  63. userID: input.userID,
  64. name,
  65. key: secretKey,
  66. timeUsed: null,
  67. }),
  68. )
  69. return keyID
  70. },
  71. )
  72. export const remove = fn(z.object({ id: z.string() }), async (input) => {
  73. // only admin can remove other user's keys
  74. await Database.use((tx) =>
  75. tx
  76. .update(KeyTable)
  77. .set({
  78. timeDeleted: sql`now()`,
  79. })
  80. .where(
  81. and(
  82. ...[
  83. eq(KeyTable.id, input.id),
  84. eq(KeyTable.workspaceID, Actor.workspace()),
  85. ...(Actor.userRole() === "admin" ? [] : [eq(KeyTable.userID, Actor.userID())]),
  86. ],
  87. ),
  88. ),
  89. )
  90. })
  91. }