Build & Deploy / build (push) Successful in 38s
Filament-style dark UI served directly by the API:
- Dashboard: player/entity/userdata/titledata counts + recent players
- /admin/players: searchable player list (name/id/steam)
- /admin/players/{id}: full player view — balance/faction/Fortuna cards, inventory
table, and every UserData key pretty-printed (collapsible)
- /admin/titledata + /admin/titledata/{key}: browse in-memory title data
Restricted to LAN/loopback callers (RemoteIpAddress check) so it is never reachable
from the public internet even though :8443 is port-forwarded.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
2.3 KiB
C#
70 lines
2.3 KiB
C#
using Microsoft.Extensions.Options;
|
|
using MongoDB.Driver;
|
|
using Prospect.Server.Api.Config;
|
|
using Prospect.Server.Api.Services.Database.Models;
|
|
|
|
namespace Prospect.Server.Api.Services.Database;
|
|
|
|
public class DbUserDataService : BaseDbService<PlayFabUserData>
|
|
{
|
|
public DbUserDataService(IOptions<DatabaseSettings> options) : base(options, nameof(PlayFabUserData))
|
|
{
|
|
}
|
|
|
|
public async Task<bool> HasAsync(string playFabId, string key)
|
|
{
|
|
return await Collection.Find(data => data.PlayFabId == playFabId && data.Key == key).AnyAsync();
|
|
}
|
|
|
|
public async Task<PlayFabUserData?> FindAsync(string playFabId, string key)
|
|
{
|
|
return await Collection.Find(data => data.PlayFabId == playFabId && data.Key == key).SingleOrDefaultAsync();
|
|
}
|
|
|
|
public async Task<PlayFabUserData> InsertAsync(string playFabId, string key, string value, bool isPublic)
|
|
{
|
|
var data = new PlayFabUserData
|
|
{
|
|
PlayFabId = playFabId,
|
|
Key = key,
|
|
Value = value,
|
|
Public = isPublic,
|
|
LastUpdated = DateTime.UtcNow
|
|
};
|
|
|
|
await Collection.InsertOneAsync(data);
|
|
|
|
return data;
|
|
}
|
|
|
|
public async Task<IAsyncCursor<PlayFabUserData>> AllAsync(string playFabId, bool publicOnly)
|
|
{
|
|
var query = publicOnly
|
|
? Collection.Find(data => data.PlayFabId == playFabId && data.Public)
|
|
: Collection.Find(data => data.PlayFabId == playFabId);
|
|
|
|
return await query.ToCursorAsync();
|
|
}
|
|
|
|
// Admin back-office: every key/value stored for a player.
|
|
public async Task<List<PlayFabUserData>> GetAllForUserAsync(string playFabId)
|
|
{
|
|
return await Collection.Find(data => data.PlayFabId == playFabId)
|
|
.SortBy(data => data.Key).ToListAsync();
|
|
}
|
|
|
|
// Admin back-office: total stored key/value documents.
|
|
public async Task<long> CountAsync()
|
|
{
|
|
return await Collection.CountDocumentsAsync(_ => true);
|
|
}
|
|
|
|
public async Task UpdateValueAsync(string dataId, string value)
|
|
{
|
|
var update = Builders<PlayFabUserData>.Update
|
|
.Set(data => data.Value, value)
|
|
.Set(data => data.LastUpdated, DateTime.UtcNow);
|
|
|
|
await Collection.UpdateOneAsync(data => data.Id == dataId, update);
|
|
}
|
|
} |