Skip to content

Commit baa97ce

Browse files
committed
Update dotenv version and enhance rule management features
- Updated dotenv package to version 17.2.2 for improved environment variable handling. - Introduced a new 'type' property in cursor rules to differentiate between 'rule' and 'command'. - Enhanced the HomePage component to support dynamic URL type selection and random slug generation for titles. - Updated API routes to handle the new 'type' property in cursor rules, ensuring proper data management and retrieval. - Improved file handling in the CLI to accommodate both rule and command types, including appropriate file extensions.
1 parent 1afb62e commit baa97ce

23 files changed

Lines changed: 297 additions & 37 deletions

File tree

.cursor/commands/purple-lake.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# API Endpoint Generator
2+
3+
This command helps create complete REST API endpoints with proper error handling, validation, and TypeScript types.
4+
5+
## Usage
6+
Use this command by typing `/api-endpoint` in Cursor when you need to create a new API route.
7+
8+
## Description
9+
Generates a full-featured API endpoint including:
10+
- Request/response TypeScript interfaces
11+
- Input validation with Zod schemas
12+
- Proper error handling and HTTP status codes
13+
- Database operations (if needed)
14+
- Authentication middleware integration
15+
- OpenAPI/Swagger documentation comments
16+
17+
## Examples
18+
19+
**Creating a user endpoint:**
20+
- Input: "Create a POST endpoint for user registration"
21+
- Generates: Complete API route with validation, password hashing, database insertion, and error handling
22+
23+
**Creating a data endpoint:**
24+
- Input: "Create a GET endpoint to fetch paginated products with filters"
25+
- Generates: Query parameter validation, database queries with pagination, proper response formatting
26+
27+
## Implementation Details
28+
29+
When you use this command, I will:
30+
31+
1. **Create the API route file** in the correct Next.js App Router structure (`app/api/[route]/route.ts`)
32+
33+
2. **Generate TypeScript interfaces** for request/response objects:
34+
```typescript
35+
interface CreateUserRequest {
36+
email: string
37+
password: string
38+
name: string
39+
}
40+
41+
interface CreateUserResponse {
42+
id: string
43+
email: string
44+
name: string
45+
createdAt: string
46+
}
47+
```
48+
49+
3. **Add Zod validation schemas**:
50+
```typescript
51+
const createUserSchema = z.object({
52+
email: z.string().email(),
53+
password: z.string().min(8),
54+
name: z.string().min(2)
55+
})
56+
```
57+
58+
4. **Include proper error handling**:
59+
```typescript
60+
try {
61+
// API logic here
62+
} catch (error) {
63+
if (error instanceof z.ZodError) {
64+
return NextResponse.json({ error: 'Validation failed', details: error.errors }, { status: 400 })
65+
}
66+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
67+
}
68+
```
69+
70+
5. **Add authentication checks** when needed:
71+
```typescript
72+
const session = await getSession()
73+
if (!session) {
74+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
75+
}
76+
```
77+
78+
6. **Include database operations** with proper transaction handling
79+
7. **Add OpenAPI documentation** comments for automatic API docs generation
80+
8. **Implement rate limiting** for public endpoints
81+
9. **Add request logging** and analytics tracking
82+
83+
## Best Practices Included
84+
- RESTful URL patterns and HTTP methods
85+
- Consistent error response formats
86+
- Input sanitization and validation
87+
- SQL injection prevention
88+
- CORS handling when needed
89+
- Response caching headers
90+
- Request/response compression

