♻️ Basically completed the separate of account service
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using OtpNet;
|
||||
@@ -19,12 +20,12 @@ public class Account : ModelBase
|
||||
|
||||
public Profile Profile { get; set; } = null!;
|
||||
public ICollection<AccountContact> Contacts { get; set; } = new List<AccountContact>();
|
||||
public ICollection<Badge> Badges { get; set; } = new List<Badge>();
|
||||
public ICollection<AccountBadge> Badges { get; set; } = new List<AccountBadge>();
|
||||
|
||||
[JsonIgnore] public ICollection<AccountAuthFactor> AuthFactors { get; set; } = new List<AccountAuthFactor>();
|
||||
[JsonIgnore] public ICollection<AccountConnection> Connections { get; set; } = new List<AccountConnection>();
|
||||
[JsonIgnore] public ICollection<Auth.Session> Sessions { get; set; } = new List<Auth.Session>();
|
||||
[JsonIgnore] public ICollection<Auth.Challenge> Challenges { get; set; } = new List<Auth.Challenge>();
|
||||
[JsonIgnore] public ICollection<Auth.AuthSession> Sessions { get; set; } = new List<Auth.AuthSession>();
|
||||
[JsonIgnore] public ICollection<Auth.AuthChallenge> Challenges { get; set; } = new List<Auth.AuthChallenge>();
|
||||
|
||||
[JsonIgnore] public ICollection<Relationship> OutgoingRelationships { get; set; } = new List<Relationship>();
|
||||
[JsonIgnore] public ICollection<Relationship> IncomingRelationships { get; set; } = new List<Relationship>();
|
||||
|
@@ -34,9 +34,9 @@ public class AccountController(
|
||||
}
|
||||
|
||||
[HttpGet("{name}/badges")]
|
||||
[ProducesResponseType<List<Badge>>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<List<AccountBadge>>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<List<Badge>>> GetBadgesByName(string name)
|
||||
public async Task<ActionResult<List<AccountBadge>>> GetBadgesByName(string name)
|
||||
{
|
||||
var account = await db.Accounts
|
||||
.Include(e => e.Badges)
|
||||
|
@@ -1,12 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Pass.Auth;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Storage;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
@@ -16,7 +14,6 @@ namespace DysonNetwork.Pass.Account;
|
||||
public class AccountCurrentController(
|
||||
AppDatabase db,
|
||||
AccountService accounts,
|
||||
FileReferenceService fileRefService,
|
||||
AccountEventService events,
|
||||
AuthService auth
|
||||
) : ControllerBase
|
||||
@@ -97,58 +94,12 @@ public class AccountCurrentController(
|
||||
|
||||
if (request.PictureId is not null)
|
||||
{
|
||||
var picture = await db.Files.Where(f => f.Id == request.PictureId).FirstOrDefaultAsync();
|
||||
if (picture is null) return BadRequest("Invalid picture id, unable to find the file on cloud.");
|
||||
|
||||
var profileResourceId = $"profile:{profile.Id}";
|
||||
|
||||
// Remove old references for the profile picture
|
||||
if (profile.Picture is not null)
|
||||
{
|
||||
var oldPictureRefs =
|
||||
await fileRefService.GetResourceReferencesAsync(profileResourceId, "profile.picture");
|
||||
foreach (var oldRef in oldPictureRefs)
|
||||
{
|
||||
await fileRefService.DeleteReferenceAsync(oldRef.Id);
|
||||
}
|
||||
}
|
||||
|
||||
profile.Picture = picture.ToReferenceObject();
|
||||
|
||||
// Create new reference
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
picture.Id,
|
||||
"profile.picture",
|
||||
profileResourceId
|
||||
);
|
||||
// TODO: Create reference, set profile picture
|
||||
}
|
||||
|
||||
if (request.BackgroundId is not null)
|
||||
{
|
||||
var background = await db.Files.Where(f => f.Id == request.BackgroundId).FirstOrDefaultAsync();
|
||||
if (background is null) return BadRequest("Invalid background id, unable to find the file on cloud.");
|
||||
|
||||
var profileResourceId = $"profile:{profile.Id}";
|
||||
|
||||
// Remove old references for the profile background
|
||||
if (profile.Background is not null)
|
||||
{
|
||||
var oldBackgroundRefs =
|
||||
await fileRefService.GetResourceReferencesAsync(profileResourceId, "profile.background");
|
||||
foreach (var oldRef in oldBackgroundRefs)
|
||||
{
|
||||
await fileRefService.DeleteReferenceAsync(oldRef.Id);
|
||||
}
|
||||
}
|
||||
|
||||
profile.Background = background.ToReferenceObject();
|
||||
|
||||
// Create new reference
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
background.Id,
|
||||
"profile.background",
|
||||
profileResourceId
|
||||
);
|
||||
// TODO: Create reference, set profile background
|
||||
}
|
||||
|
||||
db.Update(profile);
|
||||
@@ -438,7 +389,7 @@ public class AccountCurrentController(
|
||||
public string UserAgent { get; set; } = null!;
|
||||
public string DeviceId { get; set; } = null!;
|
||||
public ChallengePlatform Platform { get; set; }
|
||||
public List<Session> Sessions { get; set; } = [];
|
||||
public List<AuthSession> Sessions { get; set; } = [];
|
||||
}
|
||||
|
||||
[HttpGet("devices")]
|
||||
@@ -446,7 +397,7 @@ public class AccountCurrentController(
|
||||
public async Task<ActionResult<List<AuthorizedDevice>>> GetDevices()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser ||
|
||||
HttpContext.Items["CurrentSession"] is not Session currentSession) return Unauthorized();
|
||||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized();
|
||||
|
||||
Response.Headers.Append("X-Auth-Session", currentSession.Id.ToString());
|
||||
|
||||
@@ -475,13 +426,13 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpGet("sessions")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<Session>>> GetSessions(
|
||||
public async Task<ActionResult<List<AuthSession>>> GetSessions(
|
||||
[FromQuery] int take = 20,
|
||||
[FromQuery] int offset = 0
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser ||
|
||||
HttpContext.Items["CurrentSession"] is not Session currentSession) return Unauthorized();
|
||||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized();
|
||||
|
||||
var query = db.AuthSessions
|
||||
.Include(session => session.Account)
|
||||
@@ -503,7 +454,7 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpDelete("sessions/{id:guid}")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Session>> DeleteSession(Guid id)
|
||||
public async Task<ActionResult<AuthSession>> DeleteSession(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
@@ -520,10 +471,10 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpDelete("sessions/current")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Session>> DeleteCurrentSession()
|
||||
public async Task<ActionResult<AuthSession>> DeleteCurrentSession()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser ||
|
||||
HttpContext.Items["CurrentSession"] is not Session currentSession) return Unauthorized();
|
||||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -537,7 +488,7 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[HttpPatch("sessions/{id:guid}/label")]
|
||||
public async Task<ActionResult<Session>> UpdateSessionLabel(Guid id, [FromBody] string label)
|
||||
public async Task<ActionResult<AuthSession>> UpdateSessionLabel(Guid id, [FromBody] string label)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
@@ -553,10 +504,10 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[HttpPatch("sessions/current/label")]
|
||||
public async Task<ActionResult<Session>> UpdateCurrentSessionLabel([FromBody] string label)
|
||||
public async Task<ActionResult<AuthSession>> UpdateCurrentSessionLabel([FromBody] string label)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser ||
|
||||
HttpContext.Items["CurrentSession"] is not Session currentSession) return Unauthorized();
|
||||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -672,9 +623,9 @@ public class AccountCurrentController(
|
||||
}
|
||||
|
||||
[HttpGet("badges")]
|
||||
[ProducesResponseType<List<Badge>>(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<List<AccountBadge>>(StatusCodes.Status200OK)]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<List<Badge>>> GetBadges()
|
||||
public async Task<ActionResult<List<AccountBadge>>> GetBadges()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
@@ -686,7 +637,7 @@ public class AccountCurrentController(
|
||||
|
||||
[HttpPost("badges/{id:guid}/active")]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<Badge>> ActivateBadge(Guid id)
|
||||
public async Task<ActionResult<AccountBadge>> ActivateBadge(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
|
@@ -1,21 +1,16 @@
|
||||
using System.Globalization;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Connection;
|
||||
using DysonNetwork.Pass.Storage;
|
||||
using DysonNetwork.Pass.Wallet;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NodaTime;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
public class AccountEventService(
|
||||
AppDatabase db,
|
||||
WebSocketService ws,
|
||||
ICacheService cache,
|
||||
PaymentService payment,
|
||||
ICacheService cache,
|
||||
IStringLocalizer<Localization.AccountEventResource> localizer
|
||||
)
|
||||
{
|
||||
@@ -34,7 +29,7 @@ public class AccountEventService(
|
||||
var cachedStatus = await cache.GetAsync<Status>(cacheKey);
|
||||
if (cachedStatus is not null)
|
||||
{
|
||||
cachedStatus!.IsOnline = !cachedStatus.IsInvisible && ws.GetAccountIsConnected(userId);
|
||||
cachedStatus!.IsOnline = !cachedStatus.IsInvisible; // && ws.GetAccountIsConnected(userId);
|
||||
return cachedStatus;
|
||||
}
|
||||
|
||||
@@ -44,7 +39,7 @@ public class AccountEventService(
|
||||
.Where(e => e.ClearedAt == null || e.ClearedAt > now)
|
||||
.OrderByDescending(e => e.CreatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
var isOnline = ws.GetAccountIsConnected(userId);
|
||||
var isOnline = false; // TODO: Get connection status
|
||||
if (status is not null)
|
||||
{
|
||||
status.IsOnline = !status.IsInvisible && isOnline;
|
||||
@@ -65,7 +60,7 @@ public class AccountEventService(
|
||||
};
|
||||
}
|
||||
|
||||
return new Status
|
||||
return new Status
|
||||
{
|
||||
Attitude = StatusAttitude.Neutral,
|
||||
IsOnline = false,
|
||||
@@ -86,7 +81,7 @@ public class AccountEventService(
|
||||
var cachedStatus = await cache.GetAsync<Status>(cacheKey);
|
||||
if (cachedStatus != null)
|
||||
{
|
||||
cachedStatus.IsOnline = !cachedStatus.IsInvisible && ws.GetAccountIsConnected(userId);
|
||||
cachedStatus.IsOnline = !cachedStatus.IsInvisible /* && ws.GetAccountIsConnected(userId) */;
|
||||
results[userId] = cachedStatus;
|
||||
}
|
||||
else
|
||||
@@ -109,7 +104,7 @@ public class AccountEventService(
|
||||
|
||||
foreach (var status in statusesFromDb)
|
||||
{
|
||||
var isOnline = ws.GetAccountIsConnected(status.AccountId);
|
||||
var isOnline = false; // ws.GetAccountIsConnected(status.AccountId);
|
||||
status.IsOnline = !status.IsInvisible && isOnline;
|
||||
results[status.AccountId] = status;
|
||||
var cacheKey = $"{StatusCacheKey}{status.AccountId}";
|
||||
@@ -122,7 +117,7 @@ public class AccountEventService(
|
||||
{
|
||||
foreach (var userId in usersWithoutStatus)
|
||||
{
|
||||
var isOnline = ws.GetAccountIsConnected(userId);
|
||||
var isOnline = false; // ws.GetAccountIsConnected(userId);
|
||||
var defaultStatus = new Status
|
||||
{
|
||||
Attitude = StatusAttitude.Neutral,
|
||||
@@ -198,11 +193,11 @@ public class AccountEventService(
|
||||
public async Task<CheckInResult> CheckInDaily(Account user)
|
||||
{
|
||||
var lockKey = $"{CheckInLockKey}{user.Id}";
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var lk = await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromMilliseconds(100));
|
||||
|
||||
|
||||
if (lk != null)
|
||||
await lk.ReleaseAsync();
|
||||
}
|
||||
@@ -210,9 +205,10 @@ public class AccountEventService(
|
||||
{
|
||||
// Ignore errors from this pre-check
|
||||
}
|
||||
|
||||
|
||||
// Now try to acquire the lock properly
|
||||
await using var lockObj = await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(5));
|
||||
await using var lockObj =
|
||||
await cache.AcquireLockAsync(lockKey, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(5));
|
||||
if (lockObj is null) throw new InvalidOperationException("Check-in was in progress.");
|
||||
|
||||
var cultureInfo = new CultureInfo(user.Language, false);
|
||||
@@ -274,7 +270,7 @@ public class AccountEventService(
|
||||
s.SetProperty(b => b.Experience, b => b.Experience + result.RewardExperience)
|
||||
);
|
||||
db.AccountCheckInResults.Add(result);
|
||||
await db.SaveChangesAsync(); // Don't forget to save changes to the database
|
||||
await db.SaveChangesAsync(); // Don't forget to save changes to the database
|
||||
|
||||
// The lock will be automatically released by the await using statement
|
||||
return result;
|
||||
|
@@ -1,16 +1,14 @@
|
||||
using System.Globalization;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Auth;
|
||||
using DysonNetwork.Pass.Auth.OpenId;
|
||||
using DysonNetwork.Pass.Email;
|
||||
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using DysonNetwork.Pass.Storage;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using EFCore.BulkExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NodaTime;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
using OtpNet;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
@@ -454,7 +452,7 @@ public class AccountService(
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<Session> UpdateSessionLabel(Account account, Guid sessionId, string label)
|
||||
public async Task<AuthSession> UpdateSessionLabel(Account account, Guid sessionId, string label)
|
||||
{
|
||||
var session = await db.AuthSessions
|
||||
.Include(s => s.Challenge)
|
||||
@@ -574,7 +572,7 @@ public class AccountService(
|
||||
/// This method will grant a badge to the account.
|
||||
/// Shouldn't be exposed to normal user and the user itself.
|
||||
/// </summary>
|
||||
public async Task<Badge> GrantBadge(Account account, Badge badge)
|
||||
public async Task<AccountBadge> GrantBadge(Account account, AccountBadge badge)
|
||||
{
|
||||
badge.AccountId = account.Id;
|
||||
db.Badges.Add(badge);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using Point = NetTopologySuite.Geometries.Point;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
@@ -1,7 +1,5 @@
|
||||
using Quartz;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Storage;
|
||||
using DysonNetwork.Pass.Storage.Handlers;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Geo;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
@@ -38,7 +36,7 @@ public class ActionLogService(GeoIpService geo, FlushBufferService fbs)
|
||||
else
|
||||
throw new ArgumentException("No user context was found");
|
||||
|
||||
if (request.HttpContext.Items["CurrentSession"] is Auth.Session currentSession)
|
||||
if (request.HttpContext.Items["CurrentSession"] is Auth.AuthSession currentSession)
|
||||
log.SessionId = currentSession.Id;
|
||||
|
||||
fbs.Enqueue(log);
|
||||
|
@@ -1,11 +1,12 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
public class Badge : ModelBase
|
||||
public class AccountBadge : ModelBase
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
[MaxLength(1024)] public string Type { get; set; } = null!;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
|
||||
|
@@ -1,23 +1,18 @@
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Pages.Emails;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using DysonNetwork.Pass.Resources.Localization;
|
||||
using DysonNetwork.Pass.Resources.Pages.Emails;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NodaTime;
|
||||
using EmailResource = DysonNetwork.Pass.Localization.EmailResource;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
||||
public class MagicSpellService(
|
||||
AppDatabase db,
|
||||
EmailService email,
|
||||
IConfiguration configuration,
|
||||
ILogger<MagicSpellService> logger,
|
||||
IStringLocalizer<Localization.EmailResource> localizer
|
||||
IStringLocalizer<EmailResource> localizer
|
||||
)
|
||||
{
|
||||
public async Task<MagicSpell> CreateMagicSpell(
|
||||
@@ -84,61 +79,61 @@ public class MagicSpellService(
|
||||
|
||||
try
|
||||
{
|
||||
switch (spell.Type)
|
||||
{
|
||||
case MagicSpellType.AccountActivation:
|
||||
await email.SendTemplatedEmailAsync<LandingEmail, LandingEmailModel>(
|
||||
contact.Account.Nick,
|
||||
contact.Content,
|
||||
localizer["EmailLandingTitle"],
|
||||
new LandingEmailModel
|
||||
{
|
||||
Name = contact.Account.Name,
|
||||
Link = link
|
||||
}
|
||||
);
|
||||
break;
|
||||
case MagicSpellType.AccountRemoval:
|
||||
await email.SendTemplatedEmailAsync<AccountDeletionEmail, AccountDeletionEmailModel>(
|
||||
contact.Account.Nick,
|
||||
contact.Content,
|
||||
localizer["EmailAccountDeletionTitle"],
|
||||
new AccountDeletionEmailModel
|
||||
{
|
||||
Name = contact.Account.Name,
|
||||
Link = link
|
||||
}
|
||||
);
|
||||
break;
|
||||
case MagicSpellType.AuthPasswordReset:
|
||||
await email.SendTemplatedEmailAsync<PasswordResetEmail, PasswordResetEmailModel>(
|
||||
contact.Account.Nick,
|
||||
contact.Content,
|
||||
localizer["EmailAccountDeletionTitle"],
|
||||
new PasswordResetEmailModel
|
||||
{
|
||||
Name = contact.Account.Name,
|
||||
Link = link
|
||||
}
|
||||
);
|
||||
break;
|
||||
case MagicSpellType.ContactVerification:
|
||||
if (spell.Meta["contact_method"] is not string contactMethod)
|
||||
throw new InvalidOperationException("Contact method is not found.");
|
||||
await email.SendTemplatedEmailAsync<ContactVerificationEmail, ContactVerificationEmailModel>(
|
||||
contact.Account.Nick,
|
||||
contactMethod!,
|
||||
localizer["EmailContactVerificationTitle"],
|
||||
new ContactVerificationEmailModel
|
||||
{
|
||||
Name = contact.Account.Name,
|
||||
Link = link
|
||||
}
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
// switch (spell.Type)
|
||||
// {
|
||||
// case MagicSpellType.AccountActivation:
|
||||
// await email.SendTemplatedEmailAsync<LandingEmail, LandingEmailModel>(
|
||||
// contact.Account.Nick,
|
||||
// contact.Content,
|
||||
// localizer["EmailLandingTitle"],
|
||||
// new LandingEmailModel
|
||||
// {
|
||||
// Name = contact.Account.Name,
|
||||
// Link = link
|
||||
// }
|
||||
// );
|
||||
// break;
|
||||
// case MagicSpellType.AccountRemoval:
|
||||
// await email.SendTemplatedEmailAsync<AccountDeletionEmail, AccountDeletionEmailModel>(
|
||||
// contact.Account.Nick,
|
||||
// contact.Content,
|
||||
// localizer["EmailAccountDeletionTitle"],
|
||||
// new AccountDeletionEmailModel
|
||||
// {
|
||||
// Name = contact.Account.Name,
|
||||
// Link = link
|
||||
// }
|
||||
// );
|
||||
// break;
|
||||
// case MagicSpellType.AuthPasswordReset:
|
||||
// await email.SendTemplatedEmailAsync<PasswordResetEmail, PasswordResetEmailModel>(
|
||||
// contact.Account.Nick,
|
||||
// contact.Content,
|
||||
// localizer["EmailAccountDeletionTitle"],
|
||||
// new PasswordResetEmailModel
|
||||
// {
|
||||
// Name = contact.Account.Name,
|
||||
// Link = link
|
||||
// }
|
||||
// );
|
||||
// break;
|
||||
// case MagicSpellType.ContactVerification:
|
||||
// if (spell.Meta["contact_method"] is not string contactMethod)
|
||||
// throw new InvalidOperationException("Contact method is not found.");
|
||||
// await email.SendTemplatedEmailAsync<ContactVerificationEmail, ContactVerificationEmailModel>(
|
||||
// contact.Account.Nick,
|
||||
// contactMethod!,
|
||||
// localizer["EmailContactVerificationTitle"],
|
||||
// new ContactVerificationEmailModel
|
||||
// {
|
||||
// Name = contact.Account.Name,
|
||||
// Link = link
|
||||
// }
|
||||
// );
|
||||
// break;
|
||||
// default:
|
||||
// throw new ArgumentOutOfRangeException();
|
||||
// }
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Pass.Auth;
|
||||
using DysonNetwork.Pass.Permission;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -69,7 +70,7 @@ public class NotificationController(AppDatabase db, NotificationService nty) : C
|
||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||
var currentUser = currentUserValue as Account;
|
||||
if (currentUser == null) return Unauthorized();
|
||||
var currentSession = currentSessionValue as Session;
|
||||
var currentSession = currentSessionValue as AuthSession;
|
||||
if (currentSession == null) return Unauthorized();
|
||||
|
||||
var result =
|
||||
@@ -87,7 +88,7 @@ public class NotificationController(AppDatabase db, NotificationService nty) : C
|
||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||
var currentUser = currentUserValue as Account;
|
||||
if (currentUser == null) return Unauthorized();
|
||||
var currentSession = currentSessionValue as Session;
|
||||
var currentSession = currentSessionValue as AuthSession;
|
||||
if (currentSession == null) return Unauthorized();
|
||||
|
||||
var affectedRows = await db.NotificationPushSubscriptions
|
||||
|
@@ -9,9 +9,9 @@ namespace DysonNetwork.Pass.Account;
|
||||
|
||||
public class NotificationService(
|
||||
AppDatabase db,
|
||||
WebSocketService ws,
|
||||
IHttpClientFactory httpFactory,
|
||||
IConfiguration config)
|
||||
IConfiguration config
|
||||
)
|
||||
{
|
||||
private readonly string _notifyTopic = config["Notifications:Topic"]!;
|
||||
private readonly Uri _notifyEndpoint = new(config["Notifications:Endpoint"]!);
|
||||
@@ -31,7 +31,7 @@ public class NotificationService(
|
||||
)
|
||||
{
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
|
||||
|
||||
// First check if a matching subscription exists
|
||||
var existingSubscription = await db.NotificationPushSubscriptions
|
||||
.Where(s => s.AccountId == account.Id)
|
||||
@@ -110,12 +110,6 @@ public class NotificationService(
|
||||
|
||||
public async Task DeliveryNotification(Notification notification)
|
||||
{
|
||||
ws.SendPacketToAccount(notification.AccountId, new WebSocketPacket
|
||||
{
|
||||
Type = "notifications.new",
|
||||
Data = notification
|
||||
});
|
||||
|
||||
// Pushing the notification
|
||||
var subscribers = await db.NotificationPushSubscriptions
|
||||
.Where(s => s.AccountId == notification.AccountId)
|
||||
@@ -164,11 +158,6 @@ public class NotificationService(
|
||||
{
|
||||
notification.Account = account;
|
||||
notification.AccountId = account.Id;
|
||||
ws.SendPacketToAccount(account.Id, new WebSocketPacket
|
||||
{
|
||||
Type = "notifications.new",
|
||||
Data = notification
|
||||
});
|
||||
}
|
||||
|
||||
var subscribers = await db.NotificationPushSubscriptions
|
||||
@@ -202,11 +191,6 @@ public class NotificationService(
|
||||
{
|
||||
notification.Account = account;
|
||||
notification.AccountId = account.Id;
|
||||
ws.SendPacketToAccount(account.Id, new WebSocketPacket
|
||||
{
|
||||
Type = "notifications.new",
|
||||
Data = notification
|
||||
});
|
||||
}
|
||||
|
||||
var accountsId = accounts.Select(x => x.Id).ToList();
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using DysonNetwork.Shared.Data;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Account;
|
||||
|
Reference in New Issue
Block a user