mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 12:42:38 +00:00
refactor: reorganize metrics/stats API routes and fix session-reports 404 handling
- Rename app-metrics → metrics/app (tenant + galactic) - Rename usage-metrics → metrics/usage (tenant) - Rename platform/usage-metrics → galactic/metrics/usage - Rename platform-stats → stats/platform (anon whitelist updated) - SessionReportService: gracefully handle missing table (404) with empty list - SessionReportsSection: treat 404 as no reports yet (suppress error) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,7 @@ namespace AutopilotMonitor.Functions.Functions
|
||||
|
||||
[Function("GetAppMetrics")]
|
||||
public async Task<HttpResponseData> Run(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "app-metrics")] HttpRequestData req)
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "metrics/app")] HttpRequestData req)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace AutopilotMonitor.Functions.Functions
|
||||
|
||||
[Function("GetGalacticAppMetrics")]
|
||||
public async Task<HttpResponseData> Run(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "galactic/app-metrics")] HttpRequestData req)
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "galactic/metrics/app")] HttpRequestData req)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace AutopilotMonitor.Functions.Functions
|
||||
|
||||
[Function("GetPlatformStats")]
|
||||
public async Task<HttpResponseData> Run(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "platform-stats")] HttpRequestData req)
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "stats/platform")] HttpRequestData req)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -27,12 +27,12 @@ namespace AutopilotMonitor.Functions.Functions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET /api/platform/usage-metrics - Compute and return platform usage metrics
|
||||
/// GET /api/galactic/metrics/usage - Compute and return platform usage metrics
|
||||
/// On-demand computation with 5-minute cache (Galactic Admin only)
|
||||
/// </summary>
|
||||
[Function("GetPlatformUsageMetrics")]
|
||||
public async Task<HttpResponseData> Run(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "platform/usage-metrics")]
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "galactic/metrics/usage")]
|
||||
HttpRequestData req)
|
||||
{
|
||||
_logger.LogInformation("Platform usage metrics requested");
|
||||
|
||||
@@ -24,12 +24,12 @@ namespace AutopilotMonitor.Functions.Functions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET /api/usage-metrics?tenantId={tenantId} - Compute and return tenant-specific usage metrics
|
||||
/// GET /api/metrics/usage?tenantId={tenantId} - Compute and return tenant-specific usage metrics
|
||||
/// On-demand computation with 5-minute cache (Tenant Admin)
|
||||
/// </summary>
|
||||
[Function("GetTenantUsageMetrics")]
|
||||
public async Task<HttpResponseData> Run(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "usage-metrics")]
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "metrics/usage")]
|
||||
HttpRequestData req)
|
||||
{
|
||||
_logger.LogInformation("Tenant usage metrics requested");
|
||||
|
||||
@@ -41,7 +41,7 @@ public class AuthenticationMiddleware : IFunctionsWorkerMiddleware
|
||||
private static readonly string[] _anonymousRoutes =
|
||||
{
|
||||
"/api/health",
|
||||
"/api/platform-stats",
|
||||
"/api/stats/platform",
|
||||
"/api/agent/register-session",
|
||||
"/api/agent/ingest",
|
||||
"/api/agent/config",
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Azure;
|
||||
using Azure.Data.Tables;
|
||||
using Azure.Storage.Blobs;
|
||||
using Azure.Storage.Blobs.Models;
|
||||
@@ -172,19 +173,27 @@ namespace AutopilotMonitor.Functions.Services
|
||||
var tableClient = _storageService.GetTableServiceClient().GetTableClient(Constants.TableNames.SessionReports);
|
||||
var results = new List<SessionReportMetadata>();
|
||||
|
||||
await foreach (var entity in tableClient.QueryAsync<TableEntity>(e => e.PartitionKey == "reports"))
|
||||
try
|
||||
{
|
||||
results.Add(new SessionReportMetadata
|
||||
await foreach (var entity in tableClient.QueryAsync<TableEntity>(e => e.PartitionKey == "reports"))
|
||||
{
|
||||
ReportId = entity.GetString("ReportId") ?? string.Empty,
|
||||
TenantId = entity.GetString("TenantId") ?? string.Empty,
|
||||
SessionId = entity.GetString("SessionId") ?? string.Empty,
|
||||
Comment = entity.GetString("Comment") ?? string.Empty,
|
||||
Email = entity.GetString("Email") ?? string.Empty,
|
||||
BlobName = entity.GetString("BlobName") ?? string.Empty,
|
||||
SubmittedBy = entity.GetString("SubmittedBy") ?? string.Empty,
|
||||
SubmittedAt = entity.GetDateTimeOffset("SubmittedAt")?.UtcDateTime ?? DateTime.MinValue
|
||||
});
|
||||
results.Add(new SessionReportMetadata
|
||||
{
|
||||
ReportId = entity.GetString("ReportId") ?? string.Empty,
|
||||
TenantId = entity.GetString("TenantId") ?? string.Empty,
|
||||
SessionId = entity.GetString("SessionId") ?? string.Empty,
|
||||
Comment = entity.GetString("Comment") ?? string.Empty,
|
||||
Email = entity.GetString("Email") ?? string.Empty,
|
||||
BlobName = entity.GetString("BlobName") ?? string.Empty,
|
||||
SubmittedBy = entity.GetString("SubmittedBy") ?? string.Empty,
|
||||
SubmittedAt = entity.GetDateTimeOffset("SubmittedAt")?.UtcDateTime ?? DateTime.MinValue
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (RequestFailedException ex) when (ex.Status == 404)
|
||||
{
|
||||
// Table doesn't exist yet — no reports have been submitted
|
||||
_logger.LogDebug("SessionReports table does not exist yet, returning empty list");
|
||||
}
|
||||
|
||||
return results;
|
||||
|
||||
+5
-3
@@ -42,6 +42,11 @@ export function SessionReportsSection({
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (res.status === 404) {
|
||||
// Table/container doesn't exist yet — no reports submitted so far
|
||||
setReports([]);
|
||||
return;
|
||||
}
|
||||
if (!res.ok) throw new Error(`Failed to load reports: ${res.statusText}`);
|
||||
const data = await res.json();
|
||||
setReports(data.reports ?? []);
|
||||
@@ -63,9 +68,6 @@ export function SessionReportsSection({
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-xl font-semibold text-indigo-900 dark:text-indigo-100">Session Reports</h2>
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900/40 dark:text-purple-300">
|
||||
Private Preview
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-indigo-600 dark:text-indigo-300 mt-1">
|
||||
Sessions reported by Tenant Admins for analysis
|
||||
|
||||
@@ -322,6 +322,12 @@ export default function AdminConfigurationPage() {
|
||||
setSuccessMessage={setSuccessMessage}
|
||||
/>
|
||||
|
||||
{/* Session Reports */}
|
||||
<SessionReportsSection
|
||||
getAccessToken={getAccessToken}
|
||||
setError={setError}
|
||||
/>
|
||||
|
||||
{/* Session Event Export */}
|
||||
<SessionExportSection
|
||||
tenants={tenants}
|
||||
@@ -374,12 +380,6 @@ export default function AdminConfigurationPage() {
|
||||
setError={setError}
|
||||
setSuccessMessage={setSuccessMessage}
|
||||
/>
|
||||
|
||||
{/* Session Reports (Private Preview) */}
|
||||
<SessionReportsSection
|
||||
getAccessToken={getAccessToken}
|
||||
setError={setError}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -169,8 +169,8 @@ export default function FleetHealthPage() {
|
||||
if (!token) return;
|
||||
const days = range === "7d" ? 7 : range === "30d" ? 30 : 90;
|
||||
const endpoint = galacticAdminMode
|
||||
? `${API_BASE_URL}/api/galactic/app-metrics?days=${days}`
|
||||
: `${API_BASE_URL}/api/app-metrics?tenantId=${tenantId}&days=${days}`;
|
||||
? `${API_BASE_URL}/api/galactic/metrics/app?days=${days}`
|
||||
: `${API_BASE_URL}/api/metrics/app?tenantId=${tenantId}&days=${days}`;
|
||||
const response = await fetch(
|
||||
endpoint,
|
||||
{ headers: { Authorization: `Bearer ${token}` } }
|
||||
|
||||
@@ -96,7 +96,7 @@ export default function PlatformUsageMetricsPage() {
|
||||
throw new Error('Failed to get access token');
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/api/platform/usage-metrics`, {
|
||||
const response = await fetch(`${API_BASE_URL}/api/galactic/metrics/usage`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export default function UsageMetricsPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/api/usage-metrics?tenantId=${tenantId}`, {
|
||||
const response = await fetch(`${API_BASE_URL}/api/metrics/usage?tenantId=${tenantId}`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user