Files
rr3-server/RR3CommunityServer/Services/ServiceImplementations.cs
Daniel Elliott 0a327f3a8b Initial commit: RR3 Community Server with web admin panel
- ASP.NET Core 8 REST API server
- 12 API endpoints matching EA Synergy protocol
- SQLite database with Entity Framework Core
- Web admin panel with Bootstrap 5
- User, Catalog, Session, Purchase management
- Comprehensive documentation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-17 22:02:12 -08:00

229 lines
6.0 KiB
C#

using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using RR3CommunityServer.Models;
namespace RR3CommunityServer.Services;
public class SessionService : ISessionService
{
private readonly RR3DbContext _context;
public SessionService(RR3DbContext context)
{
_context = context;
}
public async Task<string> CreateSession(string? synergyId = null)
{
var sessionId = Guid.NewGuid().ToString();
var session = new Session
{
SessionId = sessionId,
SynergyId = synergyId,
CreatedAt = DateTime.UtcNow,
ExpiresAt = DateTime.UtcNow.AddHours(24)
};
_context.Sessions.Add(session);
await _context.SaveChangesAsync();
return sessionId;
}
public async Task<bool> ValidateSession(string sessionId)
{
var session = await _context.Sessions
.FirstOrDefaultAsync(s => s.SessionId == sessionId);
return session != null && session.ExpiresAt > DateTime.UtcNow;
}
public async Task<string?> GetSynergyIdFromSession(string sessionId)
{
var session = await _context.Sessions
.FirstOrDefaultAsync(s => s.SessionId == sessionId);
return session?.SynergyId;
}
}
public class UserService : IUserService
{
private readonly RR3DbContext _context;
public UserService(RR3DbContext context)
{
_context = context;
}
public async Task<string> GetOrCreateDeviceId(string? existingDeviceId, string hardwareId)
{
if (!string.IsNullOrEmpty(existingDeviceId))
{
var existing = await _context.Devices
.FirstOrDefaultAsync(d => d.DeviceId == existingDeviceId);
if (existing != null)
{
existing.LastSeenAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return existing.DeviceId;
}
}
var deviceId = Guid.NewGuid().ToString();
var device = new Device
{
DeviceId = deviceId,
HardwareId = hardwareId,
CreatedAt = DateTime.UtcNow,
LastSeenAt = DateTime.UtcNow
};
_context.Devices.Add(device);
await _context.SaveChangesAsync();
return deviceId;
}
public async Task<string> ValidateDeviceId(string deviceId)
{
var device = await _context.Devices
.FirstOrDefaultAsync(d => d.DeviceId == deviceId);
if (device != null)
{
device.LastSeenAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
return "valid";
}
return "invalid";
}
public async Task<string> GetOrCreateAnonUid()
{
return await Task.FromResult(Guid.NewGuid().ToString());
}
public async Task<string> GetOrCreateSynergyId(string deviceId)
{
var user = await _context.Users
.FirstOrDefaultAsync(u => u.DeviceId == deviceId);
if (user != null)
{
return user.SynergyId;
}
var synergyId = $"SYN-{Guid.NewGuid():N}";
user = new User
{
SynergyId = synergyId,
DeviceId = deviceId,
CreatedAt = DateTime.UtcNow
};
_context.Users.Add(user);
await _context.SaveChangesAsync();
return synergyId;
}
}
public class CatalogService : ICatalogService
{
private readonly RR3DbContext _context;
public CatalogService(RR3DbContext context)
{
_context = context;
}
public async Task<List<Models.CatalogItem>> GetAvailableItems()
{
var items = await _context.CatalogItems
.Where(c => c.Available)
.ToListAsync();
return items.Select(e => new Models.CatalogItem
{
itemId = e.Sku,
sku = e.Sku,
name = e.Name,
description = e.Type,
category = e.Type,
price = e.Price,
currency = "USD"
}).ToList();
}
public async Task<List<CatalogCategory>> GetCategories()
{
var items = await _context.CatalogItems
.Where(c => c.Available)
.ToListAsync();
var categories = items.GroupBy(i => i.Type)
.Select(g => new CatalogCategory
{
categoryId = g.Key,
name = g.Key,
itemIds = g.Select(i => i.Sku).ToList()
}).ToList();
return categories;
}
public async Task<string> GetDownloadUrl(string itemId)
{
return await Task.FromResult($"https://localhost:5001/downloads/{itemId}");
}
}
public class DrmService : IDrmService
{
private readonly RR3DbContext _context;
public DrmService(RR3DbContext context)
{
_context = context;
}
public async Task<string> GenerateNonce()
{
return await Task.FromResult(Convert.ToBase64String(Guid.NewGuid().ToByteArray()));
}
public async Task<List<PurchasedItem>> GetPurchasedItems(string synergyId)
{
var purchases = await _context.Purchases
.Where(p => p.SynergyId == synergyId)
.ToListAsync();
return purchases.Select(p => new PurchasedItem
{
itemId = p.ItemId,
sku = p.Sku,
orderId = p.OrderId,
purchaseTime = new DateTimeOffset(p.PurchaseTime).ToUnixTimeSeconds(),
token = p.Token
}).ToList();
}
public async Task<bool> VerifyAndRecordPurchase(string synergyId, PurchaseVerificationRequest request)
{
// For community server, we accept all purchases
var purchase = new Purchase
{
SynergyId = synergyId,
ItemId = request.sku,
Sku = request.sku,
OrderId = request.orderId,
PurchaseTime = DateTime.UtcNow,
Token = request.receipt
};
_context.Purchases.Add(purchase);
await _context.SaveChangesAsync();
return true;
}
}