-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
184 lines (158 loc) · 5.19 KB
/
server.js
File metadata and controls
184 lines (158 loc) · 5.19 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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const passport = require('passport');
require('dotenv').config();
// Import cleanup function
const { cleanupExpiredTokens } = require('./scripts/cleanupBlacklist');
// Import OAuth configuration
const { passport: oauthPassport } = require('./config/oauth');
const taskRoutes = require('./routes/tasks');
const projectRoutes = require('./routes/projects');
const uploadRoutes = require('./routes/uploads');
const healthRoutes = require('./routes/health');
const authRoutes = require('./routes/auth');
const feedbackRoutes = require('./routes/feedback');
const paymentRoutes = require('./routes/payments');
const clientRoutes = require('./routes/clients');
const invoiceRoutes = require('./routes/invoices');
const companyRoutes = require('./routes/companies');
const teamRoutes = require('./routes/teams');
const proposalTemplateRoutes = require('./routes/proposalTemplates');
const jobRoutes = require('./routes/jobs');
const dashboardRoutes = require('./routes/dashboard');
const app = express();
const PORT = process.env.PORT || 3001;
// MongoDB Connection
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
// Handle connection events
mongoose.connection.on('error', (err) => {
// Connection error logged silently in production
});
mongoose.connection.on('disconnected', () => {
// MongoDB disconnected
});
// Graceful shutdown
process.on('SIGINT', async () => {
await mongoose.connection.close();
process.exit(0);
});
} catch (error) {
// Connection error logged silently in production
process.exit(1);
}
};
// Initialize Passport
app.use(passport.initialize());
// Middleware
app.use(helmet());
// Allow configuration via env var, fallback to common defaults
const allowedOrigins = (process.env.ALLOWED_ORIGINS)
.split(',');
app.use(cors({
origin: function (origin, callback) {
// Allow requests with no origin (like mobile apps or curl requests)
if (!origin) return callback(null, true);
if (allowedOrigins.includes(origin)) {
return callback(null, true);
} else {
return callback(null, true); // Allow all origins for development
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Cache-Control', 'Pragma']
}));
app.use(morgan('combined'));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));
// Routes
app.use('/api/tasks', taskRoutes);
app.use('/api/projects', projectRoutes);
app.use('/api/uploads', uploadRoutes);
app.use('/api/health', healthRoutes);
app.use('/api/auth', authRoutes);
app.use('/api/feedback', feedbackRoutes);
app.use('/api/payments', paymentRoutes);
app.use('/api/clients', clientRoutes);
app.use('/api/invoices', invoiceRoutes);
app.use('/api/companies', companyRoutes);
app.use('/api/teams', teamRoutes);
app.use('/api/proposal-templates', proposalTemplateRoutes);
app.use('/api/jobs', jobRoutes);
app.use('/api/dashboard', dashboardRoutes);
// Error handling middleware
app.use((err, req, res, next) => {
// Server error logged silently in production
// Don't expose internal error details in production
const isProduction = process.env.NODE_ENV === 'production';
// Handle specific error types
if (err.name === 'ValidationError') {
return res.status(400).json({
error: 'Validation Error',
message: 'Please check your input data and try again.'
});
}
if (err.name === 'CastError') {
return res.status(400).json({
error: 'Invalid Data',
message: 'The provided data is not in the correct format.'
});
}
if (err.code === 11000) {
return res.status(409).json({
error: 'Duplicate Entry',
message: 'This item already exists.'
});
}
// Generic error response
res.status(500).json({
error: 'Server Error',
message: isProduction
? 'Something went wrong. Please try again later.'
: err.message
});
});
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({ error: 'Route not found' });
});
// Start server
const startServer = async () => {
try {
// Connect to MongoDB first
await connectDB();
// Then start the server
app.listen(PORT, () => {
// Server started successfully
});
// Start periodic cleanup of expired tokens (every 6 hours)
setInterval(async () => {
try {
await cleanupExpiredTokens();
} catch (error) {
// Cleanup error logged silently in production
}
}, 6 * 60 * 60 * 1000); // 6 hours
// Run initial cleanup
setTimeout(async () => {
try {
await cleanupExpiredTokens();
} catch (error) {
// Cleanup error logged silently in production
}
}, 60000); // Run after 1 minute
} catch (error) {
// Server startup error logged silently in production
process.exit(1);
}
};
startServer();