Skip to content

Commit 1814f95

Browse files
define repo interface
1 parent 63413f0 commit 1814f95

File tree

7 files changed

+201
-47
lines changed

7 files changed

+201
-47
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using Bottle.Api.Responses;
2+
using Bottle.Api.DTOs;
3+
using Bottle.Api.Services;
4+
using Microsoft.AspNetCore.Mvc;
5+
6+
namespace Bottle.Api.Controllers
7+
{
8+
[Route("api/users")]
9+
[ApiController]
10+
public class UserController : ControllerBase
11+
{
12+
private readonly UserService _userService;
13+
14+
public UserController(UserService userService)
15+
{
16+
_userService = userService;
17+
}
18+
19+
[HttpPost("register")]
20+
public async Task<IActionResult> Register([FromBody] UserCreateDTO userDto)
21+
{
22+
var response = await _userService.RegisterAsync(userDto);
23+
return StatusCode(response.StatusCode, response);
24+
}
25+
26+
[HttpPost("login")]
27+
public async Task<IActionResult> Login([FromBody] UserLoginDTO loginDto)
28+
{
29+
var response = await _userService.AuthenticateAsync(loginDto);
30+
return StatusCode(response.StatusCode, response);
31+
}
32+
33+
[HttpGet]
34+
public async Task<IActionResult> GetAllUsers()
35+
{
36+
var response = await _userService.GetAllUsersAsync();
37+
return StatusCode(response.StatusCode, response);
38+
}
39+
40+
[HttpGet("{id}")]
41+
public async Task<IActionResult> GetUser(Guid id)
42+
{
43+
var response = await _userService.GetUserByIdAsync(id);
44+
return StatusCode(response.StatusCode, response);
45+
}
46+
47+
[HttpPut("{id}")]
48+
public async Task<IActionResult> UpdateUser(Guid id, [FromBody] UserUpdateDTO updateDto)
49+
{
50+
var response = await _userService.UpdateUserAsync(id, updateDto);
51+
return StatusCode(response.StatusCode, response);
52+
}
53+
54+
[HttpDelete("{id}")]
55+
public async Task<IActionResult> DeleteUser(Guid id)
56+
{
57+
var response = await _userService.DeleteUserAsync(id);
58+
return StatusCode(response.StatusCode, response);
59+
}
60+
}
61+
}

Bottle.Api/DTOs/UserDto.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,22 @@ public class UserDto
88
public DateTime CreatedAt { get; set; }
99
}
1010

11+
public class UserCreateDTO
12+
{
13+
public string Name { get; set; } = string.Empty;
14+
public string Email { get; set; } = string.Empty;
15+
public string Password { get; set; } = string.Empty;
16+
}
17+
18+
public class UserLoginDTO
19+
{
20+
public string Email { get; set; } = string.Empty;
21+
public string Password { get; set; } = string.Empty;
22+
}
23+
24+
public class UserUpdateDTO
25+
{
26+
public string Name { get; set; } = string.Empty;
27+
}
28+
1129
}

Bottle.Api/Program.cs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using Pomelo.EntityFrameworkCore.MySql;
33
using DotNetEnv;
44
using Bottle.Api.Configurations;
5+
using Bottle.Api.Services;
6+
using Bottle.Api.Repositories;
57

68

79

@@ -31,6 +33,10 @@
3133
builder.Services.AddDbContext<MySqlDbContext>(options =>
3234
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))
3335
);
36+
builder.Services.AddHttpContextAccessor();
37+
builder.Services.AddScoped<UserService>();
38+
builder.Services.AddScoped<IUserRepository, UserRepository>();
39+
builder.Services.AddControllers();
3440

3541
// Add services to the container.
3642
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
@@ -46,28 +52,6 @@
4652

4753
app.UseHttpsRedirection();
4854

49-
var summaries = new[]
50-
{
51-
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
52-
};
53-
54-
app.MapGet("/weatherforecast", () =>
55-
{
56-
var forecast = Enumerable.Range(1, 5).Select(index =>
57-
new WeatherForecast
58-
(
59-
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
60-
Random.Shared.Next(-20, 55),
61-
summaries[Random.Shared.Next(summaries.Length)]
62-
))
63-
.ToArray();
64-
return forecast;
65-
})
66-
.WithName("GetWeatherForecast");
55+
app.MapControllers();
6756

6857
app.Run();
69-
70-
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
71-
{
72-
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
73-
}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
using System.Collections.Generic;
22
using System.Threading.Tasks;
3+
34
using Bottle.Api.Models;
45

56
namespace Bottle.Api.Repositories
67
{
78
public interface IUserRepository
89
{
9-
Task<IEnumerable<User>> GetAllUsersAsync();
10-
Task<User?> GetUserByIdAsync(Guid id);
11-
Task<User> CreateUserAsync(User user);
12-
Task<User> UpdateUserAsync(User user);
13-
Task<bool> DeleteUserAsync(int id);
10+
Task<User?> GetByEmailAsync(string email);
11+
Task<User?> GetByIdAsync(Guid id);
12+
Task<List<User>> GetAllAsync();
13+
Task<User> CreateAsync(User user);
14+
Task<bool> UpdateAsync(User user);
15+
Task<bool> DeleteAsync(Guid id);
1416
}
1517
}
Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using Bottle.Api.Configurations;
22
using Bottle.Api.Models;
33
using Microsoft.EntityFrameworkCore;
4-
using System.Collections.Generic;
5-
using System.Threading.Tasks;
64

75
namespace Bottle.Api.Repositories
86
{
@@ -15,41 +13,41 @@ public UserRepository(MySqlDbContext context)
1513
_context = context;
1614
}
1715

