mirror of https://github.com/usbharu/Hideout.git
Merge pull request #585 from usbharu/ssr-client
noscript用の簡易SSRクライアントを追加
This commit is contained in:
commit
20a762b8f4
|
@ -0,0 +1,38 @@
|
||||||
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.model.actor.Actor
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
data class ActorDetail(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val screenName: String,
|
||||||
|
val host: String,
|
||||||
|
val remoteUrl: String?,
|
||||||
|
val locked: Boolean,
|
||||||
|
val description: String,
|
||||||
|
val postsCount: Int,
|
||||||
|
val iconUrl: URI?,
|
||||||
|
val bannerURL: URI?,
|
||||||
|
val followingCount: Int?,
|
||||||
|
val followersCount: Int?,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun of(actor: Actor, iconUrl: URI?, bannerURL: URI?): ActorDetail {
|
||||||
|
return ActorDetail(
|
||||||
|
id = actor.id.id,
|
||||||
|
name = actor.name.name,
|
||||||
|
screenName = actor.screenName.screenName,
|
||||||
|
host = actor.url.host,
|
||||||
|
remoteUrl = actor.url.toString(),
|
||||||
|
locked = actor.locked,
|
||||||
|
description = actor.description.description,
|
||||||
|
postsCount = actor.postsCount.postsCount,
|
||||||
|
iconUrl = iconUrl,
|
||||||
|
bannerURL = bannerURL,
|
||||||
|
followingCount = actor.followingCount?.relationshipCount,
|
||||||
|
followersCount = actor.followersCount?.relationshipCount,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
|
||||||
|
|
||||||
|
data class GetActorDetail(
|
||||||
|
val actorName: Acct? = null,
|
||||||
|
val id: Long? = null
|
||||||
|
)
|
|
@ -0,0 +1,49 @@
|
||||||
|
package dev.usbharu.hideout.core.application.actor
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
|
||||||
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
|
import dev.usbharu.hideout.core.config.ApplicationConfig
|
||||||
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.actor.ActorRepository
|
||||||
|
import dev.usbharu.hideout.core.domain.model.media.MediaRepository
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class GetActorDetailApplicationService(
|
||||||
|
private val actorRepository: ActorRepository,
|
||||||
|
private val mediaRepository: MediaRepository,
|
||||||
|
private val applicationConfig: ApplicationConfig,
|
||||||
|
transaction: Transaction
|
||||||
|
) :
|
||||||
|
AbstractApplicationService<GetActorDetail, ActorDetail>(
|
||||||
|
transaction,
|
||||||
|
logger
|
||||||
|
) {
|
||||||
|
override suspend fun internalExecute(command: GetActorDetail, principal: Principal): ActorDetail {
|
||||||
|
val actor = if (command.id != null) {
|
||||||
|
actorRepository.findById(ActorId(command.id))
|
||||||
|
?: throw IllegalArgumentException("Actor ${command.id} not found.")
|
||||||
|
} else if (command.actorName != null) {
|
||||||
|
val host = if (command.actorName.host.isEmpty()) {
|
||||||
|
applicationConfig.url.host
|
||||||
|
} else {
|
||||||
|
command.actorName.host
|
||||||
|
}
|
||||||
|
actorRepository.findByNameAndDomain(command.actorName.userpart, host)
|
||||||
|
?: throw IllegalArgumentException("Actor ${command.actorName} not found.")
|
||||||
|
} else {
|
||||||
|
throw IllegalArgumentException("id and actorName are null.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val iconUrl = actor.icon?.let { mediaRepository.findById(it)?.url }
|
||||||
|
val bannerUrl = actor.banner?.let { mediaRepository.findById(it)?.url }
|
||||||
|
|
||||||
|
return ActorDetail.of(actor, iconUrl, bannerUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(GetActorDetailApplicationService::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,10 +19,11 @@ class GetLocalInstanceApplicationService(
|
||||||
transaction,
|
transaction,
|
||||||
logger
|
logger
|
||||||
) {
|
) {
|
||||||
var cachedInstance: Instance? = null
|
private var cachedInstance: Instance? = null
|
||||||
|
|
||||||
override suspend fun internalExecute(command: Unit, principal: Principal): Instance {
|
override suspend fun internalExecute(command: Unit, principal: Principal): Instance {
|
||||||
if (cachedInstance != null) {
|
if (cachedInstance != null) {
|
||||||
|
logger.trace("Use cache {}", cachedInstance)
|
||||||
@Suppress("UnsafeCallOnNullableType")
|
@Suppress("UnsafeCallOnNullableType")
|
||||||
return cachedInstance!!
|
return cachedInstance!!
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package dev.usbharu.hideout.core.application.timeline
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||||
|
|
||||||
|
data class GetUserTimeline(val id: Long, val page: Page)
|
|
@ -0,0 +1,53 @@
|
||||||
|
package dev.usbharu.hideout.core.application.timeline
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.post.PostDetail
|
||||||
|
import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
|
||||||
|
import dev.usbharu.hideout.core.application.shared.Transaction
|
||||||
|
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.post.PostRepository
|
||||||
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.page.PaginationList
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
|
import dev.usbharu.hideout.core.query.usertimeline.UserTimelineQueryService
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class GetUserTimelineApplicationService(
|
||||||
|
private val userTimelineQueryService: UserTimelineQueryService,
|
||||||
|
private val postRepository: PostRepository,
|
||||||
|
transaction: Transaction
|
||||||
|
) :
|
||||||
|
AbstractApplicationService<GetUserTimeline, PaginationList<PostDetail, PostId>>(transaction, logger) {
|
||||||
|
override suspend fun internalExecute(
|
||||||
|
command: GetUserTimeline,
|
||||||
|
principal: Principal
|
||||||
|
): PaginationList<PostDetail, PostId> {
|
||||||
|
val postList = postRepository.findByActorIdAndVisibilityInList(
|
||||||
|
ActorId(command.id),
|
||||||
|
listOf(Visibility.PUBLIC, Visibility.UNLISTED, Visibility.FOLLOWERS),
|
||||||
|
command.page
|
||||||
|
)
|
||||||
|
|
||||||
|
val postIdList =
|
||||||
|
postList.mapNotNull { it.repostId } + postList.mapNotNull { it.replyId } + postList.map { it.id }
|
||||||
|
|
||||||
|
val postDetailMap = userTimelineQueryService.findByIdAll(postIdList, principal).associateBy { it.id }
|
||||||
|
|
||||||
|
return PaginationList(
|
||||||
|
postList.mapNotNull {
|
||||||
|
postDetailMap[it.id.id]?.copy(
|
||||||
|
repost = postDetailMap[it.repostId?.id],
|
||||||
|
reply = postDetailMap[it.replyId?.id]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
postList.next,
|
||||||
|
postList.prev
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(GetUserTimelineApplicationService::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package dev.usbharu.hideout.core.config
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.context.MessageSourceProperties
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.context.MessageSource
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.context.annotation.Profile
|
||||||
|
import org.springframework.context.support.ReloadableResourceBundleMessageSource
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Profile("dev")
|
||||||
|
class MessageSourceConfig {
|
||||||
|
@Bean
|
||||||
|
fun messageSource(messageSourceProperties: MessageSourceProperties): MessageSource {
|
||||||
|
val reloadableResourceBundleMessageSource = ReloadableResourceBundleMessageSource()
|
||||||
|
reloadableResourceBundleMessageSource.setBasename("classpath:" + messageSourceProperties.basename)
|
||||||
|
reloadableResourceBundleMessageSource.setCacheSeconds(0)
|
||||||
|
return reloadableResourceBundleMessageSource
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Profile("dev")
|
||||||
|
@ConfigurationProperties(prefix = "spring.messages")
|
||||||
|
fun messageSourceProperties(): MessageSourceProperties = MessageSourceProperties()
|
||||||
|
}
|
|
@ -68,7 +68,7 @@ class SecurityConfig {
|
||||||
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
|
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
|
||||||
http {
|
http {
|
||||||
exceptionHandling {
|
exceptionHandling {
|
||||||
authenticationEntryPoint = LoginUrlAuthenticationEntryPoint("/login")
|
authenticationEntryPoint = LoginUrlAuthenticationEntryPoint("/auth/sign_in")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return http.build()
|
return http.build()
|
||||||
|
@ -80,20 +80,29 @@ class SecurityConfig {
|
||||||
http {
|
http {
|
||||||
authorizeHttpRequests {
|
authorizeHttpRequests {
|
||||||
authorize("/error", permitAll)
|
authorize("/error", permitAll)
|
||||||
authorize("/login", permitAll)
|
authorize("/auth/sign_in", permitAll)
|
||||||
authorize(GET, "/.well-known/**", permitAll)
|
authorize(GET, "/.well-known/**", permitAll)
|
||||||
authorize(GET, "/nodeinfo/2.0", permitAll)
|
authorize(GET, "/nodeinfo/2.0", permitAll)
|
||||||
|
|
||||||
authorize(GET, "/auth/sign_up", hasRole("ANONYMOUS"))
|
authorize(GET, "/auth/sign_up", hasRole("ANONYMOUS"))
|
||||||
authorize(POST, "/auth/sign_up", permitAll)
|
authorize(POST, "/auth/sign_up", permitAll)
|
||||||
authorize(GET, "/users/{username}/posts/{postId}", permitAll)
|
authorize(GET, "/users/{username}/posts/{postId}", permitAll)
|
||||||
|
authorize(GET, "/users/{userid}", permitAll)
|
||||||
authorize(GET, "/files/*", permitAll)
|
authorize(GET, "/files/*", permitAll)
|
||||||
authorize(POST, "/publish", authenticated)
|
authorize(POST, "/publish", authenticated)
|
||||||
authorize(GET, "/publish", authenticated)
|
authorize(GET, "/publish", authenticated)
|
||||||
|
authorize(GET, "/", permitAll)
|
||||||
|
|
||||||
authorize(anyRequest, authenticated)
|
authorize(anyRequest, authenticated)
|
||||||
}
|
}
|
||||||
formLogin {
|
formLogin {
|
||||||
|
loginPage = "/auth/sign_in"
|
||||||
|
loginProcessingUrl = "/login"
|
||||||
|
defaultSuccessUrl("/home", false)
|
||||||
|
}
|
||||||
|
logout {
|
||||||
|
logoutUrl = "/auth/sign_out"
|
||||||
|
logoutSuccessUrl = "/auth/sign_in"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return http.build()
|
return http.build()
|
||||||
|
@ -131,6 +140,7 @@ class SecurityConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@Suppress("UnsafeCallOnNullableType")
|
||||||
fun loadJwkSource(jwkConfig: JwkConfig, applicationConfig: ApplicationConfig): JWKSource<SecurityContext> {
|
fun loadJwkSource(jwkConfig: JwkConfig, applicationConfig: ApplicationConfig): JWKSource<SecurityContext> {
|
||||||
if (jwkConfig.keyId == null) {
|
if (jwkConfig.keyId == null) {
|
||||||
logger.error("hideout.security.jwt.keyId is null.")
|
logger.error("hideout.security.jwt.keyId is null.")
|
||||||
|
|
|
@ -16,20 +16,29 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.config
|
package dev.usbharu.hideout.core.config
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.infrastructure.springframework.SPAInterceptor
|
||||||
import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor
|
import dev.usbharu.hideout.generate.JsonOrFormModelMethodProcessor
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.http.converter.HttpMessageConverter
|
import org.springframework.http.converter.HttpMessageConverter
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
|
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
|
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
class MvcConfigurer(private val jsonOrFormModelMethodProcessor: JsonOrFormModelMethodProcessor) : WebMvcConfigurer {
|
class MvcConfigurer(
|
||||||
|
private val jsonOrFormModelMethodProcessor: JsonOrFormModelMethodProcessor,
|
||||||
|
private val spaInterceptor: SPAInterceptor
|
||||||
|
) : WebMvcConfigurer {
|
||||||
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
|
override fun addArgumentResolvers(resolvers: MutableList<HandlerMethodArgumentResolver>) {
|
||||||
resolvers.add(jsonOrFormModelMethodProcessor)
|
resolvers.add(jsonOrFormModelMethodProcessor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addInterceptors(registry: InterceptorRegistry) {
|
||||||
|
registry.addInterceptor(spaInterceptor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
|
|
@ -5,4 +5,14 @@ data class Acct(
|
||||||
val host: String
|
val host: String
|
||||||
) {
|
) {
|
||||||
override fun toString(): String = "acct:$userpart@$host"
|
override fun toString(): String = "acct:$userpart@$host"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun of(acct: String): Acct {
|
||||||
|
return Acct(
|
||||||
|
acct.substringBeforeLast('@'),
|
||||||
|
acct.substringAfterLast('@', "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,5 @@ package dev.usbharu.hideout.core.domain.model.userdetails
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
value class UserDetailHashedPassword(val password: String) {
|
value class UserDetailHashedPassword(val password: String) {
|
||||||
override fun toString(): String {
|
override fun toString(): String = "[MASKED]"
|
||||||
return "[MASKED]"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,5 @@ package dev.usbharu.hideout.core.domain.model.userdetails
|
||||||
|
|
||||||
@JvmInline
|
@JvmInline
|
||||||
value class UserDetailId(val id: Long) {
|
value class UserDetailId(val id: Long) {
|
||||||
override fun toString(): String {
|
override fun toString(): String = "UserDetailId(id=$id)"
|
||||||
return "UserDetailId(id=$id)"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package dev.usbharu.hideout.core.infrastructure.exposedquery
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.post.ActorDetail
|
||||||
|
import dev.usbharu.hideout.core.application.post.MediaDetail
|
||||||
|
import dev.usbharu.hideout.core.application.post.PostDetail
|
||||||
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.*
|
||||||
|
import dev.usbharu.hideout.core.query.usertimeline.UserTimelineQueryService
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class ExposedUserTimelineQueryService : UserTimelineQueryService, AbstractRepository() {
|
||||||
|
|
||||||
|
override val logger: Logger
|
||||||
|
get() = Companion.logger
|
||||||
|
|
||||||
|
protected fun authorizedQuery(principal: Principal? = null): QueryAlias {
|
||||||
|
if (principal == null) {
|
||||||
|
return Posts
|
||||||
|
.selectAll()
|
||||||
|
.where {
|
||||||
|
Posts.visibility eq Visibility.PUBLIC.name or (Posts.visibility eq Visibility.UNLISTED.name)
|
||||||
|
}.alias("authorized_table")
|
||||||
|
}
|
||||||
|
|
||||||
|
val relationshipsAlias = Relationships.alias("inverse_relationships")
|
||||||
|
|
||||||
|
return Posts
|
||||||
|
.leftJoin(PostsVisibleActors)
|
||||||
|
.leftJoin(Relationships, onColumn = { Posts.actorId }, otherColumn = { actorId })
|
||||||
|
.leftJoin(
|
||||||
|
relationshipsAlias,
|
||||||
|
onColumn = { Posts.actorId },
|
||||||
|
otherColumn = { relationshipsAlias[Relationships.targetActorId] }
|
||||||
|
)
|
||||||
|
.select(Posts.columns)
|
||||||
|
.where {
|
||||||
|
Posts.visibility eq Visibility.PUBLIC.name or
|
||||||
|
(Posts.visibility eq Visibility.UNLISTED.name) or
|
||||||
|
(Posts.visibility eq Visibility.DIRECT.name and (PostsVisibleActors.actorId eq principal.actorId.id)) or
|
||||||
|
(Posts.visibility eq Visibility.FOLLOWERS.name and (Relationships.blocking eq false and (relationshipsAlias[Relationships.following] eq true))) or
|
||||||
|
(Posts.actorId eq principal.actorId.id)
|
||||||
|
}
|
||||||
|
.alias("authorized_table")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findByIdAll(idList: List<PostId>, principal: Principal): List<PostDetail> {
|
||||||
|
val authorizedQuery = authorizedQuery(principal)
|
||||||
|
|
||||||
|
val iconMedia = Media.alias("ICON_MEDIA")
|
||||||
|
|
||||||
|
return authorizedQuery
|
||||||
|
.leftJoin(PostsVisibleActors, { authorizedQuery[Posts.id] }, { PostsVisibleActors.postId })
|
||||||
|
.leftJoin(Actors, { authorizedQuery[Posts.actorId] }, { Actors.id })
|
||||||
|
.leftJoin(iconMedia, { Actors.icon }, { iconMedia[Media.id] })
|
||||||
|
.leftJoin(PostsMedia, { authorizedQuery[Posts.id] }, { PostsMedia.postId })
|
||||||
|
.leftJoin(Media, { PostsMedia.mediaId }, { Media.id })
|
||||||
|
.selectAll()
|
||||||
|
.where { authorizedQuery[Posts.id] inList idList.map { it.id } }
|
||||||
|
.groupBy { it[authorizedQuery[Posts.id]] }
|
||||||
|
.map { it.value }
|
||||||
|
.map {
|
||||||
|
toPostDetail(it.first(), authorizedQuery, iconMedia).copy(
|
||||||
|
mediaDetailList = it.mapNotNull { resultRow ->
|
||||||
|
resultRow.toMediaOrNull()?.let { it1 -> MediaDetail.of(it1) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toPostDetail(it: ResultRow, authorizedQuery: QueryAlias, iconMedia: Alias<Media>): PostDetail {
|
||||||
|
return PostDetail(
|
||||||
|
id = it[authorizedQuery[Posts.id]],
|
||||||
|
actor = ActorDetail(
|
||||||
|
actorId = it[authorizedQuery[Posts.actorId]],
|
||||||
|
instanceId = it[Actors.instance],
|
||||||
|
name = it[Actors.name],
|
||||||
|
domain = it[Actors.domain],
|
||||||
|
screenName = it[Actors.screenName],
|
||||||
|
url = URI.create(it[Actors.url]),
|
||||||
|
locked = it[Actors.locked],
|
||||||
|
icon = it.getOrNull(iconMedia[Media.url])?.let { URI.create(it) }
|
||||||
|
),
|
||||||
|
overview = it[authorizedQuery[Posts.overview]],
|
||||||
|
text = it[authorizedQuery[Posts.text]],
|
||||||
|
content = it[authorizedQuery[Posts.content]],
|
||||||
|
createdAt = it[authorizedQuery[Posts.createdAt]],
|
||||||
|
visibility = Visibility.valueOf(it[authorizedQuery[Posts.visibility]]),
|
||||||
|
pureRepost = false,
|
||||||
|
url = URI.create(it[authorizedQuery[Posts.url]]),
|
||||||
|
apId = URI.create(it[authorizedQuery[Posts.apId]]),
|
||||||
|
repost = null,
|
||||||
|
reply = null,
|
||||||
|
sensitive = it[authorizedQuery[Posts.sensitive]],
|
||||||
|
deleted = it[authorizedQuery[Posts.deleted]],
|
||||||
|
mediaDetailList = emptyList(),
|
||||||
|
moveTo = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = LoggerFactory.getLogger(ExposedUserTimelineQueryService::class.java)
|
||||||
|
}
|
||||||
|
}
|
|
@ -211,17 +211,38 @@ class ExposedPostRepository(
|
||||||
visibilityList: List<Visibility>,
|
visibilityList: List<Visibility>,
|
||||||
of: Page?
|
of: Page?
|
||||||
): PaginationList<Post, PostId> {
|
): PaginationList<Post, PostId> {
|
||||||
return PaginationList(
|
val postList = query {
|
||||||
query {
|
val query = Posts
|
||||||
Posts
|
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where {
|
.where {
|
||||||
Posts.actorId eq actorId.id and (visibility inList visibilityList.map { it.name })
|
Posts.actorId eq actorId.id and (visibility inList visibilityList.map { it.name })
|
||||||
}
|
}
|
||||||
.let(postQueryMapper::map)
|
|
||||||
},
|
if (of?.minId != null) {
|
||||||
null,
|
query.orderBy(Posts.createdAt, SortOrder.ASC)
|
||||||
null
|
of.minId?.let { query.andWhere { Posts.id greater it } }
|
||||||
|
of.maxId?.let { query.andWhere { Posts.id less it } }
|
||||||
|
} else {
|
||||||
|
query.orderBy(Posts.createdAt, SortOrder.DESC)
|
||||||
|
of?.sinceId?.let { query.andWhere { Posts.id greater it } }
|
||||||
|
of?.maxId?.let { query.andWhere { Posts.id less it } }
|
||||||
|
}
|
||||||
|
|
||||||
|
of?.limit?.let { query.limit(it) }
|
||||||
|
|
||||||
|
query.let(postQueryMapper::map)
|
||||||
|
}
|
||||||
|
|
||||||
|
val posts = if (of?.minId != null) {
|
||||||
|
postList.reversed()
|
||||||
|
} else {
|
||||||
|
postList
|
||||||
|
}
|
||||||
|
|
||||||
|
return PaginationList(
|
||||||
|
posts,
|
||||||
|
posts.lastOrNull()?.id,
|
||||||
|
posts.firstOrNull()?.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,23 @@ fun ResultRow.toMedia(): EntityMedia {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ResultRow.toMediaOrNull(): EntityMedia? {
|
||||||
|
val fileType = FileType.valueOf(this.getOrNull(Media.type) ?: return null)
|
||||||
|
val mimeType = this.getOrNull(Media.mimeType) ?: return null
|
||||||
|
return EntityMedia(
|
||||||
|
id = MediaId(this.getOrNull(Media.id) ?: return null),
|
||||||
|
name = MediaName(this.getOrNull(Media.name) ?: return null),
|
||||||
|
url = URI.create(this.getOrNull(Media.url) ?: return null),
|
||||||
|
remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) },
|
||||||
|
thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) },
|
||||||
|
type = FileType.valueOf(this[Media.type]),
|
||||||
|
blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) },
|
||||||
|
mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType),
|
||||||
|
description = this[Media.description]?.let { MediaDescription(it) },
|
||||||
|
actorId = ActorId(this[Media.actorId])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
object Media : Table("media") {
|
object Media : Table("media") {
|
||||||
val id = long("id")
|
val id = long("id")
|
||||||
val name = varchar("name", 255)
|
val name = varchar("name", 255)
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package dev.usbharu.hideout.core.infrastructure.springframework
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.interfaces.web.common.OGP
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import jakarta.servlet.http.HttpServletResponse
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor
|
||||||
|
import org.springframework.web.servlet.ModelAndView
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class SPAInterceptor : HandlerInterceptor {
|
||||||
|
|
||||||
|
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
|
||||||
|
if (request.getParameter("s") == "f") {
|
||||||
|
request.session.setAttribute("s", "f")
|
||||||
|
} else if (request.getParameter("s") == "t") {
|
||||||
|
request.session.setAttribute("s", "t")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun postHandle(
|
||||||
|
request: HttpServletRequest,
|
||||||
|
response: HttpServletResponse,
|
||||||
|
handler: Any,
|
||||||
|
modelAndView: ModelAndView?
|
||||||
|
) {
|
||||||
|
if (modelAndView?.viewName == "error") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.session.getAttribute("s") == "f") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val ogp = modelAndView?.modelMap?.get("ogp") as? OGP
|
||||||
|
|
||||||
|
modelAndView?.clear()
|
||||||
|
modelAndView?.addObject("nsUrl", request.requestURI + "?s=f" + request.queryString?.let { "&$it" }.orEmpty())
|
||||||
|
modelAndView?.addObject("ogp", ogp)
|
||||||
|
modelAndView?.viewName = "index"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package dev.usbharu.hideout.core.interfaces.web
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.instance.GetLocalInstanceApplicationService
|
||||||
|
import dev.usbharu.hideout.core.config.ApplicationConfig
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
|
||||||
|
import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityFormLoginPrincipalContextHolder
|
||||||
|
import org.springframework.stereotype.Controller
|
||||||
|
import org.springframework.ui.Model
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class IndexController(
|
||||||
|
private val applicationConfig: ApplicationConfig,
|
||||||
|
private val springSecurityFormLoginPrincipalContextHolder: SpringSecurityFormLoginPrincipalContextHolder,
|
||||||
|
private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService
|
||||||
|
) {
|
||||||
|
@GetMapping("/")
|
||||||
|
suspend fun index(model: Model): String {
|
||||||
|
if (springSecurityFormLoginPrincipalContextHolder.getPrincipal().userDetailId != null) {
|
||||||
|
return "redirect:/home"
|
||||||
|
}
|
||||||
|
|
||||||
|
val instance = getLocalInstanceApplicationService.execute(Unit, Anonymous)
|
||||||
|
model.addAttribute("instance", instance)
|
||||||
|
model.addAttribute("applicationConfig", applicationConfig)
|
||||||
|
return "top"
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,13 +14,16 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.usbharu.hideout.core.interfaces.api.auth
|
package dev.usbharu.hideout.core.interfaces.web.auth
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.application.actor.RegisterLocalActor
|
import dev.usbharu.hideout.core.application.actor.RegisterLocalActor
|
||||||
import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService
|
import dev.usbharu.hideout.core.application.actor.RegisterLocalActorApplicationService
|
||||||
|
import dev.usbharu.hideout.core.application.instance.GetLocalInstanceApplicationService
|
||||||
|
import dev.usbharu.hideout.core.config.ApplicationConfig
|
||||||
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
|
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
|
||||||
import jakarta.servlet.http.HttpServletRequest
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
import org.springframework.stereotype.Controller
|
import org.springframework.stereotype.Controller
|
||||||
|
import org.springframework.ui.Model
|
||||||
import org.springframework.validation.annotation.Validated
|
import org.springframework.validation.annotation.Validated
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute
|
import org.springframework.web.bind.annotation.ModelAttribute
|
||||||
|
@ -28,11 +31,16 @@ import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
class AuthController(
|
class AuthController(
|
||||||
|
private val applicationConfig: ApplicationConfig,
|
||||||
private val registerLocalActorApplicationService: RegisterLocalActorApplicationService,
|
private val registerLocalActorApplicationService: RegisterLocalActorApplicationService,
|
||||||
|
private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService,
|
||||||
) {
|
) {
|
||||||
@GetMapping("/auth/sign_up")
|
@GetMapping("/auth/sign_up")
|
||||||
@Suppress("FunctionOnlyReturningConstant")
|
@Suppress("FunctionOnlyReturningConstant")
|
||||||
fun signUp(): String = "sign_up"
|
suspend fun signUp(model: Model): String {
|
||||||
|
model.addAttribute("instance", getLocalInstanceApplicationService.execute(Unit, Anonymous))
|
||||||
|
return "sign_up"
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/auth/sign_up")
|
@PostMapping("/auth/sign_up")
|
||||||
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm, request: HttpServletRequest): String {
|
suspend fun signUp(@Validated @ModelAttribute signUpForm: SignUpForm, request: HttpServletRequest): String {
|
||||||
|
@ -44,4 +52,14 @@ class AuthController(
|
||||||
request.login(signUpForm.username, signUpForm.password)
|
request.login(signUpForm.username, signUpForm.password)
|
||||||
return "redirect:$uri"
|
return "redirect:$uri"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/auth/sign_in")
|
||||||
|
suspend fun signIn(model: Model): String {
|
||||||
|
model.addAttribute("applicationConfig", applicationConfig)
|
||||||
|
return "sign_in"
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/auth/sign_out")
|
||||||
|
@Suppress("FunctionOnlyReturningConstant")
|
||||||
|
fun signOut(): String = "sign_out"
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package dev.usbharu.hideout.core.interfaces.api.auth
|
package dev.usbharu.hideout.core.interfaces.web.auth
|
||||||
|
|
||||||
data class SignUpForm(
|
data class SignUpForm(
|
||||||
val username: String,
|
val username: String,
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.usbharu.hideout.core.interfaces.web.common
|
||||||
|
|
||||||
|
data class OGP(
|
||||||
|
val title: String,
|
||||||
|
val url: String,
|
||||||
|
val description: String,
|
||||||
|
val image: String?
|
||||||
|
)
|
|
@ -13,6 +13,7 @@ import org.springframework.ui.Model
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute
|
import org.springframework.web.bind.annotation.ModelAttribute
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
class PublishController(
|
class PublishController(
|
||||||
|
@ -22,7 +23,7 @@ class PublishController(
|
||||||
private val userRegisterLocalPostApplicationService: RegisterLocalPostApplicationService
|
private val userRegisterLocalPostApplicationService: RegisterLocalPostApplicationService
|
||||||
) {
|
) {
|
||||||
@GetMapping("/publish")
|
@GetMapping("/publish")
|
||||||
suspend fun publish(model: Model): String {
|
suspend fun publish(model: Model, @RequestParam("reply_to") replyTo: Long?, @RequestParam repost: Long?): String {
|
||||||
val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal()
|
val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal()
|
||||||
|
|
||||||
if (principal.userDetailId == null) {
|
if (principal.userDetailId == null) {
|
||||||
|
@ -35,7 +36,7 @@ class PublishController(
|
||||||
val userDetail = getUserDetailApplicationService.execute(GetUserDetail(principal.userDetailId!!.id), principal)
|
val userDetail = getUserDetailApplicationService.execute(GetUserDetail(principal.userDetailId!!.id), principal)
|
||||||
model.addAttribute("instance", instance)
|
model.addAttribute("instance", instance)
|
||||||
model.addAttribute("user", userDetail)
|
model.addAttribute("user", userDetail)
|
||||||
model.addAttribute("form", PublishPost())
|
model.addAttribute("form", PublishPost(reply_to = replyTo, repost = repost))
|
||||||
return "post-postForm"
|
return "post-postForm"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +51,8 @@ class PublishController(
|
||||||
content = publishPost.status.orEmpty(),
|
content = publishPost.status.orEmpty(),
|
||||||
overview = publishPost.overview,
|
overview = publishPost.overview,
|
||||||
visibility = Visibility.valueOf(publishPost.visibility.uppercase()),
|
visibility = Visibility.valueOf(publishPost.visibility.uppercase()),
|
||||||
repostId = null,
|
repostId = publishPost.repost,
|
||||||
replyId = null,
|
replyId = publishPost.reply_to,
|
||||||
sensitive = false,
|
sensitive = false,
|
||||||
mediaIds = emptyList()
|
mediaIds = emptyList()
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
package dev.usbharu.hideout.core.interfaces.web.posts
|
package dev.usbharu.hideout.core.interfaces.web.posts
|
||||||
|
|
||||||
data class PublishPost(var status: String? = null, var overview: String? = null, var visibility: String = "PUBLIC")
|
@Suppress("ConstructorParameterNaming")
|
||||||
|
data class PublishPost(
|
||||||
|
var status: String? = null,
|
||||||
|
var overview: String? = null,
|
||||||
|
var visibility: String = "PUBLIC",
|
||||||
|
var reply_to: Long? = null,
|
||||||
|
var repost: Long? = null
|
||||||
|
)
|
||||||
|
|
|
@ -22,9 +22,9 @@ class TimelineController(
|
||||||
@GetMapping("/home")
|
@GetMapping("/home")
|
||||||
suspend fun homeTimeline(
|
suspend fun homeTimeline(
|
||||||
model: Model,
|
model: Model,
|
||||||
@RequestParam sinceId: String?,
|
@RequestParam("since_id") sinceId: String?,
|
||||||
@RequestParam maxId: String?,
|
@RequestParam("max_id") maxId: String?,
|
||||||
@RequestParam minId: String?
|
@RequestParam("min_id") minId: String?
|
||||||
): String {
|
): String {
|
||||||
val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal()
|
val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal()
|
||||||
val userDetail = transaction.transaction {
|
val userDetail = transaction.transaction {
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package dev.usbharu.hideout.core.interfaces.web.user
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.actor.GetActorDetail
|
||||||
|
import dev.usbharu.hideout.core.application.actor.GetActorDetailApplicationService
|
||||||
|
import dev.usbharu.hideout.core.application.instance.GetLocalInstanceApplicationService
|
||||||
|
import dev.usbharu.hideout.core.application.timeline.GetUserTimeline
|
||||||
|
import dev.usbharu.hideout.core.application.timeline.GetUserTimelineApplicationService
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.acct.Acct
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.page.Page
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
|
||||||
|
import dev.usbharu.hideout.core.infrastructure.springframework.SpringSecurityFormLoginPrincipalContextHolder
|
||||||
|
import org.springframework.stereotype.Controller
|
||||||
|
import org.springframework.ui.Model
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
class UserController(
|
||||||
|
private val getLocalInstanceApplicationService: GetLocalInstanceApplicationService,
|
||||||
|
private val getUserDetailApplicationService: GetActorDetailApplicationService,
|
||||||
|
private val springSecurityFormLoginPrincipalContextHolder: SpringSecurityFormLoginPrincipalContextHolder,
|
||||||
|
private val getUserTimelineApplicationService: GetUserTimelineApplicationService
|
||||||
|
) {
|
||||||
|
@GetMapping("/users/{name}")
|
||||||
|
suspend fun userById(
|
||||||
|
@PathVariable name: String,
|
||||||
|
@RequestParam("min_id") minId: Long?,
|
||||||
|
@RequestParam("max_id") maxId: Long?,
|
||||||
|
@RequestParam("since_id") sinceId: Long?,
|
||||||
|
model: Model
|
||||||
|
): String {
|
||||||
|
val principal = springSecurityFormLoginPrincipalContextHolder.getPrincipal()
|
||||||
|
|
||||||
|
model.addAttribute("instance", getLocalInstanceApplicationService.execute(Unit, Anonymous))
|
||||||
|
val actorDetail = getUserDetailApplicationService.execute(GetActorDetail(Acct.of(name)), principal)
|
||||||
|
model.addAttribute(
|
||||||
|
"user",
|
||||||
|
actorDetail
|
||||||
|
)
|
||||||
|
model.addAttribute(
|
||||||
|
"userTimeline",
|
||||||
|
getUserTimelineApplicationService.execute(
|
||||||
|
GetUserTimeline(
|
||||||
|
actorDetail.id,
|
||||||
|
Page.of(maxId, sinceId, minId, 20)
|
||||||
|
),
|
||||||
|
principal
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return "userById"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package dev.usbharu.hideout.core.query.usertimeline
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.core.application.post.PostDetail
|
||||||
|
import dev.usbharu.hideout.core.domain.model.post.PostId
|
||||||
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
|
|
||||||
|
interface UserTimelineQueryService {
|
||||||
|
/**
|
||||||
|
* replyやrepost等はnullになります
|
||||||
|
*/
|
||||||
|
suspend fun findByIdAll(idList: List<PostId>, principal: Principal): List<PostDetail>
|
||||||
|
}
|
|
@ -45,11 +45,7 @@ object RsaUtil {
|
||||||
|
|
||||||
fun decodeRsaPrivateKey(encoded: String): RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded))
|
fun decodeRsaPrivateKey(encoded: String): RSAPrivateKey = decodeRsaPrivateKey(Base64Util.decode(encoded))
|
||||||
|
|
||||||
fun encodeRsaPublicKey(publicKey: RSAPublicKey): String {
|
fun encodeRsaPublicKey(publicKey: RSAPublicKey): String = Base64Util.encode(publicKey.encoded)
|
||||||
return Base64Util.encode(publicKey.encoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encodeRsaPrivateKey(privateKey: RSAPrivateKey): String {
|
fun encodeRsaPrivateKey(privateKey: RSAPrivateKey): String = Base64Util.encode(privateKey.encoded)
|
||||||
return Base64Util.encode(privateKey.encoded)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@ spring:
|
||||||
virtual:
|
virtual:
|
||||||
enabled: true
|
enabled: true
|
||||||
messages:
|
messages:
|
||||||
basename: messages.hideout-web-messages
|
basename: messages/hideout-web-messages
|
||||||
|
cache-duration: -1
|
||||||
thymeleaf:
|
thymeleaf:
|
||||||
cache: false
|
cache: false
|
||||||
server:
|
server:
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
auth-signIn.title=\u30ED\u30B0\u30A4\u30F3 - {0}
|
||||||
|
auth-signUp.password=\u30D1\u30B9\u30EF\u30FC\u30C9
|
||||||
|
auth-signUp.register=\u767B\u9332\u3059\u308B
|
||||||
|
auth-signUp.username=\u30E6\u30FC\u30B6\u30FC\u540D
|
||||||
common.audio=\u30AA\u30FC\u30C7\u30A3\u30AA
|
common.audio=\u30AA\u30FC\u30C7\u30A3\u30AA
|
||||||
common.audio-download-link=\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9
|
common.audio-download-link=\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9
|
||||||
common.empty=\u8868\u793A\u3059\u308B\u3082\u306E\u304C\u3042\u308A\u307E\u305B\u3093
|
common.empty=\u8868\u793A\u3059\u308B\u3082\u306E\u304C\u3042\u308A\u307E\u305B\u3093
|
||||||
|
@ -16,6 +20,12 @@ post-form.new-posts=\u65B0\u3057\u3044\u6295\u7A3F!
|
||||||
post-form.new-posts-cw=CW
|
post-form.new-posts-cw=CW
|
||||||
post-form.new-posts-cw-title=\u30B3\u30F3\u30C6\u30F3\u30C4\u306B\u95B2\u89A7\u6CE8\u610F\u3092\u3064\u3051\u308B
|
post-form.new-posts-cw-title=\u30B3\u30F3\u30C6\u30F3\u30C4\u306B\u95B2\u89A7\u6CE8\u610F\u3092\u3064\u3051\u308B
|
||||||
post-form.new-posts-form-label=\u4ECA\u306A\u306B\u3057\u3066\u308B?
|
post-form.new-posts-form-label=\u4ECA\u306A\u306B\u3057\u3066\u308B?
|
||||||
|
post-form.new-posts-replyTo=\u8FD4\u4FE1\u5148
|
||||||
post-form.new-posts-submit=\u6295\u7A3F\u3059\u308B
|
post-form.new-posts-submit=\u6295\u7A3F\u3059\u308B
|
||||||
|
post-form.new-posts.repost=\u30EA\u30DD\u30B9\u30C8
|
||||||
post.repost=\u30EA\u30DD\u30B9\u30C8
|
post.repost=\u30EA\u30DD\u30B9\u30C8
|
||||||
post.repost-by={0}\u304C\u30EA\u30DD\u30B9\u30C8
|
post.repost-by={0}\u304C\u30EA\u30DD\u30B9\u30C8
|
||||||
|
user-by-id.followersCount={0} \u30D5\u30A9\u30ED\u30EF\u30FC
|
||||||
|
user-by-id.followingCount={0} \u30D5\u30A9\u30ED\u30FC\u4E2D
|
||||||
|
user-by-id.postsCount={0} \u6295\u7A3F
|
||||||
|
user-by-id.title={0} \u3055\u3093 - {1}
|
|
@ -1,3 +1,7 @@
|
||||||
|
auth-signIn.title=Sign in - {0}
|
||||||
|
auth-signUp.password=Password
|
||||||
|
auth-signUp.register=Register Account
|
||||||
|
auth-signUp.username=Username
|
||||||
common.audio=Audio
|
common.audio=Audio
|
||||||
common.audio-download-link=Download the audio.
|
common.audio-download-link=Download the audio.
|
||||||
common.empty=Empty
|
common.empty=Empty
|
||||||
|
@ -15,6 +19,11 @@ post-form.new-posts=New Posts!
|
||||||
post-form.new-posts-cw=CW
|
post-form.new-posts-cw=CW
|
||||||
post-form.new-posts-cw-title=Add content warning
|
post-form.new-posts-cw-title=Add content warning
|
||||||
post-form.new-posts-form-label=What's on your mind?
|
post-form.new-posts-form-label=What's on your mind?
|
||||||
|
post-form.new-posts-replyTo=Reply to
|
||||||
post-form.new-posts-submit=Submit!
|
post-form.new-posts-submit=Submit!
|
||||||
|
post-form.new-posts.repost=Repost
|
||||||
post.repost=Repost
|
post.repost=Repost
|
||||||
post.repost-by=Repost by {0}
|
post.repost-by=Repost by {0}
|
||||||
|
user-by-id.followersCount={0} Followers
|
||||||
|
user-by-id.followingCount={0} Following
|
||||||
|
user-by-id.postsCount={0} Posts
|
|
@ -1,3 +1,7 @@
|
||||||
|
auth-signIn.title=\u30ED\u30B0\u30A4\u30F3 - {0}
|
||||||
|
auth-signUp.password=\u30D1\u30B9\u30EF\u30FC\u30C9
|
||||||
|
auth-signUp.register=\u767B\u9332\u3059\u308B
|
||||||
|
auth-signUp.username=\u30E6\u30FC\u30B6\u30FC\u540D
|
||||||
common.audio=\u30AA\u30FC\u30C7\u30A3\u30AA
|
common.audio=\u30AA\u30FC\u30C7\u30A3\u30AA
|
||||||
common.audio-download-link=\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9
|
common.audio-download-link=\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9
|
||||||
common.empty=\u8868\u793A\u3059\u308B\u3082\u306E\u304C\u3042\u308A\u307E\u305B\u3093
|
common.empty=\u8868\u793A\u3059\u308B\u3082\u306E\u304C\u3042\u308A\u307E\u305B\u3093
|
||||||
|
@ -16,6 +20,12 @@ post-form.new-posts=\u65B0\u3057\u3044\u6295\u7A3F!
|
||||||
post-form.new-posts-cw=CW
|
post-form.new-posts-cw=CW
|
||||||
post-form.new-posts-cw-title=\u30B3\u30F3\u30C6\u30F3\u30C4\u306B\u95B2\u89A7\u6CE8\u610F\u3092\u3064\u3051\u308B
|
post-form.new-posts-cw-title=\u30B3\u30F3\u30C6\u30F3\u30C4\u306B\u95B2\u89A7\u6CE8\u610F\u3092\u3064\u3051\u308B
|
||||||
post-form.new-posts-form-label=\u4ECA\u306A\u306B\u3057\u3066\u308B?
|
post-form.new-posts-form-label=\u4ECA\u306A\u306B\u3057\u3066\u308B?
|
||||||
|
post-form.new-posts-replyTo=\u8FD4\u4FE1\u5148
|
||||||
post-form.new-posts-submit=\u6295\u7A3F\u3059\u308B
|
post-form.new-posts-submit=\u6295\u7A3F\u3059\u308B
|
||||||
|
post-form.new-posts.repost=\u30EA\u30DD\u30B9\u30C8
|
||||||
post.repost=\u30EA\u30DD\u30B9\u30C8
|
post.repost=\u30EA\u30DD\u30B9\u30C8
|
||||||
post.repost-by={0}\u304C\u30EA\u30DD\u30B9\u30C8
|
post.repost-by={0}\u304C\u30EA\u30DD\u30B9\u30C8
|
||||||
|
user-by-id.followersCount={0} \u30D5\u30A9\u30ED\u30EF\u30FC
|
||||||
|
user-by-id.followingCount={0} \u30D5\u30A9\u30ED\u30FC\u4E2D
|
||||||
|
user-by-id.postsCount={0} \u6295\u7A3F
|
||||||
|
user-by-id.title={0} \u3055\u3093 - {1}
|
|
@ -11,7 +11,8 @@
|
||||||
<!--/*@thymesVar id="post" type="dev.usbharu.hideout.core.application.post.PostDetail"*/-->
|
<!--/*@thymesVar id="post" type="dev.usbharu.hideout.core.application.post.PostDetail"*/-->
|
||||||
<img alt="" height="80px" src="" th:src="${post.actor.icon}" width="80px">
|
<img alt="" height="80px" src="" th:src="${post.actor.icon}" width="80px">
|
||||||
<div style="display: inline-block">
|
<div style="display: inline-block">
|
||||||
<p th:text="${post.actor.screenName}+'('+${post.actor.name}+'@'+${post.actor.domain}+')'"></p>
|
<a th:href="${post.actor.url}"
|
||||||
|
th:text="${post.actor.screenName}+'(@'+${post.actor.name}+'@'+${post.actor.domain}+')'"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div th:utext="${post.content}">
|
<div th:utext="${post.content}">
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
|
|
||||||
<div class="post-controller" th:fragment="single-post-controller(post)">
|
<div class="post-controller" th:fragment="single-post-controller(post)">
|
||||||
<!--/*@thymesVar id="post" type="dev.usbharu.hideout.core.application.post.PostDetail"*/-->
|
<!--/*@thymesVar id="post" type="dev.usbharu.hideout.core.application.post.PostDetail"*/-->
|
||||||
|
<a th:href="${'/publish?reply_to=' + post.id}">Reply</a>
|
||||||
<a th:href="${post.apId}">
|
<a th:href="${post.apId}">
|
||||||
<time th:datetime="${post.createdAt}" th:text="${#temporals.format(post.createdAt, 'yyyy-MM-dd HH:mm')}"></time>
|
<time th:datetime="${post.createdAt}" th:text="${#temporals.format(post.createdAt, 'yyyy-MM-dd HH:mm')}"></time>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<th:block th:fragment="simple-timline(timelineObject,href)">
|
<th:block th:fragment="simple-timline(timelineObject,href)">
|
||||||
<!--/*@thymesVar id="timelineObject" type="dev.usbharu.hideout.core.domain.model.support.page.PaginationList<dev.usbharu.hideout.core.application.post.PostDetail,dev.usbharu.hideout.core.domain.model.post.PostId>"*/-->
|
<!--/*@thymesVar id="timelineObject" type="dev.usbharu.hideout.core.domain.model.support.page.PaginationList<dev.usbharu.hideout.core.application.post.PostDetail,dev.usbharu.hideout.core.domain.model.post.PostId>"*/-->
|
||||||
<div th:if="${timelineObject.prev != null}">
|
<div th:if="${timelineObject.prev != null}">
|
||||||
<a th:href="${href + '?minId=' + timelineObject.prev.id}" th:text="#{common.paging-load}">Show more</a>
|
<a th:href="${href + '?min_id=' + timelineObject.prev.id}" th:text="#{common.paging-load}">Show more</a>
|
||||||
</div>
|
</div>
|
||||||
<div th:if="${timelineObject.isEmpty()}" th:text="#{common.empty}"></div>
|
<div th:if="${timelineObject.isEmpty()}" th:text="#{common.empty}"></div>
|
||||||
<div th:each="postDetail : ${timelineObject}">
|
<div th:each="postDetail : ${timelineObject}">
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
<th:block th:replace="~{fragments-post :: single-post-controller(${postDetail})}"></th:block>
|
<th:block th:replace="~{fragments-post :: single-post-controller(${postDetail})}"></th:block>
|
||||||
</div>
|
</div>
|
||||||
<div th:if="${timelineObject.next != null}">
|
<div th:if="${timelineObject.next != null}">
|
||||||
<a th:href="${href + '?maxId=' + timelineObject.next.id}" th:text="#{common.paging-load}">Show more</a>
|
<a th:href="${href + '?max_id=' + timelineObject.next.id}" th:text="#{common.paging-load}">Show more</a>
|
||||||
</div>
|
</div>
|
||||||
</th:block>
|
</th:block>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
<html lang="en" th:replace="~{layout::layout(${ogp}, ~{::#content})}" xmlns:th="http://www.thymeleaf.org">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Title</title>
|
<title>Title</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<div id="content" th:replace="~{fragments-timeline :: simple-timline(${timeline},'/home')}"></div>
|
||||||
<div th:replace="~{fragments-timeline :: simple-timline(${timeline},'/home')}"></div>
|
|
||||||
</noscript>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head prefix="og: http://ogp.me/ns#">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title th:title="${ogp?.title}">Index</title>
|
||||||
|
<meta property="og:url" th:content="${ogp?.url}">
|
||||||
|
<meta property="og:title" th:content="${ogp?.title}">
|
||||||
|
<meta property="og:description" th:content="${ogp?.description}">
|
||||||
|
<th:block th:if="${ogp?.image != null}">
|
||||||
|
<meta property="og:image" th:content="${ogp.image}">
|
||||||
|
</th:block>
|
||||||
|
<noscript>
|
||||||
|
<meta http-equiv="refresh" th:content="${'1; url='+nsUrl}">
|
||||||
|
</noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<a th:href="${nsUrl}">No Script</a>
|
||||||
|
</noscript>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" th:fragment="layout(ogp, content)" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head prefix="og: http://ogp.me/ns#">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<!--/*@thymesVar id="ogp" type="dev.usbharu.hideout.core.interfaces.web.common.OGP"*/-->
|
||||||
|
<title th:title="${ogp?.title}">Index</title>
|
||||||
|
<meta property="og:url" th:content="${ogp?.url}">
|
||||||
|
<meta property="og:title" th:content="${ogp?.title}">
|
||||||
|
<meta property="og:description" th:content="${ogp?.description}">
|
||||||
|
<th:block th:if="${ogp?.image != null}">
|
||||||
|
<meta property="og:image" th:content="${ogp.image}">
|
||||||
|
</th:block>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<a href="/home">Hideout</a><a href="/publish">New Post</a><a href="/users/1"><img height="80px"
|
||||||
|
src="/users/i/icon.jpg" width="80px"></a>
|
||||||
|
</header>
|
||||||
|
<hr>
|
||||||
|
<main>
|
||||||
|
<div th:replace="${content}"></div>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,5 +1,5 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
|
<html lang="ja" th:replace="~{layout::layout(${ogp}, ~{::#content})}" xmlns:th="http://www.thymeleaf.org">
|
||||||
<head prefix="og: http://ogp.me/ns#">
|
<head prefix="og: http://ogp.me/ns#">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title th:text="#{post-form.new-posts}">New Posts!</title>
|
<title th:text="#{post-form.new-posts}">New Posts!</title>
|
||||||
|
@ -7,8 +7,8 @@
|
||||||
<meta property="og:title" th:content="#{post-form.new-posts}">
|
<meta property="og:title" th:content="#{post-form.new-posts}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
|
||||||
<noscript>
|
|
||||||
<div>
|
<div>
|
||||||
<img alt="" height="80px" src="" th:src="${user.iconUrl}" width="80px">
|
<img alt="" height="80px" src="" th:src="${user.iconUrl}" width="80px">
|
||||||
<div style="display: inline-block">
|
<div style="display: inline-block">
|
||||||
|
@ -16,6 +16,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form action="/publish" method="post" th:action="@{/publish}" th:object="${form}">
|
<form action="/publish" method="post" th:action="@{/publish}" th:object="${form}">
|
||||||
|
<div>
|
||||||
|
<label for="status-form-replyto" th:text="#{post-form.new-posts-replyTo}">Reply to</label>
|
||||||
|
<input id="status-form-replyto" name="reply_to" th:value="${form.reply_to}" type="number">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="status-form-repost" th:text="#{post-form.new-posts.repost}">Repost</label>
|
||||||
|
<input id="status-form-repost" name="repost" type="number" th:value="${form.repost}">
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="stats-form-overview" th:text="#{post-form.new-posts-cw}"
|
<label for="stats-form-overview" th:text="#{post-form.new-posts-cw}"
|
||||||
th:title="#{post-form.new-posts-cw-title}"
|
th:title="#{post-form.new-posts-cw-title}"
|
||||||
|
@ -31,13 +39,16 @@
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend th:text="#{common.visibility}">Visibility</legend>
|
<legend th:text="#{common.visibility}">Visibility</legend>
|
||||||
<label for="status-form-visibility-public" th:text="#{common.visibility-public}">Public</label>
|
<label for="status-form-visibility-public" th:text="#{common.visibility-public}">Public</label>
|
||||||
<input id="status-form-visibility-public" name="visibility" th:checked="${form.visibility == 'PUBLIC'}" type="radio"
|
<input id="status-form-visibility-public" name="visibility" th:checked="${form.visibility == 'PUBLIC'}"
|
||||||
|
type="radio"
|
||||||
value="PUBLIC">
|
value="PUBLIC">
|
||||||
<label for="status-form-visibility-unlisted" th:text="#{common.visibility-unlisted}">Unlisted</label>
|
<label for="status-form-visibility-unlisted" th:text="#{common.visibility-unlisted}">Unlisted</label>
|
||||||
<input id="status-form-visibility-unlisted" name="visibility" th:checked="${form.visibility == 'UNLISTED'}" type="radio"
|
<input id="status-form-visibility-unlisted" name="visibility"
|
||||||
|
th:checked="${form.visibility == 'UNLISTED'}" type="radio"
|
||||||
value="UNLISTED">
|
value="UNLISTED">
|
||||||
<label for="status-form-visibility-followers" th:text="#{common.visibility-followers}">Followers</label>
|
<label for="status-form-visibility-followers" th:text="#{common.visibility-followers}">Followers</label>
|
||||||
<input id="status-form-visibility-followers" name="visibility" th:checked="${form.visibility == 'FOLLOWERS'}" type="radio"
|
<input id="status-form-visibility-followers" name="visibility"
|
||||||
|
th:checked="${form.visibility == 'FOLLOWERS'}" type="radio"
|
||||||
value="FOLLOWERS">
|
value="FOLLOWERS">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,7 +56,7 @@
|
||||||
<input th:value="#{post-form.new-posts-submit}" type="submit" value="Publish!">
|
<input th:value="#{post-form.new-posts-submit}" type="submit" value="Publish!">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</noscript>
|
|
||||||
|
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,24 +1,13 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
|
<html lang="ja" th:replace="~{layout::layout(${ogp},~{::#content})}" xmlns:th="http://www.thymeleaf.org">
|
||||||
<head prefix="og: http://ogp.me/ns#">
|
<head prefix="og: http://ogp.me/ns#">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title th:text="#{post-by-id.title(${post.actor.screenName},${instance.name})}">Posts - hideout</title>
|
<title>Posts - hideout</title>
|
||||||
<meta property="og:url" th:content="${post.url}">
|
|
||||||
<meta property="og:title" th:content="#{post-by-id.title(${post.actor.screenName},${instance.name})}">
|
|
||||||
<meta property="og:description" th:content="${post.text}">
|
|
||||||
<th:block
|
|
||||||
th:if="${post.mediaDetailList.isEmpty() || (post.mediaDetailList.get(0).thumbnailUrl == null && (post.mediaDetailList.get(0).type != 'Image' || post.mediaDetailList.get(0).type != 'Video'))}">
|
|
||||||
<meta property="og:image" th:content="${post.actor.icon}">
|
|
||||||
</th:block>
|
|
||||||
<th:block
|
|
||||||
th:unless="${post.mediaDetailList.isEmpty() || (post.mediaDetailList.get(0).thumbnailUrl == null && (post.mediaDetailList.get(0).type != 'Image' || post.mediaDetailList.get(0).type != 'Video'))}">
|
|
||||||
<meta property="og:image" th:content="${post.mediaDetailList.get(0).thumbnailUrl}">
|
|
||||||
</th:block>
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
|
||||||
|
<div id="content">
|
||||||
<th:block th:if=" ${post.reply != null}">
|
<th:block th:if=" ${post.reply != null}">
|
||||||
<th:block th:replace="~{fragments-post :: single-simple-post(${post.reply})}"></th:block>
|
<th:block th:replace="~{fragments-post :: single-simple-post(${post.reply})}"></th:block>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -40,6 +29,7 @@
|
||||||
<th:block th:replace="={fragments-post :: single-simple-post(${post.repost})}"></th:block>
|
<th:block th:replace="={fragments-post :: single-simple-post(${post.repost})}"></th:block>
|
||||||
<cite th:text="${post.repost.apId}"></cite>
|
<cite th:text="${post.repost.apId}"></cite>
|
||||||
</th:block>
|
</th:block>
|
||||||
</noscript>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title th:text="#{auth-signIn.title(${applicationConfig.url})}">Sign In - Hideout</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div th:if="${param.error}">
|
||||||
|
Invalid username and password.
|
||||||
|
</div>
|
||||||
|
<div th:if="${param.logout}">
|
||||||
|
You have been logged out.
|
||||||
|
</div>
|
||||||
|
<form method="post" th:action="@{/login}">
|
||||||
|
<div>
|
||||||
|
<input name="username" placeholder="Username" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input name="password" placeholder="Password" type="password"/>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Log in"/>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form method="post" th:action="@{/auth/sign_out}">
|
||||||
|
<input name="logout" type="submit" value="Sign out">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -2,13 +2,21 @@
|
||||||
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
|
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>SignUp</title>
|
<title th:text="#{auth-signUp.register}">Register Account</title>
|
||||||
|
<meta property="og:url" th:content="${instance.url + '/auth/sign_up'}">
|
||||||
|
<meta property="og:title" th:content="#{auth-signUp.register}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<form method='post' th:action="@{/auth/sign_up}">
|
<form method='post' th:action="@{/auth/sign_up}">
|
||||||
<input name='username' type='text' value=''>
|
<div>
|
||||||
<input name='password' type='password'>
|
<label for="signUp-form-username" th:text="#{auth-signUp.username}">Username</label>
|
||||||
|
<input id="signUp-form-username" name='username' type='text' value=''>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="signUp-form-password" th:text="#{auth-signUp.password}">Password</label>
|
||||||
|
<input id="signUp-form-password" name='password' type='password'>
|
||||||
|
</div>
|
||||||
<input type="submit">
|
<input type="submit">
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title th:title="${title}">Index</title>
|
||||||
|
<meta property="og:url" th:content="${url}">
|
||||||
|
<meta property="og:title" th:content="${title}">
|
||||||
|
<meta property="og:description" th:content="${description}">
|
||||||
|
<meta property="og:image" th:content="${image}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 th:text="${instance.name + ' - Hideout'}">Hideout</h1>
|
||||||
|
<div><a href="/auth/sign_up" th:unless="${applicationConfig.private}">Sign up</a> <a href="/auth/sign_in">Sign in</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" th:replace="~{layout::layout(${ogp}, ~{::#content})}" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>User - hideout</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="content">
|
||||||
|
<div>
|
||||||
|
<img alt="" height="150px" th:src="${user.iconUrl}" width="150px">
|
||||||
|
<img alt="" height="150px" th:src="${user.bannerURL}" width="600px">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<th:block th:if="${user.locked}">
|
||||||
|
<h2 th:text="${user.screenName} + '(private)'"></h2>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<th:block th:if="!${user.locked}">
|
||||||
|
<h2 th:text="${user.screenName}"></h2>
|
||||||
|
</th:block>
|
||||||
|
<p th:text="'@'+${user.name} + '@' + ${user.host}"></p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p th:text="${user.description}"></p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p th:if="user.postsCount != null" th:text="#{user-by-id.postsCount(${user.postsCount})}">0 Posts</p>
|
||||||
|
<p th:if="user.followingCount != null" th:text="#{user-by-id.followingCount(${user.followingCount})}">0
|
||||||
|
Following</p>
|
||||||
|
<p th:if="user.followersCount != null" th:text="#{user-by-id.followersCount(${user.followersCount})}">0
|
||||||
|
Followers</p>
|
||||||
|
</div>
|
||||||
|
<div th:replace="~{fragments-timeline :: simple-timline(${userTimeline},'/users/'+${user.name}+'@'+${user.host})}"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -16,9 +16,8 @@
|
||||||
|
|
||||||
package dev.usbharu.hideout.mastodon.infrastructure.exposedquery
|
package dev.usbharu.hideout.mastodon.infrastructure.exposedquery
|
||||||
|
|
||||||
import dev.usbharu.hideout.core.domain.model.actor.ActorId
|
|
||||||
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
import dev.usbharu.hideout.core.domain.model.emoji.CustomEmoji
|
||||||
import dev.usbharu.hideout.core.domain.model.media.*
|
import dev.usbharu.hideout.core.domain.model.media.FileType
|
||||||
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
import dev.usbharu.hideout.core.domain.model.post.Visibility
|
||||||
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
import dev.usbharu.hideout.core.domain.model.support.principal.Principal
|
||||||
import dev.usbharu.hideout.core.infrastructure.exposedrepository.*
|
import dev.usbharu.hideout.core.infrastructure.exposedrepository.*
|
||||||
|
@ -30,7 +29,6 @@ import dev.usbharu.hideout.mastodon.query.StatusQuery
|
||||||
import dev.usbharu.hideout.mastodon.query.StatusQueryService
|
import dev.usbharu.hideout.mastodon.query.StatusQueryService
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
import java.net.URI
|
|
||||||
import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia
|
import dev.usbharu.hideout.core.domain.model.media.Media as EntityMedia
|
||||||
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.CustomEmoji as MastodonEmoji
|
import dev.usbharu.hideout.mastodon.interfaces.api.generated.model.CustomEmoji as MastodonEmoji
|
||||||
|
|
||||||
|
@ -274,40 +272,6 @@ private fun toStatus(it: ResultRow, queryAlias: QueryAlias, inReplyToAlias: Alia
|
||||||
editedAt = null
|
editedAt = null
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ResultRow.toMedia(): EntityMedia {
|
|
||||||
val fileType = FileType.valueOf(this[Media.type])
|
|
||||||
val mimeType = this[Media.mimeType]
|
|
||||||
return EntityMedia(
|
|
||||||
id = MediaId(this[Media.id]),
|
|
||||||
name = MediaName(this[Media.name]),
|
|
||||||
url = URI.create(this[Media.url]),
|
|
||||||
remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) },
|
|
||||||
thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) },
|
|
||||||
type = fileType,
|
|
||||||
blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) },
|
|
||||||
mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType),
|
|
||||||
description = this[Media.description]?.let { MediaDescription(it) },
|
|
||||||
actorId = ActorId(this[Media.actorId])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ResultRow.toMediaOrNull(): EntityMedia? {
|
|
||||||
val fileType = FileType.valueOf(this.getOrNull(Media.type) ?: return null)
|
|
||||||
val mimeType = this.getOrNull(Media.mimeType) ?: return null
|
|
||||||
return EntityMedia(
|
|
||||||
id = MediaId(this.getOrNull(Media.id) ?: return null),
|
|
||||||
name = MediaName(this.getOrNull(Media.name) ?: return null),
|
|
||||||
url = URI.create(this.getOrNull(Media.url) ?: return null),
|
|
||||||
remoteUrl = this[Media.remoteUrl]?.let { URI.create(it) },
|
|
||||||
thumbnailUrl = this[Media.thumbnailUrl]?.let { URI.create(it) },
|
|
||||||
type = FileType.valueOf(this[Media.type]),
|
|
||||||
blurHash = this[Media.blurhash]?.let { MediaBlurHash(it) },
|
|
||||||
mimeType = MimeType(mimeType.substringBefore("/"), mimeType.substringAfter("/"), fileType),
|
|
||||||
description = this[Media.description]?.let { MediaDescription(it) },
|
|
||||||
actorId = ActorId(this[Media.actorId])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun EntityMedia.toMediaAttachments(): MediaAttachment = MediaAttachment(
|
fun EntityMedia.toMediaAttachments(): MediaAttachment = MediaAttachment(
|
||||||
id = id.id.toString(),
|
id = id.id.toString(),
|
||||||
type = when (type) {
|
type = when (type) {
|
||||||
|
|
Loading…
Reference in New Issue