abdellah0l-stack 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +50 -0
  2. package/bin/cli.js +218 -0
  3. package/package.json +30 -0
  4. package/template/README.md +61 -0
  5. package/template/components.json +22 -0
  6. package/template/drizzle.config.ts +13 -0
  7. package/template/eslint.config.mjs +25 -0
  8. package/template/next.config.ts +114 -0
  9. package/template/package.json +62 -0
  10. package/template/postcss.config.mjs +7 -0
  11. package/template/public/file.svg +1 -0
  12. package/template/public/globe.svg +1 -0
  13. package/template/public/next.svg +1 -0
  14. package/template/public/vercel.svg +1 -0
  15. package/template/public/window.svg +1 -0
  16. package/template/src/app/api/v1/auth/[...all]/route.ts +5 -0
  17. package/template/src/app/api/v1/trpc/[trpc]/route.ts +13 -0
  18. package/template/src/app/api/v1/uploadthing/core.ts +50 -0
  19. package/template/src/app/api/v1/uploadthing/route.ts +11 -0
  20. package/template/src/app/favicon.ico +0 -0
  21. package/template/src/app/globals.css +121 -0
  22. package/template/src/app/layout.tsx +50 -0
  23. package/template/src/app/page.tsx +58 -0
  24. package/template/src/components/loading-spinner.tsx +18 -0
  25. package/template/src/components/navigation.tsx +54 -0
  26. package/template/src/components/query-provider.tsx +27 -0
  27. package/template/src/components/ui/badge.tsx +46 -0
  28. package/template/src/components/ui/button.tsx +58 -0
  29. package/template/src/components/ui/card.tsx +92 -0
  30. package/template/src/components/ui/dialog.tsx +143 -0
  31. package/template/src/components/ui/input.tsx +21 -0
  32. package/template/src/components/ui/label.tsx +26 -0
  33. package/template/src/components/ui/select.tsx +185 -0
  34. package/template/src/components/ui/tabs.tsx +55 -0
  35. package/template/src/components/ui/textarea.tsx +23 -0
  36. package/template/src/data/env/client.ts +7 -0
  37. package/template/src/data/env/server.ts +13 -0
  38. package/template/src/drizzle/db.ts +6 -0
  39. package/template/src/drizzle/schema/app-schema.ts +31 -0
  40. package/template/src/drizzle/schema/auth-schema.ts +55 -0
  41. package/template/src/drizzle/schema/index.ts +14 -0
  42. package/template/src/hooks/use-auth.ts +32 -0
  43. package/template/src/hooks/use-debounce.ts +18 -0
  44. package/template/src/lib/arcjet.ts +45 -0
  45. package/template/src/lib/auth-client.ts +7 -0
  46. package/template/src/lib/auth.ts +50 -0
  47. package/template/src/lib/use-mobile.ts +29 -0
  48. package/template/src/lib/utils.ts +6 -0
  49. package/template/src/middleware.ts +14 -0
  50. package/template/src/server/index.ts +13 -0
  51. package/template/src/server/routers/posts.ts +93 -0
  52. package/template/src/server/routers/users.ts +56 -0
  53. package/template/src/server/trpc.ts +38 -0
  54. package/template/src/types/index.ts +10 -0
  55. package/template/src/utils/trpc.ts +5 -0
  56. package/template/src/utils/uploadthing.ts +10 -0
  57. package/template/tsconfig.json +42 -0
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # abdellah0l-stack
2
+
3
+ A CLI to scaffold a modern full-stack Next.js project with:
4
+
5
+ - ⚡ **Next.js 16** with App Router
6
+ - 🔷 **TypeScript**
7
+ - 🔗 **tRPC** for type-safe APIs
8
+ - ⚡ **TanStack Query** (React Query) for data fetching (optional)
9
+ - 🗃️ **Drizzle ORM** + PostgreSQL (Neon)
10
+ - 🔐 **Better-Auth** (GitHub, Google, Email)
11
+ - 🛡️ **Arcjet** for rate limiting (optional)
12
+ - 🎨 **Tailwind CSS**
13
+ - 🤖 **Vercel AI SDK** for ai integration (optional)
14
+ - 📁 **UploadThing** for uploading files (optional)
15
+
16
+ ## Usage
17
+
18
+ ```bash
19
+ npx abdellah0l-stack my-app
20
+ ```
21
+
22
+ Or install globally:
23
+
24
+ ```bash
25
+ npm install -g abdellah0l-stack
26
+ abdellah0l-stack my-app
27
+ ```
28
+
29
+ ## After Scaffolding
30
+
31
+ ```bash
32
+ cd my-app
33
+ npm install
34
+
35
+ # Set up your .env file with:
36
+ # - DATABASE_URL
37
+ # - BETTER_AUTH_SECRET
38
+ # - GITHUB_CLIENT_ID & GITHUB_CLIENT_SECRET
39
+ # - GOOGLE_CLIENT_ID & GOOGLE_CLIENT_SECRET
40
+ # - ARCJET_KEY
41
+ # - AI_GATEWAY_API_KEY (if using AI)
42
+ # - UPLOADTHING_TOKEN (if using uploads)
43
+
44
+ npm run db:push
45
+ npm run dev
46
+ ```
47
+
48
+ ## License
49
+
50
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from "path";
4
+ import fs from "fs-extra";
5
+ import prompts from "prompts";
6
+ import chalk from "chalk";
7
+ import { fileURLToPath } from "url";
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ async function main() {
13
+ console.log(chalk.blue.bold("\n🚀 abdellah0l-Stack - Next.js + tRPC + Drizzle + Better-Auth\n"));
14
+ console.log(chalk.gray("Thnx for using abdellah0l-Stack :) Let's build something awesome together.\n"));
15
+
16
+ // Get project name from args or prompt
17
+ let projectName = process.argv[2];
18
+
19
+ if (!projectName) {
20
+ const response = await prompts({
21
+ type: "text",
22
+ name: "projectName",
23
+ message: "Project name:",
24
+ initial: "my-app",
25
+ });
26
+ projectName = response.projectName;
27
+
28
+ if (!projectName) {
29
+ console.log(chalk.red("\n❌ Project name is required!\n"));
30
+ process.exit(1);
31
+ }
32
+ }
33
+
34
+ // Ask for additional options
35
+ const options = await prompts([
36
+ {
37
+ type: "text",
38
+ name: "description",
39
+ message: "Project description:",
40
+ initial: "A modern full-stack application",
41
+ },
42
+ {
43
+ type: "confirm",
44
+ name: "includeAI",
45
+ message: "Include AI features (Vercel AI SDK)?",
46
+ initial: true,
47
+ },
48
+ {
49
+ type: "confirm",
50
+ name: "includeUploadthing",
51
+ message: "Include file uploads (UploadThing)?",
52
+ initial: true,
53
+ },
54
+ {
55
+ type: "confirm",
56
+ name: "includeRateLimiting",
57
+ message: "Include rate limiting (Arcjet)?",
58
+ initial: true,
59
+ },
60
+ {
61
+ type: "confirm",
62
+ name: "includeTanstack",
63
+ message: "Include TanStack Query (React Query)?",
64
+ initial: true,
65
+ }
66
+ ]);
67
+
68
+ const targetDir = path.resolve(process.cwd(), projectName);
69
+ const templateDir = path.join(__dirname, "..", "template");
70
+
71
+ // Check if directory exists
72
+ if (fs.existsSync(targetDir)) {
73
+ const { overwrite } = await prompts({
74
+ type: "confirm",
75
+ name: "overwrite",
76
+ message: `Directory ${projectName} already exists. Overwrite?`,
77
+ initial: false,
78
+ });
79
+
80
+ if (!overwrite) {
81
+ console.log(chalk.yellow("\n⚠️ Operation cancelled.\n"));
82
+ process.exit(0);
83
+ }
84
+
85
+ await fs.remove(targetDir);
86
+ }
87
+
88
+ console.log(chalk.yellow(`\n📁 Creating project in ${chalk.white(targetDir)}...\n`));
89
+
90
+ // Copy template
91
+ await fs.copy(templateDir, targetDir);
92
+
93
+ // Update package.json with project name
94
+ const pkgPath = path.join(targetDir, "package.json");
95
+ if (fs.existsSync(pkgPath)) {
96
+ const pkg = await fs.readJson(pkgPath);
97
+ pkg.name = projectName;
98
+ pkg.description = options.description || "";
99
+
100
+ // Remove AI packages if not needed
101
+ if (!options.includeAI && pkg.dependencies) {
102
+ delete pkg.dependencies["@ai-sdk/anthropic"];
103
+ delete pkg.dependencies["ai"];
104
+ }
105
+
106
+ // Remove uploadthing if not needed
107
+ if (!options.includeUploadthing && pkg.dependencies) {
108
+ delete pkg.dependencies["uploadthing"];
109
+ delete pkg.dependencies["@uploadthing/react"];
110
+ }
111
+
112
+ // Remove arcjet if not needed
113
+ if (!options.includeRateLimiting && pkg.dependencies) {
114
+ delete pkg.dependencies["@arcjet/next"];
115
+ delete pkg.dependencies["arcjet"];
116
+ }
117
+
118
+ // Remove TanStack Query if not needed
119
+ if (!options.includeTanstack && pkg.dependencies) {
120
+ delete pkg.dependencies["@tanstack/react-query"];
121
+ delete pkg.dependencies["@tanstack/react-query-devtools"];
122
+ }
123
+
124
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
125
+ }
126
+
127
+ // Create .env from .env.example
128
+ const envExample = path.join(targetDir, ".env.example");
129
+ const envFile = path.join(targetDir, ".env");
130
+ if (fs.existsSync(envExample)) {
131
+ await fs.copy(envExample, envFile);
132
+ }
133
+
134
+ // Remove AI files if not needed
135
+ if (!options.includeAI) {
136
+ const aiPaths = [
137
+ path.join(targetDir, "src", "app", "api", "v1", "ai"),
138
+ path.join(targetDir, "src", "modules", "ai"),
139
+ path.join(targetDir, "src", "lib", "ai-schemas.ts"),
140
+ path.join(targetDir, "src", "components", "ai-error-boundary.tsx"),
141
+ path.join(targetDir, "src", "components", "ai-loading-spinner.tsx"),
142
+ path.join(targetDir, "src", "components", "ai-recommendations.tsx"),
143
+ path.join(targetDir, "src", "components", "ai-summary.tsx"),
144
+ path.join(targetDir, "src", "components", "exploreAi.tsx"),
145
+ ];
146
+ for (const p of aiPaths) {
147
+ await fs.remove(p).catch(() => {});
148
+ }
149
+ }
150
+
151
+ // Remove uploadthing files if not needed
152
+ if (!options.includeUploadthing) {
153
+ const uploadPaths = [
154
+ path.join(targetDir, "src", "app", "api", "v1", "uploadthing"),
155
+ path.join(targetDir, "src", "utils", "uploadthing.ts"),
156
+ ];
157
+ for (const p of uploadPaths) {
158
+ await fs.remove(p).catch(() => {});
159
+ }
160
+ }
161
+
162
+ // Remove arcjet files if not needed
163
+ if (!options.includeRateLimiting) {
164
+ const arcjetPaths = [
165
+ path.join(targetDir, "src", "lib", "arcjet.ts"),
166
+ ];
167
+ for (const p of arcjetPaths) {
168
+ await fs.remove(p).catch(() => {});
169
+ }
170
+ }
171
+
172
+ // Remove TanStack Query files if not needed
173
+ if (!options.includeTanstack) {
174
+ const tanstackPaths = [
175
+ path.join(targetDir, "src", "components", "query-provider.tsx"),
176
+ ];
177
+ for (const p of tanstackPaths) {
178
+ await fs.remove(p).catch(() => {});
179
+ }
180
+ }
181
+
182
+ // Success message
183
+ console.log(chalk.green.bold("\n✅ Project created successfully!\n"));
184
+
185
+ console.log(chalk.white("📦 Stack included:"));
186
+ console.log(chalk.gray(" • Next.js 16 with App Router"));
187
+ console.log(chalk.gray(" • TypeScript"));
188
+ console.log(chalk.gray(" • tRPC"));
189
+ if (options.includeTanstack) console.log(chalk.gray(" • TanStack Query (React Query)"));
190
+ console.log(chalk.gray(" • Drizzle ORM + PostgreSQL"));
191
+ console.log(chalk.gray(" • Better-Auth (GitHub, Google, Email)"));
192
+ if (options.includeRateLimiting) console.log(chalk.gray(" • Arcjet (Rate Limiting)"));
193
+ console.log(chalk.gray(" • Tailwind CSS"));
194
+ if (options.includeAI) console.log(chalk.gray(" • Vercel AI SDK (Claude)"));
195
+ if (options.includeUploadthing) console.log(chalk.gray(" • UploadThing (File Uploads)"));
196
+
197
+ console.log(chalk.white("\n📝 Next steps:\n"));
198
+ console.log(chalk.cyan(` cd ${projectName}`));
199
+ console.log(chalk.cyan(" npm install"));
200
+ console.log(chalk.gray(" # Update .env with your credentials"));
201
+ console.log(chalk.cyan(" npm run db:push"));
202
+ console.log(chalk.cyan(" npm run dev\n"));
203
+
204
+ console.log(chalk.white("🔐 Required environment variables:"));
205
+ console.log(chalk.gray(" • DATABASE_URL (Neon/PostgreSQL)"));
206
+ console.log(chalk.gray(" • BETTER_AUTH_SECRET"));
207
+ console.log(chalk.gray(" • GITHUB_CLIENT_ID & GITHUB_CLIENT_SECRET"));
208
+ console.log(chalk.gray(" • GOOGLE_CLIENT_ID & GOOGLE_CLIENT_SECRET"));
209
+ if (options.includeRateLimiting) console.log(chalk.gray(" • ARCJET_KEY"));
210
+ if (options.includeAI) console.log(chalk.gray(" • AI_GATEWAY_API_KEY"));
211
+ if (options.includeUploadthing) console.log(chalk.gray(" • UPLOADTHING_TOKEN"));
212
+ console.log("");
213
+ }
214
+
215
+ main().catch((err) => {
216
+ console.error(chalk.red("\n❌ Error:"), err.message);
217
+ process.exit(1);
218
+ });
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "abdellah0l-stack",
3
+ "version": "1.0.1",
4
+ "description": "Scaffold a Next.js project with TypeScript, tRPC, Drizzle, Better-Auth, Arcjet, and Vercel AI SDK",
5
+ "bin": {
6
+ "abdellah0l-stack": "./bin/cli.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "template"
11
+ ],
12
+ "keywords": [
13
+ "nextjs",
14
+ "trpc",
15
+ "drizzle",
16
+ "better-auth",
17
+ "arcjet",
18
+ "vercel-ai",
19
+ "boilerplate",
20
+ "cli"
21
+ ],
22
+ "author": "abdellah0l",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "chalk": "^5.3.0",
26
+ "fs-extra": "^11.2.0",
27
+ "prompts": "^2.4.2"
28
+ },
29
+ "type": "module"
30
+ }
@@ -0,0 +1,61 @@
1
+ # My App
2
+
3
+ A modern full-stack application built with:
4
+
5
+ - **Next.js 16** - React framework with App Router
6
+ - **TypeScript** - Type safety
7
+ - **tRPC** - End-to-end type-safe APIs
8
+ - **Drizzle ORM** - Type-safe database queries
9
+ - **PostgreSQL** - Database (Neon recommended)
10
+ - **Better-Auth** - Authentication (GitHub, Google, Email)
11
+ - **Arcjet** - Rate limiting and security
12
+ - **Tailwind CSS** - Styling
13
+ - **Vercel AI SDK** - AI features (optional)
14
+ - **UploadThing** - File uploads (optional)
15
+
16
+ ## Getting Started
17
+
18
+ 1. Install dependencies:
19
+ ```bash
20
+ npm install
21
+ ```
22
+
23
+ 2. Set up your environment variables (copy from `.env.example`):
24
+ ```bash
25
+ cp .env.example .env
26
+ ```
27
+
28
+ 3. Push the database schema:
29
+ ```bash
30
+ npm run db:push
31
+ ```
32
+
33
+ 4. Start the development server:
34
+ ```bash
35
+ npm run dev
36
+ ```
37
+
38
+ ## Environment Variables
39
+
40
+ | Variable | Description |
41
+ |----------|-------------|
42
+ | `DATABASE_URL` | PostgreSQL connection string |
43
+ | `BETTER_AUTH_SECRET` | Secret for auth (generate with `openssl rand -base64 32`) |
44
+ | `BETTER_AUTH_URL` | Your app URL |
45
+ | `GITHUB_CLIENT_ID` | GitHub OAuth app client ID |
46
+ | `GITHUB_CLIENT_SECRET` | GitHub OAuth app client secret |
47
+ | `GOOGLE_CLIENT_ID` | Google OAuth client ID |
48
+ | `GOOGLE_CLIENT_SECRET` | Google OAuth client secret |
49
+ | `ARCJET_KEY` | Arcjet API key |
50
+ | `AI_GATEWAY_API_KEY` | Vercel-ai API key (for AI features) |
51
+ | `UPLOADTHING_TOKEN` | UploadThing token (for file uploads) |
52
+
53
+ ## Scripts
54
+
55
+ - `npm run dev` - Start development server
56
+ - `npm run build` - Build for production
57
+ - `npm run start` - Start production server
58
+ - `npm run db:push` - Push schema to database
59
+ - `npm run db:generate` - Generate migrations
60
+ - `npm run db:migrate` - Run migrations
61
+ - `npm run db:studio` - Open Drizzle Studio
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "aliases": {
15
+ "components": "@/components",
16
+ "utils": "@/lib/utils",
17
+ "ui": "@/components/ui",
18
+ "lib": "@/lib",
19
+ "hooks": "@/hooks"
20
+ },
21
+ "registries": {}
22
+ }
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "drizzle-kit";
2
+ import { env } from "@/data/env/server";
3
+
4
+ // Drizzle configuration for database migrations
5
+ // install the extension "Drizzle ORM" in VSCode for a better experience (schema visulalization, autocompletion, etc)
6
+ export default defineConfig({
7
+ schema: "./src/drizzle/schema/index.ts",
8
+ out: "./src/drizzle/migrations",
9
+ dialect: "postgresql",
10
+ dbCredentials: {
11
+ url: env.DATABASE_URL,
12
+ },
13
+ })
@@ -0,0 +1,25 @@
1
+ import { dirname } from "path";
2
+ import { fileURLToPath } from "url";
3
+ import { FlatCompat } from "@eslint/eslintrc";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+
8
+ const compat = new FlatCompat({
9
+ baseDirectory: __dirname,
10
+ });
11
+
12
+ const eslintConfig = [
13
+ ...compat.extends("next/core-web-vitals", "next/typescript"),
14
+ {
15
+ ignores: [
16
+ "node_modules/**",
17
+ ".next/**",
18
+ "out/**",
19
+ "build/**",
20
+ "next-env.d.ts",
21
+ ],
22
+ },
23
+ ];
24
+
25
+ export default eslintConfig;
@@ -0,0 +1,114 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ // this enables the experimental React compiler for better performance and smaller bundle sizes
5
+ reactCompiler: true,
6
+ images: {
7
+ remotePatterns: [
8
+ {
9
+ protocol: 'https',
10
+ hostname: 'picsum.photos',
11
+ port: '',
12
+ pathname: '/**',
13
+ },
14
+ {
15
+ protocol: 'https',
16
+ hostname: 'images.unsplash.com',
17
+ port: '',
18
+ pathname: '/**',
19
+ },
20
+ {
21
+ protocol: 'https',
22
+ hostname: 'via.placeholder.com',
23
+ port: '',
24
+ pathname: '/**',
25
+ },
26
+ {
27
+ protocol: 'https',
28
+ hostname: 'placehold.co',
29
+ port: '',
30
+ pathname: '/**',
31
+ },
32
+ {
33
+ protocol: 'https',
34
+ hostname: 'encrypted-tbn0.gstatic.com',
35
+ port: '',
36
+ pathname: '/**',
37
+ },
38
+ {
39
+ protocol: 'https',
40
+ hostname: 'encrypted-tbn1.gstatic.com',
41
+ port: '',
42
+ pathname: '/**',
43
+ },
44
+ {
45
+ protocol: 'https',
46
+ hostname: 'encrypted-tbn2.gstatic.com',
47
+ port: '',
48
+ pathname: '/**',
49
+ },
50
+ {
51
+ protocol: 'https',
52
+ hostname: 'encrypted-tbn3.gstatic.com',
53
+ port: '',
54
+ pathname: '/**',
55
+ },
56
+ {
57
+ protocol: 'https',
58
+ hostname: 'i.pravatar.cc',
59
+ port: '',
60
+ pathname: '/**',
61
+ },
62
+ {
63
+ protocol: 'https',
64
+ hostname: 'utfs.io',
65
+ port: '',
66
+ pathname: '/a/**',
67
+ },
68
+ {
69
+ protocol: 'https',
70
+ hostname: 'hwbus3icbz.ufs.sh',
71
+ port: '',
72
+ pathname: '/f/**',
73
+ },
74
+ {
75
+ protocol: 'https',
76
+ hostname: 'avatars.githubusercontent.com',
77
+ port: '',
78
+ pathname: '/**',
79
+ },
80
+ {
81
+ protocol: 'https',
82
+ hostname: 'lh3.googleusercontent.com',
83
+ port: '',
84
+ pathname: '/**',
85
+ },
86
+ {
87
+ protocol: 'https',
88
+ hostname: 'm.media-amazon.com',
89
+ port: '',
90
+ pathname: '/**',
91
+ },
92
+ {
93
+ protocol: 'https',
94
+ hostname: 'imgv2-1-f.scribdassets.com',
95
+ port: '',
96
+ pathname: '/**',
97
+ },
98
+ {
99
+ protocol: 'https',
100
+ hostname: 'cdn2.penguin.com.au',
101
+ port: '',
102
+ pathname: '/**',
103
+ },
104
+ {
105
+ protocol: 'https',
106
+ hostname: 'glassboxx.com',
107
+ port: '',
108
+ pathname: '/**',
109
+ }
110
+ ],
111
+ },
112
+ };
113
+
114
+ export default nextConfig;
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "my-app",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "next dev --turbopack",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "db:generate": "drizzle-kit generate",
11
+ "db:migrate": "drizzle-kit migrate",
12
+ "db:studio": "drizzle-kit studio",
13
+ "db:push": "drizzle-kit push",
14
+ "lint": "eslint"
15
+ },
16
+ "dependencies": {
17
+ "@ai-sdk/anthropic": "^3.0.9",
18
+ "@arcjet/next": "^1.0.0-beta.13",
19
+ "@radix-ui/react-avatar": "^1.1.10",
20
+ "@radix-ui/react-dialog": "^1.1.15",
21
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
22
+ "@radix-ui/react-label": "^2.1.7",
23
+ "@radix-ui/react-select": "^2.2.6",
24
+ "@radix-ui/react-slot": "^1.2.3",
25
+ "@radix-ui/react-tabs": "^1.1.13",
26
+ "@t3-oss/env-nextjs": "^0.13.8",
27
+ "@tanstack/react-query": "^5.90.16",
28
+ "@trpc/client": "^11.8.1",
29
+ "@trpc/react-query": "^11.8.1",
30
+ "@trpc/server": "^11.8.1",
31
+ "@uploadthing/react": "^7.3.3",
32
+ "ai": "^6.0.18",
33
+ "better-auth": "^1.3.27",
34
+ "class-variance-authority": "^0.7.1",
35
+ "clsx": "^2.1.1",
36
+ "drizzle-orm": "^0.45.1",
37
+ "gsap": "^3.13.0",
38
+ "lucide-react": "^0.545.0",
39
+ "next": "^16.0.0",
40
+ "pg": "^8.16.3",
41
+ "react": "^19.0.0",
42
+ "react-dom": "^19.0.0",
43
+ "react-hot-toast": "^2.6.0",
44
+ "tailwind-merge": "^3.3.1",
45
+ "uploadthing": "^7.7.4",
46
+ "zod": "^3.25.76"
47
+ },
48
+ "devDependencies": {
49
+ "@eslint/eslintrc": "^3",
50
+ "@tailwindcss/postcss": "^4",
51
+ "@types/node": "^20",
52
+ "@types/pg": "^8.15.5",
53
+ "@types/react": "^19",
54
+ "@types/react-dom": "^19",
55
+ "drizzle-kit": "^0.31.8",
56
+ "eslint": "^9",
57
+ "eslint-config-next": "^16.0.0",
58
+ "tailwindcss": "^4",
59
+ "tsx": "^4.20.6",
60
+ "typescript": "^5"
61
+ }
62
+ }
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,5 @@
1
+ import { auth } from "@/lib/auth";
2
+ import { toNextJsHandler } from "better-auth/next-js";
3
+
4
+ export const { POST, GET } = toNextJsHandler(auth);
5
+
@@ -0,0 +1,13 @@
1
+ import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
2
+ import { appRouter } from '@/server';
3
+ import { createTRPCContext } from '@/server/trpc';
4
+
5
+ const handler = (req: Request) =>
6
+ fetchRequestHandler({
7
+ endpoint: '/api/v1/trpc',
8
+ req,
9
+ router: appRouter,
10
+ createContext: () => createTRPCContext({ req }),
11
+ });
12
+
13
+ export { handler as GET, handler as POST };