diff --git a/DysonNetwork.Pass/Account/AccountController.cs b/DysonNetwork.Pass/Account/AccountController.cs index fdf371f..543ff40 100644 --- a/DysonNetwork.Pass/Account/AccountController.cs +++ b/DysonNetwork.Pass/Account/AccountController.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using DysonNetwork.Pass.Affiliation; using DysonNetwork.Pass.Auth; using DysonNetwork.Pass.Credit; using DysonNetwork.Pass.Permission; @@ -22,6 +23,7 @@ public class AccountController( SubscriptionService subscriptions, AccountEventService events, SocialCreditService socialCreditService, + AffiliationSpellService ars, GeoIpService geo ) : ControllerBase { @@ -103,6 +105,8 @@ public class AccountController( [MaxLength(32)] public string Language { get; set; } = "en-us"; [Required] public string CaptchaToken { get; set; } = string.Empty; + + public string? AffiliationSpell { get; set; } } public class AccountCreateValidateRequest @@ -118,6 +122,8 @@ public class AccountController( [RegularExpression(@"^[^+]+@[^@]+\.[^@]+$", ErrorMessage = "Email address cannot contain '+' symbol.")] [MaxLength(1024)] public string? Email { get; set; } + + public string? AffiliationSpell { get; set; } } [HttpPost("validate")] @@ -138,6 +144,12 @@ public class AccountController( 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."); } diff --git a/DysonNetwork.Pass/Account/AccountService.cs b/DysonNetwork.Pass/Account/AccountService.cs index e5d2c08..e716f84 100644 --- a/DysonNetwork.Pass/Account/AccountService.cs +++ b/DysonNetwork.Pass/Account/AccountService.cs @@ -1,4 +1,5 @@ using System.Globalization; +using DysonNetwork.Pass.Affiliation; using DysonNetwork.Pass.Auth.OpenId; using DysonNetwork.Pass.Localization; using DysonNetwork.Pass.Mailer; @@ -24,6 +25,7 @@ public class AccountService( FileService.FileServiceClient files, FileReferenceService.FileReferenceServiceClient fileRefs, AccountUsernameService uname, + AffiliationSpellService ars, EmailService mailer, RingService.RingServiceClient pusher, IStringLocalizer localizer, @@ -101,6 +103,7 @@ public class AccountService( string? password, string language = "en-US", string region = "en", + string? affiliationSpell = null, bool isEmailVerified = false, bool isActivated = false ) @@ -113,7 +116,7 @@ public class AccountService( ).CountAsync(); if (dupeEmailCount > 0) throw new InvalidOperationException("Account email has already been used."); - + var account = new SnAccount { Name = name, @@ -122,7 +125,7 @@ public class AccountService( Region = region, Contacts = [ - new() + new SnAccountContact { Type = Shared.Models.AccountContactType.Email, Content = email, @@ -144,6 +147,9 @@ public class AccountService( Profile = new SnAccountProfile() }; + if (affiliationSpell is not null) + await ars.CreateAffiliationResult(affiliationSpell, $"account:{account.Id}"); + if (isActivated) { account.ActivatedAt = SystemClock.Instance.GetCurrentInstant(); @@ -193,10 +199,7 @@ public class AccountService( displayName, userInfo.Email, null, - "en-US", - "en", - userInfo.EmailVerified, - userInfo.EmailVerified + isEmailVerified: userInfo.EmailVerified ); } diff --git a/DysonNetwork.Pass/Affiliation/AffiliationSpellController.cs b/DysonNetwork.Pass/Affiliation/AffiliationSpellController.cs index a24244d..543be81 100644 --- a/DysonNetwork.Pass/Affiliation/AffiliationSpellController.cs +++ b/DysonNetwork.Pass/Affiliation/AffiliationSpellController.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using DysonNetwork.Shared.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -7,9 +8,31 @@ namespace DysonNetwork.Pass.Affiliation; [ApiController] [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> 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] public async Task>> ListCreatedSpells( [FromQuery(Name = "order")] string orderBy = "date", diff --git a/DysonNetwork.Pass/Affiliation/AffiliationSpellService.cs b/DysonNetwork.Pass/Affiliation/AffiliationSpellService.cs index 15dd4ff..aa8d092 100644 --- a/DysonNetwork.Pass/Affiliation/AffiliationSpellService.cs +++ b/DysonNetwork.Pass/Affiliation/AffiliationSpellService.cs @@ -9,26 +9,42 @@ public class AffiliationSpellService(AppDatabase db) public async Task CreateAffiliationSpell(Guid accountId, string? spellWord) { spellWord ??= _GenerateRandomString(8); - var hasTaken = await db.AffiliationSpells.AnyAsync(s => s.Spell == spellWord); - if (hasTaken) throw new InvalidOperationException("The spell has been taken."); + if (await CheckAffiliationSpellHasTaken(spellWord)) + throw new InvalidOperationException("The spell has been taken."); var spell = new SnAffiliationSpell { AccountId = accountId, Spell = spellWord }; - + db.AffiliationSpells.Add(spell); await db.SaveChangesAsync(); return spell; } - public async Task GetAffiliationSpell(string spellWord) + public async Task CreateAffiliationResult(string spellWord, string resourceId) { - var spell = await db.AffiliationSpells.FirstOrDefaultAsync(s => s.Spell == spellWord); - return spell; + var 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 CheckAffiliationSpellHasTaken(string spellWord) + { + return await db.AffiliationSpells.AnyAsync(s => s.Spell == spellWord); + } + private static string _GenerateRandomString(int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -40,6 +56,7 @@ public class AffiliationSpellService(AppDatabase db) rng.GetBytes(bytes); result[i] = chars[bytes[0] % chars.Length]; } + return new string(result); } } \ No newline at end of file diff --git a/DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs index b393690..ffa9944 100644 --- a/DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Pass/Startup/ServiceCollectionExtensions.cs @@ -11,6 +11,7 @@ using NodaTime.Serialization.SystemTextJson; using System.Text.Json; using System.Text.Json.Serialization; using DysonNetwork.Pass.Account.Presences; +using DysonNetwork.Pass.Affiliation; using DysonNetwork.Pass.Auth.OidcProvider.Options; using DysonNetwork.Pass.Auth.OidcProvider.Services; using DysonNetwork.Pass.Credit; @@ -159,6 +160,7 @@ public static class ServiceCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped();