auth.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { useSession } from "vinxi/http"
  2. import { createClient } from "@openauthjs/openauth/client"
  3. import { getRequestEvent } from "solid-js/web"
  4. import { and, Database, eq, inArray } from "@opencode/cloud-core/drizzle/index.js"
  5. import { WorkspaceTable } from "@opencode/cloud-core/schema/workspace.sql.js"
  6. import { UserTable } from "@opencode/cloud-core/schema/user.sql.js"
  7. import { query, redirect } from "@solidjs/router"
  8. import { AccountTable } from "@opencode/cloud-core/schema/account.sql.js"
  9. import { Actor } from "@opencode/cloud-core/actor.js"
  10. export async function withActor<T>(fn: () => T) {
  11. const actor = await getActor()
  12. return Actor.provide(actor.type, actor.properties, fn)
  13. }
  14. export const getActor = query(async (): Promise<Actor.Info> => {
  15. "use server"
  16. const evt = getRequestEvent()
  17. const url = new URL(evt!.request.headers.get("referer") ?? evt!.request.url)
  18. const auth = await useAuthSession()
  19. const [workspaceHint] = url.pathname.split("/").filter((x) => x.length > 0)
  20. if (!workspaceHint) {
  21. if (auth.data.current) {
  22. const current = auth.data.account[auth.data.current]
  23. return {
  24. type: "account",
  25. properties: {
  26. email: current.email,
  27. accountID: current.id,
  28. },
  29. }
  30. }
  31. if (Object.keys(auth.data.account).length > 0) {
  32. const current = Object.values(auth.data.account)[0]
  33. await auth.update(val => ({
  34. ...val,
  35. current: current.id,
  36. }))
  37. return {
  38. type: "account",
  39. properties: {
  40. email: current.email,
  41. accountID: current.id,
  42. },
  43. }
  44. }
  45. return {
  46. type: "public",
  47. properties: {},
  48. }
  49. }
  50. const accounts = Object.keys(auth.data.account)
  51. const result = await Database.transaction(async (tx) => {
  52. return await tx.select({
  53. user: UserTable
  54. })
  55. .from(AccountTable)
  56. .innerJoin(UserTable, and(eq(UserTable.email, AccountTable.email)))
  57. .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
  58. .where(
  59. and(
  60. inArray(AccountTable.id, accounts),
  61. eq(WorkspaceTable.id, workspaceHint),
  62. )
  63. )
  64. .limit(1)
  65. .execute()
  66. .then((x) => x[0])
  67. })
  68. if (result) {
  69. return {
  70. type: "user",
  71. properties: {
  72. userID: result.user.id,
  73. workspaceID: result.user.workspaceID,
  74. },
  75. }
  76. }
  77. throw redirect("/auth/authorize")
  78. }, "actor")
  79. export const AuthClient = createClient({
  80. clientID: "app",
  81. issuer: import.meta.env.VITE_AUTH_URL,
  82. })
  83. export interface AuthSession {
  84. account: Record<string, {
  85. id: string
  86. email: string
  87. }>
  88. current?: string
  89. }
  90. export function useAuthSession() {
  91. return useSession<AuthSession>({
  92. password: "0".repeat(32),
  93. name: "auth"
  94. })
  95. }
  96. export function AuthProvider() {
  97. }