-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNoteService.java
More file actions
166 lines (148 loc) · 5.97 KB
/
NoteService.java
File metadata and controls
166 lines (148 loc) · 5.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package service;
import database.DBConnection;
import model.Note;
import model.User;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* Handles all note operations: create, read, update, delete, search.
*
* All note bodies are AES-256-CBC encrypted before writing to the DB.
* They are decrypted in memory using the user's derived key after login.
*/
public class NoteService {
private final User currentUser;
private final String plainPassword; // Kept in memory only for this session
public NoteService(User user, String plainPassword) {
this.currentUser = user;
this.plainPassword = plainPassword;
}
/**
* Saves a new note (encrypted) to the database.
*/
public Note createNote(String title, String plainBody, String category) {
String encrypted = EncryptionService.encryptWithPassword(
plainBody, plainPassword, currentUser.getUsername()
);
String sql = "INSERT INTO notes (user_id, title, body, category) VALUES (?, ?, ?, ?)";
try (PreparedStatement ps = DBConnection.getConnection().prepareStatement(
sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, currentUser.getId());
ps.setString(2, title);
ps.setString(3, encrypted);
ps.setString(4, category);
ps.executeUpdate();
ResultSet keys = ps.getGeneratedKeys();
Note note = new Note(currentUser.getId(), title, encrypted, category);
if (keys.next()) note.setId(keys.getInt(1));
return note;
} catch (SQLException e) {
throw new RuntimeException("Failed to create note", e);
}
}
/**
* Returns all notes for the current user, with bodies DECRYPTED.
* Optionally filter by category (pass null for all).
*/
public List<Note> getNotes(String categoryFilter) {
String sql = categoryFilter == null
? "SELECT * FROM notes WHERE user_id = ? ORDER BY updated_at DESC"
: "SELECT * FROM notes WHERE user_id = ? AND category = ? ORDER BY updated_at DESC";
List<Note> notes = new ArrayList<>();
try (PreparedStatement ps = DBConnection.getConnection().prepareStatement(sql)) {
ps.setInt(1, currentUser.getId());
if (categoryFilter != null) ps.setString(2, categoryFilter);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Note note = new Note();
note.setId(rs.getInt("id"));
note.setUserId(rs.getInt("user_id"));
note.setTitle(rs.getString("title"));
note.setCategory(rs.getString("category"));
note.setCreatedAt(rs.getString("created_at"));
note.setUpdatedAt(rs.getString("updated_at"));
// Decrypt the body before returning to the UI
String decrypted = EncryptionService.decryptWithPassword(
rs.getString("body"), plainPassword, currentUser.getUsername()
);
note.setBody(decrypted);
notes.add(note);
}
} catch (SQLException e) {
throw new RuntimeException("Failed to fetch notes", e);
}
return notes;
}
/**
* Returns all notes (no category filter).
*/
public List<Note> getAllNotes() {
return getNotes(null);
}
/**
* Searches notes by keyword in title OR body (case-insensitive).
* Bodies are decrypted in memory before searching.
*/
public List<Note> searchNotes(String keyword) {
String lower = keyword.toLowerCase();
List<Note> result = new ArrayList<>();
for (Note note : getAllNotes()) {
if (note.getTitle().toLowerCase().contains(lower)
|| note.getBody().toLowerCase().contains(lower)) {
result.add(note);
}
}
return result;
}
/**
* Updates an existing note's title, body (re-encrypted), and category.
*/
public boolean updateNote(int noteId, String newTitle, String newPlainBody, String category) {
String encrypted = EncryptionService.encryptWithPassword(
newPlainBody, plainPassword, currentUser.getUsername()
);
String sql = """
UPDATE notes SET title = ?, body = ?, category = ?,
updated_at = datetime('now')
WHERE id = ? AND user_id = ?
""";
try (PreparedStatement ps = DBConnection.getConnection().prepareStatement(sql)) {
ps.setString(1, newTitle);
ps.setString(2, encrypted);
ps.setString(3, category);
ps.setInt(4, noteId);
ps.setInt(5, currentUser.getId());
return ps.executeUpdate() > 0;
} catch (SQLException e) {
throw new RuntimeException("Failed to update note", e);
}
}
/**
* Permanently deletes a note by ID (only if it belongs to the current user).
*/
public boolean deleteNote(int noteId) {
String sql = "DELETE FROM notes WHERE id = ? AND user_id = ?";
try (PreparedStatement ps = DBConnection.getConnection().prepareStatement(sql)) {
ps.setInt(1, noteId);
ps.setInt(2, currentUser.getId());
return ps.executeUpdate() > 0;
} catch (SQLException e) {
throw new RuntimeException("Failed to delete note", e);
}
}
/**
* Returns the count of notes per category for the current user.
*/
public int countByCategory(String category) {
String sql = "SELECT COUNT(*) FROM notes WHERE user_id = ? AND category = ?";
try (PreparedStatement ps = DBConnection.getConnection().prepareStatement(sql)) {
ps.setInt(1, currentUser.getId());
ps.setString(2, category);
ResultSet rs = ps.executeQuery();
return rs.next() ? rs.getInt(1) : 0;
} catch (SQLException e) {
return 0;
}
}
}