feat(export): add readable account archive
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using JobTrackerApi.Data;
|
||||
using JobTrackerApi.Services;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace JobTrackerApi.Controllers
|
||||
{
|
||||
@@ -12,10 +15,43 @@ namespace JobTrackerApi.Controllers
|
||||
public class ExportController : ControllerBase
|
||||
{
|
||||
private readonly JobTrackerContext _db;
|
||||
private readonly AccountDataExportService? _accountExport;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public ExportController(JobTrackerContext db)
|
||||
public ExportController(JobTrackerContext db, AccountDataExportService? accountExport = null, TimeProvider? timeProvider = null)
|
||||
{
|
||||
_db = db;
|
||||
_accountExport = accountExport;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
[HttpPost("account")]
|
||||
[EnableRateLimiting("account-data")]
|
||||
public async Task<IActionResult> ExportAccount(CancellationToken cancellationToken)
|
||||
{
|
||||
var ownerUserId = LocalAuthIdentity.GetRequiredUserId(User);
|
||||
if (ownerUserId is null) return Unauthorized();
|
||||
if (_accountExport is null) throw new InvalidOperationException("Account export is not configured.");
|
||||
|
||||
var sessionId = User.FindFirstValue("sid");
|
||||
var recentCutoff = _timeProvider.GetUtcNow().AddMinutes(-15);
|
||||
var currentSession = string.IsNullOrWhiteSpace(sessionId)
|
||||
? null
|
||||
: await _db.UserSessions.IgnoreQueryFilters().AsNoTracking()
|
||||
.FirstOrDefaultAsync(item => item.Id == sessionId && item.UserId == ownerUserId, cancellationToken);
|
||||
var recentlyAuthenticated = currentSession is { RevokedAtUtc: null } && currentSession.CreatedAtUtc >= recentCutoff;
|
||||
if (!recentlyAuthenticated)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status403Forbidden, new ProblemDetails
|
||||
{
|
||||
Title = "Recent sign-in required",
|
||||
Detail = "Sign in again before downloading a complete account export.",
|
||||
});
|
||||
}
|
||||
|
||||
var artifact = await _accountExport.CreateAsync(ownerUserId, cancellationToken);
|
||||
var stream = new FileStream(artifact.StoragePath, FileMode.Open, FileAccess.Read, FileShare.Read, 64 * 1024, FileOptions.Asynchronous | FileOptions.DeleteOnClose);
|
||||
return File(stream, "application/zip", artifact.DownloadFileName);
|
||||
}
|
||||
|
||||
[HttpGet("jobs")]
|
||||
|
||||
Reference in New Issue
Block a user