Skip to content

Commit

Permalink
Merge pull request #61 from theImmortalCoders/dev
Browse files Browse the repository at this point in the history
Release 1.8
  • Loading branch information
pablitoo1 authored Nov 20, 2024
2 parents 28d82fb + e911d34 commit 88b31b8
Show file tree
Hide file tree
Showing 50 changed files with 1,579 additions and 72 deletions.
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

0 comments on commit 88b31b8

Please sign in to comment.