@amirulabu/create-recurring-rabbit-app 0.0.0-alpha
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/bin/index.js +2 -0
- package/dist/index.js +592 -0
- package/package.json +43 -0
- package/templates/default/.editorconfig +21 -0
- package/templates/default/.env.example +15 -0
- package/templates/default/.eslintrc.json +35 -0
- package/templates/default/.prettierrc.json +7 -0
- package/templates/default/README.md +346 -0
- package/templates/default/app.config.ts +20 -0
- package/templates/default/docs/adding-features.md +439 -0
- package/templates/default/docs/adr/001-use-sqlite-for-development-database.md +22 -0
- package/templates/default/docs/adr/002-use-tanstack-start-over-nextjs.md +22 -0
- package/templates/default/docs/adr/003-use-better-auth-over-nextauth.md +22 -0
- package/templates/default/docs/adr/004-use-drizzle-over-prisma.md +22 -0
- package/templates/default/docs/adr/005-use-trpc-for-api-layer.md +22 -0
- package/templates/default/docs/adr/006-use-tailwind-css-v4-with-shadcn-ui.md +22 -0
- package/templates/default/docs/architecture.md +241 -0
- package/templates/default/docs/database.md +376 -0
- package/templates/default/docs/deployment.md +435 -0
- package/templates/default/docs/troubleshooting.md +668 -0
- package/templates/default/drizzle/migrations/0001_initial_schema.sql +39 -0
- package/templates/default/drizzle/migrations/meta/0001_snapshot.json +225 -0
- package/templates/default/drizzle/migrations/meta/_journal.json +12 -0
- package/templates/default/drizzle.config.ts +10 -0
- package/templates/default/lighthouserc.json +78 -0
- package/templates/default/src/app/__root.tsx +32 -0
- package/templates/default/src/app/api/auth/$.ts +15 -0
- package/templates/default/src/app/api/trpc.server.ts +12 -0
- package/templates/default/src/app/auth/forgot-password.tsx +107 -0
- package/templates/default/src/app/auth/login.tsx +34 -0
- package/templates/default/src/app/auth/register.tsx +34 -0
- package/templates/default/src/app/auth/reset-password.tsx +171 -0
- package/templates/default/src/app/auth/verify-email.tsx +111 -0
- package/templates/default/src/app/dashboard/index.tsx +122 -0
- package/templates/default/src/app/dashboard/settings.tsx +161 -0
- package/templates/default/src/app/globals.css +55 -0
- package/templates/default/src/app/index.tsx +83 -0
- package/templates/default/src/components/features/auth/login-form.tsx +172 -0
- package/templates/default/src/components/features/auth/register-form.tsx +202 -0
- package/templates/default/src/components/layout/dashboard-layout.tsx +27 -0
- package/templates/default/src/components/layout/header.tsx +29 -0
- package/templates/default/src/components/layout/sidebar.tsx +38 -0
- package/templates/default/src/components/ui/button.tsx +57 -0
- package/templates/default/src/components/ui/card.tsx +79 -0
- package/templates/default/src/components/ui/input.tsx +24 -0
- package/templates/default/src/lib/api.ts +42 -0
- package/templates/default/src/lib/auth.ts +292 -0
- package/templates/default/src/lib/email.ts +221 -0
- package/templates/default/src/lib/env.ts +119 -0
- package/templates/default/src/lib/hydration-timing.ts +289 -0
- package/templates/default/src/lib/monitoring.ts +336 -0
- package/templates/default/src/lib/utils.ts +6 -0
- package/templates/default/src/server/api/root.ts +10 -0
- package/templates/default/src/server/api/routers/dashboard.ts +37 -0
- package/templates/default/src/server/api/routers/user.ts +31 -0
- package/templates/default/src/server/api/trpc.ts +132 -0
- package/templates/default/src/server/auth/config.ts +241 -0
- package/templates/default/src/server/db/index.ts +153 -0
- package/templates/default/src/server/db/migrate.ts +125 -0
- package/templates/default/src/server/db/schema.ts +170 -0
- package/templates/default/src/server/db/seed.ts +130 -0
- package/templates/default/src/types/global.d.ts +25 -0
- package/templates/default/tailwind.config.js +46 -0
- package/templates/default/tsconfig.json +36 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/eslintrc",
|
|
3
|
+
"extends": [
|
|
4
|
+
"eslint:recommended",
|
|
5
|
+
"plugin:@typescript-eslint/recommended",
|
|
6
|
+
"plugin:react/recommended",
|
|
7
|
+
"plugin:react-hooks/recommended"
|
|
8
|
+
],
|
|
9
|
+
"plugins": ["@typescript-eslint", "react", "react-hooks"],
|
|
10
|
+
"parser": "@typescript-eslint/parser",
|
|
11
|
+
"parserOptions": {
|
|
12
|
+
"ecmaVersion": "latest",
|
|
13
|
+
"sourceType": "module",
|
|
14
|
+
"ecmaFeatures": {
|
|
15
|
+
"jsx": true
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"settings": {
|
|
19
|
+
"react": {
|
|
20
|
+
"version": "detect"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"rules": {
|
|
24
|
+
"@typescript-eslint/no-unused-vars": [
|
|
25
|
+
"error",
|
|
26
|
+
{
|
|
27
|
+
"argsIgnorePattern": "^_"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"@typescript-eslint/no-explicit-any": "error",
|
|
31
|
+
"react/react-in-jsx-scope": "error",
|
|
32
|
+
"react-hooks/rules-of-hooks": "error",
|
|
33
|
+
"react-hooks/exhaustive-deps": "warn"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Recurring Rabbit
|
|
2
|
+
|
|
3
|
+
> A production-ready micro-SaaS starter built with TanStack Start, tRPC, Drizzle, Better-auth, and Tailwind CSS.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies
|
|
9
|
+
npm install
|
|
10
|
+
|
|
11
|
+
# Start development server
|
|
12
|
+
npm run dev
|
|
13
|
+
|
|
14
|
+
# Open http://localhost:3000 and create an account
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## What's Included
|
|
18
|
+
|
|
19
|
+
- 🔐 **Authentication** - Email/password with verification via Better-auth
|
|
20
|
+
- 🗄️ **Database** - SQLite (dev) / Postgres (prod) with Drizzle ORM
|
|
21
|
+
- 🌐 **Full-stack** - TanStack Start with tRPC for type-safe APIs
|
|
22
|
+
- 🎨 **Styling** - Tailwind CSS with shadcn/ui components
|
|
23
|
+
- ⚡ **TypeScript** - End-to-end type safety
|
|
24
|
+
- 🔧 **Developer Tools** - Hot reload, ESLint, Prettier, VS Code config
|
|
25
|
+
|
|
26
|
+
## Project Structure
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
src/
|
|
30
|
+
├── app/ # TanStack Start routes
|
|
31
|
+
├── server/ # tRPC API and database
|
|
32
|
+
├── components/ # Reusable UI components
|
|
33
|
+
└── lib/ # Shared utilities
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Key Commands
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Development
|
|
40
|
+
npm run dev # Start development server
|
|
41
|
+
npm run build # Build for production
|
|
42
|
+
npm run start # Start production server
|
|
43
|
+
|
|
44
|
+
# Bundle Analysis
|
|
45
|
+
npm run build:analyze # Build with bundle analyzer (opens stats.html)
|
|
46
|
+
|
|
47
|
+
# Database
|
|
48
|
+
npm run db:generate # Generate migration files
|
|
49
|
+
npm run db:migrate # Apply migrations
|
|
50
|
+
npm run db:studio # Open Drizzle Studio
|
|
51
|
+
npm run db:seed # Seed database with sample data
|
|
52
|
+
|
|
53
|
+
# Code Quality
|
|
54
|
+
npm run typecheck # Run TypeScript checks
|
|
55
|
+
npm run lint # Run ESLint
|
|
56
|
+
npm run lint:fix # Fix ESLint issues
|
|
57
|
+
npm run format # Format code with Prettier
|
|
58
|
+
npm run clean # Clean build artifacts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Environment Setup
|
|
62
|
+
|
|
63
|
+
Copy `.env.example` to `.env.local` and customize:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
cp .env.example .env.local
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Required variables:
|
|
70
|
+
|
|
71
|
+
- `BETTER_AUTH_SECRET` - Random 32+ character string (auto-generated during setup)
|
|
72
|
+
- `RESEND_API_KEY` - For email verification (get from [Resend](https://resend.com))
|
|
73
|
+
|
|
74
|
+
Optional variables (production):
|
|
75
|
+
|
|
76
|
+
- `DATABASE_URL` - PostgreSQL connection string
|
|
77
|
+
- `BETTER_AUTH_URL` - Your production domain (https://yourdomain.com)
|
|
78
|
+
- `PUBLIC_APP_URL` - Same as BETTER_AUTH_URL
|
|
79
|
+
|
|
80
|
+
## Development
|
|
81
|
+
|
|
82
|
+
### Bundle Analysis
|
|
83
|
+
|
|
84
|
+
Analyze your bundle size to identify large dependencies and optimize performance:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run build:analyze
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This will build your application and generate a `stats.html` file in build output directory (`.vinxi`). Open this file in your browser to explore:
|
|
91
|
+
|
|
92
|
+
- **Treemap view** - Visual representation of module sizes
|
|
93
|
+
- **Gzip/Brotli sizes** - Real-world compression impact
|
|
94
|
+
- **Module hierarchy** - See which modules depend on what
|
|
95
|
+
|
|
96
|
+
**Tips for optimization:**
|
|
97
|
+
|
|
98
|
+
- Look for large dependencies that could be tree-shaken
|
|
99
|
+
- Identify duplicate code that can be deduplicated
|
|
100
|
+
- Check if route-based code splitting is working
|
|
101
|
+
- Remove unused dependencies when possible
|
|
102
|
+
|
|
103
|
+
### Performance Monitoring
|
|
104
|
+
|
|
105
|
+
Monitor application performance to identify bottlenecks:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Run Lighthouse CI to check performance budgets
|
|
109
|
+
npm run lighthouse
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The project includes performance monitoring tools:
|
|
113
|
+
|
|
114
|
+
**Slow Query Logging** (Development):
|
|
115
|
+
|
|
116
|
+
- Database queries over 1 second are logged to console
|
|
117
|
+
- Helps identify database performance issues
|
|
118
|
+
- Enabled automatically in development mode
|
|
119
|
+
|
|
120
|
+
**Client Hydration Timing** (Development):
|
|
121
|
+
|
|
122
|
+
- Track component hydration time
|
|
123
|
+
- Components taking over 100ms to hydrate trigger warnings
|
|
124
|
+
- Use `markHydrationStart()` and `markHydrationEnd()` in components
|
|
125
|
+
|
|
126
|
+
**Performance Budgets** (Lighthouse CI):
|
|
127
|
+
|
|
128
|
+
- First Contentful Paint: < 2000ms
|
|
129
|
+
- Largest Contentful Paint: < 2500ms
|
|
130
|
+
- Time to Interactive: < 3500ms
|
|
131
|
+
- JavaScript bundles: < 150KB
|
|
132
|
+
- Total page size: < 500KB
|
|
133
|
+
|
|
134
|
+
**Using Monitoring in Code:**
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Track database queries
|
|
138
|
+
import { measureQuery } from '@/lib/monitoring'
|
|
139
|
+
|
|
140
|
+
const result = await measureQuery('SELECT * FROM users', () => {
|
|
141
|
+
return db.query(users.findMany())
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// Track hydration
|
|
145
|
+
import { markHydrationStart, markHydrationEnd } from '@/lib/hydration-timing'
|
|
146
|
+
|
|
147
|
+
export function MyComponent() {
|
|
148
|
+
markHydrationStart('MyComponent')
|
|
149
|
+
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
markHydrationEnd('MyComponent')
|
|
152
|
+
}, [])
|
|
153
|
+
|
|
154
|
+
return <div>...</div>
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Adding New Features
|
|
159
|
+
|
|
160
|
+
**Database Tables**
|
|
161
|
+
|
|
162
|
+
1. Define schema in `src/server/db/schema.ts`
|
|
163
|
+
2. Generate migration: `npm run db:generate`
|
|
164
|
+
3. Apply migration: `npm run db:migrate`
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
export const posts = sqliteTable('posts', {
|
|
168
|
+
id: text('id')
|
|
169
|
+
.primaryKey()
|
|
170
|
+
.$defaultFn(() => createId()),
|
|
171
|
+
title: text('title').notNull(),
|
|
172
|
+
content: text('content').notNull(),
|
|
173
|
+
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
|
174
|
+
createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(
|
|
175
|
+
() => new Date()
|
|
176
|
+
),
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**tRPC Procedures**
|
|
181
|
+
|
|
182
|
+
1. Create router in `src/server/api/[feature].ts`
|
|
183
|
+
2. Add to root router in `src/server/api/root.ts`
|
|
184
|
+
3. Use in components via `api.[feature].[procedure].useQuery()`
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
export const postsRouter = router({
|
|
188
|
+
create: protectedProcedure
|
|
189
|
+
.input(z.object({ title: z.string(), content: z.string() }))
|
|
190
|
+
.mutation(async ({ ctx, input }) => {
|
|
191
|
+
return await ctx.db
|
|
192
|
+
.insert(posts)
|
|
193
|
+
.values({
|
|
194
|
+
...input,
|
|
195
|
+
userId: ctx.user.id,
|
|
196
|
+
})
|
|
197
|
+
.returning()
|
|
198
|
+
}),
|
|
199
|
+
})
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**New Pages**
|
|
203
|
+
|
|
204
|
+
1. Add route file in `src/app/[route].tsx`
|
|
205
|
+
2. Implement component with TanStack Start conventions
|
|
206
|
+
3. Add navigation in layout components
|
|
207
|
+
|
|
208
|
+
**Protected Pages**
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
export const Route = createFileRoute('/dashboard')({
|
|
212
|
+
beforeLoad: ({ context }) => {
|
|
213
|
+
if (!context.auth.user) {
|
|
214
|
+
throw redirect({ to: '/auth/login' })
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
component: Dashboard,
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Adding UI Components
|
|
222
|
+
|
|
223
|
+
1. Install shadcn component: `npx shadcn-ui add [component]`
|
|
224
|
+
2. Customize styling in component file or CSS variables
|
|
225
|
+
3. Create feature-specific components in `src/components/features/`
|
|
226
|
+
|
|
227
|
+
## Deployment
|
|
228
|
+
|
|
229
|
+
### Vercel (Recommended)
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
vercel deploy
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Set environment variables in Vercel dashboard and add production PostgreSQL database.
|
|
236
|
+
|
|
237
|
+
### Railway
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
railway up
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Railway provides built-in PostgreSQL hosting.
|
|
244
|
+
|
|
245
|
+
### Environment Variables for Production
|
|
246
|
+
|
|
247
|
+
- `BETTER_AUTH_SECRET` - New random 32+ character string
|
|
248
|
+
- `DATABASE_URL` - Production PostgreSQL connection string
|
|
249
|
+
- `BETTER_AUTH_URL` - Your production domain (https://yourdomain.com)
|
|
250
|
+
- `PUBLIC_APP_URL` - Same as BETTER_AUTH_URL
|
|
251
|
+
- `RESEND_API_KEY` - Production API key from Resend
|
|
252
|
+
|
|
253
|
+
### Database Migrations in Production
|
|
254
|
+
|
|
255
|
+
When deploying to production with PostgreSQL:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
npm run db:migrate
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Make sure to test migrations locally with PostgreSQL before deploying.
|
|
262
|
+
|
|
263
|
+
## Database Schema
|
|
264
|
+
|
|
265
|
+
The database includes the following tables:
|
|
266
|
+
|
|
267
|
+
- **users** - User accounts with email verification
|
|
268
|
+
- **sessions** - Active user sessions
|
|
269
|
+
- **accounts** - OAuth provider accounts (for future use)
|
|
270
|
+
- **verifications** - Email verification and password reset tokens
|
|
271
|
+
|
|
272
|
+
View and edit the schema in `src/server/db/schema.ts`.
|
|
273
|
+
|
|
274
|
+
## Authentication Flow
|
|
275
|
+
|
|
276
|
+
1. User registers with email/password
|
|
277
|
+
2. Email verification required before dashboard access
|
|
278
|
+
3. Sessions stored in database with automatic cleanup
|
|
279
|
+
4. CSRF protection enabled on all authenticated routes
|
|
280
|
+
5. Password reset flow available (requires Resend setup)
|
|
281
|
+
|
|
282
|
+
## Troubleshooting
|
|
283
|
+
|
|
284
|
+
### pnpm Native Module Build Issues
|
|
285
|
+
|
|
286
|
+
pnpm blocks build scripts for native modules by default. If you encounter better-sqlite3 issues:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
pnpm rebuild better-sqlite3
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Or use npm/yarn instead of pnpm.
|
|
293
|
+
|
|
294
|
+
### Drizzle-kit Migrate with SQLite
|
|
295
|
+
|
|
296
|
+
Use `drizzle-kit push` instead of `drizzle-kit migrate` for SQLite to avoid multi-statement SQL issues.
|
|
297
|
+
|
|
298
|
+
### Email Not Sending
|
|
299
|
+
|
|
300
|
+
1. Verify RESEND_API_KEY is set in `.env.local`
|
|
301
|
+
2. Check Resend dashboard for API key status
|
|
302
|
+
3. Verify email domain is verified in Resend
|
|
303
|
+
4. Configure SPF/DKIM records for email deliverability
|
|
304
|
+
|
|
305
|
+
### Database Locked (SQLite)
|
|
306
|
+
|
|
307
|
+
If you get "database is locked" errors:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
npm run clean
|
|
311
|
+
# Then restart dev server
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
This removes SQLite WAL files that can cause locking issues.
|
|
315
|
+
|
|
316
|
+
## Documentation
|
|
317
|
+
|
|
318
|
+
- [Architecture Overview](docs/architecture.md) - System design and key decisions
|
|
319
|
+
- [Adding Features](docs/adding-features.md) - Common patterns and examples
|
|
320
|
+
- [Deployment Guide](docs/deployment.md) - Platform-specific deployment instructions
|
|
321
|
+
- [Database Schema](docs/database.md) - Complete schema documentation
|
|
322
|
+
- [Troubleshooting](docs/troubleshooting.md) - Common issues and solutions
|
|
323
|
+
|
|
324
|
+
## Architecture Decisions
|
|
325
|
+
|
|
326
|
+
### Why TanStack Start over Next.js?
|
|
327
|
+
|
|
328
|
+
- Better TypeScript integration with less configuration
|
|
329
|
+
- File-based routing without App Router complexity
|
|
330
|
+
- Smaller bundle size for micro-SaaS use cases
|
|
331
|
+
|
|
332
|
+
### Why Better-auth over NextAuth?
|
|
333
|
+
|
|
334
|
+
- Simpler configuration for basic email/password auth
|
|
335
|
+
- Better TypeScript support with fewer generic type issues
|
|
336
|
+
- Database session storage built-in for scalability
|
|
337
|
+
|
|
338
|
+
### Why Drizzle over Prisma?
|
|
339
|
+
|
|
340
|
+
- Zero runtime overhead with compile-time query building
|
|
341
|
+
- Better migration control with SQL-first approach
|
|
342
|
+
- Lighter weight for simple SaaS data models
|
|
343
|
+
|
|
344
|
+
## License
|
|
345
|
+
|
|
346
|
+
MIT - See LICENSE file for details.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineConfig } from '@tanstack/start/config'
|
|
2
|
+
import { visualizer } from 'rollup-plugin-visualizer'
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
tsr: {
|
|
6
|
+
appDirectory: 'src/app',
|
|
7
|
+
routesDirectory: 'src/app',
|
|
8
|
+
},
|
|
9
|
+
vite: {
|
|
10
|
+
plugins: [
|
|
11
|
+
visualizer({
|
|
12
|
+
filename: 'stats.html',
|
|
13
|
+
open: process.env.ANALYZE_BUNDLE === 'true',
|
|
14
|
+
gzipSize: true,
|
|
15
|
+
brotliSize: true,
|
|
16
|
+
template: 'treemap',
|
|
17
|
+
}),
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
})
|