🐛 Bug fixes and improvements

This commit is contained in:
LittleSheep 2025-05-01 00:47:26 +08:00
parent 758186f674
commit 84a88222bd
13 changed files with 111 additions and 59 deletions

View File

@ -4,7 +4,6 @@ using DysonNetwork.Sphere.Storage;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using NodaTime; using NodaTime;
namespace DysonNetwork.Sphere.Account; namespace DysonNetwork.Sphere.Account;
@ -15,8 +14,8 @@ public class AccountController(
AppDatabase db, AppDatabase db,
FileService fs, FileService fs,
AuthService auth, AuthService auth,
MagicSpellService spells, AccountService accounts,
IMemoryCache memCache MagicSpellService spells
) : ControllerBase ) : ControllerBase
{ {
[HttpGet("{name}")] [HttpGet("{name}")]
@ -138,7 +137,7 @@ public class AccountController(
if (request.Nick is not null) account.Nick = request.Nick; if (request.Nick is not null) account.Nick = request.Nick;
if (request.Language is not null) account.Language = request.Language; if (request.Language is not null) account.Language = request.Language;
memCache.Remove($"user_${account.Id}"); await accounts.PurgeAccountCache(account);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return account; return account;
@ -199,7 +198,7 @@ public class AccountController(
db.Update(profile); db.Update(profile);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
memCache.Remove($"user_${userId}"); await accounts.PurgeAccountCache(currentUser);
return profile; return profile;
} }

View File

@ -1,12 +1,23 @@
using Casbin; using Casbin;
using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Permission;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using NodaTime; using NodaTime;
namespace DysonNetwork.Sphere.Account; namespace DysonNetwork.Sphere.Account;
public class AccountService(AppDatabase db, PermissionService pm) public class AccountService(AppDatabase db, PermissionService pm, IMemoryCache cache)
{ {
public async Task PurgeAccountCache(Account account)
{
var sessions = await db.AuthSessions.Where(e => e.Account.Id == account.Id).Select(e => e.Id)
.ToListAsync();
foreach (var session in sessions)
{
cache.Remove($"dyn_auth_{session}");
}
}
public async Task<Account?> LookupAccount(string probe) public async Task<Account?> LookupAccount(string probe)
{ {
var account = await db.Accounts.Where(a => a.Name == probe).FirstOrDefaultAsync(); var account = await db.Accounts.Where(a => a.Name == probe).FirstOrDefaultAsync();

View File

@ -45,6 +45,8 @@ public class MagicSpellService(AppDatabase db, EmailService email, ILogger<Magic
// TODO replace the baseurl // TODO replace the baseurl
var link = $"https://api.sn.solsynth.dev/spells/{Uri.EscapeDataString(spell.Spell)}"; var link = $"https://api.sn.solsynth.dev/spells/{Uri.EscapeDataString(spell.Spell)}";
logger.LogError($"Sending magic spell... {link}");
try try
{ {

View File

@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Sphere.Auth;
using DysonNetwork.Sphere.Post; using DysonNetwork.Sphere.Post;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -36,7 +37,6 @@ public class NotificationController(AppDatabase db, NotificationService nty) : C
public class PushNotificationSubscribeRequest public class PushNotificationSubscribeRequest
{ {
[MaxLength(4096)] public string DeviceId { get; set; } = null!;
[MaxLength(4096)] public string DeviceToken { get; set; } = null!; [MaxLength(4096)] public string DeviceToken { get; set; } = null!;
public NotificationPushProvider Provider { get; set; } public NotificationPushProvider Provider { get; set; }
} }
@ -47,12 +47,16 @@ public class NotificationController(AppDatabase db, NotificationService nty) : C
[FromBody] PushNotificationSubscribeRequest request [FromBody] PushNotificationSubscribeRequest request
) )
{ {
HttpContext.Items.TryGetValue("CurrentSession", out var currentSessionValue);
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
var currentUser = currentUserValue as Account; var currentUser = currentUserValue as Account;
if (currentUser == null) return Unauthorized(); if (currentUser == null) return Unauthorized();
var currentSession = currentSessionValue as Session;
if (currentSession == null) return Unauthorized();
var result = var result =
await nty.SubscribePushNotification(currentUser, request.Provider, request.DeviceId, request.DeviceToken); await nty.SubscribePushNotification(currentUser, request.Provider, currentSession.Challenge.DeviceId!,
request.DeviceToken);
return Ok(result); return Ok(result);
} }

View File

@ -72,11 +72,10 @@ public class NotificationService
DeviceId = deviceId, DeviceId = deviceId,
DeviceToken = deviceToken, DeviceToken = deviceToken,
Provider = provider, Provider = provider,
Account = account,
AccountId = account.Id, AccountId = account.Id,
}; };
_db.Add(subscription); _db.NotificationPushSubscriptions.Add(subscription);
await _db.SaveChangesAsync(); await _db.SaveChangesAsync();
return subscription; return subscription;

View File

@ -20,8 +20,9 @@ public class AuthController(
{ {
public class ChallengeRequest public class ChallengeRequest
{ {
[Required] public ChallengePlatform Platform { get; set; }
[Required] [MaxLength(256)] public string Account { get; set; } = string.Empty; [Required] [MaxLength(256)] public string Account { get; set; } = string.Empty;
[MaxLength(512)] public string? DeviceId { get; set; } [Required] [MaxLength(512)] public string DeviceId { get; set; }
public List<string> Audiences { get; set; } = new(); public List<string> Audiences { get; set; } = new();
public List<string> Scopes { get; set; } = new(); public List<string> Scopes { get; set; } = new();
} }
@ -52,6 +53,7 @@ public class AuthController(
Account = account, Account = account,
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)), ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)),
StepTotal = 1, StepTotal = 1,
Platform = request.Platform,
Audiences = request.Audiences, Audiences = request.Audiences,
Scopes = request.Scopes, Scopes = request.Scopes,
IpAddress = ipAddress, IpAddress = ipAddress,
@ -125,6 +127,8 @@ public class AuthController(
} }
catch catch
{ {
challenge.FailedAttempts++;
await db.SaveChangesAsync();
return BadRequest(); return BadRequest();
} }

View File

@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages;
using NodaTime; using NodaTime;
namespace DysonNetwork.Sphere.Auth; namespace DysonNetwork.Sphere.Auth;
@ -8,6 +9,7 @@ namespace DysonNetwork.Sphere.Auth;
public class Session : ModelBase public class Session : ModelBase
{ {
public Guid Id { get; set; } = Guid.NewGuid(); public Guid Id { get; set; } = Guid.NewGuid();
[MaxLength(1024)] public string? Label { get; set; }
public Instant? LastGrantedAt { get; set; } public Instant? LastGrantedAt { get; set; }
public Instant? ExpiredAt { get; set; } public Instant? ExpiredAt { get; set; }
@ -15,6 +17,23 @@ public class Session : ModelBase
[JsonIgnore] public Challenge Challenge { get; set; } = null!; [JsonIgnore] public Challenge Challenge { get; set; } = null!;
} }
public enum ChallengeType
{
Login,
OAuth
}
public enum ChallengePlatform
{
Unidentified,
Web,
Ios,
Android,
MacOs,
Windows,
Linux
}
public class Challenge : ModelBase public class Challenge : ModelBase
{ {
public Guid Id { get; set; } = Guid.NewGuid(); public Guid Id { get; set; } = Guid.NewGuid();
@ -22,6 +41,8 @@ public class Challenge : ModelBase
public int StepRemain { get; set; } public int StepRemain { get; set; }
public int StepTotal { get; set; } public int StepTotal { get; set; }
public int FailedAttempts { get; set; } public int FailedAttempts { get; set; }
public ChallengePlatform Platform { get; set; } = ChallengePlatform.Unidentified;
public ChallengeType Type { get; set; } = ChallengeType.Login;
[Column(TypeName = "jsonb")] public List<long> BlacklistFactors { get; set; } = new(); [Column(TypeName = "jsonb")] public List<long> BlacklistFactors { get; set; } = new();
[Column(TypeName = "jsonb")] public List<string> Audiences { get; set; } = new(); [Column(TypeName = "jsonb")] public List<string> Audiences { get; set; } = new();
[Column(TypeName = "jsonb")] public List<string> Scopes { get; set; } = new(); [Column(TypeName = "jsonb")] public List<string> Scopes { get; set; } = new();

View File

@ -7,29 +7,30 @@ public class UserInfoMiddleware(RequestDelegate next, IMemoryCache cache)
{ {
public async Task InvokeAsync(HttpContext context, AppDatabase db) public async Task InvokeAsync(HttpContext context, AppDatabase db)
{ {
var userIdClaim = context.User.FindFirst("user_id")?.Value; var sessionIdClaim = context.User.FindFirst("session_id")?.Value;
if (userIdClaim is not null && long.TryParse(userIdClaim, out var userId)) if (sessionIdClaim is not null && Guid.TryParse(sessionIdClaim, out var sessionId))
{ {
if (!cache.TryGetValue($"user_{userId}", out Account.Account? user)) if (!cache.TryGetValue($"dyn_auth_{sessionId}", out Session? session))
{ {
user = await db.Accounts session = await db.AuthSessions
.Include(e => e.Profile) .Include(e => e.Challenge)
.Include(e => e.Profile.Picture) .Include(e => e.Account)
.Include(e => e.Profile.Background) .Include(e => e.Account.Profile)
.Where(e => e.Id == userId) .Include(e => e.Account.Profile.Picture)
.Include(e => e.Account.Profile.Background)
.Where(e => e.Id == sessionId)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
if (user is not null) if (session is not null)
{ {
cache.Set($"user_{userId}", user, TimeSpan.FromMinutes(10)); cache.Set($"dyn_auth_{sessionId}", session, TimeSpan.FromHours(1));
} }
} }
if (user is not null) if (session is not null)
{ {
context.Items["CurrentUser"] = user; context.Items["CurrentUser"] = session.Account;
var prefix = user.IsSuperuser ? "super:" : ""; context.Items["CurrentSession"] = session;
context.Items["CurrentIdentity"] = $"{prefix}{userId}";
} }
} }

View File

@ -1,6 +1,5 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net.WebSockets; using System.Net.WebSockets;
using DysonNetwork.Sphere.Account;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
@ -11,13 +10,10 @@ namespace DysonNetwork.Sphere.Connection;
[Route("/ws")] [Route("/ws")]
public class WebSocketController : ControllerBase public class WebSocketController : ControllerBase
{ {
// Concurrent dictionary to store active WebSocket connections.
// Key: Tuple (AccountId, DeviceId); Value: WebSocket and CancellationTokenSource
private static readonly ConcurrentDictionary< private static readonly ConcurrentDictionary<
(long AccountId, string DeviceId), (long AccountId, string DeviceId),
(WebSocket Socket, CancellationTokenSource Cts) (WebSocket Socket, CancellationTokenSource Cts)
> ActiveConnections = > ActiveConnections = new();
new ConcurrentDictionary<(long, string), (WebSocket, CancellationTokenSource)>();
[Route("/ws")] [Route("/ws")]
[Authorize] [Authorize]
@ -26,18 +22,18 @@ public class WebSocketController : ControllerBase
{ {
if (HttpContext.WebSockets.IsWebSocketRequest) if (HttpContext.WebSockets.IsWebSocketRequest)
{ {
// Get AccountId from HttpContext HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
if ( HttpContext.Items.TryGetValue("CurrentSession", out var currentSessionValue);
!HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue) if (currentUserValue is not Account.Account currentUser ||
|| currentUserValue is not Account.Account currentUser currentSessionValue is not Auth.Session currentSession)
)
{ {
HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
return; return;
} }
long accountId = currentUser.Id;
// Verify deviceId var accountId = currentUser.Id;
var deviceId = currentSession.Challenge.DeviceId;
if (string.IsNullOrEmpty(deviceId)) if (string.IsNullOrEmpty(deviceId))
{ {
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
@ -45,14 +41,11 @@ public class WebSocketController : ControllerBase
} }
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
// Create a CancellationTokenSource for this connection
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
var connectionKey = (accountId, deviceId); var connectionKey = (accountId, deviceId);
// Add the connection to the active connections dictionary
if (!ActiveConnections.TryAdd(connectionKey, (webSocket, cts))) if (!ActiveConnections.TryAdd(connectionKey, (webSocket, cts)))
{ {
// Failed to add
await webSocket.CloseAsync( await webSocket.CloseAsync(
WebSocketCloseStatus.InternalServerError, WebSocketCloseStatus.InternalServerError,
"Failed to establish connection.", "Failed to establish connection.",
@ -71,7 +64,6 @@ public class WebSocketController : ControllerBase
} }
finally finally
{ {
// Connection is closed, remove it from the active connections dictionary
ActiveConnections.TryRemove(connectionKey, out _); ActiveConnections.TryRemove(connectionKey, out _);
cts.Dispose(); cts.Dispose();
} }
@ -88,25 +80,21 @@ public class WebSocketController : ControllerBase
CancellationToken cancellationToken CancellationToken cancellationToken
) )
{ {
// Buffer for receiving messages.
var buffer = new byte[1024 * 4]; var buffer = new byte[1024 * 4];
try try
{ {
// We don't handle receiving data, so we ignore the return.
var receiveResult = await webSocket.ReceiveAsync( var receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), new ArraySegment<byte>(buffer),
cancellationToken cancellationToken
); );
while (!receiveResult.CloseStatus.HasValue) while (!receiveResult.CloseStatus.HasValue)
{ {
// Keep connection alive and wait for close requests
receiveResult = await webSocket.ReceiveAsync( receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), new ArraySegment<byte>(buffer),
cancellationToken cancellationToken
); );
} }
// Close connection
await webSocket.CloseAsync( await webSocket.CloseAsync(
receiveResult.CloseStatus.Value, receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription, receiveResult.CloseStatusDescription,
@ -144,5 +132,4 @@ public class WebSocketController : ControllerBase
); );
} }
} }
} }

View File

@ -15,7 +15,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace DysonNetwork.Sphere.Migrations namespace DysonNetwork.Sphere.Migrations
{ {
[DbContext(typeof(AppDatabase))] [DbContext(typeof(AppDatabase))]
[Migration("20250429115700_InitialMigration")] [Migration("20250430163514_InitialMigration")]
partial class InitialMigration partial class InitialMigration
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -508,6 +508,10 @@ namespace DysonNetwork.Sphere.Migrations
.HasColumnType("character varying(1024)") .HasColumnType("character varying(1024)")
.HasColumnName("nonce"); .HasColumnName("nonce");
b.Property<int>("Platform")
.HasColumnType("integer")
.HasColumnName("platform");
b.Property<List<string>>("Scopes") b.Property<List<string>>("Scopes")
.IsRequired() .IsRequired()
.HasColumnType("jsonb") .HasColumnType("jsonb")
@ -521,6 +525,10 @@ namespace DysonNetwork.Sphere.Migrations
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("step_total"); .HasColumnName("step_total");
b.Property<int>("Type")
.HasColumnType("integer")
.HasColumnName("type");
b.Property<Instant>("UpdatedAt") b.Property<Instant>("UpdatedAt")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("updated_at"); .HasColumnName("updated_at");
@ -566,6 +574,11 @@ namespace DysonNetwork.Sphere.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("expired_at"); .HasColumnName("expired_at");
b.Property<string>("Label")
.HasMaxLength(1024)
.HasColumnType("character varying(1024)")
.HasColumnName("label");
b.Property<Instant?>("LastGrantedAt") b.Property<Instant?>("LastGrantedAt")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("last_granted_at"); .HasColumnName("last_granted_at");

View File

@ -171,6 +171,8 @@ namespace DysonNetwork.Sphere.Migrations
step_remain = table.Column<int>(type: "integer", nullable: false), step_remain = table.Column<int>(type: "integer", nullable: false),
step_total = table.Column<int>(type: "integer", nullable: false), step_total = table.Column<int>(type: "integer", nullable: false),
failed_attempts = table.Column<int>(type: "integer", nullable: false), failed_attempts = table.Column<int>(type: "integer", nullable: false),
platform = table.Column<int>(type: "integer", nullable: false),
type = table.Column<int>(type: "integer", nullable: false),
blacklist_factors = table.Column<List<long>>(type: "jsonb", nullable: false), blacklist_factors = table.Column<List<long>>(type: "jsonb", nullable: false),
audiences = table.Column<List<string>>(type: "jsonb", nullable: false), audiences = table.Column<List<string>>(type: "jsonb", nullable: false),
scopes = table.Column<List<string>>(type: "jsonb", nullable: false), scopes = table.Column<List<string>>(type: "jsonb", nullable: false),
@ -326,6 +328,7 @@ namespace DysonNetwork.Sphere.Migrations
columns: table => new columns: table => new
{ {
id = table.Column<Guid>(type: "uuid", nullable: false), id = table.Column<Guid>(type: "uuid", nullable: false),
label = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
last_granted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true), last_granted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
expired_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true), expired_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
account_id = table.Column<long>(type: "bigint", nullable: false), account_id = table.Column<long>(type: "bigint", nullable: false),

View File

@ -505,6 +505,10 @@ namespace DysonNetwork.Sphere.Migrations
.HasColumnType("character varying(1024)") .HasColumnType("character varying(1024)")
.HasColumnName("nonce"); .HasColumnName("nonce");
b.Property<int>("Platform")
.HasColumnType("integer")
.HasColumnName("platform");
b.Property<List<string>>("Scopes") b.Property<List<string>>("Scopes")
.IsRequired() .IsRequired()
.HasColumnType("jsonb") .HasColumnType("jsonb")
@ -518,6 +522,10 @@ namespace DysonNetwork.Sphere.Migrations
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("step_total"); .HasColumnName("step_total");
b.Property<int>("Type")
.HasColumnType("integer")
.HasColumnName("type");
b.Property<Instant>("UpdatedAt") b.Property<Instant>("UpdatedAt")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("updated_at"); .HasColumnName("updated_at");
@ -563,6 +571,11 @@ namespace DysonNetwork.Sphere.Migrations
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("expired_at"); .HasColumnName("expired_at");
b.Property<string>("Label")
.HasMaxLength(1024)
.HasColumnType("character varying(1024)")
.HasColumnName("label");
b.Property<Instant?>("LastGrantedAt") b.Property<Instant?>("LastGrantedAt")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("last_granted_at"); .HasColumnName("last_granted_at");

View File

@ -24,6 +24,7 @@ using NodaTime.Serialization.SystemTextJson;
using Quartz; using Quartz;
using tusdotnet; using tusdotnet;
using tusdotnet.Models; using tusdotnet.Models;
using tusdotnet.Models.Configuration;
using File = System.IO.File; using File = System.IO.File;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -34,7 +35,6 @@ builder.WebHost.ConfigureKestrel(options => options.Limits.MaxRequestBodySize =
// Add services to the container. // Add services to the container.
builder.Services.AddDbContext<AppDatabase>(); builder.Services.AddDbContext<AppDatabase>();
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();
builder.Services.AddControllers().AddJsonOptions(options => builder.Services.AddControllers().AddJsonOptions(options =>
@ -189,7 +189,7 @@ var tusDiskStore = new tusdotnet.Stores.TusDiskStore(
app.MapTus("/files/tus", (_) => Task.FromResult<DefaultTusConfiguration>(new() app.MapTus("/files/tus", (_) => Task.FromResult<DefaultTusConfiguration>(new()
{ {
Store = tusDiskStore, Store = tusDiskStore,
Events = new() Events = new Events
{ {
OnAuthorizeAsync = async eventContext => OnAuthorizeAsync = async eventContext =>
{ {
@ -203,8 +203,8 @@ app.MapTus("/files/tus", (_) => Task.FromResult<DefaultTusConfiguration>(new()
} }
var httpContext = eventContext.HttpContext; var httpContext = eventContext.HttpContext;
var user = httpContext.User; var user = httpContext.Items["CurrentUser"] as Account;
if (!user.Identity?.IsAuthenticated ?? true) if (user is null)
{ {
eventContext.FailRequest(HttpStatusCode.Unauthorized); eventContext.FailRequest(HttpStatusCode.Unauthorized);
return; return;
@ -223,12 +223,7 @@ app.MapTus("/files/tus", (_) => Task.FromResult<DefaultTusConfiguration>(new()
OnFileCompleteAsync = async eventContext => OnFileCompleteAsync = async eventContext =>
{ {
var httpContext = eventContext.HttpContext; var httpContext = eventContext.HttpContext;
var user = httpContext.User; if (httpContext.Items["CurrentUser"] is not Account user) return;
var userId = long.Parse(user.FindFirst("user_id")!.Value);
var db = httpContext.RequestServices.GetRequiredService<AppDatabase>();
var account = await db.Accounts.FindAsync(userId);
if (account is null) return;
var file = await eventContext.GetFileAsync(); var file = await eventContext.GetFileAsync();
var metadata = await file.GetMetadataAsync(eventContext.CancellationToken); var metadata = await file.GetMetadataAsync(eventContext.CancellationToken);
@ -238,7 +233,7 @@ app.MapTus("/files/tus", (_) => Task.FromResult<DefaultTusConfiguration>(new()
var fileService = eventContext.HttpContext.RequestServices.GetRequiredService<FileService>(); var fileService = eventContext.HttpContext.RequestServices.GetRequiredService<FileService>();
var info = await fileService.AnalyzeFileAsync(account, file.Id, fileStream, fileName, contentType); var info = await fileService.AnalyzeFileAsync(user, file.Id, fileStream, fileName, contentType);
var jsonOptions = httpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value var jsonOptions = httpContext.RequestServices.GetRequiredService<IOptions<JsonOptions>>().Value
.JsonSerializerOptions; .JsonSerializerOptions;