From 522bed6b7dabd09328b3c8aae90b06ab06344623 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 20 Aug 2025 16:52:43 -0400 Subject: [PATCH] ignore: cloud stuff --- bun.lock | 1 + cloud/app/package.json | 3 +- cloud/app/src/app.tsx | 6 +- cloud/app/src/context/auth.tsx | 85 +++++++++++++++++++++++++- cloud/app/src/routes/[workspaceID].tsx | 15 +++++ cloud/app/src/routes/auth/callback.ts | 5 -- cloud/app/src/routes/index.tsx | 18 ++++++ cloud/app/sst-env.d.ts | 9 +++ cloud/core/src/actor.ts | 1 - cloud/core/src/drizzle/index.ts | 5 +- cloud/core/src/util/memo.ts | 11 ++++ cloud/function/src/auth.ts | 9 ++- cloud/function/sst-env.d.ts | 4 -- github/sst-env.d.ts | 9 +++ infra/app.ts | 6 +- infra/cloud.ts | 15 ++++- packages/function/sst-env.d.ts | 4 -- sst-env.d.ts | 4 -- 18 files changed, 180 insertions(+), 30 deletions(-) create mode 100644 cloud/app/src/routes/[workspaceID].tsx create mode 100644 cloud/app/sst-env.d.ts create mode 100644 cloud/core/src/util/memo.ts create mode 100644 github/sst-env.d.ts diff --git a/bun.lock b/bun.lock index ac509041..1fa7b54f 100644 --- a/bun.lock +++ b/bun.lock @@ -16,6 +16,7 @@ "dependencies": { "@ibm/plex": "6.4.1", "@openauthjs/openauth": "0.0.0-20250322224806", + "@opencode/cloud-core": "workspace:*", "@solidjs/meta": "^0.29.4", "@solidjs/router": "^0.15.0", "@solidjs/start": "^1.1.0", diff --git a/cloud/app/package.json b/cloud/app/package.json index acff2b66..358c163c 100644 --- a/cloud/app/package.json +++ b/cloud/app/package.json @@ -14,7 +14,8 @@ "@solidjs/router": "^0.15.0", "@solidjs/start": "^1.1.0", "solid-js": "^1.9.5", - "vinxi": "^0.5.7" + "vinxi": "^0.5.7", + "@opencode/cloud-core": "workspace:*" }, "engines": { "node": ">=22" diff --git a/cloud/app/src/app.tsx b/cloud/app/src/app.tsx index 04c569b9..50431899 100644 --- a/cloud/app/src/app.tsx +++ b/cloud/app/src/app.tsx @@ -1,7 +1,7 @@ import { MetaProvider, Title } from "@solidjs/meta"; import { Router } from "@solidjs/router"; import { FileRoutes } from "@solidjs/start/router"; -import { Suspense } from "solid-js"; +import { ErrorBoundary, Suspense } from "solid-js"; import "@ibm/plex/css/ibm-plex.css"; import "./app.css"; @@ -11,7 +11,9 @@ export default function App() { root={props => ( SolidStart - Basic - {props.children} + Something went wrong}> + {props.children} + )} > diff --git a/cloud/app/src/context/auth.tsx b/cloud/app/src/context/auth.tsx index bec94956..88d3214c 100644 --- a/cloud/app/src/context/auth.tsx +++ b/cloud/app/src/context/auth.tsx @@ -1,9 +1,90 @@ + + import { useSession } from "vinxi/http" import { createClient } from "@openauthjs/openauth/client" +import { getRequestEvent } from "solid-js/web" +import { and, Database, eq, inArray } from "@opencode/cloud-core/drizzle/index.js" +import { WorkspaceTable } from "@opencode/cloud-core/schema/workspace.sql.js" +import { UserTable } from "@opencode/cloud-core/schema/user.sql.js" +import { query, redirect } from "@solidjs/router" +import { AccountTable } from "@opencode/cloud-core/schema/account.sql.js" +import { Actor } from "@opencode/cloud-core/actor.js" + +export async function withActor(fn: () => T) { + const actor = await getActor() + return Actor.provide(actor.type, actor.properties, fn) +} + +export const getActor = query(async (): Promise => { + "use server" + const evt = getRequestEvent() + const url = new URL(evt!.request.headers.get("referer") ?? evt!.request.url) + const auth = await useAuthSession() + const [workspaceHint] = url.pathname.split("/").filter((x) => x.length > 0) + if (!workspaceHint) { + if (auth.data.current) { + const current = auth.data.account[auth.data.current] + return { + type: "account", + properties: { + email: current.email, + accountID: current.id, + }, + } + } + if (Object.keys(auth.data.account).length > 0) { + const current = Object.values(auth.data.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) + const result = await Database.transaction(async (tx) => { + return await tx.select({ + user: UserTable + }) + .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), + ) + ) + .limit(1) + .execute() + .then((x) => x[0]) + }) + if (result) { + return { + type: "user", + properties: { + userID: result.user.id, + workspaceID: result.user.workspaceID, + }, + } + } + throw redirect("/auth/authorize") +}, "actor") + export const AuthClient = createClient({ clientID: "app", - issuer: "https://auth.dev.opencode.ai", + issuer: import.meta.env.VITE_AUTH_URL, }) export interface AuthSession { @@ -15,7 +96,6 @@ export interface AuthSession { } export function useAuthSession() { - "use server" return useSession({ password: "0".repeat(32), @@ -26,3 +106,4 @@ export function useAuthSession() { export function AuthProvider() { } + diff --git a/cloud/app/src/routes/[workspaceID].tsx b/cloud/app/src/routes/[workspaceID].tsx new file mode 100644 index 00000000..706a6432 --- /dev/null +++ b/cloud/app/src/routes/[workspaceID].tsx @@ -0,0 +1,15 @@ +import { createAsync, query } from "@solidjs/router" +import { getActor, withActor } from "~/context/auth" + +const getPosts = query(async () => { + "use server" + return withActor(() => { + return "ok" + }) +}, "posts") + + +export default function () { + const actor = createAsync(async () => getActor()) + return
{JSON.stringify(actor())}
+} diff --git a/cloud/app/src/routes/auth/callback.ts b/cloud/app/src/routes/auth/callback.ts index a561c70d..22dcb2b6 100644 --- a/cloud/app/src/routes/auth/callback.ts +++ b/cloud/app/src/routes/auth/callback.ts @@ -5,11 +5,6 @@ export async function GET(input: APIEvent) { const url = new URL(input.request.url) const code = url.searchParams.get("code") if (!code) throw new Error("No code found") - const redirectURI = `${url.origin}${url.pathname}` - console.log({ - redirectURI, - code, - }) const result = await AuthClient.exchange(code, `${url.origin}${url.pathname}`) if (result.err) { throw new Error(result.err.message) diff --git a/cloud/app/src/routes/index.tsx b/cloud/app/src/routes/index.tsx index da4e2336..057ddb49 100644 --- a/cloud/app/src/routes/index.tsx +++ b/cloud/app/src/routes/index.tsx @@ -6,6 +6,9 @@ import IMG_SPLASH from "../asset/screenshot-splash.webp" import IMG_VSCODE from "../asset/screenshot-vscode.webp" import IMG_GITHUB from "../asset/screenshot-github.webp" import { IconCopy, IconCheck } from "../component/icon" +import { createAsync, query, redirect, RouteDefinition } from "@solidjs/router" +import { getActor, withActor } from "~/context/auth" +import { Account } from "@opencode/cloud-core/account.js" function CopyStatus() { return ( @@ -16,7 +19,22 @@ function CopyStatus() { ) } +const isLoggedIn = query(async () => { + "use server" + const actor = await getActor() + if (actor.type === "account") { + const workspaces = await withActor(() => Account.workspaces()) + throw redirect("/" + workspaces[0].id) + } + return +}, "isLoggedIn") + + + export default function Home() { + createAsync(() => isLoggedIn(), { + deferStream: true, + }) onMount(() => { const commands = document.querySelectorAll("[data-copy]") for (const button of commands) { diff --git a/cloud/app/sst-env.d.ts b/cloud/app/sst-env.d.ts new file mode 100644 index 00000000..b6a7e906 --- /dev/null +++ b/cloud/app/sst-env.d.ts @@ -0,0 +1,9 @@ +/* This file is auto-generated by SST. Do not edit. */ +/* tslint:disable */ +/* eslint-disable */ +/* deno-fmt-ignore-file */ + +/// + +import "sst" +export {} \ No newline at end of file diff --git a/cloud/core/src/actor.ts b/cloud/core/src/actor.ts index beb292bb..0d13f721 100644 --- a/cloud/core/src/actor.ts +++ b/cloud/core/src/actor.ts @@ -20,7 +20,6 @@ export namespace Actor { properties: { userID: string workspaceID: string - email: string } } diff --git a/cloud/core/src/drizzle/index.ts b/cloud/core/src/drizzle/index.ts index 76220f2a..46fe93ac 100644 --- a/cloud/core/src/drizzle/index.ts +++ b/cloud/core/src/drizzle/index.ts @@ -3,7 +3,7 @@ import { Resource } from "sst" export * from "drizzle-orm" import postgres from "postgres" -function createClient() { +const createClient = memo(() => { const client = postgres({ idle_timeout: 30000, connect_timeout: 30000, @@ -19,12 +19,13 @@ function createClient() { }) return drizzle(client, {}) -} +}) import { PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core" import type { ExtractTablesWithRelations } from "drizzle-orm" import type { PostgresJsQueryResultHKT } from "drizzle-orm/postgres-js" import { Context } from "../context" +import { memo } from "../util/memo" export namespace Database { export type Transaction = PgTransaction< diff --git a/cloud/core/src/util/memo.ts b/cloud/core/src/util/memo.ts new file mode 100644 index 00000000..3c84cf1f --- /dev/null +++ b/cloud/core/src/util/memo.ts @@ -0,0 +1,11 @@ +export function memo(fn: () => T) { + let value: T | undefined + let loaded = false + + return (): T => { + if (loaded) return value as T + loaded = true + value = fn() + return value as T + } +} diff --git a/cloud/function/src/auth.ts b/cloud/function/src/auth.ts index fe31651e..bbea4154 100644 --- a/cloud/function/src/auth.ts +++ b/cloud/function/src/auth.ts @@ -2,11 +2,12 @@ import { Resource } from "sst" import { z } from "zod" import { issuer } from "@openauthjs/openauth" import { createSubjects } from "@openauthjs/openauth/subject" -import { CodeProvider } from "@openauthjs/openauth/provider/code" import { GithubProvider } from "@openauthjs/openauth/provider/github" import { GoogleOidcProvider } from "@openauthjs/openauth/provider/google" import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare" import { Account } from "@opencode/cloud-core/account.js" +import { Workspace } from "@opencode/cloud-core/workspace.js" +import { Actor } from "@opencode/cloud-core/actor.js" type Env = { AuthStorage: KVNamespace @@ -117,6 +118,12 @@ export default { email: email!, }) } + await Actor.provide("account", { accountID, email }, async () => { + const workspaces = await Account.workspaces() + if (workspaces.length === 0) { + await Workspace.create() + } + }) return ctx.subject("account", accountID, { accountID, email }) }, }).fetch(request, env, ctx) diff --git a/cloud/function/sst-env.d.ts b/cloud/function/sst-env.d.ts index 520a033f..f60ec81a 100644 --- a/cloud/function/sst-env.d.ts +++ b/cloud/function/sst-env.d.ts @@ -14,10 +14,6 @@ declare module "sst" { "type": "sst.sst.Linkable" "value": string } - "Console": { - "type": "sst.cloudflare.StaticSite" - "url": string - } "DATABASE_PASSWORD": { "type": "sst.sst.Secret" "value": string diff --git a/github/sst-env.d.ts b/github/sst-env.d.ts new file mode 100644 index 00000000..f742a120 --- /dev/null +++ b/github/sst-env.d.ts @@ -0,0 +1,9 @@ +/* This file is auto-generated by SST. Do not edit. */ +/* tslint:disable */ +/* eslint-disable */ +/* deno-fmt-ignore-file */ + +/// + +import "sst" +export {} \ No newline at end of file diff --git a/infra/app.ts b/infra/app.ts index 008c1245..190ddbcf 100644 --- a/infra/app.ts +++ b/infra/app.ts @@ -25,9 +25,9 @@ export const api = new sst.cloudflare.Worker("Api", { ]) args.migrations = { // Note: when releasing the next tag, make sure all stages use tag v2 - oldTag: $app.stage === "production" ? "" : "v1", - newTag: $app.stage === "production" ? "" : "v1", - //newSqliteClasses: ["SyncServer"], + // oldTag: $app.stage === "production" ? "" : "v1", + // newTag: $app.stage === "production" ? "" : "v1", + newSqliteClasses: ["SyncServer"], } }, }, diff --git a/infra/cloud.ts b/infra/cloud.ts index 37fe35a0..d1ffb51e 100644 --- a/infra/cloud.ts +++ b/infra/cloud.ts @@ -10,7 +10,7 @@ const DATABASE_USERNAME = new sst.Secret("DATABASE_USERNAME") const DATABASE_PASSWORD = new sst.Secret("DATABASE_PASSWORD") export const database = new sst.Linkable("Database", { properties: { - host: "aws-us-east-2-1.pg.psdb.cloud", + host: `aws-us-east-2-${$app.stage === "thdxr" ? "2" : "1"}.pg.psdb.cloud`, database: "postgres", username: DATABASE_USERNAME.value, password: DATABASE_PASSWORD.value, @@ -106,6 +106,7 @@ export const gateway = new sst.cloudflare.Worker("GatewayApi", { // CONSOLE //////////////// +/* export const console = new sst.cloudflare.x.StaticSite("Console", { domain: `console.${domain}`, path: "cloud/web", @@ -119,3 +120,15 @@ export const console = new sst.cloudflare.x.StaticSite("Console", { VITE_AUTH_URL: auth.url.apply((url) => url!), }, }) +*/ + +new sst.x.DevCommand("Solid", { + link: [database], + dev: { + directory: "cloud/app", + command: "bun dev", + }, + environment: { + VITE_AUTH_URL: auth.url.apply((url) => url!), + }, +}) diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 520a033f..f60ec81a 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -14,10 +14,6 @@ declare module "sst" { "type": "sst.sst.Linkable" "value": string } - "Console": { - "type": "sst.cloudflare.StaticSite" - "url": string - } "DATABASE_PASSWORD": { "type": "sst.sst.Secret" "value": string diff --git a/sst-env.d.ts b/sst-env.d.ts index 3af3f82c..358891fd 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -27,10 +27,6 @@ declare module "sst" { "Bucket": { "type": "sst.cloudflare.Bucket" } - "Console": { - "type": "sst.cloudflare.StaticSite" - "url": string - } "DATABASE_PASSWORD": { "type": "sst.sst.Secret" "value": string