From e366e66a69f040e4ce525d2c455a456eed536a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Jolt=20AI=20=E2=9A=A1=EF=B8=8F?= Date: Wed, 3 Jul 2024 11:51:32 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Commit=20from=20Jolt=20AI?= =?UTF-8?q?=20=E2=9A=A1=EF=B8=8F=20This=20commit=20was=20made=20by=20Jolt?= =?UTF-8?q?=20AI=20(https://jolt.multiple.dev)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Cloudflare D1 support (https://jolt.multiple.dev/tasks/f84b59e6-a9a3-45f3-b5d1-d445286df1fd) Description: Add D1 support for Unkey We should implement Cloudflare D1 support in the following ways: 1. Add Cloudflare D1 as a selectable option during the creation process. 2. Create a new folder in the extras section called 'd1'. 3. Add the necessary code to handle D1 database connections with Hono and Cloudflare workers in the new 'd1' folder. 4. Add the necessary code to use prisma or drizzle as ORM is required. This enhancement will expand database options for Unkey users and improve integration with Cloudflare services. --- src/cli/index.ts | 3 ++- src/helpers/installPackages.ts | 9 +++++++++ src/helpers/scaffoldProject.ts | 13 +++++++++++++ src/installers/drizzle.ts | 14 +++++++++++--- src/installers/index.ts | 2 +- src/installers/prisma.ts | 19 ++++++++++++++++++- templates/extras/d1/drizzle/database.ts | 8 ++++++++ templates/extras/d1/drizzle/schema.ts | 13 +++++++++++++ templates/extras/d1/prisma/database.ts | 11 +++++++++++ templates/extras/d1/prisma/schema.prisma | 14 ++++++++++++++ 10 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 templates/extras/d1/drizzle/database.ts create mode 100644 templates/extras/d1/drizzle/schema.ts create mode 100644 templates/extras/d1/prisma/database.ts create mode 100644 templates/extras/d1/prisma/schema.prisma diff --git a/src/cli/index.ts b/src/cli/index.ts index 8ab42c5..5f611e2 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -80,6 +80,7 @@ export const runCli = async (): Promise => { options: [ { value: "prisma", label: "Prisma" }, { value: "drizzle", label: "Drizzle" }, + { value: "d1", label: "D1" }, ], initialValue: "prisma", }); @@ -88,7 +89,7 @@ export const runCli = async (): Promise => { if (results.database === "none") return; return p.select({ message: "What database provider would you like to use?", - options: [{ value: "turso", label: "Turso" }], + options: [{ value: "turso", label: "Turso" }, { value: "d1", label: "D1" }], initialValue: "sqlite", }); }, diff --git a/src/helpers/installPackages.ts b/src/helpers/installPackages.ts index 0c68ecd..b3f5a25 100644 --- a/src/helpers/installPackages.ts +++ b/src/helpers/installPackages.ts @@ -1,6 +1,7 @@ import chalk from "chalk"; import ora from "ora"; +import { addPackageDependency } from "~/utils/addPackageDependency.js"; import { type InstallerOptions, type PkgInstallerMap, @@ -28,5 +29,13 @@ export const installPackages = (options: InstallPackagesOptions) => { } } + if (options.databaseProvider === 'd1') { + addPackageDependency({ + projectDir: options.projectDir, + dependencies: ['@cloudflare/d1', '@cloudflare/workers-types'], + devMode: true, + }); + } + console.info(""); }; diff --git a/src/helpers/scaffoldProject.ts b/src/helpers/scaffoldProject.ts index d40fcbd..ec3cef7 100644 --- a/src/helpers/scaffoldProject.ts +++ b/src/helpers/scaffoldProject.ts @@ -79,6 +79,19 @@ export const scaffoldProject = async ({ spinner.start(); fs.copySync(srcDir, projectDir); + + if (databaseProvider === "d1") { + const d1Dir = path.join(PKG_ROOT, "templates/extras/d1"); + if (packages.prisma.inUse) { + fs.copySync(path.join(d1Dir, "prisma/database.ts"), path.join(projectDir, "apps/api/src/database.ts")); + fs.copySync(path.join(d1Dir, "prisma/schema.prisma"), path.join(projectDir, "apps/api/prisma/schema.prisma")); + } + if (packages.drizzle.inUse) { + fs.copySync(path.join(d1Dir, "drizzle/database.ts"), path.join(projectDir, "apps/api/src/database.ts")); + fs.copySync(path.join(d1Dir, "drizzle/schema.ts"), path.join(projectDir, "apps/api/src/db/schema.ts")); + } + } + fs.renameSync( path.join(projectDir, "apps/api/_gitignore"), path.join(projectDir, "apps/api/.gitignore") diff --git a/src/installers/drizzle.ts b/src/installers/drizzle.ts index 1261553..7d8741a 100644 --- a/src/installers/drizzle.ts +++ b/src/installers/drizzle.ts @@ -32,7 +32,7 @@ export const drizzleInstaller: Installer = ({ dependencies: [ "drizzle-orm", ( - { + ({ turso: "@libsql/client", sqlite: "@libsql/client", } as const @@ -60,12 +60,17 @@ export const drizzleInstaller: Installer = ({ `,\n "packageManager": "${turboManager}" \n }` ); - const extrasDir = path.join(PKG_ROOT, "templates/extras"); - const configName = + + const configName = databaseProvider === "d1" + ? "drizzle-d1-config.ts" + : databaseProvider === "turso" databaseProvider === "turso" ? "drizzle-turso-config.ts" : "drizzle-config.ts"; const configFile = path.join(extrasDir, `/drizzle/config/${configName}`); + const databaseSrc = databaseProvider === "d1" + ? path.join(extrasDir, "d1/drizzle/database.ts") + : path.join(extrasDir, "drizzle/database.ts"); const configDest = path.join(projectDir, "apps/api/drizzle.config.ts"); const schemaName = databaseProvider === "turso" ? "turso.ts" : `base.ts`; const schemaSrc = path.join(extrasDir, "drizzle/db/", schemaName); @@ -105,6 +110,9 @@ export const drizzleInstaller: Installer = ({ if (databaseProvider === "turso") { fs.appendFileSync(envPath, `TURSO_DATABASE_URL="YOUR_DATABASE_URL_HERE"\n`); fs.appendFileSync(envPath, `TURSO_AUTH_TOKEN="YOUR_AUTH_TOKEN"\n`); + } else if (databaseProvider === "d1") { + fs.appendFileSync(envPath, `D1_DATABASE_URL="YOUR_D1_DATABASE_URL"\n`); + fs.appendFileSync(envPath, `D1_AUTH_TOKEN="YOUR_D1_AUTH_TOKEN"\n`); } else { fs.appendFileSync(envPath, `DATABASE_URL="file:./dev.db"`); } diff --git a/src/installers/index.ts b/src/installers/index.ts index 26d52ac..c8999c1 100644 --- a/src/installers/index.ts +++ b/src/installers/index.ts @@ -6,7 +6,7 @@ import { fumaInstaller } from "./fumadocs.js"; export const availablePackages = ["prisma", "drizzle", "docs"] as const; export type AvailablePackages = (typeof availablePackages)[number]; -export const databaseProviders = ["sqlite", "turso"] as const; +export const databaseProviders = ["sqlite", "turso", "d1"] as const; export type DatabaseProvider = (typeof databaseProviders)[number]; export interface InstallerOptions { diff --git a/src/installers/prisma.ts b/src/installers/prisma.ts index 0fbd2d8..f2c625d 100644 --- a/src/installers/prisma.ts +++ b/src/installers/prisma.ts @@ -62,6 +62,15 @@ export const prismaInstaller: Installer = ({ const schemaSrc = path.join( extrasDir, "prisma/schema", + `${databaseProvider === "d1" ? "d1" : "base"}${ + databaseProvider === "turso" ? "-turso" : "" + }.prisma` + ); + const databaseSrc = path.join( + extrasDir, + `d1/prisma/database.ts` + ); + const schemaSrc = path.join( `${"base"}${databaseProvider === "turso" ? "-turso" : ""}.prisma` ); let schemaText = fs.readFileSync(schemaSrc, "utf-8"); @@ -83,8 +92,13 @@ export const prismaInstaller: Installer = ({ const destination = path.join(projectDir, "apps/api/src/routes/posts.ts"); fs.copyFileSync(clientSrc, destination); // add postinstall and push script to package.json - const packageJsonPath = path.join(projectDir, "apps/api/package.json"); + if (databaseProvider === "d1") { + fs.copyFileSync(databaseSrc, databaseDest); + } else { + fs.copyFileSync(databaseSrc, databaseDest); + } + const packageJsonPath = path.join(projectDir, "apps/api/package.json"); const databaseSrc = path.join(extrasDir, "prisma/database.ts"); const databaseDest = path.join(projectDir, "apps/api/src/database.ts"); fs.copyFileSync(databaseSrc, databaseDest); @@ -111,6 +125,9 @@ export const prismaInstaller: Installer = ({ if (databaseProvider === "turso") { fs.appendFileSync(envPath, `TURSO_DATABASE_URL="YOUR_DATABASE_URL_HERE"\n`); fs.appendFileSync(envPath, `TURSO_AUTH_TOKEN="YOUR_AUTH_TOKEN"\n`); + } else if (databaseProvider === "d1") { + fs.appendFileSync(envPath, `D1_DATABASE_URL="YOUR_D1_DATABASE_URL"\n`); + fs.appendFileSync(envPath, `D1_AUTH_TOKEN="YOUR_D1_AUTH_TOKEN"\n`); } else { fs.appendFileSync(envPath, `DATABASE_URL="file:./dev.db"`); } diff --git a/templates/extras/d1/drizzle/database.ts b/templates/extras/d1/drizzle/database.ts new file mode 100644 index 0000000..514db58 --- /dev/null +++ b/templates/extras/d1/drizzle/database.ts @@ -0,0 +1,8 @@ +import { drizzle } from 'drizzle-orm/d1'; +import { D1Database } from '@cloudflare/d1'; +import { schema } from './schema'; + +export async function connectDatabase(db: D1Database) { + const drizzleInstance = drizzle(db, { schema }); + return drizzleInstance; +} diff --git a/templates/extras/d1/drizzle/schema.ts b/templates/extras/d1/drizzle/schema.ts new file mode 100644 index 0000000..45c3c20 --- /dev/null +++ b/templates/extras/d1/drizzle/schema.ts @@ -0,0 +1,13 @@ +import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; + +export const posts = sqliteTable('posts', { + id: integer('id', { mode: 'increment' }).primaryKey(), + title: text('title', { length: 256 }).notNull(), + post: text('post', { length: 256 }).notNull(), +}); + +export type Post = { + id: number; + title: string; + post: string; +}; diff --git a/templates/extras/d1/prisma/database.ts b/templates/extras/d1/prisma/database.ts new file mode 100644 index 0000000..5d44810 --- /dev/null +++ b/templates/extras/d1/prisma/database.ts @@ -0,0 +1,11 @@ +import { PrismaClient } from '@prisma/client'; + +export async function connectDatabase(env: { DATABASE_URL: string }) { + const { DATABASE_URL } = env; + + const prisma = new PrismaClient({ + datasources: { db: { url: DATABASE_URL } }, + }); + + return prisma; +} diff --git a/templates/extras/d1/prisma/schema.prisma b/templates/extras/d1/prisma/schema.prisma new file mode 100644 index 0000000..5d8f297 --- /dev/null +++ b/templates/extras/d1/prisma/schema.prisma @@ -0,0 +1,14 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("D1_DATABASE_URL") +} + +model Post { + id Int @id @default(autoincrement()) + title String @db.VarChar(255) + post String @db.Text +}