package be.simplenotes.domain import arrow.core.raise.either import be.simplenotes.domain.security.HtmlSanitizer import be.simplenotes.domain.utils.parseSearchTerms import be.simplenotes.persistence.repositories.NoteRepository import be.simplenotes.persistence.repositories.UserRepository import be.simplenotes.search.NoteSearcher import be.simplenotes.types.LoggedInUser import be.simplenotes.types.Note import be.simplenotes.types.PersistedNoteMetadata import jakarta.annotation.PostConstruct import jakarta.annotation.PreDestroy import jakarta.inject.Singleton import java.util.* @Singleton class NoteService( private val markdownService: MarkdownService, private val noteRepository: NoteRepository, private val userRepository: UserRepository, private val searcher: NoteSearcher, private val htmlSanitizer: HtmlSanitizer, ) { fun create(user: LoggedInUser, markdownText: String) = either { markdownService.renderDocument(markdownText) .map { it.copy(html = htmlSanitizer.sanitize(user, it.html)) } .map { Note(title = it.metadata.title, tags = it.metadata.tags, markdown = markdownText, html = it.html) } .map { noteRepository.create(user.userId, it) } .bind() .also { searcher.indexNote(user.userId, it) } } fun update(user: LoggedInUser, uuid: UUID, markdownText: String) = either { markdownService.renderDocument(markdownText) .map { it.copy(html = htmlSanitizer.sanitize(user, it.html)) } .map { Note( title = it.metadata.title, tags = it.metadata.tags, markdown = markdownText, html = it.html, ) } .map { noteRepository.update(user.userId, uuid, it) } .bind() ?.also { searcher.updateIndex(user.userId, it) } } fun paginatedNotes( userId: Int, page: Int, itemsPerPage: Int = 20, tag: String? = null, deleted: Boolean = false, ): PaginatedNotes { val count = noteRepository.count(userId, tag, deleted) val offset = (page - 1) * itemsPerPage val numberOfPages = (count / itemsPerPage) + 1 val notes = if (count == 0) emptyList() else noteRepository.findAll(userId, itemsPerPage, offset, tag, deleted) return PaginatedNotes(numberOfPages, notes) } fun find(userId: Int, uuid: UUID) = noteRepository.find(userId, uuid) fun trash(userId: Int, uuid: UUID): Boolean { val res = noteRepository.delete(userId, uuid, permanent = false) if (res) searcher.deleteIndex(userId, uuid) return res } fun restore(userId: Int, uuid: UUID): Boolean { val res = noteRepository.restore(userId, uuid) if (res) find(userId, uuid)?.let { note -> searcher.indexNote(userId, note) } return res } fun delete(userId: Int, uuid: UUID): Boolean { val res = noteRepository.delete(userId, uuid, permanent = true) if (res) searcher.deleteIndex(userId, uuid) return res } fun countDeleted(userId: Int) = noteRepository.count(userId, deleted = true) @PostConstruct fun indexAll() { dropAllIndexes() val userIds = userRepository.findAll() userIds.forEach { id -> val notes = noteRepository.findAllDetails(id) searcher.indexNotes(id, notes) } } fun search(userId: Int, searchInput: String) = searcher.search(userId, parseSearchTerms(searchInput)) @PreDestroy fun dropAllIndexes() = searcher.dropAll() fun makePublic(userId: Int, uuid: UUID) = noteRepository.makePublic(userId, uuid) fun makePrivate(userId: Int, uuid: UUID) = noteRepository.makePrivate(userId, uuid) fun findPublic(uuid: UUID) = noteRepository.findPublic(uuid) } data class PaginatedNotes(val pages: Int, val notes: List)