fix: add date filtering to usage/real endpoint and export
This commit is contained in:
92
server.js
92
server.js
@@ -4,6 +4,7 @@ const fs = require('fs');
|
||||
const http = require('http');
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
const { WebSocketServer } = require('ws');
|
||||
const { setupGiteaRoutes } = require('./gitea-routes.js');
|
||||
|
||||
const PORT = process.env.PORT || 8395;
|
||||
const DB_PATH = process.env.DB_PATH || path.join(__dirname, 'data', 'tasks.db');
|
||||
@@ -937,6 +938,9 @@ wss.on('connection', (socket) => {
|
||||
socket.send(JSON.stringify({ type: 'connected', payload: { ok: true } }));
|
||||
});
|
||||
|
||||
// Setup Gitea integration
|
||||
setupGiteaRoutes(app, renderPage);
|
||||
|
||||
server.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`openclaw-taskboard listening on ${PORT}`);
|
||||
});
|
||||
@@ -946,8 +950,13 @@ const REAL_SESSIONS_DIR = process.env.SESSIONS_DIR || '/app/agents';
|
||||
const SWARM_TASKS_FILE = process.env.SWARM_TASKS_FILE || '/app/swarm/active-tasks.json';
|
||||
|
||||
// GET /api/usage/real - Aggregate usage from session files
|
||||
// GET /api/usage/real - Aggregate usage from session files with date filtering
|
||||
app.get('/api/usage/real', async (req, res) => {
|
||||
try {
|
||||
const { from, to } = req.query;
|
||||
const fromDate = from ? new Date(from) : null;
|
||||
const toDate = to ? new Date(to) : null;
|
||||
|
||||
const usageByAgent = {};
|
||||
const usageByModel = {};
|
||||
let totalInput = 0, totalOutput = 0, totalCost = 0;
|
||||
@@ -975,6 +984,14 @@ app.get('/api/usage/real', async (req, res) => {
|
||||
if (!line.trim()) continue;
|
||||
try {
|
||||
const msg = JSON.parse(line);
|
||||
|
||||
// Date filtering
|
||||
if (fromDate || toDate) {
|
||||
const msgDate = new Date(msg.timestamp);
|
||||
if (fromDate && msgDate < fromDate) continue;
|
||||
if (toDate && msgDate > toDate) continue;
|
||||
}
|
||||
|
||||
if (msg.message?.usage) {
|
||||
const u = msg.message.usage;
|
||||
agentInput += u.input || 0;
|
||||
@@ -1014,6 +1031,7 @@ app.get('/api/usage/real', async (req, res) => {
|
||||
total: totalInput + totalOutput,
|
||||
cost: totalCost
|
||||
},
|
||||
filters: { from, to },
|
||||
lastUpdated: new Date().toISOString()
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -1022,7 +1040,79 @@ app.get('/api/usage/real', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/swarm/tasks - Get swarm task registry
|
||||
// GET /api/usage/export/real - Export real usage data
|
||||
app.get('/api/usage/export/real', (req, res) => {
|
||||
const { format = 'json', from, to } = req.query;
|
||||
|
||||
try {
|
||||
const fromDate = from ? new Date(from) : null;
|
||||
const toDate = to ? new Date(to) : null;
|
||||
const usageData = [];
|
||||
|
||||
if (!fs.existsSync(REAL_SESSIONS_DIR)) {
|
||||
return res.status(404).json({ error: 'sessions_dir_not_found' });
|
||||
}
|
||||
|
||||
const agents = fs.readdirSync(REAL_SESSIONS_DIR).filter(d => {
|
||||
return fs.statSync(path.join(REAL_SESSIONS_DIR, d)).isDirectory();
|
||||
});
|
||||
|
||||
for (const agent of agents) {
|
||||
const sessionsDir = path.join(REAL_SESSIONS_DIR, agent, 'sessions');
|
||||
if (!fs.existsSync(sessionsDir)) continue;
|
||||
|
||||
const sessions = fs.readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl'));
|
||||
for (const sessionFile of sessions) {
|
||||
const filePath = path.join(sessionsDir, sessionFile);
|
||||
const lines = fs.readFileSync(filePath, 'utf8').split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
try {
|
||||
const msg = JSON.parse(line);
|
||||
|
||||
if (fromDate || toDate) {
|
||||
const msgDate = new Date(msg.timestamp);
|
||||
if (fromDate && msgDate < fromDate) continue;
|
||||
if (toDate && msgDate > toDate) continue;
|
||||
}
|
||||
|
||||
if (msg.message?.usage) {
|
||||
usageData.push({
|
||||
timestamp: msg.timestamp,
|
||||
agent,
|
||||
model: msg.message.model || 'unknown',
|
||||
provider: msg.message.provider || 'unknown',
|
||||
input: msg.message.usage.input || 0,
|
||||
output: msg.message.usage.output || 0,
|
||||
total: (msg.message.usage.input || 0) + (msg.message.usage.output || 0),
|
||||
cost: msg.message.usage.cost?.total || 0
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (format === 'csv') {
|
||||
const csv = [
|
||||
'timestamp,agent,model,provider,input,output,total,cost',
|
||||
...usageData.map(r => `${r.timestamp},${r.agent},${r.model},${r.provider},${r.input},${r.output},${r.total},${r.cost}`)
|
||||
].join('\n');
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="usage-export.csv"');
|
||||
res.send(csv);
|
||||
} else {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename="usage-export.json"');
|
||||
res.json(usageData);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error exporting usage:', err);
|
||||
res.status(500).json({ error: 'failed_to_export_usage' });
|
||||
}
|
||||
});
|
||||
app.get('/api/swarm/tasks', (req, res) => {
|
||||
try {
|
||||
if (fs.existsSync(SWARM_TASKS_FILE)) {
|
||||
|
||||
Reference in New Issue
Block a user