Spaces:
Sleeping
Sleeping
Sasha commited on
Commit ·
adbcce3
1
Parent(s): 9fb93a9
perf: reduce egress by caching stats endpoints longer and limiting getChatMessages
Browse files- server/db.js +38 -27
- server/server.js +8 -8
server/db.js
CHANGED
|
@@ -1302,41 +1302,52 @@ export async function getChatMessages(streamId = null) {
|
|
| 1302 |
if (streamId) {
|
| 1303 |
return sqliteDb.prepare('SELECT message, username, display_name, timestamp FROM messages WHERE stream_id = ? AND username NOT IN (?, ?)').all(streamId, ...bots);
|
| 1304 |
} else {
|
| 1305 |
-
return sqliteDb.prepare('SELECT message, username, display_name, timestamp FROM messages WHERE username NOT IN (?, ?)').all(...bots);
|
| 1306 |
}
|
| 1307 |
} else {
|
| 1308 |
-
|
| 1309 |
-
|
| 1310 |
-
|
| 1311 |
-
|
| 1312 |
-
|
| 1313 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1314 |
.from('messages')
|
| 1315 |
.select('message, username, display_name, timestamp')
|
| 1316 |
.not('username', 'in', `(${bots.map(b => `"${b}"`).join(',')})`)
|
| 1317 |
-
.
|
| 1318 |
-
|
| 1319 |
-
|
| 1320 |
-
query = query.eq('stream_id', streamId);
|
| 1321 |
-
}
|
| 1322 |
-
|
| 1323 |
-
const { data, error } = await query;
|
| 1324 |
-
|
| 1325 |
if (error) {
|
| 1326 |
-
console.error('[Supabase] Error getting chat messages:', error);
|
| 1327 |
-
|
| 1328 |
-
}
|
| 1329 |
-
|
| 1330 |
-
if (data && data.length > 0) {
|
| 1331 |
-
allData = allData.concat(data);
|
| 1332 |
-
if (data.length < limit) break; // Fetched the last incomplete page
|
| 1333 |
-
from += limit;
|
| 1334 |
-
} else {
|
| 1335 |
-
break; // Empty page
|
| 1336 |
}
|
|
|
|
| 1337 |
}
|
| 1338 |
-
|
| 1339 |
-
return allData;
|
| 1340 |
}
|
| 1341 |
}
|
| 1342 |
|
|
|
|
| 1302 |
if (streamId) {
|
| 1303 |
return sqliteDb.prepare('SELECT message, username, display_name, timestamp FROM messages WHERE stream_id = ? AND username NOT IN (?, ?)').all(streamId, ...bots);
|
| 1304 |
} else {
|
| 1305 |
+
return sqliteDb.prepare('SELECT message, username, display_name, timestamp FROM messages WHERE username NOT IN (?, ?) ORDER BY timestamp DESC LIMIT 10000').all(...bots);
|
| 1306 |
}
|
| 1307 |
} else {
|
| 1308 |
+
if (streamId) {
|
| 1309 |
+
let allData = [];
|
| 1310 |
+
let from = 0;
|
| 1311 |
+
const limit = 1000;
|
| 1312 |
+
|
| 1313 |
+
while(true) {
|
| 1314 |
+
const { data, error } = await supabase
|
| 1315 |
+
.from('messages')
|
| 1316 |
+
.select('message, username, display_name, timestamp')
|
| 1317 |
+
.eq('stream_id', streamId)
|
| 1318 |
+
.not('username', 'in', `(${bots.map(b => `"${b}"`).join(',')})`)
|
| 1319 |
+
.range(from, from + limit - 1);
|
| 1320 |
+
|
| 1321 |
+
if (error) {
|
| 1322 |
+
console.error('[Supabase] Error getting chat messages:', error);
|
| 1323 |
+
break;
|
| 1324 |
+
}
|
| 1325 |
+
|
| 1326 |
+
if (data && data.length > 0) {
|
| 1327 |
+
allData = allData.concat(data);
|
| 1328 |
+
if (data.length < limit) break; // Fetched the last incomplete page
|
| 1329 |
+
from += limit;
|
| 1330 |
+
} else {
|
| 1331 |
+
break; // Empty page
|
| 1332 |
+
}
|
| 1333 |
+
}
|
| 1334 |
+
|
| 1335 |
+
return allData;
|
| 1336 |
+
} else {
|
| 1337 |
+
// Global query (All streams) - limit to 10,000 most recent messages to prevent huge egress
|
| 1338 |
+
const { data, error } = await supabase
|
| 1339 |
.from('messages')
|
| 1340 |
.select('message, username, display_name, timestamp')
|
| 1341 |
.not('username', 'in', `(${bots.map(b => `"${b}"`).join(',')})`)
|
| 1342 |
+
.order('timestamp', { ascending: false })
|
| 1343 |
+
.limit(10000);
|
| 1344 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1345 |
if (error) {
|
| 1346 |
+
console.error('[Supabase] Error getting global chat messages:', error);
|
| 1347 |
+
return [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1348 |
}
|
| 1349 |
+
return data || [];
|
| 1350 |
}
|
|
|
|
|
|
|
| 1351 |
}
|
| 1352 |
}
|
| 1353 |
|
server/server.js
CHANGED
|
@@ -623,13 +623,13 @@ app.get('/api/auth/logout', (req, res) => {
|
|
| 623 |
// =========================================================================
|
| 624 |
|
| 625 |
// Get list of streams
|
| 626 |
-
app.get('/api/streams', cacheMiddleware(), async (req, res) => {
|
| 627 |
const streams = await getStreamsList();
|
| 628 |
res.json(streams);
|
| 629 |
});
|
| 630 |
|
| 631 |
// Get top active chatters
|
| 632 |
-
app.get('/api/stats/chatters', cacheMiddleware(), async (req, res) => {
|
| 633 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 634 |
const limit = req.query.limit ? parseInt(req.query.limit) : 50;
|
| 635 |
|
|
@@ -638,7 +638,7 @@ app.get('/api/stats/chatters', cacheMiddleware(), async (req, res) => {
|
|
| 638 |
});
|
| 639 |
|
| 640 |
// Get true total summary statistics
|
| 641 |
-
app.get('/api/stats/summary', cacheMiddleware(), async (req, res) => {
|
| 642 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 643 |
try {
|
| 644 |
const summary = await getStatsSummary(streamId);
|
|
@@ -691,7 +691,7 @@ const WORD_MAPPINGS = {
|
|
| 691 |
};
|
| 692 |
|
| 693 |
// Get word frequencies (Voice vs Chat messages from streamer)
|
| 694 |
-
app.get('/api/stats/words', cacheMiddleware(), async (req, res) => {
|
| 695 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 696 |
const limit = req.query.limit ? parseInt(req.query.limit) : 50;
|
| 697 |
const type = req.query.type || 'voice'; // 'voice' or 'chat'
|
|
@@ -785,7 +785,7 @@ app.get('/api/stats/words', cacheMiddleware(), async (req, res) => {
|
|
| 785 |
});
|
| 786 |
|
| 787 |
// Get chat activity (messages count grouped by 5-minute intervals)
|
| 788 |
-
app.get('/api/stats/activity', cacheMiddleware(), async (req, res) => {
|
| 789 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 790 |
|
| 791 |
if (!streamId) {
|
|
@@ -823,7 +823,7 @@ app.get('/api/stats/activity', cacheMiddleware(), async (req, res) => {
|
|
| 823 |
// =========================================================================
|
| 824 |
|
| 825 |
// Get log of moderator actions
|
| 826 |
-
app.get('/api/stats/moderators', requireModeratorRole, async (req, res) => {
|
| 827 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 828 |
const limit = req.query.limit ? parseInt(req.query.limit) : 100;
|
| 829 |
|
|
@@ -832,7 +832,7 @@ app.get('/api/stats/moderators', requireModeratorRole, async (req, res) => {
|
|
| 832 |
});
|
| 833 |
|
| 834 |
// Get summary counts of actions per moderator
|
| 835 |
-
app.get('/api/stats/moderators/summary', requireModeratorRole, async (req, res) => {
|
| 836 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 837 |
|
| 838 |
const data = await getModActionsSummaryRaw(streamId);
|
|
@@ -863,7 +863,7 @@ app.get('/api/stats/moderators/summary', requireModeratorRole, async (req, res)
|
|
| 863 |
});
|
| 864 |
|
| 865 |
// GET aggregated profiles with scores for moderators
|
| 866 |
-
app.get('/api/stats/moderators/profiles', cacheMiddleware(), async (req, res) => {
|
| 867 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 868 |
const profiles = await getModeratorProfilesData(streamId);
|
| 869 |
res.json(profiles);
|
|
|
|
| 623 |
// =========================================================================
|
| 624 |
|
| 625 |
// Get list of streams
|
| 626 |
+
app.get('/api/streams', cacheMiddleware(120), async (req, res) => {
|
| 627 |
const streams = await getStreamsList();
|
| 628 |
res.json(streams);
|
| 629 |
});
|
| 630 |
|
| 631 |
// Get top active chatters
|
| 632 |
+
app.get('/api/stats/chatters', cacheMiddleware(300), async (req, res) => {
|
| 633 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 634 |
const limit = req.query.limit ? parseInt(req.query.limit) : 50;
|
| 635 |
|
|
|
|
| 638 |
});
|
| 639 |
|
| 640 |
// Get true total summary statistics
|
| 641 |
+
app.get('/api/stats/summary', cacheMiddleware(120), async (req, res) => {
|
| 642 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 643 |
try {
|
| 644 |
const summary = await getStatsSummary(streamId);
|
|
|
|
| 691 |
};
|
| 692 |
|
| 693 |
// Get word frequencies (Voice vs Chat messages from streamer)
|
| 694 |
+
app.get('/api/stats/words', cacheMiddleware(600), async (req, res) => {
|
| 695 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 696 |
const limit = req.query.limit ? parseInt(req.query.limit) : 50;
|
| 697 |
const type = req.query.type || 'voice'; // 'voice' or 'chat'
|
|
|
|
| 785 |
});
|
| 786 |
|
| 787 |
// Get chat activity (messages count grouped by 5-minute intervals)
|
| 788 |
+
app.get('/api/stats/activity', cacheMiddleware(300), async (req, res) => {
|
| 789 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 790 |
|
| 791 |
if (!streamId) {
|
|
|
|
| 823 |
// =========================================================================
|
| 824 |
|
| 825 |
// Get log of moderator actions
|
| 826 |
+
app.get('/api/stats/moderators', requireModeratorRole, cacheMiddleware(300), async (req, res) => {
|
| 827 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 828 |
const limit = req.query.limit ? parseInt(req.query.limit) : 100;
|
| 829 |
|
|
|
|
| 832 |
});
|
| 833 |
|
| 834 |
// Get summary counts of actions per moderator
|
| 835 |
+
app.get('/api/stats/moderators/summary', requireModeratorRole, cacheMiddleware(300), async (req, res) => {
|
| 836 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 837 |
|
| 838 |
const data = await getModActionsSummaryRaw(streamId);
|
|
|
|
| 863 |
});
|
| 864 |
|
| 865 |
// GET aggregated profiles with scores for moderators
|
| 866 |
+
app.get('/api/stats/moderators/profiles', cacheMiddleware(300), async (req, res) => {
|
| 867 |
const streamId = req.query.stream_id ? parseInt(req.query.stream_id) : null;
|
| 868 |
const profiles = await getModeratorProfilesData(streamId);
|
| 869 |
res.json(profiles);
|