wip: make less shit
This commit is contained in:
@@ -14,17 +14,14 @@ export const AuthClient = createClient({
|
|||||||
issuer: import.meta.env.VITE_AUTH_URL,
|
issuer: import.meta.env.VITE_AUTH_URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getActor = async (): Promise<Actor.Info> => {
|
export const getActor = async (workspace?: string): Promise<Actor.Info> => {
|
||||||
"use server"
|
"use server"
|
||||||
const evt = getRequestEvent()
|
const evt = getRequestEvent()
|
||||||
if (!evt) throw new Error("No request event")
|
if (!evt) throw new Error("No request event")
|
||||||
if (evt.locals.actor) return evt.locals.actor
|
if (evt.locals.actor) return evt.locals.actor
|
||||||
evt.locals.actor = (async () => {
|
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 auth = await useAuthSession()
|
||||||
const splits = url.pathname.split("/").filter(Boolean)
|
if (!workspace) {
|
||||||
if (splits[0] !== "workspace") {
|
|
||||||
const account = auth.data.account ?? {}
|
const account = auth.data.account ?? {}
|
||||||
const current = account[auth.data.current ?? ""]
|
const current = account[auth.data.current ?? ""]
|
||||||
if (current) {
|
if (current) {
|
||||||
@@ -55,7 +52,6 @@ export const getActor = async (): Promise<Actor.Info> => {
|
|||||||
properties: {},
|
properties: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const workspaceHint = splits[1]
|
|
||||||
const accounts = Object.keys(auth.data.account ?? {})
|
const accounts = Object.keys(auth.data.account ?? {})
|
||||||
if (accounts.length) {
|
if (accounts.length) {
|
||||||
const result = await Database.transaction(async (tx) => {
|
const result = await Database.transaction(async (tx) => {
|
||||||
@@ -66,7 +62,7 @@ export const getActor = async (): Promise<Actor.Info> => {
|
|||||||
.from(AccountTable)
|
.from(AccountTable)
|
||||||
.innerJoin(UserTable, and(eq(UserTable.email, AccountTable.email)))
|
.innerJoin(UserTable, and(eq(UserTable.email, AccountTable.email)))
|
||||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
|
.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)
|
.limit(1)
|
||||||
.execute()
|
.execute()
|
||||||
.then((x) => x[0])
|
.then((x) => x[0])
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { Actor } from "@opencode/cloud-core/actor.js"
|
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||||
import { getActor } from "./auth"
|
import { getActor } from "./auth"
|
||||||
import { getRequestEvent } from "solid-js/web"
|
|
||||||
|
|
||||||
export async function withActor<T>(fn: () => T) {
|
export async function withActor<T>(fn: () => T, workspace?: string) {
|
||||||
const actor = await getActor()
|
const actor = await getActor(workspace)
|
||||||
return Actor.provide(actor.type, actor.properties, fn)
|
return Actor.provide(actor.type, actor.properties, fn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import IMG_SPLASH from "../asset/lander/screenshot-splash.png"
|
|||||||
import IMG_VSCODE from "../asset/lander/screenshot-vscode.png"
|
import IMG_VSCODE from "../asset/lander/screenshot-vscode.png"
|
||||||
import IMG_GITHUB from "../asset/lander/screenshot-github.png"
|
import IMG_GITHUB from "../asset/lander/screenshot-github.png"
|
||||||
import { IconCopy, IconCheck } from "../component/icon"
|
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 { getActor } from "~/context/auth"
|
||||||
import { withActor } from "~/context/auth.withActor"
|
import { withActor } from "~/context/auth.withActor"
|
||||||
import { Account } from "@opencode/cloud-core/account.js"
|
import { Account } from "@opencode/cloud-core/account.js"
|
||||||
@@ -21,20 +21,17 @@ function CopyStatus() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLoggedIn = query(async () => {
|
const defaultWorkspace = query(async () => {
|
||||||
"use server"
|
"use server"
|
||||||
const actor = await getActor()
|
const actor = await getActor()
|
||||||
if (actor.type === "account") {
|
if (actor.type === "account") {
|
||||||
const workspaces = await withActor(() => Account.workspaces())
|
const workspaces = await withActor(() => Account.workspaces())
|
||||||
return workspaces[0].id
|
return workspaces[0].id
|
||||||
// throw redirect(`/workspace/${workspaces[0].id}`)
|
|
||||||
}
|
}
|
||||||
}, "isLoggedIn")
|
}, "defaultWorkspace")
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const auth = createAsync(() => isLoggedIn(), {
|
const workspace = createAsync(() => defaultWorkspace())
|
||||||
deferStream: true,
|
|
||||||
})
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const commands = document.querySelectorAll("[data-copy]")
|
const commands = document.querySelectorAll("[data-copy]")
|
||||||
for (const button of commands) {
|
for (const button of commands) {
|
||||||
@@ -90,9 +87,9 @@ export default function Home() {
|
|||||||
</a>
|
</a>
|
||||||
<span data-slot="description">, a curated list of models provided by opencode</span>
|
<span data-slot="description">, a curated list of models provided by opencode</span>
|
||||||
<span data-slot="divider"> / </span>
|
<span data-slot="divider"> / </span>
|
||||||
<a target="_self" data-slot="cta" href="/auth">
|
<A href={workspace() ? "/workspace/" + workspace() : "/auth/authorize"}>
|
||||||
{auth() ? "Dashboard" : "Sign in"}
|
{workspace() ? "Dashboard" : "Sign in"}
|
||||||
</a>
|
</A>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section data-component="features">
|
<section data-component="features">
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ import { useAuthSession } from "~/context/auth.session"
|
|||||||
import { IconLogo } from "../component/icon"
|
import { IconLogo } from "../component/icon"
|
||||||
import { withActor } from "~/context/auth.withActor"
|
import { withActor } from "~/context/auth.withActor"
|
||||||
import "./workspace.css"
|
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 { User } from "@opencode/cloud-core/user.js"
|
||||||
import { Actor } from "@opencode/cloud-core/actor.js"
|
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||||
|
|
||||||
const getUserInfo = query(async () => {
|
const getUserInfo = query(async (workspaceID: string) => {
|
||||||
"use server"
|
"use server"
|
||||||
return withActor(async () => {
|
return withActor(async () => {
|
||||||
const actor = Actor.assert("user")
|
const actor = Actor.assert("user")
|
||||||
const user = await User.fromID(actor.properties.userID)
|
const user = await User.fromID(actor.properties.userID)
|
||||||
return { user }
|
return { user }
|
||||||
})
|
}, workspaceID)
|
||||||
}, "userInfo")
|
}, "userInfo")
|
||||||
|
|
||||||
const logout = action(async () => {
|
const logout = action(async () => {
|
||||||
@@ -30,16 +30,15 @@ const logout = action(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
export default function WorkspaceLayout(props: RouteSectionProps) {
|
export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||||
const userInfo = createAsync(() => getUserInfo(), {
|
const params = useParams()
|
||||||
deferStream: true,
|
const userInfo = createAsync(() => getUserInfo(params.id))
|
||||||
})
|
|
||||||
return (
|
return (
|
||||||
<main data-page="workspace">
|
<main data-page="workspace">
|
||||||
<header data-component="workspace-header">
|
<header data-component="workspace-header">
|
||||||
<div data-slot="header-brand">
|
<div data-slot="header-brand">
|
||||||
<a href="/" data-component="site-title">
|
<A href="/" data-component="site-title">
|
||||||
<IconLogo />
|
<IconLogo />
|
||||||
</a>
|
</A>
|
||||||
</div>
|
</div>
|
||||||
<div data-slot="header-actions">
|
<div data-slot="header-actions">
|
||||||
<span>{userInfo()?.user.email}</span>
|
<span>{userInfo()?.user.email}</span>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import "./[id].css"
|
import "./[id].css"
|
||||||
import { Billing } from "@opencode/cloud-core/billing.js"
|
import { Billing } from "@opencode/cloud-core/billing.js"
|
||||||
import { Key } from "@opencode/cloud-core/key.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 { createSignal, For, Show } from "solid-js"
|
||||||
import { withActor } from "~/context/auth.withActor"
|
import { withActor } from "~/context/auth.withActor"
|
||||||
import { IconCopy, IconCheck } from "~/component/icon"
|
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"
|
"use server"
|
||||||
return withActor(() => Key.list())
|
return withActor(() => Key.list(), workspaceID)
|
||||||
}, "key.list")
|
}, "key.list")
|
||||||
|
|
||||||
const createKey = action(async (name: string) => {
|
const createKey = action(async (workspaceID: string, name: string) => {
|
||||||
"use server"
|
"use server"
|
||||||
return json(
|
return json(
|
||||||
withActor(() => Key.create({ name })),
|
withActor(() => Key.create({ name }), workspaceID),
|
||||||
{ revalidate: listKeys.key },
|
{ revalidate: listKeys.key },
|
||||||
)
|
)
|
||||||
}, "key.create")
|
}, "key.create")
|
||||||
|
|
||||||
const removeKey = action(async (id: string) => {
|
const removeKey = action(async (workspaceID: string, id: string) => {
|
||||||
"use server"
|
"use server"
|
||||||
return json(
|
return json(
|
||||||
withActor(() => Key.remove({ id })),
|
withActor(() => Key.remove({ id }), workspaceID),
|
||||||
{ revalidate: listKeys.key },
|
{ revalidate: listKeys.key },
|
||||||
)
|
)
|
||||||
}, "key.remove")
|
}, "key.remove")
|
||||||
@@ -38,7 +38,7 @@ const removeKey = action(async (id: string) => {
|
|||||||
// Billing related queries and actions
|
// Billing related queries and actions
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
|
|
||||||
const getBillingInfo = query(async () => {
|
const getBillingInfo = query(async (workspaceID: string) => {
|
||||||
"use server"
|
"use server"
|
||||||
return withActor(async () => {
|
return withActor(async () => {
|
||||||
const actor = Actor.assert("user")
|
const actor = Actor.assert("user")
|
||||||
@@ -51,25 +51,26 @@ const getBillingInfo = query(async () => {
|
|||||||
])
|
])
|
||||||
console.log("duration", Date.now() - now)
|
console.log("duration", Date.now() - now)
|
||||||
return { user, billing, payments, usage }
|
return { user, billing, payments, usage }
|
||||||
})
|
}, workspaceID)
|
||||||
}, "billingInfo")
|
}, "billingInfo")
|
||||||
|
|
||||||
const createCheckoutUrl = action(async (successUrl: string, cancelUrl: string) => {
|
const createCheckoutUrl = action(async (workspaceID: string, successUrl: string, cancelUrl: string) => {
|
||||||
"use server"
|
"use server"
|
||||||
return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl }))
|
return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl }), workspaceID)
|
||||||
}, "checkoutUrl")
|
}, "checkoutUrl")
|
||||||
|
|
||||||
const createPortalUrl = action(async (returnUrl: string) => {
|
const createPortalUrl = action(async (workspaceID: string, returnUrl: string) => {
|
||||||
"use server"
|
"use server"
|
||||||
return withActor(() => Billing.generatePortalUrl({ returnUrl }))
|
return withActor(() => Billing.generatePortalUrl({ returnUrl }), workspaceID)
|
||||||
}, "portalUrl")
|
}, "portalUrl")
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
|
const params = useParams()
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
// Keys section
|
// Keys section
|
||||||
/////////////////
|
/////////////////
|
||||||
const keys = createAsync(() => listKeys())
|
const keys = createAsync(() => listKeys(params.id))
|
||||||
const createKeyAction = useAction(createKey)
|
const createKeyAction = useAction(createKey)
|
||||||
const removeKeyAction = useAction(removeKey)
|
const removeKeyAction = useAction(removeKey)
|
||||||
const createKeySubmission = useSubmission(createKey)
|
const createKeySubmission = useSubmission(createKey)
|
||||||
@@ -134,7 +135,7 @@ export default function () {
|
|||||||
if (!keyName().trim()) return
|
if (!keyName().trim()) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createKeyAction(keyName().trim())
|
await createKeyAction(params.id, keyName().trim())
|
||||||
setKeyName("")
|
setKeyName("")
|
||||||
setShowCreateForm(false)
|
setShowCreateForm(false)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -148,7 +149,7 @@ export default function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await removeKeyAction(keyId)
|
await removeKeyAction(params.id, keyId)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to delete API key:", error)
|
console.error("Failed to delete API key:", error)
|
||||||
}
|
}
|
||||||
@@ -157,16 +158,14 @@ export default function () {
|
|||||||
/////////////////
|
/////////////////
|
||||||
// Billing section
|
// Billing section
|
||||||
/////////////////
|
/////////////////
|
||||||
const billingInfo = createAsync(() => getBillingInfo(), {
|
const billingInfo = createAsync(() => getBillingInfo(params.id))
|
||||||
deferStream: true,
|
|
||||||
})
|
|
||||||
const createCheckoutUrlAction = useAction(createCheckoutUrl)
|
const createCheckoutUrlAction = useAction(createCheckoutUrl)
|
||||||
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
|
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
|
||||||
|
|
||||||
const handleBuyCredits = async () => {
|
const handleBuyCredits = async () => {
|
||||||
try {
|
try {
|
||||||
const baseUrl = window.location.href
|
const baseUrl = window.location.href
|
||||||
const checkoutUrl = await createCheckoutUrlAction(baseUrl, baseUrl)
|
const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl)
|
||||||
if (checkoutUrl) {
|
if (checkoutUrl) {
|
||||||
window.location.href = checkoutUrl
|
window.location.href = checkoutUrl
|
||||||
}
|
}
|
||||||
|
|||||||
0
cloud/app/src/routes/workspace/index.tsx
Normal file
0
cloud/app/src/routes/workspace/index.tsx
Normal file
Reference in New Issue
Block a user