Skip to content

Commit 9659ef0

Browse files
committed
Optimize panel queries and runtime stability
1 parent 4b30b09 commit 9659ef0

5 files changed

Lines changed: 93 additions & 35 deletions

File tree

Caddyfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{$PANEL_DOMAIN} {
2+
encode gzip zstd
3+
24
reverse_proxy backend:3000 {
35
# Force HTTP/1.1 to backend (Express doesn't support h2c)
46
transport http {

index.js

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ app.use((req, res, next) => {
8282

8383
app.use(i18nMiddleware);
8484
app.use(countRequest);
85-
app.use(express.static(path.join(__dirname, 'public')));
85+
app.use(express.static(path.join(__dirname, 'public'), {
86+
maxAge: process.env.NODE_ENV === 'production' ? '1d' : 0,
87+
}));
8688

8789
// Sanitize error details from 500 responses in production
8890
app.use((req, res, next) => {
@@ -575,50 +577,78 @@ function setupWebSocketServer(server) {
575577
function setupCronJobs() {
576578
// Collect stats every 5 minutes
577579
cron.schedule('*/5 * * * *', async () => {
578-
logger.debug('[Cron] Collecting stats');
579-
await syncService.collectAllStats();
580-
581-
// Save stats snapshot for charts
582-
await statsService.saveHourlySnapshot();
580+
try {
581+
logger.debug('[Cron] Collecting stats');
582+
await syncService.collectAllStats();
583+
584+
// Save stats snapshot for charts
585+
await statsService.saveHourlySnapshot();
586+
} catch (error) {
587+
logger.error(`[Cron] Stats collection failed: ${error.message}`);
588+
}
583589
});
584590

585591
// Health check every minute
586592
cron.schedule('* * * * *', async () => {
587-
await syncService.healthCheck();
593+
try {
594+
await syncService.healthCheck();
595+
} catch (error) {
596+
logger.error(`[Cron] Health check failed: ${error.message}`);
597+
}
588598
});
589599

590600
// Save daily snapshot every hour
591601
cron.schedule('0 * * * *', async () => {
592-
logger.debug('[Cron] Saving daily stats snapshot');
593-
await statsService.saveDailySnapshot();
602+
try {
603+
logger.debug('[Cron] Saving daily stats snapshot');
604+
await statsService.saveDailySnapshot();
605+
} catch (error) {
606+
logger.error(`[Cron] Daily snapshot failed: ${error.message}`);
607+
}
594608
});
595609

596610
// Save monthly snapshot and cleanup at 00:05
597611
cron.schedule('5 0 * * *', async () => {
598-
logger.info('[Cron] Saving monthly stats snapshot');
599-
await statsService.saveMonthlySnapshot();
600-
await statsService.cleanup();
612+
try {
613+
logger.info('[Cron] Saving monthly stats snapshot');
614+
await statsService.saveMonthlySnapshot();
615+
await statsService.cleanup();
616+
} catch (error) {
617+
logger.error(`[Cron] Monthly maintenance failed: ${error.message}`);
618+
}
601619
});
602620

603621
// Clean old logs daily at 3:00
604622
cron.schedule('0 3 * * *', () => {
605-
logger.info('[Cron] Cleaning old logs');
606-
cleanOldLogs(30);
623+
try {
624+
logger.info('[Cron] Cleaning old logs');
625+
cleanOldLogs(30);
626+
} catch (error) {
627+
logger.error(`[Cron] Log cleanup failed: ${error.message}`);
628+
}
607629
});
608630

609631
// Check for scheduled backup every hour
610632
cron.schedule('0 * * * *', async () => {
611-
await backupService.scheduledBackup();
633+
try {
634+
await backupService.scheduledBackup();
635+
} catch (error) {
636+
logger.error(`[Cron] Scheduled backup failed: ${error.message}`);
637+
}
612638
});
613639

614640
// Initial health check and stats snapshot after 5 seconds
615641
setTimeout(async () => {
616-
logger.info('[Startup] Checking nodes status');
617-
await syncService.healthCheck();
618-
619-
// Initial stats snapshot
620-
await statsService.saveHourlySnapshot();
621-
logger.info('[Startup] Initial stats snapshot saved');
642+
try {
643+
logger.info('[Startup] Checking nodes status');
644+
await syncService.healthCheck();
645+
646+
// Initial stats snapshot
647+
await statsService.saveHourlySnapshot();
648+
logger.info('[Startup] Initial stats snapshot saved');
649+
} catch (error) {
650+
logger.error(`[Startup] Initial checks failed: ${error.message}`);
651+
}
622652
}, 5000);
623653
}
624654

src/models/hyUserModel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const hyUserSchema = new mongoose.Schema({
6969

7070
hyUserSchema.index({ enabled: 1 });
7171
hyUserSchema.index({ groups: 1 });
72+
hyUserSchema.index({ expireAt: 1 });
7273

7374
hyUserSchema.virtual('trafficUsedGB').get(function() {
7475
return ((this.traffic.tx + this.traffic.rx) / (1024 * 1024 * 1024)).toFixed(2);

src/models/serverGroupModel.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ const serverGroupSchema = new mongoose.Schema({
1313
subscriptionTitle: { type: String, default: '', trim: true },
1414
}, { timestamps: true });
1515

16+
serverGroupSchema.index({ active: 1 });
17+
1618
module.exports = mongoose.model('ServerGroup', serverGroupSchema);
1719

src/routes/panel.js

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -929,18 +929,35 @@ router.get('/users/:userId', requireAuth, async (req, res) => {
929929
router.get('/groups', requireAuth, async (req, res) => {
930930
try {
931931
const groups = await ServerGroup.find().sort({ name: 1 });
932-
933-
// Считаем количество нод и пользователей в каждой группе
934-
const groupsWithCounts = await Promise.all(groups.map(async (group) => {
935-
const [nodesCount, usersCount] = await Promise.all([
936-
HyNode.countDocuments({ groups: group._id }),
937-
HyUser.countDocuments({ groups: group._id }),
932+
933+
const groupIds = groups.map((group) => group._id);
934+
let nodeCountMap = new Map();
935+
let userCountMap = new Map();
936+
937+
if (groupIds.length > 0) {
938+
const [nodeCounts, userCounts] = await Promise.all([
939+
HyNode.aggregate([
940+
{ $match: { groups: { $in: groupIds } } },
941+
{ $unwind: '$groups' },
942+
{ $match: { groups: { $in: groupIds } } },
943+
{ $group: { _id: '$groups', count: { $sum: 1 } } },
944+
]),
945+
HyUser.aggregate([
946+
{ $match: { groups: { $in: groupIds } } },
947+
{ $unwind: '$groups' },
948+
{ $match: { groups: { $in: groupIds } } },
949+
{ $group: { _id: '$groups', count: { $sum: 1 } } },
950+
]),
938951
]);
939-
return {
940-
...group.toObject(),
941-
nodesCount,
942-
usersCount,
943-
};
952+
953+
nodeCountMap = new Map(nodeCounts.map((item) => [String(item._id), item.count]));
954+
userCountMap = new Map(userCounts.map((item) => [String(item._id), item.count]));
955+
}
956+
957+
const groupsWithCounts = groups.map((group) => ({
958+
...group.toObject(),
959+
nodesCount: nodeCountMap.get(String(group._id)) || 0,
960+
usersCount: userCountMap.get(String(group._id)) || 0,
944961
}));
945962

946963
render(res, 'groups', {
@@ -1198,11 +1215,17 @@ router.post('/settings/reset-traffic', requireAuth, async (req, res) => {
11981215

11991216
// Инвалидируем кэш всех пользователей
12001217
const users = await HyUser.find({}).select('userId subscriptionToken').lean();
1201-
for (const user of users) {
1202-
await cache.invalidateUser(user.userId);
1218+
const invalidateTasks = users.flatMap((user) => {
1219+
const tasks = [() => cache.invalidateUser(user.userId)];
12031220
if (user.subscriptionToken) {
1204-
await cache.invalidateSubscription(user.subscriptionToken);
1221+
tasks.push(() => cache.invalidateSubscription(user.subscriptionToken));
12051222
}
1223+
return tasks;
1224+
});
1225+
1226+
const BATCH_SIZE = 100;
1227+
for (let i = 0; i < invalidateTasks.length; i += BATCH_SIZE) {
1228+
await Promise.all(invalidateTasks.slice(i, i + BATCH_SIZE).map((task) => task()));
12061229
}
12071230

12081231
// Инвалидируем статистику

0 commit comments

Comments
 (0)