LANCommander/LANCommander.Server/Endpoints/SaveEndpoints.cs
2026-06-22 17:09:50 -05:00

304 lines
No EOL
11 KiB
C#

using AutoMapper;
using LANCommander.SDK.Enums;
using LANCommander.Server.Data.Models;
using LANCommander.Server.Extensions;
using LANCommander.Server.Services;
using Microsoft.AspNetCore.Mvc;
using System.DirectoryServices.AccountManagement;
using System.Security.Claims;
using File = System.IO.File;
using SortDirection = LANCommander.Server.Data.Enums.SortDirection;
namespace LANCommander.Server.Endpoints;
public static class SaveEndpoints
{
public static void MapSaveEndpoints(this IEndpointRouteBuilder routes)
{
var group = routes.MapGroup("/api/Saves");
group.MapGet("/", GetAsync);
group.MapGet("/{id:guid}", GetByIdAsync);
group.MapGet("/{id:guid}/Download", DownloadSaveByIdAsync);
group.MapDelete("/{id:guid}", DeleteByIdAsync);
group.MapGet("/Game/{gameId:guid}", GetSavesByGameAsync);
group.MapPost("/Game/{gameId:guid}/Upload", UploadSaveByGameAsync);
group.MapGet("/Game/{gameId:guid}/Latest", GetLatestSaveByGameAsync);
group.MapGet("/Game/{gameId:guid}/Latest/Download", DownloadLatestSaveByGameAsync);
}
public static async Task<IResult> GetAsync(
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
var user = await userService.GetAsync(userPrincipal?.Identity?.Name ?? string.Empty);
if (user == null)
{
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
var saves = await saveService.GetAsync<SDK.Models.GameSave>(gs => gs.UserId == user.Id);
return TypedResults.Ok(saves);
}
public static async Task<IResult> GetByIdAsync(
Guid id,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
var user = await userService.GetAsync(userPrincipal?.Identity?.Name ?? string.Empty);
if (user == null)
{
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
var save = await saveService.FirstOrDefaultAsync<SDK.Models.GameSave>(s => s.Id == id);
if (save == null)
return TypedResults.NotFound();
return TypedResults.Ok(save);
}
public static async Task<IResult> DeleteByIdAsync(
Guid id,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
var user = await userService.GetAsync(userPrincipal?.Identity?.Name ?? string.Empty);
if (user == null)
{
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
var save = await saveService.FirstOrDefaultAsync(s => s.Id == id && s.UserId == user.Id);
if (save == null)
return TypedResults.NotFound();
await saveService.DeleteAsync(save);
return TypedResults.NoContent();
}
public static async Task<IResult> GetSavesByGameAsync(
Guid gameId,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
var user = await userService.GetAsync(userPrincipal?.Identity?.Name ?? string.Empty);
if (user == null)
{
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
var userSaves = await saveService.GetAsync<SDK.Models.GameSave>(gs => gs.UserId == user.Id && gs.GameId == gameId);
return TypedResults.Ok(userSaves);
}
public static async Task<IResult> GetLatestSaveByGameAsync(
Guid gameId,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] IMapper mapper,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
var user = await userService.GetAsync(userPrincipal?.Identity?.Name ?? string.Empty);
if (user == null)
{
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
var latestSave = await saveService
.SortBy(s => s.CreatedOn, SortDirection.Descending)
.FirstOrDefaultAsync(gs => gs.GameId == gameId && gs.UserId == user.Id);
if (latestSave == null)
return TypedResults.NotFound();
return TypedResults.Ok(mapper.Map<SDK.Models.GameSave>(latestSave));
}
public static async Task<IResult> DownloadLatestSaveByGameAsync(
Guid gameId,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
var user = await userService.GetAsync(userPrincipal?.Identity?.Name ?? string.Empty);
if (user == null)
{
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
var latestSave = await saveService
.SortBy(s => s.CreatedOn, SortDirection.Descending)
.Include(gs => gs.Game)
.Include(gs => gs.StorageLocation)
.FirstOrDefaultAsync(gs => gs.GameId == gameId && gs.UserId == user.Id);
if (latestSave == null)
return TypedResults.NotFound();
var fileName = latestSave.GetUploadPath();
if (!File.Exists(fileName))
return TypedResults.NotFound();
var downloadName = $"{latestSave.Game.Title} - {user.UserName} - {latestSave.CreatedOn}".SanitizeFilename();
return TypedResults.File(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{downloadName}.lcs");
}
public static async Task<IResult> DownloadSaveByIdAsync(
Guid id,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
var user = await userService.GetAsync(userPrincipal?.Identity?.Name ?? string.Empty);
if (user == null)
{
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
var save = await saveService
.Include(s => s.Game)
.Include(s => s.StorageLocation)
.FirstOrDefaultAsync(s => s.Id == id && s.UserId == user.Id);
var fileName = save.GetUploadPath();
if (!File.Exists(fileName))
return TypedResults.NotFound();
var downloadName = $"{save.Game.Title} - {user.UserName} - {save.CreatedOn}".SanitizeFilename();
return TypedResults.File(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read), "application/octet-stream", $"{downloadName}.lcs");
}
public static async Task<IResult> UploadSaveByGameAsync(
Guid gameId,
ClaimsPrincipal userPrincipal,
HttpContext httpContext,
[FromServices] ILogger<Program> logger,
[FromServices] SettingsProvider<Settings.Settings> settingsProvider,
[FromServices] IMapper mapper,
[FromServices] UserService userService,
[FromServices] GameService gameService,
[FromServices] StorageLocationService storageLocationService,
[FromServices] GameSaveService saveService)
{
var user = await userService.GetAsync(userPrincipal?.Identity?.Name ?? string.Empty);
if (user == null)
{
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
var game = await gameService.GetAsync(gameId);
if (game == null)
return TypedResults.NotFound();
var saveStorageLocation =
await storageLocationService.FirstOrDefaultAsync(l => l.Default && l.Type == StorageLocationType.Save);
if (saveStorageLocation == null)
return TypedResults.InternalServerError(
"There is no save location available on the server. Check your server settings!");
var form = await httpContext.Request.ReadFormAsync();
var file = form.Files.FirstOrDefault();
if (file == null)
return TypedResults.BadRequest("No file was provided in the request.");
using (var stream = file.OpenReadStream())
{
var save = new GameSave
{
Game = game,
User = user,
Size = stream.Length,
StorageLocation = saveStorageLocation,
};
stream.Seek(0, SeekOrigin.Begin);
save = await saveService.AddAsync(save);
try
{
var saveUploadFile = save.GetUploadPath();
var saveUploadPath = Path.GetDirectoryName(saveUploadFile);
if (!Directory.Exists(saveUploadPath))
Directory.CreateDirectory(saveUploadPath);
using (var fs = File.Create(saveUploadFile))
{
await stream.CopyToAsync(fs);
}
if (settingsProvider.CurrentValue.Server.UserSaves.MaxSaves > 0)
{
var savesToCull = await saveService
.Query(q =>
{
return q
.Where(s => s.UserId == user.Id && s.GameId == game.Id)
.OrderByDescending(s => s.CreatedOn)
.Skip(settingsProvider.CurrentValue.Server.UserSaves.MaxSaves);
})
.GetAsync();
foreach (var saveToCull in savesToCull)
await saveService.DeleteAsync(saveToCull);
}
return TypedResults.Ok(mapper.Map<SDK.Models.GameSave>(save));
}
catch (Exception ex)
{
return TypedResults.InternalServerError(ex);
}
}
}
}