diff --git a/api/pom.xml b/api/pom.xml
index 619ad87..e3e0a43 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -18,6 +18,7 @@
6.3.3
3.10.2
0.4
+ 1.2.2
official
UTF-8
@@ -149,6 +150,16 @@
HikariCP
3.4.2
+
+ com.sksamuel.hoplite
+ hoplite-core
+ ${hoplite_version}
+
+
+ com.sksamuel.hoplite
+ hoplite-yaml
+ ${hoplite_version}
+
com.github.javafaker
javafaker
diff --git a/api/resources/application.conf b/api/resources/application.conf
deleted file mode 100644
index 4b934ce..0000000
--- a/api/resources/application.conf
+++ /dev/null
@@ -1,22 +0,0 @@
-ktor {
- deployment {
- port = 8081
- port = ${?PORT}
- }
- application {
- modules = [ be.vandewalleh.NotesApplicationKt.module ]
- }
-}
-
-database {
- host = "127.0.0.1"
- port = "3306"
- name = "Notes"
- user = "test"
- password = "test"
-}
-
-jwt {
- secret = "thisisasecret"
- secret = ${?SECRET}
-}
\ No newline at end of file
diff --git a/api/resources/application.yaml b/api/resources/application.yaml
new file mode 100644
index 0000000..388f21a
--- /dev/null
+++ b/api/resources/application.yaml
@@ -0,0 +1,22 @@
+env: staging
+
+database:
+ host: 127.0.0.1
+ port: 3306
+ name: Notes
+ username: test
+ password: test
+
+server:
+ host: 0.0.0.0
+ port: 8081
+
+jwt:
+ secret: ${random.string(25)}
+ auth:
+ validity: 1
+ unit: HOURS
+ refresh:
+ validity: 15
+ unit: DAYS
+
diff --git a/api/src/NotesApplication.kt b/api/src/NotesApplication.kt
index 52ce1f8..04805fe 100644
--- a/api/src/NotesApplication.kt
+++ b/api/src/NotesApplication.kt
@@ -1,42 +1,64 @@
package be.vandewalleh
-import be.vandewalleh.features.configurationFeature
+import be.vandewalleh.features.Config
import be.vandewalleh.features.configurationModule
-import be.vandewalleh.features.features
+import be.vandewalleh.features.loadFeatures
import be.vandewalleh.migrations.Migration
import be.vandewalleh.routing.registerRoutes
import be.vandewalleh.services.serviceModule
import io.ktor.application.*
import io.ktor.routing.*
+import io.ktor.server.engine.*
+import io.ktor.server.netty.*
import me.liuwj.ktorm.database.*
import org.kodein.di.Kodein
import org.kodein.di.description
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.singleton
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import javax.sql.DataSource
+import kotlin.system.exitProcess
-lateinit var kodein: Kodein
-
-@Suppress("unused") // Referenced in application.conf
-fun Application.module() {
- // must be first to be loaded
- configurationFeature()
-
- kodein = Kodein {
- import(configurationModule)
- import(serviceModule)
-
- bind() with singleton { Migration(this.kodein) }
- bind() with singleton { Database.Companion.connect(this.instance()) }
- }
-
- features()
-
- log.debug(kodein.container.tree.bindings.description())
+val kodein = Kodein {
+ import(serviceModule)
+ import(configurationModule)
+ bind() with singleton { LoggerFactory.getLogger("Application") }
+ bind() with singleton { Migration(this.kodein) }
+ bind() with singleton { Database.connect(this.instance()) }
+}
+fun main() {
+ val config by kodein.instance()
+ val logger by kodein.instance()
+ logger.info("Running application with configuration $config")
val migration by kodein.instance()
migration.migrate()
+ serve(kodein)
+}
+
+fun serve(kodein: Kodein) {
+ val config by kodein.instance()
+ val logger by kodein.instance()
+ val env = applicationEngineEnvironment {
+ module {
+ module()
+ }
+ log = logger
+ connector {
+ host = config.server.host
+ port = config.server.port
+ }
+ }
+ embeddedServer(Netty, env).start(wait = true)
+}
+
+
+fun Application.module() {
+ loadFeatures()
+
+ log.debug(kodein.container.tree.bindings.description())
routing {
registerRoutes(kodein)
diff --git a/api/src/features/ConfigurationFeature.kt b/api/src/features/ConfigurationFeature.kt
index 67aae98..6dbad66 100644
--- a/api/src/features/ConfigurationFeature.kt
+++ b/api/src/features/ConfigurationFeature.kt
@@ -1,42 +1,61 @@
package be.vandewalleh.features
import be.vandewalleh.auth.SimpleJWT
+import com.sksamuel.hoplite.ConfigLoader
+import com.sksamuel.hoplite.Masked
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
-import io.ktor.application.*
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
+import org.kodein.di.generic.singleton
import java.util.concurrent.TimeUnit
import javax.sql.DataSource
/**
- * [Kodein] configuration module
+ * [Kodein] controller module containing the app configuration
*/
-lateinit var configurationModule: Kodein.Module
+val configurationModule = Kodein.Module(name = "Configuration") {
+ bind() from singleton { ConfigLoader().loadConfigOrThrow("/application.yaml") }
+ bind(tag = "auth") with singleton { configureAuthJwt(this.kodein) }
+ bind(tag = "refresh") with singleton { configureRefreshJwt(this.kodein) }
+ bind() with singleton { configureDatasource(this.kodein) }
+}
-fun Application.configurationFeature() {
- val dataSource: DataSource = with(environment.config) {
- val host = property("database.host").getString()
- val port = property("database.port").getString()
- val name = property("database.name").getString()
+data class DatabaseConfig(val host: String, val port: Int, val name: String, val username: String, val password: Masked)
+data class ServerConfig(val host: String, val port: Int)
+data class JwtConfig(val secret: Masked, val auth: JwtValidity, val refresh: JwtValidity)
+data class JwtValidity(val validity: Long, val unit: TimeUnit)
+data class Config(val database: DatabaseConfig, val server: ServerConfig, val jwt: JwtConfig)
- val hikariConfig = HikariConfig().apply {
- jdbcUrl = "jdbc:mariadb://$host:$port/$name"
- username = this@with.property("database.user").getString()
- password = this@with.property("database.password").getString()
- }
- HikariDataSource(hikariConfig)
+private fun configureAuthJwt(kodein: Kodein): SimpleJWT {
+ val config by kodein.instance()
+ val jwtSecret = config.jwt.secret
+ val authConfig = config.jwt.auth
+ return SimpleJWT(jwtSecret.value, authConfig.validity, authConfig.unit)
+}
+
+private fun configureRefreshJwt(kodein: Kodein): SimpleJWT {
+ val config by kodein.instance()
+ val jwtSecret = config.jwt.secret
+ val refreshConfig = config.jwt.auth
+ return SimpleJWT(jwtSecret.value, refreshConfig.validity, refreshConfig.unit)
+}
+
+private fun configureDatasource(kodein: Kodein): DataSource {
+ val config by kodein.instance()
+ val dbConfig = config.database
+
+ val host = dbConfig.host
+ val port = dbConfig.port
+ val name = dbConfig.name
+
+ val hikariConfig = HikariConfig().apply {
+ jdbcUrl = "jdbc:mariadb://$host:$port/$name"
+ username = dbConfig.username
+ password = dbConfig.password.value
}
- val jwtSecret = environment.config.property("jwt.secret").getString()
- val authSimpleJwt = SimpleJWT(jwtSecret, 1, TimeUnit.HOURS)
- val refreshSimpleJwt = SimpleJWT(jwtSecret, 7, TimeUnit.DAYS)
-
- configurationModule = Kodein.Module("Configuration") {
- bind() with instance(dataSource)
- bind(tag = "auth") with instance(authSimpleJwt)
- bind(tag = "refresh") with instance(refreshSimpleJwt)
- }
+ return HikariDataSource(hikariConfig)
}
\ No newline at end of file
diff --git a/api/src/features/ContentNegotiationFeature.kt b/api/src/features/ContentNegotiationFeature.kt
index 8906914..df99c0a 100644
--- a/api/src/features/ContentNegotiationFeature.kt
+++ b/api/src/features/ContentNegotiationFeature.kt
@@ -1,6 +1,5 @@
package be.vandewalleh.features
-import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.jackson.*
diff --git a/api/src/features/ErrorFeature.kt b/api/src/features/ErrorFeature.kt
index 6ca34cc..c901e7e 100644
--- a/api/src/features/ErrorFeature.kt
+++ b/api/src/features/ErrorFeature.kt
@@ -8,7 +8,7 @@ import io.ktor.utils.io.errors.*
fun Application.handleErrors() {
install(StatusPages) {
- exception { _ ->
+ exception {
call.respond(HttpStatusCode.BadRequest)
}
}
diff --git a/api/src/features/Features.kt b/api/src/features/Features.kt
index c57e449..e2a4eb3 100644
--- a/api/src/features/Features.kt
+++ b/api/src/features/Features.kt
@@ -3,7 +3,7 @@ package be.vandewalleh.features
import be.vandewalleh.auth.authenticationModule
import io.ktor.application.*
-fun Application.features() {
+fun Application.loadFeatures() {
corsFeature()
contentNegotiationFeature()
authenticationModule()