chore: import upstream snapshot with attribution
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:38:58 +08:00
commit bb5c75ce05
8824 changed files with 1946442 additions and 0 deletions
@@ -0,0 +1,239 @@
# Git Workflow Helper
Manage Git workflows with best practices and common operations.
## Purpose
This command helps you perform common Git operations following best practices for collaborative development.
## Usage
```
/git-workflow
```
## What this command does
1. **Guides through Git operations** with proper workflow
2. **Suggests best practices** for commits and branches
3. **Helps with conflict resolution** and merging
4. **Provides templates** for commit messages
5. **Manages branching strategies** (Git Flow, GitHub Flow)
## Common Workflows
### Feature Development
```bash
# Create and switch to feature branch
git checkout -b feature/user-authentication
# Work on your feature
# ... make changes ...
# Stage and commit changes
git add .
git commit -m "feat: add user authentication system
- Implement login/logout functionality
- Add password validation
- Create user session management
- Add authentication middleware"
# Push feature branch
git push -u origin feature/user-authentication
# Create pull request (via GitHub/GitLab interface)
```
### Hotfix Workflow
```bash
# Create hotfix branch from main
git checkout main
git checkout -b hotfix/security-patch
# Fix the issue
# ... make changes ...
# Commit the fix
git commit -m "fix: resolve security vulnerability in auth module
- Patch XSS vulnerability in login form
- Update input validation
- Add CSRF protection"
# Push and create urgent PR
git push -u origin hotfix/security-patch
```
### Sync with Remote
```bash
# Update main branch
git checkout main
git pull origin main
# Update feature branch with latest main
git checkout feature/your-feature
git rebase main
# OR
git merge main
```
## Commit Message Conventions
### Conventional Commits Format
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
### Common Types
- **feat**: New feature
- **fix**: Bug fix
- **docs**: Documentation changes
- **style**: Code style changes (formatting, etc.)
- **refactor**: Code refactoring
- **test**: Adding or updating tests
- **chore**: Maintenance tasks
### Examples
```bash
# Feature
git commit -m "feat(auth): add OAuth2 integration"
# Bug fix
git commit -m "fix(api): handle null response in user endpoint"
# Documentation
git commit -m "docs: update API documentation for v2.0"
# Breaking change
git commit -m "feat!: change API response format
BREAKING CHANGE: API responses now use 'data' wrapper"
```
## Branch Management
### Git Flow Strategy
```bash
# Main branches
main # Production-ready code
develop # Integration branch
# Supporting branches
feature/* # New features
release/* # Release preparation
hotfix/* # Quick fixes to production
```
### GitHub Flow (Simplified)
```bash
# Only main branch + feature branches
main # Production-ready code
feature/* # All new work
```
## Conflict Resolution
### When Conflicts Occur
```bash
# Start merge/rebase
git merge feature-branch
# OR
git rebase main
# If conflicts occur, Git will list conflicted files
# Edit each file to resolve conflicts
# Mark conflicts as resolved
git add conflicted-file.js
# Continue the merge/rebase
git merge --continue
# OR
git rebase --continue
```
### Conflict Markers
```javascript
<<<<<<< HEAD
// Your current branch code
const user = getCurrentUser();
=======
// Incoming branch code
const user = getAuthenticatedUser();
>>>>>>> feature-branch
```
## Useful Git Commands
### Status and Information
```bash
git status # Check working directory status
git log --oneline # View commit history
git branch -a # List all branches
git remote -v # List remote repositories
```
### Undoing Changes
```bash
git checkout -- file.js # Discard changes to file
git reset HEAD file.js # Unstage file
git reset --soft HEAD~1 # Undo last commit (keep changes)
git reset --hard HEAD~1 # Undo last commit (discard changes)
```
### Stashing Work
```bash
git stash # Save current work
git stash pop # Apply and remove latest stash
git stash list # List all stashes
git stash apply stash@{1} # Apply specific stash
```
## Best Practices
1. **Commit Often** - Make small, focused commits
2. **Write Clear Messages** - Use conventional commit format
3. **Test Before Committing** - Ensure code works
4. **Pull Before Push** - Keep history clean
5. **Use Branches** - Don't work directly on main
6. **Review Code** - Use pull requests for collaboration
7. **Keep History Clean** - Rebase feature branches when appropriate
## Git Hooks (Optional)
### Pre-commit Hook
```bash
#!/bin/sh
# .git/hooks/pre-commit
# Run linter
npm run lint
if [ $? -ne 0 ]; then
echo "Linting failed. Please fix errors before committing."
exit 1
fi
# Run tests
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Please fix tests before committing."
exit 1
fi
```
### Commit Message Hook
```bash
#!/bin/sh
# .git/hooks/commit-msg
# Check commit message format
if ! grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}" "$1"; then
echo "Invalid commit message format. Use conventional commits."
exit 1
fi
```
@@ -0,0 +1,316 @@
# Project Setup Helper
Set up new projects with proper structure and best practices.
## Purpose
This command helps you set up new projects with proper directory structure, configuration files, and development environment.
## Usage
```
/project-setup
```
## What this command does
1. **Creates project structure** with standard directories
2. **Sets up configuration files** (.gitignore, README, etc.)
3. **Initializes version control** and development tools
4. **Configures environment files** and dependencies
5. **Follows language-specific** conventions and best practices
## Project Structure Templates
### Generic Project Structure
```
project-name/
├── README.md # Project documentation
├── .gitignore # Git ignore rules
├── .env.example # Environment variables template
├── LICENSE # Project license
├── CHANGELOG.md # Version history
├── docs/ # Documentation
│ ├── API.md
│ ├── CONTRIBUTING.md
│ └── DEPLOYMENT.md
├── src/ # Source code
│ ├── main/
│ ├── utils/
│ └── config/
├── tests/ # Test files
│ ├── unit/
│ ├── integration/
│ └── fixtures/
├── scripts/ # Build and deployment scripts
│ ├── build.sh
│ ├── deploy.sh
│ └── setup.sh
└── config/ # Configuration files
├── development.yml
├── production.yml
└── testing.yml
```
### Web Application Structure
```
web-app/
├── public/ # Static assets
│ ├── index.html
│ ├── favicon.ico
│ └── assets/
│ ├── css/
│ ├── js/
│ └── images/
├── src/ # Source code
│ ├── components/ # Reusable components
│ ├── pages/ # Page components
│ ├── services/ # API services
│ ├── utils/ # Utility functions
│ ├── styles/ # Stylesheets
│ └── config/ # Configuration
├── tests/ # Test files
└── build/ # Build output
```
### API Project Structure
```
api-project/
├── src/
│ ├── controllers/ # Request handlers
│ ├── models/ # Data models
│ ├── services/ # Business logic
│ ├── middleware/ # Custom middleware
│ ├── routes/ # API routes
│ ├── utils/ # Utility functions
│ └── config/ # Configuration
├── tests/
│ ├── unit/
│ ├── integration/
│ └── e2e/
├── docs/
│ ├── api-spec.yml # OpenAPI specification
│ └── README.md
└── scripts/
├── seed-db.js # Database seeding
└── migrate.js # Database migrations
```
## Essential Configuration Files
### .gitignore Template
```gitignore
# Dependencies
node_modules/
*.log
npm-debug.log*
# Environment files
.env
.env.local
.env.production
# Build outputs
dist/
build/
*.tgz
# IDE files
.vscode/
.idea/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
# Temporary files
*.tmp
*.temp
```
### README.md Template
```markdown
# Project Name
Brief description of what this project does.
## Features
- Feature 1
- Feature 2
- Feature 3
## Installation
\`\`\`bash
# Clone the repository
git clone https://github.com/username/project-name.git
# Install dependencies
npm install
# Copy environment file
cp .env.example .env
\`\`\`
## Usage
\`\`\`bash
# Start development server
npm run dev
# Run tests
npm test
# Build for production
npm run build
\`\`\`
## API Documentation
[API documentation](docs/API.md)
## Contributing
Please read [CONTRIBUTING.md](docs/CONTRIBUTING.md) for details.
## License
This project is licensed under the MIT License - see [LICENSE](LICENSE) file.
```
### .env.example Template
```env
# Application
NODE_ENV=development
PORT=3000
HOST=localhost
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=username
DB_PASSWORD=password
# API Keys
API_KEY=your-api-key-here
SECRET_KEY=your-secret-key-here
# External Services
REDIS_URL=redis://localhost:6379
EMAIL_SERVICE_API_KEY=your-email-key
```
## Development Environment Setup
### Package.json Scripts
```json
{
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"format": "prettier --write src/",
"build": "webpack --mode production",
"build:dev": "webpack --mode development",
"clean": "rm -rf dist/"
}
}
```
### Git Initialization
```bash
# Initialize Git repository
git init
# Add initial files
git add .
# Create initial commit
git commit -m "feat: initial project setup
- Add project structure
- Configure development environment
- Add documentation templates
- Set up testing framework"
# Add remote origin
git remote add origin https://github.com/username/project-name.git
# Push to remote
git push -u origin main
```
## Language-Specific Setup
### Node.js/JavaScript
```bash
npm init -y
npm install --save-dev nodemon jest eslint prettier
```
### Python
```bash
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
```
### Docker Setup
```dockerfile
# Dockerfile
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
```
```yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
volumes:
- .:/app
- /app/node_modules
```
## Best Practices
1. **Consistent Structure** - Follow established conventions
2. **Clear Documentation** - Write comprehensive README
3. **Environment Variables** - Keep secrets out of code
4. **Version Control** - Initialize Git from the start
5. **Testing Setup** - Configure testing from day one
6. **Code Quality** - Set up linting and formatting
7. **CI/CD Ready** - Structure for automated deployment
8. **Security** - Include security best practices
## Checklist
- [ ] Create project directory structure
- [ ] Initialize version control (Git)
- [ ] Set up package manager (npm, pip, etc.)
- [ ] Create README.md with project info
- [ ] Add .gitignore file
- [ ] Set up environment variables
- [ ] Configure linting and formatting
- [ ] Set up testing framework
- [ ] Create basic documentation
- [ ] Add license file
- [ ] Set up CI/CD configuration (if needed)
+41
View File
@@ -0,0 +1,41 @@
{
"mcpServers": {
"memory-bank": {
"name": "Memory Bank MCP",
"description": "Centralized memory system for AI agents",
"command": "server-memory",
"args": [],
"env": {}
},
"sequential-thinking": {
"name": "Sequential Thinking MCP",
"description": "Helps LLMs decompose complex tasks into logical steps",
"command": "code-reasoning",
"args": [],
"env": {}
},
"brave-search": {
"name": "Brave Search MCP",
"description": "Privacy-focused web search tool",
"command": "server-brave-search",
"args": [],
"env": {}
},
"google-maps": {
"name": "Google Maps MCP",
"description": "Integrates Google Maps for geolocation and directions",
"command": "server-google-maps",
"args": [],
"env": {
"GOOGLE_MAPS_API_KEY": "..."
}
},
"deep-graph": {
"name": "Deep Graph MCP (Code Graph)",
"description": "Transforms source code into semantic graphs via DeepGraph",
"command": "mcp-code-graph",
"args": [],
"env": {}
}
}
}
+109
View File
@@ -0,0 +1,109 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Universal Development Guidelines
### Code Quality Standards
- Write clean, readable, and maintainable code
- Follow consistent naming conventions across the project
- Use meaningful variable and function names
- Keep functions focused and single-purpose
- Add comments for complex logic and business rules
### Git Workflow
- Use descriptive commit messages following conventional commits format
- Create feature branches for new development
- Keep commits atomic and focused on single changes
- Use pull requests for code review before merging
- Maintain a clean commit history
### Documentation
- Keep README.md files up to date
- Document public APIs and interfaces
- Include usage examples for complex features
- Maintain inline code documentation
- Update documentation when making changes
### Testing Approach
- Write tests for new features and bug fixes
- Maintain good test coverage
- Use descriptive test names that explain the expected behavior
- Organize tests logically by feature or module
- Run tests before committing changes
### Security Best Practices
- Never commit sensitive information (API keys, passwords, tokens)
- Use environment variables for configuration
- Validate input data and sanitize outputs
- Follow principle of least privilege
- Keep dependencies updated
## Project Structure Guidelines
### File Organization
- Group related files in logical directories
- Use consistent file and folder naming conventions
- Separate source code from configuration files
- Keep build artifacts out of version control
- Organize assets and resources appropriately
### Configuration Management
- Use configuration files for environment-specific settings
- Centralize configuration in dedicated files
- Use environment variables for sensitive or environment-specific data
- Document configuration options and their purposes
- Provide example configuration files
## Development Workflow
### Before Starting Work
1. Pull latest changes from main branch
2. Create a new feature branch
3. Review existing code and architecture
4. Plan the implementation approach
### During Development
1. Make incremental commits with clear messages
2. Run tests frequently to catch issues early
3. Follow established coding standards
4. Update documentation as needed
### Before Submitting
1. Run full test suite
2. Check code quality and formatting
3. Update documentation if necessary
4. Create clear pull request description
## Common Patterns
### Error Handling
- Use appropriate error handling mechanisms for the language
- Provide meaningful error messages
- Log errors appropriately for debugging
- Handle edge cases gracefully
- Don't expose sensitive information in error messages
### Performance Considerations
- Profile code for performance bottlenecks
- Optimize database queries and API calls
- Use caching where appropriate
- Consider memory usage and resource management
- Monitor and measure performance metrics
### Code Reusability
- Extract common functionality into reusable modules
- Use dependency injection for better testability
- Create utility functions for repeated operations
- Design interfaces for extensibility
- Follow DRY (Don't Repeat Yourself) principle
## Review Checklist
Before marking any task as complete:
- [ ] Code follows established conventions
- [ ] Tests are written and passing
- [ ] Documentation is updated
- [ ] Security considerations are addressed
- [ ] Performance impact is considered
- [ ] Code is reviewed for maintainability
+96
View File
@@ -0,0 +1,96 @@
# Common Claude Code Templates
This folder contains language-agnostic templates and configurations that can be used across different programming languages and project types.
## What's Included
### Core Files
- `CLAUDE.md` - Base configuration for Claude Code with universal best practices
- `.claude/commands/` - Custom commands for common development tasks
### Common Custom Commands
- `git-workflow.md` - Git operations and workflow automation
- `code-review.md` - Code review and quality assurance tasks
- `project-setup.md` - Project initialization and configuration
- `documentation.md` - Documentation generation and maintenance
## How to Use
### For New Projects
Copy the entire `common/` folder contents to your project root:
```bash
cp -r claude-code-templates/common/* your-project/
```
### For Existing Projects
Merge the relevant files with your existing Claude Code configuration:
```bash
# Copy custom commands
cp -r claude-code-templates/common/.claude/commands/* your-project/.claude/commands/
# Review and merge CLAUDE.md content
cat claude-code-templates/common/CLAUDE.md >> your-project/CLAUDE.md
```
## Customization
### CLAUDE.md
The base CLAUDE.md file includes:
- Universal development best practices
- Common project patterns and conventions
- Git workflow guidelines
- Code quality standards
Customize it by:
- Adding project-specific information
- Modifying coding standards to match your team's preferences
- Including technology-specific guidelines
### Custom Commands
The included commands are designed to be generic and widely applicable. You can:
- Modify existing commands to match your workflow
- Add new commands for project-specific tasks
- Remove commands that don't apply to your project
## Language-Specific Integration
This common template is designed to work alongside language-specific templates:
1. Start with the common template
2. Add language-specific configurations from the appropriate folder
3. Customize both to match your project needs
## Examples
### Multi-language Projects
For projects using multiple programming languages:
```bash
# Copy common base
cp -r claude-code-templates/common/* your-project/
# Add language-specific configurations
cp -r claude-code-templates/javascript-typescript/.claude/commands/* your-project/.claude/commands/
cp -r claude-code-templates/python/.claude/commands/* your-project/.claude/commands/
```
### Team Standardization
Organizations can use this as a base template and customize it for their specific needs:
1. Fork this repository
2. Modify the common templates to match your standards
3. Distribute to development teams
4. Keep synchronized with updates
## Best Practices
- **Start Simple**: Begin with the common template and add complexity as needed
- **Document Changes**: Keep track of customizations in your project's documentation
- **Regular Updates**: Periodically review and update your Claude Code configuration
- **Team Alignment**: Ensure all team members understand the custom commands and workflows
## Contributing
If you create useful generic commands or improvements to the common template, consider contributing them back to this repository to help other developers.
+78
View File
@@ -0,0 +1,78 @@
{
"mcpServers": {
"go-sdk": {
"name": "Go SDK",
"description": "Official SDK maintained with Google for Go MCP development",
"command": "go-sdk-server",
"args": [],
"env": {}
},
"language-server": {
"name": "MCP Language Server",
"description": "Semantic tools for Go: definitions, references, diagnostics",
"command": "mcp-language-server",
"args": [],
"env": {}
},
"gin": {
"name": "Gin MCP",
"description": "Expose Gin APIs automatically as MCP tools",
"command": "gin-mcp",
"args": [],
"env": {}
},
"mysql": {
"name": "Go MySQL MCP",
"description": "Easy-to-use MySQL MCP server built in Go",
"command": "go-mcp-mysql",
"args": [],
"env": {
"MYSQL_DSN": "user:pass@tcp(host)/db"
}
},
"archer": {
"name": "Go Archer",
"description": "Visual dependency analysis for Go packages",
"command": "go-archer",
"args": [],
"env": {}
},
"memory-bank": {
"name": "Memory Bank MCP",
"description": "Centralized memory system for AI agents",
"command": "server-memory",
"args": [],
"env": {}
},
"sequential-thinking": {
"name": "Sequential Thinking MCP",
"description": "Helps LLMs decompose complex tasks into logical steps",
"command": "code-reasoning",
"args": [],
"env": {}
},
"brave-search": {
"name": "Brave Search MCP",
"description": "Privacy-focused web search tool",
"command": "server-brave-search",
"args": [],
"env": {}
},
"google-maps": {
"name": "Google Maps MCP",
"description": "Integrates Google Maps for geolocation and directions",
"command": "server-google-maps",
"args": [],
"env": {
"GOOGLE_MAPS_API_KEY": "..."
}
},
"deep-graph": {
"name": "Deep Graph MCP (Code Graph)",
"description": "Transforms source code into semantic graphs via DeepGraph",
"command": "mcp-code-graph",
"args": [],
"env": {}
}
}
}
+25
View File
@@ -0,0 +1,25 @@
# Go Claude Code Templates
## Coming Soon! 🚧
We're actively working on creating comprehensive Claude Code templates for Go development.
### What to Expect
- Best practices for Go project structure
- Integration with popular Go tools and frameworks
- Workflow optimizations for Go development
- Testing and benchmarking configurations
- Deployment and build automation
### Meanwhile...
You can use the [common templates](../common/README.md) as a starting point for your Go projects. The universal guidelines and git workflows will work well with Go development.
### Stay Updated
⭐ Star this repository to get notified when the Go templates are released!
### Contributing
Interested in helping build these templates? We welcome contributions! Please check the main repository's contribution guidelines and feel free to open an issue or pull request.
---
*Expected release: Coming soon*
@@ -0,0 +1,51 @@
# API Endpoint Generator
Generate a complete API endpoint for $ARGUMENTS following project conventions.
## Task
I'll analyze the project structure and create a new API endpoint with:
1. Route definition
2. Controller/handler function
3. Input validation
4. Service layer logic (if applicable)
5. Data access layer (if applicable)
6. Unit tests
7. Documentation
## Process
I'll follow these steps:
1. Examine the project structure to understand the architecture pattern
2. Identify existing patterns for routes, controllers, and validation
3. Create all necessary files following project conventions
4. Implement the endpoint with proper error handling
5. Add appropriate tests
6. Document the new endpoint
## Best Practices
I'll ensure the implementation includes:
- Strong typing with TypeScript
- Comprehensive error handling
- Input validation
- Security considerations (authentication/authorization)
- Proper logging
- Performance considerations
- Test coverage
## Adaptability
I'll adapt to various API architectures:
- Express/Koa/Fastify REST APIs
- GraphQL resolvers
- Next.js/Nuxt.js API routes
- Serverless functions
- tRPC endpoints
- NestJS controllers
I'll examine your project first to determine which pattern to follow.
@@ -0,0 +1,52 @@
# Debug Assistant
Help me debug the issue with $ARGUMENTS in this project.
## Task
I'll help you identify and fix the problem by:
1. Understanding the issue description
2. Analyzing relevant code
3. Identifying potential causes
4. Suggesting and implementing fixes
5. Verifying the solution works
## Process
I'll follow these steps:
1. Examine error messages, logs, or unexpected behaviors
2. Locate relevant files and code sections
3. Analyze the code flow and potential failure points
4. Identify common JavaScript/TypeScript pitfalls that might apply
5. Suggest specific fixes with explanations
6. Help implement and test the solution
## Debugging Techniques
I'll apply appropriate debugging techniques such as:
- Static code analysis to find syntax or type errors
- Runtime error analysis from logs or stack traces
- Control flow tracing to understand execution paths
- State inspection to identify incorrect values
- Dependency analysis to find version conflicts
- Network request inspection for API issues
- Browser console analysis for frontend problems
- Database query inspection for data issues
## Common Issues I Can Help With
- Type errors and null/undefined issues
- Asynchronous code problems (Promises, async/await)
- React/Vue/Angular component lifecycle issues
- API integration problems
- State management bugs
- Performance bottlenecks
- Memory leaks
- Build/compilation errors
- Testing failures
- Environment configuration issues
I'll adapt my approach based on your specific project structure, frameworks, and the nature of the problem.
@@ -0,0 +1,48 @@
# Lint Assistant
Analyze and fix linting issues in $ARGUMENTS following project conventions.
## Task
I'll help you identify and fix code style and quality issues by:
1. Running appropriate linters for the project
2. Analyzing linting errors and warnings
3. Fixing issues automatically when possible
4. Explaining complex issues that require manual intervention
5. Ensuring code follows project style guidelines
## Process
I'll follow these steps:
1. Identify the linting tools used in the project (ESLint, Prettier, TSLint, etc.)
2. Run the appropriate linting commands
3. Parse and categorize the results
4. Apply automatic fixes for common issues
5. Provide explanations and suggestions for more complex problems
6. Verify fixes don't introduce new issues
## Common Linting Issues I Can Fix
- Code style inconsistencies (spacing, indentation, quotes, etc.)
- Unused variables and imports
- Missing type annotations in TypeScript
- Accessibility (a11y) issues in UI components
- Potential bugs flagged by static analysis
- Performance issues in React/Vue components
- Security vulnerabilities detected by linters
- Deprecated API usage
- Import ordering problems
- Missing documentation
## Linting Tools I Can Work With
- ESLint (with various plugins and configs)
- Prettier
- TSLint (legacy)
- stylelint (for CSS/SCSS)
- commitlint (for commit messages)
- Custom lint rules specific to your project
I'll adapt my approach based on your project's specific linting configuration and style guide requirements.
@@ -0,0 +1,48 @@
# NPM Scripts Assistant
Help me with NPM scripts: $ARGUMENTS
## Task
I'll help you work with package.json scripts by:
1. Analyzing existing npm scripts in your project
2. Creating new scripts or modifying existing ones
3. Explaining what specific scripts do
4. Suggesting improvements or optimizations
5. Troubleshooting script execution issues
## Process
I'll follow these steps:
1. Examine your package.json file to understand current scripts
2. Analyze dependencies and devDependencies for available tools
3. Identify common patterns and conventions in your scripts
4. Implement requested changes or create new scripts
5. Provide explanations of how the scripts work
6. Test scripts when possible to verify functionality
## Common Script Types I Can Help With
- Build processes (webpack, rollup, esbuild, etc.)
- Development servers and hot reloading
- Testing (unit, integration, e2e)
- Linting and code formatting
- Type checking
- Deployment and CI/CD
- Database migrations
- Code generation
- Environment setup
- Pre/post hooks for git operations
## Script Optimization Techniques
- Parallelizing tasks for faster execution
- Adding cross-platform compatibility
- Improving error reporting and logging
- Implementing watch modes for development
- Creating composite scripts for common workflows
- Adding appropriate exit codes for CI/CD pipelines
I'll adapt my approach based on your project's specific needs, dependencies, and build tools.
@@ -0,0 +1,55 @@
# Code Refactoring Assistant
Refactor $ARGUMENTS following modern JavaScript/TypeScript best practices.
## Task
I'll help you refactor code by:
1. Analyzing the current implementation
2. Identifying improvement opportunities
3. Applying modern patterns and practices
4. Maintaining existing functionality
5. Ensuring type safety and test coverage
6. Documenting the changes made
## Process
I'll follow these steps:
1. Examine the code to understand its purpose and structure
2. Identify code smells, anti-patterns, or outdated approaches
3. Plan the refactoring strategy with clear goals
4. Implement changes incrementally while maintaining behavior
5. Verify refactored code with tests
6. Document improvements and benefits
## Refactoring Techniques
I can apply various refactoring techniques:
- Converting to modern JavaScript/TypeScript features
- Improving type definitions and type safety
- Extracting reusable functions and components
- Applying design patterns appropriately
- Converting callbacks to Promises or async/await
- Simplifying complex conditionals and loops
- Removing duplicate code
- Improving naming and readability
- Optimizing performance
- Enhancing error handling
## Modern Practices I Can Apply
- ES modules and import/export syntax
- Optional chaining and nullish coalescing
- Array and object destructuring
- Spread and rest operators
- Template literals
- Arrow functions
- Class fields and private methods
- TypeScript utility types
- Functional programming patterns
- React hooks (for React components)
I'll ensure the refactored code maintains compatibility with your project's requirements while improving quality and maintainability.
@@ -0,0 +1,61 @@
# Test Assistant
Help with tests for $ARGUMENTS following project conventions and testing best practices.
## Task
I'll help you with testing by:
1. Creating comprehensive test suites for your code
2. Implementing different types of tests (unit, integration, e2e)
3. Mocking dependencies and external services
4. Improving test coverage for existing code
5. Troubleshooting failing tests
6. Setting up testing infrastructure
## Process
I'll follow these steps:
1. Examine your project to understand testing frameworks and patterns
2. Analyze the code to be tested to understand its functionality
3. Identify appropriate testing strategies and edge cases
4. Implement tests with proper structure and assertions
5. Ensure tests are maintainable and follow best practices
6. Run tests to verify they pass and provide adequate coverage
## Testing Frameworks I Can Work With
- Jest, Vitest, Mocha, Jasmine for JavaScript/TypeScript
- React Testing Library, Enzyme for React components
- Cypress, Playwright, Puppeteer for E2E testing
- Supertest, Pactum for API testing
- Storybook for component testing
- Testing-library family for various frameworks
## Testing Techniques
I can implement various testing approaches:
- TDD (Test-Driven Development)
- BDD (Behavior-Driven Development)
- Snapshot testing
- Property-based testing
- Parameterized tests
- Contract testing
- Visual regression testing
- Performance testing
## Mocking Strategies
I can help with different mocking approaches:
- Function mocks and spies
- Module mocks
- HTTP request mocking
- Browser API mocking
- Timer mocking
- Database mocking
- Service worker mocking
I'll adapt to your project's specific testing frameworks, patterns, and conventions to ensure consistency with your existing codebase.
@@ -0,0 +1,51 @@
# TypeScript Migration Assistant
Migrate $ARGUMENTS from JavaScript to TypeScript with proper typing and modern practices.
## Task
I'll help you migrate JavaScript code to TypeScript by:
1. Converting JavaScript files to TypeScript (.js/.jsx to .ts/.tsx)
2. Adding appropriate type definitions and interfaces
3. Configuring TypeScript settings for your project
4. Resolving type errors and compatibility issues
5. Implementing modern TypeScript patterns and features
6. Ensuring backward compatibility with existing code
## Process
I'll follow these steps:
1. Examine your project structure and dependencies
2. Analyze the JavaScript code to understand its functionality
3. Create or update tsconfig.json with appropriate settings
4. Convert files to TypeScript with proper type annotations
5. Add necessary type definitions for libraries
6. Fix type errors and implement type safety
7. Refactor code to leverage TypeScript features
## TypeScript Features I Can Implement
- Interfaces and type aliases for complex data structures
- Generics for reusable, type-safe components and functions
- Union and intersection types for flexible typing
- Enums for related constants
- Utility types (Partial, Pick, Omit, etc.)
- Type guards and type narrowing
- Mapped and conditional types
- Function overloading
- Readonly properties and immutability
- Module augmentation for extending third-party types
## Migration Strategies
I can apply different migration approaches based on your needs:
- Gradual migration with allowJs and incremental adoption
- File-by-file conversion while maintaining functionality
- Comprehensive migration with full type safety
- Hybrid approach with .d.ts files for complex modules
- Integration with existing type definitions (@types packages)
I'll adapt to your project's specific requirements and ensure a smooth transition to TypeScript while maintaining existing functionality.
@@ -0,0 +1,142 @@
{
"permissions": {
"allow": [
"Bash",
"Edit",
"MultiEdit",
"Write",
"Bash(npm:*)",
"Bash(yarn:*)",
"Bash(npx:*)",
"Bash(node:*)",
"Bash(git:*)",
"Bash(eslint:*)",
"Bash(prettier:*)",
"Bash(tsc:*)",
"Bash(jest:*)",
"Bash(vitest:*)",
"Bash(webpack:*)",
"Bash(vite:*)"
],
"deny": [
"Bash(curl:*)",
"Bash(wget:*)",
"Bash(rm -rf:*)"
],
"defaultMode": "allowEdits"
},
"env": {
"BASH_DEFAULT_TIMEOUT_MS": "60000",
"BASH_MAX_OUTPUT_LENGTH": "20000",
"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "1",
"NODE_ENV": "development"
},
"includeCoAuthoredBy": true,
"cleanupPeriodDays": 30,
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \"No description\")\"' >> ~/.claude/bash-command-log.txt"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"); CONTENT=$(echo $STDIN_JSON | jq -r '.tool_input.content // \"\"); if [[ \"$FILE\" =~ \\.(js|jsx|ts|tsx)$ ]] && echo \"$CONTENT\" | grep -q 'console\\.log'; then echo 'Warning: console.log statements should be removed before committing' >&2; exit 2; fi"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" == \"package.json\" ]]; then echo 'Checking for vulnerable dependencies...'; npm audit --audit-level=moderate; fi",
"timeout": 60
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.(js|jsx|ts|tsx)$ ]]; then npx prettier --write \"$FILE\"; fi",
"timeout": 30
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.(ts|tsx)$ ]]; then RESULT=$(npx tsc --noEmit 2>&1); if [ $? -ne 0 ]; then echo \"TypeScript errors found: $RESULT\" >&2; exit 2; fi; fi",
"timeout": 30
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.(js|jsx|ts|tsx)$ ]] && grep -q 'import \\* from' \"$FILE\"; then echo 'Warning: Avoid wildcard imports for better tree-shaking' >&2; exit 2; fi"
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.(js|jsx|ts|tsx)$ && \"$FILE\" != *\".test.\"* && \"$FILE\" != *\".spec.\"* ]]; then DIR=$(dirname \"$FILE\"); BASENAME=$(basename \"$FILE\" | sed -E 's/\\.(js|jsx|ts|tsx)$//'); for TEST_FILE in \"$DIR/$BASENAME.test.\"* \"$DIR/$BASENAME.spec.\"*; do if [ -f \"$TEST_FILE\" ]; then echo \"Running tests for $TEST_FILE...\"; if command -v jest >/dev/null 2>&1; then npx jest \"$TEST_FILE\" --passWithNoTests; elif command -v vitest >/dev/null 2>&1; then npx vitest run \"$TEST_FILE\"; fi; break; fi; done; fi",
"timeout": 60
}
]
}
],
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "echo \"Claude Code notification: $(date)\" >> ~/.claude/notifications.log"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "if [[ -f package.json && $(git status --porcelain | grep -E '\\.js$|\\.jsx$|\\.ts$|\\.tsx$') ]]; then echo 'Running linter on changed files...'; npx eslint $(git diff --name-only --diff-filter=ACMR | grep -E '\\.js$|\\.jsx$|\\.ts$|\\.tsx$'); fi",
"timeout": 60
}
]
},
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "if [[ -f package.json && $(git status --porcelain | grep -E '\\.js$|\\.jsx$|\\.ts$|\\.tsx$') ]]; then echo 'Analyzing bundle size impact...'; if command -v bundlesize >/dev/null 2>&1; then npx bundlesize; elif npm list webpack-bundle-analyzer >/dev/null 2>&1; then echo 'Use: npx webpack-bundle-analyzer build/static/js/*.js'; else echo 'No bundle analysis tool found'; fi; fi",
"timeout": 60
}
]
}
]
}
}
@@ -0,0 +1,80 @@
{
"mcpServers": {
"typescript-sdk": {
"name": "TypeScript SDK",
"description": "Official Anthropic SDK for building MCP servers and clients in JS/TS",
"command": "node",
"args": ["path/to/ts-sdk-server.js"],
"env": {}
},
"github": {
"name": "GitHub MCP",
"description": "Integration with GitHub API for managing repos, issues, and PRs",
"command": "node",
"args": ["path/to/server-github"],
"env": {
"GITHUB_TOKEN": "..."
}
},
"puppeteer": {
"name": "Puppeteer MCP",
"description": "Browser automation using Google Puppeteer",
"command": "node",
"args": ["path/to/server-puppeteer"],
"env": {}
},
"slack": {
"name": "Slack MCP",
"description": "Access to real-time Slack conversations and workflows",
"command": "node",
"args": ["path/to/server-slack"],
"env": {
"SLACK_TOKEN": "..."
}
},
"filesystem": {
"name": "File System MCP",
"description": "Local file management; compatible with any language",
"command": "node",
"args": ["path/to/server-filesystem"],
"env": {}
},
"memory-bank": {
"name": "Memory Bank MCP",
"description": "Centralized memory system for AI agents",
"command": "server-memory",
"args": [],
"env": {}
},
"sequential-thinking": {
"name": "Sequential Thinking MCP",
"description": "Helps LLMs decompose complex tasks into logical steps",
"command": "code-reasoning",
"args": [],
"env": {}
},
"brave-search": {
"name": "Brave Search MCP",
"description": "Privacy-focused web search tool",
"command": "server-brave-search",
"args": [],
"env": {}
},
"google-maps": {
"name": "Google Maps MCP",
"description": "Integrates Google Maps for geolocation and directions",
"command": "server-google-maps",
"args": [],
"env": {
"GOOGLE_MAPS_API_KEY": "..."
}
},
"deep-graph": {
"name": "Deep Graph MCP (Code Graph)",
"description": "Transforms source code into semantic graphs via DeepGraph",
"command": "mcp-code-graph",
"args": [],
"env": {}
}
}
}
@@ -0,0 +1,185 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is a JavaScript/TypeScript project optimized for modern web development. The project uses industry-standard tools and follows best practices for scalable application development.
## Development Commands
### Package Management
- `npm install` or `yarn install` - Install dependencies
- `npm ci` or `yarn install --frozen-lockfile` - Install dependencies for CI/CD
- `npm update` or `yarn upgrade` - Update dependencies
### Build Commands
- `npm run build` - Build the project for production
- `npm run dev` or `npm start` - Start development server
- `npm run preview` - Preview production build locally
### Testing Commands
- `npm test` or `npm run test` - Run all tests
- `npm run test:watch` - Run tests in watch mode
- `npm run test:coverage` - Run tests with coverage report
- `npm run test:unit` - Run unit tests only
- `npm run test:integration` - Run integration tests only
- `npm run test:e2e` - Run end-to-end tests
### Code Quality Commands
- `npm run lint` - Run ESLint for code linting
- `npm run lint:fix` - Run ESLint with auto-fix
- `npm run format` - Format code with Prettier
- `npm run format:check` - Check code formatting
- `npm run typecheck` - Run TypeScript type checking
### Development Tools
- `npm run storybook` - Start Storybook (if available)
- `npm run analyze` - Analyze bundle size
- `npm run clean` - Clean build artifacts
## Technology Stack
### Core Technologies
- **JavaScript/TypeScript** - Primary programming languages
- **Node.js** - Runtime environment
- **npm/yarn** - Package management
### Common Frameworks
- **React** - UI library with hooks and functional components
- **Vue.js** - Progressive framework for building user interfaces
- **Angular** - Full-featured framework for web applications
- **Express.js** - Web application framework for Node.js
- **Next.js** - React framework with SSR/SSG capabilities
### Build Tools
- **Vite** - Fast build tool and development server
- **Webpack** - Module bundler
- **Rollup** - Module bundler for libraries
- **esbuild** - Extremely fast JavaScript bundler
### Testing Framework
- **Jest** - JavaScript testing framework
- **Vitest** - Fast unit test framework
- **Testing Library** - Simple and complete testing utilities
- **Cypress** - End-to-end testing framework
- **Playwright** - Cross-browser testing
### Code Quality Tools
- **ESLint** - JavaScript/TypeScript linter
- **Prettier** - Code formatter
- **TypeScript** - Static type checking
- **Husky** - Git hooks
## Project Structure Guidelines
### File Organization
```
src/
├── components/ # Reusable UI components
├── pages/ # Page components or routes
├── hooks/ # Custom React hooks
├── utils/ # Utility functions
├── services/ # API calls and external services
├── types/ # TypeScript type definitions
├── constants/ # Application constants
├── styles/ # Global styles and themes
└── tests/ # Test files
```
### Naming Conventions
- **Files**: Use kebab-case for file names (`user-profile.component.ts`)
- **Components**: Use PascalCase for component names (`UserProfile`)
- **Functions**: Use camelCase for function names (`getUserData`)
- **Constants**: Use UPPER_SNAKE_CASE for constants (`API_BASE_URL`)
- **Types/Interfaces**: Use PascalCase with descriptive names (`UserData`, `ApiResponse`)
## TypeScript Guidelines
### Type Safety
- Enable strict mode in `tsconfig.json`
- Use explicit types for function parameters and return values
- Prefer interfaces over types for object shapes
- Use union types for multiple possible values
- Avoid `any` type - use `unknown` when type is truly unknown
### Best Practices
- Use type guards for runtime type checking
- Leverage utility types (`Partial`, `Pick`, `Omit`, etc.)
- Create custom types for domain-specific data
- Use enums for finite sets of values
- Document complex types with JSDoc comments
## Code Quality Standards
### ESLint Configuration
- Use recommended ESLint rules for JavaScript/TypeScript
- Enable React-specific rules if using React
- Configure import/export rules for consistent module usage
- Set up accessibility rules for inclusive development
### Prettier Configuration
- Use consistent indentation (2 spaces recommended)
- Set maximum line length (80-100 characters)
- Use single quotes for strings
- Add trailing commas for better git diffs
### Testing Standards
- Aim for 80%+ test coverage
- Write unit tests for utilities and business logic
- Use integration tests for component interactions
- Implement e2e tests for critical user flows
- Follow AAA pattern (Arrange, Act, Assert)
## Performance Optimization
### Bundle Optimization
- Use code splitting for large applications
- Implement lazy loading for routes and components
- Optimize images and assets
- Use tree shaking to eliminate dead code
- Analyze bundle size regularly
### Runtime Performance
- Implement proper memoization (React.memo, useMemo, useCallback)
- Use virtualization for large lists
- Optimize re-renders in React applications
- Implement proper error boundaries
- Use web workers for heavy computations
## Security Guidelines
### Dependencies
- Regularly audit dependencies with `npm audit`
- Keep dependencies updated
- Use lock files (`package-lock.json`, `yarn.lock`)
- Avoid dependencies with known vulnerabilities
### Code Security
- Sanitize user inputs
- Use HTTPS for API calls
- Implement proper authentication and authorization
- Store sensitive data securely (environment variables)
- Use Content Security Policy (CSP) headers
## Development Workflow
### Before Starting
1. Check Node.js version compatibility
2. Install dependencies with `npm install`
3. Copy environment variables from `.env.example`
4. Run type checking with `npm run typecheck`
### During Development
1. Use TypeScript for type safety
2. Run linter frequently to catch issues early
3. Write tests for new features
4. Use meaningful commit messages
5. Review code changes before committing
### Before Committing
1. Run full test suite: `npm test`
2. Check linting: `npm run lint`
3. Verify formatting: `npm run format:check`
4. Run type checking: `npm run typecheck`
5. Test production build: `npm run build`
@@ -0,0 +1,259 @@
# JavaScript/TypeScript Templates
**Claude Code configuration template optimized for modern JavaScript and TypeScript development**
This folder contains a comprehensive Claude Code template specifically designed for JavaScript and TypeScript projects, supporting popular frameworks like React, Vue.js, Angular, and Node.js.
## 📁 What's in This Folder
This template provides the foundation for JavaScript/TypeScript development with Claude Code:
### 📄 Files Included
- **`CLAUDE.md`** - Complete JavaScript/TypeScript development guidance for Claude Code
- **`README.md`** - This documentation file
### 🎯 Template Features
When you use this template with the installer, it automatically creates:
- **`.claude/settings.json`** - Optimized settings for JS/TS projects
- **`.claude/commands/`** - Ready-to-use commands for common tasks
## 🚀 How to Use This Template
### Option 1: Automated Installation (Recommended)
Use the CLI installer to automatically set up this template in your project:
```bash
cd your-javascript-project
npx claude-code-templates --language javascript-typescript
```
The installer will:
- Copy the `CLAUDE.md` file to your project
- Auto-detect your framework (React, Vue, Node.js, etc.)
- Create appropriate `.claude/` configuration files
- Set up framework-specific commands
- Configure development workflows
### Option 2: Manual Installation
Copy the template manually for more control:
```bash
# Clone the repository
git clone https://github.com/davila7/claude-code-templates.git
# Copy the JavaScript/TypeScript template
cp claude-code-templates/javascript-typescript/CLAUDE.md your-project/
# Then use the CLI to complete the setup
cd your-project
npx claude-code-templates --language javascript-typescript
```
## 🎨 Framework Support
This template automatically configures Claude Code for:
### Frontend Frameworks
- **React** - Components, hooks, JSX, testing with React Testing Library
- **Vue.js** - Composition API, single-file components, state management
- **Angular** - TypeScript-first development, RxJS patterns, CLI integration
- **Svelte** - Compile-time optimizations, modern JavaScript patterns
### Backend Frameworks
- **Express.js** - RESTful APIs, middleware, error handling
- **Fastify** - High-performance Node.js applications
- **NestJS** - Enterprise-grade TypeScript framework
- **Next.js** - Full-stack React applications with SSR/SSG
### Build Tools & Testing
- **Vite, Webpack, esbuild** - Modern build tool configurations
- **Jest, Vitest, Cypress** - Testing framework optimization
- **ESLint, Prettier, TypeScript** - Code quality and formatting
## 🛠️ Commands Created by the Template
When installed, this template provides commands for:
### 🧪 Testing & Quality
- **`/test`** - Run tests with Jest, Vitest, or other frameworks
- **`/lint`** - ESLint with auto-fix capabilities
- **`/typescript-migrate`** - Convert JavaScript files to TypeScript
### 🔧 Development Tools
- **`/debug`** - Debug Node.js applications and browser code
- **`/refactor`** - AI-assisted code refactoring
- **`/npm-scripts`** - Manage and execute npm/yarn scripts
### ⚡ Framework-Specific Commands
- **`/react-component`** - Generate React components (React projects)
- **`/api-endpoint`** - Create Express.js endpoints (Node.js projects)
- **`/route`** - Create API routes (Node.js projects)
- **`/component`** - Create components (React/Vue projects)
## 🎯 What Happens When You Install
### Step 1: Framework Detection
The installer analyzes your project to detect:
- Package.json dependencies
- Project structure
- Framework type (React, Vue, Angular, Node.js)
### Step 2: Template Configuration
Based on detection, it creates:
```
your-project/
├── CLAUDE.md # Copied from this template
├── .claude/
│ ├── settings.json # Framework-specific settings
│ └── commands/ # Commands for your framework
│ ├── test.md
│ ├── lint.md
│ ├── debug.md
│ └── [framework-specific commands]
```
### Step 3: Framework Customization
For specific frameworks, additional commands are added:
**React Projects:**
- Component generation with TypeScript support
- React hooks creation and management
- Testing with React Testing Library patterns
**Node.js Projects:**
- RESTful API endpoint creation
- Middleware development patterns
- Database integration helpers
**Vue.js Projects:**
- Single-file component templates
- Composition API patterns
- Vue 3 best practices
## 📚 What's in the CLAUDE.md File
The `CLAUDE.md` file in this folder contains comprehensive guidance for:
### Development Commands
- Package management (npm, yarn, pnpm)
- Build commands (dev, build, preview)
- Testing commands (unit, integration, e2e)
- Code quality commands (lint, format, typecheck)
### Technology Stack Guidelines
- JavaScript/TypeScript best practices
- Framework-specific patterns (React, Vue, Angular, Node.js)
- Build tools configuration (Vite, Webpack, esbuild)
- Testing frameworks (Jest, Vitest, Cypress, Playwright)
### Project Structure Recommendations
- File organization patterns
- Naming conventions
- TypeScript configuration
- Code quality standards
### Performance & Security
- Bundle optimization strategies
- Runtime performance tips
- Security best practices
- Dependency management
## 🚀 Getting Started
1. **Navigate to your JavaScript/TypeScript project:**
```bash
cd your-project
```
2. **Run the installer:**
```bash
npx claude-code-templates --language javascript-typescript
```
3. **Start Claude Code:**
```bash
claude
```
4. **Try the commands:**
```bash
/test # Run your tests
/lint # Check code quality
/component # Create components (React/Vue)
/route # Create API routes (Node.js)
```
## 🔧 Customization
After installation, you can customize the setup:
### Modify Commands
Edit files in `.claude/commands/` to match your workflow:
```bash
# Edit the test command
vim .claude/commands/test.md
# Add a custom command
echo "# Deploy Command" > .claude/commands/deploy.md
```
### Adjust Settings
Update `.claude/settings.json` for your project:
```json
{
"framework": "react",
"testFramework": "jest",
"packageManager": "npm",
"buildTool": "vite"
}
```
### Add Framework Features
The template adapts to your specific framework needs automatically.
## 📖 Learn More
- **Main Project**: [Claude Code Templates](../README.md)
- **Common Templates**: [Universal patterns](../common/README.md)
- **Python Templates**: [Python development](../python/README.md)
- **CLI Tool**: [Automated installer](../cli-tool/README.md)
## 💡 Why Use This Template?
### Before (Manual Setup)
```bash
# Create CLAUDE.md from scratch
# Research JS/TS best practices
# Configure commands manually
# Set up linting and testing
# Configure TypeScript
# ... hours of setup
```
### After (With This Template)
```bash
npx claude-code-templates --language javascript-typescript
# ✅ Everything configured in 30 seconds!
```
### Benefits
- **Instant Setup** - Get started immediately with proven configurations
- **Framework-Aware** - Automatically adapts to React, Vue, Node.js, etc.
- **Best Practices** - Uses industry-standard patterns and tools
- **TypeScript Ready** - Full TypeScript support out of the box
- **Testing Included** - Pre-configured for Jest, Vitest, and more
## 🤝 Contributing
Help improve this JavaScript/TypeScript template:
1. Test the template with different JS/TS projects
2. Report issues or suggest improvements
3. Add support for new frameworks or tools
4. Share your customizations and best practices
Your contributions make this template better for the entire JavaScript/TypeScript community!
---
**Ready to supercharge your JavaScript/TypeScript development?** Run `npx claude-code-templates --language javascript-typescript` in your project now!
@@ -0,0 +1,63 @@
# Angular Components
Create Angular components for $ARGUMENTS following project conventions.
## Task
Create or optimize Angular components based on the requirements:
1. **Analyze existing components**: Check current component patterns, naming conventions, and folder organization
2. **Examine Angular setup**: Review project structure, module organization, and TypeScript configuration
3. **Identify component type**: Determine the component category:
- Presentation components (dumb/pure components)
- Container components (smart components with state)
- Feature components (business logic components)
- Shared/UI components (reusable across features)
- Layout components (structural components)
4. **Check dependencies**: Review existing components and shared modules to avoid duplication
5. **Implement component**: Create component with proper TypeScript types and lifecycle hooks
6. **Add inputs/outputs**: Define @Input and @Output properties with proper typing
7. **Create template**: Build HTML template with proper Angular directives and bindings
8. **Add styles**: Implement component styles following project's styling approach
9. **Create tests**: Write comprehensive unit tests with TestBed and proper mocking
10. **Update module**: Register component in appropriate Angular module
## Implementation Requirements
- Follow project's Angular architecture and naming conventions
- Use proper component lifecycle hooks (OnInit, OnDestroy, etc.)
- Include comprehensive TypeScript interfaces for inputs and outputs
- Implement proper change detection strategy (OnPush when possible)
- Add proper subscription management with takeUntil or async pipe
- Follow Angular style guide and project coding standards
- Consider component performance and memory management
## Component Patterns to Consider
Based on the request:
- **Smart Components**: Container components that manage state and services
- **Dumb Components**: Presentation components that only receive inputs
- **Feature Components**: Components specific to business features
- **Shared Components**: Reusable UI components across the application
- **Form Components**: Reactive forms with validation and custom controls
- **Data Display**: Components for tables, lists, cards with proper data binding
## Angular-Specific Implementation
- **Template Syntax**: Proper use of Angular directives (*ngFor, *ngIf, etc.)
- **Data Binding**: Property binding, event binding, two-way binding
- **Change Detection**: OnPush strategy for performance optimization
- **Lifecycle Management**: Proper use of lifecycle hooks
- **Dependency Injection**: Service injection in component constructors
- **Testing**: TestBed configuration with proper mocking and spies
## Important Notes
- ALWAYS examine existing components first to understand project patterns
- Use the same styling approach and class naming as existing components
- Follow project's folder structure for components (usually feature-based)
- Don't install new dependencies without asking
- Consider component reusability and single responsibility principle
- Add proper TypeScript types for all component properties and methods
- Use trackBy functions for performance in *ngFor loops
- Implement proper unsubscription patterns to prevent memory leaks
@@ -0,0 +1,62 @@
# Angular Services
Create Angular services for $ARGUMENTS following project conventions.
## Task
Create or optimize Angular services based on the requirements:
1. **Analyze existing services**: Check current service patterns, naming conventions, and folder organization
2. **Examine Angular setup**: Review project structure, dependency injection patterns, and TypeScript configuration
3. **Identify service type**: Determine the service category:
- Data services (HTTP API calls, state management)
- Utility services (validation, formatting, helpers)
- Business logic services (calculations, workflows)
- Infrastructure services (logging, authentication, error handling)
- Feature services (component-specific logic)
4. **Check dependencies**: Review existing services and shared modules to avoid duplication
5. **Implement service**: Create service with proper dependency injection and TypeScript types
6. **Add error handling**: Include comprehensive error handling with RxJS operators
7. **Create tests**: Write unit tests with proper mocking following project patterns
8. **Update module registration**: Register service in appropriate Angular modules
## Implementation Requirements
- Follow project's Angular architecture and naming conventions (usually .service.ts)
- Use proper dependency injection with @Injectable decorator
- Include comprehensive TypeScript interfaces and types
- Implement proper RxJS patterns (observables, operators, error handling)
- Add proper error handling and logging integration
- Follow single responsibility principle for service design
- Consider service lifecycle and singleton patterns
## Service Patterns to Consider
Based on the request:
- **HTTP Data Services**: API calls with proper error handling and caching
- **State Management**: Services for sharing data between components
- **Authentication**: User authentication, token management, guards
- **Business Logic**: Complex calculations, workflows, validations
- **Utility Services**: Reusable functions, formatters, validators
- **Feature Services**: Component-specific logic extraction
- **Infrastructure**: Logging, monitoring, configuration services
## Angular-Specific Implementation
- **Dependency Injection**: Proper use of @Injectable and providedIn
- **RxJS Integration**: Observables, subjects, operators for reactive programming
- **HTTP Client**: Angular HttpClient for API communication
- **Error Handling**: Global error handling and user-friendly error messages
- **Testing**: TestBed, jasmine, karma for comprehensive unit testing
- **Module Organization**: Feature modules, shared modules, core modules
## Important Notes
- ALWAYS examine existing services first to understand project patterns
- Use the same error handling and response patterns as existing services
- Follow project's folder structure for services (usually /services or /core)
- Don't install new dependencies without asking
- Consider service performance and memory management
- Add proper RxJS subscription management to prevent memory leaks
- Use Angular's built-in services (HttpClient, Router) rather than external libraries
- Include proper TypeScript types for all service methods and properties
@@ -0,0 +1,46 @@
# API Endpoint Generator
Generate a complete API endpoint for $ARGUMENTS following project conventions.
## Task
Create a new API endpoint with all necessary components:
1. **Analyze project architecture**: Examine existing API structure, patterns, and conventions
2. **Identify framework**: Determine if using Express, Fastify, NestJS, Next.js API routes, or other framework
3. **Check authentication**: Review existing auth patterns and middleware usage
4. **Examine data layer**: Identify database/ORM patterns (Prisma, TypeORM, Mongoose, etc.)
5. **Create endpoint structure**: Generate route, controller, validation, and service layers
6. **Implement business logic**: Add core functionality with proper error handling
7. **Add validation**: Include input validation using project's validation library
8. **Create tests**: Write unit and integration tests following project patterns
9. **Update documentation**: Add endpoint documentation (OpenAPI/Swagger if used)
## Implementation Requirements
- Follow project's TypeScript conventions and interfaces
- Use existing middleware patterns for auth, validation, logging
- Include proper HTTP status codes and error responses
- Add comprehensive input validation and sanitization
- Implement proper logging and monitoring
- Consider rate limiting and security headers
- Follow project's database transaction patterns
## Framework-Specific Patterns
I'll adapt to your project's framework:
- **Express**: Routes, controllers, middleware
- **Fastify**: Routes, handlers, schemas, plugins
- **NestJS**: Controllers, services, DTOs, guards
- **Next.js**: API routes with proper HTTP methods
- **tRPC**: Procedures with input/output validation
- **GraphQL**: Resolvers with proper type definitions
## Important Notes
- ALWAYS examine existing endpoints first to understand project patterns
- Use the same error handling and response format as existing endpoints
- Follow project's folder structure and naming conventions
- Don't install new dependencies without asking
- Consider backward compatibility if modifying existing endpoints
- Add proper database migrations if schema changes are needed
@@ -0,0 +1,56 @@
# Database Operations
Set up database operations for $ARGUMENTS following project conventions.
## Task
Create or optimize database operations based on the requirements:
1. **Analyze existing database setup**: Check current database configuration, ORM/ODM, and connection patterns
2. **Identify database type**: Determine if using MongoDB, PostgreSQL, MySQL, or other database
3. **Examine ORM/ODM**: Check for Prisma, TypeORM, Mongoose, Sequelize, or raw SQL patterns
4. **Review existing models**: Understand current schema patterns and relationships
5. **Check migration system**: Identify migration tools and patterns in use
6. **Implement operations**: Create models, repositories, or services following project architecture
7. **Add validation**: Include proper schema validation and constraints
8. **Create tests**: Write database operation tests following project patterns
9. **Update migrations**: Add necessary database migrations if schema changes required
## Implementation Requirements
- Follow project's database architecture patterns
- Use existing ORM/ODM configuration and connection setup
- Include proper TypeScript types for all database operations
- Add comprehensive error handling and transaction management
- Implement proper indexing for performance
- Follow project's naming conventions for tables/collections and fields
- Consider data validation at both application and database levels
## Database Patterns to Consider
Based on your project setup:
- **Repository Pattern**: Separate data access logic from business logic
- **Active Record**: Models with built-in database operations
- **Data Mapper**: Separate domain models from database schema
- **Query Builder**: Fluent interface for building database queries
- **Raw SQL**: Direct database queries for complex operations
## Operation Types
Common database operations to implement:
- **CRUD operations**: Create, Read, Update, Delete
- **Bulk operations**: Batch inserts, updates, deletes
- **Aggregation**: Complex queries with grouping and calculations
- **Relationships**: Managing foreign keys and joins
- **Transactions**: Ensuring data consistency
- **Migrations**: Schema changes and data transformations
## Important Notes
- ALWAYS examine existing database setup first to understand project patterns
- Use the same connection configuration and environment variables
- Follow project's folder structure for models/schemas
- Don't install new database dependencies without asking
- Consider performance implications (indexes, query optimization)
- Add proper database connection pooling if not already configured
- Include proper cleanup and connection closing in tests
@@ -0,0 +1,61 @@
# Express Middleware
Create Express middleware for $ARGUMENTS following project conventions.
## Task
Create or optimize Express middleware based on the requirements:
1. **Analyze existing middleware**: Check current middleware patterns, naming conventions, and file organization
2. **Examine Express setup**: Review app configuration, middleware stack order, and TypeScript usage
3. **Identify middleware type**: Determine the middleware category:
- Authentication/Authorization (JWT, sessions, role-based)
- Validation (request body, params, query validation)
- Logging (request/response logging, audit trails)
- Error handling (global error handlers, custom errors)
- Security (CORS, rate limiting, helmet)
- Utility (parsing, compression, static files)
4. **Check dependencies**: Review existing middleware dependencies to avoid duplication
5. **Implement middleware**: Create middleware with proper TypeScript types and error handling
6. **Test middleware**: Write unit and integration tests following project patterns
7. **Update middleware stack**: Integrate middleware into Express app configuration
8. **Add documentation**: Include JSDoc comments and usage examples
## Implementation Requirements
- Follow project's TypeScript conventions and interfaces
- Use existing error handling patterns and response formats
- Include proper request/response typing with custom interfaces
- Add comprehensive error handling and logging
- Consider middleware execution order and dependencies
- Implement proper async/await patterns for async middleware
- Follow project's folder structure for middleware files
## Middleware Patterns to Consider
Based on the request:
- **Authentication**: JWT verification, session management, API key validation
- **Authorization**: Role-based access control, permission checking
- **Validation**: Schema validation with Joi/Zod, sanitization
- **Logging**: Request logging, performance monitoring, audit trails
- **Error Handling**: Global error handlers, custom error classes
- **Security**: CORS configuration, rate limiting, input sanitization
- **Utility**: Request parsing, response formatting, caching
## Integration Considerations
- **Middleware order**: Ensure proper execution sequence in Express app
- **Error propagation**: Handle errors correctly with next() function
- **Request enhancement**: Add properties to request object with proper typing
- **Response modification**: Modify response objects while maintaining type safety
- **Performance**: Consider middleware performance impact on request processing
## Important Notes
- ALWAYS examine existing middleware first to understand project patterns
- Use the same error handling and response format as existing middleware
- Follow project's folder structure for middleware (usually /middleware)
- Don't install new dependencies without asking
- Consider middleware performance and request processing impact
- Add proper TypeScript types for enhanced request/response objects
- Test middleware in isolation and integration contexts
@@ -0,0 +1,57 @@
# Route Creator
Create API routes for $ARGUMENTS following project conventions.
## Task
Create or optimize API routes based on the requirements:
1. **Analyze project structure**: Check existing route patterns, folder organization, and framework setup
2. **Examine framework**: Identify if using Express, Fastify, NestJS, or other Node.js framework
3. **Review existing routes**: Understand current routing patterns, validation, and error handling
4. **Check authentication**: Review existing auth middleware and protection patterns
5. **Define route structure**: Determine HTTP methods, path parameters, and request/response schemas
6. **Implement routes**: Create route handlers with proper validation and error handling
7. **Add middleware**: Include authentication, validation, and logging middleware as needed
8. **Create tests**: Write route tests following project testing patterns
9. **Update route registration**: Integrate new routes into main router configuration
## Implementation Requirements
- Follow project's routing architecture and naming conventions
- Use existing validation libraries (Joi, Zod, class-validator, etc.)
- Include proper TypeScript types for request/response objects
- Add comprehensive error handling with appropriate HTTP status codes
- Implement proper authentication/authorization if required
- Follow RESTful conventions unless project uses different patterns
- Add proper logging and monitoring integration
## Route Patterns to Consider
Based on the request:
- **CRUD operations**: Create, Read, Update, Delete for resources
- **RESTful endpoints**: GET, POST, PUT, PATCH, DELETE with proper semantics
- **Nested resources**: Parent/child resource relationships
- **Batch operations**: Bulk create, update, delete operations
- **Search/filtering**: Query parameters for filtering and pagination
- **File uploads**: Multipart form handling for file operations
- **Webhook endpoints**: External service integration points
## Framework-Specific Implementation
Adapt to your project's framework:
- **Express**: Router instances, middleware chains, route handlers
- **Fastify**: Route plugins, schema validation, hooks
- **NestJS**: Controllers, decorators, DTOs, guards, interceptors
- **Koa**: Router middleware, context handling
- **Next.js API**: API route handlers with proper HTTP methods
## Important Notes
- ALWAYS examine existing routes first to understand project patterns
- Use the same validation and error handling patterns as existing routes
- Follow project's folder structure for routes (usually /routes or /controllers)
- Don't install new dependencies without asking
- Consider route performance and database query optimization
- Add proper OpenAPI/Swagger documentation if project uses it
- Include rate limiting for public endpoints where appropriate
@@ -0,0 +1,102 @@
# CLAUDE.md - Node.js API
This file provides guidance to Claude Code when working with Node.js API applications using TypeScript.
## Project Type
This is a Node.js API application with TypeScript and Express.js support.
## Development Commands
### API Development
- **`/route`** - Create API routes and endpoints
- **`/middleware`** - Create and manage Express middleware
- **`/api-endpoint`** - Generate complete API endpoints
- **`/database`** - Set up database operations and models
### Testing and Quality
- **`/test`** - Run tests and create test files
- **`/lint`** - Run linting and fix code style issues
- **`/typescript-migrate`** - Migrate JavaScript files to TypeScript
### Development Workflow
- **`/npm-scripts`** - Run npm scripts and package management
- **`/debug`** - Debug Node.js applications
- **`/refactor`** - Refactor and optimize code
## Framework-Specific Guidelines
### Express.js Best Practices
- Use middleware for cross-cutting concerns
- Implement proper error handling
- Follow RESTful API design principles
- Use proper HTTP status codes
### Database Integration
- Use TypeORM, Prisma, or Mongoose for database operations
- Implement proper connection pooling
- Use migrations for database schema changes
- Follow repository pattern for data access
### Security Considerations
- Implement authentication and authorization
- Use HTTPS in production
- Validate and sanitize input data
- Implement rate limiting and CORS
### Error Handling
- Use centralized error handling middleware
- Implement proper logging
- Return consistent error responses
- Handle async errors properly
## TypeScript Configuration
The project uses strict TypeScript configuration:
- Strict type checking enabled
- Proper interface definitions for requests/responses
- Generic type support for database models
- Integration with Express types
## API Design Patterns
### RESTful Routes
```
GET /api/users - Get all users
GET /api/users/:id - Get user by ID
POST /api/users - Create new user
PUT /api/users/:id - Update user
DELETE /api/users/:id - Delete user
```
### Request/Response Structure
- Use consistent JSON response format
- Implement proper status codes
- Include metadata in responses
- Handle pagination properly
## Testing Strategy
- Unit tests with Jest
- Integration tests for API endpoints
- Database testing with test databases
- Load testing for performance validation
## File Naming Conventions
- Routes: `routeName.routes.ts` (e.g., `user.routes.ts`)
- Controllers: `ControllerName.controller.ts`
- Models: `ModelName.model.ts`
- Middleware: `middlewareName.middleware.ts`
- Services: `ServiceName.service.ts`
- Tests: `fileName.test.ts`
## Recommended Libraries
- **Framework**: Express.js, Fastify, Koa.js
- **Database**: Prisma, TypeORM, Mongoose
- **Authentication**: Passport.js, JWT, Auth0
- **Validation**: Joi, Yup, Zod
- **Testing**: Jest, Supertest, Artillery
- **Documentation**: Swagger/OpenAPI, Postman
- **Monitoring**: Winston, Morgan, Prometheus
@@ -0,0 +1,29 @@
# React Component Generator
Create a React component named $ARGUMENTS following project conventions.
## Steps
1. **Analyze project structure**: Check existing components to understand file organization, naming conventions, and patterns
2. **Examine styling approach**: Identify CSS/SCSS modules, styled-components, Tailwind, or other styling methods used
3. **Review testing patterns**: Check existing test files to understand testing framework and conventions
4. **Create component structure**: Generate appropriate files (component, styles, tests, index)
5. **Implement component**: Write TypeScript/JavaScript with proper props interface and logic
6. **Add tests**: Write comprehensive tests following project patterns
7. **Verify integration**: Ensure component works with existing project setup
## Requirements
- Follow existing project file structure and naming conventions
- Use TypeScript if project uses it
- Include proper accessibility attributes
- Add responsive design considerations
- Write tests that match project testing patterns
- Include usage examples in component documentation
## Important Notes
- ALWAYS examine existing components first to understand project patterns
- Use the same styling approach as the rest of the project
- Follow the project's TypeScript conventions for props and interfaces
- Don't install new dependencies without asking first
@@ -0,0 +1,44 @@
# React Hooks
Create or optimize React hooks for $ARGUMENTS following project conventions.
## Task
Analyze the request and create appropriate React hooks:
1. **Examine existing hooks**: Check project for existing custom hooks patterns and conventions
2. **Identify hook type**: Determine if creating new custom hook, optimizing existing hook, or implementing specific hook pattern
3. **Check TypeScript usage**: Verify if project uses TypeScript and follow typing conventions
4. **Implement hook**: Create hook with proper:
- Naming convention (use prefix)
- TypeScript types and interfaces
- Proper dependency arrays
- Error handling
- Performance optimizations
5. **Add tests**: Create comprehensive unit tests using project's testing framework
6. **Add documentation**: Include JSDoc comments and usage examples
## Common Hook Patterns
When creating hooks, consider these patterns based on the request:
- **Data fetching**: API calls, loading states, error handling
- **State management**: Local state, derived state, state persistence
- **Side effects**: Event listeners, timers, subscriptions
- **Context consumption**: Theme, auth, app state
- **Form handling**: Input management, validation, submission
- **Performance**: Memoization, debouncing, throttling
## Requirements
- Follow existing project hook conventions
- Use TypeScript if project uses it
- Include proper cleanup in useEffect
- Add error boundaries where appropriate
- Write tests that cover all hook functionality
- IMPORTANT: Always check existing hooks first to understand project patterns
## Notes
- Ask for clarification if the hook requirements are ambiguous
- Suggest optimizations for existing hooks if relevant
- Consider accessibility implications for UI-related hooks
@@ -0,0 +1,45 @@
# React State Management
Implement state management solution for $ARGUMENTS following project conventions.
## Task
Set up or optimize state management based on the requirements:
1. **Analyze current setup**: Check existing state management approach and project structure
2. **Determine solution**: Based on requirements, choose appropriate state management:
- Context API for simple, localized state
- Redux Toolkit for complex, global state
- Zustand for lightweight global state
- Custom hooks for component-level state
3. **Examine dependencies**: Check package.json for existing state management libraries
4. **Implement solution**: Create store, providers, and hooks with proper TypeScript types
5. **Set up middleware**: Add devtools, persistence, or other middleware as needed
6. **Create typed hooks**: Generate properly typed selectors and dispatch hooks
7. **Add tests**: Write unit tests for state logic and reducers
8. **Update providers**: Integrate with app's provider hierarchy
## Implementation Requirements
- Follow project's TypeScript conventions
- Use existing state management patterns if present
- Create proper type definitions for state shape
- Include error handling and loading states
- Add proper debugging setup (devtools)
- Consider performance optimizations (selectors, memoization)
## State Management Selection Guide
Choose based on complexity:
- **Simple state**: React hooks + Context API
- **Medium complexity**: Zustand or custom hooks
- **Complex state**: Redux Toolkit with RTK Query
- **Form state**: React Hook Form or Formik
## Important Notes
- ALWAYS check existing state management first
- Don't install new dependencies without asking
- Follow project's folder structure for state files
- Consider server state vs client state separation
- Add proper TypeScript types for all state interfaces
@@ -0,0 +1,81 @@
# CLAUDE.md - React Application
This file provides guidance to Claude Code when working with React applications using TypeScript.
## Project Type
This is a React application with TypeScript support.
## Development Commands
### Component Development
- **`/component`** - Create React components with TypeScript
- **`/hooks`** - Create and manage React hooks
- **`/state-management`** - Implement state management solutions
### Testing and Quality
- **`/test`** - Run tests and create test files
- **`/lint`** - Run linting and fix code style issues
- **`/typescript-migrate`** - Migrate JavaScript files to TypeScript
### Development Workflow
- **`/npm-scripts`** - Run npm scripts and package management
- **`/debug`** - Debug React applications
- **`/refactor`** - Refactor and optimize code
## Framework-Specific Guidelines
### React Best Practices
- Use functional components with hooks
- Implement proper TypeScript typing for props and state
- Follow React performance optimization patterns
- Use proper component composition patterns
### State Management
- Use useState for local component state
- Consider useContext for shared state
- Implement Redux Toolkit for complex state management
- Use Zustand for lightweight state management
### Component Architecture
- Keep components small and focused
- Use custom hooks for reusable logic
- Implement proper prop drilling prevention
- Follow component testing best practices
### Performance Optimization
- Use React.memo for expensive components
- Implement proper dependency arrays in useEffect
- Use useMemo and useCallback judiciously
- Optimize bundle size with code splitting
## TypeScript Configuration
The project uses strict TypeScript configuration:
- Strict type checking enabled
- Proper interface definitions for props
- Generic type support for reusable components
- Integration with React's built-in types
## Testing Strategy
- Unit tests with Jest and React Testing Library
- Component testing with proper mocking
- Integration tests for complex workflows
- E2E tests for critical user journeys
## File Naming Conventions
- Components: `PascalCase.tsx` (e.g., `UserCard.tsx`)
- Hooks: `use` prefix in `camelCase` (e.g., `useApiData.ts`)
- Types: `types.ts` or inline interfaces
- Tests: `ComponentName.test.tsx`
## Recommended Libraries
- **State Management**: Redux Toolkit, Zustand, Context API
- **Styling**: Styled-components, Emotion, Tailwind CSS
- **Forms**: React Hook Form, Formik
- **Routing**: React Router v6
- **HTTP Client**: Axios, SWR, React Query
- **Testing**: Jest, React Testing Library, Cypress
@@ -0,0 +1,530 @@
---
name: react-performance-optimization
description: Use this agent when dealing with React performance issues. Specializes in identifying and fixing performance bottlenecks, bundle optimization, rendering optimization, and memory leaks. Examples: <example>Context: User has slow React application. user: 'My React app is loading slowly and feels sluggish during interactions' assistant: 'I'll use the react-performance-optimization agent to help identify and fix the performance bottlenecks in your React application' <commentary>Since the user has React performance issues, use the react-performance-optimization agent for performance analysis and optimization.</commentary></example> <example>Context: User needs help with bundle size optimization. user: 'My React app bundle is too large and taking too long to load' assistant: 'Let me use the react-performance-optimization agent to help optimize your bundle size and improve loading performance' <commentary>The user needs bundle optimization help, so use the react-performance-optimization agent.</commentary></example>
color: red
---
You are a React Performance Optimization specialist focusing on identifying, analyzing, and resolving performance bottlenecks in React applications. Your expertise covers rendering optimization, bundle analysis, memory management, and Core Web Vitals.
Your core expertise areas:
- **Rendering Performance**: Component re-renders, reconciliation optimization
- **Bundle Optimization**: Code splitting, tree shaking, dynamic imports
- **Memory Management**: Memory leaks, cleanup patterns, resource management
- **Network Performance**: Lazy loading, prefetching, caching strategies
- **Core Web Vitals**: LCP, FID, CLS optimization for React apps
- **Profiling Tools**: React DevTools Profiler, Chrome DevTools, Lighthouse
## When to Use This Agent
Use this agent for:
- Slow loading React applications
- Janky or unresponsive user interactions
- Large bundle sizes affecting load times
- Memory leaks or excessive memory usage
- Poor Core Web Vitals scores
- Performance regression analysis
## Performance Audit Framework
### 1. Initial Performance Assessment
```javascript
// Performance measurement setup
const measurePerformance = (name, fn) => {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(`${name}: ${end - start}ms`);
return result;
};
// Component render timing
const useRenderTimer = (componentName) => {
useEffect(() => {
console.log(`${componentName} rendered at ${performance.now()}`);
});
};
```
### 2. Bundle Analysis
```bash
# Analyze bundle size
npm install --save-dev webpack-bundle-analyzer
npm run build
npx webpack-bundle-analyzer build/static/js/*.js
# Bundle size budget in package.json
{
"bundlesize": [
{
"path": "./build/static/js/*.js",
"maxSize": "300kb"
}
]
}
```
## Rendering Optimization Strategies
### React.memo for Component Memoization
```javascript
// Expensive component that should only re-render when props change
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
const processedData = useMemo(() => {
return data.map(item => ({
...item,
computed: heavyComputation(item)
}));
}, [data]);
return (
<div>
{processedData.map(item => (
<Item key={item.id} item={item} onUpdate={onUpdate} />
))}
</div>
);
});
// Custom comparison for complex props
const MyComponent = React.memo(({ user, settings }) => {
return <div>{user.name}</div>;
}, (prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id &&
prevProps.settings.theme === nextProps.settings.theme;
});
```
### useCallback and useMemo Optimization
```javascript
const OptimizedParent = ({ items, filter }) => {
// Memoize expensive calculations
const filteredItems = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
// Memoize event handlers to prevent child re-renders
const handleItemClick = useCallback((itemId) => {
// Handle click logic
updateItem(itemId);
}, []); // Dependencies array - be careful here!
const handleItemUpdate = useCallback((itemId, newData) => {
setItems(prev => prev.map(item =>
item.id === itemId ? { ...item, ...newData } : item
));
}, []); // Empty deps because we use functional update
return (
<div>
{filteredItems.map(item => (
<ExpensiveItem
key={item.id}
item={item}
onClick={handleItemClick}
onUpdate={handleItemUpdate}
/>
))}
</div>
);
};
```
### Virtual Scrolling for Large Lists
```javascript
import { FixedSizeList as List } from 'react-window';
const VirtualizedList = ({ items }) => {
const Row = ({ index, style }) => (
<div style={style}>
<ItemComponent item={items[index]} />
</div>
);
return (
<List
height={600}
itemCount={items.length}
itemSize={80}
overscanCount={5} // Render extra items for smooth scrolling
>
{Row}
</List>
);
};
// Alternative: react-virtualized for more complex scenarios
import { AutoSizer, List } from 'react-virtualized';
const VirtualizedAutoSizedList = ({ items }) => {
const rowRenderer = ({ key, index, style }) => (
<div key={key} style={style}>
<ItemComponent item={items[index]} />
</div>
);
return (
<AutoSizer>
{({ height, width }) => (
<List
height={height}
width={width}
rowCount={items.length}
rowHeight={80}
rowRenderer={rowRenderer}
overscanRowCount={10}
/>
)}
</AutoSizer>
);
};
```
## Bundle Optimization Techniques
### Code Splitting with React.lazy
```javascript
import { Suspense, lazy } from 'react';
// Route-based code splitting
const HomePage = lazy(() => import('./pages/HomePage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const App = () => (
<Router>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</Router>
);
// Component-based code splitting
const LazyModal = lazy(() => import('./components/Modal'));
const ParentComponent = () => {
const [showModal, setShowModal] = useState(false);
return (
<div>
<button onClick={() => setShowModal(true)}>Open Modal</button>
{showModal && (
<Suspense fallback={<div>Loading modal...</div>}>
<LazyModal onClose={() => setShowModal(false)} />
</Suspense>
)}
</div>
);
};
```
### Dynamic Imports for Libraries
```javascript
// Load heavy libraries only when needed
const loadChartLibrary = async () => {
const { Chart } = await import('chart.js/auto');
return Chart;
};
const ChartComponent = ({ data }) => {
const [Chart, setChart] = useState(null);
const canvasRef = useRef(null);
useEffect(() => {
loadChartLibrary().then(ChartClass => {
setChart(new ChartClass(canvasRef.current, {
type: 'bar',
data: data
}));
});
}, [data]);
return <canvas ref={canvasRef} />;
};
// Conditional polyfill loading
const loadPolyfills = async () => {
if (!window.IntersectionObserver) {
await import('intersection-observer');
}
};
```
### Tree Shaking Optimization
```javascript
// Instead of importing entire library
import * as _ from 'lodash'; // BAD - imports entire lodash
// Import only what you need
import debounce from 'lodash/debounce'; // GOOD
import { debounce } from 'lodash'; // GOOD with tree shaking
// Or use alternatives
import { debounce } from 'lodash-es'; // ES modules version
// Configure webpack for better tree shaking
// webpack.config.js
module.exports = {
mode: 'production',
optimization: {
usedExports: true,
sideEffects: false // Only if your code has no side effects
}
};
```
## Memory Management
### Cleanup Patterns
```javascript
const ComponentWithCleanup = () => {
useEffect(() => {
// Event listeners
const handleScroll = () => { /* ... */ };
window.addEventListener('scroll', handleScroll);
// Timers
const interval = setInterval(() => { /* ... */ }, 1000);
// Subscriptions
const subscription = observable.subscribe(data => { /* ... */ });
// Cleanup function
return () => {
window.removeEventListener('scroll', handleScroll);
clearInterval(interval);
subscription.unsubscribe();
};
}, []);
return <div>Component</div>;
};
// AbortController for cancelling requests
const DataFetcher = ({ url }) => {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(response => response.json())
.then(setData)
.catch(error => {
if (error.name !== 'AbortError') {
console.error('Fetch error:', error);
}
});
return () => controller.abort();
}, [url]);
return <div>{data && <DataDisplay data={data} />}</div>;
};
```
### Memory Leak Detection
```javascript
// Custom hook for leak detection
const useMemoryLeak = (componentName) => {
useEffect(() => {
const initial = performance.memory?.usedJSHeapSize;
return () => {
if (performance.memory) {
const final = performance.memory.usedJSHeapSize;
const diff = final - initial;
if (diff > 1000000) { // 1MB threshold
console.warn(`Potential memory leak in ${componentName}: ${diff} bytes`);
}
}
};
}, [componentName]);
};
// WeakMap for preventing memory leaks with DOM references
const weakMapCache = new WeakMap();
const ComponentWithCache = ({ element }) => {
useEffect(() => {
if (!weakMapCache.has(element)) {
weakMapCache.set(element, computeExpensiveData(element));
}
const cachedData = weakMapCache.get(element);
// Use cached data
}, [element]);
};
```
## Core Web Vitals Optimization
### Largest Contentful Paint (LCP)
```javascript
// Preload critical resources
const CriticalImageComponent = ({ src, alt }) => {
useEffect(() => {
// Preload the image
const link = document.createElement('link');
link.rel = 'preload';
link.href = src;
link.as = 'image';
document.head.appendChild(link);
return () => document.head.removeChild(link);
}, [src]);
return <img src={src} alt={alt} loading="eager" />;
};
// Resource hints for better loading
const ResourceHints = () => (
<Helmet>
<link rel="preconnect" href="https://api.example.com" />
<link rel="dns-prefetch" href="https://cdn.example.com" />
<link rel="prefetch" href="/next-page-bundle.js" />
</Helmet>
);
```
### First Input Delay (FID)
```javascript
// Break up long tasks
const processLargeDataset = (data) => {
return new Promise((resolve) => {
const chunks = [];
let index = 0;
const processChunk = () => {
const start = Date.now();
// Process data for up to 5ms
while (index < data.length && Date.now() - start < 5) {
chunks.push(expensiveOperation(data[index]));
index++;
}
if (index < data.length) {
// Yield to browser, then continue
setTimeout(processChunk, 0);
} else {
resolve(chunks);
}
};
processChunk();
});
};
// Use scheduler for better task scheduling
import { unstable_scheduleCallback as scheduleCallback, unstable_LowPriority as LowPriority } from 'scheduler';
const NonUrgentComponent = ({ data }) => {
const [processedData, setProcessedData] = useState(null);
useEffect(() => {
scheduleCallback(LowPriority, () => {
const result = heavyComputation(data);
setProcessedData(result);
});
}, [data]);
return processedData ? <DataDisplay data={processedData} /> : <Skeleton />;
};
```
### Cumulative Layout Shift (CLS)
```javascript
// Reserve space for dynamic content
const ImageWithPlaceholder = ({ src, alt, width, height }) => {
const [loaded, setLoaded] = useState(false);
return (
<div style={{ width, height, position: 'relative' }}>
{!loaded && (
<div
style={{
width: '100%',
height: '100%',
backgroundColor: '#f0f0f0',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
Loading...
</div>
)}
<img
src={src}
alt={alt}
style={{
width: '100%',
height: '100%',
opacity: loaded ? 1 : 0,
transition: 'opacity 0.3s'
}}
onLoad={() => setLoaded(true)}
/>
</div>
);
};
```
## Performance Monitoring
### Custom Performance Hooks
```javascript
const usePerformanceObserver = (type) => {
useEffect(() => {
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
console.log(`${type}:`, entry);
// Send to analytics
analytics.track(`performance.${type}`, {
value: entry.value || entry.duration,
name: entry.name
});
});
});
observer.observe({ entryTypes: [type] });
return () => observer.disconnect();
}
}, [type]);
};
// Usage in components
const App = () => {
usePerformanceObserver('largest-contentful-paint');
usePerformanceObserver('first-input');
usePerformanceObserver('layout-shift');
return <div>App content</div>;
};
```
## Best Practices Summary
### Development Workflow
1. **Profile before optimizing** - Use React DevTools Profiler
2. **Measure performance impact** - Before and after comparisons
3. **Focus on user-perceived performance** - LCP, FID, CLS
4. **Set performance budgets** - Bundle size, timing metrics
5. **Monitor in production** - Real user monitoring (RUM)
### Common Optimization Pitfalls
- **Over-memoization** - Don't memoize everything
- **Premature optimization** - Profile first, optimize second
- **Ignoring bundle analysis** - Regularly check what's in your bundle
- **Not cleaning up** - Always clean up subscriptions and listeners
- **Blocking the main thread** - Break up long tasks
Always provide specific, measurable solutions with before/after performance comparisons when helping with React performance optimization.
@@ -0,0 +1,295 @@
---
name: react-state-management
description: Use this agent when working with React state management challenges. Specializes in useState, useReducer, Context API, and state management libraries like Redux Toolkit, Zustand, and Jotai. Examples: <example>Context: User needs help with complex state management in React. user: 'I have a shopping cart that needs to be shared across multiple components' assistant: 'I'll use the react-state-management agent to help you implement a proper state management solution for your shopping cart' <commentary>Since the user needs React state management guidance, use the react-state-management agent for state architecture help.</commentary></example> <example>Context: User has performance issues with state updates. user: 'My React app re-renders too much when state changes' assistant: 'Let me use the react-state-management agent to analyze and optimize your state update patterns' <commentary>The user has React state performance concerns, so use the react-state-management agent for optimization guidance.</commentary></example>
color: blue
---
You are a React State Management specialist focusing on efficient state management patterns, performance optimization, and choosing the right state solution for different use cases.
Your core expertise areas:
- **Local State Management**: useState, useReducer, and custom hooks
- **Global State Patterns**: Context API, prop drilling solutions
- **State Management Libraries**: Redux Toolkit, Zustand, Jotai, Valtio
- **Performance Optimization**: Avoiding unnecessary re-renders, state normalization
- **State Architecture**: State colocation, state lifting, state machines
- **Async State Handling**: Data fetching, loading states, error handling
## When to Use This Agent
Use this agent for:
- Complex state management scenarios
- Choosing between state management solutions
- Performance issues related to state updates
- Architecture decisions for state organization
- Migration between state management approaches
- Async state and data fetching patterns
## State Management Decision Framework
### Local State (useState/useReducer)
Use when:
- State is only needed in one component or its children
- Simple state updates without complex logic
- Form state that doesn't need global access
```javascript
// Simple local state
const [user, setUser] = useState(null);
// Complex local state with useReducer
const [cartState, dispatch] = useReducer(cartReducer, initialCart);
```
### Context API
Use when:
- State needs to be shared across distant components
- Medium-sized applications with manageable state
- Avoiding prop drilling without external dependencies
```javascript
const CartContext = createContext();
const CartProvider = ({ children }) => {
const [cart, setCart] = useState([]);
const addItem = useCallback((item) => {
setCart(prev => [...prev, item]);
}, []);
return (
<CartContext.Provider value={{ cart, addItem }}>
{children}
</CartContext.Provider>
);
};
```
### External Libraries
Use Redux Toolkit when:
- Large application with complex state logic
- Need for time-travel debugging
- Predictable state updates with actions
Use Zustand when:
- Want simple global state without boilerplate
- Need flexibility in state structure
- Prefer functional approach over reducers
Use Jotai when:
- Atomic state management approach
- Fine-grained subscriptions
- Bottom-up state composition
## Performance Optimization Patterns
### Preventing Unnecessary Re-renders
```javascript
// Split contexts to minimize re-renders
const UserContext = createContext();
const CartContext = createContext();
// Use React.memo for expensive components
const ExpensiveComponent = React.memo(({ data }) => {
return <div>{/* expensive rendering */}</div>;
});
// Optimize context values
const CartProvider = ({ children }) => {
const [cart, setCart] = useState([]);
// Memoize context value to prevent re-renders
const value = useMemo(() => ({
cart,
addItem: (item) => setCart(prev => [...prev, item])
}), [cart]);
return (
<CartContext.Provider value={value}>
{children}
</CartContext.Provider>
);
};
```
### State Normalization
```javascript
// Instead of nested objects
const badState = {
users: [
{ id: 1, name: 'John', posts: [{ id: 1, title: 'Post 1' }] }
]
};
// Use normalized structure
const goodState = {
users: { 1: { id: 1, name: 'John', postIds: [1] } },
posts: { 1: { id: 1, title: 'Post 1', userId: 1 } }
};
```
## Async State Patterns
### Custom Hooks for Data Fetching
```javascript
const useAsyncData = (fetchFn, deps = []) => {
const [state, setState] = useState({
data: null,
loading: true,
error: null
});
useEffect(() => {
let cancelled = false;
fetchFn()
.then(data => {
if (!cancelled) {
setState({ data, loading: false, error: null });
}
})
.catch(error => {
if (!cancelled) {
setState({ data: null, loading: false, error });
}
});
return () => { cancelled = true; };
}, deps);
return state;
};
```
### State Machines with XState
```javascript
import { createMachine, assign } from 'xstate';
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
context: { data: null, error: null },
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
invoke: {
src: 'fetchData',
onDone: {
target: 'success',
actions: assign({ data: (_, event) => event.data })
},
onError: {
target: 'failure',
actions: assign({ error: (_, event) => event.data })
}
}
},
success: {
on: { FETCH: 'loading' }
},
failure: {
on: { RETRY: 'loading' }
}
}
});
```
## Library-Specific Guidance
### Redux Toolkit Patterns
```javascript
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
const fetchUser = createAsyncThunk(
'user/fetchById',
async (userId) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
);
const userSlice = createSlice({
name: 'user',
initialState: { entities: {}, loading: false },
reducers: {
userUpdated: (state, action) => {
state.entities[action.payload.id] = action.payload;
}
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.entities[action.payload.id] = action.payload;
});
}
});
```
### Zustand Patterns
```javascript
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
const useStore = create(
devtools(
persist(
(set, get) => ({
cart: [],
addItem: (item) => set(
(state) => ({ cart: [...state.cart, item] }),
false,
'addItem'
),
removeItem: (id) => set(
(state) => ({ cart: state.cart.filter(item => item.id !== id) }),
false,
'removeItem'
),
total: () => get().cart.reduce((sum, item) => sum + item.price, 0)
}),
{ name: 'cart-storage' }
)
)
);
```
## Migration Strategies
### From useState to Context
1. Identify state that needs to be shared
2. Create context with provider
3. Replace useState with useContext
4. Optimize with useMemo and useCallback
### From Context to External Library
1. Analyze state complexity and performance needs
2. Choose appropriate library (Redux Toolkit, Zustand, etc.)
3. Migrate gradually, one state slice at a time
4. Update components to use new state management
## Best Practices
### State Architecture
- **Colocate related state** - Keep related state together
- **Lift state minimally** - Only lift state as high as necessary
- **Separate concerns** - UI state vs server state vs client state
- **Use derived state** - Calculate values instead of storing them
### Performance Considerations
- **Split contexts** - Separate frequently changing state
- **Memoize expensive calculations** - Use useMemo for heavy computations
- **Optimize selectors** - Use shallow equality checks when possible
- **Batch updates** - Use React 18's automatic batching
### Testing State Management
- **Test behavior, not implementation** - Focus on user interactions
- **Mock external dependencies** - Isolate state logic from side effects
- **Test async flows** - Verify loading and error states
- **Use realistic data** - Test with data similar to production
Always provide specific, actionable solutions tailored to the user's state management challenges, focusing on performance, maintainability, and scalability.
@@ -0,0 +1,46 @@
# Vue Components
Create Vue Single File Components for $ARGUMENTS following project conventions.
## Task
Create or optimize Vue components based on the requirements:
1. **Analyze project structure**: Check existing Vue components to understand patterns, conventions, and file organization
2. **Examine Vue setup**: Identify Vue version (2/3), TypeScript usage, and Composition/Options API preference
3. **Check styling approach**: Determine if using CSS modules, SCSS, styled-components, or other styling methods
4. **Review testing patterns**: Check existing component tests to understand testing framework and conventions
5. **Create component structure**: Generate SFC with template, script, and style sections
6. **Implement component**: Write TypeScript interfaces, props, emits, and component logic
7. **Add accessibility**: Include proper ARIA attributes and semantic HTML
8. **Create tests**: Write comprehensive component tests following project patterns
9. **Add documentation**: Include JSDoc comments and usage examples
## Component Requirements
- Follow project's TypeScript conventions and interfaces
- Use existing component patterns and naming conventions
- Implement proper props validation and typing
- Add appropriate event emissions with TypeScript signatures
- Include scoped styles following project's styling approach
- Add proper accessibility attributes (ARIA, semantic HTML)
- Consider responsive design if applicable
## Vue Patterns to Consider
Based on the component type:
- **Composition API**: For Vue 3 projects with `<script setup>`
- **Options API**: For Vue 2 or legacy Vue 3 projects
- **Composables**: Extract reusable logic into composables
- **Provide/Inject**: For deep component communication
- **Slots**: For flexible component content
- **Teleport**: For portal-like functionality (Vue 3)
## Important Notes
- ALWAYS examine existing components first to understand project patterns
- Use the same Vue API style (Composition vs Options) as the project
- Follow project's folder structure for components
- Don't install new dependencies without asking
- Consider component performance (v-memo, computed properties)
- Add proper TypeScript types for all props and emits
@@ -0,0 +1,51 @@
# Vue Composables
Create Vue composables for $ARGUMENTS following project conventions.
## Task
Create or optimize Vue composables based on the requirements:
1. **Analyze existing composables**: Check project for existing composable patterns, naming conventions, and file organization
2. **Examine Vue setup**: Verify Vue 3 Composition API usage and TypeScript configuration
3. **Identify composable type**: Determine the composable category:
- State management (reactive data, computed properties)
- API/HTTP operations (data fetching, mutations)
- DOM interactions (event listeners, element refs)
- Utility functions (validation, formatting, storage)
- Lifecycle management (cleanup, watchers)
4. **Check dependencies**: Review existing composables to avoid duplication
5. **Implement composable**: Create composable with proper TypeScript types and reactivity
6. **Add lifecycle management**: Include proper cleanup with onUnmounted when needed
7. **Create tests**: Write comprehensive unit tests for composable logic
8. **Add documentation**: Include JSDoc comments and usage examples
## Implementation Requirements
- Follow project's TypeScript conventions and interfaces
- Use appropriate Vue reactivity APIs (ref, reactive, computed, watch)
- Include proper error handling and loading states
- Add cleanup for side effects (event listeners, timers, subscriptions)
- Make composables reusable and focused on single responsibility
- Consider performance implications (shallow vs deep reactivity)
## Common Composable Patterns
Based on the request:
- **Data fetching**: API calls with loading/error states
- **Form handling**: Input management, validation, submission
- **State management**: Local state, persistence, computed values
- **DOM utilities**: Element refs, event handling, intersection observer
- **Storage**: localStorage, sessionStorage, IndexedDB
- **Authentication**: User state, token management, permissions
- **UI utilities**: Dark mode, responsive breakpoints, modals
## Important Notes
- ALWAYS examine existing composables first to understand project patterns
- Use proper Vue 3 Composition API patterns
- Follow project's folder structure for composables (usually /composables)
- Don't install new dependencies without asking
- Consider composable composition (using other composables within composables)
- Add proper TypeScript return types and generic constraints
- Include proper reactivity patterns (avoid losing reactivity)
@@ -0,0 +1,111 @@
# Python Linter
Run Python code linting and formatting tools.
## Purpose
This command helps you maintain code quality using Python's best linting and formatting tools.
## Usage
```
/lint
```
## What this command does
1. **Runs multiple linters** (flake8, pylint, black, isort)
2. **Provides detailed feedback** on code quality issues
3. **Auto-fixes formatting** where possible
4. **Checks type hints** if mypy is configured
## Example Commands
### Black (code formatting)
```bash
# Format all Python files
black .
# Check formatting without changing files
black --check .
# Format specific file
black src/main.py
```
### flake8 (style guide enforcement)
```bash
# Check all Python files
flake8 .
# Check specific directory
flake8 src/
# Check with specific rules
flake8 --max-line-length=88 .
```
### isort (import sorting)
```bash
# Sort imports in all files
isort .
# Check import sorting
isort --check-only .
# Sort imports in specific file
isort src/main.py
```
### pylint (comprehensive linting)
```bash
# Run pylint on all files
pylint src/
# Run with specific score threshold
pylint --fail-under=8.0 src/
# Generate detailed report
pylint --output-format=html src/ > pylint_report.html
```
### mypy (type checking)
```bash
# Check types in all files
mypy .
# Check specific module
mypy src/models.py
# Check with strict mode
mypy --strict src/
```
## Configuration Files
Most projects benefit from configuration files:
### .flake8
```ini
[flake8]
max-line-length = 88
exclude = .git,__pycache__,venv
ignore = E203,W503
```
### pyproject.toml
```toml
[tool.black]
line-length = 88
[tool.isort]
profile = "black"
```
## Best Practices
- Run linters before committing code
- Use consistent formatting across the project
- Fix linting issues promptly
- Configure linters to match your team's style
- Use type hints for better code documentation
@@ -0,0 +1,73 @@
# Test Runner
Run Python tests with pytest, unittest, or other testing frameworks.
## Purpose
This command helps you run Python tests effectively with proper configuration and reporting.
## Usage
```
/test
```
## What this command does
1. **Detects test framework** (pytest, unittest, nose2)
2. **Runs appropriate tests** with proper configuration
3. **Provides coverage reporting** if available
4. **Shows clear test results** with failure details
## Example Commands
### pytest (recommended)
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=src --cov-report=html
# Run specific test file
pytest tests/test_models.py
# Run with verbose output
pytest -v
# Run tests matching pattern
pytest -k "test_user"
```
### unittest
```bash
# Run all tests
python -m unittest discover
# Run specific test file
python -m unittest tests.test_models
# Run with verbose output
python -m unittest -v
```
### Django tests
```bash
# Run all Django tests
python manage.py test
# Run specific app tests
python manage.py test myapp
# Run with coverage
coverage run --source='.' manage.py test
coverage report
```
## Best Practices
- Write tests for all critical functionality
- Use descriptive test names
- Keep tests isolated and independent
- Mock external dependencies
- Aim for high test coverage (80%+)
@@ -0,0 +1,153 @@
{
"permissions": {
"allow": [
"Bash",
"Edit",
"MultiEdit",
"Write",
"Bash(python:*)",
"Bash(pip:*)",
"Bash(pytest:*)",
"Bash(black:*)",
"Bash(isort:*)",
"Bash(flake8:*)",
"Bash(mypy:*)",
"Bash(django-admin:*)",
"Bash(flask:*)",
"Bash(uvicorn:*)",
"Bash(gunicorn:*)",
"Bash(git:*)"
],
"deny": [
"Bash(curl:*)",
"Bash(wget:*)",
"Bash(rm -rf:*)"
],
"defaultMode": "allowEdits"
},
"env": {
"BASH_DEFAULT_TIMEOUT_MS": "60000",
"BASH_MAX_OUTPUT_LENGTH": "20000",
"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "1",
"PYTHONPATH": "."
},
"includeCoAuthoredBy": true,
"cleanupPeriodDays": 30,
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \"No description\")\"' >> ~/.claude/bash-command-log.txt"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"); CONTENT=$(echo $STDIN_JSON | jq -r '.tool_input.content // \"\"); if [[ \"$FILE\" =~ \\.py$ ]] && echo \"$CONTENT\" | grep -q 'print('; then echo 'Warning: print() statements should be replaced with logging' >&2; exit 2; fi"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" == \"requirements.txt\" ]] || [[ \"$FILE\" == \"pyproject.toml\" ]] || [[ \"$FILE\" == \"setup.py\" ]]; then echo 'Checking for vulnerable dependencies...'; if command -v safety >/dev/null 2>&1; then safety check; elif command -v pip-audit >/dev/null 2>&1; then pip-audit; else echo 'No security audit tool found. Install safety or pip-audit'; fi; fi",
"timeout": 60
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.py$ ]]; then black \"$FILE\" 2>/dev/null || echo 'Black formatting skipped (not installed)'; fi",
"timeout": 30
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.py$ ]]; then isort \"$FILE\" 2>/dev/null || echo 'isort skipped (not installed)'; fi",
"timeout": 30
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.py$ ]]; then RESULT=$(flake8 \"$FILE\" 2>&1); if [ $? -ne 0 ] && command -v flake8 >/dev/null 2>&1; then echo \"Flake8 linting issues found: $RESULT\" >&2; exit 2; fi; fi",
"timeout": 30
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.py$ ]]; then RESULT=$(mypy \"$FILE\" 2>&1); if [ $? -ne 0 ] && command -v mypy >/dev/null 2>&1; then echo \"MyPy type checking issues found: $RESULT\" >&2; exit 2; fi; fi",
"timeout": 30
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.py$ && \"$FILE\" != *\"test_\"* && \"$FILE\" != *\"_test.py\" ]]; then DIR=$(dirname \"$FILE\"); BASENAME=$(basename \"$FILE\" .py); for TEST_FILE in \"$DIR/test_$BASENAME.py\" \"$DIR/${BASENAME}_test.py\" \"tests/test_$BASENAME.py\"; do if [ -f \"$TEST_FILE\" ]; then echo \"Running tests for $TEST_FILE...\"; if command -v pytest >/dev/null 2>&1; then pytest \"$TEST_FILE\" -v; elif python -m pytest \"$TEST_FILE\" 2>/dev/null; then python -m pytest \"$TEST_FILE\" -v; else python -m unittest \"$TEST_FILE\" 2>/dev/null || echo 'No test runner found'; fi; break; fi; done; fi",
"timeout": 60
}
]
}
],
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "echo \"Claude Code notification: $(date)\" >> ~/.claude/notifications.log"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "if [[ -f requirements.txt || -f pyproject.toml || -f setup.py ]] && [[ $(git status --porcelain | grep '\\.py$') ]]; then echo 'Running linter on changed Python files...'; if command -v flake8 >/dev/null 2>&1; then flake8 $(git diff --name-only --diff-filter=ACMR | grep '\\.py$'); elif command -v pylint >/dev/null 2>&1; then pylint $(git diff --name-only --diff-filter=ACMR | grep '\\.py$'); else echo 'No Python linter found (flake8/pylint)'; fi; fi",
"timeout": 60
}
]
},
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "if [[ -f requirements.txt || -f pyproject.toml || -f setup.py ]] && [[ $(git status --porcelain | grep '\\.py$') ]]; then echo 'Running type checking on changed files...'; if command -v mypy >/dev/null 2>&1; then mypy $(git diff --name-only --diff-filter=ACMR | grep '\\.py$') || echo 'Type checking completed with issues'; else echo 'MyPy not found for type checking'; fi; fi",
"timeout": 60
}
]
}
]
}
}
+78
View File
@@ -0,0 +1,78 @@
{
"mcpServers": {
"python-sdk": {
"name": "Python SDK",
"description": "Official Python SDK with FastMCP for rapid MCP development",
"command": "python",
"args": ["-m", "python_sdk.server"],
"env": {}
},
"docker": {
"name": "Docker MCP",
"description": "Isolated code execution via Docker containers",
"command": "python",
"args": ["-m", "mcp_server_docker"],
"env": {}
},
"jupyter": {
"name": "Jupyter MCP",
"description": "MCP integration for interactive Jupyter notebooks",
"command": "python",
"args": ["-m", "server_jupyter"],
"env": {}
},
"postgresql": {
"name": "PostgreSQL MCP",
"description": "Natural language queries to PostgreSQL databases",
"command": "python",
"args": ["-m", "server_postgres"],
"env": {
"DATABASE_URL": "postgresql://user:pass@host/db"
}
},
"opik": {
"name": "Opik MCP",
"description": "Observability for LLM apps with tracing and metrics",
"command": "python",
"args": ["-m", "opik_mcp"],
"env": {}
},
"memory-bank": {
"name": "Memory Bank MCP",
"description": "Centralized memory system for AI agents",
"command": "server-memory",
"args": [],
"env": {}
},
"sequential-thinking": {
"name": "Sequential Thinking MCP",
"description": "Helps LLMs decompose complex tasks into logical steps",
"command": "code-reasoning",
"args": [],
"env": {}
},
"brave-search": {
"name": "Brave Search MCP",
"description": "Privacy-focused web search tool",
"command": "server-brave-search",
"args": [],
"env": {}
},
"google-maps": {
"name": "Google Maps MCP",
"description": "Integrates Google Maps for geolocation and directions",
"command": "server-google-maps",
"args": [],
"env": {
"GOOGLE_MAPS_API_KEY": "..."
}
},
"deep-graph": {
"name": "Deep Graph MCP (Code Graph)",
"description": "Transforms source code into semantic graphs via DeepGraph",
"command": "mcp-code-graph",
"args": [],
"env": {}
}
}
}
+276
View File
@@ -0,0 +1,276 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is a Python project optimized for modern Python development. The project uses industry-standard tools and follows best practices for scalable application development.
## Development Commands
### Environment Management
- `python -m venv venv` - Create virtual environment
- `source venv/bin/activate` (Linux/Mac) or `venv\Scripts\activate` (Windows) - Activate virtual environment
- `deactivate` - Deactivate virtual environment
- `pip install -r requirements.txt` - Install dependencies
- `pip install -r requirements-dev.txt` - Install development dependencies
### Package Management
- `pip install <package>` - Install a package
- `pip install -e .` - Install project in development mode
- `pip freeze > requirements.txt` - Generate requirements file
- `pip-tools compile requirements.in` - Compile requirements with pip-tools
### Testing Commands
- `pytest` - Run all tests
- `pytest -v` - Run tests with verbose output
- `pytest --cov` - Run tests with coverage report
- `pytest --cov-report=html` - Generate HTML coverage report
- `pytest -x` - Stop on first failure
- `pytest -k "test_name"` - Run specific test by name
- `python -m unittest` - Run tests with unittest
### Code Quality Commands
- `black .` - Format code with Black
- `black --check .` - Check code formatting without changes
- `isort .` - Sort imports
- `isort --check-only .` - Check import sorting
- `flake8` - Run linting with Flake8
- `pylint src/` - Run linting with Pylint
- `mypy src/` - Run type checking with MyPy
### Development Tools
- `python -m pip install --upgrade pip` - Upgrade pip
- `python -c "import sys; print(sys.version)"` - Check Python version
- `python -m site` - Show Python site information
- `python -m pdb script.py` - Debug with pdb
## Technology Stack
### Core Technologies
- **Python** - Primary programming language (3.8+)
- **pip** - Package management
- **venv** - Virtual environment management
### Common Frameworks
- **Django** - High-level web framework
- **Flask** - Micro web framework
- **FastAPI** - Modern API framework with automatic documentation
- **SQLAlchemy** - SQL toolkit and ORM
- **Pydantic** - Data validation using Python type hints
### Data Science & ML
- **NumPy** - Numerical computing
- **Pandas** - Data manipulation and analysis
- **Matplotlib/Seaborn** - Data visualization
- **Scikit-learn** - Machine learning library
- **TensorFlow/PyTorch** - Deep learning frameworks
### Testing Frameworks
- **pytest** - Testing framework
- **unittest** - Built-in testing framework
- **pytest-cov** - Coverage plugin for pytest
- **factory-boy** - Test fixtures
- **responses** - Mock HTTP requests
### Code Quality Tools
- **Black** - Code formatter
- **isort** - Import sorter
- **flake8** - Style guide enforcement
- **pylint** - Code analysis
- **mypy** - Static type checker
- **pre-commit** - Git hooks framework
## Project Structure Guidelines
### File Organization
```
src/
├── package_name/
│ ├── __init__.py
│ ├── main.py # Application entry point
│ ├── models/ # Data models
│ ├── views/ # Web views (Django/Flask)
│ ├── api/ # API endpoints
│ ├── services/ # Business logic
│ ├── utils/ # Utility functions
│ └── config/ # Configuration files
tests/
├── __init__.py
├── conftest.py # pytest configuration
├── test_models.py
├── test_views.py
└── test_utils.py
requirements/
├── base.txt # Base requirements
├── dev.txt # Development requirements
└── prod.txt # Production requirements
```
### Naming Conventions
- **Files/Modules**: Use snake_case (`user_profile.py`)
- **Classes**: Use PascalCase (`UserProfile`)
- **Functions/Variables**: Use snake_case (`get_user_data`)
- **Constants**: Use UPPER_SNAKE_CASE (`API_BASE_URL`)
- **Private methods**: Prefix with underscore (`_private_method`)
## Python Guidelines
### Type Hints
- Use type hints for function parameters and return values
- Import types from `typing` module when needed
- Use `Optional` for nullable values
- Use `Union` for multiple possible types
- Document complex types with comments
### Code Style
- Follow PEP 8 style guide
- Use meaningful variable and function names
- Keep functions focused and single-purpose
- Use docstrings for modules, classes, and functions
- Limit line length to 88 characters (Black default)
### Best Practices
- Use list comprehensions for simple transformations
- Prefer `pathlib` over `os.path` for file operations
- Use context managers (`with` statements) for resource management
- Handle exceptions appropriately with try/except blocks
- Use `logging` module instead of print statements
## Testing Standards
### Test Structure
- Organize tests to mirror source code structure
- Use descriptive test names that explain the behavior
- Follow AAA pattern (Arrange, Act, Assert)
- Use fixtures for common test data
- Group related tests in classes
### Coverage Goals
- Aim for 90%+ test coverage
- Write unit tests for business logic
- Use integration tests for external dependencies
- Mock external services in tests
- Test error conditions and edge cases
### pytest Configuration
```python
# pytest.ini or pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "--cov=src --cov-report=term-missing"
```
## Virtual Environment Setup
### Creation and Activation
```bash
# Create virtual environment
python -m venv venv
# Activate (Linux/Mac)
source venv/bin/activate
# Activate (Windows)
venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
pip install -r requirements-dev.txt
```
### Requirements Management
- Use `requirements.txt` for production dependencies
- Use `requirements-dev.txt` for development dependencies
- Consider using `pip-tools` for dependency resolution
- Pin versions for reproducible builds
## Django-Specific Guidelines
### Project Structure
```
project_name/
├── manage.py
├── project_name/
│ ├── __init__.py
│ ├── settings/
│ ├── urls.py
│ └── wsgi.py
├── apps/
│ ├── users/
│ ├── products/
│ └── orders/
└── requirements/
```
### Common Commands
- `python manage.py runserver` - Start development server
- `python manage.py migrate` - Apply database migrations
- `python manage.py makemigrations` - Create new migrations
- `python manage.py createsuperuser` - Create admin user
- `python manage.py collectstatic` - Collect static files
- `python manage.py test` - Run Django tests
## FastAPI-Specific Guidelines
### Project Structure
```
src/
├── main.py # FastAPI application
├── api/
│ ├── __init__.py
│ ├── dependencies.py # Dependency injection
│ └── v1/
│ ├── __init__.py
│ └── endpoints/
├── core/
│ ├── __init__.py
│ ├── config.py # Settings
│ └── security.py # Authentication
├── models/
├── schemas/ # Pydantic models
└── services/
```
### Common Commands
- `uvicorn main:app --reload` - Start development server
- `uvicorn main:app --host 0.0.0.0 --port 8000` - Start production server
## Security Guidelines
### Dependencies
- Regularly update dependencies with `pip list --outdated`
- Use `safety` package to check for known vulnerabilities
- Pin dependency versions in requirements files
- Use virtual environments to isolate dependencies
### Code Security
- Validate input data with Pydantic or similar
- Use environment variables for sensitive configuration
- Implement proper authentication and authorization
- Sanitize data before database operations
- Use HTTPS for production deployments
## Development Workflow
### Before Starting
1. Check Python version compatibility
2. Create and activate virtual environment
3. Install dependencies from requirements files
4. Run type checking with `mypy`
### During Development
1. Use type hints for better code documentation
2. Run tests frequently to catch issues early
3. Use meaningful commit messages
4. Format code with Black before committing
### Before Committing
1. Run full test suite: `pytest`
2. Check code formatting: `black --check .`
3. Sort imports: `isort --check-only .`
4. Run linting: `flake8`
5. Run type checking: `mypy src/`
@@ -0,0 +1,264 @@
# Django Admin Configuration
Configure Django admin interface with custom admin classes and functionality.
## Purpose
This command helps you create comprehensive Django admin configurations with advanced features and customizations.
## Usage
```
/admin
```
## What this command does
1. **Registers models** with custom admin classes
2. **Creates advanced admin interfaces** with filtering, search, and actions
3. **Adds inline editing** for related models
4. **Customizes list displays** and forms
5. **Implements admin actions** for bulk operations
## Example Output
```python
# admin.py
from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils.safestring import mark_safe
from .models import Post, Category, Tag, Comment
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
"""Admin configuration for Category model."""
list_display = ['name', 'slug', 'post_count', 'created_at']
list_filter = ['created_at']
search_fields = ['name', 'description']
prepopulated_fields = {'slug': ('name',)}
readonly_fields = ['created_at', 'updated_at']
def post_count(self, obj):
"""Display number of posts in this category."""
count = obj.posts.count()
url = reverse('admin:blog_post_changelist') + f'?category__id__exact={obj.id}'
return format_html('<a href="{}">{} posts</a>', url, count)
post_count.short_description = 'Posts'
class CommentInline(admin.TabularInline):
"""Inline admin for comments."""
model = Comment
extra = 0
readonly_fields = ['created_at', 'author']
fields = ['author', 'content', 'is_approved', 'created_at']
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
"""Advanced admin configuration for Post model."""
list_display = [
'title',
'author',
'category',
'status',
'view_count',
'created_at',
'post_preview'
]
list_filter = [
'status',
'category',
'created_at',
'updated_at',
('author', admin.RelatedOnlyFieldListFilter)
]
search_fields = ['title', 'content', 'author__username']
prepopulated_fields = {'slug': ('title',)}
readonly_fields = ['created_at', 'updated_at', 'view_count', 'post_preview']
# Custom form layout
fieldsets = (
('Content', {
'fields': ('title', 'slug', 'content', 'status')
}),
('Metadata', {
'fields': ('author', 'category', 'tags'),
'classes': ('collapse',)
}),
('SEO', {
'fields': ('meta_description', 'meta_keywords'),
'classes': ('collapse',)
}),
('Timestamps', {
'fields': ('created_at', 'updated_at', 'view_count'),
'classes': ('collapse',)
}),
)
# Many-to-many field display
filter_horizontal = ['tags']
# Inline models
inlines = [CommentInline]
# Custom list display methods
def post_preview(self, obj):
"""Show thumbnail preview of post."""
if obj.featured_image:
return format_html(
'<img src="{}" width="50" height="50" style="border-radius: 5px;" />',
obj.featured_image.url
)
return "No image"
post_preview.short_description = 'Preview'
# Custom admin actions
actions = ['make_published', 'make_draft', 'duplicate_posts']
def make_published(self, request, queryset):
"""Bulk action to publish selected posts."""
updated = queryset.update(status='published')
self.message_user(
request,
f'{updated} posts were successfully marked as published.'
)
make_published.short_description = "Mark selected posts as published"
def make_draft(self, request, queryset):
"""Bulk action to set selected posts as draft."""
updated = queryset.update(status='draft')
self.message_user(
request,
f'{updated} posts were successfully marked as draft.'
)
make_draft.short_description = "Mark selected posts as draft"
def duplicate_posts(self, request, queryset):
"""Bulk action to duplicate selected posts."""
count = 0
for post in queryset:
post.pk = None # Create new instance
post.title = f"Copy of {post.title}"
post.slug = f"copy-of-{post.slug}"
post.status = 'draft'
post.save()
count += 1
self.message_user(
request,
f'{count} posts were successfully duplicated.'
)
duplicate_posts.short_description = "Duplicate selected posts"
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
"""Admin configuration for Tag model."""
list_display = ['name', 'slug', 'post_count', 'color_preview']
search_fields = ['name']
prepopulated_fields = {'slug': ('name',)}
def post_count(self, obj):
"""Display number of posts with this tag."""
return obj.posts.count()
post_count.short_description = 'Posts'
def color_preview(self, obj):
"""Show color preview if tag has color field."""
if hasattr(obj, 'color') and obj.color:
return format_html(
'<span style="background-color: {}; padding: 2px 8px; border-radius: 3px; color: white;">{}</span>',
obj.color,
obj.name
)
return obj.name
color_preview.short_description = 'Preview'
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
"""Admin configuration for Comment model."""
list_display = ['author', 'post', 'content_preview', 'is_approved', 'created_at']
list_filter = ['is_approved', 'created_at', 'post__category']
search_fields = ['content', 'author__username', 'post__title']
readonly_fields = ['created_at', 'updated_at']
actions = ['approve_comments', 'disapprove_comments']
def content_preview(self, obj):
"""Show truncated content preview."""
return obj.content[:50] + "..." if len(obj.content) > 50 else obj.content
content_preview.short_description = 'Content'
def approve_comments(self, request, queryset):
"""Bulk approve comments."""
updated = queryset.update(is_approved=True)
self.message_user(request, f'{updated} comments were approved.')
approve_comments.short_description = "Approve selected comments"
def disapprove_comments(self, request, queryset):
"""Bulk disapprove comments."""
updated = queryset.update(is_approved=False)
self.message_user(request, f'{updated} comments were disapproved.')
disapprove_comments.short_description = "Disapprove selected comments"
# Custom admin site configuration
admin.site.site_header = "Blog Administration"
admin.site.site_title = "Blog Admin Portal"
admin.site.index_title = "Welcome to Blog Administration"
```
## Advanced Features
### Custom List Display
- **Thumbnail previews** for images
- **Related object counts** with links
- **Status indicators** with colors
- **Custom formatting** for data display
### Filtering and Search
- **Advanced filters** including date ranges
- **Related field filtering** for foreign keys
- **Search across multiple fields** including relations
- **Custom filter classes** for complex queries
### Inline Editing
- **TabularInline** for compact editing
- **StackedInline** for detailed forms
- **Custom inline forms** with additional functionality
- **Readonly fields** in inlines
### Bulk Actions
- **Status changes** for multiple objects
- **Data export** functionality
- **Batch operations** for efficiency
- **Custom business logic** in actions
### Form Customization
- **Fieldsets** for organized layouts
- **Collapsed sections** for advanced options
- **Custom widgets** for better UX
- **Validation** and error handling
## Security Considerations
- **Permission checks** in custom methods
- **Input sanitization** in admin actions
- **CSRF protection** (automatic)
- **User authentication** (built-in)
## Performance Optimization
```python
# Optimize queries with select_related/prefetch_related
class PostAdmin(admin.ModelAdmin):
def get_queryset(self, request):
queryset = super().get_queryset(request)
return queryset.select_related('author', 'category').prefetch_related('tags')
```
## Custom Admin Templates
```python
# Override admin templates
class PostAdmin(admin.ModelAdmin):
change_form_template = 'admin/blog/post/change_form.html'
change_list_template = 'admin/blog/post/change_list.html'
```
@@ -0,0 +1,124 @@
# Django Model Generator
Create Django models with proper structure and relationships.
## Purpose
This command helps you quickly create Django models with fields, relationships, and best practices.
## Usage
```
/django-model
```
## What this command does
1. **Creates model classes** with proper field definitions
2. **Adds relationships** (ForeignKey, ManyToMany, OneToOne)
3. **Includes meta options** and model methods
4. **Generates migrations** automatically
5. **Follows Django conventions** and best practices
## Example Output
```python
# models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "categories"
ordering = ['name']
def __str__(self):
return self.name
class Post(models.Model):
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
('archived', 'Archived'),
]
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
tags = models.ManyToManyField('Tag', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
verbose_name = 'blog post'
verbose_name_plural = 'blog posts'
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', kwargs={'slug': self.slug})
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(unique=True)
def __str__(self):
return self.name
```
## Field Types Supported
- **CharField** - Text fields with max length
- **TextField** - Large text fields
- **IntegerField** - Integer numbers
- **FloatField** - Floating point numbers
- **BooleanField** - True/False values
- **DateField** - Date only
- **DateTimeField** - Date and time
- **EmailField** - Email addresses
- **URLField** - URLs
- **ImageField** - Image uploads
- **FileField** - File uploads
- **JSONField** - JSON data (PostgreSQL)
## Relationships
- **ForeignKey** - One-to-many relationships
- **ManyToManyField** - Many-to-many relationships
- **OneToOneField** - One-to-one relationships
## Best Practices Included
- Proper field choices and defaults
- Appropriate related_name attributes
- __str__ methods for admin interface
- Meta class with ordering and verbose names
- get_absolute_url methods where appropriate
- Proper use of null and blank parameters
- Field validation and constraints
## After Creating Models
```bash
# Create and apply migrations
python manage.py makemigrations
python manage.py migrate
# Register in admin (optional)
# Add to admin.py:
from django.contrib import admin
from .models import Post, Category, Tag
admin.site.register([Post, Category, Tag])
```
@@ -0,0 +1,222 @@
# Django Views Generator
Create Django views with proper structure and best practices.
## Purpose
This command helps you quickly create Django views (Function-Based Views and Class-Based Views) following Django conventions.
## Usage
```
/views
```
## What this command does
1. **Creates view functions/classes** with proper structure
2. **Handles HTTP methods** (GET, POST, PUT, DELETE)
3. **Includes form handling** and validation
4. **Adds authentication/authorization** checks
5. **Follows Django best practices** and security guidelines
## Example Output
```python
# views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.http import JsonResponse
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from .models import Post, Category
from .forms import PostForm
# Function-Based Views
def post_list(request):
"""Display list of posts with pagination and filtering."""
posts = Post.objects.filter(status='published').select_related('author', 'category')
# Search functionality
search_query = request.GET.get('search')
if search_query:
posts = posts.filter(title__icontains=search_query)
# Category filtering
category_id = request.GET.get('category')
if category_id:
posts = posts.filter(category_id=category_id)
context = {
'posts': posts,
'categories': Category.objects.all(),
'search_query': search_query,
}
return render(request, 'blog/post_list.html', context)
def post_detail(request, slug):
"""Display individual post details."""
post = get_object_or_404(Post, slug=slug, status='published')
context = {
'post': post,
'related_posts': Post.objects.filter(
category=post.category,
status='published'
).exclude(id=post.id)[:3]
}
return render(request, 'blog/post_detail.html', context)
@login_required
def post_create(request):
"""Create new post."""
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
form.save_m2m() # Save many-to-many relationships
messages.success(request, 'Post created successfully!')
return redirect('post_detail', slug=post.slug)
else:
form = PostForm()
return render(request, 'blog/post_form.html', {'form': form})
@login_required
def post_edit(request, slug):
"""Edit existing post."""
post = get_object_or_404(Post, slug=slug, author=request.user)
if request.method == 'POST':
form = PostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
form.save()
messages.success(request, 'Post updated successfully!')
return redirect('post_detail', slug=post.slug)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_form.html', {
'form': form,
'post': post
})
# Class-Based Views
class PostListView(ListView):
"""List view for posts with pagination."""
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
paginate_by = 10
def get_queryset(self):
return Post.objects.filter(status='published').select_related('author', 'category')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
class PostDetailView(DetailView):
"""Detail view for individual posts."""
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
def get_queryset(self):
return Post.objects.filter(status='published')
class PostCreateView(LoginRequiredMixin, CreateView):
"""Create view for new posts."""
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UpdateView):
"""Update view for existing posts."""
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
def get_queryset(self):
return Post.objects.filter(author=self.request.user)
class PostDeleteView(LoginRequiredMixin, DeleteView):
"""Delete view for posts."""
model = Post
template_name = 'blog/post_confirm_delete.html'
success_url = reverse_lazy('post_list')
def get_queryset(self):
return Post.objects.filter(author=self.request.user)
# API Views
def api_post_list(request):
"""API endpoint for post list."""
posts = Post.objects.filter(status='published').values(
'id', 'title', 'slug', 'created_at', 'author__username'
)
return JsonResponse(list(posts), safe=False)
```
## View Types Supported
- **Function-Based Views (FBV)** - Simple, explicit control
- **Class-Based Views (CBV)** - Reusable, inheritance-based
- **Generic Views** - ListView, DetailView, CreateView, etc.
- **API Views** - JSON responses for AJAX/API calls
## Features Included
- **Authentication checks** with decorators/mixins
- **Permission handling** for user authorization
- **Form processing** with validation
- **Error handling** and user feedback
- **SEO-friendly URLs** with slugs
- **Database optimization** with select_related/prefetch_related
- **Pagination** for large datasets
- **Search and filtering** functionality
## Security Best Practices
- CSRF protection (automatic with forms)
- User authentication and authorization
- SQL injection prevention (ORM)
- XSS protection with template escaping
- Proper error handling
## URL Configuration
```python
# urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
# Function-based views
path('', views.post_list, name='post_list'),
path('post/<slug:slug>/', views.post_detail, name='post_detail'),
path('create/', views.post_create, name='post_create'),
path('edit/<slug:slug>/', views.post_edit, name='post_edit'),
# Class-based views
path('posts/', views.PostListView.as_view(), name='post_list_cbv'),
path('posts/<slug:slug>/', views.PostDetailView.as_view(), name='post_detail_cbv'),
path('posts/create/', views.PostCreateView.as_view(), name='post_create_cbv'),
path('posts/<slug:slug>/edit/', views.PostUpdateView.as_view(), name='post_edit_cbv'),
path('posts/<slug:slug>/delete/', views.PostDeleteView.as_view(), name='post_delete_cbv'),
# API endpoints
path('api/posts/', views.api_post_list, name='api_post_list'),
]
```
@@ -0,0 +1,313 @@
# Django Project Configuration
This file provides specific guidance for Django web application development using Claude Code.
## Project Overview
This is a Django web application project optimized for scalable web development with the Django framework. The project follows Django best practices and conventions.
## Django-Specific Development Commands
### Project Management
- `django-admin startproject myproject` - Create new Django project
- `python manage.py startapp myapp` - Create new Django app
- `python manage.py runserver` - Start development server
- `python manage.py runserver 0.0.0.0:8000` - Start server accessible from network
### Database Management
- `python manage.py makemigrations` - Create database migrations
- `python manage.py migrate` - Apply database migrations
- `python manage.py showmigrations` - Show migration status
- `python manage.py sqlmigrate app_name migration_name` - Show SQL for migration
- `python manage.py dbshell` - Open database shell
### User Management
- `python manage.py createsuperuser` - Create admin superuser
- `python manage.py changepassword username` - Change user password
- `python manage.py shell` - Open Django shell
### Static Files & Media
- `python manage.py collectstatic` - Collect static files for production
- `python manage.py findstatic filename` - Find static file location
### Testing & Quality
- `python manage.py test` - Run Django tests
- `python manage.py test app_name` - Run tests for specific app
- `python manage.py test --keepdb` - Run tests keeping test database
- `coverage run --source='.' manage.py test` - Run tests with coverage
### Development Tools
- `python manage.py check` - Check for Django issues
- `python manage.py validate` - Validate models
- `python manage.py inspectdb` - Generate models from existing database
- `python manage.py dumpdata app_name` - Export data
- `python manage.py loaddata fixture.json` - Import data
## Django Project Structure
```
myproject/
├── manage.py # Django management script
├── myproject/ # Project configuration
│ ├── __init__.py
│ ├── settings/ # Settings modules
│ │ ├── __init__.py
│ │ ├── base.py # Base settings
│ │ ├── development.py # Development settings
│ │ ├── production.py # Production settings
│ │ └── testing.py # Testing settings
│ ├── urls.py # URL configuration
│ ├── wsgi.py # WSGI configuration
│ └── asgi.py # ASGI configuration
├── apps/ # Django applications
│ ├── users/ # User management app
│ ├── blog/ # Blog app example
│ └── api/ # API app
├── static/ # Static files
├── media/ # User uploaded files
├── templates/ # Django templates
├── requirements/ # Requirements files
│ ├── base.txt
│ ├── development.txt
│ └── production.txt
└── tests/ # Test files
```
## Django Settings Configuration
### Base Settings (settings/base.py)
```python
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
# Security
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = False
ALLOWED_HOSTS = []
# Application definition
DJANGO_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
THIRD_PARTY_APPS = [
'rest_framework',
'django_extensions',
]
LOCAL_APPS = [
'apps.users',
'apps.blog',
]
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST', 'localhost'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}
# Internationalization
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [BASE_DIR / 'static']
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
```
## Django Best Practices
### Models
- Use descriptive model names (singular)
- Add `__str__` methods for better admin interface
- Use `related_name` for foreign keys
- Implement `get_absolute_url` method
- Add proper Meta class with ordering
### Views
- Use class-based views for complex logic
- Implement proper error handling
- Add pagination for list views
- Use `select_related` and `prefetch_related` for optimization
- Implement proper permission checks
### URLs
- Use app namespaces
- Use descriptive URL names
- Group related URLs in separate files
- Use slug fields for SEO-friendly URLs
### Templates
- Extend base templates
- Use template inheritance effectively
- Create reusable template tags
- Implement proper CSRF protection
- Use Django's built-in template filters
### Forms
- Use Django forms for validation
- Implement custom form validation
- Use ModelForms when appropriate
- Add proper error handling
- Implement CSRF protection
## Security Considerations
### Django Security Settings
```python
# Security settings for production
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_HSTS_SECONDS = 31536000
SECURE_REDIRECT_EXEMPT = []
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
```
### User Authentication
- Use Django's built-in authentication
- Implement proper password policies
- Add two-factor authentication if needed
- Use Django's permission system
- Implement proper session management
## Testing Strategy
### Test Organization
```python
# tests/test_models.py
from django.test import TestCase
from django.contrib.auth import get_user_model
from apps.blog.models import Post
User = get_user_model()
class PostModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
def test_post_creation(self):
post = Post.objects.create(
title='Test Post',
content='Test content',
author=self.user
)
self.assertEqual(post.title, 'Test Post')
self.assertEqual(str(post), 'Test Post')
```
### Test Types
- **Unit tests** for models and utilities
- **Integration tests** for views and forms
- **Functional tests** for user workflows
- **API tests** for REST endpoints
## Deployment Considerations
### Production Settings
- Use environment variables for sensitive data
- Configure proper logging
- Set up static file serving
- Configure database connection pooling
- Implement proper caching strategy
### Docker Configuration
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]
```
## Performance Optimization
### Database Optimization
- Use `select_related()` for foreign keys
- Use `prefetch_related()` for many-to-many
- Add database indexes for frequent queries
- Implement database connection pooling
- Use database query optimization tools
### Caching Strategy
- Implement Redis/Memcached for session storage
- Use template fragment caching
- Implement view-level caching
- Add database query caching
- Use CDN for static files
## Common Django Patterns
### Custom User Model
```python
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
```
### Custom Managers
```python
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published')
class Post(models.Model):
title = models.CharField(max_length=200)
status = models.CharField(max_length=20, default='draft')
objects = models.Manager() # Default manager
published = PublishedManager() # Custom manager
```
## Django Extensions & Tools
### Useful Third-Party Packages
- **Django REST Framework** - API development
- **Celery** - Asynchronous task processing
- **Django Debug Toolbar** - Development debugging
- **Django Extensions** - Additional management commands
- **Pillow** - Image processing
- **psycopg2-binary** - PostgreSQL adapter
@@ -0,0 +1,513 @@
# FastAPI Endpoints Generator
Create comprehensive FastAPI endpoints with proper structure, validation, and documentation.
## Purpose
This command helps you quickly create FastAPI endpoints with Pydantic models, dependency injection, and automatic API documentation.
## Usage
```
/api-endpoints
```
## What this command does
1. **Creates API endpoints** with proper HTTP methods
2. **Adds Pydantic models** for request/response validation
3. **Implements dependency injection** for database and auth
4. **Includes error handling** and status codes
5. **Generates automatic documentation** with OpenAPI
## Example Output
```python
# main.py
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
import uvicorn
from app.database import get_db, engine
from app.models import models
from app.routers import auth, users, posts, comments
from app.core.config import settings
# Create database tables
models.Base.metadata.create_all(bind=engine)
# Initialize FastAPI app
app = FastAPI(
title="Blog API",
description="A comprehensive blog API built with FastAPI",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=settings.ALLOWED_HOSTS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include routers
app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
app.include_router(users.router, prefix="/users", tags=["Users"])
app.include_router(posts.router, prefix="/posts", tags=["Posts"])
app.include_router(comments.router, prefix="/comments", tags=["Comments"])
@app.get("/", tags=["Root"])
async def root():
"""API root endpoint."""
return {
"message": "Welcome to Blog API",
"version": "1.0.0",
"docs": "/docs",
"redoc": "/redoc"
}
@app.get("/health", tags=["Health"])
async def health_check():
"""Health check endpoint."""
return {"status": "healthy", "timestamp": "2024-01-01T00:00:00Z"}
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True
)
```
```python
# app/routers/posts.py
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from app.database import get_db
from app.schemas import post_schemas
from app.services import post_service
from app.core.dependencies import get_current_user, get_current_active_user
from app.models.user import User
router = APIRouter()
@router.get("/", response_model=List[post_schemas.PostResponse])
async def get_posts(
skip: int = Query(0, ge=0, description="Number of posts to skip"),
limit: int = Query(10, ge=1, le=100, description="Number of posts to return"),
search: Optional[str] = Query(None, description="Search in title and content"),
category: Optional[str] = Query(None, description="Filter by category"),
published: Optional[bool] = Query(True, description="Filter by published status"),
db: Session = Depends(get_db)
):
"""
Get all posts with pagination and filtering.
- **skip**: Number of posts to skip (for pagination)
- **limit**: Maximum number of posts to return (1-100)
- **search**: Search term for title and content
- **category**: Filter posts by category
- **published**: Filter by published status
"""
posts = post_service.get_posts(
db=db,
skip=skip,
limit=limit,
search=search,
category=category,
published=published
)
return posts
@router.get("/{post_id}", response_model=post_schemas.PostResponse)
async def get_post(
post_id: int,
db: Session = Depends(get_db)
):
"""
Get a specific post by ID.
- **post_id**: Unique identifier for the post
"""
post = post_service.get_post(db=db, post_id=post_id)
if not post:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Post not found"
)
return post
@router.post("/", response_model=post_schemas.PostResponse, status_code=status.HTTP_201_CREATED)
async def create_post(
post: post_schemas.PostCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Create a new post.
- **title**: Post title (required)
- **content**: Post content (required)
- **category**: Post category (optional)
- **published**: Publication status (default: false)
"""
return post_service.create_post(
db=db,
post=post,
user_id=current_user.id
)
@router.put("/{post_id}", response_model=post_schemas.PostResponse)
async def update_post(
post_id: int,
post_update: post_schemas.PostUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Update an existing post.
- **post_id**: Unique identifier for the post
- **title**: Updated post title (optional)
- **content**: Updated post content (optional)
- **category**: Updated post category (optional)
- **published**: Updated publication status (optional)
"""
post = post_service.get_post(db=db, post_id=post_id)
if not post:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Post not found"
)
if post.author_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to update this post"
)
return post_service.update_post(
db=db,
post_id=post_id,
post_update=post_update
)
@router.delete("/{post_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_post(
post_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Delete a post.
- **post_id**: Unique identifier for the post to delete
"""
post = post_service.get_post(db=db, post_id=post_id)
if not post:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Post not found"
)
if post.author_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to delete this post"
)
post_service.delete_post(db=db, post_id=post_id)
@router.post("/{post_id}/like", response_model=post_schemas.PostResponse)
async def like_post(
post_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Like/unlike a post.
- **post_id**: Unique identifier for the post to like
"""
post = post_service.get_post(db=db, post_id=post_id)
if not post:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Post not found"
)
return post_service.toggle_like(
db=db,
post_id=post_id,
user_id=current_user.id
)
@router.get("/{post_id}/comments", response_model=List[post_schemas.CommentResponse])
async def get_post_comments(
post_id: int,
skip: int = Query(0, ge=0),
limit: int = Query(10, ge=1, le=100),
db: Session = Depends(get_db)
):
"""
Get all comments for a specific post.
- **post_id**: Unique identifier for the post
- **skip**: Number of comments to skip
- **limit**: Maximum number of comments to return
"""
post = post_service.get_post(db=db, post_id=post_id)
if not post:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Post not found"
)
return post_service.get_post_comments(
db=db,
post_id=post_id,
skip=skip,
limit=limit
)
```
```python
# app/schemas/post_schemas.py
from typing import Optional, List
from datetime import datetime
from pydantic import BaseModel, Field, validator
class PostBase(BaseModel):
"""Base post schema."""
title: str = Field(..., min_length=1, max_length=200, description="Post title")
content: str = Field(..., min_length=1, description="Post content")
category: Optional[str] = Field(None, max_length=50, description="Post category")
published: bool = Field(False, description="Publication status")
class PostCreate(PostBase):
"""Schema for creating a post."""
@validator('title')
def validate_title(cls, v):
if not v.strip():
raise ValueError('Title cannot be empty')
return v.strip()
@validator('content')
def validate_content(cls, v):
if len(v.strip()) < 10:
raise ValueError('Content must be at least 10 characters long')
return v.strip()
class PostUpdate(BaseModel):
"""Schema for updating a post."""
title: Optional[str] = Field(None, min_length=1, max_length=200)
content: Optional[str] = Field(None, min_length=1)
category: Optional[str] = Field(None, max_length=50)
published: Optional[bool] = None
@validator('title')
def validate_title(cls, v):
if v is not None and not v.strip():
raise ValueError('Title cannot be empty')
return v.strip() if v else v
@validator('content')
def validate_content(cls, v):
if v is not None and len(v.strip()) < 10:
raise ValueError('Content must be at least 10 characters long')
return v.strip() if v else v
class PostResponse(PostBase):
"""Schema for post responses."""
id: int
author_id: int
created_at: datetime
updated_at: datetime
like_count: int = 0
comment_count: int = 0
class Config:
from_attributes = True
class CommentBase(BaseModel):
"""Base comment schema."""
content: str = Field(..., min_length=1, max_length=1000, description="Comment content")
class CommentCreate(CommentBase):
"""Schema for creating a comment."""
post_id: int = Field(..., description="ID of the post to comment on")
class CommentResponse(CommentBase):
"""Schema for comment responses."""
id: int
post_id: int
author_id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
```
```python
# app/services/post_service.py
from typing import List, Optional
from sqlalchemy.orm import Session
from sqlalchemy import or_
from app.models.post import Post, Comment, PostLike
from app.schemas.post_schemas import PostCreate, PostUpdate
def get_posts(
db: Session,
skip: int = 0,
limit: int = 10,
search: Optional[str] = None,
category: Optional[str] = None,
published: Optional[bool] = True
) -> List[Post]:
"""Get posts with filtering and pagination."""
query = db.query(Post)
if published is not None:
query = query.filter(Post.published == published)
if category:
query = query.filter(Post.category == category)
if search:
query = query.filter(
or_(
Post.title.contains(search),
Post.content.contains(search)
)
)
return query.offset(skip).limit(limit).all()
def get_post(db: Session, post_id: int) -> Optional[Post]:
"""Get a single post by ID."""
return db.query(Post).filter(Post.id == post_id).first()
def create_post(db: Session, post: PostCreate, user_id: int) -> Post:
"""Create a new post."""
db_post = Post(**post.dict(), author_id=user_id)
db.add(db_post)
db.commit()
db.refresh(db_post)
return db_post
def update_post(
db: Session,
post_id: int,
post_update: PostUpdate
) -> Optional[Post]:
"""Update an existing post."""
db_post = db.query(Post).filter(Post.id == post_id).first()
if not db_post:
return None
update_data = post_update.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(db_post, field, value)
db.commit()
db.refresh(db_post)
return db_post
def delete_post(db: Session, post_id: int) -> bool:
"""Delete a post."""
db_post = db.query(Post).filter(Post.id == post_id).first()
if not db_post:
return False
db.delete(db_post)
db.commit()
return True
def toggle_like(db: Session, post_id: int, user_id: int) -> Post:
"""Toggle like status for a post."""
existing_like = db.query(PostLike).filter(
PostLike.post_id == post_id,
PostLike.user_id == user_id
).first()
if existing_like:
db.delete(existing_like)
else:
new_like = PostLike(post_id=post_id, user_id=user_id)
db.add(new_like)
db.commit()
return get_post(db, post_id)
```
## Features Included
### API Documentation
- **Automatic OpenAPI** schema generation
- **Interactive docs** at `/docs`
- **ReDoc documentation** at `/redoc`
- **Request/Response examples** in schemas
### Validation & Serialization
- **Pydantic models** for data validation
- **Custom validators** for business rules
- **Type hints** for better IDE support
- **Automatic data conversion** and validation
### Error Handling
- **HTTP status codes** for different scenarios
- **Detailed error messages** with context
- **Input validation errors** with field-specific messages
- **Custom exception handlers** for consistent responses
### Security
- **JWT authentication** with dependencies
- **Role-based access control** for endpoints
- **CORS middleware** for cross-origin requests
- **Input sanitization** through Pydantic
### Performance
- **Database query optimization** with SQLAlchemy
- **Pagination support** for large datasets
- **Async/await support** for concurrent requests
- **Connection pooling** for database efficiency
## Testing Example
```python
# tests/test_posts.py
import pytest
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_get_posts():
"""Test getting posts."""
response = client.get("/posts/")
assert response.status_code == 200
assert isinstance(response.json(), list)
def test_create_post():
"""Test creating a new post."""
post_data = {
"title": "Test Post",
"content": "This is a test post content.",
"published": True
}
response = client.post("/posts/", json=post_data)
assert response.status_code == 201
assert response.json()["title"] == "Test Post"
```
@@ -0,0 +1,775 @@
# FastAPI Authentication & Authorization
Complete authentication system with JWT tokens, OAuth2, and role-based access control.
## Usage
```bash
# Install auth dependencies
pip install python-jose[cryptography] passlib[bcrypt] python-multipart
# Generate secret key
openssl rand -hex 32
```
## JWT Configuration
```python
# app/core/security.py
from datetime import datetime, timedelta
from typing import Optional, Union, Any
from jose import JWTError, jwt
from passlib.context import CryptContext
from app.core.config import settings
# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# JWT settings
SECRET_KEY = settings.SECRET_KEY
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_DAYS = 7
def create_access_token(
subject: Union[str, Any],
expires_delta: Optional[timedelta] = None
) -> str:
"""Create JWT access token."""
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode = {"exp": expire, "sub": str(subject), "type": "access"}
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def create_refresh_token(subject: Union[str, Any]) -> str:
"""Create JWT refresh token."""
expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
to_encode = {"exp": expire, "sub": str(subject), "type": "refresh"}
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verify password against hash."""
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
"""Generate password hash."""
return pwd_context.hash(password)
def decode_token(token: str) -> Optional[dict]:
"""Decode and verify JWT token."""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except JWTError:
return None
```
## Authentication Dependencies
```python
# app/api/dependencies/auth.py
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.security import decode_token
from app.db.database import get_db
from app.models.user import User
from app.repositories.user import UserRepository
# OAuth2 scheme
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="/api/v1/auth/login",
scheme_name="JWT"
)
# Bearer token scheme
security = HTTPBearer()
async def get_current_user(
token: str = Depends(oauth2_scheme),
db: AsyncSession = Depends(get_db)
) -> User:
"""Get current authenticated user."""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
payload = decode_token(token)
if payload is None:
raise credentials_exception
user_id: str = payload.get("sub")
token_type: str = payload.get("type")
if user_id is None or token_type != "access":
raise credentials_exception
user_repo = UserRepository(User, db)
user = await user_repo.get(int(user_id))
if user is None:
raise credentials_exception
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Inactive user"
)
return user
async def get_current_active_user(
current_user: User = Depends(get_current_user)
) -> User:
"""Get current active user."""
if not current_user.is_active:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Inactive user"
)
return current_user
async def get_current_superuser(
current_user: User = Depends(get_current_user)
) -> User:
"""Get current superuser."""
if not current_user.is_superuser:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions"
)
return current_user
def require_permissions(*permissions: str):
"""Decorator for permission-based access control."""
async def permission_checker(
current_user: User = Depends(get_current_active_user)
) -> User:
# Check if user has required permissions
user_permissions = set(current_user.permissions or [])
required_permissions = set(permissions)
if not required_permissions.issubset(user_permissions) and not current_user.is_superuser:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Insufficient permissions"
)
return current_user
return permission_checker
def require_roles(*roles: str):
"""Decorator for role-based access control."""
async def role_checker(
current_user: User = Depends(get_current_active_user)
) -> User:
user_roles = set(role.name for role in current_user.roles or [])
required_roles = set(roles)
if not required_roles.issubset(user_roles) and not current_user.is_superuser:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Insufficient role permissions"
)
return current_user
return role_checker
```
## Authentication Schemas
```python
# app/schemas/auth.py
from pydantic import BaseModel, EmailStr
from typing import Optional
class Token(BaseModel):
"""Token response schema."""
access_token: str
refresh_token: str
token_type: str = "bearer"
expires_in: int
class TokenPayload(BaseModel):
"""Token payload schema."""
sub: Optional[int] = None
exp: Optional[int] = None
type: Optional[str] = None
class UserLogin(BaseModel):
"""User login schema."""
username: str
password: str
class UserRegister(BaseModel):
"""User registration schema."""
username: str
email: EmailStr
password: str
first_name: str
last_name: str
class PasswordReset(BaseModel):
"""Password reset schema."""
email: EmailStr
class PasswordResetConfirm(BaseModel):
"""Password reset confirmation schema."""
token: str
new_password: str
class ChangePassword(BaseModel):
"""Change password schema."""
current_password: str
new_password: str
class RefreshToken(BaseModel):
"""Refresh token schema."""
refresh_token: str
```
## Authentication Endpoints
```python
# app/api/v1/auth.py
from datetime import timedelta
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.database import get_db
from app.models.user import User
from app.repositories.user import UserRepository
from app.schemas.auth import (
Token, UserLogin, UserRegister, PasswordReset,
PasswordResetConfirm, ChangePassword, RefreshToken
)
from app.schemas.user import UserCreate, UserResponse
from app.core.security import (
create_access_token, create_refresh_token, verify_password,
decode_token, ACCESS_TOKEN_EXPIRE_MINUTES
)
from app.api.dependencies.auth import get_current_user, get_current_active_user
from app.services.email import send_password_reset_email
from app.services.auth import AuthService
router = APIRouter()
@router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def register(
user_data: UserRegister,
db: AsyncSession = Depends(get_db)
) -> Any:
"""Register new user."""
user_repo = UserRepository(User, db)
auth_service = AuthService(user_repo)
# Check if user already exists
if await user_repo.get_by_email(user_data.email):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Email already registered"
)
if await user_repo.get_by_username(user_data.username):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Username already taken"
)
# Create user
user = await auth_service.create_user(user_data.dict())
return user
@router.post("/login", response_model=Token)
async def login(
form_data: OAuth2PasswordRequestForm = Depends(),
db: AsyncSession = Depends(get_db)
) -> Any:
"""OAuth2 compatible token login."""
user_repo = UserRepository(User, db)
auth_service = AuthService(user_repo)
user = await auth_service.authenticate_user(
form_data.username,
form_data.password
)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Inactive user"
)
# Create tokens
access_token = create_access_token(subject=user.id)
refresh_token = create_refresh_token(subject=user.id)
return {
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer",
"expires_in": ACCESS_TOKEN_EXPIRE_MINUTES * 60
}
@router.post("/refresh", response_model=Token)
async def refresh_token(
refresh_data: RefreshToken,
db: AsyncSession = Depends(get_db)
) -> Any:
"""Refresh access token."""
payload = decode_token(refresh_data.refresh_token)
if payload is None or payload.get("type") != "refresh":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token"
)
user_id = payload.get("sub")
if user_id is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token"
)
user_repo = UserRepository(User, db)
user = await user_repo.get(int(user_id))
if user is None or not user.is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token"
)
# Create new tokens
access_token = create_access_token(subject=user.id)
new_refresh_token = create_refresh_token(subject=user.id)
return {
"access_token": access_token,
"refresh_token": new_refresh_token,
"token_type": "bearer",
"expires_in": ACCESS_TOKEN_EXPIRE_MINUTES * 60
}
@router.get("/me", response_model=UserResponse)
async def read_users_me(
current_user: User = Depends(get_current_active_user)
) -> Any:
"""Get current user."""
return current_user
@router.post("/change-password", status_code=status.HTTP_200_OK)
async def change_password(
password_data: ChangePassword,
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db)
) -> Any:
"""Change user password."""
if not verify_password(password_data.current_password, current_user.hashed_password):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Incorrect current password"
)
user_repo = UserRepository(User, db)
auth_service = AuthService(user_repo)
await auth_service.change_password(current_user.id, password_data.new_password)
return {"message": "Password changed successfully"}
@router.post("/password-reset", status_code=status.HTTP_200_OK)
async def password_reset(
reset_data: PasswordReset,
background_tasks: BackgroundTasks,
db: AsyncSession = Depends(get_db)
) -> Any:
"""Request password reset."""
user_repo = UserRepository(User, db)
user = await user_repo.get_by_email(reset_data.email)
if user:
# Generate reset token
reset_token = create_access_token(
subject=user.id,
expires_delta=timedelta(hours=1) # 1 hour expiry
)
# Send email with reset token
background_tasks.add_task(
send_password_reset_email,
email=user.email,
username=user.username,
token=reset_token
)
# Always return success to prevent email enumeration
return {"message": "Password reset email sent if account exists"}
@router.post("/password-reset-confirm", status_code=status.HTTP_200_OK)
async def password_reset_confirm(
reset_data: PasswordResetConfirm,
db: AsyncSession = Depends(get_db)
) -> Any:
"""Confirm password reset."""
payload = decode_token(reset_data.token)
if payload is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid or expired reset token"
)
user_id = payload.get("sub")
if user_id is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid reset token"
)
user_repo = UserRepository(User, db)
auth_service = AuthService(user_repo)
user = await user_repo.get(int(user_id))
if user is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid reset token"
)
await auth_service.change_password(user.id, reset_data.new_password)
return {"message": "Password reset successful"}
@router.post("/logout", status_code=status.HTTP_200_OK)
async def logout(
current_user: User = Depends(get_current_user)
) -> Any:
"""Logout user (invalidate token on client side)."""
# In a more sophisticated setup, you might want to blacklist the token
return {"message": "Successfully logged out"}
```
## Authentication Service
```python
# app/services/auth.py
from typing import Optional
from app.models.user import User
from app.repositories.user import UserRepository
from app.core.security import verify_password, get_password_hash
class AuthService:
"""Authentication service."""
def __init__(self, user_repository: UserRepository):
self.user_repo = user_repository
async def authenticate_user(
self,
username_or_email: str,
password: str
) -> Optional[User]:
"""Authenticate user by username/email and password."""
# Try to get user by username first, then by email
user = await self.user_repo.get_by_username(username_or_email)
if not user:
user = await self.user_repo.get_by_email(username_or_email)
if not user:
return None
if not verify_password(password, user.hashed_password):
return None
return user
async def create_user(self, user_data: dict) -> User:
"""Create new user."""
# Hash password
password = user_data.pop('password')
hashed_password = get_password_hash(password)
# Create user data
user_create_data = {
**user_data,
'hashed_password': hashed_password,
'is_active': True,
'is_superuser': False
}
return await self.user_repo.create(user_create_data)
async def change_password(self, user_id: int, new_password: str) -> bool:
"""Change user password."""
hashed_password = get_password_hash(new_password)
result = await self.user_repo.update(user_id, {
'hashed_password': hashed_password
})
return result is not None
async def activate_user(self, user_id: int) -> bool:
"""Activate user account."""
result = await self.user_repo.update(user_id, {'is_active': True})
return result is not None
async def deactivate_user(self, user_id: int) -> bool:
"""Deactivate user account."""
result = await self.user_repo.update(user_id, {'is_active': False})
return result is not None
```
## Role-Based Access Control
```python
# app/models/rbac.py
from sqlalchemy import Column, String, Text, ForeignKey, Table
from sqlalchemy.orm import relationship
from app.models.base import BaseModel
# Association tables for many-to-many relationships
user_roles = Table(
'user_roles',
Base.metadata,
Column('user_id', Integer, ForeignKey('users.id')),
Column('role_id', Integer, ForeignKey('roles.id'))
)
role_permissions = Table(
'role_permissions',
Base.metadata,
Column('role_id', Integer, ForeignKey('roles.id')),
Column('permission_id', Integer, ForeignKey('permissions.id'))
)
class Role(BaseModel):
"""Role model for RBAC."""
__tablename__ = "roles"
name = Column(String(50), unique=True, nullable=False, index=True)
description = Column(Text)
# Relationships
users = relationship("User", secondary=user_roles, back_populates="roles")
permissions = relationship("Permission", secondary=role_permissions, back_populates="roles")
class Permission(BaseModel):
"""Permission model for RBAC."""
__tablename__ = "permissions"
name = Column(String(100), unique=True, nullable=False, index=True)
description = Column(Text)
resource = Column(String(50), nullable=False) # e.g., 'users', 'posts'
action = Column(String(50), nullable=False) # e.g., 'create', 'read', 'update', 'delete'
# Relationships
roles = relationship("Role", secondary=role_permissions, back_populates="permissions")
# Update User model to include roles
class User(BaseModel):
# ... existing fields ...
# Relationships
roles = relationship("Role", secondary=user_roles, back_populates="users")
@property
def permissions(self) -> list[str]:
"""Get all permissions for user."""
perms = set()
for role in self.roles:
for permission in role.permissions:
perms.add(f"{permission.resource}:{permission.action}")
return list(perms)
```
## OAuth2 Integration
```python
# app/api/v1/oauth.py
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security.utils import get_authorization_scheme_param
from starlette.requests import Request
from authlib.integrations.starlette_client import OAuth
from app.core.config import settings
router = APIRouter()
# OAuth configuration
oauth = OAuth()
# Google OAuth
google = oauth.register(
name='google',
client_id=settings.GOOGLE_CLIENT_ID,
client_secret=settings.GOOGLE_CLIENT_SECRET,
server_metadata_url='https://accounts.google.com/.well-known/openid_configuration',
client_kwargs={
'scope': 'openid email profile'
}
)
# GitHub OAuth
github = oauth.register(
name='github',
client_id=settings.GITHUB_CLIENT_ID,
client_secret=settings.GITHUB_CLIENT_SECRET,
access_token_url='https://github.com/login/oauth/access_token',
access_token_params=None,
authorize_url='https://github.com/login/oauth/authorize',
authorize_params=None,
api_base_url='https://api.github.com/',
client_kwargs={'scope': 'user:email'},
)
@router.get('/google')
async def google_login(request: Request):
"""Initiate Google OAuth login."""
redirect_uri = request.url_for('google_callback')
return await google.authorize_redirect(request, redirect_uri)
@router.get('/google/callback')
async def google_callback(request: Request):
"""Handle Google OAuth callback."""
token = await google.authorize_access_token(request)
user_info = token.get('userinfo')
if user_info:
# Create or get user
# Generate JWT token
# Return token
pass
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="OAuth authentication failed"
)
```
## API Key Authentication
```python
# app/models/api_key.py
from sqlalchemy import Column, String, Boolean, ForeignKey, DateTime
from sqlalchemy.orm import relationship
from app.models.base import BaseModel
import secrets
class APIKey(BaseModel):
"""API Key model."""
__tablename__ = "api_keys"
name = Column(String(100), nullable=False)
key_hash = Column(String(255), unique=True, nullable=False, index=True)
prefix = Column(String(10), nullable=False, index=True)
is_active = Column(Boolean, default=True, nullable=False)
expires_at = Column(DateTime)
last_used_at = Column(DateTime)
# Foreign key
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
# Relationships
user = relationship("User", back_populates="api_keys")
@classmethod
def generate_key(cls) -> tuple[str, str]:
"""Generate API key and return (key, hash)."""
key = secrets.token_urlsafe(32)
prefix = key[:8]
key_hash = get_password_hash(key)
return key, prefix, key_hash
def verify_key(self, key: str) -> bool:
"""Verify API key."""
return verify_password(key, self.key_hash)
# Add to User model
class User(BaseModel):
# ... existing fields ...
# Relationships
api_keys = relationship("APIKey", back_populates="user", cascade="all, delete-orphan")
```
## Testing Authentication
```python
# tests/test_auth.py
import pytest
from httpx import AsyncClient
from app.core.security import create_access_token
@pytest.mark.asyncio
async def test_register_user(client: AsyncClient):
"""Test user registration."""
user_data = {
"username": "testuser",
"email": "test@example.com",
"password": "testpass123",
"first_name": "Test",
"last_name": "User"
}
response = await client.post("/api/v1/auth/register", json=user_data)
assert response.status_code == 201
data = response.json()
assert data["username"] == user_data["username"]
assert data["email"] == user_data["email"]
assert "hashed_password" not in data
@pytest.mark.asyncio
async def test_login_user(client: AsyncClient, test_user):
"""Test user login."""
login_data = {
"username": test_user.username,
"password": "testpass123"
}
response = await client.post(
"/api/v1/auth/login",
data=login_data,
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
assert response.status_code == 200
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
assert data["token_type"] == "bearer"
@pytest.mark.asyncio
async def test_get_current_user(client: AsyncClient, test_user):
"""Test get current user endpoint."""
token = create_access_token(subject=test_user.id)
headers = {"Authorization": f"Bearer {token}"}
response = await client.get("/api/v1/auth/me", headers=headers)
assert response.status_code == 200
data = response.json()
assert data["username"] == test_user.username
assert data["email"] == test_user.email
```
@@ -0,0 +1,657 @@
# FastAPI Database Integration
Complete database setup with SQLAlchemy, Alembic, and async support for FastAPI.
## Usage
```bash
# Initialize Alembic
alembic init alembic
# Create migration
alembic revision --autogenerate -m "Initial migration"
# Apply migrations
alembic upgrade head
# Downgrade migration
alembic downgrade -1
```
## Database Configuration
```python
# app/core/config.py
from pydantic import BaseSettings, PostgresDsn, validator
from typing import Optional, Dict, Any
import os
class Settings(BaseSettings):
"""Application settings."""
# Database
POSTGRES_SERVER: str = "localhost"
POSTGRES_USER: str = "postgres"
POSTGRES_PASSWORD: str = "password"
POSTGRES_DB: str = "fastapi_app"
POSTGRES_PORT: str = "5432"
DATABASE_URL: Optional[PostgresDsn] = None
@validator("DATABASE_URL", pre=True)
def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any:
if isinstance(v, str):
return v
return PostgresDsn.build(
scheme="postgresql+asyncpg",
user=values.get("POSTGRES_USER"),
password=values.get("POSTGRES_PASSWORD"),
host=values.get("POSTGRES_SERVER"),
port=values.get("POSTGRES_PORT"),
path=f"/{values.get('POSTGRES_DB') or ''}",
)
# Redis
REDIS_URL: str = "redis://localhost:6379/0"
# Database settings
DATABASE_POOL_SIZE: int = 10
DATABASE_MAX_OVERFLOW: int = 20
DATABASE_POOL_RECYCLE: int = 3600
class Config:
env_file = ".env"
case_sensitive = True
settings = Settings()
```
## Database Setup
```python
# app/db/database.py
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
# Create async engine
engine = create_async_engine(
str(settings.DATABASE_URL),
pool_size=settings.DATABASE_POOL_SIZE,
max_overflow=settings.DATABASE_MAX_OVERFLOW,
pool_recycle=settings.DATABASE_POOL_RECYCLE,
pool_pre_ping=True,
echo=False # Set to True for SQL debugging
)
# Create async session factory
AsyncSessionLocal = sessionmaker(
engine, class_=AsyncSession, expire_on_commit=False
)
# Base class for models
Base = declarative_base()
async def get_db() -> AsyncSession:
"""Dependency to get database session."""
async with AsyncSessionLocal() as session:
try:
yield session
finally:
await session.close()
async def create_tables():
"""Create database tables."""
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def drop_tables():
"""Drop database tables."""
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
```
## Base Model
```python
# app/models/base.py
from sqlalchemy import Column, Integer, DateTime, func
from sqlalchemy.ext.declarative import declared_attr
from app.db.database import Base
from datetime import datetime
from typing import Any
class TimestampMixin:
"""Mixin for timestamp fields."""
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
updated_at = Column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False
)
class BaseModel(Base, TimestampMixin):
"""Base model with common functionality."""
__abstract__ = True
id = Column(Integer, primary_key=True, index=True)
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()
def dict(self, exclude: set = None) -> dict[str, Any]:
"""Convert model to dictionary."""
exclude = exclude or set()
return {
column.name: getattr(self, column.name)
for column in self.__table__.columns
if column.name not in exclude
}
def __repr__(self) -> str:
return f"<{self.__class__.__name__}(id={self.id})>"
```
## Example Models
```python
# app/models/user.py
from sqlalchemy import Column, String, Boolean, Text, Index
from sqlalchemy.orm import relationship
from app.models.base import BaseModel
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
class User(BaseModel):
"""User model."""
__tablename__ = "users"
username = Column(String(50), unique=True, index=True, nullable=False)
email = Column(String(100), unique=True, index=True, nullable=False)
hashed_password = Column(String(255), nullable=False)
first_name = Column(String(50), nullable=False)
last_name = Column(String(50), nullable=False)
is_active = Column(Boolean, default=True, nullable=False)
is_superuser = Column(Boolean, default=False, nullable=False)
bio = Column(Text)
# Relationships
posts = relationship("Post", back_populates="author", cascade="all, delete-orphan")
# Indexes
__table_args__ = (
Index('idx_user_email_active', email, is_active),
Index('idx_user_username_active', username, is_active),
)
def verify_password(self, password: str) -> bool:
"""Verify password against hash."""
return pwd_context.verify(password, self.hashed_password)
@staticmethod
def get_password_hash(password: str) -> str:
"""Generate password hash."""
return pwd_context.hash(password)
def set_password(self, password: str) -> None:
"""Set user password."""
self.hashed_password = self.get_password_hash(password)
@property
def full_name(self) -> str:
"""Get user's full name."""
return f"{self.first_name} {self.last_name}"
def dict(self, exclude: set = None) -> dict:
"""Convert to dict excluding sensitive data."""
exclude = exclude or set()
exclude.add('hashed_password')
return super().dict(exclude=exclude)
# app/models/post.py
from sqlalchemy import Column, String, Text, ForeignKey, Boolean, Index
from sqlalchemy.orm import relationship
from app.models.base import BaseModel
class Post(BaseModel):
"""Blog post model."""
__tablename__ = "posts"
title = Column(String(200), nullable=False, index=True)
content = Column(Text, nullable=False)
slug = Column(String(200), unique=True, nullable=False, index=True)
is_published = Column(Boolean, default=False, nullable=False)
# Foreign keys
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
# Relationships
author = relationship("User", back_populates="posts")
# Indexes
__table_args__ = (
Index('idx_post_published_created', is_published, 'created_at'),
Index('idx_post_author_published', author_id, is_published),
)
```
## Repository Pattern
```python
# app/repositories/base.py
from typing import Generic, TypeVar, Type, Optional, List, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, delete, func
from sqlalchemy.orm import selectinload
from app.models.base import BaseModel
ModelType = TypeVar("ModelType", bound=BaseModel)
class BaseRepository(Generic[ModelType]):
"""Base repository with common CRUD operations."""
def __init__(self, model: Type[ModelType], db: AsyncSession):
self.model = model
self.db = db
async def get(self, id: int) -> Optional[ModelType]:
"""Get model by ID."""
result = await self.db.execute(
select(self.model).where(self.model.id == id)
)
return result.scalar_one_or_none()
async def get_multi(
self,
skip: int = 0,
limit: int = 100,
filters: Dict[str, Any] = None
) -> List[ModelType]:
"""Get multiple models with pagination."""
query = select(self.model)
if filters:
for field, value in filters.items():
if hasattr(self.model, field):
query = query.where(getattr(self.model, field) == value)
query = query.offset(skip).limit(limit)
result = await self.db.execute(query)
return result.scalars().all()
async def create(self, obj_in: Dict[str, Any]) -> ModelType:
"""Create new model."""
db_obj = self.model(**obj_in)
self.db.add(db_obj)
await self.db.commit()
await self.db.refresh(db_obj)
return db_obj
async def update(
self,
id: int,
obj_in: Dict[str, Any]
) -> Optional[ModelType]:
"""Update model by ID."""
await self.db.execute(
update(self.model)
.where(self.model.id == id)
.values(**obj_in)
)
await self.db.commit()
return await self.get(id)
async def delete(self, id: int) -> bool:
"""Delete model by ID."""
result = await self.db.execute(
delete(self.model).where(self.model.id == id)
)
await self.db.commit()
return result.rowcount > 0
async def count(self, filters: Dict[str, Any] = None) -> int:
"""Count models with optional filters."""
query = select(func.count(self.model.id))
if filters:
for field, value in filters.items():
if hasattr(self.model, field):
query = query.where(getattr(self.model, field) == value)
result = await self.db.execute(query)
return result.scalar()
# app/repositories/user.py
from typing import Optional
from sqlalchemy import select
from app.models.user import User
from app.repositories.base import BaseRepository
class UserRepository(BaseRepository[User]):
"""User repository with custom methods."""
async def get_by_email(self, email: str) -> Optional[User]:
"""Get user by email."""
result = await self.db.execute(
select(User).where(User.email == email)
)
return result.scalar_one_or_none()
async def get_by_username(self, username: str) -> Optional[User]:
"""Get user by username."""
result = await self.db.execute(
select(User).where(User.username == username)
)
return result.scalar_one_or_none()
async def get_active_users(self, skip: int = 0, limit: int = 100):
"""Get active users."""
return await self.get_multi(
skip=skip,
limit=limit,
filters={'is_active': True}
)
```
## Alembic Configuration
```python
# alembic/env.py
from logging.config import fileConfig
from sqlalchemy import engine_from_config, pool
from sqlalchemy.ext.asyncio import AsyncEngine
from alembic import context
import asyncio
# Import your models
from app.models.base import Base
from app.models.user import User
from app.models.post import Post
from app.core.config import settings
# Alembic Config object
config = context.config
# Override database URL
config.set_main_option("sqlalchemy.url", str(settings.DATABASE_URL))
# Interpret the config file for logging
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# Add your model's MetaData object here
target_metadata = Base.metadata
def do_run_migrations(connection):
context.configure(
connection=connection,
target_metadata=target_metadata,
compare_type=True,
compare_server_default=True,
)
with context.begin_transaction():
context.run_migrations()
async def run_migrations_online():
"""Run migrations in 'online' mode."""
configuration = config.get_section(config.config_ini_section)
configuration["sqlalchemy.url"] = str(settings.DATABASE_URL)
connectable = AsyncEngine(
engine_from_config(
configuration,
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
)
async with connectable.connect() as connection:
await connection.run_sync(do_run_migrations)
await connectable.dispose()
if context.is_offline_mode():
run_migrations_offline()
else:
asyncio.run(run_migrations_online())
```
## Database Utilities
```python
# app/db/utils.py
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import text
from app.db.database import engine, AsyncSessionLocal
from app.core.config import settings
import asyncio
async def check_database_connection() -> bool:
"""Check if database is accessible."""
try:
async with AsyncSessionLocal() as session:
await session.execute(text("SELECT 1"))
return True
except Exception:
return False
async def create_database_if_not_exists():
"""Create database if it doesn't exist."""
# This is PostgreSQL specific
import asyncpg
from urllib.parse import urlparse
url = urlparse(str(settings.DATABASE_URL))
try:
# Connect to postgres database to create our database
conn = await asyncpg.connect(
host=url.hostname,
port=url.port,
user=url.username,
password=url.password,
database='postgres'
)
# Check if database exists
exists = await conn.fetchval(
"SELECT 1 FROM pg_database WHERE datname = $1",
url.path[1:] # Remove leading slash
)
if not exists:
await conn.execute(f'CREATE DATABASE "{url.path[1:]}"')
print(f"Database {url.path[1:]} created.")
await conn.close()
except Exception as e:
print(f"Error creating database: {e}")
async def execute_raw_sql(sql: str, params: dict = None) -> list:
"""Execute raw SQL query."""
async with AsyncSessionLocal() as session:
result = await session.execute(text(sql), params or {})
return result.fetchall()
async def get_table_info(table_name: str) -> dict:
"""Get information about a table."""
sql = """
SELECT
column_name,
data_type,
is_nullable,
column_default
FROM information_schema.columns
WHERE table_name = :table_name
ORDER BY ordinal_position;
"""
result = await execute_raw_sql(sql, {'table_name': table_name})
return [
{
'column_name': row[0],
'data_type': row[1],
'is_nullable': row[2],
'column_default': row[3]
}
for row in result
]
```
## Database Initialization
```python
# app/db/init_db.py
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.database import get_db, create_tables
from app.models.user import User
from app.repositories.user import UserRepository
from app.core.config import settings
import asyncio
async def init_db() -> None:
"""Initialize database with tables and default data."""
# Create tables
await create_tables()
print("Database tables created.")
# Create default superuser
async with AsyncSessionLocal() as session:
user_repo = UserRepository(User, session)
# Check if superuser exists
existing_user = await user_repo.get_by_email("admin@example.com")
if not existing_user:
superuser_data = {
"username": "admin",
"email": "admin@example.com",
"first_name": "Admin",
"last_name": "User",
"is_superuser": True,
"is_active": True
}
superuser = User(**superuser_data)
superuser.set_password("admin123")
session.add(superuser)
await session.commit()
print("Superuser created.")
else:
print("Superuser already exists.")
if __name__ == "__main__":
asyncio.run(init_db())
```
## Testing Database
```python
# tests/conftest.py
import pytest
import asyncio
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from app.db.database import Base, get_db
from app.main import app
import pytest_asyncio
# Test database URL
TEST_DATABASE_URL = "sqlite+aiosqlite:///./test.db"
@pytest.fixture(scope="session")
def event_loop():
"""Create event loop for async tests."""
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest_asyncio.fixture(scope="session")
async def test_engine():
"""Create test database engine."""
engine = create_async_engine(TEST_DATABASE_URL, echo=False)
# Create tables
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield engine
# Drop tables
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await engine.dispose()
@pytest_asyncio.fixture
async def test_session(test_engine):
"""Create test database session."""
TestSessionLocal = sessionmaker(
test_engine, class_=AsyncSession, expire_on_commit=False
)
async with TestSessionLocal() as session:
yield session
@pytest.fixture
def override_get_db(test_session):
"""Override database dependency."""
async def _override_get_db():
yield test_session
app.dependency_overrides[get_db] = _override_get_db
yield
app.dependency_overrides = {}
```
## Database Health Check
```python
# app/api/health.py
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import text
from app.db.database import get_db
import time
router = APIRouter()
@router.get("/health")
async def health_check(db: AsyncSession = Depends(get_db)):
"""Health check endpoint."""
checks = {
"status": "healthy",
"timestamp": time.time(),
"database": await check_database_health(db),
}
# Determine overall status
if checks["database"]["status"] != "ok":
checks["status"] = "unhealthy"
raise HTTPException(status_code=503, detail=checks)
return checks
async def check_database_health(db: AsyncSession) -> dict:
"""Check database connection."""
try:
start_time = time.time()
await db.execute(text("SELECT 1"))
response_time = (time.time() - start_time) * 1000 # milliseconds
return {
"status": "ok",
"response_time_ms": round(response_time, 2)
}
except Exception as e:
return {
"status": "error",
"error": str(e)
}
```
@@ -0,0 +1,160 @@
# FastAPI Deployment
Basic production deployment setup for FastAPI applications.
## Usage
```bash
# Run with Uvicorn
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
# Docker deployment
docker build -t fastapi-app .
docker run -p 8000:8000 fastapi-app
```
## Production Configuration
```python
# app/core/config.py
from pydantic import BaseSettings
import os
class Settings(BaseSettings):
"""Production settings."""
# App
PROJECT_NAME: str = "FastAPI App"
VERSION: str = "1.0.0"
SECRET_KEY: str = os.getenv("SECRET_KEY", "dev-key")
# Server
HOST: str = "0.0.0.0"
PORT: int = 8000
WORKERS: int = 4
# Database
DATABASE_URL: str = os.getenv("DATABASE_URL", "sqlite:///./app.db")
# Redis
REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379")
class Config:
env_file = ".env"
settings = Settings()
```
## Docker Setup
```dockerfile
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy app
COPY . .
# Create non-root user
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
# Health check
HEALTHCHECK CMD curl -f http://localhost:8000/health || exit 1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```
## Docker Compose
```yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/fastapi_db
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
db:
image: postgres:15
environment:
POSTGRES_DB: fastapi_db
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
```
## Environment Variables
```bash
# .env
SECRET_KEY=your-secret-key
DATABASE_URL=postgresql://user:pass@localhost/fastapi_db
REDIS_URL=redis://localhost:6379
```
## Health Check
```python
# app/api/health.py
from fastapi import APIRouter
from app.core.config import settings
router = APIRouter()
@router.get("/health")
async def health_check():
return {
"status": "healthy",
"version": settings.VERSION
}
```
## Deployment Script
```bash
#!/bin/bash
# deploy.sh
set -e
echo "Deploying FastAPI app..."
# Build and deploy
docker-compose build
docker-compose up -d
# Health check
echo "Checking health..."
if curl -f http://localhost:8000/health; then
echo "✅ Deployment successful!"
else
echo "❌ Deployment failed!"
exit 1
fi
```
@@ -0,0 +1,927 @@
# FastAPI Testing Framework
Comprehensive testing setup for FastAPI applications with pytest and async support.
## Usage
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=app --cov-report=html
# Run specific test file
pytest tests/test_api.py
# Run with verbose output
pytest -v -s
```
## Test Configuration
```python
# pytest.ini
[tool:pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
--cov=app
--cov-report=term-missing
--cov-report=html:htmlcov
--asyncio-mode=auto
--strict-markers
--disable-warnings
markers =
unit: Unit tests
integration: Integration tests
e2e: End-to-end tests
slow: Slow running tests
auth: Authentication tests
api: API tests
asyncio_mode = auto
```
## Test Dependencies
```python
# requirements/test.txt
pytest>=7.0.0
pytest-asyncio>=0.21.0
pytest-cov>=4.0.0
httpx>=0.24.0
factory-boy>=3.2.0
faker>=18.0.0
respx>=0.20.0
pytest-mock>=3.10.0
```
## Test Fixtures
```python
# tests/conftest.py
import pytest
import asyncio
from typing import AsyncGenerator, Generator
from fastapi.testclient import TestClient
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from app.main import app
from app.db.database import get_db, Base
from app.models.user import User
from app.core.security import get_password_hash
from tests.factories import UserFactory
# Test database URL
TEST_DATABASE_URL = "sqlite+aiosqlite:///./test.db"
@pytest.fixture(scope="session")
def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]:
"""Create event loop for the test session."""
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest.fixture(scope="session")
async def test_engine():
"""Create test database engine."""
engine = create_async_engine(
TEST_DATABASE_URL,
echo=False,
future=True
)
# Create tables
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield engine
# Drop tables and dispose engine
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await engine.dispose()
@pytest.fixture
async def db_session(test_engine) -> AsyncGenerator[AsyncSession, None]:
"""Create database session for testing."""
TestSessionLocal = sessionmaker(
test_engine,
class_=AsyncSession,
expire_on_commit=False
)
async with TestSessionLocal() as session:
yield session
@pytest.fixture
def override_get_db(db_session: AsyncSession) -> Generator:
"""Override database dependency."""
async def _override_get_db():
yield db_session
app.dependency_overrides[get_db] = _override_get_db
yield
app.dependency_overrides = {}
@pytest.fixture
def client(override_get_db) -> Generator[TestClient, None, None]:
"""Create test client."""
with TestClient(app) as test_client:
yield test_client
@pytest.fixture
async def async_client(override_get_db) -> AsyncGenerator[AsyncClient, None]:
"""Create async test client."""
async with AsyncClient(app=app, base_url="http://test") as ac:
yield ac
@pytest.fixture
async def test_user(db_session: AsyncSession) -> User:
"""Create test user."""
user_data = {
"username": "testuser",
"email": "test@example.com",
"hashed_password": get_password_hash("testpass123"),
"first_name": "Test",
"last_name": "User",
"is_active": True,
"is_superuser": False
}
user = User(**user_data)
db_session.add(user)
await db_session.commit()
await db_session.refresh(user)
return user
@pytest.fixture
async def superuser(db_session: AsyncSession) -> User:
"""Create superuser."""
user_data = {
"username": "admin",
"email": "admin@example.com",
"hashed_password": get_password_hash("adminpass123"),
"first_name": "Admin",
"last_name": "User",
"is_active": True,
"is_superuser": True
}
user = User(**user_data)
db_session.add(user)
await db_session.commit()
await db_session.refresh(user)
return user
@pytest.fixture
def user_token(test_user: User) -> str:
"""Create authentication token for test user."""
from app.core.security import create_access_token
return create_access_token(subject=test_user.id)
@pytest.fixture
def superuser_token(superuser: User) -> str:
"""Create authentication token for superuser."""
from app.core.security import create_access_token
return create_access_token(subject=superuser.id)
@pytest.fixture
def auth_headers(user_token: str) -> dict[str, str]:
"""Create authorization headers."""
return {"Authorization": f"Bearer {user_token}"}
@pytest.fixture
def superuser_headers(superuser_token: str) -> dict[str, str]:
"""Create superuser authorization headers."""
return {"Authorization": f"Bearer {superuser_token}"}
```
## Test Factories
```python
# tests/factories.py
import factory
from factory import Faker, SubFactory
from app.models.user import User
from app.models.post import Post
from app.core.security import get_password_hash
class UserFactory(factory.Factory):
"""Factory for User model."""
class Meta:
model = User
username = Faker('user_name')
email = Faker('email')
first_name = Faker('first_name')
last_name = Faker('last_name')
hashed_password = factory.LazyAttribute(lambda obj: get_password_hash('testpass123'))
is_active = True
is_superuser = False
bio = Faker('text', max_nb_chars=200)
class SuperUserFactory(UserFactory):
"""Factory for superuser."""
username = 'admin'
email = 'admin@example.com'
first_name = 'Admin'
last_name = 'User'
is_superuser = True
class PostFactory(factory.Factory):
"""Factory for Post model."""
class Meta:
model = Post
title = Faker('sentence', nb_words=4)
content = Faker('text', max_nb_chars=1000)
slug = Faker('slug')
is_published = True
author = SubFactory(UserFactory)
class InactiveUserFactory(UserFactory):
"""Factory for inactive user."""
is_active = False
```
## API Testing
```python
# tests/test_api/test_users.py
import pytest
from httpx import AsyncClient
from app.models.user import User
from tests.factories import UserFactory
class TestUserAPI:
"""Test User API endpoints."""
@pytest.mark.asyncio
async def test_create_user(
self,
async_client: AsyncClient,
superuser_headers: dict
):
"""Test POST /api/v1/users/."""
user_data = {
"username": "newuser",
"email": "new@example.com",
"password": "newpass123",
"first_name": "New",
"last_name": "User"
}
response = await async_client.post(
"/api/v1/users/",
json=user_data,
headers=superuser_headers
)
assert response.status_code == 201
data = response.json()
assert data["username"] == user_data["username"]
assert data["email"] == user_data["email"]
assert "password" not in data
assert "hashed_password" not in data
@pytest.mark.asyncio
async def test_get_users(
self,
async_client: AsyncClient,
test_user: User,
auth_headers: dict
):
"""Test GET /api/v1/users/."""
response = await async_client.get(
"/api/v1/users/",
headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert "items" in data
assert "total" in data
assert "page" in data
assert "size" in data
assert len(data["items"]) >= 1
@pytest.mark.asyncio
async def test_get_user(
self,
async_client: AsyncClient,
test_user: User,
auth_headers: dict
):
"""Test GET /api/v1/users/{id}."""
response = await async_client.get(
f"/api/v1/users/{test_user.id}",
headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert data["id"] == test_user.id
assert data["username"] == test_user.username
assert data["email"] == test_user.email
@pytest.mark.asyncio
async def test_update_user(
self,
async_client: AsyncClient,
test_user: User,
auth_headers: dict
):
"""Test PUT /api/v1/users/{id}."""
update_data = {
"first_name": "Updated",
"last_name": "Name",
"bio": "Updated bio"
}
response = await async_client.put(
f"/api/v1/users/{test_user.id}",
json=update_data,
headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert data["first_name"] == update_data["first_name"]
assert data["last_name"] == update_data["last_name"]
assert data["bio"] == update_data["bio"]
@pytest.mark.asyncio
async def test_delete_user(
self,
async_client: AsyncClient,
test_user: User,
superuser_headers: dict
):
"""Test DELETE /api/v1/users/{id}."""
response = await async_client.delete(
f"/api/v1/users/{test_user.id}",
headers=superuser_headers
)
assert response.status_code == 204
# Verify user is deleted
get_response = await async_client.get(
f"/api/v1/users/{test_user.id}",
headers=superuser_headers
)
assert get_response.status_code == 404
@pytest.mark.asyncio
async def test_unauthorized_access(self, async_client: AsyncClient):
"""Test unauthorized access to protected endpoints."""
response = await async_client.get("/api/v1/users/")
assert response.status_code == 401
@pytest.mark.asyncio
async def test_forbidden_access(
self,
async_client: AsyncClient,
auth_headers: dict
):
"""Test forbidden access to admin endpoints."""
user_data = {
"username": "unauthorized",
"email": "unauthorized@example.com",
"password": "pass123"
}
response = await async_client.post(
"/api/v1/users/",
json=user_data,
headers=auth_headers # Regular user, not superuser
)
assert response.status_code == 403
```
## Authentication Testing
```python
# tests/test_api/test_auth.py
import pytest
from httpx import AsyncClient
from app.models.user import User
from app.core.security import create_access_token, decode_token
class TestAuthAPI:
"""Test authentication API endpoints."""
@pytest.mark.asyncio
async def test_register(
self,
async_client: AsyncClient
):
"""Test user registration."""
user_data = {
"username": "newuser",
"email": "new@example.com",
"password": "newpass123",
"first_name": "New",
"last_name": "User"
}
response = await async_client.post(
"/api/v1/auth/register",
json=user_data
)
assert response.status_code == 201
data = response.json()
assert data["username"] == user_data["username"]
assert data["email"] == user_data["email"]
assert "password" not in data
@pytest.mark.asyncio
async def test_register_duplicate_email(
self,
async_client: AsyncClient,
test_user: User
):
"""Test registration with duplicate email."""
user_data = {
"username": "different",
"email": test_user.email, # Duplicate email
"password": "pass123",
"first_name": "Test",
"last_name": "User"
}
response = await async_client.post(
"/api/v1/auth/register",
json=user_data
)
assert response.status_code == 400
assert "Email already registered" in response.json()["detail"]
@pytest.mark.asyncio
async def test_login(
self,
async_client: AsyncClient,
test_user: User
):
"""Test user login."""
login_data = {
"username": test_user.username,
"password": "testpass123"
}
response = await async_client.post(
"/api/v1/auth/login",
data=login_data,
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
assert response.status_code == 200
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
assert data["token_type"] == "bearer"
assert "expires_in" in data
# Verify token is valid
payload = decode_token(data["access_token"])
assert payload is not None
assert payload["sub"] == str(test_user.id)
@pytest.mark.asyncio
async def test_login_invalid_credentials(
self,
async_client: AsyncClient,
test_user: User
):
"""Test login with invalid credentials."""
login_data = {
"username": test_user.username,
"password": "wrongpassword"
}
response = await async_client.post(
"/api/v1/auth/login",
data=login_data,
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
assert response.status_code == 401
assert "Incorrect username or password" in response.json()["detail"]
@pytest.mark.asyncio
async def test_get_current_user(
self,
async_client: AsyncClient,
test_user: User,
auth_headers: dict
):
"""Test get current user endpoint."""
response = await async_client.get(
"/api/v1/auth/me",
headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert data["id"] == test_user.id
assert data["username"] == test_user.username
assert data["email"] == test_user.email
@pytest.mark.asyncio
async def test_refresh_token(
self,
async_client: AsyncClient,
test_user: User
):
"""Test token refresh."""
from app.core.security import create_refresh_token
refresh_token = create_refresh_token(subject=test_user.id)
response = await async_client.post(
"/api/v1/auth/refresh",
json={"refresh_token": refresh_token}
)
assert response.status_code == 200
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
assert data["token_type"] == "bearer"
@pytest.mark.asyncio
async def test_change_password(
self,
async_client: AsyncClient,
test_user: User,
auth_headers: dict
):
"""Test password change."""
password_data = {
"current_password": "testpass123",
"new_password": "newpass123"
}
response = await async_client.post(
"/api/v1/auth/change-password",
json=password_data,
headers=auth_headers
)
assert response.status_code == 200
assert "Password changed successfully" in response.json()["message"]
```
## Model Testing
```python
# tests/test_models/test_user.py
import pytest
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.user import User
from app.core.security import verify_password, get_password_hash
class TestUserModel:
"""Test User model."""
@pytest.mark.asyncio
async def test_create_user(self, db_session: AsyncSession):
"""Test user creation."""
user_data = {
"username": "testuser",
"email": "test@example.com",
"hashed_password": get_password_hash("password123"),
"first_name": "Test",
"last_name": "User"
}
user = User(**user_data)
db_session.add(user)
await db_session.commit()
await db_session.refresh(user)
assert user.id is not None
assert user.username == "testuser"
assert user.email == "test@example.com"
assert user.full_name == "Test User"
assert user.is_active is True
assert user.is_superuser is False
assert user.created_at is not None
assert user.updated_at is not None
def test_password_verification(self):
"""Test password verification."""
password = "testpassword123"
hashed = get_password_hash(password)
user = User(
username="test",
email="test@example.com",
hashed_password=hashed,
first_name="Test",
last_name="User"
)
assert user.verify_password(password)
assert not user.verify_password("wrongpassword")
def test_user_dict_excludes_password(self):
"""Test that dict() method excludes password."""
user = User(
username="test",
email="test@example.com",
hashed_password="hashed_password",
first_name="Test",
last_name="User"
)
user_dict = user.dict()
assert "hashed_password" not in user_dict
assert "username" in user_dict
assert "email" in user_dict
def test_user_repr(self):
"""Test user string representation."""
user = User(
id=1,
username="test",
email="test@example.com",
hashed_password="hash",
first_name="Test",
last_name="User"
)
assert repr(user) == "<User(id=1)>"
```
## Repository Testing
```python
# tests/test_repositories/test_user.py
import pytest
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.user import User
from app.repositories.user import UserRepository
from app.core.security import get_password_hash
class TestUserRepository:
"""Test UserRepository."""
@pytest.mark.asyncio
async def test_create_user(self, db_session: AsyncSession):
"""Test user creation through repository."""
repo = UserRepository(User, db_session)
user_data = {
"username": "repouser",
"email": "repo@example.com",
"hashed_password": get_password_hash("password123"),
"first_name": "Repo",
"last_name": "User"
}
user = await repo.create(user_data)
assert user.id is not None
assert user.username == "repouser"
assert user.email == "repo@example.com"
@pytest.mark.asyncio
async def test_get_by_email(self, db_session: AsyncSession, test_user: User):
"""Test get user by email."""
repo = UserRepository(User, db_session)
found_user = await repo.get_by_email(test_user.email)
assert found_user is not None
assert found_user.id == test_user.id
assert found_user.email == test_user.email
@pytest.mark.asyncio
async def test_get_by_username(self, db_session: AsyncSession, test_user: User):
"""Test get user by username."""
repo = UserRepository(User, db_session)
found_user = await repo.get_by_username(test_user.username)
assert found_user is not None
assert found_user.id == test_user.id
assert found_user.username == test_user.username
@pytest.mark.asyncio
async def test_get_multi_with_pagination(
self,
db_session: AsyncSession,
test_user: User
):
"""Test get multiple users with pagination."""
repo = UserRepository(User, db_session)
# Create additional users
for i in range(5):
user_data = {
"username": f"user{i}",
"email": f"user{i}@example.com",
"hashed_password": get_password_hash("password123"),
"first_name": f"User{i}",
"last_name": "Test"
}
await repo.create(user_data)
# Test pagination
users = await repo.get_multi(skip=0, limit=3)
assert len(users) == 3
users_page_2 = await repo.get_multi(skip=3, limit=3)
assert len(users_page_2) >= 1 # At least test_user
@pytest.mark.asyncio
async def test_update_user(self, db_session: AsyncSession, test_user: User):
"""Test user update."""
repo = UserRepository(User, db_session)
updated_user = await repo.update(test_user.id, {
"first_name": "Updated",
"bio": "Updated bio"
})
assert updated_user is not None
assert updated_user.first_name == "Updated"
assert updated_user.bio == "Updated bio"
@pytest.mark.asyncio
async def test_delete_user(self, db_session: AsyncSession, test_user: User):
"""Test user deletion."""
repo = UserRepository(User, db_session)
result = await repo.delete(test_user.id)
assert result is True
# Verify user is deleted
deleted_user = await repo.get(test_user.id)
assert deleted_user is None
```
## Performance Testing
```python
# tests/test_performance.py
import pytest
import time
import asyncio
from httpx import AsyncClient
from app.models.user import User
from tests.factories import UserFactory
@pytest.mark.slow
class TestPerformance:
"""Test application performance."""
@pytest.mark.asyncio
async def test_concurrent_requests(
self,
async_client: AsyncClient,
auth_headers: dict
):
"""Test concurrent API requests."""
async def make_request():
response = await async_client.get(
"/api/v1/users/",
headers=auth_headers
)
return response.status_code
# Make 10 concurrent requests
start_time = time.time()
tasks = [make_request() for _ in range(10)]
results = await asyncio.gather(*tasks)
end_time = time.time()
# All requests should succeed
assert all(status == 200 for status in results)
# Should complete within reasonable time
assert (end_time - start_time) < 5.0
@pytest.mark.asyncio
async def test_large_dataset_pagination(
self,
async_client: AsyncClient,
db_session,
auth_headers: dict
):
"""Test pagination with large dataset."""
# Create 100 users
users = []
for i in range(100):
user = UserFactory.build()
users.append(user)
db_session.add_all(users)
await db_session.commit()
# Test pagination performance
start_time = time.time()
response = await async_client.get(
"/api/v1/users/?skip=0&limit=50",
headers=auth_headers
)
end_time = time.time()
assert response.status_code == 200
data = response.json()
assert len(data["items"]) == 50
# Should complete quickly
assert (end_time - start_time) < 1.0
```
## Mocking External Services
```python
# tests/test_external.py
import pytest
import respx
import httpx
from app.services.email import EmailService
class TestExternalServices:
"""Test external service integrations."""
@pytest.mark.asyncio
@respx.mock
async def test_email_service(
self,
async_client: AsyncClient
):
"""Test email service with mocked external API."""
# Mock email service API
respx.post("https://api.emailservice.com/send").mock(
return_value=httpx.Response(
200,
json={"message": "Email sent successfully"}
)
)
email_service = EmailService()
result = await email_service.send_email(
to="test@example.com",
subject="Test",
body="Test email"
)
assert result["success"] is True
```
## Test Utilities
```python
# tests/utils.py
from typing import Dict, Any
from httpx import Response
import json
def assert_response_status(response: Response, expected_status: int = 200):
"""Assert response status code."""
assert response.status_code == expected_status, f"Expected {expected_status}, got {response.status_code}. Response: {response.text}"
def assert_response_json(response: Response, expected_keys: list[str] = None):
"""Assert response is valid JSON with expected keys."""
assert response.headers.get("content-type") == "application/json"
data = response.json()
if expected_keys:
for key in expected_keys:
assert key in data, f"Missing key '{key}' in response"
return data
def create_auth_headers(token: str) -> Dict[str, str]:
"""Create authorization headers with token."""
return {"Authorization": f"Bearer {token}"}
async def create_test_users(db_session, count: int = 5) -> list:
"""Create multiple test users."""
from tests.factories import UserFactory
users = []
for i in range(count):
user = UserFactory.build()
users.append(user)
db_session.add_all(users)
await db_session.commit()
return users
```
@@ -0,0 +1,229 @@
# FastAPI Project Configuration
This file provides specific guidance for FastAPI web application development using Claude Code.
## Project Overview
This is a FastAPI application project optimized for modern API development with automatic documentation, type hints, and async support.
## FastAPI-Specific Development Commands
### Project Management
- `uvicorn app.main:app --reload` - Start development server with auto-reload
- `uvicorn app.main:app --host 0.0.0.0 --port 8000` - Start server on all interfaces
- `uvicorn app.main:app --workers 4` - Start with multiple workers
### Database Management
- `alembic init alembic` - Initialize Alembic migrations
- `alembic revision --autogenerate -m "message"` - Create migration
- `alembic upgrade head` - Apply migrations
- `alembic downgrade -1` - Rollback one migration
### Development Tools
- `python -m pytest` - Run tests
- `python -m pytest --cov=app` - Run tests with coverage
- `mypy app/` - Type checking
- `black app/` - Code formatting
## FastAPI Project Structure
```
myproject/
├── app/ # Application package
│ ├── __init__.py
│ ├── main.py # FastAPI application
│ ├── core/ # Core configuration
│ │ ├── __init__.py
│ │ ├── config.py # Settings
│ │ └── security.py # Authentication
│ ├── api/ # API routes
│ │ ├── __init__.py
│ │ ├── deps.py # Dependencies
│ │ └── v1/
│ │ ├── __init__.py
│ │ ├── api.py # API router
│ │ └── endpoints/
│ ├── models/ # Database models
│ │ ├── __init__.py
│ │ ├── base.py
│ │ └── user.py
│ ├── schemas/ # Pydantic schemas
│ │ ├── __init__.py
│ │ └── user.py
│ ├── repositories/ # Data access layer
│ │ ├── __init__.py
│ │ └── user.py
│ ├── services/ # Business logic
│ │ ├── __init__.py
│ │ └── auth.py
│ └── db/ # Database configuration
│ ├── __init__.py
│ └── database.py
├── alembic/ # Database migrations
├── tests/ # Test files
├── requirements.txt # Dependencies
└── docker-compose.yml # Docker configuration
```
## FastAPI Application Setup
```python
# app/main.py
from fastapi import FastAPI
from app.core.config import settings
from app.api.v1.api import api_router
app = FastAPI(
title=settings.PROJECT_NAME,
version=settings.VERSION,
openapi_url=f"/api/v1/openapi.json"
)
# Include routers
app.include_router(api_router, prefix="/api/v1")
@app.get("/")
async def root():
return {"message": "Welcome to FastAPI"}
```
## Configuration Management
```python
# app/core/config.py
from pydantic import BaseSettings
class Settings(BaseSettings):
PROJECT_NAME: str = "FastAPI App"
VERSION: str = "1.0.0"
SECRET_KEY: str
DATABASE_URL: str
class Config:
env_file = ".env"
settings = Settings()
```
## FastAPI Best Practices
### API Design
- Use Pydantic models for request/response validation
- Implement proper HTTP status codes
- Add comprehensive API documentation
- Use dependency injection for common functionality
- Implement proper error handling
### Database Integration
- Use SQLAlchemy with async support
- Implement repository pattern for data access
- Use Alembic for database migrations
- Add proper database connection pooling
- Implement database health checks
### Authentication & Security
- Use JWT tokens for authentication
- Implement OAuth2 with scopes
- Add rate limiting for API endpoints
- Use HTTPS in production
- Implement proper CORS configuration
### Performance Optimization
- Use async/await for I/O operations
- Implement response caching
- Add database query optimization
- Use connection pooling
- Monitor application performance
## Testing Strategy
### Test Organization
```python
# tests/conftest.py
import pytest
from fastapi.testclient import TestClient
from app.main import app
@pytest.fixture
def client():
return TestClient(app)
```
### Test Types
- **Unit tests** for business logic
- **Integration tests** for API endpoints
- **Database tests** with test fixtures
- **Authentication tests** for security
## Deployment Considerations
### Production Setup
- Use Uvicorn with multiple workers
- Implement proper logging and monitoring
- Set up reverse proxy (Nginx)
- Use environment variables for configuration
- Implement health checks
### Docker Configuration
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]
```
### Environment Variables
```bash
SECRET_KEY=your-secret-key
DATABASE_URL=postgresql://user:pass@host/db
REDIS_URL=redis://localhost:6379
```
## Common FastAPI Patterns
### Dependency Injection
```python
from fastapi import Depends
from app.db.database import get_db
@app.get("/users/")
async def get_users(db: Session = Depends(get_db)):
return users
```
### Background Tasks
```python
from fastapi import BackgroundTasks
@app.post("/send-email/")
async def send_email(background_tasks: BackgroundTasks):
background_tasks.add_task(send_email_task)
return {"message": "Email sent"}
```
### Middleware
```python
@app.middleware("http")
async def add_process_time_header(request, call_next):
response = await call_next(request)
return response
```
## Development Workflow
### Getting Started
1. Clone repository
2. Create virtual environment: `python -m venv venv`
3. Install dependencies: `pip install -r requirements.txt`
4. Set environment variables
5. Run migrations: `alembic upgrade head`
6. Start server: `uvicorn app.main:app --reload`
### Code Quality
- **Black** - Code formatting
- **isort** - Import sorting
- **mypy** - Type checking
- **pytest** - Testing framework
- **flake8** - Linting
@@ -0,0 +1,384 @@
# Flask Application Factory Pattern
Create a scalable Flask application using the factory pattern with blueprints and configuration management.
## Purpose
This command helps you set up a Flask application using the application factory pattern, which is the recommended approach for larger Flask applications.
## Usage
```
/app-factory
```
## What this command does
1. **Creates application factory** with proper structure
2. **Sets up configuration management** for different environments
3. **Implements blueprints** for modular design
4. **Configures extensions** (database, auth, etc.)
5. **Adds error handling** and logging
## Example Output
```python
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_mail import Mail
from flask_wtf.csrf import CSRFProtect
from flask_cors import CORS
import logging
from logging.handlers import RotatingFileHandler
import os
# Initialize extensions
db = SQLAlchemy()
migrate = Migrate()
login = LoginManager()
mail = Mail()
csrf = CSRFProtect()
cors = CORS()
def create_app(config_class=None):
"""Application factory function."""
app = Flask(__name__)
# Load configuration
if config_class is None:
config_class = os.environ.get('FLASK_CONFIG', 'development')
if isinstance(config_class, str):
from app.config import config
app.config.from_object(config[config_class])
else:
app.config.from_object(config_class)
# Initialize extensions
db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
mail.init_app(app)
csrf.init_app(app)
cors.init_app(app)
# Configure login manager
login.login_view = 'auth.login'
login.login_message = 'Please log in to access this page.'
login.login_message_category = 'info'
# Register blueprints
from app.main import bp as main_bp
app.register_blueprint(main_bp)
from app.auth import bp as auth_bp
app.register_blueprint(auth_bp, url_prefix='/auth')
from app.api import bp as api_bp
app.register_blueprint(api_bp, url_prefix='/api')
from app.admin import bp as admin_bp
app.register_blueprint(admin_bp, url_prefix='/admin')
# Error handlers
from app.errors import bp as errors_bp
app.register_blueprint(errors_bp)
# Configure logging
if not app.debug and not app.testing:
if not os.path.exists('logs'):
os.mkdir('logs')
file_handler = RotatingFileHandler(
'logs/app.log',
maxBytes=10240,
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Flask application startup')
return app
# Import models (avoid circular imports)
from app import models
```
```python
# app/config.py
import os
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config:
"""Base configuration class."""
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Mail configuration
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_PORT = int(os.environ.get('MAIL_PORT') or 587)
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'true').lower() in ['true', 'on', '1']
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
ADMIN_EMAIL = os.environ.get('ADMIN_EMAIL')
# Pagination
POSTS_PER_PAGE = 10
USERS_PER_PAGE = 50
# Upload configuration
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB
UPLOAD_FOLDER = os.path.join(basedir, 'uploads')
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
"""Development configuration."""
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app-dev.db')
class TestingConfig(Config):
"""Testing configuration."""
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or 'sqlite://'
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
"""Production configuration."""
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
@classmethod
def init_app(cls, app):
Config.init_app(app)
# Log to stderr
import logging
from logging import StreamHandler
file_handler = StreamHandler()
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
```
```python
# app/main/__init__.py
from flask import Blueprint
bp = Blueprint('main', __name__)
from app.main import routes
```
```python
# app/main/routes.py
from flask import render_template, request, current_app
from app.main import bp
from app.models import Post
@bp.route('/')
@bp.route('/index')
def index():
"""Home page."""
page = request.args.get('page', 1, type=int)
posts = Post.query.filter_by(published=True).order_by(
Post.created_at.desc()
).paginate(
page=page,
per_page=current_app.config['POSTS_PER_PAGE'],
error_out=False
)
return render_template('index.html', posts=posts)
@bp.route('/about')
def about():
"""About page."""
return render_template('about.html')
```
```python
# app/auth/__init__.py
from flask import Blueprint
bp = Blueprint('auth', __name__)
from app.auth import routes
```
```python
# app/auth/routes.py
from flask import render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, login_required, current_user
from werkzeug.urls import url_parse
from app import db
from app.auth import bp
from app.auth.forms import LoginForm, RegistrationForm
from app.models import User
@bp.route('/login', methods=['GET', 'POST'])
def login():
"""User login."""
if current_user.is_authenticated:
return redirect(url_for('main.index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password', 'error')
return redirect(url_for('auth.login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('main.index')
return redirect(next_page)
return render_template('auth/login.html', form=form)
@bp.route('/logout')
@login_required
def logout():
"""User logout."""
logout_user()
flash('You have been logged out.', 'info')
return redirect(url_for('main.index'))
@bp.route('/register', methods=['GET', 'POST'])
def register():
"""User registration."""
if current_user.is_authenticated:
return redirect(url_for('main.index'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('Registration successful!', 'success')
return redirect(url_for('auth.login'))
return render_template('auth/register.html', form=form)
```
```python
# app/models.py
from app import db, login
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from datetime import datetime
class User(UserMixin, db.Model):
"""User model."""
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
created_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
posts = db.relationship('Post', backref='author', lazy='dynamic')
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def __repr__(self):
return f'<User {self.username}>'
@login.user_loader
def load_user(id):
return User.query.get(int(id))
class Post(db.Model):
"""Post model."""
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
published = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, index=True, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f'<Post {self.title}>'
```
```python
# app.py (main application entry point)
from app import create_app, db
from app.models import User, Post
app = create_app()
@app.shell_context_processor
def make_shell_context():
"""Add models to shell context."""
return {'db': db, 'User': User, 'Post': Post}
if __name__ == '__main__':
app.run(debug=True)
```
## Project Structure
```
project/
├── app/
│ ├── __init__.py # Application factory
│ ├── config.py # Configuration classes
│ ├── models.py # Database models
│ ├── main/
│ │ ├── __init__.py # Main blueprint
│ │ └── routes.py # Main routes
│ ├── auth/
│ │ ├── __init__.py # Auth blueprint
│ │ ├── routes.py # Auth routes
│ │ └── forms.py # Auth forms
│ ├── api/
│ │ ├── __init__.py # API blueprint
│ │ └── routes.py # API routes
│ └── templates/ # Jinja2 templates
├── migrations/ # Database migrations
├── logs/ # Application logs
├── app.py # Application entry point
├── requirements.txt # Dependencies
└── .env # Environment variables
```
## Benefits of Application Factory
- **Testing**: Easy to create app instances with different configs
- **Scalability**: Modular design with blueprints
- **Configuration**: Environment-specific settings
- **Extensions**: Proper initialization order
- **Maintainability**: Clear separation of concerns
@@ -0,0 +1,243 @@
# Flask Blueprint Generator
Create organized Flask blueprints for modular application structure.
## Usage
```bash
# Create a new blueprint
flask create-blueprint users
flask create-blueprint api/v1
```
## Blueprint Structure
Generates a complete blueprint with:
- Routes and view functions
- Error handlers
- Template folder structure
- Static file organization
## Example Blueprint
```python
# app/blueprints/users/__init__.py
from flask import Blueprint
users_bp = Blueprint(
'users',
__name__,
url_prefix='/users',
template_folder='templates',
static_folder='static'
)
from . import routes, models
# app/blueprints/users/routes.py
from flask import render_template, request, redirect, url_for, flash
from . import users_bp
from .models import User
from .forms import UserForm
@users_bp.route('/')
def index():
"""List all users."""
users = User.query.all()
return render_template('users/index.html', users=users)
@users_bp.route('/create', methods=['GET', 'POST'])
def create():
"""Create a new user."""
form = UserForm()
if form.validate_on_submit():
user = User(
username=form.username.data,
email=form.email.data
)
user.save()
flash('User created successfully!', 'success')
return redirect(url_for('users.index'))
return render_template('users/create.html', form=form)
@users_bp.route('/<int:user_id>')
def detail(user_id):
"""Show user details."""
user = User.query.get_or_404(user_id)
return render_template('users/detail.html', user=user)
@users_bp.route('/<int:user_id>/edit', methods=['GET', 'POST'])
def edit(user_id):
"""Edit an existing user."""
user = User.query.get_or_404(user_id)
form = UserForm(obj=user)
if form.validate_on_submit():
user.username = form.username.data
user.email = form.email.data
user.save()
flash('User updated successfully!', 'success')
return redirect(url_for('users.detail', user_id=user.id))
return render_template('users/edit.html', form=form, user=user)
@users_bp.route('/<int:user_id>/delete', methods=['POST'])
def delete(user_id):
"""Delete a user."""
user = User.query.get_or_404(user_id)
user.delete()
flash('User deleted successfully!', 'success')
return redirect(url_for('users.index'))
# Error handlers
@users_bp.errorhandler(404)
def not_found(error):
return render_template('users/404.html'), 404
@users_bp.errorhandler(500)
def internal_error(error):
return render_template('users/500.html'), 500
```
## Blueprint Models
```python
# app/blueprints/users/models.py
from app.extensions import db
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
class User(db.Model):
"""User model."""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(255), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
is_active = db.Column(db.Boolean, default=True)
def __repr__(self):
return f'<User {self.username}>'
def set_password(self, password):
"""Set password hash."""
self.password_hash = generate_password_hash(password)
def check_password(self, password):
"""Check password hash."""
return check_password_hash(self.password_hash, password)
def save(self):
"""Save user to database."""
db.session.add(self)
db.session.commit()
def delete(self):
"""Delete user from database."""
db.session.delete(self)
db.session.commit()
def to_dict(self):
"""Convert to dictionary."""
return {
'id': self.id,
'username': self.username,
'email': self.email,
'created_at': self.created_at.isoformat(),
'is_active': self.is_active
}
```
## Blueprint Forms
```python
# app/blueprints/users/forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, EmailField, PasswordField, BooleanField
from wtforms.validators import DataRequired, Email, Length, EqualTo
from .models import User
class UserForm(FlaskForm):
"""User creation/edit form."""
username = StringField(
'Username',
validators=[
DataRequired(),
Length(min=3, max=80)
]
)
email = EmailField(
'Email',
validators=[
DataRequired(),
Email(),
Length(max=120)
]
)
password = PasswordField(
'Password',
validators=[
DataRequired(),
Length(min=8)
]
)
confirm_password = PasswordField(
'Confirm Password',
validators=[
DataRequired(),
EqualTo('password', message='Passwords must match')
]
)
is_active = BooleanField('Active')
def validate_username(self, field):
"""Validate username uniqueness."""
if User.query.filter_by(username=field.data).first():
raise ValidationError('Username already exists.')
def validate_email(self, field):
"""Validate email uniqueness."""
if User.query.filter_by(email=field.data).first():
raise ValidationError('Email already registered.')
```
## Registration in Main App
```python
# app/__init__.py
from flask import Flask
from app.blueprints.users import users_bp
def create_app():
app = Flask(__name__)
# Register blueprints
app.register_blueprint(users_bp)
return app
```
## Template Structure
```
templates/
├── base.html
└── users/
├── index.html
├── create.html
├── detail.html
├── edit.html
├── 404.html
└── 500.html
```
## Best Practices
- Use blueprints to organize related functionality
- Keep models, forms, and routes in separate files
- Implement proper error handling
- Use URL prefixes for namespacing
- Follow RESTful routing conventions
- Include comprehensive docstrings
- Add form validation and CSRF protection
@@ -0,0 +1,410 @@
# Flask Database Management
Complete database setup and management for Flask applications using SQLAlchemy.
## Usage
```bash
# Initialize database
flask db init
# Create migration
flask db migrate -m "Initial migration"
# Apply migrations
flask db upgrade
# Downgrade migration
flask db downgrade
```
## Database Configuration
```python
# config.py
import os
from urllib.parse import quote_plus
class Config:
"""Base configuration."""
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_RECORD_QUERIES = True
class DevelopmentConfig(Config):
"""Development configuration."""
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///app.db'
class ProductionConfig(Config):
"""Production configuration."""
DEBUG = False
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
f"postgresql://{os.environ.get('DB_USER')}:{quote_plus(os.environ.get('DB_PASSWORD'))}@" \
f"{os.environ.get('DB_HOST')}:{os.environ.get('DB_PORT', '5432')}/{os.environ.get('DB_NAME')}"
class TestingConfig(Config):
"""Testing configuration."""
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
WTF_CSRF_ENABLED = False
config = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'testing': TestingConfig,
'default': DevelopmentConfig
}
```
## Database Extensions
```python
# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_wtf.csrf import CSRFProtect
from flask_caching import Cache
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
# Initialize extensions
db = SQLAlchemy()
migrate = Migrate()
login_manager = LoginManager()
csrf = CSRFProtect()
cache = Cache()
limiter = Limiter(key_func=get_remote_address)
def init_extensions(app):
"""Initialize Flask extensions."""
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)
csrf.init_app(app)
cache.init_app(app)
limiter.init_app(app)
# Configure login manager
login_manager.login_view = 'auth.login'
login_manager.login_message = 'Please log in to access this page.'
login_manager.login_message_category = 'info'
```
## Base Model
```python
# app/models/base.py
from app.extensions import db
from datetime import datetime
from sqlalchemy.ext.declarative import declared_attr
class TimestampMixin:
"""Add timestamp fields to model."""
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
class BaseModel(db.Model, TimestampMixin):
"""Base model with common functionality."""
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
def save(self, commit=True):
"""Save model to database."""
db.session.add(self)
if commit:
db.session.commit()
return self
def delete(self, commit=True):
"""Delete model from database."""
db.session.delete(self)
if commit:
db.session.commit()
def update(self, **kwargs):
"""Update model attributes."""
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
return self.save()
def to_dict(self, exclude=None):
"""Convert model to dictionary."""
exclude = exclude or []
return {
column.name: getattr(self, column.name)
for column in self.__table__.columns
if column.name not in exclude
}
@classmethod
def get_or_404(cls, id):
"""Get model by ID or raise 404."""
return cls.query.get_or_404(id)
@classmethod
def create(cls, **kwargs):
"""Create new model instance."""
instance = cls(**kwargs)
return instance.save()
```
## Example Models
```python
# app/models/user.py
from app.extensions import db
from app.models.base import BaseModel
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
class User(UserMixin, BaseModel):
"""User model."""
__tablename__ = 'users'
username = db.Column(db.String(80), unique=True, nullable=False, index=True)
email = db.Column(db.String(120), unique=True, nullable=False, index=True)
password_hash = db.Column(db.String(255), nullable=False)
first_name = db.Column(db.String(50), nullable=False)
last_name = db.Column(db.String(50), nullable=False)
is_active = db.Column(db.Boolean, default=True, nullable=False)
is_admin = db.Column(db.Boolean, default=False, nullable=False)
last_login = db.Column(db.DateTime)
# Relationships
posts = db.relationship('Post', backref='author', lazy='dynamic', cascade='all, delete-orphan')
def __repr__(self):
return f'<User {self.username}>'
def set_password(self, password):
"""Set password hash."""
self.password_hash = generate_password_hash(password)
def check_password(self, password):
"""Check password hash."""
return check_password_hash(self.password_hash, password)
@property
def full_name(self):
"""Get user's full name."""
return f"{self.first_name} {self.last_name}"
def to_dict(self, exclude=None):
"""Convert to dictionary excluding sensitive data."""
exclude = exclude or ['password_hash']
return super().to_dict(exclude=exclude)
# app/models/post.py
class Post(BaseModel):
"""Blog post model."""
__tablename__ = 'posts'
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
slug = db.Column(db.String(200), unique=True, nullable=False, index=True)
status = db.Column(db.String(20), default='draft', nullable=False)
published_at = db.Column(db.DateTime)
# Foreign keys
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
# Relationships
category = db.relationship('Category', backref='posts')
tags = db.relationship('Tag', secondary='post_tags', backref='posts')
def __repr__(self):
return f'<Post {self.title}>'
@property
def is_published(self):
"""Check if post is published."""
return self.status == 'published' and self.published_at is not None
```
## Database CLI Commands
```python
# app/cli.py
import click
from flask import current_app
from flask.cli import with_appcontext
from app.extensions import db
from app.models import User, Post, Category
@click.command()
@with_appcontext
def init_db():
"""Initialize database."""
db.create_all()
click.echo('Database initialized.')
@click.command()
@with_appcontext
def seed_db():
"""Seed database with sample data."""
# Create admin user
admin = User(
username='admin',
email='admin@example.com',
first_name='Admin',
last_name='User',
is_admin=True
)
admin.set_password('admin123')
admin.save()
# Create sample category
category = Category(
name='Technology',
description='Tech-related posts'
)
category.save()
# Create sample post
post = Post(
title='Welcome to Flask',
content='This is a sample blog post.',
slug='welcome-to-flask',
status='published',
user_id=admin.id,
category_id=category.id
)
post.save()
click.echo('Database seeded with sample data.')
@click.command()
@with_appcontext
def reset_db():
"""Reset database."""
if click.confirm('Are you sure you want to reset the database?'):
db.drop_all()
db.create_all()
click.echo('Database reset.')
def init_commands(app):
"""Register CLI commands."""
app.cli.add_command(init_db)
app.cli.add_command(seed_db)
app.cli.add_command(reset_db)
```
## Connection Pooling
```python
# app/database.py
from sqlalchemy import create_engine
from sqlalchemy.pool import QueuePool
def configure_database(app):
"""Configure database with connection pooling."""
if app.config.get('SQLALCHEMY_DATABASE_URI', '').startswith('postgresql'):
# PostgreSQL configuration
engine = create_engine(
app.config['SQLALCHEMY_DATABASE_URI'],
poolclass=QueuePool,
pool_size=10,
max_overflow=20,
pool_recycle=3600,
pool_pre_ping=True
)
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
'pool_size': 10,
'max_overflow': 20,
'pool_recycle': 3600,
'pool_pre_ping': True
}
```
## Database Utilities
```python
# app/utils/database.py
from app.extensions import db
from sqlalchemy import text
from flask import current_app
def execute_sql(sql, params=None):
"""Execute raw SQL query."""
with db.engine.connect() as conn:
result = conn.execute(text(sql), params or {})
return result.fetchall()
def backup_database():
"""Create database backup."""
# Implementation depends on database type
pass
def check_database_health():
"""Check database connection health."""
try:
db.session.execute(text('SELECT 1'))
return True
except Exception as e:
current_app.logger.error(f'Database health check failed: {e}')
return False
def get_table_info(table_name):
"""Get table information."""
inspector = db.inspect(db.engine)
return {
'columns': inspector.get_columns(table_name),
'indexes': inspector.get_indexes(table_name),
'foreign_keys': inspector.get_foreign_keys(table_name)
}
```
## Testing Database
```python
# tests/conftest.py
import pytest
from app import create_app
from app.extensions import db
from app.models import User
@pytest.fixture(scope='session')
def app():
"""Create test app."""
app = create_app('testing')
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
"""Create test client."""
return app.test_client()
@pytest.fixture
def db_session(app):
"""Create database session for testing."""
with app.app_context():
db.session.begin()
yield db.session
db.session.rollback()
@pytest.fixture
def user(db_session):
"""Create test user."""
user = User(
username='testuser',
email='test@example.com',
first_name='Test',
last_name='User'
)
user.set_password('testpass')
user.save()
return user
```
@@ -0,0 +1,620 @@
# Flask Deployment Configuration
Complete production deployment setup for Flask applications.
## Usage
```bash
# Build Docker image
docker build -t myapp .
# Run with Docker Compose
docker-compose up -d
# Deploy to cloud
gunicorn --bind 0.0.0.0:8000 app:app
```
## Production Configuration
```python
# config.py
import os
from urllib.parse import quote_plus
class ProductionConfig:
"""Production configuration."""
# Security
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = False
TESTING = False
# Database
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
f"postgresql://{os.environ.get('DB_USER')}:{quote_plus(os.environ.get('DB_PASSWORD'))}@" \
f"{os.environ.get('DB_HOST')}:{os.environ.get('DB_PORT', '5432')}/{os.environ.get('DB_NAME')}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 10,
'max_overflow': 20,
'pool_recycle': 3600,
'pool_pre_ping': True
}
# Security Headers
SECURITY_HEADERS = {
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
}
# Session
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
PERMANENT_SESSION_LIFETIME = 3600 # 1 hour
# Cache
CACHE_TYPE = 'redis'
CACHE_REDIS_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379/0')
CACHE_DEFAULT_TIMEOUT = 300
# Rate Limiting
RATELIMIT_STORAGE_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379/1')
RATELIMIT_DEFAULT = '100/hour'
# Logging
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
LOG_FILE = os.environ.get('LOG_FILE', '/var/log/app/app.log')
# File Upload
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB
UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER', '/var/uploads')
# Email
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_PORT = int(os.environ.get('MAIL_PORT', 587))
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'true').lower() in ['true', 'on', '1']
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
MAIL_DEFAULT_SENDER = os.environ.get('MAIL_DEFAULT_SENDER')
```
## WSGI Configuration
```python
# wsgi.py
import os
from app import create_app
# Get environment
config_name = os.environ.get('FLASK_ENV', 'production')
app = create_app(config_name)
if __name__ == "__main__":
app.run()
```
## Gunicorn Configuration
```python
# gunicorn.conf.py
import multiprocessing
import os
# Server socket
bind = f"0.0.0.0:{os.environ.get('PORT', 8000)}"
backlog = 2048
# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
worker_connections = 1000
timeout = 30
keepalive = 60
max_requests = 1000
max_requests_jitter = 100
# Security
limit_request_line = 4094
limit_request_fields = 100
limit_request_field_size = 8190
# Logging
accesslog = '-'
errorlog = '-'
loglevel = os.environ.get('LOG_LEVEL', 'info').lower()
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'
# Process naming
proc_name = 'flask_app'
# Server mechanics
daemon = False
pidfile = '/tmp/gunicorn.pid'
user = os.environ.get('USER', 'www-data')
group = os.environ.get('GROUP', 'www-data')
tmp_upload_dir = None
# SSL
keyfile = os.environ.get('SSL_KEYFILE')
certfile = os.environ.get('SSL_CERTFILE')
```
## Docker Configuration
```dockerfile
# Dockerfile
FROM python:3.11-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# Install system dependencies
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
curl \
&& rm -rf /var/lib/apt/lists/*
# Create app user
RUN groupadd -r appuser && useradd -r -g appuser appuser
# Set work directory
WORKDIR /app
# Install Python dependencies
COPY requirements/production.txt ./requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create necessary directories
RUN mkdir -p /var/log/app /var/uploads && \
chown -R appuser:appuser /app /var/log/app /var/uploads
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Run application
CMD ["gunicorn", "--config", "gunicorn.conf.py", "wsgi:app"]
```
## Docker Compose
```yaml
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- FLASK_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379/0
- SECRET_KEY=${SECRET_KEY}
depends_on:
- db
- redis
volumes:
- uploads:/var/uploads
- logs:/var/log/app
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
- uploads:/var/uploads:ro
depends_on:
- web
restart: unless-stopped
volumes:
postgres_data:
redis_data:
uploads:
logs:
```
## Nginx Configuration
```nginx
# nginx.conf
events {
worker_connections 1024;
}
http {
upstream app {
server web:8000;
}
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
# File upload size
client_max_body_size 16M;
# Static files
location /static/ {
alias /var/uploads/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /uploads/ {
alias /var/uploads/;
expires 1h;
}
# API rate limiting
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Login rate limiting
location /auth/login {
limit_req zone=login burst=5 nodelay;
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Main application
location / {
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeout settings
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
}
```
## Environment Variables
```bash
# .env.production
# Application
FLASK_ENV=production
SECRET_KEY=your-super-secret-key-here
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=user
DB_PASSWORD=password
# Redis
REDIS_URL=redis://localhost:6379/0
# Email
MAIL_SERVER=smtp.gmail.com
MAIL_PORT=587
MAIL_USE_TLS=true
MAIL_USERNAME=your-email@gmail.com
MAIL_PASSWORD=your-app-password
MAIL_DEFAULT_SENDER=your-email@gmail.com
# Logging
LOG_LEVEL=INFO
LOG_FILE=/var/log/app/app.log
# File Upload
UPLOAD_FOLDER=/var/uploads
# SSL (if using)
SSL_KEYFILE=/path/to/private.key
SSL_CERTFILE=/path/to/certificate.crt
```
## Health Check Endpoint
```python
# app/health.py
from flask import Blueprint, jsonify
from app.extensions import db
from sqlalchemy import text
import redis
import os
health_bp = Blueprint('health', __name__)
@health_bp.route('/health')
def health_check():
"""Application health check."""
checks = {
'status': 'healthy',
'database': check_database(),
'redis': check_redis(),
'disk_space': check_disk_space()
}
# Determine overall status
if all(check['status'] == 'ok' for check in checks.values() if isinstance(check, dict)):
status_code = 200
else:
status_code = 503
checks['status'] = 'unhealthy'
return jsonify(checks), status_code
def check_database():
"""Check database connectivity."""
try:
db.session.execute(text('SELECT 1'))
return {'status': 'ok', 'message': 'Database connection successful'}
except Exception as e:
return {'status': 'error', 'message': str(e)}
def check_redis():
"""Check Redis connectivity."""
try:
redis_url = os.environ.get('REDIS_URL', 'redis://localhost:6379/0')
r = redis.from_url(redis_url)
r.ping()
return {'status': 'ok', 'message': 'Redis connection successful'}
except Exception as e:
return {'status': 'error', 'message': str(e)}
def check_disk_space():
"""Check available disk space."""
try:
import shutil
total, used, free = shutil.disk_usage('/')
free_percent = (free / total) * 100
if free_percent > 10:
status = 'ok'
elif free_percent > 5:
status = 'warning'
else:
status = 'critical'
return {
'status': status,
'free_space_percent': round(free_percent, 2),
'free_space_gb': round(free / (1024**3), 2)
}
except Exception as e:
return {'status': 'error', 'message': str(e)}
```
## Monitoring and Logging
```python
# app/logging.py
import logging
import logging.handlers
import os
from flask import request, g
import time
def setup_logging(app):
"""Setup application logging."""
if not app.debug and not app.testing:
# File logging
if app.config.get('LOG_FILE'):
file_handler = logging.handlers.RotatingFileHandler(
app.config['LOG_FILE'],
maxBytes=10240000, # 10MB
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s '
'[in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(getattr(logging, app.config.get('LOG_LEVEL', 'INFO')))
app.logger.addHandler(file_handler)
# Console logging
if not app.logger.handlers:
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s'
))
stream_handler.setLevel(logging.INFO)
app.logger.addHandler(stream_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Application startup')
# Request timing middleware
@app.before_request
def before_request():
g.start_time = time.time()
@app.after_request
def after_request(response):
if hasattr(g, 'start_time'):
duration = time.time() - g.start_time
app.logger.info(
f'{request.method} {request.path} - '
f'{response.status_code} - {duration:.3f}s'
)
return response
```
## Database Backup Script
```bash
#!/bin/bash
# backup.sh
set -e
# Configuration
BACKUP_DIR="/var/backups/db"
DATABASE_URL="$DATABASE_URL"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$DATE.sql"
RETENTION_DAYS=7
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Create backup
echo "Creating database backup..."
pg_dump "$DATABASE_URL" > "$BACKUP_FILE"
# Compress backup
gzip "$BACKUP_FILE"
# Remove old backups
echo "Cleaning up old backups..."
find "$BACKUP_DIR" -name "backup_*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: $BACKUP_FILE.gz"
```
## Deployment Scripts
```bash
#!/bin/bash
# deploy.sh
set -e
echo "Starting deployment..."
# Pull latest code
git pull origin main
# Build new Docker image
docker-compose build web
# Run database migrations
docker-compose run --rm web flask db upgrade
# Update services
docker-compose up -d
# Health check
echo "Waiting for application to be ready..."
sleep 10
if curl -f http://localhost:8000/health; then
echo "Deployment successful!"
else
echo "Deployment failed - health check failed"
exit 1
fi
```
## Monitoring with Prometheus
```python
# app/metrics.py
from prometheus_flask_exporter import PrometheusMetrics
from flask import request
import time
def setup_metrics(app):
"""Setup Prometheus metrics."""
metrics = PrometheusMetrics(app)
# Custom metrics
metrics.info('app_info', 'Application info', version='1.0.0')
# Database connection pool metrics
@metrics.gauge('db_pool_size', 'Database connection pool size')
def db_pool_size():
from app.extensions import db
return db.engine.pool.size()
return metrics
```
@@ -0,0 +1,217 @@
# Flask Route Generator
Create Flask routes with proper structure and error handling.
## Purpose
This command helps you quickly create Flask routes with validation, error handling, and best practices.
## Usage
```
/flask-route
```
## What this command does
1. **Creates route functions** with proper decorators
2. **Adds request validation** and error handling
3. **Includes JSON responses** and status codes
4. **Implements authentication** if needed
5. **Follows Flask conventions** and best practices
## Example Output
```python
# routes.py or app.py
from flask import Flask, request, jsonify, abort
from flask_sqlalchemy import SQLAlchemy
from werkzeug.exceptions import BadRequest
app = Flask(__name__)
@app.route('/users', methods=['GET'])
def get_users():
"""Get all users with optional pagination."""
try:
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
users = User.query.paginate(
page=page,
per_page=per_page,
error_out=False
)
return jsonify({
'users': [user.to_dict() for user in users.items],
'total': users.total,
'pages': users.pages,
'current_page': page
}), 200
except Exception as e:
return jsonify({'error': 'Failed to fetch users'}), 500
@app.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
"""Get a specific user by ID."""
try:
user = User.query.get_or_404(user_id)
return jsonify(user.to_dict()), 200
except Exception as e:
return jsonify({'error': 'User not found'}), 404
@app.route('/users', methods=['POST'])
def create_user():
"""Create a new user."""
try:
data = request.get_json()
if not data:
return jsonify({'error': 'No data provided'}), 400
# Validate required fields
required_fields = ['name', 'email']
for field in required_fields:
if field not in data:
return jsonify({'error': f'{field} is required'}), 400
# Check if email already exists
if User.query.filter_by(email=data['email']).first():
return jsonify({'error': 'Email already exists'}), 409
# Create new user
user = User(
name=data['name'],
email=data['email'],
phone=data.get('phone'),
address=data.get('address')
)
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict()), 201
except BadRequest:
return jsonify({'error': 'Invalid JSON data'}), 400
except Exception as e:
db.session.rollback()
return jsonify({'error': 'Failed to create user'}), 500
@app.route('/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
"""Update an existing user."""
try:
user = User.query.get_or_404(user_id)
data = request.get_json()
if not data:
return jsonify({'error': 'No data provided'}), 400
# Update fields
if 'name' in data:
user.name = data['name']
if 'email' in data:
# Check if new email already exists
existing_user = User.query.filter_by(email=data['email']).first()
if existing_user and existing_user.id != user_id:
return jsonify({'error': 'Email already exists'}), 409
user.email = data['email']
if 'phone' in data:
user.phone = data['phone']
if 'address' in data:
user.address = data['address']
db.session.commit()
return jsonify(user.to_dict()), 200
except BadRequest:
return jsonify({'error': 'Invalid JSON data'}), 400
except Exception as e:
db.session.rollback()
return jsonify({'error': 'Failed to update user'}), 500
@app.route('/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
"""Delete a user."""
try:
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return jsonify({'message': 'User deleted successfully'}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': 'Failed to delete user'}), 500
# Error handlers
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': 'Resource not found'}), 404
@app.errorhandler(400)
def bad_request(error):
return jsonify({'error': 'Bad request'}), 400
@app.errorhandler(500)
def internal_error(error):
return jsonify({'error': 'Internal server error'}), 500
```
## Route Patterns Supported
### Basic Routes
```python
@app.route('/')
@app.route('/users')
@app.route('/users/<int:user_id>')
```
### HTTP Methods
```python
@app.route('/users', methods=['GET', 'POST'])
@app.route('/users/<int:id>', methods=['GET', 'PUT', 'DELETE'])
```
### URL Parameters
```python
@app.route('/users/<int:user_id>')
@app.route('/posts/<string:slug>')
@app.route('/files/<path:filename>')
```
## Best Practices Included
- **Input validation** for all user data
- **Proper HTTP status codes** (200, 201, 400, 404, 500)
- **JSON responses** with consistent structure
- **Error handling** with try/catch blocks
- **Database rollback** on errors
- **RESTful conventions** for URL design
- **Documentation strings** for each route
- **Request data validation** before processing
## Common Response Patterns
```python
# Success with data
return jsonify({'data': result}), 200
# Created resource
return jsonify({'data': new_resource, 'id': new_id}), 201
# Validation error
return jsonify({'error': 'Field is required'}), 400
# Not found
return jsonify({'error': 'Resource not found'}), 404
# Server error
return jsonify({'error': 'Internal server error'}), 500
```
@@ -0,0 +1,559 @@
# Flask Testing Suite
Comprehensive testing setup for Flask applications with pytest.
## Usage
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=app --cov-report=html
# Run specific test file
pytest tests/test_models.py
# Run with verbose output
pytest -v
```
## Test Configuration
```python
# pytest.ini
[tool:pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
--cov=app
--cov-report=term-missing
--cov-report=html:htmlcov
--strict-markers
--disable-warnings
markers =
unit: Unit tests
integration: Integration tests
slow: Slow running tests
auth: Authentication tests
```
## Test Fixtures
```python
# tests/conftest.py
import pytest
import tempfile
import os
from app import create_app
from app.extensions import db
from app.models import User, Post, Category
from flask_login import login_user
@pytest.fixture(scope='session')
def app():
"""Create test application."""
# Create temporary database
db_fd, db_path = tempfile.mkstemp()
app = create_app({
'TESTING': True,
'SQLALCHEMY_DATABASE_URI': f'sqlite:///{db_path}',
'WTF_CSRF_ENABLED': False,
'SECRET_KEY': 'test-secret-key'
})
with app.app_context():
db.create_all()
yield app
# Cleanup
os.close(db_fd)
os.unlink(db_path)
@pytest.fixture
def client(app):
"""Create test client."""
return app.test_client()
@pytest.fixture
def runner(app):
"""Create test CLI runner."""
return app.test_cli_runner()
@pytest.fixture
def db_session(app):
"""Create database session for testing."""
with app.app_context():
connection = db.engine.connect()
transaction = connection.begin()
# Configure session to use the connection
db.session.configure(bind=connection)
yield db.session
# Rollback transaction
transaction.rollback()
connection.close()
db.session.remove()
@pytest.fixture
def user(db_session):
"""Create test user."""
user = User(
username='testuser',
email='test@example.com',
first_name='Test',
last_name='User'
)
user.set_password('testpass123')
user.save()
return user
@pytest.fixture
def admin_user(db_session):
"""Create admin user."""
admin = User(
username='admin',
email='admin@example.com',
first_name='Admin',
last_name='User',
is_admin=True
)
admin.set_password('adminpass123')
admin.save()
return admin
@pytest.fixture
def category(db_session):
"""Create test category."""
category = Category(
name='Test Category',
description='A test category'
)
category.save()
return category
@pytest.fixture
def post(db_session, user, category):
"""Create test post."""
post = Post(
title='Test Post',
content='This is a test post content.',
slug='test-post',
status='published',
user_id=user.id,
category_id=category.id
)
post.save()
return post
@pytest.fixture
def auth_headers(user):
"""Create authentication headers."""
# For API testing
token = user.generate_auth_token()
return {'Authorization': f'Bearer {token}'}
```
## Model Testing
```python
# tests/test_models.py
import pytest
from datetime import datetime
from app.models import User, Post, Category
from werkzeug.security import check_password_hash
class TestUser:
"""Test User model."""
def test_user_creation(self, db_session):
"""Test user creation."""
user = User(
username='newuser',
email='new@example.com',
first_name='New',
last_name='User'
)
user.set_password('password123')
user.save()
assert user.id is not None
assert user.username == 'newuser'
assert user.email == 'new@example.com'
assert user.full_name == 'New User'
assert user.is_active is True
assert user.is_admin is False
assert user.created_at is not None
def test_password_hashing(self, user):
"""Test password hashing."""
user.set_password('newpassword')
assert user.password_hash != 'newpassword'
assert check_password_hash(user.password_hash, 'newpassword')
assert user.check_password('newpassword')
assert not user.check_password('wrongpassword')
def test_user_repr(self, user):
"""Test user string representation."""
assert repr(user) == '<User testuser>'
def test_user_to_dict(self, user):
"""Test user dictionary conversion."""
user_dict = user.to_dict()
assert 'username' in user_dict
assert 'email' in user_dict
assert 'password_hash' not in user_dict # Should be excluded
def test_user_relationships(self, user, post):
"""Test user relationships."""
assert post in user.posts
assert user.posts.count() == 1
class TestPost:
"""Test Post model."""
def test_post_creation(self, db_session, user, category):
"""Test post creation."""
post = Post(
title='New Post',
content='New post content',
slug='new-post',
status='draft',
user_id=user.id,
category_id=category.id
)
post.save()
assert post.id is not None
assert post.title == 'New Post'
assert post.author == user
assert post.category == category
assert not post.is_published
def test_published_status(self, post):
"""Test post published status."""
assert post.is_published # Published with published_at
post.status = 'draft'
assert not post.is_published
```
## View Testing
```python
# tests/test_views.py
import pytest
from flask import url_for
from app.models import User
class TestMainViews:
"""Test main application views."""
def test_home_page(self, client):
"""Test home page."""
response = client.get('/')
assert response.status_code == 200
assert b'Welcome' in response.data
def test_about_page(self, client):
"""Test about page."""
response = client.get('/about')
assert response.status_code == 200
class TestUserViews:
"""Test user-related views."""
def test_user_list(self, client, user):
"""Test user list page."""
response = client.get('/users/')
assert response.status_code == 200
assert user.username.encode() in response.data
def test_user_detail(self, client, user):
"""Test user detail page."""
response = client.get(f'/users/{user.id}')
assert response.status_code == 200
assert user.username.encode() in response.data
def test_user_create_get(self, client):
"""Test user creation form."""
response = client.get('/users/create')
assert response.status_code == 200
assert b'Create User' in response.data
def test_user_create_post(self, client, db_session):
"""Test user creation submission."""
data = {
'username': 'newuser',
'email': 'new@example.com',
'first_name': 'New',
'last_name': 'User',
'password': 'password123',
'confirm_password': 'password123'
}
response = client.post('/users/create', data=data, follow_redirects=True)
assert response.status_code == 200
# Check user was created
user = User.query.filter_by(username='newuser').first()
assert user is not None
assert user.email == 'new@example.com'
def test_user_edit(self, client, user):
"""Test user editing."""
data = {
'username': user.username,
'email': 'updated@example.com',
'first_name': 'Updated',
'last_name': 'User'
}
response = client.post(f'/users/{user.id}/edit', data=data, follow_redirects=True)
assert response.status_code == 200
# Refresh user from database
db_session.refresh(user)
assert user.email == 'updated@example.com'
assert user.first_name == 'Updated'
```
## Authentication Testing
```python
# tests/test_auth.py
import pytest
from flask import url_for
from app.models import User
class TestAuthentication:
"""Test authentication functionality."""
def test_login_page(self, client):
"""Test login page access."""
response = client.get('/auth/login')
assert response.status_code == 200
assert b'Login' in response.data
def test_valid_login(self, client, user):
"""Test valid user login."""
data = {
'username': user.username,
'password': 'testpass123'
}
response = client.post('/auth/login', data=data, follow_redirects=True)
assert response.status_code == 200
assert b'Welcome' in response.data
def test_invalid_login(self, client, user):
"""Test invalid login credentials."""
data = {
'username': user.username,
'password': 'wrongpassword'
}
response = client.post('/auth/login', data=data)
assert response.status_code == 200
assert b'Invalid' in response.data
def test_logout(self, client, user):
"""Test user logout."""
# Login first
with client.session_transaction() as sess:
sess['_user_id'] = str(user.id)
response = client.get('/auth/logout', follow_redirects=True)
assert response.status_code == 200
def test_register_page(self, client):
"""Test registration page."""
response = client.get('/auth/register')
assert response.status_code == 200
assert b'Register' in response.data
def test_valid_registration(self, client, db_session):
"""Test valid user registration."""
data = {
'username': 'newuser',
'email': 'new@example.com',
'first_name': 'New',
'last_name': 'User',
'password': 'password123',
'confirm_password': 'password123'
}
response = client.post('/auth/register', data=data, follow_redirects=True)
assert response.status_code == 200
# Check user was created
user = User.query.filter_by(username='newuser').first()
assert user is not None
```
## API Testing
```python
# tests/test_api.py
import pytest
import json
from flask import url_for
class TestUserAPI:
"""Test User API endpoints."""
def test_get_users(self, client, user):
"""Test GET /api/users."""
response = client.get('/api/users')
assert response.status_code == 200
data = json.loads(response.data)
assert 'users' in data
assert len(data['users']) >= 1
def test_get_user(self, client, user):
"""Test GET /api/users/<id>."""
response = client.get(f'/api/users/{user.id}')
assert response.status_code == 200
data = json.loads(response.data)
assert data['username'] == user.username
assert data['email'] == user.email
def test_create_user(self, client, db_session):
"""Test POST /api/users."""
user_data = {
'username': 'apiuser',
'email': 'api@example.com',
'first_name': 'API',
'last_name': 'User',
'password': 'password123'
}
response = client.post(
'/api/users',
data=json.dumps(user_data),
content_type='application/json'
)
assert response.status_code == 201
data = json.loads(response.data)
assert data['username'] == 'apiuser'
def test_update_user(self, client, user, auth_headers):
"""Test PUT /api/users/<id>."""
update_data = {
'email': 'updated@example.com',
'first_name': 'Updated'
}
response = client.put(
f'/api/users/{user.id}',
data=json.dumps(update_data),
content_type='application/json',
headers=auth_headers
)
assert response.status_code == 200
data = json.loads(response.data)
assert data['email'] == 'updated@example.com'
def test_delete_user(self, client, user, auth_headers):
"""Test DELETE /api/users/<id>."""
response = client.delete(
f'/api/users/{user.id}',
headers=auth_headers
)
assert response.status_code == 204
```
## Test Utilities
```python
# tests/utils.py
import json
from flask import url_for
def login_user(client, username, password):
"""Helper to login user in tests."""
return client.post('/auth/login', data={
'username': username,
'password': password
}, follow_redirects=True)
def logout_user(client):
"""Helper to logout user in tests."""
return client.get('/auth/logout', follow_redirects=True)
def assert_json_response(response, expected_status=200):
"""Assert JSON response format and status."""
assert response.status_code == expected_status
assert response.content_type == 'application/json'
return json.loads(response.data)
def create_test_data(db_session):
"""Create common test data."""
from app.models import User, Category, Post
# Create test users
users = []
for i in range(3):
user = User(
username=f'user{i}',
email=f'user{i}@example.com',
first_name=f'User{i}',
last_name='Test'
)
user.set_password('password123')
user.save()
users.append(user)
return {'users': users}
```
## Performance Testing
```python
# tests/test_performance.py
import pytest
import time
from app.models import User
@pytest.mark.slow
class TestPerformance:
"""Test application performance."""
def test_user_query_performance(self, db_session):
"""Test user query performance."""
# Create multiple users
users = []
for i in range(100):
user = User(
username=f'perfuser{i}',
email=f'perf{i}@example.com',
first_name=f'Perf{i}',
last_name='User'
)
users.append(user)
db_session.bulk_save_objects(users)
db_session.commit()
# Test query performance
start_time = time.time()
result = User.query.all()
end_time = time.time()
assert len(result) >= 100
assert (end_time - start_time) < 0.1 # Should complete in under 100ms
def test_endpoint_response_time(self, client):
"""Test endpoint response time."""
start_time = time.time()
response = client.get('/')
end_time = time.time()
assert response.status_code == 200
assert (end_time - start_time) < 0.5 # Should respond in under 500ms
```
@@ -0,0 +1,391 @@
# Flask Project Configuration
This file provides specific guidance for Flask web application development using Claude Code.
## Project Overview
This is a Flask web application project optimized for scalable web development with the Flask micro-framework. The project follows Flask best practices and modern Python development patterns.
## Flask-Specific Development Commands
### Project Management
- `flask run` - Start development server
- `flask run --host=0.0.0.0 --port=5000` - Start server accessible from network
- `flask shell` - Open Flask shell with application context
- `python -m flask --help` - Show available Flask commands
### Database Management
- `flask db init` - Initialize database migrations
- `flask db migrate -m "message"` - Create database migration
- `flask db upgrade` - Apply database migrations
- `flask db downgrade` - Rollback database migration
- `flask db current` - Show current migration
- `flask db history` - Show migration history
### Development Tools
- `flask routes` - Show all registered routes
- `flask --version` - Show Flask version
- `export FLASK_ENV=development` - Set development environment
- `export FLASK_DEBUG=1` - Enable debug mode
### Custom Commands
- `flask init-db` - Initialize database with tables
- `flask seed-db` - Seed database with sample data
- `flask reset-db` - Reset database (development only)
## Flask Project Structure
```
myproject/
├── app/ # Application package
│ ├── __init__.py # Application factory
│ ├── extensions.py # Flask extensions
│ ├── config.py # Configuration settings
│ ├── models/ # Database models
│ │ ├── __init__.py
│ │ ├── base.py # Base model class
│ │ ├── user.py # User model
│ │ └── post.py # Post model
│ ├── blueprints/ # Application blueprints
│ │ ├── __init__.py
│ │ ├── main/ # Main blueprint
│ │ ├── auth/ # Authentication blueprint
│ │ ├── api/ # API blueprint
│ │ └── admin/ # Admin blueprint
│ ├── templates/ # Jinja2 templates
│ │ ├── base.html
│ │ ├── index.html
│ │ └── auth/
│ ├── static/ # Static files
│ │ ├── css/
│ │ ├── js/
│ │ └── images/
│ ├── forms/ # WTForms
│ │ ├── __init__.py
│ │ ├── auth.py
│ │ └── user.py
│ ├── utils/ # Utility functions
│ └── cli.py # Custom CLI commands
├── migrations/ # Database migrations
├── tests/ # Test files
│ ├── conftest.py
│ ├── test_models.py
│ ├── test_views.py
│ └── test_api.py
├── requirements/ # Requirements files
│ ├── base.txt
│ ├── development.txt
│ └── production.txt
├── wsgi.py # WSGI entry point
├── gunicorn.conf.py # Gunicorn configuration
└── docker-compose.yml # Docker Compose configuration
```
## Flask Application Factory
```python
# app/__init__.py
from flask import Flask
from app.extensions import db, migrate, login_manager, csrf, cache
from app.config import config
def create_app(config_name='default'):
app = Flask(__name__)
app.config.from_object(config[config_name])
# Initialize extensions
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)
csrf.init_app(app)
cache.init_app(app)
# Register blueprints
from app.blueprints.main import main_bp
from app.blueprints.auth import auth_bp
from app.blueprints.api import api_bp
app.register_blueprint(main_bp)
app.register_blueprint(auth_bp, url_prefix='/auth')
app.register_blueprint(api_bp, url_prefix='/api/v1')
# Register CLI commands
from app.cli import init_commands
init_commands(app)
return app
```
## Configuration Management
```python
# app/config.py
import os
from datetime import timedelta
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_RECORD_QUERIES = True
# Session configuration
PERMANENT_SESSION_LIFETIME = timedelta(hours=1)
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
# File upload
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB
UPLOAD_FOLDER = os.path.join(os.getcwd(), 'uploads')
# Cache
CACHE_TYPE = 'simple'
CACHE_DEFAULT_TIMEOUT = 300
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///dev.db'
SESSION_COOKIE_SECURE = False
class ProductionConfig(Config):
DEBUG = False
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
# Security headers
SECURITY_HEADERS = {
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block'
}
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
WTF_CSRF_ENABLED = False
config = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'testing': TestingConfig,
'default': DevelopmentConfig
}
```
## Flask Best Practices
### Application Structure
- Use application factory pattern for configuration flexibility
- Organize code into blueprints for modularity
- Separate models, views, and forms into different modules
- Use extensions.py to initialize Flask extensions
- Implement proper error handling and logging
### Database Models
- Use SQLAlchemy ORM for database operations
- Implement base model with common functionality
- Add proper relationships between models
- Use database migrations for schema changes
- Implement model validation and constraints
### Blueprint Organization
- Group related functionality into blueprints
- Use URL prefixes for namespacing
- Implement blueprint-specific templates
- Add proper error handlers for each blueprint
- Use blueprint factories for complex blueprints
### Template Management
- Use template inheritance for consistent layout
- Create reusable template macros
- Implement proper CSRF protection in forms
- Use Flask-WTF for form handling and validation
- Organize templates by blueprint
### Security Considerations
- Always validate and sanitize user input
- Use Flask-Login for user session management
- Implement proper authentication and authorization
- Use CSRF protection for all forms
- Set secure session cookie configuration
- Implement rate limiting for API endpoints
## Flask Extensions
### Essential Extensions
```python
# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_wtf.csrf import CSRFProtect
from flask_caching import Cache
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
db = SQLAlchemy()
migrate = Migrate()
login_manager = LoginManager()
csrf = CSRFProtect()
cache = Cache()
limiter = Limiter(key_func=get_remote_address)
```
### Recommended Extensions
- **Flask-SQLAlchemy** - Database ORM
- **Flask-Migrate** - Database migrations
- **Flask-Login** - User session management
- **Flask-WTF** - Form handling and CSRF protection
- **Flask-Caching** - Caching support
- **Flask-Limiter** - Rate limiting
- **Flask-Mail** - Email support
- **Flask-Admin** - Admin interface
## Testing Strategy
### Test Organization
```python
# tests/conftest.py
import pytest
from app import create_app
from app.extensions import db
@pytest.fixture(scope='session')
def app():
app = create_app('testing')
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
return app.test_client()
@pytest.fixture
def runner(app):
return app.test_cli_runner()
```
### Test Types
- **Unit tests** for models and utilities
- **Integration tests** for views and API endpoints
- **Functional tests** for user workflows
- **Performance tests** for critical paths
### Testing Best Practices
- Use fixtures for common test data
- Test both success and error conditions
- Mock external dependencies
- Use factory_boy for test data generation
- Implement database transaction rollback in tests
## Performance Optimization
### Database Optimization
- Use connection pooling for production
- Implement query optimization with indexes
- Use lazy loading for relationships
- Cache frequently accessed data
- Monitor database query performance
### Caching Strategy
- Implement Redis for session storage
- Use view-level caching for static content
- Cache database query results
- Implement cache invalidation strategies
- Use CDN for static files
### Application Optimization
- Use Gunicorn with multiple workers
- Implement proper logging and monitoring
- Optimize static file serving
- Use async tasks for long-running operations
- Implement proper error handling
## Deployment Considerations
### Production Setup
- Use environment variables for configuration
- Implement proper logging and monitoring
- Set up database connection pooling
- Configure reverse proxy (Nginx)
- Use HTTPS with proper SSL certificates
### Docker Configuration
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements/production.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "--config", "gunicorn.conf.py", "wsgi:app"]
```
### Environment Variables
```bash
FLASK_ENV=production
SECRET_KEY=your-secret-key
DATABASE_URL=postgresql://user:pass@host:port/db
REDIS_URL=redis://host:port/db
```
## Common Flask Patterns
### Custom Decorators
```python
from functools import wraps
from flask import abort
from flask_login import current_user
def admin_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_admin:
abort(403)
return f(*args, **kwargs)
return decorated_function
```
### Request Context Processors
```python
@app.context_processor
def inject_user():
return dict(current_user=current_user)
```
### Custom Filters
```python
@app.template_filter('datetime')
def datetime_filter(value, format='%Y-%m-%d %H:%M'):
return value.strftime(format) if value else ''
```
## Development Workflow
### Getting Started
1. Clone the repository
2. Create virtual environment: `python -m venv venv`
3. Activate environment: `source venv/bin/activate`
4. Install dependencies: `pip install -r requirements/development.txt`
5. Set environment variables
6. Initialize database: `flask db upgrade`
7. Run development server: `flask run`
### Development Process
1. Create feature branch from main
2. Implement changes with tests
3. Run test suite: `pytest`
4. Check code quality: `flake8`, `black`
5. Create pull request for review
6. Deploy after approval
### Code Quality Tools
- **Black** - Code formatting
- **isort** - Import sorting
- **flake8** - Linting
- **mypy** - Type checking
- **pytest** - Testing framework
- **coverage** - Test coverage
@@ -0,0 +1,360 @@
# Ruby Model Generator
Create Ruby classes and models with proper structure and best practices.
## Purpose
This command helps you quickly create Ruby model classes with proper initialization, attributes, and methods following Ruby conventions.
## Usage
```
/model
```
## What this command does
1. **Creates model classes** with proper initialization methods
2. **Adds attribute accessors** and instance variables
3. **Includes validation methods** and error handling
4. **Follows Ruby conventions** and best practices
5. **Generates related test files** when applicable
## Example Output
```ruby
# app/models/user.rb
class User
attr_accessor :name, :email, :age
attr_reader :id, :created_at
def initialize(attributes = {})
@id = attributes[:id] || generate_id
@name = attributes[:name]
@email = attributes[:email]
@age = attributes[:age]
@created_at = Time.now
@errors = []
end
def valid?
@errors.clear
validate_presence_of_name
validate_email_format
validate_age_range
@errors.empty?
end
def errors
@errors.dup
end
def save
return false unless valid?
# Persist logic here
true
end
def to_h
{
id: @id,
name: @name,
email: @email,
age: @age,
created_at: @created_at
}
end
def to_json(*args)
JSON.generate(to_h, *args)
end
private
def validate_presence_of_name
if @name.nil? || @name.strip.empty?
@errors << "Name cannot be blank"
end
end
def validate_email_format
if @email && !@email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
@errors << "Email format is invalid"
end
end
def validate_age_range
if @age && (@age < 0 || @age > 150)
@errors << "Age must be between 0 and 150"
end
end
def generate_id
SecureRandom.uuid
end
end
# Example with inheritance
class AdminUser < User
attr_accessor :permissions
def initialize(attributes = {})
super
@permissions = attributes[:permissions] || []
end
def admin?
true
end
def has_permission?(permission)
@permissions.include?(permission)
end
private
def validate_permissions
unless @permissions.is_a?(Array)
@errors << "Permissions must be an array"
end
end
end
```
## Advanced Features
### Module Inclusion
```ruby
module Timestamps
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def with_timestamps
attr_reader :created_at, :updated_at
define_method :initialize do |*args|
super(*args)
@created_at ||= Time.now
@updated_at = @created_at
end
define_method :touch do
@updated_at = Time.now
end
end
end
end
class Post
include Timestamps
with_timestamps
attr_accessor :title, :content
def initialize(attributes = {})
@title = attributes[:title]
@content = attributes[:content]
super
end
end
```
### Class Methods and Scopes
```ruby
class User
@@users = []
def self.all
@@users.dup
end
def self.find_by_email(email)
@@users.find { |user| user.email == email }
end
def self.where(conditions = {})
@@users.select do |user|
conditions.all? { |key, value| user.send(key) == value }
end
end
def self.create(attributes = {})
user = new(attributes)
if user.save
@@users << user
user
else
nil
end
end
def save
return false unless valid?
unless @@users.include?(self)
@@users << self
end
true
end
def destroy
@@users.delete(self)
end
end
```
## Testing Template
```ruby
# spec/models/user_spec.rb
require 'spec_helper'
RSpec.describe User do
let(:valid_attributes) do
{
name: 'John Doe',
email: 'john@example.com',
age: 30
}
end
describe '#initialize' do
it 'sets attributes correctly' do
user = User.new(valid_attributes)
expect(user.name).to eq('John Doe')
expect(user.email).to eq('john@example.com')
expect(user.age).to eq(30)
expect(user.id).not_to be_nil
expect(user.created_at).to be_a(Time)
end
end
describe '#valid?' do
it 'returns true for valid attributes' do
user = User.new(valid_attributes)
expect(user).to be_valid
end
it 'returns false when name is blank' do
user = User.new(valid_attributes.merge(name: ''))
expect(user).not_to be_valid
expect(user.errors).to include('Name cannot be blank')
end
it 'returns false for invalid email format' do
user = User.new(valid_attributes.merge(email: 'invalid-email'))
expect(user).not_to be_valid
expect(user.errors).to include('Email format is invalid')
end
it 'returns false for invalid age' do
user = User.new(valid_attributes.merge(age: -5))
expect(user).not_to be_valid
expect(user.errors).to include('Age must be between 0 and 150')
end
end
describe '#save' do
it 'saves valid user' do
user = User.new(valid_attributes)
expect(user.save).to be true
end
it 'does not save invalid user' do
user = User.new(name: '')
expect(user.save).to be false
end
end
describe '#to_h' do
it 'returns hash representation' do
user = User.new(valid_attributes)
hash = user.to_h
expect(hash).to include(
name: 'John Doe',
email: 'john@example.com',
age: 30
)
expect(hash[:id]).not_to be_nil
expect(hash[:created_at]).to be_a(Time)
end
end
end
```
## Best Practices Included
- **Proper initialization** with hash parameters
- **Attribute accessors** for public attributes
- **Validation methods** with error collection
- **JSON serialization** support
- **Class and instance methods** separation
- **Error handling** and reporting
- **Ruby naming conventions** (snake_case)
- **Encapsulation** with private methods
## Common Patterns
### Value Objects
```ruby
class Money
include Comparable
attr_reader :amount, :currency
def initialize(amount, currency = 'USD')
@amount = amount.to_f
@currency = currency.to_s.upcase
end
def +(other)
raise ArgumentError, "Currency mismatch" unless currency == other.currency
Money.new(amount + other.amount, currency)
end
def <=>(other)
raise ArgumentError, "Currency mismatch" unless currency == other.currency
amount <=> other.amount
end
def to_s
"#{currency} #{format('%.2f', amount)}"
end
end
```
### Service Objects
```ruby
class UserRegistrationService
attr_reader :user, :errors
def initialize(user_params)
@user_params = user_params
@errors = []
end
def call
@user = User.new(@user_params)
if @user.valid?
@user.save
send_welcome_email
true
else
@errors = @user.errors
false
end
end
private
def send_welcome_email
# Email sending logic
end
end
```
@@ -0,0 +1,480 @@
# Ruby Test Generator
Create comprehensive test files with RSpec or Minitest following Ruby testing best practices.
## Purpose
This command helps you quickly create test files with proper structure, examples, and testing patterns for Ruby applications.
## Usage
```
/test
```
## What this command does
1. **Creates test files** with proper structure for RSpec or Minitest
2. **Includes test examples** for common scenarios
3. **Sets up test helpers** and support files
4. **Follows testing conventions** and best practices
5. **Generates factory/fixture data** when needed
## RSpec Example Output
```ruby
# spec/models/user_spec.rb
require 'spec_helper'
RSpec.describe User do
let(:user) { build(:user) }
let(:valid_attributes) do
{
name: 'John Doe',
email: 'john@example.com',
age: 30
}
end
describe 'validations' do
it { should validate_presence_of(:name) }
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should validate_numericality_of(:age).is_greater_than(0) }
context 'when email format is invalid' do
let(:user) { build(:user, email: 'invalid-email') }
it 'is not valid' do
expect(user).not_to be_valid
expect(user.errors[:email]).to include('is invalid')
end
end
end
describe 'associations' do
it { should have_many(:posts) }
it { should have_many(:comments) }
end
describe 'callbacks' do
describe 'before_save' do
it 'normalizes email' do
user = create(:user, email: 'JOHN@EXAMPLE.COM')
expect(user.email).to eq('john@example.com')
end
end
end
describe 'instance methods' do
describe '#full_name' do
let(:user) { build(:user, first_name: 'John', last_name: 'Doe') }
it 'returns the full name' do
expect(user.full_name).to eq('John Doe')
end
context 'when last_name is missing' do
let(:user) { build(:user, first_name: 'John', last_name: nil) }
it 'returns only first_name' do
expect(user.full_name).to eq('John')
end
end
end
describe '#active?' do
context 'when user is active' do
let(:user) { build(:user, status: 'active') }
it 'returns true' do
expect(user.active?).to be true
end
end
context 'when user is inactive' do
let(:user) { build(:user, status: 'inactive') }
it 'returns false' do
expect(user.active?).to be false
end
end
end
end
describe 'class methods' do
describe '.active' do
let!(:active_user) { create(:user, status: 'active') }
let!(:inactive_user) { create(:user, status: 'inactive') }
it 'returns only active users' do
expect(User.active).to include(active_user)
expect(User.active).not_to include(inactive_user)
end
end
describe '.find_by_email' do
let!(:user) { create(:user, email: 'test@example.com') }
it 'finds user by email' do
found_user = User.find_by_email('test@example.com')
expect(found_user).to eq(user)
end
it 'returns nil when user not found' do
found_user = User.find_by_email('nonexistent@example.com')
expect(found_user).to be_nil
end
end
end
describe 'scopes' do
describe '.recent' do
let!(:old_user) { create(:user, created_at: 1.year.ago) }
let!(:recent_user) { create(:user, created_at: 1.day.ago) }
it 'returns users created in the last 30 days' do
expect(User.recent).to include(recent_user)
expect(User.recent).not_to include(old_user)
end
end
end
end
```
## Minitest Example Output
```ruby
# test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = users(:john)
@valid_attributes = {
name: 'Jane Doe',
email: 'jane@example.com',
age: 25
}
end
# Validation tests
test 'should not save user without name' do
user = User.new(@valid_attributes.except(:name))
assert_not user.save
assert_includes user.errors[:name], "can't be blank"
end
test 'should not save user without email' do
user = User.new(@valid_attributes.except(:email))
assert_not user.save
assert_includes user.errors[:email], "can't be blank"
end
test 'should not save user with invalid email' do
user = User.new(@valid_attributes.merge(email: 'invalid-email'))
assert_not user.save
assert_includes user.errors[:email], 'is invalid'
end
test 'should not save user with duplicate email' do
user1 = User.create!(@valid_attributes)
user2 = User.new(@valid_attributes)
assert_not user2.save
assert_includes user2.errors[:email], 'has already been taken'
end
test 'should save user with valid attributes' do
user = User.new(@valid_attributes)
assert user.save
end
# Association tests
test 'should have many posts' do
assert_respond_to @user, :posts
assert_kind_of ActiveRecord::Associations::CollectionProxy, @user.posts
end
test 'should have many comments' do
assert_respond_to @user, :comments
end
# Instance method tests
test 'full_name should return first and last name' do
@user.first_name = 'John'
@user.last_name = 'Doe'
assert_equal 'John Doe', @user.full_name
end
test 'full_name should return first name only when last name is missing' do
@user.first_name = 'John'
@user.last_name = nil
assert_equal 'John', @user.full_name
end
test 'active? should return true for active users' do
@user.status = 'active'
assert @user.active?
end
test 'active? should return false for inactive users' do
@user.status = 'inactive'
assert_not @user.active?
end
# Class method tests
test 'active scope should return only active users' do
active_user = User.create!(@valid_attributes.merge(status: 'active'))
inactive_user = User.create!(@valid_attributes.merge(
email: 'inactive@example.com',
status: 'inactive'
))
active_users = User.active
assert_includes active_users, active_user
assert_not_includes active_users, inactive_user
end
test 'find_by_email should find user by email' do
user = User.create!(@valid_attributes)
found_user = User.find_by_email(@valid_attributes[:email])
assert_equal user, found_user
end
test 'find_by_email should return nil when user not found' do
found_user = User.find_by_email('nonexistent@example.com')
assert_nil found_user
end
end
```
## Controller Test Example
```ruby
# spec/controllers/users_controller_spec.rb
require 'rails_helper'
RSpec.describe UsersController, type: :controller do
let(:user) { create(:user) }
let(:valid_attributes) { attributes_for(:user) }
let(:invalid_attributes) { { name: '', email: 'invalid' } }
describe 'GET #index' do
it 'returns a success response' do
get :index
expect(response).to be_successful
end
it 'assigns @users' do
user1 = create(:user)
user2 = create(:user)
get :index
expect(assigns(:users)).to match_array([user1, user2])
end
end
describe 'GET #show' do
it 'returns a success response' do
get :show, params: { id: user.to_param }
expect(response).to be_successful
end
it 'assigns the requested user' do
get :show, params: { id: user.to_param }
expect(assigns(:user)).to eq(user)
end
end
describe 'POST #create' do
context 'with valid parameters' do
it 'creates a new User' do
expect {
post :create, params: { user: valid_attributes }
}.to change(User, :count).by(1)
end
it 'redirects to the created user' do
post :create, params: { user: valid_attributes }
expect(response).to redirect_to(User.last)
end
end
context 'with invalid parameters' do
it 'does not create a new User' do
expect {
post :create, params: { user: invalid_attributes }
}.to change(User, :count).by(0)
end
it 'renders the new template' do
post :create, params: { user: invalid_attributes }
expect(response).to render_template(:new)
end
end
end
describe 'PUT #update' do
context 'with valid parameters' do
let(:new_attributes) { { name: 'Updated Name' } }
it 'updates the requested user' do
put :update, params: { id: user.to_param, user: new_attributes }
user.reload
expect(user.name).to eq('Updated Name')
end
it 'redirects to the user' do
put :update, params: { id: user.to_param, user: new_attributes }
expect(response).to redirect_to(user)
end
end
context 'with invalid parameters' do
it 'renders the edit template' do
put :update, params: { id: user.to_param, user: invalid_attributes }
expect(response).to render_template(:edit)
end
end
end
describe 'DELETE #destroy' do
it 'destroys the requested user' do
user # Create user
expect {
delete :destroy, params: { id: user.to_param }
}.to change(User, :count).by(-1)
end
it 'redirects to the users list' do
delete :destroy, params: { id: user.to_param }
expect(response).to redirect_to(users_url)
end
end
end
```
## Factory Configuration
```ruby
# spec/factories/users.rb
FactoryBot.define do
factory :user do
sequence(:name) { |n| "User #{n}" }
sequence(:email) { |n| "user#{n}@example.com" }
age { rand(18..80) }
status { 'active' }
trait :inactive do
status { 'inactive' }
end
trait :admin do
admin { true }
end
trait :with_posts do
after(:create) do |user|
create_list(:post, 3, user: user)
end
end
factory :admin_user, traits: [:admin]
factory :inactive_user, traits: [:inactive]
end
end
```
## Test Helper Configuration
```ruby
# spec/spec_helper.rb
require 'simplecov'
SimpleCov.start 'rails' do
add_filter '/spec/'
add_filter '/config/'
add_filter '/vendor/'
add_group 'Controllers', 'app/controllers'
add_group 'Models', 'app/models'
add_group 'Services', 'app/services'
add_group 'Libraries', 'lib'
end
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
config.filter_run_when_matching :focus
config.example_status_persistence_file_path = 'spec/examples.txt'
config.disable_monkey_patching!
config.warnings = true
if config.files_to_run.one?
config.default_formatter = 'doc'
end
config.profile_examples = 10
config.order = :random
Kernel.srand config.seed
end
```
## Integration Test Example
```ruby
# spec/requests/api/users_spec.rb
require 'rails_helper'
RSpec.describe 'API::Users', type: :request do
let(:user) { create(:user) }
let(:valid_headers) {
{ 'Authorization' => "Bearer #{user.auth_token}" }
}
describe 'GET /api/users' do
it 'returns users' do
create_list(:user, 3)
get '/api/users', headers: valid_headers
expect(response).to have_http_status(:ok)
expect(JSON.parse(response.body)['users'].size).to eq(3)
end
end
describe 'POST /api/users' do
let(:valid_params) do
{
user: {
name: 'New User',
email: 'new@example.com'
}
}
end
it 'creates a new user' do
post '/api/users', params: valid_params, headers: valid_headers
expect(response).to have_http_status(:created)
expect(JSON.parse(response.body)['user']['name']).to eq('New User')
end
end
end
```
## Best Practices Included
- **Descriptive test names** that explain expected behavior
- **Proper test organization** with contexts and describes
- **Factory usage** for test data generation
- **Mocking and stubbing** for external dependencies
- **Coverage configuration** with SimpleCov
- **Test helpers** and shared examples
- **Request/Integration tests** for API endpoints
- **Feature tests** for user workflows
@@ -0,0 +1,146 @@
{
"permissions": {
"allow": [
"Bash",
"Edit",
"MultiEdit",
"Write",
"Bash(ruby:*)",
"Bash(bundle:*)",
"Bash(rails:*)",
"Bash(rake:*)",
"Bash(rspec:*)",
"Bash(rubocop:*)",
"Bash(brakeman:*)",
"Bash(irb:*)",
"Bash(pry:*)",
"Bash(gem:*)",
"Bash(rbenv:*)",
"Bash(rvm:*)",
"Bash(kamal:*)",
"Bash(git:*)"
],
"deny": [
"Bash(curl:*)",
"Bash(wget:*)",
"Bash(rm -rf:*)"
],
"defaultMode": "allowEdits"
},
"env": {
"BASH_DEFAULT_TIMEOUT_MS": "60000",
"BASH_MAX_OUTPUT_LENGTH": "20000",
"CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "1",
"BUNDLE_PATH": "vendor/bundle",
"BUNDLE_JOBS": "4"
},
"includeCoAuthoredBy": true,
"cleanupPeriodDays": 30,
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \"No description\")\"' >> ~/.claude/bash-command-log.txt"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"); CONTENT=$(echo $STDIN_JSON | jq -r '.tool_input.content // \"\"); if [[ \"$FILE\" =~ \\.rb$ ]] && echo \"$CONTENT\" | grep -q 'puts\\|p '; then echo 'Warning: puts/p statements should be replaced with proper logging' >&2; exit 2; fi"
}
]
},
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" == \"Gemfile\" ]] || [[ \"$FILE\" == \"Gemfile.lock\" ]] || [[ \"$FILE\" =~ gemspec$ ]]; then echo 'Checking for vulnerable gems...'; if command -v bundle >/dev/null 2>&1 && bundle show bundler-audit >/dev/null 2>&1; then bundle audit; elif command -v bundle >/dev/null 2>&1; then echo 'Run: bundle add bundler-audit --group development to enable security audits'; else echo 'Bundler not found for security audit'; fi; fi",
"timeout": 60
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.rb$ ]]; then if command -v rubocop >/dev/null 2>&1; then rubocop -A \"$FILE\" 2>/dev/null || echo 'RuboCop auto-correction applied'; elif command -v bundle >/dev/null 2>&1 && bundle show rubocop >/dev/null 2>&1; then bundle exec rubocop -A \"$FILE\" 2>/dev/null || echo 'RuboCop auto-correction applied'; else echo 'RuboCop not available for auto-correction'; fi; fi",
"timeout": 30
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.rb$ ]]; then RESULT=$(if command -v rubocop >/dev/null 2>&1; then rubocop \"$FILE\" 2>&1; elif command -v bundle >/dev/null 2>&1 && bundle show rubocop >/dev/null 2>&1; then bundle exec rubocop \"$FILE\" 2>&1; fi); if [ $? -ne 0 ] && [[ -n \"$RESULT\" ]]; then echo \"RuboCop linting issues found: $RESULT\" >&2; exit 2; fi; fi",
"timeout": 30
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $STDIN_JSON | jq -r '.tool_input.file_path // \"\"'); if [[ \"$FILE\" =~ \\.rb$ && \"$FILE\" != *\"spec/\"* && \"$FILE\" != *\"_spec.rb\" && \"$FILE\" != *\"test/\"* && \"$FILE\" != *\"_test.rb\" ]]; then DIR=$(dirname \"$FILE\"); BASENAME=$(basename \"$FILE\" .rb); for TEST_FILE in \"spec/${BASENAME}_spec.rb\" \"test/${BASENAME}_test.rb\" \"spec/models/${BASENAME}_spec.rb\" \"spec/controllers/${BASENAME}_spec.rb\" \"spec/services/${BASENAME}_spec.rb\"; do if [ -f \"$TEST_FILE\" ]; then echo \"Running tests for $TEST_FILE...\"; if command -v rspec >/dev/null 2>&1; then rspec \"$TEST_FILE\"; elif command -v bundle >/dev/null 2>&1 && bundle show rspec >/dev/null 2>&1; then bundle exec rspec \"$TEST_FILE\"; elif command -v ruby >/dev/null 2>&1; then ruby -Itest \"$TEST_FILE\" 2>/dev/null || echo 'Test runner not available'; fi; break; fi; done; fi",
"timeout": 60
}
]
}
],
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "echo \"Claude Code notification: $(date)\" >> ~/.claude/notifications.log"
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "if [[ -f Gemfile || -f Rakefile ]] && [[ $(git status --porcelain | grep '\\.rb$') ]]; then echo 'Running RuboCop on changed Ruby files...'; if command -v rubocop >/dev/null 2>&1; then rubocop $(git diff --name-only --diff-filter=ACMR | grep '\\.rb$'); elif command -v bundle >/dev/null 2>&1 && bundle show rubocop >/dev/null 2>&1; then bundle exec rubocop $(git diff --name-only --diff-filter=ACMR | grep '\\.rb$'); else echo 'RuboCop not found for linting'; fi; fi",
"timeout": 60
}
]
},
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "if [[ -f Gemfile || -f Rakefile ]] && [[ $(git status --porcelain | grep '\\.rb$') ]]; then echo 'Running security scan with Brakeman...'; if command -v brakeman >/dev/null 2>&1; then brakeman --quiet --no-pager || echo 'Brakeman scan completed with findings'; elif command -v bundle >/dev/null 2>&1 && bundle show brakeman >/dev/null 2>&1; then bundle exec brakeman --quiet --no-pager || echo 'Brakeman scan completed with findings'; else echo 'Brakeman not found for security scanning'; fi; fi",
"timeout": 60
}
]
},
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "if [[ -f Gemfile ]]; then echo 'Checking for gem vulnerabilities...'; if command -v bundle >/dev/null 2>&1 && bundle show bundler-audit >/dev/null 2>&1; then bundle audit || echo 'Security audit completed with findings'; else echo 'bundler-audit not found. Run: bundle add bundler-audit --group development'; fi; fi",
"timeout": 30
}
]
}
]
}
}
+83
View File
@@ -0,0 +1,83 @@
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"category": "Development Tools",
"description": "GitHub integration for repository management and issue tracking",
"complexity": "Medium",
"enabled": false,
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": ""
}
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"category": "Database",
"description": "PostgreSQL database operations and query execution",
"complexity": "Medium",
"enabled": false,
"env": {
"POSTGRES_CONNECTION_STRING": ""
}
},
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
"category": "Web Search",
"description": "Web search capabilities using Brave Search API",
"complexity": "Low",
"enabled": false,
"env": {
"BRAVE_API_KEY": ""
}
},
"ruby-docs": {
"command": "ruby",
"args": [
"-e",
"require 'json'; require 'net/http'; puts JSON.generate({tools: [{name: 'ruby_docs', description: 'Search Ruby documentation'}]})"
],
"category": "Documentation",
"description": "Ruby language documentation and API reference",
"complexity": "Low",
"enabled": true
},
"rails-docs": {
"command": "ruby",
"args": [
"-e",
"require 'json'; require 'net/http'; puts JSON.generate({tools: [{name: 'rails_docs', description: 'Search Rails documentation and guides'}]})"
],
"category": "Documentation",
"description": "Ruby on Rails framework documentation and guides",
"complexity": "Low",
"enabled": true
},
"rubygems": {
"command": "ruby",
"args": [
"-e",
"require 'json'; require 'net/http'; puts JSON.generate({tools: [{name: 'gem_search', description: 'Search and explore Ruby gems'}]})"
],
"category": "Package Management",
"description": "Search and explore Ruby gems from RubyGems.org",
"complexity": "Low",
"enabled": true
},
"bundler": {
"command": "bundle",
"args": [
"exec",
"ruby",
"-e",
"require 'json'; puts JSON.generate({tools: [{name: 'bundle_audit', description: 'Security audit for gems'}]})"
],
"category": "Security",
"description": "Bundler integration for dependency management and security auditing",
"complexity": "Medium",
"enabled": true
}
}
}
+284
View File
@@ -0,0 +1,284 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
This is a Ruby project optimized for modern Ruby development. The project uses industry-standard tools and follows best practices for scalable application development.
## Development Commands
### Environment Management
- `ruby --version` - Check Ruby version
- `rbenv versions` - List available Ruby versions (with rbenv)
- `rbenv install 3.2.0` - Install specific Ruby version
- `rbenv local 3.2.0` - Set local Ruby version
- `rvm use 3.2.0` - Use specific Ruby version (with RVM)
### Package Management
- `bundle install` - Install dependencies from Gemfile
- `bundle update` - Update all gems to latest versions
- `bundle update <gem_name>` - Update specific gem
- `bundle exec <command>` - Run command with bundled gems
- `bundle add <gem_name>` - Add new gem to Gemfile
- `bundle remove <gem_name>` - Remove gem from Gemfile
- `gem install <gem_name>` - Install gem globally
- `gem list` - List installed gems
### Testing Commands
- `rspec` - Run RSpec tests
- `rspec spec/models` - Run specific test directory
- `rspec spec/models/user_spec.rb` - Run specific test file
- `rspec --format documentation` - Run tests with detailed output
- `rspec --tag focus` - Run tests with specific tag
- `ruby -Itest test/` - Run Minitest tests
- `rake test` - Run test suite via Rake
- `bundle exec rspec` - Run RSpec with bundled gems
### Code Quality Commands
- `rubocop` - Run RuboCop linter
- `rubocop -A` - Auto-correct RuboCop violations
- `rubocop --only <cop_name>` - Run specific RuboCop cop
- `brakeman` - Run security analysis
- `reek` - Run code smell detector
- `yard` - Generate documentation
- `bundle audit` - Check for security vulnerabilities in gems
### Development Tools
- `irb` - Interactive Ruby console
- `pry` - Enhanced Ruby console (if installed)
- `ruby -c <file.rb>` - Check Ruby syntax
- `ruby -w <file.rb>` - Run with warnings enabled
## Technology Stack
### Core Technologies
- **Ruby** - Primary programming language (3.2.0+)
- **Bundler** - Dependency management
- **RubyGems** - Package management system
### Common Frameworks
- **Ruby on Rails** - Full-stack web framework
- **Sinatra** - Lightweight web framework
- **Hanami** - Modern web framework
- **Grape** - REST API framework
- **Roda** - Routing tree web framework
### Testing Frameworks
- **RSpec** - Behavior-driven development testing framework
- **Minitest** - Built-in testing framework
- **FactoryBot** - Test data generation
- **Capybara** - Acceptance testing for web applications
- **VCR** - Record HTTP interactions for tests
- **WebMock** - Mock HTTP requests
### Code Quality Tools
- **RuboCop** - Ruby static code analyzer and formatter
- **Brakeman** - Static analysis security vulnerability scanner
- **Reek** - Code smell detector
- **SimpleCov** - Code coverage analysis
- **YARD** - Documentation generation tool
### Popular Gems
- **Puma** - Web server
- **Sidekiq** - Background job processing
- **Redis** - In-memory data structure store
- **PostgreSQL/MySQL** - Database adapters
- **Devise** - Authentication solution (pre-Rails 8)
- **Pundit** - Authorization system
## Project Structure Guidelines
### File Organization
```
app/
├── models/ # Business logic and data models
├── controllers/ # Web controllers (Rails)
├── views/ # Templates and presentation
├── helpers/ # View helpers
├── services/ # Business logic services
├── workers/ # Background job workers
└── lib/ # Custom libraries
config/
├── application.rb # Application configuration
├── routes.rb # URL routing (Rails)
├── database.yml # Database configuration
└── environments/ # Environment-specific configs
spec/ or test/
├── models/ # Model tests
├── controllers/ # Controller tests
├── features/ # Feature/integration tests
├── support/ # Test support files
└── factories/ # Test data factories
lib/
├── tasks/ # Rake tasks
└── custom_modules/ # Custom Ruby modules
Gemfile # Gem dependencies
Gemfile.lock # Locked gem versions
Rakefile # Rake task definitions
```
### Naming Conventions
- **Files/Modules**: Use snake_case (`user_profile.rb`)
- **Classes**: Use PascalCase (`UserProfile`)
- **Methods/Variables**: Use snake_case (`get_user_data`)
- **Constants**: Use SCREAMING_SNAKE_CASE (`API_BASE_URL`)
- **Private methods**: Prefix with underscore or use `private` keyword
## Ruby Guidelines
### Code Style
- Follow the Ruby Style Guide
- Use meaningful variable and method names
- Keep methods focused and single-purpose
- Use Ruby idioms and conventions
- Prefer explicit over implicit when it improves clarity
- Use proper indentation (2 spaces)
### Best Practices
- Use `bundle exec` for running commands with specific gem versions
- Write tests for all public methods
- Use descriptive commit messages
- Keep Gemfile organized and commented
- Use environment variables for configuration
- Handle exceptions appropriately
- Follow DRY (Don't Repeat Yourself) principles
## Testing Standards
### Test Structure
- Organize tests to mirror source code structure
- Use descriptive test names that explain the behavior
- Follow AAA pattern (Arrange, Act, Assert)
- Use factories for test data
- Group related tests in context blocks (RSpec)
### Coverage Goals
- Aim for 90%+ test coverage
- Write unit tests for models and services
- Use integration tests for controllers and features
- Mock external dependencies
- Test error conditions and edge cases
### RSpec Configuration
```ruby
# spec/spec_helper.rb
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
config.filter_run_when_matching :focus
config.example_status_persistence_file_path = "spec/examples.txt"
config.disable_monkey_patching!
config.warnings = true
if config.files_to_run.one?
config.default_formatter = "doc"
end
config.profile_examples = 10
config.order = :random
Kernel.srand config.seed
end
```
## Bundler Configuration
### Gemfile Best Practices
```ruby
# Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '3.2.0'
# Core gems
gem 'rails', '~> 8.0.0'
gem 'pg', '~> 1.1'
gem 'puma', '~> 6.0'
# Development and test gems
group :development, :test do
gem 'debug', platforms: %i[ mri mingw x64_mingw ]
gem 'rspec-rails'
gem 'factory_bot_rails'
end
group :development do
gem 'rubocop', require: false
gem 'brakeman', require: false
gem 'web-console'
end
group :test do
gem 'capybara'
gem 'selenium-webdriver'
gem 'simplecov', require: false
end
```
### Bundle Configuration
```bash
# .bundle/config or config/bundle
BUNDLE_PATH: "vendor/bundle"
BUNDLE_JOBS: "4"
BUNDLE_RETRY: "3"
```
## Development Workflow
### Before Starting
1. Check Ruby version compatibility
2. Install dependencies with `bundle install`
3. Set up database (if applicable)
4. Run tests to ensure setup is correct
### During Development
1. Write tests first (TDD approach)
2. Run tests frequently: `bundle exec rspec`
3. Use meaningful commit messages
4. Run code quality checks regularly
### Before Committing
1. Run full test suite: `bundle exec rspec`
2. Run linter: `rubocop`
3. Run security scanner: `brakeman`
4. Check for vulnerabilities: `bundle audit`
5. Ensure code coverage is maintained
## Security Guidelines
### Gem Security
- Regularly update gems with `bundle update`
- Use `bundle audit` to check for known vulnerabilities
- Pin gem versions in Gemfile.lock
- Review gem source code for suspicious packages
### Code Security
- Validate input data
- Use environment variables for sensitive configuration
- Implement proper authentication and authorization
- Sanitize data before database operations
- Use HTTPS for production deployments
- Follow OWASP guidelines for web security
## Performance Considerations
### Code Performance
- Use proper indexing for database queries
- Implement caching strategies
- Profile code with tools like `ruby-prof`
- Monitor memory usage
- Use background jobs for heavy operations
### Gem Performance
- Choose gems wisely based on performance metrics
- Monitor gem overhead
- Use lightweight alternatives when possible
- Profile application with production-like data
@@ -0,0 +1,490 @@
# Rails 8 Native Authentication Generator
Generate Rails 8's built-in authentication system with modern security practices.
## Purpose
This command helps you implement Rails 8's new native authentication system, eliminating the need for external gems like Devise for basic authentication needs.
## Usage
```
/authentication
```
## What this command does
1. **Generates authentication models** with secure password handling
2. **Creates authentication controllers** for login/logout/signup
3. **Adds authentication views** with modern styling
4. **Implements session management** with security best practices
5. **Sets up authentication helpers** for controllers and views
## Rails 8 Authentication Generator
```bash
# Generate authentication for User model
bin/rails generate authentication User
# Or specify custom model name
bin/rails generate authentication Account
# Generate with custom attributes
bin/rails generate authentication User first_name:string last_name:string
```
## Generated User Model
```ruby
# app/models/user.rb
class User < ApplicationRecord
has_secure_password
validates :email, presence: true, uniqueness: true
validates :password, length: { minimum: 8 }, if: -> { new_record? || !password.blank? }
normalizes :email, with: ->(email) { email.strip.downcase }
before_save :normalize_email
private
def normalize_email
self.email = email.downcase.strip
end
end
```
## Authentication Controller
```ruby
# app/controllers/authentication_controller.rb
class AuthenticationController < ApplicationController
skip_before_action :authenticate_user!, only: [:new, :create]
def new
# Login page
end
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
login(user)
redirect_to root_path, notice: 'Logged in successfully'
else
flash.now[:alert] = 'Invalid email or password'
render :new, status: :unprocessable_entity
end
end
def destroy
logout
redirect_to root_path, notice: 'Logged out successfully'
end
private
def login(user)
session[:user_id] = user.id
@current_user = user
end
def logout
session[:user_id] = nil
@current_user = nil
end
end
```
## Registration Controller
```ruby
# app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
skip_before_action :authenticate_user!
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
login(@user)
redirect_to root_path, notice: 'Account created successfully'
else
render :new, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :first_name, :last_name)
end
def login(user)
session[:user_id] = user.id
@current_user = user
end
end
```
## Application Controller Updates
```ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_user!
private
def authenticate_user!
redirect_to login_path, alert: 'Please log in to continue' unless current_user
end
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def logged_in?
!!current_user
end
helper_method :logged_in?
def require_login
unless logged_in?
flash[:alert] = 'You must be logged in to access this page'
redirect_to login_path
end
end
end
```
## Authentication Views
### Login Form
```erb
<!-- app/views/authentication/new.html.erb -->
<div class="authentication-form">
<h1>Log In</h1>
<%= form_with url: login_path, local: true, class: "auth-form" do |form| %>
<div class="form-group">
<%= form.label :email, "Email" %>
<%= form.email_field :email, required: true, autofocus: true, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :password, "Password" %>
<%= form.password_field :password, required: true, class: "form-control" %>
</div>
<div class="form-actions">
<%= form.submit "Log In", class: "btn btn-primary" %>
</div>
<% end %>
<div class="auth-links">
<%= link_to "Don't have an account? Sign up", signup_path %>
</div>
</div>
```
### Registration Form
```erb
<!-- app/views/registrations/new.html.erb -->
<div class="authentication-form">
<h1>Sign Up</h1>
<%= form_with model: @user, url: signup_path, local: true, class: "auth-form" do |form| %>
<% if @user.errors.any? %>
<div class="error-messages">
<h3><%= pluralize(@user.errors.count, "error") %> prohibited this account from being saved:</h3>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= form.label :first_name, "First Name" %>
<%= form.text_field :first_name, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :last_name, "Last Name" %>
<%= form.text_field :last_name, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :email, "Email" %>
<%= form.email_field :email, required: true, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :password, "Password" %>
<%= form.password_field :password, required: true, minlength: 8, class: "form-control" %>
<small class="form-text">Minimum 8 characters</small>
</div>
<div class="form-group">
<%= form.label :password_confirmation, "Confirm Password" %>
<%= form.password_field :password_confirmation, required: true, class: "form-control" %>
</div>
<div class="form-actions">
<%= form.submit "Sign Up", class: "btn btn-primary" %>
</div>
<% end %>
<div class="auth-links">
<%= link_to "Already have an account? Log in", login_path %>
</div>
</div>
```
## Route Configuration
```ruby
# config/routes.rb
Rails.application.routes.draw do
# Authentication routes
get 'login', to: 'authentication#new'
post 'login', to: 'authentication#create'
delete 'logout', to: 'authentication#destroy'
# Registration routes
get 'signup', to: 'registrations#new'
post 'signup', to: 'registrations#create'
# User management routes
resources :users, except: [:new, :create]
# Root route
root 'dashboard#index'
end
```
## Migration Files
```ruby
# db/migrate/xxx_create_users.rb
class CreateUsers < ActiveRecord::Migration[8.0]
def change
create_table :users do |t|
t.string :email, null: false
t.string :password_digest, null: false
t.string :first_name
t.string :last_name
t.timestamps
end
add_index :users, :email, unique: true
end
end
```
## Advanced Authentication Features
### Password Reset
```ruby
# app/controllers/password_resets_controller.rb
class PasswordResetsController < ApplicationController
skip_before_action :authenticate_user!
def new
# Password reset request form
end
def create
@user = User.find_by(email: params[:email])
if @user
@user.send_password_reset
redirect_to root_path, notice: 'Email sent with password reset instructions'
else
flash.now[:alert] = 'Email address not found'
render :new
end
end
def edit
@user = User.find_by(password_reset_token: params[:id])
if @user.nil? || @user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, alert: 'Password reset has expired'
end
end
def update
@user = User.find_by(password_reset_token: params[:id])
if @user && @user.password_reset_sent_at > 2.hours.ago
if @user.update(password_params)
@user.update_columns(password_reset_token: nil, password_reset_sent_at: nil)
redirect_to root_path, notice: 'Password has been reset'
else
render :edit
end
else
redirect_to new_password_reset_path, alert: 'Password reset has expired'
end
end
private
def password_params
params.require(:user).permit(:password, :password_confirmation)
end
end
```
### User Model Extensions
```ruby
# app/models/user.rb (extended)
class User < ApplicationRecord
has_secure_password
validates :email, presence: true, uniqueness: true
validates :password, length: { minimum: 8 }, if: -> { new_record? || !password.blank? }
normalizes :email, with: ->(email) { email.strip.downcase }
# Password reset functionality
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver_now
end
def full_name
"#{first_name} #{last_name}".strip
end
def initials
"#{first_name&.first}#{last_name&.first}".upcase
end
private
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
```
## Session Security Enhancements
```ruby
# config/application.rb
config.session_store :cookie_store, {
key: '_myapp_session',
secure: Rails.env.production?,
httponly: true,
same_site: :lax
}
# config/environments/production.rb
config.force_ssl = true
config.session_store :cookie_store, {
key: '_myapp_session',
secure: true,
httponly: true,
same_site: :strict
}
```
## Authentication Helper Methods
```ruby
# app/helpers/authentication_helper.rb
module AuthenticationHelper
def user_avatar(user, size: 40)
if user.avatar.present?
image_tag user.avatar, alt: user.full_name, class: "avatar", size: "#{size}x#{size}"
else
content_tag :div, user.initials, class: "avatar avatar-initials",
style: "width: #{size}px; height: #{size}px; line-height: #{size}px;"
end
end
def current_user_menu
if logged_in?
render 'shared/user_menu'
else
render 'shared/guest_menu'
end
end
end
```
## Testing Authentication
```ruby
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
describe 'validations' do
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should validate_length_of(:password).is_at_least(8) }
it { should have_secure_password }
end
describe 'email normalization' do
it 'normalizes email to lowercase' do
user = User.create!(email: 'USER@EXAMPLE.COM', password: 'password123')
expect(user.email).to eq('user@example.com')
end
end
describe '#full_name' do
it 'returns combined first and last name' do
user = User.new(first_name: 'John', last_name: 'Doe')
expect(user.full_name).to eq('John Doe')
end
end
end
# spec/controllers/authentication_controller_spec.rb
RSpec.describe AuthenticationController, type: :controller do
describe 'POST #create' do
let(:user) { create(:user, password: 'password123') }
context 'with valid credentials' do
it 'logs in the user' do
post :create, params: { email: user.email, password: 'password123' }
expect(session[:user_id]).to eq(user.id)
expect(response).to redirect_to(root_path)
end
end
context 'with invalid credentials' do
it 'does not log in the user' do
post :create, params: { email: user.email, password: 'wrong' }
expect(session[:user_id]).to be_nil
expect(response).to render_template(:new)
end
end
end
end
```
## Security Best Practices Included
- **Secure password hashing** with bcrypt
- **Email normalization** to prevent duplicates
- **CSRF protection** enabled by default
- **Session security** with httponly and secure flags
- **Password strength validation** (minimum 8 characters)
- **Timing attack prevention** in authentication
- **Password reset token expiration**
- **SSL enforcement** in production
@@ -0,0 +1,376 @@
# Rails 8 Project Configuration
This file provides specific guidance for Ruby on Rails 8 web application development using Claude Code.
## Project Overview
This is a Ruby on Rails 8 web application project optimized for modern web development with the latest Rails features. The project follows Rails 8 conventions and leverages new capabilities like native authentication, Solid trifecta, and Kamal 2 deployment.
## Rails 8 Specific Development Commands
### Project Creation & Setup
- `rails new myapp` - Create new Rails 8 application
- `rails new myapp --skip-kamal` - Create app without Kamal deployment
- `rails new myapp --database=postgresql` - Create app with PostgreSQL
- `rails new myapp --css=tailwind` - Create app with Tailwind CSS
- `rails new myapp --javascript=esbuild` - Create app with esbuild
### Server Management
- `bin/rails server` or `bin/rails s` - Start development server
- `bin/rails server -p 4000` - Start server on port 4000
- `bin/rails server -e production` - Start in production mode
- `bin/dev` - Start development server with asset compilation (if available)
### Database Management
- `bin/rails db:create` - Create database
- `bin/rails db:migrate` - Run pending migrations
- `bin/rails db:rollback` - Rollback last migration
- `bin/rails db:reset` - Drop, create, and migrate database
- `bin/rails db:seed` - Run database seeds
- `bin/rails db:setup` - Create, migrate, and seed database
### Generation Commands
- `bin/rails generate model User name:string email:string` - Generate model
- `bin/rails generate controller Users index show` - Generate controller
- `bin/rails generate migration AddIndexToUsers email:index` - Generate migration
- `bin/rails generate authentication` - Generate Rails 8 native authentication (NEW)
- `bin/rails generate solid_queue:install` - Install Solid Queue (NEW)
- `bin/rails generate solid_cache:install` - Install Solid Cache (NEW)
### Rails 8 Native Authentication
- `bin/rails generate authentication User` - Generate authentication for User model
- `bin/rails generate authentication:views` - Generate authentication views
- `bin/rails generate authentication:controllers` - Generate authentication controllers
### Background Jobs (Solid Queue)
- `bin/rails solid_queue:start` - Start Solid Queue worker
- `bin/rails solid_queue:install` - Install Solid Queue configuration
- `SampleJob.perform_later(user)` - Enqueue background job
### Caching (Solid Cache)
- `Rails.cache.write("key", "value")` - Write to cache
- `Rails.cache.read("key")` - Read from cache
- `Rails.cache.delete("key")` - Delete from cache
- `bin/rails solid_cache:clear` - Clear all cached data
### Asset Management (Propshaft)
- `bin/rails assets:precompile` - Precompile assets for production
- `bin/rails assets:clobber` - Remove compiled assets
- Assets are automatically served in development
### Testing Commands
- `bin/rails test` - Run all tests
- `bin/rails test:models` - Run model tests
- `bin/rails test:controllers` - Run controller tests
- `bin/rails test:system` - Run system tests
- `bin/rails test test/models/user_test.rb` - Run specific test file
### Console & Debugging
- `bin/rails console` or `bin/rails c` - Start Rails console
- `bin/rails console --sandbox` - Start console in sandbox mode
- `bin/rails dbconsole` or `bin/rails db` - Start database console
### Code Quality & Security (Rails 8 Defaults)
- `bin/rails rubocop` - Run RuboCop with Rails 8 Omakase config
- `bin/rails brakeman` - Run security scan (included by default)
- `bundle audit` - Check for vulnerable gems
## Deployment with Kamal 2 (Rails 8 Default)
### Kamal 2 Commands
- `kamal setup` - Initial server setup
- `kamal deploy` - Deploy application
- `kamal redeploy` - Redeploy without setup
- `kamal rollback` - Rollback to previous version
- `kamal logs` - View application logs
- `kamal logs --follow` - Follow logs in real-time
- `kamal shell` - SSH into deployed container
- `kamal details` - Show deployment details
### Kamal 2 Configuration
Configuration is automatically generated in `config/deploy.yml`:
```yaml
# config/deploy.yml
service: myapp
image: myapp
servers:
- 192.168.1.1
registry:
server: registry.digitalocean.com
username: myusername
env:
clear:
RAILS_ENV: production
secret:
- RAILS_MASTER_KEY
builder:
multiarch: false
```
## Rails 8 Project Structure
```
myapp/
├── app/
│ ├── models/ # ActiveRecord models
│ ├── controllers/ # ActionController controllers
│ ├── views/ # ERB/HAML templates
│ ├── helpers/ # View helpers
│ ├── mailers/ # ActionMailer mailers
│ ├── jobs/ # ActiveJob jobs (Solid Queue)
│ ├── assets/ # Application assets (Propshaft)
│ └── javascript/ # JavaScript files
├── config/
│ ├── application.rb # Application configuration
│ ├── routes.rb # URL routing
│ ├── database.yml # Database configuration
│ ├── deploy.yml # Kamal 2 deployment config (NEW)
│ └── environments/ # Environment-specific configs
├── db/
│ ├── migrate/ # Database migrations
│ └── seeds.rb # Database seeds
├── test/ # Test files (default in Rails 8)
│ ├── models/
│ ├── controllers/
│ ├── integration/
│ └── system/
├── bin/ # Binstubs
├── config.ru # Rack configuration
├── Gemfile # Gem dependencies
├── Gemfile.lock # Locked gem versions
└── Dockerfile # Docker configuration (if using containers)
```
## Rails 8 New Features Integration
### 1. SQLite Production Enhancements
Rails 8 makes SQLite production-ready:
```ruby
# config/database.yml
production:
adapter: sqlite3
database: storage/production.sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
# New Rails 8 SQLite configurations
pragmas:
busy_timeout: 1000
journal_mode: WAL
synchronous: NORMAL
foreign_keys: true
```
### 2. Solid Trifecta Configuration
#### Solid Queue (Background Jobs)
```ruby
# config/application.rb
config.active_job.queue_adapter = :solid_queue
# app/jobs/sample_job.rb
class SampleJob < ApplicationJob
queue_as :default
def perform(user)
# Background job logic
end
end
```
#### Solid Cache (Caching)
```ruby
# config/application.rb
config.cache_store = :solid_cache_store
# Usage in controllers/models
Rails.cache.fetch("user_#{user.id}", expires_in: 1.hour) do
expensive_calculation(user)
end
```
#### Solid Cable (WebSockets)
```ruby
# config/application.rb
config.action_cable.adapter = :solid_cable
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
end
```
### 3. Native Authentication (Rails 8)
```ruby
# Generate authentication
bin/rails generate authentication User
# app/models/user.rb (generated)
class User < ApplicationRecord
has_secure_password
validates :email, presence: true, uniqueness: true
normalizes :email, with: ->(email) { email.strip.downcase }
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :authenticate_user!
private
def authenticate_user!
redirect_to login_path unless current_user
end
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
```
### 4. Propshaft Asset Pipeline
```ruby
# config/application.rb
# Propshaft is now the default - no configuration needed
# Assets are automatically fingerprinted and served
# Use asset helpers in views:
<%= asset_path("application.css") %>
<%= asset_path("logo.png") %>
```
## Testing in Rails 8
### Default Test Suite Setup
Rails 8 includes comprehensive testing setup by default:
```ruby
# test/test_helper.rb
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
module ActiveSupport
class TestCase
# Run tests in parallel with specified workers
parallelize(workers: :number_of_processors)
# Setup all fixtures in test/fixtures/*.yml
fixtures :all
# Add more helper methods to be used by all tests here...
end
end
```
### System Testing with Capybara
```ruby
# test/system/users_test.rb
class UsersTest < ApplicationSystemTestCase
test "creating a user" do
visit new_user_path
fill_in "Name", with: "John Doe"
fill_in "Email", with: "john@example.com"
click_button "Create User"
assert_text "User was successfully created"
end
end
```
## Security Best Practices (Rails 8)
### Built-in Security Features
- **Brakeman** - Included by default for security scanning
- **Credential Management** - Use `rails credentials:edit`
- **Content Security Policy** - Configure in `application_controller.rb`
- **Force SSL** - Enable in production environment
```ruby
# config/environments/production.rb
config.force_ssl = true
config.content_security_policy do |policy|
policy.default_src :self, :https
policy.font_src :self, :https, :data
policy.img_src :self, :https, :data
policy.object_src :none
policy.script_src :self, :https
policy.style_src :self, :https, :unsafe_inline
end
```
## Performance Optimization
### Database Optimization
```ruby
# Use includes to avoid N+1 queries
@users = User.includes(:posts).all
# Use counter_cache for associations
class Post < ApplicationRecord
belongs_to :user, counter_cache: true
end
```
### Caching Strategies
```ruby
# Fragment caching in views
<% cache @user do %>
<%= render @user %>
<% end %>
# Action caching in controllers
class UsersController < ApplicationController
caches_action :index, expires_in: 1.hour
end
```
## Production Deployment Checklist
### Environment Configuration
- [ ] Set `RAILS_ENV=production`
- [ ] Configure `RAILS_MASTER_KEY` or `config/credentials.yml.enc`
- [ ] Set up database (PostgreSQL/MySQL for production)
- [ ] Configure email delivery
- [ ] Set up error monitoring (Sentry, Bugsnag)
- [ ] Configure logging and log rotation
### Kamal 2 Deployment
- [ ] Update `config/deploy.yml` with production servers
- [ ] Set up Docker registry access
- [ ] Configure environment variables and secrets
- [ ] Run `kamal setup` for initial deployment
- [ ] Deploy with `kamal deploy`
### Performance & Monitoring
- [ ] Enable asset precompilation
- [ ] Configure CDN for static assets
- [ ] Set up application performance monitoring (APM)
- [ ] Configure health checks
- [ ] Set up backup strategies for database
## Upgrading to Rails 8
### Migration Steps
1. Update Gemfile: `gem 'rails', '~> 8.0.0'`
2. Run `bundle update rails`
3. Run `bin/rails app:update` to update configuration files
4. Review and update deprecated code
5. Update test suite to use new defaults
6. Consider migrating to Solid trifecta adapters
7. Optionally migrate authentication to native Rails 8 system
### Breaking Changes to Consider
- Ruby 3.2+ requirement
- Propshaft replaces Sprockets by default
- Some deprecated ActiveRecord methods removed
- Updated default configurations for new applications
This Rails 8 configuration provides a modern, production-ready foundation with the latest Rails features and best practices.
+78
View File
@@ -0,0 +1,78 @@
{
"mcpServers": {
"rust-sdk": {
"name": "Rust MCP SDK",
"description": "Asynchronous, high-performance SDK for Rust",
"command": "rust_mcp_server",
"args": [],
"env": {}
},
"ht-mcp": {
"name": "HT MCP",
"description": "Pure Rust implementation for headless terminal interaction",
"command": "ht-mcp",
"args": [],
"env": {}
},
"rust-docs": {
"name": "Rust Docs MCP",
"description": "Prevents outdated code suggestions by providing updated Rust docs",
"command": "rust-docs-mcp-server",
"args": [],
"env": {}
},
"substrate": {
"name": "Substrate MCP",
"description": "MCP server for interacting with Substrate-based blockchains",
"command": "substrate-mcp-rs",
"args": [],
"env": {
"NODE_URL": "..."
}
},
"mcp-proxy": {
"name": "MCP Proxy",
"description": "Fast Rust-based proxy between stdio and SSE",
"command": "mcp-proxy",
"args": [],
"env": {}
},
"memory-bank": {
"name": "Memory Bank MCP",
"description": "Centralized memory system for AI agents",
"command": "server-memory",
"args": [],
"env": {}
},
"sequential-thinking": {
"name": "Sequential Thinking MCP",
"description": "Helps LLMs decompose complex tasks into logical steps",
"command": "code-reasoning",
"args": [],
"env": {}
},
"brave-search": {
"name": "Brave Search MCP",
"description": "Privacy-focused web search tool",
"command": "server-brave-search",
"args": [],
"env": {}
},
"google-maps": {
"name": "Google Maps MCP",
"description": "Integrates Google Maps for geolocation and directions",
"command": "server-google-maps",
"args": [],
"env": {
"GOOGLE_MAPS_API_KEY": "..."
}
},
"deep-graph": {
"name": "Deep Graph MCP (Code Graph)",
"description": "Transforms source code into semantic graphs via DeepGraph",
"command": "mcp-code-graph",
"args": [],
"env": {}
}
}
}
+26
View File
@@ -0,0 +1,26 @@
# Rust Claude Code Templates
## Coming Soon! 🚧
We're actively working on creating comprehensive Claude Code templates for Rust development.
### What to Expect
- Best practices for Rust project structure
- Integration with popular Rust tools and frameworks
- Workflow optimizations for Rust development
- Testing and benchmarking configurations
- Cross-compilation and deployment setups
- Memory safety and performance guidelines
### Meanwhile...
You can use the [common templates](../common/README.md) as a starting point for your Rust projects. The universal guidelines and git workflows will work well with Rust development.
### Stay Updated
⭐ Star this repository to get notified when the Rust templates are released!
### Contributing
Interested in helping build these templates? We welcome contributions! Please check the main repository's contribution guidelines and feel free to open an issue or pull request.
---
*Expected release: Coming soon*