Compare commits

..

No commits in common. "4bec7a49d61cc668ce79900e16fab3c053f03d1d" and "2b4b33fe8904b6bc662391f777045abaf90ee43e" have entirely different histories.

38 changed files with 269 additions and 930 deletions

2
.gitignore vendored
View File

@ -37,5 +37,3 @@ out/
*.db
/src/main/resources/static/
/node_modules/
/src/main/web/generated/
/stats.html

View File

@ -80,14 +80,12 @@ dependencies {
implementation("org.xerial:sqlite-jdbc:3.40.1.0")
implementation("io.ktor:ktor-server-websockets-jvm:$ktor_version")
implementation("io.ktor:ktor-server-cio-jvm:$ktor_version")
implementation("io.ktor:ktor-server-compression:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("io.insert-koin:koin-core:$koin_version")
implementation("io.insert-koin:koin-ktor:$koin_version")
implementation("io.insert-koin:koin-logger-slf4j:$koin_version")
implementation("io.insert-koin:koin-annotations:1.2.0")
implementation("io.ktor:ktor-server-compression-jvm:2.3.0")
ksp("io.insert-koin:koin-ksp-compiler:1.2.0")

View File

@ -1,22 +0,0 @@
{
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "6.6.0",
"generators": {
"v3.0": {
"generatorName": "typescript-fetch",
"output": "src/main/web/generated",
"glob": "src/main/resources/openapi/api.yaml",
"additionalProperties": {
"modelPropertyNaming": "camelCase",
"supportsES6": true,
"withInterfaces": true,
"typescriptThreePlus": true,
"useSingleRequestParameter": false,
"prependFormOrBodyParameters": true
}
}
}
}
}

14
package-lock.json generated
View File

@ -13,7 +13,7 @@
"devDependencies": {
"@suid/vite-plugin": "^0.1.3",
"typescript": "^5.0.4",
"vite": "4.2.3",
"vite": "^4.2.1",
"vite-plugin-solid": "^2.7.0"
}
},
@ -1552,9 +1552,9 @@
"dev": true
},
"node_modules/vite": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.3.tgz",
"integrity": "sha512-kLU+m2q0Y434Y1kCy3TchefAdtFso0ILi0dLyFV8Us3InXTU11H/B5ZTqCKIQHzSKNxVG/yEx813EA9f1imQ9A==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
"integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==",
"dev": true,
"dependencies": {
"esbuild": "^0.17.5",
@ -2643,9 +2643,9 @@
"dev": true
},
"vite": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.3.tgz",
"integrity": "sha512-kLU+m2q0Y434Y1kCy3TchefAdtFso0ILi0dLyFV8Us3InXTU11H/B5ZTqCKIQHzSKNxVG/yEx813EA9f1imQ9A==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
"integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==",
"dev": true,
"requires": {
"esbuild": "^0.17.5",

View File

@ -6,7 +6,7 @@
},
"devDependencies": {
"typescript": "^5.0.4",
"vite": "4.2.3",
"vite": "^4.2.1",
"vite-plugin-solid": "^2.7.0",
"@suid/vite-plugin": "^0.1.3"
},

View File

