diff --git a/build.gradle b/build.gradle index 7c4e380..96bc6d1 100644 --- a/build.gradle +++ b/build.gradle @@ -40,9 +40,11 @@ dependencies { implementation('com.dlsc.formsfx:formsfx-core:11.6.0') { exclude group: 'org.openjfx' } + implementation 'org.postgresql:postgresql:42.7.3' implementation('net.synedra:validatorfx:0.5.0') { exclude group: 'org.openjfx' } + implementation 'org.json:json:20231013' implementation 'org.kordamp.ikonli:ikonli-javafx:12.3.1' implementation 'org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0' implementation('eu.hansolo:tilesfx:21.0.3') { diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4d8e706..f20151f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -9,6 +9,8 @@ module org.to.telegramfinalproject { requires org.kordamp.ikonli.javafx; requires org.kordamp.bootstrapfx.core; requires eu.hansolo.tilesfx; + requires org.json; + requires java.sql; opens org.to.telegramfinalproject to javafx.fxml; exports org.to.telegramfinalproject; } \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java new file mode 100644 index 0000000..91b5db1 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -0,0 +1,218 @@ +package org.to.telegramfinalproject.Client; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.to.telegramfinalproject.Models.ChatEntry; + +public class ActionHandler { + private final PrintWriter out; + private final BufferedReader in; + private final Scanner scanner; + + public ActionHandler(PrintWriter out, BufferedReader in, Scanner scanner) { + this.out = out; + this.in = in; + this.scanner = scanner; + } + + public void loginHandler() { + System.out.println("Login form: \n"); + System.out.println("Username: "); + String username = this.scanner.nextLine(); + System.out.println("Password: "); + String password = this.scanner.nextLine(); + JSONObject request = new JSONObject(); + request.put("action", "login"); + request.put("user_id", JSONObject.NULL); + request.put("username", username); + request.put("password", password); + request.put("profile_name", JSONObject.NULL); + this.send(request); + } + + public void register() { + System.out.println("Register form: \n"); + System.out.println("Username: "); + String username = this.scanner.nextLine(); + System.out.println("User id: "); + String user_id = this.scanner.nextLine(); + System.out.println("Password: "); + String password = this.scanner.nextLine(); + System.out.println("Profile name: "); + String profile_name = this.scanner.nextLine(); + JSONObject request = new JSONObject(); + request.put("action", "register"); + request.put("user_id", user_id); + request.put("username", username); + request.put("password", password); + request.put("profile_name", profile_name); + this.send(request); + } + + + public void search(){ + + System.out.print("Enter keyword to search: "); + String keyword = scanner.nextLine(); + + JSONObject request = new JSONObject(); + request.put("action", "search"); + request.put("keyword", keyword); + + send(request); + + } + + private void send(JSONObject request) { + try { + if (!request.has("action") || request.isNull("action")) { + System.err.println("Error: Request does not contain 'action'."); + return; + } + + String action = request.getString("action"); + + this.out.println(request.toString()); + + String responseText = this.in.readLine(); + + if (responseText != null) { + JSONObject response = new JSONObject(responseText); + System.out.println("Server response: " + response.getString("message")); + String status = response.getString("status"); + + if (status.equals("success") && response.has("data") && !response.isNull("data")) { + switch (action) { + case "login": + case "register": + Session.currentUser = response.getJSONObject("data"); + JSONArray chatListJson = Session.currentUser.getJSONArray("chat_list"); + List chatList = new ArrayList<>(); + + for (Object obj : chatListJson) { + JSONObject chat = (JSONObject) obj; + ChatEntry entry = new ChatEntry( + chat.getString("id"), + chat.getString("name"), + chat.getString("image_url"), + chat.getString("type"), + chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")) + ); + chatList.add(entry); + } + + Session.chatList = chatList; + break; + + case "search": + JSONArray results = response.getJSONObject("data").getJSONArray("results"); + + if (results.isEmpty()) { + System.out.println("No results found."); + } else { + System.out.println("\nSearch Results:"); + for (Object obj : results) { + JSONObject item = (JSONObject) obj; + System.out.println("- [" + item.getString("type") + "] " + item.getString("name") + " (ID: " + item.getString("id") + ")"); + } + } + break; + + case "get_messages": + break; + } + } + + } else { + System.out.println("No response from server."); + } + + } catch (IOException e) { + System.err.println("Error while communicating with server: " + e.getMessage()); + } catch (Exception e) { + System.err.println("Client error: " + e.getMessage()); + e.printStackTrace(); + } + } + + + + + public void userMenu() { + while (true) { + System.out.println("\nUser Menu:"); + System.out.println("1. Show chat list"); + System.out.println("2. Search"); + System.out.println("3. Logout"); + + System.out.print("Choose an option: "); + String choice = scanner.nextLine(); + + switch (choice) { + + case "1": + showChatListAndSelect(); + break; + case "2" : + search(); + break; + + case "3": + Session.currentUser = null; + Session.chatList = null; + System.out.println("Logged out."); + return; + default: + System.out.println("Invalid choice."); + } + } + } + + + + + public void showChatListAndSelect() { + if (Session.chatList == null || Session.chatList.isEmpty()) { + System.out.println("No chats available."); + return; + } + + System.out.println("\nYour Chats:"); + for (int i = 0; i < Session.chatList.size(); i++) { + ChatEntry entry = Session.chatList.get(i); + String time = entry.getLastMessageTime() == null ? "No messages yet" : entry.getLastMessageTime().toString(); + System.out.println((i + 1) + ". [" + entry.getType() + "] " + entry.getName() + " - Last: " + time); + } + + System.out.print("Select a chat by number: "); + int choice = Integer.parseInt(scanner.nextLine()) - 1; + + if (choice < 0 || choice >= Session.chatList.size()) { + System.out.println("Invalid selection."); + return; + } + + ChatEntry selected = Session.chatList.get(choice); + openChat(selected); + } + + + private void openChat(ChatEntry chat) { + JSONObject request = new JSONObject(); + request.put("action", "get_messages"); + request.put("receiver_id", chat.getId()); + request.put("receiver_type", chat.getType()); + + send(request); + } + +} + diff --git a/src/main/java/org/to/telegramfinalproject/Client/ClientConnection.java b/src/main/java/org/to/telegramfinalproject/Client/ClientConnection.java new file mode 100644 index 0000000..870796d --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Client/ClientConnection.java @@ -0,0 +1,30 @@ +package org.to.telegramfinalproject.Client; + +import java.io.*; +import java.net.Socket; + +public class ClientConnection { + private Socket socket; + private BufferedReader in; + private PrintWriter out; + + public ClientConnection(String host, int port) throws IOException { + this.socket = new Socket(host, port); + this.in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + this.out = new PrintWriter(socket.getOutputStream(), true); + } + + public void send(String request) { + out.println(request); + } + + public String receive() throws IOException { + return in.readLine(); + } + + public void close() throws IOException { + socket.close(); + in.close(); + out.close(); + } +} \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Client/Session.java b/src/main/java/org/to/telegramfinalproject/Client/Session.java new file mode 100644 index 0000000..9bf0d73 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Client/Session.java @@ -0,0 +1,13 @@ +package org.to.telegramfinalproject.Client; + +import org.json.JSONObject; +import org.to.telegramfinalproject.Models.ChatEntry; + +import java.util.List; + + +// method for save data from server response +public class Session { + public static JSONObject currentUser; + public static List chatList; +} \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Client/TelegramClient.java b/src/main/java/org/to/telegramfinalproject/Client/TelegramClient.java new file mode 100644 index 0000000..bd56550 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Client/TelegramClient.java @@ -0,0 +1,91 @@ +package org.to.telegramfinalproject.Client; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.Scanner; + +public class TelegramClient { + private static final String SERVER_HOST = "localhost"; + private static final int SERVER_PORT = 12345; + private Socket socket; + private BufferedReader in; + private PrintWriter out; + private final Scanner scanner; + ActionHandler handler = null; + + public TelegramClient() { + this.scanner = new Scanner(System.in); + } + + public void start() { + try { + this.socket = new Socket(SERVER_HOST, SERVER_PORT); + this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); + this.out = new PrintWriter(this.socket.getOutputStream(), true); + System.out.println(" Connected to Telegram Server"); + this.handler = new ActionHandler(this.out, this.in, this.scanner); + this.showMainMenu(); + } catch (IOException e) { + System.err.println("Error connecting to server: " + e.getMessage()); + } + + } + + private void showMainMenu() { + while(true) { + System.out.println("Main Menu:"); + System.out.println("1. Register"); + System.out.println("2. Login"); + System.out.println("3. Exit"); + System.out.print("Choose an option: "); + switch (this.scanner.nextLine()) { + case "1": + this.handler.register(); + break; + case "2": + this.handler.loginHandler(); + if (Session.currentUser != null) { + System.out.println("Login successful."); + this.handler.userMenu(); + + } else { + System.out.println("Login failed."); + } + break; + case "3": + System.out.println(" Disconnecting..."); + + try { + if (this.socket != null) { + this.socket.close(); + } + + if (this.in != null) { + this.in.close(); + } + + if (this.out != null) { + this.out.close(); + } + + System.out.println("Disconnected."); + } catch (IOException e) { + System.err.println(" Error closing connection: " + e.getMessage()); + } + + return; + default: + System.out.println("Invalid choice. Please try again."); + } + } + } + + public static void main(String[] args) { + TelegramClient client = new TelegramClient(); + client.start(); + } +} + diff --git a/src/main/java/org/to/telegramfinalproject/Database/ChannelDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/ChannelDatabase.java new file mode 100644 index 0000000..8f8de92 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Database/ChannelDatabase.java @@ -0,0 +1,78 @@ +package org.to.telegramfinalproject.Database; + +import org.to.telegramfinalproject.Models.Channel; +import org.to.telegramfinalproject.Models.Group; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class ChannelDatabase { + public static List getChannelsByUser(UUID internalUuid) { + List channels = new ArrayList<>(); + + String sql = """ + SELECT c.* FROM channels c + JOIN channel_subscribers cs ON c.internal_uuid = cs.channel_id + WHERE cs.user_id = ? + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setObject(1, internalUuid); + ResultSet rs = stmt.executeQuery(); + + while (rs.next()) { + Channel channel = new Channel(); + + channel.setInternal_uuid(UUID.fromString(rs.getString("internal_uuid"))); + channel.setChannel_id(rs.getString("channel_id")); + channel.setChannel_name(rs.getString("channel_name")); + channel.setCreator_id(UUID.fromString(rs.getString("creator_id"))); + channel.setImage_url(rs.getString("image_url")); + channel.setDescription(rs.getString("description")); + channel.setCreated_at(rs.getTimestamp("created_at").toLocalDateTime()); + + channels.add(channel); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + + return channels; + } + + + public static List searchChannels(String keyword) { + List result = new ArrayList<>(); + String sql = "SELECT * FROM channels WHERE channel_name ILIKE ? OR channel_id ILIKE ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, "%" + keyword + "%"); + stmt.setString(2, keyword); + + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + Channel channel = new Channel( + UUID.fromString(rs.getString("internal_uuid")), + rs.getString("channel_name"), + UUID.fromString(rs.getString("creator_id")), + rs.getTimestamp("created_at").toLocalDateTime() + ); + channel.setChannel_id(rs.getString("channel_id")); + channel.setImage_url(rs.getString("image_url")); + channel.setDescription(rs.getString("description")); + result.add(channel); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return result; + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Database/ConnectionDb.java b/src/main/java/org/to/telegramfinalproject/Database/ConnectionDb.java new file mode 100644 index 0000000..b58f4c0 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Database/ConnectionDb.java @@ -0,0 +1,18 @@ +package org.to.telegramfinalproject.Database; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class ConnectionDb { + private static final String JDBC_URL = "jdbc:postgresql://localhost:5432/Telegram"; + private static final String USERNAME = "postgres"; + private static final String PASSWORD = "Partow@1384"; + + public ConnectionDb() { + } + + public static Connection connect() throws SQLException { + return DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD); + } +} \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Database/ContactDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/ContactDatabase.java new file mode 100644 index 0000000..6ca06ed --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Database/ContactDatabase.java @@ -0,0 +1,158 @@ +package org.to.telegramfinalproject.Database; + +import org.to.telegramfinalproject.Models.Contact; +import org.to.telegramfinalproject.Models.User; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class ContactDatabase { + public ContactDatabase(){} + + + private static Connection getConnection() throws SQLException { + return ConnectionDb.connect(); + } + + + public boolean addContact(UUID user_id, UUID contact_id) { + String sql = "INSERT INTO contacts (user_id, contact_id) VALUES (?, ?) ON CONFLICT DO NOTHING"; + try (Connection connection = getConnection()) { + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setObject(1, user_id); + stmt.setObject(2,contact_id); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + public boolean removeContact(UUID user_id, UUID contact_id) { + String sql = "DELETE FROM contacts WHERE user_id = ? AND contact_id = ?"; + try (Connection connection = getConnection()) { + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setObject(1, user_id); + stmt.setObject(2, contact_id); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public boolean blockContact(UUID user_id, UUID contact_id) { + String sql = "UPDATE contacts SET is_blocked = TRUE WHERE user_id = ? AND contact_id = ?"; + try (Connection connection = getConnection()) { + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setObject(1, user_id); + stmt.setObject(2, contact_id); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public boolean unblockContact(UUID user_id, UUID contact_id) { + String sql = "UPDATE contacts SET is_blocked = FALSE WHERE user_id = ? AND contact_id = ?"; + try (Connection connection = getConnection()) { + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setObject(1, user_id); + stmt.setObject(2, contact_id); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public static List getContacts(UUID user_id) { + List contacts = new ArrayList<>(); + String sql = "SELECT * FROM contacts WHERE user_id = ?"; + try (Connection connection = getConnection()) { + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setObject(1, user_id); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + UUID contactId = (UUID) rs.getObject("contact_id"); + boolean isBlocked = rs.getBoolean("is_blocked"); + Timestamp addedAt = rs.getTimestamp("added_at"); + + Contact contact = new Contact(user_id, contactId); + contact.setIs_blocked(isBlocked); + contact.setAdd_at(addedAt.toLocalDateTime()); + contacts.add(contact); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return contacts; + } + + + public boolean existsContact(UUID user_id, UUID contact_id) { + String sql = "SELECT 1 FROM contacts WHERE user_id = ? AND contact_id = ? LIMIT 1"; // stop searching when find the first item in DB(LIMIT 1) + try (Connection connection = getConnection()) { + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setObject(1, user_id); + stmt.setObject(2, contact_id); + ResultSet rs = stmt.executeQuery(); + return rs.next(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + public boolean isBlocked(UUID user_id, UUID contact_id) { + String sql = "SELECT is_blocked FROM contacts WHERE user_id = ? AND contact_id = ?"; + try (Connection connection = getConnection()) { + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setObject(1, user_id); + stmt.setObject(2, contact_id); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + return rs.getBoolean("is_blocked"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + public List searchContacts(UUID user_id, String searchTerm) { + List results = new ArrayList<>(); //ILIKE case-insensitive + String sql = """ + SELECT c.contact_id, c.added_at, c.is_blocked + FROM contacts c + JOIN users u ON c.contact_id = u.internal_uuid + WHERE c.user_id = ? AND (u.user_id ILIKE ? OR u.profile_name ILIKE ?) + """; + try (Connection connection = getConnection()) { + PreparedStatement stmt = connection.prepareStatement(sql); + stmt.setObject(1, user_id); + stmt.setString(2, "%" + searchTerm + "%"); + stmt.setString(3, "%" + searchTerm + "%"); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + UUID contactId = (UUID) rs.getObject("contact_id"); + boolean isBlocked = rs.getBoolean("is_blocked"); + Timestamp addedAt = rs.getTimestamp("added_at"); + Contact contact = new Contact(user_id, contactId); + contact.setIs_blocked(isBlocked); + contact.setAdd_at(addedAt.toLocalDateTime()); + results.add(contact); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return results; + } + + +} diff --git a/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java new file mode 100644 index 0000000..79fa09b --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java @@ -0,0 +1,80 @@ +package org.to.telegramfinalproject.Database; + +import org.to.telegramfinalproject.Models.Group; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + + +public class GroupDatabase { + + public static List getGroupsByUser(UUID internalUuid) { + List groups = new ArrayList<>(); + + String sql = """ + SELECT g.* FROM groups g + JOIN group_members gm ON g.internal_uuid = gm.group_id + WHERE gm.user_id = ? + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setObject(1, internalUuid); + ResultSet rs = stmt.executeQuery(); + + while (rs.next()) { + Group group = new Group(); + + group.setInternal_uuid(UUID.fromString(rs.getString("internal_uuid"))); + group.setGroup_id(rs.getString("group_id")); + group.setGroup_name(rs.getString("group_name")); + group.setCreator_id(UUID.fromString(rs.getString("creator_id"))); + group.setImage_url(rs.getString("image_url")); + group.setDescription(rs.getString("description")); + group.setCreated_at(rs.getTimestamp("created_at").toLocalDateTime()); + + groups.add(group); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + + return groups; + } + + public static List searchGroups(String keyword) { + List result = new ArrayList<>(); + String sql = "SELECT * FROM groups WHERE group_name ILIKE ? OR group_id ILIKE ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, "%" + keyword + "%"); + stmt.setString(2, keyword); + + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + Group group = new Group( + UUID.fromString(rs.getString("internal_uuid")), + rs.getString("group_name"), + UUID.fromString(rs.getString("creator_id")), + rs.getTimestamp("created_at").toLocalDateTime() + ); + group.setGroup_id(rs.getString("group_id")); + group.setImage_url(rs.getString("image_url")); + group.setDescription(rs.getString("description")); + result.add(group); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return result; + } + + +} \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java new file mode 100644 index 0000000..9b5a814 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java @@ -0,0 +1,135 @@ +package org.to.telegramfinalproject.Database; + +import org.to.telegramfinalproject.Models.Message; + +import java.sql.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class MessageDatabase { + + + public void markMessageAsRead(UUID messageId, UUID userId) { + String sql = "INSERT INTO message_receipts (message_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, messageId); + stmt.setObject(2, userId); + stmt.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + + + + public static List getUnreadMessages(UUID userId) { + List messages = new ArrayList<>(); + + String sql = """ + SELECT m.* FROM messages m + LEFT JOIN message_receipts r ON m.message_id = r.message_id AND r.user_id = ? + WHERE r.user_id IS NULL + AND ( + (m.receiver_type = 'private' AND m.receiver_id = ?) + OR + (m.receiver_type = 'group' AND EXISTS ( + SELECT 1 FROM group_members gm WHERE gm.group_id = m.receiver_id AND gm.user_id = ? + )) + OR + (m.receiver_type = 'channel' AND EXISTS ( + SELECT 1 FROM channel_subscribers cs WHERE cs.channel_id = m.receiver_id AND cs.user_id = ? + )) + ) + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setObject(1, userId); + stmt.setObject(2, userId); + stmt.setObject(3, userId); + stmt.setObject(4, userId); + + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + messages.add(new Message( + UUID.fromString(rs.getString("message_id")), + rs.getObject("sender_id") != null ? UUID.fromString(rs.getString("sender_id")) : null, + rs.getString("receiver_type"), + UUID.fromString(rs.getString("receiver_id")), + rs.getString("content"), + rs.getString("message_type"), + rs.getString("file_url"), + rs.getTimestamp("send_at").toLocalDateTime(), + rs.getString("status"), + rs.getObject("reply_to_id") != null ? UUID.fromString(rs.getString("reply_to_id")) : null, + rs.getBoolean("is_edited"), + rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null, + rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null, + rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null + )); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + + return messages; + } + + + public static LocalDateTime getLastMessageTimeBetween(UUID user1, UUID user2, String type) { + String sql = """ + SELECT MAX(send_at) FROM messages + WHERE receiver_type = ? + AND ( + (sender_id = ? AND receiver_id = ?) + OR (sender_id = ? AND receiver_id = ?) + ) + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, type); + stmt.setObject(2, user1); + stmt.setObject(3, user2); + stmt.setObject(4, user2); + stmt.setObject(5, user1); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + Timestamp ts = rs.getTimestamp(1); + return ts != null ? ts.toLocalDateTime() : null; + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return null; + } + + + public static LocalDateTime getLastMessageTime(UUID receiverId, String type) { + String sql = "SELECT MAX(send_at) FROM messages WHERE receiver_id = ? AND receiver_type = ?"; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, receiverId); + stmt.setString(2, type); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + Timestamp ts = rs.getTimestamp(1); + return ts != null ? ts.toLocalDateTime() : null; + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return null; + } + +} diff --git a/src/main/java/org/to/telegramfinalproject/Database/userDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/userDatabase.java new file mode 100644 index 0000000..8488fda --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Database/userDatabase.java @@ -0,0 +1,286 @@ +package org.to.telegramfinalproject.Database; + + +import org.to.telegramfinalproject.Models.User; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class userDatabase { + public userDatabase() { + } + + private Connection getConnection() throws SQLException { + return ConnectionDb.connect(); + } + + public User findByUserId(String userId) { + String query = "SELECT * FROM users WHERE user_id = ?"; + + try { + User var6; + try (Connection conn = this.getConnection()) { + try (PreparedStatement stmt = conn.prepareStatement(query)) { + stmt.setString(1, userId); + ResultSet rs = stmt.executeQuery(); + if (!rs.next()) { + return null; + } + + var6 = this.extractUser(rs); + } + } + + return var6; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + + public User findByUsername(String username) { + String query = "SELECT * FROM users WHERE username = ?"; + + try { + User var6; + try (Connection conn = this.getConnection()) { + try (PreparedStatement stmt = conn.prepareStatement(query)) { + stmt.setString(1, username); + ResultSet rs = stmt.executeQuery(); + if (!rs.next()) { + return null; + } + + var6 = this.extractUser(rs); + } + } + + return var6; + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + + public boolean existsByUsername(String username) { + String query = "SELECT 1 FROM users WHERE username = ?"; + + try { + boolean var6; + try ( + Connection conn = this.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query); + ) { + stmt.setString(1, username); + ResultSet rs = stmt.executeQuery(); + var6 = rs.next(); + } + + return var6; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public boolean existsByUserId(String user_id) { + String query = "SELECT 1 FROM users WHERE user_id = ?"; + + try { + boolean var6; + try ( + Connection conn = this.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query); + ) { + stmt.setString(1, user_id); + ResultSet rs = stmt.executeQuery(); + var6 = rs.next(); + } + + return var6; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public boolean save(User user) { + String query = "INSERT INTO users (user_id, internal_uuid, username, password, profile_name) VALUES (?, ?, ?, ?, ?)"; + + try { + boolean var5; + try ( + Connection conn = this.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query); + ) { + stmt.setString(1, user.getUser_id()); + stmt.setObject(2, user.getInternal_uuid()); + stmt.setString(3, user.getUsername()); + stmt.setString(4, user.getPassword()); + stmt.setString(5, user.getProfile_name()); + var5 = stmt.executeUpdate() > 0; + } + + return var5; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public boolean updateByUUID(UUID uuid, User user) { + String query = "UPDATE users SET user_id=?, username=?, password=?, profile_name=?, bio=?, image_url=?, status=?, last_seen=? WHERE internal_uuid=?"; + + try { + boolean var6; + try ( + Connection conn = this.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query); + ) { + stmt.setString(1, user.getUser_id()); + stmt.setString(2, user.getUsername()); + stmt.setString(3, user.getPassword()); + stmt.setString(4, user.getProfile_name()); + stmt.setString(5, user.getBio()); + stmt.setString(6, user.getImage_url()); + stmt.setString(7, user.getStatus()); + stmt.setObject(8, user.getLast_seen()); + stmt.setObject(9, uuid); + var6 = stmt.executeUpdate() > 0; + } + + return var6; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public List getAll() { + List users = new ArrayList(); + String query = "SELECT * FROM users"; + + try ( + Connection conn = this.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query); + ResultSet rs = stmt.executeQuery(); + ) { + while(rs.next()) { + users.add(this.extractUser(rs)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return users; + } + + private User extractUser(ResultSet rs) throws SQLException { + return new User(rs.getString("user_id"), UUID.fromString(rs.getString("internal_uuid")), rs.getString("username"), rs.getString("password"), rs.getString("profile_name")); + } + + public boolean deleteByUUID(UUID uuid) { + String query = "DELETE FROM users WHERE internal_uuid = ?"; + + try { + boolean var5; + try ( + Connection conn = this.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query); + ) { + stmt.setObject(1, uuid); + var5 = stmt.executeUpdate() > 0; + } + + return var5; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + public static void updateUserStatus(UUID uuid, String status) { + String sql = "UPDATE users SET status = ? WHERE internal_uuid = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, status); + stmt.setObject(2, uuid); + stmt.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public static void updateLastSeen(UUID uuid) { + String sql = "UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE internal_uuid = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, uuid); + stmt.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + + public static User findByInternalUUID(UUID internalUuid) { + String sql = "SELECT * FROM users WHERE internal_uuid = ?"; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setObject(1, internalUuid); + ResultSet rs = stmt.executeQuery(); + + if (rs.next()) { + User user = new User( + rs.getString("user_id"), + UUID.fromString(rs.getString("internal_uuid")), + rs.getString("username"), + rs.getString("password"), + rs.getString("profile_name") + ); + + user.setBio(rs.getString("bio")); + user.setImage_url(rs.getString("image_url")); + user.setStatus(rs.getString("status")); + + Timestamp lastSeenTs = rs.getTimestamp("last_seen"); + if (lastSeenTs != null) { + user.setLast_seen(lastSeenTs.toLocalDateTime()); + } + + return user; + } + + } catch (SQLException e) { + e.printStackTrace(); + } + + return null; + } + + public List searchUsers(String keyword) { + String query = "SELECT * FROM users WHERE user_id ILIKE ? OR profile_name ILIKE ?"; //(ILIKE) case_insensitive + List result = new ArrayList<>(); + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + stmt.setString(1, "%" + keyword + "%"); + stmt.setString(2, "%" + keyword + "%"); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + result.add(extractUser(rs)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return result; + } + + +} + diff --git a/src/main/java/org/to/telegramfinalproject/Models/Channel.java b/src/main/java/org/to/telegramfinalproject/Models/Channel.java new file mode 100644 index 0000000..8965066 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/Channel.java @@ -0,0 +1,49 @@ +package org.to.telegramfinalproject.Models; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public class Channel { + private UUID internal_uuid; + private String channel_id; + private String channel_name; + private UUID creator_id; + private String image_url; + private String description; + private LocalDateTime created_at; + private List members; + + public Channel(UUID internal_uuid, String channel_name, UUID creator_id, LocalDateTime created_at){ + this.internal_uuid = internal_uuid; + this.channel_name = channel_name; + this.creator_id = creator_id; + this.created_at = created_at; + } + + public Channel() { + + } + + public void setChannel_id(String Channel_id){this.channel_id = Channel_id;} + public void setCreator_id(UUID creator_id){this.creator_id = creator_id;} + public void setChannel_name(String channel_name){this.channel_name = channel_name;} + public void setImage_url(String image_url){this.image_url = image_url;} + public void setCreated_at(LocalDateTime created_at){this.created_at = created_at;} + public void setDescription(String description){this.description =description;} + public void setMembers(List members){this.members = members;} + + public String getChannel_id(){return this.channel_id;} + public UUID getCreator_id(){return this.creator_id;} + public String getChannel_name(){return this.channel_name;} + public String getImage_url(){return this.image_url;} + public LocalDateTime getCreated_at(){return this.created_at;} + public String getDescription(){return this.description;} + public List getMembers(){return this.members;} + public UUID getInternal_uuid() { + return this.internal_uuid; + } + + public void setInternal_uuid(UUID internalUuid) { this.internal_uuid = internalUuid; + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/ChannelSubscribe.java b/src/main/java/org/to/telegramfinalproject/Models/ChannelSubscribe.java new file mode 100644 index 0000000..1950890 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/ChannelSubscribe.java @@ -0,0 +1,29 @@ +package org.to.telegramfinalproject.Models; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class ChannelSubscribe{ + private UUID channel_id; + private UUID user_id; + private LocalDateTime Subscribed_at; + + + public ChannelSubscribe(UUID channel_id, UUID user_id, String role){ + this.channel_id = channel_id; + this.user_id = user_id; + this.Subscribed_at = LocalDateTime.now(); + } + + public void setChannel_id(UUID group_id){this.channel_id = group_id;} + public void setUser_id(UUID user_id){this.user_id = user_id;} + public void setJoin_at(LocalDateTime join_at){this.Subscribed_at = join_at;} + + + + public UUID getChannel_id(){return this.channel_id;} + public UUID getUser_id(){return this.user_id;} + public LocalDateTime getJoin_at(){return this.Subscribed_at;} + + +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java b/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java new file mode 100644 index 0000000..52d3916 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java @@ -0,0 +1,39 @@ +package org.to.telegramfinalproject.Models; + +import java.time.LocalDateTime; + +public class ChatEntry { + private final String name; + private final String id; + private final String imageUrl; + private final String type; // "private", "group", "channel" + private final LocalDateTime lastMessageTime; + + public ChatEntry(String name, String id, String imageUrl, String type, LocalDateTime lastMessageTime) { + this.name = name; + this.id = id; + this.imageUrl = imageUrl; + this.type = type; + this.lastMessageTime = lastMessageTime; + } + + public String getName() { + return name; + } + + public String getId() { + return id; + } + + public String getImageUrl() { + return imageUrl; + } + + public String getType() { + return type; + } + + public LocalDateTime getLastMessageTime() { + return lastMessageTime; + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/Contact.java b/src/main/java/org/to/telegramfinalproject/Models/Contact.java new file mode 100644 index 0000000..8e78344 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/Contact.java @@ -0,0 +1,44 @@ +package org.to.telegramfinalproject.Models; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public class Contact { + private UUID user_id; + private UUID contact_id; + private LocalDateTime added_at; + private Boolean is_blocked; + + public Contact(UUID user_id, UUID contact_id){ + this.user_id = user_id; + this.contact_id = contact_id; + this.added_at = LocalDateTime.now(); + } + + public void setUser_id(UUID user_id){this.user_id = user_id;} + public void setContact_id(UUID contact_id){this.contact_id = contact_id;} + public void setAdd_at(LocalDateTime add_at){this.added_at =add_at;} + public void setIs_blocked(Boolean is_blocked){this.is_blocked =is_blocked;} + + public UUID getUser_id(){ + return this.user_id; + } + + public UUID getContact_id(){return this.contact_id;} + + public LocalDateTime getAdd_at() { + return added_at; + } + public Boolean getIs_blocked(){ + return is_blocked; + } + + + + + +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/Group.java b/src/main/java/org/to/telegramfinalproject/Models/Group.java new file mode 100644 index 0000000..922c725 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/Group.java @@ -0,0 +1,49 @@ +package org.to.telegramfinalproject.Models; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public class Group { + private UUID internal_uuid; + private String group_id; + private String group_name; + private UUID creator_id; + private String image_url; + private String description; + private LocalDateTime created_at; + private List members; + + public Group(UUID internal_uuid, String group_name, UUID creator_id, LocalDateTime created_at){ + this.internal_uuid = internal_uuid; + this.group_name = group_name; + this.creator_id = creator_id; + this.created_at = created_at; + } + + public Group() { + + } + + public void setGroup_id(String group_id){this.group_id = group_id;} + public void setCreator_id(UUID creator_id){this.creator_id = creator_id;} + public void setGroup_name(String group_name){this.group_name = group_name;} + public void setImage_url(String image_url){this.image_url = image_url;} + public void setCreated_at(LocalDateTime created_at){this.created_at = created_at;} + public void setDescription(String description){this.description =description;} + public void setMembers(List members){this.members = members;} + + public String getGroup_id(){return this.group_id;} + public UUID getCreator_id(){return this.creator_id;} + public String getGroup_name(){return this.group_name;} + public String getImage_url(){return this.image_url;} + public LocalDateTime getCreated_at(){return this.created_at;} + public String getDescription(){return this.description;} + public List getMembers(){return this.members;} + public UUID getInternal_uuid() { + return this.internal_uuid; + } + + public void setInternal_uuid(UUID internalUuid) { this.internal_uuid = internalUuid; + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/GroupMember.java b/src/main/java/org/to/telegramfinalproject/Models/GroupMember.java new file mode 100644 index 0000000..a9cb0ed --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/GroupMember.java @@ -0,0 +1,32 @@ +package org.to.telegramfinalproject.Models; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class GroupMember { + private UUID group_id; + private UUID user_id; + private LocalDateTime join_at; + private String role; + + public GroupMember(UUID group_id, UUID user_id, String role){ + this.group_id = group_id; + this.user_id = user_id; + this.join_at = LocalDateTime.now(); + this.role = role; + } + + public void setGroup_id(UUID group_id){this.group_id = group_id;} + public void setUser_id(UUID user_id){this.user_id = user_id;} + public void setJoin_at(LocalDateTime join_at){this.join_at = join_at;} + public void setRole(String role){this.role = role;} + + + + public UUID getGroup_id(){return this.group_id;} + public UUID getUser_id(){return this.user_id;} + public LocalDateTime getJoin_at(){return this.join_at;} + public String getRole(){return this.role;} + + +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/JsonUtil.java b/src/main/java/org/to/telegramfinalproject/Models/JsonUtil.java new file mode 100644 index 0000000..4e3a37f --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/JsonUtil.java @@ -0,0 +1,157 @@ +package org.to.telegramfinalproject.Models; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public class JsonUtil { + public static JSONArray contactListToJson(List contacts) { + + + JSONArray array = new JSONArray(); + for (Contact contact : contacts) { + JSONObject obj = new JSONObject(); + obj.put("user_id", contact.getUser_id().toString()); + obj.put("contact_id", contact.getContact_id().toString()); + obj.put("is_blocked", contact.getIs_blocked()); + obj.put("added_at", contact.getAdd_at().toString()); + array.put(obj); + } + return array; + } + + + + + public static JSONObject userToJson(User user) { + JSONObject obj = new JSONObject(); + obj.put("internalUUID", user.getInternal_uuid().toString()); + obj.put("user_id", user.getUser_id() != null ?user.getUser_id().toString() :JSONObject.NULL); + obj.put("username", user.getUsername()); + obj.put("profile_name", user.getProfile_name()); + obj.put("bio", user.getBio()!= null ?user.getBio().toString() :JSONObject.NULL); + obj.put("image_url", user.getImage_url() != null ?user.getImage_url().toString() :JSONObject.NULL); + obj.put("status", user.getStatus()); + obj.put("last_seen", user.getLast_seen() != null ? user.getLast_seen().toString() : JSONObject.NULL); + obj.put("contactList", JsonUtil.contactListToJson(user.getContactList())); + obj.put("channelList", JsonUtil.channelListToJson(user.getChannelList())); + obj.put("groupList", JsonUtil.groupListToJson(user.getGroupList())); + obj.put("unreadMessages", JsonUtil.messageListToJson(user.getUnreadMessages())); + return obj; + } + + + public static JSONArray messageListToJson(List messages) { + JSONArray array = new JSONArray(); + for (Message message : messages) { + JSONObject obj = new JSONObject(); + obj.put("message_id", message.getMessage_id().toString()); + obj.put("sender_id", message.getSender_id() != null ? message.getSender_id().toString() : JSONObject.NULL); + obj.put("receiver_type", message.getReceiver_type()); + obj.put("receiver_id", message.getReceiver_id().toString()); + obj.put("content", message.getContent()); + obj.put("message_type", message.getMessage_type()); + obj.put("file_url", message.getFile_url()); + obj.put("send_at", message.getSend_at().toString()); + obj.put("status", message.getStatus()); + obj.put("reply_to_id", message.getReply_to_id() != null ? message.getReply_to_id().toString() : JSONObject.NULL); + obj.put("is_edited", message.isIs_edited()); + obj.put("original_message_id", message.getOriginal_message_id() != null ? message.getOriginal_message_id().toString() : JSONObject.NULL); + obj.put("forwarded_by", message.getForwarded_by() != null ? message.getForwarded_by().toString() : JSONObject.NULL); + obj.put("forwarded_from", message.getForwarded_from() != null ? message.getForwarded_from().toString() : JSONObject.NULL); + array.put(obj); + } + return array; + } + + + + + public static JSONArray groupListToJson(List groups) { + JSONArray array = new JSONArray(); + for (Group group : groups) { + JSONObject obj = new JSONObject(); + obj.put("internal_uuid", group.getInternal_uuid().toString()); + obj.put("group_id", group.getGroup_id() != null ?group.getGroup_id().toString() :JSONObject.NULL); + obj.put("group_name", group.getGroup_name()); + obj.put("creator_id", group.getCreator_id().toString()); + obj.put("image_url",group.getImage_url()!= null ?group.getImage_url().toString() : JSONObject.NULL ); + obj.put("description", group.getDescription() != null ?group.getDescription().toString() : JSONObject.NULL ); + obj.put("created_at", group.getCreated_at().toString()); + obj.put("members", JsonUtil.groupMemberListToJson(group.getMembers())); + array.put(obj); + } + return array; + } + + + + + public static JSONArray channelListToJson(List channels) { + JSONArray array = new JSONArray(); + for (Channel channel : channels) { + JSONObject obj = new JSONObject(); + obj.put("internal_uuid",channel.getInternal_uuid().toString()); + obj.put("channel_id", channel.getChannel_id() != null ?channel.getChannel_id().toString() :JSONObject.NULL); + obj.put("channel_name", channel.getChannel_name()); + obj.put("creator_id", channel.getCreator_id().toString()); + obj.put("image_url",channel.getImage_url()!= null ?channel.getImage_url().toString() : JSONObject.NULL ); + obj.put("description",channel.getDescription() != null ?channel.getDescription().toString() : JSONObject.NULL ); + obj.put("created_at",channel.getCreated_at().toString()); + obj.put("members", JsonUtil.channelSubscribeToJson(channel.getMembers())); + array.put(obj); + } + return array; + } + + + + + public static JSONArray groupMemberListToJson(List members) { + JSONArray array = new JSONArray(); + for (GroupMember m : members) { + JSONObject obj = new JSONObject(); + obj.put("group_id", m.getGroup_id().toString()); + obj.put("user_id", m.getUser_id().toString()); + obj.put("joined_at", m.getJoin_at().toString()); + obj.put("role", m.getRole()); + array.put(obj); + } + return array; + } + + public static JSONArray channelSubscribeToJson(List subscribes){ + JSONArray array = new JSONArray(); + for(ChannelSubscribe s :subscribes ){ + JSONObject obj = new JSONObject(); + obj.put("channel_id",s.getChannel_id().toString()); + obj.put("user_id", s.getUser_id().toString()); + obj.put("Subscribed_at", s.getJoin_at().toString()); + } + + return array; + } + + + public static JSONArray chatListToJson(List chatList) { + JSONArray jsonArray = new JSONArray(); + + for (ChatEntry entry : chatList) { + JSONObject obj = new JSONObject(); + obj.put("id", entry.getId() != null ?entry.getId() :JSONObject.NULL); + obj.put("name", entry.getName()); + obj.put("image_url", entry.getImageUrl() != null ?entry.getImageUrl() :JSONObject.NULL); + obj.put("type", entry.getType()); + obj.put("last_message_time", entry.getLastMessageTime() != null ? entry.getLastMessageTime().toString() : JSONObject.NULL); + + jsonArray.put(obj); + } + + return jsonArray; + } + + +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/LoginBootstrapData.java b/src/main/java/org/to/telegramfinalproject/Models/LoginBootstrapData.java new file mode 100644 index 0000000..c15791f --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/LoginBootstrapData.java @@ -0,0 +1,32 @@ +package org.to.telegramfinalproject.Models; + +import java.util.List; + +public class LoginBootstrapData { + private User user; + private List contacts; + private List groups; + private List channels; + private List unreadMessages; + + public LoginBootstrapData(User user, List contacts, List groups, + List channels, List unreadMessages) { + this.user = user; + this.contacts = contacts; + this.groups = groups; + this.channels = channels; + this.unreadMessages = unreadMessages; + } + + public void setUnreadMessages(List unreadMessages){this.unreadMessages = unreadMessages;} + public void setContacts(List contacts){this.contacts =contacts;} + public void setGroups(List groups){this.groups = groups;} + public void setChannels(List channels){this.channels = channels;} + public void setUser(User user){this.user = user;} + + public List getUnreadMessages(){return unreadMessages;} + public List getGroups(){return groups;} + public List getContacts(){return contacts;} + public List getChannels(){return channels;} + public User getUser(User user){return user;} +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/Message.java b/src/main/java/org/to/telegramfinalproject/Models/Message.java new file mode 100644 index 0000000..eb7688a --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/Message.java @@ -0,0 +1,151 @@ +package org.to.telegramfinalproject.Models; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class Message { + private UUID message_id; + private UUID sender_id; + private String receiver_type; // private, group, channel + private UUID receiver_id; + private String content; + private String message_type; // TEXT, IMAGE, FILE, ... + private String file_url; + private LocalDateTime send_at; + private String status; // SENT, DELIVERED, READ + private UUID reply_to_id; + private boolean is_edited; + private UUID original_message_id; + private UUID forwarded_by; + private UUID forwarded_from; + + public Message(UUID message_id, UUID sender_id, String receiver_type, UUID receiver_id, String content, + String message_type, String file_url, LocalDateTime send_at, String status, + UUID reply_to_id, boolean is_edited, UUID original_message_id, + UUID forwarded_by, UUID forwarded_from) { + this.message_id = message_id; + this.sender_id = sender_id; + this.receiver_type = receiver_type; + this.receiver_id = receiver_id; + this.content = content; + this.message_type = message_type; + this.file_url = file_url; + this.send_at = send_at; + this.status = status; + this.reply_to_id = reply_to_id; + this.is_edited = is_edited; + this.original_message_id = original_message_id; + this.forwarded_by = forwarded_by; + this.forwarded_from = forwarded_from; + } + + + public UUID getMessage_id() { + return message_id; + } + + + public UUID getSender_id() { + return sender_id; + } + + public void setSender_id(UUID sender_id) { + this.sender_id = sender_id; + } + + public String getReceiver_type() { + return receiver_type; + } + + public void setReceiver_type(String receiver_type) { + this.receiver_type = receiver_type; + } + + public UUID getReceiver_id() { + return receiver_id; + } + + public void setReceiver_id(UUID receiver_id) { + this.receiver_id = receiver_id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getMessage_type() { + return message_type; + } + + public void setMessage_type(String message_type) { + this.message_type = message_type; + } + + public String getFile_url() { + return file_url; + } + + public void setFile_url(String file_url) { + this.file_url = file_url; + } + + public LocalDateTime getSend_at() { + return send_at; + } + + public void setSend_at(LocalDateTime send_at) { + this.send_at = send_at; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public UUID getReply_to_id() { + return reply_to_id; + } + + public void setReply_to_id(UUID reply_to_id) { + this.reply_to_id = reply_to_id; + } + + public boolean isIs_edited() { + return is_edited; + } + + public void setIs_edited(boolean is_edited) { + this.is_edited = is_edited; + } + + public UUID getOriginal_message_id() { + return original_message_id; + } + + public void setOriginal_message_id(UUID original_message_id) { + this.original_message_id = original_message_id; + } + + public UUID getForwarded_by() { + return forwarded_by; + } + + public void setForwarded_by(UUID forwarded_by) { + this.forwarded_by = forwarded_by; + } + + public UUID getForwarded_from() { + return forwarded_from; + } + + public void setForwarded_from(UUID forwarded_from) { + this.forwarded_from = forwarded_from; + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java b/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java new file mode 100644 index 0000000..9334717 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java @@ -0,0 +1,30 @@ +package org.to.telegramfinalproject.Models; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class PrivateChat { + private UUID chat_id; + private UUID user1_id; + private UUID user2_id; + private LocalDateTime created_at; + + public PrivateChat(UUID chat_id, UUID user1_id, UUID user2_id, LocalDateTime created_at){ + this.chat_id = chat_id; + this.user1_id =user1_id; + this.user2_id =user2_id; + this.created_at =created_at; + } + + public void setUser1_id(UUID user1_id){this.user1_id =user1_id;} + public void setUser2_id(UUID user2_id){this.user2_id =user2_id;} + public void setCreated_at(LocalDateTime created_at){this.created_at = created_at;} + + public UUID getUser1_id(){return this.user1_id;} + public UUID getChat_id(){return this.chat_id;} + public UUID getUser2_id(){return this.user2_id;} + public LocalDateTime getCreated_at(){return this.created_at;} + + + +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/RequestModel.java b/src/main/java/org/to/telegramfinalproject/Models/RequestModel.java new file mode 100644 index 0000000..d460e5c --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/RequestModel.java @@ -0,0 +1,39 @@ +package org.to.telegramfinalproject.Models; + +public class RequestModel { + private String action; + private String user_id; + private String username; + private String password; + private String profile_name; + + public RequestModel(String action, String user_id, String username, String password, String profile_name) { + this.action = action; + this.user_id = user_id; + this.username = username; + this.password = password; + this.profile_name = profile_name; + } + + public String getAction() { + return this.action; + } + + public String getUser_id() { + return this.user_id; + } + + public String getUsername() { + return this.username; + } + + public String getPassword() { + return this.password; + } + + public String getProfile_name() { + return this.profile_name; + } +} + + diff --git a/src/main/java/org/to/telegramfinalproject/Models/ResponseModel.java b/src/main/java/org/to/telegramfinalproject/Models/ResponseModel.java new file mode 100644 index 0000000..7434561 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/ResponseModel.java @@ -0,0 +1,32 @@ +package org.to.telegramfinalproject.Models; + +import org.json.JSONObject; + +public class ResponseModel { + private String status; + private String message; + private JSONObject data; + + + public ResponseModel(String status, String message) { + this.status = status; + this.message = message; + this.data = null; + } + + public ResponseModel(String status, String message, JSONObject data) { + this.status = status; + this.message = message; + this.data = data; + } + + public String getStatus() { + return this.status; + } + + public String getMessage() { + return this.message; + } + + public JSONObject getData() {return this.data;} +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/SearchRequestModel.java b/src/main/java/org/to/telegramfinalproject/Models/SearchRequestModel.java new file mode 100644 index 0000000..db63e19 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/SearchRequestModel.java @@ -0,0 +1,19 @@ +package org.to.telegramfinalproject.Models; + +public class SearchRequestModel { + private String action; + private String keyword; + + public SearchRequestModel(String action, String keyword) { + this.action = action; + this.keyword = keyword; + } + + public String getAction() { + return action; + } + + public String getKeyword() { + return keyword; + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Models/User.java b/src/main/java/org/to/telegramfinalproject/Models/User.java new file mode 100644 index 0000000..9e5781e --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Models/User.java @@ -0,0 +1,125 @@ +package org.to.telegramfinalproject.Models; + +import org.json.JSONObject; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public class User { + private String user_id; + private UUID internal_uuid; + private String username; + private String password; + private String profile_name; + private String bio; + private String image_url; + private String status; + private LocalDateTime last_seen; + private List contactList; + private List groupList; + private List channelList; + private List unreadMessages; + private List chatList; + + public User(String user_id, UUID internal_uuid, String username, String password, String profile_name) { + this.user_id = user_id; + this.internal_uuid = internal_uuid; + this.username = username; + this.password = password; + this.profile_name = profile_name; + } + + public void setUser_id(String user_id) { + this.user_id = user_id; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setProfile_name(String profile_name) { + this.profile_name = profile_name; + } + + public void setBio(String bio) { + this.bio = bio; + } + + public void setImage_url(String image_url) { + this.image_url = image_url; + } + + public void setStatus(String status) { + this.status = status; + } + + public void setLast_seen(LocalDateTime last_seen) { + this.last_seen = last_seen; + } + + public void setContactList(List contactList){this.contactList = contactList;} + + public void setChannelList(List channelList){this.channelList = channelList;} + + public void setGroupList(List groupList){this.groupList = groupList;} + + public void setUnreadMessages(List unreadMessages){this.unreadMessages = unreadMessages;} + + public void setChatList(List chatList){this.chatList = chatList;} + + public UUID getInternal_uuid() { + return this.internal_uuid; + } + + public String getUser_id() { + return this.user_id; + } + + public String getUsername() { + return this.username; + } + + public String getPassword() { + return this.password; + } + + public String getProfile_name() { + return this.profile_name; + } + + public String getBio() { + return this.bio; + } + + public String getImage_url() { + return this.image_url; + } + + public String getStatus() { + return this.status; + } + + public LocalDateTime getLast_seen() { + return this.last_seen; + } + + public List getContactList(){return this.contactList;} + + public List getChannelList(){return this.channelList;} + + public List getGroupList(){return this.groupList;} + + public List getUnreadMessages(){return this.unreadMessages;} + + public List getChatList(){return this.chatList;} + + + + +} + diff --git a/src/main/java/org/to/telegramfinalproject/Security/PasswordHashing.java b/src/main/java/org/to/telegramfinalproject/Security/PasswordHashing.java new file mode 100644 index 0000000..ae1b916 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Security/PasswordHashing.java @@ -0,0 +1,30 @@ +package org.to.telegramfinalproject.Security; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class PasswordHashing { + public PasswordHashing() { + } + + public static String hash(String password) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] hashedBytes = md.digest(password.getBytes()); + StringBuilder sb = new StringBuilder(); + + for(byte b : hashedBytes) { + sb.append(String.format("%02x", b)); + } + + return sb.toString(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Hashing algorithm not found!", e); + } + } + + public static boolean verify(String password, String hashedPasswordFromDB) { + return hash(password).equals(hashedPasswordFromDB); + } +} + diff --git a/src/main/java/org/to/telegramfinalproject/Server/AuthService.java b/src/main/java/org/to/telegramfinalproject/Server/AuthService.java new file mode 100644 index 0000000..1534c46 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Server/AuthService.java @@ -0,0 +1,59 @@ +package org.to.telegramfinalproject.Server; + +import java.util.UUID; +import org.to.telegramfinalproject.Database.userDatabase; +import org.to.telegramfinalproject.Models.User; +import org.to.telegramfinalproject.Security.PasswordHashing; + +public class AuthService { + private final userDatabase userDb = new userDatabase(); + + public AuthService() { + } + + public boolean register(String user_id, String username, String password, String profile_name) { + if (!this.userDb.existsByUsername(username) && !this.userDb.existsByUserId(user_id)) { + String passwordRegex = "\\b(?=[^\\s]*[A-Z])(?=[^\\s]*[a-z])(?=[^\\s]*\\d)(?=[^\\s]*[!@#$%^&*])[^\\s]{8,}\\b"; + if (!password.matches(passwordRegex)) { + System.out.println("Password doesn't Valid(At list one capital and one special char(!@#$%^&*), minimum 8 char "); + return false; + } else { + UUID uuid = UUID.randomUUID(); + password = PasswordHashing.hash(password); + User user = new User(user_id, uuid, username, password, profile_name); + return this.userDb.save(user); + } + } else { + System.out.println("Username/ user id is already taken"); + return false; + } + } + + public User login(String username, String password) { + User user = this.userDb.findByUsername(username); + if (user == null) { + System.out.println("User not found."); + return null; + } else if (!PasswordHashing.verify(password, user.getPassword())) { + System.out.println("Incorrect password"); + return null; + } else { + return user; + } + } + + public boolean loginCheck(String username , String password){ + User user = userDb.findByUsername(username); + String pass = user.getPassword(); + password = PasswordHashing.hash(password); + String Username = user.getUsername(); + boolean login = false; + if(user!= null && Username.equals(username)&& pass.equals(password)){ + login = true; + } + return login; + + } +} + + diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java new file mode 100644 index 0000000..1058e3e --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -0,0 +1,182 @@ +package org.to.telegramfinalproject.Server; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.to.telegramfinalproject.Database.*; +import org.to.telegramfinalproject.Models.*; + +import java.io.*; +import java.net.Socket; +import java.time.LocalDateTime; +import java.util.*; + +public class ClientHandler implements Runnable { + private final Socket socket; + private final AuthService authService = new AuthService(); + + public ClientHandler(Socket socket) { + this.socket = socket; + } + + @Override + public void run() { + try ( + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + PrintWriter out = new PrintWriter(socket.getOutputStream(), true) + ) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + JSONObject requestJson = new JSONObject(inputLine); + String action = requestJson.getString("action"); + ResponseModel response = null; + System.out.println("Received action: '" + action + "'"); + System.out.println("Full request: " + requestJson.toString()); + if (!requestJson.has("action") || requestJson.isNull("action")) { + ResponseModel errorResponse = new ResponseModel("error", "Missing 'action' in request."); + JSONObject responseJson = new JSONObject(); + responseJson.put("status", errorResponse.getStatus()); + responseJson.put("message", errorResponse.getMessage()); + responseJson.put("data", JSONObject.NULL); + out.println(responseJson.toString()); + continue; + } + + switch (action) { + case "register": + case "login": { + RequestModel request = new RequestModel( + action, + requestJson.optString("user_id"), + requestJson.optString("username"), + requestJson.optString("password"), + requestJson.optString("profile_name") + ); + + if (action.equals("register")) { + boolean registered = authService.register( + request.getUser_id(), + request.getUsername(), + request.getPassword(), + request.getProfile_name() + ); + response = registered + ? new ResponseModel("success", "Registration successful.") + : new ResponseModel("error", "Registration failed."); + } else { + User user = authService.login(request.getUsername(), request.getPassword()); + if (user == null) { + response = new ResponseModel("error", "Login failed."); + break; + } + + SessionManager.addUser(user.getInternal_uuid(), this.socket); + userDatabase.updateUserStatus(user.getInternal_uuid(), "online"); + + List contacts = ContactDatabase.getContacts(user.getInternal_uuid()); + List groups = GroupDatabase.getGroupsByUser(user.getInternal_uuid()); + List channels = ChannelDatabase.getChannelsByUser(user.getInternal_uuid()); + List unreadMessages = MessageDatabase.getUnreadMessages(user.getInternal_uuid()); + + user.setContactList(contacts); + user.setGroupList(groups); + user.setChannelList(channels); + user.setUnreadMessages(unreadMessages); + + List chatList = new ArrayList<>(); + for (Contact contact : contacts) { + User target = userDatabase.findByInternalUUID(contact.getContact_id()); + if (target == null) continue; + LocalDateTime last = MessageDatabase.getLastMessageTimeBetween(user.getInternal_uuid(), target.getInternal_uuid(), "private"); + chatList.add(new ChatEntry(target.getUser_id(), target.getProfile_name(), target.getImage_url(), "private", last)); + } + for (Group group : groups) { + LocalDateTime last = MessageDatabase.getLastMessageTime(group.getInternal_uuid(), "group"); + chatList.add(new ChatEntry(group.getGroup_id(), group.getGroup_name(), group.getImage_url(), "group", last)); + } + for (Channel channel : channels) { + LocalDateTime last = MessageDatabase.getLastMessageTime(channel.getInternal_uuid(), "channel"); + chatList.add(new ChatEntry(channel.getChannel_id(), channel.getChannel_name(), channel.getImage_url(), "channel", last)); + } + + chatList.sort((a, b) -> { + if (a.getLastMessageTime() == null) return 1; + if (b.getLastMessageTime() == null) return -1; + return b.getLastMessageTime().compareTo(a.getLastMessageTime()); + }); + + JSONObject userData = JsonUtil.userToJson(user); + userData.put("chat_list", JsonUtil.chatListToJson(chatList)); + response = new ResponseModel("success", "Welcome " + user.getProfile_name(), userData); + } + break; + } + + case "logout": { + String userId = requestJson.optString("user_id"); + if (userId != null && !userId.isEmpty()) { + UUID uuid = UUID.fromString(userId); + userDatabase.updateUserStatus(uuid, "offline"); + userDatabase.updateLastSeen(uuid); + SessionManager.removeUser(uuid); + response = new ResponseModel("success", "Logged out."); + } else { + response = new ResponseModel("error", "Invalid user_id for logout."); + } + break; + } + + case "search": { + String keyword = requestJson.optString("keyword"); + List results = new ArrayList<>(); + + for (User u : new userDatabase().searchUsers(keyword)) { + JSONObject obj = new JSONObject(); + obj.put("type", "user"); + obj.put("id", u.getUser_id()); + obj.put("name", u.getProfile_name()); + results.add(obj); + } + + for (Group g : GroupDatabase.searchGroups(keyword)) { + JSONObject obj = new JSONObject(); + obj.put("type", "group"); + obj.put("id", g.getGroup_id()); + obj.put("name", g.getGroup_name()); + results.add(obj); + } + + for (Channel c : ChannelDatabase.searchChannels(keyword)) { + JSONObject obj = new JSONObject(); + obj.put("type", "channel"); + obj.put("id", c.getChannel_id()); + obj.put("name", c.getChannel_name()); + results.add(obj); + } + + JSONObject data = new JSONObject(); + data.put("results", new JSONArray(results)); + response = new ResponseModel("success", "Search results found", data); + break; + } + + default: + response = new ResponseModel("error", "Unknown action: " + action); + } + + JSONObject responseJson = new JSONObject(); + responseJson.put("status", response.getStatus()); + responseJson.put("message", response.getMessage()); + responseJson.put("data", response.getData() != null ? response.getData() : JSONObject.NULL); + out.println(responseJson.toString()); + } + } catch (IOException e) { + System.out.println("Connection with client lost."); + UUID userId = SessionManager.getUserIdBySocket(this.socket); + if (userId != null) { + userDatabase.updateUserStatus(userId, "offline"); + userDatabase.updateLastSeen(userId); + SessionManager.removeUser(userId); + } + } + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Server/MainServer.java b/src/main/java/org/to/telegramfinalproject/Server/MainServer.java new file mode 100644 index 0000000..57f066a --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Server/MainServer.java @@ -0,0 +1,28 @@ +package org.to.telegramfinalproject.Server; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +public class MainServer { + private static final int PORT = 12345; + + public static void main(String[] args) { + try (ServerSocket serverSocket = new ServerSocket(PORT)) { + System.out.println("Server started on port " + PORT); + + while (true) { + Socket clientSocket = serverSocket.accept(); + System.out.println("New client connected: " + clientSocket.getInetAddress()); + + ClientHandler handler = new ClientHandler(clientSocket); + new Thread(handler).start(); + } + + } catch (IOException e) { + System.err.println("Server error: " + e.getMessage()); + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/org/to/telegramfinalproject/Server/SessionManager.java b/src/main/java/org/to/telegramfinalproject/Server/SessionManager.java new file mode 100644 index 0000000..351be7c --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Server/SessionManager.java @@ -0,0 +1,35 @@ +package org.to.telegramfinalproject.Server; + +import java.net.Socket; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class SessionManager { + private static final Map onlineUsers = new ConcurrentHashMap<>(); //catch UUID for find the user socket + + public static void addUser(UUID userId, Socket socket) { + onlineUsers.put(userId, socket); + } + + public static void removeUser(UUID userId) { + onlineUsers.remove(userId); + } + + public static boolean isOnline(UUID userId) { + return onlineUsers.containsKey(userId); + } + + public static Socket getUserSocket(UUID userId) { + return onlineUsers.get(userId); + } + + public static UUID getUserIdBySocket(Socket socket) { + for (Map.Entry entry : onlineUsers.entrySet()) { + if (entry.getValue().equals(socket)) { + return entry.getKey(); + } + } + return null; + } +} diff --git a/src/main/java/org/to/telegramfinalproject/UI/LoginForm.java b/src/main/java/org/to/telegramfinalproject/UI/LoginForm.java new file mode 100644 index 0000000..79ae18e --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/LoginForm.java @@ -0,0 +1,107 @@ +package org.to.telegramfinalproject.UI; + + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; +import javafx.stage.Stage; +import org.json.JSONObject; +import org.to.telegramfinalproject.Client.ClientConnection; +import org.to.telegramfinalproject.Database.userDatabase; +import org.to.telegramfinalproject.Models.User; +import org.to.telegramfinalproject.Security.PasswordHashing; + +import java.io.IOException; + +public class LoginForm { + + @FXML + private Button loginButton; + @FXML + private Button backButton; + @FXML private TextField usernameField; + @FXML private PasswordField passwordField; + + private ClientConnection connection; + + @FXML + public void initialize() { + + try { + connection = new ClientConnection("localhost", 12345); + } catch (Exception e) { + System.out.println("Could not connect to server: " + e.getMessage()); + } + + loginButton.setOnAction(e -> { + String username = usernameField.getText(); + String password = passwordField.getText(); + + JSONObject request = new JSONObject(); + request.put("action", "login"); + request.put("user_id", JSONObject.NULL); + request.put("username", username); + request.put("password", password); + request.put("profile_name", JSONObject.NULL); + if (connection!=null) { + connection.send(request.toString()); + } + userDatabase userDb = new userDatabase(); + User user = userDb.findByUsername(username); + + if(!userDb.existsByUsername(username)){ + Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid username"); + alert.show(); + } + else if(!PasswordHashing.verify(password,user.getPassword())){ + Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid password"); + alert.show(); + } + else if(!PasswordHashing.verify(password,user.getPassword()) && !userDb.existsByUsername(username)){ + Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid password and username"); + alert.show(); + } + else{ + try { + String responseStr = connection.receive(); + JSONObject response = new JSONObject(responseStr); + System.out.println("Status: " + response.getString("status")); + System.out.println("Message: " + response.getString("message")); + Alert alert = new Alert(Alert.AlertType.INFORMATION, " Message: " + response.getString("message")); + alert.show(); + } catch (Exception ex) { + Alert alert = new Alert(Alert.AlertType.ERROR, "Error receiving response: " + ex.getMessage()); + alert.show(); + } + } + }); + + backButton.setOnAction(e -> { + switchScene("login_view.fxml"); + }); + + + } + + private void switchScene(String fxmlFile) { + try { + + FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/" + fxmlFile)); + Parent root = loader.load(); + + + Stage stage = (Stage) backButton.getScene().getWindow(); + stage.setScene(new Scene(root)); + stage.show(); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/org/to/telegramfinalproject/UI/RegisterForm.java b/src/main/java/org/to/telegramfinalproject/UI/RegisterForm.java new file mode 100644 index 0000000..4903cc8 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/RegisterForm.java @@ -0,0 +1,116 @@ +package org.to.telegramfinalproject.UI; + + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; +import javafx.stage.Stage; +import org.json.JSONObject; +import org.to.telegramfinalproject.Client.ClientConnection; +import org.to.telegramfinalproject.Database.userDatabase; + +import java.io.IOException; + +public class RegisterForm { + @FXML + private Button submitButton; + @FXML + private Button backButton; + @FXML private TextField userIdField; + @FXML private TextField usernameField; + @FXML private TextField profileNameField; + @FXML private PasswordField passwordField; + @FXML private PasswordField confirmPasswordField; + + + + private ClientConnection connection; + + @FXML + public void initialize() { + + try { + connection = new ClientConnection("localhost", 12345); + } catch (Exception e) { + System.out.println("Could not connect to server: " + e.getMessage()); + } + + submitButton.setOnAction(e -> { + String userID = userIdField.getText(); + String username = usernameField.getText(); + String profile_name = profileNameField.getText(); + String password = passwordField.getText(); + String confirmPass =confirmPasswordField.getText(); + JSONObject request = new JSONObject(); + String passwordRegex = "\\b(?=[^\\s]*[A-Z])(?=[^\\s]*[a-z])(?=[^\\s]*\\d)(?=[^\\s]*[!@#$%^&*])[^\\s]{8,}\\b"; + + userDatabase userDb = new userDatabase(); + if(password.equals(confirmPass) && password.matches(passwordRegex) && !userDb.existsByUserId(userID)&& !userDb.existsByUsername(username)){ + try { + request.put("action", "register"); + request.put("user_id", userID); + request.put("username", username); + request.put("password", password); + request.put("profile_name", profile_name); + connection.send(request.toString()); + Alert alert = new Alert(Alert.AlertType.INFORMATION, "Registration is successful"); + alert.show(); + + } catch (Exception ex) { + Alert alert = new Alert(Alert.AlertType.ERROR, "Error receiving response: " + ex.getMessage()); + alert.show(); + } + + } + else if(!password.equals(confirmPass) && password.matches(passwordRegex)) { + Alert alert = new Alert(Alert.AlertType.ERROR, "Password doesn't match"); + alert.show(); + } + else if(userDb.existsByUserId(userID)) + { + Alert alert = new Alert(Alert.AlertType.ERROR, "User ID is already exist"); + alert.show(); + } + else if(userDb.existsByUsername(username)){ + Alert alert = new Alert(Alert.AlertType.ERROR, "Username is already exist"); + alert.show(); + } + else { + Alert alert = new Alert(Alert.AlertType.ERROR, "Password doesn't Strong enough"); + alert.show(); + } + + + + + }); + + backButton.setOnAction(e -> { + switchScene("login_view.fxml"); + }); + + + } + + private void switchScene(String fxmlFile) { + try { + + FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/" + fxmlFile)); + Parent root = loader.load(); + + + Stage stage = (Stage) backButton.getScene().getWindow(); + stage.setScene(new Scene(root)); + stage.show(); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/org/to/telegramfinalproject/UI/TelegramApplication.java b/src/main/java/org/to/telegramfinalproject/UI/TelegramApplication.java new file mode 100644 index 0000000..1119cb8 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/TelegramApplication.java @@ -0,0 +1,26 @@ +package org.to.telegramfinalproject.UI; + + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.to.telegramfinalproject.HelloApplication; + +import java.io.IOException; + +public class TelegramApplication extends Application { + @Override + public void start(Stage stage) throws IOException { + FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("login_view.fxml")); + Scene scene = new Scene(fxmlLoader.load(), 320, 240); + stage.setTitle("Hello!"); + stage.setScene(scene); + stage.show(); + } + public static void main(String[] args) { + launch(); + } + +} + diff --git a/src/main/java/org/to/telegramfinalproject/UI/login_view.java b/src/main/java/org/to/telegramfinalproject/UI/login_view.java new file mode 100644 index 0000000..a7235ee --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/UI/login_view.java @@ -0,0 +1,48 @@ +package org.to.telegramfinalproject.UI; + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.stage.Stage; + +import java.io.IOException; + +public class login_view { + + @FXML + private Button loginButton; + + @FXML + private Button registerButton; + + @FXML + public void initialize() { + + loginButton.setOnAction(e -> { + switchScene("LoginForm.fxml"); + }); + + registerButton.setOnAction(e -> { + switchScene("RegisterForm.fxml"); + }); + } + + private void switchScene(String fxmlFile) { + try { + + FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/" + fxmlFile)); + Parent root = loader.load(); + + + Stage stage = (Stage) loginButton.getScene().getWindow(); + stage.setScene(new Scene(root)); + stage.show(); + + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/src/main/resources/org/to/telegramfinalproject/LoginForm.fxml b/src/main/resources/org/to/telegramfinalproject/LoginForm.fxml new file mode 100644 index 0000000..3c3430e --- /dev/null +++ b/src/main/resources/org/to/telegramfinalproject/LoginForm.fxml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/org/to/telegramfinalproject/RegisterForm.fxml b/src/main/resources/org/to/telegramfinalproject/RegisterForm.fxml new file mode 100644 index 0000000..4f3f7dd --- /dev/null +++ b/src/main/resources/org/to/telegramfinalproject/RegisterForm.fxml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + +