From 88977ccda3fe0e52001290b05c3f346ac0105981 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Fri, 16 May 2025 23:38:33 +0800 Subject: [PATCH] :recycle: Rebuilt own auth infra --- .../Account/ActionLogService.cs | 26 ++-- .../Account/RelationshipController.cs | 144 ++++++++++++++++-- .../Account/RelationshipService.cs | 77 ++++------ DysonNetwork.Sphere/AppDatabase.cs | 6 +- DysonNetwork.Sphere/Auth/Auth.cs | 77 ++++++++++ DysonNetwork.Sphere/Auth/AuthController.cs | 23 +-- .../20250515165017_AddActionLogs.Designer.cs | 1 + DysonNetwork.Sphere/Post/PostController.cs | 2 +- DysonNetwork.Sphere/Program.cs | 75 ++++----- DysonNetwork.sln.DotSettings.user | 4 + 10 files changed, 309 insertions(+), 126 deletions(-) create mode 100644 DysonNetwork.Sphere/Auth/Auth.cs diff --git a/DysonNetwork.Sphere/Account/ActionLogService.cs b/DysonNetwork.Sphere/Account/ActionLogService.cs index f3bfcc0..1f47b41 100644 --- a/DysonNetwork.Sphere/Account/ActionLogService.cs +++ b/DysonNetwork.Sphere/Account/ActionLogService.cs @@ -20,28 +20,32 @@ public class ActionLogService(AppDatabase db, GeoIpService geo) : IDisposable _creationQueue.Enqueue(log); } - - public void CreateActionLogFromRequest(string action, Dictionary meta, HttpRequest request) + + public void CreateActionLogFromRequest(string action, Dictionary meta, HttpRequest request, + Account? account = null) { - if (request.HttpContext.Items["CurrentUser"] is not Account currentUser) - throw new ArgumentException("No user context was found"); - if (request.HttpContext.Items["CurrentSession"] is not Auth.Session currentSession) - throw new ArgumentException("No session context was found"); - var log = new ActionLog { Action = action, - AccountId = currentUser.Id, - SessionId = currentSession.Id, Meta = meta, UserAgent = request.Headers.UserAgent, IpAddress = request.HttpContext.Connection.RemoteIpAddress?.ToString(), Location = geo.GetPointFromIp(request.HttpContext.Connection.RemoteIpAddress?.ToString()) }; - + + if (request.HttpContext.Items["CurrentUser"] is Account currentUser) + log.AccountId = currentUser.Id; + else if (account != null) + log.AccountId = account.Id; + else + throw new ArgumentException("No user context was found"); + + if (request.HttpContext.Items["CurrentSession"] is Auth.Session currentSession) + log.SessionId = currentSession.Id; + _creationQueue.Enqueue(log); } - + public async Task FlushQueue() { var workingQueue = new List(); diff --git a/DysonNetwork.Sphere/Account/RelationshipController.cs b/DysonNetwork.Sphere/Account/RelationshipController.cs index bfc1603..3ab1378 100644 --- a/DysonNetwork.Sphere/Account/RelationshipController.cs +++ b/DysonNetwork.Sphere/Account/RelationshipController.cs @@ -17,11 +17,14 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) : if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); var userId = currentUser.Id; - var totalCount = await db.AccountRelationships - .CountAsync(r => r.Account.Id == userId); - var relationships = await db.AccountRelationships - .Where(r => r.Account.Id == userId) + var query = db.AccountRelationships.AsQueryable() + .Where(r => r.RelatedId == userId); + var totalCount = await query.CountAsync(); + var relationships = await query .Include(r => r.Related) + .Include(r => r.Related.Profile) + .Include(r => r.Account) + .Include(r => r.Account.Profile) .Skip(offset) .Take(take) .ToListAsync(); @@ -30,21 +33,37 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) : return relationships; } - - public class RelationshipCreateRequest + + [HttpGet("requests")] + [Authorize] + public async Task>> ListSentRequests() + { + if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); + + var relationships = await db.AccountRelationships + .Where(r => r.AccountId == currentUser.Id && r.Status == RelationshipStatus.Pending) + .Include(r => r.Related) + .Include(r => r.Related.Profile) + .Include(r => r.Account) + .Include(r => r.Account.Profile) + .ToListAsync(); + + return relationships; + } + + public class RelationshipRequest { - [Required] public long UserId { get; set; } [Required] public RelationshipStatus Status { get; set; } } - [HttpPost] + [HttpPost("{userId:guid}")] [Authorize] - public async Task> CreateRelationship([FromBody] RelationshipCreateRequest request) + public async Task> CreateRelationship(Guid userId, [FromBody] RelationshipRequest request) { if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); - - var relatedUser = await db.Accounts.FindAsync(request.UserId); - if (relatedUser is null) return BadRequest("Invalid related user"); + + var relatedUser = await db.Accounts.FindAsync(userId); + if (relatedUser is null) return NotFound("Account was not found."); try { @@ -58,4 +77,105 @@ public class RelationshipController(AppDatabase db, RelationshipService rels) : return BadRequest(err.Message); } } + + [HttpPatch("{userId:guid}")] + [Authorize] + public async Task> UpdateRelationship(Guid userId, [FromBody] RelationshipRequest request) + { + if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); + + try + { + var relationship = await rels.UpdateRelationship(currentUser.Id, userId, request.Status); + return relationship; + } + catch (ArgumentException err) + { + return NotFound(err.Message); + } + catch (InvalidOperationException err) + { + return BadRequest(err.Message); + } + } + + [HttpPost("{userId:guid}/friends")] + [Authorize] + public async Task> SendFriendRequest(Guid userId) + { + if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); + + var relatedUser = await db.Accounts.FindAsync(userId); + if (relatedUser is null) return NotFound("Account was not found."); + + try + { + var relationship = await rels.SendFriendRequest(currentUser, relatedUser); + return relationship; + } + catch (InvalidOperationException err) + { + return BadRequest(err.Message); + } + } + + [HttpPost("{userId:guid}/friends/accept")] + [Authorize] + public async Task> AcceptFriendRequest(Guid userId) + { + if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); + + var relationship = await rels.GetRelationship(userId, currentUser.Id, RelationshipStatus.Pending); + if (relationship is null) return NotFound("Friend request was not found."); + + try + { + relationship = await rels.AcceptFriendRelationship(relationship); + return relationship; + } + catch (InvalidOperationException err) + { + return BadRequest(err.Message); + } + } + + [HttpPost("{userId:guid}/friends/decline")] + [Authorize] + public async Task> DeclineFriendRequest(Guid userId) + { + if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); + + var relationship = await rels.GetRelationship(userId, currentUser.Id, RelationshipStatus.Pending); + if (relationship is null) return NotFound("Friend request was not found."); + + try + { + relationship = await rels.AcceptFriendRelationship(relationship, status: RelationshipStatus.Blocked); + return relationship; + } + catch (InvalidOperationException err) + { + return BadRequest(err.Message); + } + } + + [HttpPost("{userId:guid}/block")] + [Authorize] + public async Task> BlockUser(Guid userId) + { + if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); + + var relatedUser = await db.Accounts.FindAsync(userId); + if (relatedUser is null) return NotFound("Account was not found."); + + try + { + var relationship = await rels.BlockAccount(currentUser, relatedUser); + return relationship; + } + catch (InvalidOperationException err) + { + return BadRequest(err.Message); + } + } } \ No newline at end of file diff --git a/DysonNetwork.Sphere/Account/RelationshipService.cs b/DysonNetwork.Sphere/Account/RelationshipService.cs index c235c97..c47a0c0 100644 --- a/DysonNetwork.Sphere/Account/RelationshipService.cs +++ b/DysonNetwork.Sphere/Account/RelationshipService.cs @@ -1,32 +1,31 @@ -using DysonNetwork.Sphere.Permission; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using NodaTime; namespace DysonNetwork.Sphere.Account; -public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCache cache) +public class RelationshipService(AppDatabase db, IMemoryCache cache) { - public async Task HasExistingRelationship(Account userA, Account userB) + public async Task HasExistingRelationship(Guid accountId, Guid relatedId) { var count = await db.AccountRelationships - .Where(r => (r.AccountId == userA.Id && r.AccountId == userB.Id) || - (r.AccountId == userB.Id && r.AccountId == userA.Id)) + .Where(r => (r.AccountId == accountId && r.RelatedId == relatedId) || + (r.AccountId == relatedId && r.AccountId == accountId)) .CountAsync(); return count > 0; } public async Task GetRelationship( - Account account, - Account related, - RelationshipStatus? status, + Guid accountId, + Guid relatedId, + RelationshipStatus? status = null, bool ignoreExpired = false ) { var now = Instant.FromDateTimeUtc(DateTime.UtcNow); - var queries = db.AccountRelationships - .Where(r => r.AccountId == account.Id && r.AccountId == related.Id); - if (ignoreExpired) queries = queries.Where(r => r.ExpiredAt > now); + var queries = db.AccountRelationships.AsQueryable() + .Where(r => r.AccountId == accountId && r.RelatedId == relatedId); + if (!ignoreExpired) queries = queries.Where(r => r.ExpiredAt > now); if (status is not null) queries = queries.Where(r => r.Status == status); var relationship = await queries.FirstOrDefaultAsync(); return relationship; @@ -37,7 +36,7 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa if (status == RelationshipStatus.Pending) throw new InvalidOperationException( "Cannot create relationship with pending status, use SendFriendRequest instead."); - if (await HasExistingRelationship(sender, target)) + if (await HasExistingRelationship(sender.Id, target.Id)) throw new InvalidOperationException("Found existing relationship between you and target user."); var relationship = new Relationship @@ -49,17 +48,23 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa db.AccountRelationships.Add(relationship); await db.SaveChangesAsync(); - await ApplyRelationshipPermissions(relationship); - + cache.Remove($"UserFriends_{relationship.AccountId}"); cache.Remove($"UserFriends_{relationship.RelatedId}"); return relationship; } + public async Task BlockAccount(Account sender, Account target) + { + if (await HasExistingRelationship(sender.Id, target.Id)) + return await UpdateRelationship(sender.Id, target.Id, RelationshipStatus.Blocked); + return await CreateRelationship(sender, target, RelationshipStatus.Blocked); + } + public async Task SendFriendRequest(Account sender, Account target) { - if (await HasExistingRelationship(sender, target)) + if (await HasExistingRelationship(sender.Id, target.Id)) throw new InvalidOperationException("Found existing relationship between you and target user."); var relationship = new Relationship @@ -81,7 +86,9 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa RelationshipStatus status = RelationshipStatus.Friends ) { - if (relationship.Status == RelationshipStatus.Pending) + if (relationship.Status != RelationshipStatus.Pending) + throw new ArgumentException("Cannot accept friend request that not in pending status."); + if (status == RelationshipStatus.Pending) throw new ArgumentException("Cannot accept friend request by setting the new status to pending."); // Whatever the receiver decides to apply which status to the relationship, @@ -100,27 +107,22 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa await db.SaveChangesAsync(); - await Task.WhenAll( - ApplyRelationshipPermissions(relationship), - ApplyRelationshipPermissions(relationshipBackward) - ); - cache.Remove($"UserFriends_{relationship.AccountId}"); cache.Remove($"UserFriends_{relationship.RelatedId}"); return relationshipBackward; } - public async Task UpdateRelationship(Account account, Account related, RelationshipStatus status) + public async Task UpdateRelationship(Guid accountId, Guid relatedId, RelationshipStatus status) { - var relationship = await GetRelationship(account, related, status); + var relationship = await GetRelationship(accountId, relatedId, status); if (relationship is null) throw new ArgumentException("There is no relationship between you and the user."); if (relationship.Status == status) return relationship; relationship.Status = status; db.Update(relationship); await db.SaveChangesAsync(); - await ApplyRelationshipPermissions(relationship); - cache.Remove($"UserFriends_{related.Id}"); + cache.Remove($"UserFriends_{accountId}"); + cache.Remove($"UserFriends_{relatedId}"); return relationship; } @@ -139,27 +141,10 @@ public class RelationshipService(AppDatabase db, PermissionService pm, IMemoryCa return friends ?? []; } - private async Task ApplyRelationshipPermissions(Relationship relationship) + public async Task HasRelationshipWithStatus(Guid accountId, Guid relatedId, + RelationshipStatus status = RelationshipStatus.Friends) { - // Apply the relationship permissions to casbin enforcer - // domain: the user - // status is friends: all permissions are allowed by default, expect specially specified - // status is blocked: all permissions are disallowed by default, expect specially specified - // others: use the default permissions by design - - var domain = $"user:{relationship.AccountId.ToString()}"; - var target = $"user:{relationship.RelatedId.ToString()}"; - - await pm.RemovePermissionNode(target, domain, "*"); - - bool? value = relationship.Status switch - { - RelationshipStatus.Friends => true, - RelationshipStatus.Blocked => false, - _ => null, - }; - if (value is null) return; - - await pm.AddPermissionNode(target, domain, "*", value); + var relationship = await GetRelationship(accountId, relatedId, status); + return relationship is not null; } } \ No newline at end of file diff --git a/DysonNetwork.Sphere/AppDatabase.cs b/DysonNetwork.Sphere/AppDatabase.cs index c253df9..6348e0c 100644 --- a/DysonNetwork.Sphere/AppDatabase.cs +++ b/DysonNetwork.Sphere/AppDatabase.cs @@ -81,15 +81,15 @@ public class AppDatabase( { var dataSourceBuilder = new NpgsqlDataSourceBuilder(configuration.GetConnectionString("App")); dataSourceBuilder.EnableDynamicJson(); + dataSourceBuilder.UseNetTopologySuite(); dataSourceBuilder.UseNodaTime(); - var dataSource = dataSourceBuilder.Build(); optionsBuilder.UseNpgsql( - dataSource, + dataSourceBuilder.Build(), opt => opt - .UseNodaTime() .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) .UseNetTopologySuite() + .UseNodaTime() ).UseSnakeCaseNamingConvention(); optionsBuilder.UseAsyncSeeding(async (context, _, cancellationToken) => diff --git a/DysonNetwork.Sphere/Auth/Auth.cs b/DysonNetwork.Sphere/Auth/Auth.cs new file mode 100644 index 0000000..e853f8e --- /dev/null +++ b/DysonNetwork.Sphere/Auth/Auth.cs @@ -0,0 +1,77 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Cryptography; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; + +namespace DysonNetwork.Sphere.Auth; + +public static class AuthConstants +{ + public const string SchemeName = "DysonToken"; + public const string TokenQueryParamName = "tk"; +} + +public class DysonTokenAuthOptions : AuthenticationSchemeOptions; + +public class DysonTokenAuthHandler : AuthenticationHandler +{ + private TokenValidationParameters _tokenValidationParameters; + + public DysonTokenAuthHandler( + IOptionsMonitor options, + IConfiguration configuration, + ILoggerFactory logger, + UrlEncoder encoder) + : base(options, logger, encoder) + { + var publicKey = File.ReadAllText(configuration["Jwt:PublicKeyPath"]!); + var rsa = RSA.Create(); + rsa.ImportFromPem(publicKey); + _tokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = false, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = "solar-network", + IssuerSigningKey = new RsaSecurityKey(rsa) + }; + } + + protected override async Task HandleAuthenticateAsync() + { + var token = _ExtractToken(Request); + + if (string.IsNullOrEmpty(token)) + return AuthenticateResult.Fail("No token was provided."); + + try + { + var tokenHandler = new JwtSecurityTokenHandler(); + var principal = tokenHandler.ValidateToken(token, _tokenValidationParameters, out var validatedToken); + + var ticket = new AuthenticationTicket(principal, AuthConstants.SchemeName); + return AuthenticateResult.Success(ticket); + } + catch (Exception ex) + { + return AuthenticateResult.Fail($"Authentication failed: {ex.Message}"); + } + } + + private static string? _ExtractToken(HttpRequest request) + { + if (request.Query.TryGetValue(AuthConstants.TokenQueryParamName, out var queryToken)) + return queryToken; + + var authHeader = request.Headers.Authorization.ToString(); + if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + return authHeader["Bearer ".Length..].Trim(); + + return request.Cookies.TryGetValue(AuthConstants.TokenQueryParamName, out var cookieToken) + ? cookieToken + : null; + } +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Auth/AuthController.cs b/DysonNetwork.Sphere/Auth/AuthController.cs index b3aa860..270ef2a 100644 --- a/DysonNetwork.Sphere/Auth/AuthController.cs +++ b/DysonNetwork.Sphere/Auth/AuthController.cs @@ -66,11 +66,11 @@ public class AuthController( await db.AuthChallenges.AddAsync(challenge); await db.SaveChangesAsync(); - + als.CreateActionLogFromRequest(ActionLogType.ChallengeAttempt, - new Dictionary { { "challenge_id", challenge.Id } }, Request + new Dictionary { { "challenge_id", challenge.Id } }, Request, account ); - + return challenge; } @@ -115,7 +115,7 @@ public class AuthController( [FromBody] PerformChallengeRequest request ) { - var challenge = await db.AuthChallenges.FindAsync(id); + var challenge = await db.AuthChallenges.Include(e => e.Account).FirstOrDefaultAsync(e => e.Id == id); if (challenge is null) return NotFound("Auth challenge was not found."); var factor = await db.AccountAuthFactors.FindAsync(request.FactorId); @@ -133,10 +133,11 @@ public class AuthController( challenge.BlacklistFactors.Add(factor.Id); db.Update(challenge); als.CreateActionLogFromRequest(ActionLogType.ChallengeSuccess, - new Dictionary { + new Dictionary + { { "challenge_id", challenge.Id }, { "factor_id", factor.Id } - }, Request + }, Request, challenge.Account ); } else @@ -149,10 +150,11 @@ public class AuthController( challenge.FailedAttempts++; db.Update(challenge); als.CreateActionLogFromRequest(ActionLogType.ChallengeFailure, - new Dictionary { + new Dictionary + { { "challenge_id", challenge.Id }, { "factor_id", factor.Id } - }, Request + }, Request, challenge.Account ); await db.SaveChangesAsync(); return BadRequest("Invalid password."); @@ -161,10 +163,11 @@ public class AuthController( if (challenge.StepRemain == 0) { als.CreateActionLogFromRequest(ActionLogType.NewLogin, - new Dictionary { + new Dictionary + { { "challenge_id", challenge.Id }, { "account_id", challenge.AccountId } - }, Request + }, Request, challenge.Account ); } diff --git a/DysonNetwork.Sphere/Migrations/20250515165017_AddActionLogs.Designer.cs b/DysonNetwork.Sphere/Migrations/20250515165017_AddActionLogs.Designer.cs index 2c46e98..0465807 100644 --- a/DysonNetwork.Sphere/Migrations/20250515165017_AddActionLogs.Designer.cs +++ b/DysonNetwork.Sphere/Migrations/20250515165017_AddActionLogs.Designer.cs @@ -12,6 +12,7 @@ using NetTopologySuite.Geometries; using NodaTime; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; using NpgsqlTypes; +using Point = NetTopologySuite.Geometries.Point; #nullable disable diff --git a/DysonNetwork.Sphere/Post/PostController.cs b/DysonNetwork.Sphere/Post/PostController.cs index 1fb6f15..b1f7394 100644 --- a/DysonNetwork.Sphere/Post/PostController.cs +++ b/DysonNetwork.Sphere/Post/PostController.cs @@ -137,7 +137,7 @@ public class PostController( [MaxLength(1024)] public string? Title { get; set; } [MaxLength(4096)] public string? Description { get; set; } public string? Content { get; set; } - public PostVisibility? Visibility { get; set; } + public PostVisibility? Visibility { get; set; } = PostVisibility.Public; public PostType? Type { get; set; } [MaxLength(16)] public List? Tags { get; set; } [MaxLength(8)] public List? Categories { get; set; } diff --git a/DysonNetwork.Sphere/Program.cs b/DysonNetwork.Sphere/Program.cs index 9cc5335..86520f9 100644 --- a/DysonNetwork.Sphere/Program.cs +++ b/DysonNetwork.Sphere/Program.cs @@ -54,13 +54,15 @@ builder.Services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower; options.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); -}).AddDataAnnotationsLocalization(options => { +}).AddDataAnnotationsLocalization(options => +{ options.DataAnnotationLocalizerProvider = (type, factory) => factory.Create(typeof(SharedResource)); }); builder.Services.AddRazorPages(); -builder.Services.Configure(options => { +builder.Services.Configure(options => +{ var supportedCultures = new[] { new CultureInfo("en-us"), @@ -82,21 +84,12 @@ builder.Services.AddRateLimiter(o => o.AddFixedWindowLimiter(policyName: "fixed" })); builder.Services.AddCors(); builder.Services.AddAuthorization(); -builder.Services.AddAuthentication("Bearer").AddJwtBearer(options => -{ - var publicKey = File.ReadAllText(builder.Configuration["Jwt:PublicKeyPath"]!); - var rsa = RSA.Create(); - rsa.ImportFromPem(publicKey); - options.TokenValidationParameters = new TokenValidationParameters +builder.Services.AddAuthentication(options => { - ValidateIssuer = true, - ValidateAudience = false, - ValidateLifetime = true, - ValidateIssuerSigningKey = true, - ValidIssuer = "solar-network", - IssuerSigningKey = new RsaSecurityKey(rsa) - }; -}); + options.DefaultAuthenticateScheme = AuthConstants.SchemeName; + options.DefaultChallengeScheme = AuthConstants.SchemeName; + }) + .AddScheme(AuthConstants.SchemeName, _ => { }); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => @@ -155,7 +148,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddSingleton(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -191,7 +184,7 @@ builder.Services.AddQuartz(q => .WithIdentity("CloudFilesUnusedRecyclingTrigger") .WithSimpleSchedule(o => o.WithIntervalInHours(1).RepeatForever()) ); - + var actionLogFlushJob = new JobKey("ActionLogFlush"); q.AddJob(opts => opts.WithIdentity(actionLogFlushJob)); q.AddTrigger(opts => opts @@ -283,34 +276,30 @@ app.MapTus("/files/tus", _ => Task.FromResult(new() { using var scope = eventContext.HttpContext.RequestServices.CreateScope(); var services = scope.ServiceProvider; - - try - { - var httpContext = eventContext.HttpContext; - if (httpContext.Items["CurrentUser"] is not Account user) return; - var file = await eventContext.GetFileAsync(); - var metadata = await file.GetMetadataAsync(eventContext.CancellationToken); - var fileName = metadata.TryGetValue("filename", out var fn) ? fn.GetString(Encoding.UTF8) : "uploaded_file"; - var contentType = metadata.TryGetValue("content-type", out var ct) ? ct.GetString(Encoding.UTF8) : null; - - var fileStream = await file.GetContentAsync(eventContext.CancellationToken); - - var fileService = services.GetRequiredService(); - var info = await fileService.ProcessNewFileAsync(user, file.Id, fileStream, fileName, contentType); + var httpContext = eventContext.HttpContext; + if (httpContext.Items["CurrentUser"] is not Account user) return; - using var finalScope = eventContext.HttpContext.RequestServices.CreateScope(); - var jsonOptions = finalScope.ServiceProvider.GetRequiredService>().Value.JsonSerializerOptions; - var infoJson = JsonSerializer.Serialize(info, jsonOptions); - eventContext.HttpContext.Response.Headers.Append("X-FileInfo", infoJson); + var file = await eventContext.GetFileAsync(); + var metadata = await file.GetMetadataAsync(eventContext.CancellationToken); + var fileName = metadata.TryGetValue("filename", out var fn) + ? fn.GetString(Encoding.UTF8) + : "uploaded_file"; + var contentType = metadata.TryGetValue("content-type", out var ct) ? ct.GetString(Encoding.UTF8) : null; - // Dispose the stream after all processing is complete - await fileStream.DisposeAsync(); - } - catch (Exception ex) - { - throw; - } + var fileStream = await file.GetContentAsync(eventContext.CancellationToken); + + var fileService = services.GetRequiredService(); + var info = await fileService.ProcessNewFileAsync(user, file.Id, fileStream, fileName, contentType); + + using var finalScope = eventContext.HttpContext.RequestServices.CreateScope(); + var jsonOptions = finalScope.ServiceProvider.GetRequiredService>().Value + .JsonSerializerOptions; + var infoJson = JsonSerializer.Serialize(info, jsonOptions); + eventContext.HttpContext.Response.Headers.Append("X-FileInfo", infoJson); + + // Dispose the stream after all processing is complete + await fileStream.DisposeAsync(); } } })); diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index ec70261..ae4f23f 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -1,6 +1,7 @@  ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -35,6 +36,8 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -55,6 +58,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded