Files
jobtrackingapp/JobTrackerApi/Controllers/AiUsageController.cs
T
cesnimda 134aac7bcf
CI and Deploy / test (pull_request) Successful in 5m19s
CI and Deploy / deploy (pull_request) Has been skipped
feat(ai): centralize durable usage
Add a content-free usage ledger with legacy backfill. Reserve Workspace and durable Strategy/CV work before execution so deleted history or duplicate admission cannot reset limits.
2026-08-15 20:03:06 +02:00

54 lines
2.1 KiB
C#

using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace JobTrackerApi.Controllers;
[ApiController]
[Route("api/ai/usage")]
[Authorize(AuthenticationSchemes = "local")]
public sealed class AiUsageController : ControllerBase
{
private readonly UserManager<ApplicationUser> _users;
private readonly JobTrackerContext _db;
private readonly AiUsageMeter? _usage;
public AiUsageController(UserManager<ApplicationUser> users, JobTrackerContext db, AiUsageMeter? usage = null)
{
_users = users;
_db = db;
_usage = usage;
}
public sealed record UsagePeriodDto(int Calls, long InputCharacters, long OutputCharacters, long EstimatedTokens);
public sealed record UsageDto(UsagePeriodDto CurrentMonth, UsagePeriodDto AllTime, string Plan, int MonthlyCallLimit, long MonthlyTokenLimit, long StorageUsedBytes, long StorageLimitBytes);
[HttpGet]
public async Task<ActionResult<UsageDto>> Get(CancellationToken cancellationToken)
{
var user = await _users.GetUserAsync(User);
if (user is null) return Unauthorized();
var roles = await _users.GetRolesAsync(user);
var entitlements = AccountPlans.ForRoles(roles);
var meter = _usage ?? new AiUsageMeter(_db, TimeProvider.System);
var currentMonth = ToDto(await meter.CurrentMonthAsync(user.Id, cancellationToken));
return Ok(new UsageDto(
currentMonth,
ToDto(await meter.AllTimeAsync(user.Id, cancellationToken)),
AccountPlans.Name(entitlements),
entitlements.MonthlyAiCalls,
entitlements.MonthlyAiTokens,
await _db.Attachments.Where(x => x.JobApplication.OwnerUserId == user.Id)
.SumAsync(x => (long?)x.FileSize, cancellationToken) ?? 0,
entitlements.StorageBytes));
}
private static UsagePeriodDto ToDto(AiUsageTotals totals)
=> new(totals.Calls, totals.InputCharacters, totals.OutputCharacters, totals.EstimatedTokens);
}