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>
This commit is contained in:
228
RR3CommunityServer/Services/ServiceImplementations.cs
Normal file
228
RR3CommunityServer/Services/ServiceImplementations.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user