diff --git a/.env.example b/.env.example deleted file mode 100644 index 61ec987..0000000 --- a/.env.example +++ /dev/null @@ -1,10 +0,0 @@ -# Create a copy of this file and name it .env -# You can do so with the following command: -# cp .env.example .env -# -# The environment variables defined here should only be used during development -DATABASE_PASS=pass - -DATABASE_NAME=jotter - -AUTH_SECRET=secret \ No newline at end of file diff --git a/.gitignore b/.gitignore index 96fab4f..ba6f3d0 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ yarn-error.log* # Misc .DS_Store *.pem + +# SQLite +local.db diff --git a/apps/front-end/.env b/apps/front-end/.env index e38be8a..ace8267 100644 --- a/apps/front-end/.env +++ b/apps/front-end/.env @@ -1,8 +1,5 @@ NEXT_PUBLIC_COLLAB_SERVER_URL=ws://localhost:1234 -# Local development database -# This should match the same one configured for the collaboration server -DATABASE_URL=mysql://root:pass@localhost/jotter - +TURSO_DATABASE_URL=file:../../local.db NEXTAUTH_URL=http://localhost:3000 \ No newline at end of file diff --git a/apps/front-end/drizzle.config.ts b/apps/front-end/drizzle.config.ts index 298240c..ed415a8 100644 --- a/apps/front-end/drizzle.config.ts +++ b/apps/front-end/drizzle.config.ts @@ -2,9 +2,9 @@ import { defineConfig } from "drizzle-kit"; export default defineConfig({ schema: "./src/schema.ts", - driver: "mysql2", + driver: "libsql", out: "./drizzle", dbCredentials: { - uri: process.env.DATABASE_URL!, + url: process.env.TURSO_DATABASE_URL!, }, }); diff --git a/apps/front-end/package.json b/apps/front-end/package.json index 7a17548..d5e296d 100644 --- a/apps/front-end/package.json +++ b/apps/front-end/package.json @@ -3,15 +3,14 @@ "version": "0.1.0", "private": true, "scripts": { - "predev": "docker compose --env-file ../../.env up -d", "dev": "next dev", "dev:collab": "turbo run dev --filter=front-end --filter=collab-server", "build": "next build", "start": "next start", "lint": "next lint", "test:e2e": "playwright test", - "db:generate": "drizzle-kit generate:mysql", - "db:push": "drizzle-kit push:mysql" + "db:generate": "drizzle-kit generate:sqlite", + "db:push": "drizzle-kit push:sqlite" }, "dependencies": { "@auth/drizzle-adapter": "^0.7.0", @@ -24,6 +23,7 @@ "@lexical/selection": "^0.13.0", "@lexical/utils": "^0.13.0", "@lexical/yjs": "^0.13.0", + "@libsql/client": "^0.5.3", "@radix-ui/react-accordion": "^1.1.1", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-context-menu": "^2.1.3", @@ -40,13 +40,12 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "cmdk": "^0.2.0", - "drizzle-orm": "^0.29.3", + "drizzle-orm": "^0.29.5", "eslint": "^8.51.0", "eslint-config-next": "^14.1.1", "jose": "^5.2.2", "jotai": "^2.4.3", "lexical": "^0.13.0", - "mysql2": "^3.9.1", "nanoid": "^5.0.2", "next": "^14.1.1", "next-auth": "^4.24.5", diff --git a/apps/front-end/src/config/env-server.ts b/apps/front-end/src/config/env-server.ts index 381d6af..278a81a 100644 --- a/apps/front-end/src/config/env-server.ts +++ b/apps/front-end/src/config/env-server.ts @@ -8,7 +8,17 @@ const envSchema = z.object({ DISCORD_SECRET: z.string().min(1), DISCORD_CLIENT_ID: z.string().min(1), NEXTAUTH_SECRET: z.string().min(1), - DATABASE_URL: z.string().min(1), + TURSO_DATABASE_URL: z.string().min(1), + TURSO_AUTH_TOKEN: z + .string() + .min(1) + .optional() + .refine((token) => { + if (process.env.NODE_ENV === "production") { + return Boolean(token); + } + return true; + }, "A token is required to access production database."), NEXTAUTH_URL: z.string().min(1), }); diff --git a/apps/front-end/src/lib/db.ts b/apps/front-end/src/lib/db.ts index 733a099..a2c48a1 100644 --- a/apps/front-end/src/lib/db.ts +++ b/apps/front-end/src/lib/db.ts @@ -1,10 +1,13 @@ -import mysql from "mysql2/promise"; import env from "@/config/env-server"; -import { drizzle } from "drizzle-orm/mysql2"; import * as schema from "@/schema"; +import { createClient } from "@libsql/client"; +import { drizzle } from "drizzle-orm/libsql"; -const connection = await mysql.createConnection(env.DATABASE_URL); +const connection = await createClient({ + url: env.TURSO_DATABASE_URL, + authToken: env.TURSO_AUTH_TOKEN, +}); -const db = drizzle(connection, { mode: "planetscale", schema }); +const db = drizzle(connection, { schema }); export default db; diff --git a/apps/front-end/src/schema.ts b/apps/front-end/src/schema.ts index c857ec1..5ca4dd2 100644 --- a/apps/front-end/src/schema.ts +++ b/apps/front-end/src/schema.ts @@ -1,48 +1,35 @@ import type { AdapterAccount } from "@auth/core/adapters"; import { relations } from "drizzle-orm"; import { - char, - customType, - int, - mysqlTable, + blob, + integer, primaryKey, - timestamp, - varchar, -} from "drizzle-orm/mysql-core"; + sqliteTable, + text, +} from "drizzle-orm/sqlite-core"; -const blob = customType<{ data: Buffer }>({ - dataType() { - return "blob"; - }, -}); - -export const users = mysqlTable("user", { - id: varchar("id", { length: 255 }).notNull().primaryKey(), - name: varchar("name", { length: 255 }), - email: varchar("email", { length: 255 }), - emailVerified: timestamp("emailVerified", { - mode: "date", - fsp: 3, - }).defaultNow(), - image: varchar("image", { length: 255 }), +export const users = sqliteTable("user", { + id: text("id").notNull().primaryKey(), + name: text("name"), + email: text("email"), + emailVerified: integer("emailVerified", { mode: "timestamp_ms" }), + image: text("image"), }); -export const accounts = mysqlTable( +export const accounts = sqliteTable( "account", { - userId: varchar("userId", { length: 255 }).notNull(), - type: varchar("type", { length: 255 }) - .$type() - .notNull(), - provider: varchar("provider", { length: 255 }).notNull(), - providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(), - refresh_token: varchar("refresh_token", { length: 255 }), - access_token: varchar("access_token", { length: 255 }), - expires_at: int("expires_at"), - token_type: varchar("token_type", { length: 255 }), - scope: varchar("scope", { length: 255 }), - id_token: varchar("id_token", { length: 2048 }), - session_state: varchar("session_state", { length: 255 }), + userId: text("userId").notNull(), + type: text("type").$type().notNull(), + provider: text("provider").notNull(), + providerAccountId: text("providerAccountId").notNull(), + refresh_token: text("refresh_token"), + access_token: text("access_token"), + expires_at: integer("expires_at"), + token_type: text("token_type"), + scope: text("scope"), + id_token: text("id_token"), + session_state: text("session_state"), }, (account) => ({ compoundKey: primaryKey({ @@ -51,18 +38,18 @@ export const accounts = mysqlTable( }), ); -export const sessions = mysqlTable("session", { - sessionToken: varchar("sessionToken", { length: 255 }).notNull().primaryKey(), - userId: varchar("userId", { length: 255 }).notNull(), - expires: timestamp("expires", { mode: "date" }).notNull(), +export const sessions = sqliteTable("session", { + sessionToken: text("sessionToken").notNull().primaryKey(), + userId: text("userId").notNull(), + expires: integer("expires", { mode: "timestamp_ms" }).notNull(), }); -export const verificationTokens = mysqlTable( +export const verificationTokens = sqliteTable( "verificationToken", { - identifier: varchar("identifier", { length: 255 }).notNull(), - token: varchar("token", { length: 255 }).notNull(), - expires: timestamp("expires", { mode: "date" }).notNull(), + identifier: text("identifier").notNull(), + token: text("token").notNull(), + expires: integer("expires", { mode: "timestamp_ms" }).notNull(), }, (vt) => ({ compoundKey: primaryKey({ @@ -71,22 +58,22 @@ export const verificationTokens = mysqlTable( }), ); -export const documents = mysqlTable("document", { - name: char("name", { length: 21 }).primaryKey(), - createdOn: timestamp("createdOn", { mode: "date" }).defaultNow(), - modifiedOn: timestamp("modifiedOn", { mode: "date" }).defaultNow(), +export const documents = sqliteTable("document", { + name: text("name").primaryKey(), + createdOn: integer("createdOn", { mode: "timestamp_ms" }), + modifiedOn: integer("modifiedOn", { mode: "timestamp_ms" }), data: blob("data"), - notebookId: char("id", { length: 21 }).notNull(), + notebookId: text("id").notNull(), }); -export const notebooks = mysqlTable("notebook", { - id: char("id", { length: 21 }).notNull().primaryKey(), - authorId: varchar("authorId", { length: 255 }).notNull(), +export const notebooks = sqliteTable("notebook", { + id: text("id").notNull().primaryKey(), + authorId: text("authorId").notNull(), }); -export const notebookDocuments = mysqlTable("notebook_document", { - notebookId: char("notebookId", { length: 21 }).notNull().primaryKey(), - documentName: char("documentName", { length: 21 }).notNull(), +export const notebookDocuments = sqliteTable("notebook_document", { + notebookId: text("notebookId").notNull().primaryKey(), + documentName: text("documentName").notNull(), }); /******************************************************************************