🐛 Bug fixes and improvements
This commit is contained in:
parent
758186f674
commit
84a88222bd
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -46,6 +46,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
|
||||||
{
|
{
|
||||||
switch (spell.Type)
|
switch (spell.Type)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
@ -145,4 +133,3 @@ public class WebSocketController : ControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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");
|
@ -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),
|
@ -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");
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user