feat: フォロー承認制ユーザーとするかの設定をMastodon APIから行えるように

This commit is contained in:
usbharu 2023-12-12 00:03:48 +09:00
parent 1eaac1247a
commit 6b535a042a
6 changed files with 176 additions and 5 deletions

View File

@ -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
)

View File

@ -11,4 +11,6 @@ interface UserService {
suspend fun createLocalUser(user: UserCreateDto): Actor suspend fun createLocalUser(user: UserCreateDto): Actor
suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor suspend fun createRemoteUser(user: RemoteUserCreateDto): Actor
suspend fun updateUser(userId: Long, updateUserDto: UpdateUserDto)
} }

View File

@ -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 { companion object {
private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java) private val logger = LoggerFactory.getLogger(UserServiceImpl::class.java)
} }

View File

@ -143,4 +143,14 @@ class MastodonAccountApiController(
return ResponseEntity.ok(removeFromFollowers) return ResponseEntity.ok(removeFromFollowers)
} }
override suspend fun apiV1AccountsUpdateCredentialsPatch(updateCredentials: UpdateCredentials?): ResponseEntity<Account> {
val principal = SecurityContextHolder.getContext().getAuthentication().principal as Jwt
val userid = principal.getClaim<String>("uid").toLong()
val removeFromFollowers = accountApiService.updateProfile(userid, updateCredentials)
return ResponseEntity.ok(removeFromFollowers)
}
} }

View File

@ -2,10 +2,13 @@ package dev.usbharu.hideout.mastodon.service.account
import dev.usbharu.hideout.application.external.Transaction import dev.usbharu.hideout.application.external.Transaction
import dev.usbharu.hideout.core.domain.model.relationship.RelationshipRepository 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.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.UserCreateDto
import dev.usbharu.hideout.core.service.user.UserService import dev.usbharu.hideout.core.service.user.UserService
import dev.usbharu.hideout.domain.mastodon.model.generated.* 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 dev.usbharu.hideout.mastodon.query.StatusQueryService
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@ -45,6 +48,7 @@ interface AccountApiService {
suspend fun unblock(userid: Long, target: Long): Relationship suspend fun unblock(userid: Long, target: Long): Relationship
suspend fun unfollow(userid: Long, target: Long): Relationship suspend fun unfollow(userid: Long, target: Long): Relationship
suspend fun removeFromFollowers(userid: Long, target: Long): Relationship suspend fun removeFromFollowers(userid: Long, target: Long): Relationship
suspend fun updateProfile(userid: Long, updateCredentials: UpdateCredentials?): Account
} }
@Service @Service
@ -54,7 +58,8 @@ class AccountApiServiceImpl(
private val userService: UserService, private val userService: UserService,
private val statusQueryService: StatusQueryService, private val statusQueryService: StatusQueryService,
private val relationshipService: RelationshipService, private val relationshipService: RelationshipService,
private val relationshipRepository: RelationshipRepository private val relationshipRepository: RelationshipRepository,
private val mediaService: MediaService
) : ) :
AccountApiService { AccountApiService {
override suspend fun accountsStatuses( override suspend fun accountsStatuses(
@ -153,6 +158,51 @@ class AccountApiServiceImpl(
return@transaction fetchRelationship(userid, target) 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 { private fun from(account: Account): CredentialAccount {
return CredentialAccount( return CredentialAccount(
id = account.id, id = account.id,
@ -180,10 +230,10 @@ class AccountApiServiceImpl(
suspendex = account.suspendex, suspendex = account.suspendex,
limited = account.limited, limited = account.limited,
followingCount = account.followingCount, followingCount = account.followingCount,
source = CredentialAccountSource( source = AccountSource(
account.note, account.note,
account.fields, account.fields,
CredentialAccountSource.Privacy.public, AccountSource.Privacy.public,
false, false,
0 0
), ),

View File

@ -237,6 +237,27 @@ paths:
items: items:
$ref: "#/components/schemas/Relationship" $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}: /api/v1/accounts/{id}:
get: get:
tags: tags:
@ -664,6 +685,9 @@ components:
type: integer type: integer
following_count: following_count:
type: integer type: integer
source:
$ref: "#/components/schemas/AccountSource"
required: required:
- id - id
- username - username
@ -747,7 +771,7 @@ components:
following_count: following_count:
type: integer type: integer
source: source:
$ref: "#/components/schemas/CredentialAccountSource" $ref: "#/components/schemas/AccountSource"
role: role:
$ref: "#/components/schemas/Role" $ref: "#/components/schemas/Role"
required: required:
@ -774,7 +798,7 @@ components:
- followers_count - followers_count
- source - source
CredentialAccountSource: AccountSource:
type: object type: object
properties: properties:
note: note:
@ -1632,6 +1656,58 @@ components:
items: items:
type: string 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: securitySchemes:
OAuth2: OAuth2:
type: oauth2 type: oauth2