mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
feat: Enhance room management and moderation APIs
- Added support for multiple tag filters in HotRoomServer endpoint, allowing clients to query hot rooms with multiple tags. - Introduced a new DeleteRoom endpoint for owners to soft-archive their rooms, ensuring room data integrity. - Updated MagicDoor endpoint to return a structured response including refresh timing. - Improved reporting functionality to handle form submissions correctly, ensuring room reports are persisted. - Refactored per-field mutation endpoints to support both legacy and new client formats, accommodating changes in request structure. - Implemented a multi-field payload reader to handle complex requests more efficiently. - Enhanced logging for room code management to track player actions and conflicts.
This commit is contained in:
@@ -274,6 +274,14 @@ public class RoomKeysController(DorkNetDbContext db) : ControllerBase
|
||||
RoomKey = key is null ? null : ToWire(key),
|
||||
};
|
||||
|
||||
/// <summary>The 2023-03-21 room-key formatter reads nine members —
|
||||
/// RoomKeyId, ReplicationId, RoomId, Name, Description, Price,
|
||||
/// PurchaseCurrencyId, CreatedAt, ImageName
|
||||
/// (RecNet.Runtime/ANHGLIFGACE.txt:627-814 name table, :829-993 reader).
|
||||
/// CreatedAt was missing and silently defaulted to DateTime.MinValue in the
|
||||
/// key ring. PurchaseCurrencyId (Nullable<Guid>) and ImageName have no
|
||||
/// column on RoomKeyEntity yet, so they are still omitted rather than
|
||||
/// emitted as permanent nulls — both are absent-tolerant on the client.</summary>
|
||||
public static object ToWire(RoomKeyEntity key) => new
|
||||
{
|
||||
RoomKeyId = key.Id,
|
||||
@@ -284,6 +292,7 @@ public class RoomKeysController(DorkNetDbContext db) : ControllerBase
|
||||
key.Name,
|
||||
key.Description,
|
||||
key.Price,
|
||||
key.CreatedAt,
|
||||
};
|
||||
|
||||
private static RoomKeyStatus ValidateText(string? name, string? description, int price)
|
||||
@@ -328,50 +337,82 @@ public class RoomKeysController(DorkNetDbContext db) : ControllerBase
|
||||
return ids.Distinct().Take(200).ToList();
|
||||
}
|
||||
|
||||
/// <summary>Read a room-key write request.
|
||||
///
|
||||
/// The 2023-03-21 client puts every scalar of create/update in the QUERY
|
||||
/// STRING, not in a body: <c>BNDIAONDFFF.AFGEDDANEKP</c> only appends to the
|
||||
/// param dictionary at field +0x20 (BNDIAONDFFF.txt:492-506), and the send
|
||||
/// path joins that dictionary with <c>"&"</c> and glues it onto the URL
|
||||
/// behind <c>"?"</c> (BNDIAONDFFF.txt:3196, 3238, 3248-3251). So
|
||||
/// <c>POST api/roomkeys/v1/create</c> and
|
||||
/// <c>PUT api/roomkeys/v1/update*</c> arrive with an EMPTY body — reading
|
||||
/// form/JSON only left Name null and RoomId 0, i.e. every create answered
|
||||
/// NameTooShort and every edit DoesNotExist.
|
||||
///
|
||||
/// Query wins, form second, and a JSON body is only parsed when neither
|
||||
/// carried anything (older clients and the admin UI still post JSON).</summary>
|
||||
private async Task<T> ReadBodyAsync<T>() where T : new()
|
||||
{
|
||||
if (Request.HasFormContentType)
|
||||
var form = Request.HasFormContentType ? await Request.ReadFormAsync() : null;
|
||||
|
||||
string? Field(params string[] names)
|
||||
{
|
||||
var form = await Request.ReadFormAsync();
|
||||
if (typeof(T) == typeof(NewRoomKeyRequest))
|
||||
foreach (var name in names)
|
||||
{
|
||||
var q = Request.Query[name].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(q)) return q;
|
||||
if (form is not null)
|
||||
{
|
||||
var f = form[name].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(f)) return f;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(NewRoomKeyRequest))
|
||||
{
|
||||
var roomId = Field("roomId", "RoomId");
|
||||
var name = Field("name", "Name");
|
||||
var description = Field("description", "Description");
|
||||
var price = Field("price", "Price");
|
||||
if (roomId is not null || name is not null || description is not null || price is not null)
|
||||
{
|
||||
object req = new NewRoomKeyRequest
|
||||
{
|
||||
RoomId = long.TryParse(form["roomId"].FirstOrDefault()
|
||||
?? form["RoomId"].FirstOrDefault(), out var roomId)
|
||||
? roomId
|
||||
: 0,
|
||||
Name = form["name"].FirstOrDefault() ?? form["Name"].FirstOrDefault(),
|
||||
Description = form["description"].FirstOrDefault() ?? form["Description"].FirstOrDefault(),
|
||||
Price = int.TryParse(form["price"].FirstOrDefault()
|
||||
?? form["Price"].FirstOrDefault(), out var price)
|
||||
? price
|
||||
: 0,
|
||||
RoomId = long.TryParse(roomId, out var parsedRoomId) ? parsedRoomId : 0,
|
||||
Name = name,
|
||||
Description = description,
|
||||
Price = int.TryParse(price, out var parsedPrice) ? parsedPrice : 0,
|
||||
};
|
||||
return (T)req;
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(UpdateRoomKeyRequest))
|
||||
}
|
||||
else if (typeof(T) == typeof(UpdateRoomKeyRequest))
|
||||
{
|
||||
// updateName sends only Name, updatePrice only Price, etc. — every
|
||||
// field the client omitted stays null here and the handler keeps the
|
||||
// stored value (AHMBBJNANBP.txt:1300-1345, 1550-1559, 1763-1772,
|
||||
// 2018-2043).
|
||||
var roomKeyId = Field("roomKeyId", "RoomKeyId");
|
||||
var name = Field("name", "Name");
|
||||
var description = Field("description", "Description");
|
||||
var price = Field("price", "Price");
|
||||
if (roomKeyId is not null || name is not null || description is not null || price is not null)
|
||||
{
|
||||
object req = new UpdateRoomKeyRequest
|
||||
{
|
||||
RoomKeyId = long.TryParse(form["roomKeyId"].FirstOrDefault()
|
||||
?? form["RoomKeyId"].FirstOrDefault(), out var roomKeyId)
|
||||
? roomKeyId
|
||||
: 0,
|
||||
Name = form["name"].FirstOrDefault() ?? form["Name"].FirstOrDefault(),
|
||||
Description = form["description"].FirstOrDefault() ?? form["Description"].FirstOrDefault(),
|
||||
Price = int.TryParse(form["price"].FirstOrDefault()
|
||||
?? form["Price"].FirstOrDefault(), out var price)
|
||||
? price
|
||||
: null,
|
||||
RoomKeyId = long.TryParse(roomKeyId, out var parsedKeyId) ? parsedKeyId : 0,
|
||||
Name = name,
|
||||
Description = description,
|
||||
Price = int.TryParse(price, out var parsedPrice) ? parsedPrice : null,
|
||||
};
|
||||
return (T)req;
|
||||
}
|
||||
|
||||
return new T();
|
||||
}
|
||||
|
||||
if (form is not null) return new T();
|
||||
|
||||
try
|
||||
{
|
||||
return await JsonSerializer.DeserializeAsync<T>(Request.Body, JsonOptions) ?? new T();
|
||||
|
||||
Reference in New Issue
Block a user