Implement Phase 6: automated bank sync (Plaid + SimpleFIN) & settings
Add automated transaction syncing via Plaid and SimpleFIN providers, a Settings page for managing connections and account mappings, background scheduled syncing, and sync history logging. New backend: BankSync plugin project with PlaidSyncProvider and SimpleFinSyncProvider, SyncConnection/LinkedAccount/SyncLog models, AES-256 encryption service, BankSyncService orchestration, BankSyncBackgroundService (15-min tick), and BankSyncController (13 endpoints). EF migration creates sync_connections, linked_accounts, and sync_logs tables. New frontend: Settings view with 3 tabs (Profile, Bank Connections, Sync History), bankSync API service, BankSyncComplete SignalR notifications, and Settings nav item in sidebar + avatar dropdown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Purrse.Core.DTOs;
|
||||
using Purrse.Core.Interfaces.Services;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Purrse.Api.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("api/bank-sync")]
|
||||
public class BankSyncController : ControllerBase
|
||||
{
|
||||
private readonly IBankSyncService _bankSyncService;
|
||||
private Guid UserId => Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
|
||||
|
||||
public BankSyncController(IBankSyncService bankSyncService) => _bankSyncService = bankSyncService;
|
||||
|
||||
[HttpGet("connections")]
|
||||
public async Task<ActionResult<List<SyncConnectionResponse>>> GetConnections()
|
||||
=> Ok(await _bankSyncService.GetConnectionsAsync(UserId));
|
||||
|
||||
[HttpGet("connections/{id}")]
|
||||
public async Task<ActionResult<SyncConnectionResponse>> GetConnection(Guid id)
|
||||
=> Ok(await _bankSyncService.GetConnectionAsync(UserId, id));
|
||||
|
||||
[HttpPut("connections/{id}")]
|
||||
public async Task<ActionResult<SyncConnectionResponse>> UpdateConnection(Guid id, UpdateConnectionRequest request)
|
||||
=> Ok(await _bankSyncService.UpdateConnectionAsync(UserId, id, request));
|
||||
|
||||
[HttpDelete("connections/{id}")]
|
||||
public async Task<IActionResult> DeleteConnection(Guid id)
|
||||
{
|
||||
await _bankSyncService.DeleteConnectionAsync(UserId, id);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("plaid/create-link-token")]
|
||||
public async Task<ActionResult<CreateLinkTokenResponse>> CreatePlaidLinkToken()
|
||||
=> Ok(await _bankSyncService.CreatePlaidLinkTokenAsync(UserId));
|
||||
|
||||
[HttpPost("plaid/complete-link")]
|
||||
public async Task<ActionResult<SyncConnectionResponse>> CompletePlaidLink(CreatePlaidConnectionRequest request)
|
||||
=> Ok(await _bankSyncService.CompletePlaidLinkAsync(UserId, request));
|
||||
|
||||
[HttpPost("simplefin/connect")]
|
||||
public async Task<ActionResult<SyncConnectionResponse>> ConnectSimpleFin(CreateSimpleFinConnectionRequest request)
|
||||
=> Ok(await _bankSyncService.CreateSimpleFinConnectionAsync(UserId, request));
|
||||
|
||||
[HttpGet("connections/{id}/discover-accounts")]
|
||||
public async Task<ActionResult<List<DiscoveredAccountResponse>>> DiscoverAccounts(Guid id)
|
||||
=> Ok(await _bankSyncService.DiscoverAccountsAsync(UserId, id));
|
||||
|
||||
[HttpPost("connections/{id}/link-account")]
|
||||
public async Task<ActionResult<LinkedAccountResponse>> LinkAccount(Guid id, LinkAccountRequest request)
|
||||
=> Ok(await _bankSyncService.LinkAccountAsync(UserId, id, request));
|
||||
|
||||
[HttpPut("linked-accounts/{id}")]
|
||||
public async Task<ActionResult<LinkedAccountResponse>> UpdateLinkedAccount(Guid id, UpdateLinkedAccountRequest request)
|
||||
=> Ok(await _bankSyncService.UpdateLinkedAccountAsync(UserId, id, request));
|
||||
|
||||
[HttpPost("connections/{id}/sync-now")]
|
||||
public async Task<ActionResult<SyncResultResponse>> SyncNow(Guid id)
|
||||
=> Ok(await _bankSyncService.SyncNowAsync(UserId, id));
|
||||
|
||||
[HttpPost("sync-all")]
|
||||
public async Task<ActionResult<SyncResultResponse>> SyncAll()
|
||||
=> Ok(await _bankSyncService.SyncAllAsync(UserId));
|
||||
|
||||
[HttpGet("sync-logs")]
|
||||
public async Task<ActionResult<List<SyncLogResponse>>> GetSyncLogs([FromQuery] Guid? connectionId, [FromQuery] int limit = 50)
|
||||
=> Ok(await _bankSyncService.GetSyncLogsAsync(UserId, connectionId, limit));
|
||||
}
|
||||
Reference in New Issue
Block a user