авторизация

This commit is contained in:
Sergey Karmanov 2023-12-24 04:16:18 +03:00
parent d2d2685672
commit b5477d529f
Signed by: serega404
GPG Key ID: B6AD49C8C835460C
31 changed files with 973 additions and 967 deletions

13
CyberBoom/AuthOptions.cs Normal file
View File

@ -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));
}

View File

@ -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<Claim> claims) => new JwtSecurityTokenHandler()
.WriteToken(
JwtSecurityToken(claims)
);
public JwtSecurityToken JwtSecurityToken(List<Claim> 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,
};
}

4
CyberBoom/Consts.cs Normal file
View File

@ -0,0 +1,4 @@
public static class Consts
{
public const char TOKENS_SEPORATOR = ';';
}

View File

@ -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<IActionResult> Post([FromForm] PostMeetingDto meeting)
{
await meeting.SpeackerImage.WriteFilesToDirectory();
await meeting.PlaceImages.WriteFilesToDirectory();
var meetingWrite = meeting.Adapt<Meeting>();
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<IActionResult> Put([FromForm] PutMeetingDto meeting)
{
await meeting.SpeackerImage.WriteFilesToDirectory();
await meeting.PlaceImages.WriteFilesToDirectory();
var meetingWrite = meeting.Adapt<Meeting>();
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<IActionResult> 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);
}
}

View File

@ -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<IActionResult> Post([FromBody] PostQuestionDto question)
{
var dbWr = question.Adapt<Question>();
await _applicationContext.Questions.AddAsync(dbWr);
await _applicationContext.SaveChangesAsync();
return Ok(new { dbWr.Id });
}
[Authorize]
[HttpPut]
public async Task<IActionResult> 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<IActionResult> 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);
}
}

View File

@ -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<IActionResult> Post([FromBody] PostReactionDto reaction)
{
var dbWr = reaction.Adapt<Reaction>();
await _applicationContext.Reactions.AddAsync(dbWr);
await _applicationContext.SaveChangesAsync();
return Ok(new { dbWr.Id });
}
[Authorize]
[HttpDelete]
public async Task<IActionResult> 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<IActionResult> 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);
}
}

View File

@ -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<IActionResult> Post([FromBody] PostReviewDto review)
{
var dbWr = review.Adapt<Review>();
await _applicationContext.Reviews.AddAsync(dbWr);
await _applicationContext.SaveChangesAsync();
return Ok(new { dbWr.Id });
}
[Authorize]
[HttpPut]
public async Task<IActionResult> 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<IActionResult> 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);
}
}

View File

