1+ package com.panomc.platform.route.api
2+
3+ import com.panomc.platform.AppConstants
4+ import com.panomc.platform.annotation.Endpoint
5+ import com.panomc.platform.config.ConfigManager
6+ import com.panomc.platform.model.Api
7+ import com.panomc.platform.model.Path
8+ import com.panomc.platform.model.Result
9+ import com.panomc.platform.model.RouteType
10+ import com.panomc.platform.util.HashUtil.hash
11+ import com.panomc.platform.util.MimeTypeUtil
12+ import io.vertx.ext.web.RoutingContext
13+ import io.vertx.ext.web.validation.ValidationHandler
14+ import io.vertx.ext.web.validation.builder.Parameters.param
15+ import io.vertx.ext.web.validation.builder.ValidationHandlerBuilder
16+ import io.vertx.json.schema.SchemaRepository
17+ import io.vertx.json.schema.common.dsl.Schemas.stringSchema
18+ import java.io.File
19+
20+ @Endpoint
21+ class GetThemeSettingResourceAPI (private val configManager : ConfigManager ) : Api() {
22+ override val paths = listOf (Path (" /api/theme/file/:filename" , RouteType .GET ))
23+
24+ companion object {
25+ private const val CACHE_TTL_SECONDS = 7 * 24 * 60 * 60 // 1 week
26+ }
27+
28+ override fun getValidationHandler (schemaRepository : SchemaRepository ): ValidationHandler =
29+ ValidationHandlerBuilder .create(schemaRepository)
30+ .pathParameter(param(" filename" , stringSchema()))
31+ .build()
32+
33+ override suspend fun handle (context : RoutingContext ): Result ? {
34+ val parameters = getParameters(context)
35+
36+ val filename = parameters.pathParameter(" filename" ).string
37+
38+ val path = configManager.config
39+ .fileUploadsFolder + File .separator + AppConstants .THEME_SETTINS_FILE_UPLOAD_FOLDER + File .separator +
40+ filename
41+
42+ val file = File (path)
43+
44+ if (! file.exists()) {
45+ context.response().setStatusCode(404 ).end()
46+
47+ return null
48+ }
49+
50+ val actualHash = File (path).inputStream().hash()
51+
52+ val etag = " \" $actualHash \" " // strong ETag
53+ val ifNoneMatch = context.request().getHeader(" If-None-Match" )
54+ if (ifNoneMatch?.split(' ,' )?.map { it.trim() }?.contains(etag) == true ) {
55+ context.response()
56+ .setStatusCode(304 )
57+ .putHeader(" ETag" , etag)
58+ .putHeader(" Cache-Control" , " public, max-age=$CACHE_TTL_SECONDS , immutable" )
59+ .end()
60+ return null
61+ }
62+
63+ val mimeType = MimeTypeUtil .getMimeTypeFromFileName(path)
64+
65+ val response = context.response()
66+ response.putHeader(" Content-Type" , mimeType)
67+ response.putHeader(" ETag" , etag)
68+ response.putHeader(" Cache-Control" , " public, max-age=$CACHE_TTL_SECONDS , immutable" )
69+
70+ response.sendFile(path)
71+
72+ return null
73+ }
74+ }
0 commit comments