using DeluxeBackend.Models; using DeluxeBackend.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; using System.IO; using static DeluxeBackend.Enums; namespace DeluxeBackend.Controllers.Accounts { [Route("Accounts/account/me")] [ApiController] public partial class MeController : ControllerBase { private readonly ILiteDbService db; private readonly IJwtService jwt; private readonly IPasswordService passwordService; private readonly INotificationService ws; public MeController(ILiteDbService _db, IJwtService _jwtService, IPasswordService _passwordService, INotificationService _ws) { db = _db; jwt = _jwtService; passwordService = _passwordService; ws = _ws; } [HttpGet] [Authorize] public async Task Me() { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); return Ok(account.ToDictionaryMe()); } [HttpPut("birthday")] [Authorize] public async Task Birthday([FromForm] string birthday) { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); if (account.Birthday.HasValue) { return Conflict(new { Success = false, Error = "Account birthday has already been set." }); } if (DateTime.TryParse(birthday, out DateTime parsedDateTime)) { if (parsedDateTime > DateTime.UtcNow || parsedDateTime < DateTime.UtcNow.AddYears(-125)) { return BadRequest(new { Success = false, Error = "Please provide a valid birth date." }); } account.Birthday = DateOnly.FromDateTime(parsedDateTime); db.Accounts.Update(account); return Ok(new { Success = true }); } return BadRequest(new { Success = false, Error = "Invalid date format received." }); } [HttpPut("username")] [Authorize] public async Task Username([FromForm] string username) { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); if (string.IsNullOrWhiteSpace(username)) { return BadRequest(new { Success = false, Error = "Username cannot be empty." }); } username = username.Trim(); if (username.Length < 3 || username.Length > 20) { return BadRequest(new { Success = false, Error = "Username must be between 3 and 20 characters." }); } if (!UsernameRegex().IsMatch(username)) { return BadRequest(new { Success = false, Error = "Username can only contain letters, numbers, and underscores." }); } string[] reservedNames = { "admin", "administrator", "moderator", "system", "support", "staff", "deluxe" }; if (reservedNames.Contains(username.ToLowerInvariant())) { return BadRequest(new { Success = false, Error = "This username is reserved." }); } bool isTaken = db.Accounts.Exists(a => a.Username.Equals(username, StringComparison.OrdinalIgnoreCase)); if (isTaken) { return Conflict(new { Success = false, Error = $"The username '{username}' is already taken." }); } account.Username = username; account.DisplayName = username; db.Accounts.Update(account); await ws.SendToPlayer(account.Id, "SelfAccountUpdate", account.ToDictionaryMe()); await ws.SendToPlayerSubs(account.Id, "AccountUpdate", account.ToDictionary()); return Ok(new { Success = true }); } [HttpPost("/Auth/account/me/changepassword")] [Authorize] public async Task Changepassword([FromForm] string newPassword, [FromForm] string oldPassword = "") { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); if (string.IsNullOrWhiteSpace(newPassword) || newPassword.Length < 6) { return BadRequest(new { Success = false, Error = "New password must be at least 6 characters long." }); } if (!string.IsNullOrEmpty(account.PasswordHash)) { if (string.IsNullOrEmpty(oldPassword)) { return BadRequest(new { Success = false, Error = "Old password is required." }); } var verificationResult = passwordService.VerifyPassword(account, oldPassword); if (verificationResult == Microsoft.AspNetCore.Identity.PasswordVerificationResult.Failed) { return BadRequest(new { Success = false, Error = "Old password is incorrect." }); } } passwordService.setPassword(account, newPassword); db.Accounts.Update(account); return Ok(new { Success = true }); } [HttpPost("/Auth/account/me/haspassword")] [Authorize] public async Task HasPassword([FromForm] string newPassword, [FromForm] string oldPassword = "") { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); return Ok(!string.IsNullOrEmpty(account.PasswordHash)); } [HttpGet("/Accounts/parentalcontrol/me")] [Authorize] public async Task ParentalControl() { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); return Ok(new { accountId = account.Id, disallowInAppPurchases = false }); } [HttpPut("personalpronouns")] [Authorize] public async Task PersonalPronouns([FromForm] PronounsType pronounFlags) { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); account.Pronouns = pronounFlags; db.Accounts.Update(account); await ws.SendToPlayerSubs(account.Id, "AccountUpdate", account.ToDictionary()); return Ok(new { Success = true }); } [HttpPut("identityflags")] [Authorize] public async Task IdentityFlags([FromForm] IdentityFlagsType identityFlags) { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); account.IdentityFlags = identityFlags; db.Accounts.Update(account); await ws.SendToPlayerSubs(account.Id, "AccountUpdate", account.ToDictionary()); return Ok(new { Success = true }); } [HttpPut("bio")] [Authorize] public async Task Bio([FromForm] string bio) { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); if (bio != null && bio.Length > 250) { return BadRequest(new { Success = false, Error = "Bio cannot exceed 250 characters." }); } account.Bio = bio ?? string.Empty; db.Accounts.Update(account); await ws.SendToPlayerSubs(account.Id, "AccountUpdate", account.ToDictionary()); return Ok(new { Success = true }); } [HttpPut("profileimage")] [Authorize] public async Task ProfileImage([FromForm(Name = "imageName"), Required] string ImageName) { Account? account = await jwt.GetLogin(User); if (account == null) return Unauthorized(); string sanitizedPath = Path.GetFileName(ImageName); if (string.IsNullOrWhiteSpace(sanitizedPath) || sanitizedPath != ImageName) { return BadRequest(new { Success = false, Error = "Invalid image file name format." }); } account.ImageName = ImageName; db.Accounts.Update(account); await ws.SendToPlayerSubs(account.Id, "AccountUpdate", account.ToDictionary()); return Ok(new { Success = true }); } [GeneratedRegex("^[a-zA-Z0-9_]+$")] private static partial Regex UsernameRegex(); } }