@ -1,17 +1,19 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using Mapster; using System.Security.Claims;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Google; using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using static Consts; using static Consts;
namespace CyberBoom.Controllers; namespace CyberBoom.Controllers;
[ApiController] [ApiController]
[Route("/api/[controller]")] [Route("/api/[controller]")]
public class UsersController : ControllerBase public class UsersController : ControllerBase
@ -22,18 +24,41 @@ public class UsersController : ControllerBase
private readonly RoleManager<IdentityRole> _roleManager; private readonly RoleManager<IdentityRole> _roleManager;
public UsersController(ApplicationContext applicationContext, UserManager<User> userManager, RoleManager<IdentityRole> roleManager) public UsersController(
ApplicationContext applicationContext,
UserManager<User> userManager,
RoleManager<IdentityRole> roleManager
)
{ {
_applicationContext = applicationContext; _applicationContext = applicationContext;
_userManager = userManager; _userManager = userManager;
_roleManager = roleManager; _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] [HttpPost]
public async Task<IActionResult> Post([FromForm]UserPost user) public async Task<IActionResult> Post([FromForm] UserPost user)
{ {
await user.Avatar.WriteFileToDirectory(); await user.Avatar.WriteFileToDirectory();
var userWr = new User { var userWr = new User
{
AvatarUrl = user.Avatar.FileName, AvatarUrl = user.Avatar.FileName,
Fio = user.Fio, Fio = user.Fio,
Specialities = user.Specialities, Specialities = user.Specialities,
@ -41,26 +66,43 @@ public class UsersController : ControllerBase
UserName = user.Username UserName = user.Username
}; };
var result = await _userManager.CreateAsync(userWr); var result = await _userManager.CreateAsync(userWr);
if(result.Succeeded)
return Ok( if (!result.Succeeded)
new {
userWr.Id
}
);
return BadRequest(result.Errors); 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<Claim>
{
new Claim(ClaimsIdentity.DefaultNameClaimType, user.UserName!),
new Claim(ClaimsIdentity.DefaultRoleClaimType, role)
};
var bOpt = new BearerAccessTokenOptions();
return bOpt.GetBearerToken(claims);
}
[Authorize(Roles = "модератор")]
[HttpPut] [HttpPut]
public async Task<IActionResult> Put([FromForm]UserPut user) public async Task<IActionResult> Put([FromForm] UserPut user)
{ {
await user.Avatar.WriteFileToDirectory(); await user.Avatar.WriteFileToDirectory();
var fuser = await _userManager.FindByIdAsync(user.Id); var fuser = await _userManager.FindByIdAsync(user.Id);
if(fuser is null) if (fuser is null)
throw new Exception("user not found"); throw new Exception("user not found");
fuser.AvatarUrl = user.Avatar.FileName; fuser.AvatarUrl = user.Avatar.FileName;
fuser.Fio = user.Fio; fuser.Fio = user.Fio;
fuser.Specialities = user.Specialities; fuser.Specialities = user.Specialities;
@ -68,19 +110,18 @@ public class UsersController : ControllerBase
fuser.UserName = user.Username; fuser.UserName = user.Username;
var result = await _userManager.UpdateAsync(fuser); var result = await _userManager.UpdateAsync(fuser);
if(result.Succeeded) if (result.Succeeded)
return Ok( return Ok();
);
return BadRequest(result.Errors); return BadRequest(result.Errors);
} }
[Authorize(Roles = "модератор")]
[HttpPost("moderator")] [HttpPost("moderator")]
public async Task<IActionResult> PostModerator([FromForm]UserPost user) public async Task<IActionResult> PostModerator([FromForm] UserPost user)
{ {
await user.Avatar.WriteFileToDirectory(); await user.Avatar.WriteFileToDirectory();
var userWr = new User { var userWr = new User
{
AvatarUrl = user.Avatar.FileName, AvatarUrl = user.Avatar.FileName,
Fio = user.Fio, Fio = user.Fio,
Specialities = user.Specialities, Specialities = user.Specialities,
@ -88,480 +129,44 @@ public class UsersController : ControllerBase
UserName = user.Username UserName = user.Username
}; };
var result = await _userManager.CreateAsync(userWr); var result = await _userManager.CreateAsync(userWr);
if(!result.Succeeded) if (!result.Succeeded)
return BadRequest(result.Errors); return BadRequest(result.Errors);
var isExists = await _roleManager.RoleExistsAsync("модератор"); var role = "модератор";
if(!isExists){ await AddUerToRole(userWr, role);
var roleResult = await _roleManager.CreateAsync(new IdentityRole("модератор")); var token = GetToken(userWr, role);
if(!roleResult.Succeeded) return Ok(new { userWr.Id, Token = token });
throw new Exception("cannot create role");
}
var addingRole = await _userManager.AddToRoleAsync(userWr, "модератор");
if(!addingRole.Succeeded)
throw new Exception("cannot create role");
return Ok(
new {
userWr.Id
}
);
} }
[Authorize]
[HttpGet] [HttpGet]
public async Task<IActionResult> GetUserData(string id) public async Task<IActionResult> GetUserData(string id)
{ {
var user = await _userManager.FindByIdAsync(id); var user = await _userManager.FindByIdAsync(id);
if(user is null) if (user is null)
return BadRequest(); return BadRequest();
var role = await _userManager.GetRolesAsync(user); var role = await _userManager.GetRolesAsync(user);
return Ok(new { return Ok(new { user, role });
user,
role
});
} }
[Authorize]
[HttpGet("stats")] [HttpGet("stats")]
public async Task<IActionResult> GetUserStats(string id) public async Task<IActionResult> GetUserStats(string id)
{ {
var user = await _userManager.FindByIdAsync(id); var user = await _userManager.FindByIdAsync(id);
if(user is null) if (user is null)
return BadRequest(); return BadRequest();
var stats = await _applicationContext.GetStatistic(id); var stats = await _applicationContext.GetStatistic(id);
var achievmnets = _applicationContext.Achievments.Where(c => c.UserId == id); var achievmnets = _applicationContext.Achievments.Where(c => c.UserId == id);
return Ok(new { Stats = stats, Achievments = achievmnets });
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<IActionResult> Post([FromForm]PostMeetingDto meeting)
{
await meeting.SpeackerImage.WriteFilesToDirectory();
await meeting.PlaceImages.WriteFilesToDirectory();
var meetingWrite = meeting.Adapt<Meeting>();
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<IActionResult> Put([FromForm]PutMeetingDto meeting)
{
await meeting.SpeackerImage.WriteFilesToDirectory();
await meeting.PlaceImages.WriteFilesToDirectory();
var meetingWrite = meeting.Adapt<Meeting>();
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<IActionResult> 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);
}
}
[ApiController]
[Route("/api/[controller]")]
public class ReviewsController : ControllerBase
{
private readonly ApplicationContext _applicationContext;
public ReviewsController(ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] PostReviewDto review)
{
var dbWr = review.Adapt<Review>();
await _applicationContext.Reviews.AddAsync(dbWr);
await _applicationContext.SaveChangesAsync();
return Ok(new {
dbWr.Id
});
}
[HttpPut]
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> Post([FromBody] PostQuestionDto question)
{
var dbWr = question.Adapt<Question>();
await _applicationContext.Questions.AddAsync(dbWr);
await _applicationContext.SaveChangesAsync();
return Ok(new {
dbWr.Id
});
}
[HttpPut]
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> Post([FromBody] PostReactionDto reaction)
{
var dbWr = reaction.Adapt<Reaction>();
await _applicationContext.Reactions.AddAsync(dbWr);
await _applicationContext.SaveChangesAsync();
return Ok(new {
dbWr.Id
});
}
[HttpDelete]
public async Task<IActionResult> 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<IActionResult> 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<IActionResult> Post([FromBody] PostUserWriteToMetingDto write)
{
var dbWr = write.Adapt<UserWriteToMeting>();
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<List<Achievment>> WriteAchievment(StatsData stats, string userId)
{
List <Achievment> achievments = new List<Achievment>();
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<IActionResult> 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<IActionResult> 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);
}
}

View File

@ -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<IActionResult> Post([FromBody] PostUserWriteToMetingDto write)
{
var dbWr = write.Adapt<UserWriteToMeting>();
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<List<Achievment>> WriteAchievment(StatsData stats, string userId)
{
List<Achievment> achievments = new List<Achievment>();
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<IActionResult> 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<IActionResult> 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);
}
}

View File

@ -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

86
CyberBoom/DataHelpers.cs Normal file
View File

@ -0,0 +1,86 @@
using Microsoft.EntityFrameworkCore;
using static Consts;
public static class DataHelpers
{
public static string JoinFileNames(this IEnumerable<IFormFile> files) => files.Select(s => s.FileName).JoinStrings();
public static string JoinStrings(this IEnumerable<string> 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<IFormFile> files)
{
foreach(var file in files)
{
await file.WriteFileToDirectory();
}
}
public static async Task<StatsData> 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;
}
}

