Repository navigation
Expand file tree
/
Copy pathserver.js
More file actions
159 lines (137 loc) · 4.86 KB
/
Copy pathserver.js
File metadata and controls
159 lines (137 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
const dns = require("dns");
dns.setServers(["8.8.8.8", "1.1.1.1"]);
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const path = require("path");
const helmet = require("helmet");
const rateLimit = require("express-rate-limit");
const morgan = require("morgan");
require("dotenv").config({ path: path.join(__dirname, ".env") });
// Swagger
const swaggerUi = require("swagger-ui-express");
const swaggerSpec = require("./src/config/swagger");
// Import routes
const authRoutes = require("./src/modules/auth/auth.routes");
const usersRoutes = require("./src/modules/users/user.routes");
const blogsRoutes = require("./src/modules/blogs/blog.routes");
const categoriesRoutes = require("./src/modules/categories/category.routes");
const uploadRoutes = require("./src/modules/upload/upload.routes");
const commentsRoutes = require("./src/modules/comments/comment.routes");
const notificationsRoutes = require("./src/modules/notifications/notification.routes");
const app = express();
app.set("trust proxy", 1);
// HTTP Request Logger Middleware
app.use(morgan("dev")); // Logs requests to the console (e.g., GET /health 200 15ms)
// ── Security Middleware ──
// Helmet — sets 11 security HTTP headers (XSS, clickjacking, MIME sniffing, etc.)
app.use(
helmet({
crossOriginOpenerPolicy: { policy: "same-origin-allow-popups" },
})
);
// CORS — only allow your own frontend origins (set ALLOWED_ORIGINS in .env)
const allowedOrigins = process.env.ALLOWED_ORIGINS
? process.env.ALLOWED_ORIGINS.split(",").map((o) => o.trim())
: [];
app.use(
cors({
origin: (origin, callback) => {
// Allow requests with no origin (curl, Postman, mobile apps, server-to-server)
// and allow localhost for development
if (
!origin ||
allowedOrigins.includes(origin) ||
origin.startsWith("http://localhost:") ||
origin.startsWith("http://127.0.0.1:")
) {
callback(null, true);
} else {
console.error("CORS Error: Origin not allowed -", origin);
callback(new Error("Not allowed by CORS"));
}
},
credentials: true,
})
);
// Rate limiting — general (100 req / 15 min per IP)
const generalLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 1000,
standardHeaders: true,
legacyHeaders: false,
message: { success: false, message: "Too many requests, please try again later." },
});
// Rate limiting — strict for auth routes (10 req / 15 min per IP)
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: false,
message: { success: false, message: "Too many login attempts, please try again later." },
});
app.use(generalLimiter);
// Body parsing — with size limits to prevent payload attacks
app.use(express.json({ limit: "10kb" }));
app.use(express.urlencoded({ extended: true, limit: "10kb" }));
// MongoDB Connection
const uri = process.env.MONGODB_URI;
if (uri) {
const safeUri = uri.replace(/:([^:@]{8,})@/, ':****@');
console.log("Connecting to MongoDB:", safeUri);
} else {
console.error("❌ MONGODB_URI is not defined in environment variables");
}
mongoose
.connect(uri)
.then(() => {
console.log("✅ Connected to MongoDB successfully");
})
.catch((error) => {
console.error("❌ MongoDB connection error:", error);
process.exit(1);
});
// Swagger API docs — available at /api-docs
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
customCss: '.swagger-ui .topbar { display: none }',
customSiteTitle: "Blog Post API — Swagger Docs",
}));
// Routes — auth routes get the stricter rate limiter
app.use("/v1/auth", authLimiter, authRoutes);
app.use("/v1/users", usersRoutes);
app.use("/v1/blogs", blogsRoutes);
app.use("/v1/categories", categoriesRoutes);
app.use("/v1/upload", uploadRoutes);
app.use("/v1/comments", commentsRoutes);
app.use("/v1/notifications", notificationsRoutes);
// Health check route
app.get("/health", (req, res) => {
res.status(200).json({
success: true,
message: "Server is running and ci cd is working ",
timestamp: new Date().toISOString(),
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({
success: false,
message: "Route not found",
});
});
// Error handler — don't leak stack traces or internal details in production
app.use((err, req, res, next) => {
console.error("Error:", err);
const isProd = process.env.NODE_ENV === "production";
res.status(err.status || 500).json({
success: false,
message: isProd ? "Internal server error" : (err.message || "Internal server error"),
});
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`🚀 Server is running on port ${PORT}`);
console.log(`📍 Health check: http://localhost:${PORT}/health`);
console.log(`📚 Swagger docs: http://localhost:${PORT}/api-docs`);
});