workspace.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { z } from "zod"
  2. import { fn } from "./util/fn"
  3. import { Actor } from "./actor"
  4. import { Database } from "./drizzle"
  5. import { Identifier } from "./identifier"
  6. import { UserTable } from "./schema/user.sql"
  7. import { BillingTable } from "./schema/billing.sql"
  8. import { WorkspaceTable } from "./schema/workspace.sql"
  9. import { AccountTable } from "./schema/account.sql"
  10. import { Key } from "./key"
  11. import { and, eq, isNull, sql } from "drizzle-orm"
  12. export namespace Workspace {
  13. export const Region = z.enum(["us", "eu", "sg", "cn"])
  14. export type Region = z.infer<typeof Region>
  15. export const create = fn(
  16. z.object({
  17. name: z.string().min(1),
  18. }),
  19. async ({ name }) => {
  20. const account = Actor.assert("account")
  21. const workspaceID = Identifier.create("workspace")
  22. const userID = Identifier.create("user")
  23. await Database.transaction(async (tx) => {
  24. const active = await tx
  25. .select({ id: AccountTable.id })
  26. .from(AccountTable)
  27. .where(and(eq(AccountTable.id, account.properties.accountID), isNull(AccountTable.timeDeleted)))
  28. .then((rows) => rows[0])
  29. if (!active) throw new Error("Account is not active")
  30. await tx.insert(WorkspaceTable).values({
  31. id: workspaceID,
  32. name,
  33. })
  34. await tx.insert(UserTable).values({
  35. workspaceID,
  36. id: userID,
  37. accountID: account.properties.accountID,
  38. name: "",
  39. role: "admin",
  40. })
  41. await tx.insert(BillingTable).values({
  42. workspaceID,
  43. id: Identifier.create("billing"),
  44. balance: 0,
  45. })
  46. })
  47. await Actor.provide(
  48. "system",
  49. {
  50. workspaceID,
  51. },
  52. () => Key.create({ userID, name: "Default API Key" }),
  53. )
  54. return workspaceID
  55. },
  56. )
  57. export const update = fn(
  58. z.object({
  59. name: z.string().min(1).max(255).optional(),
  60. region: z.array(Region).min(1).optional(),
  61. }),
  62. async (input) => {
  63. Actor.assertAdmin()
  64. const workspaceID = Actor.workspace()
  65. return await Database.use((tx) =>
  66. tx
  67. .update(WorkspaceTable)
  68. .set({
  69. ...("name" in input ? { name: input.name } : {}),
  70. ...("region" in input ? { region: input.region } : {}),
  71. })
  72. .where(eq(WorkspaceTable.id, workspaceID)),
  73. )
  74. },
  75. )
  76. export const setDefaultRegion = fn(
  77. z.object({
  78. country: z.string().optional(),
  79. }),
  80. async (input) => {
  81. const region: Workspace.Region[] =
  82. input.country?.toUpperCase() === "CN" ? ["us", "eu", "sg", "cn"] : ["us", "eu", "sg"]
  83. await Database.use((tx) =>
  84. tx
  85. .update(WorkspaceTable)
  86. .set({ region })
  87. .where(and(eq(WorkspaceTable.id, Actor.workspace()), isNull(WorkspaceTable.region))),
  88. )
  89. return region
  90. },
  91. )
  92. export const remove = fn(z.void(), async () => {
  93. await Database.use((tx) =>
  94. tx
  95. .update(WorkspaceTable)
  96. .set({ timeDeleted: sql`now()` })
  97. .where(eq(WorkspaceTable.id, Actor.workspace())),
  98. )
  99. })
  100. }