Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 102 additions & 3 deletions Controllers/AdminController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Swashbuckle.AspNetCore.Annotations;
using AutoMapper;
using garge_api.Models.Admin;
using Microsoft.EntityFrameworkCore;

namespace garge_api.Controllers
{
Expand Down Expand Up @@ -200,21 +201,119 @@ public async Task<IActionResult> RemoveRole([FromRoute] string roleName, [FromQu
}

/// <summary>
/// Gets all users.
/// Gets all users with their roles.
/// </summary>
/// <returns>A list of all users.</returns>
[HttpGet("/api/users")]
[SwaggerOperation(Summary = "Gets all users.")]
[SwaggerResponse(200, "Users retrieved successfully.", typeof(IEnumerable<UserDto>))]
public IActionResult GetUsers()
public async Task<IActionResult> GetUsers()
{
_logger.LogInformation("GetUsers called by {@LogData}", new { User = User.Identity?.Name });

var users = _userManager.Users.ToList();
var dtos = _mapper.Map<IEnumerable<UserDto>>(users);
var dtos = new List<UserDto>();
foreach (var user in users)
{
var dto = _mapper.Map<UserDto>(user);
dto.Roles = await _userManager.GetRolesAsync(user);
dtos.Add(dto);
}
return Ok(dtos);
}

/// <summary>
/// Gets aggregate platform stats.
/// </summary>
[HttpGet("/api/admin/stats")]
[SwaggerOperation(Summary = "Gets aggregate platform stats.")]
[SwaggerResponse(200, "Stats retrieved successfully.", typeof(AdminStatsDto))]
public async Task<IActionResult> GetStats()
{
_logger.LogInformation("GetStats called by {@LogData}", new { User = User.Identity?.Name });

var stats = new AdminStatsDto
{
TotalUsers = await _userManager.Users.CountAsync(),
TotalSensors = await _context.Sensors.CountAsync(),
TotalSwitches = await _context.Switches.CountAsync(),
ActiveAutomations = await _context.AutomationRules.CountAsync(r => r.IsEnabled),
};
return Ok(stats);
}

/// <summary>
/// Gets all discovered MQTT devices.
/// </summary>
[HttpGet("/api/admin/devices")]
[SwaggerOperation(Summary = "Gets all discovered MQTT devices.")]
public async Task<IActionResult> GetDevices()
{
_logger.LogInformation("GetDevices called by {@LogData}", new { User = User.Identity?.Name });

var devices = await _context.DiscoveredDevices
.OrderByDescending(d => d.Timestamp)
.ToListAsync();
return Ok(devices);
}

/// <summary>
/// Gets cumulative daily stats over time for charting.
/// </summary>
[HttpGet("/api/admin/stats/history")]
[SwaggerOperation(Summary = "Gets cumulative daily stats over time.")]
public async Task<IActionResult> GetStatsHistory()
{
_logger.LogInformation("GetStatsHistory called by {@LogData}", new { User = User.Identity?.Name });

var userDates = await _userManager.Users
.Select(u => u.CreatedAt.Date)
.ToListAsync();

var sensorDates = await _context.Sensors
.Select(s => s.CreatedAt.Date)
.ToListAsync();

var switchDates = await _context.Switches
.Select(s => s.CreatedAt.Date)
.ToListAsync();

var automationDates = await _context.AutomationRules
.Select(a => a.CreatedAt.Date)
.ToListAsync();

var sanityFloor = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var allDates = userDates.Concat(sensorDates).Concat(switchDates).Concat(automationDates)
.Where(d => d >= sanityFloor)
.ToList();
if (!allDates.Any()) return Ok(new List<object>());

var start = allDates.Min();
var today = DateTime.UtcNow.Date;

var result = new List<object>();
int users = 0, sensors = 0, switches = 0, automations = 0;

for (var date = start; date <= today; date = date.AddDays(1))
{
users += userDates.Count(d => d == date);
sensors += sensorDates.Count(d => d == date);
switches += switchDates.Count(d => d == date);
automations += automationDates.Count(d => d == date);

result.Add(new
{
date = date.ToString("yyyy-MM-dd"),
totalUsers = users,
totalSensors = sensors,
totalSwitches = switches,
totalAutomations = automations,
});
}

return Ok(result);
}

/// <summary>
/// Deletes a user by their ID.
/// </summary>
Expand Down
10 changes: 10 additions & 0 deletions Dtos/Admin/AdminStatsDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace garge_api.Dtos.Admin
{
public class AdminStatsDto
{
public int TotalUsers { get; set; }
public int TotalSensors { get; set; }
public int TotalSwitches { get; set; }
public int ActiveAutomations { get; set; }
}
}
1 change: 1 addition & 0 deletions Dtos/Admin/UserDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public class UserDto
public required string FirstName { get; set; }
public required string LastName { get; set; }
public required string Email { get; set; }
public IList<string> Roles { get; set; } = [];
}
}
Loading