This commit is contained in:
Artemy
2025-06-27 21:50:23 +07:00
parent 05298c004c
commit 26dc4ccc1f
6 changed files with 302 additions and 155 deletions
+5 -5
View File
@@ -21,7 +21,7 @@ public class FriendsController : Controller
_friendsService = friendsService;
}
[HttpGet("search")]
[HttpGet("search")] //api/friends/search
public async Task<IActionResult> Search(string query)
{
try
@@ -37,7 +37,7 @@ public class FriendsController : Controller
}
}
[HttpPost("request")]
[HttpPost("request")] //api/friends/request
public async Task<IActionResult> SendRequest(Guid targetUserId)
{
try
@@ -62,7 +62,7 @@ public class FriendsController : Controller
}
}
[HttpGet("requests")]
[HttpGet("requests")] //api/friends/requests
public async Task<IActionResult> GetIncomingRequests()
{
try
@@ -78,7 +78,7 @@ public class FriendsController : Controller
}
}
[HttpPost("accept")]
[HttpPost("accept")] //api/friends/accept
public async Task<IActionResult> AcceptFriend(Guid requesterId)
{
try
@@ -98,7 +98,7 @@ public class FriendsController : Controller
}
}
[HttpGet]
[HttpGet] //api/friends/
public async Task<IActionResult> GetFriends()
{
try
+1 -5
View File
@@ -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;
+5
View File
@@ -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();
+54
View File
@@ -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<List<UserDto>> SearchAsync(string query)
{
var response = await _client.GetAsync($"/api/friends/search?query={query}");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<List<UserDto>>() ?? new List<UserDto>();
}
public async Task SendFriendRequestAsync(Guid targetUserId)
{
var response = await _client.PostAsync($"api/friends/request?targetUserId={targetUserId}", null);
response.EnsureSuccessStatusCode();
}
public async Task<List<FriendshipDto>> GetIncomingRequestsAsync()
{
var response = await _client.GetAsync("/api/friends/requests");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<List<FriendshipDto>>() ?? new List<FriendshipDto>();
}
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<List<UserDto>> GetFriendsAsync()
{
var response = await _client.GetAsync("/api/friends");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<List<UserDto>>() ?? new List<UserDto>();
}
}
}
+65
View File
@@ -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<string> 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<TokenResponse>();
return result?.Token;
}
var error = await response.Content.ReadAsStringAsync();
throw new Exception($"Registration failed: {error}");
}
public async Task<string> 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<TokenResponse>();
return result?.Token;
}
var error = await response.Content.ReadAsStringAsync();
throw new Exception($"Login failed: {error}");
}
}
public class TokenResponse
{
public string Token { get; set; }
}
}
+172 -145
View File
@@ -1,186 +1,213 @@
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]
*------------------------------------
* >> "Привет, Егор!"
* >> "Пойдем завтра гулять?!"
*/
namespace Govor.ConsoleClient
{
class Program
{
private static HttpClient _httpClient = new HttpClient();
private static HubConnection _hubConnection;
private static string _jwtToken;
private static Guid _userId;
private static Guid _toUserId;
static string? AuthToken = null;
static HttpClientService HttpService = new("https://localhost:7155"); // поменяй URL на свой
private static FriendsClient friendsClient;
static Dictionary<string, List<string>> ChatHistory = new();
static string CurrentChatUser = null;
static async Task Main(string[] args)
static async Task Main()
{
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();
// Аутентификация и получение JWT
bool authenticated = await AuthenticateAsync(username, password);
if (!authenticated)
{
Console.WriteLine("Authentication failed. Exiting...");
return;
}
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.");
LoginWindow();
while (true)
{
string input = Console.ReadLine();
if (input.ToLower() == "exit")
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");
}
}
static async void HandleCommand(string input)
{
var args = input.Split(' ', 2);
var command = args[0].ToLower();
var argument = args.Length > 1 ? args[1] : null;
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;
await ProcessCommandAsync(input);
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;
}
// Отключение SignalR
await _hubConnection.StopAsync();
CurrentChatUser = argument;
ShowChat(argument);
break;
case "/back":
CurrentChatUser = null;
break;
default:
Console.WriteLine("[Ошибка] Неизвестная команда");
break;
}
}
private static async Task<bool> AuthenticateAsync(string username, string password)
static void ShowChat(string user)
{
try
Console.Clear();
Console.WriteLine($"/*====================================");
Console.WriteLine($" * Личные сообщения | {user}");
Console.WriteLine($" *====================================");
if (!ChatHistory.ContainsKey(user))
{
RegistrationRequest loginData = new RegistrationRequest()
ChatHistory[user] = new List<string>
{
Name = username,
Password = password
$"[15:59] Вы добавили ({user}) в друзья",
$"[16:30] {user} >> Привет!",
$"[16:31] Ты >> Че как?"
};
var response = await _httpClient.PostAsJsonAsync(
"https://localhost:7155/api/Auth/login",
loginData);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine($"Login failed: {response.StatusCode}");
return false;
}
var json = await response.Content.ReadAsStringAsync();
var tokenResponse = JsonSerializer.Deserialize<LoginResponse>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
foreach (var msg in ChatHistory[user])
Console.WriteLine(" * " + msg);
_jwtToken = tokenResponse.token;
return true;
Console.WriteLine(" *------------------------------------");
}
catch (Exception ex)
static void SendMessage(string message)
{
Console.WriteLine($"Authentication error: {ex.Message}");
return false;
if (string.IsNullOrWhiteSpace(message)) return;
ChatHistory[CurrentChatUser].Add($"Ты >> {message}");
Console.WriteLine($">> {message}");
}
static void LoginWindow()
{
Console.WriteLine($"/*====================================");
Console.WriteLine($" * Добро пожаловать в Govor!");
Console.WriteLine($" *====================================");
Console.WriteLine(" * /reg - создать аккаунт ");
Console.WriteLine(" * /login - войти");
}
}
}
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<string, Guid>("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;
}
if (!Guid.TryParse(parts[1], out Guid toUserId))
{
Console.WriteLine("Invalid toUserId format. Must be a valid Guid.");
return;
}
string message = string.Join(" ", parts[2..]);
if (string.IsNullOrWhiteSpace(message))
{
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}");
}
}
}
public class LoginResponse
{