mirror of https://github.com/usbharu/Hideout.git
feat: 投稿APIを追加
This commit is contained in:
parent
19e44c4675
commit
0e00e9526d
|
@ -1,15 +1,30 @@
|
|||
package dev.usbharu.hideout.controller.mastodon
|
||||
|
||||
import dev.usbharu.hideout.controller.mastodon.generated.DefaultApi
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequest
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.V1Instance
|
||||
import dev.usbharu.hideout.domain.model.UserDetailsImpl
|
||||
import dev.usbharu.hideout.service.api.mastodon.InstanceApiService
|
||||
import dev.usbharu.hideout.service.api.mastodon.StatusesApiService
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.stereotype.Controller
|
||||
|
||||
|
||||
@Controller
|
||||
class MastodonApiController(private val instanceApiService: InstanceApiService) : DefaultApi {
|
||||
class MastodonApiController(
|
||||
private val instanceApiService: InstanceApiService,
|
||||
private val statusesApiService: StatusesApiService
|
||||
) : DefaultApi {
|
||||
override suspend fun apiV1InstanceGet(): ResponseEntity<V1Instance> {
|
||||
return ResponseEntity(instanceApiService.v1Instance(), HttpStatus.OK)
|
||||
}
|
||||
|
||||
override suspend fun apiV1StatusesPost(statusesRequest: StatusesRequest): ResponseEntity<Status> {
|
||||
val principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal()
|
||||
require(principal is UserDetailsImpl)
|
||||
return ResponseEntity(statusesApiService.postStatus(statusesRequest, principal), HttpStatus.OK)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package dev.usbharu.hideout.domain.model
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import org.springframework.security.core.userdetails.User
|
||||
import java.io.Serial
|
||||
|
||||
class UserDetailsImpl(
|
||||
val id: Long,
|
||||
username: String?,
|
||||
password: String?,
|
||||
enabled: Boolean,
|
||||
accountNonExpired: Boolean,
|
||||
credentialsNonExpired: Boolean,
|
||||
accountNonLocked: Boolean,
|
||||
authorities: MutableCollection<out GrantedAuthority>?
|
||||
) : User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities) {
|
||||
companion object {
|
||||
@Serial
|
||||
private const val serialVersionUID: Long = -899168205656607781L
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package dev.usbharu.hideout.service.api.mastodon
|
||||
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Status
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.StatusesRequest
|
||||
import dev.usbharu.hideout.domain.model.UserDetailsImpl
|
||||
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
|
||||
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
|
||||
import dev.usbharu.hideout.exception.FailedToGetResourcesException
|
||||
import dev.usbharu.hideout.query.PostQueryService
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import dev.usbharu.hideout.service.mastodon.AccountService
|
||||
import dev.usbharu.hideout.service.post.PostService
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.Instant
|
||||
|
||||
@Service
|
||||
interface StatusesApiService {
|
||||
suspend fun postStatus(statusesRequest: StatusesRequest, user: UserDetailsImpl): Status
|
||||
}
|
||||
|
||||
|
||||
@Service
|
||||
class StatsesApiServiceImpl(
|
||||
private val postService: PostService,
|
||||
private val accountService: AccountService,
|
||||
private val postQueryService: PostQueryService,
|
||||
private val userQueryService: UserQueryService
|
||||
) :
|
||||
StatusesApiService {
|
||||
override suspend fun postStatus(statusesRequest: StatusesRequest, user: UserDetailsImpl): Status {
|
||||
|
||||
val visibility = when (statusesRequest.visibility) {
|
||||
StatusesRequest.Visibility.public -> Visibility.PUBLIC
|
||||
StatusesRequest.Visibility.unlisted -> Visibility.UNLISTED
|
||||
StatusesRequest.Visibility.private -> Visibility.FOLLOWERS
|
||||
StatusesRequest.Visibility.direct -> Visibility.DIRECT
|
||||
null -> Visibility.PUBLIC
|
||||
}
|
||||
|
||||
val post = postService.createLocal(
|
||||
PostCreateDto(
|
||||
statusesRequest.status.orEmpty(),
|
||||
statusesRequest.spoilerText,
|
||||
visibility,
|
||||
null,
|
||||
statusesRequest.inReplyToId?.toLongOrNull(),
|
||||
user.id
|
||||
)
|
||||
)
|
||||
val account = accountService.findById(user.id)
|
||||
|
||||
val postVisibility = when (statusesRequest.visibility) {
|
||||
StatusesRequest.Visibility.public -> Status.Visibility.public
|
||||
StatusesRequest.Visibility.unlisted -> Status.Visibility.unlisted
|
||||
StatusesRequest.Visibility.private -> Status.Visibility.private
|
||||
StatusesRequest.Visibility.direct -> Status.Visibility.direct
|
||||
null -> Status.Visibility.public
|
||||
}
|
||||
|
||||
val replyUser = if (post.replyId != null) {
|
||||
try {
|
||||
userQueryService.findById(postQueryService.findById(post.replyId).userId).id
|
||||
} catch (e: FailedToGetResourcesException) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
|
||||
return Status(
|
||||
id = post.id.toString(),
|
||||
uri = post.apId,
|
||||
createdAt = Instant.ofEpochMilli(post.createdAt).toString(),
|
||||
account = account,
|
||||
content = post.text,
|
||||
visibility = postVisibility,
|
||||
sensitive = post.sensitive,
|
||||
spoilerText = post.overview.orEmpty(),
|
||||
mediaAttachments = emptyList(),
|
||||
mentions = emptyList(),
|
||||
tags = emptyList(),
|
||||
emojis = emptyList(),
|
||||
reblogsCount = 0,
|
||||
favouritesCount = 0,
|
||||
repliesCount = 0,
|
||||
url = post.url,
|
||||
post.replyId?.toString(),
|
||||
inReplyToAccountId = replyUser?.toString(),
|
||||
reblog = null,
|
||||
language = null,
|
||||
text = post.text,
|
||||
editedAt = null,
|
||||
application = null,
|
||||
poll = null,
|
||||
card = null,
|
||||
favourited = null,
|
||||
reblogged = null,
|
||||
muted = null,
|
||||
bookmarked = null,
|
||||
pinned = null,
|
||||
filtered = null
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,39 @@
|
|||
package dev.usbharu.hideout.service.mastodon
|
||||
|
||||
import dev.usbharu.hideout.domain.mastodon.model.generated.Account
|
||||
import dev.usbharu.hideout.query.UserQueryService
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
interface AccountService {
|
||||
suspend fun findById()
|
||||
suspend fun findById(id: Long): Account
|
||||
}
|
||||
|
||||
@Service
|
||||
class AccountServiceImpl(private val userQueryService: UserQueryService) : AccountService {
|
||||
override suspend fun findById(id: Long): Account {
|
||||
val findById = userQueryService.findById(id)
|
||||
return Account(
|
||||
id = findById.id.toString(),
|
||||
username = findById.name,
|
||||
acct = "${findById.name}@${findById.domain}",
|
||||
url = findById.url,
|
||||
displayName = findById.screenName,
|
||||
note = findById.description,
|
||||
avatar = findById.url + "/icon.jpg",
|
||||
avatarStatic = findById.url + "/icon.jpg",
|
||||
header = findById.url + "/header.jpg",
|
||||
headerStatic = findById.url + "/header.jpg",
|
||||
locked = false,
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
findById.createdAt.toString(),
|
||||
findById.createdAt.toString(),
|
||||
0,
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,27 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/V1Instance"
|
||||
|
||||
/api/v1/statuses:
|
||||
post:
|
||||
security:
|
||||
- OAuth2:
|
||||
- "write:statuses"
|
||||
requestBody:
|
||||
description: 投稿する内容
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/StatusesRequest"
|
||||
responses:
|
||||
200:
|
||||
description: 成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Status"
|
||||
|
||||
components:
|
||||
schemas:
|
||||
Account:
|
||||
|
@ -250,7 +271,7 @@ components:
|
|||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/StatusMention"
|
||||
tag:
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/StatusTag"
|
||||
|
@ -277,7 +298,7 @@ components:
|
|||
$ref: "#/components/schemas/Status"
|
||||
poll:
|
||||
$ref: "#/components/schemas/Poll"
|
||||
PreviewCard:
|
||||
card:
|
||||
$ref: "#/components/schemas/PreviewCard"
|
||||
language:
|
||||
type: string
|
||||
|
@ -302,7 +323,28 @@ components:
|
|||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/FilterResult"
|
||||
|
||||
required:
|
||||
- id
|
||||
- uri
|
||||
- created_at
|
||||
- account
|
||||
- content
|
||||
- visibility
|
||||
- sensitive
|
||||
- spoiler_text
|
||||
- media_attachments
|
||||
- mentions
|
||||
- tags
|
||||
- emojis
|
||||
- reblogs_count
|
||||
- favourites_count
|
||||
- replies_count
|
||||
- url
|
||||
- in_reply_to_id
|
||||
- in_reply_to_account_id
|
||||
- language
|
||||
- text
|
||||
- edited_at
|
||||
|
||||
MediaAttachment:
|
||||
type: object
|
||||
|
@ -844,3 +886,95 @@ components:
|
|||
type: string
|
||||
content:
|
||||
type: string
|
||||
|
||||
StatusesRequest:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
nullable: true
|
||||
media_ids:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
poll:
|
||||
$ref: "#/components/schemas/StatusesRequestPoll"
|
||||
in_reply_to_id:
|
||||
type: string
|
||||
sensitive:
|
||||
type: boolean
|
||||
spoiler_text:
|
||||
type: string
|
||||
visibility:
|
||||
type: string
|
||||
enum:
|
||||
- public
|
||||
- unlisted
|
||||
- private
|
||||
- direct
|
||||
language:
|
||||
type: string
|
||||
scheduled_at:
|
||||
type: string
|
||||
|
||||
StatusesRequestPoll:
|
||||
type: object
|
||||
properties:
|
||||
options:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
expires_in:
|
||||
type: integer
|
||||
multiple:
|
||||
type: boolean
|
||||
hide_totals:
|
||||
type: boolean
|
||||
|
||||
securitySchemes:
|
||||
OAuth2:
|
||||
type: oauth2
|
||||
description: Mastodon Oauth
|
||||
flows:
|
||||
authorizationCode:
|
||||
authorizationUrl: /oauth/authorize
|
||||
tokenUrl: /oauth/token
|
||||
scopes:
|
||||
read:accounts: ""
|
||||
read:blocks: ""
|
||||
read:bookmarks: ""
|
||||
read:favourites: ""
|
||||
read:filters: ""
|
||||
read:follows: ""
|
||||
read:lists: ""
|
||||
read:mutes: ""
|
||||
read:notifications: ""
|
||||
read:search: ""
|
||||
read:statuses: ""
|
||||
write:accounts: ""
|
||||
write:blocks: ""
|
||||
write:bookmarks: ""
|
||||
write:conversations: ""
|
||||
write:favourites: ""
|
||||
write:filters: ""
|
||||
write:follows: ""
|
||||
write:lists: ""
|
||||
write:media: ""
|
||||
write:mutes: ""
|
||||
write:notifications: ""
|
||||
write:reports: ""
|
||||
write:statuses: ""
|
||||
admin:read:accounts: ""
|
||||
admin:read:reports: ""
|
||||
admin:read:domain_allows: ""
|
||||
admin:read:domain_blocks: ""
|
||||
admin:read:ip_blocks: ""
|
||||
admin:read:email_domain_blocks: ""
|
||||
admin:read:canonical_email_blocks: ""
|
||||
admin:write:accounts: ""
|
||||
admin:write:reports: ""
|
||||
admin:write:domain_allows: ""
|
||||
admin:write:domain_blocks: ""
|
||||
admin:write:ip_blocks: ""
|
||||
admin:write:email_domain_blocks: ""
|
||||
admin:write:canonical_email_blocks: ""
|
||||
|
|
Loading…
Reference in New Issue