Files
OTSSignsOrchestrator/OTSSignsOrchestrator.Desktop/Services/IServerApiClient.cs
Matt Batchelder c6d46098dd feat: Implement provisioning pipelines for subscription management
- Add ReactivatePipeline to handle subscription reactivation, including scaling Docker services, health verification, status updates, audit logging, and broadcasting status changes.
- Introduce RotateCredentialsPipeline for OAuth2 credential rotation, managing the deletion of old apps, creation of new ones, credential storage, access verification, and audit logging.
- Create StepRunner to manage job step execution, including lifecycle management and progress broadcasting via SignalR.
- Implement SuspendPipeline for subscription suspension, scaling down services, updating statuses, logging audits, and broadcasting changes.
- Add UpdateScreenLimitPipeline to update Xibo CMS screen limits and record snapshots.
- Introduce XiboFeatureManifests for hardcoded feature ACLs per role.
- Add docker-compose.dev.yml for local development with PostgreSQL setup.
2026-03-18 10:27:26 -04:00

153 lines
5.0 KiB
C#

using Refit;
namespace OTSSignsOrchestrator.Desktop.Services;
// ── DTOs matching server REST API responses ─────────────────────────────────
public record FleetSummaryDto
{
public Guid CustomerId { get; init; }
public string Abbreviation { get; init; } = string.Empty;
public string CompanyName { get; init; } = string.Empty;
public string Plan { get; init; } = string.Empty;
public int ScreenCount { get; init; }
public string HealthStatus { get; init; } = "Unknown";
public DateTime? LastHealthCheck { get; init; }
public bool HasRunningJob { get; init; }
}
public record CustomerDetailDto
{
public Guid Id { get; init; }
public string Abbreviation { get; init; } = string.Empty;
public string CompanyName { get; init; } = string.Empty;
public string? AdminEmail { get; init; }
public string Plan { get; init; } = string.Empty;
public int ScreenCount { get; init; }
public string Status { get; init; } = string.Empty;
public DateTime CreatedAt { get; init; }
public List<CustomerInstanceDto> Instances { get; init; } = [];
public List<CustomerJobDto> ActiveJobs { get; init; } = [];
}
public record CustomerInstanceDto
{
public Guid Id { get; init; }
public string? XiboUrl { get; init; }
public string? DockerStackName { get; init; }
public string HealthStatus { get; init; } = "Unknown";
public DateTime? LastHealthCheck { get; init; }
}
public record CustomerJobDto
{
public Guid Id { get; init; }
public string JobType { get; init; } = string.Empty;
public string Status { get; init; } = string.Empty;
public DateTime CreatedAt { get; init; }
public DateTime? StartedAt { get; init; }
}
public record CreateJobRequest(Guid CustomerId, string JobType, string? Parameters);
public record CreateJobResponse
{
public Guid Id { get; init; }
public string JobType { get; init; } = string.Empty;
public string Status { get; init; } = string.Empty;
}
public record JobDetailDto
{
public Guid Id { get; init; }
public Guid CustomerId { get; init; }
public string JobType { get; init; } = string.Empty;
public string Status { get; init; } = string.Empty;
public string? TriggeredBy { get; init; }
public string? Parameters { get; init; }
public DateTime CreatedAt { get; init; }
public DateTime? StartedAt { get; init; }
public DateTime? CompletedAt { get; init; }
public string? ErrorMessage { get; init; }
public List<JobStepDto> Steps { get; init; } = [];
}
public record JobStepDto
{
public Guid Id { get; init; }
public string StepName { get; init; } = string.Empty;
public string Status { get; init; } = string.Empty;
public string? LogOutput { get; init; }
public DateTime? StartedAt { get; init; }
public DateTime? CompletedAt { get; init; }
}
public record LoginRequest(string Email, string Password);
public record RefreshRequest(string RefreshToken);
public record AuthResponse
{
public string Token { get; init; } = string.Empty;
public string RefreshToken { get; init; } = string.Empty;
}
public record RefreshResponse
{
public string Token { get; init; } = string.Empty;
}
// ── Refit interface ─────────────────────────────────────────────────────────
[Headers("Accept: application/json")]
public interface IServerApiClient
{
[Get("/api/fleet")]
Task<List<FleetSummaryDto>> GetFleetAsync();
[Get("/api/fleet/{id}")]
Task<CustomerDetailDto> GetCustomerDetailAsync(Guid id);
[Post("/api/jobs")]
Task<CreateJobResponse> CreateJobAsync([Body] CreateJobRequest body);
[Get("/api/jobs/{id}")]
Task<JobDetailDto> GetJobAsync(Guid id);
[Post("/api/auth/login")]
Task<AuthResponse> LoginAsync([Body] LoginRequest body);
[Post("/api/auth/refresh")]
Task<RefreshResponse> RefreshAsync([Body] RefreshRequest body);
[Get("/api/reports/billing")]
Task<HttpResponseMessage> GetBillingCsvAsync([Query] DateOnly from, [Query] DateOnly to);
[Get("/api/reports/fleet-health")]
Task<HttpResponseMessage> GetFleetHealthPdfAsync();
[Post("/api/fleet/bulk/{action}")]
Task<HttpResponseMessage> BulkActionAsync(string action);
}
// ── DelegatingHandler for Bearer token injection ────────────────────────────
public class AuthHeaderHandler : DelegatingHandler
{
private readonly TokenStoreService _tokenStore;
public AuthHeaderHandler(TokenStoreService tokenStore)
{
_tokenStore = tokenStore;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var jwt = _tokenStore.GetJwt();
if (!string.IsNullOrEmpty(jwt))
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwt);
return await base.SendAsync(request, cancellationToken);
}
}