99 lines
3.7 KiB
C#
99 lines
3.7 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using OtaFleet.Api.Data;
|
|
using OtaFleet.Api.Data.Entities;
|
|
|
|
namespace OtaFleet.Api.Endpoints;
|
|
|
|
public static class VehicleEndpoints
|
|
{
|
|
public static void MapVehicleEndpoints(this IEndpointRouteBuilder app)
|
|
{
|
|
var group = app.MapGroup("/api/vehicles");
|
|
|
|
group.MapPost("/register", async (OtaDbContext db, RegisterVehicleRequest request) =>
|
|
{
|
|
var vehicle = await db.Vehicles.FindAsync(request.Vin);
|
|
if (vehicle == null)
|
|
{
|
|
vehicle = new Vehicle
|
|
{
|
|
Vin = request.Vin,
|
|
Status = "Online",
|
|
CurrentVersion = request.CurrentVersion,
|
|
LastHeartbeat = DateTime.UtcNow
|
|
};
|
|
db.Vehicles.Add(vehicle);
|
|
}
|
|
else
|
|
{
|
|
vehicle.Status = "Online";
|
|
vehicle.CurrentVersion = request.CurrentVersion;
|
|
vehicle.LastHeartbeat = DateTime.UtcNow;
|
|
}
|
|
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(vehicle);
|
|
});
|
|
|
|
group.MapPost("/heartbeat", async (OtaDbContext db, HeartbeatRequest request) =>
|
|
{
|
|
var vehicle = await db.Vehicles.FindAsync(request.Vin);
|
|
if (vehicle == null) return Results.NotFound();
|
|
|
|
vehicle.LastHeartbeat = DateTime.UtcNow;
|
|
vehicle.Status = request.Status;
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok();
|
|
});
|
|
|
|
group.MapGet("/updates", async (OtaDbContext db, string vin) =>
|
|
{
|
|
var vehicle = await db.Vehicles.Include(v => v.Group).FirstOrDefaultAsync(v => v.Vin == vin);
|
|
if (vehicle == null) return Results.NotFound();
|
|
|
|
// Find deployment for this vehicle or its group
|
|
var deployment = await db.Deployments
|
|
.Include(d => d.Update)
|
|
.Where(d => (d.TargetVin == vin || (vehicle.GroupId.HasValue && d.TargetGroupId == vehicle.GroupId)) && d.Status != "Completed") // Simple logic, can be improved
|
|
.OrderByDescending(d => d.CreatedAt)
|
|
.FirstOrDefaultAsync();
|
|
|
|
if (deployment == null) return Results.NoContent();
|
|
|
|
// Check if vehicle already has this version
|
|
if (deployment.Update.Version == vehicle.CurrentVersion) return Results.NoContent();
|
|
|
|
return Results.Ok(new VehicleUpdateResponse
|
|
{
|
|
UpdateId = deployment.Update.Id,
|
|
Version = deployment.Update.Version,
|
|
Description = deployment.Update.Description,
|
|
DownloadUrl = $"/api/vehicles/updates/{deployment.Update.Id}/download"
|
|
});
|
|
});
|
|
|
|
group.MapGet("/updates/{id}/download", async (OtaDbContext db, int id) =>
|
|
{
|
|
var update = await db.FirmwareUpdates.FindAsync(id);
|
|
if (update == null) return Results.NotFound();
|
|
|
|
if (!File.Exists(update.FilePath)) return Results.NotFound("File not found on server");
|
|
|
|
var fileName = Path.GetFileName(update.FilePath);
|
|
var mimeType = "application/octet-stream";
|
|
|
|
return Results.File(update.FilePath, mimeType, fileName);
|
|
});
|
|
}
|
|
}
|
|
|
|
public record RegisterVehicleRequest(string Vin, string CurrentVersion);
|
|
public record HeartbeatRequest(string Vin, string Status);
|
|
public record VehicleUpdateResponse
|
|
{
|
|
public int UpdateId { get; set; }
|
|
public string Version { get; set; } = string.Empty;
|
|
public string? Description { get; set; }
|
|
public string DownloadUrl { get; set; } = string.Empty;
|
|
}
|