Use affiliation spell for registeration

This commit is contained in:
2025-12-02 00:54:57 +08:00
parent 13b2e46ecc
commit 2cce5ebf80
5 changed files with 72 additions and 15 deletions

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using DysonNetwork.Pass.Affiliation;
using DysonNetwork.Pass.Auth; using DysonNetwork.Pass.Auth;
using DysonNetwork.Pass.Credit; using DysonNetwork.Pass.Credit;
using DysonNetwork.Pass.Permission; using DysonNetwork.Pass.Permission;
@@ -22,6 +23,7 @@ public class AccountController(
SubscriptionService subscriptions, SubscriptionService subscriptions,
AccountEventService events, AccountEventService events,
SocialCreditService socialCreditService, SocialCreditService socialCreditService,
AffiliationSpellService ars,
GeoIpService geo GeoIpService geo
) : ControllerBase ) : ControllerBase
{ {
@@ -103,6 +105,8 @@ public class AccountController(
[MaxLength(32)] public string Language { get; set; } = "en-us"; [MaxLength(32)] public string Language { get; set; } = "en-us";
[Required] public string CaptchaToken { get; set; } = string.Empty; [Required] public string CaptchaToken { get; set; } = string.Empty;
public string? AffiliationSpell { get; set; }
} }
public class AccountCreateValidateRequest public class AccountCreateValidateRequest
@@ -118,6 +122,8 @@ public class AccountController(
[RegularExpression(@"^[^+]+@[^@]+\.[^@]+$", ErrorMessage = "Email address cannot contain '+' symbol.")] [RegularExpression(@"^[^+]+@[^@]+\.[^@]+$", ErrorMessage = "Email address cannot contain '+' symbol.")]
[MaxLength(1024)] [MaxLength(1024)]
public string? Email { get; set; } public string? Email { get; set; }
public string? AffiliationSpell { get; set; }
} }
[HttpPost("validate")] [HttpPost("validate")]
@@ -138,6 +144,12 @@ public class AccountController(
return BadRequest("Email has already been used."); return BadRequest("Email has already been used.");
} }
if (request.AffiliationSpell is not null)
{
if (!await ars.CheckAffiliationSpellHasTaken(request.AffiliationSpell))
return BadRequest("No affiliation spell has been found.");
}
return Ok("Everything seems good."); return Ok("Everything seems good.");
} }

View File

