diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 2f28b36b0..995dbf19f 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -86,14 +86,6 @@ Thank you for your interest in contributing to OpenCut! This document provides g
# Or use an online generator: https://generate-secret.vercel.app/32
```
- **Optional Variables (for Google OAuth):**
-
- ```bash
- # Only needed if you want to test Google login
- GOOGLE_CLIENT_ID="your-google-client-id"
- GOOGLE_CLIENT_SECRET="your-google-client-secret"
- ```
-
5. Run database migrations: `bun run db:migrate`
6. Start the development server: `bun run dev`
diff --git a/README.md b/README.md
index 30fc43a60..6d3937a7f 100644
--- a/README.md
+++ b/README.md
@@ -124,14 +124,6 @@ Before you begin, ensure you have the following installed on your system:
# Or use an online generator: https://generate-secret.vercel.app/32
```
- **Optional Variables (for Google OAuth):**
-
- ```bash
- # Only needed if you want to test Google login
- GOOGLE_CLIENT_ID="your-google-client-id"
- GOOGLE_CLIENT_SECRET="your-google-client-secret"
- ```
-
5. Run database migrations: `bun run db:migrate` from (inside apps/web)
6. Start the development server: `bun run dev` from (inside apps/web)
@@ -155,6 +147,10 @@ We welcome contributions! Please see our [Contributing Guide](.github/CONTRIBUTI
Thanks to [Vercel](https://vercel.com?utm_source=github-opencut&utm_campaign=oss) for their support of open-source software.
+
+
+
+
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FOpenCut-app%2FOpenCut&project-name=opencut&repository-name=opencut)
## License
diff --git a/apps/web/.env.example b/apps/web/.env.example
index 834171a02..d63df851e 100644
--- a/apps/web/.env.example
+++ b/apps/web/.env.example
@@ -7,10 +7,6 @@ DATABASE_URL="postgresql://opencut:opencutthegoat@localhost:5432/opencut"
NEXT_PUBLIC_BETTER_AUTH_URL=http://localhost:3000
BETTER_AUTH_SECRET=your-secret-key-here
-#Google Clients
-GOOGLE_CLIENT_ID=
-GOOGLE_CLIENT_SECRET=
-
# Development Environment
NODE_ENV=development
diff --git a/apps/web/drizzle.config.ts b/apps/web/drizzle.config.ts
index 615eea570..87547bd04 100644
--- a/apps/web/drizzle.config.ts
+++ b/apps/web/drizzle.config.ts
@@ -1,23 +1,20 @@
import type { Config } from "drizzle-kit";
import * as dotenv from "dotenv";
+import { env } from "@/env";
// Load the right env file based on environment
-if (process.env.NODE_ENV === "production") {
+if (env.NODE_ENV === "production") {
dotenv.config({ path: ".env.production" });
} else {
dotenv.config({ path: ".env.local" });
}
-if (!process.env.DATABASE_URL) {
- throw new Error("DATABASE_URL is not set");
-}
-
export default {
schema: "../../packages/db/src/schema.ts",
dialect: "postgresql",
dbCredentials: {
- url: process.env.DATABASE_URL,
+ url: env.DATABASE_URL,
},
out: "./migrations",
- strict: process.env.NODE_ENV === "production",
+ strict: env.NODE_ENV === "production",
} satisfies Config;
diff --git a/apps/web/migrations/0002_cuddly_pretty_boy.sql b/apps/web/migrations/0002_cuddly_pretty_boy.sql
new file mode 100644
index 000000000..fc8aed796
--- /dev/null
+++ b/apps/web/migrations/0002_cuddly_pretty_boy.sql
@@ -0,0 +1 @@
+ALTER TABLE "users" ALTER COLUMN "email_verified" SET DEFAULT false;
\ No newline at end of file
diff --git a/apps/web/migrations/meta/0002_snapshot.json b/apps/web/migrations/meta/0002_snapshot.json
new file mode 100644
index 000000000..9155d9714
--- /dev/null
+++ b/apps/web/migrations/meta/0002_snapshot.json
@@ -0,0 +1,359 @@
+{
+ "id": "1b9148ed-497b-4e53-8cf6-f556c8fe7f7f",
+ "prevId": "b7d920ca-6dd0-430f-8ee6-1d38fdf3e80f",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.accounts": {
+ "name": "accounts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "accounts_user_id_users_id_fk": {
+ "name": "accounts_user_id_users_id_fk",
+ "tableFrom": "accounts",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.sessions": {
+ "name": "sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sessions_user_id_users_id_fk": {
+ "name": "sessions_user_id_users_id_fk",
+ "tableFrom": "sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sessions_token_unique": {
+ "name": "sessions_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.verifications": {
+ "name": "verifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ },
+ "public.waitlist": {
+ "name": "waitlist",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "waitlist_email_unique": {
+ "name": "waitlist_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": true
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json
index 9cbceb13c..309c040e0 100644
--- a/apps/web/migrations/meta/_journal.json
+++ b/apps/web/migrations/meta/_journal.json
@@ -15,6 +15,13 @@
"when": 1750689835736,
"tag": "0001_tricky_jackpot",
"breakpoints": true
+ },
+ {
+ "idx": 2,
+ "version": "7",
+ "when": 1752569400809,
+ "tag": "0002_cuddly_pretty_boy",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/apps/web/package.json b/apps/web/package.json
index 4e5018202..07b36dcb0 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -21,6 +21,8 @@
"@hookform/resolvers": "^3.9.1",
"@opencut/auth": "workspace:*",
"@opencut/db": "workspace:*",
+ "@t3-oss/env-core": "^0.13.8",
+ "@t3-oss/env-nextjs": "^0.13.8",
"@upstash/ratelimit": "^2.0.5",
"@upstash/redis": "^1.35.0",
"@vercel/analytics": "^1.4.1",
@@ -36,7 +38,7 @@
"input-otp": "^1.4.1",
"lucide-react": "^0.468.0",
"motion": "^12.18.1",
- "next": "^15.3.4",
+ "next": "^15.4.1",
"next-themes": "^0.4.4",
"pg": "^8.16.2",
"radix-ui": "^1.4.2",
@@ -45,6 +47,7 @@
"react-dom": "^18.2.0",
"react-hook-form": "^7.54.0",
"react-icons": "^5.4.0",
+ "react-markdown": "^10.1.0",
"react-phone-number-input": "^3.4.11",
"react-resizable-panels": "^2.1.7",
"recharts": "^2.14.1",
diff --git a/apps/web/public/opengraph-image.jpg b/apps/web/public/open-graph/default.jpg
similarity index 100%
rename from apps/web/public/opengraph-image.jpg
rename to apps/web/public/open-graph/default.jpg
diff --git a/apps/web/public/open-graph/roadmap.jpg b/apps/web/public/open-graph/roadmap.jpg
new file mode 100644
index 000000000..63374c4f6
Binary files /dev/null and b/apps/web/public/open-graph/roadmap.jpg differ
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 71716e74b..9ac54262f 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -4,7 +4,6 @@ import Script from "next/script";
import "./globals.css";
import { Toaster } from "../components/ui/sonner";
import { TooltipProvider } from "../components/ui/tooltip";
-import { DevelopmentDebug } from "../components/development-debug";
import { StorageProvider } from "../components/storage-provider";
import { baseMetaData } from "./metadata";
import { defaultFont } from "../lib/font-config";
@@ -24,7 +23,6 @@ export default function RootLayout({
{children}
-