From b5477d529fbd6a7dba3bda5bdc494aff6de1c5ac Mon Sep 17 00:00:00 2001 From: Sergey Karmanov Date: Sun, 24 Dec 2023 04:16:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CyberBoom/AuthOptions.cs | 13 + CyberBoom/BearerAccessTokenOptions.cs | 52 ++ CyberBoom/Consts.cs | 4 + CyberBoom/Controllers/MeetingsController.cs | 72 +++ CyberBoom/Controllers/QuestionsController.cs | 63 ++ CyberBoom/Controllers/ReactionsController.cs | 60 ++ CyberBoom/Controllers/ReviewsController.cs | 73 +++ CyberBoom/Controllers/UserController.cs | 547 +++--------------- .../UserWriteToMetingController.cs | 114 ++++ CyberBoom/CyberBoom.sln | 25 - CyberBoom/DataHelpers.cs | 86 +++ CyberBoom/DbContext/Achievment.cs | 13 + CyberBoom/DbContext/ApplicationContext.cs | 47 ++ .../DbContext/Dtos/Meetings/PostMeetingDto.cs | 31 + .../DbContext/Dtos/Meetings/PutMeetingDto.cs | 33 ++ .../Dtos/Questions/PostQuestionDto.cs | 8 + .../Dtos/Questions/PutQuestionDto.cs | 6 + .../Dtos/Reaction/PostReactionDto.cs | 9 + .../DbContext/Dtos/Review/PostReviewDto.cs | 14 + .../DbContext/Dtos/Review/PutReviewDto.cs | 12 + .../Dtos/User/PostUserWriteToMetingDto.cs | 6 + CyberBoom/DbContext/Dtos/User/StatsData.cs | 17 + CyberBoom/DbContext/Dtos/User/UserPost.cs | 12 + CyberBoom/DbContext/Dtos/User/UserPut.cs | 14 + CyberBoom/DbContext/Meeting.cs | 37 ++ CyberBoom/DbContext/Question.cs | 15 + CyberBoom/DbContext/Reaction.cs | 13 + CyberBoom/DbContext/Review.cs | 21 + CyberBoom/DbContext/User.cs | 341 ----------- CyberBoom/DbContext/UserWriteToMeting.cs | 11 + CyberBoom/Program.cs | 171 ++---- 31 files changed, 973 insertions(+), 967 deletions(-) create mode 100644 CyberBoom/AuthOptions.cs create mode 100644 CyberBoom/BearerAccessTokenOptions.cs create mode 100644 CyberBoom/Consts.cs create mode 100644 CyberBoom/Controllers/MeetingsController.cs create mode 100644 CyberBoom/Controllers/QuestionsController.cs create mode 100644 CyberBoom/Controllers/ReactionsController.cs create mode 100644 CyberBoom/Controllers/ReviewsController.cs create mode 100644 CyberBoom/Controllers/UserWriteToMetingController.cs delete mode 100644 CyberBoom/CyberBoom.sln create mode 100644 CyberBoom/DataHelpers.cs create mode 100644 CyberBoom/DbContext/Achievment.cs create mode 100644 CyberBoom/DbContext/ApplicationContext.cs create mode 100644 CyberBoom/DbContext/Dtos/Meetings/PostMeetingDto.cs create mode 100644 CyberBoom/DbContext/Dtos/Meetings/PutMeetingDto.cs create mode 100644 CyberBoom/DbContext/Dtos/Questions/PostQuestionDto.cs create mode 100644 CyberBoom/DbContext/Dtos/Questions/PutQuestionDto.cs create mode 100644 CyberBoom/DbContext/Dtos/Reaction/PostReactionDto.cs create mode 100644 CyberBoom/DbContext/Dtos/Review/PostReviewDto.cs create mode 100644 CyberBoom/DbContext/Dtos/Review/PutReviewDto.cs create mode 100644 CyberBoom/DbContext/Dtos/User/PostUserWriteToMetingDto.cs create mode 100644 CyberBoom/DbContext/Dtos/User/StatsData.cs create mode 100644 CyberBoom/DbContext/Dtos/User/UserPost.cs create mode 100644 CyberBoom/DbContext/Dtos/User/UserPut.cs create mode 100644 CyberBoom/DbContext/Meeting.cs create mode 100644 CyberBoom/DbContext/Question.cs create mode 100644 CyberBoom/DbContext/Reaction.cs create mode 100644 CyberBoom/DbContext/Review.cs create mode 100644 CyberBoom/DbContext/UserWriteToMeting.cs diff --git a/CyberBoom/AuthOptions.cs b/CyberBoom/AuthOptions.cs new file mode 100644 index 0000000..87e6aec --- /dev/null +++ b/CyberBoom/AuthOptions.cs @@ -0,0 +1,13 @@ +using System.Text; +using Microsoft.IdentityModel.Tokens; + +public class AuthOptions +{ + public const string ISSUER = "MyAuthServer"; // издатель токена + public const string AUDIENCE = "MyAuthClient"; // потребитель токена + const string KEY = "mysupersecret_secretkey!123"; // ключ для шифрации + + public const int LIFETIME = 1; + public static SymmetricSecurityKey GetSymmetricSecurityKey() => + new SymmetricSecurityKey(Encoding.UTF8.GetBytes(KEY)); +} diff --git a/CyberBoom/BearerAccessTokenOptions.cs b/CyberBoom/BearerAccessTokenOptions.cs new file mode 100644 index 0000000..fb49952 --- /dev/null +++ b/CyberBoom/BearerAccessTokenOptions.cs @@ -0,0 +1,52 @@ +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; + +public class BearerAccessTokenOptions +{ + public string GetBearerToken(List claims) => new JwtSecurityTokenHandler() + .WriteToken( + JwtSecurityToken(claims) + ); + + + public JwtSecurityToken JwtSecurityToken(List claims) => new JwtSecurityToken( + issuer: Issuer, + audience: Audience, + notBefore: DateTime.UtcNow, + claims: claims, + expires: BearerTokenLifeTime, + signingCredentials: SigningCredentials + ); + public string SecurityAlgorithm { get; set; } = SecurityAlgorithms.HmacSha256; + public SigningCredentials SigningCredentials => new SigningCredentials(GetSymmetricSecurityKey, SecurityAlgorithm); + public DateTime BearerTokenLifeTime => DateTime.UtcNow.Add(TimeSpan.FromHours(Lifetime)); + public bool RequiredHttpsMetadata { get; set; } = false; + public string Issuer { get; set; } = "BackendHackathon"; + public string Audience { get; set; } = "BackendHackathon"; + public string Key { get; set; } = "5XGgEtGK9jsNxIUQvxef7wtAE6LwbLWd6LFwpryYoF9w=="; + public int Lifetime { get; set; } = 100; + public SymmetricSecurityKey GetSymmetricSecurityKey => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Key)); + + public TokenValidationParameters TokenValidationParameters => new() + { + // укзывает, будет ли валидироваться издатель при валидации токена + ValidateIssuer = true, + // строка, представляющая издателя + ValidIssuer = Issuer, + + // будет ли валидироваться потребитель токена + ValidateAudience = true, + // установка потребителя токена + ValidAudience = Audience, + // будет ли валидироваться время существования + ValidateLifetime = true, + + ClockSkew = TimeSpan.Zero, + // установка ключа безопасности + IssuerSigningKey = GetSymmetricSecurityKey, + // валидация ключа безопасности + ValidateIssuerSigningKey = true, + }; +} \ No newline at end of file diff --git a/CyberBoom/Consts.cs b/CyberBoom/Consts.cs new file mode 100644 index 0000000..a34fac3 --- /dev/null +++ b/CyberBoom/Consts.cs @@ -0,0 +1,4 @@ +public static class Consts +{ + public const char TOKENS_SEPORATOR = ';'; +} diff --git a/CyberBoom/Controllers/MeetingsController.cs b/CyberBoom/Controllers/MeetingsController.cs new file mode 100644 index 0000000..5baa7f4 --- /dev/null +++ b/CyberBoom/Controllers/MeetingsController.cs @@ -0,0 +1,72 @@ +using Mapster; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace CyberBoom.Controllers; + +[Authorize] +[ApiController] +[Route("/api/[controller]")] +public class MeetingsController : ControllerBase +{ + private readonly ApplicationContext _applicationContext; + + public MeetingsController(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + [HttpPost] + public async Task Post([FromForm] PostMeetingDto meeting) + { + await meeting.SpeackerImage.WriteFilesToDirectory(); + await meeting.PlaceImages.WriteFilesToDirectory(); + var meetingWrite = meeting.Adapt(); + + meetingWrite.SpeackerImage = meeting.SpeackerImage.JoinFileNames(); + meetingWrite.PlaceImages = meeting.PlaceImages.JoinFileNames(); + + await _applicationContext.Meetings.AddAsync(meetingWrite); + + await _applicationContext.SaveChangesAsync(); + + return Ok(new { meetingWrite.Id }); + } + + [HttpPut] + public async Task Put([FromForm] PutMeetingDto meeting) + { + await meeting.SpeackerImage.WriteFilesToDirectory(); + await meeting.PlaceImages.WriteFilesToDirectory(); + + var meetingWrite = meeting.Adapt(); + + meetingWrite.SpeackerImage = meeting.SpeackerImage.JoinFileNames(); + meetingWrite.PlaceImages = meeting.PlaceImages.JoinFileNames(); + + var findedMeeting = await _applicationContext.Meetings.FirstAsync(s => s.Id == meeting.Id); + findedMeeting = meetingWrite; + + await _applicationContext.SaveChangesAsync(); + return Ok(); + } + + [AllowAnonymous] + [HttpGet] + public async Task Get(string id) + { + var meeting = await _applicationContext.Meetings.FirstOrDefaultAsync(s => s.Id == id); + + return Ok(meeting); + } + + [AllowAnonymous] + [HttpGet("list")] + public IActionResult GetList(int offset, int limit) + { + var meetings = _applicationContext.Meetings.AsNoTracking().Skip(offset).Take(limit); + + return Ok(meetings); + } +} diff --git a/CyberBoom/Controllers/QuestionsController.cs b/CyberBoom/Controllers/QuestionsController.cs new file mode 100644 index 0000000..341b023 --- /dev/null +++ b/CyberBoom/Controllers/QuestionsController.cs @@ -0,0 +1,63 @@ +using Mapster; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace CyberBoom.Controllers; + +[ApiController] +[Route("/api/[controller]")] +public class QuestionsController : ControllerBase +{ + private readonly ApplicationContext _applicationContext; + + public QuestionsController(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + [Authorize] + [HttpPost] + public async Task Post([FromBody] PostQuestionDto question) + { + var dbWr = question.Adapt(); + await _applicationContext.Questions.AddAsync(dbWr); + + await _applicationContext.SaveChangesAsync(); + + return Ok(new { dbWr.Id }); + } + + [Authorize] + [HttpPut] + public async Task Put([FromBody] PutQuestionDto question) + { + var fReview = await _applicationContext.Questions.FirstAsync(r => r.Id == question.Id); + + fReview.Text = question.Text; + + await _applicationContext.SaveChangesAsync(); + + return Ok(); + } + + [HttpGet] + public async Task Get(string id) + { + var question = await _applicationContext.Questions.FirstAsync(s => s.Id == id); + + return Ok(question); + } + + [HttpGet("list")] + public IActionResult GetList(int offset, int limit) + { + var questions = _applicationContext.Questions + .AsNoTracking() + .Include(c => c.User) + .Skip(offset) + .Take(limit); + + return Ok(questions); + } +} diff --git a/CyberBoom/Controllers/ReactionsController.cs b/CyberBoom/Controllers/ReactionsController.cs new file mode 100644 index 0000000..8c7acfb --- /dev/null +++ b/CyberBoom/Controllers/ReactionsController.cs @@ -0,0 +1,60 @@ +using Mapster; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace CyberBoom.Controllers; + +[ApiController] +[Route("/api/[controller]")] +public class ReactionsController : ControllerBase +{ + private readonly ApplicationContext _applicationContext; + + public ReactionsController(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + [Authorize] + [HttpPost] + public async Task Post([FromBody] PostReactionDto reaction) + { + var dbWr = reaction.Adapt(); + + await _applicationContext.Reactions.AddAsync(dbWr); + + await _applicationContext.SaveChangesAsync(); + + return Ok(new { dbWr.Id }); + } + + [Authorize] + [HttpDelete] + public async Task Delete(string id) + { + var fReview = await _applicationContext.Reactions.FirstAsync(r => r.Id == id); + + _applicationContext.Reactions.Remove(fReview); + + await _applicationContext.SaveChangesAsync(); + + return Ok(); + } + + [HttpGet] + public async Task Get(string id) + { + var reaction = await _applicationContext.Reactions.FirstAsync(s => s.Id == id); + + return Ok(reaction); + } + + [HttpGet("list")] + public IActionResult GetList(int offset, int limit) + { + var reactions = _applicationContext.Reactions.AsNoTracking().Skip(offset).Take(limit); + + return Ok(reactions); + } +} diff --git a/CyberBoom/Controllers/ReviewsController.cs b/CyberBoom/Controllers/ReviewsController.cs new file mode 100644 index 0000000..04848e2 --- /dev/null +++ b/CyberBoom/Controllers/ReviewsController.cs @@ -0,0 +1,73 @@ +using Mapster; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace CyberBoom.Controllers; + +[ApiController] +[Route("/api/[controller]")] +public class ReviewsController : ControllerBase +{ + private readonly ApplicationContext _applicationContext; + + public ReviewsController(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + + [Authorize] + [HttpPost] + public async Task Post([FromBody] PostReviewDto review) + { + var dbWr = review.Adapt(); + + await _applicationContext.Reviews.AddAsync(dbWr); + + await _applicationContext.SaveChangesAsync(); + + return Ok(new { dbWr.Id }); + } + + + [Authorize] + [HttpPut] + public async Task Put([FromBody] PutReviewDto review) + { + var fReview = await _applicationContext.Reviews.FirstAsync(r => r.Id == review.Id); + + fReview.Text = review.Text; + fReview.Score = review.Score; + fReview.Date = review.Date; + + await _applicationContext.SaveChangesAsync(); + + return Ok(); + } + + [HttpGet] + public async Task Get(string id) + { + var review = await _applicationContext.Reviews.FirstAsync(s => s.Id == id); + + var user = await _applicationContext.Users.FirstAsync(s => s.Id == review.UserId); + + return Ok(new { review, user }); + } + + [HttpGet("list")] + public IActionResult GetList(int offset, int limit) + { + var reviews = _applicationContext.Reviews + .AsNoTracking() + .Include(c => c.User) + .Skip(offset) + .Take(limit); + + // var userIds = reviews.Select(u => u.UserId).ToArray(); + // var users = _applicationContext.Users.Where(u => userIds.Contains(u.Id)); + + return Ok(reviews); + } +} diff --git a/CyberBoom/Controllers/UserController.cs b/CyberBoom/Controllers/UserController.cs index e2e5f62..3512392 100644 --- a/CyberBoom/Controllers/UserController.cs +++ b/CyberBoom/Controllers/UserController.cs @@ -1,17 +1,19 @@ using System.IdentityModel.Tokens.Jwt; -using Mapster; +using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Google; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.IdentityModel.Tokens; using static Consts; namespace CyberBoom.Controllers; + + [ApiController] [Route("/api/[controller]")] public class UsersController : ControllerBase @@ -22,18 +24,41 @@ public class UsersController : ControllerBase private readonly RoleManager _roleManager; - public UsersController(ApplicationContext applicationContext, UserManager userManager, RoleManager roleManager) + public UsersController( + ApplicationContext applicationContext, + UserManager userManager, + RoleManager roleManager + ) { _applicationContext = applicationContext; _userManager = userManager; _roleManager = roleManager; } + async Task AddUerToRole(User user, string role) + { + var isExists = await _roleManager.RoleExistsAsync(role); + + if (!isExists) + { + var roleResult = await _roleManager.CreateAsync(new IdentityRole(role)); + if (!roleResult.Succeeded) + throw new Exception("cannot create role"); + } + + var addingRole = await _userManager.AddToRoleAsync(user, role); + + if (!addingRole.Succeeded) + throw new Exception("cannot create role"); + } + + [AllowAnonymous] [HttpPost] - public async Task Post([FromForm]UserPost user) + public async Task Post([FromForm] UserPost user) { await user.Avatar.WriteFileToDirectory(); - var userWr = new User { + var userWr = new User + { AvatarUrl = user.Avatar.FileName, Fio = user.Fio, Specialities = user.Specialities, @@ -41,26 +66,43 @@ public class UsersController : ControllerBase UserName = user.Username }; var result = await _userManager.CreateAsync(userWr); - if(result.Succeeded) - return Ok( - new { - userWr.Id - } - ); - return BadRequest(result.Errors); + + if (!result.Succeeded) + return BadRequest(result.Errors); + + + + var role = user.Username == "moderator" ? "модератор" : "спикер"; + + await AddUerToRole(userWr, role); + + var token = GetToken(userWr, role); + + return Ok(new { userWr.Id, Token = token }); } + string GetToken(User user, string role) + { + var claims = new List + { + new Claim(ClaimsIdentity.DefaultNameClaimType, user.UserName!), + new Claim(ClaimsIdentity.DefaultRoleClaimType, role) + }; + var bOpt = new BearerAccessTokenOptions(); + return bOpt.GetBearerToken(claims); + } + + [Authorize(Roles = "модератор")] [HttpPut] - public async Task Put([FromForm]UserPut user) + public async Task Put([FromForm] UserPut user) { await user.Avatar.WriteFileToDirectory(); - + var fuser = await _userManager.FindByIdAsync(user.Id); - if(fuser is null) + if (fuser is null) throw new Exception("user not found"); - fuser.AvatarUrl = user.Avatar.FileName; fuser.Fio = user.Fio; fuser.Specialities = user.Specialities; @@ -68,500 +110,63 @@ public class UsersController : ControllerBase fuser.UserName = user.Username; var result = await _userManager.UpdateAsync(fuser); - if(result.Succeeded) - return Ok( - - ); + if (result.Succeeded) + return Ok(); return BadRequest(result.Errors); } + [Authorize(Roles = "модератор")] [HttpPost("moderator")] - public async Task PostModerator([FromForm]UserPost user) + public async Task PostModerator([FromForm] UserPost user) { - await user.Avatar.WriteFileToDirectory(); - var userWr = new User { + var userWr = new User + { AvatarUrl = user.Avatar.FileName, Fio = user.Fio, Specialities = user.Specialities, TelegramBotUrl = user.TelegramBotUrl, UserName = user.Username }; - - var result = await _userManager.CreateAsync(userWr); - - if(!result.Succeeded) + + if (!result.Succeeded) return BadRequest(result.Errors); - - var isExists = await _roleManager.RoleExistsAsync("модератор"); - if(!isExists){ - var roleResult = await _roleManager.CreateAsync(new IdentityRole("модератор")); - if(!roleResult.Succeeded) - throw new Exception("cannot create role"); - } - - var addingRole = await _userManager.AddToRoleAsync(userWr, "модератор"); + var role = "модератор"; - if(!addingRole.Succeeded) - throw new Exception("cannot create role"); - - return Ok( - new { - userWr.Id - } - ); + await AddUerToRole(userWr, role); + var token = GetToken(userWr, role); + return Ok(new { userWr.Id, Token = token }); } + [Authorize] [HttpGet] public async Task GetUserData(string id) { var user = await _userManager.FindByIdAsync(id); - if(user is null) + if (user is null) return BadRequest(); var role = await _userManager.GetRolesAsync(user); - return Ok(new { - user, - role - }); + return Ok(new { user, role }); } + [Authorize] [HttpGet("stats")] public async Task GetUserStats(string id) { var user = await _userManager.FindByIdAsync(id); - if(user is null) + if (user is null) return BadRequest(); var stats = await _applicationContext.GetStatistic(id); var achievmnets = _applicationContext.Achievments.Where(c => c.UserId == id); - - - return Ok(new { - Stats = stats, - Achievments = achievmnets - }); - } -} - -[ApiController] -[Route("/api/[controller]")] -public class MeetingsController : ControllerBase -{ - - - - private readonly ApplicationContext _applicationContext; - - public MeetingsController(ApplicationContext applicationContext) - { - - - _applicationContext = applicationContext; - } - - - [HttpPost] - public async Task Post([FromForm]PostMeetingDto meeting) - { - await meeting.SpeackerImage.WriteFilesToDirectory(); - await meeting.PlaceImages.WriteFilesToDirectory(); - var meetingWrite = meeting.Adapt(); - - meetingWrite.SpeackerImage = meeting.SpeackerImage.JoinFileNames(); - meetingWrite.PlaceImages = meeting.PlaceImages.JoinFileNames(); - - await _applicationContext.Meetings.AddAsync(meetingWrite); - - await _applicationContext.SaveChangesAsync(); - - - return Ok(new { - meetingWrite.Id - }); - } - - [HttpPut] - public async Task Put([FromForm]PutMeetingDto meeting) - { - await meeting.SpeackerImage.WriteFilesToDirectory(); - await meeting.PlaceImages.WriteFilesToDirectory(); - - var meetingWrite = meeting.Adapt(); - - meetingWrite.SpeackerImage = meeting.SpeackerImage.JoinFileNames(); - meetingWrite.PlaceImages = meeting.PlaceImages.JoinFileNames(); - - var findedMeeting = await _applicationContext.Meetings.FirstAsync(s => s.Id == meeting.Id); - findedMeeting = meetingWrite; - - await _applicationContext.SaveChangesAsync(); - return Ok(); - } - - [HttpGet] - public async Task Get(string id) - { - var meeting = await _applicationContext.Meetings.FirstOrDefaultAsync(s => s.Id == id); - - - return Ok(meeting); - } - - - [HttpGet("list")] - public IActionResult GetList(int offset, int limit) - { - var meetings = _applicationContext.Meetings.AsNoTracking().Skip(offset).Take(limit); - - - return Ok(meetings); + return Ok(new { Stats = stats, Achievments = achievmnets }); } } - - - -[ApiController] -[Route("/api/[controller]")] -public class ReviewsController : ControllerBase -{ - - private readonly ApplicationContext _applicationContext; - - public ReviewsController(ApplicationContext applicationContext) - { - _applicationContext = applicationContext; - } - - - [HttpPost] - public async Task Post([FromBody] PostReviewDto review) - { - var dbWr = review.Adapt(); - - await _applicationContext.Reviews.AddAsync(dbWr); - - await _applicationContext.SaveChangesAsync(); - - return Ok(new { - dbWr.Id - }); - } - - [HttpPut] - public async Task Put([FromBody]PutReviewDto review) - { - - var fReview = await _applicationContext.Reviews.FirstAsync(r => r.Id == review.Id); - - - fReview.Text = review.Text; - fReview.Score = review.Score; - fReview.Date = review.Date; - - - await _applicationContext.SaveChangesAsync(); - - return Ok(); - } - - [HttpGet] - public async Task Get(string id) - { - var review = await _applicationContext.Reviews.FirstAsync(s => s.Id == id); - - var user = await _applicationContext.Users.FirstAsync(s => s.Id == review.UserId); - - return Ok(new { - review, - user - }); - } - - - [HttpGet("list")] - public IActionResult GetList(int offset, int limit) - { - var reviews = _applicationContext.Reviews.AsNoTracking() - .Include(c => c.User) - .Skip(offset) - .Take(limit); - - // var userIds = reviews.Select(u => u.UserId).ToArray(); - // var users = _applicationContext.Users.Where(u => userIds.Contains(u.Id)); - - return Ok( - reviews - ); - } -} - - - -[ApiController] -[Route("/api/[controller]")] -public class QuestionsController : ControllerBase -{ - - private readonly ApplicationContext _applicationContext; - - public QuestionsController(ApplicationContext applicationContext) - { - _applicationContext = applicationContext; - } - - - [HttpPost] - public async Task Post([FromBody] PostQuestionDto question) - { - var dbWr = question.Adapt(); - await _applicationContext.Questions.AddAsync(dbWr); - - await _applicationContext.SaveChangesAsync(); - - return Ok(new { - dbWr.Id - }); - } - - [HttpPut] - public async Task Put([FromBody]PutQuestionDto question) - { - - var fReview = await _applicationContext.Questions.FirstAsync(r => r.Id == question.Id); - - fReview.Text = question.Text; - - await _applicationContext.SaveChangesAsync(); - - return Ok(); - } - - [HttpGet] - public async Task Get(string id) - { - var question = await _applicationContext.Questions - .FirstAsync(s => s.Id == id); - - - return Ok(question); - } - - - [HttpGet("list")] - public IActionResult GetList(int offset, int limit) - { - var questions = _applicationContext.Questions.AsNoTracking() - .Include(c => c.User) - .Skip(offset) - .Take(limit); - - - return Ok(questions); - } -} - - - -[ApiController] -[Route("/api/[controller]")] -public class ReactionsController : ControllerBase -{ - - private readonly ApplicationContext _applicationContext; - - public ReactionsController(ApplicationContext applicationContext) - { - _applicationContext = applicationContext; - } - - - [HttpPost] - public async Task Post([FromBody] PostReactionDto reaction) - { - var dbWr = reaction.Adapt(); - - await _applicationContext.Reactions.AddAsync(dbWr); - - await _applicationContext.SaveChangesAsync(); - - return Ok(new { - dbWr.Id - }); - } - - [HttpDelete] - public async Task Delete(string id) - { - - var fReview = await _applicationContext.Reactions.FirstAsync(r => r.Id == id); - - - _applicationContext.Reactions.Remove(fReview); - - - await _applicationContext.SaveChangesAsync(); - - return Ok(); - } - - [HttpGet] - public async Task Get(string id) - { - var reaction = await _applicationContext.Reactions - .FirstAsync(s => s.Id == id); - - - return Ok(reaction); - } - - - [HttpGet("list")] - public IActionResult GetList(int offset, int limit) - { - var reactions = _applicationContext.Reactions.AsNoTracking() - .Skip(offset) - .Take(limit); - - - return Ok(reactions); - } -} - - - - - -[ApiController] -[Route("/api/users/meetings")] -public class UserWriteToMetingController : ControllerBase -{ - - private readonly ApplicationContext _applicationContext; - - public UserWriteToMetingController(ApplicationContext applicationContext) - { - _applicationContext = applicationContext; - } - - - [HttpPost] - public async Task Post([FromBody] PostUserWriteToMetingDto write) - { - var dbWr = write.Adapt(); - - var meeting = await _applicationContext.Meetings.FirstAsync(m => m.Id == write.MeetingId); - - - - if(DateTime.UtcNow > meeting.Time) - return BadRequest(); - - - - await _applicationContext.UserWriteToMetings.AddAsync(dbWr); - - - var user = await _applicationContext.Users.FirstAsync(u => u.Id == write.UserId); - - - var newStats = await _applicationContext.GetStatistic(write.UserId); - - - var achievments = await WriteAchievment(newStats, write.UserId); - - - await _applicationContext.SaveChangesAsync(); - - return Ok(new { - dbWr.Id, - Achievments = achievments - }); - - } - - async Task> WriteAchievment(StatsData stats, string userId) - { - List achievments = new List(); - if(stats.Count > 0 && stats.Count % 5 == 0) - { - var achievment = new Achievment{ - Name = $"Редстоун Наблюдатель Level {stats.Count / 5}", - Text = "Вы cамый настоящий Редстоун Наблюдатель из игры Майнкрафт, который не пропускате ни единой всречи!", - UserId = userId - }; - achievments.Add(achievment); - await _applicationContext.Achievments.AddAsync( - achievment - ); - } - var achievedTags = stats.StatsByTag.Where(st => st.Count > 0 && st.Count % 5 == 0); - if(achievedTags.Count() > 0 && stats.Count % 5 == 0) - { - - foreach(var tag in achievedTags) - { - var achievment = new Achievment{ - Name = $"Вкачаю все в {tag.Tag} Level {tag.Count / 5}", - Text = $"Вы нежалеете очки времени на ветку {tag.Tag}, продолжайте в том же духе!", - UserId = userId - }; - achievments.Add(achievment); - await _applicationContext.Achievments.AddAsync( - achievment - ); - } - - } - return achievments; - } - - [HttpDelete] - public async Task Delete(string id) - { - var fReview = await _applicationContext.UserWriteToMetings.FirstAsync(r => r.Id == id); - - - var meeting = await _applicationContext.Meetings.FirstAsync(m => m.Id == fReview.MeetingId); - - if(DateTime.UtcNow > meeting.Time) - return BadRequest(); - - _applicationContext.UserWriteToMetings.Remove(fReview); - - - await _applicationContext.SaveChangesAsync(); - - return Ok(); - } - - [HttpGet] - public async Task Get(string id) - { - var review = await _applicationContext.UserWriteToMetings - .FirstAsync(s => s.Id == id); - - - return Ok(review); - } - - - [HttpGet("list")] - public IActionResult GetList(int offset, int limit) - { - var reviews = _applicationContext.UserWriteToMetings.AsNoTracking() - .Skip(offset) - .Take(limit); - - - return Ok(reviews); - } -} - diff --git a/CyberBoom/Controllers/UserWriteToMetingController.cs b/CyberBoom/Controllers/UserWriteToMetingController.cs new file mode 100644 index 0000000..c5f2c00 --- /dev/null +++ b/CyberBoom/Controllers/UserWriteToMetingController.cs @@ -0,0 +1,114 @@ +using Mapster; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace CyberBoom.Controllers; + +[ApiController] +[Route("/api/users/meetings")] +public class UserWriteToMetingController : ControllerBase +{ + private readonly ApplicationContext _applicationContext; + + public UserWriteToMetingController(ApplicationContext applicationContext) + { + _applicationContext = applicationContext; + } + + + [Authorize] + [HttpPost] + public async Task Post([FromBody] PostUserWriteToMetingDto write) + { + var dbWr = write.Adapt(); + + var meeting = await _applicationContext.Meetings.FirstAsync(m => m.Id == write.MeetingId); + + if (DateTime.UtcNow > meeting.Time) + return BadRequest(); + + await _applicationContext.UserWriteToMetings.AddAsync(dbWr); + + var user = await _applicationContext.Users.FirstAsync(u => u.Id == write.UserId); + + var newStats = await _applicationContext.GetStatistic(write.UserId); + + var achievments = await WriteAchievment(newStats, write.UserId); + + await _applicationContext.SaveChangesAsync(); + + return Ok(new { dbWr.Id, Achievments = achievments }); + } + + async Task> WriteAchievment(StatsData stats, string userId) + { + List achievments = new List(); + if (stats.Count > 0 && stats.Count % 5 == 0) + { + var achievment = new Achievment + { + Name = $"Редстоун Наблюдатель Level {stats.Count / 5}", + Text = + "Вы cамый настоящий Редстоун Наблюдатель из игры Майнкрафт, который не пропускате ни единой всречи!", + UserId = userId + }; + achievments.Add(achievment); + await _applicationContext.Achievments.AddAsync(achievment); + } + var achievedTags = stats.StatsByTag.Where(st => st.Count > 0 && st.Count % 5 == 0); + if (achievedTags.Count() > 0 && stats.Count % 5 == 0) + { + foreach (var tag in achievedTags) + { + var achievment = new Achievment + { + Name = $"Вкачаю все в {tag.Tag} Level {tag.Count / 5}", + Text = + $"Вы нежалеете очки времени на ветку {tag.Tag}, продолжайте в том же духе!", + UserId = userId + }; + achievments.Add(achievment); + await _applicationContext.Achievments.AddAsync(achievment); + } + } + return achievments; + } + + [Authorize] + [HttpDelete] + public async Task Delete(string id) + { + var fReview = await _applicationContext.UserWriteToMetings.FirstAsync(r => r.Id == id); + + var meeting = await _applicationContext.Meetings.FirstAsync(m => m.Id == fReview.MeetingId); + + if (DateTime.UtcNow > meeting.Time) + return BadRequest(); + + _applicationContext.UserWriteToMetings.Remove(fReview); + + await _applicationContext.SaveChangesAsync(); + + return Ok(); + } + + [HttpGet] + public async Task Get(string id) + { + var review = await _applicationContext.UserWriteToMetings.FirstAsync(s => s.Id == id); + + return Ok(review); + } + + [HttpGet("list")] + public IActionResult GetList(int offset, int limit) + { + var reviews = _applicationContext.UserWriteToMetings + .AsNoTracking() + .Skip(offset) + .Take(limit); + + return Ok(reviews); + } +} diff --git a/CyberBoom/CyberBoom.sln b/CyberBoom/CyberBoom.sln deleted file mode 100644 index 886f16b..0000000 --- a/CyberBoom/CyberBoom.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CyberBoom", "CyberBoom.csproj", "{A8F9D7D8-DDA5-479B-8735-00E706A5827F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A8F9D7D8-DDA5-479B-8735-00E706A5827F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A8F9D7D8-DDA5-479B-8735-00E706A5827F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A8F9D7D8-DDA5-479B-8735-00E706A5827F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A8F9D7D8-DDA5-479B-8735-00E706A5827F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {674E1E42-AF3F-4DDA-A4F1-EDD84025DB24} - EndGlobalSection -EndGlobal diff --git a/CyberBoom/DataHelpers.cs b/CyberBoom/DataHelpers.cs new file mode 100644 index 0000000..e6b5d4f --- /dev/null +++ b/CyberBoom/DataHelpers.cs @@ -0,0 +1,86 @@ +using Microsoft.EntityFrameworkCore; +using static Consts; + +public static class DataHelpers +{ + public static string JoinFileNames(this IEnumerable files) => files.Select(s => s.FileName).JoinStrings(); + + public static string JoinStrings(this IEnumerable files) => String.Join(TOKENS_SEPORATOR, files.Select(s => s)); + + public static TimeSpan ParseDuration(this string duration) + { + var durArr = duration.Split(TOKENS_SEPORATOR, StringSplitOptions.RemoveEmptyEntries); + + var hours = int.Parse(durArr.First()); + var minutes = int.Parse(durArr[1]); + + return new TimeSpan(hours, minutes, 0); + + } + + public static async Task WriteFileToDirectory(this IFormFile file) + { + var readStream = file.OpenReadStream(); + var memstream = new MemoryStream(); + await readStream.CopyToAsync(memstream); + await File.WriteAllBytesAsync(Path.Combine("cyber-boom-files", file.FileName), memstream.ToArray()); + } + + public static async Task WriteFilesToDirectory(this IEnumerable files) + { + foreach(var file in files) + { + await file.WriteFileToDirectory(); + } + + } + + public static async Task GetStatistic(this ApplicationContext applicationContext, string id) + { + var specialities = await applicationContext.UserWriteToMetings.Where(c => c.UserId == id) + .Join(applicationContext.Meetings, + m => m.MeetingId, + m => m.Id, + (c,m) => new { + m.Tags, + m.Id, + m.Duration, + m.Time + } + ).Where(t => DateTime.UtcNow > t.Time).ToArrayAsync(); + + var selectedSpecialities = specialities.Select(s => new { + s.Id, + Tags = s.Tags.Split(TOKENS_SEPORATOR, StringSplitOptions.RemoveEmptyEntries), + Duration = s.Duration.ParseDuration().TotalHours + }); + + var allTags = selectedSpecialities.SelectMany(s => s.Tags).Distinct(); + var count = selectedSpecialities.Count(); + + StatsData stats = new StatsData{ + Count = count, + Hours = selectedSpecialities.Sum(m => m.Duration) * count + + }; + foreach(var tag in allTags) + { + //StatsData.TagStats + var specByTag = selectedSpecialities.Where(f => f.Tags.Contains(tag)); + var countByTag = specByTag.Count(); + var hours = selectedSpecialities.Sum(s => s.Duration) * countByTag; + + var stat = new StatsData.TagStats + { + Count = countByTag, + Tag = tag, + Hours = hours + }; + stats.StatsByTag.Add(stat); + } + + + + return stats; + } +} diff --git a/CyberBoom/DbContext/Achievment.cs b/CyberBoom/DbContext/Achievment.cs new file mode 100644 index 0000000..262d58f --- /dev/null +++ b/CyberBoom/DbContext/Achievment.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations.Schema; + +public class Achievment +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public string Id { get; set; } = null!; + + public string UserId { get; set; } = null!; + + public string Name { get; set; } = null!; + + public string Text { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/ApplicationContext.cs b/CyberBoom/DbContext/ApplicationContext.cs new file mode 100644 index 0000000..88b985b --- /dev/null +++ b/CyberBoom/DbContext/ApplicationContext.cs @@ -0,0 +1,47 @@ +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +public class ApplicationContext : IdentityDbContext +{ + public DbSet Meetings { get; set; } + + public DbSet Reviews { get; set; } + + public DbSet Reactions { get; set; } + + public DbSet UserWriteToMetings { get; set; } + + public DbSet Questions { get; set; } + + + public DbSet Achievments { get; set; } + + public ApplicationContext(DbContextOptions options) + : base(options) + { + Database.EnsureCreated(); + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + builder.Entity().HasMany().WithOne().HasForeignKey(c => c.MeetingId); + builder.Entity().HasMany().WithOne().HasForeignKey(c => c.MeetingId); + + + + + builder.Entity().HasOne().WithMany().HasForeignKey(c => c.UserId); + builder.Entity().HasOne(c => c.User).WithMany().HasForeignKey(c => c.UserId); + + builder.Entity().HasOne().WithMany().HasForeignKey(c => c.MeetingId); + builder.Entity().HasOne(c => c.User).WithMany().HasForeignKey(c => c.UserId); + + builder.Entity().HasOne().WithMany().HasForeignKey(c => c.QuestionId); + + builder.Entity().HasMany().WithOne().HasForeignKey(c => c.MeetingId); + builder.Entity().HasOne().WithMany().HasForeignKey(c => c.UserId); + + builder.Entity().HasOne().WithMany().HasForeignKey(c => c.UserId); + } +} \ No newline at end of file diff --git a/CyberBoom/DbContext/Dtos/Meetings/PostMeetingDto.cs b/CyberBoom/DbContext/Dtos/Meetings/PostMeetingDto.cs new file mode 100644 index 0000000..cf3dddf --- /dev/null +++ b/CyberBoom/DbContext/Dtos/Meetings/PostMeetingDto.cs @@ -0,0 +1,31 @@ +public class PostMeetingDto +{ + public DateTime Time { get; set; } + + public string Title { get; set; } = null!; + + public string Theme { get; set; } = null!; + + public string SpeakerName { get; set; } = null!; + + public IEnumerable SpeackerImage { get; set; } = null!; + + public IEnumerable PlaceImages { get; set; } = null!; + + + public string Splecializations { get; set; } = null!; + + public string Type { get; set; } = "онлайн/офлайн"; + + public string SpeakerTelephone { get; set; } = null!; + + public string SpeakerEmail { get; set; } = null!; + + public string Tags { get; set; } = null!; + + public string Urls { get; set; } = null!; + + public string PlaceAdress { get; set; } = null!; + + public string Duration { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Dtos/Meetings/PutMeetingDto.cs b/CyberBoom/DbContext/Dtos/Meetings/PutMeetingDto.cs new file mode 100644 index 0000000..de31e7b --- /dev/null +++ b/CyberBoom/DbContext/Dtos/Meetings/PutMeetingDto.cs @@ -0,0 +1,33 @@ +public class PutMeetingDto +{ + public String Id { get; set; } = null!; + + public DateTime Time { get; set; } + + public string Title { get; set; } = null!; + + public string Theme { get; set; } = null!; + + public string SpeakerName { get; set; } = null!; + + public IEnumerable SpeackerImage { get; set; } = null!; + + public IEnumerable PlaceImages { get; set; } = null!; + + public string Splecializations { get; set; } = null!; + + + public string Type { get; set; } = "онлайн/офлайн"; + + public string SpeakerTelephone { get; set; } = null!; + + public string SpeakerEmail { get; set; } = null!; + + public string Tags { get; set; } = null!; + + public string Urls { get; set; } = null!; + + public string PlaceAdress { get; set; } = null!; + + public string Duration { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Dtos/Questions/PostQuestionDto.cs b/CyberBoom/DbContext/Dtos/Questions/PostQuestionDto.cs new file mode 100644 index 0000000..8c99838 --- /dev/null +++ b/CyberBoom/DbContext/Dtos/Questions/PostQuestionDto.cs @@ -0,0 +1,8 @@ +public class PostQuestionDto +{ + public string Text { get; set; } = null!; + + public string MeetingId { get; set; } = null!; + + public string UserId { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Dtos/Questions/PutQuestionDto.cs b/CyberBoom/DbContext/Dtos/Questions/PutQuestionDto.cs new file mode 100644 index 0000000..6bd2224 --- /dev/null +++ b/CyberBoom/DbContext/Dtos/Questions/PutQuestionDto.cs @@ -0,0 +1,6 @@ +public class PutQuestionDto +{ + public string Id { get; set; } = null!; + + public string Text { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Dtos/Reaction/PostReactionDto.cs b/CyberBoom/DbContext/Dtos/Reaction/PostReactionDto.cs new file mode 100644 index 0000000..fa8c2db --- /dev/null +++ b/CyberBoom/DbContext/Dtos/Reaction/PostReactionDto.cs @@ -0,0 +1,9 @@ +public class PostReactionDto +{ + + public string QuestionId { get; set; } = null!; + + public string UserId { get; set; } = null!; + + public bool IsLike { get; set; } = true; +} diff --git a/CyberBoom/DbContext/Dtos/Review/PostReviewDto.cs b/CyberBoom/DbContext/Dtos/Review/PostReviewDto.cs new file mode 100644 index 0000000..047123b --- /dev/null +++ b/CyberBoom/DbContext/Dtos/Review/PostReviewDto.cs @@ -0,0 +1,14 @@ +public class PostReviewDto +{ + public String MeetingId { get; set; } = null!; + + public String UserId { get; set; } = null!; + + public string Text { get; set; } = null!; + + public int Score { get; set; } = 0; + + DateTime _date; + + public DateTime Date { get => _date; set => _date = value.ToUniversalTime(); } +} diff --git a/CyberBoom/DbContext/Dtos/Review/PutReviewDto.cs b/CyberBoom/DbContext/Dtos/Review/PutReviewDto.cs new file mode 100644 index 0000000..395e0f5 --- /dev/null +++ b/CyberBoom/DbContext/Dtos/Review/PutReviewDto.cs @@ -0,0 +1,12 @@ +public class PutReviewDto +{ + public string Id { get; set; } = null!; + + public string Text { get; set; } = null!; + + public int Score { get; set; } = 0; + + DateTime _date; + + public DateTime Date { get => _date; set => _date = value.ToUniversalTime(); } +} diff --git a/CyberBoom/DbContext/Dtos/User/PostUserWriteToMetingDto.cs b/CyberBoom/DbContext/Dtos/User/PostUserWriteToMetingDto.cs new file mode 100644 index 0000000..541c665 --- /dev/null +++ b/CyberBoom/DbContext/Dtos/User/PostUserWriteToMetingDto.cs @@ -0,0 +1,6 @@ +public class PostUserWriteToMetingDto +{ + public String UserId { get; set; } = null!; + + public String MeetingId { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Dtos/User/StatsData.cs b/CyberBoom/DbContext/Dtos/User/StatsData.cs new file mode 100644 index 0000000..9539ce1 --- /dev/null +++ b/CyberBoom/DbContext/Dtos/User/StatsData.cs @@ -0,0 +1,17 @@ +public class StatsData +{ + public double Hours{ get; set; } + + public int Count { get; set; } + + public List StatsByTag { get; set; } = new List(); + + public class TagStats + { + public string Tag { get; set; } = null!; + + public int Count { get; set; } + + public double Hours { get; set; } + } +} diff --git a/CyberBoom/DbContext/Dtos/User/UserPost.cs b/CyberBoom/DbContext/Dtos/User/UserPost.cs new file mode 100644 index 0000000..1109932 --- /dev/null +++ b/CyberBoom/DbContext/Dtos/User/UserPost.cs @@ -0,0 +1,12 @@ +public class UserPost +{ + public IFormFile Avatar { get; set; } = null!; + + public string Fio { get; set; } = null!; + + public string Username { get; set; } = null!; + + public string Specialities { get; set; } = null!; + + public string TelegramBotUrl { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Dtos/User/UserPut.cs b/CyberBoom/DbContext/Dtos/User/UserPut.cs new file mode 100644 index 0000000..c7850df --- /dev/null +++ b/CyberBoom/DbContext/Dtos/User/UserPut.cs @@ -0,0 +1,14 @@ +public class UserPut +{ + public string Id { get; set; } = null!; + + public IFormFile Avatar { get; set; } = null!; + + public string Fio { get; set; } = null!; + + public string Username { get; set; } = null!; + + public string Specialities { get; set; } = null!; + + public string TelegramBotUrl { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Meeting.cs b/CyberBoom/DbContext/Meeting.cs new file mode 100644 index 0000000..85d1337 --- /dev/null +++ b/CyberBoom/DbContext/Meeting.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations.Schema; + +public class Meeting +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public String Id { get; set; } = null!; + + public DateTime Time { get; set; } + + public string Title { get; set; } = null!; + + public string Theme { get; set; } = null!; + + public string SpeakerName { get; set; } = null!; + + public string SpeackerImage { get; set; } = null!; + + public string Splecializations { get; set; } = null!; //speacker specilization + + + public string Type { get; set; } = "онлайн/офлайн"; + + public string SpeakerTelephone { get; set; } = null!; + + + public string PlaceImages { get; set; } = null!; + + public string SpeakerEmail { get; set; } = null!; + + public string Tags { get; set; } = null!; + + public string Urls { get; set; } = null!; + + public string PlaceAdress { get; set; } = null!; + + public string Duration { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Question.cs b/CyberBoom/DbContext/Question.cs new file mode 100644 index 0000000..59e2f70 --- /dev/null +++ b/CyberBoom/DbContext/Question.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations.Schema; + +public class Question +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public string Id { get; set; } = null!; + + public string Text { get; set; } = null!; + + public string MeetingId { get; set; } = null!; + + public string UserId { get; set; } = null!; + + public User User { get; set; } = null!; +} diff --git a/CyberBoom/DbContext/Reaction.cs b/CyberBoom/DbContext/Reaction.cs new file mode 100644 index 0000000..c3575fc --- /dev/null +++ b/CyberBoom/DbContext/Reaction.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations.Schema; + +public class Reaction +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public string Id { get; set; } = null!; + + public string QuestionId { get; set; } = null!; + + public string UserId { get; set; } = null!; + + public bool IsLike { get; set; } = true; +} diff --git a/CyberBoom/DbContext/Review.cs b/CyberBoom/DbContext/Review.cs new file mode 100644 index 0000000..27722a4 --- /dev/null +++ b/CyberBoom/DbContext/Review.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations.Schema; + +public class Review +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public string Id { get; set; } = null!; + + public string MeetingId { get; set; } = null!; + + public string UserId { get; set; } = null!; + + public string Text { get; set; } = null!; + + public int Score { get; set; } = 0; + + public User User { get; set; } = null!; + + DateTime _date; + + public DateTime Date { get => _date; set => _date = value.ToUniversalTime(); } +} diff --git a/CyberBoom/DbContext/User.cs b/CyberBoom/DbContext/User.cs index 86d060b..d92d86b 100644 --- a/CyberBoom/DbContext/User.cs +++ b/CyberBoom/DbContext/User.cs @@ -1,7 +1,4 @@ using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using System.ComponentModel.DataAnnotations.Schema; public class User : IdentityUser @@ -18,341 +15,3 @@ public class User : IdentityUser } - -public class UserPost -{ - public IFormFile Avatar { get; set; } = null!; - - public string Fio { get; set; } = null!; - - public string Username { get; set; } = null!; - - public string Specialities { get; set; } = null!; - - public string TelegramBotUrl { get; set; } = null!; -} - -public class StatsData -{ - public double Hours{ get; set; } - - public int Count { get; set; } - - public List StatsByTag { get; set; } = new List(); - - public class TagStats - { - public string Tag { get; set; } = null!; - - public int Count { get; set; } - - public double Hours { get; set; } - } -} - -public class UserPut -{ - public string Id { get; set; } = null!; - - public IFormFile Avatar { get; set; } = null!; - - public string Fio { get; set; } = null!; - - public string Username { get; set; } = null!; - - public string Specialities { get; set; } = null!; - - public string TelegramBotUrl { get; set; } = null!; -} - -public class PostMeetingDto -{ - public DateTime Time { get; set; } - - public string Title { get; set; } = null!; - - public string Theme { get; set; } = null!; - - public string SpeakerName { get; set; } = null!; - - public IEnumerable SpeackerImage { get; set; } = null!; - - public IEnumerable PlaceImages { get; set; } = null!; - - - public string Splecializations { get; set; } = null!; - - public string Type { get; set; } = "онлайн/офлайн"; - - public string SpeakerTelephone { get; set; } = null!; - - public string SpeakerEmail { get; set; } = null!; - - public string Tags { get; set; } = null!; - - public string Urls { get; set; } = null!; - - public string PlaceAdress { get; set; } = null!; - - public string Duration { get; set; } = null!; -} - - -public class UserWriteToMeting -{ - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public String Id { get; set; } = null!; - - public String UserId { get; set; } = null!; - - public String MeetingId { get; set; } = null!; -} - - -public class PostUserWriteToMetingDto -{ - public String UserId { get; set; } = null!; - - public String MeetingId { get; set; } = null!; -} - - -public class PutMeetingDto -{ - public String Id { get; set; } = null!; - - public DateTime Time { get; set; } - - public string Title { get; set; } = null!; - - public string Theme { get; set; } = null!; - - public string SpeakerName { get; set; } = null!; - - public IEnumerable SpeackerImage { get; set; } = null!; - - public IEnumerable PlaceImages { get; set; } = null!; - - public string Splecializations { get; set; } = null!; - - - public string Type { get; set; } = "онлайн/офлайн"; - - public string SpeakerTelephone { get; set; } = null!; - - public string SpeakerEmail { get; set; } = null!; - - public string Tags { get; set; } = null!; - - public string Urls { get; set; } = null!; - - public string PlaceAdress { get; set; } = null!; - - public string Duration { get; set; } = null!; -} - - -public class Meeting -{ - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public String Id { get; set; } = null!; - - public DateTime Time { get; set; } - - public string Title { get; set; } = null!; - - public string Theme { get; set; } = null!; - - public string SpeakerName { get; set; } = null!; - - public string SpeackerImage { get; set; } = null!; - - public string Splecializations { get; set; } = null!; //speacker specilization - - - public string Type { get; set; } = "онлайн/офлайн"; - - public string SpeakerTelephone { get; set; } = null!; - - - public string PlaceImages { get; set; } = null!; - - public string SpeakerEmail { get; set; } = null!; - - public string Tags { get; set; } = null!; - - public string Urls { get; set; } = null!; - - public string PlaceAdress { get; set; } = null!; - - public string Duration { get; set; } = null!; -} - - -public class PostReviewDto -{ - public String MeetingId { get; set; } = null!; - - public String UserId { get; set; } = null!; - - public string Text { get; set; } = null!; - - public int Score { get; set; } = 0; - - DateTime _date; - - public DateTime Date { get => _date; set => _date = value.ToUniversalTime(); } -} - - - -public class PutReviewDto -{ - public string Id { get; set; } = null!; - - public string Text { get; set; } = null!; - - public int Score { get; set; } = 0; - - DateTime _date; - - public DateTime Date { get => _date; set => _date = value.ToUniversalTime(); } -} - - -public class PostQuestionDto -{ - public string Text { get; set; } = null!; - - public string MeetingId { get; set; } = null!; - - public string UserId { get; set; } = null!; -} - - -public class PutQuestionDto -{ - public string Id { get; set; } = null!; - - public string Text { get; set; } = null!; -} - -public class Question -{ - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public string Id { get; set; } = null!; - - public string Text { get; set; } = null!; - - public string MeetingId { get; set; } = null!; - - public string UserId { get; set; } = null!; - - public User User { get; set; } = null!; -} - - -public class Review -{ - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public string Id { get; set; } = null!; - - public string MeetingId { get; set; } = null!; - - public string UserId { get; set; } = null!; - - public string Text { get; set; } = null!; - - public int Score { get; set; } = 0; - - public User User { get; set; } = null!; - - DateTime _date; - - public DateTime Date { get => _date; set => _date = value.ToUniversalTime(); } -} - - -public class Achievment -{ - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public string Id { get; set; } = null!; - - public string UserId { get; set; } = null!; - - public string Name { get; set; } = null!; - - public string Text { get; set; } = null!; -} - - -public class PostReactionDto -{ - - public string QuestionId { get; set; } = null!; - - public string UserId { get; set; } = null!; - - public bool IsLike { get; set; } = true; -} - - - -public class Reaction -{ - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public string Id { get; set; } = null!; - - public string QuestionId { get; set; } = null!; - - public string UserId { get; set; } = null!; - - public bool IsLike { get; set; } = true; -} - - - -public class ApplicationContext : IdentityDbContext -{ - public DbSet Meetings { get; set; } - - public DbSet Reviews { get; set; } - - public DbSet Reactions { get; set; } - - public DbSet UserWriteToMetings { get; set; } - - public DbSet Questions { get; set; } - - - public DbSet Achievments { get; set; } - - public ApplicationContext(DbContextOptions options) - : base(options) - { - Database.EnsureCreated(); - } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - builder.Entity().HasMany().WithOne().HasForeignKey(c => c.MeetingId); - builder.Entity().HasMany().WithOne().HasForeignKey(c => c.MeetingId); - - - - - builder.Entity().HasOne().WithMany().HasForeignKey(c => c.UserId); - builder.Entity().HasOne(c => c.User).WithMany().HasForeignKey(c => c.UserId); - - builder.Entity().HasOne().WithMany().HasForeignKey(c => c.MeetingId); - builder.Entity().HasOne(c => c.User).WithMany().HasForeignKey(c => c.UserId); - - builder.Entity().HasOne().WithMany().HasForeignKey(c => c.QuestionId); - - builder.Entity().HasMany().WithOne().HasForeignKey(c => c.MeetingId); - builder.Entity().HasOne().WithMany().HasForeignKey(c => c.UserId); - - builder.Entity().HasOne().WithMany().HasForeignKey(c => c.UserId); - } -} \ No newline at end of file diff --git a/CyberBoom/DbContext/UserWriteToMeting.cs b/CyberBoom/DbContext/UserWriteToMeting.cs new file mode 100644 index 0000000..719fc19 --- /dev/null +++ b/CyberBoom/DbContext/UserWriteToMeting.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations.Schema; + +public class UserWriteToMeting +{ + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public String Id { get; set; } = null!; + + public String UserId { get; set; } = null!; + + public String MeetingId { get; set; } = null!; +} diff --git a/CyberBoom/Program.cs b/CyberBoom/Program.cs index 9351f3b..6ad0586 100644 --- a/CyberBoom/Program.cs +++ b/CyberBoom/Program.cs @@ -1,11 +1,12 @@ -using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; -using Microsoft.IdentityModel.Tokens; using Mapster; using static Consts; using Microsoft.Extensions.FileProviders; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.OpenApi.Models; TypeAdapterConfig.NewConfig().Map(d => d.SpeackerImage, s => s.SpeackerImage.JoinFileNames()); @@ -31,44 +32,51 @@ builder.Services.AddDbContext(options => builder.Services.AddIdentity() .AddEntityFrameworkStores(); -builder.Services.AddAuthorization(); -builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer(options => - { - options.TokenValidationParameters = new TokenValidationParameters - { - // указывает, будет ли валидироваться издатель при валидации токена - ValidateIssuer = true, - // строка, представляющая издателя - ValidIssuer = AuthOptions.ISSUER, - // будет ли валидироваться потребитель токена - ValidateAudience = true, - // установка потребителя токена - ValidAudience = AuthOptions.AUDIENCE, - // будет ли валидироваться время существования - ValidateLifetime = true, - // установка ключа безопасности - IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(), - // валидация ключа безопасности - ValidateIssuerSigningKey = true, - }; - }); -// .AddGoogle(googleOptions => -// { -// googleOptions.ClientId = configuration["Authentication:Google:ClientId"] ?? throw new NullReferenceException(""); -// googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"] ?? throw new NullReferenceException(""); -// }); +builder.Services.AddAuthentication(opt => { + opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; +}) +.AddJwtBearer(options => +{ + var bearerOptions = new BearerAccessTokenOptions(); + options.RequireHttpsMetadata = bearerOptions.RequiredHttpsMetadata; + options.TokenValidationParameters = bearerOptions.TokenValidationParameters; +}); -// builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) -// .AddEntityFrameworkStores(); -// builder.Services.AddRazorPages(); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); + + c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below.", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} + } + }); +}); builder.Services.AddCors(); var app = builder.Build(); @@ -97,100 +105,3 @@ app.MapControllers(); //app.MapRazorPages(); app.Run(); - -public class AuthOptions -{ - public const string ISSUER = "MyAuthServer"; // издатель токена - public const string AUDIENCE = "MyAuthClient"; // потребитель токена - const string KEY = "mysupersecret_secretkey!123"; // ключ для шифрации - public static SymmetricSecurityKey GetSymmetricSecurityKey() => - new SymmetricSecurityKey(Encoding.UTF8.GetBytes(KEY)); -} - -public static class Consts -{ - public const char TOKENS_SEPORATOR = ';'; -} -public static class DataHelpers -{ - public static string JoinFileNames(this IEnumerable files) => files.Select(s => s.FileName).JoinStrings(); - - public static string JoinStrings(this IEnumerable files) => String.Join(TOKENS_SEPORATOR, files.Select(s => s)); - - public static TimeSpan ParseDuration(this string duration) - { - var durArr = duration.Split(TOKENS_SEPORATOR, StringSplitOptions.RemoveEmptyEntries); - - var hours = int.Parse(durArr.First()); - var minutes = int.Parse(durArr[1]); - - return new TimeSpan(hours, minutes, 0); - - } - - public static async Task WriteFileToDirectory(this IFormFile file) - { - var readStream = file.OpenReadStream(); - var memstream = new MemoryStream(); - await readStream.CopyToAsync(memstream); - await File.WriteAllBytesAsync(Path.Combine("cyber-boom-files", file.FileName), memstream.ToArray()); - } - - public static async Task WriteFilesToDirectory(this IEnumerable files) - { - foreach(var file in files) - { - await file.WriteFileToDirectory(); - } - - } - - public static async Task GetStatistic(this ApplicationContext applicationContext, string id) - { - var specialities = await applicationContext.UserWriteToMetings.Where(c => c.UserId == id) - .Join(applicationContext.Meetings, - m => m.MeetingId, - m => m.Id, - (c,m) => new { - m.Tags, - m.Id, - m.Duration, - m.Time - } - ).Where(t => DateTime.UtcNow > t.Time).ToArrayAsync(); - - var selectedSpecialities = specialities.Select(s => new { - s.Id, - Tags = s.Tags.Split(TOKENS_SEPORATOR, StringSplitOptions.RemoveEmptyEntries), - Duration = s.Duration.ParseDuration().TotalHours - }); - - var allTags = selectedSpecialities.SelectMany(s => s.Tags).Distinct(); - var count = selectedSpecialities.Count(); - - StatsData stats = new StatsData{ - Count = count, - Hours = selectedSpecialities.Sum(m => m.Duration) * count - - }; - foreach(var tag in allTags) - { - //StatsData.TagStats - var specByTag = selectedSpecialities.Where(f => f.Tags.Contains(tag)); - var countByTag = specByTag.Count(); - var hours = selectedSpecialities.Sum(s => s.Duration) * countByTag; - - var stat = new StatsData.TagStats - { - Count = countByTag, - Tag = tag, - Hours = hours - }; - stats.StatsByTag.Add(stat); - } - - - - return stats; - } -}