Skip to content
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
71 changes: 71 additions & 0 deletions FinancialTracker/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using FinancialTracker.DTOs.AuthDtos;
using FinancialTracker.Interfaces.Auth;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;

namespace FinancialTracker.Controllers;

[ApiController]
[Route("api/auth")]
public class AuthController(IAuthService authService, ILogger<AuthController> logger) : ControllerBase
{
[HttpPost("register")]
public async Task<ActionResult<AuthResponseDto>> Register(RegisterRequestDto registerDto)
{
try
{
var result = await authService.RegisterAsync(registerDto);
return Ok(result);
}
catch (InvalidOperationException ex)
{
logger.LogWarning("Ошибка регистрации: {Message}", ex.Message);
return BadRequest(new { message = ex.Message });
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при регистрации");
return StatusCode(500, new { message = "Внутренняя ошибка сервера при регистрации" });
}
}

[HttpPost("login")]
public async Task<ActionResult<AuthResponseDto>> Login(LoginRequestDto loginDto)
{
try
{
var result = await authService.LoginAsync(loginDto);
return Ok(result);
}
catch (InvalidOperationException ex)
{
logger.LogWarning("Ошибка входа: {Message}", ex.Message);
return Unauthorized(new { message = ex.Message });
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при входе");
return StatusCode(500, new { message = "Внутренняя ошибка сервера при входе" });
}
}

[HttpPost("refresh")]
public async Task<ActionResult<AuthResponseDto>> Refresh(RefreshTokenRequestDto refreshDto)
{
try
{
var result = await authService.RefreshTokenAsync(refreshDto.RefreshToken);
return Ok(result);
}
catch (SecurityTokenException ex)
{
logger.LogWarning("Невалидный refresh token: {Message}", ex.Message);
return Unauthorized(new { message = "Невалидный refresh token" });
}
catch (Exception ex)
{
logger.LogError(ex, "Ошибка при обновлении токена");
return StatusCode(500, new { message = "Внутренняя ошибка сервера при обновлении токена" });
}
}
}
2 changes: 2 additions & 0 deletions FinancialTracker/Controllers/CategoriesController.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using FinancialTracker.DTOs.CategoryDtos;
using FinancialTracker.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace FinancialTracker.Controllers;

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class CategoriesController(
Expand Down
2 changes: 2 additions & 0 deletions FinancialTracker/Controllers/TransactionsController.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using FinancialTracker.DTOs.TransactionDtos;
using FinancialTracker.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace FinancialTracker.Controllers;

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class TransactionsController(
Expand Down
9 changes: 9 additions & 0 deletions FinancialTracker/DTOs/AuthDtos/AuthResponseDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FinancialTracker.DTOs.AuthDtos;

public class AuthResponseDto
{
public required string AccessToken { get; set; }
public required string RefreshToken { get; set; }
public DateTime AccessTokenExpires { get; set; }
public DateTime RefreshTokenExpires { get; set; }
}
7 changes: 7 additions & 0 deletions FinancialTracker/DTOs/AuthDtos/LoginRequestDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace FinancialTracker.DTOs.AuthDtos;

public class LoginRequestDto
{
public required string Email { get; set; }
public required string Password { get; set; }
}
6 changes: 6 additions & 0 deletions FinancialTracker/DTOs/AuthDtos/RefreshTokenRequestDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace FinancialTracker.DTOs.AuthDtos;

public class RefreshTokenRequestDto
{
public required string RefreshToken { get; set; }
}
9 changes: 9 additions & 0 deletions FinancialTracker/DTOs/AuthDtos/RegisterRequestDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FinancialTracker.DTOs.AuthDtos;

public class RegisterRequestDto
{
public required string Email { get; set; }
public required string Password { get; set; }
public required string FirstName { get; set; }
public required string LastName { get; set; }
}
2 changes: 2 additions & 0 deletions FinancialTracker/Data/Entities/Category.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ public class Category
{
public int Id { get; set; }
public required string Name { get; set; }
public Guid UserId { get; set; }

public List<Transaction> Transactions { get; set; } = new();
public User User { get; set; } = null!;
}
13 changes: 13 additions & 0 deletions FinancialTracker/Data/Entities/RefreshToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace FinancialTracker.Data.Entities;

public class RefreshToken
{
public int Id { get; set; }
public required string Token { get; set; }
public Guid UserId { get; set; }
public DateTime Expires { get; set; }
public DateTime Created { get; set; } = DateTime.UtcNow;
public bool IsRevoked { get; set; }

public User User { get; set; } = null!;
}
3 changes: 3 additions & 0 deletions FinancialTracker/Data/Entities/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ public class Transaction

public required int CategoryId { get; set; }
public Category Category { get; set; } = null!;

public Guid UserId { get; set; }
public User User { get; set; } = null!;
}
14 changes: 14 additions & 0 deletions FinancialTracker/Data/Entities/User.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace FinancialTracker.Data.Entities;

public class User
{
public Guid Id { get; set; }
public required string Email { get; set; }
public required string PasswordHash { get; set; }
public required string FirstName { get; set; }
public required string LastName { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;

public List<Transaction> Transactions { get; set; } = new();
public List<Category> Categories { get; set; } = new();
}
86 changes: 76 additions & 10 deletions FinancialTracker/Data/FinancialDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ public class FinancialDbContext(DbContextOptions<FinancialDbContext> options) :
{
public DbSet<Category> Categories { get; set; }
public DbSet<Transaction> Transactions { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<RefreshToken> RefreshTokens { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new CategoryConfiguration());
modelBuilder.ApplyConfiguration(new TransactionConfiguration());
modelBuilder.ApplyConfiguration(new RefreshTokenConfiguration());
base.OnModelCreating(modelBuilder);
}
}
Expand All @@ -23,21 +27,18 @@ public void Configure(EntityTypeBuilder<Category> builder)
{
builder.HasKey(e => e.Id);

builder.HasIndex(e => e.Name)
builder.HasIndex(e => new { e.Name, e.UserId })
.IsUnique();

builder.HasMany(e => e.Transactions)
.WithOne(e => e.Category)
.HasForeignKey(e => e.CategoryId)
.OnDelete(DeleteBehavior.Restrict);

builder.HasData(
new Category { Id = 1, Name = "Еда" },
new Category { Id = 2, Name = "Транспорт" },
new Category { Id = 3, Name = "Развлечения" },
new Category { Id = 4, Name = "Зарплата" },
new Category { Id = 5, Name = "Прочее" }
);
.OnDelete(DeleteBehavior.Cascade);

builder.HasOne(e => e.User)
.WithMany(e => e.Categories)
.HasForeignKey(e => e.UserId)
.OnDelete(DeleteBehavior.Cascade);
}
}

Expand All @@ -55,5 +56,70 @@ public void Configure(EntityTypeBuilder<Transaction> builder)
.WithMany(e => e.Transactions)
.HasForeignKey(e => e.CategoryId)
.OnDelete(DeleteBehavior.Cascade);

builder.HasOne(e => e.User)
.WithMany(e => e.Transactions)
.HasForeignKey(e => e.UserId)
.OnDelete(DeleteBehavior.Cascade);
}
}

