Refactor + // img download
This commit is contained in:
parent
b1e2efa75f
commit
d3bcff470c
16
pom.xml
16
pom.xml
@ -9,7 +9,7 @@
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<main.class>be.simplenotes.c2c/C2cKt</main.class>
|
||||
<main.class>be.simplenotes.c2c/Camp2CampKt</main.class>
|
||||
<openhtml.version>1.0.4</openhtml.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
@ -204,20 +204,6 @@
|
||||
<mainClass>${main.class}</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>com.github.ben-manes.caffeine:caffeine</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>org.jetbrains.kotlin:kotlin-reflect</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
@ -1,102 +0,0 @@
|
||||
package be.simplenotes.c2c
|
||||
|
||||
import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.api.ApiUrls
|
||||
import be.simplenotes.c2c.pdf.PdfCreator
|
||||
import be.simplenotes.c2c.pdf.StreamFactory
|
||||
import be.simplenotes.c2c.routes.IndexRoute
|
||||
import be.simplenotes.c2c.routes.PdfRoute
|
||||
import be.simplenotes.c2c.routes.RouteRoute
|
||||
import be.simplenotes.c2c.routes.SearchRoute
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import com.mitchellbosecke.pebble.loader.ClasspathLoader
|
||||
import org.apache.hc.client5.http.impl.DefaultRedirectStrategy
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder
|
||||
import org.http4k.client.ApacheClient
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.contract
|
||||
import org.http4k.contract.openapi.ApiInfo
|
||||
import org.http4k.contract.openapi.v3.OpenApi3
|
||||
import org.http4k.core.Filter
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import org.http4k.core.then
|
||||
import org.http4k.filter.RequestFilters
|
||||
import org.http4k.filter.ServerFilters
|
||||
import org.http4k.format.Jackson
|
||||
import org.http4k.server.asServer
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
val module = module {
|
||||
single {
|
||||
ApacheClient(
|
||||
HttpClientBuilder
|
||||
.create()
|
||||
.setRedirectStrategy(DefaultRedirectStrategy())
|
||||
.build()
|
||||
)
|
||||
}
|
||||
single { ApiUrls(get()) }
|
||||
single { Jackson.mapper }
|
||||
single { Api(get(), get()) }
|
||||
single { PdfCreator(get()) }
|
||||
single { StreamFactory(get()) }
|
||||
single {
|
||||
PebbleEngine
|
||||
.Builder()
|
||||
.loader(ClasspathLoader().apply {
|
||||
prefix = "views/"
|
||||
suffix = ".twig"
|
||||
})
|
||||
.cacheActive(false)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
val routes = module {
|
||||
single(named<SearchRoute>()) { SearchRoute(get()).searchRoute() } bind ContractRoute::class
|
||||
single(named<RouteRoute>()) { RouteRoute(get()).routeRoute() } bind ContractRoute::class
|
||||
single(named<PdfRoute>()) { PdfRoute(get(), get()).route() } bind ContractRoute::class
|
||||
single(named<IndexRoute>()) { IndexRoute(get()).route() } bind ContractRoute::class
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val koin = startKoin {
|
||||
modules(module, routes)
|
||||
}.koin
|
||||
|
||||
val appRoutes = koin.getAll<ContractRoute>()
|
||||
val app = contract {
|
||||
renderer = OpenApi3(ApiInfo("Camp2Camp", "1.0-SNAPSHOT"), Jackson)
|
||||
descriptionPath = "/api/swagger.json"
|
||||
routes.all.addAll(appRoutes)
|
||||
}
|
||||
|
||||
val logger = LoggerFactory.getLogger("Camp2Camp")
|
||||
|
||||
val loggingFilter = RequestFilters.Tap {
|
||||
logger.info("${it.method} ${it.uri}")
|
||||
}
|
||||
|
||||
val catchAll = Filter { next ->
|
||||
{
|
||||
try {
|
||||
next(it)
|
||||
} catch (e: Exception) {
|
||||
val sw = StringWriter()
|
||||
e.printStackTrace(PrintWriter(sw))
|
||||
logger.error(sw.toString())
|
||||
Response(Status.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catchAll.then(ServerFilters.GZip()).then(loggingFilter).then(app).asServer(CustomApacheServer(4000)).start()
|
||||
logger.info("Listening on http://localhost:4000")
|
||||
}
|
||||
53
src/main/kotlin/Camp2Camp.kt
Normal file
53
src/main/kotlin/Camp2Camp.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package be.simplenotes.c2c
|
||||
|
||||
import be.simplenotes.c2c.filters.catchAllFilter
|
||||
import be.simplenotes.c2c.filters.loggingFilter
|
||||
import be.simplenotes.c2c.routes.RouteProvider
|
||||
import org.http4k.contract.contract
|
||||
import org.http4k.contract.openapi.ApiInfo
|
||||
import org.http4k.contract.openapi.v3.OpenApi3
|
||||
import org.http4k.core.then
|
||||
import org.http4k.filter.ServerFilters
|
||||
import org.http4k.format.Jackson
|
||||
import org.http4k.server.ServerConfig
|
||||
import org.http4k.server.asServer
|
||||
import org.koin.core.context.startKoin
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
fun main() {
|
||||
val koin = startKoin {
|
||||
modules(
|
||||
serverModule,
|
||||
routes,
|
||||
clientModule,
|
||||
apiModule,
|
||||
utilsModule,
|
||||
pdfModule,
|
||||
)
|
||||
properties(mapOf(
|
||||
"port" to System.getenv().getOrDefault("PORT", "4000"),
|
||||
"host" to System.getenv().getOrDefault("HOST", "localhost"),
|
||||
))
|
||||
}.koin
|
||||
|
||||
val appRoutes = koin.getAll<RouteProvider>().map(RouteProvider::route)
|
||||
val app = contract {
|
||||
renderer = OpenApi3(ApiInfo("Camp2Camp", "1.0-SNAPSHOT"), Jackson)
|
||||
descriptionPath = "/api/swagger.json"
|
||||
routes.all.addAll(appRoutes)
|
||||
}
|
||||
|
||||
val logger = LoggerFactory.getLogger("Camp2Camp")
|
||||
val server = koin.get<ServerConfig>()
|
||||
val address = koin.get<InetSocketAddress>()
|
||||
|
||||
catchAllFilter(logger)
|
||||
.then(ServerFilters.GZip())
|
||||
.then(loggingFilter(logger))
|
||||
.then(app)
|
||||
.asServer(server)
|
||||
.start()
|
||||
|
||||
logger.info("Listening on http://${address.hostName}:${address.port}")
|
||||
}
|
||||
53
src/main/kotlin/Modules.kt
Normal file
53
src/main/kotlin/Modules.kt
Normal file
@ -0,0 +1,53 @@
|
||||
package be.simplenotes.c2c
|
||||
|
||||
import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.api.ApiUrls
|
||||
import be.simplenotes.c2c.api.ImageDownloader
|
||||
import be.simplenotes.c2c.pdf.PdfCreator
|
||||
import be.simplenotes.c2c.routes.*
|
||||
import be.simplenotes.c2c.server.CustomApacheServer
|
||||
import be.simplenotes.c2c.templates.PebbleEngine
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import org.apache.hc.client5.http.impl.DefaultRedirectStrategy
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder
|
||||
import org.http4k.client.ApacheClient
|
||||
import org.http4k.format.Jackson
|
||||
import org.http4k.server.ServerConfig
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
val routes = module {
|
||||
single { SearchRouteProvider(get()) } bind RouteProvider::class
|
||||
single { RouteRouteProvider(get()) } bind RouteProvider::class
|
||||
single { PdfRouteProvider(get(), get(), get()) } bind RouteProvider::class
|
||||
single { IndexRouteProvider(get()) } bind RouteProvider::class
|
||||
}
|
||||
|
||||
val serverModule = module {
|
||||
single { InetSocketAddress(getProperty("host"), getProperty("port").toInt()) }
|
||||
single<ServerConfig> { CustomApacheServer(get()) }
|
||||
}
|
||||
|
||||
val clientModule = module {
|
||||
single { ApacheClient(get()) }
|
||||
single { HttpClientBuilder.create().setRedirectStrategy(DefaultRedirectStrategy()).build() }
|
||||
}
|
||||
|
||||
val apiModule = module {
|
||||
single { ApiUrls(get()) }
|
||||
single { Api(get(), get()) }
|
||||
single { ImageDownloader(get()) }
|
||||
}
|
||||
|
||||
val utilsModule = module {
|
||||
single { Jackson.mapper }
|
||||
single { PebbleEngine(cache = false, prefix = "templates/") }
|
||||
}
|
||||
|
||||
val pdfModule = module {
|
||||
val pdf = named("pdf")
|
||||
single { PdfCreator(get(pdf)) }
|
||||
single(pdf) { get<PebbleEngine>().getTemplate("pdf") }
|
||||
}
|
||||
24
src/main/kotlin/api/ImageDownloader.kt
Normal file
24
src/main/kotlin/api/ImageDownloader.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package be.simplenotes.c2c.api
|
||||
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Request
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.stream.Collectors
|
||||
|
||||
class ImageDownloader(private val client: HttpHandler) {
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
fun download(images: List<Image>): Map<String, ByteArray?> =
|
||||
images.map(Image::medium)
|
||||
.parallelStream()
|
||||
.collect(Collectors.toConcurrentMap(
|
||||
{ it },
|
||||
{
|
||||
val res = client(Request(Method.GET, it))
|
||||
logger.info("GET ${res.status} $it")
|
||||
if (res.status.successful) res.body.stream.readAllBytes()
|
||||
else null
|
||||
}
|
||||
))
|
||||
}
|
||||
26
src/main/kotlin/filters/Filters.kt
Normal file
26
src/main/kotlin/filters/Filters.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package be.simplenotes.c2c.filters
|
||||
|
||||
import org.http4k.core.Filter
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import org.http4k.filter.RequestFilters
|
||||
import org.slf4j.Logger
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
fun loggingFilter(logger: Logger) = RequestFilters.Tap {
|
||||
logger.info("${it.method} ${it.uri}")
|
||||
}
|
||||
|
||||
fun catchAllFilter(logger: Logger) = Filter { next ->
|
||||
{
|
||||
try {
|
||||
next(it)
|
||||
} catch (e: Exception) {
|
||||
val sw = StringWriter()
|
||||
e.printStackTrace(PrintWriter(sw))
|
||||
logger.error(sw.toString())
|
||||
Response(Status.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,59 +2,19 @@ package be.simplenotes.c2c.pdf
|
||||
|
||||
import be.simplenotes.c2c.api.Route
|
||||
import be.simplenotes.c2c.templates.render
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import com.mitchellbosecke.pebble.loader.ClasspathLoader
|
||||
import com.openhtmltopdf.extend.FSStream
|
||||
import com.mitchellbosecke.pebble.template.PebbleTemplate
|
||||
import com.openhtmltopdf.extend.FSStreamFactory
|
||||
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder
|
||||
import com.openhtmltopdf.util.XRLog
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Request
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.helper.W3CDom
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class StreamFactory(private val client: HttpHandler) : FSStreamFactory {
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
class PdfCreator(private val template: PebbleTemplate) {
|
||||
|
||||
private val imgCache = Caffeine.newBuilder()
|
||||
.maximumSize(15)
|
||||
.expireAfterWrite(15, TimeUnit.MINUTES)
|
||||
.build<String, ByteArray>()
|
||||
|
||||
override fun getUrl(url: String) = object : FSStream {
|
||||
override fun getStream(): InputStream? {
|
||||
val bytes = imgCache.get(url) {
|
||||
logger.info("Downloading $url")
|
||||
val res = client(Request(Method.GET, url))
|
||||
if (res.status.successful) res.body.stream.readAllBytes()
|
||||
else null
|
||||
}
|
||||
return bytes?.inputStream()
|
||||
}
|
||||
|
||||
override fun getReader() = null
|
||||
}
|
||||
}
|
||||
|
||||
class PdfCreator(private val streamFactory: StreamFactory) {
|
||||
private val engine = PebbleEngine
|
||||
.Builder()
|
||||
.loader(ClasspathLoader().apply {
|
||||
prefix = "templates/"
|
||||
suffix = ".twig"
|
||||
})
|
||||
.cacheActive(true)
|
||||
.build()
|
||||
|
||||
fun create(route: Route): ByteArrayInputStream {
|
||||
val html = engine.render("index", mapOf("route" to route))
|
||||
fun create(route: Route, streamFactory: FSStreamFactory): ByteArrayInputStream {
|
||||
val html = template.render(mapOf("route" to route))
|
||||
|
||||
XRLog.setLoggingEnabled(false)
|
||||
|
||||
|
||||
12
src/main/kotlin/pdf/PreloadedStreamFactory.kt
Normal file
12
src/main/kotlin/pdf/PreloadedStreamFactory.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package be.simplenotes.c2c.pdf
|
||||
|
||||
import com.openhtmltopdf.extend.FSStream
|
||||
import com.openhtmltopdf.extend.FSStreamFactory
|
||||
import java.io.InputStream
|
||||
|
||||
class PreloadedStreamFactory(private val images: Map<String, ByteArray?>) : FSStreamFactory {
|
||||
override fun getUrl(url: String) = object : FSStream {
|
||||
override fun getStream(): InputStream? = images[url]?.inputStream()
|
||||
override fun getReader() = null
|
||||
}
|
||||
}
|
||||
@ -4,20 +4,18 @@ import be.simplenotes.c2c.templates.render
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.meta
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import org.http4k.core.*
|
||||
|
||||
class IndexRoute(private val engine: PebbleEngine) {
|
||||
class IndexRouteProvider(private val engine: PebbleEngine) : RouteProvider {
|
||||
|
||||
fun route(): ContractRoute {
|
||||
override fun route(): ContractRoute {
|
||||
val spec = "/" meta {
|
||||
summary = "Redoc ui"
|
||||
produces += ContentType.TEXT_HTML
|
||||
} bindContract Method.GET
|
||||
|
||||
val route: HttpHandler = {
|
||||
Response(Status.OK).body(engine.render("index"))
|
||||
Response(Status.OK).body(engine.render("index")).header("Content-Type", "text/html; charset=utf-8")
|
||||
}
|
||||
|
||||
return spec to route
|
||||
@ -1,38 +0,0 @@
|
||||
package be.simplenotes.c2c.routes
|
||||
|
||||
import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.pdf.PdfCreator
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.div
|
||||
import org.http4k.contract.meta
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import org.http4k.lens.Path
|
||||
|
||||
// TODO: rename..
|
||||
class PdfRoute(private val api: Api, private val pdfCreator: PdfCreator) {
|
||||
|
||||
fun route(): ContractRoute {
|
||||
val spec = "/route" / Path.of("id", "Route's ID") / "pdf" meta {
|
||||
summary = "Generate pdf from routes"
|
||||
} bindContract Method.GET
|
||||
|
||||
fun route(id: String, pdf: String): HttpHandler = {
|
||||
when (val route = api.getRoute(id)) {
|
||||
null -> Response(Status.NOT_FOUND)
|
||||
else -> {
|
||||
val bytes = pdfCreator.create(route)
|
||||
Response(Status.OK)
|
||||
.body(bytes)
|
||||
.header("Content-Type", "application/pdf")
|
||||
.header("Content-Disposition", "attachment; filename=\"out.pdf\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spec to ::route
|
||||
}
|
||||
|
||||
}
|
||||
39
src/main/kotlin/routes/PdfRouteProvider.kt
Normal file
39
src/main/kotlin/routes/PdfRouteProvider.kt
Normal file
@ -0,0 +1,39 @@
|
||||
package be.simplenotes.c2c.routes
|
||||
|
||||
import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.api.ImageDownloader
|
||||
import be.simplenotes.c2c.pdf.PdfCreator
|
||||
import be.simplenotes.c2c.pdf.PreloadedStreamFactory
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.div
|
||||
import org.http4k.contract.meta
|
||||
import org.http4k.core.*
|
||||
import org.http4k.lens.Path
|
||||
|
||||
class PdfRouteProvider(
|
||||
private val api: Api,
|
||||
private val pdfCreator: PdfCreator,
|
||||
private val imageDownloader: ImageDownloader,
|
||||
) : RouteProvider {
|
||||
|
||||
override fun route(): ContractRoute {
|
||||
val spec = "/route" / Path.of("id", "Route's ID") / "pdf" meta {
|
||||
summary = "Generate pdf from routes"
|
||||
produces += ContentType("application/pdf")
|
||||
} bindContract Method.GET
|
||||
|
||||
fun route(id: String, pdf: String): HttpHandler = {
|
||||
api.getRoute(id)?.let {
|
||||
val imageMap = imageDownloader.download(it.associations.images)
|
||||
|
||||
Response(Status.OK)
|
||||
.body(pdfCreator.create(it, PreloadedStreamFactory(imageMap)))
|
||||
.header("Content-Type", "application/pdf")
|
||||
.header("Content-Disposition", "attachment; filename=\"out.pdf\"")
|
||||
} ?: Response(Status.NOT_FOUND)
|
||||
}
|
||||
|
||||
return spec to ::route
|
||||
}
|
||||
|
||||
}
|
||||
7
src/main/kotlin/routes/RouteProvider.kt
Normal file
7
src/main/kotlin/routes/RouteProvider.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package be.simplenotes.c2c.routes
|
||||
|
||||
import org.http4k.contract.ContractRoute
|
||||
|
||||
interface RouteProvider {
|
||||
fun route(): ContractRoute
|
||||
}
|
||||
@ -10,13 +10,14 @@ import org.http4k.format.Jackson.auto
|
||||
import org.http4k.lens.Path
|
||||
|
||||
// TODO: rename..
|
||||
class RouteRoute(private val api: Api) {
|
||||
class RouteRouteProvider(private val api: Api) : RouteProvider {
|
||||
|
||||
fun routeRoute(): ContractRoute {
|
||||
override fun route(): ContractRoute {
|
||||
val responseLens = Body.auto<Route>("Route").toLens()
|
||||
|
||||
val spec = "/route" / Path.of("id", "Route's ID") meta {
|
||||
summary = "Route details"
|
||||
produces += ContentType.APPLICATION_JSON
|
||||
} bindContract Method.GET
|
||||
|
||||
fun route(id: String): HttpHandler = {
|
||||
@ -5,21 +5,22 @@ import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.api.RouteResult
|
||||
import be.simplenotes.c2c.api.SearchResults
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.Tag
|
||||
import org.http4k.contract.RequestMeta
|
||||
import org.http4k.contract.meta
|
||||
import org.http4k.core.*
|
||||
import org.http4k.format.Jackson.auto
|
||||
import org.http4k.lens.Query
|
||||
|
||||
class SearchRoute(private val api: Api) {
|
||||
class SearchRouteProvider(private val api: Api) : RouteProvider {
|
||||
|
||||
fun searchRoute(): ContractRoute {
|
||||
override fun route(): ContractRoute {
|
||||
val query = Query.required("q", "Search terms")
|
||||
val responseLens = Body.auto<SearchResults>("Search results").toLens()
|
||||
|
||||
val spec = "/search" meta {
|
||||
summary = "Search routes and waypoints"
|
||||
queries += query
|
||||
receiving(RequestMeta(Request(Method.GET, "/search").query("q", "tour de bavon")))
|
||||
returning(Status.OK, responseLens to SearchResults(listOf(RouteResult(
|
||||
id = "57103",
|
||||
title = "Tour de Bavon : Thor",
|
||||
@ -1,4 +1,4 @@
|
||||
package be.simplenotes.c2c
|
||||
package be.simplenotes.c2c.server
|
||||
|
||||
import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap
|
||||
import org.apache.hc.core5.http.io.SocketConfig
|
||||
@ -6,13 +6,16 @@ import org.http4k.core.HttpHandler
|
||||
import org.http4k.server.Http4kRequestHandler
|
||||
import org.http4k.server.Http4kServer
|
||||
import org.http4k.server.ServerConfig
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
class CustomApacheServer(private val socketAddress: InetSocketAddress) : ServerConfig {
|
||||
|
||||
class CustomApacheServer(val port: Int) : ServerConfig {
|
||||
override fun toServer(httpHandler: HttpHandler): Http4kServer = object : Http4kServer {
|
||||
val handler = Http4kRequestHandler(httpHandler)
|
||||
|
||||
val server = ServerBootstrap.bootstrap()
|
||||
.setListenerPort(port)
|
||||
.setListenerPort(socketAddress.port)
|
||||
.setLocalAddress(socketAddress.address)
|
||||
.setSocketConfig(SocketConfig.custom()
|
||||
.setTcpNoDelay(true)
|
||||
.setSoKeepAlive(true)
|
||||
@ -28,6 +31,6 @@ class CustomApacheServer(val port: Int) : ServerConfig {
|
||||
|
||||
override fun stop() = apply { server.stop() }
|
||||
|
||||
override fun port(): Int = port
|
||||
override fun port(): Int = socketAddress.port
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,21 @@
|
||||
package be.simplenotes.c2c.templates
|
||||
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import com.mitchellbosecke.pebble.loader.ClasspathLoader
|
||||
import com.mitchellbosecke.pebble.template.PebbleTemplate
|
||||
import java.io.StringWriter
|
||||
|
||||
fun PebbleEngine.render(name: String, args: Map<String, Any?> = mapOf()): String {
|
||||
val template = getTemplate(name)
|
||||
fun PebbleEngine.render(name: String, args: Map<String, Any?> = mapOf()) = getTemplate(name).render(args)
|
||||
|
||||
fun PebbleTemplate.render(args: Map<String, Any?> = mapOf()): String {
|
||||
val writer = StringWriter()
|
||||
template.evaluate(writer, args)
|
||||
evaluate(writer, args)
|
||||
return writer.toString()
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun PebbleEngine(cache: Boolean, prefix: String? = null, suffix: String? = ".twig"): PebbleEngine =
|
||||
PebbleEngine.Builder().loader(ClasspathLoader().also { loader ->
|
||||
prefix?.let { loader.prefix = it }
|
||||
suffix?.let { loader.suffix = it }
|
||||
}).cacheActive(cache).build()
|
||||
|
||||
@ -1,10 +1,19 @@
|
||||
<h1>{{ route.title }}</h1>
|
||||
<p>{{ route.summary }}</p>
|
||||
<p>{{ route.description }}</p>
|
||||
{% for img in route.associations.images %}
|
||||
<figure>
|
||||
<img src="{{ img.medium }}"
|
||||
alt="{{ img.title }}">
|
||||
<figcaption>{{ img.title }}</figcaption>
|
||||
</figure>
|
||||
{% endfor %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>ReDoc</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<redoc spec-url='/api/swagger.json'></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
10
src/main/resources/templates/pdf.twig
Normal file
10
src/main/resources/templates/pdf.twig
Normal file
@ -0,0 +1,10 @@
|
||||
<h1>{{ route.title }}</h1>
|
||||
<p>{{ route.summary }}</p>
|
||||
<p>{{ route.description }}</p>
|
||||
{% for img in route.associations.images %}
|
||||
<figure>
|
||||
<img src="{{ img.medium }}"
|
||||
alt="{{ img.title }}">
|
||||
<figcaption>{{ img.title }}</figcaption>
|
||||
</figure>
|
||||
{% endfor %}
|
||||
@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>ReDoc</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<redoc spec-url='/api/swagger.json'></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user