@ -95,15 +95,17 @@ fun Application.parent() {
runBlocking {
inject<IServerInitialiseService>().value.init()
}
configureCompression()
configureHTTP()
configureStaticRouting()
configureMonitoring()
configureSerialization()
register(inject<IUserService>().value)
configureSecurity(
inject<IUserAuthService>().value,
inject<IMetaService>().value,
inject<IUserRepository>().value,
inject<IJwtService>().value,
inject<JwkProvider>().value,
inject<IMetaService>().value
)
configureRouting(
httpSignatureVerifyService = inject<HttpSignatureVerifyService>().value,
@ -112,10 +114,6 @@ fun Application.parent() {
activityPubUserService = inject<ActivityPubUserService>().value,
postService = inject<IPostApiService>().value,
userApiService = inject<IUserApiService>().value,
userAuthService = inject<IUserAuthService>().value,
userRepository = inject<IUserRepository>().value,
jwtService = inject<IJwtService>().value,
metaService = inject<IMetaService>().value
)
}

View File

@ -10,8 +10,6 @@ open class Note : Object {
var inReplyTo: String? = null
protected constructor() : super()
@Suppress("LongParameterList")
constructor(
type: List<String> = emptyList(),
name: String,

View File

@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.JsonNode
import dev.usbharu.hideout.service.activitypub.ExtendedActivityVocabulary
class ObjectDeserializer : JsonDeserializer<Object>() {
@Suppress("LongMethod")
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Object {
requireNotNull(p)
val treeNode: JsonNode = requireNotNull(p.codec?.readTree(p))

View File

@ -1,31 +0,0 @@
package dev.usbharu.hideout.domain.model.hideout.dto
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import dev.usbharu.hideout.domain.model.hideout.entity.User
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
data class PostResponse(
val id: Long,
val user: UserResponse,
val overview: String? = null,
val text: String? = null,
val createdAt: Long,
val visibility: Visibility,
val url: String,
val sensitive: Boolean = false,
) {
companion object {
fun from(post: Post, user: User): PostResponse {
return PostResponse(
id = post.id,
user = UserResponse.from(user),
overview = post.overview,
text = post.text,
createdAt = post.createdAt,
visibility = post.visibility,
url = post.url,
sensitive = post.sensitive
)
}
}
}

View File

@ -18,7 +18,7 @@ data class User(
) {
override fun toString(): String {
return "User(id=$id, name='$name', domain='$domain', screenName='$screenName', description='$description'," +
" password=****, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey'," +
" privateKey=****, createdAt=$createdAt)"
" password=****, inbox='$inbox', outbox='$outbox', url='$url', publicKey='$publicKey'," +
" privateKey=****, createdAt=$createdAt)"
}
}

View File

@ -1,19 +0,0 @@
package dev.usbharu.hideout.plugins
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.plugins.compression.*
fun Application.configureCompression() {
install(Compression) {
gzip {
matchContentType(ContentType.Application.JavaScript)
priority = 1.0
}
deflate {
matchContentType(ContentType.Application.JavaScript)
priority = 10.0
minimumSize(1024) // condition
}
}
}

View File

@ -1,10 +1,8 @@
package dev.usbharu.hideout.plugins
import dev.usbharu.hideout.repository.IUserRepository
import dev.usbharu.hideout.routing.activitypub.inbox
import dev.usbharu.hideout.routing.activitypub.outbox
import dev.usbharu.hideout.routing.activitypub.usersAP
import dev.usbharu.hideout.routing.api.internal.v1.auth
import dev.usbharu.hideout.routing.api.internal.v1.posts
import dev.usbharu.hideout.routing.api.internal.v1.users
import dev.usbharu.hideout.routing.wellknown.webfinger
@ -13,9 +11,6 @@ import dev.usbharu.hideout.service.activitypub.ActivityPubUserService
import dev.usbharu.hideout.service.api.IPostApiService
import dev.usbharu.hideout.service.api.IUserApiService
import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService
import dev.usbharu.hideout.service.auth.IJwtService
import dev.usbharu.hideout.service.core.IMetaService
import dev.usbharu.hideout.service.user.IUserAuthService
import dev.usbharu.hideout.service.user.IUserService
import io.ktor.server.application.*
import io.ktor.server.plugins.autohead.*
@ -28,11 +23,7 @@ fun Application.configureRouting(
userService: IUserService,
activityPubUserService: ActivityPubUserService,
postService: IPostApiService,
userApiService: IUserApiService,
userAuthService: IUserAuthService,
userRepository: IUserRepository,
jwtService: IJwtService,
metaService: IMetaService
userApiService: IUserApiService
) {
install(AutoHeadResponse)
routing {
@ -43,7 +34,6 @@ fun Application.configureRouting(
route("/api/internal/v1") {
posts(postService)
users(userService, userApiService)
auth(userAuthService, userRepository, jwtService)
}
}
}

View File

@ -2,12 +2,19 @@ package dev.usbharu.hideout.plugins
import com.auth0.jwk.JwkProvider
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
import dev.usbharu.hideout.exception.UserNotFoundException
import dev.usbharu.hideout.repository.IUserRepository
import dev.usbharu.hideout.service.auth.IJwtService
import dev.usbharu.hideout.service.core.IMetaService
import dev.usbharu.hideout.service.user.IUserAuthService
import dev.usbharu.hideout.util.JsonWebKeyUtil
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
@ -15,8 +22,11 @@ const val TOKEN_AUTH = "jwt-auth"
@Suppress("MagicNumber")
fun Application.configureSecurity(
jwkProvider: JwkProvider,
metaService: IMetaService
userAuthService: IUserAuthService,
metaService: IMetaService,
userRepository: IUserRepository,
jwtService: IJwtService,
jwkProvider: JwkProvider
) {
val issuer = Config.configData.url
install(Authentication) {
@ -38,6 +48,24 @@ fun Application.configureSecurity(
}
routing {
post("/login") {
val loginUser = call.receive<UserLogin>()
val check = userAuthService.verifyAccount(loginUser.username, loginUser.password)
if (check.not()) {
return@post call.respond(HttpStatusCode.Unauthorized)
}
val user = userRepository.findByNameAndDomain(loginUser.username, Config.configData.domain)
?: throw UserNotFoundException("${loginUser.username} was not found.")
return@post call.respond(jwtService.createToken(user))
}
post("/refresh-token") {
val refreshToken = call.receive<RefreshToken>()
return@post call.respond(jwtService.refreshToken(refreshToken))
}
get("/.well-known/jwks.json") {
//language=JSON
val jwt = metaService.getJwtMeta()
@ -46,5 +74,12 @@ fun Application.configureSecurity(
text = JsonWebKeyUtil.publicKeyToJwk(jwt.publicKey, jwt.kid.toString())
)
}
authenticate(TOKEN_AUTH) {
get("/auth-check") {
val principal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
val username = principal.payload.getClaim("uid")
call.respondText("Hello $username")
}
}
}
}

View File

@ -88,9 +88,7 @@ class PostRepositoryImpl(database: Database, private val idGenerateService: IdGe
limit: Int?,
userId: Long?
): List<Post> {
return query {
Posts.select { Posts.visibility eq Visibility.PUBLIC.ordinal }.map { it.toPost() }
}
TODO("Not yet implemented")
}
override suspend fun findByUserNameAndDomain(

View File

@ -1,48 +0,0 @@
package dev.usbharu.hideout.routing.api.internal.v1
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
import dev.usbharu.hideout.exception.UserNotFoundException
import dev.usbharu.hideout.plugins.TOKEN_AUTH
import dev.usbharu.hideout.repository.IUserRepository
import dev.usbharu.hideout.service.auth.IJwtService
import dev.usbharu.hideout.service.user.IUserAuthService
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Route.auth(
userAuthService: IUserAuthService,
userRepository: IUserRepository,
jwtService: IJwtService
) {
post("/login") {
val loginUser = call.receive<UserLogin>()
val check = userAuthService.verifyAccount(loginUser.username, loginUser.password)
if (check.not()) {
return@post call.respond(HttpStatusCode.Unauthorized)
}
val user = userRepository.findByNameAndDomain(loginUser.username, Config.configData.domain)
?: throw UserNotFoundException("${loginUser.username} was not found.")
return@post call.respond(jwtService.createToken(user))
}
post("/refresh-token") {
val refreshToken = call.receive<RefreshToken>()
return@post call.respond(jwtService.refreshToken(refreshToken))
}
authenticate(TOKEN_AUTH) {
get("/auth-check") {
val principal = call.principal<JWTPrincipal>() ?: throw IllegalStateException("no principal")
val username = principal.payload.getClaim("uid")
call.respondText("Hello $username")
}
}
}

View File

@ -42,11 +42,11 @@ fun Route.users(userService: IUserService, userApiService: IUserApiService) {
authenticate(TOKEN_AUTH, optional = true) {
get {
val userParameter = (
call.parameters["name"]
?: throw ParameterNotExistException(
"Parameter(name='userName@domain') does not exist."
call.parameters["name"]
?: throw ParameterNotExistException(
"Parameter(name='userName@domain') does not exist."
)
)
)
if (userParameter.toLongOrNull() != null) {
return@get call.respond(userApiService.findById(userParameter.toLong()))
} else {
@ -92,11 +92,11 @@ fun Route.users(userService: IUserService, userApiService: IUserApiService) {
route("/following") {
get {
val userParameter = (
call.parameters["name"]
?: throw ParameterNotExistException(
"Parameter(name='userName@domain') does not exist."
call.parameters["name"]
?: throw ParameterNotExistException(
"Parameter(name='userName@domain') does not exist."
)
)
)
if (userParameter.toLongOrNull() != null) {
return@get call.respond(userApiService.findFollowings(userParameter.toLong()))
}

View File

@ -1,12 +1,12 @@
package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import java.time.Instant
@Suppress("LongParameterList")
interface IPostApiService {
suspend fun createPost(postForm: dev.usbharu.hideout.domain.model.hideout.form.Post, userId: Long): PostResponse
suspend fun getById(id: Long, userId: Long?): PostResponse
suspend fun createPost(postForm: dev.usbharu.hideout.domain.model.hideout.form.Post, userId: Long): Post
suspend fun getById(id: Long, userId: Long?): Post
suspend fun getAll(
since: Instant? = null,
until: Instant? = null,
@ -14,7 +14,7 @@ interface IPostApiService {
maxId: Long? = null,
limit: Int? = null,
userId: Long? = null
): List<PostResponse>
): List<Post>
suspend fun getByUser(
nameOrId: String,
@ -24,5 +24,5 @@ interface IPostApiService {
maxId: Long? = null,
limit: Int? = null,
userId: Long? = null
): List<PostResponse>
): List<Post>
}

View File

@ -2,16 +2,11 @@ package dev.usbharu.hideout.service.api
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.dto.PostCreateDto
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
import dev.usbharu.hideout.repository.*
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import dev.usbharu.hideout.exception.PostNotFoundException
import dev.usbharu.hideout.repository.IPostRepository
import dev.usbharu.hideout.service.post.IPostService
import dev.usbharu.hideout.util.AcctUtil
import kotlinx.coroutines.Dispatchers
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.innerJoin
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import org.koin.core.annotation.Single
import java.time.Instant
import dev.usbharu.hideout.domain.model.hideout.form.Post as FormPost
@ -19,11 +14,10 @@ import dev.usbharu.hideout.domain.model.hideout.form.Post as FormPost
@Single
class PostApiServiceImpl(
private val postService: IPostService,
private val postRepository: IPostRepository,
private val userRepository: IUserRepository
private val postRepository: IPostRepository
) : IPostApiService {
override suspend fun createPost(postForm: FormPost, userId: Long): PostResponse {
val createdPost = postService.createLocal(
override suspend fun createPost(postForm: FormPost, userId: Long): Post {
return postService.createLocal(
PostCreateDto(
text = postForm.text,
overview = postForm.overview,
@ -33,20 +27,11 @@ class PostApiServiceImpl(
userId = userId
)
)
val creator = userRepository.findById(userId)
return PostResponse.from(createdPost, creator!!)
}
@Suppress("InjectDispatcher")
suspend fun <T> query(block: suspend () -> T): T =
newSuspendedTransaction(Dispatchers.IO) { block() }
override suspend fun getById(id: Long, userId: Long?): PostResponse {
val query = query {
Posts.innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { Users.id }).select { Posts.id eq id }
.single()
}
return PostResponse.from(query.toPost(), query.toUser())
override suspend fun getById(id: Long, userId: Long?): Post {
return postRepository.findOneById(id, userId)
?: throw PostNotFoundException("$id was not found or is not authorized.")
}
override suspend fun getAll(
@ -56,12 +41,7 @@ class PostApiServiceImpl(
maxId: Long?,
limit: Int?,
userId: Long?
): List<PostResponse> {
return query {
Posts.innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { id }).selectAll()
.map { PostResponse.from(it.toPost(), it.toUser()) }
}
}
): List<Post> = postRepository.findAll(since, until, minId, maxId, limit, userId)
override suspend fun getByUser(
nameOrId: String,
@ -71,22 +51,23 @@ class PostApiServiceImpl(
maxId: Long?,
limit: Int?,
userId: Long?
): List<PostResponse> {
): List<Post> {
val idOrNull = nameOrId.toLongOrNull()
return if (idOrNull == null) {
val acct = AcctUtil.parse(nameOrId)
query {
Posts.innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { id }).select {
Users.name.eq(acct.username)
.and(Users.domain eq (acct.domain ?: Config.configData.domain))
}.map { PostResponse.from(it.toPost(), it.toUser()) }
}
postRepository.findByUserNameAndDomain(
username = acct.username,
s = acct.domain
?: Config.configData.domain,
since = since,
until = until,
minId = minId,
maxId = maxId,
limit = limit,
userId = userId
)
} else {
query {
Posts.innerJoin(Users, onColumn = { Posts.userId }, otherColumn = { id }).select {
Posts.userId eq idOrNull
}.map { PostResponse.from(it.toPost(), it.toUser()) }
}
postRepository.findByUserId(idOrNull, since, until, minId, maxId, limit, userId)
}
}
}

View File

@ -73,7 +73,6 @@ class ExposedJobRepository(
}
}
@Suppress("SuspendFunWithFlowReturnType")
override suspend fun findNext(names: Set<String>, status: Set<JobStatus>, limit: Int): Flow<ScheduledJob> {
return query {
jobs.select(

View File

@ -20,7 +20,7 @@ paths:
schema:
type: array
items:
$ref: "#/components/schemas/PostResponse"
$ref: "#/components/schemas/Post"
401:
$ref: "#/components/responses/Unauthorized"
403:
@ -37,7 +37,7 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/PostRequest"
$ref: "#/components/schemas/Post"
responses:
200:
description: 成功
@ -65,7 +65,7 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/PostResponse"
$ref: "#/components/schemas/Post"
401:
$ref: "#/components/responses/Unauthorized"
403:
@ -90,7 +90,7 @@ paths:
schema:
type: array
items:
$ref: "#/components/schemas/PostResponse"
$ref: "#/components/schemas/Post"
401:
$ref: "#/components/responses/Unauthorized"
403:
@ -114,7 +114,7 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/PostResponse"
$ref: "#/components/schemas/Post"
401:
$ref: "#/components/responses/Unauthorized"
403:
@ -137,7 +137,7 @@ paths:
schema:
type: array
items:
$ref: "#/components/schemas/UserResponse"
$ref: "#/components/schemas/User"
post:
summary: ユーザーを作成する
@ -181,7 +181,7 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/UserResponse"
$ref: "#/components/schemas/User"
404:
$ref: "#/components/responses/NotFound"
@ -198,7 +198,7 @@ paths:
schema:
type: array
items:
$ref: "#/components/schemas/UserResponse"
$ref: "#/components/schemas/User"
post:
summary: ユーザーをフォローする
security:
@ -228,47 +228,7 @@ paths:
schema:
type: array
items:
$ref: "#/components/schemas/UserResponse"
/login:
post:
summary: ログインする
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UserLogin"
responses:
200:
description: ログイン成功
content:
application/json:
schema:
$ref: "#/components/schemas/JwtToken"
/refresh-token:
post:
summary: 期限切れトークンの再発行をする
responses:
200:
description: トークンの再発行に成功
content:
application/json:
schema:
$ref: "#/components/schemas/JwtToken"
/auth-check:
get:
summary: 認証チェック
responses:
200:
description: 認証に成功
content:
text/plain:
schema:
type: string
$ref: "#/components/schemas/User"
components:
responses:
@ -301,23 +261,8 @@ components:
type: string
schemas:
Visibility:
type: string
enum:
- public
- unlisted
- followers
- direct
UserResponse:
User:
type: object
required:
- id
- name
- domain
- screenName
- description
- url
- createdAt
properties:
id:
type: number
@ -332,30 +277,23 @@ components:
type: string
description:
type: string
nullable: true
url:
type: string
readOnly: true
createdAt:
type: number
readOnly: true
PostResponse:
Post:
type: object
required:
- id
- user
- text
- createdAt
- visibility
- url
- sensitive
properties:
id:
type: integer
format: int64
readOnly: true
user:
$ref: "#/components/schemas/UserResponse"
userId:
type: integer
format: int64
readOnly: true
overview:
type: string
text:
@ -365,7 +303,12 @@ components:
format: int64
readOnly: true
visibility:
$ref: "#/components/schemas/Visibility"
type: string
enum:
- public
- unlisted
- followers
- direct
url:
type: string
format: uri
@ -380,49 +323,13 @@ components:
readOnly: true
sensitive:
type: boolean
apId:
type: string
format: url
readOnly: true
PostRequest:
type: object
properties:
overview:
type: string
text:
type: string
visibility:
$ref: "#/components/schemas/Visibility"
repostId:
type: integer
format: int64
replyId:
type: integer
format: int64
sensitive:
type: boolean
JwtToken:
type: object
properties:
token:
type: string
refreshToken:
type: string
RefreshToken:
type: object
properties:
refreshToken:
type: string
UserLogin:
type: object
properties:
username:
type: string
password:
type: string
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT

View File

@ -1,44 +1,58 @@
import {Component, createEffect, createSignal} from "solid-js";
import {Route, Router, Routes} from "@solidjs/router";
import {TopPage} from "./pages/TopPage";
import {createTheme, CssBaseline, ThemeProvider, useMediaQuery} from "@suid/material";
import {createCookieStorage} from "@solid-primitives/storage";
import {ApiProvider} from "./lib/ApiProvider";
import {Configuration, DefaultApi} from "./generated";
import {LoginPage} from "./pages/LoginPage";
import {Component, createSignal} from "solid-js";
export const App: Component = () => {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const [cookie, setCookie] = createCookieStorage()
const [api, setApi] = createSignal(new DefaultApi(new Configuration({
basePath: window.location.origin + "/api/internal/v1",
accessToken: cookie.token as string
})))
createEffect(() => {
setApi(
new DefaultApi(new Configuration({
basePath: window.location.origin + "/api/internal/v1",
accessToken : cookie.token as string
})))
})
const fn = (form: HTMLButtonElement) => {
console.log(form)
}
const [username, setUsername] = createSignal("")
const [password, setPassword] = createSignal("")
const theme = createTheme({
palette: {
mode: prefersDarkMode() ? 'dark' : 'light',
}
})
return (
<ApiProvider api={api()}>
<ThemeProvider theme={theme}>
<CssBaseline/>
<Router>
<Routes>
<Route path="/" component={TopPage}/>
<Route path="/login" component={LoginPage}/>
</Routes>
</Router>
</ThemeProvider>
</ApiProvider>
<form onSubmit={function (e: SubmitEvent) {
e.preventDefault()
fetch("/login", {
method: "POST",
body: JSON.stringify({username: username(), password: password()}),
headers: {
'Content-Type': 'application/json'
}
}).then(res => res.json())
// .then(res => fetch("/auth-check", {
// method: "GET",
// headers: {
// 'Authorization': 'Bearer ' + res.token
// }
// }))
// .then(res => res.json())
.then(res => {
console.log(res.token);
fetch("/refresh-token", {
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({refreshToken: res.refreshToken}),
}).then(res=> res.json()).then(res => console.log(res.token))
})
}
}>
<input name="username" type="text" placeholder="Username" required
onChange={(e) => setUsername(e.currentTarget.value)}/>
<input name="password" type="password" placeholder="Password" required
onChange={(e) => setPassword(e.currentTarget.value)}/>
<button type="submit">Submit</button>
</form>
)
}
declare module 'solid-js' {
namespace JSX {
interface Directives {
fn: (form: HTMLFormElement) => void
}
}
}

View File

@ -1,8 +0,0 @@
import {Avatar as SuidAvatar} from "@suid/material";
import {Component, JSXElement} from "solid-js";
export const Avatar: Component<{ src: string }> = (props): JSXElement => {
return (
<SuidAvatar src={props.src}/>
)
}

View File

@ -1,14 +0,0 @@
import {ParentComponent} from "solid-js";
import {Button, ListItem, ListItemAvatar, ListItemButton, ListItemIcon, ListItemText} from "@suid/material";
import {Link} from "@solidjs/router";
export const SidebarButton: ParentComponent<{ text: string,linkTo:string }> = (props) => {
return (
<ListItem>
<ListItemButton component={Link} href={props.linkTo}>
<ListItemIcon>{props.children}</ListItemIcon>
<ListItemText primary={props.text}/>
</ListItemButton>
</ListItem>
)
}

View File

@ -1,8 +0,0 @@
import {createContextProvider} from "@solid-primitives/context";
import {createSignal} from "solid-js";
import {DefaultApi, DefaultApiInterface} from "../generated";
export const [ApiProvider,useApi] = createContextProvider((props:{api:DefaultApiInterface}) => {
const [api,setApi] = createSignal(props.api);
return api
},()=>new DefaultApi());

View File

@ -1,16 +0,0 @@
import {DefaultApiInterface} from "../generated";
export class ApiWrapper {
api: DefaultApiInterface;
constructor(initApi: DefaultApiInterface) {
this.api = initApi;
console.log(this.api);
console.log(this.postsGet());
}
postsGet = async () => this.api.postsGet()
usersUserNameGet = async (userName: string) => this.api.usersUserNameGet(userName);
}

View File

@ -1,29 +0,0 @@
import {Component, Match, Switch} from "solid-js";
import {Home, Lock, Mail, Public} from "@suid/icons-material";
import {IconButton} from "@suid/material";
import {Visibility} from "../generated";
export const ShareScopeIndicator: Component<{ visibility: Visibility }> = (props) => {
return <Switch fallback={<Public/>}>
<Match when={props.visibility == "public"}>
<IconButton>
<Public/>
</IconButton>
</Match>
<Match when={props.visibility == "direct"}>
<IconButton>
<Mail/>
</IconButton>
</Match>
<Match when={props.visibility == "followers"}>
<IconButton>
<Lock/>
</IconButton>
</Match>
<Match when={props.visibility == "unlisted"}>
<IconButton>
<Home/>
</IconButton>
</Match>
</Switch>
}

View File

@ -1,45 +0,0 @@
import {Component, createSignal} from "solid-js";
import {Box, Card, CardActions, CardContent, CardHeader, IconButton, Menu, MenuItem, Typography} from "@suid/material";
import {Avatar} from "../atoms/Avatar";
import {Favorite, MoreVert, Reply, ScreenRotationAlt} from "@suid/icons-material";
import {ShareScopeIndicator} from "../molecules/ShareScopeIndicator";
import {PostResponse} from "../generated";
export const Post: Component<{ post: PostResponse }> = (props) => {
const [anchorEl, setAnchorEl] = createSignal<null | HTMLElement>(null)
const open = () => Boolean(anchorEl());
const handleClose = () => {
setAnchorEl(null);
}
return (
<Card>
<CardHeader avatar={<Avatar src={props.post.user.url + "/icon.jpg"}/>} title={props.post.user.screenName}
subheader={`${props.post.user.name}@${props.post.user.domain}`}
action={<IconButton onclick={(event) => {
setAnchorEl(event.currentTarget)
}}><MoreVert/><Menu disableScrollLock anchorEl={anchorEl()} open={open()} onClose={handleClose}><MenuItem
onclick={handleClose}>aaa</MenuItem></Menu> </IconButton>}/>
<CardContent>
<Typography>
{props.post.text}
</Typography>
</CardContent>
<CardActions disableSpacing>
<IconButton>
<Reply/>
</IconButton>
<IconButton>
<ScreenRotationAlt/>
</IconButton>
<IconButton>
<Favorite/>
</IconButton>
<Box sx={{marginLeft: "auto"}}>
<Typography>{new Date(props.post.createdAt).toDateString()}</Typography>
</Box>
<ShareScopeIndicator visibility={props.post.visibility}/>
</CardActions>
</Card>
)
}

View File

@ -1,43 +0,0 @@
import {Component, createSignal} from "solid-js";
import {Button, IconButton, Paper, Stack, TextField, Typography} from "@suid/material";
import {Avatar} from "../atoms/Avatar";
import {AddPhotoAlternate, Poll, Public} from "@suid/icons-material";
import {useApi} from "../lib/ApiProvider";
export const PostForm: Component<{ label: string }> = (props) => {
const [text, setText] = createSignal("")
const api = useApi()
return (
<Paper sx={{width: "100%"}}>
<Stack>
<Stack direction={"row"} spacing={2} sx={{padding: 2}}>
<Avatar src={""}/>
<TextField label={props.label} multiline rows={4} variant={"standard"} onChange={(event)=>setText(event.target.value)} fullWidth/>
</Stack>
<Stack direction={"row"} justifyContent={"space-between"} sx={{padding: 2}}>
<Stack direction={"row"} justifyContent={"flex-start"} alignItems={"center"}>
<IconButton>
<AddPhotoAlternate/>
</IconButton>
<IconButton>
<Poll/>
</IconButton>
<IconButton>
<Public/>
</IconButton>
</Stack>
<Stack direction={"row"} alignItems={"center"} spacing={2}>
<Typography>
aaa
</Typography>
<Button variant={"contained"} onClick={() => {
api().postsPost({text: text()}).then(()=>setText(""))
}}>
稿
</Button>
</Stack>
</Stack>
</Stack>
</Paper>
)
}

View File

@ -1,58 +0,0 @@
import {Button, Card, CardContent, CardHeader, Modal, Stack, TextField} from "@suid/material";
import {Component, createSignal} from "solid-js";
import {createCookieStorage} from "@solid-primitives/storage";
import {useApi} from "../lib/ApiProvider";
import {useNavigate} from "@solidjs/router";
export const LoginPage: Component = () => {
const [username, setUsername] = createSignal("")
const [password, setPassword] = createSignal("")
const [cookie, setCookie] = createCookieStorage();
const navigator = useNavigate();
const api = useApi();
const onSubmit: () => void = () => {
api().loginPost({password: password(), username: username()}).then(value => {
setCookie("token", value.token);
setCookie("refresh-token", value.refreshToken)
navigator("/")
}).catch(reason => {
console.log(reason);
setPassword("")
})
}
return (
<Modal open>
<Card>
<CardHeader/>
<CardContent>
<Stack spacing={3}>
<TextField
value={username()}
onChange={(event) => setUsername(event.target.value)}
label="Username"
type="text"
autoComplete="username"
variant="standard"
/>
<TextField
value={password()}
onChange={(event) => setPassword(event.target.value)}
label="Password"
type="password"
autoComplete="current-password"
variant="standard"
/>
<Button type={"submit"} onClick={onSubmit}>Login</Button>
</Stack>
</CardContent>
</Card>
</Modal>
)
}

View File

@ -1,24 +0,0 @@
import {Component} from "solid-js";
import {MainPage} from "../templates/MainPage";
import {PostForm} from "../organisms/PostForm";
import {Stack} from "@suid/material";
import {PostResponse} from "../generated";
import {PostList} from "../templates/PostList";
import {useApi} from "../lib/ApiProvider";
import {createStore} from "solid-js/store";
export const TopPage: Component = () => {
const api = useApi()
const [posts, setPosts] = createStore<PostResponse[]>([])
api().postsGet().then((res)=>setPosts(res))
return (
<MainPage>
<Stack spacing={1} alignItems={"stretch"}>
<PostForm label={"投稿する"}/>
<PostList posts={posts}/>
</Stack>
</MainPage>
)
}

View File

@ -1,20 +0,0 @@
import {createSignal, ParentComponent} from "solid-js";
import {Grid} from "@suid/material";
import {Sidebar} from "./Sidebar";
export const MainPage: ParentComponent = (props) => {
return (
<Grid container spacing={2} wrap={"nowrap"}>
<Grid item xs={0} md={3}>
<Sidebar/>
</Grid>
<Grid item xs={12} md={6}>
{props.children}
</Grid>
<Grid item xs={0} md={3}>
</Grid>
</Grid>
)
}

View File

@ -1,14 +0,0 @@
import {Component, For} from "solid-js";
import {CircularProgress} from "@suid/material";
import {Post} from "../organisms/Post";
import {PostResponse} from "../generated";
export const PostList: Component<{ posts: PostResponse[] | undefined }> = (props) => {
return (
<For each={props.posts} fallback={<CircularProgress/>}>
{
(item, index) => <Post post={item}/>
}
</For>
)
}

View File

@ -1,13 +0,0 @@
import {Component} from "solid-js";
import {Button, List, Stack} from "@suid/material";
import {Home} from "@suid/icons-material";
import {SidebarButton} from "../atoms/SidebarButton";
export const Sidebar: Component = (props) => {
return (
<List>
<SidebarButton text={"AP"} linkTo={"/"}></SidebarButton>
<SidebarButton text={"Home"} linkTo={"/"}><Home/></SidebarButton>
</List>
)
}

View File

@ -15,7 +15,6 @@ import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken
import dev.usbharu.hideout.domain.model.hideout.form.UserLogin
import dev.usbharu.hideout.exception.InvalidRefreshTokenException
import dev.usbharu.hideout.repository.IUserRepository
import dev.usbharu.hideout.routing.api.internal.v1.auth
import dev.usbharu.hideout.service.auth.IJwtService
import dev.usbharu.hideout.service.core.IMetaService
import dev.usbharu.hideout.service.user.IUserAuthService
@ -25,7 +24,6 @@ import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.config.*
import io.ktor.server.routing.*
import io.ktor.server.testing.*
import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.anyString
@ -72,10 +70,7 @@ class SecurityKtTest {
val jwkProvider = mock<JwkProvider>()
application {
configureSerialization()
configureSecurity(jwkProvider, metaService)
routing {
auth(userAuthService, userRepository, jwtService)
}
configureSecurity(userAuthService, metaService, userRepository, jwtService, jwkProvider)
}
client.post("/login") {
@ -102,10 +97,7 @@ class SecurityKtTest {
val jwkProvider = mock<JwkProvider>()
application {
configureSerialization()
configureSecurity(jwkProvider, metaService)
routing {
auth(userAuthService, userRepository, jwtService)
}
configureSecurity(userAuthService, metaService, userRepository, jwtService, jwkProvider)
}
client.post("/login") {
contentType(ContentType.Application.Json)
@ -130,10 +122,7 @@ class SecurityKtTest {
val jwkProvider = mock<JwkProvider>()
application {
configureSerialization()
configureSecurity(jwkProvider, metaService)
routing {
auth(userAuthService, userRepository, jwtService)
}
configureSecurity(userAuthService, metaService, userRepository, jwtService, jwkProvider)
}
client.post("/login") {
contentType(ContentType.Application.Json)
@ -151,10 +140,7 @@ class SecurityKtTest {
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
application {
configureSerialization()
configureSecurity(mock(), mock())
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), mock(), mock(), mock(), mock())
}
client.get("/auth-check").apply {
assertEquals(HttpStatusCode.Unauthorized, call.response.status)
@ -169,10 +155,7 @@ class SecurityKtTest {
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
application {
configureSerialization()
configureSecurity(mock(), mock())
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), mock(), mock(), mock(), mock())
}
client.get("/auth-check") {
header("Authorization", "Digest dsfjjhogalkjdfmlhaog")
@ -189,10 +172,7 @@ class SecurityKtTest {
Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper())
application {
configureSerialization()
configureSecurity(mock(), mock())
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), mock(), mock(), mock(), mock())
}
client.get("/auth-check") {
header("Authorization", "")
@ -210,10 +190,7 @@ class SecurityKtTest {
application {
configureSerialization()
configureSecurity(mock(), mock())
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), mock(), mock(), mock(), mock())
}
client.get("/auth-check") {
header("Authorization", "Bearer ")
@ -267,12 +244,11 @@ class SecurityKtTest {
)
)
}
val userRepository = mock<IUserRepository>()
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(jwkProvider, metaService)
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
}
client.get("/auth-check") {
@ -328,12 +304,11 @@ class SecurityKtTest {
)
)
}
val userRepository = mock<IUserRepository>()
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(jwkProvider, metaService)
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
}
client.get("/auth-check") {
header("Authorization", "Bearer $token")
@ -387,12 +362,11 @@ class SecurityKtTest {
)
)
}
val userRepository = mock<IUserRepository>()
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(jwkProvider, metaService)
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
}
client.get("/auth-check") {
header("Authorization", "Bearer $token")
@ -446,12 +420,11 @@ class SecurityKtTest {
)
)
}
val userRepository = mock<IUserRepository>()
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(jwkProvider, metaService)
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
}
client.get("/auth-check") {
header("Authorization", "Bearer $token")
@ -504,12 +477,11 @@ class SecurityKtTest {
)
)
}
val userRepository = mock<IUserRepository>()
val jwtService = mock<IJwtService>()
application {
configureSerialization()
configureSecurity(jwkProvider, metaService)
routing {
auth(mock(), mock(), mock())
}
configureSecurity(mock(), metaService, userRepository, jwtService, jwkProvider)
}
client.get("/auth-check") {
header("Authorization", "Bearer $token")
@ -529,10 +501,7 @@ class SecurityKtTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
routing {
auth(mock(), mock(), jwtService)
}
configureSecurity(mock(), mock(), mock(), jwtService, mock())
}
client.post("/refresh-token") {
header("Content-Type", "application/json")
@ -554,10 +523,7 @@ class SecurityKtTest {
application {
configureStatusPages()
configureSerialization()
configureSecurity(mock(), mock())
routing {
auth(mock(), mock(), jwtService)
}
configureSecurity(mock(), mock(), mock(), jwtService, mock())
}
client.post("/refresh-token") {
header("Content-Type", "application/json")

View File

@ -4,8 +4,6 @@ import com.auth0.jwt.interfaces.Claim
import com.auth0.jwt.interfaces.Payload
import com.fasterxml.jackson.module.kotlin.readValue
import dev.usbharu.hideout.config.Config
import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse
import dev.usbharu.hideout.domain.model.hideout.dto.UserResponse
import dev.usbharu.hideout.domain.model.hideout.entity.Post
import dev.usbharu.hideout.domain.model.hideout.entity.Visibility
import dev.usbharu.hideout.plugins.TOKEN_AUTH
@ -34,27 +32,18 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
)
val posts = listOf(
PostResponse(
Post(
id = 12345,
user = user,
userId = 4321,
text = "test1",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1"
),
PostResponse(
Post(
id = 123456,
user = user,
userId = 4322,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -75,7 +64,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -100,35 +89,27 @@ class PostsTest {
val payload = mock<Payload> {
on { getClaim(eq("uid")) } doReturn claim
}
val user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
)
val posts = listOf(
PostResponse(
Post(
id = 12345,
user = user,
userId = 4321,
text = "test1",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1"
),
PostResponse(
Post(
id = 123456,
user = user,
userId = 4322,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/2"
),
PostResponse(
Post(
id = 1234567,
user = user,
userId = 4333,
text = "Followers only",
visibility = Visibility.FOLLOWERS,
createdAt = Instant.now().toEpochMilli(),
@ -175,18 +156,9 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
)
val post = PostResponse(
id = 12345,
user = user,
val post = Post(
12345,
1234,
text = "aaa",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -197,7 +169,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -215,17 +187,9 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val post = PostResponse(
val post = Post(
12345,
UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
),
1234,
text = "aaa",
visibility = Visibility.FOLLOWERS,
createdAt = Instant.now().toEpochMilli(),
@ -278,22 +242,14 @@ class PostsTest {
onBlocking { createPost(any(), any()) } doAnswer {
val argument = it.getArgument<dev.usbharu.hideout.domain.model.hideout.form.Post>(0)
val userId = it.getArgument<Long>(1)
PostResponse(
id = 123L,
user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
),
overview = null,
text = argument.text,
createdAt = Instant.now().toEpochMilli(),
visibility = Visibility.PUBLIC,
url = "https://example.com"
Post(
123L,
userId,
null,
argument.text,
Instant.now().toEpochMilli(),
Visibility.PUBLIC,
"https://example.com"
)
}
}
@ -334,27 +290,18 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
)
val posts = listOf(
PostResponse(
Post(
id = 12345,
user = user,
userId = 1,
text = "test1",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1"
),
PostResponse(
Post(
id = 123456,
user = user,
userId = 1,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -376,7 +323,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -395,27 +342,18 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
)
val posts = listOf(
PostResponse(
Post(
id = 12345,
user = user,
userId = 1,
text = "test1",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1"
),
PostResponse(
Post(
id = 123456,
user = user,
userId = 1,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -437,7 +375,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -456,27 +394,18 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
)
val posts = listOf(
PostResponse(
Post(
id = 12345,
user = user,
userId = 1,
text = "test1",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1"
),
PostResponse(
Post(
id = 123456,
user = user,
userId = 1,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -498,7 +427,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -517,27 +446,18 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
)
val posts = listOf(
PostResponse(
Post(
id = 12345,
user = user,
userId = 1,
text = "test1",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
url = "https://example.com/posts/1"
),
PostResponse(
Post(
id = 123456,
user = user,
userId = 1,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -559,7 +479,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -578,17 +498,9 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val post = PostResponse(
val post = Post(
id = 123456,
user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
),
userId = 1,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -599,7 +511,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -618,17 +530,9 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val post = PostResponse(
val post = Post(
id = 123456,
user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
),
userId = 1,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -639,7 +543,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -658,17 +562,9 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val post = PostResponse(
val post = Post(
id = 123456,
user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
),
userId = 1,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -679,7 +575,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)
@ -698,17 +594,9 @@ class PostsTest {
environment {
config = ApplicationConfig("empty.conf")
}
val post = PostResponse(
val post = Post(
id = 123456,
user = UserResponse(
id = 54321,
name = "user1",
domain = "example.com",
screenName = "user 1",
description = "Test user",
url = "https://example.com/users/54321",
createdAt = Instant.now().toEpochMilli()
),
userId = 1,
text = "test2",
visibility = Visibility.PUBLIC,
createdAt = Instant.now().toEpochMilli(),
@ -719,7 +607,7 @@ class PostsTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
posts(postService)

View File

@ -58,7 +58,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userService)
@ -96,7 +96,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(userService, mock())
@ -127,7 +127,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(userService, mock())
@ -162,7 +162,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -195,7 +195,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -228,7 +228,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -261,7 +261,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -306,7 +306,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -351,7 +351,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -396,7 +396,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -591,7 +591,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -636,7 +636,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)
@ -681,7 +681,7 @@ class UsersTest {
}
application {
configureSerialization()
configureSecurity(mock(), mock())
configureSecurity(mock(), mock(), mock(), mock(), mock())
routing {
route("/api/internal/v1") {
users(mock(), userApiService)

View File

@ -48,21 +48,13 @@ class ActivityPubReceiveFollowServiceImplTest {
firstValue.invoke(scheduleContext, ReceiveFollowJob)
val actor = scheduleContext.props.props[ReceiveFollowJob.actor.name]
val targetActor = scheduleContext.props.props[ReceiveFollowJob.targetActor.name]
val follow = scheduleContext.props.props[ReceiveFollowJob.follow.name] as String
val follow = scheduleContext.props.props[ReceiveFollowJob.follow.name]
assertEquals("https://follower.example.com", actor)
assertEquals("https://example.com", targetActor)
//language=JSON
assertEquals(
Json.parseToJsonElement(
"""{
"type": "Follow",
"name": "Follow",
"actor": "https://follower.example.com",
"object": "https://example.com",
"@context": null
}"""
),
Json.parseToJsonElement(follow)
"""{"type":"Follow","name":"Follow","actor":"https://follower.example.com","object":"https://example.com","@context":null}""",
follow
)
}
}
@ -163,14 +155,7 @@ class ActivityPubReceiveFollowServiceImplTest {
data = mapOf<String, Any>(
ReceiveFollowJob.actor.name to "https://follower.example.com",
ReceiveFollowJob.targetActor.name to "https://example.com",
//language=JSON
ReceiveFollowJob.follow.name to """{
"type": "Follow",
"name": "Follow",
"object": "https://example.com",
"actor": "https://follower.example.com",
"@context": null
}"""
ReceiveFollowJob.follow.name to """{"type":"Follow","name":"Follow","object":"https://example.com","actor":"https://follower.example.com","@context":null}"""
),
json = Json
)

View File

@ -1,24 +1,21 @@
import {defineConfig, splitVendorChunkPlugin} from 'vite';
import { defineConfig } from 'vite';
import solidPlugin from 'vite-plugin-solid';
import suidPlugin from "@suid/vite-plugin";
import visualizer from "rollup-plugin-visualizer";
export default defineConfig({
plugins: [solidPlugin(),suidPlugin(),splitVendorChunkPlugin()],
plugins: [solidPlugin(),suidPlugin()],
server: {
port: 3000,
proxy: {
'/api': 'http://localhost:8080',
'/login': 'http://localhost:8080',
'/auth-check': 'http://localhost:8080',
'/refresh-token': 'http://localhost:8080',
}
},
root: './src/main/web',
build: {
target: 'esnext',
outDir: '../resources/static',
rollupOptions:{
plugins: [
visualizer()
]
}
},
});