View File

@ -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!;
}

View File

@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
public class ApplicationContext : IdentityDbContext<User>
{
public DbSet<Meeting> Meetings { get; set; }
public DbSet<Review> Reviews { get; set; }
public DbSet<Reaction> Reactions { get; set; }
public DbSet<UserWriteToMeting> UserWriteToMetings { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<Achievment> Achievments { get; set; }
public ApplicationContext(DbContextOptions<ApplicationContext> options)
: base(options)
{
Database.EnsureCreated();
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Meeting>().HasMany<Review>().WithOne().HasForeignKey(c => c.MeetingId);
builder.Entity<Meeting>().HasMany<Question>().WithOne().HasForeignKey(c => c.MeetingId);
builder.Entity<Reaction>().HasOne<User>().WithMany().HasForeignKey(c => c.UserId);
builder.Entity<Review>().HasOne(c => c.User).WithMany().HasForeignKey(c => c.UserId);
builder.Entity<Question>().HasOne<Meeting>().WithMany().HasForeignKey(c => c.MeetingId);
builder.Entity<Question>().HasOne(c => c.User).WithMany().HasForeignKey(c => c.UserId);
builder.Entity<Reaction>().HasOne<Question>().WithMany().HasForeignKey(c => c.QuestionId);
builder.Entity<Meeting>().HasMany<UserWriteToMeting>().WithOne().HasForeignKey(c => c.MeetingId);
builder.Entity<UserWriteToMeting>().HasOne<User>().WithMany().HasForeignKey(c => c.UserId);
builder.Entity<Achievment>().HasOne<User>().WithMany().HasForeignKey(c => c.UserId);
}
}

View File

@ -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<IFormFile> SpeackerImage { get; set; } = null!;
public IEnumerable<IFormFile> 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!;
}

View File

@ -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<IFormFile> SpeackerImage { get; set; } = null!;
public IEnumerable<IFormFile> 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!;
}

View File

@ -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!;
}

View File

@ -0,0 +1,6 @@
public class PutQuestionDto
{
public string Id { get; set; } = null!;
public string Text { get; set; } = null!;
}

View File

@ -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;
}

View File

@ -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(); }
}

View File

@ -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(); }
}

View File

