Update config
This commit is contained in:
parent
204ae7988e
commit
7ad8b7039b
@ -1,6 +1 @@
|
||||
# mariadb
|
||||
MYSQL_ROOT_PASSWORD=
|
||||
MYSQL_PASSWORD=
|
||||
# simplenotes
|
||||
DB_PASSWORD=
|
||||
JWT_SECRET=
|
||||
|
||||
@ -12,18 +12,15 @@ FROM alpine
|
||||
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
ENV APPLICATION_USER simplenotes
|
||||
RUN adduser -D -g '' $APPLICATION_USER
|
||||
|
||||
RUN mkdir /app
|
||||
RUN chown -R $APPLICATION_USER /app
|
||||
|
||||
USER $APPLICATION_USER
|
||||
RUN mkdir /app/data
|
||||
|
||||
COPY --from=jdkbuilder /myjdk /myjdk
|
||||
COPY app/build/libs/app-with-dependencies*.jar /app/simplenotes.jar
|
||||
WORKDIR /app
|
||||
|
||||
VOLUME /app/data
|
||||
|
||||
ENV SERVER_HOST 0.0.0.0
|
||||
|
||||
CMD [ \
|
||||
|
||||
@ -4,7 +4,11 @@ import io.micronaut.context.ApplicationContext
|
||||
import java.lang.Runtime.getRuntime
|
||||
|
||||
fun main() {
|
||||
val ctx = ApplicationContext.run()
|
||||
val env = if (System.getenv("ENV") == "dev") "dev" else "prod"
|
||||
val ctx = ApplicationContext.builder()
|
||||
.deduceEnvironment(false)
|
||||
.environments(env)
|
||||
.start()
|
||||
ctx.createBean(Server::class.java)
|
||||
getRuntime().addShutdownHook(Thread { ctx.stop() })
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
package be.simplenotes.app.controllers
|
||||
|
||||
import be.simplenotes.domain.HealthCheckService
|
||||
import org.http4k.core.Request
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status.Companion.OK
|
||||
import org.http4k.core.Status.Companion.SERVICE_UNAVAILABLE
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class HealthCheckController(private val healthCheckService: HealthCheckService) {
|
||||
fun healthCheck(@Suppress("UNUSED_PARAMETER") request: Request) =
|
||||
if (healthCheckService.isOk()) Response(OK) else Response(SERVICE_UNAVAILABLE)
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
package be.simplenotes.app.routes
|
||||
|
||||
import be.simplenotes.app.controllers.BaseController
|
||||
import be.simplenotes.app.controllers.HealthCheckController
|
||||
import be.simplenotes.app.controllers.NoteController
|
||||
import be.simplenotes.app.controllers.UserController
|
||||
import be.simplenotes.app.filters.ImmutableFilter
|
||||
@ -19,7 +18,6 @@ import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class BasicRoutes(
|
||||
private val healthCheckController: HealthCheckController,
|
||||
private val baseCtrl: BaseController,
|
||||
private val userCtrl: UserController,
|
||||
private val noteCtrl: NoteController,
|
||||
@ -52,8 +50,6 @@ class BasicRoutes(
|
||||
"/notes/public/{uuid}" bind GET to noteCtrl::public,
|
||||
)
|
||||
),
|
||||
|
||||
"/health" bind GET to healthCheckController::healthCheck,
|
||||
staticHandler
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,10 +1,3 @@
|
||||
db:
|
||||
jdbc-url: jdbc:h2:./notes-db;
|
||||
username: h2
|
||||
password: ''
|
||||
connection-timeout: 3000
|
||||
maximum-pool-size: 10
|
||||
|
||||
jwt:
|
||||
secret: 'PliLvfk7l4WF+cZJk66LR5Mpnh+ocbvJ2wfUCK2UCms='
|
||||
validity: 24
|
||||
@ -13,3 +6,5 @@ jwt:
|
||||
server:
|
||||
host: localhost
|
||||
port: 8080
|
||||
|
||||
data-dir: ./data
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
package be.simplenotes.config
|
||||
|
||||
import io.micronaut.context.annotation.ConfigurationInject
|
||||
import io.micronaut.context.annotation.ConfigurationProperties
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@ConfigurationProperties("db")
|
||||
data class DataSourceConfig @ConfigurationInject constructor(
|
||||
val jdbcUrl: String,
|
||||
val username: String,
|
||||
val password: String,
|
||||
val maximumPoolSize: Int,
|
||||
val connectionTimeout: Long,
|
||||
) {
|
||||
override fun toString() = "DataSourceConfig(jdbcUrl='$jdbcUrl', username='$username', password='***', " +
|
||||
"maximumPoolSize=$maximumPoolSize, connectionTimeout=$connectionTimeout)"
|
||||
}
|
||||
|
||||
@ConfigurationProperties("jwt")
|
||||
data class JwtConfig @ConfigurationInject constructor(
|
||||
val secret: String,
|
||||
val validity: Long,
|
||||
val timeUnit: TimeUnit,
|
||||
) {
|
||||
override fun toString() = "JwtConfig(secret='***', validity=$validity, timeUnit=$timeUnit)"
|
||||
}
|
||||
|
||||
@ConfigurationProperties("server")
|
||||
data class ServerConfig @ConfigurationInject constructor(
|
||||
val host: String,
|
||||
val port: Int,
|
||||
)
|
||||
58
config/src/DataConfig.kt
Normal file
58
config/src/DataConfig.kt
Normal file
@ -0,0 +1,58 @@
|
||||
package be.simplenotes.config
|
||||
|
||||
import io.micronaut.context.annotation.*
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
data class DataConfig(val dataDir: String)
|
||||
|
||||
data class DataSourceConfig(
|
||||
val jdbcUrl: String,
|
||||
val maximumPoolSize: Int,
|
||||
val connectionTimeout: Long,
|
||||
)
|
||||
|
||||
@ConfigurationProperties("jwt")
|
||||
data class JwtConfig @ConfigurationInject constructor(
|
||||
val secret: String,
|
||||
val validity: Long,
|
||||
val timeUnit: TimeUnit,
|
||||
) {
|
||||
override fun toString() = "JwtConfig(secret='***', validity=$validity, timeUnit=$timeUnit)"
|
||||
}
|
||||
|
||||
@ConfigurationProperties("server")
|
||||
data class ServerConfig @ConfigurationInject constructor(
|
||||
val host: String,
|
||||
val port: Int,
|
||||
)
|
||||
|
||||
@Factory
|
||||
class ConfigFactory {
|
||||
|
||||
@Singleton
|
||||
@Requires(notEnv = ["test"])
|
||||
fun datasourceConfig(dataConfig: DataConfig) = DataSourceConfig(
|
||||
jdbcUrl = "jdbc:h2:" + Path.of(dataConfig.dataDir, "simplenotes").normalize().toAbsolutePath(),
|
||||
maximumPoolSize = 10,
|
||||
connectionTimeout = 1000
|
||||
)
|
||||
|
||||
@Singleton
|
||||
@Requires(env = ["test"])
|
||||
fun testDatasourceConfig() = DataSourceConfig(
|
||||
jdbcUrl = "jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1",
|
||||
maximumPoolSize = 2,
|
||||
connectionTimeout = 1000
|
||||
)
|
||||
|
||||
@Singleton
|
||||
@Requires(notEnv = ["test"])
|
||||
fun dataConfig(@Property(name = "data-dir") dataDir: String) = DataConfig(dataDir)
|
||||
|
||||
@Singleton
|
||||
@Requires(env = ["test"])
|
||||
fun testDataConfig() = DataConfig("/tmp")
|
||||
|
||||
}
|
||||
@ -1,54 +1,19 @@
|
||||
version: '2.2'
|
||||
|
||||
services:
|
||||
|
||||
db:
|
||||
image: mariadb:10.5.5
|
||||
container_name: simplenotes-mariadb
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Brussels
|
||||
- MYSQL_DATABASE=simplenotes
|
||||
- MYSQL_USER=simplenotes
|
||||
# .env:
|
||||
# - MYSQL_ROOT_PASSWORD
|
||||
# - MYSQL_PASSWORD
|
||||
volumes:
|
||||
- notes-db-volume:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: "mysql --protocol=tcp -u simplenotes -p$MYSQL_PASSWORD -e 'show databases'"
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
start_period: 2s
|
||||
retries: 10
|
||||
|
||||
simplenotes:
|
||||
image: hubv/simplenotes
|
||||
container_name: simplenotes
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Brussels
|
||||
- DB_JDBC_URL=jdbc:mariadb://db:3306/simplenotes
|
||||
- DB_USERNAME=simplenotes
|
||||
# .env:
|
||||
# - JWT_SECRET
|
||||
# - DB_PASSWORD
|
||||
ports:
|
||||
- 127.0.0.1:8080:8080
|
||||
healthcheck:
|
||||
test: "curl --fail -s http://localhost:8080/health"
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
start_period: 2s
|
||||
retries: 3
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./simplenotes-data:/app/data
|
||||
|
||||
|
||||
volumes:
|
||||
notes-db-volume:
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
package be.simplenotes.domain
|
||||
|
||||
import be.simplenotes.persistence.DbHealthCheck
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface HealthCheckService {
|
||||
fun isOk(): Boolean
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class HealthCheckServiceImpl(private val dbHealthCheck: DbHealthCheck) : HealthCheckService {
|
||||
override fun isOk() = dbHealthCheck.isOk()
|
||||
}
|
||||
@ -5,7 +5,6 @@ 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.persistence.transactions.TransactionService
|
||||
import be.simplenotes.search.NoteSearcher
|
||||
import be.simplenotes.types.LoggedInUser
|
||||
import be.simplenotes.types.Note
|
||||
@ -22,31 +21,34 @@ class NoteService(
|
||||
private val noteRepository: NoteRepository,
|
||||
private val userRepository: UserRepository,
|
||||
private val searcher: NoteSearcher,
|
||||
private val htmlSanitizer: HtmlSanitizer,
|
||||
private val transaction: TransactionService,
|
||||
private val htmlSanitizer: HtmlSanitizer
|
||||
) {
|
||||
|
||||
fun create(user: LoggedInUser, markdownText: String) = transaction.use {
|
||||
either.eager<MarkdownParsingError, PersistedNote> {
|
||||
markdownService.renderDocument(markdownText)
|
||||
.map { it.copy(html = htmlSanitizer.sanitize(user, it.html)) }
|
||||
.map { Note(it.metadata, markdown = markdownText, html = it.html) }
|
||||
.map { noteRepository.create(user.userId, it) }
|
||||
.bind()
|
||||
.also { searcher.indexNote(user.userId, it) }
|
||||
}
|
||||
fun create(user: LoggedInUser, markdownText: String) = either.eager<MarkdownParsingError, PersistedNote> {
|
||||
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) = transaction.use {
|
||||
fun update(user: LoggedInUser, uuid: UUID, markdownText: String) =
|
||||
either.eager<MarkdownParsingError, PersistedNote?> {
|
||||
markdownService.renderDocument(markdownText)
|
||||
.map { it.copy(html = htmlSanitizer.sanitize(user, it.html)) }
|
||||
.map { Note(it.metadata, markdown = markdownText, html = 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,
|
||||
@ -64,22 +66,22 @@ class NoteService(
|
||||
|
||||
fun find(userId: Int, uuid: UUID) = noteRepository.find(userId, uuid)
|
||||
|
||||
fun trash(userId: Int, uuid: UUID): Boolean = transaction.use {
|
||||
fun trash(userId: Int, uuid: UUID): Boolean {
|
||||
val res = noteRepository.delete(userId, uuid, permanent = false)
|
||||
if (res) searcher.deleteIndex(userId, uuid)
|
||||
res
|
||||
return res
|
||||
}
|
||||
|
||||
fun restore(userId: Int, uuid: UUID): Boolean = transaction.use {
|
||||
fun restore(userId: Int, uuid: UUID): Boolean {
|
||||
val res = noteRepository.restore(userId, uuid)
|
||||
if (res) find(userId, uuid)?.let { note -> searcher.indexNote(userId, note) }
|
||||
res
|
||||
return res
|
||||
}
|
||||
|
||||
fun delete(userId: Int, uuid: UUID): Boolean = transaction.use {
|
||||
fun delete(userId: Int, uuid: UUID): Boolean {
|
||||
val res = noteRepository.delete(userId, uuid, permanent = true)
|
||||
if (res) searcher.deleteIndex(userId, uuid)
|
||||
res
|
||||
return res
|
||||
}
|
||||
|
||||
fun countDeleted(userId: Int) = noteRepository.count(userId, deleted = true)
|
||||
@ -99,8 +101,8 @@ class NoteService(
|
||||
@PreDestroy
|
||||
fun dropAllIndexes() = searcher.dropAll()
|
||||
|
||||
fun makePublic(userId: Int, uuid: UUID) = transaction.use { noteRepository.makePublic(userId, uuid) }
|
||||
fun makePrivate(userId: Int, uuid: UUID) = transaction.use { noteRepository.makePrivate(userId, uuid) }
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@ import be.simplenotes.domain.security.PasswordHash
|
||||
import be.simplenotes.domain.security.SimpleJwt
|
||||
import be.simplenotes.domain.validation.UserValidations
|
||||
import be.simplenotes.persistence.repositories.UserRepository
|
||||
import be.simplenotes.persistence.transactions.TransactionService
|
||||
import be.simplenotes.search.NoteSearcher
|
||||
import be.simplenotes.types.LoggedInUser
|
||||
import be.simplenotes.types.PersistedUser
|
||||
@ -29,16 +28,13 @@ internal class UserServiceImpl(
|
||||
private val passwordHash: PasswordHash,
|
||||
private val jwt: SimpleJwt<LoggedInUser>,
|
||||
private val searcher: NoteSearcher,
|
||||
private val transactionService: TransactionService,
|
||||
) : UserService {
|
||||
|
||||
override fun register(form: RegisterForm) = transactionService.use {
|
||||
UserValidations.validateRegister(form)
|
||||
.filterOrElse({ !userRepository.exists(it.username) }, { RegisterError.UserExists })
|
||||
.map { it.copy(password = passwordHash.crypt(it.password)) }
|
||||
.map { userRepository.create(it) }
|
||||
.leftIfNull { RegisterError.UserExists }
|
||||
}
|
||||
override fun register(form: RegisterForm) = UserValidations.validateRegister(form)
|
||||
.filterOrElse({ !userRepository.exists(it.username) }, { RegisterError.UserExists })
|
||||
.map { it.copy(password = passwordHash.crypt(it.password)) }
|
||||
.map { userRepository.create(it) }
|
||||
.leftIfNull { RegisterError.UserExists }
|
||||
|
||||
override fun login(form: LoginForm) = either.eager<LoginError, Token> {
|
||||
UserValidations.validateLogin(form)
|
||||
@ -50,18 +46,16 @@ internal class UserServiceImpl(
|
||||
.bind()
|
||||
}
|
||||
|
||||
override fun delete(form: DeleteForm) = transactionService.use {
|
||||
either.eager<DeleteError, Unit> {
|
||||
val user = !UserValidations.validateDelete(form)
|
||||
val persistedUser = !userRepository.find(user.username).rightIfNotNull { DeleteError.Unregistered }
|
||||
!Either.conditionally(
|
||||
passwordHash.verify(user.password, persistedUser.password),
|
||||
{ DeleteError.WrongPassword },
|
||||
{ }
|
||||
)
|
||||
!Either.conditionally(userRepository.delete(persistedUser.id), { DeleteError.Unregistered }, { })
|
||||
searcher.dropIndex(persistedUser.id)
|
||||
}
|
||||
override fun delete(form: DeleteForm) = either.eager<DeleteError, Unit> {
|
||||
val user = !UserValidations.validateDelete(form)
|
||||
val persistedUser = !userRepository.find(user.username).rightIfNotNull { DeleteError.Unregistered }
|
||||
!Either.conditionally(
|
||||
passwordHash.verify(user.password, persistedUser.password),
|
||||
{ DeleteError.WrongPassword },
|
||||
{ }
|
||||
)
|
||||
!Either.conditionally(userRepository.delete(persistedUser.id), { DeleteError.Unregistered }, { })
|
||||
searcher.dropIndex(persistedUser.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@ import be.simplenotes.domain.security.UserJwtMapper
|
||||
import be.simplenotes.domain.testutils.isLeftOfType
|
||||
import be.simplenotes.domain.testutils.isRight
|
||||
import be.simplenotes.persistence.repositories.UserRepository
|
||||
import be.simplenotes.persistence.transactions.TransactionService
|
||||
import be.simplenotes.types.PersistedUser
|
||||
import com.natpryce.hamkrest.assertion.assertThat
|
||||
import com.natpryce.hamkrest.equalTo
|
||||
@ -23,16 +22,12 @@ internal class UserServiceTest {
|
||||
val passwordHash = BcryptPasswordHash(test = true)
|
||||
val jwtConfig = JwtConfig("a secret", 1, TimeUnit.HOURS)
|
||||
val simpleJwt = SimpleJwt(jwtConfig, UserJwtMapper())
|
||||
val noopTransactionService = object : TransactionService {
|
||||
override fun <T> use(block: () -> T) = block()
|
||||
}
|
||||
|
||||
val userService = UserServiceImpl(
|
||||
userRepository = userRepository,
|
||||
passwordHash = passwordHash,
|
||||
jwt = simpleJwt,
|
||||
searcher = mockk(),
|
||||
transactionService = noopTransactionService
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
|
||||
@ -29,8 +29,8 @@ class PersistenceModule {
|
||||
val hikariConfig = HikariConfig().also {
|
||||
it.jdbcUrl = conf.jdbcUrl
|
||||
it.driverClassName = "org.h2.Driver"
|
||||
it.username = conf.username
|
||||
it.password = conf.password
|
||||
it.username = ""
|
||||
it.password = ""
|
||||
it.maximumPoolSize = conf.maximumPoolSize
|
||||
it.connectionTimeout = conf.connectionTimeout
|
||||
it.dataSourceProperties["CASE_INSENSITIVE_IDENTIFIERS"] = "TRUE"
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
package be.simplenotes.persistence
|
||||
|
||||
import be.simplenotes.config.DataSourceConfig
|
||||
import io.micronaut.context.ApplicationContext
|
||||
import io.micronaut.context.BeanContext
|
||||
import org.flywaydb.core.Flyway
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.parallel.ResourceLock
|
||||
import javax.sql.DataSource
|
||||
@ -12,25 +11,10 @@ import javax.sql.DataSource
|
||||
@ResourceLock("h2")
|
||||
abstract class DbTest {
|
||||
|
||||
val beanContext = BeanContext.build()
|
||||
val beanContext = ApplicationContext.build().deduceEnvironment(false).environments("test").start()
|
||||
|
||||
inline fun <reified T> BeanContext.getBean(): T = getBean(T::class.java)
|
||||
|
||||
@BeforeAll
|
||||
fun setComponent() {
|
||||
beanContext
|
||||
.registerSingleton(
|
||||
DataSourceConfig(
|
||||
jdbcUrl = "jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1",
|
||||
username = "h2",
|
||||
password = "",
|
||||
maximumPoolSize = 2,
|
||||
connectionTimeout = 3000
|
||||
)
|
||||
)
|
||||
.start()
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun beforeEach() {
|
||||
val migration = beanContext.getBean<DbMigrations>()
|
||||
|
||||
@ -8,6 +8,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":types"))
|
||||
implementation(project(":config"))
|
||||
|
||||
implementation(Libs.Lucene.core)
|
||||
implementation(Libs.Lucene.queryParser)
|
||||
|
||||
@ -7,18 +7,15 @@ import org.apache.lucene.document.Field
|
||||
import org.apache.lucene.document.StringField
|
||||
import org.apache.lucene.document.TextField
|
||||
|
||||
internal fun PersistedNote.toDocument(): Document {
|
||||
val note = this
|
||||
return Document().apply {
|
||||
// non searchable fields
|
||||
add(StringField(uuidField, UuidFieldConverter.toDoc(note.uuid), Field.Store.YES))
|
||||
add(StringField(updatedAtField, LocalDateTimeFieldConverter.toDoc(note.updatedAt), Field.Store.YES))
|
||||
internal fun PersistedNote.toDocument() = Document().apply {
|
||||
// non searchable fields
|
||||
add(StringField(uuidField, UuidFieldConverter.toDoc(uuid), Field.Store.YES))
|
||||
add(StringField(updatedAtField, LocalDateTimeFieldConverter.toDoc(updatedAt), Field.Store.YES))
|
||||
|
||||
// searchable fields
|
||||
add(TextField(titleField, note.meta.title, Field.Store.YES))
|
||||
add(TextField(tagsField, TagsFieldConverter.toDoc(note.meta.tags), Field.Store.YES))
|
||||
add(TextField(contentField, note.markdown, Field.Store.YES))
|
||||
}
|
||||
// searchable fields
|
||||
add(TextField(titleField, title, Field.Store.YES))
|
||||
add(TextField(tagsField, TagsFieldConverter.toDoc(tags), Field.Store.YES))
|
||||
add(TextField(contentField, markdown, Field.Store.YES))
|
||||
}
|
||||
|
||||
internal fun Document.toNoteMeta() = PersistedNoteMetadata(
|
||||
|
||||
@ -16,12 +16,7 @@ import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class NoteSearcherImpl(
|
||||
@Named("search-index")
|
||||
basePath: Path,
|
||||
) : NoteSearcher {
|
||||
|
||||
constructor() : this(Path.of("/tmp", "lucene"))
|
||||
internal class NoteSearcherImpl(@Named("search-index") basePath: Path) : NoteSearcher {
|
||||
|
||||
private val baseFile = basePath.toFile()
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package be.simplenotes.search
|
||||
|
||||
import be.simplenotes.config.DataConfig
|
||||
import io.micronaut.context.annotation.Factory
|
||||
import io.micronaut.context.annotation.Prototype
|
||||
import java.nio.file.Path
|
||||
@ -10,5 +11,5 @@ class SearchModule {
|
||||
|
||||
@Named("search-index")
|
||||
@Prototype
|
||||
internal fun luceneIndex() = Path.of(".lucene")
|
||||
internal fun luceneIndex(dataConfig: DataConfig) = Path.of(dataConfig.dataDir).resolve("index")
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package be.simplenotes.search
|
||||
|
||||
import be.simplenotes.types.NoteMetadata
|
||||
import be.simplenotes.types.PersistedNote
|
||||
import be.simplenotes.types.PersistedNoteMetadata
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
@ -9,6 +8,7 @@ import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.parallel.ResourceLock
|
||||
import java.nio.file.Path
|
||||
import java.time.LocalDateTime
|
||||
import java.util.*
|
||||
|
||||
@ -16,7 +16,7 @@ import java.util.*
|
||||
internal class NoteSearcherImplTest {
|
||||
|
||||
// region setup
|
||||
private val searcher = NoteSearcherImpl()
|
||||
private val searcher = NoteSearcherImpl(Path.of("/tmp", "lucene"))
|
||||
|
||||
private fun index(
|
||||
title: String,
|
||||
@ -25,11 +25,12 @@ internal class NoteSearcherImplTest {
|
||||
uuid: UUID = UUID.randomUUID(),
|
||||
): PersistedNote {
|
||||
val note = PersistedNote(
|
||||
NoteMetadata(title, tags),
|
||||
title = title,
|
||||
tags = tags,
|
||||
markdown = content,
|
||||
html = "",
|
||||
LocalDateTime.MIN,
|
||||
uuid,
|
||||
updatedAt = LocalDateTime.MIN,
|
||||
uuid = uuid,
|
||||
public = false
|
||||
)
|
||||
searcher.indexNote(1, note)
|
||||
@ -150,7 +151,7 @@ internal class NoteSearcherImplTest {
|
||||
@Test
|
||||
fun `update index`() {
|
||||
val note = index("first")
|
||||
searcher.updateIndex(1, note.copy(meta = note.meta.copy(title = "new")))
|
||||
searcher.updateIndex(1, note.copy(title = "new"))
|
||||
assertThat(search("first")).isEmpty()
|
||||
assertThat(search("new")).hasSize(1)
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@ class NoteView(@Named("styles") styles: String) : View(styles) {
|
||||
}
|
||||
|
||||
fun renderedNote(loggedInUser: LoggedInUser?, note: PersistedNote, shared: Boolean) = renderPage(
|
||||
note.meta.title,
|
||||
note.title,
|
||||
loggedInUser = loggedInUser,
|
||||
scripts = listOf("/highlight.10.1.2.js", "/init-highlight.0.0.1.js")
|
||||
) {
|
||||
@ -136,9 +136,9 @@ class NoteView(@Named("styles") styles: String) : View(styles) {
|
||||
}
|
||||
|
||||
div("flex items-center justify-between mb-4") {
|
||||
h1("text-3xl fond-bold underline") { +note.meta.title }
|
||||
h1("text-3xl fond-bold underline") { +note.title }
|
||||
span("space-x-2") {
|
||||
note.meta.tags.forEach {
|
||||
note.tags.forEach {
|
||||
a(href = "/notes?tag=$it", classes = "tag") {
|
||||
+"#$it"
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user