Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 1.8 #61

Merged
merged 16 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions rag-2-backend/Config/ServiceRegistrationExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using rag_2_backend.Infrastructure.Module.Administration;
using rag_2_backend.Infrastructure.Module.Auth;
using rag_2_backend.Infrastructure.Module.Background;
using rag_2_backend.Infrastructure.Module.Course;
using rag_2_backend.Infrastructure.Module.Email;
using rag_2_backend.Infrastructure.Module.Game;
using rag_2_backend.Infrastructure.Module.GameRecord;
Expand Down Expand Up @@ -52,6 +53,8 @@ private static void ConfigServices(IServiceCollection services)
services.AddScoped<GameDao>();
services.AddScoped<GameRecordDao>();
services.AddScoped<StatsUtil>();
services.AddScoped<CourseDao>();
services.AddScoped<CourseService>();
}

private static void ConfigSwagger(IServiceCollection services)
Expand Down
20 changes: 20 additions & 0 deletions rag-2-backend/Infrastructure/Common/Mapper/CourseMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#region

using rag_2_backend.Infrastructure.Database.Entity;
using rag_2_backend.Infrastructure.Module.Course.Dto;

#endregion

namespace rag_2_backend.Infrastructure.Common.Mapper;

public static class CourseMapper
{
public static CourseResponse Map(Course course)
{
return new CourseResponse
{
Id = course.Id,
Name = course.Name
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public static GameRecordResponse Map(GameRecord gameRecord)
Started = gameRecord.Started,
EndState = gameRecord.EndState,
OutputSpec = gameRecord.OutputSpec,
SizeMb = gameRecord.SizeMb
SizeMb = gameRecord.SizeMb,
User = UserMapper.Map(gameRecord.User),
IsEmptyRecord = gameRecord.IsEmptyRecord
};
}

Expand Down
8 changes: 5 additions & 3 deletions rag-2-backend/Infrastructure/Common/Mapper/UserMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ public static UserResponse Map(User user)
Id = user.Id,
Email = user.Email,
Role = user.Role,
StudyCycleYearA = user.StudyCycleYearA,
StudyCycleYearB = user.StudyCycleYearB,
StudyCycleYearA = user.StudyCycleYearA == 0 ? null : user.StudyCycleYearA,
StudyCycleYearB = user.StudyCycleYearB == 0 ? null : user.StudyCycleYearB,
Name = user.Name,
LastPlayed = user.LastPlayed.Equals(DateTime.MinValue) ? null : user.LastPlayed,
Banned = user.Banned
Banned = user.Banned,
Course = user.Course != null ? CourseMapper.Map(user.Course) : null,
Group = user.Group
};
}
}
23 changes: 23 additions & 0 deletions rag-2-backend/Infrastructure/Dao/CourseDao.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#region

using HttpExceptions.Exceptions;
using rag_2_backend.Infrastructure.Database;
using rag_2_backend.Infrastructure.Database.Entity;

#endregion

namespace rag_2_backend.Infrastructure.Dao;

public class CourseDao(DatabaseContext dbContext)
{
public virtual Course GetCourseByIdOrThrow(int id)
{
return dbContext.Courses.SingleOrDefault(u => u.Id == id) ??
throw new NotFoundException("Course not found");
}

public virtual List<Course> GetAllCourses()
{
return dbContext.Courses.ToList();
}
}
9 changes: 5 additions & 4 deletions rag-2-backend/Infrastructure/Dao/GameRecordDao.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ namespace rag_2_backend.Infrastructure.Dao;