app/[userId]/[ruleId]/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ interface CursorRule {
1515
id: string
1616
title: string
1717
content: string
18+
type: string
1819
ruleType: string
1920
views: number
2021
createdAt: string

app/api/cursor-rules/route.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export async function GET(request: NextRequest) {
7171
id: cursorRule.id,
7272
title: cursorRule.title,
7373
content: cursorRule.content,
74+
type: cursorRule.type,
7475
ruleType: cursorRule.ruleType,
7576
isPublic: cursorRule.isPublic,
7677
views: cursorRule.views,
@@ -121,7 +122,7 @@ export async function POST(request: NextRequest) {
121122
}
122123

123124
const body = await request.json()
124-
const { title, content, ruleType = "always", isPublic = false } = body
125+
const { title, content, type = "rule", ruleType = "always", isPublic = false } = body
125126

126127
if (!title || !content) {
127128
return NextResponse.json({ error: "Title and content are required" }, { status: 400 })
@@ -135,14 +136,15 @@ export async function POST(request: NextRequest) {
135136
userId: session.user.id,
136137
title,
137138
content,
139+
type,
138140
ruleType,
139141
isPublic,
140142
views: 0,
141143
createdAt: now,
142144
updatedAt: now,
143145
}).returning()
144146

145-
await track('Rule Created', { ruleId: id, isPublic, ruleType })
147+
await track('Rule Created', { ruleId: id, isPublic, type, ruleType }, { request })
146148
return NextResponse.json(newRule)
147149
} catch (error) {
148150
console.error("Error creating cursor rule:", error)
@@ -182,7 +184,7 @@ export async function PUT(request: NextRequest) {
182184
}
183185

184186
const body = await request.json()
185-
const { id, title, content, ruleType, isPublic } = body
187+
const { id, title, content, type, ruleType, isPublic } = body
186188

187189
if (!id || !title || !content) {
188190
return NextResponse.json({ error: "ID, title and content are required" }, { status: 400 })
@@ -193,6 +195,7 @@ export async function PUT(request: NextRequest) {
193195
.set({
194196
title,
195197
content,
198+
type,
196199
ruleType,
197200
isPublic,
198201
updatedAt: new Date(),
@@ -207,7 +210,7 @@ export async function PUT(request: NextRequest) {
207210
return NextResponse.json({ error: "Rule not found or unauthorized" }, { status: 404 })
208211
}
209212

210-
await track('Rule Updated', { ruleId: id, isPublic: Boolean(isPublic), ruleType })
213+
await track('Rule Updated', { ruleId: id, isPublic: Boolean(isPublic), type, ruleType }, { request })
211214
return NextResponse.json(updatedRule)
212215
} catch (error) {
213216
console.error("Error updating cursor rule:", error)
@@ -262,7 +265,7 @@ export async function DELETE(request: NextRequest) {
262265
return NextResponse.json({ error: "Rule not found or unauthorized" }, { status: 404 })
263266
}
264267

265-
await track('Rule Deleted', { ruleId: id })
268+
await track('Rule Deleted', { ruleId: id }, { request })
266269
return NextResponse.json({ message: "Rule deleted successfully" })
267270
} catch (error) {
268271
console.error("Error deleting cursor rule:", error)

app/api/feed/hot/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export async function GET(request: NextRequest) {
1111
id: cursorRule.id,
1212
title: cursorRule.title,
1313
content: cursorRule.content,
14+
type: cursorRule.type,
1415
ruleType: cursorRule.ruleType,
1516
views: cursorRule.views,
1617
createdAt: cursorRule.createdAt,

app/api/feed/new/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export async function GET(request: NextRequest) {
1111
id: cursorRule.id,
1212
title: cursorRule.title,
1313
content: cursorRule.content,
14+
type: cursorRule.type,
1415
ruleType: cursorRule.ruleType,
1516
views: cursorRule.views,
1617
createdAt: cursorRule.createdAt,

app/api/my-rules/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export async function GET(request: NextRequest) {
4242
id: cursorRule.id,
4343
title: cursorRule.title,
4444
content: cursorRule.content,
45+
type: cursorRule.type,
4546
ruleType: cursorRule.ruleType,
4647
isPublic: cursorRule.isPublic,
4748
views: cursorRule.views,

app/api/public-rule/[userId]/[ruleId]/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export async function GET(
1515
id: cursorRule.id,
1616
title: cursorRule.title,
1717
content: cursorRule.content,
18+
type: cursorRule.type,
1819
ruleType: cursorRule.ruleType,
1920
views: cursorRule.views,
2021
createdAt: cursorRule.createdAt,

app/api/registry/[ruleId]/route.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export async function GET(
1616
id: cursorRule.id,
1717
title: cursorRule.title,
1818
content: cursorRule.content,
19+
type: cursorRule.type,
1920
ruleType: cursorRule.ruleType,
2021
userId: cursorRule.userId,
2122
user: {
@@ -36,15 +37,19 @@ export async function GET(
3637
}
3738

3839
// Generate universal registry item with content
40+
const isCommand = rule.type === 'command';
41+
const directory = isCommand ? 'commands' : 'rules';
42+
const extension = isCommand ? 'md' : 'mdc';
43+
3944
const registryItem = {
4045
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
4146
"name": rule.title,
4247
"type": "registry:item", // Changed to registry:item for universal items
4348
"files": [
4449
{
45-
"path": `cursor.link/rules/${rule.title}.mdc`, // Source path (not used but required)
50+
"path": `cursor.link/${directory}/${rule.title}.${extension}`, // Source path (not used but required)
4651
"type": "registry:file",
47-
"target": `~/.cursor/rules/${rule.title}.mdc`, // Explicit target makes it universal
52+
"target": `~/.cursor/${directory}/${rule.title}.${extension}`, // Explicit target makes it universal
4853
"content": rule.content // Include the actual content
4954
}
5055
]

app/api/registry/rules/[ruleId]/route.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export async function GET(
1616
id: cursorRule.id,
1717
title: cursorRule.title,
1818
content: cursorRule.content,
19+
type: cursorRule.type,
1920
ruleType: cursorRule.ruleType,
2021
})
2122
.from(cursorRule)
@@ -30,20 +31,21 @@ export async function GET(
3031
return NextResponse.json({ error: "Rule file not found" }, { status: 404 })
3132
}
3233

33-
// Generate .mdc file content dynamically
34-
const mdcContent = `---
34+
// Generate file content dynamically based on type
35+
const fileContent = `---
3536
description: ${rule.title}
3637
globs:
3738
alwaysApply: ${rule.ruleType === 'always' ? 'true' : 'false'}
3839
---
3940
4041
${rule.content}`
4142

42-
return new NextResponse(mdcContent, {
43+
return new NextResponse(fileContent, {
4344
status: 200,
4445
headers: {
4546
"Content-Type": "text/markdown",
4647
"Cache-Control": "public, max-age=300, s-maxage=300", // 5 minute cache
48+
"Content-Disposition": `attachment; filename="${rule.title}.${rule.type === 'command' ? 'md' : 'mdc'}"`,
4749
},
4850
})
4951
} catch (error) {

app/api/rule/[slug]/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export async function GET(
8181
id: cursorRule.id,
8282
title: cursorRule.title,
8383
content: cursorRule.content,
84+
type: cursorRule.type,
8485
ruleType: cursorRule.ruleType,
8586
isPublic: cursorRule.isPublic,
8687
views: cursorRule.views,

0 commit comments

Comments
 (0)