summaryrefslogtreecommitdiffhomepage
path: root/cloud/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'cloud/core/src')
-rw-r--r--cloud/core/src/billing.ts4
-rw-r--r--cloud/core/src/drizzle/index.ts58
-rw-r--r--cloud/core/src/drizzle/types.ts8
-rw-r--r--cloud/core/src/key.ts46
-rw-r--r--cloud/core/src/schema/account.sql.ts4
-rw-r--r--cloud/core/src/schema/billing.sql.ts18
-rw-r--r--cloud/core/src/schema/key.sql.ts4
-rw-r--r--cloud/core/src/schema/user.sql.ts8
-rw-r--r--cloud/core/src/schema/workspace.sql.ts8
-rw-r--r--cloud/core/src/util/memo.ts11
10 files changed, 73 insertions, 96 deletions
diff --git a/cloud/core/src/billing.ts b/cloud/core/src/billing.ts
index 620a72212..ee44a24ef 100644
--- a/cloud/core/src/billing.ts
+++ b/cloud/core/src/billing.ts
@@ -80,14 +80,12 @@ export namespace Billing {
cacheWriteTokens: input.cacheWriteTokens,
cost,
})
- const [updated] = await tx
+ await tx
.update(BillingTable)
.set({
balance: sql`${BillingTable.balance} - ${cost}`,
})
.where(eq(BillingTable.workspaceID, workspaceID))
- .returning()
- return updated.balance
})
},
)
diff --git a/cloud/core/src/drizzle/index.ts b/cloud/core/src/drizzle/index.ts
index 23441928e..806037996 100644
--- a/cloud/core/src/drizzle/index.ts
+++ b/cloud/core/src/drizzle/index.ts
@@ -1,41 +1,33 @@
-import { drizzle } from "drizzle-orm/postgres-js"
+import { drizzle } from "drizzle-orm/planetscale-serverless"
import { Resource } from "@opencode/cloud-resource"
export * from "drizzle-orm"
-import postgres from "postgres"
+import { Client } from "@planetscale/database"
-const init = () => {
- const client = postgres({
- idle_timeout: 30000,
- connect_timeout: 30000,
- host: Resource.Database.host,
- database: Resource.Database.database,
- user: Resource.Database.username,
- password: Resource.Database.password,
- port: Resource.Database.port,
- ssl: {
- rejectUnauthorized: false,
- },
- max: 6,
- })
- return drizzle(client, {})
-}
-
-const createClient = init
-
-import { PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core"
+import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core"
import type { ExtractTablesWithRelations } from "drizzle-orm"
-import type { PostgresJsQueryResultHKT } from "drizzle-orm/postgres-js"
+import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless"
import { Context } from "../context"
import { memo } from "../util/memo"
export namespace Database {
- export type Transaction = PgTransaction<
- PostgresJsQueryResultHKT,
- Record<string, unknown>,
- ExtractTablesWithRelations<Record<string, unknown>>
+ export type Transaction = MySqlTransaction<
+ PlanetscaleQueryResultHKT,
+ PlanetScalePreparedQueryHKT,
+ Record<string, never>,
+ ExtractTablesWithRelations<Record<string, never>>
>
- export type TxOrDb = Transaction | ReturnType<typeof createClient>
+ const client = memo(() => {
+ const result = new Client({
+ host: Resource.Database.host,
+ username: Resource.Database.username,
+ password: Resource.Database.password,
+ })
+ const db = drizzle(result, {})
+ return db
+ })
+
+ export type TxOrDb = Transaction | ReturnType<typeof client>
const TransactionContext = Context.create<{
tx: TxOrDb
@@ -48,14 +40,13 @@ export namespace Database {
return tx.transaction(callback)
} catch (err) {
if (err instanceof Context.NotFound) {
- const client = createClient()
const effects: (() => void | Promise<void>)[] = []
const result = await TransactionContext.provide(
{
effects,
- tx: client,
+ tx: client(),
},
- () => callback(client),
+ () => callback(client()),
)
await Promise.all(effects.map((x) => x()))
return result
@@ -76,15 +67,14 @@ export namespace Database {
}
}
- export async function transaction<T>(callback: (tx: TxOrDb) => Promise<T>, config?: PgTransactionConfig) {
+ export async function transaction<T>(callback: (tx: TxOrDb) => Promise<T>, config?: MySqlTransactionConfig) {
try {
const { tx } = TransactionContext.use()
return callback(tx)
} catch (err) {
if (err instanceof Context.NotFound) {
- const client = createClient()
const effects: (() => void | Promise<void>)[] = []
- const result = await client.transaction(async (tx) => {
+ const result = await client().transaction(async (tx) => {
return TransactionContext.provide({ tx, effects }, () => callback(tx))
}, config)
await Promise.all(effects.map((x) => x()))
diff --git a/cloud/core/src/drizzle/types.ts b/cloud/core/src/drizzle/types.ts
index 5ae95d011..f16ad5a8a 100644
--- a/cloud/core/src/drizzle/types.ts
+++ b/cloud/core/src/drizzle/types.ts
@@ -1,4 +1,5 @@
-import { bigint, timestamp, varchar } from "drizzle-orm/pg-core"
+import { sql } from "drizzle-orm"
+import { bigint, timestamp, varchar } from "drizzle-orm/mysql-core"
export const ulid = (name: string) => varchar(name, { length: 30 })
@@ -15,7 +16,7 @@ export const id = () => ulid("id").notNull()
export const utc = (name: string) =>
timestamp(name, {
- withTimezone: true,
+ fsp: 3,
})
export const currency = (name: string) =>
@@ -25,5 +26,8 @@ export const currency = (name: string) =>
export const timestamps = {
timeCreated: utc("time_created").notNull().defaultNow(),
+ timeUpdated: utc("time_updated")
+ .notNull()
+ .default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
timeDeleted: utc("time_deleted"),
}
diff --git a/cloud/core/src/key.ts b/cloud/core/src/key.ts
index cf4f6e410..b195bd7c9 100644
--- a/cloud/core/src/key.ts
+++ b/cloud/core/src/key.ts
@@ -36,44 +36,26 @@ export namespace Key {
randomPart += chars.charAt(Math.floor(Math.random() * chars.length))
}
const secretKey = `sk-${randomPart}`
-
- const keyRecord = await Database.use((tx) =>
- tx
- .insert(KeyTable)
- .values({
- id: Identifier.create("key"),
- workspaceID: user.properties.workspaceID,
- userID: user.properties.userID,
- name,
- key: secretKey,
- timeUsed: null,
- })
- .returning(),
+ const keyID = Identifier.create("key")
+
+ await Database.use((tx) =>
+ tx.insert(KeyTable).values({
+ id: keyID,
+ workspaceID: user.properties.workspaceID,
+ userID: user.properties.userID,
+ name,
+ key: secretKey,
+ timeUsed: null,
+ }),
)
- return {
- key: secretKey,
- id: keyRecord[0].id,
- name: keyRecord[0].name,
- created: keyRecord[0].timeCreated,
- }
+ return keyID
})
export const remove = fn(z.object({ id: z.string() }), async (input) => {
const user = Actor.assert("user")
- const { id } = input
-
- const result = await Database.use((tx) =>
- tx
- .delete(KeyTable)
- .where(and(eq(KeyTable.id, id), eq(KeyTable.workspaceID, user.properties.workspaceID)))
- .returning({ id: KeyTable.id }),
+ await Database.use((tx) =>
+ tx.delete(KeyTable).where(and(eq(KeyTable.id, input.id), eq(KeyTable.workspaceID, user.properties.workspaceID))),
)
-
- if (result.length === 0) {
- throw new Error("Key not found")
- }
-
- return { id: result[0].id }
})
}
diff --git a/cloud/core/src/schema/account.sql.ts b/cloud/core/src/schema/account.sql.ts
index 1733f0a15..4d9937114 100644
--- a/cloud/core/src/schema/account.sql.ts
+++ b/cloud/core/src/schema/account.sql.ts
@@ -1,7 +1,7 @@
-import { pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core"
+import { mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
import { id, timestamps } from "../drizzle/types"
-export const AccountTable = pgTable(
+export const AccountTable = mysqlTable(
"account",
{
id: id(),
diff --git a/cloud/core/src/schema/billing.sql.ts b/cloud/core/src/schema/billing.sql.ts
index 96b29f5de..eff1f6550 100644
--- a/cloud/core/src/schema/billing.sql.ts
+++ b/cloud/core/src/schema/billing.sql.ts
@@ -1,8 +1,8 @@
-import { bigint, boolean, integer, pgTable, varchar } from "drizzle-orm/pg-core"
+import { bigint, boolean, int, mysqlTable, varchar } from "drizzle-orm/mysql-core"
import { timestamps, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
-export const BillingTable = pgTable(
+export const BillingTable = mysqlTable(
"billing",
{
...workspaceColumns,
@@ -16,7 +16,7 @@ export const BillingTable = pgTable(
(table) => [...workspaceIndexes(table)],
)
-export const PaymentTable = pgTable(
+export const PaymentTable = mysqlTable(
"payment",
{
...workspaceColumns,
@@ -28,17 +28,17 @@ export const PaymentTable = pgTable(
(table) => [...workspaceIndexes(table)],
)
-export const UsageTable = pgTable(
+export const UsageTable = mysqlTable(
"usage",
{
...workspaceColumns,
...timestamps,
model: varchar("model", { length: 255 }).notNull(),
- inputTokens: integer("input_tokens").notNull(),
- outputTokens: integer("output_tokens").notNull(),
- reasoningTokens: integer("reasoning_tokens"),
- cacheReadTokens: integer("cache_read_tokens"),
- cacheWriteTokens: integer("cache_write_tokens"),
+ inputTokens: int("input_tokens").notNull(),
+ outputTokens: int("output_tokens").notNull(),
+ reasoningTokens: int("reasoning_tokens"),
+ cacheReadTokens: int("cache_read_tokens"),
+ cacheWriteTokens: int("cache_write_tokens"),
cost: bigint("cost", { mode: "number" }).notNull(),
},
(table) => [...workspaceIndexes(table)],
diff --git a/cloud/core/src/schema/key.sql.ts b/cloud/core/src/schema/key.sql.ts
index 240736b86..200e829cb 100644
--- a/cloud/core/src/schema/key.sql.ts
+++ b/cloud/core/src/schema/key.sql.ts
@@ -1,8 +1,8 @@
-import { text, pgTable, varchar, uniqueIndex } from "drizzle-orm/pg-core"
+import { text, mysqlTable, varchar, uniqueIndex } from "drizzle-orm/mysql-core"
import { timestamps, utc, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
-export const KeyTable = pgTable(
+export const KeyTable = mysqlTable(
"key",
{
...workspaceColumns,
diff --git a/cloud/core/src/schema/user.sql.ts b/cloud/core/src/schema/user.sql.ts
index 34cbd6beb..00c372d1a 100644
--- a/cloud/core/src/schema/user.sql.ts
+++ b/cloud/core/src/schema/user.sql.ts
@@ -1,16 +1,16 @@
-import { text, pgTable, uniqueIndex, varchar, integer } from "drizzle-orm/pg-core"
+import { text, mysqlTable, uniqueIndex, varchar, int } from "drizzle-orm/mysql-core"
import { timestamps, utc, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
-export const UserTable = pgTable(
+export const UserTable = mysqlTable(
"user",
{
...workspaceColumns,
...timestamps,
- email: text("email").notNull(),
+ email: varchar("email", { length: 255 }).notNull(),
name: varchar("name", { length: 255 }).notNull(),
timeSeen: utc("time_seen"),
- color: integer("color"),
+ color: int("color"),
},
(table) => [...workspaceIndexes(table), uniqueIndex("user_email").on(table.workspaceID, table.email)],
)
diff --git a/cloud/core/src/schema/workspace.sql.ts b/cloud/core/src/schema/workspace.sql.ts
index 3e9379e1f..979255428 100644
--- a/cloud/core/src/schema/workspace.sql.ts
+++ b/cloud/core/src/schema/workspace.sql.ts
@@ -1,7 +1,7 @@
-import { primaryKey, foreignKey, pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core"
+import { primaryKey, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
import { timestamps, ulid } from "../drizzle/types"
-export const WorkspaceTable = pgTable(
+export const WorkspaceTable = mysqlTable(
"workspace",
{
id: ulid("id").notNull().primaryKey(),
@@ -17,9 +17,5 @@ export function workspaceIndexes(table: any) {
primaryKey({
columns: [table.workspaceID, table.id],
}),
- foreignKey({
- foreignColumns: [WorkspaceTable.id],
- columns: [table.workspaceID],
- }),
]
}
diff --git a/cloud/core/src/util/memo.ts b/cloud/core/src/util/memo.ts
index 3c84cf1fb..49043010f 100644
--- a/cloud/core/src/util/memo.ts
+++ b/cloud/core/src/util/memo.ts
@@ -1,11 +1,18 @@
-export function memo<T>(fn: () => T) {
+export function memo<T>(fn: () => T, cleanup?: (input: T) => Promise<void>) {
let value: T | undefined
let loaded = false
- return (): T => {
+ const result = (): T => {
if (loaded) return value as T
loaded = true
value = fn()
return value as T
}
+ result.reset = async () => {
+ if (cleanup && value) await cleanup(value)
+ loaded = false
+ value = undefined
+ }
+
+ return result
}