-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
159 lines (124 loc) · 5.09 KB
/
Copy pathserver.js
File metadata and controls
159 lines (124 loc) · 5.09 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 express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const mongoose = require('mongoose');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const MONGO_URI = '';
const JWT_SECRET = '';
const PORT = 3000;
const app = express();
app.use(express.json());
app.use(cors());
const userSchema = new mongoose.Schema({
username: { type: String, unique: true, required: true },
passwordHash: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
const onlineSessionSchema = new mongoose.Schema({
username: { type: String, required: true },
token: { type: String, required: true },
lastHeartbeat: { type: Date, default: Date.now }
});
const OnlineSession = mongoose.model('OnlineSession', onlineSessionSchema);
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 10,
message: { error: 'تعداد درخواستهای مجاز تمام شده. کمی صبر کنید.' }
});
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.status(401).json({ error: 'توکن یافت نشد' });
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) return res.status(403).json({ error: 'توکن نامعتبر' });
req.user = user;
next();
});
}
app.post('/api/register', async (req, res) => {
const { username, password } = req.body;
if (!username || !password) return res.status(400).json({ error: 'نام کاربری و رمز عبور الزامی است' });
const existingUser = await User.findOne({ username });
if (existingUser) return res.status(409).json({ error: 'این نام کاربری قبلاً ثبت شده است' });
const passwordHash = await bcrypt.hash(password, 10);
const user = new User({ username, passwordHash });
await user.save();
res.status(201).json({ message: 'کاربر با موفقیت ایجاد شد' });
});
app.post('/api/login', loginLimiter, async (req, res) => {
const { username, password } = req.body;
if (!username || !password) return res.status(400).json({ error: 'نام کاربری و رمز عبور را وارد کنید' });
const user = await User.findOne({ username });
if (!user) return res.status(401).json({ error: 'نام کاربری یا رمز عبور اشتباه است' });
const match = await bcrypt.compare(password, user.passwordHash);
if (!match) return res.status(401).json({ error: 'نام کاربری یا رمز عبور اشتباه است' });
const token = jwt.sign({ username: user.username }, JWT_SECRET, { expiresIn: '7d' });
await OnlineSession.findOneAndUpdate(
{ username: user.username },
{ token, lastHeartbeat: new Date() },
{ upsert: true, new: true }
);
res.json({ success: true, token, username: user.username });
});
app.get('/api/auth-check', authenticateToken, (req, res) => {
res.json({ isAuthenticated: true, username: req.user.username });
});
app.post('/api/logout', authenticateToken, async (req, res) => {
await OnlineSession.deleteOne({ username: req.user.username });
res.json({ success: true });
});
app.post('/api/heartbeat', authenticateToken, async (req, res) => {
await OnlineSession.findOneAndUpdate(
{ username: req.user.username },
{ lastHeartbeat: new Date() }
);
res.json({ success: true });
});
app.get('/api/online-count', async (req, res) => {
const onlineThreshold = new Date(Date.now() - 5 * 60 * 1000); // ۵ دقیقه
const count = await OnlineSession.countDocuments({ lastHeartbeat: { $gte: onlineThreshold } });
res.json({ online: count });
});
async function connectDB(retries = 5, delay = 5000) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
await mongoose.connect(MONGO_URI, {
serverSelectionTimeoutMS: 5000
});
console.log('✅ MongoDB connected');
return true;
} catch (err) {
console.error(`❌ Attempt ${attempt}/${retries} - MongoDB connection error: ${err.message}`);
if (attempt < retries) {
console.log(`🔄 Retrying in ${delay / 1000}s...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
return false;
}
mongoose.connection.on('disconnected', () => {
console.warn('⚠️ MongoDB disconnected');
});
mongoose.connection.on('error', (err) => {
console.error('❌ MongoDB runtime error:', err.message);
});
async function startServer() {
const dbConnected = await connectDB();
if (!dbConnected) {
console.error('🚨 SERVER START FAILED: No database connection');
return { success: false, error: 'Database connection failed' };
}
return new Promise((resolve) => {
const server = app.listen(PORT, () => {
console.log(`🚀 Server running on port ${PORT}`);
resolve({ success: true, server });
}).on('error', (err) => {
console.error('❌ Express server error:', err.message);
resolve({ success: false, error: err.message });
});
});
}
module.exports = { startServer, app };