diff --git a/Govor.API/Controllers/FriendsController.cs b/Govor.API/Controllers/FriendsController.cs index c9cb8b4..96492fd 100644 --- a/Govor.API/Controllers/FriendsController.cs +++ b/Govor.API/Controllers/FriendsController.cs @@ -21,7 +21,7 @@ public class FriendsController : Controller _friendsService = friendsService; } - [HttpGet("search")] + [HttpGet("search")] //api/friends/search public async Task Search(string query) { try @@ -37,7 +37,7 @@ public class FriendsController : Controller } } - [HttpPost("request")] + [HttpPost("request")] //api/friends/request public async Task SendRequest(Guid targetUserId) { try @@ -62,7 +62,7 @@ public class FriendsController : Controller } } - [HttpGet("requests")] + [HttpGet("requests")] //api/friends/requests public async Task GetIncomingRequests() { try @@ -78,7 +78,7 @@ public class FriendsController : Controller } } - [HttpPost("accept")] + [HttpPost("accept")] //api/friends/accept public async Task AcceptFriend(Guid requesterId) { try @@ -98,7 +98,7 @@ public class FriendsController : Controller } } - [HttpGet] + [HttpGet] //api/friends/ public async Task GetFriends() { try diff --git a/Govor.API/Hubs/ChatsHub.cs b/Govor.API/Hubs/ChatsHub.cs index b54a5cb..b959c3f 100644 --- a/Govor.API/Hubs/ChatsHub.cs +++ b/Govor.API/Hubs/ChatsHub.cs @@ -58,11 +58,6 @@ public class ChatsHub : Hub } var senderId = GetUserId(); - if (senderId == Guid.Empty) - { - _logger.LogError("Could not retrieve sender userId"); - throw new InvalidOperationException("User not authenticated"); - } // Проверка существования получателя try @@ -100,6 +95,7 @@ public class ChatsHub : Hub var userIdClaim = Context.User?.FindFirst("userID")?.Value; if (string.IsNullOrEmpty(userIdClaim) || !Guid.TryParse(userIdClaim, out var userId)) { + _logger.LogError("Could not retrieve sender userId"); throw new UnauthorizedAccessException("userID claim is missing or invalid"); } return userId; diff --git a/Govor.API/Program.cs b/Govor.API/Program.cs index bf1019a..1b9bad4 100644 --- a/Govor.API/Program.cs +++ b/Govor.API/Program.cs @@ -59,6 +59,11 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) builder.Services.AddAuthorization(); builder.Services.AddControllers(); +builder.Services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve; + }); // Init DI builder.Services.AddServices(); diff --git a/Govor.Console/FriendsClient.cs b/Govor.Console/FriendsClient.cs new file mode 100644 index 0000000..21d327a --- /dev/null +++ b/Govor.Console/FriendsClient.cs @@ -0,0 +1,54 @@ +using System.Net.Http; +using System.Net.Http.Json; +using Govor.Contracts.DTOs; + +namespace Govor.ConsoleClient +{ + public class FriendsClient + { + private readonly HttpClient _client; + + public FriendsClient(HttpClient client) + { + _client = client; + } + + public async Task> SearchAsync(string query) + { + var response = await _client.GetAsync($"/api/friends/search?query={query}"); + response.EnsureSuccessStatusCode(); + + return await response.Content.ReadFromJsonAsync>() ?? new List(); + } + + public async Task SendFriendRequestAsync(Guid targetUserId) + { + var response = await _client.PostAsync($"api/friends/request?targetUserId={targetUserId}", null); + response.EnsureSuccessStatusCode(); + } + + public async Task> GetIncomingRequestsAsync() + { + var response = await _client.GetAsync("/api/friends/requests"); + response.EnsureSuccessStatusCode(); + + return await response.Content.ReadFromJsonAsync>() ?? new List(); + } + + public async Task AcceptFriendRequestAsync(Guid requesterId) + { + var content = JsonContent.Create(requesterId); + var response = await _client.PostAsync("/api/friends/accept", content); + response.EnsureSuccessStatusCode(); + } + + public async Task> GetFriendsAsync() + { + var response = await _client.GetAsync("/api/friends"); + response.EnsureSuccessStatusCode(); + + return await response.Content.ReadFromJsonAsync>() ?? new List(); + } + } + +} diff --git a/Govor.Console/HttpClientService.cs b/Govor.Console/HttpClientService.cs new file mode 100644 index 0000000..93aa10f --- /dev/null +++ b/Govor.Console/HttpClientService.cs @@ -0,0 +1,65 @@ +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json; +using System.Threading.Tasks; +using Govor.Contracts.Requests; + +namespace Govor.ConsoleClient +{ + public class HttpClientService + { + private readonly HttpClient _client; + + public HttpClientService(string baseUrl) + { + _client = new HttpClient + { + BaseAddress = new Uri(baseUrl) + }; + } + + public async Task RegisterAsync(string username, string password, string inviteCode) + { + var request = new RegistrationRequest + { + Name = username, + Password = password, + InviteLink = inviteCode + }; + + var response = await _client.PostAsJsonAsync("/api/auth/register", request); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + return result?.Token; + } + + var error = await response.Content.ReadAsStringAsync(); + throw new Exception($"Registration failed: {error}"); + } + + public async Task LoginAsync(string username, string password) + { + var request = new LoginRequest + { + Name = username, + Password = password + }; + + var response = await _client.PostAsJsonAsync("/api/auth/login", request); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + return result?.Token; + } + + var error = await response.Content.ReadAsStringAsync(); + throw new Exception($"Login failed: {error}"); + } + } + + public class TokenResponse + { + public string Token { get; set; } + } +} diff --git a/Govor.Console/Program.cs b/Govor.Console/Program.cs index 126420e..817720a 100644 --- a/Govor.Console/Program.cs +++ b/Govor.Console/Program.cs @@ -1,187 +1,214 @@ using Microsoft.AspNetCore.SignalR.Client; using System.IdentityModel.Tokens.Jwt; +using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; using Govor.Contracts.Requests; /*==================================== - *Горелые Петушки Чат + *Личные сообщения| Егор *==================================== + * [15:59] Вы добавили (Егор) в друзья + * + * [16:30] Егор >> Привет! + * [16:31] Ты >> Че как? * - * [16:30] Егор >> Привет, Питухам! - * [16:31] Anon >> Че как? - * Я норм << [16:32] *------------------------------------ - * >> "Привет, Егор!" + * >> "Пойдем завтра гулять?!" */ -class Program +namespace Govor.ConsoleClient { - private static HttpClient _httpClient = new HttpClient(); - private static HubConnection _hubConnection; - private static string _jwtToken; - private static Guid _userId; - private static Guid _toUserId; - - static async Task Main(string[] args) + class Program { - Console.WriteLine("Welcome to the Chat Client!"); - Console.WriteLine("Enter your username:"); - string username = Console.ReadLine(); - Console.WriteLine("Enter your password:"); - string password = Console.ReadLine(); + static string? AuthToken = null; + static HttpClientService HttpService = new("https://localhost:7155"); // поменяй URL на свой + private static FriendsClient friendsClient; + static Dictionary> ChatHistory = new(); + static string CurrentChatUser = null; - // Аутентификация и получение JWT - bool authenticated = await AuthenticateAsync(username, password); - if (!authenticated) + static async Task Main() { - Console.WriteLine("Authentication failed. Exiting..."); - return; + LoginWindow(); + + while (true) + { + Console.Write(CurrentChatUser == null ? "/> " : ">> "); + var input = Console.ReadLine(); + + if (string.IsNullOrWhiteSpace(input)) continue; + + if (input.StartsWith("/")) + HandleCommand(input); + else if (CurrentChatUser != null) + SendMessage(input); + else + Console.WriteLine("[Система] Введите команду, например /friends"); + } } - Console.Clear(); - - // Извлечение UserId из JWT - _userId = ExtractUserIdFromJwt(_jwtToken); - Console.WriteLine($"Authenticated successfully! Your UserId: {_userId}"); - - // Настройка SignalR - await SetupSignalRAsync(); - - // Обработка команд - Console.WriteLine("Enter command /chat {username} to send a chat message"); - Console.WriteLine("Enter commands in format: /send {message}, in chat {username}"); - Console.WriteLine("Type 'exit' to quit."); - - while (true) + static async void HandleCommand(string input) { - string input = Console.ReadLine(); - if (input.ToLower() == "exit") - break; + var args = input.Split(' ', 2); + var command = args[0].ToLower(); + var argument = args.Length > 1 ? args[1] : null; - await ProcessCommandAsync(input); + switch (command) + { + case "/login": + Console.Write("username: "); + var loginUsername = Console.ReadLine(); + + Console.Write("password: "); + var loginPassword = Console.ReadLine(); + + try + { + AuthToken = await HttpService.LoginAsync(loginUsername, loginPassword); + HttpClient sharedClient = new(); + sharedClient.BaseAddress = new Uri("https://localhost:7155"); + sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken); + + friendsClient = new FriendsClient(sharedClient); + Console.WriteLine("[Успех] Вход выполнен. Токен сохранен."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + break; + case "/reg": + Console.Write("username: "); + var regUsername = Console.ReadLine(); + + Console.Write("password: "); + var regPassword = Console.ReadLine(); + + Console.Write("invitation: "); + var inviteCode = Console.ReadLine(); + + try + { + AuthToken = await HttpService.RegisterAsync(regUsername, regPassword, inviteCode); + HttpClient sharedClient = new(); + sharedClient.BaseAddress = new Uri("https://localhost:7155"); + sharedClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken); + + friendsClient = new FriendsClient(sharedClient); + Console.WriteLine("[Успех] Регистрация завершена. Токен сохранен."); + } + catch (Exception ex) + { + Console.WriteLine($"[Ошибка] {ex.Message}"); + } + break; + case "/search": + Console.Write("Введите имя друга: "); + var q = Console.ReadLine(); + var foundUsers = await friendsClient.SearchAsync(q); + foreach (var user in foundUsers) + { + Console.WriteLine($"{user.Username} [{user.Id}]"); + } + break; + + case "/exit": + Console.WriteLine("[Система] Выход..."); + Environment.Exit(0); + break; + + case "/friends": + var friends = await friendsClient.GetFriendsAsync(); + foreach (var f in friends) + { + Console.WriteLine($"{f.Username} | был онлайн: {f.WasOnline}"); + } + break; + case "/friend": + Console.Write("Введите ID пользователя, которому хотите отправить запрос: "); + var targetId = Guid.Parse(Console.ReadLine()); + await friendsClient.SendFriendRequestAsync(targetId); + Console.WriteLine("Запрос отправлен"); + break; + + case "/accept": + Console.Write("Введите ID пользователя, чью заявку принимаете: "); + var acceptId = Guid.Parse(Console.ReadLine()); + await friendsClient.AcceptFriendRequestAsync(acceptId); + Console.WriteLine("Принято"); + break; + case "/incoming": + var requests = await friendsClient.GetIncomingRequestsAsync(); + foreach (var r in requests) + { + Console.WriteLine($"Запрос от: {r.RequesterId} (добавьте через /accept {r.RequesterId})"); + } + break; + case "/chat": + if (string.IsNullOrEmpty(argument)) + { + Console.WriteLine("[Ошибка] Укажите имя друга"); + break; + } + + CurrentChatUser = argument; + ShowChat(argument); + break; + + case "/back": + CurrentChatUser = null; + break; + + default: + Console.WriteLine("[Ошибка] Неизвестная команда"); + break; + } } - // Отключение SignalR - await _hubConnection.StopAsync(); - } - - private static async Task AuthenticateAsync(string username, string password) - { - try + static void ShowChat(string user) { - RegistrationRequest loginData = new RegistrationRequest() - { - Name = username, - Password = password - }; + Console.Clear(); + Console.WriteLine($"/*===================================="); + Console.WriteLine($" * Личные сообщения | {user}"); + Console.WriteLine($" *===================================="); - var response = await _httpClient.PostAsJsonAsync( - "https://localhost:7155/api/Auth/login", - loginData); - - if (!response.IsSuccessStatusCode) + if (!ChatHistory.ContainsKey(user)) { - Console.WriteLine($"Login failed: {response.StatusCode}"); - return false; + ChatHistory[user] = new List + { + $"[15:59] Вы добавили ({user}) в друзья", + $"[16:30] {user} >> Привет!", + $"[16:31] Ты >> Че как?" + }; } - var json = await response.Content.ReadAsStringAsync(); - var tokenResponse = JsonSerializer.Deserialize(json, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + foreach (var msg in ChatHistory[user]) + Console.WriteLine(" * " + msg); - _jwtToken = tokenResponse.token; - return true; - } - catch (Exception ex) - { - Console.WriteLine($"Authentication error: {ex.Message}"); - return false; - } - } - - private static Guid ExtractUserIdFromJwt(string jwtToken) - { - try - { - var handler = new JwtSecurityTokenHandler(); - var token = handler.ReadJwtToken(jwtToken); - var userIdClaim = token.Claims.FirstOrDefault(c => c.Type == "sub" || c.Type == "userID")?.Value; - return Guid.TryParse(userIdClaim, out Guid userId) ? userId : Guid.Empty; - } - catch (Exception ex) - { - Console.WriteLine($"Error extracting UserId: {ex.Message}"); - return Guid.Empty; - } - } - - private static async Task SetupSignalRAsync() - { - _hubConnection = new HubConnectionBuilder() - .WithUrl("https://localhost:7155/api/chats", options => - { - options.AccessTokenProvider = () => Task.FromResult(_jwtToken); - }) - .WithAutomaticReconnect() - .Build(); - - _hubConnection.On("Receive", (message, senderId) => - { - Console.WriteLine($"[{DateTime.Now}] From {senderId}: {message}"); - }); - - try - { - await _hubConnection.StartAsync(); - Console.WriteLine("Connected to SignalR hub."); - } - catch (Exception ex) - { - Console.WriteLine($"SignalR connection error: {ex.Message}"); - } - } - - private static async Task ProcessCommandAsync(string input) - { - if (string.IsNullOrWhiteSpace(input)) - return; - - var parts = input.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (parts.Length < 3 || parts[0].ToLower() != "/send") - { - Console.WriteLine("Invalid command. Use: /send {toUserId} {message}"); - return; + Console.WriteLine(" *------------------------------------"); } - if (!Guid.TryParse(parts[1], out Guid toUserId)) + static void SendMessage(string message) { - Console.WriteLine("Invalid toUserId format. Must be a valid Guid."); - return; + if (string.IsNullOrWhiteSpace(message)) return; + + ChatHistory[CurrentChatUser].Add($"Ты >> {message}"); + Console.WriteLine($">> {message}"); } - string message = string.Join(" ", parts[2..]); - if (string.IsNullOrWhiteSpace(message)) + static void LoginWindow() { - Console.WriteLine("Message cannot be empty."); - return; - } - - try - { - await _hubConnection.InvokeAsync("Send", message, toUserId); - Console.WriteLine($"Message sent to {toUserId}"); - } - catch (Exception ex) - { - Console.WriteLine($"Error sending message: {ex.Message}"); + Console.WriteLine($"/*===================================="); + Console.WriteLine($" * Добро пожаловать в Govor!"); + Console.WriteLine($" *===================================="); + Console.WriteLine(" * /reg - создать аккаунт "); + Console.WriteLine(" * /login - войти"); } } } + public class LoginResponse { public string token { get; set; }