18-
public async Task<IEnumerable<User>> GetAllUsersAsync()
16+
public async Task<User?> GetByEmailAsync(string email)
1917
{
20-
return await _context.Users.ToListAsync();
18+
return await _context.Users.FirstOrDefaultAsync(u => u.Email == email);
19+
}
20+
21+
public async Task<User?> GetByIdAsync(Guid id)
22+
{
23+
return await _context.Users.FindAsync(id);
2124
}
2225

23-
public async Task<User?> GetUserByIdAsync(Guid id)
26+
public async Task<List<User>> GetAllAsync()
2427
{
25-
return await _context.Users.FirstOrDefaultAsync(u => u.Id == id);
28+
return await _context.Users.ToListAsync();
2629
}
2730

28-
public async Task<User> CreateUserAsync(User user)
31+
public async Task<User> CreateAsync(User user)
2932
{
3033
_context.Users.Add(user);
3134
await _context.SaveChangesAsync();
3235
return user;
3336
}
3437

35-
public async Task<User> UpdateUserAsync(User user)
38+
public async Task<bool> UpdateAsync(User user)
3639
{
3740
_context.Users.Update(user);
38-
await _context.SaveChangesAsync();
39-
return user;
41+
return await _context.SaveChangesAsync() > 0;
4042
}
4143

42-
public async Task<bool> DeleteUserAsync(int id)
44+
public async Task<bool> DeleteAsync(Guid id)
4345
{
4446
var user = await _context.Users.FindAsync(id);
45-
if (user == null)
46-
{
47-
return false;
48-
}
47+
if (user == null) return false;
4948

5049
_context.Users.Remove(user);
51-
await _context.SaveChangesAsync();
52-
return true;
50+
return await _context.SaveChangesAsync() > 0;
5351
}
5452
}
5553
}

Bottle.Api/Responses/ApiResponse.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ public class ApiResponse<T>
66
public string Message { get; set; }
77
public object Data { get; set; } = new { };
88

9-
public ApiResponse(int statusCode, string message, T data)
9+
public ApiResponse(int statusCode, string message, T? data = default)
1010
{
1111
StatusCode = statusCode;
1212
Message = message;
13-
Data = new { data };
13+
Data = data is null ? new { } : data;
1414
}
1515
}
1616
}

Bottle.Api/Services/UserService.cs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System.Security.Cryptography;
2+
using System.Text;
3+
using Bottle.Api.Responses;
4+
using Bottle.Api.DTOs;
5+
using Bottle.Api.Models;
6+
using Bottle.Api.Repositories;
7+
8+
namespace Bottle.Api.Services
9+
{
10+
public class UserService
11+
{
12+
private readonly IUserRepository _userRepository;
13+
14+
public UserService(IUserRepository userRepository)
15+
{
16+
_userRepository = userRepository;
17+
}
18+
19+
public async Task<ApiResponse<User>> RegisterAsync(UserCreateDTO userDto)
20+
{
21+
if (await _userRepository.GetByEmailAsync(userDto.Email) != null)
22+
return new ApiResponse<User>(400, "Email already exists", null);
23+
24+
var user = new User
25+
{
26+
Name = userDto.Name,
27+
Email = userDto.Email,
28+
PasswordHash = HashPassword(userDto.Password)
29+
};
30+
31+
var createdUser = await _userRepository.CreateAsync(user);
32+
return new ApiResponse<User>(201, "User registered successfully", createdUser);
33+
}
34+
35+
public async Task<ApiResponse<User>> AuthenticateAsync(UserLoginDTO loginDto)
36+
{
37+
var user = await _userRepository.GetByEmailAsync(loginDto.Email);
38+
if (user == null || !VerifyPassword(loginDto.Password, user.PasswordHash))
39+
return new ApiResponse<User>(401, "Invalid email or password", null);
40+
41+
return new ApiResponse<User>(200, "Login successful", user);
42+
}
43+
44+
public async Task<ApiResponse<List<User>>> GetAllUsersAsync()
45+
{
46+
var users = await _userRepository.GetAllAsync();
47+
return new ApiResponse<List<User>>(200, "Users retrieved successfully", users);
48+
}
49+
50+
public async Task<ApiResponse<User>> GetUserByIdAsync(Guid id)
51+
{
52+
var user = await _userRepository.GetByIdAsync(id);
53+
if (user == null)
54+
return new ApiResponse<User>(404, "User not found", null);
55+
56+
return new ApiResponse<User>(200, "User retrieved successfully", user);
57+
}
58+
59+
public async Task<ApiResponse<bool>> UpdateUserAsync(Guid id, UserUpdateDTO updateDto)
60+
{
61+
var user = await _userRepository.GetByIdAsync(id);
62+
if (user == null) return new ApiResponse<bool>(404, "User not found", false);
63+
64+
user.Name = updateDto.Name;
65+
var updated = await _userRepository.UpdateAsync(user);
66+
67+
return new ApiResponse<bool>(200, "User updated successfully", updated);
68+
}
69+
70+
public async Task<ApiResponse<bool>> DeleteUserAsync(Guid id)
71+
{
72+
var deleted = await _userRepository.DeleteAsync(id);
73+
if (!deleted) return new ApiResponse<bool>(404, "User not found", false);
74+
75+
return new ApiResponse<bool>(200, "User deleted successfully", true);
76+
}
77+
78+
private static string HashPassword(string password)
79+
{
80+
using var sha256 = SHA256.Create();
81+
var bytes = Encoding.UTF8.GetBytes(password);
82+
var hash = sha256.ComputeHash(bytes);
83+
return Convert.ToBase64String(hash);
84+
}
85+
86+
private static bool VerifyPassword(string password, string storedHash)
87+
{
88+
return HashPassword(password) == storedHash;
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)