mirror of
https://github.com/CannedNet/CannedNet.git
synced 2026-10-25 23:18:54 -07:00
487 lines
19 KiB
C#
487 lines
19 KiB
C#
using System.Text.Json;
|
|
using CannedNet.Data;
|
|
using CannedNet.Models;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace CannedNet.Services.Controllers;
|
|
|
|
[ApiController, Route("admin/api")]
|
|
[Authorize(AuthenticationSchemes = "AdminApiKey")]
|
|
public class AdminController : ControllerBase
|
|
{
|
|
private const string JsonDir = "JSON";
|
|
|
|
[HttpGet("endpoints")]
|
|
public async Task<IResult> GetEndpoints()
|
|
{
|
|
var json = await System.IO.File.ReadAllTextAsync(Path.Combine(JsonDir, "endpoints.json"));
|
|
return Results.Content(json, "application/json");
|
|
}
|
|
|
|
[HttpPut("endpoints")]
|
|
public async Task<IResult> SaveEndpoints()
|
|
{
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
await System.IO.File.WriteAllTextAsync(Path.Combine(JsonDir, "endpoints.json"), body);
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
|
|
[HttpGet("accounts/search")]
|
|
public async Task<IResult> SearchAccounts([FromQuery] string q, AppDbContext db)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(q))
|
|
return Results.Ok(new List<Account>());
|
|
|
|
var accounts = await db.Accounts
|
|
.Where(a => a.Username != null && a.Username.ToLower().Contains(q.ToLower())
|
|
|| a.DisplayName != null && a.DisplayName.ToLower().Contains(q.ToLower())
|
|
|| a.AccountId.ToString() == q)
|
|
.Take(50)
|
|
.ToListAsync();
|
|
|
|
return Results.Ok(accounts);
|
|
}
|
|
|
|
[HttpGet("accounts/{id:int}")]
|
|
public async Task<IResult> GetAccount(int id, AppDbContext db)
|
|
{
|
|
var account = await db.Accounts.FindAsync(id);
|
|
if (account == null)
|
|
return Results.NotFound();
|
|
|
|
var bio = await db.PlayerBios.FirstOrDefaultAsync(b => b.accountId == id);
|
|
var progression = await db.PlayerProgressions.FirstOrDefaultAsync(p => p.PlayerId == id);
|
|
|
|
return Results.Ok(new
|
|
{
|
|
Account = account,
|
|
Bio = bio?.bio ?? "",
|
|
Progression = progression,
|
|
AvatarItems = await db.AvatarItems.Where(a => a.OwnerAccountId == id).ToListAsync(),
|
|
Settings = await db.PlayerSettings.Where(s => s.PlayerId == id).ToListAsync(),
|
|
TokenBalances = await db.TokenBalances.Where(t => t.Id == id).ToListAsync()
|
|
});
|
|
}
|
|
|
|
[HttpPut("accounts/{id:int}")]
|
|
public async Task<IResult> UpdateAccount(int id, AppDbContext db)
|
|
{
|
|
var account = await db.Accounts.FindAsync(id);
|
|
if (account == null)
|
|
return Results.NotFound();
|
|
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
var data = JsonSerializer.Deserialize<JsonElement>(body);
|
|
|
|
if (data.TryGetProperty("username", out var username))
|
|
account.Username = username.GetString();
|
|
if (data.TryGetProperty("displayName", out var displayName))
|
|
account.DisplayName = displayName.GetString();
|
|
if (data.TryGetProperty("profileImage", out var profileImage))
|
|
account.ProfileImage = profileImage.GetString();
|
|
if (data.TryGetProperty("isJunior", out var isJunior))
|
|
account.IsJunior = isJunior.GetBoolean();
|
|
if (data.TryGetProperty("personalPronouns", out var personalPronouns))
|
|
account.PersonalPronouns = personalPronouns.GetInt32();
|
|
if (data.TryGetProperty("identityFlags", out var identityFlags))
|
|
account.IdentityFlags = identityFlags.GetInt32();
|
|
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
|
|
[HttpDelete("accounts/{id:int}")]
|
|
public async Task<IResult> DeleteAccount(int id, AppDbContext db)
|
|
{
|
|
var account = await db.Accounts.FindAsync(id);
|
|
if (account == null)
|
|
return Results.NotFound();
|
|
|
|
db.Accounts.Remove(account);
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
|
|
[HttpGet("rooms/search")]
|
|
public async Task<IResult> SearchRooms([FromQuery] string q, AppDbContext db)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(q))
|
|
return Results.Ok(new List<Room>());
|
|
|
|
var rooms = await db.Rooms
|
|
.Where(r => r.Name.ToLower().Contains(q.ToLower())
|
|
|| r.Description.ToLower().Contains(q.ToLower())
|
|
|| r.RoomId.ToString() == q
|
|
|| r.Id.ToString() == q)
|
|
.Take(50)
|
|
.ToListAsync();
|
|
|
|
return Results.Ok(rooms);
|
|
}
|
|
|
|
[HttpGet("rooms/{id:int}")]
|
|
public async Task<IResult> GetRoom(int id, AppDbContext db)
|
|
{
|
|
var room = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == id);
|
|
if (room == null)
|
|
return Results.NotFound();
|
|
|
|
var subRooms = await db.SubRooms.Where(s => s.RoomId == id).ToListAsync();
|
|
var loadScreens = await db.LoadScreens.Where(l => l.RoomId == id).ToListAsync();
|
|
var promoImages = await db.PromoImages.Where(p => p.RoomId == id).ToListAsync();
|
|
var roles = await db.RoomRoles.Where(r => r.RoomId == id).ToListAsync();
|
|
|
|
return Results.Ok(new
|
|
{
|
|
Room = room,
|
|
SubRooms = subRooms,
|
|
LoadScreens = loadScreens,
|
|
PromoImages = promoImages,
|
|
Roles = roles
|
|
});
|
|
}
|
|
|
|
[HttpPut("rooms/{id:int}")]
|
|
public async Task<IResult> UpdateRoom(int id, AppDbContext db)
|
|
{
|
|
var room = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == id);
|
|
if (room == null)
|
|
return Results.NotFound();
|
|
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
var data = JsonSerializer.Deserialize<JsonElement>(body);
|
|
|
|
if (data.TryGetProperty("name", out var name))
|
|
room.Name = name.GetString() ?? room.Name;
|
|
if (data.TryGetProperty("description", out var desc))
|
|
room.Description = desc.GetString() ?? room.Description;
|
|
if (data.TryGetProperty("imageName", out var imageName))
|
|
room.ImageName = imageName.GetString() ?? room.ImageName;
|
|
if (data.TryGetProperty("state", out var state))
|
|
room.State = state.GetInt32();
|
|
if (data.TryGetProperty("accessibility", out var accessibility))
|
|
room.Accessibility = accessibility.GetInt32();
|
|
if (data.TryGetProperty("isDorm", out var isDorm))
|
|
room.IsDorm = isDorm.GetBoolean();
|
|
if (data.TryGetProperty("isRRO", out var isRRO))
|
|
room.IsRRO = isRRO.GetBoolean();
|
|
if (data.TryGetProperty("cloningAllowed", out var cloningAllowed))
|
|
room.CloningAllowed = cloningAllowed.GetBoolean();
|
|
if (data.TryGetProperty("minLevel", out var minLevel))
|
|
room.MinLevel = minLevel.GetInt32();
|
|
if (data.TryGetProperty("warningMask", out var warningMask))
|
|
room.WarningMask = warningMask.GetInt32();
|
|
if (data.TryGetProperty("customWarning", out var customWarning))
|
|
room.CustomWarning = customWarning.GetString();
|
|
if (data.TryGetProperty("supportsVRLow", out var supportsVRLow))
|
|
room.SupportsVRLow = supportsVRLow.GetBoolean();
|
|
if (data.TryGetProperty("supportsQuest2", out var supportsQuest2))
|
|
room.SupportsQuest2 = supportsQuest2.GetBoolean();
|
|
if (data.TryGetProperty("supportsMobile", out var supportsMobile))
|
|
room.SupportsMobile = supportsMobile.GetBoolean();
|
|
if (data.TryGetProperty("supportsScreens", out var supportsScreens))
|
|
room.SupportsScreens = supportsScreens.GetBoolean();
|
|
if (data.TryGetProperty("supportsWalkVR", out var supportsWalkVR))
|
|
room.SupportsWalkVR = supportsWalkVR.GetBoolean();
|
|
if (data.TryGetProperty("supportsTeleportVR", out var supportsTeleportVR))
|
|
room.SupportsTeleportVR = supportsTeleportVR.GetBoolean();
|
|
if (data.TryGetProperty("supportsJuniors", out var supportsJuniors))
|
|
room.SupportsJuniors = supportsJuniors.GetBoolean();
|
|
if (data.TryGetProperty("tags", out var tags))
|
|
room.Tags = tags.GetString() ?? room.Tags;
|
|
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
|
|
[HttpDelete("rooms/{id:int}")]
|
|
public async Task<IResult> DeleteRoom(int id, AppDbContext db)
|
|
{
|
|
var room = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == id);
|
|
if (room == null)
|
|
return Results.NotFound();
|
|
|
|
db.SubRooms.RemoveRange(db.SubRooms.Where(s => s.RoomId == id));
|
|
db.LoadScreens.RemoveRange(db.LoadScreens.Where(l => l.RoomId == id));
|
|
db.PromoImages.RemoveRange(db.PromoImages.Where(p => p.RoomId == id));
|
|
db.RoomRoles.RemoveRange(db.RoomRoles.Where(r => r.RoomId == id));
|
|
db.Rooms.Remove(room);
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
|
|
[HttpPost("rooms")]
|
|
public async Task<IResult> CreateRoom(AppDbContext db)
|
|
{
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
var data = JsonSerializer.Deserialize<JsonElement>(body);
|
|
|
|
int maxRoomId = await db.Rooms.MaxAsync(r => (int?)r.RoomId) ?? 0;
|
|
int maxId = await db.Rooms.MaxAsync(r => (int?)r.Id) ?? 0;
|
|
int newRoomId = data.TryGetProperty("roomId", out var rid) ? rid.GetInt32() : maxRoomId + 1;
|
|
|
|
var room = new Room
|
|
{
|
|
Id = maxId + 1,
|
|
RoomId = newRoomId,
|
|
Name = data.TryGetProperty("name", out var name) ? name.GetString() ?? "New Room" : "New Room",
|
|
Description = data.TryGetProperty("description", out var desc) ? desc.GetString() ?? "" : "",
|
|
CreatorAccountId = data.TryGetProperty("creatorAccountId", out var creatorId) ? creatorId.GetInt32() : 0,
|
|
ImageName = data.TryGetProperty("imageName", out var img) ? img.GetString() ?? "" : "",
|
|
State = data.TryGetProperty("state", out var state) ? state.GetInt32() : 0,
|
|
Accessibility = data.TryGetProperty("accessibility", out var accessibility) ? accessibility.GetInt32() : 0,
|
|
IsDorm = false,
|
|
IsRRO = false,
|
|
CloningAllowed = true,
|
|
SupportsVRLow = true,
|
|
SupportsQuest2 = true,
|
|
SupportsMobile = true,
|
|
SupportsScreens = true,
|
|
SupportsWalkVR = true,
|
|
SupportsTeleportVR = true,
|
|
SupportsJuniors = true,
|
|
MinLevel = 0,
|
|
WarningMask = 0,
|
|
CreatedAt = DateTime.UtcNow,
|
|
Tags = "[]"
|
|
};
|
|
|
|
db.Rooms.Add(room);
|
|
await db.SaveChangesAsync();
|
|
|
|
// create default subroom
|
|
if (data.TryGetProperty("createSubRoom", out var createSub) && createSub.GetBoolean())
|
|
{
|
|
var sub = new SubRoom
|
|
{
|
|
RoomId = newRoomId,
|
|
SubRoomId = 1,
|
|
Name = room.Name,
|
|
DataBlob = "",
|
|
IsSandbox = false,
|
|
MaxPlayers = 16,
|
|
Accessibility = 0,
|
|
// TODO: allow changing this scene id thru the dashboard
|
|
UnitySceneId = "00000000-0000-0000-0000-000000000000"
|
|
};
|
|
db.SubRooms.Add(sub);
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
return Results.Ok(room);
|
|
}
|
|
|
|
[HttpPost("rooms/{roomId:int}/subrooms")]
|
|
public async Task<IResult> CreateSubRoom(int roomId, AppDbContext db)
|
|
{
|
|
var room = await db.Rooms.FirstOrDefaultAsync(r => r.RoomId == roomId);
|
|
if (room == null)
|
|
return Results.NotFound();
|
|
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
var data = JsonSerializer.Deserialize<JsonElement>(body);
|
|
|
|
int maxSubId = await db.SubRooms.Where(s => s.RoomId == roomId).MaxAsync(s => (int?)s.SubRoomId) ?? 0;
|
|
|
|
var sub = new SubRoom
|
|
{
|
|
RoomId = roomId,
|
|
SubRoomId = maxSubId + 1,
|
|
Name = data.TryGetProperty("name", out var name) ? name.GetString() ?? "New SubRoom" : "New SubRoom",
|
|
DataBlob = data.TryGetProperty("dataBlob", out var dataBlob) ? dataBlob.GetString() ?? "" : "",
|
|
IsSandbox = data.TryGetProperty("isSandbox", out var isSandbox) && isSandbox.GetBoolean(),
|
|
MaxPlayers = data.TryGetProperty("maxPlayers", out var maxPlayers) ? maxPlayers.GetInt32() : 16,
|
|
Accessibility = data.TryGetProperty("accessibility", out var accessibility) ? accessibility.GetInt32() : 0,
|
|
UnitySceneId = data.TryGetProperty("unitySceneId", out var sceneId) ? sceneId.GetString() ?? "" : "",
|
|
};
|
|
|
|
db.SubRooms.Add(sub);
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(sub);
|
|
}
|
|
|
|
[HttpGet("config/gameconfigs")]
|
|
public async Task<IResult> GetGameConfigs()
|
|
{
|
|
var json = await System.IO.File.ReadAllTextAsync(Path.Combine(JsonDir, "gameconfigs.json"));
|
|
return Results.Content(json, "application/json");
|
|
}
|
|
|
|
[HttpPut("config/gameconfigs")]
|
|
public async Task<IResult> SaveGameConfigs()
|
|
{
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
await System.IO.File.WriteAllTextAsync(Path.Combine(JsonDir, "gameconfigs.json"), body);
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
|
|
[HttpGet("config/communityboard")]
|
|
public async Task<IResult> GetCommunityBoard()
|
|
{
|
|
var json = await System.IO.File.ReadAllTextAsync(Path.Combine(JsonDir, "communityboard.json"));
|
|
return Results.Content(json, "application/json");
|
|
}
|
|
|
|
[HttpPut("config/communityboard")]
|
|
public async Task<IResult> SaveCommunityBoard()
|
|
{
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
await System.IO.File.WriteAllTextAsync(Path.Combine(JsonDir, "communityboard.json"), body);
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
|
|
[HttpGet("config/configv2")]
|
|
public async Task<IResult> GetConfigV2()
|
|
{
|
|
var json = await System.IO.File.ReadAllTextAsync(Path.Combine(JsonDir, "configv2.json"));
|
|
return Results.Content(json, "application/json");
|
|
}
|
|
|
|
[HttpPut("config/configv2")]
|
|
public async Task<IResult> SaveConfigV2()
|
|
{
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
await System.IO.File.WriteAllTextAsync(Path.Combine(JsonDir, "configv2.json"), body);
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
|
|
[HttpGet("storefronts")]
|
|
public async Task<IResult> GetStorefronts(AppDbContext db)
|
|
{
|
|
var storefronts = await db.Storefronts
|
|
.Include(s => s.Items)
|
|
.ThenInclude(i => i.GiftDrops)
|
|
.Include(s => s.Items)
|
|
.ThenInclude(i => i.Prices)
|
|
.ToListAsync();
|
|
return Results.Ok(storefronts);
|
|
}
|
|
|
|
[HttpGet("storefronts/{id:int}")]
|
|
public async Task<IResult> GetStorefront(int id, AppDbContext db)
|
|
{
|
|
var storefront = await db.Storefronts
|
|
.Include(s => s.Items)
|
|
.ThenInclude(i => i.GiftDrops)
|
|
.Include(s => s.Items)
|
|
.ThenInclude(i => i.Prices)
|
|
.FirstOrDefaultAsync(s => s.Id == id);
|
|
|
|
if (storefront == null)
|
|
return Results.NotFound();
|
|
|
|
return Results.Ok(storefront);
|
|
}
|
|
|
|
[HttpPost("storefronts/{storefrontId:int}/items")]
|
|
public async Task<IResult> CreateStorefrontItem(int storefrontId, AppDbContext db)
|
|
{
|
|
var storefront = await db.Storefronts.FindAsync(storefrontId);
|
|
if (storefront == null)
|
|
return Results.NotFound();
|
|
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
var data = JsonSerializer.Deserialize<JsonElement>(body);
|
|
|
|
var item = new StorefrontItem
|
|
{
|
|
StorefrontId = storefrontId,
|
|
PurchasableItemId = data.TryGetProperty("purchasableItemId", out var pid) ? pid.GetInt32() : 0,
|
|
Type = data.TryGetProperty("type", out var type) ? type.GetInt32() : 0,
|
|
IsFeatured = data.TryGetProperty("isFeatured", out var featured) && featured.GetBoolean(),
|
|
NewUntil = data.TryGetProperty("newUntil", out var newUntil) && newUntil.ValueKind == JsonValueKind.String
|
|
? DateTime.Parse(newUntil.GetString()!)
|
|
: null
|
|
};
|
|
|
|
db.StorefrontItems.Add(item);
|
|
await db.SaveChangesAsync();
|
|
|
|
// create default price if provided
|
|
if (data.TryGetProperty("price", out var priceEl) && data.TryGetProperty("currencyType", out var currencyEl))
|
|
{
|
|
db.StorefrontPrices.Add(new StorefrontPrice
|
|
{
|
|
StorefrontItemId = item.Id,
|
|
CurrencyType = currencyEl.GetInt32(),
|
|
Price = priceEl.GetInt32()
|
|
});
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
return Results.Ok(item);
|
|
}
|
|
|
|
[HttpPut("storefronts/items/{itemId:int}")]
|
|
public async Task<IResult> UpdateStorefrontItem(int itemId, AppDbContext db)
|
|
{
|
|
var item = await db.StorefrontItems
|
|
.Include(i => i.Prices)
|
|
.Include(i => i.GiftDrops)
|
|
.FirstOrDefaultAsync(i => i.Id == itemId);
|
|
|
|
if (item == null)
|
|
return Results.NotFound();
|
|
|
|
using var reader = new StreamReader(Request.Body);
|
|
var body = await reader.ReadToEndAsync();
|
|
var data = JsonSerializer.Deserialize<JsonElement>(body);
|
|
|
|
if (data.TryGetProperty("purchasableItemId", out var pid))
|
|
item.PurchasableItemId = pid.GetInt32();
|
|
if (data.TryGetProperty("type", out var type))
|
|
item.Type = type.GetInt32();
|
|
if (data.TryGetProperty("isFeatured", out var featured))
|
|
item.IsFeatured = featured.GetBoolean();
|
|
if (data.TryGetProperty("newUntil", out var newUntil))
|
|
item.NewUntil = newUntil.ValueKind == JsonValueKind.String ? DateTime.Parse(newUntil.GetString()!) : null;
|
|
|
|
// update prices
|
|
if (data.TryGetProperty("prices", out var prices))
|
|
{
|
|
db.StorefrontPrices.RemoveRange(item.Prices);
|
|
foreach (var p in prices.EnumerateArray())
|
|
{
|
|
db.StorefrontPrices.Add(new StorefrontPrice
|
|
{
|
|
StorefrontItemId = item.Id,
|
|
CurrencyType = p.GetProperty("currencyType").GetInt32(),
|
|
Price = p.GetProperty("price").GetInt32()
|
|
});
|
|
}
|
|
}
|
|
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(item);
|
|
}
|
|
|
|
[HttpDelete("storefronts/items/{itemId:int}")]
|
|
public async Task<IResult> DeleteStorefrontItem(int itemId, AppDbContext db)
|
|
{
|
|
var item = await db.StorefrontItems
|
|
.Include(i => i.Prices)
|
|
.Include(i => i.GiftDrops)
|
|
.FirstOrDefaultAsync(i => i.Id == itemId);
|
|
|
|
if (item == null)
|
|
return Results.NotFound();
|
|
|
|
db.StorefrontPrices.RemoveRange(item.Prices);
|
|
db.GiftDrops.RemoveRange(item.GiftDrops);
|
|
db.StorefrontItems.Remove(item);
|
|
await db.SaveChangesAsync();
|
|
return Results.Ok(new { success = true });
|
|
}
|
|
}
|