feat(postit): migrate editable profile data and admin profile flow

This commit is contained in:
Paul Schneider 2026-10-07 12:20:26 +01:00
commit af1e572f32
Signed by: notazof
GPG key ID: AD4B9B545FBD7D12
38 changed files with 6573 additions and 19 deletions

View file

@ -0,0 +1,54 @@
#nullable enable
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Yavsc.Models;
namespace Yavsc.Server.Helpers;
public sealed class ResourceUsageTelemetryMiddleware
{
private readonly RequestDelegate _next;
private readonly IServiceScopeFactory _scopeFactory;
public ResourceUsageTelemetryMiddleware(
RequestDelegate next,
IServiceScopeFactory scopeFactory)
{
_next = next;
_scopeFactory = scopeFactory;
}
public async Task InvokeAsync(HttpContext context)
{
var userId = context.User.GetUserId();
if (string.IsNullOrWhiteSpace(userId))
{
await _next(context);
return;
}
var userName = context.User.Identity?.Name ?? userId;
var requestBytes = context.Request.ContentLength ?? 0L;
try
{
await _next(context);
}
finally
{
var responseBytes = context.Response.ContentLength ?? 0L;
var totalBytes = requestBytes + responseBytes;
var bandwidthMb = totalBytes <= 0 ? 0m : (decimal)totalBytes / (1024m * 1024m);
using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await ResourceUsageTracker.RecordAsync(
db,
userId,
userName,
apiCalls: 1m,
bandwidthMb: bandwidthMb,
actorUserId: userId);
}
}
}

View file

@ -0,0 +1,69 @@
#nullable enable
using Microsoft.EntityFrameworkCore;
using Yavsc.Models;
namespace Yavsc.Server.Helpers;
public static class ResourceUsageTracker
{
public static async Task RecordAsync(
ApplicationDbContext dbContext,
string userId,
string userName,
decimal cpuSeconds = 0m,
long storageBytes = 0,
decimal apiCalls = 0m,
decimal bandwidthMb = 0m,
string? actorUserId = null,
string currency = Constants.ResourceUsageDefaultCurrency)
{
if (dbContext is null || string.IsNullOrWhiteSpace(userId))
{
return;
}
var effectiveActor = string.IsNullOrWhiteSpace(actorUserId) ? userId : actorUserId;
var now = DateTime.UtcNow;
var storageMb = storageBytes <= 0 ? 0m : (decimal)storageBytes / (1024m * 1024m);
var record = await dbContext.ResourceUsageRecords
.Where(r => r.UserId == userId)
.OrderByDescending(r => r.RecordedAtUtc)
.FirstOrDefaultAsync();
if (record is null)
{
record = new ResourceUsageRecord
{
UserId = userId,
UserName = string.IsNullOrWhiteSpace(userName) ? userId : userName,
RecordedAtUtc = now,
ApiCalls = apiCalls,
CpuSeconds = cpuSeconds,
BandwidthMb = bandwidthMb,
StorageMb = storageMb,
EstimatedCompensation = 0m,
Currency = currency,
UserCreated = effectiveActor,
UserModified = effectiveActor,
DateCreated = now,
DateModified = now,
};
dbContext.ResourceUsageRecords.Add(record);
await dbContext.SaveChangesAsync(effectiveActor);
return;
}
record.UserName = string.IsNullOrWhiteSpace(userName) ? record.UserName : userName;
record.RecordedAtUtc = now;
record.ApiCalls += apiCalls;
record.CpuSeconds += cpuSeconds;
record.BandwidthMb += bandwidthMb;
record.StorageMb += storageMb;
record.Currency = string.IsNullOrWhiteSpace(currency) ? record.Currency : currency;
record.DateModified = now;
record.UserModified = effectiveActor;
await dbContext.SaveChangesAsync(effectiveActor);
}
}

View file

@ -58,6 +58,14 @@ namespace Yavsc.Models
}
builder.UseIdentityByDefaultColumns();
builder.Entity<ResourceUsageRecord>(entity =>
{
entity.HasKey(r => r.Id);
entity.HasIndex(r => new { r.UserId, r.RecordedAtUtc });
entity.Property(r => r.Currency).HasMaxLength(8).HasDefaultValue(Constants.ResourceUsageDefaultCurrency);
entity.Property(r => r.UserName).HasMaxLength(256);
});
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
@ -324,6 +332,7 @@ namespace Yavsc.Models
public DbSet<Estimate> Estimates { get; set; }
public DbSet<Signature> Signatures { get; set; }
public DbSet<ResourceUsageRecord> ResourceUsageRecords { get; set; }
public DbSet<AccountBalance> BankStatus { get; set; }
public DbSet<BalanceImpact> BalanceImpact { get; set; }

View file

@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Yavsc.Models;
public sealed class ResourceUsageRecord : ITrackedEntity
{
[Key]
public long Id { get; set; }
[Required]
[MaxLength(256)]
public string UserId { get; set; } = string.Empty;
[Required]
[MaxLength(256)]
public string UserName { get; set; } = string.Empty;
[Required]
public DateTime RecordedAtUtc { get; set; } = DateTime.UtcNow;
public decimal ApiCalls { get; set; }
public decimal CpuSeconds { get; set; }
public decimal BandwidthMb { get; set; }
public decimal StorageMb { get; set; }
public decimal EstimatedCompensation { get; set; }
[Required]
[MaxLength(8)]
public string Currency { get; set; } = Constants.ResourceUsageDefaultCurrency;
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
public string UserCreated { get; set; } = string.Empty;
public DateTime DateModified { get; set; } = DateTime.UtcNow;
public string UserModified { get; set; } = string.Empty;
}

View file

@ -3,7 +3,7 @@ using Yavsc.Abstract.Identity;
namespace Yavsc.Models.Auth
{
public class Me : IApplicationUser {
public Me(string userId, string userName, string email, string avatar, ILocation address, string gCalId)
public Me(string userId, string userName, string email, string avatar, ILocation address, string gCalId, string? fullName = null)
{
Id = userId;
UserName = userName;
@ -11,10 +11,14 @@ namespace Yavsc.Models.Auth
Avatar = avatar;
PostalAddress = address;
DedicatedGoogleCalendar = gCalId;
FullName = fullName ?? string.Empty;
Address = address?.Address ?? string.Empty;
}
public string Id { get; set; }
public string UserName { get; set; }
public string FullName { get; set; }
public string EMail { get; set; }
public string Address { get; set; }
public string[] Roles { get; set; }
/// <summary>
/// Known as profile, could point to an avatar
@ -24,18 +28,18 @@ namespace Yavsc.Models.Auth
public IAccountBalance AccountBalance
{
get; set;
get; set;
}
public string DedicatedGoogleCalendar
{
get; set;
get; set;
}
public ILocation PostalAddress
{
get; set;
get; set;
}
}
}
}