diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..409263e
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,30 @@
+
+
+
+
+ postgresql
+ true
+ org.postgresql.Driver
+ jdbc:postgresql://localhost:5432/postgres
+
+
+
+
+
+
+ $ProjectFileDir$
+
+
+ postgresql
+ true
+ org.postgresql.Driver
+ jdbc:postgresql://localhost:5432/postgres
+
+
+
+
+
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..601f39c
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
new file mode 100644
index 0000000..a7c53a0
--- /dev/null
+++ b/.idea/sqldialects.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Client/pom.xml b/Client/pom.xml
index e7ad365..d00cd2a 100644
--- a/Client/pom.xml
+++ b/Client/pom.xml
@@ -17,4 +17,17 @@
UTF-8
+
+
+ org.openjfx
+ javafx-controls
+ 23.0.2
+
+
+ org.openjfx
+ javafx-base
+ 23.0.2
+
+
+
\ No newline at end of file
diff --git a/Client/src/main/java/ru/project/ChatClientGUI.java b/Client/src/main/java/ru/project/ChatClientGUI.java
new file mode 100644
index 0000000..37d5a53
--- /dev/null
+++ b/Client/src/main/java/ru/project/ChatClientGUI.java
@@ -0,0 +1,115 @@
+package ru.project;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.VBox;
+import javafx.stage.Stage;
+import javafx.geometry.Insets;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+
+public class ChatClientGUI extends Application {
+ private TextArea chatArea;
+ private TextField inputField;
+ private Button sendButton;
+ private Socket socket;
+ private DataInputStream in;
+ private DataOutputStream out;
+ private boolean isBanned = false;
+
+ @Override
+ public void start(Stage primaryStage) {
+ chatArea = new TextArea();
+ chatArea.setEditable(false);
+ chatArea.setWrapText(true);
+
+ inputField = new TextField();
+ inputField.setPromptText("Введите сообщение...");
+
+ Button sendButton = new Button("Отправить");
+ sendButton.setOnAction(e -> sendMessage());
+
+ VBox root = new VBox(10,chatArea, inputField, sendButton);
+ root.setPadding(new Insets(10, 10, 10, 10));
+ Scene scene = new Scene(root, 400, 300);
+
+ primaryStage.setTitle("Чат-клиент");
+ primaryStage.setScene(scene);
+ primaryStage.show();
+
+ connectToServer();
+ }
+
+ public static void main(String[] args) {
+ launch(args);
+ }
+
+ private void sendMessage() {
+ String message = inputField.getText().trim();
+ if (!message.isEmpty() && !isBanned) {
+ try {
+ out.writeUTF(message); // Отправляем на сервер
+ inputField.clear(); // Очищаем поле
+ } catch (IOException e) {
+ chatArea.appendText("Ошибка отправки сообщения\n");
+ }
+ } else if (isBanned) {
+ chatArea.appendText("Вы не можете писать сообщения, так как заблокированы.\n");
+ }
+ }
+ private void connectToServer() {
+ try {
+ socket = new Socket("localhost", 8189);
+ in = new DataInputStream(socket.getInputStream());
+ out = new DataOutputStream(socket.getOutputStream());
+ chatArea.appendText("Подключено к серверу\n");
+
+ new Thread(() -> {
+ try {
+ while (true) {
+ String message = in.readUTF();
+ Platform.runLater(() -> processServerMessage(message));
+ }
+ } catch (Exception e) {
+ Platform.runLater(() -> chatArea.appendText(e.getMessage() + "\n"));
+ }
+ }).start();
+ } catch (Exception e) {
+ chatArea.appendText(e.getMessage() + "\n");
+ }
+ }
+ private void processServerMessage(String message) {
+ if (message.startsWith("/")) {
+ if (message.equalsIgnoreCase("/exitok")) {
+ disconnect();
+ } else if (message.startsWith("/authOK ")) {
+ chatArea.appendText("Авторизация успешна! Ваш ник: " + message.split(" ")[1] + "\n");
+ } else if (message.startsWith("/regOK ")) {
+ chatArea.appendText("Регистрация успешна! Ваш ник: " + message.split(" ")[1] + "\n");
+ } else if (message.equalsIgnoreCase("/banok")) {
+ isBanned = true;
+ chatArea.appendText("Вы были заблокированы администратором.\n");
+ } else if (message.equalsIgnoreCase("/unbanok")) {
+ isBanned = false;
+ chatArea.appendText("Вы теперь разбанены и можете писать сообщения.\n");
+ }
+ } else {
+ chatArea.appendText(message + "\n"); // Обычное сообщение
+ }
+ }
+ private void disconnect() {
+ try {
+ if (socket != null) socket.close();
+ if (in != null) in.close();
+ if (out != null) out.close();
+ chatArea.appendText("Отключено от сервера\n");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/Client/src/main/java/ru/project/Client.java b/Client/src/main/java/ru/project/Client.java
new file mode 100644
index 0000000..5d7f515
--- /dev/null
+++ b/Client/src/main/java/ru/project/Client.java
@@ -0,0 +1,96 @@
+package ru.project;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.Scanner;
+
+public class Client {
+ private Socket socket;
+ private DataOutputStream out;
+ private DataInputStream in;
+ private Scanner scanner;
+ private boolean isBanned = false;
+
+ public Client() throws IOException {
+ scanner = new Scanner(System.in);
+ socket = new Socket("localhost", 8189);
+ out = new DataOutputStream(socket.getOutputStream());
+ in = new DataInputStream(socket.getInputStream());
+
+ new Thread(() -> {
+ try {
+ while (!socket.isClosed()) {
+ String message = in.readUTF();
+ if (message.startsWith("/")) {
+ if (message.equalsIgnoreCase("/exitok")) {
+ break;
+ }
+ if (message.startsWith("/authOK ")) {
+ System.out.println("Авторизация прошла успешно! Имя пользователя: "
+ + message.split(" ")[1]);
+ }
+ if (message.startsWith("/regOK ")) {
+ System.out.println("Регистрация прошла успешно! Имя пользователя: "
+ + message.split(" ")[1]);
+ }
+ if (message.equalsIgnoreCase("/banok")) {
+ isBanned = true;
+ System.out.println("Вы были заблокированы администратором.");
+ }
+ if (message.equalsIgnoreCase("/unbanok")) {
+ isBanned = false;
+ System.out.println("Теперь вы не заблокированы. Вам доступна отправка сообщений в чат.");
+ }
+ } else {
+ System.out.println(message);
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ disconnect();
+ }
+ }).start();
+
+ while (!socket.isClosed()) {
+ String message = scanner.nextLine();
+ try {
+ if (!isBanned) {
+ out.writeUTF(message);
+ } else System.out.println("Вы не можете писать сообщения.");
+ } catch (SocketException e) {
+ System.out.println("Вы были отключены от сервера.");
+ }
+ if (message.equalsIgnoreCase("/exit")) {
+ break;
+ }
+ }
+ }
+
+ public void disconnect() {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ if (socket != null) {
+ socket.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/Client/src/main/java/ru/project/ClientApplication.java b/Client/src/main/java/ru/project/ClientApplication.java
new file mode 100644
index 0000000..e4252de
--- /dev/null
+++ b/Client/src/main/java/ru/project/ClientApplication.java
@@ -0,0 +1,14 @@
+package ru.project;
+
+import java.io.IOException;
+
+
+public class ClientApplication {
+ public static void main(String[] args) {
+ try {
+ new Client();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/Server/src/main/java/ru/project/Authenticator.java b/Server/src/main/java/ru/project/Authenticator.java
new file mode 100644
index 0000000..df49723
--- /dev/null
+++ b/Server/src/main/java/ru/project/Authenticator.java
@@ -0,0 +1,7 @@
+package ru.project;
+
+public interface Authenticator {
+ void initialize();
+ boolean authenticate(ClientHandler clientHandler, String login, String password);
+ boolean registration(ClientHandler clientHandler, String username, String login, String password);
+}
diff --git a/Server/src/main/java/ru/project/ClientHandler.java b/Server/src/main/java/ru/project/ClientHandler.java
index 6bdca6f..c15ac90 100644
--- a/Server/src/main/java/ru/project/ClientHandler.java
+++ b/Server/src/main/java/ru/project/ClientHandler.java
@@ -4,44 +4,143 @@
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
+import java.text.SimpleDateFormat;
+import java.util.Date;
public class ClientHandler {
- private Socket socket;
- private Server server;
- private DataInputStream in;
- private DataOutputStream out;
-
+ private final Socket socket;
+ private final Server server;
+ private final DataInputStream in;
+ private final DataOutputStream out;
private String username;
- private static int userCount = 0;
-
+ private Role role;
+ private final long lastActiveTime;
+ private String currentRoom = null;
+ boolean banFlag;
public ClientHandler(Socket socket, Server server) throws IOException {
this.socket = socket;
this.server = server;
this.in = new DataInputStream(socket.getInputStream());
this.out = new DataOutputStream(socket.getOutputStream());
-
- userCount++;
- username = "user" + userCount;
+ this.lastActiveTime = System.currentTimeMillis();
new Thread(() -> {
try {
System.out.println("Клиент подключился " + socket.getPort());
-
+ //Цикл логина
while (true) {
+ sendMsg("""
+ Для начала работы надо пройти аутентификацию или регистрацию
+ Формат команды для аутентификации: /log
+ Формат команды для регистрации: /reg
+ Для выхода используйте комманду /exit""");
String message = in.readUTF();
+ if (message.startsWith("/")) {
+ if (message.equals("/exit")) {
+ sendMsg("/exitOK");
+ break;
+ }
+ if (message.equalsIgnoreCase("/log")) {
+ String[] loginData = new String[2];
+ sendMsg("Введите логин: ");
+ loginData[0] = in.readUTF();
+ sendMsg("Введите пароль: ");
+ loginData[1] = in.readUTF();
+ if (server.getAuthenticator()
+ .authenticate(this, loginData[0], loginData[1])) {
+ break;
+ } else {
+ sendMsg("Ошибка авторизации!");
+ continue;
+ }
+ }
+ if (message.equalsIgnoreCase("/reg")) {
+ String[] regData = new String[3];
+ sendMsg("""
+ Введите никнейм.
+ Никнейм не должен содержать более 20 символов, и не менее 5 символов
+ Можно использовать цифры, латинские буквы и знаки "_" и "-\"""");
+ regData[0] = in.readUTF();
+ sendMsg("""
+ Введите логин.
+ Логин не должен содержать более 20 символов, и не менее 5 символов
+ Можно использовать цифры, латинские буквы и знаки "_" и "-\"""");
+ regData[1] = in.readUTF();
+ sendMsg("Введите пароль.\n" +
+ "Пароль должен содержать от 5 до 20 символов и обязательно включать в себя любой специальный символ.");
+ regData[2] = in.readUTF();
+ if (server.getAuthenticator()
+ .registration(this, regData[0], regData[1], regData[2])) {
+ break;
+ } else {
+ sendMsg("Ошибка регистрации!");
+ }
+ }
+ }
+ else {
+ sendMsg("Неверная команда");
+ }
+ }
+ //Цикл работы
+ while (true) {
+ String message = in.readUTF();
+ this.banFlag = false;
if (message.startsWith("/")) {
if (message.equalsIgnoreCase("/exit")) {
sendMsg("/exitok");
break;
+ } else if (message.startsWith("/w ")) {
+ String[] tokens = message.split(" ", 3);
+ if (tokens.length == 3) {
+ String recipient = tokens[1];
+ String privateMessage = tokens[2];
+ server.sendPrivateMessage(this, recipient, privateMessage);
+ } else {
+ sendMsg("Неверный формат комманды. Используйте /w <никнейм> <сообщение>");
+ }
+ } else if (message.startsWith("/kick ")) {
+ String[] tokens = message.split(" ", 2);
+ if (tokens.length != 2) {
+ sendMsg("Неверный формат команды /kick");
+ continue;
+ }
+ server.kickUser(tokens[1], this);
+ } else if (message.startsWith("/ban ")) {
+ String[] tokens = message.split(" ", 2);
+ if (tokens.length != 2) {
+ sendMsg("Неверный формат команды /ban");
+ continue;
+ }
+ server.banUser(tokens[1], this);
+ } else if (message.startsWith("/unban ")) {
+ String[] tokens = message.split(" ", 2);
+ if (tokens.length != 2) {
+ sendMsg("Неверный формат комманды /unban");
+ continue;
+ }
+ server.unbanUser(tokens[1], this);
+ } else if (message.equalsIgnoreCase("/online")) {
+ sendMsg(server.getOnlineUsers());
+ } else if (message.startsWith("/createroom ")) {
+ String roomName = message.substring(12).trim();
+ server.createRoom(roomName, this);
+ } else if (message.startsWith("/room ")) {
+ String roomName = message.substring(6).trim();
+ server.joinRoom(roomName, this);
+ } else if (message.equals("/exitroom")) {
+ server.exitRoom(this);
}
+ } else if (currentRoom != null) {
+ server.sendToRoom(currentRoom, "[" + getCurrentTime() + "] " + username + ": " + message);
} else {
- server.broadcastMessage(username + " : " + message);
+ String messageWithTime = "[" + getCurrentTime() + "]" + username + " : " + message;
+ server.broadcastMessage(messageWithTime);
}
}
} catch (IOException e) {
- e.printStackTrace();
+ System.out.println("Сокет закрыт, клиент на порту " + socket.getPort() + " отключен.");
} finally {
disconnect();
}
@@ -84,4 +183,31 @@ public void disconnect() {
public String getUsername() {
return username;
}
+ public void setUsername(String username) {
+ this.username = username;
+ }
+ public void setRole(Role role) {
+ this.role = role;
+ }
+ public Role getRole() {
+ return role;
+ }
+ private String getCurrentTime() {
+ return new SimpleDateFormat("HH:mm:ss").format(new Date());
+ }
+ public long getLastActiveTime() {
+ return lastActiveTime;
+ }
+ public void setCurrentRoom(String roomName) {
+ this.currentRoom = roomName;
+ }
+ public String getCurrentRoom() {
+ return currentRoom;
+ }
+ public boolean isBanned() {
+ return banFlag;
+ }
+ public void changeBanFlag() {
+ banFlag = !banFlag;
+ }
}
diff --git a/Server/src/main/java/ru/project/Role.java b/Server/src/main/java/ru/project/Role.java
new file mode 100644
index 0000000..b3efc8a
--- /dev/null
+++ b/Server/src/main/java/ru/project/Role.java
@@ -0,0 +1,6 @@
+package ru.project;
+
+public enum Role {
+ USER,
+ ADMIN;
+}
diff --git a/Server/src/main/java/ru/project/Server.java b/Server/src/main/java/ru/project/Server.java
index 6fe59bc..29c4a6e 100644
--- a/Server/src/main/java/ru/project/Server.java
+++ b/Server/src/main/java/ru/project/Server.java
@@ -1,5 +1,9 @@
package ru.project;
+import ru.project.checktime.CheckTime;
+import ru.project.database.DatabaseManager;
+import ru.project.rooms.Rooms;
+
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
@@ -9,18 +13,25 @@
public class Server {
private int port;
private List clients;
+ private Authenticator authenticator;
+ private final DatabaseManager databaseManager;
+ private final Rooms rooms = new Rooms();
- public Server(int port) {
+ public Server(int port, DatabaseManager databaseManager) {
this.port = port;
+ this.databaseManager = databaseManager;
clients = new CopyOnWriteArrayList<>();
- }
+ authenticator = new inMemoryAuthenticator(this);
+ new CheckTime(this).start();
+ }
public void start(){
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("Сервер запущен на порту: " + port);
+ authenticator.initialize();
while (true) {
Socket socket = serverSocket.accept();
- subscribe(new ClientHandler(socket, this));
+ new ClientHandler(socket, this);
}
} catch (IOException e) {
@@ -30,11 +41,14 @@ public void start(){
public void subscribe(ClientHandler clientHandler){
clients.add(clientHandler);
+ broadcastMessage("В чат зашел: " + clientHandler.getUsername());
+ System.out.println("[SERVER] Пользователь " + clientHandler.getUsername() + " подключился.");
}
public void unsubscribe(ClientHandler clientHandler){
clients.remove(clientHandler);
broadcastMessage("Из чата вышел: "+ clientHandler.getUsername());
+ System.out.println("[SERVER] Клиент " + clientHandler.getUsername() + " отключён.");
}
public void broadcastMessage(String message){
@@ -42,4 +56,110 @@ public void broadcastMessage(String message){
c.sendMsg(message);
}
}
+ public void sendPrivateMessage(ClientHandler sender, String recipient, String message) {
+ for (ClientHandler client : clients) {
+ if (client.getUsername().equals(recipient)) {
+ client.sendMsg(sender.getUsername() + ": " + message);
+ sender.sendMsg(recipient + ": " + message);
+ return;
+ }
+ }
+ sender.sendMsg("Ошибка: пользователь с ником " + recipient + " не найден.");
+ }
+ public boolean isUserLoggedIn(String username){
+ for (ClientHandler client : clients) {
+ if (client.getUsername().equals(username)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ public Authenticator getAuthenticator() {
+ return authenticator;
+ }
+ public void kickUser(String usernameToKick, ClientHandler adminHandler) {
+ if (adminHandler.getRole().equals(Role.ADMIN)) {
+ for (ClientHandler client : clients) {
+ if (client.getUsername().equals(usernameToKick)) {
+ client.sendMsg("/exitok");
+ broadcastMessage("Пользователь " + usernameToKick + " был отключён администратором");
+ return;
+ }
+ }
+ adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToKick + " не найден");
+ } else {
+ adminHandler.sendMsg("Недостаточно прав");
+ }
+ }
+ public void banUser(String usernameToBan, ClientHandler adminHandler) {
+ if (adminHandler.getRole().equals(Role.ADMIN)) {
+ for (ClientHandler client : clients) {
+ if (client.getUsername().equals(usernameToBan)) {
+ if (client.isBanned()) {
+ adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToBan + " уже забанен!");
+ } else {
+ client.sendMsg("/banok");
+ broadcastMessage("Пользователь " + usernameToBan + " был забанен администратором");
+ client.changeBanFlag();
+ }
+ return;
+ }
+ }
+ adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToBan + " не найден");
+ } else {
+ adminHandler.sendMsg("Недостаточно прав");
+ }
+ }
+ public void unbanUser(String usernameToUnban, ClientHandler adminHandler) {
+ if (adminHandler.getRole().equals(Role.ADMIN)) {
+ for (ClientHandler client : clients) {
+ if (client.getUsername().equals(usernameToUnban)) {
+ if (!client.isBanned()) {
+ adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToUnban + " не находится в бане");
+ } else {
+ client.sendMsg("/unbanok");
+ broadcastMessage("Пользователь " + usernameToUnban + " был разбанен администратором");
+ client.changeBanFlag();
+ }
+ return;
+ }
+ }
+ adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToUnban + " не найден");
+ } else {
+ adminHandler.sendMsg("Недостаточно прав");
+ }
+ }
+ public DatabaseManager getDatabaseManager() {
+ return databaseManager;
+ }
+
+ public String getOnlineUsers() {
+ StringBuilder sb = new StringBuilder("Сейчас онлайн:\n");
+ for (ClientHandler client : clients) {
+ sb.append(client.getUsername()).append("\n");
+ }
+ return sb.toString();
+ }
+ public List getClients() {
+ return this.clients;
+ }
+ public void createRoom(String roomName, ClientHandler owner) {
+ rooms.createRoom(roomName, owner);
+ }
+
+ public void joinRoom(String roomName, ClientHandler client) {
+ rooms.joinRoom(roomName, client);
+ }
+
+ public void exitRoom(ClientHandler client) {
+ rooms.exitRoom(client);
+ }
+
+ public void sendToRoom(String roomName, String message) {
+ rooms.sendToRoom(roomName, message);
+ }
+
+ public boolean isUserInRoom(ClientHandler client) {
+ return rooms.isUserInRoom(client);
+ }
}
diff --git a/Server/src/main/java/ru/project/ServerApplication.java b/Server/src/main/java/ru/project/ServerApplication.java
index 2dbdb42..f44d85c 100644
--- a/Server/src/main/java/ru/project/ServerApplication.java
+++ b/Server/src/main/java/ru/project/ServerApplication.java
@@ -1,7 +1,9 @@
package ru.project;
+import ru.project.database.DatabaseManager;
+
public class ServerApplication {
public static void main(String[] args) {
- new Server(8189).start();
+ new Server(8189, new DatabaseManager()).start();
}
}
diff --git a/Server/src/main/java/ru/project/checktime/CheckTime.java b/Server/src/main/java/ru/project/checktime/CheckTime.java
new file mode 100644
index 0000000..60aad48
--- /dev/null
+++ b/Server/src/main/java/ru/project/checktime/CheckTime.java
@@ -0,0 +1,32 @@
+package ru.project.checktime;
+
+import ru.project.*;
+
+public class CheckTime extends Thread {
+ private static final long TIMEOUT = 20 * 60 * 1000;
+ private final Server server;
+
+ public CheckTime(Server server) {
+ this.server = server;
+ }
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ Thread.sleep(60*1000);
+ long currentTime = System.currentTimeMillis();
+
+ for (ClientHandler client : server.getClients()) {
+ if (currentTime - client.getLastActiveTime() > TIMEOUT) {
+ System.out.println("Отключение неактивного клиента: " + client.getUsername());
+ client.sendMsg("/exitok");
+ server.unsubscribe(client);
+ client.disconnect();
+ }
+ }
+ } catch (InterruptedException e) {
+ server.broadcastMessage("Ошибка CheckTime");
+ }
+ }
+ }
+}
diff --git a/Server/src/main/java/ru/project/database/DatabaseManager.java b/Server/src/main/java/ru/project/database/DatabaseManager.java
new file mode 100644
index 0000000..cdc8cac
--- /dev/null
+++ b/Server/src/main/java/ru/project/database/DatabaseManager.java
@@ -0,0 +1,120 @@
+package ru.project.database;
+
+import ru.project.Role;
+import java.sql.*;
+
+public class DatabaseManager {
+ private static final String DB_URL = "jdbc:postgresql://localhost:5432/postgres?currentSchema=public&user=admin&password=admin";
+
+
+ public DatabaseManager() {
+ try (Connection conn = DriverManager.getConnection(DB_URL);
+ Statement stmt = conn.createStatement()) {
+ String createTableSQL = """
+ CREATE TABLE IF NOT EXISTS users (
+ id SERIAL PRIMARY KEY,
+ username TEXT NOT NULL UNIQUE,
+ login TEXT NOT NULL UNIQUE,
+ password TEXT NOT NULL,
+ role TEXT NOT NULL
+ );
+ """;
+ String addAdmin = """
+ INSERT INTO users (username, login, password, role)
+ SELECT 'admin', 'admin', 'admin', 'ADMIN'
+ WHERE NOT EXISTS (SELECT 1 FROM users WHERE username = 'admin');
+ """;
+ stmt.execute(createTableSQL);
+ stmt.executeUpdate(addAdmin);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ public String authenticate(String login, String password) {
+ String query = "SELECT username FROM users WHERE login = ? AND password = ?";
+ try (Connection conn = DriverManager.getConnection(DB_URL);
+ PreparedStatement pstmt = conn.prepareStatement(query)) {
+ pstmt.setString(1, login);
+ pstmt.setString(2, password);
+ ResultSet rs = pstmt.executeQuery();
+ if (rs.next()) {
+ return rs.getString("username");
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ public boolean registerUser(String username, String login, String password, Role role) {
+ String query = "INSERT INTO users (username, login, password, role) VALUES (?, ?, ?, ?)";
+ try (Connection conn = DriverManager.getConnection(DB_URL);
+ PreparedStatement pstmt = conn.prepareStatement(query)) {
+ pstmt.setString(1, username);
+ pstmt.setString(2, login);
+ pstmt.setString(3, password);
+ pstmt.setString(4, String.valueOf(role));
+ pstmt.executeUpdate();
+ return true;
+ } catch (SQLException e) {
+ if (e.getMessage().contains("UNIQUE")) {
+ System.out.println("Логин или имя пользователя уже существуют.");
+ } else {
+ e.printStackTrace();
+ }
+ return false;
+ }
+ }
+ public String getUserRole(String username) {
+ String query = "SELECT role FROM users WHERE username = ?";
+ try (Connection conn = DriverManager.getConnection(DB_URL);
+ PreparedStatement pstmt = conn.prepareStatement(query)) {
+ pstmt.setString(1, username);
+ ResultSet rs = pstmt.executeQuery();
+ if (rs.next()) {
+ return rs.getString("role");
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ public boolean isUsernameAlreadyExist(String username) {
+ String query = "SELECT 1 FROM users WHERE username = ?";
+ try (Connection conn = DriverManager.getConnection(DB_URL);
+ PreparedStatement pstmt = conn.prepareStatement(query)) {
+ pstmt.setString(1, username);
+ ResultSet rs = pstmt.executeQuery();
+ return rs.next();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ public boolean isLoginAlreadyExist(String login) {
+ String query = "SELECT 1 FROM users WHERE login = ?";
+ try (Connection conn = DriverManager.getConnection(DB_URL);
+ PreparedStatement pstmt = conn.prepareStatement(query)) {
+ pstmt.setString(1, login);
+ ResultSet rs = pstmt.executeQuery();
+ return rs.next();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ public String getUsernameByLoginAndPassword(String login, String password) {
+ String query = "SELECT username FROM users WHERE username = ? AND password = ?";
+ try (Connection conn = DriverManager.getConnection(DB_URL);
+ PreparedStatement pstmt = conn.prepareStatement(query)) {
+ pstmt.setString(1, login);
+ pstmt.setString(2, password);
+ ResultSet rs = pstmt.executeQuery();
+ if (rs.next()) {
+ return rs.getString("username");
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/Server/src/main/java/ru/project/inMemoryAuthenticator.java b/Server/src/main/java/ru/project/inMemoryAuthenticator.java
new file mode 100644
index 0000000..bd3a72e
--- /dev/null
+++ b/Server/src/main/java/ru/project/inMemoryAuthenticator.java
@@ -0,0 +1,86 @@
+package ru.project;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class inMemoryAuthenticator implements Authenticator {
+ public static class User {
+ private final String username;
+ private final String login;
+ private final String password;
+
+ public User(String username, String login, String password) {
+ this.username = username;
+ this.login = login;
+ this.password = password;
+ }
+ }
+ private final List users;
+ private final Server server;
+ public inMemoryAuthenticator(Server server) {
+ users = new CopyOnWriteArrayList<>();
+ this.server = server;
+ users.add(new User("admin", "admin", "admin"));
+ }
+ @Override
+ public void initialize() {
+ System.out.println("Инициализация");
+ }
+ @Override
+ public boolean authenticate(ClientHandler clientHandler, String login, String password) {
+ String username = server.getDatabaseManager().getUsernameByLoginAndPassword(login, password);
+ if (username == null) {
+ clientHandler.sendMsg("Неверный логин и/или пароль");
+ return false;
+ }
+ if (server.isUserLoggedIn(username)) {
+ clientHandler.sendMsg("Данный пользователь уже в сети");
+ return false;
+ }
+ clientHandler.setUsername(server.getDatabaseManager().authenticate(login, password));
+ if (server.getDatabaseManager().getUserRole(username).equals("ADMIN")) {
+ clientHandler.setRole(Role.ADMIN);
+ } else {
+ clientHandler.setRole(Role.USER);
+ }
+ server.subscribe(clientHandler);
+ clientHandler.sendMsg("/authOK " + username);
+ return true;
+ }
+ @Override
+ public boolean registration(ClientHandler clientHandler, String username, String login, String password) {
+ String regex = "^[a-zA-Z0-9_-]{5,20}$";
+ String regexForPassword = "^(?=.*[!@#$%^&*(),.?\":{}|<>])[a-zA-Z0-9!@#$%^&*(),.?\":{}|<>]{5,20}$";
+ if(!username.matches(regex)) {
+ clientHandler.sendMsg("Убедитесь, что имя пользователя соответствует требованиям");
+ return false;
+ }
+ if(!login.matches(regex)) {
+ clientHandler.sendMsg("Убедитесь, что логин соответствует требованиям");
+ return false;
+ }
+ if (!password.matches(regexForPassword)) {
+ clientHandler.sendMsg("Убедитесь, что пароль соответствует требованиям");
+ return false;
+ }
+ if (server.getDatabaseManager().isLoginAlreadyExist(login)) {
+ clientHandler.sendMsg("Такой логин уже используется");
+ return false;
+ }
+ if (server.getDatabaseManager().isUsernameAlreadyExist(username)) {
+ clientHandler.sendMsg("Такое имя пользователя уже используется");
+ return false;
+ }
+ server.getDatabaseManager().registerUser(username, login, password, Role.USER);
+ users.add(new User(username, login, password));
+ clientHandler.setUsername(username);
+ if (clientHandler.getUsername().equals("admin")) {
+ clientHandler.setRole(Role.ADMIN);
+ } else {
+ clientHandler.setRole(Role.USER);
+ }
+ server.subscribe(clientHandler);
+ clientHandler.sendMsg("/regOK " + username);
+ return true;
+ }
+}
diff --git a/Server/src/main/java/ru/project/rooms/Rooms.java b/Server/src/main/java/ru/project/rooms/Rooms.java
new file mode 100644
index 0000000..bcd8a5b
--- /dev/null
+++ b/Server/src/main/java/ru/project/rooms/Rooms.java
@@ -0,0 +1,62 @@
+package ru.project.rooms;
+
+import ru.project.ClientHandler;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class Rooms {
+ private final Map> rooms = new ConcurrentHashMap<>();
+
+ public void createRoom(String roomName, ClientHandler owner) {
+ if (rooms.containsKey(roomName)) {
+ owner.sendMsg("Комната с таким именем уже существует.");
+ return;
+ }
+ if (owner.getCurrentRoom() != null) {
+ owner.sendMsg("Вы уже создали комнату: " + owner.getCurrentRoom());
+ return;
+ }
+ rooms.put(roomName, new CopyOnWriteArrayList<>(List.of(owner)));
+ owner.setCurrentRoom(roomName);
+ owner.sendMsg("Вы создали комнату: " + roomName);
+ }
+
+ public void joinRoom(String roomName, ClientHandler client) {
+ if (!rooms.containsKey(roomName)) {
+ client.sendMsg("Комната " + roomName + " не существует.");
+ return;
+ }
+ if (client.getCurrentRoom() != null) {
+ client.sendMsg("Вы уже находитесь в комнате: " + client.getCurrentRoom());
+ return;
+ }
+ rooms.get(roomName).add(client);
+ client.setCurrentRoom(roomName);
+ sendToRoom(roomName, client.getUsername() + " присоединился к комнате.");
+ }
+
+ public void exitRoom(ClientHandler client) {
+ String roomName = client.getCurrentRoom();
+ if (roomName == null) {
+ client.sendMsg("Вы не находитесь в комнате.");
+ return;
+ }
+ rooms.get(roomName).remove(client);
+ client.setCurrentRoom(null);
+ sendToRoom(roomName, client.getUsername() + " покинул комнату.");
+ client.sendMsg("Вы вышли из комнаты.");
+ }
+ public void sendToRoom(String roomName, String message) {
+ if (!rooms.containsKey(roomName)) return;
+ for (ClientHandler client : rooms.get(roomName)) {
+ client.sendMsg(message);
+ }
+ }
+
+ public boolean isUserInRoom(ClientHandler client) {
+ return client.getCurrentRoom() != null;
+ }
+}
diff --git a/pom.xml b/pom.xml
index 7271f2e..4e0fbfe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,11 +12,19 @@
Server
Client
-
+
+
+
+ org.postgresql
+ postgresql
+ 42.7.4
+
+
23
23
UTF-8
+
\ No newline at end of file