({
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