Merge pull request 'refactoring' (#7) from refactoring into develop
Reviewed-on: michele/Map#7
This commit is contained in:
commit
2d14cf8059
197 changed files with 4812 additions and 4673 deletions
12
.dockerignore
Normal file
12
.dockerignore
Normal file
|
@ -0,0 +1,12 @@
|
|||
# directories
|
||||
**/bin/
|
||||
**/obj/
|
||||
**/out/
|
||||
|
||||
# files
|
||||
Dockerfile*
|
||||
**/*.trx
|
||||
**/*.md
|
||||
**/*.ps1
|
||||
**/*.cmd
|
||||
**/*.sh
|
8
.editorconfig
Normal file
8
.editorconfig
Normal file
|
@ -0,0 +1,8 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
38
.vscode/launch.json
vendored
38
.vscode/launch.json
vendored
|
@ -2,19 +2,53 @@
|
|||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Docker .NET Core Attach (Preview)",
|
||||
"type": "docker",
|
||||
"request": "attach",
|
||||
"platform": "netCore",
|
||||
"sourceFileMap": {
|
||||
"/": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Sledgemapper",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build sledgemapper",
|
||||
"program": "${workspaceFolder}/Sledgemapper/bin/Debug/netcoreapp3.1/sledgemapper.dll",
|
||||
"program": "${workspaceFolder}/Sledgemapper/bin/Debug/net5.0/sledgemapper.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Sledgemapper",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Sledgemapper.API",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build sledgemapper.api",
|
||||
"program": "${workspaceFolder}/Sledgemapper.Api/bin/Debug/net5.0/sledgemapper.api.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Sledgemapper.Api",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false,
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Identity.API",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build identity",
|
||||
"program": "${workspaceFolder}/Identity/bin/Debug/netcoreapp3.1/WebApi.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Identity",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
|
|
19
.vscode/tasks.json
vendored
19
.vscode/tasks.json
vendored
|
@ -1,6 +1,23 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
|
||||
"label": "build identity",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Identity/WebApi.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
"label": "build sledgemapper",
|
||||
|
@ -25,7 +42,7 @@
|
|||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/Sledgemapper.Api/src/Sledgemapper.Api.csproj",
|
||||
"${workspaceFolder}/Sledgemapper.Api/Sledgemapper.Api.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
|
59
Dockerfile
Normal file
59
Dockerfile
Normal file
|
@ -0,0 +1,59 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
||||
WORKDIR /src
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
#COPY *.sln .
|
||||
# COPY SignalRChat/*.csproj ./aspnetapp/
|
||||
# WORKDIR /source/aspnetapp
|
||||
# RUN dotnet restore
|
||||
COPY Sledgemapper.Api/*.csproj ./Sledgemapper.Api/
|
||||
COPY Sledgemapper.Shared/*.csproj ./Sledgemapper.Shared/
|
||||
|
||||
WORKDIR /src/Sledgemapper.Api
|
||||
RUN dotnet restore
|
||||
|
||||
WORKDIR /src/Sledgemapper.Shared
|
||||
RUN dotnet restore
|
||||
|
||||
# copy everything else and build app
|
||||
WORKDIR /src
|
||||
COPY Sledgemapper.Api/. ./Sledgemapper.Api/
|
||||
COPY Sledgemapper.Shared/. ./Sledgemapper.Shared/
|
||||
WORKDIR /src/Sledgemapper.Api
|
||||
RUN dotnet publish -c release -o /app --no-restore
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:5.0
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
ENTRYPOINT ["dotnet", "SignalRChat.dll"]
|
||||
|
||||
|
||||
# # https://hub.docker.com/_/microsoft-dotnet-core
|
||||
# FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
||||
|
||||
# # copy csproj and restore as distinct layers
|
||||
# #COPY *.sln .
|
||||
# WORKDIR /source
|
||||
# COPY Sledgemapper.Api/*.csproj ./Sledgemapper.Api/
|
||||
# WORKDIR /source/Sledgemapper.Api
|
||||
# RUN dotnet restore
|
||||
|
||||
# WORKDIR /source
|
||||
# COPY Sledgemapper.Shared/*.csproj ./Sledgemapper.Shared/
|
||||
# WORKDIR /source/Sledgemapper.Shared
|
||||
# RUN dotnet restore
|
||||
|
||||
# # copy everything else and build app
|
||||
# WORKDIR /source
|
||||
# COPY Sledgemapper.Api/. ./Sledgemapper.Api/
|
||||
# COPY Sledgemapper.Shared/. ./Sledgemapper.Shared/
|
||||
# WORKDIR /source/Sledgemapper.Api
|
||||
# RUN dotnet publish -c release -o /app --no-restore
|
||||
|
||||
# # final stage/image
|
||||
# FROM mcr.microsoft.com/dotnet/aspnet:5.0
|
||||
# WORKDIR /app
|
||||
# COPY --from=build /app ./
|
||||
# ENTRYPOINT ["dotnet", "Sledgemapper.Api.dll"]
|
37
Sledgemapper.Api/.vscode/launch.json
vendored
37
Sledgemapper.Api/.vscode/launch.json
vendored
|
@ -1,37 +0,0 @@
|
|||
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (web)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/SignalRChat/bin/Debug/net5.0/SignalRChat.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/Views": "${workspaceFolder}/Views"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
42
Sledgemapper.Api/.vscode/tasks.json
vendored
42
Sledgemapper.Api/.vscode/tasks.json
vendored
|
@ -1,42 +0,0 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/SignalRChat/SignalRChat.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/SignalRChat/SignalRChat.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"${workspaceFolder}/SignalRChat/SignalRChat.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
19
Sledgemapper.Api/Commands/BaseCommand.cs
Normal file
19
Sledgemapper.Api/Commands/BaseCommand.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using MediatR;
|
||||
using System;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public abstract class BaseCommand<T> : IRequest<T>
|
||||
{
|
||||
public double Timestamp { get; private set; }
|
||||
public string SessionName { get; private set; }
|
||||
public int UserId { get; private set; }
|
||||
|
||||
public BaseCommand(string sessionName, int userId)
|
||||
{
|
||||
Timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
SessionName = sessionName;
|
||||
UserId = userId;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Commands/DeleteOverlayCommand.cs
Normal file
14
Sledgemapper.Api/Commands/DeleteOverlayCommand.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class DeleteOverlayCommand : BaseCommand<bool>
|
||||
{
|
||||
public Overlay Overlay { get; private set; }
|
||||
|
||||
public DeleteOverlayCommand(string sessionName, Overlay overlay, int userId) : base(sessionName, userId)
|
||||
{
|
||||
Overlay = overlay;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Commands/DeleteTileCommand.cs
Normal file
14
Sledgemapper.Api/Commands/DeleteTileCommand.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class DeleteTileCommand : BaseCommand<bool>
|
||||
{
|
||||
public Tile Tile { get; private set; }
|
||||
|
||||
public DeleteTileCommand(string sessionName, Tile tile, int userId) : base(sessionName, userId)
|
||||
{
|
||||
Tile = tile;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Commands/DeleteWallCommand.cs
Normal file
14
Sledgemapper.Api/Commands/DeleteWallCommand.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class DeleteWallCommand : BaseCommand<bool>
|
||||
{
|
||||
public Wall Wall { get; private set; }
|
||||
|
||||
public DeleteWallCommand(string sessionName, Wall wall, int userId) : base(sessionName, userId)
|
||||
{
|
||||
Wall = wall;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Commands/GetMapSnapshotCommand.cs
Normal file
14
Sledgemapper.Api/Commands/GetMapSnapshotCommand.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class GetMapSnapshotCommand : IRequest<Session>
|
||||
{
|
||||
public string SessionName { get; private set; }
|
||||
public GetMapSnapshotCommand(string sessionName)
|
||||
{
|
||||
SessionName = sessionName;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Commands/NewOverlayCommand.cs
Normal file
14
Sledgemapper.Api/Commands/NewOverlayCommand.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class NewOverlayCommand : BaseCommand<bool>
|
||||
{
|
||||
public Overlay Overlay { get; private set; }
|
||||
|
||||
public NewOverlayCommand(string sessionName, Overlay overlay, int userId) : base(sessionName, userId)
|
||||
{
|
||||
Overlay = overlay;
|
||||
}
|
||||
}
|
||||
}
|
9
Sledgemapper.Api/Commands/NewSessionCommand.cs
Normal file
9
Sledgemapper.Api/Commands/NewSessionCommand.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class NewSessionCommand : BaseCommand<bool>
|
||||
{
|
||||
public NewSessionCommand(string sessionName, int userId):base(sessionName, userId)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
13
Sledgemapper.Api/Commands/NewSnapshotCommand.cs
Normal file
13
Sledgemapper.Api/Commands/NewSnapshotCommand.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class NewSnapshotCommand : BaseCommand<bool>
|
||||
{
|
||||
public Session Session { get; private set; }
|
||||
public NewSnapshotCommand(string sessionName, Session session, int userId) : base(sessionName, userId)
|
||||
{
|
||||
Session = session;
|
||||
}
|
||||
}
|
||||
}
|
13
Sledgemapper.Api/Commands/NewTileCommand.cs
Normal file
13
Sledgemapper.Api/Commands/NewTileCommand.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class NewTileCommand : BaseCommand<bool>
|
||||
{
|
||||
public Tile Tile { get; private set; }
|
||||
public NewTileCommand(string sessionName, Tile tile, int userId) : base(sessionName, userId)
|
||||
{
|
||||
Tile = tile;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Commands/NewWallCommand.cs
Normal file
14
Sledgemapper.Api/Commands/NewWallCommand.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class NewWallCommand : BaseCommand<bool>
|
||||
{
|
||||
public Wall Wall { get; private set; }
|
||||
|
||||
public NewWallCommand(string sessionName, Wall wall, int userId) : base(sessionName, userId)
|
||||
{
|
||||
Wall = wall;
|
||||
}
|
||||
}
|
||||
}
|
75
Sledgemapper.Api/Controllers/SessionController.cs
Normal file
75
Sledgemapper.Api/Controllers/SessionController.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Sledgemapper.Api.Commands;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[Route("[controller]/{sessionName}")]
|
||||
public class SessionController : ControllerBase
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
private int UserId => int.Parse(HttpContext.User.Identity.Name);
|
||||
|
||||
public SessionController(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
[HttpPost]
|
||||
public async Task<bool> Post(string sessionName)
|
||||
{
|
||||
var result = await _mediator.Send(new NewSessionCommand(sessionName, UserId));
|
||||
return result;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<Session> Get(string sessionName)
|
||||
{
|
||||
var result = await _mediator.Send(new GetMapSnapshotCommand(sessionName));
|
||||
return result;
|
||||
}
|
||||
|
||||
[HttpPost("snapshot")]
|
||||
public async Task Post(string sessionName, [FromBody] Session session)
|
||||
{
|
||||
await _mediator.Send(new NewSnapshotCommand(sessionName, session, UserId));
|
||||
}
|
||||
|
||||
[HttpPost("tile")]
|
||||
public async Task Post(string sessionName, [FromBody] Tile tile)
|
||||
{
|
||||
await _mediator.Send(new NewTileCommand(sessionName, tile, UserId));
|
||||
}
|
||||
|
||||
[HttpPost("overlay")]
|
||||
public async Task Post(string sessionName, [FromBody] Overlay overlay)
|
||||
{
|
||||
await _mediator.Send(new NewOverlayCommand(sessionName, overlay, UserId));
|
||||
}
|
||||
|
||||
[HttpPost("wall")]
|
||||
public async Task Post(string sessionName, [FromBody] Wall wall)
|
||||
{
|
||||
await _mediator.Send(new NewWallCommand(sessionName, wall, UserId));
|
||||
}
|
||||
|
||||
[HttpDelete("tile")]
|
||||
public async Task Delete(string sessionName, [FromBody] Tile tile)
|
||||
{
|
||||
await _mediator.Send(new DeleteTileCommand(sessionName, tile, UserId));
|
||||
}
|
||||
|
||||
[HttpDelete("overlay")]
|
||||
public async Task Delete(string sessionName, [FromBody] Overlay overlay)
|
||||
{
|
||||
await _mediator.Send(new DeleteOverlayCommand(sessionName, overlay, UserId));
|
||||
}
|
||||
|
||||
[HttpDelete("wall")]
|
||||
public async Task Delete(string sessionName, [FromBody] Wall wall)
|
||||
{
|
||||
await _mediator.Send(new DeleteWallCommand(sessionName, wall, UserId));
|
||||
}
|
||||
}
|
||||
}
|
136
Sledgemapper.Api/Controllers/UsersController.cs
Normal file
136
Sledgemapper.Api/Controllers/UsersController.cs
Normal file
|
@ -0,0 +1,136 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using AutoMapper;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Sledgemapper.Services;
|
||||
using Sledgemapper.Entities;
|
||||
using Sledgemapper.Models.Users;
|
||||
using Sledgemapper.Helpers;
|
||||
|
||||
namespace Sledgemapper.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private IUserService _userService;
|
||||
private IMapper _mapper;
|
||||
private readonly AppSettings _appSettings;
|
||||
|
||||
public UsersController(
|
||||
IUserService userService,
|
||||
IMapper mapper,
|
||||
IOptions<AppSettings> appSettings)
|
||||
{
|
||||
_userService = userService;
|
||||
_mapper = mapper;
|
||||
_appSettings = appSettings.Value;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("authenticate")]
|
||||
public IActionResult Authenticate([FromBody]AuthenticateModel model)
|
||||
{
|
||||
var user = _userService.Authenticate(model.Username, model.Password);
|
||||
|
||||
if (user == null)
|
||||
return BadRequest(new { message = "Username or password is incorrect" });
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(new Claim[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, user.Id.ToString())
|
||||
}),
|
||||
Expires = DateTime.UtcNow.AddDays(7),
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
var tokenString = tokenHandler.WriteToken(token);
|
||||
|
||||
// return basic user info and authentication token
|
||||
return Ok(new
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
FirstName = user.FirstName,
|
||||
LastName = user.LastName,
|
||||
Initials = user.Initials,
|
||||
Token = tokenString
|
||||
});
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("register")]
|
||||
public IActionResult Register([FromBody]RegisterModel model)
|
||||
{
|
||||
// map model to entity
|
||||
var user = _mapper.Map<User>(model);
|
||||
|
||||
try
|
||||
{
|
||||
// create user
|
||||
_userService.Create(user, model.Password);
|
||||
return Ok();
|
||||
}
|
||||
catch (AppException ex)
|
||||
{
|
||||
// return error message if there was an exception
|
||||
return BadRequest(new { message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult GetAll()
|
||||
{
|
||||
var users = _userService.GetAll();
|
||||
var model = _mapper.Map<IList<UserModel>>(users);
|
||||
return Ok(model);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult GetById(int id)
|
||||
{
|
||||
var user = _userService.GetById(id);
|
||||
var model = _mapper.Map<UserModel>(user);
|
||||
return Ok(model);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public IActionResult Update(int id, [FromBody]UpdateModel model)
|
||||
{
|
||||
// map model to entity and set id
|
||||
var user = _mapper.Map<User>(model);
|
||||
user.Id = id;
|
||||
|
||||
try
|
||||
{
|
||||
// update user
|
||||
_userService.Update(user, model.Password);
|
||||
return Ok();
|
||||
}
|
||||
catch (AppException ex)
|
||||
{
|
||||
// return error message if there was an exception
|
||||
return BadRequest(new { message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
_userService.Delete(id);
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
81
Sledgemapper.Api/Data/MyDbContext.cs
Normal file
81
Sledgemapper.Api/Data/MyDbContext.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Sledgemapper.Api.Models;
|
||||
namespace Sledgemapper.Api.Data
|
||||
{
|
||||
public static class DbInitializer
|
||||
{
|
||||
public static void Initialize(MyDbContext context)
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
}
|
||||
}
|
||||
|
||||
public class MyDbContext : DbContext
|
||||
{
|
||||
public DbSet<MapLog> MapLogs { get; set; }
|
||||
public DbSet<Session> Sessions { get; set; }
|
||||
public DbSet<UserConnection> UserConnections { get; set; }
|
||||
public DbSet<SessionUser> SessionUsers { get; set; }
|
||||
public DbSet<Snapshot> Snapshots { get; set; }
|
||||
|
||||
public MyDbContext(DbContextOptions options) : base(options)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
// {
|
||||
// // optionsBuilder.
|
||||
// // options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName);
|
||||
// // optionsBuilder.UseSqlite("Filename=SledgemapperDatabase.db", options =>
|
||||
// // {
|
||||
// // options.MigrationsAssembly(Assembly.GetExecutingAssembly().FullName);
|
||||
// // });
|
||||
// optionsBuilder.UseSqlite("Filename=MyDatabase.db").UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
|
||||
|
||||
// base.OnConfiguring(optionsBuilder);
|
||||
// }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
// Map table names
|
||||
modelBuilder.Entity<MapLog>().ToTable("MapLog", "dbo");
|
||||
modelBuilder.Entity<MapLog>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.MapLogId);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Session>().ToTable("Session", "dbo");
|
||||
modelBuilder.Entity<Session>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.SessionId);
|
||||
entity.HasIndex(e => e.SessionName).IsUnique();
|
||||
});
|
||||
|
||||
|
||||
modelBuilder.Entity<UserConnection>().ToTable("UserConnection", "dbo");
|
||||
modelBuilder.Entity<UserConnection>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.UserConnectionId);
|
||||
entity.HasIndex(e => e.UserId);
|
||||
|
||||
});
|
||||
|
||||
modelBuilder.Entity<SessionUser>().ToTable("SessionUser", "dbo");
|
||||
modelBuilder.Entity<SessionUser>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.SessionUserId);
|
||||
entity.HasIndex(e => e.SessionId);
|
||||
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Snapshot>().ToTable("Snapshot", "dbo");
|
||||
modelBuilder.Entity<Snapshot>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.SnapshotId);
|
||||
});
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
#COPY *.sln .
|
||||
COPY SignalRChat/*.csproj ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet restore
|
||||
|
||||
# copy everything else and build app
|
||||
COPY SignalRChat/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app --no-restore
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:5.0
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
ENTRYPOINT ["dotnet", "SignalRChat.dll"]
|
|
@ -1,27 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r linux-musl-arm64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r linux-musl-arm64 --self-contained false --no-restore
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine-arm64v8
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
|
||||
# See: https://github.com/dotnet/announcements/issues/20
|
||||
# Uncomment to enable globalization APIs (or delete)
|
||||
#ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT false
|
||||
#RUN apk add --no-cache icu-libs
|
||||
#ENV LC_ALL en_US.UTF-8
|
||||
#ENV LANG en_US.UTF-8
|
||||
|
||||
ENTRYPOINT ["./aspnetapp"]
|
|
@ -1,27 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r linux-musl-x64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r linux-musl-x64 --self-contained false --no-restore
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
|
||||
# See: https://github.com/dotnet/announcements/issues/20
|
||||
# Uncomment to enable globalization APIs (or delete)
|
||||
#ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT false
|
||||
#RUN apk add --no-cache icu-libs
|
||||
#ENV LC_ALL en_US.UTF-8
|
||||
#ENV LANG en_US.UTF-8
|
||||
|
||||
ENTRYPOINT ["./aspnetapp"]
|
|
@ -1,27 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r linux-musl-x64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r linux-musl-x64 --self-contained true --no-restore /p:PublishTrimmed=true /p:PublishReadyToRun=true
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
|
||||
# See: https://github.com/dotnet/announcements/issues/20
|
||||
# Uncomment to enable globalization APIs (or delete)
|
||||
#ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT false
|
||||
#RUN apk add --no-cache icu-libs
|
||||
#ENV LC_ALL en_US.UTF-8
|
||||
#ENV LANG en_US.UTF-8
|
||||
|
||||
ENTRYPOINT ["./aspnetapp"]
|
|
@ -1,19 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r linux-arm
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r linux-arm --self-contained false --no-restore
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim-arm32v7
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
ENTRYPOINT ["./aspnetapp"]
|
|
@ -1,19 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r linux-arm64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r linux-arm64 --self-contained false --no-restore
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim-arm64v8
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
ENTRYPOINT ["./aspnetapp"]
|
|
@ -1,19 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r linux-x64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
ENTRYPOINT ["./aspnetapp"]
|
|
@ -1,19 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r linux-x64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained true --no-restore /p:PublishTrimmed=true /p:PublishReadyToRun=true
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-buster-slim
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
ENTRYPOINT ["./aspnetapp"]
|
|
@ -1,20 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r win-x64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r win-x64 --self-contained false --no-restore
|
||||
|
||||
# final stage/image
|
||||
# Uses the 2009 release; 2004, 1909, 1903, and 1809 are other choices
|
||||
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-nanoserver-2009 AS runtime
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
ENTRYPOINT ["aspnetapp"]
|
|
@ -1,24 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r win-x64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r win-x64 --self-contained true --no-restore /p:PublishTrimmed=true /p:PublishReadyToRun=true
|
||||
|
||||
# final stage/image
|
||||
# Uses the 2009 release; 2004, 1909, 1903, and 1809 are other choices
|
||||
FROM mcr.microsoft.com/windows/nanoserver:2009 AS runtime
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
|
||||
# Configure web servers to bind to port 80 when present
|
||||
ENV ASPNETCORE_URLS=http://+:80
|
||||
|
||||
ENTRYPOINT ["aspnetapp"]
|
|
@ -1,19 +0,0 @@
|
|||
# https://hub.docker.com/_/microsoft-dotnet-core
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
|
||||
WORKDIR /source
|
||||
|
||||
# copy csproj and restore as distinct layers
|
||||
COPY *.sln .
|
||||
COPY aspnetapp/*.csproj ./aspnetapp/
|
||||
RUN dotnet restore -r linux-x64
|
||||
|
||||
# copy everything else and build app
|
||||
COPY aspnetapp/. ./aspnetapp/
|
||||
WORKDIR /source/aspnetapp
|
||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore
|
||||
|
||||
# final stage/image
|
||||
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-bionic
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
ENTRYPOINT ["./aspnetapp"]
|
13
Sledgemapper.Api/Entities/User.cs
Normal file
13
Sledgemapper.Api/Entities/User.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Sledgemapper.Entities
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Initials { get; set; }
|
||||
public byte[] PasswordHash { get; set; }
|
||||
public byte[] PasswordSalt { get; set; }
|
||||
}
|
||||
}
|
41
Sledgemapper.Api/Handlers/DeleteOverlayCommandHandler.cs
Normal file
41
Sledgemapper.Api/Handlers/DeleteOverlayCommandHandler.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Commands;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class DeleteOverlayCommandHandler : IRequestHandler<DeleteOverlayCommand, bool>
|
||||
{
|
||||
private readonly MyDbContext _dbcontext;
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public DeleteOverlayCommandHandler(IMediator mediator, MyDbContext dbcontext) { _dbcontext = dbcontext; _mediator = mediator; }
|
||||
|
||||
public async Task<bool> Handle(DeleteOverlayCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var jsonString = JsonSerializer.Serialize<Overlay>(notification.Overlay);
|
||||
var session = _dbcontext.Sessions.First(m=>m.SessionName== notification.SessionName);
|
||||
|
||||
_dbcontext.MapLogs.Add(new Sledgemapper.Api.Models.MapLog
|
||||
{
|
||||
Operation = "D",
|
||||
SessionId = session.SessionId,
|
||||
Type = "O",
|
||||
Timestamp = notification.Timestamp,
|
||||
Object = jsonString,
|
||||
UserId=notification.UserId
|
||||
});
|
||||
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
await _mediator.Publish(new DeleteOverlayNotification(session, notification.Overlay, notification.UserId));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
40
Sledgemapper.Api/Handlers/DeleteTileCommandHandler.cs
Normal file
40
Sledgemapper.Api/Handlers/DeleteTileCommandHandler.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Commands;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class DeleteTileCommandHandler : IRequestHandler<DeleteTileCommand,bool>
|
||||
{
|
||||
private readonly MyDbContext _dbcontext;
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public DeleteTileCommandHandler(IMediator mediator, MyDbContext dbcontext) { _dbcontext = dbcontext; _mediator = mediator; }
|
||||
|
||||
public async Task<bool> Handle(DeleteTileCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var jsonString = JsonSerializer.Serialize<Tile>(notification.Tile);
|
||||
var session = _dbcontext.Sessions.First(m=>m.SessionName== notification.SessionName);
|
||||
|
||||
_dbcontext.MapLogs.Add(new Sledgemapper.Api.Models.MapLog
|
||||
{
|
||||
Operation = "D",
|
||||
SessionId = session.SessionId,
|
||||
Type = "T",
|
||||
Timestamp = notification.Timestamp,
|
||||
Object = jsonString,
|
||||
UserId = notification.UserId
|
||||
});
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
await _mediator.Publish(new DeleteTileNotification(session, notification.Tile, notification.UserId));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
43
Sledgemapper.Api/Handlers/DeleteWallCommandHandler.cs
Normal file
43
Sledgemapper.Api/Handlers/DeleteWallCommandHandler.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Sledgemapper.Api.Commands;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
|
||||
public class DeleteWallCommandHandler : IRequestHandler<DeleteWallCommand, bool>
|
||||
{
|
||||
private readonly MyDbContext _dbcontext;
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public DeleteWallCommandHandler(IMediator mediator, MyDbContext dbcontext) { _dbcontext = dbcontext; _mediator = mediator; }
|
||||
|
||||
|
||||
public async Task<bool> Handle(DeleteWallCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var jsonString = JsonSerializer.Serialize<Wall>(notification.Wall);
|
||||
var session = _dbcontext.Sessions.First(m => m.SessionName == notification.SessionName);
|
||||
|
||||
_dbcontext.MapLogs.Add(new Sledgemapper.Api.Models.MapLog
|
||||
{
|
||||
Operation = "D",
|
||||
SessionId = session.SessionId,
|
||||
Type = "W",
|
||||
Timestamp = notification.Timestamp,
|
||||
Object = jsonString,
|
||||
UserId = notification.UserId
|
||||
});
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
await _mediator.Publish(new DeleteWallNotification(session, notification.Wall, notification.UserId));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
93
Sledgemapper.Api/Handlers/GetMapSnapshotCommandHandler.cs
Normal file
93
Sledgemapper.Api/Handlers/GetMapSnapshotCommandHandler.cs
Normal file
|
@ -0,0 +1,93 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using Sledgemapper.Api.Models;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class GetMapSnapshotCommandHandler : IRequestHandler<GetMapSnapshotCommand, Sledgemapper.Shared.Entities.Session>
|
||||
{
|
||||
private readonly MyDbContext _dbcontext;
|
||||
|
||||
public GetMapSnapshotCommandHandler(MyDbContext dbcontext) { _dbcontext = dbcontext; }
|
||||
|
||||
public async Task<Sledgemapper.Shared.Entities.Session> Handle(GetMapSnapshotCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
Snapshot snapshot;
|
||||
double timestamp;
|
||||
Sledgemapper.Shared.Entities.Session mapSession;
|
||||
var session = _dbcontext.Sessions.First(m => m.SessionName == notification.SessionName);
|
||||
snapshot = _dbcontext.Snapshots.OrderByDescending(s => s.Timestamp).FirstOrDefault(m => m.SessionId == session.SessionId);
|
||||
if (snapshot is null)
|
||||
{
|
||||
|
||||
timestamp = 0;
|
||||
mapSession = new Shared.Entities.Session();
|
||||
}
|
||||
else
|
||||
{
|
||||
mapSession = JsonSerializer.Deserialize<Sledgemapper.Shared.Entities.Session>(snapshot.Object);
|
||||
timestamp = snapshot.Timestamp;
|
||||
}
|
||||
|
||||
var mapUpdates = _dbcontext.MapLogs.Where(m => m.SessionId == session.SessionId && m.Timestamp > timestamp).OrderBy(m => m.Timestamp).ToList();
|
||||
foreach (var mapUpdate in mapUpdates)
|
||||
{
|
||||
if (mapUpdate.Operation == "N")
|
||||
{
|
||||
switch (mapUpdate.Type)
|
||||
{
|
||||
case "T":
|
||||
var tile = JsonSerializer.Deserialize<Tile>(mapUpdate.Object);
|
||||
mapSession.NewTile(tile, tile.ID);
|
||||
break;
|
||||
case "W":
|
||||
var wall = JsonSerializer.Deserialize<Wall>(mapUpdate.Object);
|
||||
mapSession.NewWall(wall, wall.ID);
|
||||
break;
|
||||
case "O":
|
||||
var overlay = JsonSerializer.Deserialize<Overlay>(mapUpdate.Object);
|
||||
mapSession.NewOverlay(overlay, overlay.ID);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if (mapUpdate.Operation == "D")
|
||||
{
|
||||
switch (mapUpdate.Type)
|
||||
{
|
||||
case "T":
|
||||
var tile = JsonSerializer.Deserialize<Tile>(mapUpdate.Object);
|
||||
mapSession.DeleteTile(tile);
|
||||
break;
|
||||
case "W":
|
||||
var wall = JsonSerializer.Deserialize<Wall>(mapUpdate.Object);
|
||||
mapSession.DeleteWall(wall);
|
||||
break;
|
||||
case "O":
|
||||
var overlay = JsonSerializer.Deserialize<Overlay>(mapUpdate.Object);
|
||||
mapSession.DeleteOverlay(overlay);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mapUpdates.Any())
|
||||
{
|
||||
var newSnapshot = new Snapshot
|
||||
{
|
||||
SessionId = session.SessionId,
|
||||
Timestamp = mapUpdates.Max(mapSession => mapSession.Timestamp),
|
||||
Object = JsonSerializer.Serialize<Sledgemapper.Shared.Entities.Session>(mapSession)
|
||||
};
|
||||
await _dbcontext.Snapshots.AddAsync(newSnapshot);
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
}
|
||||
return mapSession;
|
||||
}
|
||||
}
|
||||
}
|
39
Sledgemapper.Api/Handlers/NewOverlayCommandHandler.cs
Normal file
39
Sledgemapper.Api/Handlers/NewOverlayCommandHandler.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Commands;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class NewOverlayCommandHandler : IRequestHandler<NewOverlayCommand, bool>
|
||||
{
|
||||
private readonly MyDbContext _dbcontext;
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public NewOverlayCommandHandler(IMediator mediator, MyDbContext dbcontext) { _dbcontext = dbcontext; _mediator = mediator; }
|
||||
|
||||
public async Task<bool> Handle(NewOverlayCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var jsonString = JsonSerializer.Serialize<Overlay>(notification.Overlay);
|
||||
var session = _dbcontext.Sessions.First(m => m.SessionName == notification.SessionName);
|
||||
_dbcontext.MapLogs.Add(new Sledgemapper.Api.Models.MapLog
|
||||
{
|
||||
Operation = "N",
|
||||
SessionId = session.SessionId,
|
||||
Type = "O",
|
||||
Timestamp = notification.Timestamp,
|
||||
Object = jsonString,
|
||||
UserId = notification.UserId,
|
||||
});
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
await _mediator.Publish(new NewOverlayNotification(session, notification.Overlay, notification.UserId));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
37
Sledgemapper.Api/Handlers/NewSnapshotCommandHandler.cs
Normal file
37
Sledgemapper.Api/Handlers/NewSnapshotCommandHandler.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Data;
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class NewSnapshotCommandHandler : IRequestHandler<NewSnapshotCommand, bool>
|
||||
{
|
||||
private readonly MyDbContext _dbcontext;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public NewSnapshotCommandHandler(IMediator mediator, MyDbContext dbcontext) { _dbcontext = dbcontext; _mediator = mediator; }
|
||||
|
||||
public async Task<bool> Handle(NewSnapshotCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
var session = _dbcontext.Sessions.First(m => m.SessionName == notification.SessionName);
|
||||
|
||||
var newSnapshot = new Sledgemapper.Api.Models.Snapshot{
|
||||
SessionId=session.SessionId,
|
||||
Timestamp=notification.Timestamp,
|
||||
Object = JsonSerializer.Serialize<Sledgemapper.Shared.Entities.Session>(notification.Session)
|
||||
|
||||
};
|
||||
await _dbcontext.Snapshots.AddAsync(newSnapshot);
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
41
Sledgemapper.Api/Handlers/NewTileCommandHandler.cs
Normal file
41
Sledgemapper.Api/Handlers/NewTileCommandHandler.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Data;
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using System.Linq;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Commands
|
||||
{
|
||||
public class NewTileCommandHandler : IRequestHandler<NewTileCommand, bool>
|
||||
{
|
||||
private readonly MyDbContext _dbcontext;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public NewTileCommandHandler(IMediator mediator, MyDbContext dbcontext) { _dbcontext = dbcontext; _mediator = mediator; }
|
||||
|
||||
public async Task<bool> Handle(NewTileCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var jsonString = JsonSerializer.Serialize<Tile>(notification.Tile);
|
||||
var session = _dbcontext.Sessions.First(m => m.SessionName == notification.SessionName);
|
||||
|
||||
_dbcontext.MapLogs.Add(new Sledgemapper.Api.Models.MapLog
|
||||
{
|
||||
Operation = "N",
|
||||
SessionId = session.SessionId,
|
||||
Type = "T",
|
||||
Timestamp = notification.Timestamp,
|
||||
Object = jsonString,
|
||||
UserId = notification.UserId
|
||||
});
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
await _mediator.Publish(new NewTileNotification(session, notification.Tile, notification.UserId));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
39
Sledgemapper.Api/Handlers/NewWallCommandHandler.cs
Normal file
39
Sledgemapper.Api/Handlers/NewWallCommandHandler.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
using Sledgemapper.Api.Commands;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class NewWallCommandHandler : IRequestHandler<NewWallCommand, bool>
|
||||
{
|
||||
private readonly MyDbContext _dbcontext;
|
||||
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public NewWallCommandHandler(IMediator mediator, MyDbContext dbcontext) { _dbcontext = dbcontext; _mediator = mediator; }
|
||||
|
||||
public async Task<bool> Handle(NewWallCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var jsonString = JsonSerializer.Serialize<Wall>(notification.Wall);
|
||||
var session = _dbcontext.Sessions.First(m => m.SessionName == notification.SessionName);
|
||||
_dbcontext.MapLogs.Add(new Sledgemapper.Api.Models.MapLog
|
||||
{
|
||||
Operation = "N",
|
||||
SessionId = session.SessionId,
|
||||
Type = "W",
|
||||
Timestamp = notification.Timestamp,
|
||||
Object = jsonString,
|
||||
UserId = notification.UserId,
|
||||
});
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
await _mediator.Publish(new NewWallNotification(session, notification.Wall, notification.UserId));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
22
Sledgemapper.Api/Handlers/SendDeleteOverlayMessage.cs
Normal file
22
Sledgemapper.Api/Handlers/SendDeleteOverlayMessage.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using SignalRChat.Hubs;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Clients;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class SendDeleteOverlayMessage : INotificationHandler<DeleteOverlayNotification>
|
||||
{
|
||||
private readonly IHubContext<SledgemapperHub, ISledgemapperClient> _hub;
|
||||
|
||||
public SendDeleteOverlayMessage(IHubContext<SledgemapperHub, ISledgemapperClient> hub) => _hub = hub;
|
||||
|
||||
public async Task Handle(DeleteOverlayNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await _hub.Clients.Groups(notification.Session.SessionName).DeleteOverlay(notification.Overlay);
|
||||
}
|
||||
}
|
||||
}
|
22
Sledgemapper.Api/Handlers/SendDeleteTileMessage.cs
Normal file
22
Sledgemapper.Api/Handlers/SendDeleteTileMessage.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using SignalRChat.Hubs;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Clients;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class SendDeleteTileMessage : INotificationHandler<DeleteTileNotification>
|
||||
{
|
||||
private readonly IHubContext<SledgemapperHub, ISledgemapperClient> _hub;
|
||||
|
||||
public SendDeleteTileMessage(IHubContext<SledgemapperHub, ISledgemapperClient> hub) => _hub = hub;
|
||||
|
||||
public async Task Handle(DeleteTileNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await _hub.Clients.Groups(notification.Session.SessionName).DeleteTile(notification.Tile);
|
||||
}
|
||||
}
|
||||
}
|
22
Sledgemapper.Api/Handlers/SendDeleteWallMessage.cs
Normal file
22
Sledgemapper.Api/Handlers/SendDeleteWallMessage.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using SignalRChat.Hubs;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Clients;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class SendDeleteWallMessage : INotificationHandler<DeleteWallNotification>
|
||||
{
|
||||
private readonly IHubContext<SledgemapperHub, ISledgemapperClient> _hub;
|
||||
|
||||
public SendDeleteWallMessage(IHubContext<SledgemapperHub, ISledgemapperClient> hub) => _hub = hub;
|
||||
|
||||
public async Task Handle(DeleteWallNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await _hub.Clients.Groups(notification.Session.SessionName).DeleteWall(notification.Wall);
|
||||
}
|
||||
}
|
||||
}
|
22
Sledgemapper.Api/Handlers/SendNewOverlayMessage.cs
Normal file
22
Sledgemapper.Api/Handlers/SendNewOverlayMessage.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using SignalRChat.Hubs;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Clients;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class SendNewOverlayMessage : INotificationHandler<NewOverlayNotification>
|
||||
{
|
||||
private readonly IHubContext<SledgemapperHub, ISledgemapperClient> _hub;
|
||||
|
||||
public SendNewOverlayMessage(IHubContext<SledgemapperHub, ISledgemapperClient> hub) => _hub = hub;
|
||||
|
||||
public async Task Handle(NewOverlayNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await _hub.Clients.Groups(notification.Session.SessionName).NewOverlay(notification.Overlay);
|
||||
}
|
||||
}
|
||||
}
|
23
Sledgemapper.Api/Handlers/SendNewTileMessage.cs
Normal file
23
Sledgemapper.Api/Handlers/SendNewTileMessage.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using SignalRChat.Hubs;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Clients;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class SendNewTileMessage : INotificationHandler<NewTileNotification>
|
||||
{
|
||||
private readonly IHubContext<SledgemapperHub, ISledgemapperClient> _hub;
|
||||
|
||||
public SendNewTileMessage(IHubContext<SledgemapperHub, ISledgemapperClient> hub) => _hub = hub;
|
||||
|
||||
public async Task Handle(NewTileNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
await _hub.Clients.Groups(notification.Session.SessionName).NewTile(notification.Tile);
|
||||
}
|
||||
}
|
||||
}
|
22
Sledgemapper.Api/Handlers/SendNewWallMessage.cs
Normal file
22
Sledgemapper.Api/Handlers/SendNewWallMessage.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using SignalRChat.Hubs;
|
||||
using Sledgemapper.Api.Notifications;
|
||||
using Sledgemapper.Clients;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class SendNewWallMessage : INotificationHandler<NewWallNotification>
|
||||
{
|
||||
private readonly IHubContext<SledgemapperHub, ISledgemapperClient> _hub;
|
||||
|
||||
public SendNewWallMessage(IHubContext<SledgemapperHub, ISledgemapperClient> hub) => _hub = hub;
|
||||
|
||||
public async Task Handle(NewWallNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await _hub.Clients.Groups(notification.Session.SessionName).NewWall(notification.Wall);
|
||||
}
|
||||
}
|
||||
}
|
32
Sledgemapper.Api/Handlers/StartNewSessionHandler.cs
Normal file
32
Sledgemapper.Api/Handlers/StartNewSessionHandler.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using MediatR;
|
||||
using Sledgemapper.Api.Commands;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Sledgemapper.Api.Models;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sledgemapper.Api.Handlers
|
||||
{
|
||||
public class StartNewSessionHandler : IRequestHandler<NewSessionCommand, bool>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
private readonly MyDbContext _dbcontext;
|
||||
|
||||
public StartNewSessionHandler(IMediator mediator, MyDbContext dbcontext)
|
||||
{
|
||||
_mediator = mediator;
|
||||
_dbcontext = dbcontext;
|
||||
}
|
||||
|
||||
public async Task<bool> Handle(NewSessionCommand notification, CancellationToken cancellationToken)
|
||||
{
|
||||
_dbcontext.Sessions.Add(new Session
|
||||
{
|
||||
SessionName = notification.SessionName,
|
||||
OwnerUserId = notification.UserId
|
||||
});
|
||||
await _dbcontext.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
19
Sledgemapper.Api/Helpers/AppException.cs
Normal file
19
Sledgemapper.Api/Helpers/AppException.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Sledgemapper.Helpers
|
||||
{
|
||||
// Custom exception class for throwing application specific exceptions (e.g. for validation)
|
||||
// that can be caught and handled within the application
|
||||
public class AppException : Exception
|
||||
{
|
||||
public AppException() : base() {}
|
||||
|
||||
public AppException(string message) : base(message) { }
|
||||
|
||||
public AppException(string message, params object[] args)
|
||||
: base(string.Format(CultureInfo.CurrentCulture, message, args))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
7
Sledgemapper.Api/Helpers/AppSettings.cs
Normal file
7
Sledgemapper.Api/Helpers/AppSettings.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Sledgemapper.Helpers
|
||||
{
|
||||
public class AppSettings
|
||||
{
|
||||
public string Secret { get; set; }
|
||||
}
|
||||
}
|
16
Sledgemapper.Api/Helpers/AutoMapperProfile.cs
Normal file
16
Sledgemapper.Api/Helpers/AutoMapperProfile.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using AutoMapper;
|
||||
using Sledgemapper.Entities;
|
||||
using Sledgemapper.Models.Users;
|
||||
|
||||
namespace Sledgemapper.Helpers
|
||||
{
|
||||
public class AutoMapperProfile : Profile
|
||||
{
|
||||
public AutoMapperProfile()
|
||||
{
|
||||
CreateMap<User, UserModel>();
|
||||
CreateMap<RegisterModel, User>();
|
||||
CreateMap<UpdateModel, User>();
|
||||
}
|
||||
}
|
||||
}
|
24
Sledgemapper.Api/Helpers/DataContext.cs
Normal file
24
Sledgemapper.Api/Helpers/DataContext.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Sledgemapper.Entities;
|
||||
|
||||
namespace Sledgemapper.Helpers
|
||||
{
|
||||
public class DataContext : DbContext
|
||||
{
|
||||
protected readonly IConfiguration Configuration;
|
||||
|
||||
public DataContext(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
// connect to sql server database
|
||||
options.UseSqlServer(Configuration.GetConnectionString("WebApiDatabase"));
|
||||
}
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
}
|
||||
}
|
16
Sledgemapper.Api/Helpers/SqliteDataContext.cs
Normal file
16
Sledgemapper.Api/Helpers/SqliteDataContext.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Sledgemapper.Helpers
|
||||
{
|
||||
public class SqliteDataContext : DataContext
|
||||
{
|
||||
public SqliteDataContext(IConfiguration configuration) : base(configuration) { }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
// connect to sqlite database
|
||||
options.UseSqlite(Configuration.GetConnectionString("WebApiDatabase"));
|
||||
}
|
||||
}
|
||||
}
|
150
Sledgemapper.Api/Hubs/SledgemapperHub.cs
Normal file
150
Sledgemapper.Api/Hubs/SledgemapperHub.cs
Normal file
|
@ -0,0 +1,150 @@
|
|||
using Microsoft.AspNetCore.SignalR;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using Sledgemapper.Shared.Entities;
|
||||
using Sledgemapper.Clients;
|
||||
using System;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Sledgemapper.Api.Models;
|
||||
using Sledgemapper.Helpers;
|
||||
|
||||
namespace SignalRChat.Hubs
|
||||
{
|
||||
[Authorize]
|
||||
public class SledgemapperHub : Hub<ISledgemapperClient>
|
||||
{
|
||||
private static readonly ConcurrentDictionary<int, string> UserColors = new();
|
||||
private readonly MyDbContext _dbContext;
|
||||
private readonly DataContext _datacontext;
|
||||
|
||||
public SledgemapperHub(MyDbContext dbContext, DataContext datacontext)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_datacontext = datacontext;
|
||||
}
|
||||
|
||||
// other colors
|
||||
// #cca300, #20f200, #004011, #00e6d6, #005c73, #0057d9, #d900ca, #660029, #d9003a
|
||||
// private static Dictionary<string, Session> _sessions = new Dictionary<string, Session>();
|
||||
public List<string> Colors = new()
|
||||
{
|
||||
"#e6194B",
|
||||
"#f58231",
|
||||
"#3cb44b",
|
||||
"#000075",
|
||||
"#911eb4",
|
||||
"#800000",
|
||||
"#808000",
|
||||
"#469990"
|
||||
};
|
||||
|
||||
public async Task NewTile(string sessionName, Tile tile)
|
||||
{
|
||||
await Clients.Group(sessionName).NewTile(tile);
|
||||
}
|
||||
|
||||
public async Task NewWall(string sessionName, Wall tile)
|
||||
{
|
||||
await Clients.Group(sessionName).NewWall(tile);
|
||||
}
|
||||
|
||||
public async Task NewOverlay(string sessionName, Overlay tile)
|
||||
{
|
||||
await Clients.Group(sessionName).NewOverlay(tile);
|
||||
}
|
||||
|
||||
public async Task DeleteTile(string sessionName, Tile tile)
|
||||
{
|
||||
await Clients.Group(sessionName).DeleteTile(tile);
|
||||
}
|
||||
|
||||
public async Task DeleteWall(string sessionName, Wall tile)
|
||||
{
|
||||
await Clients.Group(sessionName).DeleteWall(tile);
|
||||
}
|
||||
|
||||
public async Task DeleteOverlay(string sessionName, Overlay tile)
|
||||
{
|
||||
await Clients.Group(sessionName).DeleteOverlay(tile);
|
||||
}
|
||||
|
||||
public async Task<Sledgemapper.Shared.Entities.Session> JoinSession(string sessionName)
|
||||
{
|
||||
var session = _dbContext.Sessions.FirstOrDefault(s => s.SessionName == sessionName);
|
||||
var userId = int.Parse(Context.User.Identity.Name);
|
||||
|
||||
if (session != null)
|
||||
{
|
||||
var userSession = new SessionUser { SessionId = session.SessionId, UserId = userId };
|
||||
_dbContext.SessionUsers.Add(userSession);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, session.SessionName);
|
||||
var user = _datacontext.Users.First(u => u.Id == userId);
|
||||
|
||||
var player = new Player { UserId = userId, Initials = user.Initials, Position = new Tile { X = 0, Y = 0 }, Color = UserColors[userId] };
|
||||
|
||||
await Clients.Group(session.SessionName).NewPlayer(player);
|
||||
await Clients.Group(session.SessionName).RefreshPlayers();
|
||||
|
||||
var newSession = new Sledgemapper.Shared.Entities.Session
|
||||
{
|
||||
SessionName = session.SessionName,
|
||||
SessionId = session.SessionId
|
||||
};
|
||||
|
||||
return newSession;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdatePosition(string sessionName, int sessionId, Tile tile)
|
||||
{
|
||||
var userId = int.Parse(Context.User.Identity.Name);
|
||||
var SessionUsers = _dbContext.SessionUsers.Where(m => m.SessionId == sessionId).OrderBy(m => m.UserId).ToList();
|
||||
var user = _datacontext.Users.First(u => u.Id == userId);
|
||||
var player = new Player { UserId = userId, Initials = user.Initials, Position = tile, Color = UserColors[userId]};
|
||||
await Clients.Group(sessionName).PlayerUpdate(player);
|
||||
}
|
||||
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
var userId = int.Parse(Context.User.Identity.Name);
|
||||
var userConnection = new UserConnection { ConnectionId = Context.ConnectionId, UserId = userId };
|
||||
_dbContext.UserConnections.Add(userConnection);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
var availableColor = Colors.Where(m => !UserColors.Values.Contains(m)).First();
|
||||
UserColors.AddOrUpdate(userId, availableColor, (key, oldValue) => availableColor);
|
||||
await base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
public override async Task OnDisconnectedAsync(Exception exception)
|
||||
{
|
||||
var userConnection = _dbContext.UserConnections.FirstOrDefault(m => m.ConnectionId == Context.ConnectionId);
|
||||
var userId=userConnection.UserId;
|
||||
if (userConnection != null)
|
||||
{
|
||||
_dbContext.UserConnections.Remove(userConnection);
|
||||
}
|
||||
|
||||
var userSessions = _dbContext.SessionUsers.Where(m => m.UserId == userId).ToList();
|
||||
{
|
||||
foreach (var userSession in userSessions)
|
||||
{
|
||||
var session = _dbContext.Sessions.FirstOrDefault(m => m.SessionId == userSession.SessionId);
|
||||
await Clients.Group(session.SessionName).PlayerUpdate(null); //send remove player
|
||||
_dbContext.SessionUsers.Remove(userSession);
|
||||
}
|
||||
}
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
21
Sledgemapper.Api/LICENSE
Normal file
21
Sledgemapper.Api/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Jason Watmore
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
BIN
Sledgemapper.Api/LocalDatabase.db
Normal file
BIN
Sledgemapper.Api/LocalDatabase.db
Normal file
Binary file not shown.
53
Sledgemapper.Api/Migrations/SqlServerMigrations/20200102103423_InitialCreate.Designer.cs
generated
Normal file
53
Sledgemapper.Api/Migrations/SqlServerMigrations/20200102103423_InitialCreate.Designer.cs
generated
Normal file
|
@ -0,0 +1,53 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Sledgemapper.Helpers;
|
||||
|
||||
namespace Sledgemapper.Migrations.SqlServerMigrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20200102103423_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Sledgemapper.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<byte[]>("PasswordHash")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<byte[]>("PasswordSalt")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Sledgemapper.Migrations.SqlServerMigrations
|
||||
{
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
FirstName = table.Column<string>(nullable: true),
|
||||
LastName = table.Column<string>(nullable: true),
|
||||
Username = table.Column<string>(nullable: true),
|
||||
PasswordHash = table.Column<byte[]>(nullable: true),
|
||||
PasswordSalt = table.Column<byte[]>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Sledgemapper.Helpers;
|
||||
|
||||
namespace Sledgemapper.Migrations.SqlServerMigrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
partial class DataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Sledgemapper.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<byte[]>("PasswordHash")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<byte[]>("PasswordSalt")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
52
Sledgemapper.Api/Migrations/SqliteMigrations/20200102102942_InitialCreate.Designer.cs
generated
Normal file
52
Sledgemapper.Api/Migrations/SqliteMigrations/20200102102942_InitialCreate.Designer.cs
generated
Normal file
|
@ -0,0 +1,52 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Sledgemapper.Helpers;
|
||||
|
||||
namespace Sledgemapper.Migrations.SqliteMigrations
|
||||
{
|
||||
[DbContext(typeof(SqliteDataContext))]
|
||||
[Migration("20200102102942_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0");
|
||||
|
||||
modelBuilder.Entity("Sledgemapper.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Initials")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("PasswordHash")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<byte[]>("PasswordSalt")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Sledgemapper.Migrations.SqliteMigrations
|
||||
{
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
FirstName = table.Column<string>(nullable: true),
|
||||
LastName = table.Column<string>(nullable: true),
|
||||
Username = table.Column<string>(nullable: true),
|
||||
Initials = table.Column<string>(nullable: true),
|
||||
PasswordHash = table.Column<byte[]>(nullable: true),
|
||||
PasswordSalt = table.Column<byte[]>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Sledgemapper.Helpers;
|
||||
|
||||
namespace Sledgemapper.Migrations.SqliteMigrations
|
||||
{
|
||||
[DbContext(typeof(SqliteDataContext))]
|
||||
partial class SqliteDataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0");
|
||||
|
||||
modelBuilder.Entity("Sledgemapper.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Initials")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("PasswordHash")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<byte[]>("PasswordSalt")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
13
Sledgemapper.Api/Models copy/Users/AuthenticateModel.cs
Normal file
13
Sledgemapper.Api/Models copy/Users/AuthenticateModel.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sledgemapper.Models.Users
|
||||
{
|
||||
public class AuthenticateModel
|
||||
{
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
22
Sledgemapper.Api/Models copy/Users/RegisterModel.cs
Normal file
22
Sledgemapper.Api/Models copy/Users/RegisterModel.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sledgemapper.Models.Users
|
||||
{
|
||||
public class RegisterModel
|
||||
{
|
||||
[Required]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[Required]
|
||||
public string LastName { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Initials { get; set; }
|
||||
}
|
||||
}
|
10
Sledgemapper.Api/Models copy/Users/UpdateModel.cs
Normal file
10
Sledgemapper.Api/Models copy/Users/UpdateModel.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Sledgemapper.Models.Users
|
||||
{
|
||||
public class UpdateModel
|
||||
{
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
11
Sledgemapper.Api/Models copy/Users/UserModel.cs
Normal file
11
Sledgemapper.Api/Models copy/Users/UserModel.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Sledgemapper.Models.Users
|
||||
{
|
||||
public class UserModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Initials { get; set; }
|
||||
}
|
||||
}
|
32
Sledgemapper.Api/Models/MapLog.cs
Normal file
32
Sledgemapper.Api/Models/MapLog.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sledgemapper.Api.Models
|
||||
{
|
||||
|
||||
public class MapLog
|
||||
{
|
||||
[Key]
|
||||
public int MapLogId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int UserId{get;set;}
|
||||
|
||||
[Required]
|
||||
public int SessionId { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(1)]
|
||||
public string Operation { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(256)]
|
||||
public string Type { get; set; }
|
||||
|
||||
[Required]
|
||||
[MaxLength(256)]
|
||||
public string Object { get; set; }
|
||||
|
||||
[Required]
|
||||
public double Timestamp { get; set; }
|
||||
}
|
||||
}
|
16
Sledgemapper.Api/Models/Session.cs
Normal file
16
Sledgemapper.Api/Models/Session.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sledgemapper.Api.Models
|
||||
{
|
||||
public class Session
|
||||
{
|
||||
[Key]
|
||||
public int SessionId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string SessionName{get;set;}
|
||||
|
||||
[Required]
|
||||
public int OwnerUserId { get; set; }
|
||||
}
|
||||
}
|
16
Sledgemapper.Api/Models/SessionUser.cs
Normal file
16
Sledgemapper.Api/Models/SessionUser.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sledgemapper.Api.Models
|
||||
{
|
||||
public class SessionUser
|
||||
{
|
||||
[Key]
|
||||
public int SessionUserId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int SessionId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int UserId { get; set; }
|
||||
}
|
||||
}
|
21
Sledgemapper.Api/Models/Snapshot.cs
Normal file
21
Sledgemapper.Api/Models/Snapshot.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sledgemapper.Api.Models
|
||||
{
|
||||
|
||||
public class Snapshot
|
||||
{
|
||||
[Key]
|
||||
public int SnapshotId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int SessionId { get; set; }
|
||||
|
||||
|
||||
[Required]
|
||||
public string Object { get; set; }
|
||||
|
||||
[Required]
|
||||
public double Timestamp { get; set; }
|
||||
}
|
||||
}
|
15
Sledgemapper.Api/Models/UserConnection.cs
Normal file
15
Sledgemapper.Api/Models/UserConnection.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sledgemapper.Api.Models
|
||||
{
|
||||
public class UserConnection
|
||||
{
|
||||
[Key]
|
||||
public int UserConnectionId { get; set; }
|
||||
[Required]
|
||||
public int UserId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ConnectionId{get;set;}
|
||||
}
|
||||
}
|
20
Sledgemapper.Api/Notifications/BaseNotification.cs
Normal file
20
Sledgemapper.Api/Notifications/BaseNotification.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using MediatR;
|
||||
using System;
|
||||
|
||||
namespace Sledgemapper.Api.Notifications
|
||||
{
|
||||
public abstract class BaseNotification : INotification
|
||||
{
|
||||
public double Timestamp { get; private set; }
|
||||
public int UserId { get; private set; }
|
||||
public int SessionId { get; set; }
|
||||
public Models.Session Session { get; set; }
|
||||
|
||||
public BaseNotification(Models.Session session, int userId)
|
||||
{
|
||||
Timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
Session = session; ;
|
||||
UserId = userId;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Notifications/DeleteOverlayNotification.cs
Normal file
14
Sledgemapper.Api/Notifications/DeleteOverlayNotification.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Notifications
|
||||
{
|
||||
public class DeleteOverlayNotification : BaseNotification
|
||||
{
|
||||
public Overlay Overlay { get; private set; }
|
||||
|
||||
public DeleteOverlayNotification(Models.Session session, Overlay overlay, int userId) : base(session, userId)
|
||||
{
|
||||
Overlay = overlay;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Notifications/DeleteTileNotification.cs
Normal file
14
Sledgemapper.Api/Notifications/DeleteTileNotification.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Notifications
|
||||
{
|
||||
public class DeleteTileNotification : BaseNotification
|
||||
{
|
||||
public Tile Tile { get; private set; }
|
||||
|
||||
public DeleteTileNotification(Models.Session session, Tile tile, int userId) : base(session, userId)
|
||||
{
|
||||
Tile = tile;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Notifications/DeleteWallNotification.cs
Normal file
14
Sledgemapper.Api/Notifications/DeleteWallNotification.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Notifications
|
||||
{
|
||||
public class DeleteWallNotification : BaseNotification
|
||||
{
|
||||
public Wall Wall { get; private set; }
|
||||
|
||||
public DeleteWallNotification(Models.Session session, Wall wall, int userId) : base(session, userId)
|
||||
{
|
||||
Wall = wall;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Notifications/NewOverlayNotification.cs
Normal file
14
Sledgemapper.Api/Notifications/NewOverlayNotification.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Notifications
|
||||
{
|
||||
public class NewOverlayNotification : BaseNotification
|
||||
{
|
||||
public Overlay Overlay { get; private set; }
|
||||
|
||||
public NewOverlayNotification(Models.Session session, Overlay overlay, int userId) : base(session, userId)
|
||||
{
|
||||
Overlay = overlay;
|
||||
}
|
||||
}
|
||||
}
|
8
Sledgemapper.Api/Notifications/NewSessionNotification.cs
Normal file
8
Sledgemapper.Api/Notifications/NewSessionNotification.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Sledgemapper.Api.Notifications
|
||||
{
|
||||
public class NewSessionNotification : BaseNotification
|
||||
{
|
||||
public NewSessionNotification(string sessionName, int userId) : base(new Models.Session { SessionName = sessionName }, userId)
|
||||
{ }
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Notifications/NewTileNotification.cs
Normal file
14
Sledgemapper.Api/Notifications/NewTileNotification.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Notifications
|
||||
{
|
||||
public class NewTileNotification : BaseNotification
|
||||
{
|
||||
public Tile Tile { get; private set; }
|
||||
|
||||
public NewTileNotification(Models.Session session, Tile tile, int userId) : base(session, userId)
|
||||
{
|
||||
Tile = tile;
|
||||
}
|
||||
}
|
||||
}
|
14
Sledgemapper.Api/Notifications/NewWallNotification.cs
Normal file
14
Sledgemapper.Api/Notifications/NewWallNotification.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Sledgemapper.Shared.Entities;
|
||||
|
||||
namespace Sledgemapper.Api.Notifications
|
||||
{
|
||||
public class NewWallNotification : BaseNotification
|
||||
{
|
||||
public Wall Wall { get; private set; }
|
||||
|
||||
public NewWallNotification(Models.Session session, Wall wall, int userId) : base(session, userId)
|
||||
{
|
||||
Wall = wall;
|
||||
}
|
||||
}
|
||||
}
|
51
Sledgemapper.Api/Program.cs
Normal file
51
Sledgemapper.Api/Program.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sledgemapper.Api.Data;
|
||||
|
||||
namespace SignalRChat
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host= CreateHostBuilder(args).Build();
|
||||
CreateDbIfNotExists(host);
|
||||
host.Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>().UseUrls("http://localhost:5000");
|
||||
});
|
||||
|
||||
// public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
// Host.CreateDefaultBuilder(args)
|
||||
// .ConfigureWebHostDefaults(webBuilder =>
|
||||
// {
|
||||
// webBuilder.UseStartup<Startup>();
|
||||
// });
|
||||
|
||||
private static void CreateDbIfNotExists(IHost host)
|
||||
{
|
||||
using (var scope = host.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
try
|
||||
{
|
||||
var context = services.GetRequiredService<MyDbContext>();
|
||||
DbInitializer.Initialize(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||
logger.LogError(ex, "An error occurred creating the DB.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
Sledgemapper.Api/Properties/launchSettings.json
Normal file
10
Sledgemapper.Api/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"profiles": {
|
||||
"Development": {
|
||||
"commandName": "Project",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
Sledgemapper.Api/README.md
Normal file
5
Sledgemapper.Api/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# aspnet-core-3-registration-login-api
|
||||
|
||||
ASP.NET Core 3.1 - Simple API for User Management, Authentication and Registration
|
||||
|
||||
For documentation and instructions check out https://jasonwatmore.com/post/2019/10/14/aspnet-core-3-simple-api-for-authentication-registration-and-user-management
|
159
Sledgemapper.Api/Services/UserService.cs
Normal file
159
Sledgemapper.Api/Services/UserService.cs
Normal file
|
@ -0,0 +1,159 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Sledgemapper.Entities;
|
||||
using Sledgemapper.Helpers;
|
||||
|
||||
namespace Sledgemapper.Services
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
User Authenticate(string username, string password);
|
||||
IEnumerable<User> GetAll();
|
||||
User GetById(int id);
|
||||
User Create(User user, string password);
|
||||
void Update(User user, string password = null);
|
||||
void Delete(int id);
|
||||
}
|
||||
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private DataContext _context;
|
||||
|
||||
public UserService(DataContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public User Authenticate(string username, string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
||||
return null;
|
||||
|
||||
var user = _context.Users.SingleOrDefault(x => x.Username == username);
|
||||
|
||||
// check if username exists
|
||||
if (user == null)
|
||||
return null;
|
||||
|
||||
// check if password is correct
|
||||
if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt))
|
||||
return null;
|
||||
|
||||
// authentication successful
|
||||
return user;
|
||||
}
|
||||
|
||||
public IEnumerable<User> GetAll()
|
||||
{
|
||||
return _context.Users;
|
||||
}
|
||||
|
||||
public User GetById(int id)
|
||||
{
|
||||
return _context.Users.Find(id);
|
||||
}
|
||||
|
||||
public User Create(User user, string password)
|
||||
{
|
||||
// validation
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
throw new AppException("Password is required");
|
||||
|
||||
if (_context.Users.Any(x => x.Username == user.Username))
|
||||
throw new AppException("Username \"" + user.Username + "\" is already taken");
|
||||
|
||||
byte[] passwordHash, passwordSalt;
|
||||
CreatePasswordHash(password, out passwordHash, out passwordSalt);
|
||||
|
||||
user.PasswordHash = passwordHash;
|
||||
user.PasswordSalt = passwordSalt;
|
||||
|
||||
_context.Users.Add(user);
|
||||
_context.SaveChanges();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public void Update(User userParam, string password = null)
|
||||
{
|
||||
var user = _context.Users.Find(userParam.Id);
|
||||
|
||||
if (user == null)
|
||||
throw new AppException("User not found");
|
||||
|
||||
// update username if it has changed
|
||||
if (!string.IsNullOrWhiteSpace(userParam.Username) && userParam.Username != user.Username)
|
||||
{
|
||||
// throw error if the new username is already taken
|
||||
if (_context.Users.Any(x => x.Username == userParam.Username))
|
||||
throw new AppException("Username " + userParam.Username + " is already taken");
|
||||
|
||||
user.Username = userParam.Username;
|
||||
}
|
||||
|
||||
// update user properties if provided
|
||||
if (!string.IsNullOrWhiteSpace(userParam.FirstName))
|
||||
user.FirstName = userParam.FirstName;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(userParam.LastName))
|
||||
user.LastName = userParam.LastName;
|
||||
|
||||
// update password if provided
|
||||
if (!string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
byte[] passwordHash, passwordSalt;
|
||||
CreatePasswordHash(password, out passwordHash, out passwordSalt);
|
||||
|
||||
user.PasswordHash = passwordHash;
|
||||
user.PasswordSalt = passwordSalt;
|
||||
}
|
||||
|
||||
_context.Users.Update(user);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
{
|
||||
var user = _context.Users.Find(id);
|
||||
if (user != null)
|
||||
{
|
||||
_context.Users.Remove(user);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// private helper methods
|
||||
|
||||
private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
|
||||
{
|
||||
if (password == null) throw new ArgumentNullException("password");
|
||||
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");
|
||||
|
||||
using (var hmac = new System.Security.Cryptography.HMACSHA512())
|
||||
{
|
||||
passwordSalt = hmac.Key;
|
||||
passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt)
|
||||
{
|
||||
if (password == null) throw new ArgumentNullException("password");
|
||||
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");
|
||||
if (storedHash.Length != 64) throw new ArgumentException("Invalid length of password hash (64 bytes expected).", "passwordHash");
|
||||
if (storedSalt.Length != 128) throw new ArgumentException("Invalid length of password salt (128 bytes expected).", "passwordHash");
|
||||
|
||||
using (var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt))
|
||||
{
|
||||
var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
|
||||
for (int i = 0; i < computedHash.Length; i++)
|
||||
{
|
||||
if (computedHash[i] != storedHash[i]) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
26
Sledgemapper.Api/Sledgemapper.Api.csproj
Normal file
26
Sledgemapper.Api/Sledgemapper.Api.csproj
Normal file
|
@ -0,0 +1,26 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Sledgemapper.Shared\Sledgemapper.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="10.1.1" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
|
||||
<PackageReference Include="mediatr" Version="9.0.0" />
|
||||
<PackageReference Include="mediatr.extensions.microsoft.dependencyinjection" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
141
Sledgemapper.Api/Startup.cs
Normal file
141
Sledgemapper.Api/Startup.cs
Normal file
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using SignalRChat.Hubs;
|
||||
using Sledgemapper.Api.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MediatR;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Sledgemapper.Helpers;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using System.Text;
|
||||
using AutoMapper;
|
||||
using Sledgemapper.Services;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace SignalRChat
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
private readonly IWebHostEnvironment _env;
|
||||
public IConfiguration Configuration { get; }
|
||||
public Startup(IWebHostEnvironment env, IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
_env = env;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
if (_env.IsProduction())
|
||||
services.AddDbContext<DataContext>();
|
||||
else
|
||||
services.AddDbContext<DataContext, SqliteDataContext>();
|
||||
|
||||
// services.AddRazorPages();
|
||||
services.AddCors();
|
||||
services.AddControllers();
|
||||
services.AddSignalR();
|
||||
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
|
||||
services.AddMediatR(typeof(Startup));
|
||||
services.AddDbContext<MyDbContext>(options => {options.UseSqlite("Data Source=sledgemapper.db"); options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);});
|
||||
|
||||
// services.AddEntityFrameworkSqlite().AddDbContext<MyDbContext>();
|
||||
// configure strongly typed settings objects
|
||||
var c = Configuration.GetSection("AppSettings");
|
||||
var appSettingsSection = Configuration.GetSection("AppSettings");
|
||||
services.Configure<AppSettings>(appSettingsSection);
|
||||
|
||||
// configure jwt authentication
|
||||
var appSettings = appSettingsSection.Get<AppSettings>();
|
||||
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
|
||||
services.AddAuthentication(x =>
|
||||
{
|
||||
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(x =>
|
||||
{
|
||||
x.Events = new JwtBearerEvents
|
||||
{
|
||||
OnTokenValidated = context =>
|
||||
{
|
||||
var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
|
||||
var userId = int.Parse(context.Principal.Identity.Name);
|
||||
var user = userService.GetById(userId);
|
||||
if (user == null)
|
||||
{
|
||||
// return unauthorized if user no longer exists
|
||||
context.Fail("Unauthorized");
|
||||
}
|
||||
context.HttpContext.User.Claims.Append(new Claim(ClaimTypes.Name, user.Username));
|
||||
context.HttpContext.User.Claims.Append(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
x.RequireHttpsMetadata = false;
|
||||
x.SaveToken = true;
|
||||
x.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false
|
||||
};
|
||||
});
|
||||
|
||||
// configure DI for application services
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
|
||||
{
|
||||
dataContext.Database.Migrate();
|
||||
|
||||
// if (env.IsDevelopment())
|
||||
// {
|
||||
// app.UseDeveloperExceptionPage();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// app.UseExceptionHandler("/Error");
|
||||
// // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
// app.UseHsts();
|
||||
// }
|
||||
|
||||
// //app.UseHttpsRedirection();
|
||||
// app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
// global cors policy
|
||||
app.UseCors(x => x
|
||||
.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader());
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
// endpoints.MapRazorPages();
|
||||
endpoints.MapHub<SledgemapperHub>("/sledgemapperhub");
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
12
Sledgemapper.Api/appsettings.Development.json
Normal file
12
Sledgemapper.Api/appsettings.Development.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"WebApiDatabase": "Data Source=LocalDatabase.db"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
16
Sledgemapper.Api/appsettings.json
Normal file
16
Sledgemapper.Api/appsettings.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"AppSettings": {
|
||||
"Secret": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"WebApiDatabase": "ENTER PRODUCTION SQL SERVER CONNECTION STRING HERE"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
BIN
Sledgemapper.Api/sledgemapper.db
Normal file
BIN
Sledgemapper.Api/sledgemapper.db
Normal file
Binary file not shown.
34
Sledgemapper.Api/src/.vscode/launch.json
vendored
34
Sledgemapper.Api/src/.vscode/launch.json
vendored
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (web)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/bin/Debug/net5.0/SignalRChat.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/Views": "${workspaceFolder}/Views"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
42
Sledgemapper.Api/src/.vscode/tasks.json
vendored
42
Sledgemapper.Api/src/.vscode/tasks.json
vendored
|
@ -1,42 +0,0 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/SignalRChat.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/SignalRChat.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"${workspaceFolder}/SignalRChat.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
using Microsoft.AspNetCore.SignalR;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using Sledgemapper.Entities;
|
||||
|
||||
namespace SignalRChat.Hubs
|
||||
{
|
||||
|
||||
|
||||
public class ChatHub : Hub
|
||||
{
|
||||
private static Dictionary<string, SessionData> _sessions = new Dictionary<string, SessionData>();
|
||||
public List<string> Colors = new List<string>{"CC0000",
|
||||
"CC3300",
|
||||
"FFCC00",
|
||||
"009900",
|
||||
"006666",
|
||||
"0066FF",
|
||||
"0000CC",
|
||||
"663399",
|
||||
"CC0099"};
|
||||
|
||||
public async Task SendMessage(string user, string message, string sessionName)
|
||||
{
|
||||
await Clients.Group(sessionName).SendAsync("ReceiveMessage", user, message);
|
||||
}
|
||||
|
||||
public async Task NewTile(string sessionName, Tile tile)
|
||||
{
|
||||
var existingTile = _sessions[sessionName].Map.TryGetValue(tile.ToString(), out var t);
|
||||
if (existingTile)
|
||||
{
|
||||
|
||||
_sessions[sessionName].Map.TryRemove(t.ToString(), out var rtile);
|
||||
}
|
||||
_sessions[sessionName].Map.TryAdd(tile.ToString(), tile);
|
||||
|
||||
|
||||
await Clients.Group(sessionName).SendAsync("NewTile", tile);
|
||||
}
|
||||
|
||||
public async Task NewWall(string sessionName, Wall tile)
|
||||
{
|
||||
var existingTile = _sessions[sessionName].Walls.TryGetValue(tile.ToString(), out var t);
|
||||
if (existingTile)
|
||||
{
|
||||
_sessions[sessionName].Walls.TryRemove(t.ToString(), out var rtile);
|
||||
|
||||
}
|
||||
_sessions[sessionName].Walls.TryAdd(tile.ToString(), tile);
|
||||
await Clients.Group(sessionName).SendAsync("NewWall", tile);
|
||||
}
|
||||
|
||||
public async Task NewOverlay(string sessionName, Overlay tile)
|
||||
{
|
||||
var existingTile = _sessions[sessionName].Overlays.TryGetValue(tile.ToString(), out var t);
|
||||
if (existingTile)
|
||||
{
|
||||
_sessions[sessionName].Overlays.TryRemove(t.ToString(), out var rtile);
|
||||
|
||||
}
|
||||
_sessions[sessionName].Overlays.TryAdd(tile.ToString(), tile);
|
||||
await Clients.Group(sessionName).SendAsync("NewOverlay", tile);
|
||||
}
|
||||
|
||||
public async Task DeleteTile(string sessionName, Tile tile)
|
||||
{
|
||||
_sessions[sessionName].Map.TryRemove(tile.ToString(), out var rtile);
|
||||
|
||||
|
||||
await Clients.Group(sessionName).SendAsync("DeleteTile", tile);
|
||||
}
|
||||
|
||||
public async Task DeleteWall(string sessionName, Wall tile)
|
||||
{
|
||||
_sessions[sessionName].Walls.TryRemove(tile.ToString(), out var rtile);
|
||||
|
||||
|
||||
await Clients.Group(sessionName).SendAsync("DeleteWall", tile);
|
||||
}
|
||||
|
||||
public async Task DeleteOverlay(string sessionName, Overlay tile)
|
||||
{
|
||||
_sessions[sessionName].Overlays.TryRemove(tile.ToString(), out var rtile);
|
||||
|
||||
|
||||
await Clients.Group(sessionName).SendAsync("DeleteOverlay", tile);
|
||||
}
|
||||
|
||||
public async Task<SessionData> NewSession(string sessionName, string initials)
|
||||
{
|
||||
var session = new SessionData();
|
||||
session.Colors = new List<string>(Colors);
|
||||
session.Colors.Shuffle();
|
||||
var player = new Player { Position = new Tile { X = 0, Y = 0 }, ConnectionId = Context.ConnectionId, Color = session.Colors[0], Initials = initials };
|
||||
session.Players.Add(player);
|
||||
_sessions.Add(sessionName, session);
|
||||
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, sessionName);
|
||||
return session;
|
||||
}
|
||||
|
||||
public async Task<SessionData> JoinSession(string sessionName, string initials)
|
||||
{
|
||||
if (_sessions.ContainsKey(sessionName))
|
||||
{
|
||||
var session = _sessions[sessionName];
|
||||
var player = new Player { Position = new Tile { X = 0, Y = 0 }, ConnectionId = Context.ConnectionId, Color = session.Colors[session.Players.Count], Initials = initials };
|
||||
session.Players.Add(player);
|
||||
await Clients.Group(sessionName).SendAsync("NewPlayer", player);
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, sessionName);
|
||||
return session;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdatePosition(string sessionName, Tile tile)
|
||||
{
|
||||
var player = _sessions[sessionName].Players.First(m => m.ConnectionId == Context.ConnectionId);
|
||||
player.Position = tile;
|
||||
await Clients.Group(sessionName).SendAsync("PlayerUpdate", player);
|
||||
|
||||
}
|
||||
|
||||
public async Task<SessionData> Refresh(string sessionName)
|
||||
{
|
||||
return _sessions[sessionName];
|
||||
}
|
||||
|
||||
public async Task Sync(string sessionName, SessionData map)
|
||||
{
|
||||
_sessions[sessionName].Map = map.Map;
|
||||
_sessions[sessionName].Overlays = map.Overlays;
|
||||
_sessions[sessionName].Walls = map.Walls;
|
||||
|
||||
await Clients.Group(sessionName).SendAsync("UpdateMap", _sessions[sessionName]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExtensionMethods
|
||||
{
|
||||
private static Random rng = new Random();
|
||||
|
||||
public static void Shuffle<T>(this IList<T> list)
|
||||
{
|
||||
int n = list.Count;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = rng.Next(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
@page
|
||||
@model ErrorModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
|
@ -1,32 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace SignalRChat.Pages
|
||||
{
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public class ErrorModel : PageModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
private readonly ILogger<ErrorModel> _logger;
|
||||
|
||||
public ErrorModel(ILogger<ErrorModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
@page
|
||||
<div class="container">
|
||||
<div class="row"> </div>
|
||||
<div class="row">
|
||||
<div class="col-2">User</div>
|
||||
<div class="col-4"><input type="text" id="userInput" /></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-2">Message</div>
|
||||
<div class="col-4"><input type="text" id="messageInput" /></div>
|
||||
</div>
|
||||
<div class="row"> </div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<input type="button" id="sendButton" value="Send Message" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<ul id="messagesList"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<script src="~/js/signalr/dist/browser/signalr.js"></script>
|
||||
<script src="~/js/chat.js"></script>
|
|
@ -1,25 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace SignalRChat.Pages
|
||||
{
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
private readonly ILogger<IndexModel> _logger;
|
||||
|
||||
public IndexModel(ILogger<IndexModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
@page
|
||||
@model PrivacyModel
|
||||
@{
|
||||
ViewData["Title"] = "Privacy Policy";
|
||||
}
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
|
||||
<p>Use this page to detail your site's privacy policy.</p>
|
|
@ -1,24 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace SignalRChat.Pages
|
||||
{
|
||||
public class PrivacyModel : PageModel
|
||||
{
|
||||
private readonly ILogger<PrivacyModel> _logger;
|
||||
|
||||
public PrivacyModel(ILogger<PrivacyModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - SignalRChat</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="~/css/site.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" asp-area="" asp-page="/Index">SignalRChat</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer class="border-top footer text-muted">
|
||||
<div class="container">
|
||||
© 2020 - SignalRChat - <a asp-area="" asp-page="/Privacy">Privacy</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
|
@ -1,2 +0,0 @@
|
|||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
|
@ -1,3 +0,0 @@
|
|||
@using SignalRChat
|
||||
@namespace SignalRChat.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue