Commit 0
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
using GameAuthApi.Models;
|
||||
using GameAuthApi.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameAuthApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private readonly UserService _userService;
|
||||
private readonly IConfiguration _config;
|
||||
private readonly BlacklistService _blacklistService;
|
||||
|
||||
public AuthController(UserService userService, IConfiguration config, BlacklistService blacklistService)
|
||||
{
|
||||
_userService = userService;
|
||||
_config = config;
|
||||
_blacklistService = blacklistService;
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> Register([FromBody] RegisterRequest credentials)
|
||||
{
|
||||
if (await _userService.GetByUsernameAsync(credentials.Username) != null)
|
||||
return BadRequest("User already exists");
|
||||
|
||||
var hash = BCrypt.Net.BCrypt.HashPassword(credentials.Password);
|
||||
|
||||
var user = new User
|
||||
{
|
||||
Username = credentials.Username,
|
||||
PasswordHash = hash,
|
||||
Role = "USER",
|
||||
Email = credentials.Email
|
||||
};
|
||||
|
||||
await _userService.CreateAsync(user);
|
||||
return Ok("User created");
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromBody] LoginRequest credentials)
|
||||
{
|
||||
var user = await _userService.GetByUsernameAsync(credentials.Username);
|
||||
if (user == null || !BCrypt.Net.BCrypt.Verify(credentials.Password, user.PasswordHash))
|
||||
return Unauthorized();
|
||||
|
||||
var accessToken = GenerateJwtToken(user);
|
||||
var refreshToken = Guid.NewGuid().ToString();
|
||||
var refreshExpiry = DateTime.UtcNow.AddDays(7);
|
||||
|
||||
user.RefreshToken = refreshToken;
|
||||
user.RefreshTokenExpiry = refreshExpiry;
|
||||
await _userService.UpdateAsync(user);
|
||||
|
||||
return Ok(new { accessToken, refreshToken });
|
||||
}
|
||||
|
||||
[HttpPost("refresh")]
|
||||
public async Task<IActionResult> Refresh([FromBody] RefreshRequest req)
|
||||
{
|
||||
var user = await _userService.GetByUsernameAsync(req.Username);
|
||||
if (user == null || user.RefreshToken != req.RefreshToken || user.RefreshTokenExpiry < DateTime.UtcNow)
|
||||
return Unauthorized("Invalid or expired refresh token");
|
||||
|
||||
var newAccessToken = GenerateJwtToken(user);
|
||||
return Ok(new { accessToken = newAccessToken });
|
||||
}
|
||||
|
||||
[HttpPost("logout")]
|
||||
[Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
var token = HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ", "");
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
return BadRequest("Token missing");
|
||||
|
||||
var jwtToken = new JwtSecurityTokenHandler().ReadToken(token) as JwtSecurityToken;
|
||||
var expiry = jwtToken.ValidTo;
|
||||
|
||||
await _blacklistService.AddToBlacklistAsync(token, expiry);
|
||||
return Ok("Token blacklisted, user logged out.");
|
||||
}
|
||||
|
||||
[HttpPost("role")]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> ChangeUserRole([FromBody] ChangeRoleRequest request)
|
||||
{
|
||||
if (request.NewRole != "USER" && request.NewRole != "SUPER")
|
||||
return BadRequest("Role must be 'USER' or 'SUPER'");
|
||||
|
||||
var user = await _userService.GetByUsernameAsync(request.Username);
|
||||
if (user == null)
|
||||
return NotFound("User not found");
|
||||
|
||||
user.Role = request.NewRole;
|
||||
await _userService.UpdateAsync(user);
|
||||
|
||||
return Ok($"{request.Username}'s role updated to {request.NewRole}");
|
||||
}
|
||||
|
||||
private string GenerateJwtToken(User user)
|
||||
{
|
||||
var key = Encoding.UTF8.GetBytes(_config["Jwt:Key"]);
|
||||
var creds = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, user.Username),
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||
new Claim(ClaimTypes.Role, user.Role)
|
||||
};
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
_config["Jwt:Issuer"],
|
||||
_config["Jwt:Issuer"],
|
||||
claims,
|
||||
expires: DateTime.UtcNow.AddMinutes(15),
|
||||
signingCredentials: creds);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
public class RefreshRequest
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string RefreshToken { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using GameAuthApi.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace GameAuthApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ItemController : ControllerBase
|
||||
{
|
||||
private readonly ItemService _itemService;
|
||||
private readonly LocationService _locationService;
|
||||
|
||||
public ItemController(ItemService itemService, LocationService locationService)
|
||||
{
|
||||
_itemService = itemService;
|
||||
_locationService = locationService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> Create([FromBody] CreateItemRequest req)
|
||||
{
|
||||
var location = await _locationService.GetByIdAsync(req.LocationId);
|
||||
if (location == null)
|
||||
return BadRequest("Invalid location ID.");
|
||||
|
||||
var item = new Item
|
||||
{
|
||||
Name = req.Name,
|
||||
LocationId = req.LocationId
|
||||
};
|
||||
|
||||
await _itemService.CreateAsync(item);
|
||||
return Ok(item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using GameAuthApi.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace GameAuthApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class LocationController : ControllerBase
|
||||
{
|
||||
private readonly LocationService _locationService;
|
||||
private readonly PlayerService _playerService;
|
||||
|
||||
public LocationController(LocationService locationService, PlayerService playerService)
|
||||
{
|
||||
_locationService = locationService;
|
||||
_playerService = playerService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> Create([FromBody] CreateLocationRequest req)
|
||||
{
|
||||
var newLoc = new Location
|
||||
{
|
||||
X = req.X,
|
||||
Y = req.Y,
|
||||
Z = req.Z,
|
||||
N = req.N,
|
||||
NE = req.NE,
|
||||
E = req.E,
|
||||
SE = req.SE,
|
||||
S = req.S,
|
||||
SW = req.SW,
|
||||
W = req.W,
|
||||
NW = req.NW
|
||||
};
|
||||
|
||||
await _locationService.CreateAsync(newLoc);
|
||||
|
||||
return Ok(newLoc);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> Delete(string id)
|
||||
{
|
||||
var location = await _locationService.GetByIdAsync(id);
|
||||
if (location == null)
|
||||
return NotFound("Location not found.");
|
||||
|
||||
// Null out all players at this location
|
||||
var affectedPlayers = await _playerService.GetByLocationIdAsync(id);
|
||||
foreach (var player in affectedPlayers)
|
||||
{
|
||||
player.LocationId = null;
|
||||
await _playerService.UpdateAsync(player);
|
||||
}
|
||||
|
||||
await _locationService.DeleteAsync(id);
|
||||
return Ok($"Location deleted and {affectedPlayers.Count} player(s) unlinked.");
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[Authorize(Roles = "USER")]
|
||||
public async Task<IActionResult> GetById(string id)
|
||||
{
|
||||
var location = await _locationService.GetByIdAsync(id);
|
||||
if (location == null)
|
||||
return NotFound("Location not found");
|
||||
|
||||
return Ok(location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
// Controllers/PlayerController.cs
|
||||
using GameAuthApi.Models;
|
||||
using GameAuthApi.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GameAuthApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class PlayerController : ControllerBase
|
||||
{
|
||||
private readonly PlayerService _playerService;
|
||||
private readonly LocationService _locationService;
|
||||
private readonly SpawnableService _spawnableService;
|
||||
private readonly ItemService _itemService;
|
||||
|
||||
public PlayerController(PlayerService playerService, LocationService locationService, SpawnableService spawnableService, ItemService itemService)
|
||||
{
|
||||
_playerService = playerService;
|
||||
_locationService = locationService;
|
||||
_spawnableService = spawnableService;
|
||||
_itemService = itemService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> Create([FromBody] CreatePlayerRequest req)
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
return Unauthorized("User ID not found in token.");
|
||||
|
||||
var existing = await _playerService.GetByUserIdAsync(userId);
|
||||
if (existing != null)
|
||||
return BadRequest("Player already exists for this user.");
|
||||
|
||||
Location spawnLocation = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.LocationId))
|
||||
{
|
||||
spawnLocation = await _locationService.GetByIdAsync(req.LocationId);
|
||||
if (spawnLocation == null)
|
||||
return BadRequest("Specified location does not exist.");
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnLocation = await _locationService.GetSpawnLocationAsync();
|
||||
// If not found, spawnLocation remains null
|
||||
}
|
||||
|
||||
// create the inventory location
|
||||
var inventoryLocation = new Location
|
||||
{
|
||||
LocationType = "Inventory",
|
||||
X = null,
|
||||
Y = null,
|
||||
Z = null,
|
||||
N = null,
|
||||
NE = null,
|
||||
E = null,
|
||||
SE = null,
|
||||
S = null,
|
||||
SW = null,
|
||||
W = null,
|
||||
NW = null,
|
||||
Players = new List<string>() // optional
|
||||
};
|
||||
|
||||
await _locationService.CreateAsync(inventoryLocation);
|
||||
|
||||
var player = new Player
|
||||
{
|
||||
UserId = userId,
|
||||
Name = req.Name,
|
||||
LocationId = spawnLocation?.Id,
|
||||
InventoryLocationId = inventoryLocation.Id,
|
||||
Climbing = 3
|
||||
};
|
||||
|
||||
await _playerService.CreateAsync(player);
|
||||
|
||||
if (spawnLocation != null)
|
||||
{
|
||||
await _locationService.AddPlayerToLocationAsync(spawnLocation.Id, player.Id);
|
||||
}
|
||||
|
||||
return Ok(player);
|
||||
}
|
||||
|
||||
[HttpDelete]
|
||||
[Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> Delete()
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var role = User.FindFirstValue(ClaimTypes.Role);
|
||||
|
||||
var player = await _playerService.GetByUserIdAsync(userId);
|
||||
if (player == null)
|
||||
return NotFound("No player found for this user.");
|
||||
|
||||
// Remove from current location if set
|
||||
if (!string.IsNullOrEmpty(player.LocationId))
|
||||
{
|
||||
await _locationService.RemovePlayerFromLocationAsync(player.LocationId, player.Id);
|
||||
}
|
||||
|
||||
await _playerService.DeleteAsync(player.Id);
|
||||
return Ok($"Player '{player.Name}' deleted.");
|
||||
}
|
||||
|
||||
[HttpPost("teleport")]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> AdminTeleport([FromBody] TeleportRequest req)
|
||||
{
|
||||
var player = await _playerService.GetByIdAsync(req.PlayerId);
|
||||
if (player == null)
|
||||
return NotFound("Player not found.");
|
||||
|
||||
var newLocation = await _locationService.GetByIdAsync(req.LocationId);
|
||||
if (newLocation == null)
|
||||
return BadRequest("Target location does not exist.");
|
||||
|
||||
if (!string.IsNullOrEmpty(player.LocationId))
|
||||
{
|
||||
await _locationService.RemovePlayerFromLocationAsync(player.LocationId, player.Id);
|
||||
}
|
||||
|
||||
player.LocationId = newLocation.Id;
|
||||
await _playerService.UpdateAsync(player);
|
||||
await _locationService.AddPlayerToLocationAsync(newLocation.Id, player.Id);
|
||||
|
||||
return Ok($"Player '{player.Name}' teleported to '{newLocation.X}, {newLocation.Y}'.");
|
||||
}
|
||||
|
||||
[HttpPost("move")]
|
||||
[Authorize(Roles = "USER,SUPER")]
|
||||
public async Task<IActionResult> Move([FromBody] MoveRequest req)
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var player = await _playerService.GetByUserIdAsync(userId);
|
||||
if (player == null)
|
||||
return NotFound("Player not found.");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(player.LocationId))
|
||||
return BadRequest("Player is not currently in a location.");
|
||||
|
||||
var currentLocation = await _locationService.GetByIdAsync(player.LocationId);
|
||||
if (currentLocation == null)
|
||||
return BadRequest("Current location not found.");
|
||||
|
||||
var direction = req.Direction.ToUpper();
|
||||
var directionMap = new Dictionary<string, (int dx, int dy, string reverse)>
|
||||
{
|
||||
{ "N", (0, 1, "S") }, { "NE", (1, 1, "SW") }, { "E", (1, 0, "W") }, { "SE", (1, -1, "NW") },
|
||||
{ "S", (0, -1, "N") }, { "SW", (-1, -1, "NE") }, { "W", (-1, 0, "E") }, { "NW", (-1, 1, "SE") }
|
||||
};
|
||||
|
||||
if (!directionMap.ContainsKey(direction))
|
||||
return BadRequest("Invalid direction. Use N, NE, E, SE, S, SW, W, or NW.");
|
||||
|
||||
var (dx, dy, reverseDir) = directionMap[direction];
|
||||
|
||||
var targetId = typeof(Location).GetProperty(direction)?.GetValue(currentLocation)?.ToString();
|
||||
Location targetLocation;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(targetId))
|
||||
{
|
||||
targetLocation = await _locationService.GetByIdAsync(targetId);
|
||||
if (targetLocation == null)
|
||||
return BadRequest("Linked location ID is invalid.");
|
||||
|
||||
var z1 = currentLocation.Z ?? 0;
|
||||
var z2 = targetLocation.Z ?? 0;
|
||||
var zDiff = Math.Abs(z1 - z2);
|
||||
|
||||
if (zDiff > player.Climbing)
|
||||
{
|
||||
return BadRequest($"Cannot move {req.Direction.ToUpper()}: elevation difference ({zDiff}) exceeds your climbing ability ({player.Climbing}).");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate new location
|
||||
var newX = currentLocation.X + dx;
|
||||
var newY = currentLocation.Y + dy;
|
||||
var random = new Random();
|
||||
var newZ = currentLocation.Z + random.Next(-1, 2); // -1, 0, or +1
|
||||
|
||||
string itemId = null;
|
||||
var spawnables = await _spawnableService.GetAllAsync();
|
||||
|
||||
if (spawnables.Any())
|
||||
{
|
||||
var rng = new Random();
|
||||
var roll = rng.Next(0, 100); // 0–99
|
||||
|
||||
foreach (var s in spawnables.OrderBy(s => s.SpawnRate))
|
||||
{
|
||||
if (roll < s.SpawnRate)
|
||||
{
|
||||
var tempItem = new Item
|
||||
{
|
||||
Name = s.Name,
|
||||
LocationId = null
|
||||
};
|
||||
|
||||
await _itemService.CreateAsync(tempItem);
|
||||
itemId = tempItem.Id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targetLocation = new Location
|
||||
{
|
||||
LocationType = "World",
|
||||
X = newX,
|
||||
Y = newY,
|
||||
Z = newZ,
|
||||
Content = itemId != null ? [itemId] : []
|
||||
};
|
||||
|
||||
await _locationService.CreateAsync(targetLocation);
|
||||
|
||||
if (itemId != null)
|
||||
{
|
||||
var createdItem = await _itemService.GetByIdAsync(itemId);
|
||||
createdItem.LocationId = targetLocation.Id;
|
||||
await _itemService.UpdateAsync(createdItem);
|
||||
}
|
||||
|
||||
// Link current → new
|
||||
typeof(Location).GetProperty(direction)?.SetValue(currentLocation, targetLocation.Id);
|
||||
// Link new → current
|
||||
typeof(Location).GetProperty(reverseDir)?.SetValue(targetLocation, currentLocation.Id);
|
||||
|
||||
await _locationService.UpdateAsync(currentLocation);
|
||||
await _locationService.UpdateAsync(targetLocation);
|
||||
|
||||
// Link additional neighbors
|
||||
foreach (var entry in directionMap)
|
||||
{
|
||||
var dir = entry.Key;
|
||||
var (nx, ny, rev) = entry.Value;
|
||||
|
||||
var neighbor = await _locationService.GetByCoordinatesAsync(
|
||||
(targetLocation.X ?? 0) + nx,
|
||||
(targetLocation.Y ?? 0) + ny,
|
||||
targetLocation.Z ?? 0);
|
||||
if (neighbor != null)
|
||||
{
|
||||
typeof(Location).GetProperty(dir)?.SetValue(targetLocation, neighbor.Id);
|
||||
typeof(Location).GetProperty(rev)?.SetValue(neighbor, targetLocation.Id);
|
||||
await _locationService.UpdateAsync(neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
await _locationService.UpdateAsync(targetLocation);
|
||||
}
|
||||
|
||||
// Move the player
|
||||
await _locationService.RemovePlayerFromLocationAsync(currentLocation.Id, player.Id);
|
||||
await _locationService.AddPlayerToLocationAsync(targetLocation.Id, player.Id);
|
||||
|
||||
player.LocationId = targetLocation.Id;
|
||||
await _playerService.UpdateAsync(player);
|
||||
|
||||
return Ok($"Player '{player.Name}' moved {direction} to '{targetLocation.X}, {targetLocation.Y}'.");
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, (int dx, int dy, string reverse)> DirectionData = new()
|
||||
{
|
||||
{ "N", (0, 1, "S") }, { "NE", (1, 1, "SW") }, { "E", (1, 0, "W") }, { "SE", (1, -1, "NW") },
|
||||
{ "S", (0, -1, "N") }, { "SW", (-1, -1, "NE") }, { "W", (-1, 0, "E") }, { "NW", (-1, 1, "SE") }
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using GameAuthApi.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace GameAuthApi.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class SpawnableController : ControllerBase
|
||||
{
|
||||
private readonly SpawnableService _spawnableService;
|
||||
|
||||
public SpawnableController(SpawnableService spawnableService)
|
||||
{
|
||||
_spawnableService = spawnableService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> Create([FromBody] CreateSpawnableRequest req)
|
||||
{
|
||||
var spawnable = new Spawnable
|
||||
{
|
||||
Name = req.Name.ToLower(),
|
||||
SpawnRate = req.SpawnRate
|
||||
};
|
||||
|
||||
await _spawnableService.CreateAsync(spawnable);
|
||||
return Ok(spawnable);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Authorize(Roles = "SUPER")]
|
||||
public async Task<IActionResult> GetAll()
|
||||
{
|
||||
var all = await _spawnableService.GetAllAsync();
|
||||
return Ok(all);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.21.0" />
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,24 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.2.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameAuthApi", "GameAuthApi.csproj", "{349B73A5-7632-5ADF-4645-BB2EF73FA948}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{349B73A5-7632-5ADF-4645-BB2EF73FA948}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{349B73A5-7632-5ADF-4645-BB2EF73FA948}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{349B73A5-7632-5ADF-4645-BB2EF73FA948}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{349B73A5-7632-5ADF-4645-BB2EF73FA948}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7EFA1A51-A917-4BB3-9C99-E90800825E9F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace GameAuthApi.Middleware;
|
||||
|
||||
public class TokenBlacklistMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public TokenBlacklistMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context, BlacklistService blacklistService)
|
||||
{
|
||||
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ", "");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
if (await blacklistService.IsTokenBlacklistedAsync(token))
|
||||
{
|
||||
context.Response.StatusCode = 401;
|
||||
await context.Response.WriteAsync("Token has been revoked");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class BlacklistedToken
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
public string Token { get; set; }
|
||||
public DateTime Expiry { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class ChangeRoleRequest
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string NewRole { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class CreateItemRequest
|
||||
{
|
||||
public string LocationId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class CreateLocationRequest
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Z { get; set; }
|
||||
|
||||
public string N { get; set; }
|
||||
public string NE { get; set; }
|
||||
public string E { get; set; }
|
||||
public string SE { get; set; }
|
||||
public string S { get; set; }
|
||||
public string SW { get; set; }
|
||||
public string W { get; set; }
|
||||
public string NW { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Models/CreatePlayerRequest.cs
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class CreatePlayerRequest
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string LocationId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class CreateSpawnableRequest
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int SpawnRate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class MoveRequest
|
||||
{
|
||||
public string Direction { get; set; } // N, NE, E, SE, S, SW, W, NW
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class RegisterRequest
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Email { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class TeleportRequest
|
||||
{
|
||||
public string PlayerId { get; set; }
|
||||
public string LocationId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class Item
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string LocationId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class Location
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string LocationType { get; set; } = "World";
|
||||
|
||||
public int? X { get; set; }
|
||||
public int? Y { get; set; }
|
||||
public int? Z { get; set; }
|
||||
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string N { get; set; }
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string NE { get; set; }
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string E { get; set; }
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string SE { get; set; }
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string S { get; set; }
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string SW { get; set; }
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string W { get; set; }
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string NW { get; set; }
|
||||
|
||||
public List<string> Players { get; set; } = [];
|
||||
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public List<string> Content { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Models/Player.cs
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class Player
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string UserId { get; set; } // Linked to user
|
||||
public string Name { get; set; } // In-game player name
|
||||
public string LocationId { get; set; } // Current location (nullable)
|
||||
public string InventoryLocationId { get; set; } // Inventory location (nullable)
|
||||
public int Climbing { get; set; } = 3; // Climbing skill level (default 3)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class Spawnable
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public int SpawnRate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace GameAuthApi.Models;
|
||||
|
||||
public class User
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
public string PasswordHash { get; set; }
|
||||
public string Role { get; set; } = "player";
|
||||
public string RefreshToken { get; set; }
|
||||
public DateTime RefreshTokenExpiry { get; set; }
|
||||
public string Email { get; set; }
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
using GameAuthApi.Services;
|
||||
using GameAuthApi.Middleware;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new() { Title = "GameAuthApi", Version = "v1" });
|
||||
|
||||
c.AddSecurityDefinition("Bearer", new()
|
||||
{
|
||||
Name = "Authorization",
|
||||
Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey,
|
||||
Scheme = "Bearer",
|
||||
BearerFormat = "JWT",
|
||||
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
|
||||
Description = "Enter: `Bearer <your JWT token>`"
|
||||
});
|
||||
|
||||
c.AddSecurityRequirement(new()
|
||||
{
|
||||
{
|
||||
new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new Microsoft.OpenApi.Models.OpenApiReference
|
||||
{
|
||||
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
new string[] {}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
builder.Services.AddSingleton<UserService>();
|
||||
builder.Services.AddSingleton<LocationService>();
|
||||
builder.Services.AddSingleton<PlayerService>();
|
||||
builder.Services.AddSingleton<SpawnableService>();
|
||||
builder.Services.AddSingleton<ItemService>();
|
||||
builder.Services.AddSingleton<BlacklistService>();
|
||||
|
||||
var jwtKey = builder.Configuration["Jwt:Key"];
|
||||
var keyBytes = Encoding.UTF8.GetBytes(jwtKey);
|
||||
|
||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidIssuer = builder.Configuration["Jwt:Issuer"],
|
||||
ValidAudience = builder.Configuration["Jwt:Issuer"],
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(keyBytes)
|
||||
};
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
app.UseMiddleware<GameAuthApi.Middleware.TokenBlacklistMiddleware>();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace GameAuthApi.Services;
|
||||
|
||||
public class BlacklistService
|
||||
{
|
||||
private readonly IMongoCollection<BlacklistedToken> _blacklist;
|
||||
|
||||
public BlacklistService(IConfiguration config)
|
||||
{
|
||||
var client = new MongoClient(config["MongoDB:ConnectionString"]);
|
||||
var db = client.GetDatabase(config["MongoDB:DatabaseName"]);
|
||||
_blacklist = db.GetCollection<BlacklistedToken>("BlacklistedTokens");
|
||||
}
|
||||
|
||||
public async Task AddToBlacklistAsync(string token, DateTime expiry)
|
||||
{
|
||||
var entry = new BlacklistedToken { Token = token, Expiry = expiry };
|
||||
await _blacklist.InsertOneAsync(entry);
|
||||
}
|
||||
|
||||
public async Task<bool> IsTokenBlacklistedAsync(string token)
|
||||
{
|
||||
var exists = await _blacklist.Find(x => x.Token == token && x.Expiry > DateTime.UtcNow).FirstOrDefaultAsync();
|
||||
return exists != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace GameAuthApi.Services;
|
||||
|
||||
public class ItemService
|
||||
{
|
||||
private readonly IMongoCollection<Item> _items;
|
||||
|
||||
public ItemService(IConfiguration config)
|
||||
{
|
||||
var client = new MongoClient(config["MongoDB:ConnectionString"]);
|
||||
var db = client.GetDatabase(config["MongoDB:DatabaseName"]);
|
||||
_items = db.GetCollection<Item>("Items");
|
||||
}
|
||||
|
||||
public async Task CreateAsync(Item item)
|
||||
{
|
||||
await _items.InsertOneAsync(item);
|
||||
}
|
||||
|
||||
public async Task<List<Item>> GetByLocationAsync(string locationId)
|
||||
{
|
||||
return await _items.Find(i => i.LocationId == locationId).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Item> GetByIdAsync(string id)
|
||||
{
|
||||
return await _items.Find(i => i.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Item item)
|
||||
{
|
||||
await _items.ReplaceOneAsync(i => i.Id == item.Id, item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace GameAuthApi.Services;
|
||||
|
||||
public class LocationService
|
||||
{
|
||||
private readonly IMongoCollection<Location> _locations;
|
||||
|
||||
public LocationService(IConfiguration config)
|
||||
{
|
||||
var client = new MongoClient(config["MongoDB:ConnectionString"]);
|
||||
var db = client.GetDatabase(config["MongoDB:DatabaseName"]);
|
||||
_locations = db.GetCollection<Location>("Locations");
|
||||
}
|
||||
|
||||
public async Task<Location> GetByIdAsync(string id)
|
||||
{
|
||||
if (!ObjectId.TryParse(id, out _))
|
||||
return null; // prevent crash if id is invalid
|
||||
|
||||
return await _locations.Find(x => x.Id == id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task CreateAsync(Location loc) =>
|
||||
await _locations.InsertOneAsync(loc);
|
||||
|
||||
public async Task DeleteAsync(string id) =>
|
||||
await _locations.DeleteOneAsync(loc => loc.Id == id);
|
||||
public async Task UpdateAsync(Location loc) =>
|
||||
await _locations.ReplaceOneAsync(l => l.Id == loc.Id, loc);
|
||||
|
||||
public async Task AddPlayerToLocationAsync(string locationId, string playerId)
|
||||
{
|
||||
var update = Builders<Location>.Update.AddToSet(l => l.Players, playerId);
|
||||
await _locations.UpdateOneAsync(l => l.Id == locationId, update);
|
||||
}
|
||||
|
||||
public async Task<Location> GetByCoordinatesAsync(int x, int y, int z)
|
||||
{
|
||||
return await _locations.Find(loc => loc.X == x && loc.Y == y && loc.Z == z).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<Location> GetSpawnLocationAsync()
|
||||
{
|
||||
return await _locations.Find(loc => loc.X == 0 && loc.Y == 0).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task RemovePlayerFromLocationAsync(string locationId, string playerId)
|
||||
{
|
||||
var update = Builders<Location>.Update.Pull(l => l.Players, playerId);
|
||||
await _locations.UpdateOneAsync(l => l.Id == locationId, update);
|
||||
}
|
||||
|
||||
public async Task AddItemToLocationAsync(string locationId, string itemId)
|
||||
{
|
||||
var update = Builders<Location>.Update.Push(l => l.Content, itemId);
|
||||
await _locations.UpdateOneAsync(l => l.Id == locationId, update);
|
||||
}
|
||||
|
||||
public async Task RemoveItemFromLocationAsync(string locationId, string itemId)
|
||||
{
|
||||
var update = Builders<Location>.Update.Pull(l => l.Content, itemId);
|
||||
await _locations.UpdateOneAsync(l => l.Id == locationId, update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Services/PlayerService.cs
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace GameAuthApi.Services;
|
||||
|
||||
public class PlayerService
|
||||
{
|
||||
private readonly IMongoCollection<Player> _players;
|
||||
|
||||
public PlayerService(IConfiguration config)
|
||||
{
|
||||
var client = new MongoClient(config["MongoDB:ConnectionString"]);
|
||||
var db = client.GetDatabase(config["MongoDB:DatabaseName"]);
|
||||
_players = db.GetCollection<Player>("Players");
|
||||
}
|
||||
|
||||
public async Task<Player> GetByUserIdAsync(string userId) =>
|
||||
await _players.Find(p => p.UserId == userId).FirstOrDefaultAsync();
|
||||
|
||||
public async Task<List<Player>> GetByLocationIdAsync(string locationId)
|
||||
{
|
||||
return await _players.Find(p => p.LocationId == locationId).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Player> GetByIdAsync(string id) =>
|
||||
await _players.Find(p => p.Id == id).FirstOrDefaultAsync();
|
||||
|
||||
public async Task CreateAsync(Player player) =>
|
||||
await _players.InsertOneAsync(player);
|
||||
|
||||
public async Task UpdateAsync(Player player)
|
||||
{
|
||||
await _players.ReplaceOneAsync(p => p.Id == player.Id, player);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(string id)
|
||||
{
|
||||
await _players.DeleteOneAsync(p => p.Id == id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace GameAuthApi.Services;
|
||||
|
||||
public class SpawnableService
|
||||
{
|
||||
private readonly IMongoCollection<Spawnable> _spawnables;
|
||||
|
||||
public SpawnableService(IConfiguration config)
|
||||
{
|
||||
var client = new MongoClient(config["MongoDB:ConnectionString"]);
|
||||
var db = client.GetDatabase(config["MongoDB:DatabaseName"]);
|
||||
_spawnables = db.GetCollection<Spawnable>("Spawnables");
|
||||
}
|
||||
|
||||
public async Task CreateAsync(Spawnable spawnable)
|
||||
{
|
||||
await _spawnables.InsertOneAsync(spawnable);
|
||||
}
|
||||
|
||||
public async Task<List<Spawnable>> GetAllAsync()
|
||||
{
|
||||
return await _spawnables.Find(_ => true).ToListAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Threading.Tasks;
|
||||
using GameAuthApi.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace GameAuthApi.Services;
|
||||
|
||||
public class UserService
|
||||
{
|
||||
private readonly IMongoCollection<User> _users;
|
||||
|
||||
public UserService(IConfiguration config)
|
||||
{
|
||||
var client = new MongoClient(config["MongoDB:ConnectionString"]);
|
||||
var database = client.GetDatabase(config["MongoDB:DatabaseName"]);
|
||||
_users = database.GetCollection<User>("Users");
|
||||
}
|
||||
|
||||
public async Task<User> GetByUsernameAsync(string username) =>
|
||||
await _users.Find(u => u.Username == username).FirstOrDefaultAsync();
|
||||
|
||||
public async Task CreateAsync(User user) =>
|
||||
await _users.InsertOneAsync(user);
|
||||
|
||||
public async Task UpdateAsync(User user) =>
|
||||
await _users.ReplaceOneAsync(u => u.Id == user.Id, user);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"MongoDB": {
|
||||
"ConnectionString": "mongodb://192.168.86.50:27017",
|
||||
"DatabaseName": "GameDb"
|
||||
},
|
||||
"Jwt": {
|
||||
"Key": "SuperUltraSecureJwtKeyWithAtLeast32Chars!!",
|
||||
"Issuer": "GameAuthApi"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Reference in New Issue
Block a user