neoagent 2.4.1-beta.44 → 2.4.1-beta.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +12 -0
- package/docs/configuration.md +6 -0
- package/flutter_app/lib/main_app_shell.dart +0 -2
- package/flutter_app/lib/main_chat.dart +1 -0
- package/flutter_app/lib/main_controller.dart +14 -2
- package/flutter_app/lib/main_models.dart +36 -0
- package/flutter_app/lib/main_navigation.dart +1 -9
- package/flutter_app/lib/main_settings.dart +4 -265
- package/flutter_app/lib/main_shared.dart +138 -3
- package/flutter_app/lib/main_unified.dart +5 -100
- package/lib/manager.js +32 -0
- package/package.json +1 -1
- package/runtime/paths.js +3 -1
- package/server/admin/access.js +198 -0
- package/server/admin/admin.css +268 -0
- package/server/admin/admin.js +348 -0
- package/server/admin/analytics.js +128 -0
- package/server/admin/index.html +993 -0
- package/server/admin/login.html +251 -0
- package/server/admin/logo.svg +43 -0
- package/server/admin/sql.js +134 -0
- package/server/admin/users.js +147 -0
- package/server/db/database.js +19 -0
- package/server/http/static.js +4 -1
- package/server/index.js +1 -1
- package/server/middleware/adminAuth.js +48 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +70538 -71095
- package/server/routes/admin.js +618 -0
- package/server/routes/settings.js +10 -2
- package/server/services/account/admin_two_factor.js +132 -0
- package/server/services/widgets/service.js +17 -1
- package/server/utils/logger.js +44 -6
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Analytics ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
async function loadAnalytics() {
|
|
6
|
+
const el = document.getElementById('analytics-content');
|
|
7
|
+
if (!el) return;
|
|
8
|
+
el.innerHTML = '<div class="empty"><span class="spinner"></span></div>';
|
|
9
|
+
try {
|
|
10
|
+
const data = await api('/admin/api/analytics').then((r) => r.json());
|
|
11
|
+
renderAnalytics(el, data);
|
|
12
|
+
setTs('analytics-ts', 'Updated');
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (err.message !== 'unauthorized') el.innerHTML = '<div class="empty">Failed to load analytics</div>';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function renderAnalytics(el, { stats, topUsers, recentRuns }) {
|
|
19
|
+
const s = stats || {};
|
|
20
|
+
el.innerHTML = `
|
|
21
|
+
<div class="stat-grid">
|
|
22
|
+
<div class="stat-card">
|
|
23
|
+
<div class="stat-value">${fmtNum(s.totalUsers)}</div>
|
|
24
|
+
<div class="stat-label">Total Users</div>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="stat-card">
|
|
27
|
+
<div class="stat-value">${fmtNum(s.activeToday)}</div>
|
|
28
|
+
<div class="stat-label">Active Today</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="stat-card">
|
|
31
|
+
<div class="stat-value">${fmtNum(s.newThisWeek)}</div>
|
|
32
|
+
<div class="stat-label">New This Week</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="stat-card">
|
|
35
|
+
<div class="stat-value">${fmtNum(s.totalRuns)}</div>
|
|
36
|
+
<div class="stat-label">Total Runs</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="stat-card">
|
|
39
|
+
<div class="stat-value">${fmtNum(s.runsToday)}</div>
|
|
40
|
+
<div class="stat-label">Runs Today</div>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="stat-card">
|
|
43
|
+
<div class="stat-value">${fmtTokens(s.totalTokens)}</div>
|
|
44
|
+
<div class="stat-label">Total Tokens</div>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="stat-card">
|
|
47
|
+
<div class="stat-value">${fmtNum(s.activeSessions)}</div>
|
|
48
|
+
<div class="stat-label">Active Sessions</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="stat-card">
|
|
51
|
+
<div class="stat-value">${fmtBytes(s.totalStorage)}</div>
|
|
52
|
+
<div class="stat-label">Artifact Storage</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
${renderTopUsers(topUsers || [])}
|
|
57
|
+
${renderRecentRuns(recentRuns || [])}
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function renderTopUsers(users) {
|
|
62
|
+
if (!users.length) return '';
|
|
63
|
+
const maxRuns = Math.max(...users.map((u) => u.runs), 1);
|
|
64
|
+
return `
|
|
65
|
+
<div class="card" style="margin-top:20px;">
|
|
66
|
+
<div class="card-title">Top Users by Runs</div>
|
|
67
|
+
<table class="analytics-table">
|
|
68
|
+
<thead><tr>
|
|
69
|
+
<th>User</th><th>Runs</th><th>Tokens</th><th>Storage</th><th>Activity</th>
|
|
70
|
+
</tr></thead>
|
|
71
|
+
<tbody>${users.map((u) => `
|
|
72
|
+
<tr>
|
|
73
|
+
<td><span class="user-avatar">${esc((u.display_name || u.username || '?')[0]).toUpperCase()}</span> ${esc(u.display_name || u.username)}</td>
|
|
74
|
+
<td>${fmtNum(u.runs)}</td>
|
|
75
|
+
<td>${fmtTokens(u.tokens)}</td>
|
|
76
|
+
<td>${fmtBytes(u.storage)}</td>
|
|
77
|
+
<td>
|
|
78
|
+
<div class="mini-bar"><div class="mini-bar-fill" style="width:${Math.round((u.runs / maxRuns) * 100)}%"></div></div>
|
|
79
|
+
</td>
|
|
80
|
+
</tr>`).join('')}
|
|
81
|
+
</tbody>
|
|
82
|
+
</table>
|
|
83
|
+
</div>`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function renderRecentRuns(runs) {
|
|
87
|
+
if (!runs.length) return '';
|
|
88
|
+
return `
|
|
89
|
+
<div class="card" style="margin-top:16px;">
|
|
90
|
+
<div class="card-title">Recent Runs</div>
|
|
91
|
+
<table class="analytics-table">
|
|
92
|
+
<thead><tr>
|
|
93
|
+
<th>User</th><th>Title</th><th>Status</th><th>Tokens</th><th>Started</th>
|
|
94
|
+
</tr></thead>
|
|
95
|
+
<tbody>${runs.map((r) => `
|
|
96
|
+
<tr>
|
|
97
|
+
<td>${esc(r.username)}</td>
|
|
98
|
+
<td class="cell-truncate">${esc(r.title || '(untitled)')}</td>
|
|
99
|
+
<td><span class="run-badge run-${esc(r.status)}">${esc(r.status)}</span></td>
|
|
100
|
+
<td>${fmtTokens(r.total_tokens)}</td>
|
|
101
|
+
<td>${fmtTime(r.created_at)}</td>
|
|
102
|
+
</tr>`).join('')}
|
|
103
|
+
</tbody>
|
|
104
|
+
</table>
|
|
105
|
+
</div>`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── Shared number helpers (also used by other modules) ────────────────────
|
|
109
|
+
|
|
110
|
+
function fmtNum(n) {
|
|
111
|
+
if (n == null) return '—';
|
|
112
|
+
return Number(n).toLocaleString();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function fmtTokens(n) {
|
|
116
|
+
if (!n) return '0';
|
|
117
|
+
if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M';
|
|
118
|
+
if (n >= 1_000) return (n / 1_000).toFixed(1) + 'K';
|
|
119
|
+
return String(n);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function fmtBytes(n) {
|
|
123
|
+
if (!n) return '0 B';
|
|
124
|
+
if (n >= 1_073_741_824) return (n / 1_073_741_824).toFixed(1) + ' GB';
|
|
125
|
+
if (n >= 1_048_576) return (n / 1_048_576).toFixed(1) + ' MB';
|
|
126
|
+
if (n >= 1_024) return (n / 1_024).toFixed(1) + ' KB';
|
|
127
|
+
return n + ' B';
|
|
128
|
+
}
|