updateweb
This commit is contained in:
48
backend/src/@types/index.ts
Normal file
48
backend/src/@types/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Shared TypeScript type definitions for backend
|
||||
*
|
||||
* Purpose: Centralized type definitions used across controllers, services, and models
|
||||
* Ensures type safety and consistency throughout the backend codebase
|
||||
*/
|
||||
|
||||
// User types
|
||||
export interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
role: 'admin' | 'customer';
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface AuthPayload {
|
||||
userId: number;
|
||||
username: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
// Product types
|
||||
export interface Product {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
price: number;
|
||||
category: string;
|
||||
stock: number;
|
||||
images: string[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// API Response types
|
||||
export interface ApiResponse<T = any> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
// Request types (extends Express Request)
|
||||
export interface AuthRequest extends Request {
|
||||
user?: AuthPayload;
|
||||
}
|
||||
31
backend/src/config/app.ts
Normal file
31
backend/src/config/app.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Application Configuration
|
||||
*
|
||||
* Purpose: General application settings (port, CORS, JWT, rate limiting)
|
||||
* Loaded from environment variables with sensible defaults
|
||||
*/
|
||||
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
export const appConfig = {
|
||||
// Server
|
||||
port: parseInt(process.env.PORT || '3000'),
|
||||
env: process.env.NODE_ENV || 'development',
|
||||
|
||||
// JWT
|
||||
jwtSecret: process.env.JWT_SECRET || 'your-secret-key-change-in-production',
|
||||
jwtExpiresIn: process.env.JWT_EXPIRES_IN || '7d',
|
||||
|
||||
// CORS
|
||||
corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:5173',
|
||||
|
||||
// Upload limits
|
||||
maxFileSize: parseInt(process.env.MAX_FILE_SIZE || '5242880'), // 5MB default
|
||||
|
||||
// Rate limiting
|
||||
rateLimitWindow: 15 * 60 * 1000, // 15 minutes
|
||||
rateLimitMax: 100, // requests per window
|
||||
};
|
||||
|
||||
export default appConfig;
|
||||
31
backend/src/config/database.ts
Normal file
31
backend/src/config/database.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Database Configuration
|
||||
*
|
||||
* Purpose: Centralized database connection settings
|
||||
* Manages connection pooling, SSL, and environment-specific configs
|
||||
*/
|
||||
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
export const databaseConfig = {
|
||||
// Database connection
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: parseInt(process.env.DB_PORT || '5432'),
|
||||
database: process.env.DB_NAME || 'skyartshop',
|
||||
user: process.env.DB_USER || 'postgres',
|
||||
password: process.env.DB_PASSWORD || '',
|
||||
|
||||
// Connection pool settings
|
||||
pool: {
|
||||
min: 2,
|
||||
max: 10,
|
||||
idleTimeoutMillis: 30000,
|
||||
connectionTimeoutMillis: 2000,
|
||||
},
|
||||
|
||||
// SSL for production
|
||||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
|
||||
};
|
||||
|
||||
export default databaseConfig;
|
||||
3
backend/src/controllers/.gitkeep
Normal file
3
backend/src/controllers/.gitkeep
Normal file
@@ -0,0 +1,3 @@
|
||||
# Controllers go here
|
||||
# Each controller handles HTTP requests for a specific resource
|
||||
# Example: productController.ts, authController.ts, userController.ts
|
||||
24
backend/src/helpers/jwt.ts
Normal file
24
backend/src/helpers/jwt.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* JWT Helper Functions
|
||||
*
|
||||
* Purpose: Generate and verify JWT tokens for authentication
|
||||
* Centralized token logic for consistency
|
||||
*/
|
||||
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { appConfig } from '../config/app';
|
||||
import { AuthPayload } from '../@types';
|
||||
|
||||
export function generateToken(payload: AuthPayload): string {
|
||||
return jwt.sign(payload, appConfig.jwtSecret, {
|
||||
expiresIn: appConfig.jwtExpiresIn,
|
||||
});
|
||||
}
|
||||
|
||||
export function verifyToken(token: string): AuthPayload | null {
|
||||
try {
|
||||
return jwt.verify(token, appConfig.jwtSecret) as AuthPayload;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
34
backend/src/helpers/response.ts
Normal file
34
backend/src/helpers/response.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Response Helper Functions
|
||||
*
|
||||
* Purpose: Consistent API response formatting across all endpoints
|
||||
* Ensures all responses follow the same structure
|
||||
*/
|
||||
|
||||
import { Response } from 'express';
|
||||
import { ApiResponse } from '../@types';
|
||||
|
||||
export function sendSuccess<T>(res: Response, data: T, message?: string, statusCode = 200): void {
|
||||
const response: ApiResponse<T> = {
|
||||
success: true,
|
||||
data,
|
||||
...(message && { message }),
|
||||
};
|
||||
res.status(statusCode).json(response);
|
||||
}
|
||||
|
||||
export function sendError(res: Response, error: string, statusCode = 400): void {
|
||||
const response: ApiResponse = {
|
||||
success: false,
|
||||
error,
|
||||
};
|
||||
res.status(statusCode).json(response);
|
||||
}
|
||||
|
||||
export function sendCreated<T>(res: Response, data: T, message = 'Resource created successfully'): void {
|
||||
sendSuccess(res, data, message, 201);
|
||||
}
|
||||
|
||||
export function sendNoContent(res: Response): void {
|
||||
res.status(204).send();
|
||||
}
|
||||
49
backend/src/middlewares/authenticate.ts
Normal file
49
backend/src/middlewares/authenticate.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Authentication Middleware
|
||||
*
|
||||
* Purpose: Verify JWT tokens and attach user info to requests
|
||||
* Applied to protected routes that require user authentication
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { appConfig } from '../config/app';
|
||||
import { AuthPayload } from '../@types';
|
||||
|
||||
export interface AuthRequest extends Request {
|
||||
user?: AuthPayload;
|
||||
}
|
||||
|
||||
export function authenticate(req: AuthRequest, res: Response, next: NextFunction): void {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
res.status(401).json({ success: false, error: 'No token provided' });
|
||||
return;
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
|
||||
|
||||
const decoded = jwt.verify(token, appConfig.jwtSecret) as AuthPayload;
|
||||
req.user = decoded;
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
res.status(401).json({ success: false, error: 'Invalid or expired token' });
|
||||
}
|
||||
}
|
||||
|
||||
export function isAdmin(req: AuthRequest, res: Response, next: NextFunction): void {
|
||||
if (!req.user) {
|
||||
res.status(401).json({ success: false, error: 'Authentication required' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.user.role !== 'admin') {
|
||||
res.status(403).json({ success: false, error: 'Admin access required' });
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
45
backend/src/middlewares/errorHandler.ts
Normal file
45
backend/src/middlewares/errorHandler.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Global Error Handler Middleware
|
||||
*
|
||||
* Purpose: Catch all errors, log them, and return consistent error responses
|
||||
* Applied as the last middleware in the Express chain
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { appConfig } from '../config/app';
|
||||
|
||||
export class AppError extends Error {
|
||||
constructor(
|
||||
public statusCode: number,
|
||||
public message: string,
|
||||
public isOperational = true
|
||||
) {
|
||||
super(message);
|
||||
Object.setPrototypeOf(this, AppError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
export function errorHandler(
|
||||
err: Error | AppError,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): void {
|
||||
console.error('Error:', err);
|
||||
|
||||
if (err instanceof AppError) {
|
||||
res.status(err.statusCode).json({
|
||||
success: false,
|
||||
error: err.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Unexpected errors
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: appConfig.env === 'production'
|
||||
? 'Internal server error'
|
||||
: err.message,
|
||||
});
|
||||
}
|
||||
22
backend/src/middlewares/requestLogger.ts
Normal file
22
backend/src/middlewares/requestLogger.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Request Logger Middleware
|
||||
*
|
||||
* Purpose: Log all incoming requests with method, path, IP, and response time
|
||||
* Useful for debugging and monitoring API usage
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
export function requestLogger(req: Request, res: Response, next: NextFunction): void {
|
||||
const start = Date.now();
|
||||
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - start;
|
||||
const { method, originalUrl, ip } = req;
|
||||
const { statusCode } = res;
|
||||
|
||||
console.log(`[${new Date().toISOString()}] ${method} ${originalUrl} - ${statusCode} - ${duration}ms - ${ip}`);
|
||||
});
|
||||
|
||||
next();
|
||||
}
|
||||
3
backend/src/models/.gitkeep
Normal file
3
backend/src/models/.gitkeep
Normal file
@@ -0,0 +1,3 @@
|
||||
# Models/Repositories go here
|
||||
# Database access layer and query methods
|
||||
# Example: Product.ts, User.ts, Order.ts
|
||||
3
backend/src/routes/.gitkeep
Normal file
3
backend/src/routes/.gitkeep
Normal file
@@ -0,0 +1,3 @@
|
||||
# Route definitions go here
|
||||
# Maps URLs to controllers and applies middleware
|
||||
# Example: products.ts, auth.ts, users.ts
|
||||
62
backend/src/server.ts
Normal file
62
backend/src/server.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Backend Entry Point
|
||||
*
|
||||
* Purpose: Initialize Express app, apply middleware, mount routes, start server
|
||||
* This is where the entire backend application comes together
|
||||
*/
|
||||
|
||||
import express, { Application } from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import compression from 'compression';
|
||||
import { appConfig } from './config/app';
|
||||
import { requestLogger } from './middlewares/requestLogger';
|
||||
import { errorHandler } from './middlewares/errorHandler';
|
||||
|
||||
// Initialize Express app
|
||||
const app: Application = express();
|
||||
|
||||
// Security middleware
|
||||
app.use(helmet());
|
||||
app.use(cors({ origin: appConfig.corsOrigin }));
|
||||
|
||||
// Body parsing
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
||||
|
||||
// Compression
|
||||
app.use(compression());
|
||||
|
||||
// Request logging
|
||||
app.use(requestLogger);
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Server is running',
|
||||
environment: appConfig.env,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
|
||||
// API Routes
|
||||
// TODO: Import and mount your route files here
|
||||
// Example:
|
||||
// import authRoutes from './routes/auth';
|
||||
// import productRoutes from './routes/products';
|
||||
// app.use('/api/auth', authRoutes);
|
||||
// app.use('/api/products', productRoutes);
|
||||
|
||||
// Error handling (must be last)
|
||||
app.use(errorHandler);
|
||||
|
||||
// Start server
|
||||
const PORT = appConfig.port;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🚀 Backend server running on http://localhost:${PORT}`);
|
||||
console.log(`📝 Environment: ${appConfig.env}`);
|
||||
console.log(`🔗 CORS enabled for: ${appConfig.corsOrigin}`);
|
||||
});
|
||||
|
||||
export default app;
|
||||
3
backend/src/services/.gitkeep
Normal file
3
backend/src/services/.gitkeep
Normal file
@@ -0,0 +1,3 @@
|
||||
# Services go here
|
||||
# Contains business logic, data processing, and orchestration
|
||||
# Example: productService.ts, authService.ts, emailService.ts
|
||||
54
backend/src/validators/productValidator.ts
Normal file
54
backend/src/validators/productValidator.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Product Validation Schemas
|
||||
*
|
||||
* Purpose: Validate product-related request data before it reaches controllers
|
||||
* Prevents invalid data from entering the system
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
export const createProductSchema = z.object({
|
||||
name: z.string().min(1).max(200),
|
||||
description: z.string().min(10).max(5000),
|
||||
price: z.number().positive(),
|
||||
category: z.string().min(1),
|
||||
stock: z.number().int().nonnegative(),
|
||||
images: z.array(z.string().url()).optional(),
|
||||
});
|
||||
|
||||
export const updateProductSchema = createProductSchema.partial();
|
||||
|
||||
export function validateCreateProduct(req: Request, res: Response, next: NextFunction): void {
|
||||
try {
|
||||
createProductSchema.parse(req.body);
|
||||
next();
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Validation failed',
|
||||
details: error.errors,
|
||||
});
|
||||
return;
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateUpdateProduct(req: Request, res: Response, next: NextFunction): void {
|
||||
try {
|
||||
updateProductSchema.parse(req.body);
|
||||
next();
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Validation failed',
|
||||
details: error.errors,
|
||||
});
|
||||
return;
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user