feat: complete release readiness work

- consolidate API ownership and remove dead vendor code

- add Stripe billing, learning paths, and public CV hardening

- add migration, recovery, security, audit, and browser gates
This commit is contained in:
cesnimda
2026-07-31 16:54:16 +02:00
parent a23c3dfc97
commit ce76046a29
1634 changed files with 6889 additions and 135429 deletions
@@ -3,6 +3,7 @@ using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
namespace JobTrackerApi.Controllers;
@@ -15,11 +16,13 @@ public sealed class PublicCvController : ControllerBase
{
private readonly UserManager<ApplicationUser> _users;
private readonly ICvVariantService _variants;
private readonly ICvPdfExporter _pdf;
public PublicCvController(UserManager<ApplicationUser> users, ICvVariantService variants)
public PublicCvController(UserManager<ApplicationUser> users, ICvVariantService variants, ICvPdfExporter pdf)
{
_users = users;
_variants = variants;
_pdf = pdf;
}
[HttpGet("{slug}")]
@@ -37,6 +40,22 @@ public sealed class PublicCvController : ControllerBase
return Ok(new { html = result.Value.render.Html, name = person.FallbackName });
}
[HttpGet("{slug}/pdf")]
[EnableRateLimiting("public-pdf")]
public async Task<IActionResult> DownloadPdf(string slug, CancellationToken ct)
{
var ownerId = await _variants.GetPublicOwnerAsync(slug, ct);
if (ownerId is null) return NotFound();
var person = Person(await _users.FindByIdAsync(ownerId));
var result = await _variants.RenderPublicAsync(slug, person, ct);
if (result is null) return NotFound();
var render = result.Value.render;
var artifact = await _pdf.ExportAsync(new TailoredCvRenderResult(render.ThemeId, render.SuggestedFileName, render.Html), ct);
return File(artifact.Bytes, "application/pdf", artifact.FileName);
}
private static CvRenderPerson Person(ApplicationUser? user)
{
if (user is null) return new CvRenderPerson("Candidate", null);