30 lines
1.9 KiB
C#
30 lines
1.9 KiB
C#
using Microsoft.AspNetCore.DataProtection;
|
|
namespace GuestOps.Web;
|
|
public sealed class WorkerHeartbeat
|
|
{
|
|
[MongoDB.Bson.Serialization.Attributes.BsonId] public string Id {get;set;}="worker";
|
|
public DateTime At {get;set;}=DateTime.UtcNow;
|
|
}
|
|
public static class BackupProbe
|
|
{
|
|
const string Value="GuestOps backup key verification v1";
|
|
public static string Create(IDataProtectionProvider protection)=>protection.CreateProtector("GuestOps.BackupProbe.v1").Protect(Value);
|
|
public static bool Verify(IDataProtectionProvider protection,string value)=>protection.CreateProtector("GuestOps.BackupProbe.v1").Unprotect(value)==Value;
|
|
}
|
|
public static class Operations
|
|
{
|
|
public static void Map(WebApplication app,RouteGroupBuilder api,bool preview)
|
|
{
|
|
app.MapGet("/health/ready",async(IStore store,HttpContext c)=>
|
|
{
|
|
c.Response.Headers.CacheControl="no-store";
|
|
try{await store.Ping();return Results.Ok(new{status="ready"});}catch{return Results.Json(new{status="unavailable"},statusCode:503);}
|
|
});
|
|
api.MapGet("/operations",async(HttpContext c,IStore store)=>
|
|
{
|
|
var id=Session.Hotel(c);var boxes=await store.List<Mailbox>(id);var messages=await store.List<Conversation>(id);var seen=await store.WorkerLastSeen();
|
|
return Results.Ok(new{checkedAt=DateTime.UtcNow,preview,database="Reachable",worker=new{lastSeenAt=seen,state=preview?"Preview":seen==null?"NotSeen":seen<DateTime.UtcNow.AddMinutes(-3)?"Stale":"Reporting"},mailboxes=new{total=boxes.Count,connected=boxes.Count(x=>x.Status=="Connected"),attention=boxes.Count(x=>x.Status!="Connected"||x.SyncError.Length>0)},replies=new{sampleSize=messages.Count,sampleLimit=500,pending=messages.Count(x=>x.Delivery?.State is "Pending" or "Sending"),uncertain=messages.Count(x=>x.Delivery?.State=="NeedsReview"),rejected=messages.Count(x=>x.Delivery?.State=="Rejected")}});
|
|
}).RequireAuthorization("Owner");
|
|
}
|
|
}
|