fixes according to updates
This commit is contained in:
parent
7551d7c2bb
commit
a3ff08af27
@ -5,11 +5,11 @@ import dev.inmo.postssystem.features.users.common.User
|
||||
import dev.inmo.micro_utils.common.*
|
||||
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
||||
import io.ktor.client.HttpClientConfig
|
||||
import io.ktor.client.features.cookies.*
|
||||
import io.ktor.client.features.expectSuccess
|
||||
import io.ktor.client.plugins.cookies.AcceptAllCookiesStorage
|
||||
import io.ktor.client.plugins.cookies.HttpCookies
|
||||
import io.ktor.client.plugins.expectSuccess
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.HttpReceivePipeline
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -25,10 +25,6 @@ fun HttpClientConfig<*>.installClientAuthenticator(
|
||||
onUserRetrieved: suspend (User?) -> Unit,
|
||||
onAuthKeyInvalidated: suspend () -> Unit
|
||||
) {
|
||||
// install(Logging) {
|
||||
// logger = Logger.DEFAULT
|
||||
// level = LogLevel.HEADERS
|
||||
// }
|
||||
install(HttpCookies) {
|
||||
// Will keep an in-memory map with all the cookies from previous requests.
|
||||
storage = AcceptAllCookiesStorage()
|
||||
@ -98,13 +94,13 @@ fun HttpClientConfig<*>.installClientAuthenticator(
|
||||
|
||||
receivePipeline.intercept(HttpReceivePipeline.Before) {
|
||||
if (
|
||||
context.request.url.toString().startsWith(baseUrl)
|
||||
&& context.response.status == HttpStatusCode.Unauthorized
|
||||
it.request.url.toString().startsWith(baseUrl)
|
||||
&& it.status == HttpStatusCode.Unauthorized
|
||||
) {
|
||||
authMutex.withLock { refreshToken() }
|
||||
val newResponse = context.client ?.request<HttpResponse>{
|
||||
takeFrom(context.request)
|
||||
} ?: return@intercept
|
||||
val newResponse = it.call.client.request {
|
||||
takeFrom(it.request)
|
||||
}
|
||||
proceedWith(newResponse)
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ import dev.inmo.postssystem.features.users.common.User
|
||||
import dev.inmo.micro_utils.coroutines.safely
|
||||
import dev.inmo.micro_utils.ktor.server.*
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.*
|
||||
import io.ktor.application.*
|
||||
import io.ktor.auth.*
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.sessions.*
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.sessions.sessions
|
||||
import kotlinx.serialization.builtins.nullable
|
||||
|
||||
data class AuthUserPrincipal(
|
||||
@ -58,7 +58,7 @@ class AuthenticationRoutingConfigurator(
|
||||
}
|
||||
}
|
||||
}
|
||||
post(authRefreshPathPart) {
|
||||
post (authRefreshPathPart) {
|
||||
safely(
|
||||
{
|
||||
// TODO:: add error info
|
||||
@ -107,7 +107,7 @@ class AuthenticationRoutingConfigurator(
|
||||
}
|
||||
}
|
||||
|
||||
override fun Authentication.Configuration.invoke() {
|
||||
override fun AuthenticationConfig.invoke() {
|
||||
session<AuthToken> {
|
||||
validate {
|
||||
val result = authTokensService.getUserPrincipal(it)
|
||||
|
@ -4,14 +4,14 @@ import dev.inmo.postssystem.features.auth.common.AuthToken
|
||||
import dev.inmo.postssystem.features.common.common.Milliseconds
|
||||
import dev.inmo.postssystem.features.auth.common.tokenSessionKey
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationSessionsConfigurator
|
||||
import io.ktor.sessions.*
|
||||
import io.ktor.server.sessions.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class SessionAuthenticationConfigurator(
|
||||
private val maxAge: Milliseconds
|
||||
) : ApplicationSessionsConfigurator.Element {
|
||||
private val maxAgeInSeconds = TimeUnit.MILLISECONDS.toSeconds(maxAge)
|
||||
override fun Sessions.Configuration.invoke() {
|
||||
override fun SessionsConfig.invoke() {
|
||||
cookie<AuthToken>(tokenSessionKey) {
|
||||
cookie.maxAgeInSeconds = maxAgeInSeconds
|
||||
serializer = object : SessionSerializer<AuthToken> {
|
||||
|
@ -17,7 +17,7 @@ kotlin {
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
api libs.ktor.auth
|
||||
api libs.ktor.server.auth
|
||||
api libs.logback
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
package dev.inmo.postssystem.features.common.server.sessions
|
||||
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.KtorApplicationConfigurator
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.auth.Authentication
|
||||
import io.ktor.auth.authentication
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.auth.*
|
||||
|
||||
class ApplicationAuthenticationConfigurator(
|
||||
private val elements: List<Element>
|
||||
) : KtorApplicationConfigurator {
|
||||
fun interface Element { operator fun Authentication.Configuration.invoke() }
|
||||
fun interface Element { operator fun AuthenticationConfig.invoke() }
|
||||
|
||||
override fun Application.configure() {
|
||||
authentication {
|
||||
|
@ -8,6 +8,7 @@ import dev.inmo.micro_utils.repos.ReadCRUDRepo
|
||||
import dev.inmo.micro_utils.repos.ktor.client.crud.KtorReadStandardCrudRepo
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.statement.readBytes
|
||||
import kotlinx.serialization.BinaryFormat
|
||||
@ -31,8 +32,8 @@ class ClientReadFilesStorage(
|
||||
filesGetFilesPathPart
|
||||
)
|
||||
|
||||
override suspend fun getBytes(id: FileId): ByteArray = client.post<HttpResponse>(fullFilesGetBytesPath) {
|
||||
body = serialFormat.encodeToByteArray(FileId.serializer(), id)
|
||||
override suspend fun getBytes(id: FileId): ByteArray = client.post(fullFilesGetBytesPath) {
|
||||
setBody(serialFormat.encodeToByteArray(FileId.serializer(), id))
|
||||
}.readBytes()
|
||||
|
||||
override suspend fun getFullFileInfo(
|
||||
|
@ -6,10 +6,10 @@ import dev.inmo.micro_utils.ktor.server.*
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureReadStandardCrudRepoRoutes
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureWriteStandardCrudRepoRoutes
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.response.respondBytes
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.response.respondBytes
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.builtins.nullable
|
||||
|
||||
class FilesRoutingConfigurator(
|
||||
|
@ -4,7 +4,7 @@ import dev.inmo.postssystem.features.roles.common.*
|
||||
import dev.inmo.postssystem.features.roles.manager.common.GeneralRolesManagerRole
|
||||
import dev.inmo.postssystem.features.roles.server.RolesChecker
|
||||
import dev.inmo.postssystem.features.users.common.User
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
|
||||
object RolesManagerRolesChecker : RolesChecker<Role> {
|
||||
override val key: String
|
||||
|
@ -2,7 +2,7 @@ package dev.inmo.postssystem.features.roles.server
|
||||
|
||||
import dev.inmo.postssystem.features.roles.common.*
|
||||
import dev.inmo.postssystem.features.users.common.User
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
|
||||
interface RolesChecker<T : Role> {
|
||||
val key: String
|
||||
|
@ -3,9 +3,8 @@ package dev.inmo.postssystem.features.roles.server
|
||||
import dev.inmo.postssystem.features.roles.common.*
|
||||
import dev.inmo.micro_utils.ktor.server.*
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
|
||||
|
@ -6,18 +6,16 @@ import dev.inmo.postssystem.features.auth.server.tokens.AuthTokensService
|
||||
import dev.inmo.postssystem.features.common.server.sessions.ApplicationAuthenticationConfigurator
|
||||
import dev.inmo.postssystem.features.roles.common.Role
|
||||
import dev.inmo.postssystem.features.roles.common.RolesStorage
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.Authentication
|
||||
import io.ktor.auth.session
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.response.respond
|
||||
|
||||
class RolesAuthenticationConfigurator<T : Role>(
|
||||
private val usersRolesStorage: RolesStorage<T>,
|
||||
private val authTokensService: AuthTokensService,
|
||||
private val rolesCheckers: List<RolesChecker<T>>
|
||||
) : ApplicationAuthenticationConfigurator.Element {
|
||||
override fun Authentication.Configuration.invoke() {
|
||||
override fun AuthenticationConfig.invoke() {
|
||||
rolesCheckers.forEach { checker ->
|
||||
session<AuthToken>(checker.key) {
|
||||
validate {
|
||||
|
@ -3,8 +3,8 @@ package dev.inmo.postssystem.features.roles.server
|
||||
import dev.inmo.postssystem.features.roles.common.*
|
||||
import dev.inmo.micro_utils.ktor.server.*
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
|
@ -3,11 +3,11 @@ package dev.inmo.postssystem.features.status.server
|
||||
import dev.inmo.postssystem.features.status.common.statusAuthorisedPathPart
|
||||
import dev.inmo.postssystem.features.status.common.statusRootPart
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
object StatusRoutingConfigurator : ApplicationRoutingConfigurator.Element {
|
||||
override fun Route.invoke() {
|
||||
|
@ -15,7 +15,7 @@ kotlin {
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
api libs.ktor.auth
|
||||
api libs.ktor.server.auth
|
||||
api libs.ktor.server.sessions
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ import dev.inmo.micro_utils.ktor.server.UnifiedRouter
|
||||
import dev.inmo.postssystem.features.users.common.*
|
||||
import dev.inmo.micro_utils.ktor.server.configurators.ApplicationRoutingConfigurator
|
||||
import dev.inmo.micro_utils.repos.ktor.server.crud.configureReadStandardCrudRepoRoutes
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.route
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
import kotlinx.serialization.builtins.nullable
|
||||
|
||||
class UsersStorageServerRoutesConfigurator(
|
||||
|
@ -53,7 +53,7 @@ ktor-client-apache = { module = "io.ktor:ktor-client-apache", version.ref = "kto
|
||||
ktor-client-auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" }
|
||||
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
|
||||
ktor-http = { module = "io.ktor:ktor-http", version.ref = "ktor" }
|
||||
ktor-auth = { module = "io.ktor:ktor-auth", version.ref = "ktor" }
|
||||
ktor-server-auth = { module = "io.ktor:ktor-server-auth", version.ref = "ktor" }
|
||||
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
|
||||
ktor-server-sessions = { module = "io.ktor:ktor-server-sessions", version.ref = "ktor" }
|
||||
ktor-websockets = { module = "io.ktor:ktor-websockets", version.ref = "ktor" }
|
||||
|
@ -26,7 +26,6 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
println(this::class)
|
||||
allTargetsConfiguration()
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,8 @@ import dev.inmo.postssystem.features.publication.common.publicatorsRootPath
|
||||
import dev.inmo.postssystem.features.publication.server.PublicationManager
|
||||
import dev.inmo.postssystem.publicators.simple.common.simplePublicatorPublishPathPart
|
||||
import dev.inmo.postssystem.publicators.simple.common.simplePublicatorRootPath
|
||||
import io.ktor.application.call
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
|
||||
class SimplePublicatorRoutingConfigurator(
|
||||
|
@ -2,6 +2,7 @@ package dev.inmo.postssystem.services.posts.client
|
||||
|
||||
import dev.inmo.micro_utils.common.*
|
||||
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
||||
import dev.inmo.micro_utils.ktor.client.tempUpload
|
||||
import dev.inmo.micro_utils.ktor.common.buildStandardUrl
|
||||
import dev.inmo.micro_utils.ktor.common.encodeHex
|
||||
import dev.inmo.micro_utils.repos.ktor.common.crud.createRouting
|
||||
@ -10,6 +11,7 @@ import dev.inmo.micro_utils.repos.ktor.common.one_to_many.removeRoute
|
||||
import dev.inmo.postssystem.features.common.common.FileBasedInputProvider
|
||||
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
|
||||
import dev.inmo.postssystem.features.content.common.*
|
||||
import dev.inmo.postssystem.features.files.common.FileId
|
||||
import dev.inmo.postssystem.features.posts.common.PostId
|
||||
import dev.inmo.postssystem.features.posts.common.RegisteredPost
|
||||
import dev.inmo.postssystem.services.posts.common.*
|
||||
@ -61,7 +63,7 @@ class ClientWritePostsService(
|
||||
provider.file
|
||||
)
|
||||
println(fileId)
|
||||
it.copy(inputProvider = TempFileIdentifierInputProvider(fileId))
|
||||
it.copy(inputProvider = TempFileIdentifierInputProvider(FileId(fileId.string)))
|
||||
}
|
||||
is TempFileIdentifierInputProvider -> it
|
||||
else -> return@prepareContent null
|
||||
|
@ -1,12 +0,0 @@
|
||||
package dev.inmo.postssystem.services.posts.client
|
||||
|
||||
import dev.inmo.micro_utils.common.MPPFile
|
||||
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
||||
import dev.inmo.postssystem.features.files.common.FileId
|
||||
|
||||
internal expect suspend fun UnifiedRequester.tempUpload(
|
||||
fullTempUploadDraftPath: String,
|
||||
file: MPPFile,
|
||||
onUpload: (Long, Long) -> Unit = { _, _ -> }
|
||||
): FileId
|
||||
|
@ -1,49 +0,0 @@
|
||||
package dev.inmo.postssystem.services.posts.client
|
||||
|
||||
import dev.inmo.micro_utils.common.MPPFile
|
||||
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
||||
import dev.inmo.postssystem.features.files.common.FileId
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.GlobalScope.coroutineContext
|
||||
import org.w3c.xhr.*
|
||||
|
||||
internal actual suspend fun UnifiedRequester.tempUpload(
|
||||
fullTempUploadDraftPath: String,
|
||||
file: MPPFile,
|
||||
onUpload: (Long, Long) -> Unit
|
||||
): FileId {
|
||||
val formData = FormData()
|
||||
val answer = CompletableDeferred<FileId>()
|
||||
|
||||
formData.append(
|
||||
"data",
|
||||
file
|
||||
)
|
||||
|
||||
val request = XMLHttpRequest()
|
||||
request.responseType = XMLHttpRequestResponseType.TEXT
|
||||
request.upload.onprogress = {
|
||||
onUpload(it.loaded.toLong(), it.total.toLong())
|
||||
}
|
||||
request.onload = {
|
||||
if (request.status == 200.toShort()) {
|
||||
answer.complete(FileId(request.responseText))
|
||||
} else {
|
||||
answer.completeExceptionally(Exception("Something went wrong"))
|
||||
}
|
||||
}
|
||||
request.onerror = {
|
||||
answer.completeExceptionally(Exception("Something went wrong"))
|
||||
}
|
||||
request.open("POST", fullTempUploadDraftPath, true)
|
||||
request.send(formData)
|
||||
|
||||
currentCoroutineContext().job.invokeOnCompletion {
|
||||
runCatching {
|
||||
request.abort()
|
||||
}
|
||||
}
|
||||
|
||||
return answer.await()
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
package dev.inmo.postssystem.services.posts.client
|
||||
|
||||
import dev.inmo.micro_utils.common.MPPFile
|
||||
import dev.inmo.micro_utils.common.filename
|
||||
import dev.inmo.micro_utils.ktor.client.UnifiedRequester
|
||||
import dev.inmo.micro_utils.ktor.client.inputProvider
|
||||
import dev.inmo.postssystem.features.common.common.mimeType
|
||||
import dev.inmo.postssystem.features.files.common.FileId
|
||||
import io.ktor.client.features.onUpload
|
||||
import io.ktor.client.request.forms.formData
|
||||
import io.ktor.client.request.forms.submitFormWithBinaryData
|
||||
import io.ktor.http.Headers
|
||||
import io.ktor.http.HttpHeaders
|
||||
|
||||
internal actual suspend fun UnifiedRequester.tempUpload(
|
||||
fullTempUploadDraftPath: String,
|
||||
file: MPPFile,
|
||||
onUpload: (Long, Long) -> Unit
|
||||
): FileId {
|
||||
val inputProvider = file.inputProvider()
|
||||
val fileId = client.submitFormWithBinaryData<String>(
|
||||
fullTempUploadDraftPath,
|
||||
formData = formData {
|
||||
append(
|
||||
"data",
|
||||
inputProvider,
|
||||
Headers.build {
|
||||
append(HttpHeaders.ContentType, file.mimeType.raw)
|
||||
append(HttpHeaders.ContentDisposition, "filename=\"${file.filename.string}\"")
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
onUpload(onUpload)
|
||||
}
|
||||
return FileId(fileId)
|
||||
}
|
||||
|
@ -16,16 +16,15 @@ import dev.inmo.postssystem.features.content.common.*
|
||||
import dev.inmo.postssystem.features.files.common.FileId
|
||||
import dev.inmo.postssystem.features.posts.common.*
|
||||
import dev.inmo.postssystem.services.posts.common.*
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.content.PartData
|
||||
import io.ktor.http.content.streamProvider
|
||||
import io.ktor.request.isMultipart
|
||||
import io.ktor.request.receiveMultipart
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.*
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.request.receiveMultipart
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.util.asStream
|
||||
import io.ktor.util.pipeline.PipelineContext
|
||||
import io.ktor.utils.io.core.use
|
||||
|
@ -7,12 +7,11 @@ import dev.inmo.postssystem.features.publication.server.PublicationPost
|
||||
import dev.inmo.postssystem.features.publication.server.PublicationTarget
|
||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||
import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe
|
||||
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
|
||||
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
||||
import dev.inmo.tgbotapi.requests.send.SendTextMessage
|
||||
import dev.inmo.tgbotapi.requests.send.media.*
|
||||
import dev.inmo.tgbotapi.types.ChatId
|
||||
import dev.inmo.tgbotapi.utils.StorageFile
|
||||
import dev.inmo.tgbotapi.utils.StorageFileInfo
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.core.readBytes
|
||||
import kotlinx.coroutines.delay
|
||||
@ -26,9 +25,9 @@ class PublicationTargetTelegram(
|
||||
when (val content = it.content) {
|
||||
is BinaryContent -> {
|
||||
val storageFile by lazy {
|
||||
StorageFile(StorageFileInfo(content.filename.name)) {
|
||||
MultipartFile(content.filename.name) {
|
||||
content.inputProvider()
|
||||
}.asMultipartFile()
|
||||
}
|
||||
}
|
||||
when (content.mimeType) {
|
||||
is KnownMimeTypes.Image.Jpeg,
|
||||
|
Loading…
Reference in New Issue
Block a user