create-n8-app 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +239 -0
- package/package.json +58 -0
- package/template/_env.example +34 -0
- package/template/_env.local +31 -0
- package/template/_eslintrc.json +3 -0
- package/template/_gitignore +46 -0
- package/template/_package.json +59 -0
- package/template/app/api/auth/[...nextauth]/route.ts +3 -0
- package/template/app/api/trpc/[trpc]/route.ts +19 -0
- package/template/app/auth/signin/page.tsx +39 -0
- package/template/app/globals.css +33 -0
- package/template/app/layout.tsx +28 -0
- package/template/app/page.tsx +68 -0
- package/template/app/providers.tsx +47 -0
- package/template/components/auth/user-button.tsx +46 -0
- package/template/components/ui/.gitkeep +2 -0
- package/template/components.json +21 -0
- package/template/drizzle/.gitkeep +1 -0
- package/template/drizzle.config.ts +10 -0
- package/template/hooks/.gitkeep +1 -0
- package/template/lib/auth.ts +44 -0
- package/template/lib/db.ts +10 -0
- package/template/lib/env.ts +76 -0
- package/template/lib/trpc.ts +4 -0
- package/template/lib/utils.ts +6 -0
- package/template/next-env.d.ts +5 -0
- package/template/next.config.ts +14 -0
- package/template/postcss.config.mjs +7 -0
- package/template/prettier.config.js +11 -0
- package/template/public/.gitkeep +1 -0
- package/template/server/api/root.ts +22 -0
- package/template/server/api/routers/example.ts +76 -0
- package/template/server/api/trpc.ts +67 -0
- package/template/server/db/schema.ts +95 -0
- package/template/stores/example-store.ts +121 -0
- package/template/tests/example.test.ts +22 -0
- package/template/tests/setup.ts +22 -0
- package/template/tsconfig.json +27 -0
- package/template/vitest.config.ts +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nate McGrady
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# create-n8-app
|
|
2
|
+
|
|
3
|
+
Create a new Next.js app with the N8 stack - a modern, full-stack template featuring:
|
|
4
|
+
|
|
5
|
+
- **Next.js 16** - App Router, Server Components, Server Actions, Turbopack
|
|
6
|
+
- **TypeScript** - Strict mode enabled
|
|
7
|
+
- **Tailwind CSS v4** - Modern CSS-first configuration
|
|
8
|
+
- **Shadcn/ui** - Beautiful, accessible components
|
|
9
|
+
- **Drizzle ORM** - Type-safe database queries
|
|
10
|
+
- **Neon** - Serverless PostgreSQL
|
|
11
|
+
- **tRPC** - End-to-end type safety
|
|
12
|
+
- **TanStack Query** - Powerful async state management
|
|
13
|
+
- **Zustand** - Lightweight global state
|
|
14
|
+
- **NextAuth.js** - Authentication with GitHub OAuth
|
|
15
|
+
- **Vitest** - Fast unit testing
|
|
16
|
+
- **Prettier** - Code formatting with Tailwind plugin
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# With npm
|
|
22
|
+
npm create n8-app@latest my-app
|
|
23
|
+
|
|
24
|
+
# With pnpm
|
|
25
|
+
pnpm create n8-app@latest my-app
|
|
26
|
+
|
|
27
|
+
# With yarn
|
|
28
|
+
yarn create n8-app my-app
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm create n8-app@latest my-app --skip-install # Skip dependency installation
|
|
35
|
+
npm create n8-app@latest my-app --skip-git # Skip git initialization
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## After Creation
|
|
39
|
+
|
|
40
|
+
1. **Set up your database**
|
|
41
|
+
- Create a Neon project at [neon.tech](https://neon.tech)
|
|
42
|
+
- Copy the connection string to `.env.local`
|
|
43
|
+
|
|
44
|
+
2. **Set up GitHub OAuth**
|
|
45
|
+
- Create an OAuth App at [github.com/settings/developers](https://github.com/settings/developers)
|
|
46
|
+
- Set callback URL to `http://localhost:3000/api/auth/callback/github`
|
|
47
|
+
- Copy Client ID and Secret to `.env.local`
|
|
48
|
+
|
|
49
|
+
3. **Generate a secret**
|
|
50
|
+
```bash
|
|
51
|
+
openssl rand -base64 32
|
|
52
|
+
```
|
|
53
|
+
Add to `.env.local` as `AUTH_SECRET`
|
|
54
|
+
|
|
55
|
+
4. **Run database migrations**
|
|
56
|
+
```bash
|
|
57
|
+
pnpm db:generate
|
|
58
|
+
pnpm db:push
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
5. **Start developing**
|
|
62
|
+
```bash
|
|
63
|
+
pnpm dev
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Project Structure
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
my-app/
|
|
70
|
+
├── app/ # Next.js App Router
|
|
71
|
+
│ ├── api/ # API routes (tRPC, auth)
|
|
72
|
+
│ ├── auth/ # Auth pages
|
|
73
|
+
│ ├── globals.css # Tailwind styles
|
|
74
|
+
│ ├── layout.tsx # Root layout
|
|
75
|
+
│ ├── page.tsx # Home page
|
|
76
|
+
│ └── providers.tsx # Client providers
|
|
77
|
+
├── components/ # React components
|
|
78
|
+
│ ├── auth/ # Auth components
|
|
79
|
+
│ └── ui/ # Shadcn components
|
|
80
|
+
├── hooks/ # Custom React hooks
|
|
81
|
+
├── lib/ # Utility functions
|
|
82
|
+
│ ├── auth.ts # NextAuth config
|
|
83
|
+
│ ├── db.ts # Database client
|
|
84
|
+
│ ├── env.ts # Environment validation
|
|
85
|
+
│ ├── trpc.ts # tRPC client
|
|
86
|
+
│ └── utils.ts # Utilities
|
|
87
|
+
├── server/ # Server-side code
|
|
88
|
+
│ ├── api/ # tRPC routers
|
|
89
|
+
│ └── db/ # Database schema
|
|
90
|
+
├── stores/ # Zustand stores
|
|
91
|
+
├── tests/ # Test files
|
|
92
|
+
├── drizzle/ # Database migrations
|
|
93
|
+
└── public/ # Static assets
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Scripts
|
|
97
|
+
|
|
98
|
+
| Command | Description |
|
|
99
|
+
|---------|-------------|
|
|
100
|
+
| `pnpm dev` | Start development server with Turbopack |
|
|
101
|
+
| `pnpm build` | Build for production |
|
|
102
|
+
| `pnpm start` | Start production server |
|
|
103
|
+
| `pnpm lint` | Run ESLint |
|
|
104
|
+
| `pnpm format` | Format code with Prettier |
|
|
105
|
+
| `pnpm test` | Run tests with Vitest |
|
|
106
|
+
| `pnpm db:generate` | Generate Drizzle migrations |
|
|
107
|
+
| `pnpm db:push` | Push schema to database |
|
|
108
|
+
| `pnpm db:studio` | Open Drizzle Studio |
|
|
109
|
+
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
### Adding Shadcn Components
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npx shadcn@latest add button
|
|
116
|
+
npx shadcn@latest add card
|
|
117
|
+
npx shadcn@latest add input
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Creating tRPC Routes
|
|
121
|
+
|
|
122
|
+
Add new routers in `server/api/routers/` and register them in `server/api/root.ts`.
|
|
123
|
+
|
|
124
|
+
### Using Zustand
|
|
125
|
+
|
|
126
|
+
See `stores/example-store.ts` for patterns. Remember:
|
|
127
|
+
- Use React Query for server data
|
|
128
|
+
- Use Zustand only for client state (UI, preferences, cart)
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/create-project.ts
|
|
7
|
+
import fs2 from "fs-extra";
|
|
8
|
+
import path2 from "path";
|
|
9
|
+
import prompts from "prompts";
|
|
10
|
+
import chalk2 from "chalk";
|
|
11
|
+
|
|
12
|
+
// src/utils/copy-template.ts
|
|
13
|
+
import fs from "fs-extra";
|
|
14
|
+
import path from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
var __dirname = path.dirname(__filename);
|
|
18
|
+
async function copyTemplate(targetDir, projectName) {
|
|
19
|
+
const templateDir = path.resolve(__dirname, "..", "template");
|
|
20
|
+
if (!await fs.pathExists(templateDir)) {
|
|
21
|
+
throw new Error(`Template directory not found at: ${templateDir}`);
|
|
22
|
+
}
|
|
23
|
+
await fs.copy(templateDir, targetDir);
|
|
24
|
+
const filesToRename = [
|
|
25
|
+
{ from: "_package.json", to: "package.json" },
|
|
26
|
+
{ from: "_gitignore", to: ".gitignore" },
|
|
27
|
+
{ from: "_env.example", to: ".env.example" },
|
|
28
|
+
{ from: "_env.local", to: ".env.local" },
|
|
29
|
+
{ from: "_eslintrc.json", to: ".eslintrc.json" }
|
|
30
|
+
];
|
|
31
|
+
for (const { from, to } of filesToRename) {
|
|
32
|
+
const fromPath = path.join(targetDir, from);
|
|
33
|
+
const toPath = path.join(targetDir, to);
|
|
34
|
+
if (await fs.pathExists(fromPath)) {
|
|
35
|
+
await fs.rename(fromPath, toPath);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const packageJsonPath = path.join(targetDir, "package.json");
|
|
39
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
40
|
+
packageJson.name = projectName;
|
|
41
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/utils/install-deps.ts
|
|
45
|
+
import { execa } from "execa";
|
|
46
|
+
|
|
47
|
+
// src/utils/logger.ts
|
|
48
|
+
import chalk from "chalk";
|
|
49
|
+
var logger = {
|
|
50
|
+
info: (message) => {
|
|
51
|
+
console.log(chalk.blue("\u2139"), message);
|
|
52
|
+
},
|
|
53
|
+
success: (message) => {
|
|
54
|
+
console.log(chalk.green("\u2714"), message);
|
|
55
|
+
},
|
|
56
|
+
warn: (message) => {
|
|
57
|
+
console.log(chalk.yellow("\u26A0"), message);
|
|
58
|
+
},
|
|
59
|
+
error: (message) => {
|
|
60
|
+
console.log(chalk.red("\u2716"), message);
|
|
61
|
+
},
|
|
62
|
+
step: (step, total, message) => {
|
|
63
|
+
console.log(chalk.cyan(`[${step}/${total}]`), message);
|
|
64
|
+
},
|
|
65
|
+
blank: () => {
|
|
66
|
+
console.log();
|
|
67
|
+
},
|
|
68
|
+
title: (message) => {
|
|
69
|
+
console.log();
|
|
70
|
+
console.log(chalk.bold.magenta(message));
|
|
71
|
+
console.log();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// src/utils/install-deps.ts
|
|
76
|
+
async function installDependencies(targetDir) {
|
|
77
|
+
logger.info("Installing dependencies with pnpm...");
|
|
78
|
+
try {
|
|
79
|
+
await execa("pnpm", ["install"], {
|
|
80
|
+
cwd: targetDir,
|
|
81
|
+
stdio: "inherit"
|
|
82
|
+
});
|
|
83
|
+
logger.success("Dependencies installed successfully");
|
|
84
|
+
} catch (error) {
|
|
85
|
+
logger.warn("pnpm not found, trying npm...");
|
|
86
|
+
try {
|
|
87
|
+
await execa("npm", ["install"], {
|
|
88
|
+
cwd: targetDir,
|
|
89
|
+
stdio: "inherit"
|
|
90
|
+
});
|
|
91
|
+
logger.success("Dependencies installed successfully with npm");
|
|
92
|
+
} catch {
|
|
93
|
+
throw new Error("Failed to install dependencies");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function runShadcnInit(targetDir) {
|
|
98
|
+
logger.info("Initializing Shadcn/ui...");
|
|
99
|
+
try {
|
|
100
|
+
await execa("npx", ["shadcn@latest", "init", "-y", "-d"], {
|
|
101
|
+
cwd: targetDir,
|
|
102
|
+
stdio: "inherit"
|
|
103
|
+
});
|
|
104
|
+
logger.success("Shadcn/ui initialized successfully");
|
|
105
|
+
} catch (error) {
|
|
106
|
+
logger.warn('Shadcn/ui initialization skipped - you can run "npx shadcn@latest init" later');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/utils/git.ts
|
|
111
|
+
import { execa as execa2 } from "execa";
|
|
112
|
+
async function initGit(targetDir) {
|
|
113
|
+
logger.info("Initializing git repository...");
|
|
114
|
+
try {
|
|
115
|
+
await execa2("git", ["init"], {
|
|
116
|
+
cwd: targetDir,
|
|
117
|
+
stdio: "pipe"
|
|
118
|
+
});
|
|
119
|
+
await execa2("git", ["add", "-A"], {
|
|
120
|
+
cwd: targetDir,
|
|
121
|
+
stdio: "pipe"
|
|
122
|
+
});
|
|
123
|
+
await execa2("git", ["commit", "-m", "Initial commit from create-n8-app"], {
|
|
124
|
+
cwd: targetDir,
|
|
125
|
+
stdio: "pipe"
|
|
126
|
+
});
|
|
127
|
+
logger.success("Git repository initialized with initial commit");
|
|
128
|
+
} catch (error) {
|
|
129
|
+
logger.warn("Git initialization skipped - git may not be installed");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/helpers/get-package-manager.ts
|
|
134
|
+
function getPackageManager() {
|
|
135
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
136
|
+
if (userAgent.startsWith("pnpm")) {
|
|
137
|
+
return "pnpm";
|
|
138
|
+
}
|
|
139
|
+
if (userAgent.startsWith("yarn")) {
|
|
140
|
+
return "yarn";
|
|
141
|
+
}
|
|
142
|
+
if (userAgent.startsWith("bun")) {
|
|
143
|
+
return "bun";
|
|
144
|
+
}
|
|
145
|
+
return "npm";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/create-project.ts
|
|
149
|
+
async function createProject(options) {
|
|
150
|
+
logger.title("\u{1F680} Create N8 App");
|
|
151
|
+
let projectName = options.projectName;
|
|
152
|
+
if (!projectName) {
|
|
153
|
+
const response = await prompts({
|
|
154
|
+
type: "text",
|
|
155
|
+
name: "projectName",
|
|
156
|
+
message: "What is your project named?",
|
|
157
|
+
initial: "my-n8-app",
|
|
158
|
+
validate: (value) => {
|
|
159
|
+
if (!value) return "Project name is required";
|
|
160
|
+
if (!/^[a-z0-9-]+$/.test(value)) {
|
|
161
|
+
return "Project name can only contain lowercase letters, numbers, and hyphens";
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
if (!response.projectName) {
|
|
167
|
+
logger.error("Project name is required");
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
projectName = response.projectName;
|
|
171
|
+
}
|
|
172
|
+
const targetDir = path2.resolve(process.cwd(), projectName);
|
|
173
|
+
if (await fs2.pathExists(targetDir)) {
|
|
174
|
+
const { overwrite } = await prompts({
|
|
175
|
+
type: "confirm",
|
|
176
|
+
name: "overwrite",
|
|
177
|
+
message: `Directory ${projectName} already exists. Overwrite?`,
|
|
178
|
+
initial: false
|
|
179
|
+
});
|
|
180
|
+
if (!overwrite) {
|
|
181
|
+
logger.error("Operation cancelled");
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
await fs2.remove(targetDir);
|
|
185
|
+
}
|
|
186
|
+
const totalSteps = options.skipInstall ? 2 : 4;
|
|
187
|
+
logger.step(1, totalSteps, "Creating project structure...");
|
|
188
|
+
await fs2.ensureDir(targetDir);
|
|
189
|
+
await copyTemplate(targetDir, projectName);
|
|
190
|
+
logger.success("Project structure created");
|
|
191
|
+
if (!options.skipGit) {
|
|
192
|
+
logger.step(2, totalSteps, "Initializing git...");
|
|
193
|
+
await initGit(targetDir);
|
|
194
|
+
}
|
|
195
|
+
if (!options.skipInstall) {
|
|
196
|
+
logger.step(options.skipGit ? 2 : 3, totalSteps, "Installing dependencies...");
|
|
197
|
+
await installDependencies(targetDir);
|
|
198
|
+
logger.step(options.skipGit ? 3 : 4, totalSteps, "Setting up Shadcn/ui...");
|
|
199
|
+
await runShadcnInit(targetDir);
|
|
200
|
+
}
|
|
201
|
+
const pm = getPackageManager();
|
|
202
|
+
const runCmd = pm === "npm" ? "npm run" : pm;
|
|
203
|
+
logger.blank();
|
|
204
|
+
logger.title("\u2705 Success! Your N8 app is ready.");
|
|
205
|
+
console.log(chalk2.dim(" Next steps:"));
|
|
206
|
+
console.log();
|
|
207
|
+
console.log(chalk2.cyan(` cd ${projectName}`));
|
|
208
|
+
if (options.skipInstall) {
|
|
209
|
+
console.log(chalk2.cyan(` ${pm} install`));
|
|
210
|
+
}
|
|
211
|
+
console.log(chalk2.cyan(` ${runCmd} dev`));
|
|
212
|
+
console.log();
|
|
213
|
+
console.log(chalk2.dim(" Don't forget to:"));
|
|
214
|
+
console.log(chalk2.dim(" \u2022 Set up your .env.local with database and auth credentials"));
|
|
215
|
+
console.log(chalk2.dim(' \u2022 Run "pnpm drizzle-kit generate" to create migrations'));
|
|
216
|
+
console.log(chalk2.dim(' \u2022 Add Shadcn components with "npx shadcn@latest add [component]"'));
|
|
217
|
+
console.log();
|
|
218
|
+
console.log(chalk2.bold(" Happy coding! \u{1F389}"));
|
|
219
|
+
console.log();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/index.ts
|
|
223
|
+
var program = new Command();
|
|
224
|
+
program.name("create-n8-app").description("Create a new Next.js app with the N8 stack").version("0.1.0").argument("[project-name]", "Name of the project").option("--skip-install", "Skip installing dependencies").option("--skip-git", "Skip initializing git repository").action(async (projectName, options) => {
|
|
225
|
+
try {
|
|
226
|
+
await createProject({
|
|
227
|
+
projectName,
|
|
228
|
+
skipInstall: options.skipInstall ?? false,
|
|
229
|
+
skipGit: options.skipGit ?? false
|
|
230
|
+
});
|
|
231
|
+
} catch (error) {
|
|
232
|
+
logger.error("Failed to create project");
|
|
233
|
+
if (error instanceof Error) {
|
|
234
|
+
logger.error(error.message);
|
|
235
|
+
}
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-n8-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create a Next.js app with the N8 stack - Next.js 16, Tailwind v4, Shadcn/ui, Drizzle, tRPC, TanStack Query, Zustand, NextAuth, and more",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-n8-app": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
15
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
16
|
+
"prepublishOnly": "pnpm build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"create",
|
|
20
|
+
"nextjs",
|
|
21
|
+
"next",
|
|
22
|
+
"react",
|
|
23
|
+
"typescript",
|
|
24
|
+
"tailwind",
|
|
25
|
+
"shadcn",
|
|
26
|
+
"drizzle",
|
|
27
|
+
"trpc",
|
|
28
|
+
"tanstack-query",
|
|
29
|
+
"zustand",
|
|
30
|
+
"nextauth",
|
|
31
|
+
"starter",
|
|
32
|
+
"template",
|
|
33
|
+
"scaffold"
|
|
34
|
+
],
|
|
35
|
+
"author": "Nate McG",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/natemcgrady/create-n8-app"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/fs-extra": "^11.0.4",
|
|
43
|
+
"@types/node": "^22.10.0",
|
|
44
|
+
"@types/prompts": "^2.4.9",
|
|
45
|
+
"tsup": "^8.3.5",
|
|
46
|
+
"typescript": "^5.7.2"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"chalk": "^5.3.0",
|
|
50
|
+
"commander": "^12.1.0",
|
|
51
|
+
"execa": "^9.5.1",
|
|
52
|
+
"fs-extra": "^11.2.0",
|
|
53
|
+
"prompts": "^2.4.2"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18.0.0"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# N8 Stack Environment Variables
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Copy this file to .env.local and fill in your values
|
|
5
|
+
# Never commit .env.local to version control!
|
|
6
|
+
|
|
7
|
+
# =============================================================================
|
|
8
|
+
# Database (Neon)
|
|
9
|
+
# =============================================================================
|
|
10
|
+
# Get your connection string from: https://neon.tech
|
|
11
|
+
DATABASE_URL="postgresql://user:password@host/database?sslmode=require"
|
|
12
|
+
|
|
13
|
+
# =============================================================================
|
|
14
|
+
# Authentication (NextAuth.js / Auth.js)
|
|
15
|
+
# =============================================================================
|
|
16
|
+
# Generate a secret with: openssl rand -base64 32
|
|
17
|
+
AUTH_SECRET="your-secret-here-at-least-32-characters"
|
|
18
|
+
|
|
19
|
+
# GitHub OAuth
|
|
20
|
+
# Create an OAuth App at: https://github.com/settings/developers
|
|
21
|
+
# Set callback URL to: http://localhost:3000/api/auth/callback/github
|
|
22
|
+
AUTH_GITHUB_ID="your-github-client-id"
|
|
23
|
+
AUTH_GITHUB_SECRET="your-github-client-secret"
|
|
24
|
+
|
|
25
|
+
# =============================================================================
|
|
26
|
+
# Public Variables (exposed to browser)
|
|
27
|
+
# =============================================================================
|
|
28
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
29
|
+
|
|
30
|
+
# =============================================================================
|
|
31
|
+
# AI (Optional)
|
|
32
|
+
# =============================================================================
|
|
33
|
+
# Get your API key from: https://platform.openai.com
|
|
34
|
+
OPENAI_API_KEY="sk-..."
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# N8 Stack Environment Variables
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Fill in your values below
|
|
5
|
+
|
|
6
|
+
# =============================================================================
|
|
7
|
+
# Database (Neon)
|
|
8
|
+
# =============================================================================
|
|
9
|
+
DATABASE_URL="postgresql://user:password@host/database?sslmode=require"
|
|
10
|
+
|
|
11
|
+
# =============================================================================
|
|
12
|
+
# Authentication (NextAuth.js / Auth.js)
|
|
13
|
+
# =============================================================================
|
|
14
|
+
AUTH_SECRET="change-me-generate-with-openssl-rand-base64-32"
|
|
15
|
+
AUTH_GITHUB_ID=""
|
|
16
|
+
AUTH_GITHUB_SECRET=""
|
|
17
|
+
|
|
18
|
+
# =============================================================================
|
|
19
|
+
# Public Variables
|
|
20
|
+
# =============================================================================
|
|
21
|
+
NEXT_PUBLIC_APP_URL="http://localhost:3000"
|
|
22
|
+
|
|
23
|
+
# =============================================================================
|
|
24
|
+
# AI (Optional)
|
|
25
|
+
# =============================================================================
|
|
26
|
+
# OPENAI_API_KEY=""
|
|
27
|
+
|
|
28
|
+
# =============================================================================
|
|
29
|
+
# Skip validation during initial setup
|
|
30
|
+
# =============================================================================
|
|
31
|
+
SKIP_ENV_VALIDATION="true"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
.pnpm-store
|
|
4
|
+
|
|
5
|
+
# Next.js
|
|
6
|
+
.next
|
|
7
|
+
out
|
|
8
|
+
|
|
9
|
+
# Production
|
|
10
|
+
build
|
|
11
|
+
dist
|
|
12
|
+
|
|
13
|
+
# Debug
|
|
14
|
+
npm-debug.log*
|
|
15
|
+
yarn-debug.log*
|
|
16
|
+
yarn-error.log*
|
|
17
|
+
|
|
18
|
+
# Local env files
|
|
19
|
+
.env
|
|
20
|
+
.env.local
|
|
21
|
+
.env.development.local
|
|
22
|
+
.env.test.local
|
|
23
|
+
.env.production.local
|
|
24
|
+
|
|
25
|
+
# Vercel
|
|
26
|
+
.vercel
|
|
27
|
+
|
|
28
|
+
# TypeScript
|
|
29
|
+
*.tsbuildinfo
|
|
30
|
+
next-env.d.ts
|
|
31
|
+
|
|
32
|
+
# Testing
|
|
33
|
+
coverage
|
|
34
|
+
|
|
35
|
+
# IDE
|
|
36
|
+
.idea
|
|
37
|
+
.vscode
|
|
38
|
+
*.swp
|
|
39
|
+
*.swo
|
|
40
|
+
|
|
41
|
+
# OS
|
|
42
|
+
.DS_Store
|
|
43
|
+
Thumbs.db
|
|
44
|
+
|
|
45
|
+
# Drizzle
|
|
46
|
+
drizzle/*.sql
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-n8-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev --turbopack",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "next lint",
|
|
10
|
+
"format": "prettier --write .",
|
|
11
|
+
"format:check": "prettier --check .",
|
|
12
|
+
"test": "vitest",
|
|
13
|
+
"test:run": "vitest run",
|
|
14
|
+
"db:generate": "drizzle-kit generate",
|
|
15
|
+
"db:migrate": "drizzle-kit migrate",
|
|
16
|
+
"db:push": "drizzle-kit push",
|
|
17
|
+
"db:studio": "drizzle-kit studio"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@auth/drizzle-adapter": "^1.7.0",
|
|
21
|
+
"@neondatabase/serverless": "^0.10.4",
|
|
22
|
+
"@tanstack/react-query": "^5.62.0",
|
|
23
|
+
"@trpc/client": "^11.0.0-rc.660",
|
|
24
|
+
"@trpc/react-query": "^11.0.0-rc.660",
|
|
25
|
+
"@trpc/server": "^11.0.0-rc.660",
|
|
26
|
+
"ai": "^4.0.0",
|
|
27
|
+
"@ai-sdk/openai": "^1.0.0",
|
|
28
|
+
"drizzle-orm": "^0.36.4",
|
|
29
|
+
"next": "^15.1.0",
|
|
30
|
+
"next-auth": "^5.0.0-beta.25",
|
|
31
|
+
"react": "^19.0.0",
|
|
32
|
+
"react-dom": "^19.0.0",
|
|
33
|
+
"zod": "^3.23.8",
|
|
34
|
+
"zustand": "^5.0.2",
|
|
35
|
+
"superjson": "^2.2.2",
|
|
36
|
+
"clsx": "^2.1.1",
|
|
37
|
+
"tailwind-merge": "^2.6.0",
|
|
38
|
+
"lucide-react": "^0.468.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@tailwindcss/postcss": "^4.0.0",
|
|
42
|
+
"@types/node": "^22.10.0",
|
|
43
|
+
"@types/react": "^19.0.0",
|
|
44
|
+
"@types/react-dom": "^19.0.0",
|
|
45
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
46
|
+
"drizzle-kit": "^0.28.1",
|
|
47
|
+
"jsdom": "^25.0.1",
|
|
48
|
+
"postcss": "^8.4.49",
|
|
49
|
+
"prettier": "^3.4.2",
|
|
50
|
+
"prettier-plugin-tailwindcss": "^0.6.9",
|
|
51
|
+
"tailwindcss": "^4.0.0",
|
|
52
|
+
"typescript": "^5.7.2",
|
|
53
|
+
"vitest": "^2.1.8",
|
|
54
|
+
"@testing-library/react": "^16.1.0",
|
|
55
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
56
|
+
"eslint": "^9.16.0",
|
|
57
|
+
"eslint-config-next": "^15.1.0"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
|
|
2
|
+
import { appRouter } from '@/server/api/root'
|
|
3
|
+
import { createTRPCContext } from '@/server/api/trpc'
|
|
4
|
+
|
|
5
|
+
const handler = (req: Request) =>
|
|
6
|
+
fetchRequestHandler({
|
|
7
|
+
endpoint: '/api/trpc',
|
|
8
|
+
req,
|
|
9
|
+
router: appRouter,
|
|
10
|
+
createContext: () => createTRPCContext({ headers: req.headers }),
|
|
11
|
+
onError:
|
|
12
|
+
process.env.NODE_ENV === 'development'
|
|
13
|
+
? ({ path, error }) => {
|
|
14
|
+
console.error(`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message}`)
|
|
15
|
+
}
|
|
16
|
+
: undefined,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export { handler as GET, handler as POST }
|