public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(e => e.Id);

builder.HasIndex(e => e.Email)
.IsUnique();

builder.Property(e => e.Email)
.IsRequired();

builder.Property(e => e.PasswordHash)
.IsRequired();

builder.Property(e => e.FirstName)
.IsRequired()
.HasMaxLength(100);

builder.Property(e => e.LastName)
.IsRequired()
.HasMaxLength(100);

builder.HasMany(e => e.Transactions)
.WithOne(e => e.User)
.HasForeignKey(e => e.UserId)
.OnDelete(DeleteBehavior.Cascade);

builder.HasMany(e => e.Categories)
.WithOne(e => e.User)
.HasForeignKey(e => e.UserId)
.OnDelete(DeleteBehavior.Cascade);
}
}

public class RefreshTokenConfiguration : IEntityTypeConfiguration<RefreshToken>
{
public void Configure(EntityTypeBuilder<RefreshToken> builder)
{
builder.HasKey(e => e.Id);

builder.HasIndex(e => e.Token)
.IsUnique();

builder.Property(e => e.Token)
.IsRequired()
.HasMaxLength(500);

builder.Property(e => e.Expires)
.IsRequired();

builder.HasOne(e => e.User)
.WithMany()
.HasForeignKey(e => e.UserId)
.OnDelete(DeleteBehavior.Cascade);

builder.HasIndex(e => new { e.UserId, e.IsRevoked, e.Expires });
}
}
Loading