summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-08-20 16:52:43 -0400
committerDax Raad <[email protected]>2025-08-20 17:01:18 -0400
commit522bed6b7dabd09328b3c8aae90b06ab06344623 (patch)
tree905dca3e2b3fb4e765d03f2ec78e285f19a31155
parentdda672284c69ad399029316a63677cf49cbc54f3 (diff)
downloadopencode-522bed6b7dabd09328b3c8aae90b06ab06344623.tar.gz
opencode-522bed6b7dabd09328b3c8aae90b06ab06344623.zip
ignore: cloud stuff
-rw-r--r--bun.lock1
-rw-r--r--cloud/app/package.json3
-rw-r--r--cloud/app/src/app.tsx6
-rw-r--r--cloud/app/src/context/auth.tsx85
-rw-r--r--cloud/app/src/routes/[workspaceID].tsx15
-rw-r--r--cloud/app/src/routes/auth/callback.ts5
-rw-r--r--cloud/app/src/routes/index.tsx18
-rw-r--r--cloud/app/sst-env.d.ts9
-rw-r--r--cloud/core/src/actor.ts1
-rw-r--r--cloud/core/src/drizzle/index.ts5
-rw-r--r--cloud/core/src/util/memo.ts11
-rw-r--r--cloud/function/src/auth.ts9
-rw-r--r--cloud/function/sst-env.d.ts4
-rw-r--r--github/sst-env.d.ts9
-rw-r--r--infra/app.ts6
-rw-r--r--infra/cloud.ts15
-rw-r--r--packages/function/sst-env.d.ts4
-rw-r--r--sst-env.d.ts4
18 files changed, 180 insertions, 30 deletions
diff --git a/bun.lock b/bun.lock
index ac5090413..1fa7b54fa 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 acff2b667..358c163c4 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 04c569b91..504318995 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 => (
<MetaProvider>
<Title>SolidStart - Basic</Title>
- <Suspense>{props.children}</Suspense>
+ <ErrorBoundary fallback={<div>Something went wrong</div>}>
+ <Suspense>{props.children}</Suspense>
+ </ErrorBoundary>
</MetaProvider>
)}
>
diff --git a/cloud/app/src/context/auth.tsx b/cloud/app/src/context/auth.tsx
index bec949568..88d3214c1 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<T>(fn: () => T) {
+ const actor = await getActor()
+ return Actor.provide(actor.type, actor.properties, fn)
+}
+
+export const getActor = query(async (): Promise<Actor.Info> => {
+ "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<AuthSession>({
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 000000000..706a64323
--- /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 <div>{JSON.stringify(actor())}</div>
+}
diff --git a/cloud/app/src/routes/auth/callback.ts b/cloud/app/src/routes/auth/callback.ts
index a561c70d0..22dcb2b6d 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 da4e23364..057ddb49e 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 000000000..b6a7e9066
--- /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 */
+
+/// <reference path="../../sst-env.d.ts" />
+
+import "sst"
+export {} \ No newline at end of file
diff --git a/cloud/core/src/actor.ts b/cloud/core/src/actor.ts
index beb292bb8..0d13f7216 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 76220f2a2..46fe93ac4 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 000000000..3c84cf1fb
--- /dev/null
+++ b/cloud/core/src/util/memo.ts
@@ -0,0 +1,11 @@
+export function memo<T>(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 fe31651e5..bbea41540 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 520a033ff..f60ec81a0 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 000000000..f742a1200
--- /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 */
+
+/// <reference path="../sst-env.d.ts" />
+
+import "sst"
+export {} \ No newline at end of file
diff --git a/infra/app.ts b/infra/app.ts
index 008c1245d..190ddbcf4 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 37fe35a0d..d1ffb51e0 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 520a033ff..f60ec81a0 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 3af3f82c0..358891fdf 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