import { getRequestEvent } from "solid-js/web" import { and, Database, eq, inArray, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js" import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js" import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js" import { redirect } from "@solidjs/router" import { Actor } from "@opencode-ai/console-core/actor.js" import { createClient } from "@openauthjs/openauth/client" export const AuthClient = createClient({ clientID: "app", issuer: import.meta.env.VITE_AUTH_URL, }) import { useSession } from "@solidjs/start/http" import { Resource } from "@opencode-ai/console-resource" export interface AuthSession { account?: Record< string, { id: string email: string } > current?: string } export function useAuthSession() { return useSession({ password: Resource.ZEN_SESSION_SECRET.value, name: "auth", maxAge: 60 * 60 * 24 * 365, cookie: { secure: false, httpOnly: true, }, }) } export const getActor = async (workspace?: string): Promise => { "use server" const evt = getRequestEvent() if (!evt) throw new Error("No request event") if (evt.locals.actor) return evt.locals.actor evt.locals.actor = (async () => { const auth = await useAuthSession() if (!workspace) { const account = auth.data.account ?? {} const current = account[auth.data.current ?? ""] if (current) { return { type: "account", properties: { email: current.email, accountID: current.id, }, } } if (Object.keys(account).length > 0) { const current = Object.values(account)[0] await auth.update((val) => ({ ...val, current: current.id, })) return { type: "account", properties: { email: current.email, accountID: current.id, }, } } return { type: "public", properties: {}, } } const accounts = Object.keys(auth.data.account ?? {}) if (accounts.length) { const user = await Database.use((tx) => tx .select({ id: UserTable.id, workspaceID: UserTable.workspaceID, accountID: UserTable.accountID, role: UserTable.role, migratedAt: WorkspaceTable.migrated_at, }) .from(UserTable) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID)) .where( and( eq(UserTable.workspaceID, workspace), isNull(UserTable.timeDeleted), inArray(UserTable.accountID, accounts), ), ) .limit(1) .execute() .then((x) => x[0]), ) if (user) { if (user.migratedAt) { const destination = Resource.ConsoleMigration.consoleUrl if (!destination) throw new Error("New Console URL is not configured") evt.response.headers.set("Cache-Control", "no-store") throw redirect(`${destination}/login`, { status: evt.request.method === "GET" || evt.request.method === "HEAD" ? 302 : 303, headers: { "Cache-Control": "no-store" }, }) } await Database.use((tx) => tx .update(UserTable) .set({ timeSeen: sql`now()` }) .where(and(eq(UserTable.workspaceID, workspace), eq(UserTable.id, user.id))), ) return { type: "user", properties: { userID: user.id, workspaceID: user.workspaceID, accountID: user.accountID, role: user.role, }, } } } throw redirect("/auth/authorize") })() return evt.locals.actor }