feat/Update_Controllers_to_Allow_for_Premium_Membership

This commit is contained in:
cesnimda
2026-08-03 09:17:28 +02:00
parent de937d25dc
commit c3f4a57195
187 changed files with 26062 additions and 991 deletions
@@ -82,6 +82,13 @@ namespace JobTrackerApi.Controllers
return await _users.FindByIdAsync(userId);
}
private async Task<bool> CanCurrentUserUseAiAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var user = await GetCurrentUserAsync(cancellationToken);
return user is not null && user.AiEnabled && AccountPlans.ForRoles(await _users.GetRolesAsync(user)).Ai;
}
private async Task<TailoredCvDraft?> FindTailoredCvDraftAsync(int jobId, CancellationToken cancellationToken)
{
return await _db.TailoredCvDrafts.FirstOrDefaultAsync(x => x.JobApplicationId == jobId, cancellationToken);
@@ -622,7 +629,9 @@ Canonical profile:
var d = RulesEngine.Evaluate(settings, job, now, lm);
// Prefer translated content for the detailed summary so Norwegian postings
// surface readable English analysis while the original text remains available.
var full = await _summarizer.SummarizeAsync(BuildSummarySource(job), 250, 40);
var full = await CanCurrentUserUseAiAsync(cancellationToken)
? await _summarizer.SummarizeAsync(BuildSummarySource(job), 250, 40)
: null;
return Ok(BuildJobApplicationDto(job, d, fullSummary: full));
}
@@ -774,8 +783,8 @@ Canonical profile:
// Generate and persist a short summary at creation time to avoid repeated model calls.
try
{
var shortSum = await _summarizer.SummarizeAsync(BuildSummarySource(job), 160, 60);
job.ShortSummary = shortSum;
if (await CanCurrentUserUseAiAsync(cancellationToken))
job.ShortSummary = await _summarizer.SummarizeAsync(BuildSummarySource(job), 160, 60);
}
catch
{
@@ -957,6 +966,7 @@ Canonical profile:
[HttpPost("{id:int}/refresh-ai")]
[Authorize(Policy = ProEntitlement.Policy)]
public async Task<ActionResult<JobApplicationDto>> RefreshAi([FromRoute] int id, CancellationToken cancellationToken)
{
var job = await _db.JobApplications
@@ -1082,51 +1092,6 @@ Canonical profile:
return Ok(items);
}
[HttpGet("{id:int}/timeline")]
public async Task<ActionResult<List<TimelineItemDto>>> GetTimeline([FromRoute] int id, CancellationToken cancellationToken)
{
var exists = await _db.JobApplications.AnyAsync(j => j.Id == id, cancellationToken);
if (!exists) return NotFound();
var events = await _db.JobEvents
.AsNoTracking()
.Where(e => e.JobApplicationId == id)
.Select(e => new TimelineItemDto(
"event",
e.At,
new { e.Id, e.Type, e.OldValue, e.NewValue, e.Note }
))
.ToListAsync(cancellationToken);
var messages = await _db.Correspondences
.AsNoTracking()
.Where(c => c.JobApplicationId == id)
.Select(c => new TimelineItemDto(
"message",
c.Date,
new { c.Id, c.From, c.Subject, c.Channel, c.Content }
))
.ToListAsync(cancellationToken);
var attachments = await _db.Attachments
.AsNoTracking()
.Where(a => a.JobApplicationId == id)
.Select(a => new TimelineItemDto(
"attachment",
a.UploadDate,
new { a.Id, a.FileName, a.FileType, a.FileSize }
))
.ToListAsync(cancellationToken);
var all = events
.Concat(messages)
.Concat(attachments)
.OrderByDescending(x => x.At)
.ToList();
return Ok(all);
}
[HttpGet("stats")]
public async Task<ActionResult<JobStats>> GetStats(CancellationToken cancellationToken)
=> Ok(await _analytics.GetStatsAsync(cancellationToken));
@@ -1424,6 +1389,7 @@ Canonical profile:
}
[HttpGet("{id:int}/candidate-fit")]
[Authorize(Policy = ProEntitlement.Policy)]
public async Task<ActionResult<CandidateFitDto>> GetCandidateFit([FromRoute] int id, [FromQuery] string? attachmentIds, [FromQuery] bool refresh, CancellationToken cancellationToken)
{
var job = await _db.JobApplications
@@ -1559,6 +1525,7 @@ Candidate CV/profile:
}
[HttpGet("{id:int}/focus-plan")]
[Authorize(Policy = ProEntitlement.Policy)]
public async Task<ActionResult<FocusPlanDto>> GetFocusPlan([FromRoute] int id, [FromQuery] string? attachmentIds, [FromQuery] bool refresh, CancellationToken cancellationToken)
{
var job = await _db.JobApplications
@@ -1676,7 +1643,8 @@ Candidate master CV:
await _db.SaveChangesAsync(cancellationToken);
}
[HttpGet("{id:int}/interview-prep")]
[HttpGet("{id:int}/interview-prep/brief")]
[Authorize(Policy = ProEntitlement.Policy)]
public async Task<ActionResult<InterviewPrepDto>> GetInterviewPrep([FromRoute] int id, [FromQuery] string? attachmentIds, [FromQuery] bool refresh, CancellationToken cancellationToken)
{
var job = await _db.JobApplications
@@ -1867,6 +1835,7 @@ Candidate master CV:
}
[HttpPost("{id:int}/generate-tailored-cv-draft")]
[Authorize(Policy = ProEntitlement.Policy)]
public async Task<ActionResult<TailoredCvDraftDto>> GenerateTailoredCvDraft([FromRoute] int id, [FromQuery] string? mode, CancellationToken cancellationToken)
{
var job = await _db.JobApplications
@@ -1975,6 +1944,7 @@ Candidate master CV:
}
[HttpPost("{id:int}/generate-application-package")]
[Authorize(Policy = ProEntitlement.Policy)]
public async Task<ActionResult<GenerateApplicationPackageDto>> GenerateApplicationPackage([FromRoute] int id, [FromQuery] string? mode, [FromQuery] string? coverLetterStyle, [FromQuery] string? attachmentIds, CancellationToken cancellationToken)
{
var job = await _db.JobApplications
@@ -2240,6 +2210,7 @@ Candidate master CV:
}
[HttpGet("{id:int}/followup-draft")]
[Authorize(Policy = ProEntitlement.Policy)]
public async Task<ActionResult<FollowUpDraftDto>> GetFollowUpDraft([FromRoute] int id, [FromQuery] string? mode, [FromQuery] string? attachmentIds, CancellationToken cancellationToken)
{
var job = await _db.JobApplications