auth.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { getRequestEvent } from "solid-js/web"
  2. import { and, Database, eq, inArray, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js"
  3. import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
  4. import { redirect } from "@solidjs/router"
  5. import { Actor } from "@opencode-ai/console-core/actor.js"
  6. import { createClient } from "@openauthjs/openauth/client"
  7. export const AuthClient = createClient({
  8. clientID: "app",
  9. issuer: import.meta.env.VITE_AUTH_URL,
  10. })
  11. import { useSession } from "@solidjs/start/http"
  12. import { Resource } from "@opencode-ai/console-resource"
  13. export interface AuthSession {
  14. account?: Record<
  15. string,
  16. {
  17. id: string
  18. email: string
  19. }
  20. >
  21. current?: string
  22. }
  23. export function useAuthSession() {
  24. return useSession<AuthSession>({
  25. password: Resource.ZEN_SESSION_SECRET.value,
  26. name: "auth",
  27. maxAge: 60 * 60 * 24 * 365,
  28. cookie: {
  29. secure: false,
  30. httpOnly: true,
  31. },
  32. })
  33. }
  34. export const getActor = async (workspace?: string): Promise<Actor.Info> => {
  35. "use server"
  36. const evt = getRequestEvent()
  37. if (!evt) throw new Error("No request event")
  38. if (evt.locals.actor) return evt.locals.actor
  39. evt.locals.actor = (async () => {
  40. const auth = await useAuthSession()
  41. if (!workspace) {
  42. const account = auth.data.account ?? {}
  43. const current = account[auth.data.current ?? ""]
  44. if (current) {
  45. return {
  46. type: "account",
  47. properties: {
  48. email: current.email,
  49. accountID: current.id,
  50. },
  51. }
  52. }
  53. if (Object.keys(account).length > 0) {
  54. const current = Object.values(account)[0]
  55. await auth.update((val) => ({
  56. ...val,
  57. current: current.id,
  58. }))
  59. return {
  60. type: "account",
  61. properties: {
  62. email: current.email,
  63. accountID: current.id,
  64. },
  65. }
  66. }
  67. return {
  68. type: "public",
  69. properties: {},
  70. }
  71. }
  72. const accounts = Object.keys(auth.data.account ?? {})
  73. if (accounts.length) {
  74. const user = await Database.use((tx) =>
  75. tx
  76. .select()
  77. .from(UserTable)
  78. .where(
  79. and(
  80. eq(UserTable.workspaceID, workspace),
  81. isNull(UserTable.timeDeleted),
  82. inArray(UserTable.accountID, accounts),
  83. ),
  84. )
  85. .limit(1)
  86. .execute()
  87. .then((x) => x[0]),
  88. )
  89. if (user) {
  90. await Database.use((tx) =>
  91. tx
  92. .update(UserTable)
  93. .set({ timeSeen: sql`now()` })
  94. .where(and(eq(UserTable.workspaceID, workspace), eq(UserTable.id, user.id))),
  95. )
  96. return {
  97. type: "user",
  98. properties: {
  99. userID: user.id,
  100. workspaceID: user.workspaceID,
  101. accountID: user.accountID,
  102. role: user.role,
  103. },
  104. }
  105. }
  106. }
  107. throw redirect("/auth/authorize")
  108. })()
  109. return evt.locals.actor
  110. }