Mark-Lasfar commited on
Commit ยท
fe9d07e
1
Parent(s): 57b7b7d
Add GraphQL
Browse files
server.js
CHANGED
|
@@ -1465,7 +1465,10 @@ const postSchema = new mongoose.Schema({
|
|
| 1465 |
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
|
| 1466 |
text: String,
|
| 1467 |
createdAt: { type: Date, default: Date.now },
|
| 1468 |
-
likes: [{
|
|
|
|
|
|
|
|
|
|
| 1469 |
edited: { type: Boolean, default: false },
|
| 1470 |
editedAt: Date,
|
| 1471 |
|
|
@@ -1476,30 +1479,36 @@ const postSchema = new mongoose.Schema({
|
|
| 1476 |
}],
|
| 1477 |
reportCount: { type: Number, default: 0 },
|
| 1478 |
hidden: { type: Boolean, default: false },
|
| 1479 |
-
|
| 1480 |
-
|
| 1481 |
-
|
| 1482 |
-
|
| 1483 |
-
|
| 1484 |
-
|
| 1485 |
-
|
| 1486 |
-
|
| 1487 |
-
|
| 1488 |
-
|
| 1489 |
-
|
| 1490 |
-
|
| 1491 |
-
|
| 1492 |
-
|
| 1493 |
-
|
| 1494 |
-
|
| 1495 |
-
|
| 1496 |
-
|
| 1497 |
-
|
| 1498 |
-
|
| 1499 |
-
|
| 1500 |
-
|
| 1501 |
-
|
| 1502 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1503 |
|
| 1504 |
|
| 1505 |
}],
|
|
@@ -1537,6 +1546,7 @@ const postSchema = new mongoose.Schema({
|
|
| 1537 |
updatedAt: { type: Date, default: Date.now }
|
| 1538 |
});
|
| 1539 |
|
|
|
|
| 1540 |
// Indexes for performance
|
| 1541 |
postSchema.index({ userId: 1, createdAt: -1 });
|
| 1542 |
postSchema.index({ createdAt: -1 });
|
|
@@ -1976,8 +1986,7 @@ const storySchema = new mongoose.Schema({
|
|
| 1976 |
},
|
| 1977 |
expiresAt: {
|
| 1978 |
type: Date,
|
| 1979 |
-
default: () => new Date(Date.now() + 24 * 60 * 60 * 1000)
|
| 1980 |
-
index: true
|
| 1981 |
}
|
| 1982 |
});
|
| 1983 |
|
|
@@ -2275,8 +2284,7 @@ const storeSettingsSchema = new mongoose.Schema({
|
|
| 2275 |
},
|
| 2276 |
enabled: {
|
| 2277 |
type: Boolean,
|
| 2278 |
-
default: false
|
| 2279 |
-
index: true
|
| 2280 |
},
|
| 2281 |
|
| 2282 |
// ============================================
|
|
@@ -7038,10 +7046,31 @@ app.use('/api/login', loginLimiter);
|
|
| 7038 |
async function generateTokens(user) {
|
| 7039 |
user.refreshTokens = user.refreshTokens.filter(t => new Date(t.createdAt) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000));
|
| 7040 |
if (user.refreshTokens.length >= 10) {
|
| 7041 |
-
user.refreshTokens.shift();
|
| 7042 |
}
|
| 7043 |
-
|
| 7044 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7045 |
user.refreshTokens.push({ token: refreshToken, createdAt: new Date() });
|
| 7046 |
await user.save();
|
| 7047 |
return { token, refreshToken };
|
|
@@ -7470,6 +7499,9 @@ app.post('/api/upload', authenticateToken, upload.single('file'), async (req, re
|
|
| 7470 |
}
|
| 7471 |
});
|
| 7472 |
|
|
|
|
|
|
|
|
|
|
| 7473 |
app.post('/api/login', [
|
| 7474 |
body('email').isEmail().withMessage('Invalid email format'),
|
| 7475 |
body('password').notEmpty().withMessage('Password is required')
|
|
@@ -7487,8 +7519,25 @@ app.post('/api/login', [
|
|
| 7487 |
|
| 7488 |
// โ
ูู ูุงู ุฃุฏู
ู โ ุชุฎุทู OTP
|
| 7489 |
if (user.isAdmin) {
|
| 7490 |
-
const token = jwt.sign(
|
| 7491 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7492 |
user.refreshTokens.push({ token: refreshToken });
|
| 7493 |
await user.save();
|
| 7494 |
logger.info(`Admin login: ${email} - Token issued without OTP`);
|
|
@@ -7502,7 +7551,6 @@ app.post('/api/login', [
|
|
| 7502 |
user.otpExpires = Date.now() + 10 * 60 * 1000;
|
| 7503 |
await user.save();
|
| 7504 |
|
| 7505 |
-
// ูุฑุณู ุงูู response ุนูู ุทูู
|
| 7506 |
res.status(403).json({
|
| 7507 |
error: 'Email not verified',
|
| 7508 |
requiresVerification: true,
|
|
@@ -7510,7 +7558,6 @@ app.post('/api/login', [
|
|
| 7510 |
message: 'Verification code sent to your email'
|
| 7511 |
});
|
| 7512 |
|
| 7513 |
-
// ุฅุฑุณุงู ุงูุฅูู
ูู ุนุจุฑ Brevo ูู ุงูุฎูููุฉ
|
| 7514 |
setTimeout(async () => {
|
| 7515 |
try {
|
| 7516 |
await sendEmailWithBrevo(
|
|
@@ -7540,8 +7587,25 @@ app.post('/api/login', [
|
|
| 7540 |
}
|
| 7541 |
|
| 7542 |
// โ
ุงูุฅูู
ูู ู
ูุนู โ ุชููู ู
ุจุงุดุฑ
|
| 7543 |
-
const token = jwt.sign(
|
| 7544 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7545 |
user.refreshTokens.push({ token: refreshToken });
|
| 7546 |
await user.save();
|
| 7547 |
res.json({ token, refreshToken });
|
|
@@ -7552,6 +7616,7 @@ app.post('/api/login', [
|
|
| 7552 |
}
|
| 7553 |
});
|
| 7554 |
|
|
|
|
| 7555 |
// ============================================
|
| 7556 |
// POST /api/verify-email - ุงูุชุญูู ู
ู ุงูุจุฑูุฏ ุงูุฅููุชุฑููู
|
| 7557 |
// ============================================
|
|
@@ -7572,15 +7637,30 @@ app.post('/api/verify-email', [
|
|
| 7572 |
return res.status(400).json({ error: 'Invalid or expired verification code' });
|
| 7573 |
}
|
| 7574 |
|
| 7575 |
-
// โ
ุชูุนูู ุงูุญุณุงุจ
|
| 7576 |
user.emailVerified = true;
|
| 7577 |
user.otp = null;
|
| 7578 |
user.otpExpires = null;
|
| 7579 |
await user.save();
|
| 7580 |
|
| 7581 |
-
|
| 7582 |
-
|
| 7583 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7584 |
user.refreshTokens.push({ token: refreshToken });
|
| 7585 |
await user.save();
|
| 7586 |
|
|
@@ -7683,8 +7763,25 @@ app.post('/api/login/verify-otp', otpVerifyLimiter, async (req, res) => {
|
|
| 7683 |
if (!user) {
|
| 7684 |
return res.status(401).json({ error: 'Invalid or expired OTP' });
|
| 7685 |
}
|
| 7686 |
-
const token = jwt.sign(
|
| 7687 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7688 |
user.refreshTokens.push({ token: refreshToken });
|
| 7689 |
user.otp = null;
|
| 7690 |
user.otpExpires = null;
|
|
|
|
| 1465 |
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
|
| 1466 |
text: String,
|
| 1467 |
createdAt: { type: Date, default: Date.now },
|
| 1468 |
+
likes: [{
|
| 1469 |
+
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
|
| 1470 |
+
likedAt: { type: Date, default: Date.now }
|
| 1471 |
+
}],
|
| 1472 |
edited: { type: Boolean, default: false },
|
| 1473 |
editedAt: Date,
|
| 1474 |
|
|
|
|
| 1479 |
}],
|
| 1480 |
reportCount: { type: Number, default: 0 },
|
| 1481 |
hidden: { type: Boolean, default: false },
|
| 1482 |
+
replies: [{
|
| 1483 |
+
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
|
| 1484 |
+
text: String,
|
| 1485 |
+
createdAt: { type: Date, default: Date.now },
|
| 1486 |
+
likes: [{
|
| 1487 |
+
userId: {
|
| 1488 |
+
type: mongoose.Schema.Types.ObjectId,
|
| 1489 |
+
ref: 'User'
|
| 1490 |
+
},
|
| 1491 |
+
likedAt: {
|
| 1492 |
+
type: Date,
|
| 1493 |
+
default: Date.now
|
| 1494 |
+
}
|
| 1495 |
+
}],
|
| 1496 |
+
edited: { type: Boolean, default: false },
|
| 1497 |
+
editedAt: Date,
|
| 1498 |
+
reports: [{
|
| 1499 |
+
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
|
| 1500 |
+
reason: { type: String, default: 'No reason provided' },
|
| 1501 |
+
reportedAt: { type: Date, default: Date.now }
|
| 1502 |
+
}],
|
| 1503 |
+
reportCount: { type: Number, default: 0 },
|
| 1504 |
+
hidden: { type: Boolean, default: false },
|
| 1505 |
+
|
| 1506 |
+
// โ
โ
โ
ุงูู
ูุชุงุญ: ุงูุฑุฏูุฏ ุชุญุช ุงูุฑุฏูุฏ (Self-Referencing)
|
| 1507 |
+
replies: {
|
| 1508 |
+
type: Array,
|
| 1509 |
+
default: []
|
| 1510 |
+
}
|
| 1511 |
+
}]
|
| 1512 |
|
| 1513 |
|
| 1514 |
}],
|
|
|
|
| 1546 |
updatedAt: { type: Date, default: Date.now }
|
| 1547 |
});
|
| 1548 |
|
| 1549 |
+
|
| 1550 |
// Indexes for performance
|
| 1551 |
postSchema.index({ userId: 1, createdAt: -1 });
|
| 1552 |
postSchema.index({ createdAt: -1 });
|
|
|
|
| 1986 |
},
|
| 1987 |
expiresAt: {
|
| 1988 |
type: Date,
|
| 1989 |
+
default: () => new Date(Date.now() + 24 * 60 * 60 * 1000)
|
|
|
|
| 1990 |
}
|
| 1991 |
});
|
| 1992 |
|
|
|
|
| 2284 |
},
|
| 2285 |
enabled: {
|
| 2286 |
type: Boolean,
|
| 2287 |
+
default: false
|
|
|
|
| 2288 |
},
|
| 2289 |
|
| 2290 |
// ============================================
|
|
|
|
| 7046 |
async function generateTokens(user) {
|
| 7047 |
user.refreshTokens = user.refreshTokens.filter(t => new Date(t.createdAt) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000));
|
| 7048 |
if (user.refreshTokens.length >= 10) {
|
| 7049 |
+
user.refreshTokens.shift();
|
| 7050 |
}
|
| 7051 |
+
|
| 7052 |
+
// โ
โ
โ
ุฃุถู username ู profile.nickname ูู ุงูู JWT
|
| 7053 |
+
const token = jwt.sign(
|
| 7054 |
+
{
|
| 7055 |
+
userId: user._id,
|
| 7056 |
+
isAdmin: user.isAdmin,
|
| 7057 |
+
username: user.username,
|
| 7058 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7059 |
+
},
|
| 7060 |
+
JWT_SECRET,
|
| 7061 |
+
{ expiresIn: '30d' }
|
| 7062 |
+
);
|
| 7063 |
+
|
| 7064 |
+
const refreshToken = jwt.sign(
|
| 7065 |
+
{
|
| 7066 |
+
userId: user._id,
|
| 7067 |
+
username: user.username,
|
| 7068 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7069 |
+
},
|
| 7070 |
+
process.env.REFRESH_TOKEN_SECRET,
|
| 7071 |
+
{ expiresIn: '90d' }
|
| 7072 |
+
);
|
| 7073 |
+
|
| 7074 |
user.refreshTokens.push({ token: refreshToken, createdAt: new Date() });
|
| 7075 |
await user.save();
|
| 7076 |
return { token, refreshToken };
|
|
|
|
| 7499 |
}
|
| 7500 |
});
|
| 7501 |
|
| 7502 |
+
// ============================================
|
| 7503 |
+
// POST /api/login
|
| 7504 |
+
// ============================================
|
| 7505 |
app.post('/api/login', [
|
| 7506 |
body('email').isEmail().withMessage('Invalid email format'),
|
| 7507 |
body('password').notEmpty().withMessage('Password is required')
|
|
|
|
| 7519 |
|
| 7520 |
// โ
ูู ูุงู ุฃุฏู
ู โ ุชุฎุทู OTP
|
| 7521 |
if (user.isAdmin) {
|
| 7522 |
+
const token = jwt.sign(
|
| 7523 |
+
{
|
| 7524 |
+
userId: user._id,
|
| 7525 |
+
isAdmin: user.isAdmin,
|
| 7526 |
+
username: user.username,
|
| 7527 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7528 |
+
},
|
| 7529 |
+
JWT_SECRET,
|
| 7530 |
+
{ expiresIn: '30d' }
|
| 7531 |
+
);
|
| 7532 |
+
const refreshToken = jwt.sign(
|
| 7533 |
+
{
|
| 7534 |
+
userId: user._id,
|
| 7535 |
+
username: user.username,
|
| 7536 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7537 |
+
},
|
| 7538 |
+
JWT_SECRET,
|
| 7539 |
+
{ expiresIn: '90d' }
|
| 7540 |
+
);
|
| 7541 |
user.refreshTokens.push({ token: refreshToken });
|
| 7542 |
await user.save();
|
| 7543 |
logger.info(`Admin login: ${email} - Token issued without OTP`);
|
|
|
|
| 7551 |
user.otpExpires = Date.now() + 10 * 60 * 1000;
|
| 7552 |
await user.save();
|
| 7553 |
|
|
|
|
| 7554 |
res.status(403).json({
|
| 7555 |
error: 'Email not verified',
|
| 7556 |
requiresVerification: true,
|
|
|
|
| 7558 |
message: 'Verification code sent to your email'
|
| 7559 |
});
|
| 7560 |
|
|
|
|
| 7561 |
setTimeout(async () => {
|
| 7562 |
try {
|
| 7563 |
await sendEmailWithBrevo(
|
|
|
|
| 7587 |
}
|
| 7588 |
|
| 7589 |
// โ
ุงูุฅูู
ูู ู
ูุนู โ ุชููู ู
ุจุงุดุฑ
|
| 7590 |
+
const token = jwt.sign(
|
| 7591 |
+
{
|
| 7592 |
+
userId: user._id,
|
| 7593 |
+
isAdmin: user.isAdmin,
|
| 7594 |
+
username: user.username,
|
| 7595 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7596 |
+
},
|
| 7597 |
+
JWT_SECRET,
|
| 7598 |
+
{ expiresIn: '30d' }
|
| 7599 |
+
);
|
| 7600 |
+
const refreshToken = jwt.sign(
|
| 7601 |
+
{
|
| 7602 |
+
userId: user._id,
|
| 7603 |
+
username: user.username,
|
| 7604 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7605 |
+
},
|
| 7606 |
+
JWT_SECRET,
|
| 7607 |
+
{ expiresIn: '90d' }
|
| 7608 |
+
);
|
| 7609 |
user.refreshTokens.push({ token: refreshToken });
|
| 7610 |
await user.save();
|
| 7611 |
res.json({ token, refreshToken });
|
|
|
|
| 7616 |
}
|
| 7617 |
});
|
| 7618 |
|
| 7619 |
+
|
| 7620 |
// ============================================
|
| 7621 |
// POST /api/verify-email - ุงูุชุญูู ู
ู ุงูุจุฑูุฏ ุงูุฅููุชุฑููู
|
| 7622 |
// ============================================
|
|
|
|
| 7637 |
return res.status(400).json({ error: 'Invalid or expired verification code' });
|
| 7638 |
}
|
| 7639 |
|
|
|
|
| 7640 |
user.emailVerified = true;
|
| 7641 |
user.otp = null;
|
| 7642 |
user.otpExpires = null;
|
| 7643 |
await user.save();
|
| 7644 |
|
| 7645 |
+
const token = jwt.sign(
|
| 7646 |
+
{
|
| 7647 |
+
userId: user._id,
|
| 7648 |
+
isAdmin: user.isAdmin,
|
| 7649 |
+
username: user.username,
|
| 7650 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7651 |
+
},
|
| 7652 |
+
JWT_SECRET,
|
| 7653 |
+
{ expiresIn: '30d' }
|
| 7654 |
+
);
|
| 7655 |
+
const refreshToken = jwt.sign(
|
| 7656 |
+
{
|
| 7657 |
+
userId: user._id,
|
| 7658 |
+
username: user.username,
|
| 7659 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7660 |
+
},
|
| 7661 |
+
process.env.REFRESH_TOKEN_SECRET,
|
| 7662 |
+
{ expiresIn: '90d' }
|
| 7663 |
+
);
|
| 7664 |
user.refreshTokens.push({ token: refreshToken });
|
| 7665 |
await user.save();
|
| 7666 |
|
|
|
|
| 7763 |
if (!user) {
|
| 7764 |
return res.status(401).json({ error: 'Invalid or expired OTP' });
|
| 7765 |
}
|
| 7766 |
+
const token = jwt.sign(
|
| 7767 |
+
{
|
| 7768 |
+
userId: user._id,
|
| 7769 |
+
isAdmin: user.isAdmin,
|
| 7770 |
+
username: user.username,
|
| 7771 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7772 |
+
},
|
| 7773 |
+
JWT_SECRET,
|
| 7774 |
+
{ expiresIn: '1h' }
|
| 7775 |
+
);
|
| 7776 |
+
const refreshToken = jwt.sign(
|
| 7777 |
+
{
|
| 7778 |
+
userId: user._id,
|
| 7779 |
+
username: user.username,
|
| 7780 |
+
'profile.nickname': user.profile?.nickname || user.username
|
| 7781 |
+
},
|
| 7782 |
+
JWT_SECRET,
|
| 7783 |
+
{ expiresIn: '7d' }
|
| 7784 |
+
);
|
| 7785 |
user.refreshTokens.push({ token: refreshToken });
|
| 7786 |
user.otp = null;
|
| 7787 |
user.otpExpires = null;
|