@@ -1,4 +1,5 @@
using System.Globalization; using System.Globalization;
using DysonNetwork.Pass.Affiliation;
using DysonNetwork.Pass.Auth.OpenId; using DysonNetwork.Pass.Auth.OpenId;
using DysonNetwork.Pass.Localization; using DysonNetwork.Pass.Localization;
using DysonNetwork.Pass.Mailer; using DysonNetwork.Pass.Mailer;
@@ -24,6 +25,7 @@ public class AccountService(
FileService.FileServiceClient files, FileService.FileServiceClient files,
FileReferenceService.FileReferenceServiceClient fileRefs, FileReferenceService.FileReferenceServiceClient fileRefs,
AccountUsernameService uname, AccountUsernameService uname,
AffiliationSpellService ars,
EmailService mailer, EmailService mailer,
RingService.RingServiceClient pusher, RingService.RingServiceClient pusher,
IStringLocalizer<NotificationResource> localizer, IStringLocalizer<NotificationResource> localizer,
@@ -101,6 +103,7 @@ public class AccountService(
string? password, string? password,
string language = "en-US", string language = "en-US",
string region = "en", string region = "en",
string? affiliationSpell = null,
bool isEmailVerified = false, bool isEmailVerified = false,
bool isActivated = false bool isActivated = false
) )
@@ -113,7 +116,7 @@ public class AccountService(
).CountAsync(); ).CountAsync();
if (dupeEmailCount > 0) if (dupeEmailCount > 0)
throw new InvalidOperationException("Account email has already been used."); throw new InvalidOperationException("Account email has already been used.");
var account = new SnAccount var account = new SnAccount
{ {
Name = name, Name = name,
@@ -122,7 +125,7 @@ public class AccountService(
Region = region, Region = region,
Contacts = Contacts =
[ [
new() new SnAccountContact
{ {
Type = Shared.Models.AccountContactType.Email, Type = Shared.Models.AccountContactType.Email,
Content = email, Content = email,
@@ -144,6 +147,9 @@ public class AccountService(
Profile = new SnAccountProfile() Profile = new SnAccountProfile()
}; };
if (affiliationSpell is not null)
await ars.CreateAffiliationResult(affiliationSpell, $"account:{account.Id}");
if (isActivated) if (isActivated)
{ {
account.ActivatedAt = SystemClock.Instance.GetCurrentInstant(); account.ActivatedAt = SystemClock.Instance.GetCurrentInstant();
@@ -193,10 +199,7 @@ public class AccountService(
displayName, displayName,
userInfo.Email, userInfo.Email,
null, null,
"en-US", isEmailVerified: userInfo.EmailVerified
"en",
userInfo.EmailVerified,
userInfo.EmailVerified
); );
} }

View File

@@ -1,3 +1,4 @@
using System.ComponentModel.DataAnnotations;
using DysonNetwork.Shared.Models; using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -7,9 +8,31 @@ namespace DysonNetwork.Pass.Affiliation;
[ApiController] [ApiController]
[Route("/api/affiliations")] [Route("/api/affiliations")]
public class AffiliationSpellController(AppDatabase db) : ControllerBase public class AffiliationSpellController(AppDatabase db, AffiliationSpellService ars) : ControllerBase
{ {
[HttpGet("me")] public class CreateAffiliationSpellRequest
{
[MaxLength(1024)] public string? Spell { get; set; }
}
[HttpPost]
[Authorize]
public async Task<ActionResult<SnAffiliationSpell>> CreateSpell([FromBody] CreateAffiliationSpellRequest request)
{
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try
{
var spell = await ars.CreateAffiliationSpell(currentUser.Id, request.Spell);
return Ok(spell);
}
catch (InvalidOperationException e)
{
return BadRequest(e.Message);
}
}
[HttpGet]
[Authorize] [Authorize]
public async Task<ActionResult<List<SnAffiliationSpell>>> ListCreatedSpells( public async Task<ActionResult<List<SnAffiliationSpell>>> ListCreatedSpells(
[FromQuery(Name = "order")] string orderBy = "date", [FromQuery(Name = "order")] string orderBy = "date",

View File

@@ -9,26 +9,42 @@ public class AffiliationSpellService(AppDatabase db)
public async Task<SnAffiliationSpell> CreateAffiliationSpell(Guid accountId, string? spellWord) public async Task<SnAffiliationSpell> CreateAffiliationSpell(Guid accountId, string? spellWord)
{ {
spellWord ??= _GenerateRandomString(8); spellWord ??= _GenerateRandomString(8);
var hasTaken = await db.AffiliationSpells.AnyAsync(s => s.Spell == spellWord); if (await CheckAffiliationSpellHasTaken(spellWord))
if (hasTaken) throw new InvalidOperationException("The spell has been taken."); throw new InvalidOperationException("The spell has been taken.");
var spell = new SnAffiliationSpell var spell = new SnAffiliationSpell
{ {
AccountId = accountId, AccountId = accountId,
Spell = spellWord Spell = spellWord
}; };
db.AffiliationSpells.Add(spell); db.AffiliationSpells.Add(spell);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return spell; return spell;
} }
public async Task<SnAffiliationSpell?> GetAffiliationSpell(string spellWord) public async Task<SnAffiliationResult> CreateAffiliationResult(string spellWord, string resourceId)
{ {
var spell = await db.AffiliationSpells.FirstOrDefaultAsync(s => s.Spell == spellWord); var spell =
return spell; await db.AffiliationSpells.FirstOrDefaultAsync(a => a.Spell == spellWord);
if (spell is null) throw new InvalidOperationException("The spell was not found.");
var result = new SnAffiliationResult
{
Spell = spell,
ResourceIdentifier = resourceId
};
db.AffiliationResults.Add(result);
await db.SaveChangesAsync();
return result;
} }
public async Task<bool> CheckAffiliationSpellHasTaken(string spellWord)
{
return await db.AffiliationSpells.AnyAsync(s => s.Spell == spellWord);
}
private static string _GenerateRandomString(int length) private static string _GenerateRandomString(int length)
{ {
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -40,6 +56,7 @@ public class AffiliationSpellService(AppDatabase db)
rng.GetBytes(bytes); rng.GetBytes(bytes);
result[i] = chars[bytes[0] % chars.Length]; result[i] = chars[bytes[0] % chars.Length];
} }
return new string(result); return new string(result);
} }
} }

View File

@@ -11,6 +11,7 @@ using NodaTime.Serialization.SystemTextJson;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using DysonNetwork.Pass.Account.Presences; using DysonNetwork.Pass.Account.Presences;
using DysonNetwork.Pass.Affiliation;
using DysonNetwork.Pass.Auth.OidcProvider.Options; using DysonNetwork.Pass.Auth.OidcProvider.Options;
using DysonNetwork.Pass.Auth.OidcProvider.Services; using DysonNetwork.Pass.Auth.OidcProvider.Services;
using DysonNetwork.Pass.Credit; using DysonNetwork.Pass.Credit;
@@ -159,6 +160,7 @@ public static class ServiceCollectionExtensions
services.AddScoped<ExperienceService>(); services.AddScoped<ExperienceService>();
services.AddScoped<RealmService>(); services.AddScoped<RealmService>();
services.AddScoped<LotteryService>(); services.AddScoped<LotteryService>();
services.AddScoped<AffiliationSpellService>();
services.AddScoped<SpotifyPresenceService>(); services.AddScoped<SpotifyPresenceService>();
services.AddScoped<SteamPresenceService>(); services.AddScoped<SteamPresenceService>();