lookup-user.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { Database, eq, sql, inArray } from "../src/drizzle/index.js"
  2. import { AuthTable } from "../src/schema/auth.sql.js"
  3. import { UserTable } from "../src/schema/user.sql.js"
  4. import { BillingTable, PaymentTable, UsageTable } from "../src/schema/billing.sql.js"
  5. import { WorkspaceTable } from "../src/schema/workspace.sql.js"
  6. // get input from command line
  7. const identifier = process.argv[2]
  8. if (!identifier) {
  9. console.error("Usage: bun lookup-user.ts <email|workspaceID>")
  10. process.exit(1)
  11. }
  12. if (identifier.startsWith("wrk_")) {
  13. await printWorkspace(identifier)
  14. } else {
  15. const authData = await Database.use(async (tx) =>
  16. tx.select().from(AuthTable).where(eq(AuthTable.subject, identifier)),
  17. )
  18. if (authData.length === 0) {
  19. console.error("Email not found")
  20. process.exit(1)
  21. }
  22. if (authData.length > 1) console.warn("Multiple users found for email", identifier)
  23. // Get all auth records for email
  24. const accountID = authData[0].accountID
  25. await printTable("Auth", (tx) => tx.select().from(AuthTable).where(eq(AuthTable.accountID, accountID)))
  26. // Get all workspaces for this account
  27. const users = await printTable("Workspaces", (tx) =>
  28. tx
  29. .select({
  30. userID: UserTable.id,
  31. workspaceID: UserTable.workspaceID,
  32. workspaceName: WorkspaceTable.name,
  33. role: UserTable.role,
  34. })
  35. .from(UserTable)
  36. .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
  37. .where(eq(UserTable.accountID, accountID)),
  38. )
  39. // Get all payments for these workspaces
  40. await Promise.all(users.map((u: { workspaceID: string }) => printWorkspace(u.workspaceID)))
  41. }
  42. async function printWorkspace(workspaceID: string) {
  43. const workspace = await Database.use((tx) =>
  44. tx
  45. .select()
  46. .from(WorkspaceTable)
  47. .where(eq(WorkspaceTable.id, workspaceID))
  48. .then((rows) => rows[0]),
  49. )
  50. printHeader(`Workspace "${workspace.name}" (${workspace.id})`)
  51. await printTable("Billing", (tx) =>
  52. tx
  53. .select({
  54. balance: BillingTable.balance,
  55. customerID: BillingTable.customerID,
  56. })
  57. .from(BillingTable)
  58. .where(eq(BillingTable.workspaceID, workspace.id))
  59. .then(
  60. (rows) =>
  61. rows.map((row) => ({
  62. ...row,
  63. balance: `$${(row.balance / 100000000).toFixed(2)}`,
  64. }))[0],
  65. ),
  66. )
  67. await printTable("Payments", (tx) =>
  68. tx
  69. .select({
  70. amount: PaymentTable.amount,
  71. paymentID: PaymentTable.paymentID,
  72. invoiceID: PaymentTable.invoiceID,
  73. timeCreated: PaymentTable.timeCreated,
  74. timeRefunded: PaymentTable.timeRefunded,
  75. })
  76. .from(PaymentTable)
  77. .where(eq(PaymentTable.workspaceID, workspace.id))
  78. .orderBy(sql`${PaymentTable.timeCreated} DESC`)
  79. .limit(100)
  80. .then((rows) =>
  81. rows.map((row) => ({
  82. ...row,
  83. amount: `$${(row.amount / 100000000).toFixed(2)}`,
  84. paymentID: row.paymentID
  85. ? `https://dashboard.stripe.com/acct_1RszBH2StuRr0lbX/payments/${row.paymentID}`
  86. : null,
  87. })),
  88. ),
  89. )
  90. await printTable("Usage", (tx) =>
  91. tx
  92. .select({
  93. model: UsageTable.model,
  94. provider: UsageTable.provider,
  95. inputTokens: UsageTable.inputTokens,
  96. outputTokens: UsageTable.outputTokens,
  97. reasoningTokens: UsageTable.reasoningTokens,
  98. cacheReadTokens: UsageTable.cacheReadTokens,
  99. cacheWrite5mTokens: UsageTable.cacheWrite5mTokens,
  100. cacheWrite1hTokens: UsageTable.cacheWrite1hTokens,
  101. cost: UsageTable.cost,
  102. timeCreated: UsageTable.timeCreated,
  103. })
  104. .from(UsageTable)
  105. .where(eq(UsageTable.workspaceID, workspace.id))
  106. .orderBy(sql`${UsageTable.timeCreated} DESC`)
  107. .limit(10)
  108. .then((rows) =>
  109. rows.map((row) => ({
  110. ...row,
  111. cost: `$${(row.cost / 100000000).toFixed(2)}`,
  112. })),
  113. ),
  114. )
  115. }
  116. function printHeader(title: string) {
  117. console.log()
  118. console.log("─".repeat(title.length))
  119. console.log(`${title}`)
  120. console.log("─".repeat(title.length))
  121. }
  122. function printTable(title: string, callback: (tx: Database.TxOrDb) => Promise<any>): Promise<any> {
  123. return Database.use(async (tx) => {
  124. const data = await callback(tx)
  125. console.log(`\n== ${title} ==`)
  126. if (data.length === 0) {
  127. console.log("(no data)")
  128. } else {
  129. console.table(data)
  130. }
  131. return data
  132. })
  133. }