From 6b535a042aa645badf86f4a21079f1f30792adf4 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 12 Dec 2023 00:03:48 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A9=E3=83=AD=E3=83=BC?= =?UTF-8?q?=E6=89=BF=E8=AA=8D=E5=88=B6=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?= =?UTF-8?q?=E3=81=A8=E3=81=99=E3=82=8B=E3=81=8B=E3=81=AE=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=82=92Mastodon=20API=E3=81=8B=E3=82=89=E8=A1=8C=E3=81=88?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/service/user/UpdateUserDto.kt | 12 +++ .../hideout/core/service/user/UserService.kt | 2 + .../core/service/user/UserServiceImpl.kt | 21 +++++ .../account/MastodonAccountApiController.kt | 10 +++ .../service/account/AccountApiService.kt | 56 ++++++++++++- src/main/resources/openapi/mastodon.yaml | 80 ++++++++++++++++++- 6 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/dev/usbharu/hideout/core/service/user/UpdateUserDto.kt diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UpdateUserDto.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UpdateUserDto.kt new file mode 100644 index 00000000..5406fd02 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UpdateUserDto.kt @@ -0,0 +1,12 @@ +package dev.usbharu.hideout.core.service.user + +import dev.usbharu.hideout.core.domain.model.media.Media + +data class UpdateUserDto( + val screenName: String, + val description: String, + val avatarMedia: Media?, + val headerMedia: Media?, + val autoAcceptFollowRequest: Boolean, + val autoAcceptFolloweeFollowRequest: Boolean +) diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt index 6ef40b6e..06715df5 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserService.kt @@ -11,4 +11,6 @@ interface UserService { suspend fun createLocalUser(user: UserCreateDto): Actor suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor + + suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto) } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt index 49ff8091..f126fbf7 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/service/user/UserServiceImpl.kt @@ -94,6 +94,27 @@ class UserServiceImpl( } } + override suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto) { + val userDetail = userDetailRepository.findByActorId(userId) + ?: throw IllegalArgumentException("userId: $userId was not found.") + + val actor = actorRepository.findById(userId) ?: throw IllegalArgumentException("userId $userId was not found.") + + actorRepository.save( + actor.copy( + screenName = updateUserDto.screenName, + description = updateUserDto.description, + ) + ) + + userDetailRepository.save( + userDetail.copy( + autoAcceptFollowRequest = updateUserDto.autoAcceptFollowRequest, + autoAcceptFolloweeFollowRequest = updateUserDto.autoAcceptFolloweeFollowRequest + ) + ) + } + companion object { private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java) } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt index f9af1c7d..b516c3ec 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/interfaces/api/account/MastodonAccountApiController.kt @@ -143,4 +143,14 @@ class MastodonAccountApiController( return ResponseEntity.ok(removeFromFollowers) } + + override suspend fun apiV1AccountsUpdateCredentialsPatch(updateCredentials: UpdateCredentials?): ResponseEntity { + val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt + + val userid = principal.getClaim("uid").toLong() + + val removeFromFollowers = accountApiService.updateProfile(userid, updateCredentials) + + return ResponseEntity.ok(removeFromFollowers) + } } diff --git a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt index 7a19b065..b85b7d67 100644 --- a/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/mastodon/service/account/AccountApiService.kt @@ -2,10 +2,13 @@ package dev.usbharu.hideout.mastodon.service.account import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository +import dev.usbharu.hideout.core.service.media.MediaService import dev.usbharu.hideout.core.service.relationship.RelationshipService +import dev.usbharu.hideout.core.service.user.UpdateUserDto import dev.usbharu.hideout.core.service.user.UserCreateDto import dev.usbharu.hideout.core.service.user.UserService import dev.usbharu.hideout.domain.mastodon.model.generated.* +import dev.usbharu.hideout.mastodon.interfaces.api.media.MediaRequest import dev.usbharu.hideout.mastodon.query.StatusQueryService import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -45,6 +48,7 @@ interface AccountApiService { suspend fun unblock(userid: Long, target: Long): Relationship suspend fun unfollow(userid: Long, target: Long): Relationship suspend fun removeFromFollowers(userid: Long, target: Long): Relationship + suspend fun updateProfile(userid: Long, updateCredentials: UpdateCredentials?): Account } @Service @@ -54,7 +58,8 @@ class AccountApiServiceImpl( private val userService: UserService, private val statusQueryService: StatusQueryService, private val relationshipService: RelationshipService, - private val relationshipRepository: RelationshipRepository + private val relationshipRepository: RelationshipRepository, + private val mediaService: MediaService ) : AccountApiService { override suspend fun accountsStatuses( @@ -153,6 +158,51 @@ class AccountApiServiceImpl( return@transaction fetchRelationship(userid, target) } + override suspend fun updateProfile(userid: Long, updateCredentials: UpdateCredentials?): Account = + transaction.transaction { + + val avatarMedia = if (updateCredentials?.avatar != null) { + mediaService.uploadLocalMedia( + MediaRequest( + updateCredentials.avatar, + null, + null, + null + ) + ) + } else { + null + } + + val headerMedia = if (updateCredentials?.header != null) { + mediaService.uploadLocalMedia( + MediaRequest( + updateCredentials.header, + null, + null, + null + ) + ) + } else { + null + } + + + val account = accountService.findById(userid) + + val updateUserDto = UpdateUserDto( + screenName = updateCredentials?.displayName ?: account.displayName, + description = updateCredentials?.note ?: account.note, + avatarMedia = avatarMedia, + headerMedia = headerMedia, + autoAcceptFollowRequest = updateCredentials?.locked ?: account.locked, + autoAcceptFolloweeFollowRequest = false + ) + userService.updateUser(userid, updateUserDto) + + accountService.findById(userid) + } + private fun from(account: Account): CredentialAccount { return CredentialAccount( id = account.id, @@ -180,10 +230,10 @@ class AccountApiServiceImpl( suspendex = account.suspendex, limited = account.limited, followingCount = account.followingCount, - source = CredentialAccountSource( + source = AccountSource( account.note, account.fields, - CredentialAccountSource.Privacy.public, + AccountSource.Privacy.public, false, 0 ), diff --git a/src/main/resources/openapi/mastodon.yaml b/src/main/resources/openapi/mastodon.yaml index a43da7de..49e382f6 100644 --- a/src/main/resources/openapi/mastodon.yaml +++ b/src/main/resources/openapi/mastodon.yaml @@ -237,6 +237,27 @@ paths: items: $ref: "#/components/schemas/Relationship" + /api/v1/accounts/update_credentials: + patch: + tags: + - account + security: + - OAuth2: + - "write:accounts" + requestBody: + required: false + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateCredentials" + responses: + 200: + description: 成功 + content: + application/json: + schema: + $ref: "#/components/schemas/Account" + /api/v1/accounts/{id}: get: tags: @@ -664,6 +685,9 @@ components: type: integer following_count: type: integer + source: + $ref: "#/components/schemas/AccountSource" + required: - id - username @@ -747,7 +771,7 @@ components: following_count: type: integer source: - $ref: "#/components/schemas/CredentialAccountSource" + $ref: "#/components/schemas/AccountSource" role: $ref: "#/components/schemas/Role" required: @@ -774,7 +798,7 @@ components: - followers_count - source - CredentialAccountSource: + AccountSource: type: object properties: note: @@ -1632,6 +1656,58 @@ components: items: type: string + UpdateCredentials: + type: object + properties: + display_name: + type: string + note: + type: string + avatar: + type: string + format: binary + header: + type: string + format: binary + locked: + type: boolean + bot: + type: boolean + discoverable: + type: boolean + hide_collections: + type: boolean + indexable: + type: boolean + fields_attributes: + type: object + additionalProperties: + $ref: "#/components/schemas/UpdateCredentialsFieldsAttributes" + source: + $ref: "#/components/schemas/UpdateCredentialsSource" + + UpdateCredentialsSource: + type: object + properties: + privacy: + type: string + enum: + - public + - unlisted + - private + sensitive: + type: boolean + language: + type: string + + UpdateCredentialsFieldsAttributes: + type: object + properties: + name: + type: string + value: + type: string + securitySchemes: OAuth2: type: oauth2