Initial commit
This commit is contained in:
389
commands/api/api-new.md
Normal file
389
commands/api/api-new.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# Create New API Endpoint (RepairCoin)
|
||||
|
||||
Create a new API endpoint for RepairCoin. This command supports both:
|
||||
1. **Backend Express API** (Domain-Driven Design) - Primary use case
|
||||
2. **Frontend Next.js API Routes** - For frontend-only features
|
||||
|
||||
---
|
||||
|
||||
## Part 1: Backend Express API (Primary)
|
||||
|
||||
Use this for main backend features requiring database access, blockchain interaction, or cross-domain events.
|
||||
|
||||
### Architecture
|
||||
|
||||
RepairCoin uses Domain-Driven Design with these domains:
|
||||
- **admin** - Platform analytics, treasury, user management
|
||||
- **customer** - Customer management, tiers, referrals, balances
|
||||
- **shop** - Shop subscriptions, RCN purchasing, reward issuance
|
||||
- **token** - RCN/RCG minting, redemption, cross-shop transfers
|
||||
- **webhook** - FixFlow and Stripe webhook processing
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
backend/src/domains/{domain}/
|
||||
├── {Domain}Domain.ts # Domain module
|
||||
├── routes/
|
||||
│ ├── index.ts # Main router
|
||||
│ └── {feature}.ts # Feature routes
|
||||
├── controllers/
|
||||
│ └── {Feature}Controller.ts
|
||||
└── services/
|
||||
└── {Feature}Service.ts
|
||||
```
|
||||
|
||||
### Step 1: Create Routes File
|
||||
|
||||
**Path**: `backend/src/domains/{domain}/routes/{feature}.ts`
|
||||
|
||||
```typescript
|
||||
import { Router } from 'express';
|
||||
import { requireRole, authMiddleware } from '../../../middleware/auth';
|
||||
import {
|
||||
validateRequired,
|
||||
validateEthereumAddress,
|
||||
validateEmail,
|
||||
validateNumeric,
|
||||
asyncHandler
|
||||
} from '../../../middleware/errorHandler';
|
||||
import { {Feature}Controller } from '../controllers/{Feature}Controller';
|
||||
import { {Feature}Service } from '../services/{Feature}Service';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Initialize service and controller
|
||||
const {feature}Service = new {Feature}Service();
|
||||
const {feature}Controller = new {Feature}Controller({feature}Service);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/{domain}/{feature}:
|
||||
* post:
|
||||
* summary: {Description}
|
||||
* tags: [{Domain}]
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - field1
|
||||
* properties:
|
||||
* field1:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Success
|
||||
*/
|
||||
router.post('/{feature}',
|
||||
authMiddleware,
|
||||
requireRole(['admin', 'shop']),
|
||||
validateRequired(['field1']),
|
||||
asyncHandler({feature}Controller.{method}.bind({feature}Controller))
|
||||
);
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
### Step 2: Create Controller
|
||||
|
||||
**Path**: `backend/src/domains/{domain}/controllers/{Feature}Controller.ts`
|
||||
|
||||
```typescript
|
||||
import { Request, Response } from 'express';
|
||||
import { {Feature}Service } from '../services/{Feature}Service';
|
||||
import { ResponseHelper } from '../../../utils/responseHelper';
|
||||
|
||||
export class {Feature}Controller {
|
||||
constructor(private {feature}Service: {Feature}Service) {}
|
||||
|
||||
async {method}(req: Request, res: Response) {
|
||||
try {
|
||||
const { field1, field2 } = req.body;
|
||||
const { id } = req.params;
|
||||
|
||||
const result = await this.{feature}Service.{method}({
|
||||
field1,
|
||||
field2,
|
||||
id,
|
||||
userAddress: req.user?.address,
|
||||
userRole: req.user?.role
|
||||
});
|
||||
|
||||
ResponseHelper.success(res, result);
|
||||
} catch (error: unknown) {
|
||||
const err = error as Error;
|
||||
if (err.message === 'Not found') {
|
||||
ResponseHelper.notFound(res, err.message);
|
||||
} else if (err.message === 'Unauthorized') {
|
||||
ResponseHelper.forbidden(res, err.message);
|
||||
} else if (err.message.includes('already exists')) {
|
||||
ResponseHelper.conflict(res, err.message);
|
||||
} else {
|
||||
ResponseHelper.error(res, err.message, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Create Service
|
||||
|
||||
**Path**: `backend/src/domains/{domain}/services/{Feature}Service.ts`
|
||||
|
||||
```typescript
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { {Entity}Repository } from '../../../repositories/{Entity}Repository';
|
||||
import { eventBus, createDomainEvent } from '../../../events/EventBus';
|
||||
|
||||
interface {Method}Input {
|
||||
field1: string;
|
||||
field2?: number;
|
||||
userAddress?: string;
|
||||
userRole?: string;
|
||||
}
|
||||
|
||||
export class {Feature}Service {
|
||||
private {entity}Repository: {Entity}Repository;
|
||||
|
||||
constructor() {
|
||||
this.{entity}Repository = new {Entity}Repository();
|
||||
}
|
||||
|
||||
async {method}(input: {Method}Input): Promise<unknown> {
|
||||
logger.info('{Feature}Service.{method} called', { input });
|
||||
|
||||
try {
|
||||
// Validation
|
||||
if (!input.field1) {
|
||||
throw new Error('field1 is required');
|
||||
}
|
||||
|
||||
// Authorization
|
||||
if (input.userRole !== 'admin') {
|
||||
throw new Error('Unauthorized');
|
||||
}
|
||||
|
||||
// Business logic
|
||||
const result = await this.{entity}Repository.{operation}({
|
||||
field1: input.field1
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
throw new Error('Not found');
|
||||
}
|
||||
|
||||
// Publish event
|
||||
await eventBus.publish(createDomainEvent(
|
||||
'{domain}.{event}',
|
||||
result.id,
|
||||
{ field1: input.field1 },
|
||||
'{Feature}Service'
|
||||
));
|
||||
|
||||
logger.info('{Feature}Service.{method} completed');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
const err = error as Error;
|
||||
logger.error('{Feature}Service.{method} error', {
|
||||
error: err.message
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Register Routes
|
||||
|
||||
Update `backend/src/domains/{domain}/routes/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { Router } from 'express';
|
||||
import {feature}Routes from './{feature}';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use('/{feature}', {feature}Routes);
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
### Middleware Options
|
||||
|
||||
```typescript
|
||||
// Authentication
|
||||
authMiddleware // JWT auth
|
||||
|
||||
// Authorization
|
||||
requireRole(['admin']) // Admin only
|
||||
requireRole(['admin', 'shop']) // Admin or Shop
|
||||
requireRole(['customer']) // Customer only
|
||||
|
||||
// Validation
|
||||
validateRequired(['field1', 'field2']) // Required fields
|
||||
validateEthereumAddress('walletAddress') // Ethereum address
|
||||
validateEmail('email') // Email format
|
||||
validateNumeric('amount', 0.1, 1000) // Numeric range
|
||||
```
|
||||
|
||||
### Response Helpers
|
||||
|
||||
```typescript
|
||||
ResponseHelper.success(res, data, 'Message');
|
||||
ResponseHelper.created(res, data, 'Created');
|
||||
ResponseHelper.badRequest(res, 'Validation error');
|
||||
ResponseHelper.unauthorized(res, 'Auth required');
|
||||
ResponseHelper.forbidden(res, 'Insufficient permissions');
|
||||
ResponseHelper.notFound(res, 'Not found');
|
||||
ResponseHelper.conflict(res, 'Already exists');
|
||||
ResponseHelper.error(res, 'Internal error', 500);
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
**Path**: `backend/tests/{domain}/{feature}.test.ts`
|
||||
|
||||
```typescript
|
||||
import request from 'supertest';
|
||||
import { describe, it, expect, beforeAll } from '@jest/globals';
|
||||
import RepairCoinApp from '../../src/app';
|
||||
|
||||
describe('{Feature} API', () => {
|
||||
let app: unknown;
|
||||
let token: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
process.env.NODE_ENV = 'test';
|
||||
const repairCoinApp = new RepairCoinApp();
|
||||
await repairCoinApp.initialize();
|
||||
app = repairCoinApp.app;
|
||||
|
||||
const auth = await request(app as any)
|
||||
.post('/api/auth/admin')
|
||||
.send({ walletAddress: process.env.ADMIN_ADDRESSES?.split(',')[0] });
|
||||
token = auth.body.token;
|
||||
});
|
||||
|
||||
it('should create {feature}', async () => {
|
||||
const response = await request(app as any)
|
||||
.post('/api/{domain}/{feature}')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({ field1: 'value1' });
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should reject unauthorized', async () => {
|
||||
const response = await request(app as any)
|
||||
.post('/api/{domain}/{feature}')
|
||||
.send({ field1: 'value1' });
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] Identified correct domain
|
||||
- [ ] Created routes file with middleware
|
||||
- [ ] Created controller with error handling
|
||||
- [ ] Created service with business logic
|
||||
- [ ] Registered routes in domain index
|
||||
- [ ] Added Swagger documentation
|
||||
- [ ] Added TypeScript types (no `any`)
|
||||
- [ ] Implemented auth/validation
|
||||
- [ ] Published domain events if needed
|
||||
- [ ] Created integration tests
|
||||
- [ ] Tested with auth token
|
||||
|
||||
---
|
||||
|
||||
## Part 2: Frontend Next.js API Route (Secondary)
|
||||
|
||||
Use this for frontend-only features (e.g., client-side data transformation, SSR helpers).
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
frontend/app/api/{route}/route.ts
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
```typescript
|
||||
import { NextRequest, NextResponse } from 'next';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Validation schema
|
||||
const RequestSchema = z.object({
|
||||
field1: z.string().min(1),
|
||||
field2: z.number().optional()
|
||||
});
|
||||
|
||||
type RequestBody = z.infer<typeof RequestSchema>;
|
||||
|
||||
// Response helper
|
||||
function success<T>(data: T) {
|
||||
return NextResponse.json({ success: true, data });
|
||||
}
|
||||
|
||||
function error(message: string, status: number = 500) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: message },
|
||||
{ status }
|
||||
);
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const body: unknown = await req.json();
|
||||
|
||||
// Validate
|
||||
const validated = RequestSchema.parse(body) as RequestBody;
|
||||
|
||||
// Business logic
|
||||
const result = {
|
||||
id: '123',
|
||||
...validated
|
||||
};
|
||||
|
||||
return success(result);
|
||||
} catch (err) {
|
||||
if (err instanceof z.ZodError) {
|
||||
return error(err.errors[0].message, 400);
|
||||
}
|
||||
return error('Internal error');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Never use `any` type** - Always use proper TypeScript types
|
||||
2. **Always use asyncHandler** - For Express route error handling
|
||||
3. **Use ResponseHelper** - Consistent API responses
|
||||
4. **Bind controller methods** - `.bind(controller)` in routes
|
||||
5. **Validate inputs** - Use validation middleware
|
||||
6. **Check authorization** - In service layer
|
||||
7. **Log operations** - Use logger for debugging
|
||||
8. **Publish events** - For cross-domain communication
|
||||
9. **Write tests** - Integration tests with auth
|
||||
|
||||
## Examples
|
||||
|
||||
- Customer API: `backend/src/domains/customer/`
|
||||
- Shop API: `backend/src/domains/shop/routes/subscription.ts`
|
||||
- Token API: `backend/src/domains/token/routes/redemptionSession.ts`
|
||||
147
commands/api/api-protect.md
Normal file
147
commands/api/api-protect.md
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
description: Add authentication, authorization, and security to API endpoints
|
||||
model: claude-sonnet-4-5
|
||||
---
|
||||
|
||||
Add comprehensive security, authentication, and authorization to the specified API route.
|
||||
|
||||
## Target API Route
|
||||
|
||||
$ARGUMENTS
|
||||
|
||||
## Security Layers to Implement
|
||||
|
||||
###1. **Authentication** (Who are you?)
|
||||
- Verify user identity
|
||||
- Token validation (JWT, session, API keys)
|
||||
- Handle expired/invalid tokens
|
||||
|
||||
### 2. **Authorization** (What can you do?)
|
||||
- Role-based access control (RBAC)
|
||||
- Resource-level permissions
|
||||
- Check user ownership
|
||||
|
||||
### 3. **Input Validation**
|
||||
- Sanitize all inputs
|
||||
- SQL/NoSQL injection prevention
|
||||
- XSS prevention
|
||||
- Type validation with Zod
|
||||
|
||||
### 4. **Rate Limiting**
|
||||
- Prevent abuse
|
||||
- Per-user/IP limits
|
||||
- Sliding window algorithm
|
||||
|
||||
### 5. **CORS** (if needed)
|
||||
- Whitelist allowed origins
|
||||
- Proper headers
|
||||
- Credentials handling
|
||||
|
||||
## Implementation Approach
|
||||
|
||||
### For Supabase Projects:
|
||||
```typescript
|
||||
// Use Supabase Auth + RLS
|
||||
- getUser() from server-side client
|
||||
- RLS policies for data access
|
||||
- Service role key for admin operations
|
||||
```
|
||||
|
||||
### For NextAuth.js Projects:
|
||||
```typescript
|
||||
// Use NextAuth sessions
|
||||
- getServerSession() in route handlers
|
||||
- Protect with middleware
|
||||
- Role checking logic
|
||||
```
|
||||
|
||||
### For Custom Auth:
|
||||
```typescript
|
||||
// JWT validation
|
||||
- Verify tokens
|
||||
- Decode and validate claims
|
||||
- Check expiration
|
||||
```
|
||||
|
||||
## Security Checklist
|
||||
|
||||
**Authentication**
|
||||
- Verify authentication tokens
|
||||
- Handle missing/invalid tokens (401)
|
||||
- Check token expiration
|
||||
- Secure token storage recommendations
|
||||
|
||||
**Authorization**
|
||||
- Check user roles/permissions (403)
|
||||
- Verify resource ownership
|
||||
- Implement least privilege principle
|
||||
- Log authorization failures
|
||||
|
||||
**Input Validation**
|
||||
- Validate all inputs with Zod
|
||||
- Sanitize SQL/NoSQL inputs
|
||||
- Escape special characters
|
||||
- Limit payload sizes
|
||||
|
||||
**Rate Limiting**
|
||||
- Per-user limits
|
||||
- Per-IP limits
|
||||
- Clear error messages (429)
|
||||
- Retry-After headers
|
||||
|
||||
**CORS**
|
||||
- Whitelist specific origins
|
||||
- Handle preflight requests
|
||||
- Secure credentials
|
||||
- Appropriate headers
|
||||
|
||||
**Error Handling**
|
||||
- Don't expose stack traces
|
||||
- Generic error messages
|
||||
- Log detailed errors server-side
|
||||
- Consistent error format
|
||||
|
||||
**Logging & Monitoring**
|
||||
- Log authentication attempts
|
||||
- Log authorization failures
|
||||
- Track suspicious activity
|
||||
- Monitor rate limit hits
|
||||
|
||||
## What to Generate
|
||||
|
||||
1. **Protected Route Handler** - Secured version of the API route
|
||||
2. **Middleware/Utilities** - Reusable auth helpers
|
||||
3. **Type Definitions** - User, permissions, roles
|
||||
4. **Error Responses** - Standardized auth errors
|
||||
5. **Usage Examples** - Client-side integration
|
||||
|
||||
## Common Patterns for Solo Developers
|
||||
|
||||
**Pattern 1: Simple Token Auth**
|
||||
```typescript
|
||||
// For internal tools, admin panels
|
||||
const token = request.headers.get('authorization')
|
||||
if (token !== process.env.ADMIN_TOKEN) {
|
||||
return new Response('Unauthorized', { status: 401 })
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern 2: User-based Auth**
|
||||
```typescript
|
||||
// For user-facing apps
|
||||
const user = await getCurrentUser(request)
|
||||
if (!user) {
|
||||
return new Response('Unauthorized', { status: 401 })
|
||||
}
|
||||
```
|
||||
|
||||
**Pattern 3: Role-based Auth**
|
||||
```typescript
|
||||
// For apps with different user types
|
||||
const user = await getCurrentUser(request)
|
||||
if (!user || !hasRole(user, 'admin')) {
|
||||
return new Response('Forbidden', { status: 403 })
|
||||
}
|
||||
```
|
||||
|
||||
Generate production-ready, secure code that follows the principle of least privilege.
|
||||
116
commands/api/api-test.md
Normal file
116
commands/api/api-test.md
Normal file
@@ -0,0 +1,116 @@
|
||||
---
|
||||
description: Test API endpoints with automated test generation
|
||||
model: claude-sonnet-4-5
|
||||
---
|
||||
|
||||
Generate comprehensive API tests for the specified endpoint.
|
||||
|
||||
## Target
|
||||
|
||||
$ARGUMENTS
|
||||
|
||||
## Test Strategy for Solo Developers
|
||||
|
||||
Create practical, maintainable tests using modern tools:
|
||||
|
||||
### 1. **Testing Approach**
|
||||
- Unit tests for validation logic
|
||||
- Integration tests for full API flow
|
||||
- Edge case coverage
|
||||
- Error scenario testing
|
||||
|
||||
### 2. **Tools** (choose based on project)
|
||||
- **Vitest** - Fast, modern (recommended for new projects)
|
||||
- **Jest** - Established, widely used
|
||||
- **Supertest** - HTTP assertions
|
||||
- **MSW** - API mocking
|
||||
|
||||
### 3. **Test Coverage**
|
||||
|
||||
**Happy Paths**
|
||||
- Valid inputs return expected results
|
||||
- Proper status codes
|
||||
- Correct response structure
|
||||
|
||||
**Error Paths**
|
||||
- Invalid input validation
|
||||
- Authentication failures
|
||||
- Rate limiting
|
||||
- Server errors
|
||||
- Missing required fields
|
||||
|
||||
**Edge Cases**
|
||||
- Empty requests
|
||||
- Malformed JSON
|
||||
- Large payloads
|
||||
- Special characters
|
||||
- SQL injection attempts
|
||||
- XSS attempts
|
||||
|
||||
### 4. **Test Structure**
|
||||
|
||||
```typescript
|
||||
describe('API Endpoint', () => {
|
||||
describe('Success Cases', () => {
|
||||
it('should handle valid request', () => {})
|
||||
it('should return correct status code', () => {})
|
||||
})
|
||||
|
||||
describe('Validation', () => {
|
||||
it('should reject invalid input', () => {})
|
||||
it('should validate required fields', () => {})
|
||||
})
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it('should handle server errors', () => {})
|
||||
it('should return proper error format', () => {})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### 5. **What to Generate**
|
||||
|
||||
1. **Test File** - Complete test suite with all scenarios
|
||||
2. **Mock Data** - Realistic test fixtures
|
||||
3. **Helper Functions** - Reusable test utilities
|
||||
4. **Setup/Teardown** - Database/state management
|
||||
5. **Quick Test Script** - npm script to run tests
|
||||
|
||||
## Key Testing Principles
|
||||
|
||||
- Test behavior, not implementation
|
||||
- Clear, descriptive test names
|
||||
- Arrange-Act-Assert pattern
|
||||
- Independent tests (no shared state)
|
||||
- Fast execution (<5s for unit tests)
|
||||
- Realistic mock data
|
||||
- Test error messages
|
||||
- L Don't test framework internals
|
||||
- L Don't mock what you don't own
|
||||
- L Avoid brittle tests
|
||||
|
||||
## Additional Scenarios to Cover
|
||||
|
||||
1. **Authentication/Authorization**
|
||||
- Valid tokens
|
||||
- Expired tokens
|
||||
- Missing tokens
|
||||
- Invalid permissions
|
||||
|
||||
2. **Data Validation**
|
||||
- Type mismatches
|
||||
- Out of range values
|
||||
- SQL/NoSQL injection
|
||||
- XSS payloads
|
||||
|
||||
3. **Rate Limiting**
|
||||
- Within limits
|
||||
- Exceeding limits
|
||||
- Reset behavior
|
||||
|
||||
4. **Performance**
|
||||
- Response times
|
||||
- Large dataset handling
|
||||
- Concurrent requests
|
||||
|
||||
Generate production-ready tests I can run immediately with `npm test`.
|
||||
Reference in New Issue
Block a user