Skip to content

Commit

Permalink
Added a new route for setting icon avatars; made uploadImageAvatar re…
Browse files Browse the repository at this point in the history
…turn a User
  • Loading branch information
Mnemotechnician committed Nov 26, 2023
1 parent e3be245 commit b361470
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ open class MinchatLauncher : Runnable {

transaction {
SchemaUtils.create(Channels, Messages, Users)
SchemaUtils.createMissingTablesAndColumns(Channels, Messages, Users)
SchemaUtils.createMissingTablesAndColumns(Channels, Messages, Users, ChannelGroups)
}

Log.lifecycle { "Loading the SSL key store." }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,46 @@ class UserModule : AbstractMinchatServerModule() {
call.respondFile(targetFile)
}

post(Route.User.setIconAvatar) {
val userId = call.parameters.getOrFail<Long>("id")
val data = call.receive<IconAvatarSetRequest>()

if (data.iconName.let { it != null && it.length !in 3..128 }) {
illegalInput("Malformed icon name.")
}

lateinit var invokingUser: User
lateinit var targetUser: User
newSuspendedTransaction {
invokingUser = Users.getByToken(call.token())
invokingUser.checkAndUpdateUserPunishments(checkMute = false)

if (invokingUser.id == userId) {
targetUser = invokingUser
} else {
targetUser = Users.getById(userId)
if (!invokingUser.canEditUser(targetUser)) {
accessDenied("You are not allowed to edit this user.")
}
}

Users.update({ Users.id eq targetUser.id }) {
it[avatarIconName] = data.iconName
it[avatarType] = data.iconName?.let { User.Avatar.Type.ICON } // null for null icons
it[avatarHash] = null
it[avatarWidth] = 0
it[avatarHeight] = 0
}

val updatedUser = Users.getById(targetUser.id)
call.respond(updatedUser)
server.sendEvent(UserModifyEvent(updatedUser))

Log.info { "${targetUser.loggable()}'s icon avatar was set to ${data.iconName} by ${invokingUser.loggable()}" }
}

}

post(Route.User.uploadImageAvatar) {
val userId = call.parameters.getOrFail<Long>("id")
val contentLength = call.request.contentLength()
Expand Down Expand Up @@ -231,10 +271,11 @@ class UserModule : AbstractMinchatServerModule() {
}
}

Log.info { "Avatar update performed for user ${targetUser.loggable()} by ${invokingUser.loggable()}." }
Log.info { "Avatar update was performed for user ${targetUser.loggable()} by ${invokingUser.loggable()}." }

call.response.statusOk()
server.sendEvent(UserModifyEvent(Users.getById(userId)))
val updatedUser = Users.getById(userId)
call.respond(updatedUser)
server.sendEvent(UserModifyEvent(updatedUser))
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions minchat-common/src/main/kotlin/io/minchat/common/Route.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.minchat.common

import io.minchat.common.event.Event
import io.minchat.common.request.*
import io.minchat.common.entity.*

/**
* All offical MinChat server routes.
Expand Down Expand Up @@ -69,6 +70,12 @@ object Route {
* Response: a raw file containing the avatar of the user.
*/
val getImageAvatar = to("image-avatar")
/**
* POST. Requires authorization.
* Body: [IconAvatarSetRequest].
* Response: an updated [User] object.
*/
val setIconAvatar = to("set-icon-avatar")
/**
* POST. Requires authorization.
* Body: a raw file containing the avatar of the user.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ class UserPunishmentsModifyRequest(
val newMute: User.Punishment?,
val newBan: User.Punishment?
)

/** Request to set the avatar of a user to the given icon, or reset to default. */
@Serializable
class IconAvatarSetRequest(val iconName: String?)
Original file line number Diff line number Diff line change
Expand Up @@ -434,12 +434,20 @@ class MinchatRestClient(
return rootService.validateToken(account!!.user.username, account!!.token)
}

/** Makes the avatar of a user an icon avatar. Requires a logged-in account. */
suspend fun setIconAvatar(id: Long, iconName: String?) =
userService.setIconAvatar(id, account().token, iconName)
.also(cache::set)
.withClient(this)

/**
* Uploads an image avatar to the server.
* Requires a logged-in account.
*/
suspend fun uploadImageAvatar(id: Long, image: ByteReadChannel, progressHandler: (Float) -> Unit) =
userService.uploadImageAvatar(id, account().token, image, progressHandler)
.also(cache::set)
.withClient(this)

// Admin-only
/** Modifies the punishments of the user with the specified id. Returns the updated user. Requires admin rights. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,16 @@ data class MinchatUser(
progressHandler: (Float) -> Unit = {}
): File? = avatar?.let { rest.getCacheableAvatar(id, it, full, progressHandler) }

/** Sets the avatar of this user to the specified icon, or resets it if the icon is null. */
suspend fun setIconAvatar(icon: String?) =
rest.setIconAvatar(id, icon)

/** Uploads the provided image avatar to the server. */
suspend fun uploadAvatar(image: ByteReadChannel, progressHandler: (Float) -> Unit = {}) =
rest.uploadImageAvatar(id, image, progressHandler)

/** Uploads the provided image avatar to the server. */
suspend fun uploadAvatar(image: ByteArray, progressHandler: (Float) -> Unit = {}): Unit =
suspend fun uploadAvatar(image: ByteArray, progressHandler: (Float) -> Unit = {}): MinchatUser =
uploadAvatar(ByteReadChannel(image), progressHandler)

/** Fetches all DM channels associated with this user. */
Expand Down Expand Up @@ -138,6 +142,7 @@ data class MinchatUser(
username: String = data.username,
nickname: String? = data.nickname,
discriminator: Int = data.discriminator,
avatar: User.Avatar? = data.avatar,
role: User.RoleBitSet = data.role,
mute: User.Punishment? = data.mute,
ban: User.Punishment? = data.ban,
Expand All @@ -148,6 +153,7 @@ data class MinchatUser(
username = username,
nickname = nickname,
discriminator = discriminator,
avatar = avatar,
role = role,
mute = mute,
ban = ban,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,32 @@ class UserService(baseUrl: String, client: HttpClient) : AbstractRestService(bas
}
}.body<ByteArray>()

suspend fun setIconAvatar(
id: Long,
token: String,
iconName: String?
): User = run {
client.post(makeRouteUrl(Route.User.setIconAvatar, id)) {
contentType(ContentType.Application.Json)
authorizeBearer(token)
setBody(IconAvatarSetRequest(iconName))
}.body<User>()
}

suspend fun uploadImageAvatar(
id: Long,
token: String,
image: ByteReadChannel,
progressHandler: (Float) -> Unit
) {
): User = run {
client.post(makeRouteUrl(Route.User.uploadImageAvatar, id)) {
contentType(ContentType.Image.Any)
authorizeBearer(token)
setBody(image)
onUpload { bytesSentTotal, contentLength ->
progressHandler.invoke(bytesSentTotal.toFloat() / contentLength)
}
}
}.body<User>()
}

/** Modifies the punishments of the user with the specified id. Returns the updated user. Admin-only. */
Expand Down

0 comments on commit b361470

Please sign in to comment.