Add remaining project files

This commit is contained in:
Marco Baldwin
2026-07-23 18:21:43 -07:00
parent c12d8ac35d
commit 6e15e89a9d
453 changed files with 64265 additions and 0 deletions
+141
View File
@@ -0,0 +1,141 @@
using DeluxeBackend.Models;
using DeluxeBackend.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using static DeluxeBackend.Enums;
using static System.Runtime.InteropServices.JavaScript.JSType;
using static DeluxeBackend.Extensions.RoomExtensions;
namespace DeluxeBackend.Controllers.Rooms
{
[Route("Rooms")]
[ApiController]
public class GetController(ILiteDbService db, IJwtService jwt, DiscordBotService discord) : ControllerBase
{
[HttpGet("rooms")]
public async Task<IActionResult> Room([FromQuery(Name = "name"), Required] string roomName, [FromQuery(Name = "include")] RoomDetailsMask include = RoomDetailsMask.None)
{
Room? room = db.Rooms.Include(x => x.Creator).FindOne(x => x.Name == roomName);
if (room == null) return NotFound();
Account? account = await jwt.GetLogin(User);
if (room.Accessibility == RoomAccessibility.Private)
{
if (account == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner))
{
AccountPresence presence = db.Presences.Include(x => x.Account).Include(x => x.Instance).Include(x => x.Instance!.SubRoom).Include(x => x.Instance!.SubRoom!.Room).Include(x => x.Instance!.SubRoom!.Room!.Creator).FindOne(x => x.Account.Id == account.Id);
presence ??= new AccountPresence
{
Account = account,
};
bool blockData = true;
if (presence.Instance != null)
{
if (presence.Instance!.SubRoom!.Room!.Id == room.Id)
{
blockData = false;
}
}
if (blockData) { include &= ~RoomDetailsMask.DataBlob; }
}
}
if (room.Accessibility == RoomAccessibility.Unlisted)
{
if (account == null) return NotFound();
}
if (account == null)
{
include &= ~RoomDetailsMask.DataBlob;
}
return Ok(await room.ToDictionary(discord, db, include));
}
[HttpGet("rooms/{roomId}")]
public async Task<IActionResult> RoomById([FromRoute(Name = "roomId"), Required] int roomId, [FromQuery(Name = "include")] RoomDetailsMask include = RoomDetailsMask.None)
{
Room? room = db.Rooms.Include(x => x.Creator).FindById(roomId);
if (room == null) return NotFound();
Account? account = await jwt.GetLogin(User);
if (room.Accessibility == RoomAccessibility.Private)
{
if (account == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner))
{
AccountPresence presence = db.Presences.Include(x => x.Account).Include(x => x.Instance).Include(x => x.Instance!.SubRoom).Include(x => x.Instance!.SubRoom!.Room).Include(x => x.Instance!.SubRoom!.Room!.Creator).FindOne(x => x.Account.Id == account.Id);
presence ??= new AccountPresence
{
Account = account,
};
bool blockData = true;
if (presence.Instance != null)
{
if (presence.Instance!.SubRoom!.Room!.Id == room.Id)
{
blockData = false;
}
}
if (blockData) { include &= ~RoomDetailsMask.DataBlob; }
}
}
if (room.Accessibility == RoomAccessibility.Unlisted)
{
if (account == null) return NotFound();
}
if (account == null)
{
include &= ~RoomDetailsMask.DataBlob;
}
return Ok(await room.ToDictionary(discord, db, include));
}
[HttpGet("rooms/bulk")]
public async Task<IActionResult> Bulk([FromQuery(Name = "name")] List<string> roomNames, [FromQuery(Name = "id")] List<long> roomIds)
{
roomNames ??= [];
roomIds ??= [];
Account? account = await jwt.GetLogin(User);
var rooms = db.Rooms
.Include(x => x.Creator)
.FindAll()
.Where(x =>
roomIds.Contains(x.Id) ||
roomNames.Contains(x.Name))
.ToList();
List<Dictionary<string, object>> response = [];
foreach (var room in rooms)
{
if (room.Accessibility == RoomAccessibility.Private)
{
if (account == null)
{
continue;
}
}
if (room.Accessibility == RoomAccessibility.Unlisted)
{
if (account == null)
{
continue;
}
}
response.Add(await room.ToDictionary(discord, db, RoomDetailsMask.None));
}
return Ok(response);
}
}
}
@@ -0,0 +1,108 @@
using DeluxeBackend.Models;
using DeluxeBackend.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace DeluxeBackend.Controllers.Rooms
{
[Route("Rooms/rooms/{roomId:long}/interactionby")]
[ApiController]
[Authorize]
public class InteractionByController(ILiteDbService db, IJwtService jwt) : ControllerBase
{
[HttpGet("me")]
public async Task<IActionResult> Me([FromRoute] long roomId)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
string? lastVisitedAt = null;
if (account.VisitedRooms != null && account.VisitedRooms.TryGetValue(roomId, out var visitDate))
{
lastVisitedAt = visitDate.ToString("O");
}
return Ok(new
{
Cheered = room.Stats.CheeredIds.Contains(account.Id),
Favorited = room.Stats.FavoritedIds.Contains(account.Id),
LastVisitedAt = lastVisitedAt
});
}
[HttpPut("me/cheer")]
public async Task<IActionResult> Cheer([FromRoute] long roomId)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.Stats.CheeredIds.Contains(account.Id))
{
room.Stats.CheeredIds.Add(account.Id);
db.Rooms.Update(room);
}
return Ok(new { Success = true });
}
[HttpDelete("me/cheer")]
public async Task<IActionResult> RemoveCheer([FromRoute] long roomId)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (room.Stats.CheeredIds.Contains(account.Id))
{
room.Stats.CheeredIds.Remove(account.Id);
db.Rooms.Update(room);
}
return Ok(new { Success = true });
}
[HttpPut("me/favorite")]
public async Task<IActionResult> Favorite([FromRoute] long roomId)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.Stats.FavoritedIds.Contains(account.Id))
{
room.Stats.FavoritedIds.Add(account.Id);
db.Rooms.Update(room);
}
return Ok(new { Success = true });
}
[HttpDelete("me/favorite")]
public async Task<IActionResult> RemoveFavorite([FromRoute] long roomId)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (room.Stats.FavoritedIds.Contains(account.Id))
{
room.Stats.FavoritedIds.Remove(account.Id);
db.Rooms.Update(room);
}
return Ok(new { Success = true });
}
}
}
+182
View File
@@ -0,0 +1,182 @@
using DeluxeBackend.Models;
using DeluxeBackend.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.ComponentModel.DataAnnotations;
using static DeluxeBackend.Enums;
using static DeluxeBackend.Extensions.RoomExtensions;
namespace DeluxeBackend.Controllers.Rooms
{
[Route("Rooms/rooms/{roomId:long}")]
[ApiController]
[Authorize]
public class RoomEditController(ILiteDbService db,IJwtService jwt,DiscordBotService discord,INotificationService ws) : ControllerBase
{
[HttpPut("automute")]
public async Task<IActionResult> Automute([FromRoute] long roomId, [FromForm] bool disable)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner)) return Forbid();
room.DisableMicAutoMute = disable;
db.Rooms.Update(room);
await ws.SendToPlayerSubs(account.Id, "RoomUpdate", await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetailsNoDataBlob));
return Ok(new { Success = true, Value = await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetails) });
}
[HttpPut("cloning")]
public async Task<IActionResult> Cloning([FromRoute] long roomId, [FromForm] bool cloningAllowed)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner)) return Forbid();
room.CloningAllowed = cloningAllowed;
db.Rooms.Update(room);
await ws.SendToPlayerSubs(account.Id, "RoomUpdate", await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetailsNoDataBlob));
return Ok(new { Success = true, Value = await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetails) });
}
[HttpPut("creator")]
public async Task<IActionResult> Creator([FromRoute] long roomId, [FromForm] ulong accountId)
{
return Ok(new { Success = false, Error = "Room ownership transfers are currently disabled." });
}
[HttpPut("comments")]
public async Task<IActionResult> Comments([FromRoute] long roomId, [FromForm] bool disable)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner)) return Forbid();
room.DisableRoomComments = disable;
db.Rooms.Update(room);
await ws.SendToPlayerSubs(account.Id, "RoomUpdate", await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetailsNoDataBlob));
return Ok(new { Success = true, Value = await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetails) });
}
[HttpPut("voice_chat_encryption")]
public async Task<IActionResult> VoiceChatEncryption([FromRoute] long roomId, [FromForm] bool encryptVoiceChat)
{
return Ok(new { Success = false, Error = "Voice chat encryption settings cannot be modified manually." });
}
[HttpPut("restrictions")]
public async Task<IActionResult> Restrictions(
[FromRoute] long roomId,
[FromForm] bool supportsScreens,
[FromForm] bool supportsWalkVR,
[FromForm] bool supportsTeleportVR,
[FromForm] bool supportsJuniors)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner)) return Forbid();
RoomSupports flags = RoomSupports.None;
if (supportsScreens) flags |= RoomSupports.Screens;
if (supportsWalkVR) flags |= RoomSupports.WalkVR;
if (supportsTeleportVR) flags |= RoomSupports.TeleportVR;
if (supportsJuniors) flags |= RoomSupports.Juniors;
room.SupportedPlayerTypes = flags;
db.Rooms.Update(room);
await ws.SendToPlayerSubs(account.Id, "RoomUpdate", await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetailsNoDataBlob));
return Ok(new { Success = true, Value = await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetails) });
}
[HttpPut("warning")]
public async Task<IActionResult> Warning([FromRoute] long roomId, [FromForm] RoomWarningMask warningMask, [FromForm] string customWarning = "")
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner)) return Forbid();
// Mitigation: Guard against severe memory buffer bloat / database inflation DoS vectors
if (customWarning != null && customWarning.Length > 500)
{
return BadRequest(new { Success = false, Error = "Custom warning string length cannot exceed 500 characters." });
}
room.WarningMask = warningMask;
room.CustomWarning = string.IsNullOrWhiteSpace(customWarning) ? string.Empty : customWarning.Trim();
db.Rooms.Update(room);
await ws.SendToPlayerSubs(account.Id, "RoomUpdate", await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetailsNoDataBlob));
return Ok(new { Success = true, Value = await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetails) });
}
[HttpPut("image")]
public async Task<IActionResult> Image([FromRoute] long roomId, [FromForm] string imageName = "")
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner)) return Forbid();
// Mitigation: Defend against Path Traversal escape sequences (e.g., "../../../etc")
string sanitizedImage = Path.GetFileName(imageName);
if (imageName != sanitizedImage && !string.IsNullOrWhiteSpace(imageName))
{
return BadRequest(new { Success = false, Error = "Invalid room image asset format." });
}
room.ImageName = imageName;
db.Rooms.Update(room);
await ws.SendToPlayerSubs(account.Id, "RoomUpdate", await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetailsNoDataBlob));
return Ok(new { Success = true, Value = await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetails) });
}
[HttpPut("accessibility")]
public async Task<IActionResult> Accessibility([FromRoute] long roomId, [FromForm] RoomAccessibility accessibility)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
Room? room = db.Rooms.FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner)) return Forbid();
room.Accessibility = accessibility;
db.Rooms.Update(room);
await ws.SendToPlayerSubs(account.Id, "RoomUpdate", await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetailsNoDataBlob));
return Ok(new { Success = true, Value = await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetails) });
}
}
}