mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 23:18:56 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,477 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text.Json;
|
||||
using RadicadeServer.API.Notifications;
|
||||
using RadicadeServer.Auth;
|
||||
using RadicadeServer.DB;
|
||||
using RadicadeServer.Extensions;
|
||||
using RadicadeServer.Radicade.Discord;
|
||||
using RadicadeServer.RecNet.Accounts;
|
||||
using RadicadeServer.RecNet.Auth;
|
||||
using RadicadeServer.RecNet.Enums.Messages;
|
||||
using RadicadeServer.RecNet.Enums.Moderation;
|
||||
using RadicadeServer.RecNet.Enums.Players;
|
||||
using RadicadeServer.RecNet.Messages;
|
||||
using RadicadeServer.RecNet.Moderation;
|
||||
using RadicadeServer.RecNet.Notifications;
|
||||
using RadicadeServer.RecNet.Rooms;
|
||||
using RadicadeServer.Utils;
|
||||
|
||||
namespace RadicadeServer.Admin
|
||||
{
|
||||
[Route("admin/profiles")]
|
||||
[EnableCors("AdminPanel")]
|
||||
public class AdminProfileAPI(RecDBContext db) : Controller
|
||||
{
|
||||
private Task<IActionResult?> RejectInvalidKey(string key)
|
||||
{
|
||||
return Task.FromResult<IActionResult?>(key.IsAdminKey() ? null : Unauthorized("Invalid secret"));
|
||||
}
|
||||
|
||||
private async Task NotifyUser(long playerId)
|
||||
{
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == playerId);
|
||||
if (profile == null)
|
||||
return;
|
||||
|
||||
var plrData = await db.Players.FirstOrDefaultAsync(x => x.AccountId == playerId);
|
||||
if (plrData == null)
|
||||
return;
|
||||
|
||||
await Notify.SendNotify(playerId, new Notification
|
||||
{
|
||||
Id = "AccountUpdate",
|
||||
Msg = profile
|
||||
});
|
||||
await Notify.UpdateSubscriptions(playerId, Notifs.SubscriptionUpdateProfile, profile);
|
||||
|
||||
await Notify.SendNotify(playerId, new Notification
|
||||
{
|
||||
Id = "SelfAccountUpdate",
|
||||
Msg = new SelfAccount
|
||||
{
|
||||
juniorState = plrData.Birthday != null && plrData.Birthday.Value.Year > 1900
|
||||
? JuniorState.BasedOnBirthday : JuniorState.PendingOverride,
|
||||
accountId = playerId,
|
||||
parentAccountId = plrData.Parent,
|
||||
availableUsernameChanges = 99,
|
||||
bio = profile.bio,
|
||||
displayName = profile.displayName,
|
||||
email = plrData.Email,
|
||||
createdAt = profile.createdAt,
|
||||
isJunior = profile.isJunior,
|
||||
phone = plrData.Phone,
|
||||
platforms = profile.platforms,
|
||||
profileImage = profile.profileImage,
|
||||
username = profile.username
|
||||
}
|
||||
});
|
||||
}
|
||||
[HttpGet("whitelist")]
|
||||
public async Task<IActionResult> WhitelistUser([FromQuery] string key, [FromQuery] long playerId)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == playerId);
|
||||
if (profile == null) return NotFound();
|
||||
|
||||
var cachedLogins = await db.CachedLogins.Where(x => x.accountId == playerId).ToListAsync();
|
||||
|
||||
string addedIds = "";
|
||||
List<string> ids = new();
|
||||
foreach (var cached in cachedLogins)
|
||||
{
|
||||
if (cached.platform != Platforms.Steam) continue;
|
||||
if (!SteamWhitelist.Contains(cached.platformId))
|
||||
{
|
||||
SteamWhitelist.Add(cached.platformId);
|
||||
addedIds += cached.platformId + ",";
|
||||
ids.Add(cached.platformId);
|
||||
}
|
||||
}
|
||||
|
||||
addedIds = addedIds.Trim().TrimEnd(',');
|
||||
|
||||
return Ok($"Whitelisted @{profile.username}! Added Steam IDs: {addedIds}");
|
||||
}
|
||||
[HttpGet("whitelist/toggle")]
|
||||
public async Task<IActionResult> ToggleWhitelist([FromQuery] string key, [FromQuery] bool enabled)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
SteamWhitelist.Enabled = enabled;
|
||||
|
||||
return Ok(enabled
|
||||
? "Steam whitelist is now ENABLED. Only approved Steam IDs can sign in.\nConfig: " + SteamWhitelist.FilePath
|
||||
: "Steam whitelist is now DISABLED. Anyone can sign in.");
|
||||
}
|
||||
[HttpGet("whitelist/list")]
|
||||
public async Task<IActionResult> GetWhitelist([FromQuery] string key)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
return Ok($"Whitelist {(SteamWhitelist.Enabled ? "ENABLED" : "disabled")} ({SteamWhitelist.SteamIds.Count} IDs)\nFilename: {SteamWhitelist.FilePath}\n```json\n{new SteamWhitelist.WhitelistFile { Enabled = SteamWhitelist.Enabled, SteamIds = SteamWhitelist.SteamIds.ToList() }.ToJson()}\n```");
|
||||
}
|
||||
[HttpGet("whitelist/add")]
|
||||
public async Task<IActionResult> AddWhitelistId([FromQuery] string key, [FromQuery] string steamId)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
if (string.IsNullOrWhiteSpace(steamId)) return BadRequest("No steamId given.");
|
||||
|
||||
var added = SteamWhitelist.Add(steamId);
|
||||
return Ok(added
|
||||
? $"Steam ID {steamId} was added to the whitelist."
|
||||
: $"Steam ID {steamId} is already on the whitelist.");
|
||||
}
|
||||
[HttpGet("whitelist/remove")]
|
||||
public async Task<IActionResult> RemoveWhitelistId([FromQuery] string key, [FromQuery] string steamId)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
if (string.IsNullOrWhiteSpace(steamId)) return BadRequest("No steamId given.");
|
||||
|
||||
var removed = SteamWhitelist.Remove(steamId);
|
||||
return Ok(removed
|
||||
? $"Steam ID {steamId} was removed from the whitelist."
|
||||
: $"Steam ID {steamId} is not on the whitelist.");
|
||||
}
|
||||
[HttpGet("set/uname")]
|
||||
public async Task<IActionResult> SetUsername([FromQuery] string key, [FromQuery] long playerId, [FromQuery] string newName)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == playerId);
|
||||
if (profile == null) return NotFound();
|
||||
|
||||
profile.username = newName;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await NotifyUser(profile.accountId);
|
||||
|
||||
return Ok("Updated profile!\n```json" + profile.ToJson() + "```");
|
||||
}
|
||||
[HttpGet("set/dname")]
|
||||
public async Task<IActionResult> SetDisplayName([FromQuery] string key, [FromQuery] long playerId, [FromQuery] string newName)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == playerId);
|
||||
if (profile == null) return NotFound();
|
||||
|
||||
profile.displayName = newName;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await NotifyUser(profile.accountId);
|
||||
|
||||
return Ok("Updated profile!\n```json" + profile.ToJson() + "```");
|
||||
}
|
||||
[HttpGet("set/image")]
|
||||
public async Task<IActionResult> SetProfileImage([FromQuery] string key, [FromQuery] long playerId, [FromQuery] string imgName)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == playerId);
|
||||
if (profile == null) return NotFound();
|
||||
|
||||
profile.profileImage = imgName;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
await NotifyUser(profile.accountId);
|
||||
|
||||
return Ok("Updated profile!\n```json" + profile.ToJson() + "```");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpGet("profiles/{profileId}")]
|
||||
public async Task<IActionResult> GetRoom([FromQuery] string key, long profileId)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == profileId);
|
||||
if (profile == null) return NotFound();
|
||||
return Ok(profile);
|
||||
}
|
||||
[HttpGet("ban")]
|
||||
public async Task<IActionResult> BanUser([FromQuery] string key, [FromQuery] long userId, [FromQuery] ReportCategory category, [FromQuery] int duration, [FromQuery] string reason)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == userId);
|
||||
if (profile == null) return NotFound("User does not exist.");
|
||||
|
||||
bool didBan = false;
|
||||
|
||||
|
||||
DateTime banExpiry = DateTime.MaxValue;
|
||||
string appealCode = string.Empty;
|
||||
var existingModeration = await db.Moderation.FirstOrDefaultAsync(x => x.PlayerId == userId && x.IsBan);
|
||||
if (existingModeration != null)
|
||||
{
|
||||
if (duration > 0)
|
||||
{
|
||||
|
||||
var expireTime = DateTime.UtcNow.AddMinutes(duration);
|
||||
existingModeration.ExpireDuration = expireTime;
|
||||
existingModeration.Message = reason;
|
||||
existingModeration.Category = category;
|
||||
existingModeration.IsBan = true;
|
||||
didBan = true;
|
||||
banExpiry = expireTime;
|
||||
}
|
||||
else if (duration == -1)
|
||||
{
|
||||
|
||||
existingModeration.ExpireDuration = DateTime.MaxValue;
|
||||
existingModeration.Message = reason;
|
||||
existingModeration.Category = category;
|
||||
existingModeration.IsBan = true;
|
||||
didBan = true;
|
||||
banExpiry = DateTime.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
db.Moderation.Remove(existingModeration);
|
||||
didBan = false;
|
||||
|
||||
|
||||
|
||||
|
||||
await Notify.SendNotify(userId, new Notification { Id = NotificationType.RefreshLogin, Msg = profile });
|
||||
|
||||
await PlayerUtils.ReleaseModerationHoldAsync(userId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (duration > 0)
|
||||
{
|
||||
|
||||
|
||||
|
||||
var expireTime = DateTime.UtcNow.AddMinutes(duration);
|
||||
var (newCode, _) = await PlayerUtils.GetAppealForNewBanAsync(db, userId);
|
||||
var newModeration = new ModerationDB
|
||||
{
|
||||
Category = category,
|
||||
ExpireDuration = expireTime,
|
||||
IsBan = true,
|
||||
Message = reason,
|
||||
PlayerId = userId,
|
||||
AppealCode = newCode
|
||||
};
|
||||
await db.Moderation.AddAsync(newModeration);
|
||||
appealCode = newCode ?? string.Empty;
|
||||
didBan = true;
|
||||
banExpiry = expireTime;
|
||||
}
|
||||
else if (duration == -1)
|
||||
{
|
||||
|
||||
var expireTime = DateTime.MaxValue;
|
||||
var (newCode, _) = await PlayerUtils.GetAppealForNewBanAsync(db, userId);
|
||||
var newModeration = new ModerationDB
|
||||
{
|
||||
Category = category,
|
||||
ExpireDuration = expireTime,
|
||||
IsBan = true,
|
||||
Message = reason,
|
||||
PlayerId = userId,
|
||||
AppealCode = newCode
|
||||
};
|
||||
await db.Moderation.AddAsync(newModeration);
|
||||
appealCode = newCode ?? string.Empty;
|
||||
didBan = true;
|
||||
banExpiry = expireTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (didBan)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var activeBan = existingModeration ?? await db.Moderation.FirstOrDefaultAsync(x => x.PlayerId == userId && x.IsBan);
|
||||
if (activeBan != null && string.IsNullOrWhiteSpace(activeBan.AppealCode))
|
||||
{
|
||||
var (newCode, _) = await PlayerUtils.GetAppealForNewBanAsync(db, userId);
|
||||
activeBan.AppealCode = newCode;
|
||||
appealCode = newCode ?? string.Empty;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(appealCode) && activeBan != null)
|
||||
{
|
||||
appealCode = activeBan.AppealCode ?? string.Empty;
|
||||
}
|
||||
|
||||
await db.VoteKicks.AddAsync(new VoteKickLog
|
||||
{
|
||||
PlayerId = userId,
|
||||
Reason = reason,
|
||||
Category = category,
|
||||
IsForce = true,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
ExpiresAt = banExpiry
|
||||
});
|
||||
|
||||
|
||||
|
||||
var rep = await db.Reputations.FirstOrDefaultAsync(x => x.AccountId == userId);
|
||||
if (rep != null)
|
||||
rep.ApplyBanPenalty(permanent: duration == -1, cheating: false);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
|
||||
|
||||
|
||||
if (didBan)
|
||||
{
|
||||
await AdminActionDiscord.LogBanAsync(db, userId,
|
||||
bannedBy: "Admin (Profile API)",
|
||||
causedByReports: false,
|
||||
reason: reason,
|
||||
category: category,
|
||||
expiry: banExpiry,
|
||||
isModerator: false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (didBan)
|
||||
{
|
||||
var banStart = (await db.Moderation.AsNoTracking().FirstOrDefaultAsync(x => x.PlayerId == userId && x.IsBan))?.CreatedAt;
|
||||
await PlayerUtils.KickOrPromptAsync(db, userId, PlayerUtils.BuildBlockDetails(true, category, reason, banExpiry, appealCode: appealCode, startedAtUtc: banStart ?? DateTime.UtcNow));
|
||||
}
|
||||
|
||||
if (didBan)
|
||||
{
|
||||
|
||||
return Ok(duration == -1
|
||||
? $"Successfully banned @{profile.username} permanently!"
|
||||
: $"Successfully banned @{profile.username} for {duration} minutes!");
|
||||
}
|
||||
else
|
||||
return Ok($"Successfully unbanned @{profile.username}!");
|
||||
}
|
||||
|
||||
[HttpGet("coach")]
|
||||
public async Task<IActionResult> SendCoachMessage([FromQuery] string key, [FromQuery] long userId, [FromQuery] string message)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == userId);
|
||||
if (profile == null) return NotFound("User does not exist.");
|
||||
|
||||
|
||||
|
||||
|
||||
await Notify.SendMessage(userId, new Message
|
||||
{
|
||||
Type = MessageType.CoachMessage,
|
||||
Data = message,
|
||||
FromPlayerId = 1
|
||||
});
|
||||
|
||||
return Ok($"Successfully sent coach message to @{profile.username}!");
|
||||
}
|
||||
|
||||
[HttpGet("coach/all")]
|
||||
public async Task<IActionResult> SendCoachMessageToAll([FromQuery] string key, [FromQuery] string message)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profiles = await db.Profiles.ToListAsync();
|
||||
long playerCount = 0;
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
|
||||
if (profile.accountId == 1) continue;
|
||||
|
||||
await Notify.SendMessage(profile.accountId, new Message
|
||||
{
|
||||
Type = MessageType.CoachMessage,
|
||||
Data = message,
|
||||
FromPlayerId = 1
|
||||
});
|
||||
playerCount++;
|
||||
}
|
||||
|
||||
return Ok($"Successfully sent coach message to {playerCount} players!");
|
||||
}
|
||||
|
||||
[HttpGet("forcequit")]
|
||||
public async Task<IActionResult> ForceQuitPlayer([FromQuery] string key, [FromQuery] long userId)
|
||||
{
|
||||
var rejection = await RejectInvalidKey(key);
|
||||
if (rejection != null) return rejection;
|
||||
|
||||
var profile = await db.Profiles.FirstOrDefaultAsync(x => x.accountId == userId);
|
||||
if (profile == null) return NotFound("User does not exist.");
|
||||
|
||||
|
||||
|
||||
Notify.SendReliably(userId, new Notification { Id = NotificationType.ModerationQuitGame, Msg = "" },
|
||||
TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(1), Notify.EmptyObjectPayload(NotificationType.ModerationQuitGame));
|
||||
|
||||
|
||||
var presence = await db.Presences.FirstOrDefaultAsync(x => x.playerId == userId);
|
||||
if (presence != null)
|
||||
{
|
||||
presence.isOnline = false;
|
||||
presence.roomInstance = null;
|
||||
presence.LoginLock = null;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return Ok($"Successfully force quit @{profile.username}!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user