Separating from KTor
This commit is contained in:
parent
e9f71cdab9
commit
a907c57134
@ -1,3 +1,3 @@
|
||||
kotlin.code.style=official
|
||||
|
||||
toolsVersion=0.11.5-kotlin-1.6.21
|
||||
toolsVersion=0.11.7-kotlin-1.7.0
|
@ -296,7 +296,7 @@ internal fun Application.spcMaster(context: Context, dataPath: Path, prefix: Str
|
||||
|
||||
val snark = context.fetch(SnarkPlugin)
|
||||
|
||||
val magProgPageContext: PageContext = snark.parse(prefix, dataPath.resolve("content"))
|
||||
val magProgPageContext: PageContext = snark.read(dataPath.resolve("content"), prefix)
|
||||
|
||||
routing {
|
||||
route(prefix) {
|
||||
|
@ -5,8 +5,6 @@ import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.log
|
||||
import io.ktor.server.html.respondHtml
|
||||
import io.ktor.server.http.content.files
|
||||
import io.ktor.server.http.content.static
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.html.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
@ -304,26 +302,17 @@ internal fun Application.spcHome(context: Context, rootPath: Path, prefix: Strin
|
||||
|
||||
val snark = context.fetch(SnarkPlugin)
|
||||
|
||||
val homePageContext = snark.parse(prefix, rootPath.resolve("content"))
|
||||
val homePageContext = snark.read(rootPath.resolve("content"), prefix)
|
||||
|
||||
routing {
|
||||
route(prefix) {
|
||||
snark(homePageContext) {
|
||||
staticDirectory("assets", rootPath.resolve("assets"))
|
||||
staticDirectory("images", rootPath.resolve("images"))
|
||||
page { spcHome() }
|
||||
}
|
||||
|
||||
with(homePageContext) {
|
||||
static("assets") {
|
||||
files(rootPath.resolve("assets").toFile())
|
||||
}
|
||||
|
||||
static("images") {
|
||||
files(rootPath.resolve("images").toFile())
|
||||
}
|
||||
|
||||
get {
|
||||
withRequest(call.request) {
|
||||
call.respondHtml {
|
||||
spcHome()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spcDirectory("consulting")
|
||||
spcPage("ru/consulting")
|
||||
|
@ -63,12 +63,15 @@ internal val Data<*>.published: Boolean get() = meta["published"].string != "fal
|
||||
|
||||
fun PageContext(rootUrl: String, data: DataSet<*>): PageContext = PageContext(rootUrl, data.meta, data)
|
||||
|
||||
fun SnarkPlugin.parse(rootUrl: String, path: Path): PageContext {
|
||||
fun SnarkPlugin.read(path: Path, rootUrl: String = "/"): PageContext {
|
||||
val parsedData: DataSet<Any> = readDirectory(path)
|
||||
|
||||
return PageContext(rootUrl, parsedData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute uri in [PageContext] with uri in the call to properly resolve relative refs. Only host properties are substituted.
|
||||
*/
|
||||
context(PageContext) inline fun withRequest(request: ApplicationRequest, block: context(PageContext) () -> Unit) {
|
||||
val uri = URLBuilder(
|
||||
protocol = URLProtocol.createOrDefault(request.origin.scheme),
|
||||
|
61
src/main/kotlin/space/kscience/snark/RouteBuilder.kt
Normal file
61
src/main/kotlin/space/kscience/snark/RouteBuilder.kt
Normal file
@ -0,0 +1,61 @@
|
||||
package space.kscience.snark
|
||||
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.html.respondHtml
|
||||
import io.ktor.server.http.content.file
|
||||
import io.ktor.server.http.content.files
|
||||
import io.ktor.server.http.content.static
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.route
|
||||
import kotlinx.html.HTML
|
||||
import java.nio.file.Path
|
||||
|
||||
interface RouteBuilder {
|
||||
val pageContext: PageContext
|
||||
|
||||
fun staticFile(remotePath: String, file: Path)
|
||||
|
||||
fun staticDirectory(remotePath: String, directory: Path)
|
||||
|
||||
fun page(route: String = "", content: context(PageContext) HTML.() -> Unit)
|
||||
|
||||
fun route(route: String, block: RouteBuilder.() -> Unit)
|
||||
}
|
||||
|
||||
class KtorRouteBuilder(override val pageContext: PageContext, private val ktorRoute: Route) : RouteBuilder {
|
||||
override fun staticFile(remotePath: String, file: Path) {
|
||||
ktorRoute.file(remotePath, file.toFile())
|
||||
}
|
||||
|
||||
override fun staticDirectory(remotePath: String, directory: Path) {
|
||||
ktorRoute.static(remotePath) {
|
||||
files(directory.toFile())
|
||||
}
|
||||
}
|
||||
|
||||
override fun page(route: String, content: context(PageContext) HTML.() -> Unit) {
|
||||
ktorRoute.get(route) {
|
||||
with(pageContext) {
|
||||
withRequest(call.request) {
|
||||
val innerContext = this
|
||||
call.respondHtml {
|
||||
content(innerContext, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun route(route: String, block: RouteBuilder.() -> Unit) {
|
||||
ktorRoute.route(route) {
|
||||
block(KtorRouteBuilder(pageContext, this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.snark(pageContext: PageContext, block: context(PageContext) RouteBuilder.() -> Unit) {
|
||||
with(pageContext){
|
||||
block(KtorRouteBuilder(pageContext, this@snark))
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package space.kscience.snark
|
||||
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.utils.io.core.Input
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.unsafe
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
object SnarkHtmlParser : SnarkParser<HtmlFragment> {
|
||||
override val contentType: ContentType = ContentType.Text.Html
|
||||
override val fileExtensions: Set<String> = setOf("html")
|
||||
override val type: KType = typeOf<HtmlFragment>()
|
||||
|
||||
override fun readObject(input: Input): HtmlFragment = {
|
||||
div {
|
||||
unsafe { +input.readText() }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package space.kscience.snark
|
||||
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.util.extension
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import space.kscience.dataforge.context.*
|
||||
@ -24,10 +23,11 @@ import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* A parser of binary content including priority flag and file extensions
|
||||
*/
|
||||
@Type(SnarkParser.TYPE)
|
||||
interface SnarkParser<out R : Any> : IOReader<R> {
|
||||
val contentType: ContentType
|
||||
|
||||
val fileExtensions: Set<String>
|
||||
|
||||
val priority: Int get() = DEFAULT_PRIORITY
|
||||
@ -46,16 +46,17 @@ interface SnarkParser<out R : Any> : IOReader<R> {
|
||||
internal class SnarkParserWrapper<R : Any>(
|
||||
val reader: IOReader<R>,
|
||||
override val type: KType,
|
||||
override val contentType: ContentType,
|
||||
override val fileExtensions: Set<String>,
|
||||
) : SnarkParser<R>, IOReader<R> by reader
|
||||
|
||||
/**
|
||||
* Create a generic parser from reader
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
inline fun <reified R : Any> SnarkParser(
|
||||
reader: IOReader<R>,
|
||||
contentType: ContentType,
|
||||
vararg fileExtensions: String,
|
||||
): SnarkParser<R> = SnarkParserWrapper(reader, typeOf<R>(), contentType, fileExtensions.toSet())
|
||||
): SnarkParser<R> = SnarkParserWrapper(reader, typeOf<R>(), fileExtensions.toSet())
|
||||
|
||||
@OptIn(DFExperimental::class)
|
||||
class SnarkPlugin : AbstractPlugin() {
|
||||
@ -88,8 +89,11 @@ class SnarkPlugin : AbstractPlugin() {
|
||||
SnarkParser.TYPE -> mapOf(
|
||||
"html".asName() to SnarkHtmlParser,
|
||||
"markdown".asName() to SnarkMarkdownParser,
|
||||
"json".asName() to SnarkParser(JsonMetaFormat, ContentType.Application.Json, "json"),
|
||||
"yaml".asName() to SnarkParser(YamlMetaFormat, ContentType.Application.Json, "yaml", "yml")
|
||||
"json".asName() to SnarkParser(JsonMetaFormat, "json"),
|
||||
"yaml".asName() to SnarkParser(YamlMetaFormat, "yaml", "yml"),
|
||||
"png".asName() to SnarkParser(ImageIOReader, "png"),
|
||||
"jpg".asName() to SnarkParser(ImageIOReader, "jpg", "jpeg"),
|
||||
"gif".asName() to SnarkParser(ImageIOReader, "jpg", "jpeg"),
|
||||
)
|
||||
else -> super.content(target)
|
||||
}
|
||||
|
@ -1,34 +1,53 @@
|
||||
package space.kscience.snark
|
||||
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.util.asStream
|
||||
import io.ktor.utils.io.core.Input
|
||||
import kotlinx.html.div
|
||||
import kotlinx.html.unsafe
|
||||
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
|
||||
import org.intellij.markdown.html.HtmlGenerator
|
||||
import org.intellij.markdown.parser.MarkdownParser
|
||||
import space.kscience.dataforge.io.IOReader
|
||||
import java.awt.image.BufferedImage
|
||||
import javax.imageio.ImageIO
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
object SnarkMarkdownParser:SnarkParser<HtmlFragment> {
|
||||
override val contentType: ContentType = ContentType.Text.Html
|
||||
internal object SnarkHtmlParser : SnarkParser<HtmlFragment> {
|
||||
override val fileExtensions: Set<String> = setOf("html")
|
||||
override val type: KType = typeOf<HtmlFragment>()
|
||||
|
||||
override fun readObject(input: Input): HtmlFragment = {
|
||||
div {
|
||||
unsafe { +input.readText() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object SnarkMarkdownParser : SnarkParser<HtmlFragment> {
|
||||
override val fileExtensions: Set<String> = setOf("markdown", "mdown", "mkdn", "mkd", "md")
|
||||
override val type: KType = typeOf<HtmlFragment>()
|
||||
|
||||
private val markdownFlavor = CommonMarkFlavourDescriptor()
|
||||
private val markdownParser = MarkdownParser(markdownFlavor)
|
||||
|
||||
override fun readObject(input: Input): HtmlFragment {
|
||||
override fun readObject(input: Input): HtmlFragment {
|
||||
val src = input.readText()
|
||||
val parsedTree = markdownParser.buildMarkdownTreeFromString(src)
|
||||
val htmlString = HtmlGenerator(src, parsedTree, markdownFlavor).generateHtml()
|
||||
|
||||
return {
|
||||
div{
|
||||
div {
|
||||
unsafe {
|
||||
+htmlString
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object ImageIOReader : IOReader<BufferedImage> {
|
||||
override val type: KType get() = typeOf<BufferedImage>()
|
||||
|
||||
override fun readObject(input: Input): BufferedImage = ImageIO.read(input.asStream())
|
||||
}
|
Loading…
Reference in New Issue
Block a user