diff --git a/bus/src/main/java/de/srsoftware/umbrella/messagebus/EventQueue.java b/bus/src/main/java/de/srsoftware/umbrella/messagebus/EventQueue.java index 047a02b7..ce8f83fe 100644 --- a/bus/src/main/java/de/srsoftware/umbrella/messagebus/EventQueue.java +++ b/bus/src/main/java/de/srsoftware/umbrella/messagebus/EventQueue.java @@ -10,9 +10,11 @@ import java.util.LinkedList; public class EventQueue extends LinkedList> implements AutoCloseable, EventListener { private final InetSocketAddress addr; + private final System.Logger log; public EventQueue(InetSocketAddress addr){ this.addr = addr; + log = System.getLogger(addr.toString()); messageBus().register(this); } @@ -30,7 +32,7 @@ public class EventQueue extends LinkedList> implements AutoCloseable, E @Override public void onEvent(Event event) { - System.getLogger(addr.toString()).log(System.Logger.Level.INFO,"adding event to queue of {1}: {0}",event.eventType(),addr); + log.log(System.Logger.Level.INFO,"adding event to queue of {1}: {0}",event.eventType(),addr); add(event); } } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Envelope.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Envelope.java index fad56ac2..0e14c5c0 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Envelope.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Envelope.java @@ -22,7 +22,7 @@ import org.json.JSONObject; public class Envelope> { private final T message; private final Set receivers; - private final LocalDateTime time; + private LocalDateTime time; private final long id; public Envelope(long id, T message, User receiver){ @@ -73,7 +73,7 @@ public class Envelope> { } public boolean isFor(User receiver) { - return receivers.contains(receiver); + return receivers.stream().anyMatch(r -> r.equals(receiver)); } public T message(){ @@ -84,6 +84,11 @@ public class Envelope> { return receivers; } + public Envelope time(LocalDateTime timestamp){ + this.time = timestamp; + return this; + } + public LocalDateTime time(){ return time; } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java b/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java index 653b9692..f72adc98 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/UmbrellaUser.java @@ -25,11 +25,14 @@ public class UmbrellaUser extends User implements Mappable, Owner { @Override public boolean equals(Object o) { - if (!(o instanceof UmbrellaUser user)) return false; - return Objects.equals(email(), user.email()) - && Objects.equals(name(), user.name()) - && Objects.equals(id, user.id) - && Objects.equals(language(), user.language()); + if (o instanceof UmbrellaUser user) { + return Objects.equals(email(), user.email()) + && Objects.equals(name(), user.name()) + && Objects.equals(id, user.id) + && Objects.equals(language(), user.language()); + } + if (o instanceof User user) return user.equals(this); + return false; } @Override diff --git a/frontend/src/routes/message/Messages.svelte b/frontend/src/routes/message/Messages.svelte index 79061cbf..90c81663 100644 --- a/frontend/src/routes/message/Messages.svelte +++ b/frontend/src/routes/message/Messages.svelte @@ -9,17 +9,17 @@ let messages = []; let timer = null; - async function display(hash){ - const url = api(`message/${hash}`); + async function display(id){ + const url = api(`message/${id}`); const res = await get(url); if (res.ok){ yikes(); const json = await res.json(); if (timer) clearTimeout(timer); for (let i in messages){ - if (messages[i].hash == hash){ + if (messages[i].id == id){ messages[i].body = json.body; - timer = setTimeout(() => {mark_read(hash)}, 2000); + timer = setTimeout(() => {mark_read(id)}, 2000); } else { delete messages[i].body; } @@ -40,13 +40,13 @@ } } - async function mark_read(hash){ + async function mark_read(id){ timer = null; - const url = api(`message/read/${hash}`); + const url = api(`message/read/${id}`); const res = await patch(url); if (res.ok) { for (let i in messages){ - if (messages[i].hash == hash) messages[i].read = true; + if (messages[i].id == id) messages[i].read = true; } yikes(); } else { @@ -73,7 +73,7 @@ {#each messages as message} - display(message.hash)}> + display(message.id)}> {#if message.read}→ {/if} {message.timestamp} @@ -82,7 +82,7 @@ {message.subject} {#if message.body} - +
{message.body.trim()}
diff --git a/messages/src/main/java/de/srsoftware/umbrella/message/MessageSystem.java b/messages/src/main/java/de/srsoftware/umbrella/message/MessageSystem.java index 849c5888..521c266c 100644 --- a/messages/src/main/java/de/srsoftware/umbrella/message/MessageSystem.java +++ b/messages/src/main/java/de/srsoftware/umbrella/message/MessageSystem.java @@ -112,13 +112,13 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener Optional token = SessionToken.from(ex).map(Token::of); var user = ModuleRegistry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); - var head = path.pop(); - return switch (head){ + var id = path.pop(); + return switch (id){ case null -> listMessages(ex,user.get()); case SETTINGS -> getSettings(ex,user.get()); default -> { try { - yield getMessage(ex,user.get(),Integer.parseInt(head)); + yield getMessage(ex,user.get(),Integer.parseInt(id)); } catch (NumberFormatException ignored) { } @@ -152,8 +152,8 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener } } - private boolean getMessage(HttpExchange ex, UmbrellaUser user, int hash) throws IOException { - var envelope = queue.getEnvelope(hash).filter(env -> env.isFor(user)); + private boolean getMessage(HttpExchange ex, UmbrellaUser user, long id) throws IOException { + var envelope = queue.getEnvelope(id).filter(env -> env.isFor(user)); if (envelope.isPresent()) return sendMessage(ex, user, envelope.get()); return notFound(ex); } @@ -221,7 +221,7 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener try { var envelopes = queue.getEnvelopesFor(receiver); - envelopes.stream().map(Envelope::message).forEach(combined::merge); + envelopes.stream().forEach(combined::merge); send(combined,date); envelopes.forEach(env -> queue.markRead(env.hashCode(),receiver)); } catch (Exception ex){ @@ -329,8 +329,8 @@ public class MessageSystem extends BaseHandler implements PostBox, EventListener var sender = message.sender().name(); var subject = message.subject(); var time = envelope.time().format(TIME_FORMATTER); - var hash = envelope.hashCode(); - return new JSONObject(Map.of(SENDER,sender,SUBJECT,subject,TIMESTAMP,time,HASH,hash)); + var id = envelope.id(); + return new JSONObject(Map.of(SENDER,sender,SUBJECT,subject,TIMESTAMP,time,ID,id)); } } diff --git a/messages/src/main/java/de/srsoftware/umbrella/message/SqliteMessageDb.java b/messages/src/main/java/de/srsoftware/umbrella/message/SqliteMessageDb.java index 453c6c95..d3df9220 100644 --- a/messages/src/main/java/de/srsoftware/umbrella/message/SqliteMessageDb.java +++ b/messages/src/main/java/de/srsoftware/umbrella/message/SqliteMessageDb.java @@ -7,7 +7,6 @@ import static de.srsoftware.tools.jdbc.Query.*; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; import static de.srsoftware.umbrella.core.Errors.*; import static de.srsoftware.umbrella.core.ModuleRegistry.userService; -import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR; import static de.srsoftware.umbrella.core.constants.Constants.COUNT; import static de.srsoftware.umbrella.core.constants.Field.*; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; @@ -19,6 +18,7 @@ import static java.time.ZoneOffset.UTC; import de.srsoftware.tools.jdbc.Query; import de.srsoftware.umbrella.core.BaseDb; +import de.srsoftware.umbrella.core.Util; import de.srsoftware.umbrella.core.constants.Text; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.*; @@ -120,7 +120,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N rs.close(); var receivers = new ArrayList(); - rs = select(ALL).from(TABLE_ATTACHMENTS).where(MESSAGE_ID,equal(messageId)).exec(db); + rs = select(ALL).from(TABLE_RECEIVERS).where(MESSAGE_ID,equal(messageId)).exec(db); while (rs.next()) receivers.add(new User(rs.getString(NAME),new EmailAddress(rs.getString(EMAIL)),null)); rs.close(); @@ -135,9 +135,8 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N if (envelope != null) return Optional.of(envelope); return Optional.empty(); } catch (SQLException e) { - throw failedToLoadObject(Text.MESSAGE,id); + throw failedToLoadObject(Text.MESSAGE,messageId); } - throw new UmbrellaException(HTTP_SERVER_ERROR,"{class}.getEnvelope({id}) not implemented!","class",getClass().getSimpleName(), ID ,id); // TODO } @Override @@ -163,7 +162,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N var messageId = rs.getLong(ID); var sender = userService().loadUser(rs.getLong(SENDER_USER_ID)); var msg = new TranslatedMessage(sender,rs.getString(SUBJECT),rs.getString(BODY), attachments.get(messageId)); - var envelope = new Envelope<>(messageId, msg, user); + var envelope = new Envelope<>(messageId, msg, user).time(Util.dateTimeOf(rs.getLong(TIMESTAMP))); envelopes.add(envelope); } rs.close(); @@ -206,6 +205,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N @Override public Optional> markRead(long messageId, User user) { try { + var envelope = getEnvelope(messageId); Query.delete().from(TABLE_RECEIVERS).where(MESSAGE_ID,equal(messageId)).where(EMAIL,equal(user.email().toString())).execute(db); var rs = select(COUNT).from(TABLE_RECEIVERS).where(MESSAGE_ID,equal(messageId)).exec(db); Integer count = null; @@ -216,7 +216,7 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N delete().from(TABLE_MESSAGES).where(ID,equal(messageId)).execute(db); } - // TODO load message or fail + return envelope; } catch (SQLException e) { throw failedToDropObject(Text.RECEIVER); } @@ -239,8 +239,10 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT N for (var receiver : envelope.receivers()){ insertInto(TABLE_RECEIVERS,MESSAGE_ID,EMAIL,NAME).values(messageId,receiver.email(),receiver.name()).execute(db).close(); } - for (var attachment : envelope.message().attachments()){ - insertInto(TABLE_ATTACHMENTS,MESSAGE_ID,NAME,MIME,DATA).values(messageId,attachment.name(),attachment.mime(),attachment.content()).execute(db).close(); + if (envelope.message().attachments() != null) { + for (var attachment : envelope.message().attachments()) { + insertInto(TABLE_ATTACHMENTS, MESSAGE_ID, NAME, MIME, DATA).values(messageId, attachment.name(), attachment.mime(), attachment.content()).execute(db).close(); + } } } catch (SQLException e) { throw failedToStoreObject(envelope).causedBy(e); diff --git a/messages/src/main/java/de/srsoftware/umbrella/message/model/CombinedMessage.java b/messages/src/main/java/de/srsoftware/umbrella/message/model/CombinedMessage.java index 2f0b78c6..7a5bd897 100644 --- a/messages/src/main/java/de/srsoftware/umbrella/message/model/CombinedMessage.java +++ b/messages/src/main/java/de/srsoftware/umbrella/message/model/CombinedMessage.java @@ -6,6 +6,9 @@ import static java.lang.System.Logger.Level.TRACE; import static java.text.MessageFormat.format; import de.srsoftware.umbrella.core.model.*; + +import java.text.DateFormat; +import java.time.format.DateTimeFormatter; import java.util.*; public class CombinedMessage { @@ -14,20 +17,24 @@ public class CombinedMessage { private final Set attachments = new HashSet<>(); private final StringBuilder combinedBody = new StringBuilder(); private final User receiver; + private final String lang; private String combinedSubject = null; private final List> mergedMessages = new ArrayList<>(); private final Translatable subjectForCombinedMessage; private UmbrellaUser sender = null; + private static DateTimeFormatter DT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public CombinedMessage(Translatable subjectForCombinedMessage, User receiver){ LOG.log(DEBUG,"Creating combined message for {0}…",receiver); this.subjectForCombinedMessage = subjectForCombinedMessage; this.receiver = receiver; + this.lang = receiver.language(); } - public void merge(Message message) { - LOG.log(TRACE,"Merging {0} into combined message…",message); - var lang = receiver.language(); + public void merge(Envelope envelope) { + LOG.log(TRACE,"Merging {0} into combined message…",envelope); + var message = envelope.message(); + if (message instanceof TranslatableMessage tm) message = tm.translate(lang); var body = message.body(); var subject = message.subject().toString(); @@ -38,11 +45,11 @@ public class CombinedMessage { combinedSubject = subject; break; case 1: - combinedBody.insert(0,format("# {0} / {1}:\n\n",sender,subject)); // insert sender and subject of first message right before the body of the first message + combinedBody.insert(0,format("# {0} @ {1}\n→ {2}:\n\n",sender,envelope.time().format(DT_FORMAT),subject)); // insert sender and subject of first message right before the body of the first message combinedSubject = subjectForCombinedMessage.translate(lang); // no break here, we need to append the subject and content default: - combinedBody.append("\n\n━━━━━━━━━━━━━━━━━━━━━\n\n# ").append(message.sender()).append(" / ").append(subject).append(":\n\n"); + combinedBody.append("\n\n━━━━━━━━━━━━━━━━━━━━━\n\n# ").append(message.sender()).append(" @ ").append(envelope.time().format(DT_FORMAT)).append("\n→ ").append(subject).append(":\n\n"); combinedBody.append(body); } if (message.attachments() != null) attachments.addAll(message.attachments());