- Initialize Git repository with main branch - Create comprehensive .gitignore for Node.js, React, and environment files - Set up directory structure (frontend/, backend/, docs/) - Create detailed README.md with project overview and setup instructions - Add .env.example with all required environment variables - Configure Prettier for consistent code formatting All acceptance criteria met: ✅ Git repository initialized with appropriate .gitignore ✅ Directory structure matches Technical Assumptions ✅ README.md created with project overview and setup docs ✅ .env.example file with all required environment variables ✅ Prettier config files added for code formatting consistency 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
81 lines
2.6 KiB
Markdown
81 lines
2.6 KiB
Markdown
# Security and Performance
|
|
|
|
## Security Requirements
|
|
|
|
**Frontend Security:**
|
|
- **CSP Headers:** Set via Coolify/Nginx:
|
|
```
|
|
Content-Security-Policy: default-src 'self';
|
|
script-src 'self';
|
|
style-src 'self' 'unsafe-inline';
|
|
img-src 'self' https://covers.openlibrary.org;
|
|
connect-src 'self' https://openlibrary.org;
|
|
```
|
|
- **XSS Prevention:** React escapes by default, validate all user inputs
|
|
- **Secure Storage:** No sensitive data stored in localStorage (no auth in MVP)
|
|
|
|
**Backend Security:**
|
|
- **Input Validation:** express-validator on all POST/PUT endpoints
|
|
- **Rate Limiting:** Add express-rate-limit if abuse detected (not in MVP)
|
|
```javascript
|
|
const rateLimit = require('express-rate-limit');
|
|
const limiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 100, // 100 requests per window
|
|
});
|
|
app.use('/api/', limiter);
|
|
```
|
|
- **CORS Policy:**
|
|
```javascript
|
|
app.use(cors({
|
|
origin: process.env.CORS_ORIGIN || 'http://localhost:5173',
|
|
credentials: true,
|
|
}));
|
|
```
|
|
- **Helmet Security Headers:**
|
|
```javascript
|
|
app.use(helmet({
|
|
contentSecurityPolicy: false, // Handled by Nginx
|
|
hsts: { maxAge: 31536000 },
|
|
}));
|
|
```
|
|
|
|
**Authentication Security (Future v1.1):**
|
|
- **Token Storage:** httpOnly cookies (not localStorage)
|
|
- **Session Management:** JWT with 1-hour expiration, refresh tokens
|
|
- **Password Policy:** bcrypt hashing, min 8 characters, complexity requirements
|
|
|
|
## Performance Optimization
|
|
|
|
**Frontend Performance:**
|
|
- **Bundle Size Target:** <500KB gzipped
|
|
- Vite automatically code-splits and tree-shakes
|
|
- Lazy load non-critical routes: `const BookDetail = lazy(() => import('./pages/BookDetail'))`
|
|
- **Loading Strategy:**
|
|
- Critical path: App shell → Book list
|
|
- Lazy load: BookDetail, Calendar, AddBook
|
|
- Prefetch on hover for instant navigation
|
|
- **Caching Strategy:**
|
|
- Service worker caches static assets (cache-first)
|
|
- API responses cached for 5 minutes (stale-while-revalidate)
|
|
- Book search results cached in-memory (1 hour)
|
|
|
|
**Backend Performance:**
|
|
- **Response Time Target:** <200ms for CRUD, <500ms for calculations
|
|
- **Database Optimization:**
|
|
- Indexes on `deadlineDate`, `bookId`, `logDate`
|
|
- Limit queries: `take: 10` for logs, `where: { status: 'reading' }`
|
|
- Use Prisma's `include` to avoid N+1 queries
|
|
- **Caching Strategy:**
|
|
- Book metadata from Open Library cached in-memory (1 hour Map)
|
|
- No Redis needed for single-user MVP
|
|
- Consider Redis if adding multi-user in v1.1
|
|
|
|
**Lighthouse Targets:**
|
|
- Performance: >90
|
|
- Accessibility: >90
|
|
- Best Practices: >95
|
|
- SEO: >90 (PWA)
|
|
|
|
---
|