mirror of https://github.com/usbharu/Hideout.git
feat: ログインできるように
This commit is contained in:
parent
e14a4aa89a
commit
4d68b150dd
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
data class GetUserDetail(val id: Long)
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
|
||||
import dev.usbharu.hideout.core.application.shared.CommandExecutor
|
||||
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmojiRepository
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetailRepository
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class GetUserDetailApplicationService(
|
||||
private val actorRepository: ActorRepository,
|
||||
private val userDetailRepository: UserDetailRepository,
|
||||
private val customEmojiRepository: CustomEmojiRepository,
|
||||
transaction: Transaction,
|
||||
) :
|
||||
AbstractApplicationService<GetUserDetail, UserDetail>(transaction, Companion.logger) {
|
||||
companion object {
|
||||
val logger = LoggerFactory.getLogger(GetUserDetailApplicationService::class.java)
|
||||
}
|
||||
|
||||
override suspend fun internalExecute(command: GetUserDetail, executor: CommandExecutor): UserDetail {
|
||||
val userDetail = userDetailRepository.findById(command.id)
|
||||
?: throw IllegalArgumentException("actor does not exist")
|
||||
val actor = actorRepository.findById(userDetail.actorId)!!
|
||||
|
||||
val emojis = customEmojiRepository.findByIds(actor.emojis.map { it.emojiId })
|
||||
|
||||
return UserDetail.of(actor, userDetail, emojis)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.application.actor
|
||||
|
||||
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
||||
import dev.usbharu.hideout.core.domain.model.userdetails.UserDetail
|
||||
import java.time.Instant
|
||||
|
||||
data class UserDetail(
|
||||
val id: Long,
|
||||
val userDetailId: Long,
|
||||
val name: String,
|
||||
val domain: String,
|
||||
val screenName: String,
|
||||
val url: String,
|
||||
val iconUrl: String,
|
||||
val description: String,
|
||||
val locked: Boolean,
|
||||
val emojis: List<CustomEmoji>,
|
||||
val createdAt: Instant,
|
||||
val lastPostAt: Instant?,
|
||||
val postsCount: Int,
|
||||
val followingCount: Int?,
|
||||
val followersCount: Int?,
|
||||
val moveTo: Long?,
|
||||
val suspend: Boolean,
|
||||
) {
|
||||
companion object {
|
||||
fun of(
|
||||
actor: Actor,
|
||||
userDetail: UserDetail,
|
||||
customEmojis: List<CustomEmoji>,
|
||||
): dev.usbharu.hideout.core.application.actor.UserDetail {
|
||||
return UserDetail(
|
||||
actor.id.id,
|
||||
userDetail.id.id,
|
||||
actor.name.name,
|
||||
actor.domain.domain,
|
||||
actor.screenName.screenName,
|
||||
actor.url.toString(),
|
||||
actor.url.toString(),
|
||||
actor.description.description,
|
||||
actor.locked,
|
||||
customEmojis,
|
||||
actor.createdAt,
|
||||
actor.lastPostAt,
|
||||
actor.postsCount.postsCount,
|
||||
actor.followingCount?.relationshipCount,
|
||||
actor.followersCount?.relationshipCount,
|
||||
actor.moveTo?.id,
|
||||
actor.suspend
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,4 +21,5 @@ interface CustomEmojiRepository {
|
|||
suspend fun findById(id: Long): CustomEmoji?
|
||||
suspend fun delete(customEmoji: CustomEmoji)
|
||||
suspend fun findByNamesAndDomain(names: List<String>, domain: String): List<CustomEmoji>
|
||||
suspend fun findByIds(ids: List<Long>): List<CustomEmoji>
|
||||
}
|
||||
|
|
|
@ -20,4 +20,5 @@ interface UserDetailRepository {
|
|||
suspend fun save(userDetail: UserDetail): UserDetail
|
||||
suspend fun delete(userDetail: UserDetail)
|
||||
suspend fun findByActorId(actorId: Long): UserDetail?
|
||||
suspend fun findById(id: Long): UserDetail?
|
||||
}
|
||||
|
|
|
@ -70,8 +70,8 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository,
|
|||
CustomEmojis.deleteWhere { id eq customEmoji.id.emojiId }
|
||||
}
|
||||
|
||||
override suspend fun findByNamesAndDomain(names: List<String>, domain: String): List<CustomEmoji> {
|
||||
return CustomEmojis
|
||||
override suspend fun findByNamesAndDomain(names: List<String>, domain: String): List<CustomEmoji> = query {
|
||||
return@query CustomEmojis
|
||||
.selectAll()
|
||||
.where {
|
||||
CustomEmojis.name inList names and (CustomEmojis.domain eq domain)
|
||||
|
@ -79,6 +79,15 @@ class CustomEmojiRepositoryImpl : CustomEmojiRepository,
|
|||
.map { it.toCustomEmoji() }
|
||||
}
|
||||
|
||||
override suspend fun findByIds(ids: List<Long>): List<CustomEmoji> = query {
|
||||
return@query CustomEmojis
|
||||
.selectAll()
|
||||
.where {
|
||||
CustomEmojis.id inList ids
|
||||
}
|
||||
.map { it.toCustomEmoji() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(CustomEmojiRepositoryImpl::class.java)
|
||||
}
|
||||
|
|
|
@ -74,6 +74,21 @@ class UserDetailRepositoryImpl : UserDetailRepository, AbstractRepository() {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Long): UserDetail? = query {
|
||||
UserDetails
|
||||
.selectAll().where { UserDetails.id eq id }
|
||||
.singleOrNull()
|
||||
?.let {
|
||||
UserDetail.create(
|
||||
UserDetailId(it[UserDetails.id]),
|
||||
ActorId(it[UserDetails.actorId]),
|
||||
UserDetailHashedPassword(it[UserDetails.password]),
|
||||
it[UserDetails.autoAcceptFolloweeFollowRequest],
|
||||
it[UserDetails.lastMigration]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = LoggerFactory.getLogger(UserDetailRepositoryImpl::class.java)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
||||
|
||||
import dev.usbharu.hideout.core.application.shared.CommandExecutor
|
||||
|
||||
class Oauth2CommandExecutor(override val executor: String, val userDetailId: Long) : CommandExecutor
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2024 usbharu
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.usbharu.hideout.core.infrastructure.springframework.oauth2
|
||||
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.oauth2.jwt.Jwt
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Component
|
||||
class Oauth2CommandExecutorFactory {
|
||||
fun getCommandExecutor(): Oauth2CommandExecutor {
|
||||
val principal = SecurityContextHolder.getContext().authentication.principal as Jwt
|
||||
|
||||
return Oauth2CommandExecutor(
|
||||
principal.subject,
|
||||
principal.getClaim<String>("uid").toLong()
|
||||
)
|
||||
|
||||
}
|
||||
}
|
|
@ -43,6 +43,8 @@ class JsonOrFormModelMethodProcessor(
|
|||
webRequest: NativeWebRequest,
|
||||
binderFactory: WebDataBinderFactory?,
|
||||
): Any? {
|
||||
|
||||
|
||||
val contentType = webRequest.getHeader("Content-Type").orEmpty()
|
||||
logger.trace("ContentType is {}", contentType)
|
||||
if (contentType.contains(isJsonRegex)) {
|
||||
|
|
|
@ -16,14 +16,19 @@
|
|||
|
||||
package dev.usbharu.hideout.mastodon.interfaces.api
|
||||
|
||||
import dev.usbharu.hideout.core.application.actor.GetUserDetail
|
||||
import dev.usbharu.hideout.core.application.actor.GetUserDetailApplicationService
|
||||
import dev.usbharu.hideout.core.infrastructure.springframework.oauth2.Oauth2CommandExecutorFactory
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.AccountApi
|
||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.stereotype.Controller
|
||||
|
||||
@Controller
|
||||
class SpringAccountApi : AccountApi {
|
||||
class SpringAccountApi(
|
||||
private val oauth2CommandExecutorFactory: Oauth2CommandExecutorFactory,
|
||||
private val getUserDetailApplicationService: GetUserDetailApplicationService,
|
||||
) : AccountApi {
|
||||
override suspend fun apiV1AccountsIdBlockPost(id: String): ResponseEntity<Relationship> {
|
||||
return super.apiV1AccountsIdBlockPost(id)
|
||||
}
|
||||
|
@ -68,7 +73,55 @@ class SpringAccountApi : AccountApi {
|
|||
}
|
||||
|
||||
override suspend fun apiV1AccountsVerifyCredentialsGet(): ResponseEntity<CredentialAccount> {
|
||||
return super.apiV1AccountsVerifyCredentialsGet()
|
||||
val commandExecutor = oauth2CommandExecutorFactory.getCommandExecutor()
|
||||
val localActor =
|
||||
getUserDetailApplicationService.execute(GetUserDetail(commandExecutor.userDetailId), commandExecutor)
|
||||
|
||||
return ResponseEntity.ok(
|
||||
CredentialAccount(
|
||||
id = localActor.id.toString(),
|
||||
username = localActor.name,
|
||||
acct = localActor.name + "@" + localActor.domain,
|
||||
url = localActor.url,
|
||||
displayName = localActor.screenName,
|
||||
note = localActor.description,
|
||||
avatar = localActor.iconUrl,
|
||||
avatarStatic = localActor.iconUrl,
|
||||
header = localActor.iconUrl,
|
||||
headerStatic = localActor.iconUrl,
|
||||
locked = localActor.locked,
|
||||
fields = emptyList(),
|
||||
emojis = localActor.emojis.map {
|
||||
CustomEmoji(
|
||||
shortcode = it.name,
|
||||
url = it.url.toString(),
|
||||
staticUrl = it.url.toString(),
|
||||
true,
|
||||
category = it.category.orEmpty()
|
||||
)
|
||||
},
|
||||
bot = false,
|
||||
group = false,
|
||||
discoverable = true,
|
||||
createdAt = localActor.createdAt.toString(),
|
||||
lastStatusAt = localActor.lastPostAt?.toString(),
|
||||
statusesCount = localActor.postsCount,
|
||||
followersCount = localActor.followersCount,
|
||||
followingCount = localActor.followingCount,
|
||||
moved = localActor.moveTo != null,
|
||||
noindex = true,
|
||||
suspendex = localActor.suspend,
|
||||
limited = false,
|
||||
role = null,
|
||||
source = AccountSource(
|
||||
localActor.description,
|
||||
emptyList(),
|
||||
AccountSource.Privacy.PUBLIC,
|
||||
false,
|
||||
0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun apiV1FollowRequestsAccountIdAuthorizePost(accountId: String): ResponseEntity<Relationship> {
|
||||
|
|
|
@ -242,6 +242,9 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AppsRequest"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AppsRequest"
|
||||
|
||||
responses:
|
||||
200:
|
||||
|
@ -1660,8 +1663,6 @@ components:
|
|||
- created_at
|
||||
- last_status_at
|
||||
- statuses_count
|
||||
- followers_count
|
||||
- followers_count
|
||||
- source
|
||||
|
||||
AccountSource:
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
{{/vars}}
|
||||
*/{{#discriminator}}
|
||||
{{>typeInfoAnnotation}}{{/discriminator}}
|
||||
{{#discriminator}}interface {{classname}}{{/discriminator}}{{^discriminator}}{{#hasVars}}data {{/hasVars}}class {{classname}}(
|
||||
|
||||
{{#discriminator}}interface {{classname}}{{/discriminator}}{{^discriminator}}{{#hasVars}}data {{/hasVars}}class {{classname}} @ConstructorProperties( {{#vars}}"{{baseName}}",{{/vars}} ) constructor(
|
||||
{{#requiredVars}}
|
||||
{{>dataClassReqVar}}{{^-last}},
|
||||
{{/-last}}{{/requiredVars}}{{#hasRequired}}{{#hasOptional}},
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
@Schema({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}description = "{{{description}}}"){{/swagger2AnnotationLibrary}}{{#swagger1AnnotationLibrary}}
|
||||
@ApiModelProperty({{#example}}example = "{{#lambdaRemoveLineBreak}}{{#lambdaEscapeDoubleQuote}}{{{.}}}{{/lambdaEscapeDoubleQuote}}{{/lambdaRemoveLineBreak}}", {{/example}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}"){{/swagger1AnnotationLibrary}}{{#deprecated}}
|
||||
@Deprecated(message = ""){{/deprecated}}
|
||||
@get:JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{{name}}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}}
|
||||
@get:JsonProperty("{{{baseName}}}"){{#isInherited}} override{{/isInherited}} {{>modelMutable}} {{#lambda.camelcase}} {{{name}}} {{/lambda.camelcase}}: {{#isEnum}}{{#isArray}}{{baseType}}<{{/isArray}}{{classname}}.{{{nameInCamelCase}}}{{#isArray}}>{{/isArray}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}? = {{{defaultValue}}}{{^defaultValue}}null{{/defaultValue}}
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.Objects
|
|||
{{#swagger1AnnotationLibrary}}
|
||||
import io.swagger.annotations.ApiModelProperty
|
||||
{{/swagger1AnnotationLibrary}}
|
||||
|
||||
import java.beans.ConstructorProperties
|
||||
{{#models}}
|
||||
{{#model}}
|
||||
{{#isEnum}}{{>enumClass}}{{/isEnum}}{{^isEnum}}{{>dataClass}}{{/isEnum}}
|
||||
|
|
Loading…
Reference in New Issue