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>
80 lines
2.7 KiB
C#
80 lines
2.7 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 DbUserService : BaseDbService<PlayFabUser>
|
|
{
|
|
public DbUserService(IOptions<DatabaseSettings> settings) : base(settings, nameof(PlayFabUser))
|
|
{
|
|
}
|
|
|
|
public async Task<PlayFabUser> FindAsync(PlayFabUserAuthType type, string key)
|
|
{
|
|
return await Collection.Find(user => user.Auth.Any(auth =>
|
|
auth.Type == type &&
|
|
auth.Key == key)).FirstOrDefaultAsync();
|
|
}
|
|
|
|
// Resolve a player by its PlayFab id (used to display squad member names).
|
|
public async Task<PlayFabUser> FindByIdAsync(string id)
|
|
{
|
|
return await Collection.Find(user => user.Id == id).FirstOrDefaultAsync();
|
|
}
|
|
|
|
// Admin back-office: list every player.
|
|
public async Task<List<PlayFabUser>> GetAllAsync()
|
|
{
|
|
return await Collection.Find(_ => true).ToListAsync();
|
|
}
|
|
|
|
// Persist a display-name change (UpdateUserTitleDisplayName). Without this the rename
|
|
// is echoed back to the client but lost on next login.
|
|
public async Task UpdateDisplayNameAsync(string id, string displayName)
|
|
{
|
|
var update = Builders<PlayFabUser>.Update.Set(user => user.DisplayName, displayName);
|
|
await Collection.UpdateOneAsync(user => user.Id == id, update);
|
|
}
|
|
|
|
private async Task<PlayFabUser> CreateAsync(PlayFabUserAuthType type, string key)
|
|
{
|
|
var user = new PlayFabUser
|
|
{
|
|
DisplayName = "Unknown",
|
|
Auth = new List<PlayFabUserAuth>
|
|
{
|
|
new PlayFabUserAuth
|
|
{
|
|
Type = type,
|
|
Key = key
|
|
}
|
|
}
|
|
};
|
|
|
|
await Collection.InsertOneAsync(user);
|
|
|
|
return user;
|
|
}
|
|
|
|
public async Task<PlayFabUser> FindOrCreateAsync(PlayFabUserAuthType type, string key)
|
|
{
|
|
return await FindAsync(type, key) ??
|
|
await CreateAsync(type, key);
|
|
}
|
|
|
|
// Resolve several auth keys (e.g. a batch of Steam IDs) to the players that exist on
|
|
// this server in a single query. Used by the friends list to keep only the imported
|
|
// Steam friends who have actually played here.
|
|
public async Task<List<PlayFabUser>> FindManyAsync(PlayFabUserAuthType type, IReadOnlyCollection<string> keys)
|
|
{
|
|
if (keys == null || keys.Count == 0)
|
|
{
|
|
return new List<PlayFabUser>();
|
|
}
|
|
var wanted = new HashSet<string>(keys);
|
|
return await Collection.Find(user => user.Auth.Any(auth =>
|
|
auth.Type == type && wanted.Contains(auth.Key))).ToListAsync();
|
|
}
|
|
} |