ddshi 55627762e1 feat: complete backend API with JWT auth, events, notes, AI routes
- Add Express.js server with TypeScript
- Configure Prisma ORM with PostgreSQL schema
- Implement JWT authentication (register, login, logout, refresh)
- Add rate limiting for auth endpoints (10 attempts/15min)
- Password strength validation (8+ chars, uppercase, lowercase, number)
- Events CRUD API (anniversaries and reminders)
- Notes API (single note per user)
- AI parse endpoint with DeepSeek integration
- Security: Helmet, rate limiting, input validation, error handling
- Fix: JWT_SECRET environment variable validation

Code review: Architect approved
Tests: Build verified
2026-01-29 13:08:48 +08:00

67 lines
1.7 KiB
TypeScript

import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
export interface JwtPayload {
userId: string;
email: string;
}
export interface AuthenticatedRequest extends Request {
user?: JwtPayload;
}
// JWT configuration with environment validation
const JWT_SECRET_ENV = process.env.JWT_SECRET;
if (!JWT_SECRET_ENV) {
throw new Error('FATAL: JWT_SECRET environment variable is required. Set it in .env file.');
}
export const JWT_SECRET = JWT_SECRET_ENV;
export const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';
export const JWT_REFRESH_EXPIRES_IN = process.env.JWT_REFRESH_EXPIRES_IN || '30d';
// Verify JWT token middleware
export const authenticateToken = (
req: AuthenticatedRequest,
res: Response,
next: NextFunction
) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({ error: 'Authentication required' });
}
try {
const decoded = jwt.verify(token, JWT_SECRET) as JwtPayload;
req.user = decoded;
next();
} catch (error) {
return res.status(403).json({ error: 'Invalid or expired token' });
}
};
// Generate access token
export const generateAccessToken = (payload: JwtPayload): string => {
return jwt.sign(payload, JWT_SECRET, {
expiresIn: JWT_EXPIRES_IN,
});
};
// Generate refresh token
export const generateRefreshToken = (payload: JwtPayload): string => {
return jwt.sign(payload, JWT_SECRET, {
expiresIn: JWT_REFRESH_EXPIRES_IN,
});
};
// Verify refresh token
export const verifyRefreshToken = (token: string): JwtPayload | null => {
try {
return jwt.verify(token, JWT_SECRET) as JwtPayload;
} catch {
return null;
}
};