From 48a79b11738c3e55f25b49497b8ff78dfaee1222 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 3 Sep 2025 13:10:57 -0400 Subject: [PATCH] wip: make less shit --- cloud/app/src/context/auth.ts | 10 ++---- cloud/app/src/context/auth.withActor.ts | 5 ++- cloud/app/src/routes/index.tsx | 17 +++++------ cloud/app/src/routes/workspace.tsx | 15 +++++---- cloud/app/src/routes/workspace/[id].tsx | 39 ++++++++++++------------ cloud/app/src/routes/workspace/index.tsx | 0 6 files changed, 38 insertions(+), 48 deletions(-) create mode 100644 cloud/app/src/routes/workspace/index.tsx diff --git a/cloud/app/src/context/auth.ts b/cloud/app/src/context/auth.ts index e80604f8..e08d965b 100644 --- a/cloud/app/src/context/auth.ts +++ b/cloud/app/src/context/auth.ts @@ -14,17 +14,14 @@ export const AuthClient = createClient({ issuer: import.meta.env.VITE_AUTH_URL, }) -export const getActor = async (): Promise => { +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 () => { - console.log("getActor") - const url = new URL(evt.request.headers.has("x-server-id") ? evt.request.headers.get("referer")! : evt.request.url) const auth = await useAuthSession() - const splits = url.pathname.split("/").filter(Boolean) - if (splits[0] !== "workspace") { + if (!workspace) { const account = auth.data.account ?? {} const current = account[auth.data.current ?? ""] if (current) { @@ -55,7 +52,6 @@ export const getActor = async (): Promise => { properties: {}, } } - const workspaceHint = splits[1] const accounts = Object.keys(auth.data.account ?? {}) if (accounts.length) { const result = await Database.transaction(async (tx) => { @@ -66,7 +62,7 @@ export const getActor = async (): Promise => { .from(AccountTable) .innerJoin(UserTable, and(eq(UserTable.email, AccountTable.email))) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID)) - .where(and(inArray(AccountTable.id, accounts), eq(WorkspaceTable.id, workspaceHint))) + .where(and(inArray(AccountTable.id, accounts), eq(WorkspaceTable.id, workspace))) .limit(1) .execute() .then((x) => x[0]) diff --git a/cloud/app/src/context/auth.withActor.ts b/cloud/app/src/context/auth.withActor.ts index 5c87f133..4cfd5c3e 100644 --- a/cloud/app/src/context/auth.withActor.ts +++ b/cloud/app/src/context/auth.withActor.ts @@ -1,8 +1,7 @@ import { Actor } from "@opencode/cloud-core/actor.js" import { getActor } from "./auth" -import { getRequestEvent } from "solid-js/web" -export async function withActor(fn: () => T) { - const actor = await getActor() +export async function withActor(fn: () => T, workspace?: string) { + const actor = await getActor(workspace) return Actor.provide(actor.type, actor.properties, fn) } diff --git a/cloud/app/src/routes/index.tsx b/cloud/app/src/routes/index.tsx index 042b1527..74100075 100644 --- a/cloud/app/src/routes/index.tsx +++ b/cloud/app/src/routes/index.tsx @@ -7,7 +7,7 @@ import IMG_SPLASH from "../asset/lander/screenshot-splash.png" import IMG_VSCODE from "../asset/lander/screenshot-vscode.png" import IMG_GITHUB from "../asset/lander/screenshot-github.png" import { IconCopy, IconCheck } from "../component/icon" -import { createAsync, query, redirect } from "@solidjs/router" +import { createAsync, query, redirect, A } from "@solidjs/router" import { getActor } from "~/context/auth" import { withActor } from "~/context/auth.withActor" import { Account } from "@opencode/cloud-core/account.js" @@ -21,20 +21,17 @@ function CopyStatus() { ) } -const isLoggedIn = query(async () => { +const defaultWorkspace = query(async () => { "use server" const actor = await getActor() if (actor.type === "account") { const workspaces = await withActor(() => Account.workspaces()) return workspaces[0].id - // throw redirect(`/workspace/${workspaces[0].id}`) } -}, "isLoggedIn") +}, "defaultWorkspace") export default function Home() { - const auth = createAsync(() => isLoggedIn(), { - deferStream: true, - }) + const workspace = createAsync(() => defaultWorkspace()) onMount(() => { const commands = document.querySelectorAll("[data-copy]") for (const button of commands) { @@ -90,9 +87,9 @@ export default function Home() { , a curated list of models provided by opencode  /  - - {auth() ? "Dashboard" : "Sign in"} - + + {workspace() ? "Dashboard" : "Sign in"} +
diff --git a/cloud/app/src/routes/workspace.tsx b/cloud/app/src/routes/workspace.tsx index 853cb370..791d2211 100644 --- a/cloud/app/src/routes/workspace.tsx +++ b/cloud/app/src/routes/workspace.tsx @@ -3,17 +3,17 @@ import { useAuthSession } from "~/context/auth.session" import { IconLogo } from "../component/icon" import { withActor } from "~/context/auth.withActor" import "./workspace.css" -import { query, action, redirect, createAsync, RouteSectionProps } from "@solidjs/router" +import { query, action, redirect, createAsync, RouteSectionProps, Navigate, useNavigate, useParams, A } from "@solidjs/router" import { User } from "@opencode/cloud-core/user.js" import { Actor } from "@opencode/cloud-core/actor.js" -const getUserInfo = query(async () => { +const getUserInfo = query(async (workspaceID: string) => { "use server" return withActor(async () => { const actor = Actor.assert("user") const user = await User.fromID(actor.properties.userID) return { user } - }) + }, workspaceID) }, "userInfo") const logout = action(async () => { @@ -30,16 +30,15 @@ const logout = action(async () => { }) export default function WorkspaceLayout(props: RouteSectionProps) { - const userInfo = createAsync(() => getUserInfo(), { - deferStream: true, - }) + const params = useParams() + const userInfo = createAsync(() => getUserInfo(params.id)) return (
{userInfo()?.user.email} diff --git a/cloud/app/src/routes/workspace/[id].tsx b/cloud/app/src/routes/workspace/[id].tsx index 84d2c10b..85f4cc26 100644 --- a/cloud/app/src/routes/workspace/[id].tsx +++ b/cloud/app/src/routes/workspace/[id].tsx @@ -1,7 +1,7 @@ import "./[id].css" import { Billing } from "@opencode/cloud-core/billing.js" import { Key } from "@opencode/cloud-core/key.js" -import { action, createAsync, query, useAction, useSubmission, json } from "@solidjs/router" +import { action, createAsync, query, useAction, useSubmission, json, useParams } from "@solidjs/router" import { createSignal, For, Show } from "solid-js" import { withActor } from "~/context/auth.withActor" import { IconCopy, IconCheck } from "~/component/icon" @@ -13,23 +13,23 @@ import { Actor } from "@opencode/cloud-core/actor.js" ///////////////////////////////////// -const listKeys = query(async () => { +const listKeys = query(async (workspaceID: string) => { "use server" - return withActor(() => Key.list()) + return withActor(() => Key.list(), workspaceID) }, "key.list") -const createKey = action(async (name: string) => { +const createKey = action(async (workspaceID: string, name: string) => { "use server" return json( - withActor(() => Key.create({ name })), + withActor(() => Key.create({ name }), workspaceID), { revalidate: listKeys.key }, ) }, "key.create") -const removeKey = action(async (id: string) => { +const removeKey = action(async (workspaceID: string, id: string) => { "use server" return json( - withActor(() => Key.remove({ id })), + withActor(() => Key.remove({ id }), workspaceID), { revalidate: listKeys.key }, ) }, "key.remove") @@ -38,7 +38,7 @@ const removeKey = action(async (id: string) => { // Billing related queries and actions ///////////////////////////////////// -const getBillingInfo = query(async () => { +const getBillingInfo = query(async (workspaceID: string) => { "use server" return withActor(async () => { const actor = Actor.assert("user") @@ -51,25 +51,26 @@ const getBillingInfo = query(async () => { ]) console.log("duration", Date.now() - now) return { user, billing, payments, usage } - }) + }, workspaceID) }, "billingInfo") -const createCheckoutUrl = action(async (successUrl: string, cancelUrl: string) => { +const createCheckoutUrl = action(async (workspaceID: string, successUrl: string, cancelUrl: string) => { "use server" - return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl })) + return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl }), workspaceID) }, "checkoutUrl") -const createPortalUrl = action(async (returnUrl: string) => { +const createPortalUrl = action(async (workspaceID: string, returnUrl: string) => { "use server" - return withActor(() => Billing.generatePortalUrl({ returnUrl })) + return withActor(() => Billing.generatePortalUrl({ returnUrl }), workspaceID) }, "portalUrl") export default function () { + const params = useParams() ///////////////// // Keys section ///////////////// - const keys = createAsync(() => listKeys()) + const keys = createAsync(() => listKeys(params.id)) const createKeyAction = useAction(createKey) const removeKeyAction = useAction(removeKey) const createKeySubmission = useSubmission(createKey) @@ -134,7 +135,7 @@ export default function () { if (!keyName().trim()) return try { - await createKeyAction(keyName().trim()) + await createKeyAction(params.id, keyName().trim()) setKeyName("") setShowCreateForm(false) } catch (error) { @@ -148,7 +149,7 @@ export default function () { } try { - await removeKeyAction(keyId) + await removeKeyAction(params.id, keyId) } catch (error) { console.error("Failed to delete API key:", error) } @@ -157,16 +158,14 @@ export default function () { ///////////////// // Billing section ///////////////// - const billingInfo = createAsync(() => getBillingInfo(), { - deferStream: true, - }) + const billingInfo = createAsync(() => getBillingInfo(params.id)) const createCheckoutUrlAction = useAction(createCheckoutUrl) const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl) const handleBuyCredits = async () => { try { const baseUrl = window.location.href - const checkoutUrl = await createCheckoutUrlAction(baseUrl, baseUrl) + const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl) if (checkoutUrl) { window.location.href = checkoutUrl } diff --git a/cloud/app/src/routes/workspace/index.tsx b/cloud/app/src/routes/workspace/index.tsx new file mode 100644 index 00000000..e69de29b