public class GameRecordDao(DatabaseContext dbContext)
{
public virtual List<GameRecordResponse> GetRecordsByGameAndUser(int gameId, string email)
public virtual List<GameRecordResponse> GetRecordsByGameAndUser(int gameId, int userId)
{
return dbContext.GameRecords
.Include(r => r.Game)
.Include(r => r.User)
.Where(r => r.Game.Id == gameId && r.User.Email == email)
.Where(r => r.Game.Id == gameId && r.User.Id == userId)
.ToList()
.Select(GameRecordMapper.Map)
.ToList();
Expand Down Expand Up @@ -72,7 +72,7 @@ public virtual void PerformGameRecordTransaction(Game game, GameRecord gameRecor
try
{
dbContext.Database.ExecuteSqlRaw(
"SELECT InsertRecordedGame(@GameId, @Values, @UserId, @Players, @OutputSpec, @EndState, @Started, @Ended, @SizeMb)",
"SELECT InsertRecordedGame(@GameId, @Values, @UserId, @Players, @OutputSpec, @EndState, @Started, @Ended, @SizeMb, @IsEmptyRecord)",
new NpgsqlParameter("@GameId", game.Id),
new NpgsqlParameter("@Values", JsonSerializer.Serialize(gameRecord.Values)),
new NpgsqlParameter("@UserId", user.Id),
Expand All @@ -81,7 +81,8 @@ public virtual void PerformGameRecordTransaction(Game game, GameRecord gameRecor
new NpgsqlParameter("@EndState", gameRecord.EndState),
new NpgsqlParameter("@Started", gameRecord.Started),
new NpgsqlParameter("@Ended", gameRecord.Ended),
new NpgsqlParameter("@SizeMb", gameRecord.SizeMb)
new NpgsqlParameter("@SizeMb", gameRecord.SizeMb),
new NpgsqlParameter("@IsEmptyRecord", gameRecord.IsEmptyRecord)
);
transaction.Commit();
}
Expand Down
7 changes: 7 additions & 0 deletions rag-2-backend/Infrastructure/Dao/RefreshTokenDao.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,11 @@ public virtual void RemoveTokensForUser(User user)
context.RefreshTokens.RemoveRange(unusedTokens);
context.SaveChanges();
}

public virtual void RemoveTokenByToken(string token)
{
var unusedTokens = context.RefreshTokens.Where(r => r.Token == token).ToList();
context.RefreshTokens.RemoveRange(unusedTokens);
context.SaveChanges();
}
}
5 changes: 4 additions & 1 deletion rag-2-backend/Infrastructure/Dao/UserDao.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#region

using HttpExceptions.Exceptions;
using Microsoft.EntityFrameworkCore;
using rag_2_backend.Infrastructure.Database;
using rag_2_backend.Infrastructure.Database.Entity;

Expand All @@ -18,7 +19,9 @@ public virtual User GetUserByIdOrThrow(int id)

public virtual User GetUserByEmailOrThrow(string email)
{
return context.Users.SingleOrDefault(u => u.Email == email) ??
return context.Users
.Include(u => u.Course)
.SingleOrDefault(u => u.Email == email) ??
throw new NotFoundException("User not found");
}

Expand Down
1 change: 1 addition & 0 deletions rag-2-backend/Infrastructure/Database/DatabaseContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class DatabaseContext(DbContextOptions<DatabaseContext> options) : DbCont
public virtual required DbSet<AccountConfirmationToken> AccountConfirmationTokens { get; init; }
public virtual required DbSet<RefreshToken> RefreshTokens { get; init; }
public virtual required DbSet<PasswordResetToken> PasswordResetTokens { get; init; }
public virtual DbSet<Course> Courses { get; init; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Expand Down
17 changes: 17 additions & 0 deletions rag-2-backend/Infrastructure/Database/Entity/Course.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#region

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

#endregion

namespace rag_2_backend.Infrastructure.Database.Entity;

[Table("course_table")]
[Index(nameof(Name), IsUnique = true)]
public class Course
{
[Key] public int Id { get; init; }
[MaxLength(100)] public string Name { get; set; } = "";
}
1 change: 1 addition & 0 deletions rag-2-backend/Infrastructure/Database/Entity/GameRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ public class GameRecord
[MaxLength(1000)] public string? OutputSpec { get; init; }
[MaxLength(500)] public string? EndState { get; init; }
public double SizeMb { get; init; }
public bool IsEmptyRecord { get; init; }
}
14 changes: 12 additions & 2 deletions rag-2-backend/Infrastructure/Database/Entity/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ public User() //for ef

public User(string email)
{
var domain = email.Split('@')[1];
string domain;
try
{
domain = email.Split('@')[1];
}
catch (Exception)
{
throw new BadRequestException("Invalid email address");
}

if (!domain.Equals("stud.prz.edu.pl") && !domain.Equals("prz.edu.pl"))
throw new BadRequestException("Wrong domain");
Expand All @@ -32,11 +40,13 @@ public User(string email)
[Key] public int Id { get; init; }
[MaxLength(100)] public string Email { get; init; } = "";
[MaxLength(100)] public required string Password { get; set; }
[MaxLength(100)] public required string Name { get; init; }
[MaxLength(100)] public required string Name { get; set; }
public Role Role { get; set; }
public bool Confirmed { get; set; }
public int StudyCycleYearA { get; set; }
public int StudyCycleYearB { get; set; }
public bool Banned { get; set; }
public DateTime LastPlayed { get; set; }
public Course? Course { get; set; }
[MaxLength(100)] public string? Group { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ public void ChangeRole([Required] int userId, [Required] Role role)
administrationService.ChangeRole(userId, role);
}

/// <summary>Get details of any user by user ID, only yours if not admin or teacher (Auth)</summary>
/// <summary>Get details of any user by user ID (Admin, Teacher)</summary>
/// <response code="403">Cannot view details</response>
[HttpGet("{userId:int}/details")]
[Authorize]
[Authorize(Roles = "Admin,Teacher")]
public UserResponse GetUserDetails([Required] int userId)
{
return administrationService.GetUserDetails(AuthDao.GetPrincipalEmail(User), userId);
Expand Down
11 changes: 9 additions & 2 deletions rag-2-backend/Infrastructure/Module/Auth/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ public void VerifyToken()
[HttpPost("login")]
public string Login([FromBody] [Required] UserLoginRequest loginRequest)
{
var refreshTokenExpiryDays = loginRequest.RememberMe
? double.Parse(config["RefreshToken:ExpireDaysRememberMe"] ?? "10")
: double.Parse(config["RefreshToken:ExpireDays"] ?? "1");

var response = authService.LoginUser(
loginRequest.Email,
loginRequest.Password,
double.Parse(config["RefreshToken:ExpireDays"] ?? "30")
refreshTokenExpiryDays
);

HttpContext.Response.Cookies.Append("refreshToken", response.RefreshToken,
Expand Down Expand Up @@ -65,7 +69,10 @@ public string RefreshToken()
[Authorize]
public void Logout()
{
authService.LogoutUser(AuthDao.GetPrincipalEmail(User));
HttpContext.Request.Cookies.TryGetValue("refreshToken", out var refreshToken);

if (refreshToken != null)
authService.LogoutUser(refreshToken);
}

/// <summary>Get current user details (Auth)</summary>
Expand Down
6 changes: 2 additions & 4 deletions rag-2-backend/Infrastructure/Module/Auth/AuthService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,9 @@ public UserResponse GetMe(string email)
return UserMapper.Map(userDao.GetUserByEmailOrThrow(email));
}

public void LogoutUser(string email)
public void LogoutUser(string token)
{
var user = userDao.GetUserByEmailOrThrow(email);
refreshTokenDao.RemoveTokensForUser(user);
refreshTokenDao.RemoveTokenByToken(token);
}

//
Expand All @@ -73,7 +72,6 @@ private RefreshToken GenerateRefreshToken(double refreshTokenExpirationTimeDays,
Expiration = DateTime.Now.AddDays(refreshTokenExpirationTimeDays),
Token = Guid.NewGuid().ToString()
};
refreshTokenDao.RemoveTokensForUser(user);
databaseContext.RefreshTokens.Add(refreshToken);
databaseContext.SaveChanges();
return refreshToken;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ public class UserLoginRequest
{
public required string Email { get; set; }
public required string Password { get; set; }
public required bool RememberMe { get; set; }
}
51 changes: 51 additions & 0 deletions rag-2-backend/Infrastructure/Module/Course/CourseController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#region

using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using rag_2_backend.Infrastructure.Module.Course.Dto;

#endregion

namespace rag_2_backend.Infrastructure.Module.Course;

[ApiController]
[Route("api/[controller]")]
public class CourseController(CourseService courseService) : ControllerBase
{
/// <summary>Get courses available in system</summary>
[HttpGet]
public async Task<IEnumerable<CourseResponse>> GetCourses()
{
return await courseService.GetCourses();
}

/// <summary>Add new course to system (Admin)</summary>
/// <response code="400">Course with this name already exists</response>
[HttpPost]
[Authorize(Roles = "Admin")]
public void Add([FromBody] [Required] CourseRequest request)
{
courseService.AddCourse(request);
}

/// <summary>Edit existing course (Admin)</summary>
/// <response code="404">Course not found</response>
/// <response code="400">Course with this name already exists</response>
[HttpPut("{id:int}")]
[Authorize(Roles = "Admin")]
public void Edit([FromBody] [Required] CourseRequest request, int id)
{
courseService.EditCourse(request, id);
}

/// <summary>Remove existing course (Admin)</summary>
/// <response code="404">Course not found</response>
/// <response code="400">Cannot delete used course</response>
[HttpDelete("{id:int}")]
[Authorize(Roles = "Admin")]
public void Remove([Required] int id)
{
courseService.RemoveCourse(id);
}
}
Loading
Loading