@ -0,0 +1,6 @@
public class PostUserWriteToMetingDto
{
public String UserId { get; set; } = null!;
public String MeetingId { get; set; } = null!;
}

View File

@ -0,0 +1,17 @@
public class StatsData
{
public double Hours{ get; set; }
public int Count { get; set; }
public List<TagStats> StatsByTag { get; set; } = new List<TagStats>();
public class TagStats
{
public string Tag { get; set; } = null!;
public int Count { get; set; }
public double Hours { get; set; }
}
}

View File

@ -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!;
}

View File

@ -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!;
}

View File

@ -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!;
}

View File

@ -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!;
}

View File

@ -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;
}

View File

@ -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(); }
}

View File

@ -1,7 +1,4 @@
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
public class User : IdentityUser 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<TagStats> StatsByTag { get; set; } = new List<TagStats>();
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<IFormFile> SpeackerImage { get; set; } = null!;
public IEnumerable<IFormFile> 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<IFormFile> SpeackerImage { get; set; } = null!;
public IEnumerable<IFormFile> 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<User>
{
public DbSet<Meeting> Meetings { get; set; }
public DbSet<Review> Reviews { get; set; }
public DbSet<Reaction> Reactions { get; set; }
public DbSet<UserWriteToMeting> UserWriteToMetings { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<Achievment> Achievments { get; set; }
public ApplicationContext(DbContextOptions<ApplicationContext> options)
: base(options)
{
Database.EnsureCreated();
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Meeting>().HasMany<Review>().WithOne().HasForeignKey(c => c.MeetingId);
builder.Entity<Meeting>().HasMany<Question>().WithOne().HasForeignKey(c => c.MeetingId);
builder.Entity<Reaction>().HasOne<User>().WithMany().HasForeignKey(c => c.UserId);
builder.Entity<Review>().HasOne(c => c.User).WithMany().HasForeignKey(c => c.UserId);
builder.Entity<Question>().HasOne<Meeting>().WithMany().HasForeignKey(c => c.MeetingId);
builder.Entity<Question>().HasOne(c => c.User).WithMany().HasForeignKey(c => c.UserId);
builder.Entity<Reaction>().HasOne<Question>().WithMany().HasForeignKey(c => c.QuestionId);
builder.Entity<Meeting>().HasMany<UserWriteToMeting>().WithOne().HasForeignKey(c => c.MeetingId);
builder.Entity<UserWriteToMeting>().HasOne<User>().WithMany().HasForeignKey(c => c.UserId);
builder.Entity<Achievment>().HasOne<User>().WithMany().HasForeignKey(c => c.UserId);
}
}

View File

@ -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!;
}

View File

@ -1,11 +1,12 @@
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Mapster; using Mapster;
using static Consts; using static Consts;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.OpenApi.Models;
TypeAdapterConfig<PutMeetingDto, Meeting>.NewConfig().Map(d => d.SpeackerImage, s => s.SpeackerImage.JoinFileNames()); TypeAdapterConfig<PutMeetingDto, Meeting>.NewConfig().Map(d => d.SpeackerImage, s => s.SpeackerImage.JoinFileNames());
@ -31,44 +32,51 @@ builder.Services.AddDbContext<ApplicationContext>(options =>
builder.Services.AddIdentity<User, IdentityRole>() builder.Services.AddIdentity<User, IdentityRole>()
.AddEntityFrameworkStores<ApplicationContext>(); .AddEntityFrameworkStores<ApplicationContext>();
builder.Services.AddAuthorization(); builder.Services.AddAuthentication(opt => {
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
.AddJwtBearer(options => opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
{ })
options.TokenValidationParameters = new TokenValidationParameters .AddJwtBearer(options =>
{ {
// указывает, будет ли валидироваться издатель при валидации токена var bearerOptions = new BearerAccessTokenOptions();
ValidateIssuer = true, options.RequireHttpsMetadata = bearerOptions.RequiredHttpsMetadata;
// строка, представляющая издателя options.TokenValidationParameters = bearerOptions.TokenValidationParameters;
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.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
// .AddEntityFrameworkStores<ApplicationContext>();
// builder.Services.AddRazorPages();
builder.Services.AddControllers(); builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer(); 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(); builder.Services.AddCors();
var app = builder.Build(); var app = builder.Build();
@ -97,100 +105,3 @@ app.MapControllers();
//app.MapRazorPages(); //app.MapRazorPages();
app.Run(); 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<IFormFile> files) => files.Select(s => s.FileName).JoinStrings();
public static string JoinStrings(this IEnumerable<string> 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<IFormFile> files)
{
foreach(var file in files)
{
await file.WriteFileToDirectory();
}
}
public static async Task<StatsData> 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;
}
}