claude-code-templates 1.15.0 → 1.15.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.
- package/README.md +7 -7
- package/bin/create-claude-config.js +15 -8
- package/package.json +2 -3
- package/src/analytics/core/AgentAnalyzer.js +17 -3
- package/src/analytics/core/ProcessDetector.js +23 -7
- package/src/analytics/core/StateCalculator.js +102 -33
- package/src/analytics/data/DataCache.js +7 -7
- package/src/analytics-web/chats_mobile.html +2590 -0
- package/src/analytics-web/components/App.js +10 -10
- package/src/analytics-web/components/SessionTimer.js +1 -1
- package/src/analytics-web/components/Sidebar.js +5 -14
- package/src/analytics-web/index.html +932 -78
- package/src/analytics.js +263 -5
- package/src/chats-mobile.js +682 -0
- package/src/claude-api-proxy.js +460 -0
- package/src/file-operations.js +422 -83
- package/src/health-check.js +310 -0
- package/src/index.js +944 -56
- package/src/tracking-service.js +31 -34
- package/components/agents/api-security-audit.md +0 -92
- package/components/agents/database-optimization.md +0 -94
- package/components/agents/react-performance-optimization.md +0 -64
- package/components/commands/check-file.md +0 -53
- package/components/commands/generate-tests.md +0 -68
- package/components/mcps/deepgraph-nextjs.json +0 -12
- package/components/mcps/deepgraph-react.json +0 -12
- package/components/mcps/deepgraph-typescript.json +0 -12
- package/components/mcps/deepgraph-vue.json +0 -12
- package/components/mcps/filesystem-access.json +0 -12
- package/components/mcps/github-integration.json +0 -11
- package/components/mcps/memory-integration.json +0 -8
- package/components/mcps/mysql-integration.json +0 -11
- package/components/mcps/postgresql-integration.json +0 -11
- package/components/mcps/web-fetch.json +0 -8
- package/src/analytics-web/components/AgentsPage.js +0 -4761
- package/templates/common/.claude/commands/git-workflow.md +0 -239
- package/templates/common/.claude/commands/project-setup.md +0 -316
- package/templates/common/.mcp.json +0 -41
- package/templates/common/CLAUDE.md +0 -109
- package/templates/common/README.md +0 -96
- package/templates/go/.mcp.json +0 -78
- package/templates/go/README.md +0 -25
- package/templates/javascript-typescript/.claude/commands/api-endpoint.md +0 -51
- package/templates/javascript-typescript/.claude/commands/debug.md +0 -52
- package/templates/javascript-typescript/.claude/commands/lint.md +0 -48
- package/templates/javascript-typescript/.claude/commands/npm-scripts.md +0 -48
- package/templates/javascript-typescript/.claude/commands/refactor.md +0 -55
- package/templates/javascript-typescript/.claude/commands/test.md +0 -61
- package/templates/javascript-typescript/.claude/commands/typescript-migrate.md +0 -51
- package/templates/javascript-typescript/.claude/settings.json +0 -142
- package/templates/javascript-typescript/.mcp.json +0 -80
- package/templates/javascript-typescript/CLAUDE.md +0 -185
- package/templates/javascript-typescript/README.md +0 -259
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/components.md +0 -63
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/services.md +0 -62
- package/templates/javascript-typescript/examples/node-api/.claude/commands/api-endpoint.md +0 -46
- package/templates/javascript-typescript/examples/node-api/.claude/commands/database.md +0 -56
- package/templates/javascript-typescript/examples/node-api/.claude/commands/middleware.md +0 -61
- package/templates/javascript-typescript/examples/node-api/.claude/commands/route.md +0 -57
- package/templates/javascript-typescript/examples/node-api/CLAUDE.md +0 -102
- package/templates/javascript-typescript/examples/react-app/.claude/commands/component.md +0 -29
- package/templates/javascript-typescript/examples/react-app/.claude/commands/hooks.md +0 -44
- package/templates/javascript-typescript/examples/react-app/.claude/commands/state-management.md +0 -45
- package/templates/javascript-typescript/examples/react-app/CLAUDE.md +0 -81
- package/templates/javascript-typescript/examples/react-app/agents/react-performance-optimization.md +0 -530
- package/templates/javascript-typescript/examples/react-app/agents/react-state-management.md +0 -295
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/components.md +0 -46
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/composables.md +0 -51
- package/templates/python/.claude/commands/lint.md +0 -111
- package/templates/python/.claude/commands/test.md +0 -73
- package/templates/python/.claude/settings.json +0 -153
- package/templates/python/.mcp.json +0 -78
- package/templates/python/CLAUDE.md +0 -276
- package/templates/python/examples/django-app/.claude/commands/admin.md +0 -264
- package/templates/python/examples/django-app/.claude/commands/django-model.md +0 -124
- package/templates/python/examples/django-app/.claude/commands/views.md +0 -222
- package/templates/python/examples/django-app/CLAUDE.md +0 -313
- package/templates/python/examples/django-app/agents/django-api-security.md +0 -642
- package/templates/python/examples/django-app/agents/django-database-optimization.md +0 -752
- package/templates/python/examples/fastapi-app/.claude/commands/api-endpoints.md +0 -513
- package/templates/python/examples/fastapi-app/.claude/commands/auth.md +0 -775
- package/templates/python/examples/fastapi-app/.claude/commands/database.md +0 -657
- package/templates/python/examples/fastapi-app/.claude/commands/deployment.md +0 -160
- package/templates/python/examples/fastapi-app/.claude/commands/testing.md +0 -927
- package/templates/python/examples/fastapi-app/CLAUDE.md +0 -229
- package/templates/python/examples/flask-app/.claude/commands/app-factory.md +0 -384
- package/templates/python/examples/flask-app/.claude/commands/blueprint.md +0 -243
- package/templates/python/examples/flask-app/.claude/commands/database.md +0 -410
- package/templates/python/examples/flask-app/.claude/commands/deployment.md +0 -620
- package/templates/python/examples/flask-app/.claude/commands/flask-route.md +0 -217
- package/templates/python/examples/flask-app/.claude/commands/testing.md +0 -559
- package/templates/python/examples/flask-app/CLAUDE.md +0 -391
- package/templates/ruby/.claude/commands/model.md +0 -360
- package/templates/ruby/.claude/commands/test.md +0 -480
- package/templates/ruby/.claude/settings.json +0 -146
- package/templates/ruby/.mcp.json +0 -83
- package/templates/ruby/CLAUDE.md +0 -284
- package/templates/ruby/examples/rails-app/.claude/commands/authentication.md +0 -490
- package/templates/ruby/examples/rails-app/CLAUDE.md +0 -376
- package/templates/rust/.mcp.json +0 -78
- package/templates/rust/README.md +0 -26
|
@@ -1,513 +0,0 @@
|
|
|
1
|
-
# FastAPI Endpoints Generator
|
|
2
|
-
|
|
3
|
-
Create comprehensive FastAPI endpoints with proper structure, validation, and documentation.
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
This command helps you quickly create FastAPI endpoints with Pydantic models, dependency injection, and automatic API documentation.
|
|
8
|
-
|
|
9
|
-
## Usage
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
/api-endpoints
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## What this command does
|
|
16
|
-
|
|
17
|
-
1. **Creates API endpoints** with proper HTTP methods
|
|
18
|
-
2. **Adds Pydantic models** for request/response validation
|
|
19
|
-
3. **Implements dependency injection** for database and auth
|
|
20
|
-
4. **Includes error handling** and status codes
|
|
21
|
-
5. **Generates automatic documentation** with OpenAPI
|
|
22
|
-
|
|
23
|
-
## Example Output
|
|
24
|
-
|
|
25
|
-
```python
|
|
26
|
-
# main.py
|
|
27
|
-
from fastapi import FastAPI, Depends, HTTPException, status
|
|
28
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
29
|
-
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
30
|
-
from sqlalchemy.orm import Session
|
|
31
|
-
import uvicorn
|
|
32
|
-
|
|
33
|
-
from app.database import get_db, engine
|
|
34
|
-
from app.models import models
|
|
35
|
-
from app.routers import auth, users, posts, comments
|
|
36
|
-
from app.core.config import settings
|
|
37
|
-
|
|
38
|
-
# Create database tables
|
|
39
|
-
models.Base.metadata.create_all(bind=engine)
|
|
40
|
-
|
|
41
|
-
# Initialize FastAPI app
|
|
42
|
-
app = FastAPI(
|
|
43
|
-
title="Blog API",
|
|
44
|
-
description="A comprehensive blog API built with FastAPI",
|
|
45
|
-
version="1.0.0",
|
|
46
|
-
docs_url="/docs",
|
|
47
|
-
redoc_url="/redoc"
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
# Add CORS middleware
|
|
51
|
-
app.add_middleware(
|
|
52
|
-
CORSMiddleware,
|
|
53
|
-
allow_origins=settings.ALLOWED_HOSTS,
|
|
54
|
-
allow_credentials=True,
|
|
55
|
-
allow_methods=["*"],
|
|
56
|
-
allow_headers=["*"],
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
# Include routers
|
|
60
|
-
app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
|
|
61
|
-
app.include_router(users.router, prefix="/users", tags=["Users"])
|
|
62
|
-
app.include_router(posts.router, prefix="/posts", tags=["Posts"])
|
|
63
|
-
app.include_router(comments.router, prefix="/comments", tags=["Comments"])
|
|
64
|
-
|
|
65
|
-
@app.get("/", tags=["Root"])
|
|
66
|
-
async def root():
|
|
67
|
-
"""API root endpoint."""
|
|
68
|
-
return {
|
|
69
|
-
"message": "Welcome to Blog API",
|
|
70
|
-
"version": "1.0.0",
|
|
71
|
-
"docs": "/docs",
|
|
72
|
-
"redoc": "/redoc"
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@app.get("/health", tags=["Health"])
|
|
76
|
-
async def health_check():
|
|
77
|
-
"""Health check endpoint."""
|
|
78
|
-
return {"status": "healthy", "timestamp": "2024-01-01T00:00:00Z"}
|
|
79
|
-
|
|
80
|
-
if __name__ == "__main__":
|
|
81
|
-
uvicorn.run(
|
|
82
|
-
"main:app",
|
|
83
|
-
host="0.0.0.0",
|
|
84
|
-
port=8000,
|
|
85
|
-
reload=True
|
|
86
|
-
)
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
```python
|
|
90
|
-
# app/routers/posts.py
|
|
91
|
-
from typing import List, Optional
|
|
92
|
-
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
|
93
|
-
from sqlalchemy.orm import Session
|
|
94
|
-
|
|
95
|
-
from app.database import get_db
|
|
96
|
-
from app.schemas import post_schemas
|
|
97
|
-
from app.services import post_service
|
|
98
|
-
from app.core.dependencies import get_current_user, get_current_active_user
|
|
99
|
-
from app.models.user import User
|
|
100
|
-
|
|
101
|
-
router = APIRouter()
|
|
102
|
-
|
|
103
|
-
@router.get("/", response_model=List[post_schemas.PostResponse])
|
|
104
|
-
async def get_posts(
|
|
105
|
-
skip: int = Query(0, ge=0, description="Number of posts to skip"),
|
|
106
|
-
limit: int = Query(10, ge=1, le=100, description="Number of posts to return"),
|
|
107
|
-
search: Optional[str] = Query(None, description="Search in title and content"),
|
|
108
|
-
category: Optional[str] = Query(None, description="Filter by category"),
|
|
109
|
-
published: Optional[bool] = Query(True, description="Filter by published status"),
|
|
110
|
-
db: Session = Depends(get_db)
|
|
111
|
-
):
|
|
112
|
-
"""
|
|
113
|
-
Get all posts with pagination and filtering.
|
|
114
|
-
|
|
115
|
-
- **skip**: Number of posts to skip (for pagination)
|
|
116
|
-
- **limit**: Maximum number of posts to return (1-100)
|
|
117
|
-
- **search**: Search term for title and content
|
|
118
|
-
- **category**: Filter posts by category
|
|
119
|
-
- **published**: Filter by published status
|
|
120
|
-
"""
|
|
121
|
-
posts = post_service.get_posts(
|
|
122
|
-
db=db,
|
|
123
|
-
skip=skip,
|
|
124
|
-
limit=limit,
|
|
125
|
-
search=search,
|
|
126
|
-
category=category,
|
|
127
|
-
published=published
|
|
128
|
-
)
|
|
129
|
-
return posts
|
|
130
|
-
|
|
131
|
-
@router.get("/{post_id}", response_model=post_schemas.PostResponse)
|
|
132
|
-
async def get_post(
|
|
133
|
-
post_id: int,
|
|
134
|
-
db: Session = Depends(get_db)
|
|
135
|
-
):
|
|
136
|
-
"""
|
|
137
|
-
Get a specific post by ID.
|
|
138
|
-
|
|
139
|
-
- **post_id**: Unique identifier for the post
|
|
140
|
-
"""
|
|
141
|
-
post = post_service.get_post(db=db, post_id=post_id)
|
|
142
|
-
if not post:
|
|
143
|
-
raise HTTPException(
|
|
144
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
145
|
-
detail="Post not found"
|
|
146
|
-
)
|
|
147
|
-
return post
|
|
148
|
-
|
|
149
|
-
@router.post("/", response_model=post_schemas.PostResponse, status_code=status.HTTP_201_CREATED)
|
|
150
|
-
async def create_post(
|
|
151
|
-
post: post_schemas.PostCreate,
|
|
152
|
-
db: Session = Depends(get_db),
|
|
153
|
-
current_user: User = Depends(get_current_active_user)
|
|
154
|
-
):
|
|
155
|
-
"""
|
|
156
|
-
Create a new post.
|
|
157
|
-
|
|
158
|
-
- **title**: Post title (required)
|
|
159
|
-
- **content**: Post content (required)
|
|
160
|
-
- **category**: Post category (optional)
|
|
161
|
-
- **published**: Publication status (default: false)
|
|
162
|
-
"""
|
|
163
|
-
return post_service.create_post(
|
|
164
|
-
db=db,
|
|
165
|
-
post=post,
|
|
166
|
-
user_id=current_user.id
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
@router.put("/{post_id}", response_model=post_schemas.PostResponse)
|
|
170
|
-
async def update_post(
|
|
171
|
-
post_id: int,
|
|
172
|
-
post_update: post_schemas.PostUpdate,
|
|
173
|
-
db: Session = Depends(get_db),
|
|
174
|
-
current_user: User = Depends(get_current_active_user)
|
|
175
|
-
):
|
|
176
|
-
"""
|
|
177
|
-
Update an existing post.
|
|
178
|
-
|
|
179
|
-
- **post_id**: Unique identifier for the post
|
|
180
|
-
- **title**: Updated post title (optional)
|
|
181
|
-
- **content**: Updated post content (optional)
|
|
182
|
-
- **category**: Updated post category (optional)
|
|
183
|
-
- **published**: Updated publication status (optional)
|
|
184
|
-
"""
|
|
185
|
-
post = post_service.get_post(db=db, post_id=post_id)
|
|
186
|
-
if not post:
|
|
187
|
-
raise HTTPException(
|
|
188
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
189
|
-
detail="Post not found"
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
if post.author_id != current_user.id:
|
|
193
|
-
raise HTTPException(
|
|
194
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
|
195
|
-
detail="Not authorized to update this post"
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
return post_service.update_post(
|
|
199
|
-
db=db,
|
|
200
|
-
post_id=post_id,
|
|
201
|
-
post_update=post_update
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
@router.delete("/{post_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
205
|
-
async def delete_post(
|
|
206
|
-
post_id: int,
|
|
207
|
-
db: Session = Depends(get_db),
|
|
208
|
-
current_user: User = Depends(get_current_active_user)
|
|
209
|
-
):
|
|
210
|
-
"""
|
|
211
|
-
Delete a post.
|
|
212
|
-
|
|
213
|
-
- **post_id**: Unique identifier for the post to delete
|
|
214
|
-
"""
|
|
215
|
-
post = post_service.get_post(db=db, post_id=post_id)
|
|
216
|
-
if not post:
|
|
217
|
-
raise HTTPException(
|
|
218
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
219
|
-
detail="Post not found"
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
if post.author_id != current_user.id:
|
|
223
|
-
raise HTTPException(
|
|
224
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
|
225
|
-
detail="Not authorized to delete this post"
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
post_service.delete_post(db=db, post_id=post_id)
|
|
229
|
-
|
|
230
|
-
@router.post("/{post_id}/like", response_model=post_schemas.PostResponse)
|
|
231
|
-
async def like_post(
|
|
232
|
-
post_id: int,
|
|
233
|
-
db: Session = Depends(get_db),
|
|
234
|
-
current_user: User = Depends(get_current_active_user)
|
|
235
|
-
):
|
|
236
|
-
"""
|
|
237
|
-
Like/unlike a post.
|
|
238
|
-
|
|
239
|
-
- **post_id**: Unique identifier for the post to like
|
|
240
|
-
"""
|
|
241
|
-
post = post_service.get_post(db=db, post_id=post_id)
|
|
242
|
-
if not post:
|
|
243
|
-
raise HTTPException(
|
|
244
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
245
|
-
detail="Post not found"
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
return post_service.toggle_like(
|
|
249
|
-
db=db,
|
|
250
|
-
post_id=post_id,
|
|
251
|
-
user_id=current_user.id
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
@router.get("/{post_id}/comments", response_model=List[post_schemas.CommentResponse])
|
|
255
|
-
async def get_post_comments(
|
|
256
|
-
post_id: int,
|
|
257
|
-
skip: int = Query(0, ge=0),
|
|
258
|
-
limit: int = Query(10, ge=1, le=100),
|
|
259
|
-
db: Session = Depends(get_db)
|
|
260
|
-
):
|
|
261
|
-
"""
|
|
262
|
-
Get all comments for a specific post.
|
|
263
|
-
|
|
264
|
-
- **post_id**: Unique identifier for the post
|
|
265
|
-
- **skip**: Number of comments to skip
|
|
266
|
-
- **limit**: Maximum number of comments to return
|
|
267
|
-
"""
|
|
268
|
-
post = post_service.get_post(db=db, post_id=post_id)
|
|
269
|
-
if not post:
|
|
270
|
-
raise HTTPException(
|
|
271
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
272
|
-
detail="Post not found"
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
return post_service.get_post_comments(
|
|
276
|
-
db=db,
|
|
277
|
-
post_id=post_id,
|
|
278
|
-
skip=skip,
|
|
279
|
-
limit=limit
|
|
280
|
-
)
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
```python
|
|
284
|
-
# app/schemas/post_schemas.py
|
|
285
|
-
from typing import Optional, List
|
|
286
|
-
from datetime import datetime
|
|
287
|
-
from pydantic import BaseModel, Field, validator
|
|
288
|
-
|
|
289
|
-
class PostBase(BaseModel):
|
|
290
|
-
"""Base post schema."""
|
|
291
|
-
title: str = Field(..., min_length=1, max_length=200, description="Post title")
|
|
292
|
-
content: str = Field(..., min_length=1, description="Post content")
|
|
293
|
-
category: Optional[str] = Field(None, max_length=50, description="Post category")
|
|
294
|
-
published: bool = Field(False, description="Publication status")
|
|
295
|
-
|
|
296
|
-
class PostCreate(PostBase):
|
|
297
|
-
"""Schema for creating a post."""
|
|
298
|
-
|
|
299
|
-
@validator('title')
|
|
300
|
-
def validate_title(cls, v):
|
|
301
|
-
if not v.strip():
|
|
302
|
-
raise ValueError('Title cannot be empty')
|
|
303
|
-
return v.strip()
|
|
304
|
-
|
|
305
|
-
@validator('content')
|
|
306
|
-
def validate_content(cls, v):
|
|
307
|
-
if len(v.strip()) < 10:
|
|
308
|
-
raise ValueError('Content must be at least 10 characters long')
|
|
309
|
-
return v.strip()
|
|
310
|
-
|
|
311
|
-
class PostUpdate(BaseModel):
|
|
312
|
-
"""Schema for updating a post."""
|
|
313
|
-
title: Optional[str] = Field(None, min_length=1, max_length=200)
|
|
314
|
-
content: Optional[str] = Field(None, min_length=1)
|
|
315
|
-
category: Optional[str] = Field(None, max_length=50)
|
|
316
|
-
published: Optional[bool] = None
|
|
317
|
-
|
|
318
|
-
@validator('title')
|
|
319
|
-
def validate_title(cls, v):
|
|
320
|
-
if v is not None and not v.strip():
|
|
321
|
-
raise ValueError('Title cannot be empty')
|
|
322
|
-
return v.strip() if v else v
|
|
323
|
-
|
|
324
|
-
@validator('content')
|
|
325
|
-
def validate_content(cls, v):
|
|
326
|
-
if v is not None and len(v.strip()) < 10:
|
|
327
|
-
raise ValueError('Content must be at least 10 characters long')
|
|
328
|
-
return v.strip() if v else v
|
|
329
|
-
|
|
330
|
-
class PostResponse(PostBase):
|
|
331
|
-
"""Schema for post responses."""
|
|
332
|
-
id: int
|
|
333
|
-
author_id: int
|
|
334
|
-
created_at: datetime
|
|
335
|
-
updated_at: datetime
|
|
336
|
-
like_count: int = 0
|
|
337
|
-
comment_count: int = 0
|
|
338
|
-
|
|
339
|
-
class Config:
|
|
340
|
-
from_attributes = True
|
|
341
|
-
|
|
342
|
-
class CommentBase(BaseModel):
|
|
343
|
-
"""Base comment schema."""
|
|
344
|
-
content: str = Field(..., min_length=1, max_length=1000, description="Comment content")
|
|
345
|
-
|
|
346
|
-
class CommentCreate(CommentBase):
|
|
347
|
-
"""Schema for creating a comment."""
|
|
348
|
-
post_id: int = Field(..., description="ID of the post to comment on")
|
|
349
|
-
|
|
350
|
-
class CommentResponse(CommentBase):
|
|
351
|
-
"""Schema for comment responses."""
|
|
352
|
-
id: int
|
|
353
|
-
post_id: int
|
|
354
|
-
author_id: int
|
|
355
|
-
created_at: datetime
|
|
356
|
-
updated_at: datetime
|
|
357
|
-
|
|
358
|
-
class Config:
|
|
359
|
-
from_attributes = True
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
```python
|
|
363
|
-
# app/services/post_service.py
|
|
364
|
-
from typing import List, Optional
|
|
365
|
-
from sqlalchemy.orm import Session
|
|
366
|
-
from sqlalchemy import or_
|
|
367
|
-
|
|
368
|
-
from app.models.post import Post, Comment, PostLike
|
|
369
|
-
from app.schemas.post_schemas import PostCreate, PostUpdate
|
|
370
|
-
|
|
371
|
-
def get_posts(
|
|
372
|
-
db: Session,
|
|
373
|
-
skip: int = 0,
|
|
374
|
-
limit: int = 10,
|
|
375
|
-
search: Optional[str] = None,
|
|
376
|
-
category: Optional[str] = None,
|
|
377
|
-
published: Optional[bool] = True
|
|
378
|
-
) -> List[Post]:
|
|
379
|
-
"""Get posts with filtering and pagination."""
|
|
380
|
-
query = db.query(Post)
|
|
381
|
-
|
|
382
|
-
if published is not None:
|
|
383
|
-
query = query.filter(Post.published == published)
|
|
384
|
-
|
|
385
|
-
if category:
|
|
386
|
-
query = query.filter(Post.category == category)
|
|
387
|
-
|
|
388
|
-
if search:
|
|
389
|
-
query = query.filter(
|
|
390
|
-
or_(
|
|
391
|
-
Post.title.contains(search),
|
|
392
|
-
Post.content.contains(search)
|
|
393
|
-
)
|
|
394
|
-
)
|
|
395
|
-
|
|
396
|
-
return query.offset(skip).limit(limit).all()
|
|
397
|
-
|
|
398
|
-
def get_post(db: Session, post_id: int) -> Optional[Post]:
|
|
399
|
-
"""Get a single post by ID."""
|
|
400
|
-
return db.query(Post).filter(Post.id == post_id).first()
|
|
401
|
-
|
|
402
|
-
def create_post(db: Session, post: PostCreate, user_id: int) -> Post:
|
|
403
|
-
"""Create a new post."""
|
|
404
|
-
db_post = Post(**post.dict(), author_id=user_id)
|
|
405
|
-
db.add(db_post)
|
|
406
|
-
db.commit()
|
|
407
|
-
db.refresh(db_post)
|
|
408
|
-
return db_post
|
|
409
|
-
|
|
410
|
-
def update_post(
|
|
411
|
-
db: Session,
|
|
412
|
-
post_id: int,
|
|
413
|
-
post_update: PostUpdate
|
|
414
|
-
) -> Optional[Post]:
|
|
415
|
-
"""Update an existing post."""
|
|
416
|
-
db_post = db.query(Post).filter(Post.id == post_id).first()
|
|
417
|
-
if not db_post:
|
|
418
|
-
return None
|
|
419
|
-
|
|
420
|
-
update_data = post_update.dict(exclude_unset=True)
|
|
421
|
-
for field, value in update_data.items():
|
|
422
|
-
setattr(db_post, field, value)
|
|
423
|
-
|
|
424
|
-
db.commit()
|
|
425
|
-
db.refresh(db_post)
|
|
426
|
-
return db_post
|
|
427
|
-
|
|
428
|
-
def delete_post(db: Session, post_id: int) -> bool:
|
|
429
|
-
"""Delete a post."""
|
|
430
|
-
db_post = db.query(Post).filter(Post.id == post_id).first()
|
|
431
|
-
if not db_post:
|
|
432
|
-
return False
|
|
433
|
-
|
|
434
|
-
db.delete(db_post)
|
|
435
|
-
db.commit()
|
|
436
|
-
return True
|
|
437
|
-
|
|
438
|
-
def toggle_like(db: Session, post_id: int, user_id: int) -> Post:
|
|
439
|
-
"""Toggle like status for a post."""
|
|
440
|
-
existing_like = db.query(PostLike).filter(
|
|
441
|
-
PostLike.post_id == post_id,
|
|
442
|
-
PostLike.user_id == user_id
|
|
443
|
-
).first()
|
|
444
|
-
|
|
445
|
-
if existing_like:
|
|
446
|
-
db.delete(existing_like)
|
|
447
|
-
else:
|
|
448
|
-
new_like = PostLike(post_id=post_id, user_id=user_id)
|
|
449
|
-
db.add(new_like)
|
|
450
|
-
|
|
451
|
-
db.commit()
|
|
452
|
-
return get_post(db, post_id)
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
## Features Included
|
|
456
|
-
|
|
457
|
-
### API Documentation
|
|
458
|
-
- **Automatic OpenAPI** schema generation
|
|
459
|
-
- **Interactive docs** at `/docs`
|
|
460
|
-
- **ReDoc documentation** at `/redoc`
|
|
461
|
-
- **Request/Response examples** in schemas
|
|
462
|
-
|
|
463
|
-
### Validation & Serialization
|
|
464
|
-
- **Pydantic models** for data validation
|
|
465
|
-
- **Custom validators** for business rules
|
|
466
|
-
- **Type hints** for better IDE support
|
|
467
|
-
- **Automatic data conversion** and validation
|
|
468
|
-
|
|
469
|
-
### Error Handling
|
|
470
|
-
- **HTTP status codes** for different scenarios
|
|
471
|
-
- **Detailed error messages** with context
|
|
472
|
-
- **Input validation errors** with field-specific messages
|
|
473
|
-
- **Custom exception handlers** for consistent responses
|
|
474
|
-
|
|
475
|
-
### Security
|
|
476
|
-
- **JWT authentication** with dependencies
|
|
477
|
-
- **Role-based access control** for endpoints
|
|
478
|
-
- **CORS middleware** for cross-origin requests
|
|
479
|
-
- **Input sanitization** through Pydantic
|
|
480
|
-
|
|
481
|
-
### Performance
|
|
482
|
-
- **Database query optimization** with SQLAlchemy
|
|
483
|
-
- **Pagination support** for large datasets
|
|
484
|
-
- **Async/await support** for concurrent requests
|
|
485
|
-
- **Connection pooling** for database efficiency
|
|
486
|
-
|
|
487
|
-
## Testing Example
|
|
488
|
-
|
|
489
|
-
```python
|
|
490
|
-
# tests/test_posts.py
|
|
491
|
-
import pytest
|
|
492
|
-
from fastapi.testclient import TestClient
|
|
493
|
-
from app.main import app
|
|
494
|
-
|
|
495
|
-
client = TestClient(app)
|
|
496
|
-
|
|
497
|
-
def test_get_posts():
|
|
498
|
-
"""Test getting posts."""
|
|
499
|
-
response = client.get("/posts/")
|
|
500
|
-
assert response.status_code == 200
|
|
501
|
-
assert isinstance(response.json(), list)
|
|
502
|
-
|
|
503
|
-
def test_create_post():
|
|
504
|
-
"""Test creating a new post."""
|
|
505
|
-
post_data = {
|
|
506
|
-
"title": "Test Post",
|
|
507
|
-
"content": "This is a test post content.",
|
|
508
|
-
"published": True
|
|
509
|
-
}
|
|
510
|
-
response = client.post("/posts/", json=post_data)
|
|
511
|
-
assert response.status_code == 201
|
|
512
|
-
assert response.json()["title"] == "Test Post"
|
|
513
|
-
```
|