using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using QMax.Api.Configuration; using QMax.Api.Contracts; using QMax.Api.Data; using QMax.Api.Data.Entities; using QMax.Api.Infrastructure.Max; using QMax.Api.Services; namespace QMax.Api.Controllers; [ApiController] [Authorize] [Route("api/max")] public sealed class MaxController( IMaxBridgeClient maxBridge, MaxBridgeSyncService syncService, QMaxDbContext db, IOptions options) : ControllerBase { private readonly QMaxOptions _options = options.Value; [HttpGet("status")] public async Task> Status(CancellationToken cancellationToken) { var status = await maxBridge.GetStatusAsync(cancellationToken); await SaveStateAsync(status, cancellationToken); return ToDto(status); } [HttpPost("login/start")] public async Task> BeginLogin(BeginMaxLoginRequest request, CancellationToken cancellationToken) { var phone = string.IsNullOrWhiteSpace(request.PhoneNumber) ? _options.MaxPhoneNumber : request.PhoneNumber; if (string.IsNullOrWhiteSpace(phone)) { return BadRequest("Phone number is required."); } var status = await maxBridge.BeginPhoneLoginAsync(phone, cancellationToken); await SaveStateAsync(status, cancellationToken); return ToDto(status); } [HttpPost("login/code")] public async Task> SubmitCode(SubmitMaxCodeRequest request, CancellationToken cancellationToken) { var status = await maxBridge.SubmitLoginCodeAsync(request.Code, cancellationToken); await SaveStateAsync(status, cancellationToken); return ToDto(status); } [HttpGet("browser/snapshot")] public async Task> Snapshot(CancellationToken cancellationToken) { var snapshot = await maxBridge.GetSnapshotAsync(cancellationToken); return new MaxBrowserSnapshotDto(snapshot.Url, snapshot.Title, snapshot.BodyText, snapshot.ScreenshotPngBase64, snapshot.CapturedAt); } [HttpPost("sync")] public async Task> Sync(CancellationToken cancellationToken) { var changed = await syncService.SyncOnceAsync(cancellationToken); return new { changed }; } private async Task SaveStateAsync(MaxBridgeStatus status, CancellationToken cancellationToken) { var state = await db.MaxAccountStates.FirstOrDefaultAsync(x => x.Id == 1, cancellationToken); if (state is null) { state = new MaxAccountState { Id = 1 }; db.MaxAccountStates.Add(state); } state.PhoneNumber = _options.MaxPhoneNumber; state.Status = status.Status; state.IsAuthorized = status.IsAuthorized; state.LastUrl = status.Url; state.LastError = status.LastError; state.UpdatedAt = DateTimeOffset.UtcNow; await db.SaveChangesAsync(cancellationToken); } private static MaxBridgeStatusDto ToDto(MaxBridgeStatus status) { return new MaxBridgeStatusDto(status.Mode, status.IsAuthorized, status.LoginStage, status.Status, status.Url, status.Title, status.LastError, status.UpdatedAt); } }