mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 07:29:00 -07:00
936 lines
39 KiB
C#
936 lines
39 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using RadicadeServer.API.Notifications;
|
||
using RadicadeServer.DB;
|
||
using RadicadeServer.Extensions;
|
||
using RadicadeServer.RecNet.Auth;
|
||
using RadicadeServer.RecNet.Enums.Messages;
|
||
using RadicadeServer.RecNet.Messages;
|
||
using System.Text.Json.Nodes;
|
||
|
||
|
||
namespace RadicadeServer.Clubs;
|
||
|
||
[Route("clubs/club")]
|
||
[Route("api/clubs/v1/club")]
|
||
[Route("api/clubs/v2/club")]
|
||
public class ClubsAPI : Controller
|
||
{
|
||
private readonly RecDBContext db;
|
||
|
||
public ClubsAPI(RecDBContext db)
|
||
{
|
||
this.db = db;
|
||
}
|
||
|
||
private long ViewerId => User.GetUserId() ?? 0;
|
||
|
||
[HttpGet("home/me"), Authorize]
|
||
[HttpGet("~/club/home/me"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/home/me"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/home/me"), Authorize]
|
||
public async Task<IActionResult> GetMyClubHome()
|
||
{
|
||
var homeClub = await ClubStore.GetHomeClubAsync(db, ViewerId);
|
||
return homeClub == null
|
||
? NotFound()
|
||
: Ok(ClubStore.BuildClub(homeClub, ViewerId));
|
||
}
|
||
|
||
[HttpPut("~/clubs/club/home/me"), Authorize]
|
||
[HttpPut("~/club/home/me"), Authorize]
|
||
[HttpPut("~/api/clubs/v1/home/me"), Authorize]
|
||
[HttpPut("~/api/clubs/v2/home/me"), Authorize]
|
||
public async Task<IActionResult> SetMyClubHome()
|
||
{
|
||
var body = await ReadBodyObjectAsync();
|
||
var clubId = ReadLong(body, "clubId", "ClubId");
|
||
if (clubId is null or <= 0) return BadRequest(new { error = "Invalid clubId.", success = false, value = (object?)null });
|
||
|
||
var club = await ClubStore.GetAsync(db, clubId.Value);
|
||
if (club == null) return NotFound();
|
||
if (ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Member)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "You are not a member of that club.", success = false, value = (object?)null });
|
||
|
||
var updated = await ClubStore.SetHomeClubAsync(db, ViewerId, clubId.Value);
|
||
return Ok(new { error = "", success = true, value = ClubStore.BuildClub(updated, ViewerId) });
|
||
}
|
||
|
||
[HttpDelete("~/clubs/club/home/me"), Authorize]
|
||
[HttpDelete("~/club/home/me"), Authorize]
|
||
[HttpDelete("~/api/clubs/v1/home/me"), Authorize]
|
||
[HttpDelete("~/api/clubs/v2/home/me"), Authorize]
|
||
public async Task<IActionResult> ClearMyClubHome()
|
||
{
|
||
await ClubStore.SetHomeClubAsync(db, ViewerId, null);
|
||
return Ok(new { error = "", success = true, value = (object?)null });
|
||
}
|
||
|
||
[HttpGet("{clubId}"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/{clubId:long}"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/{clubId:long}"), Authorize]
|
||
public async Task<IActionResult> GetClub(long clubId)
|
||
{
|
||
return Ok(ClubStore.BuildClub(await ClubStore.GetAsync(db, clubId), ViewerId));
|
||
}
|
||
|
||
[HttpGet("{clubId}/details"), Authorize]
|
||
[HttpGet("~/club/{clubId:long}/details"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/{clubId:long}/details"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/{clubId:long}/details"), Authorize]
|
||
public async Task<IActionResult> GetClubDetails(long clubId)
|
||
{
|
||
return Ok(await ClubStore.BuildClubDetailsAsync(db, await ClubStore.GetAsync(db, clubId), ViewerId));
|
||
}
|
||
|
||
[HttpGet("{clubId:long}/mainimage")]
|
||
[HttpGet("~/club/{clubId:long}/mainimage")]
|
||
public async Task<IActionResult> GetClubMainImage(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
return Ok(new
|
||
{
|
||
error = string.Empty,
|
||
success = true,
|
||
value = await ClubStore.BuildClubDetailsAsync(db, club, ViewerId)
|
||
});
|
||
}
|
||
|
||
[HttpPut("{clubId:long}/mainimage"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/mainimage"), Authorize]
|
||
public async Task<IActionResult> SetClubMainImage(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var body = await ReadBodyObjectAsync();
|
||
var imageName = ReadString(body, "imageName", "ImageName")?.Trim();
|
||
if (string.IsNullOrWhiteSpace(imageName))
|
||
return BadRequest(new { error = "imageName is required.", success = false, value = (object?)null });
|
||
|
||
var updated = await ClubStore.UpdateAsync(db, clubId, new JsonObject { ["mainImageName"] = imageName });
|
||
if (updated == null) return NotFound();
|
||
return Ok(new
|
||
{
|
||
error = string.Empty,
|
||
success = true,
|
||
value = await ClubStore.BuildClubDetailsAsync(db, updated, ViewerId)
|
||
});
|
||
}
|
||
|
||
[HttpGet("{clubId}/members")]
|
||
[HttpGet("~/club/{clubId:long}/members")]
|
||
[HttpGet("~/api/clubs/v1/{clubId:long}/members")]
|
||
[HttpGet("~/api/clubs/v2/{clubId:long}/members")]
|
||
public async Task<IActionResult> GetClubMembers(long clubId, [FromQuery] int? membershipType = null, [FromQuery] int? sortBy = null)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
var members = ClubStore.GetMembers(club);
|
||
if (membershipType.HasValue)
|
||
members = members.Where(member => member.MembershipType == membershipType.Value).ToArray();
|
||
members = sortBy switch
|
||
{
|
||
1 => members.OrderBy(member => member.AccountId).ToArray(),
|
||
2 => members.OrderBy(member => member.CreatedAt).ToArray(),
|
||
_ => members.OrderByDescending(member => member.MembershipType).ThenBy(member => member.CreatedAt).ToArray()
|
||
};
|
||
var values = members
|
||
.Select(member => ClubStore.BuildMemberListItem(member, clubId))
|
||
.ToArray();
|
||
|
||
return Ok(new Dictionary<string, object?>
|
||
{
|
||
["error"] = string.Empty,
|
||
["success"] = true,
|
||
["value"] = values
|
||
});
|
||
}
|
||
|
||
[HttpGet("{clubId}/hasDisabledClubChat"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/{clubId:long}/hasDisabledClubChat"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/{clubId:long}/hasDisabledClubChat"), Authorize]
|
||
public IActionResult GetHasDisabledClubChat(long clubId)
|
||
{
|
||
return Content("false", "application/json");
|
||
}
|
||
|
||
[HttpGet("{clubId:long}/members/{playerId:long}"), Authorize]
|
||
[HttpGet("~/club/{clubId:long}/members/{playerId:long}"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/{clubId:long}/members/{playerId:long}"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/{clubId:long}/members/{playerId:long}"), Authorize]
|
||
public async Task<IActionResult> GetClubMembership(long clubId, long playerId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
var role = ClubStore.GetMembershipType(club, playerId);
|
||
var membership = ClubStore.GetMembers(club).FirstOrDefault(member => member.AccountId == playerId);
|
||
var profile = await db.Profiles.AsNoTracking().FirstOrDefaultAsync(x => x.accountId == playerId);
|
||
|
||
return Ok(new
|
||
{
|
||
clubId = clubId,
|
||
creatorAccountId = club?.ownerPlayerId ?? 0L,
|
||
membershipType = role,
|
||
LastAnnouncementId = 0L,
|
||
LastReadAnnouncementId = 0L,
|
||
announcements = Array.Empty<object>(),
|
||
subscriptionId = clubId,
|
||
playerId = playerId,
|
||
membershipStatus = role >= ClubMembershipTypes.Member ? 1 : 0,
|
||
member = membership == null ? null : ClubStore.BuildMember(membership, club, profile),
|
||
role = role,
|
||
subscribed = role >= ClubMembershipTypes.Member,
|
||
created = membership == null ? 0L : new DateTimeOffset(membership.CreatedAt).ToUnixTimeSeconds(),
|
||
createdAt = membership == null ? 0L : new DateTimeOffset(membership.CreatedAt).ToUnixTimeSeconds()
|
||
});
|
||
}
|
||
|
||
[HttpGet("mine/created"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/mine/created"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/mine/created"), Authorize]
|
||
public async Task<IActionResult> GetMyCreatedClubs()
|
||
{
|
||
var clubs = await ClubStore.LoadAsync(db);
|
||
return Ok(clubs.Where(x => x.ownerPlayerId == ViewerId).Select(x => ClubStore.BuildClub(x, ViewerId)).ToArray());
|
||
}
|
||
|
||
[HttpGet("mine/member"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/mine/member"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/mine/member"), Authorize]
|
||
public async Task<IActionResult> GetMyMemberClubs()
|
||
{
|
||
var clubs = await ClubStore.LoadAsync(db);
|
||
return Ok(clubs
|
||
.Where(club => ClubStore.GetMembershipType(club, ViewerId) >= ClubMembershipTypes.Member)
|
||
.Select(club => ClubStore.BuildClub(club, ViewerId))
|
||
.ToArray());
|
||
}
|
||
|
||
[HttpGet("account/{accountId}/created"), Authorize]
|
||
[HttpGet("~/clubs/account/{accountId:long}/created"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/account/{accountId:long}/created"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/account/{accountId:long}/created"), Authorize]
|
||
public async Task<IActionResult> GetClubsCreatedByAccount(long accountId)
|
||
{
|
||
var clubs = await ClubStore.LoadAsync(db);
|
||
return Ok(clubs.Where(x => x.ownerPlayerId == accountId).Select(x => ClubStore.BuildClub(x, ViewerId)).ToArray());
|
||
}
|
||
|
||
[HttpGet("~/clubs/account/{accountId:long}/clubs")]
|
||
[HttpGet("~/api/clubs/v1/account/{accountId:long}/clubs")]
|
||
[HttpGet("~/api/clubs/v2/account/{accountId:long}/clubs")]
|
||
public Task<IActionResult> GetClubsForAccount(long accountId) => GetClubsCreatedByAccount(accountId);
|
||
|
||
[HttpGet("mostactivetoday"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/mostactivetoday"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/mostactivetoday"), Authorize]
|
||
public async Task<IActionResult> GetMostActiveToday()
|
||
{
|
||
var clubs = await ClubStore.LoadAsync(db);
|
||
clubs = ClubStore.FilterByCategory(clubs, ClubStore.CategoryFilter(Request.Query));
|
||
return Ok(clubs.Select(x => ClubStore.BuildClub(x, ViewerId)).ToArray());
|
||
}
|
||
|
||
[HttpGet("mostactivenow"), Authorize]
|
||
public async Task<IActionResult> GetMostActiveNow()
|
||
{
|
||
var clubs = await ClubStore.LoadAsync(db);
|
||
clubs = ClubStore.FilterByCategory(clubs, ClubStore.CategoryFilter(Request.Query));
|
||
return Ok(clubs.Select(x => ClubStore.BuildClub(x, ViewerId)).ToArray());
|
||
}
|
||
|
||
[HttpGet("categoryTags"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/categoryTags"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/categoryTags"), Authorize]
|
||
public IActionResult GetCategoryTags()
|
||
{
|
||
return Ok(new[] { "Social", "Casual", "Competitive", "Creative", "Entertainment", "Educational", "Lifestyle" });
|
||
}
|
||
|
||
[HttpGet("search"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/search"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/search"), Authorize]
|
||
public async Task<IActionResult> SearchClubs(
|
||
[FromQuery] string? query = null,
|
||
[FromQuery] int sort = 0,
|
||
[FromQuery] string? category = null,
|
||
[FromQuery] int skip = 0,
|
||
[FromQuery] int count = 30,
|
||
[FromQuery] int take = 0)
|
||
{
|
||
var limit = take > 0 ? take : count;
|
||
var clubs = await ClubStore.LoadAsync(db);
|
||
if (!string.IsNullOrWhiteSpace(query))
|
||
{
|
||
clubs = clubs
|
||
.Where(x => x.name.Contains(query, StringComparison.OrdinalIgnoreCase) ||
|
||
x.description.Contains(query, StringComparison.OrdinalIgnoreCase))
|
||
.ToList();
|
||
}
|
||
|
||
clubs = ClubStore.FilterByCategory(clubs, ClubStore.CategoryFilter(Request.Query) ?? category);
|
||
|
||
var items = clubs
|
||
.Skip(Math.Max(0, skip))
|
||
.Take(Math.Max(1, limit))
|
||
.Select(x => ClubStore.BuildClub(x, ViewerId))
|
||
.ToArray();
|
||
|
||
return Ok(ClubStore.BuildSearchResponse(items, Math.Max(0, skip), Math.Max(1, limit)));
|
||
}
|
||
|
||
[HttpGet("cancreate"), Authorize]
|
||
[HttpGet("can-create"), Authorize]
|
||
[HttpGet("create/eligibility"), Authorize]
|
||
[HttpGet("creation/eligibility"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/cancreate"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/can-create"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/create/eligibility"), Authorize]
|
||
[HttpGet("~/api/clubs/v1/creation/eligibility"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/cancreate"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/can-create"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/create/eligibility"), Authorize]
|
||
[HttpGet("~/api/clubs/v2/creation/eligibility"), Authorize]
|
||
public IActionResult CanCreateClub()
|
||
{
|
||
return Ok(ClubStore.BuildEligibility());
|
||
}
|
||
|
||
[HttpPut("{clubId:long}/members/invite"), Authorize]
|
||
[HttpPut("{clubId:long}/members/invitemembers"), Authorize]
|
||
[HttpPost("{clubId:long}/members/invitemembers"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/members/invite"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/members/invitemembers"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/invitemembers"), Authorize]
|
||
public async Task<IActionResult> InviteMember(long clubId)
|
||
{
|
||
var inviterId = ViewerId;
|
||
if (inviterId <= 0) return Unauthorized();
|
||
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
|
||
var inviterRole = ClubStore.GetMembershipType(club, inviterId);
|
||
if (inviterRole < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var (rawAccountIds, rawMembershipType) = await ReadInviteMemberFieldsAsync();
|
||
if (rawAccountIds.Count == 0)
|
||
return BadRequest(new { error = "Invalid accountId.", success = false, value = (object?)null });
|
||
|
||
var membershipType = string.IsNullOrWhiteSpace(rawMembershipType)
|
||
? ClubMembershipTypes.Member
|
||
: ParseInviteMembershipType(rawMembershipType);
|
||
if (membershipType is not (ClubMembershipTypes.Member or ClubMembershipTypes.Moderator or ClubMembershipTypes.Coowner))
|
||
return BadRequest(new { error = "Invalid membershipType.", success = false, value = (object?)null });
|
||
if (membershipType > inviterRole)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var updated = club;
|
||
foreach (var rawAccountId in rawAccountIds)
|
||
{
|
||
if (!long.TryParse(rawAccountId, out var accountId) || accountId <= 0)
|
||
return BadRequest(new { error = "Invalid accountId.", success = false, value = (object?)null });
|
||
if (accountId == club.ownerPlayerId)
|
||
return BadRequest(new { error = "You can’t change the club’s creator.", success = false, value = (object?)null });
|
||
|
||
|
||
|
||
|
||
|
||
if (ClubStore.GetMembershipType(updated, accountId) >= ClubMembershipTypes.Member)
|
||
{
|
||
updated = await ClubStore.SetMemberTypeAsync(db, clubId, accountId, membershipType, inviterId) ?? updated;
|
||
continue;
|
||
}
|
||
|
||
updated = await ClubStore.InviteMemberAsync(db, clubId, accountId, membershipType, inviterId) ?? updated;
|
||
|
||
await Notify.SendMessage(accountId, new Message
|
||
{
|
||
FromPlayerId = inviterId,
|
||
Type = membershipType switch
|
||
{
|
||
ClubMembershipTypes.Coowner => MessageType.ClubCoownerInvited,
|
||
ClubMembershipTypes.Moderator => MessageType.ClubModeratorInvited,
|
||
_ => MessageType.ClubMemberInvited
|
||
},
|
||
Data = clubId.ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||
Details = membershipType.ToString(System.Globalization.CultureInfo.InvariantCulture)
|
||
});
|
||
}
|
||
|
||
return Ok(new Dictionary<string, object?>
|
||
{
|
||
["error"] = string.Empty,
|
||
["success"] = true,
|
||
["value"] = await ClubStore.BuildClubDetailsAsync(db, updated, inviterId)
|
||
});
|
||
}
|
||
|
||
[HttpPut("{clubId:long}/members/changetype"), Authorize]
|
||
[HttpPost("{clubId:long}/members/changetype"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/members/changetype"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/changetype"), Authorize]
|
||
public async Task<IActionResult> ChangeMemberType(long clubId)
|
||
{
|
||
var actorId = ViewerId;
|
||
if (actorId <= 0) return Unauthorized();
|
||
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
var actorRole = ClubStore.GetMembershipType(club, actorId);
|
||
if (actorRole < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var body = await ReadBodyObjectAsync();
|
||
var accountId = ReadLong(body, "accountId", "AccountId", "playerId", "PlayerId", "targetPlayerId", "TargetPlayerId")
|
||
?? ReadLongFromRequest(Request, "accountId", "AccountId", "playerId", "PlayerId", "targetPlayerId", "TargetPlayerId");
|
||
if (accountId is null or <= 0 || accountId == club.ownerPlayerId)
|
||
return BadRequest(new { error = "Invalid accountId.", success = false, value = (object?)null });
|
||
if (ClubStore.GetMembershipType(club, accountId.Value) < ClubMembershipTypes.Member)
|
||
return BadRequest(new { error = "That player is not a member of this club.", success = false, value = (object?)null });
|
||
|
||
var requested = ReadLong(body, "membershipType", "MembershipType", "type", "Type", "role", "Role")
|
||
?? ReadLongFromRequest(Request, "membershipType", "MembershipType", "type", "Type", "role", "Role");
|
||
if (requested is null or < 0)
|
||
return BadRequest(new { error = "Invalid membershipType.", success = false, value = (object?)null });
|
||
var membershipType = (int)requested.Value;
|
||
if (membershipType is not (ClubMembershipTypes.Member or ClubMembershipTypes.Moderator or ClubMembershipTypes.Coowner))
|
||
return BadRequest(new { error = "Invalid membershipType.", success = false, value = (object?)null });
|
||
if (membershipType > actorRole)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var updated = await ClubStore.SetMemberTypeAsync(db, clubId, accountId.Value, membershipType, actorId);
|
||
if (updated == null) return NotFound();
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, updated, actorId) });
|
||
}
|
||
|
||
[HttpPost("{clubId:long}/members/acceptinvite"), Authorize]
|
||
[HttpPut("{clubId:long}/members/acceptinvite"), Authorize]
|
||
[HttpPost("{clubId:long}/members/accept"), Authorize]
|
||
[HttpPut("{clubId:long}/members/accept"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/acceptinvite"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/members/acceptinvite"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/accept"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/members/accept"), Authorize]
|
||
public async Task<IActionResult> AcceptClubInvitation(long clubId)
|
||
{
|
||
var inviteeId = ViewerId;
|
||
if (inviteeId <= 0) return Unauthorized();
|
||
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
var pending = ClubStore.GetMembers(club).FirstOrDefault(member => member.AccountId == inviteeId);
|
||
if (pending?.MembershipType != ClubMembershipTypes.PendingInvited)
|
||
return BadRequest(new { error = "No pending invitation exists.", success = false, value = (object?)null });
|
||
|
||
var updated = await ClubStore.AcceptInvitationAsync(db, clubId, inviteeId);
|
||
if (updated == null) return NotFound();
|
||
return Ok(new Dictionary<string, object?>
|
||
{
|
||
["error"] = string.Empty,
|
||
["success"] = true,
|
||
["value"] = await ClubStore.BuildClubDetailsAsync(db, updated, inviteeId)
|
||
});
|
||
}
|
||
|
||
[HttpPost("{clubId:long}/members/declineinvite"), Authorize]
|
||
[HttpPut("{clubId:long}/members/declineinvite"), Authorize]
|
||
[HttpPost("{clubId:long}/members/decline"), Authorize]
|
||
[HttpDelete("{clubId:long}/members/invite"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/declineinvite"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/members/declineinvite"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/decline"), Authorize]
|
||
[HttpDelete("~/club/{clubId:long}/members/invite"), Authorize]
|
||
public async Task<IActionResult> DeclineClubInvitation(long clubId)
|
||
{
|
||
var inviteeId = ViewerId;
|
||
if (inviteeId <= 0) return Unauthorized();
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
var pending = ClubStore.GetMembers(club).FirstOrDefault(member => member.AccountId == inviteeId);
|
||
if (pending?.MembershipType != ClubMembershipTypes.PendingInvited)
|
||
return BadRequest(new { error = "No pending invitation exists.", success = false, value = (object?)null });
|
||
var updated = await ClubStore.RemoveMemberAsync(db, clubId, inviteeId);
|
||
return Ok(new Dictionary<string, object?>
|
||
{
|
||
["error"] = string.Empty,
|
||
["success"] = true,
|
||
["value"] = await ClubStore.BuildClubDetailsAsync(db, updated, inviteeId)
|
||
});
|
||
}
|
||
|
||
private static readonly string[] InviteAccountIdKeys =
|
||
{
|
||
"accountId", "AccountId", "accountIds", "AccountIds", "accountId[]", "AccountId[]",
|
||
"playerId", "PlayerId", "playerIds", "PlayerIds", "invitedPlayerId", "InvitedPlayerId",
|
||
"invitedPlayerIds", "InvitedPlayerIds", "invitedPlayerIds[]", "InvitedPlayerIds[]",
|
||
"targetPlayerIds", "TargetPlayerIds"
|
||
};
|
||
|
||
private static int ParseInviteMembershipType(string raw)
|
||
{
|
||
if (int.TryParse(raw, out var numeric)) return numeric;
|
||
|
||
return raw.Trim().ToLowerInvariant() switch
|
||
{
|
||
"member" or "regular" or "invited" => ClubMembershipTypes.Member,
|
||
"moderator" or "mod" => ClubMembershipTypes.Moderator,
|
||
"coowner" or "co-owner" or "co_owner" or "co owner" => ClubMembershipTypes.Coowner,
|
||
_ => -1
|
||
};
|
||
}
|
||
|
||
private async Task<(List<string> AccountIds, string? MembershipType)> ReadInviteMemberFieldsAsync()
|
||
{
|
||
var accountIds = new List<string>();
|
||
string? membershipType = null;
|
||
|
||
void Collect(string key, string? value)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(value)) return;
|
||
if (key.Equals("membershipType", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
membershipType ??= value;
|
||
return;
|
||
}
|
||
if (!InviteAccountIdKeys.Contains(key, StringComparer.OrdinalIgnoreCase)) return;
|
||
|
||
foreach (var part in value.Split(new[] { ',', '|', ' ' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||
if (long.TryParse(part, out _) && !accountIds.Contains(part)) accountIds.Add(part);
|
||
}
|
||
|
||
if (Request.HasFormContentType)
|
||
{
|
||
var form = await Request.ReadFormAsync();
|
||
foreach (var field in form)
|
||
foreach (var value in field.Value)
|
||
Collect(field.Key, value);
|
||
}
|
||
else if (Request.ContentLength is > 0)
|
||
{
|
||
try
|
||
{
|
||
Request.EnableBuffering();
|
||
if (await JsonNode.ParseAsync(Request.Body) is JsonObject body)
|
||
foreach (var pair in body)
|
||
{
|
||
if (pair.Value is JsonArray array)
|
||
foreach (var item in array) Collect(pair.Key, item?.ToString());
|
||
else
|
||
Collect(pair.Key, pair.Value?.ToString());
|
||
}
|
||
|
||
Request.Body.Position = 0;
|
||
}
|
||
catch
|
||
{
|
||
try { Request.Body.Position = 0; } catch { }
|
||
}
|
||
}
|
||
|
||
foreach (var key in InviteAccountIdKeys)
|
||
foreach (var value in Request.Query[key]) Collect(key, value);
|
||
membershipType ??= Request.Query["membershipType"].FirstOrDefault()
|
||
?? Request.Query["MembershipType"].FirstOrDefault();
|
||
|
||
return (accountIds, membershipType);
|
||
}
|
||
|
||
[HttpPost("create"), Authorize]
|
||
[HttpPost("~/club/create"), Authorize]
|
||
[HttpPost("createclub"), Authorize]
|
||
[HttpPost("~/api/clubs/v1/create"), Authorize]
|
||
[HttpPost("~/api/clubs/v1/createclub"), Authorize]
|
||
[HttpPost("~/api/clubs/v2/create"), Authorize]
|
||
[HttpPost("~/api/clubs/v2/createclub"), Authorize]
|
||
public async Task<IActionResult> CreateClub([FromForm] string? name = null, [FromForm] string? description = null)
|
||
{
|
||
var userId = ViewerId;
|
||
if (userId <= 0) return Unauthorized("Missing user ID.");
|
||
|
||
var fields = await ReadCreateClubFields(name, description);
|
||
var club = await ClubStore.CreateAsync(
|
||
db,
|
||
userId,
|
||
fields.Name,
|
||
fields.Description,
|
||
fields.Category,
|
||
fields.Visibility ?? 1,
|
||
fields.Joinability ?? 0,
|
||
fields.AllowJuniors ?? true,
|
||
fields.MinLevel ?? 0,
|
||
fields.MainImageName);
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, club, userId) });
|
||
}
|
||
|
||
private async Task<(string? Name, string? Description, string? Category, int? Visibility, int? Joinability, bool? AllowJuniors, int? MinLevel, string? MainImageName)> ReadCreateClubFields(string? name, string? description)
|
||
{
|
||
string? category = null;
|
||
string? visibility = null;
|
||
string? joinability = null;
|
||
string? allowJuniors = null;
|
||
string? minLevel = null;
|
||
string? mainImageName = null;
|
||
|
||
if (Request.HasFormContentType)
|
||
{
|
||
var form = await Request.ReadFormAsync();
|
||
name ??= FirstFormValue(form, "name", "Name", "clubName", "ClubName");
|
||
description ??= FirstFormValue(form, "description", "Description", "clubDescription", "ClubDescription");
|
||
category = FirstFormValue(form, "category", "Category", "primaryTag", "PrimaryTag");
|
||
visibility = FirstFormValue(form, "visibility", "Visibility");
|
||
joinability = FirstFormValue(form, "joinability", "Joinability");
|
||
allowJuniors = FirstFormValue(form, "allowJuniors", "AllowJuniors");
|
||
minLevel = FirstFormValue(form, "minLevel", "MinLevel");
|
||
mainImageName = FirstFormValue(form, "mainImageName", "MainImageName");
|
||
}
|
||
else if (Request.ContentLength is > 0)
|
||
{
|
||
using var reader = new StreamReader(Request.Body);
|
||
var rawBody = await reader.ReadToEndAsync();
|
||
if (!string.IsNullOrWhiteSpace(rawBody) && JsonNode.Parse(rawBody) is JsonObject body)
|
||
{
|
||
name ??= FirstJsonValue(body, "name", "Name", "clubName", "ClubName");
|
||
description ??= FirstJsonValue(body, "description", "Description", "clubDescription", "ClubDescription");
|
||
category = FirstJsonValue(body, "category", "Category", "primaryTag", "PrimaryTag");
|
||
visibility = FirstJsonValue(body, "visibility", "Visibility");
|
||
joinability = FirstJsonValue(body, "joinability", "Joinability");
|
||
allowJuniors = FirstJsonValue(body, "allowJuniors", "AllowJuniors");
|
||
minLevel = FirstJsonValue(body, "minLevel", "MinLevel");
|
||
mainImageName = FirstJsonValue(body, "mainImageName", "MainImageName");
|
||
}
|
||
}
|
||
|
||
name ??= Request.Query["name"].FirstOrDefault() ?? Request.Query["Name"].FirstOrDefault() ??
|
||
Request.Query["clubName"].FirstOrDefault() ?? Request.Query["ClubName"].FirstOrDefault();
|
||
description ??= Request.Query["description"].FirstOrDefault() ?? Request.Query["Description"].FirstOrDefault() ??
|
||
Request.Query["clubDescription"].FirstOrDefault() ?? Request.Query["ClubDescription"].FirstOrDefault();
|
||
category ??= Request.Query["category"].FirstOrDefault() ?? Request.Query["Category"].FirstOrDefault() ??
|
||
Request.Query["primaryTag"].FirstOrDefault() ?? Request.Query["PrimaryTag"].FirstOrDefault();
|
||
|
||
return (
|
||
name,
|
||
description,
|
||
category,
|
||
ParseVisibility(visibility),
|
||
ParseJoinability(joinability),
|
||
ParseFormBool(allowJuniors),
|
||
int.TryParse(minLevel, out var parsedLevel) && parsedLevel >= 0 ? parsedLevel : null,
|
||
mainImageName);
|
||
}
|
||
|
||
private static int? ParseVisibility(string? raw) => raw?.Trim().ToLowerInvariant() switch
|
||
{
|
||
"private" or "0" => 0,
|
||
"public" or "1" => 1,
|
||
_ => null
|
||
};
|
||
|
||
private static int? ParseJoinability(string? raw) => raw?.Trim().ToLowerInvariant().Replace("_", "") switch
|
||
{
|
||
"open" or "0" => 0,
|
||
"inviteonly" or "1" => 1,
|
||
"asktojoin" or "requesttojoin" or "2" => 2,
|
||
_ => null
|
||
};
|
||
|
||
private static bool? ParseFormBool(string? raw) => raw?.Trim().ToLowerInvariant() switch
|
||
{
|
||
"true" or "1" or "yes" => true,
|
||
"false" or "0" or "no" => false,
|
||
_ => null
|
||
};
|
||
|
||
[HttpPut("{clubId:long}/minlevel"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/minlevel"), Authorize]
|
||
public async Task<IActionResult> SetMinimumLevel(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var rawMinLevel = await FirstFormValueAsync(Request, "minLevel", "MinLevel");
|
||
if (!int.TryParse(rawMinLevel, out var minLevel) || minLevel < 0)
|
||
{
|
||
var body = await ReadBodyObjectAsync();
|
||
var parsed = ReadLong(body, "minLevel", "MinLevel");
|
||
if (parsed is null or < 0 or > int.MaxValue)
|
||
return BadRequest(new { error = "Invalid minLevel.", success = false, value = (object?)null });
|
||
minLevel = (int)parsed.Value;
|
||
}
|
||
|
||
var updated = await ClubStore.UpdateAsync(db, clubId, new JsonObject { ["minLevel"] = minLevel });
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, updated, ViewerId) });
|
||
}
|
||
|
||
[HttpPut("{clubId:long}/modifydetails"), Authorize]
|
||
[HttpPut("{clubId:long}/modify"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/modifydetails"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/modify"), Authorize]
|
||
public async Task<IActionResult> ModifyClub(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var body = await ReadBodyObjectAsync();
|
||
if (body == null) return BadRequest(new { error = "Invalid request.", success = false, value = (object?)null });
|
||
var updated = await ClubStore.UpdateAsync(db, clubId, body);
|
||
if (updated == null) return NotFound();
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, updated, ViewerId) });
|
||
}
|
||
|
||
[HttpPut("{clubId:long}/clubhouse"), Authorize]
|
||
[HttpDelete("{clubId:long}/clubhouse"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/clubhouse"), Authorize]
|
||
[HttpDelete("~/club/{clubId:long}/clubhouse"), Authorize]
|
||
public async Task<IActionResult> SetClubhouse(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
long? roomId = null;
|
||
if (!HttpMethods.IsDelete(Request.Method))
|
||
{
|
||
var body = await ReadBodyObjectAsync();
|
||
var raw = ReadLong(body, "roomId", "RoomId");
|
||
if (body?.Count > 0 && raw == null)
|
||
return BadRequest(new { error = "Invalid roomId.", success = false, value = (object?)null });
|
||
roomId = raw;
|
||
}
|
||
|
||
var updated = await ClubStore.SetClubhouseAsync(db, clubId, roomId);
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, updated, ViewerId) });
|
||
}
|
||
|
||
[HttpPut("{clubId:long}/additionalimage/{index:int}"), Authorize]
|
||
[HttpDelete("{clubId:long}/additionalimage/{index:int}"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/additionalimage/{index:int}"), Authorize]
|
||
[HttpDelete("~/club/{clubId:long}/additionalimage/{index:int}"), Authorize]
|
||
public async Task<IActionResult> SetAdditionalImage(long clubId, int index)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
if (index < 0 || index >= 3)
|
||
return BadRequest(new { error = "Invalid additional image slot.", success = false, value = (object?)null });
|
||
|
||
var body = HttpMethods.IsDelete(Request.Method) ? null : await ReadBodyObjectAsync();
|
||
var imageName = body == null ? "" : ReadString(body, "imageName", "ImageName") ?? "";
|
||
var updated = await ClubStore.SetAdditionalImageAsync(db, clubId, index, imageName);
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, updated, ViewerId) });
|
||
}
|
||
|
||
[HttpDelete("{clubId:long}"), Authorize]
|
||
[HttpDelete("~/club/{clubId:long}"), Authorize]
|
||
public async Task<IActionResult> DeleteClub(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (club.ownerPlayerId != ViewerId)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
await ClubStore.DeleteAsync(db, clubId);
|
||
return Ok(new { error = "", success = true, value = (object?)null });
|
||
}
|
||
|
||
[HttpPut("{clubId:long}/members/requesttojoin"), Authorize]
|
||
[HttpPut("~/club/{clubId:long}/members/requesttojoin"), Authorize]
|
||
public async Task<IActionResult> RequestToJoinClub(long clubId)
|
||
{
|
||
var existingClub = await ClubStore.GetAsync(db, clubId);
|
||
if (existingClub == null) return NotFound();
|
||
ClubRecord? club;
|
||
if (ClubStore.GetMembershipType(existingClub, ViewerId) == ClubMembershipTypes.PendingInvited)
|
||
club = await ClubStore.AcceptInvitationAsync(db, clubId, ViewerId);
|
||
else
|
||
club = await ClubStore.RequestToJoinAsync(db, clubId, ViewerId);
|
||
if (club == null) return NotFound();
|
||
var role = ClubStore.GetMembershipType(club, ViewerId);
|
||
if (role == ClubMembershipTypes.Banned)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "You are banned from this club.", success = false, value = (object?)null });
|
||
if (club.joinability == 1 && role < ClubMembershipTypes.Member)
|
||
return BadRequest(new { error = "This club is invite only.", success = false, value = (object?)null });
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, club, ViewerId) });
|
||
}
|
||
|
||
[HttpPost("{clubId:long}/members/leave"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/leave"), Authorize]
|
||
public async Task<IActionResult> LeaveClub(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (club.ownerPlayerId == ViewerId)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "You created this club — delete it instead of leaving.", success = false, value = (object?)null });
|
||
|
||
club = await ClubStore.RemoveMemberAsync(db, clubId, ViewerId) ?? club;
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, club, ViewerId) });
|
||
}
|
||
|
||
[HttpPost("{clubId:long}/members/remove"), Authorize]
|
||
[HttpDelete("{clubId:long}/members/remove"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/remove"), Authorize]
|
||
[HttpDelete("~/club/{clubId:long}/members/remove"), Authorize]
|
||
public async Task<IActionResult> RemoveClubMember(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
var actorRole = ClubStore.GetMembershipType(club, ViewerId);
|
||
if (actorRole < ClubMembershipTypes.Moderator)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var body = await ReadBodyObjectAsync();
|
||
var accountId = ReadLong(body, "accountId", "AccountId", "playerId", "PlayerId", "targetPlayerId", "TargetPlayerId")
|
||
?? ReadLongFromRequest(Request, "accountId", "AccountId", "playerId", "PlayerId", "targetPlayerId", "TargetPlayerId");
|
||
if (accountId is null or <= 0 || accountId == club.ownerPlayerId || accountId == ViewerId)
|
||
return BadRequest(new { error = "Invalid accountId.", success = false, value = (object?)null });
|
||
var targetRole = ClubStore.GetMembershipType(club, accountId.Value);
|
||
if (targetRole >= ClubMembershipTypes.Moderator && actorRole < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
|
||
var updated = await ClubStore.RemoveMemberAsync(db, clubId, accountId.Value);
|
||
return updated == null
|
||
? NotFound()
|
||
: Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, updated, ViewerId) });
|
||
}
|
||
|
||
[HttpPost("{clubId:long}/members/ban"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/ban"), Authorize]
|
||
public async Task<IActionResult> BanMember(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Moderator)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
var body = await ReadBodyObjectAsync();
|
||
var accountId = ReadLong(body, "accountId", "AccountId", "playerId", "PlayerId", "targetPlayerId", "TargetPlayerId");
|
||
if (accountId is null or <= 0 || accountId == club.ownerPlayerId || accountId == ViewerId)
|
||
return BadRequest(new { error = "Invalid accountId.", success = false, value = (object?)null });
|
||
var targetRole = ClubStore.GetMembershipType(club, accountId.Value);
|
||
if (targetRole >= ClubMembershipTypes.Moderator && ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Coowner)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
var updated = await ClubStore.SetMemberTypeAsync(db, clubId, accountId.Value, ClubMembershipTypes.Banned, ViewerId);
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, updated, ViewerId) });
|
||
}
|
||
|
||
[HttpPost("{clubId:long}/members/unban"), Authorize]
|
||
[HttpPost("~/club/{clubId:long}/members/unban"), Authorize]
|
||
public async Task<IActionResult> UnbanMember(long clubId)
|
||
{
|
||
var club = await ClubStore.GetAsync(db, clubId);
|
||
if (club == null) return NotFound();
|
||
if (ClubStore.GetMembershipType(club, ViewerId) < ClubMembershipTypes.Moderator)
|
||
return StatusCode(StatusCodes.Status403Forbidden, new { error = "Insufficient permissions.", success = false, value = (object?)null });
|
||
var body = await ReadBodyObjectAsync();
|
||
var accountId = ReadLong(body, "accountId", "AccountId", "playerId", "PlayerId", "targetPlayerId", "TargetPlayerId");
|
||
if (accountId is null or <= 0)
|
||
return BadRequest(new { error = "Invalid accountId.", success = false, value = (object?)null });
|
||
var updated = await ClubStore.SetMemberTypeAsync(db, clubId, accountId.Value, ClubMembershipTypes.None, ViewerId);
|
||
return Ok(new { error = "", success = true, value = await ClubStore.BuildClubDetailsAsync(db, updated, ViewerId) });
|
||
}
|
||
|
||
private async Task<JsonObject?> ReadBodyObjectAsync()
|
||
{
|
||
if (Request.HasFormContentType)
|
||
{
|
||
var form = await Request.ReadFormAsync();
|
||
var jsonForm = new JsonObject();
|
||
foreach (var pair in form)
|
||
{
|
||
if (pair.Value.Count > 1)
|
||
{
|
||
var values = new JsonArray();
|
||
foreach (var value in pair.Value) values.Add(value);
|
||
jsonForm[pair.Key] = values;
|
||
}
|
||
else
|
||
{
|
||
jsonForm[pair.Key] = pair.Value.FirstOrDefault();
|
||
}
|
||
}
|
||
return jsonForm;
|
||
}
|
||
if (Request.ContentLength is not > 0) return new JsonObject();
|
||
try
|
||
{
|
||
Request.EnableBuffering();
|
||
var body = await JsonNode.ParseAsync(Request.Body);
|
||
Request.Body.Position = 0;
|
||
return body as JsonObject;
|
||
}
|
||
catch
|
||
{
|
||
try { Request.Body.Position = 0; } catch { }
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private static string? ReadString(JsonObject? body, params string[] keys)
|
||
{
|
||
if (body == null) return null;
|
||
foreach (var key in keys)
|
||
foreach (var pair in body)
|
||
if (pair.Key.Equals(key, StringComparison.OrdinalIgnoreCase))
|
||
return pair.Value?.ToString();
|
||
return null;
|
||
}
|
||
|
||
private static long? ReadLong(JsonObject? body, params string[] keys)
|
||
{
|
||
var value = ReadString(body, keys);
|
||
return long.TryParse(value, out var parsed) ? parsed : null;
|
||
}
|
||
|
||
private static long? ReadLongFromRequest(HttpRequest request, params string[] keys)
|
||
{
|
||
foreach (var key in keys)
|
||
if (long.TryParse(request.Query[key].FirstOrDefault(), out var value)) return value;
|
||
return null;
|
||
}
|
||
|
||
private static async Task<string?> FirstFormValueAsync(HttpRequest request, params string[] keys)
|
||
{
|
||
if (!request.HasFormContentType) return null;
|
||
var form = await request.ReadFormAsync();
|
||
foreach (var key in keys)
|
||
foreach (var pair in form)
|
||
if (pair.Key.Equals(key, StringComparison.OrdinalIgnoreCase)) return pair.Value.FirstOrDefault();
|
||
return null;
|
||
}
|
||
|
||
private static string? FirstFormValue(IFormCollection form, params string[] keys)
|
||
{
|
||
foreach (var key in keys)
|
||
{
|
||
var value = form[key].FirstOrDefault();
|
||
if (!string.IsNullOrWhiteSpace(value)) return value;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private static string? FirstJsonValue(JsonObject? json, params string[] keys)
|
||
{
|
||
if (json == null) return null;
|
||
foreach (var key in keys)
|
||
{
|
||
if (json.TryGetPropertyValue(key, out var value))
|
||
{
|
||
var text = value?.ToString();
|
||
if (!string.IsNullOrWhiteSpace(text)) return text;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
}
|