feat: landing page + logo, dev-mode banner + sync cap, non-blocking sync with progress splash

This commit is contained in:
cesnimda
2026-06-30 16:58:35 +02:00
parent dcb939e4f2
commit fe5920919f
29 changed files with 552 additions and 39 deletions
@@ -0,0 +1,38 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace InboxIntel.Api.Controllers;
/// <summary>
/// Public metadata the SPA reads on load to decide whether to show the dev
/// banner. devMode falls back to the hosting environment when App:DevMode is
/// unset, and can be forced on/off via the App:DevMode config / App__DevMode env.
/// </summary>
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/app")]
public class AppInfoController : ControllerBase
{
private readonly IConfiguration _config;
private readonly IWebHostEnvironment _env;
public AppInfoController(IConfiguration config, IWebHostEnvironment env)
{
_config = config;
_env = env;
}
[HttpGet("info")]
[AllowAnonymous]
public IActionResult Info()
{
var devMode = _config.GetValue<bool?>("App:DevMode") ?? _env.IsDevelopment();
return Ok(new
{
environment = _env.EnvironmentName,
devMode,
maxMessages = _config.GetValue<int>("GmailSync:MaxMessages")
});
}
}
@@ -10,20 +10,21 @@ public class SyncController : ApiControllerBase
[HttpGet("status")]
public async Task<IActionResult> Status(CancellationToken ct)
=> Ok(new { status = (await _sync.GetStatusAsync(UserId, ct)).ToString() });
=> Ok(await _sync.GetProgressAsync(UserId, ct));
/// <summary>Triggers a full inbox sync (runs in the background task queue in production).</summary>
/// <summary>Queues a full inbox sync. Returns immediately; poll /sync/status for progress.</summary>
[HttpPost("full")]
public async Task<IActionResult> Full(CancellationToken ct)
{
await _sync.RunFullSyncAsync(UserId, ct);
await _sync.QueueSyncAsync(UserId, fullSync: true, ct);
return Accepted();
}
/// <summary>Queues an incremental sync (becomes a full sync on first run).</summary>
[HttpPost("incremental")]
public async Task<IActionResult> Incremental(CancellationToken ct)
{
await _sync.RunIncrementalSyncAsync(UserId, ct);
await _sync.QueueSyncAsync(UserId, fullSync: false, ct);
return Accepted();
}
}
+2 -1
View File
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<RootNamespace>InboxIntel.Api</RootNamespace>
<AssemblyName>InboxIntel.Api</AssemblyName>
<UserSecretsId>210c6d96-c7e4-4ee9-8982-8b91424979b8</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.7" />
+18 -1
View File
@@ -38,13 +38,30 @@ var google = builder.Configuration.GetSection(GoogleOAuthOptions.SectionName).Ge
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
// Challenge via the cookie scheme so unauthenticated API (XHR) calls get a
// 401 instead of a redirect to Google. The SPA's axios interceptor turns
// that 401 into a top-level navigation to /auth/login, which then starts
// the Google flow explicitly. (A 302 to Google on an XHR is CORS-blocked.)
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.Name = "inboxintel.session";
options.ExpireTimeSpan = TimeSpan.FromDays(7);
options.SlidingExpiration = true;
// API-style behaviour: return status codes rather than redirecting to a login page.
options.Events.OnRedirectToLogin = ctx =>
{
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
};
options.Events.OnRedirectToAccessDenied = ctx =>
{
ctx.Response.StatusCode = StatusCodes.Status403Forbidden;
return Task.CompletedTask;
};
})
.AddGoogle(options =>
{
@@ -2,6 +2,12 @@
"DataProtection": {
"KeyPath": "./keys"
},
"App": {
"DevMode": true
},
"GmailSync": {
"MaxMessages": 1000
},
"Serilog": {
"MinimumLevel": {
"Default": "Debug"
+5 -1
View File
@@ -19,12 +19,16 @@
"https://www.googleapis.com/auth/gmail.modify"
]
},
"App": {
"DevMode": false
},
"GmailSync": {
"PageSize": 100,
"MaxParallelism": 4,
"MaxRetries": 5,
"BackoffBaseMs": 500,
"DailySyncHourUtc": 3
"DailySyncHourUtc": 3,
"MaxMessages": 0
},
"Ai": {
"Mode": "Disabled",