244 lines
8.7 KiB
C#
244 lines
8.7 KiB
C#
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<IActionResult> Me()
|
|
{
|
|
Account? account = await jwt.GetLogin(User);
|
|
if (account == null) return Unauthorized();
|
|
|
|
return Ok(account.ToDictionaryMe());
|
|
}
|
|
|
|
[HttpPut("birthday")]
|
|
[Authorize]
|
|
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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();
|
|
}
|
|
} |